pidfd_util/pidfd_impl.rs
1// SPDX-FileCopyrightText: 2026 The pidfd-util-rs authors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use super::lowlevel::{pidfd_send_signal, pidfd_try_wait, pidfd_wait};
5use std::cell::OnceCell;
6use std::io;
7use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
8use std::process::ExitStatus;
9
10/// A file descriptor that refers to a process.
11///
12/// This type represents a Linux pidfd (process file descriptor), which is a file descriptor
13/// that refers to a process. Unlike traditional PIDs, pidfds cannot be reused, making them
14/// safe from PID reuse race conditions.
15///
16/// On nightly Rust with the `nightly` feature, this re-exports `std::os::linux::process::PidFd`.
17/// On stable Rust, this provides a compatible implementation.
18pub struct PidFd {
19 fd: OwnedFd,
20 exit_status: OnceCell<io::Result<ExitStatus>>,
21}
22
23impl PidFd {
24 fn new(fd: OwnedFd) -> Self {
25 Self {
26 fd,
27 exit_status: OnceCell::new(),
28 }
29 }
30
31 /// Sends `SIGKILL` to the process referred to by the pidfd.
32 ///
33 /// This is a convenience method equivalent to `send_signal(libc::SIGKILL)`.
34 pub fn kill(&self) -> io::Result<()> {
35 pidfd_send_signal(self, libc::SIGKILL)
36 }
37
38 /// Waits for the process referred to by the pidfd to exit and returns its exit status.
39 ///
40 /// This method blocks until the process exits. Use [`try_wait`](Self::try_wait)
41 /// for a non-blocking alternative, or [`AsyncPidFd::wait`](crate::AsyncPidFd::wait)
42 /// for async code.
43 pub fn wait(&self) -> io::Result<ExitStatus> {
44 let status = self.exit_status.get_or_init(|| pidfd_wait(self));
45
46 match status {
47 Ok(v) => Ok(*v),
48 Err(e) => Err(io::Error::from_raw_os_error(e.raw_os_error().unwrap())),
49 }
50 }
51
52 /// Checks if the process referred to by the pidfd has exited without blocking.
53 ///
54 /// Returns `Ok(Some(status))` if the process has exited, `Ok(None)` if it's still running.
55 pub fn try_wait(&self) -> io::Result<Option<ExitStatus>> {
56 pidfd_try_wait(self)
57 }
58}
59
60impl AsRawFd for PidFd {
61 #[inline]
62 fn as_raw_fd(&self) -> RawFd {
63 self.fd.as_raw_fd()
64 }
65}
66
67impl FromRawFd for PidFd {
68 unsafe fn from_raw_fd(fd: RawFd) -> Self {
69 // SAFETY:
70 // The caller must ensure that fd is a valid Pidfd.
71 unsafe { Self::new(OwnedFd::from_raw_fd(fd)) }
72 }
73}
74
75impl IntoRawFd for PidFd {
76 fn into_raw_fd(self) -> RawFd {
77 self.fd.into_raw_fd()
78 }
79}
80
81impl AsFd for PidFd {
82 fn as_fd(&self) -> BorrowedFd<'_> {
83 self.fd.as_fd()
84 }
85}
86
87impl From<OwnedFd> for PidFd {
88 fn from(fd: OwnedFd) -> Self {
89 Self::new(fd)
90 }
91}
92
93impl From<PidFd> for OwnedFd {
94 fn from(pid_fd: PidFd) -> Self {
95 pid_fd.fd
96 }
97}