use std::io;
use crate::status::normalize_exit_status;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WaitMode {
Poll,
Block,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ProcessEvent {
Running,
Continued,
Stopped(i32),
Exited(i32),
Signaled(i32),
}
#[cfg(feature = "unix-runtime")]
pub(crate) fn wait_process(pid: libc::pid_t, mode: WaitMode) -> Result<ProcessEvent, io::Error> {
let options = match mode {
WaitMode::Poll => libc::WNOHANG | libc::WUNTRACED | libc::WCONTINUED,
WaitMode::Block => libc::WUNTRACED | libc::WCONTINUED,
};
loop {
let mut wstatus: i32 = 0;
let waited = unsafe { libc::waitpid(pid, &mut wstatus, options) };
if waited == 0 {
return Ok(ProcessEvent::Running);
}
if waited == pid {
return Ok(wait_status_to_event(wstatus));
}
if waited < 0 {
let err = io::Error::last_os_error();
if err.raw_os_error() == Some(libc::EINTR) {
continue;
}
return Err(err);
}
}
}
pub(crate) fn wait_child_status(
mut wait_once: impl FnMut() -> Result<ProcessEvent, io::Error>,
) -> i32 {
loop {
match wait_once() {
Ok(ProcessEvent::Running | ProcessEvent::Continued) => continue,
Ok(ProcessEvent::Stopped(sig)) => return normalize_exit_status(128 + i64::from(sig)),
Ok(ProcessEvent::Exited(code)) => return normalize_exit_status(code),
Ok(ProcessEvent::Signaled(sig)) => return normalize_exit_status(128 + i64::from(sig)),
Err(_) => return 128,
}
}
}
#[cfg(feature = "unix-runtime")]
fn wait_status_to_event(wstatus: i32) -> ProcessEvent {
if libc::WIFCONTINUED(wstatus) {
ProcessEvent::Continued
} else if libc::WIFSTOPPED(wstatus) {
ProcessEvent::Stopped(libc::WSTOPSIG(wstatus))
} else if libc::WIFEXITED(wstatus) {
ProcessEvent::Exited(libc::WEXITSTATUS(wstatus))
} else if libc::WIFSIGNALED(wstatus) {
ProcessEvent::Signaled(libc::WTERMSIG(wstatus))
} else {
ProcessEvent::Exited(128)
}
}