diff --git a/src/header/sys_wait/mod.rs b/src/header/sys_wait/mod.rs index 11f4bf2c..23e1a356 100644 --- a/src/header/sys_wait/mod.rs +++ b/src/header/sys_wait/mod.rs @@ -4,13 +4,17 @@ use crate::{ error::ResultExt, + header::signal::siginfo_t, out::Out, platform::{ - Pal, Sys, - types::{c_int, pid_t}, + ERRNO, Pal, Sys, + types::{c_int, c_uint, pid_t}, }, }; +pub type idtype_t = c_int; +pub type id_t = c_uint; + pub const WNOHANG: c_int = 1; pub const WUNTRACED: c_int = 2; @@ -24,25 +28,112 @@ pub const __WALL: c_int = 0x4000_0000; #[allow(overflowing_literals)] pub const __WCLONE: c_int = 0x8000_0000; +pub const P_ALL: idtype_t = 0; +pub const P_PID: idtype_t = 1; +pub const P_PGID: idtype_t = 2; + +pub const CLD_EXITED: c_int = 1; +pub const CLD_KILLED: c_int = 2; +pub const CLD_DUMPED: c_int = 3; +pub const CLD_TRAPPED: c_int = 4; +pub const CLD_STOPPED: c_int = 5; +pub const CLD_CONTINUED: c_int = 6; + +fn wexitstatus(status: c_int) -> c_int { (status >> 8) & 0xff } +fn wtermsig(status: c_int) -> c_int { status & 0x7f } +fn wstopsig(status: c_int) -> c_int { wexitstatus(status) } +fn wcoredump(status: c_int) -> bool { (status & 0x80) != 0 } +fn wifexited(status: c_int) -> bool { (status & 0x7f) == 0 } +fn wifstopped(status: c_int) -> bool { (status & 0xff) == 0x7f } +fn wifcontinued(status: c_int) -> bool { status == 0xffff } + /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn wait(stat_loc: *mut c_int) -> pid_t { unsafe { waitpid(!0, stat_loc, 0) } } -/* - * TODO: implement idtype_t, id_t, and siginfo_t - * - * #[unsafe(no_mangle)] - * pub unsafe extern "C" fn waitid( - * idtype: idtype_t, - * id: id_t, - * infop: siginfo_t, - * options: c_int - * ) -> c_int { - * unimplemented!(); - * } - */ +fn map_waitid_target(idtype: idtype_t, id: id_t) -> Option { + match idtype { + P_ALL => Some(-1), + P_PID => Some(id as pid_t), + P_PGID => Some(if id == 0 { 0 } else { -(id as pid_t) }), + _ => None, + } +} + +fn map_waitid_options(options: c_int) -> Option { + let interest = options & (WEXITED | WSTOPPED | WCONTINUED); + if interest == 0 { + return None; + } + + let mut waitpid_options = 0; + if options & WNOHANG != 0 { + waitpid_options |= WNOHANG; + } + if options & WSTOPPED != 0 { + waitpid_options |= WUNTRACED; + } + if options & WCONTINUED != 0 { + waitpid_options |= WCONTINUED; + } + Some(waitpid_options) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn waitid( + idtype: idtype_t, + id: id_t, + infop: *mut siginfo_t, + options: c_int, +) -> c_int { + if infop.is_null() { + ERRNO.set(crate::header::errno::EFAULT); + return -1; + } + + let Some(pid_target) = map_waitid_target(idtype, id) else { + ERRNO.set(crate::header::errno::EINVAL); + return -1; + }; + let Some(waitpid_options) = map_waitid_options(options) else { + ERRNO.set(crate::header::errno::EINVAL); + return -1; + }; + + let mut status = 0; + let pid = Sys::waitpid(pid_target, Some(Out::from_mut(&mut status)), waitpid_options).or_minus_one_errno(); + if pid < 0 { + return -1; + } + + unsafe { *infop = core::mem::zeroed() }; + if pid == 0 { + return 0; + } + + unsafe { + (*infop).si_pid = pid; + (*infop).si_signo = crate::header::signal::SIGCHLD as c_int; + (*infop).si_errno = 0; + if wifexited(status) { + (*infop).si_code = CLD_EXITED; + (*infop).si_status = wexitstatus(status); + } else if wifstopped(status) { + (*infop).si_code = CLD_STOPPED; + (*infop).si_status = wstopsig(status); + } else if wifcontinued(status) { + (*infop).si_code = CLD_CONTINUED; + (*infop).si_status = crate::header::signal::SIGCONT as c_int; + } else { + (*infop).si_status = wtermsig(status); + (*infop).si_code = if wcoredump(status) { CLD_DUMPED } else { CLD_KILLED }; + } + } + + 0 +} /// See . #[unsafe(no_mangle)]