1use std::time::Duration;
7
8use crate::common::{paths, Error, Result};
9use crate::ipc::{transport, DaemonClient};
10
11const SPAWN_TIMEOUT_SECS: u64 = 5;
13
14pub async fn ensure_daemon_running() -> Result<()> {
16 match DaemonClient::connect().await {
18 Ok(_) => return Ok(()), Err(Error::DaemonNotRunning) => {
20 spawn_daemon().await?;
22 }
23 Err(e) => return Err(e),
24 }
25
26 Ok(())
27}
28
29async fn spawn_daemon() -> Result<()> {
31 tracing::debug!("Spawning daemon process");
32
33 let exe_path = std::env::current_exe().map_err(|e| {
35 Error::Internal(format!("Failed to get current executable path: {}", e))
36 })?;
37
38 paths::ensure_socket_dir()?;
40
41 paths::remove_socket()?;
43
44 #[cfg(unix)]
47 {
48 use std::os::unix::process::CommandExt;
49 use std::fs::File;
50
51 let dev_null = File::open("/dev/null")
53 .map_err(|e| Error::Internal(format!("Failed to open /dev/null: {}", e)))?;
54 let dev_null_out = File::create("/dev/null")
55 .map_err(|e| Error::Internal(format!("Failed to open /dev/null for write: {}", e)))?;
56
57 std::process::Command::new(&exe_path)
58 .arg("daemon")
59 .stdin(std::process::Stdio::from(dev_null))
60 .stdout(std::process::Stdio::from(dev_null_out.try_clone().unwrap()))
61 .stderr(std::process::Stdio::from(dev_null_out))
62 .process_group(0) .spawn()
64 .map_err(|e| Error::Internal(format!("Failed to spawn daemon: {}", e)))?;
65 }
66
67 #[cfg(windows)]
68 {
69 use std::os::windows::process::CommandExt;
70 const DETACHED_PROCESS: u32 = 0x00000008;
71 const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
72 std::process::Command::new(&exe_path)
73 .arg("daemon")
74 .stdin(std::process::Stdio::null())
75 .stdout(std::process::Stdio::null())
76 .stderr(std::process::Stdio::null())
77 .creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
78 .spawn()
79 .map_err(|e| Error::Internal(format!("Failed to spawn daemon: {}", e)))?;
80 }
81
82 let deadline = std::time::Instant::now() + Duration::from_secs(SPAWN_TIMEOUT_SECS);
84
85 loop {
86 if std::time::Instant::now() >= deadline {
87 return Err(Error::DaemonSpawnTimeout(SPAWN_TIMEOUT_SECS));
88 }
89
90 tokio::time::sleep(Duration::from_millis(50)).await;
92
93 #[cfg(unix)]
95 if !paths::socket_path().exists() {
96 continue;
97 }
98
99 match transport::connect().await {
101 Ok(_) => {
102 tracing::debug!("Daemon started successfully");
103 return Ok(());
104 }
105 Err(_) => continue,
106 }
107 }
108}