diff --git a/synit-pid1/src/main.rs b/synit-pid1/src/main.rs index 6a1935f..f21ac78 100644 --- a/synit-pid1/src/main.rs +++ b/synit-pid1/src/main.rs @@ -1,10 +1,12 @@ use clap::Clap; -use nix::sys::signal::{killpg, Signal}; +use nix::sys::reboot::{reboot, RebootMode}; +use nix::sys::signal::{kill, Signal, SigHandler}; use nix::sys::wait; use nix::unistd; use std::convert::TryInto; +use std::io::Write; use std::sync::Arc; use syndicate::actor::*; @@ -42,13 +44,24 @@ impl Pid1Listener { impl Entity for Pid1Listener { } -async fn handle_sigchld_and_waitpid() -> ActorResult { +fn ignore_signal(kind: SignalKind) -> ActorResult { + let sig: Signal = kind.as_raw_value().try_into()?; + unsafe { nix::sys::signal::signal(sig, SigHandler::SigIgn) }?; + Ok(()) +} + +async fn handle_sigchld_and_waitpid() -> Result { + // For now, we use Alpine's userland reboot, poweroff and halt tools. + // These send SIGTERM, SIGUSR2 and SIGUSR1, respectively. + let mut sigchlds = signal(SignalKind::child())?; let mut sigints = signal(SignalKind::interrupt())?; let mut sigterms = signal(SignalKind::terminate())?; + let mut sigusr2s = signal(SignalKind::user_defined2())?; + let mut sigusr1s = signal(SignalKind::user_defined1())?; tracing::info!("Awaiting signals..."); - loop { + let next_step = loop { select! { _ = sigchlds.recv() => { loop { @@ -70,26 +83,49 @@ async fn handle_sigchld_and_waitpid() -> ActorResult { } _ = sigints.recv() => { tracing::debug!("Received SIGINT"); - let result = killpg(unistd::getpgrp(), Some(Signal::SIGINT)); - tracing::debug!("killpg result: {:?}", result); - break; + break RebootMode::RB_AUTOBOOT; } _ = sigterms.recv() => { tracing::debug!("Received SIGTERM"); - let result = killpg(unistd::getpgrp(), Some(Signal::SIGTERM)); - tracing::debug!("killpg result: {:?}", result); - break; + break RebootMode::RB_AUTOBOOT; + } + _ = sigusr2s.recv() => { + tracing::debug!("Received SIGUSR2"); + break RebootMode::RB_POWER_OFF; + } + _ = sigusr1s.recv() => { + tracing::debug!("Received SIGUSR1"); + break RebootMode::RB_HALT_SYSTEM; } } - } + }; - Ok(()) + ignore_signal(SignalKind::interrupt())?; + ignore_signal(SignalKind::terminate())?; + ignore_signal(SignalKind::user_defined2())?; + ignore_signal(SignalKind::user_defined1())?; + + tracing::info!("Terminating, {:?}", next_step); + + std::io::stdout().flush()?; + std::io::stderr().flush()?; + nix::unistd::sync(); + std::thread::sleep(std::time::Duration::from_millis(100)); + + nix::unistd::sync(); + let _ = kill(unistd::Pid::from_raw(-1), Some(Signal::SIGINT)); + // ^ ignore result! We're about to reboot anyway + + nix::unistd::sync(); + Ok(next_step) } #[tokio::main] async fn main() -> ActorResult { syndicate::convenient_logging()?; + tracing::info!("Startup with PID {}", unistd::getpid()); + match unistd::setsid() { Ok(_pid) => tracing::info!("setsid(2): new session is {}", _pid), Err(e) => tracing::info!("setsid(2) failed: {:?}", &e), @@ -141,7 +177,7 @@ async fn main() -> ActorResult { Ok(()) }); - handle_sigchld_and_waitpid().await?; + reboot(handle_sigchld_and_waitpid().await?)?; Ok(()) }