use std::thread;
use nix::sys::signal::{SigSet, SigmaskHow, Signal};
#[cfg(feature = "tokio")]
use tokio::signal::unix::{SignalKind, signal};
#[cfg(feature = "tokio")]
use killswitch::KillSwitch;
use crate::err::Error;
#[cfg(feature = "tokio")]
pub async fn wait_shutdown<F>(f: F, ks: KillSwitch)
where
F: FnOnce() + Send
{
wait_oneshot_signal(SignalKind::interrupt(), f, ks).await;
}
#[cfg(feature = "tokio")]
pub async fn wait_term<F>(f: F, ks: KillSwitch)
where
F: FnOnce() + Send
{
wait_oneshot_signal(SignalKind::terminate(), f, ks).await;
}
#[cfg(feature = "tokio")]
pub async fn wait_reload<F>(f: F, ks: KillSwitch)
where
F: Fn() + Send
{
wait_repeating_signal(SignalKind::hangup(), f, ks).await;
}
#[cfg(feature = "tokio")]
pub async fn wait_user1<F>(f: F, ks: KillSwitch)
where
F: Fn() + Send
{
wait_repeating_signal(SignalKind::user_defined1(), f, ks).await;
}
#[cfg(feature = "tokio")]
pub async fn wait_user2<F>(f: F, ks: KillSwitch)
where
F: Fn() + Send
{
wait_repeating_signal(SignalKind::user_defined2(), f, ks).await;
}
#[cfg(feature = "tokio")]
pub async fn wait_repeating_signal<F>(
sigkind: SignalKind,
f: F,
ks: KillSwitch
) where
F: Fn() + Send
{
tracing::trace!("Repeating {:?} task launched", sigkind);
let Ok(mut sig) = signal(sigkind) else {
log::error!("Unable to create {sigkind:?} Future");
return;
};
loop {
tokio::select! {
_ = sig.recv() => {
tracing::debug!("Received {:?} -- running closure", sigkind);
f();
}
() = ks.wait() => {
tracing::debug!("killswitch triggered");
break;
}
}
}
tracing::trace!("{:?} terminating", sigkind);
}
#[cfg(feature = "tokio")]
pub async fn wait_oneshot_signal<F>(sigkind: SignalKind, f: F, ks: KillSwitch)
where
F: FnOnce() + Send
{
tracing::trace!("One-shot {:?} task launched", sigkind);
let Ok(mut sig) = signal(sigkind) else {
log::error!("Unable to create {sigkind:?} Future");
return;
};
tokio::select! {
_ = sig.recv() => {
tracing::debug!("Received {sigkind:?} -- running closure" );
f();
}
() = ks.wait() => {
tracing::debug!("killswitch triggered");
}
}
tracing::trace!("{:?} terminating", sigkind);
}
pub enum SigType {
Usr1,
Usr2,
Int,
Term,
Hup
}
pub fn sync_sigmon<F>(f: F) -> Result<thread::JoinHandle<()>, Error>
where
F: Fn(SigType) + Send + 'static
{
let mut ss = SigSet::empty();
ss.add(Signal::SIGINT);
ss.add(Signal::SIGTERM);
ss.add(Signal::SIGHUP);
ss.add(Signal::SIGUSR1);
ss.add(Signal::SIGUSR2);
let mut oldset = SigSet::empty();
nix::sys::signal::pthread_sigmask(
SigmaskHow::SIG_SETMASK,
Some(&ss),
Some(&mut oldset)
)
.unwrap();
let jh = thread::Builder::new()
.name("sigmon".into())
.spawn(move || {
let mask = unsafe {
let mut mask: libc::sigset_t = std::mem::zeroed();
libc::sigemptyset(&raw mut mask);
libc::sigaddset(&raw mut mask, libc::SIGINT);
libc::sigaddset(&raw mut mask, libc::SIGTERM);
libc::sigaddset(&raw mut mask, libc::SIGHUP);
libc::sigaddset(&raw mut mask, libc::SIGUSR1);
libc::sigaddset(&raw mut mask, libc::SIGUSR2);
mask
};
loop {
let mut sig: libc::c_int = 0;
let ret = unsafe { libc::sigwait(&raw const mask, &raw mut sig) };
if ret == 0 {
let signal = Signal::try_from(sig).unwrap();
match signal {
Signal::SIGUSR1 => {
f(SigType::Usr1);
}
Signal::SIGUSR2 => {
f(SigType::Usr2);
}
Signal::SIGINT => {
f(SigType::Int);
break;
}
Signal::SIGTERM => {
f(SigType::Term);
break;
}
Signal::SIGHUP => {
f(SigType::Hup);
}
_ => {}
}
}
}
})?;
Ok(jh)
}