From 19ce424e3abd95bbfa16afe7d231a456494f7c85 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 22 Sep 2025 10:55:03 -0400 Subject: [PATCH] cmdext: Add prctl wrapper We have this in bootc, but it's generally useful. Signed-off-by: Colin Walters --- src/cmdext.rs | 27 +++++++++++++++++++++++++++ tests/it/main.rs | 11 +++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/cmdext.rs b/src/cmdext.rs index 5fe4ad3..fb40603 100644 --- a/src/cmdext.rs +++ b/src/cmdext.rs @@ -24,6 +24,19 @@ pub trait CapStdExtCommandExt { /// Use the given directory as the current working directory for the process. fn cwd_dir(&mut self, dir: Dir) -> &mut Self; + + /// On Linux, arrange for [`SIGTERM`] to be delivered to the child if the + /// parent *thread* exits. This helps avoid leaking child processes if + /// the parent crashes for example. + /// + /// # IMPORTANT + /// + /// Due to the semantics of this + /// will cause the child to exit when the parent *thread* (not process) exits. In + /// particular this can become problematic when used with e.g. a threadpool such + /// as Tokio's . + #[cfg(any(target_os = "linux", target_os = "android"))] + fn lifecycle_bind_to_parent_thread(&mut self) -> &mut Self; } #[allow(unsafe_code)] @@ -58,6 +71,20 @@ impl CapStdExtCommandExt for std::process::Command { } self } + + #[cfg(any(target_os = "linux", target_os = "android"))] + fn lifecycle_bind_to_parent_thread(&mut self) -> &mut Self { + // SAFETY: This API is safe to call in a forked child. + unsafe { + self.pre_exec(|| { + rustix::process::set_parent_process_death_signal(Some( + rustix::process::Signal::TERM, + )) + .map_err(Into::into) + }); + } + self + } } #[cfg(test)] diff --git a/tests/it/main.rs b/tests/it/main.rs index 3de4152..6f54a85 100644 --- a/tests/it/main.rs +++ b/tests/it/main.rs @@ -675,3 +675,14 @@ fn test_big_xattr() -> Result<()> { Ok(()) } + +#[test] +#[cfg(any(target_os = "linux", target_os = "android"))] +fn test_lifecycle_bind_to_parent_thread() -> Result<()> { + let status = Command::new("true") + .lifecycle_bind_to_parent_thread() + .status()?; + assert!(status.success()); + + Ok(()) +}