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