std/sys/pal/unix/linux/
pidfd.rs

1use crate::io;
2use crate::os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
3use crate::sys::fd::FileDesc;
4use crate::sys::process::ExitStatus;
5use crate::sys::{AsInner, FromInner, IntoInner, cvt};
6
7#[cfg(test)]
8mod tests;
9
10#[derive(Debug)]
11pub(crate) struct PidFd(FileDesc);
12
13impl PidFd {
14    pub fn kill(&self) -> io::Result<()> {
15        self.send_signal(libc::SIGKILL)
16    }
17
18    #[cfg(any(test, target_env = "gnu", target_env = "musl"))]
19    pub(crate) fn current_process() -> io::Result<PidFd> {
20        let pid = crate::process::id();
21        let pidfd = cvt(unsafe { libc::syscall(libc::SYS_pidfd_open, pid, 0) })?;
22        Ok(unsafe { PidFd::from_raw_fd(pidfd as RawFd) })
23    }
24
25    #[cfg(any(test, target_env = "gnu", target_env = "musl"))]
26    pub(crate) fn pid(&self) -> io::Result<u32> {
27        use crate::sys::weak::weak;
28
29        // since kernel 6.13
30        // https://lore.kernel.org/all/20241010155401.2268522-1-luca.boccassi@gmail.com/
31        let mut pidfd_info: libc::pidfd_info = unsafe { crate::mem::zeroed() };
32        pidfd_info.mask = libc::PIDFD_INFO_PID as u64;
33        match cvt(unsafe { libc::ioctl(self.0.as_raw_fd(), libc::PIDFD_GET_INFO, &mut pidfd_info) })
34        {
35            Ok(_) => {}
36            Err(e) if e.raw_os_error() == Some(libc::EINVAL) => {
37                // kernel doesn't support that ioctl, try the glibc helper that looks at procfs
38                weak!(
39                    fn pidfd_getpid(pidfd: RawFd) -> libc::pid_t;
40                );
41                if let Some(pidfd_getpid) = pidfd_getpid.get() {
42                    let pid: libc::c_int = cvt(unsafe { pidfd_getpid(self.0.as_raw_fd()) })?;
43                    return Ok(pid as u32);
44                }
45                return Err(e);
46            }
47            Err(e) => return Err(e),
48        }
49
50        Ok(pidfd_info.pid)
51    }
52
53    fn exit_for_reaped_child(&self) -> io::Result<ExitStatus> {
54        // since kernel 6.15
55        // https://lore.kernel.org/linux-fsdevel/20250305-work-pidfs-kill_on_last_close-v3-0-c8c3d8361705@kernel.org/T/
56        let mut pidfd_info: libc::pidfd_info = unsafe { crate::mem::zeroed() };
57        pidfd_info.mask = libc::PIDFD_INFO_EXIT as u64;
58        cvt(unsafe { libc::ioctl(self.0.as_raw_fd(), libc::PIDFD_GET_INFO, &mut pidfd_info) })?;
59        Ok(ExitStatus::new(pidfd_info.exit_code))
60    }
61
62    fn waitid(&self, options: libc::c_int) -> io::Result<Option<ExitStatus>> {
63        let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() };
64        let r = cvt(unsafe {
65            libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, options)
66        });
67        match r {
68            Err(waitid_err) if waitid_err.raw_os_error() == Some(libc::ECHILD) => {
69                // already reaped
70                match self.exit_for_reaped_child() {
71                    Ok(exit_status) => return Ok(Some(exit_status)),
72                    Err(_) => return Err(waitid_err),
73                }
74            }
75            Err(e) => return Err(e),
76            Ok(_) => {}
77        }
78        if unsafe { siginfo.si_pid() } == 0 {
79            Ok(None)
80        } else {
81            Ok(Some(ExitStatus::from_waitid_siginfo(siginfo)))
82        }
83    }
84
85    pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> {
86        cvt(unsafe {
87            libc::syscall(
88                libc::SYS_pidfd_send_signal,
89                self.0.as_raw_fd(),
90                signal,
91                crate::ptr::null::<()>(),
92                0,
93            )
94        })
95        .map(drop)
96    }
97
98    pub fn wait(&self) -> io::Result<ExitStatus> {
99        let r = self.waitid(libc::WEXITED)?;
100        match r {
101            Some(exit_status) => Ok(exit_status),
102            None => unreachable!("waitid with WEXITED should not return None"),
103        }
104    }
105
106    pub fn try_wait(&self) -> io::Result<Option<ExitStatus>> {
107        self.waitid(libc::WEXITED | libc::WNOHANG)
108    }
109}
110
111impl AsInner<FileDesc> for PidFd {
112    fn as_inner(&self) -> &FileDesc {
113        &self.0
114    }
115}
116
117impl IntoInner<FileDesc> for PidFd {
118    fn into_inner(self) -> FileDesc {
119        self.0
120    }
121}
122
123impl FromInner<FileDesc> for PidFd {
124    fn from_inner(inner: FileDesc) -> Self {
125        Self(inner)
126    }
127}
128
129impl FromRawFd for PidFd {
130    unsafe fn from_raw_fd(fd: RawFd) -> Self {
131        Self(FileDesc::from_raw_fd(fd))
132    }
133}
134
135impl IntoRawFd for PidFd {
136    fn into_raw_fd(self) -> RawFd {
137        self.0.into_raw_fd()
138    }
139}