use std::process::{Command, Stdio};
use std::fs;
use std::path::PathBuf;
use crate::cli::{DaemonArgs, DaemonAction};
fn pid_path() -> PathBuf {
std::env::temp_dir().join("nomad-daemon.pid")
}
fn daemon_binary() -> PathBuf {
let candidates = [
PathBuf::from("/usr/local/bin/nomad-daemon"),
PathBuf::from("/usr/bin/nomad-daemon"),
std::env::current_exe()
.ok()
.and_then(|p| p.parent().map(|d| d.join("nomad-daemon")))
.unwrap_or_default(),
];
for c in &candidates {
if c.exists() {
return c.clone();
}
}
PathBuf::from("nomad-daemon")
}
pub fn run(args: DaemonArgs) -> Result<(), Box<dyn std::error::Error>> {
match args.action {
DaemonAction::Start { port, cache_cap, parse_concurrency } => cmd_start(port, cache_cap, parse_concurrency),
DaemonAction::Stop => cmd_stop(),
DaemonAction::Status => cmd_status(),
DaemonAction::Restart { port } => {
cmd_stop().ok();
std::thread::sleep(std::time::Duration::from_secs(1));
cmd_start(port, 1, 2)
}
}
}
fn cmd_start(port: u16, cache_cap: usize, parse_concurrency: usize) -> Result<(), Box<dyn std::error::Error>> {
if is_running() {
println!("nomad-daemon is already running");
return Ok(());
}
let binary = daemon_binary();
if !binary.exists() {
println!("Building nomad-daemon...");
let status = Command::new("cargo")
.args(["build", "--release", "-p", "nomad-daemon"])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()?;
if !status.success() {
return Err("failed to build nomad-daemon".into());
}
let target = PathBuf::from("target/release/nomad-daemon");
if target.exists() {
fs::copy(&target, &binary).ok();
}
}
let child = Command::new(&binary)
.env("PORT", port.to_string())
.env("MALLOC_ARENA_MAX", "1")
.env("NOMAD_CACHE_CAP", cache_cap.to_string())
.env("NOMAD_PARSE_CONCURRENCY", parse_concurrency.to_string())
.env("NOMAD_API_AUTH", "disabled")
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()?;
let pid = child.id();
fs::write(pid_path(), pid.to_string())?;
for _ in 0..20 {
if crate::client::daemon_alive(port) {
println!("nomad-daemon started (PID {pid}, port {port})");
return Ok(());
}
std::thread::sleep(std::time::Duration::from_millis(250));
}
println!("Started but waiting for readiness...");
Ok(())
}
fn cmd_stop() -> Result<(), Box<dyn std::error::Error>> {
let pid_path = pid_path();
if !pid_path.exists() {
#[cfg(unix)]
{
let _ = Command::new("pkill")
.args(["-x", "nomad-daemon"])
.stdout(Stdio::null())
.status();
}
#[cfg(windows)]
{
let _ = Command::new("taskkill")
.args(["/f", "/im", "nomad-daemon.exe"])
.stdout(Stdio::null())
.status();
}
println!("nomad-daemon stopped");
return Ok(());
}
let pid_str = fs::read_to_string(&pid_path)?;
let pid: u32 = pid_str.trim().parse()?;
#[cfg(unix)]
{
use std::process;
let rc = Command::new("kill")
.args(["-TERM", &pid.to_string()])
.stdout(Stdio::null())
.status()?;
if rc.success() {
for _ in 0..10 {
if process::Command::new("kill")
.args(["-0", &pid.to_string()])
.stdout(Stdio::null())
.status()
.is_err()
{
break;
}
std::thread::sleep(std::time::Duration::from_millis(200));
}
}
}
#[cfg(windows)]
{
let _ = Command::new("taskkill")
.args(["/pid", &pid.to_string()])
.stdout(Stdio::null())
.status();
}
fs::remove_file(&pid_path).ok();
println!("nomad-daemon stopped");
Ok(())
}
fn cmd_status() -> Result<(), Box<dyn std::error::Error>> {
if is_running() {
for port in &[8000, 8001, 8080] {
if crate::client::daemon_alive(*port) {
crate::client::health(*port, false)?;
return Ok(());
}
}
println!("nomad-daemon: PID EXISTS but not responding");
} else {
println!("nomad-daemon: STOPPED");
}
Ok(())
}
fn is_running() -> bool {
let pid_path = pid_path();
if !pid_path.exists() {
return false;
}
let pid_str = match fs::read_to_string(&pid_path) {
Ok(s) => s,
Err(_) => return false,
};
let pid: u32 = match pid_str.trim().parse() {
Ok(p) => p,
Err(_) => return false,
};
#[cfg(unix)]
{
std::process::Command::new("kill")
.args(["-0", &pid.to_string()])
.stdout(Stdio::null())
.status()
.is_ok()
}
#[cfg(windows)]
{
std::process::Command::new("tasklist")
.args(["/fi", &format!("PID eq {}", pid)])
.stdout(Stdio::null())
.status()
.is_ok()
}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
{
false
}
}