aiclient_api/daemon/
mod.rs1pub mod control;
2
3use anyhow::{Context, Result};
4use std::path::Path;
5
6pub fn read_pid() -> Result<Option<u32>> {
8 let pid_path = crate::util::xdg::pid_path();
9 if !pid_path.exists() {
10 return Ok(None);
11 }
12 let content = std::fs::read_to_string(&pid_path)?;
13 let pid: u32 = content.trim().parse()?;
14
15 let alive = unsafe { libc::kill(pid as i32, 0) == 0 };
16 if alive {
17 Ok(Some(pid))
18 } else {
19 let _ = std::fs::remove_file(&pid_path);
20 Ok(None)
21 }
22}
23
24pub fn write_pid(pid: u32) -> Result<()> {
26 let pid_path = crate::util::xdg::pid_path();
27 if let Some(parent) = pid_path.parent() {
28 std::fs::create_dir_all(parent)?;
29 }
30 std::fs::write(&pid_path, pid.to_string())?;
31 Ok(())
32}
33
34pub fn remove_pid() -> Result<()> {
36 let pid_path = crate::util::xdg::pid_path();
37 if pid_path.exists() {
38 std::fs::remove_file(&pid_path)?;
39 }
40 Ok(())
41}
42
43pub fn stop_daemon() -> Result<()> {
45 match read_pid()? {
46 Some(pid) => {
47 eprintln!("Stopping daemon (pid {})...", pid);
48 unsafe {
49 libc::kill(pid as i32, libc::SIGTERM);
50 }
51
52 for _ in 0..100 {
53 std::thread::sleep(std::time::Duration::from_millis(100));
54 let alive = unsafe { libc::kill(pid as i32, 0) == 0 };
55 if !alive {
56 remove_pid()?;
57 eprintln!("Daemon stopped.");
58 return Ok(());
59 }
60 }
61
62 eprintln!("Daemon didn't stop gracefully, sending SIGKILL...");
63 unsafe {
64 libc::kill(pid as i32, libc::SIGKILL);
65 }
66 std::thread::sleep(std::time::Duration::from_millis(500));
67 remove_pid()?;
68 eprintln!("Daemon killed.");
69 Ok(())
70 }
71 None => {
72 eprintln!("Daemon is not running.");
73 Ok(())
74 }
75 }
76}
77
78pub fn daemonize(log_file: &Path) -> Result<()> {
80 use daemonize::Daemonize;
81
82 let pid_path = crate::util::xdg::pid_path();
83 let runtime_dir = crate::util::xdg::runtime_dir();
84 std::fs::create_dir_all(&runtime_dir)?;
85
86 if let Some(parent) = log_file.parent() {
87 std::fs::create_dir_all(parent)?;
88 }
89
90 let stdout = std::fs::OpenOptions::new()
91 .create(true)
92 .append(true)
93 .open(log_file)?;
94 let stderr = stdout.try_clone()?;
95
96 let daemon = Daemonize::new()
97 .pid_file(&pid_path)
98 .working_directory(".")
99 .stdout(stdout)
100 .stderr(stderr);
101
102 daemon.start().context("Failed to daemonize")?;
103
104 Ok(())
105}