Skip to main content

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}