use std::{
env, io,
os::unix::{
io::{AsFd, AsRawFd, BorrowedFd, RawFd},
net::{UnixListener, UnixStream},
},
path::{Path, PathBuf},
};
use crate::{util, wire::Backend, PendingRequestResult};
pub use crate::eiproto_eis::*;
pub struct Listener {
listener: util::UnlinkOnDrop<UnixListener>,
_lock: Option<util::LockFile>,
}
impl Listener {
pub fn bind(path: &Path) -> io::Result<Self> {
Self::bind_inner(PathBuf::from(path), None)
}
pub fn bind_auto() -> io::Result<Option<Self>> {
let xdg_dir = if let Some(var) = env::var_os("XDG_RUNTIME_DIR") {
PathBuf::from(var)
} else {
return Ok(None);
};
#[allow(clippy::never_loop)] for i in 1..33 {
let lock_path = xdg_dir.join(format!("eis-{i}.lock"));
let Some(lock_file) = util::LockFile::new(lock_path)? else {
continue;
};
let sock_path = xdg_dir.join(format!("eis-{i}"));
return Self::bind_inner(sock_path, Some(lock_file)).map(Some);
}
Ok(None)
}
fn bind_inner(path: PathBuf, lock: Option<util::LockFile>) -> io::Result<Self> {
let listener = UnixListener::bind(&path)?;
listener.set_nonblocking(true)?;
let listener = util::UnlinkOnDrop::new(listener, path);
Ok(Self {
listener,
_lock: lock,
})
}
pub fn accept(&self) -> io::Result<Option<Context>> {
match self.listener.accept() {
Ok((socket, _)) => Ok(Some(Context::new(socket)?)),
Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None),
Err(e) => Err(e),
}
}
}
impl AsFd for Listener {
fn as_fd(&self) -> BorrowedFd {
self.listener.as_fd()
}
}
impl AsRawFd for Listener {
fn as_raw_fd(&self) -> RawFd {
self.listener.as_raw_fd()
}
}
#[derive(Clone, Debug)]
pub struct Context(pub(crate) Backend);
impl AsFd for Context {
fn as_fd(&self) -> BorrowedFd {
self.0.as_fd()
}
}
impl AsRawFd for Context {
fn as_raw_fd(&self) -> RawFd {
self.0.as_fd().as_raw_fd()
}
}
impl Context {
pub(crate) fn new(socket: UnixStream) -> io::Result<Self> {
Ok(Self(Backend::new(socket, false)?))
}
pub fn read(&self) -> io::Result<usize> {
self.0.read()
}
pub fn pending_request(&self) -> Option<PendingRequestResult<Request>> {
self.0.pending(Request::parse)
}
pub fn handshake(&self) -> handshake::Handshake {
self.0.object_for_id(0).unwrap().downcast_unchecked()
}
pub fn flush(&self) -> rustix::io::Result<()> {
self.0.flush()
}
}
#[doc(hidden)]
pub trait Interface: crate::wire::Interface {}