#![allow(clippy::expect_used)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::match_wild_err_arm)]
#![allow(clippy::uninlined_format_args)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
#![allow(clippy::needless_continue)]
#![allow(clippy::doc_markdown)]
#![allow(clippy::indexing_slicing)]
#![allow(clippy::panic)]
#![allow(clippy::unnecessary_wraps)]
#![allow(clippy::explicit_iter_loop)]
#![allow(clippy::ignored_unit_patterns)]
#![allow(clippy::for_kv_map)]
use std::{
collections::HashMap,
process::{Command, exit},
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
thread,
time::{Duration, Instant},
};
use fork::{Fork, fork, waitpid};
#[derive(Debug, Clone)]
struct ProcessInfo {
name: String,
pid: libc::pid_t,
started_at: Instant,
restarts: u32,
max_restarts: u32,
}
struct Supervisor {
processes: HashMap<Fork, ProcessInfo>,
shutdown: Arc<AtomicBool>,
}
impl Supervisor {
fn new() -> Self {
Self {
processes: HashMap::new(),
shutdown: Arc::new(AtomicBool::new(false)),
}
}
fn spawn(&mut self, name: String, command: Vec<String>) -> std::io::Result<Fork> {
match fork()? {
result @ Fork::Parent(pid) => {
println!("✅ Spawned '{}' with PID: {}", name, pid);
self.processes.insert(
result,
ProcessInfo {
name: name.clone(),
pid,
started_at: Instant::now(),
restarts: 0,
max_restarts: 3,
},
);
Ok(result)
}
Fork::Child => {
let program = &command[0];
let args = &command[1..];
Command::new(program)
.args(args)
.status()
.expect("Failed to execute command");
exit(0);
}
}
}
fn handle_exit(&mut self, fork_result: Fork) {
if let Some(info) = self.processes.remove(&fork_result) {
let uptime = info.started_at.elapsed();
let name = info.name.clone();
let restarts = info.restarts;
let max_restarts = info.max_restarts;
println!(
"\n💀 Process '{}' (PID: {}) exited after {:.2}s",
name,
info.pid,
uptime.as_secs_f64()
);
if restarts < max_restarts && !self.shutdown.load(Ordering::Relaxed) {
println!(
"🔄 Restarting '{}' (restart {}/{})",
name,
restarts + 1,
max_restarts
);
if let Err(e) = self.restart(info) {
eprintln!("❌ Failed to restart '{}': {}", name, e);
}
} else if restarts >= max_restarts {
println!(
"⚠️ Process '{}' reached max restarts, not restarting",
name
);
}
}
}
fn restart(&mut self, mut info: ProcessInfo) -> std::io::Result<()> {
let name = info.name.clone();
info.restarts += 1;
info.started_at = Instant::now();
let command = vec!["sleep".to_string(), "2".to_string()];
match fork()? {
result @ Fork::Parent(pid) => {
info.pid = pid;
self.processes.insert(result, info);
Ok(())
}
Fork::Child => {
Command::new(&command[0])
.args(&command[1..])
.status()
.unwrap_or_else(|e| panic!("Failed to execute {}: {}", name, e));
exit(0);
}
}
}
fn wait_for_exit(&mut self) -> std::io::Result<()> {
for (fork_result, _info) in self.processes.clone().iter() {
if let Some(pid) = fork_result.child_pid() {
match waitpid(pid) {
Ok(_) => {
self.handle_exit(*fork_result);
return Ok(());
}
Err(_) => {
continue;
}
}
}
}
thread::sleep(Duration::from_millis(100));
Ok(())
}
fn stats(&self) -> String {
let total = self.processes.len();
let total_restarts: u32 = self.processes.values().map(|p| p.restarts).sum();
format!(
"📊 Supervisor Stats: {} processes, {} total restarts",
total, total_restarts
)
}
fn list(&self) {
println!("\n📋 Supervised Processes:");
println!("┌─────────────────┬──────────┬──────────┬──────────┐");
println!("│ Name │ PID │ Uptime │ Restarts │");
println!("├─────────────────┼──────────┼──────────┼──────────┤");
for (_fork, info) in &self.processes {
let uptime = info.started_at.elapsed().as_secs();
println!(
"│ {:15} │ {:8} │ {:6}s │ {:8} │",
info.name, info.pid, uptime, info.restarts
);
}
println!("└─────────────────┴──────────┴──────────┴──────────┘\n");
}
fn shutdown(&mut self) {
println!("\n🛑 Shutting down supervisor...");
self.shutdown.store(true, Ordering::Relaxed);
for (fork_result, info) in &self.processes {
println!(" Stopping '{}' (PID: {})", info.name, info.pid);
if let Some(pid) = fork_result.child_pid() {
let _ = waitpid(pid);
}
}
self.processes.clear();
println!("✅ All processes stopped");
}
}
fn main() {
println!("🚀 Advanced Process Supervisor\n");
let mut supervisor = Supervisor::new();
println!("Starting workers...\n");
for i in 1..=3 {
let name = format!("worker-{}", i);
let command = vec!["sleep".to_string(), format!("{}", i * 2)];
match supervisor.spawn(name, command) {
Ok(_) => {}
Err(e) => eprintln!("Failed to spawn worker: {}", e),
}
}
supervisor.list();
println!("📡 Supervisor running. Waiting for process events...\n");
for _ in 0..10 {
if supervisor.processes.is_empty() {
println!("✅ All processes completed");
break;
}
if let Err(e) = supervisor.wait_for_exit() {
eprintln!("Error waiting for child: {}", e);
break;
}
println!("\n{}\n", supervisor.stats());
}
supervisor.shutdown();
}