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