rust_shell/
process_manager.rs

1// Copyright 2017 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use libc::c_int;
16use libc;
17use libc::sigset_t;
18use std::sync::Arc;
19use std::sync::Mutex;
20use std::thread::ThreadId;
21use local_shell::LocalShell;
22use std::collections::HashMap;
23use result::check_errno;
24use result::ShellError;
25use std::mem;
26use errno::Errno;
27use std::thread;
28
29/// Managing global child process state.
30pub struct ProcessManager {
31    children: HashMap<ThreadId, Arc<Mutex<LocalShell>>>
32}
33
34impl ProcessManager {
35    fn new() -> ProcessManager {
36        ProcessManager {
37            children: HashMap::new()
38        }
39    }
40
41    pub fn add_local_shell(&mut self, id: &ThreadId,
42                           shell: &Arc<Mutex<LocalShell>>) {
43        self.children.insert(id.clone(), shell.clone());
44    }
45
46    pub fn remove_local_shell(&mut self, id: &ThreadId) {
47        self.children.remove(id);
48    }
49}
50
51/// Traps SIGINT and SIGTERM, waits for child process completion, and exits
52/// the current process.
53///
54/// It must be invoked before any thread is launched, because it internally
55/// uses pthread_sigmask.
56#[allow(dead_code)]
57pub fn trap_signal_and_wait_children() -> Result<(), ShellError> {
58    unsafe {
59        let mut sigset = mem::uninitialized::<sigset_t>();
60        check_errno("sigemptyset",
61                    libc::sigemptyset(&mut sigset as *mut sigset_t))?;
62        check_errno("sigaddset", libc::sigaddset(
63                &mut sigset as *mut sigset_t, libc::SIGINT))?;
64        check_errno("sigaddset", libc::sigaddset(
65                &mut sigset as *mut sigset_t, libc::SIGTERM))?;
66
67        let mut oldset = mem::uninitialized::<sigset_t>();
68        let result = libc::pthread_sigmask(
69            libc::SIG_BLOCK, &mut sigset as *mut sigset_t,
70            &mut oldset as *mut sigset_t);
71        if result != 0 {
72            return Err(ShellError::Errno("pthread_sigmask", Errno(result)));
73        }
74
75        thread::spawn(move || {
76            info!("Start waitinig signal");
77            let mut signal: c_int = 0;
78            let result = libc::sigwait(
79                &sigset as *const sigset_t, &mut signal as *mut c_int);
80            if result != 0 {
81                eprintln!("sigwait failed {}", result);
82                return;
83            }
84            info!("Signal {} is received", signal);
85            let mut lock = PROCESS_MANAGER.lock().unwrap();
86            let mut children = lock.children.drain().collect::<Vec<_>>();
87            info!("Wait for {} child processes exiting", children.len());
88            for &mut (_, ref entry) in &mut children {
89                let mut lock = entry.lock().unwrap();
90                lock.wait();
91            }
92            ::std::process::exit(128 + signal);
93        });
94        Ok(())
95    }
96}
97
98lazy_static! {
99    pub static ref PROCESS_MANAGER: Mutex<ProcessManager> =
100        Mutex::new(ProcessManager::new());
101}