From abb4a0ef29714f78d1fd422fb874aa97d17747b4 Mon Sep 17 00:00:00 2001 From: Thomas Orozco Date: Thu, 3 Feb 2022 09:40:45 +0000 Subject: [PATCH] Add a `process_group` method to UNIX `CommandExt` --- library/std/src/os/unix/process.rs | 10 ++++++ .../src/sys/unix/process/process_common.rs | 12 ++++++- .../sys/unix/process/process_common/tests.rs | 35 +++++++++++++++++++ .../std/src/sys/unix/process/process_unix.rs | 15 +++++++- 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 855f900430c4a..d95bc9b15c9c4 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -149,6 +149,11 @@ pub trait CommandExt: Sealed { fn arg0(&mut self, arg: S) -> &mut process::Command where S: AsRef; + + /// Sets the process group ID of the child process. Translates to a `setpgid` call in the child + /// process. + #[unstable(feature = "process_set_process_group", issue = "93857")] + fn process_group(&mut self, pgroup: i32) -> &mut process::Command; } #[stable(feature = "rust1", since = "1.0.0")] @@ -201,6 +206,11 @@ impl CommandExt for process::Command { self.as_inner_mut().set_arg_0(arg.as_ref()); self } + + fn process_group(&mut self, pgroup: i32) -> &mut process::Command { + self.as_inner_mut().pgroup(pgroup); + 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 7ac2f9d8af75a..1d9e211d5efe1 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -18,7 +18,7 @@ use crate::sys_common::IntoInner; #[cfg(not(target_os = "fuchsia"))] use crate::sys::fs::OpenOptions; -use libc::{c_char, c_int, gid_t, uid_t, EXIT_FAILURE, EXIT_SUCCESS}; +use libc::{c_char, c_int, gid_t, pid_t, uid_t, EXIT_FAILURE, EXIT_SUCCESS}; cfg_if::cfg_if! { if #[cfg(target_os = "fuchsia")] { @@ -82,6 +82,7 @@ pub struct Command { stderr: Option, #[cfg(target_os = "linux")] create_pidfd: bool, + pgroup: Option, } // Create a new type for argv, so that we can make it `Send` and `Sync` @@ -145,6 +146,7 @@ impl Command { stdin: None, stdout: None, stderr: None, + pgroup: None, } } @@ -167,6 +169,7 @@ impl Command { stdout: None, stderr: None, create_pidfd: false, + pgroup: None, } } @@ -202,6 +205,9 @@ impl Command { pub fn groups(&mut self, groups: &[gid_t]) { self.groups = Some(Box::from(groups)); } + pub fn pgroup(&mut self, pgroup: pid_t) { + self.pgroup = Some(pgroup); + } #[cfg(target_os = "linux")] pub fn create_pidfd(&mut self, val: bool) { @@ -266,6 +272,10 @@ impl Command { self.groups.as_deref() } + pub fn get_pgroup(&self) -> Option { + self.pgroup + } + 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 10aa34e9443b7..73e9b2f9d75a9 100644 --- a/library/std/src/sys/unix/process/process_common/tests.rs +++ b/library/std/src/sys/unix/process/process_common/tests.rs @@ -67,3 +67,38 @@ fn test_process_mask() { t!(cat.wait()); } } + +#[test] +fn test_process_group_posix_spawn() { + unsafe { + // 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.pgroup(0); + cmd.stdin(Stdio::MakePipe); + cmd.stdout(Stdio::MakePipe); + let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true)); + + // Check that we can kill its 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_process_group_no_posix_spawn() { + unsafe { + // Same as above, create hang-y cat. This time, force using the non-posix_spawnp path. + let mut cmd = Command::new(OsStr::new("cat")); + cmd.pgroup(0); + cmd.pre_exec(Box::new(|| Ok(()))); // pre_exec forces fork + exec + cmd.stdin(Stdio::MakePipe); + cmd.stdout(Stdio::MakePipe); + let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true)); + + // Check that we can kill its process group, which means there *is* one. + t!(cvt(libc::kill(-(cat.id() as libc::pid_t), libc::SIGINT))); + + t!(cat.wait()); + } +} diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index bce35b380e677..86bb5b5d07b74 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -320,6 +320,10 @@ impl Command { cvt(libc::chdir(cwd.as_ptr()))?; } + if let Some(pgroup) = self.get_pgroup() { + cvt(libc::setpgid(0, pgroup))?; + } + // emscripten has no signal support. #[cfg(not(target_os = "emscripten"))] { @@ -459,6 +463,8 @@ impl Command { None => None, }; + let pgroup = self.get_pgroup(); + // Safety: -1 indicates we don't have a pidfd. let mut p = unsafe { Process::new(0, -1) }; @@ -487,6 +493,8 @@ impl Command { cvt_nz(libc::posix_spawnattr_init(attrs.as_mut_ptr()))?; let attrs = PosixSpawnattr(&mut attrs); + let mut flags = 0; + let mut file_actions = MaybeUninit::uninit(); cvt_nz(libc::posix_spawn_file_actions_init(file_actions.as_mut_ptr()))?; let file_actions = PosixSpawnFileActions(&mut file_actions); @@ -516,13 +524,18 @@ impl Command { cvt_nz(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?; } + if let Some(pgroup) = pgroup { + flags |= libc::POSIX_SPAWN_SETPGROUP; + cvt_nz(libc::posix_spawnattr_setpgroup(attrs.0.as_mut_ptr(), pgroup))?; + } + let mut set = MaybeUninit::::uninit(); cvt(sigemptyset(set.as_mut_ptr()))?; cvt_nz(libc::posix_spawnattr_setsigmask(attrs.0.as_mut_ptr(), set.as_ptr()))?; cvt(sigaddset(set.as_mut_ptr(), libc::SIGPIPE))?; cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.0.as_mut_ptr(), set.as_ptr()))?; - let flags = libc::POSIX_SPAWN_SETSIGDEF | libc::POSIX_SPAWN_SETSIGMASK; + flags |= libc::POSIX_SPAWN_SETSIGDEF | libc::POSIX_SPAWN_SETSIGMASK; cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; // Make sure we synchronize access to the global `environ` resource