diff --git a/changelog/1868.added.md b/changelog/1868.added.md index 80d57077a9..af1bdf8f17 100644 --- a/changelog/1868.added.md +++ b/changelog/1868.added.md @@ -1,2 +1,3 @@ - Added `pidfd_getfd` on Linux. -- Added `pid_open` on Linux. \ No newline at end of file +- Added `pid_open` on Linux. +- Added `pidfd_send_signal` on Linux. \ No newline at end of file diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 612cbf56f8..94e9faa3ce 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -224,5 +224,5 @@ feature! { pub mod timer; } -#[cfg(all(target_os = "linux", feature = "process"))] +#[cfg(all(target_os = "linux", feature = "signal", feature = "process"))] pub mod pidfd; diff --git a/src/sys/pidfd.rs b/src/sys/pidfd.rs index 7ad7999f17..d0bbfa4d1e 100644 --- a/src/sys/pidfd.rs +++ b/src/sys/pidfd.rs @@ -1,6 +1,7 @@ //! pidfd related functionality use crate::errno::Errno; +use crate::sys::signal::Signal; use crate::unistd::Pid; use crate::Result; use std::convert::TryFrom; @@ -70,3 +71,45 @@ pub fn pid_open(pid: Pid, nonblock: bool) -> Result { _ => unreachable!(), } } + +/// Sends the signal `sig` to the target process referred to by `pid`, a PID file descriptor that +/// refers to a process. +/// +/// If the info argument is some [`libc::siginfo_t`] buffer, that buffer should be populated as +/// described in [rt_sigqueueinfo(2)](https://man7.org/linux/man-pages/man2/rt_sigqueueinfo.2.html). +/// +/// If the info argument is `None`, this is equivalent to specifying a pointer to a `siginfo_t` +/// buffer whose fields match the values that are implicitly supplied when a signal is sent using +/// [`crate::sys::signal::kill`]: +/// +/// - `si_signo` is set to the signal number; +/// - `si_errno` is set to 0; +/// - `si_code` is set to SI_USER; +/// - `si_pid` is set to the caller's PID; and +/// - `si_uid` is set to the caller's real user ID. +/// +/// The calling process must either be in the same PID namespace as the process referred to by +/// pidfd, or be in an ancestor of that namespace. +pub fn pidfd_send_signal( + pid: Fd, + sig: Signal, + info: Option, +) -> Result<()> { + let info = match info { + Some(i) => &i, + None => std::ptr::null(), + }; + match unsafe { + libc::syscall( + libc::SYS_pidfd_send_signal, + pid.as_fd().as_raw_fd(), + sig as i32, + info, + 0u32, + ) + } { + -1 => Err(Errno::last()), + 0 => Ok(()), + _ => unreachable!(), + } +} diff --git a/test/sys/mod.rs b/test/sys/mod.rs index ae4ff953fe..0e6efc43a4 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -57,3 +57,6 @@ mod test_pthread; mod test_ptrace; #[cfg(any(target_os = "android", target_os = "linux"))] mod test_timerfd; + +#[cfg(all(target_os = "linux", feature = "signal", feature = "process"))] +pub mod test_pidfd; diff --git a/test/sys/test_pidfd.rs b/test/sys/test_pidfd.rs new file mode 100644 index 0000000000..5b470fd0a5 --- /dev/null +++ b/test/sys/test_pidfd.rs @@ -0,0 +1,28 @@ +use nix::{ + sys::{ + pidfd::{pid_open, pidfd_send_signal}, + signal::Signal, + signalfd::SigSet, + wait::waitpid, + }, + unistd::{fork, ForkResult}, +}; + +#[test] +fn test_pidfd_send_signal() { + match unsafe { fork().unwrap() } { + ForkResult::Parent { child } => { + // Send SIGUSR1 + let pid_fd = pid_open(child, false).unwrap(); + pidfd_send_signal(pid_fd, Signal::SIGUSR1, None).unwrap(); + // Wait for child to exit. + waitpid(child, None).unwrap(); + } + ForkResult::Child => { + // Wait for SIGUSR1 + let mut mask = SigSet::empty(); + mask.add(Signal::SIGUSR1); + assert_eq!(mask.wait().unwrap(), Signal::SIGUSR1); + } + } +}