a653rs_linux_core/
fd.rs

1//! Implementation of PID file descriptors
2// TODO: Consider renaming this module to "pidfd" for less ambiguity
3// TODO: Remove this, as soon as the following is available in stable Rust:
4// https://doc.rust-lang.org/stable/std/os/linux/process/struct.PidFd.html
5use std::io::ErrorKind;
6use std::os::unix::prelude::{AsRawFd, FromRawFd, OwnedFd, RawFd};
7use std::time::{Duration, Instant};
8
9use anyhow::{anyhow, Result};
10use nix::libc::{c_uint, syscall, SYS_pidfd_open};
11use nix::unistd::Pid;
12use polling::{Event, Events, Poller};
13
14use crate::error::{ResultExt, SystemError, TypedError, TypedResult};
15
16#[derive(Debug)]
17/// The fundamental error type for this crate
18// TODO: Consider replacing it with a normal TypedError and using
19// TimeDurationExceeded instead
20pub enum PidWaitError {
21    /// A timeout has a occurred
22    Timeout,
23    /// Another error has occurred
24    Err(TypedError),
25}
26
27impl From<TypedError> for PidWaitError {
28    fn from(e: TypedError) -> Self {
29        Self::Err(e)
30    }
31}
32
33#[derive(Debug)]
34/// The internal type of this module for handling PidFds.
35pub struct PidFd(OwnedFd);
36
37impl PidFd {
38    /// Returns when the PidFd is ready to be read or if timeout occurred
39    pub fn wait_exited_timeout(&self, timeout: Duration) -> Result<(), PidWaitError> {
40        let now = Instant::now();
41
42        let poller = Poller::new()
43            .map_err(anyhow::Error::from)
44            .typ(SystemError::Panic)?;
45
46        loop {
47            // The second argument to Poller::modify() is totally valid and correct, due to
48            // epoll(2) internals, which demand providing a "user data variable" -- a
49            // feature that we make no use of.
50            poller
51                .modify(&self.0, Event::readable(42))
52                .map_err(anyhow::Error::from)
53                .typ(SystemError::Panic)?;
54
55            let poll_res = poller.wait(
56                &mut Events::new(),
57                Some(timeout.saturating_sub(now.elapsed())),
58            );
59            match poll_res {
60                Ok(0) => return Err(PidWaitError::Timeout),
61                Ok(_) => return Ok(()),
62                Err(e) => {
63                    if e.kind() != ErrorKind::Interrupted {
64                        return Err(anyhow::Error::from(e))
65                            .typ(SystemError::Panic)
66                            .map_err(PidWaitError::Err);
67                    }
68                }
69            }
70        }
71    }
72}
73
74impl AsRawFd for PidFd {
75    fn as_raw_fd(&self) -> RawFd {
76        self.0.as_raw_fd()
77    }
78}
79
80impl TryFrom<Pid> for PidFd {
81    type Error = TypedError;
82
83    fn try_from(value: Pid) -> TypedResult<Self> {
84        let pidfd: std::os::raw::c_int = unsafe {
85            syscall(SYS_pidfd_open, value.as_raw(), 0 as c_uint)
86                .try_into()
87                .typ(SystemError::Panic)?
88        };
89        if pidfd < 0 {
90            // TODO: pidfd will be -1 in that case. Printing this is not useful. Access
91            // errno instead.
92            return Err(anyhow!("Error getting pidfd from {value}. {pidfd}"))
93                .typ(SystemError::Panic);
94        }
95        Ok(PidFd(unsafe { OwnedFd::from_raw_fd(pidfd) }))
96    }
97}