sys_wait: implement waitid (idtype_t/id_t/P_*/CLD_*, siginfo population)
Adapted from the archived local P3-waitid.patch. Fills a POSIX gap needed by job-control-capable shells (brush/nix). waitid() maps (idtype,id)->waitpid pid, translates WEXITED/WSTOPPED/WCONTINUED/WNOWAIT options, and populates siginfo_t (si_pid/si_signo/si_code/si_status) from the wait status.
This commit is contained in:
+158
-15
@@ -4,13 +4,38 @@
|
||||
|
||||
use crate::{
|
||||
error::ResultExt,
|
||||
header::signal::siginfo_t,
|
||||
out::Out,
|
||||
platform::{
|
||||
Pal, Sys,
|
||||
types::{c_int, pid_t},
|
||||
ERRNO, Pal, Sys,
|
||||
types::{c_int, id_t, pid_t},
|
||||
},
|
||||
};
|
||||
|
||||
/// `idtype_t` for [`waitid`]. POSIX specifies this as an enum; relibc uses the
|
||||
/// underlying integer representation together with the `P_*` constants below.
|
||||
pub type idtype_t = c_int;
|
||||
|
||||
/// [`waitid`]: wait for any child.
|
||||
pub const P_ALL: idtype_t = 0;
|
||||
/// [`waitid`]: wait for the child whose process ID equals `id`.
|
||||
pub const P_PID: idtype_t = 1;
|
||||
/// [`waitid`]: wait for any child whose process group ID equals `id`.
|
||||
pub const P_PGID: idtype_t = 2;
|
||||
|
||||
/// `siginfo_t.si_code`: child exited normally.
|
||||
pub const CLD_EXITED: c_int = 1;
|
||||
/// `siginfo_t.si_code`: child was killed by a signal.
|
||||
pub const CLD_KILLED: c_int = 2;
|
||||
/// `siginfo_t.si_code`: child was killed by a signal and dumped core.
|
||||
pub const CLD_DUMPED: c_int = 3;
|
||||
/// `siginfo_t.si_code`: traced child has trapped.
|
||||
pub const CLD_TRAPPED: c_int = 4;
|
||||
/// `siginfo_t.si_code`: child has stopped.
|
||||
pub const CLD_STOPPED: c_int = 5;
|
||||
/// `siginfo_t.si_code`: stopped child has continued.
|
||||
pub const CLD_CONTINUED: c_int = 6;
|
||||
|
||||
/// Do not hang if no status is available; return immediately.
|
||||
pub const WNOHANG: c_int = 1;
|
||||
/// Report status of stopped child process.
|
||||
@@ -54,19 +79,137 @@ 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 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
|
||||
}
|
||||
|
||||
/// Translate a `(idtype, id)` pair into the `pid` argument understood by
|
||||
/// [`waitpid`]. Returns `None` for an unrecognised `idtype`.
|
||||
fn map_waitid_target(idtype: idtype_t, id: id_t) -> Option<pid_t> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate `waitid` options into `waitpid` options. `waitid` requires at
|
||||
/// least one of `WEXITED`/`WSTOPPED`/`WCONTINUED`; `None` signals `EINVAL`.
|
||||
fn map_waitid_options(options: c_int) -> Option<c_int> {
|
||||
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;
|
||||
}
|
||||
if options & WNOWAIT != 0 {
|
||||
waitpid_options |= WNOWAIT;
|
||||
}
|
||||
|
||||
Some(waitpid_options)
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/waitid.html>.
|
||||
///
|
||||
/// Waits for one of the caller's children to change state and reports the
|
||||
/// change through `infop`. On success returns `0`; on error returns `-1` and
|
||||
/// sets errno. With `WNOHANG` set and no child ready, returns `0` with
|
||||
/// `infop->si_pid == 0`.
|
||||
#[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(unsafe { Out::from_mut(&mut status) }), waitpid_options)
|
||||
.or_minus_one_errno();
|
||||
if pid < 0 {
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
*infop = core::mem::zeroed();
|
||||
}
|
||||
if pid == 0 {
|
||||
// WNOHANG requested and no child was ready.
|
||||
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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/waitpid.html>.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user