use std::time::Duration;
use crate::common::{paths, Error, Result};
use crate::ipc::{transport, DaemonClient};
const SPAWN_TIMEOUT_SECS: u64 = 5;
pub async fn ensure_daemon_running() -> Result<()> {
match DaemonClient::connect().await {
Ok(_) => return Ok(()), Err(Error::DaemonNotRunning) => {
spawn_daemon().await?;
}
Err(e) => return Err(e),
}
Ok(())
}
async fn spawn_daemon() -> Result<()> {
tracing::debug!("Spawning daemon process");
let exe_path = std::env::current_exe().map_err(|e| {
Error::Internal(format!("Failed to get current executable path: {}", e))
})?;
paths::ensure_socket_dir()?;
paths::remove_socket()?;
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
use std::fs::File;
let dev_null = File::open("/dev/null")
.map_err(|e| Error::Internal(format!("Failed to open /dev/null: {}", e)))?;
let dev_null_out = File::create("/dev/null")
.map_err(|e| Error::Internal(format!("Failed to open /dev/null for write: {}", e)))?;
std::process::Command::new(&exe_path)
.arg("daemon")
.stdin(std::process::Stdio::from(dev_null))
.stdout(std::process::Stdio::from(dev_null_out.try_clone().unwrap()))
.stderr(std::process::Stdio::from(dev_null_out))
.process_group(0) .spawn()
.map_err(|e| Error::Internal(format!("Failed to spawn daemon: {}", e)))?;
}
#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
const DETACHED_PROCESS: u32 = 0x00000008;
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
std::process::Command::new(&exe_path)
.arg("daemon")
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
.spawn()
.map_err(|e| Error::Internal(format!("Failed to spawn daemon: {}", e)))?;
}
let deadline = std::time::Instant::now() + Duration::from_secs(SPAWN_TIMEOUT_SECS);
loop {
if std::time::Instant::now() >= deadline {
return Err(Error::DaemonSpawnTimeout(SPAWN_TIMEOUT_SECS));
}
tokio::time::sleep(Duration::from_millis(50)).await;
#[cfg(unix)]
if !paths::socket_path().exists() {
continue;
}
match transport::connect().await {
Ok(_) => {
tracing::debug!("Daemon started successfully");
return Ok(());
}
Err(_) => continue,
}
}
}