use std::time::Duration;
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ShutdownStrategy {
DirectReboot,
SignalInit,
}
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
const fn shutdown_strategy(pid: u32) -> ShutdownStrategy {
if pid == 1 {
ShutdownStrategy::DirectReboot
} else {
ShutdownStrategy::SignalInit
}
}
#[cfg(target_os = "linux")]
mod platform {
use super::{ShutdownStrategy, shutdown_strategy};
use std::time::{Duration, Instant};
pub fn poweroff(grace: Duration) {
match shutdown_strategy(std::process::id()) {
ShutdownStrategy::DirectReboot => direct_reboot(grace),
ShutdownStrategy::SignalInit => signal_init_then_reboot(grace),
}
}
fn signal_init_then_reboot(grace: Duration) {
tracing::info!("Shutdown: requesting orderly poweroff from busybox init (PID 1)");
unsafe { libc::kill(1, libc::SIGUSR2) };
std::thread::sleep(grace);
tracing::warn!("Shutdown: busybox init did not power off in time; forcing poweroff");
unsafe { libc::sync() };
unsafe { libc::reboot(libc::LINUX_REBOOT_CMD_POWER_OFF) };
tracing::error!("Shutdown: reboot(POWER_OFF) returned unexpectedly, forcing exit");
unsafe { libc::_exit(1) };
}
fn direct_reboot(grace: Duration) {
tracing::info!("Shutdown: sending SIGTERM to all processes");
unsafe { libc::kill(-1, libc::SIGTERM) };
wait_for_children(grace);
tracing::info!("Shutdown: sending SIGKILL to remaining processes");
unsafe { libc::kill(-1, libc::SIGKILL) };
wait_for_children(Duration::from_millis(500));
tracing::info!("Shutdown: syncing filesystems");
unsafe { libc::sync() };
tracing::info!("Shutdown: powering off");
unsafe { libc::reboot(libc::LINUX_REBOOT_CMD_POWER_OFF) };
tracing::error!("Shutdown: reboot(POWER_OFF) returned unexpectedly, forcing exit");
unsafe { libc::_exit(1) };
}
fn wait_for_children(timeout: Duration) {
let deadline = Instant::now() + timeout;
loop {
if Instant::now() >= deadline {
return;
}
let mut status: i32 = 0;
let pid = unsafe { libc::waitpid(-1, &raw mut status, libc::WNOHANG) };
if pid <= 0 {
if pid == -1 {
let err = std::io::Error::last_os_error();
if err.raw_os_error() == Some(libc::ECHILD) {
tracing::debug!("Shutdown: no more children");
return;
}
}
std::thread::sleep(Duration::from_millis(100));
}
}
}
}
#[cfg(target_os = "linux")]
pub use platform::poweroff;
#[cfg(not(target_os = "linux"))]
#[allow(dead_code, reason = "stub for non-Linux development builds")]
pub fn poweroff(_grace: Duration) {
tracing::warn!("poweroff not supported on this platform");
}
#[cfg(test)]
mod tests {
use super::{ShutdownStrategy, shutdown_strategy};
#[test]
fn pid1_drives_shutdown_directly() {
assert_eq!(shutdown_strategy(1), ShutdownStrategy::DirectReboot);
}
#[test]
fn non_pid1_delegates_to_busybox_init() {
assert_eq!(shutdown_strategy(2), ShutdownStrategy::SignalInit);
assert_eq!(shutdown_strategy(1234), ShutdownStrategy::SignalInit);
}
}