246 lines
6.0 KiB
Diff
246 lines
6.0 KiB
Diff
diff --git a/src/header/sys_wait/mod.rs b/src/header/sys_wait/mod.rs
|
|
--- a/src/header/sys_wait/mod.rs
|
|
+++ b/src/header/sys_wait/mod.rs
|
|
@@ -4,12 +4,16 @@
|
|
|
|
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,143 @@
|
|
#[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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html>.
|
|
#[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<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,
|
|
+ }
|
|
+}
|
|
+
|
|
+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)
|
|
+}
|
|
+
|
|
+#[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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/waitpid.html>.
|
|
#[unsafe(no_mangle)]
|
|
diff --git a/tests/Makefile.tests.mk b/tests/Makefile.tests.mk
|
|
--- a/tests/Makefile.tests.mk
|
|
+++ b/tests/Makefile.tests.mk
|
|
@@ -312,6 +312,7 @@
|
|
grp/getgrgid_r \
|
|
grp/getgrnam_r \
|
|
grp/gr_iter \
|
|
+ waitid \
|
|
waitpid \
|
|
waitpid_multiple \
|
|
$(FAILING_TESTS)
|
|
diff --git a/tests/waitid.c b/tests/waitid.c
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/tests/waitid.c
|
|
@@ -0,0 +1,50 @@
|
|
+#include <assert.h>
|
|
+#include <signal.h>
|
|
+#include <stdlib.h>
|
|
+#include <sys/wait.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include "test_helpers.h"
|
|
+
|
|
+static void wait_until_child_exits(pid_t pid) {
|
|
+ siginfo_t info;
|
|
+ for (int i = 0; i < 50; ++i) {
|
|
+ info.si_pid = 0;
|
|
+ int ret = waitid(P_PID, pid, &info, WEXITED | WNOHANG | WNOWAIT);
|
|
+ ERROR_IF(waitid, ret, == -1);
|
|
+ if (info.si_pid == pid) {
|
|
+ assert(info.si_code == CLD_EXITED);
|
|
+ assert(info.si_status == 42);
|
|
+ return;
|
|
+ }
|
|
+ usleep(10000);
|
|
+ }
|
|
+ assert(!"waitid never observed child exit");
|
|
+}
|
|
+
|
|
+int main(void) {
|
|
+ pid_t pid = fork();
|
|
+ ERROR_IF(fork, pid, == -1);
|
|
+
|
|
+ if (pid == 0) {
|
|
+ usleep(50000);
|
|
+ _Exit(42);
|
|
+ }
|
|
+
|
|
+ siginfo_t info;
|
|
+ info.si_pid = 0;
|
|
+ int ret = waitid(P_PID, pid, &info, WEXITED | WNOHANG | WNOWAIT);
|
|
+ ERROR_IF(waitid, ret, == -1);
|
|
+ assert(info.si_pid == 0);
|
|
+
|
|
+ wait_until_child_exits(pid);
|
|
+
|
|
+ int status = 0;
|
|
+ pid_t waited = waitpid(pid, &status, 0);
|
|
+ ERROR_IF(waitpid, waited, == -1);
|
|
+ assert(waited == pid);
|
|
+ assert(WIFEXITED(status));
|
|
+ assert(WEXITSTATUS(status) == 42);
|
|
+
|
|
+ return EXIT_SUCCESS;
|
|
+}
|