1#![deny(missing_docs)]
2
3extern crate mio;
71extern crate mio_extras;
72#[cfg(target_os = "windows")]
73extern crate winapi;
74
75use mio::{Evented, Poll, PollOpt, Ready, Token};
76use mio_extras::channel::{channel, Receiver, Sender};
77use std::io::{Error, ErrorKind, Read, Result, Write};
78use std::process::{Child, Command, ExitStatus};
79use std::thread::spawn;
80
81#[cfg(test)]
82mod test;
83
84pub trait CommandAsync {
86 fn spawn_async(&mut self) -> Result<Process>;
88}
89
90impl CommandAsync for Command {
91 fn spawn_async(&mut self) -> Result<Process> {
92 let child = self.spawn()?;
93 Ok(Process::from_child(child))
94 }
95}
96
97pub struct Process {
99 receiver: Receiver<ProcessEvent>,
100 stdin: Option<std::process::ChildStdin>,
101 id: u32,
102}
103
104impl Process {
105 pub fn try_recv(&mut self) -> std::result::Result<ProcessEvent, std::sync::mpsc::TryRecvError> {
107 self.receiver.try_recv()
108 }
109
110 pub(crate) fn from_child(mut child: Child) -> Process {
111 let (sender, receiver) = channel();
112 if let Some(stdout) = child.stdout.take() {
113 spawn(create_reader(stdout, sender.clone(), StdioChannel::Stdout));
114 }
115 if let Some(stderr) = child.stderr.take() {
116 spawn(create_reader(stderr, sender.clone(), StdioChannel::Stderr));
117 }
118 let stdin = child.stdin.take();
119 let id = child.id();
120 spawn(move || {
121 let result = match child.wait_with_output() {
122 Err(e) => {
123 let _ = sender.send(ProcessEvent::CommandError(e));
124 return;
125 }
126 Ok(r) => r,
127 };
128 if !result.stdout.is_empty() {
129 if SendResult::Abort
130 == try_send_buffer(&result.stdout, StdioChannel::Stdout, &sender)
131 {
132 return;
133 }
134 }
135 if !result.stderr.is_empty() {
136 if SendResult::Abort
137 == try_send_buffer(&result.stderr, StdioChannel::Stderr, &sender)
138 {
139 return;
140 }
141 }
142 let _ = sender.send(ProcessEvent::Exit(result.status));
143 });
144 Process {
145 receiver,
146 stdin,
147 id,
148 }
149 }
150
151 #[cfg(target_os = "windows")]
153 pub fn kill(&mut self) -> Result<()> {
154 use std::collections::HashMap;
155 use std::io::Error;
156 use std::mem;
157 use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
158 use winapi::um::processthreadsapi::{OpenProcess, TerminateProcess};
159 use winapi::um::tlhelp32::{
160 CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32,
161 TH32CS_SNAPPROCESS,
162 };
163 use winapi::um::winnt::PROCESS_TERMINATE;
164
165 type ParentID = u32;
169 type ChildID = u32;
170 let mut processes = HashMap::<ParentID, Vec<ChildID>>::new();
171
172 let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) };
173 if snapshot == INVALID_HANDLE_VALUE {
174 unsafe {
175 CloseHandle(snapshot);
176 }
177 return Err(Error::last_os_error());
178 }
179
180 let mut process_entry_32: PROCESSENTRY32 = unsafe { mem::zeroed() };
181 process_entry_32.dwSize = mem::size_of::<PROCESSENTRY32>() as u32;
182 if 0 == unsafe { Process32First(snapshot, &mut process_entry_32) } {
183 unsafe {
184 CloseHandle(snapshot);
185 }
186 return Err(Error::last_os_error());
187 }
188
189 processes
191 .entry(process_entry_32.th32ParentProcessID)
192 .or_insert_with(Vec::new)
193 .push(process_entry_32.th32ProcessID);
194
195 while unsafe { Process32Next(snapshot, &mut process_entry_32) } != 0 {
196 processes
198 .entry(process_entry_32.th32ParentProcessID)
199 .or_insert_with(Vec::new)
200 .push(process_entry_32.th32ProcessID);
201 }
202
203 unsafe {
204 CloseHandle(snapshot);
205 }
206
207 fn kill_pid(pid: u32, processes: &HashMap<ParentID, Vec<ChildID>>) -> Result<()> {
209 if let Some(children) = processes.get(&pid) {
210 for child in children {
211 kill_pid(*child, processes)?;
212 }
213 }
214 let handle = unsafe { OpenProcess(PROCESS_TERMINATE, 0, pid) };
216 if handle.is_null() {
217 Err(Error::last_os_error())
219 } else if 0 == unsafe { TerminateProcess(handle, 0) } {
220 Err(Error::last_os_error())
222 } else {
223 Ok(())
224 }
225 }
226
227 kill_pid(self.id(), &processes)
228 }
229
230 #[cfg(target_os = "linux")]
232 pub fn kill(&mut self) -> Result<()> {
233 extern crate libc;
234 use std::io::Error;
235
236 let result = unsafe { libc::kill(self.id() as i32, libc::SIGKILL) };
237 if result == 0 {
238 Ok(())
239 } else {
240 Err(Error::last_os_error())
241 }
242 }
243
244 pub fn id(&self) -> u32 {
246 self.id
247 }
248}
249
250impl Write for Process {
251 fn write(&mut self, buf: &[u8]) -> Result<usize> {
256 match self.stdin.as_mut() {
257 Some(ref mut stdin) => stdin.write(buf),
258 None => Err(Error::from(ErrorKind::NotConnected)),
259 }
260 }
261
262 fn flush(&mut self) -> Result<()> {
267 match self.stdin.as_mut() {
268 Some(ref mut stdin) => stdin.flush(),
269 None => Err(Error::from(ErrorKind::NotConnected)),
270 }
271 }
272}
273
274impl Evented for Process {
275 fn register(&self, poll: &Poll, token: Token, interest: Ready, ops: PollOpt) -> Result<()> {
277 self.receiver.register(poll, token, interest, ops)
278 }
279 fn reregister(&self, poll: &Poll, token: Token, interest: Ready, ops: PollOpt) -> Result<()> {
281 self.receiver.reregister(poll, token, interest, ops)
282 }
283 fn deregister(&self, poll: &Poll) -> Result<()> {
285 self.receiver.deregister(poll)
286 }
287}
288
289#[derive(PartialEq, Eq, Debug)]
290enum SendResult {
291 Abort,
292 Ok,
293}
294
295fn try_send_buffer(
296 buffer: &[u8],
297 channel: StdioChannel,
298 sender: &Sender<ProcessEvent>,
299) -> SendResult {
300 let str = match std::str::from_utf8(buffer) {
301 Ok(s) => s,
302 Err(e) => {
303 let _ = sender.send(ProcessEvent::Utf8Error(channel, e));
304 return SendResult::Abort;
305 }
306 };
307 if str.is_empty() {
308 println!("Aborting try_send_buffer because we're sending empty strings");
309 println!("Channel: {:?}", channel);
310 return SendResult::Abort;
311 }
312 if let Err(_) = sender.send(ProcessEvent::Data(channel, String::from(str))) {
313 SendResult::Abort
314 } else {
315 SendResult::Ok
316 }
317}
318
319fn create_reader<T: Read + 'static>(
320 mut stream: T,
321 sender: Sender<ProcessEvent>,
322 channel: StdioChannel,
323) -> impl FnOnce() {
324 move || {
325 let mut buffer = [0u8; 1024];
326 loop {
327 match stream.read(&mut buffer[..]) {
328 Ok(0) => {
329 break;
331 }
332 Ok(n) => {
333 if SendResult::Abort == try_send_buffer(&buffer[..n], channel, &sender) {
334 break;
335 }
336 }
337 Err(e) => {
338 let _ = sender.send(ProcessEvent::IoError(channel, e));
339 break;
340 }
341 }
342 }
343 }
344}
345
346#[derive(Debug)]
348pub enum ProcessEvent {
349 Data(StdioChannel, String),
351
352 CommandError(std::io::Error),
354
355 IoError(StdioChannel, std::io::Error),
357
358 Utf8Error(StdioChannel, std::str::Utf8Error),
360
361 Exit(ExitStatus),
363}
364
365#[derive(Copy, Clone, Debug)]
367pub enum StdioChannel {
368 Stdout,
370
371 Stderr,
373}