more reasonable tracking of open FDs

This commit is contained in:
Qyriad 2026-03-19 19:45:09 +01:00
parent 398fccc5d0
commit aee8dcb31a
6 changed files with 163 additions and 77 deletions

View file

@ -7,19 +7,15 @@ use std::{
};
use circular_buffer::CircularBuffer;
use iddqd::BiHashMap;
use mio::{Events, Interest, Poll, Token, net::UnixListener, unix::SourceFd};
use rustix::{
buffer::{Buffer, spare_capacity},
fs::{FileType, OFlags, Stat, fcntl_setfl},
io::Errno,
buffer::spare_capacity,
fs::{FileType, Stat},
net::SocketFlags,
process::Uid,
termios::{
ControlModes, InputModes, LocalModes, OptionalActions, OutputModes, SpecialCodeIndex,
SpecialCodes, Termios, tcgetattr, tcsetattr,
},
};
use serde::{Deserialize, Serialize};
@ -27,7 +23,7 @@ use serde_json::StreamDeserializer;
use crate::prelude::*;
use crate::OwnedFdWithFlags;
use crate::{OwnedFdWithFlags, TokenFd};
pub static UID: LazyLock<Uid> = LazyLock::new(|| rustix::process::getuid());
@ -109,10 +105,33 @@ pub struct Daemon {
poll_error_buffer: CircularBuffer<ERROR_BUFFER_LEN, IoError>,
fd_error_buffer: CircularBuffer<ERROR_BUFFER_LEN, IoError>,
// Bijective mapping of [`mio::Token`]s to [`RawFd`]s.
tokfd: BiHashMap<TokenFd>,
cmd_buffer: Vec<u8>,
next_timeout: Option<Duration>,
}
/// `tokfd` handling.
impl Daemon {
//fn register(&mut self, token: Token, fd: RawFd) {
// self.insert_unique
//}
fn fd_for_token(&self, token: Token) -> Option<RawFd> {
self.tokfd
.get1(&token)
.map(|TokenFd { fd, .. }| fd)
.copied()
}
fn token_for_fd(&self, fd: RawFd) -> Option<Token> {
self.tokfd
.get2(&fd)
.map(|TokenFd { token, .. }| token)
.copied()
}
}
impl Daemon {
pub fn new(fd: OwnedFd, name_or_path: Box<OsStr>) -> Self {
let fd = OwnedFdWithFlags::new_with_fallback(fd);
@ -126,6 +145,7 @@ impl Daemon {
fd,
path: name_or_path,
poll_error_buffer: Default::default(),
tokfd: Default::default(),
fd_error_buffer: Default::default(),
cmd_buffer: Vec::with_capacity(1024),
next_timeout: TIMEOUT_NEVER,
@ -137,9 +157,8 @@ impl Daemon {
let _ = rustix::fs::unlink(path);
let listener = UnixListener::bind(path)
.tap_err(|e| error!("failed to bind AF_UNIX socket at {}: {e}", path.display()))?;
//let (stream, _addr) = listener.accept().unwrap_or_else(|e| todo!("error: {e}"));
//warn!("stream is: {stream:?} ({})", stream.as_raw_fd());
let listener_owned_fd = OwnedFd::from(listener);
// FIXME: should we KEEP_ALIVE?
rustix::net::sockopt::set_socket_keepalive(&listener_owned_fd, true).unwrap();
let path: Box<OsStr> = path.to_path_buf().into_boxed_path().into_boxed_os_str();
@ -249,8 +268,19 @@ impl Daemon {
let raw_fd = self.fd.as_raw_fd();
let mut daemon_source = SourceFd(&raw_fd);
const DAEMON: Token = Token(0);
self.tokfd
.insert_unique(TokenFd {
token: DAEMON,
fd: raw_fd,
})
.unwrap();
let mut next_token_number: usize = 1;
let mut extra_tokens: Vec<(Token, RawFd)> = Default::default();
let mut next_token = || -> Token {
let t = Token(next_token_number);
next_token_number = next_token_number.saturating_add(1);
t
};
let mut poll = Poll::new().unwrap_or_else(|e| unreachable!("creating new mio Poll: {e}"));
poll.registry()
@ -259,27 +289,14 @@ impl Daemon {
let mut events = Events::with_capacity(1024);
let example = DaemonCmd::Append {
//name: ConvenientAttrPath::Dotted(Box::from(
// "services.gotosocial.settings.application-name",
//)),
//name: ConvenientAttrPath::Split(Box::from([
// Box::from("services"),
// Box::from("gotosocial"),
//])),
name: ConvenientAttrPath::clone_from_split(&[
"services",
"gotosocial",
"settings",
"application-name",
]),
value: Box::from("foo"),
};
//let example_as_json = serde_json::to_string_pretty(&example).unwrap();
//info!("{}", example_as_json);
loop {
if let Some(timeout) = self.next_timeout {
debug!(
"epoll_wait() with a timeout: {}",
humantime::format_duration(timeout),
);
}
match poll.poll(&mut events, self.next_timeout.take()) {
Ok(_) => {
if events.is_empty() {
@ -333,50 +350,25 @@ impl Daemon {
},
};
// Add this stream to our poll interest list.
let mut stream_fd = stream_fd.into_raw_fd();
// And add this stream to our poll interest list.
if let Err(idx) =
extra_tokens.binary_search_by_key(&stream_fd, |(_, fd)| fd.as_raw_fd())
{
let token = Token(next_token_number);
extra_tokens.insert(idx, (token, stream_fd));
next_token_number = next_token_number.wrapping_add(1);
let mut source = SourceFd(&mut stream_fd);
poll.registry()
.register(&mut source, token, Interest::READABLE)
.unwrap();
}
let token = next_token();
self.tokfd
.insert_unique((token, stream_fd).into())
.unwrap_or_else(|e| unreachable!("? {e}"));
let mut source = SourceFd(&mut stream_fd);
poll.registry()
.register(&mut source, token, Interest::READABLE)
.unwrap();
// Wait for the next poll to handle.
//match self.read_cmd(Some(&stream_fd)) {
// Ok(()) => (),
// Err(e) if e.kind() == IoErrorKind::WouldBlock => {
// continue;
// },
// Err(e) => {
// self.fd_error_buffer.try_push_back(e.into()).tap_err(|e| {
// error!(
// "Accumulated too many errors for fd {:?} {e}",
// &stream_fd,
// )
// })?;
// },
//}
},
other_token => {
let index = match extra_tokens
.binary_search_by_key(&other_token, |&(t, _)| t)
{
Ok(index) => index,
Err(index) => unreachable!(
"tried to get index ({index}) for non-existent token {other_token:?}"
),
};
// This must be a stream fd.
let (_, stream_fd) = extra_tokens[index];
let stream_fd = self.fd_for_token(other_token).unwrap_or_else(|| {
unreachable!("tried to get fd for no-existent token? {other_token:?}")
});
// SAFETY: oh boy.
let stream_fd = unsafe { BorrowedFd::borrow_raw(stream_fd) };
self.read_cmd(Some(&stream_fd)).unwrap();
@ -386,14 +378,14 @@ impl Daemon {
}
}
fn get_fallback_fd_name<Fd: AsRawFd>(fd: Fd) -> Option<Box<OsStr>> {
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)
.ok()
}
//fn get_fallback_fd_name<Fd: AsRawFd>(fd: Fd) -> Option<Box<OsStr>> {
// 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)
// .ok()
//}
}
impl Drop for Daemon {