171 lines
3.8 KiB
Rust
171 lines
3.8 KiB
Rust
use std::{os::fd::RawFd, sync::OnceLock};
|
|
|
|
use circular_buffer::CircularBuffer;
|
|
use iddqd::{BiHashItem, IdOrdItem};
|
|
use mio::Token;
|
|
use rustix::process::Pid;
|
|
|
|
use crate::prelude::*;
|
|
|
|
const ERROR_BUFFER_LEN: usize = 8;
|
|
|
|
#[derive(Debug)]
|
|
pub struct FdInfo {
|
|
pub fd: RawFd,
|
|
pub kind: FdKind,
|
|
pub name: OnceLock<Box<OsStr>>,
|
|
pub error_buffer: CircularBuffer<ERROR_BUFFER_LEN, IoError>,
|
|
}
|
|
|
|
impl FdInfo {
|
|
pub fn new<Fd: AsRawFd>(fd: Fd, kind: FdKind) -> Self {
|
|
Self {
|
|
fd: fd.as_raw_fd(),
|
|
kind,
|
|
name: Default::default(),
|
|
error_buffer: Default::default(),
|
|
}
|
|
}
|
|
|
|
pub fn new_with_name<Fd: AsRawFd>(fd: Fd, kind: FdKind, name: Box<OsStr>) -> Self {
|
|
Self {
|
|
fd: fd.as_raw_fd(),
|
|
kind,
|
|
name: OnceLock::from(name),
|
|
error_buffer: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FdInfo {
|
|
pub(crate) fn guess_name<Fd: AsRawFd>(fd: Fd) -> Result<Box<OsStr>, IoError> {
|
|
let dev_fd_path = Path::new("/dev/fd").join(fd.as_raw_fd().to_string());
|
|
|
|
fs_err::read_link(dev_fd_path)
|
|
.map(PathBuf::into_os_string)
|
|
.map(OsString::into_boxed_os_str)
|
|
}
|
|
|
|
pub fn name(&self) -> &OsStr {
|
|
if let Some(name) = self.name.get() {
|
|
return name;
|
|
}
|
|
|
|
match Self::guess_name(self.fd) {
|
|
Ok(name) => {
|
|
let prev = self.name.set(name);
|
|
debug_assert_eq!(prev, Ok(()));
|
|
},
|
|
Err(e) => {
|
|
warn!(
|
|
"can't read link for {} /dev/fd/{}: {e}",
|
|
self.kind.name_str(),
|
|
self.fd,
|
|
);
|
|
return OsStr::new("«unknown»");
|
|
},
|
|
}
|
|
|
|
self.name.get().unwrap_or_else(|| unreachable!())
|
|
}
|
|
|
|
pub fn display(&self) -> FdInfoDisplay<'_> {
|
|
FdInfoDisplay { inner: self }
|
|
}
|
|
}
|
|
|
|
impl IdOrdItem for FdInfo {
|
|
type Key<'a> = &'a RawFd;
|
|
|
|
iddqd::id_upcast!();
|
|
|
|
fn key(&self) -> &RawFd {
|
|
&self.fd
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct FdInfoDisplay<'a> {
|
|
inner: &'a FdInfo,
|
|
}
|
|
impl<'a> Display for FdInfoDisplay<'a> {
|
|
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
|
write!(
|
|
f,
|
|
"{} fd {} ({})",
|
|
self.inner.kind.name_str(),
|
|
self.inner.fd,
|
|
self.inner.name().to_string_lossy(),
|
|
)?;
|
|
if !self.inner.error_buffer.is_empty() {
|
|
write!(f, "; with errors: {}", self.inner.error_buffer.len())?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Copy)]
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
|
#[non_exhaustive]
|
|
pub enum FdKind {
|
|
File,
|
|
Socket,
|
|
SockStream,
|
|
Poller,
|
|
ChildStdout,
|
|
ChildStderr,
|
|
Pid(Pid),
|
|
#[default]
|
|
Unknown,
|
|
}
|
|
|
|
impl FdKind {
|
|
pub fn name_str(self) -> &'static str {
|
|
use FdKind::*;
|
|
match self {
|
|
File => "file",
|
|
Socket => "socket",
|
|
SockStream => "socket stream",
|
|
Poller => "poller",
|
|
ChildStdout => "child stdout",
|
|
ChildStderr => "child stderr",
|
|
Pid(_) => "pidfd",
|
|
Unknown => "«unknown»",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy)]
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct TokenFd {
|
|
pub token: Token,
|
|
pub fd: RawFd,
|
|
}
|
|
|
|
impl BiHashItem for TokenFd {
|
|
type K1<'a> = Token;
|
|
type K2<'a> = RawFd;
|
|
|
|
iddqd::bi_upcast!();
|
|
|
|
fn key1(&self) -> Token {
|
|
self.token
|
|
}
|
|
|
|
fn key2(&self) -> RawFd {
|
|
self.fd
|
|
}
|
|
}
|
|
|
|
impl From<TokenFd> for (Token, RawFd) {
|
|
fn from(TokenFd { token, fd }: TokenFd) -> (Token, RawFd) {
|
|
(token, fd)
|
|
}
|
|
}
|
|
|
|
impl From<(Token, RawFd)> for TokenFd {
|
|
fn from((token, fd): (Token, RawFd)) -> TokenFd {
|
|
TokenFd { token, fd }
|
|
}
|
|
}
|