summaryrefslogtreecommitdiff
path: root/init/src/services/units.rs
diff options
context:
space:
mode:
Diffstat (limited to 'init/src/services/units.rs')
-rw-r--r--init/src/services/units.rs86
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(())
+}