2026-03-19 20:44:57 +01:00
|
|
|
use std::{os::fd::RawFd, sync::OnceLock};
|
2026-03-19 19:45:09 +01:00
|
|
|
|
2026-03-19 20:44:57 +01:00
|
|
|
use circular_buffer::CircularBuffer;
|
|
|
|
|
use iddqd::{BiHashItem, IdOrdItem};
|
2026-03-19 19:45:09 +01:00
|
|
|
use mio::Token;
|
|
|
|
|
|
2026-03-19 20:44:57 +01:00
|
|
|
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(Box::from(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!())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl IdOrdItem for FdInfo {
|
|
|
|
|
type Key<'a> = &'a RawFd;
|
|
|
|
|
|
|
|
|
|
fn key(&self) -> &RawFd {
|
|
|
|
|
&self.fd
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iddqd::id_upcast!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Copy)]
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
|
#[non_exhaustive]
|
|
|
|
|
pub enum FdKind {
|
|
|
|
|
File,
|
|
|
|
|
Socket,
|
|
|
|
|
SockStream,
|
|
|
|
|
Poller,
|
|
|
|
|
Unknown,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FdKind {
|
|
|
|
|
pub fn name_str(self) -> &'static str {
|
|
|
|
|
use FdKind::*;
|
|
|
|
|
match self {
|
|
|
|
|
File => "file",
|
|
|
|
|
Socket => "socket",
|
|
|
|
|
SockStream => "socket stream",
|
|
|
|
|
Poller => "poller",
|
|
|
|
|
Unknown => "«unknown»",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for FdKind {
|
|
|
|
|
fn default() -> FdKind {
|
|
|
|
|
FdKind::Unknown
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 19:45:09 +01:00
|
|
|
#[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;
|
|
|
|
|
|
|
|
|
|
fn key1(&self) -> Token {
|
|
|
|
|
self.token
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn key2(&self) -> RawFd {
|
|
|
|
|
self.fd
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iddqd::bi_upcast!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 }
|
|
|
|
|
}
|
|
|
|
|
}
|