Rust · 3061 bytes Raw Blame History
1 //! IPC server for gardmd
2 //!
3 //! Handles connections from the greeter and processes requests.
4
5 use anyhow::Result;
6 use gardm_ipc::{Request, Response, SOCKET_PATH};
7 use std::os::unix::fs::PermissionsExt;
8 use std::path::Path;
9 use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
10 use tokio::net::{UnixListener, UnixStream};
11 use tokio::sync::mpsc;
12
13 /// IPC server for handling greeter connections
14 pub struct Server {
15 listener: UnixListener,
16 }
17
18 impl Server {
19 /// Create a new IPC server
20 pub async fn new() -> Result<Self> {
21 Self::bind(SOCKET_PATH).await
22 }
23
24 /// Bind to a specific socket path
25 pub async fn bind(path: &str) -> Result<Self> {
26 let path = Path::new(path);
27
28 // Remove stale socket if it exists
29 if path.exists() {
30 std::fs::remove_file(path)?;
31 }
32
33 // Ensure parent directory exists
34 if let Some(parent) = path.parent() {
35 std::fs::create_dir_all(parent)?;
36 }
37
38 let listener = UnixListener::bind(path)?;
39
40 // Set socket permissions (world read/write for development)
41 // TODO: Tighten to 0o660 with gardm group in production
42 std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o666))?;
43
44 tracing::info!("IPC server listening on {}", path.display());
45
46 Ok(Self { listener })
47 }
48
49 /// Accept a new connection
50 pub async fn accept(&self) -> Result<ClientConnection> {
51 let (stream, _addr) = self.listener.accept().await?;
52 tracing::debug!("New greeter connection");
53 Ok(ClientConnection::new(stream))
54 }
55 }
56
57 /// A connected greeter client
58 pub struct ClientConnection {
59 reader: BufReader<tokio::net::unix::OwnedReadHalf>,
60 writer: tokio::net::unix::OwnedWriteHalf,
61 }
62
63 impl ClientConnection {
64 fn new(stream: UnixStream) -> Self {
65 let (read, write) = stream.into_split();
66 Self {
67 reader: BufReader::new(read),
68 writer: write,
69 }
70 }
71
72 /// Receive a request from the greeter
73 pub async fn recv(&mut self) -> Result<Option<Request>> {
74 let mut line = String::new();
75 let n = self.reader.read_line(&mut line).await?;
76 if n == 0 {
77 return Ok(None);
78 }
79 let request: Request = serde_json::from_str(&line)?;
80 tracing::debug!(?request, "Received request");
81 Ok(Some(request))
82 }
83
84 /// Send a response to the greeter
85 pub async fn send(&mut self, response: &Response) -> Result<()> {
86 let json = serde_json::to_string(response)?;
87 self.writer.write_all(json.as_bytes()).await?;
88 self.writer.write_all(b"\n").await?;
89 self.writer.flush().await?;
90 tracing::debug!(?response, "Sent response");
91 Ok(())
92 }
93 }
94
95 /// Commands from external sources (e.g., signal handlers)
96 #[derive(Debug)]
97 pub enum DaemonCommand {
98 Shutdown,
99 Reload,
100 }
101
102 /// Create a channel for daemon commands
103 pub fn command_channel() -> (mpsc::Sender<DaemonCommand>, mpsc::Receiver<DaemonCommand>) {
104 mpsc::channel(16)
105 }
106