diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 35 |
1 files changed, 18 insertions, 17 deletions
diff --git a/src/main.rs b/src/main.rs index e0545e6..12a08b6 100644 --- a/src/main.rs +++ b/src/main.rs | |||
| @@ -8,6 +8,7 @@ use std::path::Path; | |||
| 8 | use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; | 8 | use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; |
| 9 | use std::sync::mpsc::channel; | 9 | use std::sync::mpsc::channel; |
| 10 | use glob::glob; | 10 | use glob::glob; |
| 11 | use anyhow::{Result, Context}; | ||
| 11 | 12 | ||
| 12 | #[derive(Parser)] | 13 | #[derive(Parser)] |
| 13 | #[command(name = "demon")] | 14 | #[command(name = "demon")] |
| @@ -114,11 +115,11 @@ fn main() { | |||
| 114 | } | 115 | } |
| 115 | } | 116 | } |
| 116 | 117 | ||
| 117 | fn run_command(command: Commands) -> Result<(), Box<dyn std::error::Error>> { | 118 | fn run_command(command: Commands) -> Result<()> { |
| 118 | match command { | 119 | match command { |
| 119 | Commands::Run(args) => { | 120 | Commands::Run(args) => { |
| 120 | if args.command.is_empty() { | 121 | if args.command.is_empty() { |
| 121 | return Err("Command cannot be empty".into()); | 122 | return Err(anyhow::anyhow!("Command cannot be empty")); |
| 122 | } | 123 | } |
| 123 | run_daemon(&args.id, &args.command) | 124 | run_daemon(&args.id, &args.command) |
| 124 | } | 125 | } |
| @@ -147,14 +148,14 @@ fn run_command(command: Commands) -> Result<(), Box<dyn std::error::Error>> { | |||
| 147 | } | 148 | } |
| 148 | } | 149 | } |
| 149 | 150 | ||
| 150 | fn run_daemon(id: &str, command: &[String]) -> Result<(), Box<dyn std::error::Error>> { | 151 | fn run_daemon(id: &str, command: &[String]) -> Result<()> { |
| 151 | let pid_file = format!("{}.pid", id); | 152 | let pid_file = format!("{}.pid", id); |
| 152 | let stdout_file = format!("{}.stdout", id); | 153 | let stdout_file = format!("{}.stdout", id); |
| 153 | let stderr_file = format!("{}.stderr", id); | 154 | let stderr_file = format!("{}.stderr", id); |
| 154 | 155 | ||
| 155 | // Check if process is already running | 156 | // Check if process is already running |
| 156 | if is_process_running(&pid_file)? { | 157 | if is_process_running(&pid_file)? { |
| 157 | return Err(format!("Process '{}' is already running", id).into()); | 158 | return Err(anyhow::anyhow!("Process '{}' is already running", id)); |
| 158 | } | 159 | } |
| 159 | 160 | ||
| 160 | tracing::info!("Starting daemon '{}' with command: {:?}", id, command); | 161 | tracing::info!("Starting daemon '{}' with command: {:?}", id, command); |
| @@ -177,7 +178,7 @@ fn run_daemon(id: &str, command: &[String]) -> Result<(), Box<dyn std::error::Er | |||
| 177 | .stderr(Stdio::from(stderr_redirect)) | 178 | .stderr(Stdio::from(stderr_redirect)) |
| 178 | .stdin(Stdio::null()) | 179 | .stdin(Stdio::null()) |
| 179 | .spawn() | 180 | .spawn() |
| 180 | .map_err(|e| format!("Failed to start process '{}': {}", program, e))?; | 181 | .with_context(|| format!("Failed to start process '{}' with args {:?}", program, args))?; |
| 181 | 182 | ||
| 182 | // Write PID to file | 183 | // Write PID to file |
| 183 | let mut pid_file_handle = File::create(&pid_file)?; | 184 | let mut pid_file_handle = File::create(&pid_file)?; |
| @@ -191,7 +192,7 @@ fn run_daemon(id: &str, command: &[String]) -> Result<(), Box<dyn std::error::Er | |||
| 191 | Ok(()) | 192 | Ok(()) |
| 192 | } | 193 | } |
| 193 | 194 | ||
| 194 | fn is_process_running(pid_file: &str) -> Result<bool, Box<dyn std::error::Error>> { | 195 | fn is_process_running(pid_file: &str) -> Result<bool> { |
| 195 | // Try to read the PID file | 196 | // Try to read the PID file |
| 196 | let mut file = match File::open(pid_file) { | 197 | let mut file = match File::open(pid_file) { |
| 197 | Ok(f) => f, | 198 | Ok(f) => f, |
| @@ -214,7 +215,7 @@ fn is_process_running(pid_file: &str) -> Result<bool, Box<dyn std::error::Error> | |||
| 214 | Ok(output.status.success()) | 215 | Ok(output.status.success()) |
| 215 | } | 216 | } |
| 216 | 217 | ||
| 217 | fn stop_daemon(id: &str, timeout: u64) -> Result<(), Box<dyn std::error::Error>> { | 218 | fn stop_daemon(id: &str, timeout: u64) -> Result<()> { |
| 218 | let pid_file = format!("{}.pid", id); | 219 | let pid_file = format!("{}.pid", id); |
| 219 | 220 | ||
| 220 | // Check if PID file exists | 221 | // Check if PID file exists |
| @@ -255,7 +256,7 @@ fn stop_daemon(id: &str, timeout: u64) -> Result<(), Box<dyn std::error::Error>> | |||
| 255 | .output()?; | 256 | .output()?; |
| 256 | 257 | ||
| 257 | if !output.status.success() { | 258 | if !output.status.success() { |
| 258 | return Err(format!("Failed to send SIGTERM to PID {}", pid).into()); | 259 | return Err(anyhow::anyhow!("Failed to send SIGTERM to PID {}", pid)); |
| 259 | } | 260 | } |
| 260 | 261 | ||
| 261 | // Wait for the process to terminate | 262 | // Wait for the process to terminate |
| @@ -280,14 +281,14 @@ fn stop_daemon(id: &str, timeout: u64) -> Result<(), Box<dyn std::error::Error>> | |||
| 280 | .output()?; | 281 | .output()?; |
| 281 | 282 | ||
| 282 | if !output.status.success() { | 283 | if !output.status.success() { |
| 283 | return Err(format!("Failed to send SIGKILL to PID {}", pid).into()); | 284 | return Err(anyhow::anyhow!("Failed to send SIGKILL to PID {}", pid)); |
| 284 | } | 285 | } |
| 285 | 286 | ||
| 286 | // Wait a bit more for SIGKILL to take effect | 287 | // Wait a bit more for SIGKILL to take effect |
| 287 | thread::sleep(Duration::from_secs(1)); | 288 | thread::sleep(Duration::from_secs(1)); |
| 288 | 289 | ||
| 289 | if is_process_running_by_pid(pid) { | 290 | if is_process_running_by_pid(pid) { |
| 290 | return Err(format!("Process {} is still running after SIGKILL", pid).into()); | 291 | return Err(anyhow::anyhow!("Process {} is still running after SIGKILL", pid)); |
| 291 | } | 292 | } |
| 292 | 293 | ||
| 293 | println!("Process '{}' (PID: {}) terminated forcefully", id, pid); | 294 | println!("Process '{}' (PID: {}) terminated forcefully", id, pid); |
| @@ -307,7 +308,7 @@ fn is_process_running_by_pid(pid: u32) -> bool { | |||
| 307 | } | 308 | } |
| 308 | } | 309 | } |
| 309 | 310 | ||
| 310 | fn cat_logs(id: &str, show_stdout: bool, show_stderr: bool) -> Result<(), Box<dyn std::error::Error>> { | 311 | fn cat_logs(id: &str, show_stdout: bool, show_stderr: bool) -> Result<()> { |
| 311 | let stdout_file = format!("{}.stdout", id); | 312 | let stdout_file = format!("{}.stdout", id); |
| 312 | let stderr_file = format!("{}.stderr", id); | 313 | let stderr_file = format!("{}.stderr", id); |
| 313 | 314 | ||
| @@ -348,7 +349,7 @@ fn cat_logs(id: &str, show_stdout: bool, show_stderr: bool) -> Result<(), Box<dy | |||
| 348 | Ok(()) | 349 | Ok(()) |
| 349 | } | 350 | } |
| 350 | 351 | ||
| 351 | fn tail_logs(id: &str, show_stdout: bool, show_stderr: bool) -> Result<(), Box<dyn std::error::Error>> { | 352 | fn tail_logs(id: &str, show_stdout: bool, show_stderr: bool) -> Result<()> { |
| 352 | let stdout_file = format!("{}.stdout", id); | 353 | let stdout_file = format!("{}.stdout", id); |
| 353 | let stderr_file = format!("{}.stderr", id); | 354 | let stderr_file = format!("{}.stderr", id); |
| 354 | 355 | ||
| @@ -464,7 +465,7 @@ fn tail_logs(id: &str, show_stdout: bool, show_stderr: bool) -> Result<(), Box<d | |||
| 464 | Ok(()) | 465 | Ok(()) |
| 465 | } | 466 | } |
| 466 | 467 | ||
| 467 | fn read_file_content(file: &mut File) -> Result<String, Box<dyn std::error::Error>> { | 468 | fn read_file_content(file: &mut File) -> Result<String> { |
| 468 | let mut content = String::new(); | 469 | let mut content = String::new(); |
| 469 | file.read_to_string(&mut content)?; | 470 | file.read_to_string(&mut content)?; |
| 470 | Ok(content) | 471 | Ok(content) |
| @@ -474,7 +475,7 @@ fn handle_file_change( | |||
| 474 | file_path: &str, | 475 | file_path: &str, |
| 475 | positions: &mut std::collections::HashMap<String, u64>, | 476 | positions: &mut std::collections::HashMap<String, u64>, |
| 476 | show_headers: bool | 477 | show_headers: bool |
| 477 | ) -> Result<(), Box<dyn std::error::Error>> { | 478 | ) -> Result<()> { |
| 478 | let mut file = File::open(file_path)?; | 479 | let mut file = File::open(file_path)?; |
| 479 | let current_pos = positions.get(file_path).copied().unwrap_or(0); | 480 | let current_pos = positions.get(file_path).copied().unwrap_or(0); |
| 480 | 481 | ||
| @@ -500,7 +501,7 @@ fn handle_file_change( | |||
| 500 | Ok(()) | 501 | Ok(()) |
| 501 | } | 502 | } |
| 502 | 503 | ||
| 503 | fn list_daemons() -> Result<(), Box<dyn std::error::Error>> { | 504 | fn list_daemons() -> Result<()> { |
| 504 | println!("{:<20} {:<8} {:<10} {}", "ID", "PID", "STATUS", "COMMAND"); | 505 | println!("{:<20} {:<8} {:<10} {}", "ID", "PID", "STATUS", "COMMAND"); |
| 505 | println!("{}", "-".repeat(50)); | 506 | println!("{}", "-".repeat(50)); |
| 506 | 507 | ||
| @@ -557,7 +558,7 @@ fn list_daemons() -> Result<(), Box<dyn std::error::Error>> { | |||
| 557 | Ok(()) | 558 | Ok(()) |
| 558 | } | 559 | } |
| 559 | 560 | ||
| 560 | fn status_daemon(id: &str) -> Result<(), Box<dyn std::error::Error>> { | 561 | fn status_daemon(id: &str) -> Result<()> { |
| 561 | let pid_file = format!("{}.pid", id); | 562 | let pid_file = format!("{}.pid", id); |
| 562 | let stdout_file = format!("{}.stdout", id); | 563 | let stdout_file = format!("{}.stdout", id); |
| 563 | let stderr_file = format!("{}.stderr", id); | 564 | let stderr_file = format!("{}.stderr", id); |
| @@ -614,7 +615,7 @@ fn status_daemon(id: &str) -> Result<(), Box<dyn std::error::Error>> { | |||
| 614 | Ok(()) | 615 | Ok(()) |
| 615 | } | 616 | } |
| 616 | 617 | ||
| 617 | fn clean_orphaned_files() -> Result<(), Box<dyn std::error::Error>> { | 618 | fn clean_orphaned_files() -> Result<()> { |
| 618 | tracing::info!("Scanning for orphaned daemon files..."); | 619 | tracing::info!("Scanning for orphaned daemon files..."); |
| 619 | 620 | ||
| 620 | let mut cleaned_count = 0; | 621 | let mut cleaned_count = 0; |
