1#[cfg(unix)]
4mod client;
5#[cfg(unix)]
6mod notify;
7#[cfg(unix)]
8mod paths;
9#[cfg(unix)]
10mod protocol;
11#[cfg(unix)]
12mod server;
13
14#[cfg(unix)]
15pub use client::{
16 flush as client_flush, ping, read_pid, response_found, response_messages, response_ok,
17 response_uuid, response_value, shutdown, Client,
18};
19#[cfg(unix)]
20pub use notify::{subscribe, watch, EventHub, Notice};
21#[cfg(unix)]
22pub use protocol::{ok_empty, Request, Response};
23#[cfg(unix)]
24pub use paths::{daemon_dir, events_socket_path, pid_path, socket_path};
25#[cfg(unix)]
26pub use server::run as run_server;
27
28use std::process::{Command, Stdio};
29
30use crate::error::{Error, Result};
31use crate::home::UnifierHome;
32
33pub fn ensure_running(home: &UnifierHome) -> Result<()> {
35 if is_running(home) {
36 return Ok(());
37 }
38 start(home, false)
39}
40
41pub fn is_running(home: &UnifierHome) -> bool {
43 #[cfg(unix)]
44 {
45 Client::is_running(home)
46 }
47 #[cfg(not(unix))]
48 {
49 let _ = home;
50 false
51 }
52}
53
54pub fn start(home: &UnifierHome, foreground: bool) -> Result<()> {
56 #[cfg(not(unix))]
57 {
58 let _ = (home, foreground);
59 return Err(Error::msg("hot daemon requires a Unix platform"));
60 }
61
62 #[cfg(unix)]
63 {
64 if Client::is_running(home) {
65 return Err(Error::msg("daemon is already running"));
66 }
67
68 home.ensure()?;
69 std::fs::create_dir_all(crate::daemon::paths::daemon_dir(home))?;
70
71 if foreground {
72 return run_server(home.clone());
73 }
74
75 let exe = std::env::current_exe()?;
76 let mut cmd = Command::new(exe);
77 cmd.arg("daemon").arg("run");
78 if let Some(p) = home.global_path().to_str() {
79 cmd.args(["--home", p]);
80 }
81 if let Some(name) = home.chroot_name() {
82 cmd.args(["--chroot", name]);
83 }
84 cmd.stdin(Stdio::null())
85 .stdout(Stdio::null())
86 .stderr(Stdio::null());
87
88 let child = cmd.spawn()?;
89 wait_for_socket(home, child.id())?;
90 Ok(())
91 }
92}
93
94pub fn stop(home: &UnifierHome) -> Result<()> {
96 #[cfg(not(unix))]
97 {
98 let _ = home;
99 return Err(Error::msg("hot daemon requires a Unix platform"));
100 }
101
102 #[cfg(unix)]
103 {
104 shutdown(home)
105 }
106}
107
108pub fn status(home: &UnifierHome) -> Result<()> {
110 #[cfg(not(unix))]
111 {
112 let _ = home;
113 println!("daemon: unavailable (requires Unix)");
114 return Ok(());
115 }
116
117 #[cfg(unix)]
118 {
119 if Client::is_running(home) {
120 let pid = read_pid(home)?.unwrap_or(0);
121 println!("daemon: running (pid {pid})");
122 println!("socket: {}", socket_path(home).display());
123 println!("events: {}", events_socket_path(home).display());
124 } else {
125 println!("daemon: stopped");
126 }
127 Ok(())
128 }
129}
130
131pub fn flush(home: &UnifierHome) -> Result<()> {
133 #[cfg(not(unix))]
134 {
135 let _ = home;
136 return Err(Error::msg("hot daemon requires a Unix platform"));
137 }
138
139 #[cfg(unix)]
140 {
141 let dirty = client_flush(home)?;
142 if dirty {
143 println!("flushed dirty state to disk");
144 } else {
145 println!("nothing to flush");
146 }
147 Ok(())
148 }
149}
150
151#[cfg(unix)]
152fn wait_for_socket(home: &UnifierHome, _pid: u32) -> Result<()> {
153 let path = socket_path(home);
154 for _ in 0..100 {
155 if path.exists() && Client::is_running(home) {
156 return ping(home);
157 }
158 std::thread::sleep(std::time::Duration::from_millis(50));
159 }
160 Err(Error::msg("daemon failed to start"))
161}