diff --git a/crates/xy-ipc/src/lib.rs b/crates/xy-ipc/src/lib.rs index bc716a6..8629b3b 100644 --- a/crates/xy-ipc/src/lib.rs +++ b/crates/xy-ipc/src/lib.rs @@ -3,6 +3,7 @@ pub mod client; pub mod envelope; pub mod framing; +pub mod server; pub use client::{Client, ClientError}; pub use envelope::{ @@ -10,3 +11,4 @@ pub use envelope::{ RpcError, }; pub use framing::JsonFramed; +pub use server::{bind, Connection}; diff --git a/crates/xy-ipc/src/server.rs b/crates/xy-ipc/src/server.rs new file mode 100644 index 0000000..fcead9b --- /dev/null +++ b/crates/xy-ipc/src/server.rs @@ -0,0 +1,67 @@ +use crate::envelope::{Incoming, Notification, Response}; +use crate::framing::JsonFramed; +use std::path::Path; +use std::sync::Arc; +use tokio::net::{UnixListener, UnixStream}; +use tokio::sync::Mutex; + +pub struct Connection { + inner: Arc>, +} + +impl Connection { + pub fn new(stream: UnixStream) -> Self { + Self { + inner: Arc::new(Mutex::new(JsonFramed::new(stream))), + } + } + + pub async fn read_incoming(&self) -> std::io::Result> { + let mut g = self.inner.lock().await; + g.read::().await + } + + pub async fn write_response(&self, r: &Response) -> std::io::Result<()> { + let mut g = self.inner.lock().await; + g.write(r).await + } + + pub async fn write_notification(&self, n: &Notification) -> std::io::Result<()> { + let mut g = self.inner.lock().await; + g.write(n).await + } +} + +pub fn bind(socket_path: &Path) -> std::io::Result { + if socket_path.exists() { + std::fs::remove_file(socket_path)?; + } + + if let Some(parent) = socket_path.parent() { + std::fs::create_dir_all(parent)?; + } + + let listener = UnixListener::bind(socket_path)?; + + use std::os::unix::fs::PermissionsExt; + std::fs::set_permissions(socket_path, std::fs::Permissions::from_mode(0o600))?; + + Ok(listener) +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::tempdir; + + #[tokio::test] + async fn bind_creates_socket_with_0600() { + let dir = tempdir().unwrap(); + let path = dir.path().join("x.sock"); + let _listener = bind(&path).unwrap(); + + use std::os::unix::fs::PermissionsExt; + let mode = std::fs::metadata(&path).unwrap().permissions().mode() & 0o777; + assert_eq!(mode, 0o600); + } +}