1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use libc::c_int;
use libc;
use local_shell::current_shell;
use result::ShellError;
use result::ShellResult;
use result::ShellResultExt;
use result::check_errno;
use std::io::Read;
use std::mem;
use std::process::Child;
use std::process::Command;
use std::sync::Arc;
use std::sync::RwLock;

#[derive(Debug)]
pub struct ShellChildCore {
    command_line: String,
    pub child: Child,
}

impl ShellChildCore {
    fn new(command_line: String, child: Child) -> ShellChildCore {
        ShellChildCore {
            command_line: command_line,
            child: child,
        }
    }

    pub fn signal(&self, sig: c_int) -> Result<(), ShellError> {
        let kill_pid = self.child.id() as i32;

        info!("Sending signal {} to {}", sig, self.child.id());
        unsafe {
            check_errno("kill", libc::kill(kill_pid, sig))?;
        }
        Ok(())
    }

    pub fn wait_null(&self) -> Result<(), ShellError> {
        unsafe {
            let mut info = mem::uninitialized::<libc::siginfo_t>();
            check_errno("waitid",
                        libc::waitid(
                            libc::P_PID,
                            self.child.id() as u32,
                            &mut info as *mut libc::siginfo_t,
                            libc::WEXITED | libc::WNOWAIT))?;
        }
        Ok(())
    }

    pub fn wait(mut self) -> ShellResult {
        ShellResult::from_status(self.command_line, self.child.wait()?)
    }
}

/// Arc holding `ShellChildCore`.
///
/// This is a combination of the following types.
///
///  - `Arc` to make it accessbile by mutliple threads. (e.g.
///    the thread launched the `ShellChildCore` and the thread sending a signal
///    via `ShellHandle`.
///  - `RwLock` to `signal()` while `wait_null()` is blocking. Both `signal()`
///    and `wait_null()` reguires the read lock which can be obtained by
///    multiple threads at the same time.
///  - `Option` to enable to `take()` ownership of `ShellChildCore` to inovke
///    `wait()`.
pub type ShellChildArc = Arc<RwLock<Option<ShellChildCore>>>;

/// This wraps `ShellChildArc` and provides helper functions.
pub struct ShellChild(pub ShellChildArc);

impl ShellChild {
    pub fn new(line: String, mut command: Command)
            -> Result<ShellChild, ShellError> {
        let shell = current_shell();
        let mut lock = shell.lock().unwrap();
        if lock.signaled() {
            return Err(ShellError::from_signal(line, 101))
        }
        let child = command.spawn()?;
        let process = Arc::new(RwLock::new(
                Some(ShellChildCore::new(line, child))));
        lock.add_process(&process);
        Ok(ShellChild(process))
    }

    /// Sends a signal to the process.
    pub fn signal(&self, signal: c_int) -> Result<(), ShellError> {
        let process = &self.0;
        let process = process.read().unwrap();
        process.as_ref().ok_or(ShellError::NoSuchProcess)?.signal(signal)
    }

    /// Waits for termination of the process.
    pub fn wait(self) -> ShellResult {
        {
            let data = self.0.read().unwrap();
            data.as_ref().ok_or(ShellError::NoSuchProcess)?.wait_null()?;
        }
        let result = {
            let mut data = self.0.write().unwrap();
            data.take().ok_or(ShellError::NoSuchProcess)
                .and_then(|c| c.wait())
        };
        {
            let shell = current_shell();
            let mut lock = shell.lock().unwrap();
            lock.remove_process(&self.0);
        }
        result
    }

    /// Obtains stdout as utf8 string.
    /// Returns Err if it returns non-zero exit code.
    pub fn stdout_utf8(self) -> Result<String, ShellError> {
        let mut string = String::new();
        {
            let mut lock = self.0.write().unwrap();
            let lock = lock.as_mut().ok_or(ShellError::NoSuchProcess)?;
            lock.child.stdout.as_mut().unwrap().read_to_string(&mut string)?;
        }
        self.wait()?;
        Ok(string)
    }
}