[go: up one dir, main page]

reis/
eis.rs

1//! Server-side EI protocol.
2//!
3//! Use [Listener] to create a socket, listening for clients creating a new
4//! [Context].
5
6use std::{
7    env, io,
8    os::unix::{
9        io::{AsFd, AsRawFd, BorrowedFd, RawFd},
10        net::{UnixListener, UnixStream},
11    },
12    path::{Path, PathBuf},
13};
14
15use crate::{util, wire::Backend, PendingRequestResult};
16
17// Re-export generate bindings
18pub use crate::eiproto_eis::*;
19
20#[derive(Debug)]
21pub struct Listener {
22    listener: util::UnlinkOnDrop<UnixListener>,
23    _lock: Option<util::LockFile>,
24}
25
26impl Listener {
27    // TODO Use a lock here
28    pub fn bind(path: &Path) -> io::Result<Self> {
29        Self::bind_inner(PathBuf::from(path), None)
30    }
31
32    // XXX result type?
33    // Error if XDG_RUNTIME_DIR not set?
34    pub fn bind_auto() -> io::Result<Option<Self>> {
35        let xdg_dir = if let Some(var) = env::var_os("XDG_RUNTIME_DIR") {
36            PathBuf::from(var)
37        } else {
38            return Ok(None);
39        };
40        for i in 1..33 {
41            let lock_path = xdg_dir.join(format!("eis-{i}.lock"));
42            let Some(lock_file) = util::LockFile::new(lock_path)? else {
43                // Already locked, continue to next number
44                continue;
45            };
46            let sock_path = xdg_dir.join(format!("eis-{i}"));
47            return Self::bind_inner(sock_path, Some(lock_file)).map(Some);
48        }
49        Ok(None)
50    }
51
52    fn bind_inner(path: PathBuf, lock: Option<util::LockFile>) -> io::Result<Self> {
53        let listener = UnixListener::bind(&path)?;
54        listener.set_nonblocking(true)?;
55        let listener = util::UnlinkOnDrop::new(listener, path);
56        Ok(Self {
57            listener,
58            _lock: lock,
59        })
60    }
61
62    pub fn accept(&self) -> io::Result<Option<Context>> {
63        match self.listener.accept() {
64            Ok((socket, _)) => Ok(Some(Context::new(socket)?)),
65            Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None),
66            Err(e) => Err(e),
67        }
68    }
69}
70
71impl AsFd for Listener {
72    fn as_fd(&self) -> BorrowedFd {
73        self.listener.as_fd()
74    }
75}
76
77impl AsRawFd for Listener {
78    fn as_raw_fd(&self) -> RawFd {
79        self.listener.as_raw_fd()
80    }
81}
82
83#[derive(Clone, Debug)]
84pub struct Context(pub(crate) Backend);
85
86impl AsFd for Context {
87    fn as_fd(&self) -> BorrowedFd {
88        self.0.as_fd()
89    }
90}
91
92impl AsRawFd for Context {
93    fn as_raw_fd(&self) -> RawFd {
94        self.0.as_fd().as_raw_fd()
95    }
96}
97
98impl Context {
99    pub fn new(socket: UnixStream) -> io::Result<Self> {
100        Ok(Self(Backend::new(socket, false)?))
101    }
102
103    /// Read any pending data on socket into buffer
104    pub fn read(&self) -> io::Result<usize> {
105        self.0.read()
106    }
107
108    // XXX iterator
109    pub fn pending_request(&self) -> Option<PendingRequestResult<Request>> {
110        self.0.pending(Request::parse)
111    }
112
113    pub fn handshake(&self) -> handshake::Handshake {
114        self.0.object_for_id(0).unwrap().downcast_unchecked()
115    }
116
117    pub fn flush(&self) -> rustix::io::Result<()> {
118        self.0.flush()
119    }
120}
121
122#[doc(hidden)]
123pub trait Interface: crate::wire::Interface {}