1use 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)]
17pub enum PidWaitError {
21 Timeout,
23 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)]
34pub struct PidFd(OwnedFd);
36
37impl PidFd {
38 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 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 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}