aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml1
-rw-r--r--src/main.rs198
3 files changed, 100 insertions, 106 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 25252ed..31d29b6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -188,7 +188,6 @@ dependencies = [
188 "assert_cmd", 188 "assert_cmd",
189 "clap", 189 "clap",
190 "ctrlc", 190 "ctrlc",
191 "glob",
192 "notify", 191 "notify",
193 "predicates", 192 "predicates",
194 "tempfile", 193 "tempfile",
@@ -267,12 +266,6 @@ dependencies = [
267] 266]
268 267
269[[package]] 268[[package]]
270name = "glob"
271version = "0.3.2"
272source = "registry+https://github.com/rust-lang/crates.io-index"
273checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
274
275[[package]]
276name = "heck" 269name = "heck"
277version = "0.5.0" 270version = "0.5.0"
278source = "registry+https://github.com/rust-lang/crates.io-index" 271source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index ee3e226..0af2bf8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,7 +7,6 @@ edition = "2024"
7anyhow = "1.0.98" 7anyhow = "1.0.98"
8clap = { version = "4.5.40", features = ["derive"] } 8clap = { version = "4.5.40", features = ["derive"] }
9ctrlc = "3.4.7" 9ctrlc = "3.4.7"
10glob = "0.3.2"
11notify = "8.0.0" 10notify = "8.0.0"
12tracing = "0.1.41" 11tracing = "0.1.41"
13tracing-subscriber = "0.3.19" 12tracing-subscriber = "0.3.19"
diff --git a/src/main.rs b/src/main.rs
index 09000ff..37a7cdb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,7 +7,6 @@ use std::time::Duration;
7use std::path::Path; 7use std::path::Path;
8use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; 8use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
9use std::sync::mpsc::channel; 9use std::sync::mpsc::channel;
10use glob::glob;
11use anyhow::{Result, Context}; 10use anyhow::{Result, Context};
12 11
13#[derive(Parser)] 12#[derive(Parser)]
@@ -524,56 +523,50 @@ fn list_daemons(quiet: bool) -> Result<()> {
524 let mut found_any = false; 523 let mut found_any = false;
525 524
526 // Find all .pid files in current directory 525 // Find all .pid files in current directory
527 for entry in glob("*.pid")? { 526 for entry in find_pid_files()? {
528 match entry { 527 found_any = true;
529 Ok(path) => { 528 let path = entry.path();
530 found_any = true; 529 let path_str = path.to_string_lossy();
531 let path_str = path.to_string_lossy(); 530
532 531 // Extract ID from filename (remove .pid extension)
533 // Extract ID from filename (remove .pid extension) 532 let id = path_str.strip_suffix(".pid").unwrap_or(&path_str);
534 let id = path_str.strip_suffix(".pid").unwrap_or(&path_str); 533
535 534 // Read PID from file
536 // Read PID from file 535 match std::fs::read_to_string(&path) {
537 match std::fs::read_to_string(&path) { 536 Ok(contents) => {
538 Ok(contents) => { 537 let pid_str = contents.trim();
539 let pid_str = contents.trim(); 538 match pid_str.parse::<u32>() {
540 match pid_str.parse::<u32>() { 539 Ok(pid) => {
541 Ok(pid) => { 540 let status = if is_process_running_by_pid(pid) {
542 let status = if is_process_running_by_pid(pid) { 541 "RUNNING"
543 "RUNNING" 542 } else {
544 } else { 543 "DEAD"
545 "DEAD" 544 };
546 }; 545
547 546 if quiet {
548 if quiet { 547 println!("{}:{}:{}", id, pid, status);
549 println!("{}:{}:{}", id, pid, status); 548 } else {
550 } else { 549 // Try to read command from a hypothetical command file
551 // Try to read command from a hypothetical command file 550 // For now, we'll just show "N/A" since we don't store the command
552 // For now, we'll just show "N/A" since we don't store the command 551 let command = "N/A";
553 let command = "N/A"; 552 println!("{:<20} {:<8} {:<10} {}", id, pid, status, command);
554 println!("{:<20} {:<8} {:<10} {}", id, pid, status, command);
555 }
556 }
557 Err(_) => {
558 if quiet {
559 println!("{}:INVALID:ERROR", id);
560 } else {
561 println!("{:<20} {:<8} {:<10} {}", id, "INVALID", "ERROR", "Invalid PID file");
562 }
563 }
564 } 553 }
565 } 554 }
566 Err(e) => { 555 Err(_) => {
567 if quiet { 556 if quiet {
568 println!("{}:ERROR:ERROR", id); 557 println!("{}:INVALID:ERROR", id);
569 } else { 558 } else {
570 println!("{:<20} {:<8} {:<10} {}", id, "ERROR", "ERROR", format!("Cannot read: {}", e)); 559 println!("{:<20} {:<8} {:<10} {}", id, "INVALID", "ERROR", "Invalid PID file");
571 } 560 }
572 } 561 }
573 } 562 }
574 } 563 }
575 Err(e) => { 564 Err(e) => {
576 tracing::warn!("Error reading glob entry: {}", e); 565 if quiet {
566 println!("{}:ERROR:ERROR", id);
567 } else {
568 println!("{:<20} {:<8} {:<10} {}", id, "ERROR", "ERROR", format!("Cannot read: {}", e));
569 }
577 } 570 }
578 } 571 }
579 } 572 }
@@ -648,78 +641,72 @@ fn clean_orphaned_files() -> Result<()> {
648 let mut cleaned_count = 0; 641 let mut cleaned_count = 0;
649 642
650 // Find all .pid files in current directory 643 // Find all .pid files in current directory
651 for entry in glob("*.pid")? { 644 for entry in find_pid_files()? {
652 match entry { 645 let path = entry.path();
653 Ok(path) => { 646 let path_str = path.to_string_lossy();
654 let path_str = path.to_string_lossy(); 647 let id = path_str.strip_suffix(".pid").unwrap_or(&path_str);
655 let id = path_str.strip_suffix(".pid").unwrap_or(&path_str); 648
656 649 // Read PID from file
657 // Read PID from file 650 match std::fs::read_to_string(&path) {
658 match std::fs::read_to_string(&path) { 651 Ok(contents) => {
659 Ok(contents) => { 652 let pid_str = contents.trim();
660 let pid_str = contents.trim(); 653 match pid_str.parse::<u32>() {
661 match pid_str.parse::<u32>() { 654 Ok(pid) => {
662 Ok(pid) => { 655 // Check if process is still running
663 // Check if process is still running 656 if !is_process_running_by_pid(pid) {
664 if !is_process_running_by_pid(pid) { 657 println!("Cleaning up orphaned files for '{}' (PID: {})", id, pid);
665 println!("Cleaning up orphaned files for '{}' (PID: {})", id, pid); 658
666 659 // Remove PID file
667 // Remove PID file 660 if let Err(e) = std::fs::remove_file(&path) {
668 if let Err(e) = std::fs::remove_file(&path) { 661 tracing::warn!("Failed to remove {}: {}", path_str, e);
669 tracing::warn!("Failed to remove {}: {}", path_str, e); 662 } else {
670 } else { 663 tracing::info!("Removed {}", path_str);
671 tracing::info!("Removed {}", path_str); 664 }
672 } 665
673 666 // Remove stdout file if it exists
674 // Remove stdout file if it exists 667 let stdout_file = format!("{}.stdout", id);
675 let stdout_file = format!("{}.stdout", id); 668 if Path::new(&stdout_file).exists() {
676 if Path::new(&stdout_file).exists() { 669 if let Err(e) = std::fs::remove_file(&stdout_file) {
677 if let Err(e) = std::fs::remove_file(&stdout_file) { 670 tracing::warn!("Failed to remove {}: {}", stdout_file, e);
678 tracing::warn!("Failed to remove {}: {}", stdout_file, e);
679 } else {
680 tracing::info!("Removed {}", stdout_file);
681 }
682 }
683
684 // Remove stderr file if it exists
685 let stderr_file = format!("{}.stderr", id);
686 if Path::new(&stderr_file).exists() {
687 if let Err(e) = std::fs::remove_file(&stderr_file) {
688 tracing::warn!("Failed to remove {}: {}", stderr_file, e);
689 } else {
690 tracing::info!("Removed {}", stderr_file);
691 }
692 }
693
694 cleaned_count += 1;
695 } else { 671 } else {
696 tracing::info!("Skipping '{}' (PID: {}) - process is still running", id, pid); 672 tracing::info!("Removed {}", stdout_file);
697 } 673 }
698 } 674 }
699 Err(_) => { 675
700 println!("Cleaning up invalid PID file: {}", path_str); 676 // Remove stderr file if it exists
701 if let Err(e) = std::fs::remove_file(&path) { 677 let stderr_file = format!("{}.stderr", id);
702 tracing::warn!("Failed to remove invalid PID file {}: {}", path_str, e); 678 if Path::new(&stderr_file).exists() {
679 if let Err(e) = std::fs::remove_file(&stderr_file) {
680 tracing::warn!("Failed to remove {}: {}", stderr_file, e);
703 } else { 681 } else {
704 tracing::info!("Removed invalid PID file {}", path_str); 682 tracing::info!("Removed {}", stderr_file);
705 cleaned_count += 1;
706 } 683 }
707 } 684 }
685
686 cleaned_count += 1;
687 } else {
688 tracing::info!("Skipping '{}' (PID: {}) - process is still running", id, pid);
708 } 689 }
709 } 690 }
710 Err(_) => { 691 Err(_) => {
711 println!("Cleaning up unreadable PID file: {}", path_str); 692 println!("Cleaning up invalid PID file: {}", path_str);
712 if let Err(e) = std::fs::remove_file(&path) { 693 if let Err(e) = std::fs::remove_file(&path) {
713 tracing::warn!("Failed to remove unreadable PID file {}: {}", path_str, e); 694 tracing::warn!("Failed to remove invalid PID file {}: {}", path_str, e);
714 } else { 695 } else {
715 tracing::info!("Removed unreadable PID file {}", path_str); 696 tracing::info!("Removed invalid PID file {}", path_str);
716 cleaned_count += 1; 697 cleaned_count += 1;
717 } 698 }
718 } 699 }
719 } 700 }
720 } 701 }
721 Err(e) => { 702 Err(_) => {
722 tracing::warn!("Error reading glob entry: {}", e); 703 println!("Cleaning up unreadable PID file: {}", path_str);
704 if let Err(e) = std::fs::remove_file(&path) {
705 tracing::warn!("Failed to remove unreadable PID file {}: {}", path_str, e);
706 } else {
707 tracing::info!("Removed unreadable PID file {}", path_str);
708 cleaned_count += 1;
709 }
723 } 710 }
724 } 711 }
725 } 712 }
@@ -963,3 +950,18 @@ demon list --quiet > process_status.txt
963 950
964This tool is designed for Linux environments and provides a simple interface for managing background processes with persistent logging."#); 951This tool is designed for Linux environments and provides a simple interface for managing background processes with persistent logging."#);
965} 952}
953
954fn find_pid_files() -> Result<Vec<std::fs::DirEntry>> {
955 let entries = std::fs::read_dir(".")?
956 .filter_map(|entry| {
957 entry.ok().and_then(|e| {
958 e.path()
959 .extension()
960 .and_then(|ext| ext.to_str())
961 .filter(|ext| *ext == "pid")
962 .map(|_| e)
963 })
964 })
965 .collect();
966 Ok(entries)
967}