Skip to content

Commit 757d1b4

Browse files
committed
add pidfd_send_signal(2)
1 parent b79265c commit 757d1b4

3 files changed

Lines changed: 127 additions & 3 deletions

File tree

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: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
33
use std::{
44
hint::unreachable_unchecked,
5-
os::fd::{FromRawFd as _, OwnedFd, RawFd},
5+
os::fd::{AsFd, AsRawFd, FromRawFd as _, OwnedFd, RawFd},
6+
ptr,
67
};
78

89
use bitflags::bitflags;
10+
use libc::siginfo_t;
911

10-
use crate::{errno::Errno, unistd::Pid};
12+
use crate::{errno::Errno, sys::signal::Signal, unistd::Pid};
1113

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

test/sys/test_pidfd.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,55 @@ fn test_pidfd_open() {
88
pidfd_open(getpid(), PidfdOpenFlags::empty())
99
.expect("should be able to get pidfd");
1010
}
11+
12+
#[cfg(feature = "signal")]
13+
mod send_signal {
14+
use nix::{
15+
sys::{
16+
pidfd::{
17+
pidfd_open, pidfd_send_signal, PidfdOpenFlags,
18+
PidfdSendSignalFlags,
19+
},
20+
signal::Signal,
21+
wait::{waitpid, WaitStatus},
22+
},
23+
unistd::{fork, getpid, ForkResult},
24+
};
25+
26+
#[test]
27+
fn test_pidfd_send_signal() {
28+
// NOTE: This function MUST be async-signal-safe.
29+
fn child_process() -> ! {
30+
let pidfd = pidfd_open(getpid(), PidfdOpenFlags::empty())
31+
.unwrap_or_else(|_| std::process::exit(1));
32+
33+
// Code beyond this call should be unreachable.
34+
pidfd_send_signal(
35+
&pidfd,
36+
Signal::SIGKILL,
37+
None,
38+
PidfdSendSignalFlags::empty(),
39+
)
40+
.unwrap_or_else(|_| std::process::exit(1));
41+
42+
std::process::exit(1)
43+
}
44+
45+
// SAFETY: `child_process` is async-signal-safe.
46+
let child = unsafe {
47+
match fork().expect("should be able to fork") {
48+
ForkResult::Parent { child } => child,
49+
ForkResult::Child => child_process(),
50+
}
51+
};
52+
53+
assert!(matches!(
54+
waitpid(child, None),
55+
Ok(WaitStatus::Signaled(
56+
x,
57+
Signal::SIGKILL,
58+
_
59+
)) if x == child
60+
));
61+
}
62+
}

0 commit comments

Comments
 (0)