Skip to main content
Glama

CodeGraph CLI MCP Server

by Jakedismo
pid.rs4.65 kB
// ABOUTME: PID file management for daemon lifecycle // ABOUTME: Handles writing, reading, and cleaning up PID files use anyhow::{Context, Result}; use std::fs; use std::path::{Path, PathBuf}; use tracing::{debug, info, warn}; /// PID file manager for daemon processes pub struct PidFile { path: PathBuf, } impl PidFile { /// Create a new PID file manager with the given path pub fn new(path: impl Into<PathBuf>) -> Self { Self { path: path.into() } } /// Create a PID file manager with the default path pub fn default_path(project_root: &Path) -> PathBuf { project_root.join(".codegraph").join("daemon.pid") } /// Write the current process ID to the PID file pub fn write(&self) -> Result<()> { let pid = std::process::id(); // Ensure parent directory exists if let Some(parent) = self.path.parent() { fs::create_dir_all(parent) .with_context(|| format!("Failed to create PID file directory: {:?}", parent))?; } fs::write(&self.path, pid.to_string()) .with_context(|| format!("Failed to write PID file: {:?}", self.path))?; info!("PID file written: {:?} (PID: {})", self.path, pid); Ok(()) } /// Read the PID from the file pub fn read(&self) -> Result<Option<u32>> { if !self.path.exists() { debug!("PID file does not exist: {:?}", self.path); return Ok(None); } let content = fs::read_to_string(&self.path) .with_context(|| format!("Failed to read PID file: {:?}", self.path))?; let pid: u32 = content .trim() .parse() .with_context(|| format!("Invalid PID in file: {:?}", self.path))?; Ok(Some(pid)) } /// Remove the PID file pub fn remove(&self) -> Result<()> { if self.path.exists() { fs::remove_file(&self.path) .with_context(|| format!("Failed to remove PID file: {:?}", self.path))?; info!("PID file removed: {:?}", self.path); } Ok(()) } /// Check if a process with the stored PID is running pub fn is_process_running(&self) -> Result<bool> { match self.read()? { Some(pid) => Ok(is_pid_running(pid)), None => Ok(false), } } /// Get the path to the PID file pub fn path(&self) -> &Path { &self.path } /// Clean up stale PID file if process is not running pub fn cleanup_stale(&self) -> Result<bool> { if let Some(pid) = self.read()? { if !is_pid_running(pid) { warn!("Removing stale PID file for non-running process {}", pid); self.remove()?; return Ok(true); } } Ok(false) } } /// Check if a process with the given PID is running #[cfg(unix)] fn is_pid_running(pid: u32) -> bool { // Use kill -0 via shell to check if process exists std::process::Command::new("kill") .args(["-0", &pid.to_string()]) .stdout(std::process::Stdio::null()) .stderr(std::process::Stdio::null()) .status() .map(|s| s.success()) .unwrap_or(false) } #[cfg(not(unix))] fn is_pid_running(_pid: u32) -> bool { // On non-Unix systems, assume process is running if we can't check true } impl Drop for PidFile { fn drop(&mut self) { // Only remove PID file if it contains our PID if let Ok(Some(stored_pid)) = self.read() { if stored_pid == std::process::id() { let _ = self.remove(); } } } } #[cfg(test)] mod tests { use super::*; use tempfile::TempDir; #[test] fn test_pid_file_write_and_read() { let temp_dir = TempDir::new().unwrap(); let pid_path = temp_dir.path().join("test.pid"); let pid_file = PidFile::new(&pid_path); pid_file.write().unwrap(); let read_pid = pid_file.read().unwrap(); assert_eq!(read_pid, Some(std::process::id())); } #[test] fn test_pid_file_remove() { let temp_dir = TempDir::new().unwrap(); let pid_path = temp_dir.path().join("test.pid"); let pid_file = PidFile::new(&pid_path); pid_file.write().unwrap(); assert!(pid_path.exists()); pid_file.remove().unwrap(); assert!(!pid_path.exists()); } #[test] fn test_pid_file_nonexistent_read() { let pid_file = PidFile::new("/nonexistent/path/test.pid"); let result = pid_file.read().unwrap(); assert_eq!(result, None); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Jakedismo/codegraph-rust'

If you have feedback or need assistance with the MCP directory API, please join our Discord server