carapace 0.2.0

A code runner for online judge
Documentation
use crate::utils::libc_call;

use std::mem::{self, ManuallyDrop};

use std::panic::{self, AssertUnwindSafe};

use std::{io, ptr};

use nix::sched::CloneFlags;

use nix::unistd::Pid;

pub fn wait_child(child_pid: Pid) -> io::Result<(i32, i32)> {
    unsafe fn waitid(
        pid: u32,
        info: &mut libc::siginfo_t,
        nohang: bool,
    ) -> io::Result<Option<(i32, i32)>> {
        let options = if nohang {
            libc::WEXITED | libc::WNOHANG
        } else {
            libc::WEXITED
        };

        libc_call(|| libc::waitid(libc::P_PID, pid, info, options))?;

        if info.si_pid() > 0 {
            if info.si_code == libc::CLD_EXITED {
                Ok(Some((info.si_status(), 0)))
            } else {
                Ok(Some((0, info.si_status())))
            }
        } else {
            Ok(None)
        }
    }

    let pid = child_pid.as_raw() as u32;
    let mut info: libc::siginfo_t = unsafe { mem::zeroed() };

    loop {
        if let Some(ret) = unsafe { waitid(pid, &mut info, false)? } {
            return Ok(ret);
        }
    }
}

pub unsafe fn clone_proc<F: FnOnce() -> libc::c_int>(
    cb: F,
    stack: &mut [u8],
    flags: CloneFlags,
    signal: libc::c_int,
) -> io::Result<Pid> {
    extern "C" fn child_fn<F>(data: *mut libc::c_void) -> libc::c_int
    where
        F: FnOnce() -> libc::c_int + Sized,
    {
        let f = unsafe { ptr::read(data.cast::<F>()) };
        panic::catch_unwind(AssertUnwindSafe(|| f())).unwrap_or(101)
    }

    let mut f = ManuallyDrop::new(cb);

    let data: *mut F = &mut *f;
    let stack_top = stack.as_mut_ptr().add(stack.len());

    let ret = libc_call(|| {
        libc::clone(
            child_fn::<F>,
            stack_top.cast(),
            flags.bits() | signal,
            data.cast(),
        )
    });

    Ok(Pid::from_raw(ret? as _))
}