aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs130
1 files changed, 98 insertions, 32 deletions
diff --git a/src/main.rs b/src/main.rs
index 4ab1dd9..246af67 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,6 +9,36 @@ use std::sync::mpsc::channel;
9use std::thread; 9use std::thread;
10use std::time::Duration; 10use std::time::Duration;
11 11
12/// Error types for reading PID files
13#[derive(Debug)]
14pub enum PidFileReadError {
15 /// The PID file does not exist
16 FileNotFound,
17 /// The PID file exists but has invalid content
18 FileInvalid(String),
19 /// IO error occurred while reading
20 IoError(std::io::Error),
21}
22
23impl std::fmt::Display for PidFileReadError {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self {
26 PidFileReadError::FileNotFound => write!(f, "PID file not found"),
27 PidFileReadError::FileInvalid(reason) => write!(f, "PID file invalid: {}", reason),
28 PidFileReadError::IoError(err) => write!(f, "IO error reading PID file: {}", err),
29 }
30 }
31}
32
33impl std::error::Error for PidFileReadError {
34 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
35 match self {
36 PidFileReadError::IoError(err) => Some(err),
37 _ => None,
38 }
39 }
40}
41
12/// Represents the contents of a PID file 42/// Represents the contents of a PID file
13#[derive(Debug, Clone)] 43#[derive(Debug, Clone)]
14struct PidFile { 44struct PidFile {
@@ -35,23 +65,34 @@ impl PidFile {
35 } 65 }
36 66
37 /// Read PID file from a file 67 /// Read PID file from a file
38 fn read_from_file<P: AsRef<Path>>(path: P) -> Result<Self> { 68 fn read_from_file<P: AsRef<Path>>(path: P) -> Result<Self, PidFileReadError> {
39 let contents = std::fs::read_to_string(path)?; 69 let contents = match std::fs::read_to_string(&path) {
70 Ok(contents) => contents,
71 Err(err) => {
72 return if err.kind() == std::io::ErrorKind::NotFound {
73 Err(PidFileReadError::FileNotFound)
74 } else {
75 Err(PidFileReadError::IoError(err))
76 };
77 }
78 };
79
40 let lines: Vec<&str> = contents.lines().collect(); 80 let lines: Vec<&str> = contents.lines().collect();
41 81
42 if lines.is_empty() { 82 if lines.is_empty() {
43 return Err(anyhow::anyhow!("PID file is empty")); 83 return Err(PidFileReadError::FileInvalid("PID file is empty".to_string()));
44 } 84 }
45 85
46 let pid = lines[0] 86 let pid = lines[0].trim().parse::<u32>().map_err(|_| {
47 .trim() 87 PidFileReadError::FileInvalid("Invalid PID on first line".to_string())
48 .parse::<u32>() 88 })?;
49 .context("Failed to parse PID from first line")?;
50 89
51 let command: Vec<String> = lines[1..].iter().map(|line| line.to_string()).collect(); 90 let command: Vec<String> = lines[1..].iter().map(|line| line.to_string()).collect();
52 91
53 if command.is_empty() { 92 if command.is_empty() {
54 return Err(anyhow::anyhow!("No command found in PID file")); 93 return Err(PidFileReadError::FileInvalid(
94 "No command found in PID file".to_string(),
95 ));
55 } 96 }
56 97
57 Ok(Self { pid, command }) 98 Ok(Self { pid, command })
@@ -256,14 +297,11 @@ fn run_daemon(id: &str, command: &[String]) -> Result<()> {
256} 297}
257 298
258fn is_process_running(pid_file: &str) -> Result<bool> { 299fn is_process_running(pid_file: &str) -> Result<bool> {
259 // Try to read the PID file
260 if !Path::new(pid_file).exists() {
261 return Ok(false); // No PID file means no running process
262 }
263
264 let pid_file_data = match PidFile::read_from_file(pid_file) { 300 let pid_file_data = match PidFile::read_from_file(pid_file) {
265 Ok(data) => data, 301 Ok(data) => data,
266 Err(_) => return Ok(false), // Invalid PID file 302 Err(PidFileReadError::FileNotFound) => return Ok(false), // No PID file means no running process
303 Err(PidFileReadError::FileInvalid(_)) => return Ok(false), // Invalid PID file means no running process
304 Err(PidFileReadError::IoError(err)) => return Err(err.into()), // Propagate IO errors
267 }; 305 };
268 306
269 // Check if process is still running using kill -0 307 // Check if process is still running using kill -0
@@ -280,15 +318,18 @@ fn stop_daemon(id: &str, timeout: u64) -> Result<()> {
280 // Check if PID file exists and read PID data 318 // Check if PID file exists and read PID data
281 let pid_file_data = match PidFile::read_from_file(&pid_file) { 319 let pid_file_data = match PidFile::read_from_file(&pid_file) {
282 Ok(data) => data, 320 Ok(data) => data,
283 Err(_) => { 321 Err(PidFileReadError::FileNotFound) => {
284 if Path::new(&pid_file).exists() { 322 println!("Process '{}' is not running (no PID file found)", id);
285 println!("Process '{}': invalid PID file, removing it", id); 323 return Ok(());
286 std::fs::remove_file(&pid_file)?; 324 }
287 } else { 325 Err(PidFileReadError::FileInvalid(_)) => {
288 println!("Process '{}' is not running (no PID file found)", id); 326 println!("Process '{}': invalid PID file, removing it", id);
289 } 327 std::fs::remove_file(&pid_file)?;
290 return Ok(()); 328 return Ok(());
291 } 329 }
330 Err(PidFileReadError::IoError(err)) => {
331 return Err(anyhow::anyhow!("Failed to read PID file: {}", err));
332 }
292 }; 333 };
293 334
294 let pid = pid_file_data.pid; 335 let pid = pid_file_data.pid;
@@ -614,13 +655,34 @@ fn list_daemons(quiet: bool) -> Result<()> {
614 println!("{:<20} {:<8} {:<10} {}", id, pid_file_data.pid, status, command); 655 println!("{:<20} {:<8} {:<10} {}", id, pid_file_data.pid, status, command);
615 } 656 }
616 } 657 }
617 Err(_) => { 658 Err(PidFileReadError::FileNotFound) => {
659 // This shouldn't happen since we found the file, but handle gracefully
660 if quiet {
661 println!("{}:NOTFOUND:ERROR", id);
662 } else {
663 println!(
664 "{:<20} {:<8} {:<10} {}",
665 id, "NOTFOUND", "ERROR", "PID file disappeared"
666 );
667 }
668 }
669 Err(PidFileReadError::FileInvalid(reason)) => {
618 if quiet { 670 if quiet {
619 println!("{}:INVALID:ERROR", id); 671 println!("{}:INVALID:ERROR", id);
620 } else { 672 } else {
621 println!( 673 println!(
622 "{:<20} {:<8} {:<10} {}", 674 "{:<20} {:<8} {:<10} {}",
623 id, "INVALID", "ERROR", "Invalid PID file" 675 id, "INVALID", "ERROR", reason
676 );
677 }
678 }
679 Err(PidFileReadError::IoError(_)) => {
680 if quiet {
681 println!("{}:ERROR:ERROR", id);
682 } else {
683 println!(
684 "{:<20} {:<8} {:<10} {}",
685 id, "ERROR", "ERROR", "Cannot read PID file"
624 ); 686 );
625 } 687 }
626 } 688 }
@@ -642,12 +704,6 @@ fn status_daemon(id: &str) -> Result<()> {
642 println!("Daemon: {}", id); 704 println!("Daemon: {}", id);
643 println!("PID file: {}", pid_file); 705 println!("PID file: {}", pid_file);
644 706
645 // Check if PID file exists
646 if !Path::new(&pid_file).exists() {
647 println!("Status: NOT FOUND (no PID file)");
648 return Ok(());
649 }
650
651 // Read PID data from file 707 // Read PID data from file
652 match PidFile::read_from_file(&pid_file) { 708 match PidFile::read_from_file(&pid_file) {
653 Ok(pid_file_data) => { 709 Ok(pid_file_data) => {
@@ -676,8 +732,14 @@ fn status_daemon(id: &str) -> Result<()> {
676 println!("Note: Use 'demon clean' to remove orphaned files"); 732 println!("Note: Use 'demon clean' to remove orphaned files");
677 } 733 }
678 } 734 }
679 Err(e) => { 735 Err(PidFileReadError::FileNotFound) => {
680 println!("Status: ERROR (cannot read PID file: {})", e); 736 println!("Status: NOT FOUND (no PID file)");
737 }
738 Err(PidFileReadError::FileInvalid(reason)) => {
739 println!("Status: ERROR (invalid PID file: {})", reason);
740 }
741 Err(PidFileReadError::IoError(err)) => {
742 println!("Status: ERROR (cannot read PID file: {})", err);
681 } 743 }
682 } 744 }
683 745
@@ -741,7 +803,11 @@ fn clean_orphaned_files() -> Result<()> {
741 ); 803 );
742 } 804 }
743 } 805 }
744 Err(_) => { 806 Err(PidFileReadError::FileNotFound) => {
807 // This shouldn't happen since we found the file, but handle gracefully
808 tracing::warn!("PID file {} disappeared during processing", path_str);
809 }
810 Err(PidFileReadError::FileInvalid(_)) | Err(PidFileReadError::IoError(_)) => {
745 println!("Cleaning up invalid PID file: {}", path_str); 811 println!("Cleaning up invalid PID file: {}", path_str);
746 if let Err(e) = std::fs::remove_file(&path) { 812 if let Err(e) = std::fs::remove_file(&path) {
747 tracing::warn!("Failed to remove invalid PID file {}: {}", path_str, e); 813 tracing::warn!("Failed to remove invalid PID file {}: {}", path_str, e);