Skip to content

Commit 70f234a

Browse files
committed
add pidfd_send_signal(2)
1 parent b79265c commit 70f234a

File tree

3 files changed

+141
-5
lines changed

3 files changed

+141
-5
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: 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: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,74 @@
11
use nix::{
2+
errno::Errno,
23
sys::pidfd::{pidfd_open, PidfdOpenFlags},
34
unistd::getpid,
45
};
56

67
#[test]
78
fn test_pidfd_open() {
8-
pidfd_open(getpid(), PidfdOpenFlags::empty())
9-
.expect("should be able to get pidfd");
9+
match pidfd_open(getpid(), PidfdOpenFlags::empty()) {
10+
Ok(_) => (),
11+
Err(Errno::ENOSYS) => (),
12+
Err(e) => panic!("{e}"),
13+
}
14+
}
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, PidfdOpenFlags,
23+
PidfdSendSignalFlags,
24+
},
25+
signal::Signal,
26+
wait::{waitpid, WaitStatus},
27+
},
28+
unistd::{fork, getpid, ForkResult},
29+
};
30+
31+
#[test]
32+
fn test_pidfd_send_signal() {
33+
// NOTE: This function MUST be async-signal-safe.
34+
fn child_process() -> ! {
35+
let pidfd = match pidfd_open(getpid(), PidfdOpenFlags::empty()) {
36+
Ok(x) => x,
37+
Err(Errno::ENOSYS) => std::process::exit(2),
38+
Err(_) => std::process::exit(1),
39+
};
40+
41+
// Code beyond this call should be unreachable.
42+
match pidfd_send_signal(
43+
&pidfd,
44+
Signal::SIGKILL,
45+
None,
46+
PidfdSendSignalFlags::empty(),
47+
) {
48+
Ok(()) => (),
49+
Err(Errno::ENOSYS) => std::process::exit(2),
50+
Err(_) => std::process::exit(1),
51+
}
52+
53+
std::process::exit(1)
54+
}
55+
56+
// SAFETY: `child_process` is async-signal-safe.
57+
let child = unsafe {
58+
match fork().expect("should be able to fork") {
59+
ForkResult::Parent { child } => child,
60+
ForkResult::Child => child_process(),
61+
}
62+
};
63+
64+
match waitpid(child, None) {
65+
// Kernel has PIDFD syscalls and they worked.
66+
Ok(WaitStatus::Signaled(x, Signal::SIGKILL, _)) if x == child => (),
67+
68+
// Kernel does not have PIDFD syscalls.
69+
Ok(WaitStatus::Exited(x, 2)) if x == child => (),
70+
71+
_ => panic!("unexpected waitpid result"),
72+
}
73+
}
1074
}

0 commit comments

Comments
 (0)