greppy/daemon/
process.rs

1use crate::core::config::Config;
2use crate::core::error::{Error, Result};
3use std::process::{Command, Stdio};
4
5#[cfg(windows)]
6use std::os::windows::process::CommandExt;
7
8/// Check if daemon is running
9pub fn is_running() -> Result<bool> {
10    let pid_path = Config::pid_path()?;
11    if !pid_path.exists() {
12        return Ok(false);
13    }
14
15    let pid_str = std::fs::read_to_string(&pid_path)?;
16    let pid: u32 = pid_str.trim().parse().unwrap_or(0);
17
18    if pid == 0 {
19        return Ok(false);
20    }
21
22    // Check if process exists
23    #[cfg(unix)]
24    {
25        let result = unsafe { libc::kill(pid as i32, 0) };
26        Ok(result == 0)
27    }
28
29    #[cfg(windows)]
30    {
31        // On Windows, use tasklist to check if process exists
32        let output = Command::new("tasklist")
33            .args(["/FI", &format!("PID eq {}", pid), "/NH"])
34            .output();
35
36        match output {
37            Ok(out) => {
38                let stdout = String::from_utf8_lossy(&out.stdout);
39                Ok(stdout.contains(&pid.to_string()))
40            }
41            Err(_) => Ok(false),
42        }
43    }
44}
45
46/// Get daemon PID
47pub fn get_pid() -> Result<Option<u32>> {
48    let pid_path = Config::pid_path()?;
49    if !pid_path.exists() {
50        return Ok(None);
51    }
52
53    let pid_str = std::fs::read_to_string(&pid_path)?;
54    let pid: u32 = pid_str.trim().parse().unwrap_or(0);
55
56    if pid == 0 {
57        return Ok(None);
58    }
59
60    Ok(Some(pid))
61}
62
63/// Start daemon in background
64pub fn start_daemon() -> Result<u32> {
65    if is_running()? {
66        if let Some(pid) = get_pid()? {
67            return Err(Error::DaemonError {
68                message: format!("Daemon already running with PID {}", pid),
69            });
70        }
71    }
72
73    Config::ensure_home()?;
74
75    // Get current executable path
76    let exe = std::env::current_exe()?;
77
78    // Spawn daemon process
79    #[cfg(unix)]
80    let child = Command::new(&exe)
81        .arg("__daemon")
82        .stdin(Stdio::null())
83        .stdout(Stdio::null())
84        .stderr(Stdio::null())
85        .spawn()?;
86
87    #[cfg(windows)]
88    let child = Command::new(&exe)
89        .arg("__daemon")
90        .creation_flags(0x00000008) // DETACHED_PROCESS
91        .stdin(Stdio::null())
92        .stdout(Stdio::null())
93        .stderr(Stdio::null())
94        .spawn()?;
95
96    let pid = child.id();
97
98    // Write PID file
99    let pid_path = Config::pid_path()?;
100    std::fs::write(&pid_path, pid.to_string())?;
101
102    // Wait a moment for daemon to start
103    std::thread::sleep(std::time::Duration::from_millis(100));
104
105    Ok(pid)
106}
107
108/// Stop daemon
109pub fn stop_daemon() -> Result<bool> {
110    let pid = match get_pid()? {
111        Some(p) => p,
112        None => return Ok(false),
113    };
114
115    #[cfg(unix)]
116    {
117        unsafe {
118            libc::kill(pid as i32, libc::SIGTERM);
119        }
120    }
121
122    #[cfg(windows)]
123    {
124        // On Windows, use taskkill
125        let _ = Command::new("taskkill")
126            .args(["/PID", &pid.to_string(), "/F"])
127            .output();
128    }
129
130    // Clean up files
131    let pid_path = Config::pid_path()?;
132    if pid_path.exists() {
133        let _ = std::fs::remove_file(&pid_path);
134    }
135
136    #[cfg(unix)]
137    {
138        let socket_path = Config::socket_path()?;
139        if socket_path.exists() {
140            let _ = std::fs::remove_file(&socket_path);
141        }
142    }
143
144    #[cfg(windows)]
145    {
146        let port_path = Config::port_path()?;
147        if port_path.exists() {
148            let _ = std::fs::remove_file(&port_path);
149        }
150    }
151
152    Ok(true)
153}