From 77237d47b9a815ceed0905bba389a4991d8cd630 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Thu, 16 Nov 2023 18:06:38 +0000 Subject: [PATCH 1/3] feat: pidfd_getfd --- changelog/1868.added.md | 1 + src/sys/mod.rs | 3 +++ src/sys/pidfd.rs | 45 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 changelog/1868.added.md create mode 100644 src/sys/pidfd.rs diff --git a/changelog/1868.added.md b/changelog/1868.added.md new file mode 100644 index 0000000000..049d2cc9b5 --- /dev/null +++ b/changelog/1868.added.md @@ -0,0 +1 @@ +Added `pidfd_getfd` on Linux. \ No newline at end of file diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 93339d1935..48a6a76968 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -193,3 +193,6 @@ feature! { #![feature = "time"] pub mod timer; } + +#[cfg(target_os = "linux")] +pub mod pidfd; diff --git a/src/sys/pidfd.rs b/src/sys/pidfd.rs new file mode 100644 index 0000000000..c6cff792e6 --- /dev/null +++ b/src/sys/pidfd.rs @@ -0,0 +1,45 @@ +//! pidfd related functionality + +use crate::errno::Errno; +use crate::Result; +use std::convert::TryFrom; +use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd}; + +/// Allocates a new file descriptor in the calling process. This new file descriptor is a duplicate +/// of an existing file descriptor, `target`, in the process referred to by the PID file descriptor +/// `pid`. +/// +/// The duplicate file descriptor refers to the same open file description (see +/// [open(2)](https://man7.org/linux/man-pages/man2/open.2.html)) as the original file descriptor in +/// the process referred to by `pid`. The two file descriptors thus share file status flags and +/// file offset. Furthermore, operations on the underlying file object (for example, assigning an +/// address to a socket object using [bind(2)](https://man7.org/linux/man-pages/man2/bind.2.html)) +/// can equally be performed via the duplicate file descriptor. +/// +/// The close-on-exec flag ([`libc::FD_CLOEXEC`]; see +/// [fcntl(2)](https://man7.org/linux/man-pages/man2/fcntl.2.html)) is set on the returned file +/// descriptor. +/// +/// Permission to duplicate another process's file descriptor is governed by a ptrace access mode +/// PTRACE_MODE_ATTACH_REALCREDS check (see +/// [ptrace(2)](https://man7.org/linux/man-pages/man2/ptrace.2.html)). +pub fn pidfd_getfd( + pid: PFd, + target: TFd, +) -> Result { + #[allow(clippy::useless_conversion)] // Not useless on all OSes + match unsafe { + libc::syscall( + libc::SYS_pidfd_getfd, + pid.as_fd().as_raw_fd(), + target.as_fd().as_raw_fd(), + 0, + ) + } { + -1 => Err(Errno::last()), + fd @ 0.. => { + Ok(unsafe { OwnedFd::from_raw_fd(i32::try_from(fd).unwrap()) }) + } + _ => unreachable!(), + } +} \ No newline at end of file From 69e10c6e8c02ccf470c953909da347ecf7b03c28 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Thu, 16 Nov 2023 18:06:18 +0000 Subject: [PATCH 2/3] feat: pid_open --- changelog/1868.added.md | 3 ++- src/sys/mod.rs | 2 +- src/sys/pidfd.rs | 29 ++++++++++++++++++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/changelog/1868.added.md b/changelog/1868.added.md index 049d2cc9b5..80d57077a9 100644 --- a/changelog/1868.added.md +++ b/changelog/1868.added.md @@ -1 +1,2 @@ -Added `pidfd_getfd` on Linux. \ No newline at end of file +- Added `pidfd_getfd` on Linux. +- Added `pid_open` on Linux. \ No newline at end of file diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 48a6a76968..c79063b0d8 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -194,5 +194,5 @@ feature! { pub mod timer; } -#[cfg(target_os = "linux")] +#[cfg(all(target_os = "linux", feature = "process"))] pub mod pidfd; diff --git a/src/sys/pidfd.rs b/src/sys/pidfd.rs index c6cff792e6..7ad7999f17 100644 --- a/src/sys/pidfd.rs +++ b/src/sys/pidfd.rs @@ -1,6 +1,7 @@ //! pidfd related functionality use crate::errno::Errno; +use crate::unistd::Pid; use crate::Result; use std::convert::TryFrom; use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd}; @@ -42,4 +43,30 @@ pub fn pidfd_getfd( } _ => unreachable!(), } -} \ No newline at end of file +} + +/// Creates a file descriptor that refers to the process whose PID is specified in `pid`. The file +/// descriptor is returned as the function result; the close-on-exec flag is set on the file +/// descriptor. +/// +/// If `nonblock == true` returns a nonblocking file descriptor. If the process +/// referred to by the file descriptor has not yet terminated, +/// then an attempt to wait on the file descriptor using +/// waitid(2) will immediately return the error EAGAIN rather +/// than blocking. +pub fn pid_open(pid: Pid, nonblock: bool) -> Result { + #[allow(clippy::useless_conversion)] // Not useless on all OSes + match unsafe { + libc::syscall( + libc::SYS_pidfd_open, + pid, + if nonblock { libc::PIDFD_NONBLOCK } else { 0 }, + ) + } { + -1 => Err(Errno::last()), + fd @ 0.. => { + Ok(unsafe { OwnedFd::from_raw_fd(i32::try_from(fd).unwrap()) }) + } + _ => unreachable!(), + } +} From 3d29e8c4a9f629a45e1bd7e1774cc9bd1ed4e3d5 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Thu, 16 Nov 2023 18:06:09 +0000 Subject: [PATCH 3/3] feat: pidfd_send_signal --- changelog/1868.added.md | 3 ++- src/sys/mod.rs | 2 +- src/sys/pidfd.rs | 43 +++++++++++++++++++++++++++++++++++++++++ test/sys/mod.rs | 3 +++ test/sys/test_pidfd.rs | 28 +++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 test/sys/test_pidfd.rs 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 c79063b0d8..a62be01704 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -194,5 +194,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 fb3f6be0e5..93e8a7474e 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -82,3 +82,6 @@ mod test_statfs; target_os = "haiku" )))] mod test_resource; + +#[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); + } + } +}