fix(process): safely exit forked child after spawn

Co-Authored-By: Michael Murphy <michael@mmurphy.dev>
This commit is contained in:
Andrei Ivanou 2026-05-17 11:08:40 +02:00 • committed by Ashley Wulber
parent 631c81b170
commit 5782cb927e

View file

@ -5,7 +5,7 @@
use smol::io::AsyncReadExt; use smol::io::AsyncReadExt;
use std::io; use std::io;
use std::os::fd::OwnedFd; use std::os::fd::OwnedFd;
use std::process::{Command, Stdio, exit}; use std::process::{Command, Stdio};
#[cfg(feature = "tokio")] #[cfg(feature = "tokio")]
use tokio::io::AsyncReadExt; use tokio::io::AsyncReadExt;
@ -56,24 +56,33 @@ pub async fn spawn(mut command: Command) -> Option<u32> {
match unsafe { libc::fork() } { match unsafe { libc::fork() } {
// Parent process // Parent process
1.. => { fork_pid @ 1..=i32::MAX => {
// Drop copy of write end, then read PID from pipe // Drop copy of write end, then read PID from pipe
drop(write); drop(write);
let pid = read_from_pipe(read).await; let pid = read_from_pipe(read).await;
// wait to prevent zombie _ = rustix::process::waitpid(
_ = rustix::process::wait(rustix::process::WaitOptions::empty()); rustix::process::Pid::from_raw(fork_pid),
rustix::process::WaitOptions::empty(),
);
pid pid
} }
// Child process // Child process
0 => { 0 => {
let _res = rustix::process::setsid(); let _res = rustix::process::setsid();
if let Ok(child) = command.spawn() { let exit_status = if let Ok(child) = command.spawn() {
// Write PID to pipe // Write PID to pipe
let _ = rustix::io::write(write, &child.id().to_be_bytes()); let _ = rustix::io::write(write, &child.id().to_be_bytes());
} 0
} else {
1
};
exit(0) // # Safety
// Required for child fork to exit without affecting the parent.
unsafe {
libc::_exit(exit_status);
}
} }
..=-1 => { ..=-1 => {