From 193749b2d9c14af00f38665155853792c96df4ba Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 29 Nov 2023 18:34:42 +0000 Subject: [PATCH] add test --- src/unistd.rs | 84 +++++++++++++++++++++++---------------------- test/test_unistd.rs | 29 ++++++++++++++++ 2 files changed, 72 insertions(+), 41 deletions(-) diff --git a/src/unistd.rs b/src/unistd.rs index 44348e69c6..96a7c46e44 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -2911,52 +2911,54 @@ mod getres { } } -feature! { -#![feature = "process"] +#[cfg(feature = "process")] #[cfg(target_os = "freebsd")] - libc_bitflags! { - /// Flags for [`rfork`] +libc_bitflags! { + /// Flags for [`rfork`] + /// + /// subset of flags supported by FreeBSD 12.x and onwards + /// with a safe outcome, thus as `RFMEM` can possibly lead to undefined behavior, + /// it is not in the list. And `rfork_thread` is deprecated. + pub struct RforkFlags: libc::c_int { + /// creates a new process. + RFPROC; + /// the child process will detach from the parent. + /// however, no status will be emitted at child's exit. + RFNOWAIT; + /// the file descriptor's table will be copied + RFFDG; + /// a new file descriptor's table will be created + RFCFDG; + /// force sharing the sigacts structure between + /// the child and the parent. /// - /// subset of flags supported by FreeBSD 12.x and onwards - /// with a safe outcome, thus as `RFMEM` can possibly lead to undefined behavior, - /// it is not in the list. And `rfork_thread` is deprecated. - pub struct RforkFlags: libc::c_int { - /// creates a new process. - RFPROC; - /// the child process will detach from the parent. - /// however, no status will be emitted at child's exit. - RFNOWAIT; - /// the file descriptor's table will be copied - RFFDG; - /// a new file descriptor's table will be created - RFCFDG; - /// force sharing the sigacts structure between - /// the child and the parent. - /// - /// FIXME: waiting to be in the libc's crate. - // RFSIGSHARE; - /// enables kernel thread support. - RFTHREAD; - /// sets a status to emit at child's exit. - RFTSIGZMB; - /// linux's behavior compatibility setting. - /// emits SIGUSR1 as opposed to SIGCHLD upon child's exit. - RFLINUXTHPN; - } + /// FIXME: waiting to be in the libc's crate. + // RFSIGSHARE; + /// enables kernel thread support. + RFTHREAD; + /// sets a status to emit at child's exit. + RFTSIGZMB; + /// linux's behavior compatibility setting. + /// emits SIGUSR1 as opposed to SIGCHLD upon child's exit. + RFLINUXTHPN; } +} - /// rfork can be used to have a tigher control about which resources child - /// and parent process will be sharing, file descriptors, address spaces - /// and child exit's behavior. - pub unsafe fn rfork(flags: RforkFlags) -> Result { - let res = unsafe { libc::rfork(flags.bits()) }; - use ForkResult::*; +feature! { +#![feature = "process"] +#[cfg(target_os = "freebsd")] +/// rfork can be used to have a tigher control about which resources child +/// and parent process will be sharing, file descriptors, address spaces +/// and child exit's behavior. +pub unsafe fn rfork(flags: RforkFlags) -> Result { + use ForkResult::*; + let res = unsafe { libc::rfork(flags.bits()) }; - Errno::result(res).map(|res| match res { - 0 => Child, - res => Parent { child: Pid(res) }, - }) - } + Errno::result(res).map(|res| match res { + 0 => Child, + res => Parent { child: Pid(res) }, + }) +} } #[cfg(feature = "fs")] diff --git a/test/test_unistd.rs b/test/test_unistd.rs index 5a58585e7d..d734df51d8 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -66,6 +66,35 @@ fn test_fork_and_waitpid() { } } +#[test] +#[cfg(target_os = "freebsd")] +fn test_rfork_and_waitpid() { + let _m = crate::FORK_MTX.lock(); + + // Safe: Child only calls `_exit`, which is signal-safe + match unsafe { rfork(RforkFlags::RFPROC | RforkFlags::RFTHREAD) }.expect("Error: Rfork Failed") { + Child => unsafe { _exit(0) }, + Parent { child } => { + // assert that child was created and pid > 0 + let child_raw: ::libc::pid_t = child.into(); + assert!(child_raw > 0); + let wait_status = waitpid(child, None); + match wait_status { + // assert that waitpid returned correct status and the pid is the one of the child + Ok(WaitStatus::Exited(pid_t, _)) => assert_eq!(pid_t, child), + + // panic, must never happen + s @ Ok(_) => { + panic!("Child exited {s:?}, should never happen") + } + + // panic, waitpid should never fail + Err(s) => panic!("Error: waitpid returned Err({s:?}"), + } + } + } +} + #[test] fn test_wait() { // Grab FORK_MTX so wait doesn't reap a different test's child process