mxsh 0.2.0

Embeddable POSIX-style shell parser and runtime
Documentation
use std::io;

use crate::status::normalize_exit_status;

/// Wait behavior for runtime-managed processes.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WaitMode {
    Poll,
    Block,
}

/// Structured process lifecycle events surfaced by a runtime.
#[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)
    }
}