Skip to content

Commit 3a20eef

Browse files
authored
FreeBSD's rfork wrapper proposal (#2121)
1 parent 75bddf2 commit 3a20eef

File tree

3 files changed

+80
-0
lines changed

3 files changed

+80
-0
lines changed

changelog/2121.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added rfork support for FreeBSD in `unistd`

src/unistd.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2898,6 +2898,54 @@ mod getres {
28982898
}
28992899
}
29002900

2901+
#[cfg(feature = "process")]
2902+
#[cfg(target_os = "freebsd")]
2903+
libc_bitflags! {
2904+
/// Flags for [`rfork`]
2905+
///
2906+
/// subset of flags supported by FreeBSD 12.x and onwards
2907+
/// with a safe outcome, thus as `RFMEM` can possibly lead to undefined behavior,
2908+
/// it is not in the list. And `rfork_thread` is deprecated.
2909+
pub struct RforkFlags: libc::c_int {
2910+
/// creates a new process.
2911+
RFPROC;
2912+
/// the child process will detach from the parent.
2913+
/// however, no status will be emitted at child's exit.
2914+
RFNOWAIT;
2915+
/// the file descriptor's table will be copied
2916+
RFFDG;
2917+
/// a new file descriptor's table will be created
2918+
RFCFDG;
2919+
/// force sharing the sigacts structure between
2920+
/// the child and the parent.
2921+
RFSIGSHARE;
2922+
/// enables kernel thread support.
2923+
RFTHREAD;
2924+
/// sets a status to emit at child's exit.
2925+
RFTSIGZMB;
2926+
/// linux's behavior compatibility setting.
2927+
/// emits SIGUSR1 as opposed to SIGCHLD upon child's exit.
2928+
RFLINUXTHPN;
2929+
}
2930+
}
2931+
2932+
feature! {
2933+
#![feature = "process"]
2934+
#[cfg(target_os = "freebsd")]
2935+
/// rfork can be used to have a tigher control about which resources child
2936+
/// and parent process will be sharing, file descriptors, address spaces
2937+
/// and child exit's behavior.
2938+
pub unsafe fn rfork(flags: RforkFlags) -> Result<ForkResult> {
2939+
use ForkResult::*;
2940+
let res = unsafe { libc::rfork(flags.bits()) };
2941+
2942+
Errno::result(res).map(|res| match res {
2943+
0 => Child,
2944+
res => Parent { child: Pid(res) },
2945+
})
2946+
}
2947+
}
2948+
29012949
#[cfg(feature = "fs")]
29022950
libc_bitflags! {
29032951
/// Options for access()

test/test_unistd.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,37 @@ fn test_fork_and_waitpid() {
6666
}
6767
}
6868

69+
#[test]
70+
#[cfg(target_os = "freebsd")]
71+
fn test_rfork_and_waitpid() {
72+
let _m = crate::FORK_MTX.lock();
73+
74+
// Safe: Child only calls `_exit`, which is signal-safe
75+
match unsafe { rfork(RforkFlags::RFPROC | RforkFlags::RFTHREAD) }
76+
.expect("Error: Rfork Failed")
77+
{
78+
Child => unsafe { _exit(0) },
79+
Parent { child } => {
80+
// assert that child was created and pid > 0
81+
let child_raw: ::libc::pid_t = child.into();
82+
assert!(child_raw > 0);
83+
let wait_status = waitpid(child, None);
84+
match wait_status {
85+
// assert that waitpid returned correct status and the pid is the one of the child
86+
Ok(WaitStatus::Exited(pid_t, _)) => assert_eq!(pid_t, child),
87+
88+
// panic, must never happen
89+
s @ Ok(_) => {
90+
panic!("Child exited {s:?}, should never happen")
91+
}
92+
93+
// panic, waitpid should never fail
94+
Err(s) => panic!("Error: waitpid returned Err({s:?}"),
95+
}
96+
}
97+
}
98+
}
99+
69100
#[test]
70101
fn test_wait() {
71102
// Grab FORK_MTX so wait doesn't reap a different test's child process

0 commit comments

Comments
 (0)