diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 09b2bfe39f094..0db873bb42e51 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -179,6 +179,9 @@ pub trait CommandExt: Sealed { /// ``` #[stable(feature = "process_set_process_group", since = "1.64.0")] fn process_group(&mut self, pgroup: i32) -> &mut process::Command; + + #[unstable(feature = "process_setsid", issue = "105376")] + fn setsid(&mut self) -> &mut process::Command; } #[stable(feature = "rust1", since = "1.0.0")] @@ -224,6 +227,11 @@ impl CommandExt for process::Command { self.as_inner_mut().pgroup(pgroup); self } + + fn setsid(&mut self) -> &mut process::Command { + self.as_inner_mut().setsid(); + self + } } /// Unix-specific extensions to [`process::ExitStatus`] and diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index afd03d79c0ba6..41aec6710a9f0 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -107,6 +107,7 @@ pub struct Command { #[cfg(target_os = "linux")] create_pidfd: bool, pgroup: Option, + setsid: bool, } // Create a new type for argv, so that we can make it `Send` and `Sync` @@ -197,6 +198,7 @@ impl Command { stdout: None, stderr: None, pgroup: None, + setsid: false, } } @@ -222,6 +224,7 @@ impl Command { stderr: None, create_pidfd: false, pgroup: None, + setsid: false, } } @@ -261,6 +264,10 @@ impl Command { self.pgroup = Some(pgroup); } + pub fn setsid(&mut self) { + self.setsid = true; + } + #[cfg(target_os = "linux")] pub fn create_pidfd(&mut self, val: bool) { self.create_pidfd = val; @@ -333,6 +340,11 @@ impl Command { self.pgroup } + #[allow(dead_code)] + pub fn get_setsid(&self) -> bool { + self.setsid + } + pub fn get_closures(&mut self) -> &mut Vec io::Result<()> + Send + Sync>> { &mut self.closures } diff --git a/library/std/src/sys/unix/process/process_common/tests.rs b/library/std/src/sys/unix/process/process_common/tests.rs index 03631e4e33bf5..e522634c10086 100644 --- a/library/std/src/sys/unix/process/process_common/tests.rs +++ b/library/std/src/sys/unix/process/process_common/tests.rs @@ -136,6 +136,64 @@ fn test_process_group_no_posix_spawn() { } } +#[test] +#[cfg_attr( + any( + // See test_process_mask + target_os = "macos", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "riscv64", + ), + ignore +)] +fn test_setsid_posix_spawn() { + // Spawn a cat subprocess that's just going to hang since there is no I/O. + let mut cmd = Command::new(OsStr::new("cat")); + cmd.setsid(); + cmd.stdin(Stdio::MakePipe); + cmd.stdout(Stdio::MakePipe); + let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true)); + + unsafe { + // Setsid will create a new session and process group, so check that + // we can kill the process group, which means there *is* one. + t!(cvt(libc::kill(-(cat.id() as libc::pid_t), libc::SIGINT))); + + t!(cat.wait()); + } +} + +#[test] +#[cfg_attr( + any( + // See test_process_mask + target_os = "macos", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "riscv64", + ), + ignore +)] +fn test_setsid_no_posix_spawn() { + let mut cmd = Command::new(OsStr::new("cat")); + cmd.setsid(); + cmd.stdin(Stdio::MakePipe); + cmd.stdout(Stdio::MakePipe); + + unsafe { + // Same as above, create hang-y cat. This time, force using the non-posix_spawn path. + cmd.pre_exec(Box::new(|| Ok(()))); // pre_exec forces fork + exec rather than posix spawn. + let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true)); + + // Setsid will create a new session and process group, so check that + // we can kill the process group, which means there *is* one. + t!(cvt(libc::kill(-(cat.id() as libc::pid_t), libc::SIGINT))); + + t!(cat.wait()); + } +} + #[test] fn test_program_kind() { let vectors = &[ diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 39d1c8b1d8ebc..c0cb0035ea807 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -328,6 +328,10 @@ impl Command { cvt(libc::setpgid(0, pgroup))?; } + if self.get_setsid() { + cvt(libc::setsid())?; + } + // emscripten has no signal support. #[cfg(not(target_os = "emscripten"))] { @@ -467,6 +471,7 @@ impl Command { }; let pgroup = self.get_pgroup(); + let setsid = self.get_setsid(); // Safety: -1 indicates we don't have a pidfd. let mut p = unsafe { Process::new(0, -1) }; @@ -550,6 +555,10 @@ impl Command { flags |= libc::POSIX_SPAWN_SETSIGDEF; } + if setsid { + flags |= libc::POSIX_SPAWN_SETSID; + } + cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; // Make sure we synchronize access to the global `environ` resource