Skip to content

Commit 8064dbe

Browse files
committed
add pidfd_send_signal(2)
1 parent f133b44 commit 8064dbe

File tree

3 files changed

+137
-1
lines changed

3 files changed

+137
-1
lines changed

changelog/2748.added.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Added `pidfd_open(2)`
1+
Added `pidfd_open(2)` and `pidfd_send_signal(2)`

src/sys/pidfd.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::{
44
hint::unreachable_unchecked,
55
os::fd::{FromRawFd as _, OwnedFd, RawFd},
6+
ptr,
67
};
78

89
use bitflags::bitflags;
@@ -58,3 +59,79 @@ pub fn pidfd_open(pid: Pid, flags: PidfdFlags) -> Result<OwnedFd, Errno> {
5859
}
5960
}
6061
}
62+
63+
feature! {
64+
#![feature = "signal"]
65+
66+
use std::os::fd::{AsFd, AsRawFd};
67+
68+
use libc::siginfo_t;
69+
70+
use crate::sys::signal::Signal;
71+
72+
bitflags! {
73+
/// Flags for [`pidfd_send_signal`].
74+
#[derive(Copy, Clone)]
75+
pub struct PidfdSignalFlags: libc::c_uint {
76+
/// See [`pidfd_send_signal(2)`] for details.
77+
///
78+
/// [`pidfd_send_signal(2)`]: https://man7.org/linux/man-pages/man2/pidfd_send_signal.2.html
79+
const PIDFD_SIGNAL_THREAD = libc::PIDFD_SIGNAL_THREAD;
80+
81+
/// See [`pidfd_send_signal(2)`] for details.
82+
///
83+
/// [`pidfd_send_signal(2)`]: https://man7.org/linux/man-pages/man2/pidfd_send_signal.2.html
84+
const PIDFD_SIGNAL_THREAD_GROUP = libc::PIDFD_SIGNAL_THREAD_GROUP;
85+
86+
/// See [`pidfd_send_signal(2)`] for details.
87+
///
88+
/// [`pidfd_send_signal(2)`]: https://man7.org/linux/man-pages/man2/pidfd_send_signal.2.html
89+
const PIDFD_SIGNAL_PROCESS_GROUP = libc::PIDFD_SIGNAL_PROCESS_GROUP;
90+
}
91+
}
92+
93+
/// Send a signal to a process by its PIDFD.
94+
///
95+
/// See [`pidfd_send_signal(2)`] for details.
96+
///
97+
/// [`pidfd_send_signal(2)`]: https://man7.org/linux/man-pages/man2/pidfd_send_signal.2.html
98+
///
99+
/// # Safety
100+
///
101+
/// This function is [async-signal-safe], although it may modify `errno`.
102+
///
103+
/// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html
104+
pub fn pidfd_send_signal<T>(
105+
pidfd: T,
106+
signal: Signal,
107+
signal_info: Option<siginfo_t>,
108+
flags: PidfdSignalFlags,
109+
) -> Result<(), Errno>
110+
where
111+
T: AsFd,
112+
{
113+
let signal_info = match signal_info {
114+
Some(x) => &x,
115+
None => ptr::null(),
116+
};
117+
118+
// SAFETY:
119+
//
120+
// * Arguments passed to the syscall have the correct types.
121+
// * The kernel should not return any value other than `0` and `-1`.
122+
unsafe {
123+
match libc::syscall(
124+
libc::SYS_pidfd_send_signal,
125+
pidfd.as_fd().as_raw_fd(),
126+
signal as libc::c_int,
127+
signal_info,
128+
flags.bits(),
129+
) {
130+
0 => Ok(()),
131+
-1 => Err(Errno::last()),
132+
_ => unreachable_unchecked(),
133+
}
134+
}
135+
}
136+
137+
}

test/sys/test_pidfd.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,62 @@ fn test_pidfd_open() {
1212
Err(e) => panic!("{e}"),
1313
}
1414
}
15+
16+
#[cfg(feature = "signal")]
17+
mod send_signal {
18+
use nix::{
19+
errno::Errno,
20+
sys::{
21+
pidfd::{
22+
pidfd_open, pidfd_send_signal, PidfdFlags, PidfdSignalFlags,
23+
},
24+
signal::Signal,
25+
wait::{waitpid, WaitStatus},
26+
},
27+
unistd::{fork, getpid, ForkResult},
28+
};
29+
30+
#[test]
31+
fn test_pidfd_send_signal() {
32+
// NOTE: This function MUST be async-signal-safe.
33+
fn child_process() -> ! {
34+
let pidfd = match pidfd_open(getpid(), PidfdFlags::empty()) {
35+
Ok(x) => x,
36+
Err(Errno::ENOSYS) => std::process::exit(2),
37+
Err(_) => std::process::exit(1),
38+
};
39+
40+
// Code beyond this call should be unreachable.
41+
match pidfd_send_signal(
42+
&pidfd,
43+
Signal::SIGKILL,
44+
None,
45+
PidfdSignalFlags::empty(),
46+
) {
47+
Ok(()) => (),
48+
Err(Errno::ENOSYS) => std::process::exit(2),
49+
Err(_) => std::process::exit(1),
50+
}
51+
52+
std::process::exit(1)
53+
}
54+
55+
// SAFETY: `child_process` is async-signal-safe.
56+
let child = unsafe {
57+
match fork().expect("should be able to fork") {
58+
ForkResult::Parent { child } => child,
59+
ForkResult::Child => child_process(),
60+
}
61+
};
62+
63+
match waitpid(child, None) {
64+
// Kernel has PIDFD syscalls and they worked.
65+
Ok(WaitStatus::Signaled(x, Signal::SIGKILL, _)) if x == child => (),
66+
67+
// Kernel does not have PIDFD syscalls.
68+
Ok(WaitStatus::Exited(x, 2)) if x == child => (),
69+
70+
_ => panic!("unexpected waitpid result"),
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)