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