run_shell/
shell_child.rs

1use std::mem::MaybeUninit;
2
3use libc;
4use libc::c_int;
5use local_shell::current_shell;
6use result::check_errno;
7use result::ShellError;
8use result::ShellResult;
9use result::ShellResultExt;
10use std::io::Read;
11use std::process::Child;
12use std::process::Command;
13use std::sync::Arc;
14use std::sync::RwLock;
15
16#[derive(Debug)]
17pub struct ShellChildCore {
18    command_line: String,
19    pub child: Child,
20}
21
22impl ShellChildCore {
23    fn new(command_line: String, child: Child) -> ShellChildCore {
24        ShellChildCore {
25            command_line: command_line,
26            child: child,
27        }
28    }
29
30    pub fn signal(&self, sig: c_int) -> Result<(), ShellError> {
31        let kill_pid = self.child.id() as i32;
32
33        info!("Sending signal {} to {}", sig, self.child.id());
34        unsafe {
35            check_errno("kill", libc::kill(kill_pid, sig))?;
36        }
37        Ok(())
38    }
39    #[allow(warnings)]
40    pub fn wait_null(&self) -> Result<(), ShellError> {
41        unsafe {
42            let mut info = MaybeUninit::<libc::siginfo_t>::uninit();
43            check_errno(
44                "waitid",
45                libc::waitid(
46                    libc::P_PID,
47                    self.child.id() as u32,
48                    &mut info.assume_init(),
49                    libc::WEXITED | libc::WNOWAIT,
50                ),
51            )?;
52        }
53        Ok(())
54    }
55
56    pub fn wait(mut self) -> ShellResult {
57        ShellResult::from_status(self.command_line, self.child.wait()?)
58    }
59}
60
61/// Arc holding `ShellChildCore`.
62///
63/// This is a combination of the following types.
64///
65///  - `Arc` to make it accessbile by mutliple threads. (e.g.
66///    the thread launched the `ShellChildCore` and the thread sending a signal
67///    via `ShellHandle`.
68///  - `RwLock` to `signal()` while `wait_null()` is blocking. Both `signal()`
69///    and `wait_null()` reguires the read lock which can be obtained by
70///    multiple threads at the same time.
71///  - `Option` to enable to `take()` ownership of `ShellChildCore` to inovke
72///    `wait()`.
73pub type ShellChildArc = Arc<RwLock<Option<ShellChildCore>>>;
74
75/// This wraps `ShellChildArc` and provides helper functions.
76pub struct ShellChild(pub ShellChildArc);
77
78impl ShellChild {
79    pub fn new(line: String, mut command: Command) -> Result<ShellChild, ShellError> {
80        let shell = current_shell();
81        let mut lock = shell.lock().unwrap();
82        if lock.signaled() {
83            return Err(ShellError::from_signal(line, 101));
84        }
85        let child = command.spawn()?;
86        let process = Arc::new(RwLock::new(Some(ShellChildCore::new(line, child))));
87        lock.add_process(&process);
88        Ok(ShellChild(process))
89    }
90
91    /// Sends a signal to the process.
92    pub fn signal(&self, signal: c_int) -> Result<(), ShellError> {
93        let process = &self.0;
94        let process = process.read().unwrap();
95        process
96            .as_ref()
97            .ok_or(ShellError::NoSuchProcess)?
98            .signal(signal)
99    }
100
101    /// Waits for termination of the process.
102    pub fn wait(self) -> ShellResult {
103        {
104            let data = self.0.read().unwrap();
105            data.as_ref()
106                .ok_or(ShellError::NoSuchProcess)?
107                .wait_null()?;
108        }
109        let result = {
110            let mut data = self.0.write().unwrap();
111            data.take()
112                .ok_or(ShellError::NoSuchProcess)
113                .and_then(|c| c.wait())
114        };
115        {
116            let shell = current_shell();
117            let mut lock = shell.lock().unwrap();
118            lock.remove_process(&self.0);
119        }
120        result
121    }
122
123    /// Obtains stdout as utf8 string.
124    /// Returns Err if it returns non-zero exit code.
125    pub fn stdout_utf8(self) -> Result<String, ShellError> {
126        let mut string = String::new();
127        {
128            let mut lock = self.0.write().unwrap();
129            let lock = lock.as_mut().ok_or(ShellError::NoSuchProcess)?;
130            lock.child
131                .stdout
132                .as_mut()
133                .unwrap()
134                .read_to_string(&mut string)?;
135        }
136        self.wait()?;
137        Ok(string)
138    }
139}