2026-07-20 09:04:57 +07:00
|
|
|
//! Daemon server — owns the agent state, listens on a per-session Unix
|
|
|
|
|
//! socket, and drives one attached client at a time.
|
|
|
|
|
//!
|
|
|
|
|
//! Flow: `run_daemon()` creates a session + lock → binds a Unix socket
|
|
|
|
|
//! under `<store>/run/<session_id>.sock` → blocks for a single client to
|
|
|
|
|
//! `accept()` → loops reading `ClientRequest`s, translating each into
|
|
|
|
|
//! `Action`(s) via the same `handle_key`/`apply_action` path the
|
|
|
|
|
//! single-process mode uses, then pushes a full state update back →
|
|
|
|
|
//! on `Close` or client disconnect, cleans up the socket file, saves
|
|
|
|
|
//! settings, and releases the lock.
|
|
|
|
|
//!
|
|
|
|
|
//! Why: reuses `crate::handler::handle_key` by synthesising a
|
|
|
|
|
//! `crossterm::KeyEvent` from the IPC `KeyAction`, so daemon and
|
|
|
|
|
//! single-process modes share identical key-handling logic.
|
|
|
|
|
|
|
|
|
|
use anyhow::Result;
|
|
|
|
|
use zesdex_infrastructure::ipc::server::IpcServer;
|
|
|
|
|
|
|
|
|
|
use crate::handler::handle_daemon_client;
|
|
|
|
|
use crate::state::create_session;
|
|
|
|
|
|
|
|
|
|
/// Run zesdex as a background daemon: owns the agent state, listens on a
|
|
|
|
|
/// per-session Unix socket, and drives one attached client.
|
|
|
|
|
///
|
|
|
|
|
/// Flow: create session + lock it → bind a Unix socket under
|
|
|
|
|
/// `<store>/run/<session_id>.sock` → block for a single client to
|
|
|
|
|
/// `accept()` → loop reading `ClientRequest`s, translating each into
|
|
|
|
|
/// `Action`(s) → on `Close` or client disconnect, clean up the socket file,
|
|
|
|
|
/// save settings, and release the lock.
|
|
|
|
|
pub fn run_daemon() -> Result<()> {
|
|
|
|
|
tracing::info!("starting daemon process");
|
|
|
|
|
let (store, _session_lock_guard, mut state, _rt) = create_session()?;
|
|
|
|
|
|
|
|
|
|
let run_dir = store.base_dir.join("run");
|
|
|
|
|
std::fs::create_dir_all(&run_dir)?;
|
|
|
|
|
let socket_path = run_dir.join(format!("{}.sock", state.session_id));
|
|
|
|
|
let addr = socket_path.to_string_lossy().to_string();
|
|
|
|
|
|
|
|
|
|
let server = IpcServer::bind_unix(&addr)?;
|
2026-07-20 10:55:09 +07:00
|
|
|
tracing::info!("daemon listening on {addr}");
|
2026-07-20 09:04:57 +07:00
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let conn = match server.accept() {
|
|
|
|
|
Ok(c) => c,
|
|
|
|
|
Err(e) => {
|
2026-07-20 10:55:09 +07:00
|
|
|
tracing::error!("daemon accept error: {e}");
|
2026-07-20 09:04:57 +07:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-07-20 10:55:09 +07:00
|
|
|
tracing::info!("daemon client connected");
|
2026-07-20 09:04:57 +07:00
|
|
|
|
|
|
|
|
if let Err(e) = handle_daemon_client(conn, &mut state) {
|
2026-07-20 10:55:09 +07:00
|
|
|
tracing::error!("daemon error handling client: {e}");
|
2026-07-20 09:04:57 +07:00
|
|
|
}
|
|
|
|
|
|
2026-07-20 10:55:09 +07:00
|
|
|
tracing::info!("daemon client disconnected");
|
2026-07-20 09:04:57 +07:00
|
|
|
state.save_settings();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let _ = std::fs::remove_file(&socket_path);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|