use std::io::ErrorKind;
use std::os::unix::prelude::{AsRawFd, FromRawFd, OwnedFd, RawFd};
use std::time::{Duration, Instant};
use anyhow::{anyhow, Result};
use nix::libc::{c_uint, syscall, SYS_pidfd_open};
use nix::unistd::Pid;
use polling::{Event, Events, Poller};
use crate::error::{ResultExt, SystemError, TypedError, TypedResult};
#[derive(Debug)]
pub enum PidWaitError {
Timeout,
Err(TypedError),
}
impl From<TypedError> for PidWaitError {
fn from(e: TypedError) -> Self {
Self::Err(e)
}
}
#[derive(Debug)]
pub struct PidFd(OwnedFd);
impl PidFd {
pub fn wait_exited_timeout(&self, timeout: Duration) -> Result<(), PidWaitError> {
let now = Instant::now();
let poller = Poller::new()
.map_err(anyhow::Error::from)
.typ(SystemError::Panic)?;
loop {
poller
.modify(&self.0, Event::readable(42))
.map_err(anyhow::Error::from)
.typ(SystemError::Panic)?;
let poll_res = poller.wait(
&mut Events::new(),
Some(timeout.saturating_sub(now.elapsed())),
);
match poll_res {
Ok(0) => return Err(PidWaitError::Timeout),
Ok(_) => return Ok(()),
Err(e) => {
if e.kind() != ErrorKind::Interrupted {
return Err(anyhow::Error::from(e))
.typ(SystemError::Panic)
.map_err(PidWaitError::Err);
}
}
}
}
}
}
impl AsRawFd for PidFd {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl TryFrom<Pid> for PidFd {
type Error = TypedError;
fn try_from(value: Pid) -> TypedResult<Self> {
let pidfd: std::os::raw::c_int = unsafe {
syscall(SYS_pidfd_open, value.as_raw(), 0 as c_uint)
.try_into()
.typ(SystemError::Panic)?
};
if pidfd < 0 {
return Err(anyhow!("Error getting pidfd from {value}. {pidfd}"))
.typ(SystemError::Panic);
}
Ok(PidFd(unsafe { OwnedFd::from_raw_fd(pidfd) }))
}
}