Rollup merge of #142391 - LevitatingBusinessMan:setsid, r=workingjubilee
rust: library: Add `setsid` method to `CommandExt` trait Add a setsid method to the CommandExt trait so that callers can create a process in a new session and process group whilst still using the POSIX spawn fast path. Tracking issue: rust-lang/rust#105376 ACP: https://github.com/rust-lang/libs-team/issues/184 This PR was previously submitted by ``@HarveyHunt`` (whom I marked as Co-Author in the commit message) in rust-lang/rust#105377. However that PR went stale. I applied the [suggestion](https://github.com/rust-lang/rust/pull/105377/files/231d19fcbfe155b2e85116865adae4253380ff1f#r1893457943) to change the function signature to `fn setsid(&mut self, setsid: bool) -> &mut Command`.
This commit is contained in:
commit
2730bebbf8
4 changed files with 89 additions and 0 deletions
|
|
@ -210,6 +210,9 @@ pub trait CommandExt: Sealed {
|
|||
/// intentional difference from the underlying `chroot` system call.)
|
||||
#[unstable(feature = "process_chroot", issue = "141298")]
|
||||
fn chroot<P: AsRef<Path>>(&mut self, dir: P) -> &mut process::Command;
|
||||
|
||||
#[unstable(feature = "process_setsid", issue = "105376")]
|
||||
fn setsid(&mut self, setsid: bool) -> &mut process::Command;
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
|
@ -260,6 +263,11 @@ impl CommandExt for process::Command {
|
|||
self.as_inner_mut().chroot(dir.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
fn setsid(&mut self, setsid: bool) -> &mut process::Command {
|
||||
self.as_inner_mut().setsid(setsid);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Unix-specific extensions to [`process::ExitStatus`] and
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ pub struct Command {
|
|||
#[cfg(target_os = "linux")]
|
||||
create_pidfd: bool,
|
||||
pgroup: Option<pid_t>,
|
||||
setsid: bool,
|
||||
}
|
||||
|
||||
// passed back to std::process with the pipes connected to the child, if any
|
||||
|
|
@ -185,6 +186,7 @@ impl Command {
|
|||
#[cfg(target_os = "linux")]
|
||||
create_pidfd: false,
|
||||
pgroup: None,
|
||||
setsid: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -220,6 +222,9 @@ impl Command {
|
|||
self.cwd(&OsStr::new("/"));
|
||||
}
|
||||
}
|
||||
pub fn setsid(&mut self, setsid: bool) {
|
||||
self.setsid = setsid;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn create_pidfd(&mut self, val: bool) {
|
||||
|
|
@ -298,6 +303,10 @@ impl Command {
|
|||
pub fn get_chroot(&self) -> Option<&CStr> {
|
||||
self.chroot.as_deref()
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn get_setsid(&self) -> bool {
|
||||
self.setsid
|
||||
}
|
||||
|
||||
pub fn get_closures(&mut self) -> &mut Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>> {
|
||||
&mut self.closures
|
||||
|
|
|
|||
|
|
@ -134,6 +134,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(true);
|
||||
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(true);
|
||||
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 = &[
|
||||
|
|
|
|||
|
|
@ -340,6 +340,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"))]
|
||||
{
|
||||
|
|
@ -741,6 +745,16 @@ impl Command {
|
|||
flags |= libc::POSIX_SPAWN_SETSIGDEF;
|
||||
}
|
||||
|
||||
if self.get_setsid() {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
|
||||
flags |= libc::POSIX_SPAWN_SETSID;
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
|
||||
|
||||
// Make sure we synchronize access to the global `environ` resource
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue