yarli_cli/yarli-exec/src/
pidfd.rs1#[cfg(unix)]
11mod inner {
12 use std::os::unix::io::RawFd;
13
14 pub struct PidFd {
16 fd: RawFd,
17 }
18
19 impl PidFd {
20 #[cfg(target_os = "linux")]
24 pub fn open(pid: u32) -> std::io::Result<Self> {
25 let fd = unsafe { libc::syscall(libc::SYS_pidfd_open, pid as libc::c_int, 0) };
26 if fd < 0 {
27 Err(std::io::Error::last_os_error())
28 } else {
29 Ok(Self { fd: fd as RawFd })
30 }
31 }
32
33 #[cfg(not(target_os = "linux"))]
34 pub fn open(_pid: u32) -> std::io::Result<Self> {
35 Err(std::io::Error::new(
36 std::io::ErrorKind::Unsupported,
37 "pidfd_open is only available on Linux 5.3+",
38 ))
39 }
40
41 #[cfg(target_os = "linux")]
43 pub fn send_signal(&self, sig: i32) -> std::io::Result<()> {
44 let rc = unsafe {
45 libc::syscall(
46 libc::SYS_pidfd_send_signal,
47 self.fd,
48 sig,
49 std::ptr::null::<libc::siginfo_t>(),
50 0u32,
51 )
52 };
53 if rc == 0 {
54 Ok(())
55 } else {
56 Err(std::io::Error::last_os_error())
57 }
58 }
59
60 #[cfg(not(target_os = "linux"))]
61 pub fn send_signal(&self, _sig: i32) -> std::io::Result<()> {
62 Err(std::io::Error::new(
63 std::io::ErrorKind::Unsupported,
64 "pidfd_send_signal is only available on Linux 5.3+",
65 ))
66 }
67
68 pub fn as_raw_fd(&self) -> RawFd {
70 self.fd
71 }
72 }
73
74 impl Drop for PidFd {
75 fn drop(&mut self) {
76 unsafe {
77 libc::close(self.fd);
78 }
79 }
80 }
81
82 pub enum ProcessHandle {
84 PidFd(PidFd),
86 RawPid(u32),
88 }
89
90 impl ProcessHandle {
91 pub fn acquire(pid: u32) -> Self {
93 match PidFd::open(pid) {
94 Ok(fd) => ProcessHandle::PidFd(fd),
95 Err(_) => ProcessHandle::RawPid(pid),
96 }
97 }
98
99 pub fn send_signal(&self, sig: i32) -> std::io::Result<()> {
101 match self {
102 ProcessHandle::PidFd(fd) => fd.send_signal(sig),
103 ProcessHandle::RawPid(pid) => {
104 let rc = unsafe { libc::kill(*pid as i32, sig) };
105 if rc == 0 {
106 Ok(())
107 } else {
108 let err = std::io::Error::last_os_error();
109 if matches!(err.raw_os_error(), Some(code) if code == libc::ESRCH) {
111 Ok(())
112 } else {
113 Err(err)
114 }
115 }
116 }
117 }
118 }
119
120 pub fn is_pidfd(&self) -> bool {
122 matches!(self, ProcessHandle::PidFd(_))
123 }
124 }
125
126 pub fn signal_process_group(pgid: i32, signal: i32) -> std::io::Result<()> {
131 let rc = unsafe { libc::kill(-pgid, signal) };
132 if rc == 0 {
133 return Ok(());
134 }
135 let err = std::io::Error::last_os_error();
136 if matches!(err.raw_os_error(), Some(code) if code == libc::ESRCH) {
137 return Ok(());
138 }
139 Err(err)
140 }
141}
142
143#[cfg(not(unix))]
145mod inner {
146 pub enum ProcessHandle {}
148
149 impl ProcessHandle {
150 pub fn acquire(_pid: u32) -> Option<Self> {
151 None
152 }
153
154 pub fn send_signal(&self, _sig: i32) -> std::io::Result<()> {
155 match *self {}
156 }
157
158 pub fn is_pidfd(&self) -> bool {
159 match *self {}
160 }
161 }
162
163 pub fn signal_process_group(_pgid: i32, _signal: i32) -> std::io::Result<()> {
164 Ok(())
165 }
166}
167
168pub use inner::*;
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[cfg(target_os = "linux")]
175 #[test]
176 fn pidfd_open_current_process() {
177 let pid = std::process::id();
178 let fd = PidFd::open(pid).expect("should open pidfd for own process");
179 assert!(fd.as_raw_fd() >= 0);
180 }
181
182 #[cfg(target_os = "linux")]
183 #[test]
184 fn pidfd_send_signal_zero() {
185 let pid = std::process::id();
186 let fd = PidFd::open(pid).expect("should open pidfd");
187 fd.send_signal(0).expect("signal 0 to self should succeed");
189 }
190
191 #[cfg(target_os = "linux")]
192 #[test]
193 fn pidfd_open_invalid_pid() {
194 let result = PidFd::open(u32::MAX);
197 assert!(result.is_err());
198 }
199
200 #[cfg(unix)]
201 #[test]
202 fn process_handle_acquire_returns_some_variant() {
203 let pid = std::process::id();
204 let handle = ProcessHandle::acquire(pid);
205 handle.send_signal(0).expect("signal 0 to self should work");
208 }
209
210 #[cfg(unix)]
211 #[test]
212 fn process_handle_send_signal_zero() {
213 let pid = std::process::id();
214 let handle = ProcessHandle::acquire(pid);
215 handle
216 .send_signal(0)
217 .expect("signal probe via handle should succeed");
218 }
219
220 #[cfg(unix)]
221 #[test]
222 fn process_handle_raw_pid_variant_send_signal_zero() {
223 let handle = ProcessHandle::RawPid(std::process::id());
224 assert!(!handle.is_pidfd());
225 handle
226 .send_signal(0)
227 .expect("signal probe via raw pid should succeed");
228 }
229}