use errno::{errno, set_errno};
use nix::sys::signal;
use nix::sys::wait::{waitpid, WaitPidFlag as WF, WaitStatus as WS};
use nix::unistd::Pid;
use std::collections::{HashMap, HashSet};
use std::sync::Mutex;
lazy_static! {
static ref REAP_MAP: Mutex<HashMap<i32, i32>> = Mutex::new(HashMap::new());
static ref STOP_MAP: Mutex<HashSet<i32>> = Mutex::new(HashSet::new());
static ref CONT_MAP: Mutex<HashSet<i32>> = Mutex::new(HashSet::new());
static ref KILL_MAP: Mutex<HashMap<i32, i32>> = Mutex::new(HashMap::new());
}
pub fn killed_map_insert(pid: i32, sig: i32) {
if let Ok(mut m) = KILL_MAP.try_lock() {
m.insert(pid, sig);
}
}
pub fn killed_map_pop(pid: i32) -> Option<i32> {
if let Ok(mut m) = KILL_MAP.try_lock() {
m.remove(&pid)
} else {
None
}
}
pub fn insert_cont_map(pid: i32) {
if let Ok(mut m) = CONT_MAP.try_lock() {
m.insert(pid);
}
}
pub fn pop_cont_map(pid: i32) -> bool {
match CONT_MAP.try_lock() {
Ok(mut m) => m.remove(&pid),
Err(_) => false,
}
}
pub fn insert_stopped_map(pid: i32) {
if let Ok(mut m) = STOP_MAP.try_lock() {
m.insert(pid);
}
}
pub fn pop_stopped_map(pid: i32) -> bool {
match STOP_MAP.try_lock() {
Ok(mut m) => m.remove(&pid),
Err(_) => false,
}
}
pub fn insert_reap_map(pid: i32, status: i32) {
if let Ok(mut m) = REAP_MAP.try_lock() {
m.insert(pid, status);
}
}
pub fn pop_reap_map(pid: i32) -> Option<i32> {
match REAP_MAP.try_lock() {
Ok(mut m) => m.remove(&pid),
Err(_) => None,
}
}
pub fn block_signals() {
let mut sigset = signal::SigSet::empty();
sigset.add(signal::SIGCHLD);
match signal::sigprocmask(signal::SigmaskHow::SIG_BLOCK, Some(&sigset), None) {
Ok(_) => {}
Err(e) => {
log!("sigprocmask block error: {:?}", e);
}
}
}
pub fn unblock_signals() {
let mut sigset = signal::SigSet::empty();
sigset.add(signal::SIGCHLD);
match signal::sigprocmask(signal::SigmaskHow::SIG_UNBLOCK, Some(&sigset), None) {
Ok(_) => {}
Err(e) => {
log!("sigprocmask unblock error: {:?}", e);
}
}
}
#[allow(unreachable_patterns)]
pub extern "C" fn handle_sigchld(_sig: i32) {
let saved_errno = errno();
let options = Some(WF::WUNTRACED | WF::WNOHANG | WF::WCONTINUED);
loop {
match waitpid(Pid::from_raw(-1), options) {
Ok(WS::Exited(pid, status)) => {
insert_reap_map(i32::from(pid), status);
}
Ok(WS::Stopped(pid, _sig)) => {
insert_stopped_map(i32::from(pid));
}
Ok(WS::Continued(pid)) => {
insert_cont_map(i32::from(pid));
}
Ok(WS::Signaled(pid, sig, _core_dumped)) => {
killed_map_insert(i32::from(pid), sig as i32);
}
Ok(WS::StillAlive) => {
break;
}
Ok(_others) => {
}
Err(e) => {
if e == nix::Error::ECHILD {
break;
}
log!("chld waitpid error: {:?}", e);
break;
}
}
}
set_errno(saved_errno);
}
pub fn setup_sigchld_handler() {
let sigset = signal::SigSet::empty();
let handler = signal::SigHandler::Handler(handle_sigchld);
let flags = signal::SaFlags::SA_RESTART;
let sa = signal::SigAction::new(handler, flags, sigset);
unsafe {
match signal::sigaction(signal::SIGCHLD, &sa) {
Ok(_) => {}
Err(e) => {
log!("sigaction error: {:?}", e);
}
}
}
}