greppy/daemon/
process.rs

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