use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use tokio::signal::unix::{SignalKind, signal};
use tokio_util::sync::CancellationToken;
use crate::logging::Phase;
use super::SignalError;
pub fn send_sigterm(pid: u32) -> std::io::Result<()> {
use nix::sys::signal::{Signal, kill};
use nix::unistd::Pid;
match kill(Pid::from_raw(pid as i32), Signal::SIGTERM) {
Ok(()) => Ok(()),
Err(nix::errno::Errno::ESRCH) => Ok(()),
Err(errno) => Err(std::io::Error::from_raw_os_error(errno as i32)),
}
}
pub fn pid_alive(pid: u32) -> bool {
use nix::sys::signal::kill;
use nix::unistd::Pid;
match kill(Pid::from_raw(pid as i32), None) {
Ok(()) => true,
Err(nix::errno::Errno::EPERM) => true,
Err(_) => false,
}
}
pub fn install(shutdown: CancellationToken) -> Result<(), SignalError> {
let forced = Arc::new(AtomicBool::new(false));
install_stream(SignalKind::interrupt(), "SIGINT", &shutdown, &forced)?;
install_stream(SignalKind::terminate(), "SIGTERM", &shutdown, &forced)?;
let mut hup = signal(SignalKind::hangup()).map_err(SignalError::Install)?;
tokio::spawn(async move {
while hup.recv().await.is_some() {
tracing::info!(
phase = %Phase::Daemon,
"SIGHUP received; ignoring (no reload configured)",
);
}
});
Ok(())
}
fn install_stream(
kind: SignalKind,
label: &'static str,
shutdown: &CancellationToken,
forced: &Arc<AtomicBool>,
) -> Result<(), SignalError> {
let mut stream = signal(kind).map_err(SignalError::Install)?;
let shutdown = shutdown.clone();
let forced = Arc::clone(forced);
tokio::spawn(async move {
while stream.recv().await.is_some() {
if forced.swap(true, Ordering::SeqCst) {
tracing::error!(
phase = %Phase::Daemon,
signal = label,
"second shutdown signal; aborting",
);
std::process::exit(130);
}
tracing::info!(
phase = %Phase::Daemon,
signal = label,
"shutdown signal received; requesting graceful shutdown",
);
shutdown.cancel();
}
});
Ok(())
}