diff options
Diffstat (limited to 'init/src/services')
| -rw-r--r-- | init/src/services/units.rs | 86 |
1 files changed, 82 insertions, 4 deletions
diff --git a/init/src/services/units.rs b/init/src/services/units.rs index 2497668..ec0098b 100644 --- a/init/src/services/units.rs +++ b/init/src/services/units.rs @@ -11,12 +11,17 @@ // of service and showing its output? idk 4 now, look into how its supposed to be use crate::{RUNLEVEL_STATE, log_success, log_warning}; +use libc::{kill, SIGKILL, SIGTERM}; use serde::Deserialize; use std::{ fs::{read_dir, read_to_string}, process::Child, + sync::Mutex, }; +/// Global list of running service PIDs for graceful shutdown +static SERVICE_PIDS: Mutex<Vec<(u32, String)>> = Mutex::new(Vec::new()); + /* [info] name = "name" @@ -154,13 +159,18 @@ pub fn services_mainloop() -> Result<(), Box<dyn std::error::Error + Send>> { if unit.config.runlevel == current_runlevel { match std::process::Command::new(&unit.config.exec).spawn() { Ok(child) => { + let pid = child.id(); pids.push(( child, unit.config.exec.clone(), unit.config.restart.clone(), 0, )); - log_success(&format!("Started: {}", unit.config.exec)); + // Register PID for graceful shutdown + if let Ok(mut guard) = SERVICE_PIDS.lock() { + guard.push((pid, unit.config.exec.clone())); + } + log_success(&format!("Started: {} (PID: {})", unit.config.exec, pid)); } Err(e) => { log_warning(&format!("Failed to start {}: {}", unit.config.exec, e)); @@ -180,7 +190,13 @@ pub fn services_mainloop() -> Result<(), Box<dyn std::error::Error + Send>> { let (child, exec, restart_policy, restart_count) = entry; match child.try_wait() { Ok(Some(status)) => { - log_warning(&format!("Service exited: {} (status: {:?})", exec, status)); + let pid = child.id(); + log_warning(&format!("Service exited: {} (PID: {}, status: {:?})", exec, pid, status)); + + // Remove from global PID list + if let Ok(mut guard) = SERVICE_PIDS.lock() { + guard.retain(|(p, _)| *p != pid); + } let should_restart = match *restart_policy { Restart::Always => true, @@ -199,11 +215,18 @@ pub fn services_mainloop() -> Result<(), Box<dyn std::error::Error + Send>> { match std::process::Command::new(exec.as_str()).spawn() { Ok(new_child) => { + let new_pid = new_child.id(); *child = new_child; *restart_count += 1; + // Update PID in global list + if let Ok(mut guard) = SERVICE_PIDS.lock() { + if let Some(pos) = guard.iter().position(|(p, e)| *p == pid && e == exec) { + guard[pos] = (new_pid, exec.clone()); + } + } log_success(&format!( - "Restarted {} (attempt {})", - exec, restart_count + "Restarted {} (attempt {}, new PID: {})", + exec, restart_count, new_pid )); true } @@ -296,3 +319,58 @@ fn parse_all_units() -> Result<Vec<Unit>, Box<dyn std::error::Error + Send>> { Ok(units) } + +/// Stop all running services gracefully +/// Sends SIGTERM, waits 5 seconds, then SIGKILL +pub fn stop_all_services() -> Result<(), Box<dyn std::error::Error>> { + log_warning("Stopping all services..."); + + let pids = { + let guard = SERVICE_PIDS.lock().map_err(|e| e.to_string())?; + guard.clone() + }; + + if pids.is_empty() { + log_warning("No services to stop."); + return Ok(()); + } + + for (pid, name) in &pids { + log_warning(&format!("Sending SIGTERM to {} (PID: {})", name, pid)); + unsafe { + kill(*pid as i32, SIGTERM); + } + } + + for _ in 0..50 { + std::thread::sleep(std::time::Duration::from_millis(100)); + let mut all_dead = true; + for (pid, _) in &pids { + if unsafe { kill(*pid as i32, 0) } == 0 { + all_dead = false; + break; + ` } + } + if all_dead { + break; + } + } + + // Send SIGKILL to remaining processes + for (pid, name) in &pids { + unsafe { + if kill(*pid as i32, 0) == 0 { + log_warning(&format!("Sending SIGKILL to {} (PID: {})", name, pid)); + kill(*pid as i32, SIGKILL); + } + } + } + + // Clear the global list + if let Ok(mut guard) = SERVICE_PIDS.lock() { + guard.clear(); + } + + log_warning("All services stopped."); + Ok(()) +} |
