diff options
| -rw-r--r-- | README.md | 350 | ||||
| -rw-r--r-- | src/main.rs | 21 |
2 files changed, 362 insertions, 9 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..cbc8fe5 --- /dev/null +++ b/README.md | |||
| @@ -0,0 +1,350 @@ | |||
| 1 | # Demon - Background Process Manager | ||
| 2 | |||
| 3 | A lightweight, intuitive CLI tool for spawning, managing, and monitoring background processes on Linux systems. Perfect for development servers, long-running tasks, and automation workflows. | ||
| 4 | |||
| 5 | ## ✨ Features | ||
| 6 | |||
| 7 | - **Simple Process Management**: Start, stop, and monitor background processes with ease | ||
| 8 | - **Persistent Logging**: Automatic stdout/stderr capture to files | ||
| 9 | - **Real-time Monitoring**: Tail logs in real-time with file watching | ||
| 10 | - **Process Lifecycle**: Graceful termination with SIGTERM/SIGKILL fallback | ||
| 11 | - **Machine-readable Output**: Perfect for scripting and LLM agent integration | ||
| 12 | - **Zero Configuration**: Works out of the box, no setup required | ||
| 13 | |||
| 14 | ## 🚀 Installation | ||
| 15 | |||
| 16 | ### From Source | ||
| 17 | ```bash | ||
| 18 | git clone https://github.com/yourusername/demon | ||
| 19 | cd demon | ||
| 20 | cargo install --path . | ||
| 21 | ``` | ||
| 22 | |||
| 23 | ### From Crates.io (Coming Soon) | ||
| 24 | ```bash | ||
| 25 | cargo install demon | ||
| 26 | ``` | ||
| 27 | |||
| 28 | ## 🎯 Quick Start | ||
| 29 | |||
| 30 | ```bash | ||
| 31 | # Start a development server | ||
| 32 | demon run web-server python -m http.server 8080 | ||
| 33 | |||
| 34 | # Monitor the logs in real-time | ||
| 35 | demon tail web-server | ||
| 36 | |||
| 37 | # Check what's running | ||
| 38 | demon list | ||
| 39 | |||
| 40 | # Stop the server | ||
| 41 | demon stop web-server | ||
| 42 | |||
| 43 | # Clean up finished processes | ||
| 44 | demon clean | ||
| 45 | ``` | ||
| 46 | |||
| 47 | ## 📋 Command Reference | ||
| 48 | |||
| 49 | ### `demon run <id> [command...]` | ||
| 50 | Spawn a background process with the given identifier. | ||
| 51 | |||
| 52 | ```bash | ||
| 53 | # Basic usage | ||
| 54 | demon run my-app ./my-application | ||
| 55 | |||
| 56 | # Development server | ||
| 57 | demon run dev-server npm run dev | ||
| 58 | |||
| 59 | # Complex commands (use -- to separate) | ||
| 60 | demon run backup-job -- rsync -av /data/ /backup/ | ||
| 61 | |||
| 62 | # Long-running computation | ||
| 63 | demon run ml-training python train_model.py --epochs 100 | ||
| 64 | ``` | ||
| 65 | |||
| 66 | ### `demon list [--quiet]` | ||
| 67 | List all managed processes and their status. | ||
| 68 | |||
| 69 | ```bash | ||
| 70 | # Human-readable format | ||
| 71 | demon list | ||
| 72 | |||
| 73 | # Machine-readable format (for scripts/agents) | ||
| 74 | demon list --quiet | ||
| 75 | ``` | ||
| 76 | |||
| 77 | ### `demon status <id>` | ||
| 78 | Show detailed status information for a specific process. | ||
| 79 | |||
| 80 | ```bash | ||
| 81 | demon status web-server | ||
| 82 | ``` | ||
| 83 | |||
| 84 | ### `demon stop <id> [--timeout <seconds>]` | ||
| 85 | Stop a running process gracefully (SIGTERM, then SIGKILL if needed). | ||
| 86 | |||
| 87 | ```bash | ||
| 88 | # Default 10-second timeout | ||
| 89 | demon stop web-server | ||
| 90 | |||
| 91 | # Custom timeout | ||
| 92 | demon stop slow-service --timeout 30 | ||
| 93 | ``` | ||
| 94 | |||
| 95 | ### `demon tail <id> [--stdout] [--stderr]` | ||
| 96 | Follow log files in real-time (like `tail -f`). | ||
| 97 | |||
| 98 | ```bash | ||
| 99 | # Follow both stdout and stderr | ||
| 100 | demon tail web-server | ||
| 101 | |||
| 102 | # Follow only stdout | ||
| 103 | demon tail web-server --stdout | ||
| 104 | |||
| 105 | # Follow only stderr | ||
| 106 | demon tail web-server --stderr | ||
| 107 | ``` | ||
| 108 | |||
| 109 | ### `demon cat <id> [--stdout] [--stderr]` | ||
| 110 | Display the complete contents of log files. | ||
| 111 | |||
| 112 | ```bash | ||
| 113 | # Show both logs | ||
| 114 | demon cat web-server | ||
| 115 | |||
| 116 | # Show only stdout | ||
| 117 | demon cat web-server --stdout | ||
| 118 | ``` | ||
| 119 | |||
| 120 | ### `demon clean` | ||
| 121 | Remove orphaned files from processes that are no longer running. | ||
| 122 | |||
| 123 | ```bash | ||
| 124 | demon clean | ||
| 125 | ``` | ||
| 126 | |||
| 127 | ### `demon llm` | ||
| 128 | Output comprehensive usage guide optimized for LLM consumption. | ||
| 129 | |||
| 130 | ```bash | ||
| 131 | demon llm | ||
| 132 | ``` | ||
| 133 | |||
| 134 | ## 🎮 Use Cases | ||
| 135 | |||
| 136 | ### Development Workflows | ||
| 137 | Perfect for managing development servers and build processes: | ||
| 138 | |||
| 139 | ```bash | ||
| 140 | # Start multiple development services | ||
| 141 | demon run api-server npm run dev | ||
| 142 | demon run frontend yarn start | ||
| 143 | demon run db-server docker run -p 5432:5432 postgres | ||
| 144 | |||
| 145 | # Monitor everything | ||
| 146 | demon list | ||
| 147 | demon tail api-server --stderr # Watch for errors | ||
| 148 | ``` | ||
| 149 | |||
| 150 | ### LLM Agent Integration | ||
| 151 | Designed for seamless automation and LLM agent workflows: | ||
| 152 | |||
| 153 | ```bash | ||
| 154 | # Agents can start long-running processes | ||
| 155 | demon run data-processor python process_large_dataset.py | ||
| 156 | |||
| 157 | # Check status programmatically | ||
| 158 | if demon status data-processor | grep -q "RUNNING"; then | ||
| 159 | echo "Processing is still running" | ||
| 160 | fi | ||
| 161 | |||
| 162 | # Get machine-readable process list | ||
| 163 | demon list --quiet | while IFS=: read id pid status; do | ||
| 164 | echo "Process $id ($pid) is $status" | ||
| 165 | done | ||
| 166 | ``` | ||
| 167 | |||
| 168 | ### Background Tasks & Scripts | ||
| 169 | Ideal for CI/CD, backups, and system maintenance: | ||
| 170 | |||
| 171 | ```bash | ||
| 172 | # Database backup | ||
| 173 | demon run nightly-backup -- pg_dump mydb > backup.sql | ||
| 174 | |||
| 175 | # Log file processing | ||
| 176 | demon run log-analyzer tail -f /var/log/app.log | grep ERROR | ||
| 177 | |||
| 178 | # System monitoring | ||
| 179 | demon run monitor -- iostat -x 1 | ||
| 180 | ``` | ||
| 181 | |||
| 182 | ### DevOps & System Administration | ||
| 183 | Manage services without complex init systems: | ||
| 184 | |||
| 185 | ```bash | ||
| 186 | # Application deployment | ||
| 187 | demon run app-server ./deploy.sh production | ||
| 188 | |||
| 189 | # Health monitoring | ||
| 190 | demon run health-check -- while true; do curl -f http://localhost:8080/health || exit 1; sleep 30; done | ||
| 191 | |||
| 192 | # Resource monitoring | ||
| 193 | demon run resource-monitor -- top -b -n1 | head -20 | ||
| 194 | ``` | ||
| 195 | |||
| 196 | ## 🏗️ How It Works | ||
| 197 | |||
| 198 | When you run `demon run web-server python -m http.server 8080`: | ||
| 199 | |||
| 200 | 1. **Process Creation**: Spawns the process detached from your terminal | ||
| 201 | 2. **File Management**: Creates three files: | ||
| 202 | - `web-server.pid` - Contains the process ID and command | ||
| 203 | - `web-server.stdout` - Captures standard output | ||
| 204 | - `web-server.stderr` - Captures error output | ||
| 205 | 3. **Process Monitoring**: Tracks process lifecycle independently | ||
| 206 | 4. **Log Management**: Files persist after process termination for inspection | ||
| 207 | |||
| 208 | ## 🤖 LLM Agent Integration | ||
| 209 | |||
| 210 | Demon is specifically designed to work seamlessly with LLM agents and automation tools: | ||
| 211 | |||
| 212 | ### Machine-Readable Output | ||
| 213 | ```bash | ||
| 214 | # Get process status in parseable format | ||
| 215 | demon list --quiet | ||
| 216 | # Output: web-server:12345:RUNNING | ||
| 217 | # backup-job:12346:DEAD | ||
| 218 | ``` | ||
| 219 | |||
| 220 | ### Scripting Examples | ||
| 221 | ```bash | ||
| 222 | # Start service if not running | ||
| 223 | demon list --quiet | grep -q "web-server:" || demon run web-server python -m http.server | ||
| 224 | |||
| 225 | # Wait for process to finish | ||
| 226 | while demon list --quiet | grep -q "backup-job:.*:RUNNING"; do | ||
| 227 | sleep 5 | ||
| 228 | done | ||
| 229 | |||
| 230 | # Get all running processes | ||
| 231 | demon list --quiet | grep ":RUNNING" | cut -d: -f1 | ||
| 232 | ``` | ||
| 233 | |||
| 234 | ### Error Handling | ||
| 235 | ```bash | ||
| 236 | # Check if process started successfully | ||
| 237 | if demon run api-server ./start-api.sh; then | ||
| 238 | echo "API server started successfully" | ||
| 239 | demon tail api-server & # Monitor in background | ||
| 240 | else | ||
| 241 | echo "Failed to start API server" | ||
| 242 | exit 1 | ||
| 243 | fi | ||
| 244 | ``` | ||
| 245 | |||
| 246 | ## 📁 File Management | ||
| 247 | |||
| 248 | ### File Locations | ||
| 249 | All files are created in the current working directory: | ||
| 250 | - `<id>.pid` - Process ID and command information | ||
| 251 | - `<id>.stdout` - Standard output log | ||
| 252 | - `<id>.stderr` - Standard error log | ||
| 253 | |||
| 254 | ### Cleanup | ||
| 255 | - Files persist after process termination for inspection | ||
| 256 | - Use `demon clean` to remove files from dead processes | ||
| 257 | - Consider adding `*.pid`, `*.stdout`, `*.stderr` to `.gitignore` | ||
| 258 | |||
| 259 | ### Log Rotation | ||
| 260 | - Demon doesn't handle log rotation internally | ||
| 261 | - For long-running processes, implement rotation in your application | ||
| 262 | - Or use external tools like `logrotate` | ||
| 263 | |||
| 264 | ## 🔧 Advanced Usage | ||
| 265 | |||
| 266 | ### Process Management | ||
| 267 | ```bash | ||
| 268 | # Graceful shutdown with custom timeout | ||
| 269 | demon stop long-running-task --timeout 60 | ||
| 270 | |||
| 271 | # Force kill if process is stuck | ||
| 272 | demon stop stuck-process --timeout 1 | ||
| 273 | ``` | ||
| 274 | |||
| 275 | ### Monitoring & Debugging | ||
| 276 | ```bash | ||
| 277 | # Monitor multiple processes | ||
| 278 | for id in web-server api-server worker; do | ||
| 279 | demon tail $id --stderr & | ||
| 280 | done | ||
| 281 | |||
| 282 | # Check resource usage | ||
| 283 | demon run monitor -- ps aux | grep my-app | ||
| 284 | demon cat monitor | ||
| 285 | ``` | ||
| 286 | |||
| 287 | ### Integration with System Tools | ||
| 288 | ```bash | ||
| 289 | # Use with systemd | ||
| 290 | demon run my-service systemctl --user start my-app | ||
| 291 | |||
| 292 | # Use with Docker | ||
| 293 | demon run container -- docker run -d --name myapp nginx | ||
| 294 | |||
| 295 | # Use with tmux/screen for complex setups | ||
| 296 | demon run dev-env -- tmux new-session -d 'npm run dev' | ||
| 297 | ``` | ||
| 298 | |||
| 299 | ## ⚠️ System Requirements | ||
| 300 | |||
| 301 | - **Operating System**: Linux (uses `kill` command for process management) | ||
| 302 | - **Rust**: 1.70+ (for building from source) | ||
| 303 | - **Permissions**: Standard user permissions (no root required) | ||
| 304 | |||
| 305 | ## 🔒 Security Considerations | ||
| 306 | |||
| 307 | - Demon runs processes with the same permissions as the calling user | ||
| 308 | - PID files contain process information - protect accordingly | ||
| 309 | - Log files may contain sensitive information from your applications | ||
| 310 | - No network access or elevated privileges required | ||
| 311 | |||
| 312 | ## 🤝 Contributing | ||
| 313 | |||
| 314 | We welcome contributions! Here's how to get started: | ||
| 315 | |||
| 316 | 1. **Fork the repository** | ||
| 317 | 2. **Create a feature branch**: `git checkout -b feature/amazing-feature` | ||
| 318 | 3. **Make your changes** | ||
| 319 | 4. **Run tests**: `cargo test` | ||
| 320 | 5. **Format code**: `cargo fmt` | ||
| 321 | 6. **Submit a pull request** | ||
| 322 | |||
| 323 | ### Development Setup | ||
| 324 | ```bash | ||
| 325 | git clone https://github.com/yourusername/demon | ||
| 326 | cd demon | ||
| 327 | cargo build | ||
| 328 | cargo test | ||
| 329 | ``` | ||
| 330 | |||
| 331 | ## 📝 License | ||
| 332 | |||
| 333 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. | ||
| 334 | |||
| 335 | ## 🐛 Bug Reports & Feature Requests | ||
| 336 | |||
| 337 | Found a bug or have a feature idea? Please open an issue on [GitHub Issues](https://github.com/yourusername/demon/issues). | ||
| 338 | |||
| 339 | ## 📚 Similar Tools | ||
| 340 | |||
| 341 | - **pm2** - Process manager for Node.js applications | ||
| 342 | - **supervisor** - Process control system for Unix | ||
| 343 | - **systemd** - System and service manager for Linux | ||
| 344 | - **screen/tmux** - Terminal multiplexers | ||
| 345 | |||
| 346 | Demon focuses on simplicity, LLM integration, and developer experience over complex process management features. | ||
| 347 | |||
| 348 | --- | ||
| 349 | |||
| 350 | **Built with ❤️ in Rust** \ No newline at end of file | ||
diff --git a/src/main.rs b/src/main.rs index bedcb74..e90bfed 100644 --- a/src/main.rs +++ b/src/main.rs | |||
| @@ -80,12 +80,15 @@ impl PidFile { | |||
| 80 | let lines: Vec<&str> = contents.lines().collect(); | 80 | let lines: Vec<&str> = contents.lines().collect(); |
| 81 | 81 | ||
| 82 | if lines.is_empty() { | 82 | if lines.is_empty() { |
| 83 | return Err(PidFileReadError::FileInvalid("PID file is empty".to_string())); | 83 | return Err(PidFileReadError::FileInvalid( |
| 84 | "PID file is empty".to_string(), | ||
| 85 | )); | ||
| 84 | } | 86 | } |
| 85 | 87 | ||
| 86 | let pid = lines[0].trim().parse::<u32>().map_err(|_| { | 88 | let pid = lines[0] |
| 87 | PidFileReadError::FileInvalid("Invalid PID on first line".to_string()) | 89 | .trim() |
| 88 | })?; | 90 | .parse::<u32>() |
| 91 | .map_err(|_| PidFileReadError::FileInvalid("Invalid PID on first line".to_string()))?; | ||
| 89 | 92 | ||
| 90 | let command: Vec<String> = lines[1..].iter().map(|line| line.to_string()).collect(); | 93 | let command: Vec<String> = lines[1..].iter().map(|line| line.to_string()).collect(); |
| 91 | 94 | ||
| @@ -647,7 +650,10 @@ fn list_daemons(quiet: bool) -> Result<()> { | |||
| 647 | println!("{}:{}:{}", id, pid_file_data.pid, status); | 650 | println!("{}:{}:{}", id, pid_file_data.pid, status); |
| 648 | } else { | 651 | } else { |
| 649 | let command = pid_file_data.command_string(); | 652 | let command = pid_file_data.command_string(); |
| 650 | println!("{:<20} {:<8} {:<10} {}", id, pid_file_data.pid, status, command); | 653 | println!( |
| 654 | "{:<20} {:<8} {:<10} {}", | ||
| 655 | id, pid_file_data.pid, status, command | ||
| 656 | ); | ||
| 651 | } | 657 | } |
| 652 | } | 658 | } |
| 653 | Err(PidFileReadError::FileNotFound) => { | 659 | Err(PidFileReadError::FileNotFound) => { |
| @@ -665,10 +671,7 @@ fn list_daemons(quiet: bool) -> Result<()> { | |||
| 665 | if quiet { | 671 | if quiet { |
| 666 | println!("{}:INVALID:ERROR", id); | 672 | println!("{}:INVALID:ERROR", id); |
| 667 | } else { | 673 | } else { |
| 668 | println!( | 674 | println!("{:<20} {:<8} {:<10} {}", id, "INVALID", "ERROR", reason); |
| 669 | "{:<20} {:<8} {:<10} {}", | ||
| 670 | id, "INVALID", "ERROR", reason | ||
| 671 | ); | ||
| 672 | } | 675 | } |
| 673 | } | 676 | } |
| 674 | Err(PidFileReadError::IoError(_)) => { | 677 | Err(PidFileReadError::IoError(_)) => { |
