more reasonable tracking of open FDs
This commit is contained in:
parent
398fccc5d0
commit
aee8dcb31a
6 changed files with 163 additions and 77 deletions
146
src/daemon.rs
146
src/daemon.rs
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue