Add send_process_group_signal to existing unix_send_signal feature

This function wraps POSIX `killpg()`, and on Linux additionally
may be implemented by `pidfd_send_signal`.
This commit is contained in:
John Millikin
2026-05-13 19:24:01 +09:00
parent 8b03437a8f
commit 2c30279ff6
6 changed files with 81 additions and 0 deletions
+36
View File
@@ -420,6 +420,38 @@ pub trait ChildExt: Sealed {
/// }
/// ```
fn send_signal(&self, signal: i32) -> io::Result<()>;
/// Sends a signal to a child process's process group.
///
/// # Errors
///
/// This function will return an error if the signal is invalid or if the
/// child process does not have a process group. The integer values
/// associated with signals are implementation-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, CommandExt}, process::{Command, Stdio}};
///
/// use libc::SIGTERM;
///
/// fn main() -> io::Result<()> {
/// # if cfg!(not(all(target_vendor = "apple", not(target_os = "macos")))) {
/// let child = Command::new("cat")
/// .stdin(Stdio::piped())
/// .process_group(0)
/// .spawn()?;
/// child.send_process_group_signal(SIGTERM)?;
/// # }
/// Ok(())
/// }
/// ```
#[unstable(feature = "unix_send_signal", issue = "141975")]
fn send_process_group_signal(&self, signal: i32) -> io::Result<()>;
}
#[unstable(feature = "unix_send_signal", issue = "141975")]
@@ -427,6 +459,10 @@ impl ChildExt for process::Child {
fn send_signal(&self, signal: i32) -> io::Result<()> {
self.handle.send_signal(signal)
}
fn send_process_group_signal(&self, signal: i32) -> io::Result<()> {
self.handle.send_process_group_signal(signal)
}
}
#[stable(feature = "process_extensions", since = "1.2.0")]
@@ -95,6 +95,21 @@ pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> {
.map(drop)
}
pub(crate) fn send_process_group_signal(&self, signal: i32) -> io::Result<()> {
// since kernel 6.9
// https://lore.kernel.org/all/20240210-chihuahua-hinzog-3945b6abd44a@brauner/
cvt(unsafe {
libc::syscall(
libc::SYS_pidfd_send_signal,
self.0.as_raw_fd(),
signal,
crate::ptr::null::<()>(),
libc::PIDFD_SIGNAL_PROCESS_GROUP,
)
})
.map(drop)
}
pub fn wait(&self) -> io::Result<ExitStatus> {
let r = self.waitid(libc::WEXITED)?;
match r {
@@ -158,6 +158,11 @@ pub fn send_signal(&self, _signal: i32) -> io::Result<()> {
unimplemented!()
}
pub fn send_process_group_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;
+13
View File
@@ -1002,6 +1002,19 @@ pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> {
cvt(unsafe { libc::kill(self.pid, signal) }).map(drop)
}
pub(crate) fn send_process_group_signal(&self, signal: i32) -> io::Result<()> {
// See note in `send_signal` regarding recycled PIDs.
if self.status.is_some() {
return Ok(());
}
#[cfg(target_os = "linux")]
if let Some(pid_fd) = self.pidfd.as_ref() {
// The `PIDFD_SIGNAL_PROCESS_GROUP` flag requires kernel >= 6.9
return pid_fd.send_process_group_signal(signal);
}
cvt(unsafe { libc::killpg(self.pid, signal) }).map(drop)
}
pub fn wait(&mut self) -> io::Result<ExitStatus> {
use crate::sys::cvt_r;
if let Some(status) = self.status {
@@ -49,6 +49,10 @@ pub fn send_signal(&self, _signal: i32) -> io::Result<()> {
unsupported()
}
pub fn send_process_group_signal(&self, _signal: i32) -> io::Result<()> {
unsupported()
}
pub fn wait(&mut self) -> io::Result<ExitStatus> {
unsupported()
}
@@ -161,6 +161,14 @@ pub fn send_signal(&self, signal: i32) -> io::Result<()> {
}
}
pub fn send_process_group_signal(&self, signal: i32) -> io::Result<()> {
// See note in `send_signal` regarding recycled PIDs.
if self.status.is_some() {
return Ok(());
}
cvt(unsafe { libc::killpg(self.pid, signal) }).map(drop)
}
pub fn wait(&mut self) -> io::Result<ExitStatus> {
use crate::sys::cvt_r;
if let Some(status) = self.status {