diff options
| author | Trevor Gross <t.gross35@gmail.com> | 2025-06-20 02:50:38 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-20 02:50:38 -0400 |
| commit | e381a14b7cb07eb3be4da73a032cb8f4481a05b9 (patch) | |
| tree | e7c8aaf3223ba74aabfdada24aec308ffcf20eab | |
| parent | c117ebefd23c47fa71252291f7fb597555c74daa (diff) | |
| parent | b3914945ae2d77770c25ed124a8450d82c8f7fe9 (diff) | |
| download | rust-e381a14b7cb07eb3be4da73a032cb8f4481a05b9.tar.gz rust-e381a14b7cb07eb3be4da73a032cb8f4481a05b9.zip | |
Rollup merge of #141990 - Qelxiros:141975-unix_send_signal, r=ChrisDenton,tgross35
Implement send_signal for unix child processes Tracking issue: rust-lang/rust#141975 There are two main differences between my implementation and the Public API section of the tracking issue. ~First, `send_signal` requires a mutable reference, like `Child::kill`.~ Second, `ChildExt` has `Sealed` as a supertrait, bringing it more in line with other extension traits like `CommandExt`. try-job: `dist-various*` try-job: `test-various*`
| -rw-r--r-- | library/std/src/os/unix/mod.rs | 3 | ||||
| -rw-r--r-- | library/std/src/os/unix/process.rs | 35 | ||||
| -rw-r--r-- | library/std/src/sys/pal/unix/linux/pidfd.rs | 6 | ||||
| -rw-r--r-- | library/std/src/sys/process/unix/fuchsia.rs | 5 | ||||
| -rw-r--r-- | library/std/src/sys/process/unix/unix.rs | 12 | ||||
| -rw-r--r-- | library/std/src/sys/process/unix/unsupported.rs | 6 | ||||
| -rw-r--r-- | library/std/src/sys/process/unix/vxworks.rs | 8 |
7 files changed, 67 insertions, 8 deletions
diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index 5802b653965..78c957270c4 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -116,6 +116,9 @@ pub mod prelude { #[stable(feature = "rust1", since = "1.0.0")] pub use super::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; #[doc(no_inline)] + #[unstable(feature = "unix_send_signal", issue = "141975")] + pub use super::process::ChildExt; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::process::{CommandExt, ExitStatusExt}; #[doc(no_inline)] diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 57ce3c5a4bf..3f4fe2e56ec 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -378,6 +378,41 @@ impl ExitStatusExt for process::ExitStatusError { } } +#[unstable(feature = "unix_send_signal", issue = "141975")] +pub trait ChildExt: Sealed { + /// Sends a signal to a child process. + /// + /// # Errors + /// + /// This function will return an error if the signal is invalid. The integer values associated + /// with signals are implemenation-specific, so it's encouraged to use a crate that provides + /// posix bindings. + /// + /// # Examples + /// + /// ```rust + /// #![feature(unix_send_signal)] + /// + /// use std::{io, os::unix::process::ChildExt, process::{Command, Stdio}}; + /// + /// use libc::SIGTERM; + /// + /// fn main() -> io::Result<()> { + /// let child = Command::new("cat").stdin(Stdio::piped()).spawn()?; + /// child.send_signal(SIGTERM)?; + /// Ok(()) + /// } + /// ``` + fn send_signal(&self, signal: i32) -> io::Result<()>; +} + +#[unstable(feature = "unix_send_signal", issue = "141975")] +impl ChildExt for process::Child { + fn send_signal(&self, signal: i32) -> io::Result<()> { + self.handle.send_signal(signal) + } +} + #[stable(feature = "process_extensions", since = "1.2.0")] impl FromRawFd for process::Stdio { #[inline] diff --git a/library/std/src/sys/pal/unix/linux/pidfd.rs b/library/std/src/sys/pal/unix/linux/pidfd.rs index 2d949ec9e91..47e9a616bfd 100644 --- a/library/std/src/sys/pal/unix/linux/pidfd.rs +++ b/library/std/src/sys/pal/unix/linux/pidfd.rs @@ -13,11 +13,15 @@ pub(crate) struct PidFd(FileDesc); impl PidFd { pub fn kill(&self) -> io::Result<()> { + self.send_signal(libc::SIGKILL) + } + + pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> { cvt(unsafe { libc::syscall( libc::SYS_pidfd_send_signal, self.0.as_raw_fd(), - libc::SIGKILL, + signal, crate::ptr::null::<()>(), 0, ) diff --git a/library/std/src/sys/process/unix/fuchsia.rs b/library/std/src/sys/process/unix/fuchsia.rs index 017ab91797c..d71be510b6a 100644 --- a/library/std/src/sys/process/unix/fuchsia.rs +++ b/library/std/src/sys/process/unix/fuchsia.rs @@ -152,6 +152,11 @@ impl Process { Ok(()) } + pub fn send_signal(&self, _signal: i32) -> io::Result<()> { + // Fuchsia doesn't have a direct equivalent for signals + unimplemented!() + } + pub fn wait(&mut self) -> io::Result<ExitStatus> { let mut proc_info: zx_info_process_t = Default::default(); let mut actual: size_t = 0; diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 4f595ac9a1c..1fe80c13b81 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -963,9 +963,13 @@ impl Process { self.pid as u32 } - pub fn kill(&mut self) -> io::Result<()> { + pub fn kill(&self) -> io::Result<()> { + self.send_signal(libc::SIGKILL) + } + + pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> { // If we've already waited on this process then the pid can be recycled - // and used for another process, and we probably shouldn't be killing + // and used for another process, and we probably shouldn't be signaling // random processes, so return Ok because the process has exited already. if self.status.is_some() { return Ok(()); @@ -973,9 +977,9 @@ impl Process { #[cfg(target_os = "linux")] if let Some(pid_fd) = self.pidfd.as_ref() { // pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too - return pid_fd.kill(); + return pid_fd.send_signal(signal); } - cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) + cvt(unsafe { libc::kill(self.pid, signal) }).map(drop) } pub fn wait(&mut self) -> io::Result<ExitStatus> { diff --git a/library/std/src/sys/process/unix/unsupported.rs b/library/std/src/sys/process/unix/unsupported.rs index e86561a5c5c..87403cd50f8 100644 --- a/library/std/src/sys/process/unix/unsupported.rs +++ b/library/std/src/sys/process/unix/unsupported.rs @@ -40,7 +40,11 @@ impl Process { 0 } - pub fn kill(&mut self) -> io::Result<()> { + pub fn kill(&self) -> io::Result<()> { + unsupported() + } + + pub fn send_signal(&self, _signal: i32) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs index f33b4a375da..51ae8c56bdb 100644 --- a/library/std/src/sys/process/unix/vxworks.rs +++ b/library/std/src/sys/process/unix/vxworks.rs @@ -146,14 +146,18 @@ impl Process { self.pid as u32 } - pub fn kill(&mut self) -> io::Result<()> { + pub fn kill(&self) -> io::Result<()> { + self.send_signal(libc::SIGKILL) + } + + pub fn send_signal(&self, signal: i32) -> io::Result<()> { // If we've already waited on this process then the pid can be recycled // and used for another process, and we probably shouldn't be killing // random processes, so return Ok because the process has exited already. if self.status.is_some() { Ok(()) } else { - cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) + cvt(unsafe { libc::kill(self.pid, signal) }).map(drop) } } |
