use std::io;
use std::process::Command;
use std::time::{Duration, Instant};
use tracing::{debug, info};
use super::discovery::{self, DaemonInfo};
const SPAWN_TIMEOUT: Duration = Duration::from_secs(10);
const POLL_INTERVAL: Duration = Duration::from_millis(100);
pub fn ensure_daemon(port: u16) -> io::Result<DaemonInfo> {
if let Some(info) = discovery::discover() {
debug!(endpoint = %info.hyperd_endpoint, "daemon already running");
return Ok(info);
}
info!("no running daemon detected, spawning one");
spawn_detached(port)?;
wait_for_daemon()
}
fn spawn_detached(port: u16) -> io::Result<()> {
let exe = std::env::current_exe()?;
let port_str = port.to_string();
let mut cmd = Command::new(&exe);
cmd.arg("daemon").arg("--port").arg(&port_str);
cmd.stdin(std::process::Stdio::null());
cmd.stdout(std::process::Stdio::null());
cmd.stderr(std::process::Stdio::null());
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
unsafe {
cmd.pre_exec(|| {
libc::setsid();
Ok(())
});
}
}
#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
const DETACHED_PROCESS: u32 = 0x0000_0008;
cmd.creation_flags(CREATE_NO_WINDOW | DETACHED_PROCESS);
}
let child = cmd.spawn()?;
info!(pid = child.id(), "daemon process spawned");
Ok(())
}
fn wait_for_daemon() -> io::Result<DaemonInfo> {
let start = Instant::now();
loop {
if let Some(info) = discovery::discover() {
info!(endpoint = %info.hyperd_endpoint, "daemon is ready");
return Ok(info);
}
if start.elapsed() >= SPAWN_TIMEOUT {
return Err(io::Error::new(
io::ErrorKind::TimedOut,
format!(
"daemon did not become ready within {} seconds",
SPAWN_TIMEOUT.as_secs()
),
));
}
std::thread::sleep(POLL_INTERVAL);
}
}