diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs index e90bfed..5547d9a 100644 --- a/src/main.rs +++ b/src/main.rs | |||
| @@ -142,6 +142,9 @@ enum Commands { | |||
| 142 | 142 | ||
| 143 | /// Output comprehensive usage guide for LLMs | 143 | /// Output comprehensive usage guide for LLMs |
| 144 | Llm, | 144 | Llm, |
| 145 | |||
| 146 | /// Wait for a daemon process to terminate | ||
| 147 | Wait(WaitArgs), | ||
| 145 | } | 148 | } |
| 146 | 149 | ||
| 147 | #[derive(Args)] | 150 | #[derive(Args)] |
| @@ -204,6 +207,20 @@ struct StatusArgs { | |||
| 204 | id: String, | 207 | id: String, |
| 205 | } | 208 | } |
| 206 | 209 | ||
| 210 | #[derive(Args)] | ||
| 211 | struct WaitArgs { | ||
| 212 | /// Process identifier | ||
| 213 | id: String, | ||
| 214 | |||
| 215 | /// Timeout in seconds (0 = infinite) | ||
| 216 | #[arg(long, default_value = "30")] | ||
| 217 | timeout: u64, | ||
| 218 | |||
| 219 | /// Polling interval in seconds | ||
| 220 | #[arg(long, default_value = "1")] | ||
| 221 | interval: u64, | ||
| 222 | } | ||
| 223 | |||
| 207 | fn main() { | 224 | fn main() { |
| 208 | tracing_subscriber::fmt() | 225 | tracing_subscriber::fmt() |
| 209 | .with_writer(std::io::stderr) | 226 | .with_writer(std::io::stderr) |
| @@ -243,6 +260,7 @@ fn run_command(command: Commands) -> Result<()> { | |||
| 243 | print_llm_guide(); | 260 | print_llm_guide(); |
| 244 | Ok(()) | 261 | Ok(()) |
| 245 | } | 262 | } |
| 263 | Commands::Wait(args) => wait_daemon(&args.id, args.timeout, args.interval), | ||
| 246 | } | 264 | } |
| 247 | } | 265 | } |
| 248 | 266 | ||
| @@ -954,6 +972,28 @@ demon tail web-server # Follow both logs | |||
| 954 | demon tail web-server --stdout # Follow only stdout | 972 | demon tail web-server --stdout # Follow only stdout |
| 955 | ``` | 973 | ``` |
| 956 | 974 | ||
| 975 | ### demon wait <id> [--timeout <seconds>] [--interval <seconds>] | ||
| 976 | Blocks until a daemon process terminates. | ||
| 977 | |||
| 978 | **Syntax**: `demon wait <id> [--timeout <seconds>] [--interval <seconds>]` | ||
| 979 | |||
| 980 | **Behavior**: | ||
| 981 | - Checks if PID file exists and process is running | ||
| 982 | - Polls the process every `interval` seconds (default: 1 second) | ||
| 983 | - Waits for up to `timeout` seconds (default: 30 seconds) | ||
| 984 | - Use `--timeout 0` for infinite wait | ||
| 985 | - Exits successfully when process terminates | ||
| 986 | - Fails with error if process doesn't exist or timeout is reached | ||
| 987 | - Does not clean up PID files (use `demon clean` for that) | ||
| 988 | |||
| 989 | **Examples**: | ||
| 990 | ```bash | ||
| 991 | demon wait web-server # Wait 30s for termination | ||
| 992 | demon wait backup-job --timeout 0 # Wait indefinitely | ||
| 993 | demon wait data-processor --timeout 3600 # Wait up to 1 hour | ||
| 994 | demon wait short-task --interval 2 # Poll every 2 seconds | ||
| 995 | ``` | ||
| 996 | |||
| 957 | ### demon clean | 997 | ### demon clean |
| 958 | Removes orphaned files from processes that are no longer running. | 998 | Removes orphaned files from processes that are no longer running. |
| 959 | 999 | ||
| @@ -996,6 +1036,13 @@ demon status my-web-server # Check if it started | |||
| 996 | demon tail my-web-server # Monitor logs | 1036 | demon tail my-web-server # Monitor logs |
| 997 | ``` | 1037 | ``` |
| 998 | 1038 | ||
| 1039 | ### Waiting for Process Completion | ||
| 1040 | ```bash | ||
| 1041 | demon run batch-job python process_data.py | ||
| 1042 | demon wait batch-job --timeout 600 # Wait up to 10 minutes | ||
| 1043 | demon cat batch-job # Check output after completion | ||
| 1044 | ``` | ||
| 1045 | |||
| 999 | ### Running a Backup Job | 1046 | ### Running a Backup Job |
| 1000 | ```bash | 1047 | ```bash |
| 1001 | demon run nightly-backup -- rsync -av /data/ /backup/ | 1048 | demon run nightly-backup -- rsync -av /data/ /backup/ |
| @@ -1059,6 +1106,59 @@ This tool is designed for Linux environments and provides a simple interface for | |||
| 1059 | ); | 1106 | ); |
| 1060 | } | 1107 | } |
| 1061 | 1108 | ||
| 1109 | fn wait_daemon(id: &str, timeout: u64, interval: u64) -> Result<()> { | ||
| 1110 | let pid_file = format!("{}.pid", id); | ||
| 1111 | |||
| 1112 | // Check if PID file exists and read PID data | ||
| 1113 | let pid_file_data = match PidFile::read_from_file(&pid_file) { | ||
| 1114 | Ok(data) => data, | ||
| 1115 | Err(PidFileReadError::FileNotFound) => { | ||
| 1116 | return Err(anyhow::anyhow!("Process '{}' not found (no PID file)", id)); | ||
| 1117 | } | ||
| 1118 | Err(PidFileReadError::FileInvalid(reason)) => { | ||
| 1119 | return Err(anyhow::anyhow!("Process '{}' has invalid PID file: {}", id, reason)); | ||
| 1120 | } | ||
| 1121 | Err(PidFileReadError::IoError(err)) => { | ||
| 1122 | return Err(anyhow::anyhow!("Failed to read PID file for '{}': {}", id, err)); | ||
| 1123 | } | ||
| 1124 | }; | ||
| 1125 | |||
| 1126 | let pid = pid_file_data.pid; | ||
| 1127 | |||
| 1128 | // Check if process is currently running | ||
| 1129 | if !is_process_running_by_pid(pid) { | ||
| 1130 | return Err(anyhow::anyhow!("Process '{}' is not running", id)); | ||
| 1131 | } | ||
| 1132 | |||
| 1133 | tracing::info!("Waiting for process '{}' (PID: {}) to terminate", id, pid); | ||
| 1134 | |||
| 1135 | // Handle infinite timeout case | ||
| 1136 | if timeout == 0 { | ||
| 1137 | loop { | ||
| 1138 | if !is_process_running_by_pid(pid) { | ||
| 1139 | tracing::info!("Process '{}' (PID: {}) has terminated", id, pid); | ||
| 1140 | return Ok(()); | ||
| 1141 | } | ||
| 1142 | thread::sleep(Duration::from_secs(interval)); | ||
| 1143 | } | ||
| 1144 | } | ||
| 1145 | |||
| 1146 | // Handle timeout case | ||
| 1147 | let mut elapsed = 0; | ||
| 1148 | while elapsed < timeout { | ||
| 1149 | if !is_process_running_by_pid(pid) { | ||
| 1150 | tracing::info!("Process '{}' (PID: {}) has terminated", id, pid); | ||
| 1151 | return Ok(()); | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | thread::sleep(Duration::from_secs(interval)); | ||
| 1155 | elapsed += interval; | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | // Timeout reached | ||
| 1159 | Err(anyhow::anyhow!("Timeout reached waiting for process '{}' to terminate", id)) | ||
| 1160 | } | ||
| 1161 | |||
| 1062 | fn find_pid_files() -> Result<Vec<std::fs::DirEntry>> { | 1162 | fn find_pid_files() -> Result<Vec<std::fs::DirEntry>> { |
| 1063 | let entries = std::fs::read_dir(".")? | 1163 | let entries = std::fs::read_dir(".")? |
| 1064 | .filter_map(|entry| { | 1164 | .filter_map(|entry| { |
