use crate::CoreError;
use crate::error::syscall_ret;
use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
pub type SignalSet = libc::sigset_t;
pub type ThreadId = libc::pthread_t;
pub const SIGINT: i32 = libc::SIGINT;
pub const SIGTERM: i32 = libc::SIGTERM;
pub const SIGPIPE: i32 = libc::SIGPIPE;
pub const SIGKILL: i32 = libc::SIGKILL;
static SHUTDOWN_FLAG_PTR: AtomicPtr<AtomicBool> = AtomicPtr::new(std::ptr::null_mut());
extern "C" fn shutdown_signal_handler(_sig: libc::c_int) {
let flag = SHUTDOWN_FLAG_PTR.load(Ordering::Relaxed);
if !flag.is_null() {
unsafe {
(*flag).store(true, Ordering::Release);
}
}
}
pub fn install_shutdown_flag(flag: &'static AtomicBool) -> Result<(), CoreError> {
install_shutdown_flag_inner(flag).map(|_| ())
}
pub struct ShutdownFlagGuard {
old_sigint: libc::sigaction,
old_sigterm: libc::sigaction,
old_flag: *mut AtomicBool,
}
impl Drop for ShutdownFlagGuard {
fn drop(&mut self) {
SHUTDOWN_FLAG_PTR.store(self.old_flag, Ordering::Release);
let _ = restore_signal_handler(SIGTERM, &self.old_sigterm);
let _ = restore_signal_handler(SIGINT, &self.old_sigint);
}
}
pub fn install_shutdown_flag_guard(
flag: &'static AtomicBool,
) -> Result<ShutdownFlagGuard, CoreError> {
let (old_sigint, old_sigterm, old_flag) = install_shutdown_flag_inner(flag)?;
Ok(ShutdownFlagGuard {
old_sigint,
old_sigterm,
old_flag,
})
}
fn install_shutdown_flag_inner(
flag: &'static AtomicBool,
) -> Result<(libc::sigaction, libc::sigaction, *mut AtomicBool), CoreError> {
let old_flag = SHUTDOWN_FLAG_PTR.load(Ordering::Acquire);
let old_sigint = install_signal_handler(SIGINT)?;
match install_signal_handler(SIGTERM) {
Ok(old_sigterm) => {
SHUTDOWN_FLAG_PTR.store(
flag as *const AtomicBool as *mut AtomicBool,
Ordering::Release,
);
Ok((old_sigint, old_sigterm, old_flag))
}
Err(err) => {
restore_signal_handler(SIGINT, &old_sigint)?;
Err(err)
}
}
}
#[inline]
pub fn shutdown_requested(flag: &AtomicBool) -> bool {
flag.load(Ordering::Acquire)
}
fn install_signal_handler(sig: libc::c_int) -> Result<libc::sigaction, CoreError> {
let mut action: libc::sigaction = unsafe { std::mem::zeroed() };
let mut old_action: libc::sigaction = unsafe { std::mem::zeroed() };
action.sa_sigaction = shutdown_signal_handler as *const () as usize;
action.sa_flags = 0;
unsafe { libc::sigemptyset(&mut action.sa_mask) };
let ret = unsafe { libc::sigaction(sig, &action, &mut old_action) };
if ret == -1 {
Err(last_sigaction_error(sig))
} else {
Ok(old_action)
}
}
fn restore_signal_handler(sig: libc::c_int, old_action: &libc::sigaction) -> Result<(), CoreError> {
let ret = unsafe { libc::sigaction(sig, old_action, std::ptr::null_mut()) };
if ret == -1 {
Err(last_sigaction_error(sig))
} else {
Ok(())
}
}
fn last_sigaction_error(sig: libc::c_int) -> CoreError {
let op = match sig {
SIGINT => "sigaction(SIGINT)",
SIGTERM => "sigaction(SIGTERM)",
_ => "sigaction",
};
let code = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
CoreError::sys(code, op)
}
pub struct SignalRuntime;
impl SignalRuntime {
pub fn empty_set() -> SignalSet {
let mut set: SignalSet = unsafe { std::mem::zeroed() };
unsafe { libc::sigemptyset(&mut set) };
set
}
pub fn set_with(signals: &[i32]) -> Result<SignalSet, CoreError> {
let mut set: SignalSet = unsafe { std::mem::zeroed() };
unsafe { libc::sigemptyset(&mut set) };
for &sig in signals {
let ret = unsafe { libc::sigaddset(&mut set, sig) };
if ret == -1 {
return Err(CoreError::sys(libc::EINVAL, "sigaddset"));
}
}
Ok(set)
}
pub fn block_current_thread(signals: &SignalSet) -> Result<SignalSet, CoreError> {
let mut previous = Self::empty_set();
let result = unsafe { libc::pthread_sigmask(libc::SIG_BLOCK, signals, &mut previous) };
if result == 0 {
Ok(previous)
} else {
Err(CoreError::sys(result, "pthread_sigmask(SIG_BLOCK)"))
}
}
pub fn restore_current_thread(mask: &SignalSet) -> Result<(), CoreError> {
let result =
unsafe { libc::pthread_sigmask(libc::SIG_SETMASK, mask, std::ptr::null_mut()) };
if result == 0 {
Ok(())
} else {
Err(CoreError::sys(result, "pthread_sigmask(SIG_SETMASK)"))
}
}
pub fn wait(signals: &SignalSet) -> Result<i32, CoreError> {
let mut received_signal = 0;
let result = unsafe { libc::sigwait(signals, &mut received_signal) };
if result == 0 {
Ok(received_signal)
} else {
Err(CoreError::sys(result, "sigwait"))
}
}
pub fn interrupt_thread(thread: ThreadId, signal: i32) -> Result<(), CoreError> {
let result = unsafe { libc::pthread_kill(thread, signal) };
if result == 0 {
Ok(())
} else {
Err(CoreError::sys(result, "pthread_kill"))
}
}
pub fn unblock_all() -> Result<(), CoreError> {
let empty_mask = Self::empty_set();
let r = unsafe { libc::sigprocmask(libc::SIG_SETMASK, &empty_mask, std::ptr::null_mut()) };
syscall_ret(r, "sigprocmask")
}
pub fn reset_default(sig: i32) -> Result<(), CoreError> {
let prev = unsafe { libc::signal(sig, libc::SIG_DFL) };
if prev == libc::SIG_ERR {
Err(CoreError::sys(
std::io::Error::last_os_error().raw_os_error().unwrap_or(0),
"signal(SIG_DFL)",
))
} else {
Ok(())
}
}
}