use errno::Errno;
use libc;
use libc::c_int;
use libc::sigset_t;
use local_shell::LocalShell;
use result::check_errno;
use result::ShellError;
use std::collections::HashMap;
use std::mem::MaybeUninit;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::thread::ThreadId;
pub struct ProcessManager {
children: HashMap<ThreadId, Arc<Mutex<LocalShell>>>,
}
impl ProcessManager {
fn new() -> ProcessManager {
ProcessManager {
children: HashMap::new(),
}
}
pub fn add_local_shell(&mut self, id: &ThreadId, shell: &Arc<Mutex<LocalShell>>) {
self.children.insert(id.clone(), shell.clone());
}
pub fn remove_local_shell(&mut self, id: &ThreadId) {
self.children.remove(id);
}
}
#[allow(warnings)]
pub fn trap_signal_and_wait_children() -> Result<(), ShellError> {
unsafe {
let mut sigset = MaybeUninit::<sigset_t>::uninit();
check_errno("sigemptyset", libc::sigemptyset(&mut sigset.assume_init()))?;
check_errno(
"sigaddset",
libc::sigaddset(&mut sigset.assume_init(), libc::SIGINT),
)?;
check_errno(
"sigaddset",
libc::sigaddset(&mut sigset.assume_init(), libc::SIGTERM),
)?;
let mut oldset = MaybeUninit::<sigset_t>::uninit();
let result = libc::pthread_sigmask(
libc::SIG_BLOCK,
&mut sigset.assume_init(),
&mut oldset.assume_init(),
);
if result != 0 {
return Err(ShellError::Errno("pthread_sigmask", Errno(result)));
}
thread::spawn(move || {
info!("Start waitinig signal");
let mut signal: c_int = 0;
let result = libc::sigwait(&sigset.assume_init(), &mut signal as *mut c_int);
if result != 0 {
eprintln!("sigwait failed {}", result);
return;
}
info!("Signal {} is received", signal);
let mut lock = PROCESS_MANAGER.lock().unwrap();
let mut children = lock.children.drain().collect::<Vec<_>>();
info!("Wait for {} child processes exiting", children.len());
for &mut (_, ref entry) in &mut children {
let mut lock = entry.lock().unwrap();
lock.wait();
}
::std::process::exit(128 + signal);
});
Ok(())
}
}
lazy_static! {
pub static ref PROCESS_MANAGER: Mutex<ProcessManager> = Mutex::new(ProcessManager::new());
}