use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
static INTERRUPTED: AtomicBool = AtomicBool::new(false);
static CURRENT_CHILD: AtomicI32 = AtomicI32::new(0);
static CHILD_IS_GROUP: AtomicBool = AtomicBool::new(false);
pub fn install() {
#[cfg(unix)]
{
let handler = handle as extern "C" fn(libc::c_int) as libc::sighandler_t;
unsafe {
libc::signal(libc::SIGINT, handler);
libc::signal(libc::SIGTERM, handler);
}
}
}
#[cfg(unix)]
extern "C" fn handle(_signal: libc::c_int) {
INTERRUPTED.store(true, Ordering::SeqCst);
let child = CURRENT_CHILD.load(Ordering::SeqCst);
if child > 0 {
unsafe {
if CHILD_IS_GROUP.load(Ordering::SeqCst) {
libc::killpg(child, libc::SIGTERM);
} else {
libc::kill(child, libc::SIGTERM);
}
}
}
}
pub fn interrupted() -> bool {
INTERRUPTED.load(Ordering::SeqCst)
}
pub fn register_child(pid: i32, own_group: bool) {
CHILD_IS_GROUP.store(own_group, Ordering::SeqCst);
CURRENT_CHILD.store(pid, Ordering::SeqCst);
}
pub fn clear_child() {
CURRENT_CHILD.store(0, Ordering::SeqCst);
CHILD_IS_GROUP.store(false, Ordering::SeqCst);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_registered_child_is_forgotten_once_it_is_cleared() {
register_child(4242, true);
assert_eq!(CURRENT_CHILD.load(Ordering::SeqCst), 4242);
assert!(CHILD_IS_GROUP.load(Ordering::SeqCst));
clear_child();
assert_eq!(CURRENT_CHILD.load(Ordering::SeqCst), 0);
assert!(!CHILD_IS_GROUP.load(Ordering::SeqCst));
}
#[cfg(unix)]
#[test]
fn the_handler_records_an_interrupt() {
install();
unsafe {
libc::raise(libc::SIGINT);
}
assert!(interrupted(), "the SIGINT handler did not run");
}
}