use crate::cli::output::OutputConfig;
use anyhow::{Context, Result};
use std::path::PathBuf;
pub fn pid_file_path() -> Result<PathBuf> {
let dir = trusty_common::resolve_data_dir("trusty-memory")
.context("resolving trusty-memory data dir for pid file")?;
Ok(dir.join("trusty-memory.pid"))
}
pub fn write_pid_file(pid: u32) -> Result<()> {
let path = pid_file_path()?;
std::fs::write(&path, pid.to_string())
.with_context(|| format!("writing pid file at {}", path.display()))?;
Ok(())
}
pub fn read_pid_file() -> Option<u32> {
let path = pid_file_path().ok()?;
let contents = std::fs::read_to_string(&path).ok()?;
contents.trim().parse::<u32>().ok()
}
pub fn remove_pid_file() -> Result<()> {
let path = pid_file_path()?;
match std::fs::remove_file(&path) {
Ok(()) => Ok(()),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
Err(e) => Err(e).with_context(|| format!("removing pid file at {}", path.display())),
}
}
pub async fn handle(_out: &OutputConfig) -> Result<()> {
let Some(pid) = read_pid_file() else {
println!("No daemon running (no PID file).");
return Ok(());
};
println!("Stopping daemon (PID {pid})…");
let status = std::process::Command::new("kill")
.arg("-TERM")
.arg(pid.to_string())
.status();
match status {
Ok(s) if s.success() => {
for _ in 0..50 {
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
let cleared = trusty_common::read_daemon_addr("trusty-memory")
.ok()
.flatten()
.map(|s| s.is_empty())
.unwrap_or(true);
if cleared {
println!("Daemon stopped.");
let _ = remove_pid_file();
return Ok(());
}
}
println!("Daemon may still be shutting down (timeout after 5 s).");
}
_ => {
println!("Failed to send SIGTERM (process may already be gone).");
let _ = remove_pid_file();
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pid_file_round_trips() {
let prior = read_pid_file();
write_pid_file(424242).unwrap();
assert_eq!(read_pid_file(), Some(424242));
match prior {
Some(p) => write_pid_file(p).unwrap(),
None => remove_pid_file().unwrap(),
}
}
}