2015-06-30 21:53:12 +00:00
|
|
|
/*
|
|
|
|
* This file is part of the uutils coreutils package.
|
|
|
|
*
|
|
|
|
* (c) Maciej Dziardziel <fiedzia@gmail.com>
|
|
|
|
*
|
|
|
|
* For the full copyright and license information, please view the LICENSE file
|
|
|
|
* that was distributed with this source code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern crate libc;
|
|
|
|
|
|
|
|
use libc::{c_int, pid_t};
|
|
|
|
use std::fmt;
|
|
|
|
use std::io;
|
|
|
|
use std::process::Child;
|
|
|
|
use std::sync::{Arc, Condvar, Mutex};
|
|
|
|
use std::thread;
|
2016-08-04 23:16:38 +00:00
|
|
|
use std::time::{Duration, Instant};
|
2015-06-30 21:53:12 +00:00
|
|
|
|
|
|
|
// This is basically sys::unix::process::ExitStatus
|
|
|
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
|
|
|
pub enum ExitStatus {
|
|
|
|
Code(i32),
|
|
|
|
Signal(i32),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ExitStatus {
|
|
|
|
fn from_status(status: c_int) -> ExitStatus {
|
|
|
|
if status & 0x7F != 0 { // WIFSIGNALED(status)
|
|
|
|
ExitStatus::Signal(status & 0x7F)
|
|
|
|
} else {
|
|
|
|
ExitStatus::Code(status & 0xFF00 >> 8)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn success(&self) -> bool {
|
|
|
|
match *self {
|
|
|
|
ExitStatus::Code(code) => code == 0,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn code(&self) -> Option<i32> {
|
|
|
|
match *self {
|
|
|
|
ExitStatus::Code(code) => Some(code),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn signal(&self) -> Option<i32> {
|
|
|
|
match *self {
|
|
|
|
ExitStatus::Signal(code) => Some(code),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for ExitStatus {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match *self {
|
|
|
|
ExitStatus::Code(code) => write!(f, "exit code: {}", code),
|
|
|
|
ExitStatus::Signal(code) => write!(f, "exit code: {}", code),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Missing methods for Child objects
|
|
|
|
pub trait ChildExt {
|
|
|
|
/// Send a signal to a Child process.
|
|
|
|
fn send_signal(&mut self, signal: usize) -> io::Result<()>;
|
|
|
|
|
|
|
|
/// Wait for a process to finish or return after the specified duration.
|
2016-08-04 23:16:38 +00:00
|
|
|
fn wait_or_timeout(&mut self, timeout: Duration) -> io::Result<Option<ExitStatus>>;
|
2015-06-30 21:53:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ChildExt for Child {
|
|
|
|
fn send_signal(&mut self, signal: usize) -> io::Result<()> {
|
2015-11-16 04:47:09 +00:00
|
|
|
if unsafe { libc::kill(self.id() as pid_t,
|
|
|
|
signal as i32) } != 0 {
|
2015-06-30 21:53:12 +00:00
|
|
|
Err(io::Error::last_os_error())
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-04 23:16:38 +00:00
|
|
|
fn wait_or_timeout(&mut self, timeout: Duration) -> io::Result<Option<ExitStatus>> {
|
2015-06-30 21:53:12 +00:00
|
|
|
// The result will be written to that Option, protected by a Mutex
|
|
|
|
// Then the Condvar will be signaled
|
|
|
|
let state = Arc::new((
|
|
|
|
Mutex::new(Option::None::<io::Result<ExitStatus>>),
|
|
|
|
Condvar::new(),
|
|
|
|
));
|
|
|
|
|
|
|
|
// Start the waiting thread
|
|
|
|
let state_th = state.clone();
|
|
|
|
let pid_th = self.id();
|
|
|
|
thread::spawn(move || {
|
|
|
|
let &(ref lock_th, ref cvar_th) = &*state_th;
|
|
|
|
// Child::wait() would need a &mut to self, can't use that...
|
|
|
|
// use waitpid() directly, with our own ExitStatus
|
|
|
|
let mut status: c_int = 0;
|
|
|
|
let r = unsafe { libc::waitpid(pid_th as i32, &mut status, 0) };
|
|
|
|
// Fill the Option and notify on the Condvar
|
|
|
|
let mut exitstatus_th = lock_th.lock().unwrap();
|
|
|
|
if r != pid_th as c_int {
|
|
|
|
*exitstatus_th = Some(Err(io::Error::last_os_error()));
|
|
|
|
} else {
|
|
|
|
let s = ExitStatus::from_status(status);
|
|
|
|
*exitstatus_th = Some(Ok(s));
|
|
|
|
}
|
|
|
|
cvar_th.notify_one();
|
|
|
|
});
|
|
|
|
|
|
|
|
// Main thread waits
|
|
|
|
let &(ref lock, ref cvar) = &*state;
|
|
|
|
let mut exitstatus = lock.lock().unwrap();
|
|
|
|
// Condvar::wait_timeout_ms() can wake too soon, in this case wait again
|
2016-08-04 23:16:38 +00:00
|
|
|
let start = Instant::now();
|
2016-08-05 00:02:55 +00:00
|
|
|
loop {
|
|
|
|
if let Some(exitstatus) = exitstatus.take() {
|
|
|
|
return exitstatus.map(Some);
|
|
|
|
}
|
2016-08-04 23:16:38 +00:00
|
|
|
if start.elapsed() >= timeout {
|
2015-06-30 21:53:12 +00:00
|
|
|
return Ok(None)
|
|
|
|
}
|
2016-08-04 23:16:38 +00:00
|
|
|
let cvar_timeout = timeout - start.elapsed();
|
|
|
|
exitstatus = cvar.wait_timeout(exitstatus, cvar_timeout).unwrap().0;
|
2015-06-30 21:53:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|