use libc::{self, pid_t, c_int};
use {Errno, Result};
use sys::signal::Signal;
mod ffi {
use libc::{pid_t, c_int};
extern {
pub fn waitpid(pid: pid_t, status: *mut c_int, options: c_int) -> pid_t;
}
}
#[cfg(not(any(target_os = "linux",
target_os = "android")))]
libc_bitflags!(
pub flags WaitPidFlag: c_int {
WNOHANG,
WUNTRACED,
}
);
#[cfg(any(target_os = "linux",
target_os = "android"))]
libc_bitflags!(
pub flags WaitPidFlag: c_int {
WNOHANG,
WUNTRACED,
WEXITED,
WCONTINUED,
WNOWAIT, __WNOTHREAD, __WALL, __WCLONE,
}
);
#[cfg(any(target_os = "linux",
target_os = "android"))]
const WSTOPPED: WaitPidFlag = WUNTRACED;
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
pub enum WaitStatus {
Exited(pid_t, i8),
Signaled(pid_t, Signal, bool),
Stopped(pid_t, Signal),
#[cfg(any(target_os = "linux", target_os = "android"))]
PtraceEvent(pid_t, Signal, c_int),
Continued(pid_t),
StillAlive
}
#[cfg(any(target_os = "linux",
target_os = "android"))]
mod status {
use sys::signal::Signal;
use libc::c_int;
pub fn exited(status: i32) -> bool {
(status & 0x7F) == 0
}
pub fn exit_status(status: i32) -> i8 {
((status & 0xFF00) >> 8) as i8
}
pub fn signaled(status: i32) -> bool {
((((status & 0x7f) + 1) as i8) >> 1) > 0
}
pub fn term_signal(status: i32) -> Signal {
Signal::from_c_int(status & 0x7f).unwrap()
}
pub fn dumped_core(status: i32) -> bool {
(status & 0x80) != 0
}
pub fn stopped(status: i32) -> bool {
(status & 0xff) == 0x7f
}
pub fn stop_signal(status: i32) -> Signal {
Signal::from_c_int((status & 0xFF00) >> 8).unwrap()
}
pub fn stop_additional(status: i32) -> c_int {
(status >> 16) as c_int
}
pub fn continued(status: i32) -> bool {
status == 0xFFFF
}
}
#[cfg(any(target_os = "macos",
target_os = "ios"))]
mod status {
use sys::signal::{Signal,SIGCONT};
const WCOREFLAG: i32 = 0x80;
const WSTOPPED: i32 = 0x7f;
fn wstatus(status: i32) -> i32 {
status & 0x7F
}
pub fn exit_status(status: i32) -> i8 {
((status >> 8) & 0xFF) as i8
}
pub fn stop_signal(status: i32) -> Signal {
Signal::from_c_int(status >> 8).unwrap()
}
pub fn continued(status: i32) -> bool {
wstatus(status) == WSTOPPED && stop_signal(status) == SIGCONT
}
pub fn stopped(status: i32) -> bool {
wstatus(status) == WSTOPPED && stop_signal(status) != SIGCONT
}
pub fn exited(status: i32) -> bool {
wstatus(status) == 0
}
pub fn signaled(status: i32) -> bool {
wstatus(status) != WSTOPPED && wstatus(status) != 0
}
pub fn term_signal(status: i32) -> Signal {
Signal::from_c_int(wstatus(status)).unwrap()
}
pub fn dumped_core(status: i32) -> bool {
(status & WCOREFLAG) != 0
}
}
#[cfg(any(target_os = "freebsd",
target_os = "openbsd",
target_os = "dragonfly",
target_os = "netbsd"))]
mod status {
use sys::signal::Signal;
const WCOREFLAG: i32 = 0x80;
const WSTOPPED: i32 = 0x7f;
fn wstatus(status: i32) -> i32 {
status & 0x7F
}
pub fn stopped(status: i32) -> bool {
wstatus(status) == WSTOPPED
}
pub fn stop_signal(status: i32) -> Signal {
Signal::from_c_int(status >> 8).unwrap()
}
pub fn signaled(status: i32) -> bool {
wstatus(status) != WSTOPPED && wstatus(status) != 0 && status != 0x13
}
pub fn term_signal(status: i32) -> Signal {
Signal::from_c_int(wstatus(status)).unwrap()
}
pub fn exited(status: i32) -> bool {
wstatus(status) == 0
}
pub fn exit_status(status: i32) -> i8 {
(status >> 8) as i8
}
pub fn continued(status: i32) -> bool {
status == 0x13
}
pub fn dumped_core(status: i32) -> bool {
(status & WCOREFLAG) != 0
}
}
fn decode(pid : pid_t, status: i32) -> WaitStatus {
if status::exited(status) {
WaitStatus::Exited(pid, status::exit_status(status))
} else if status::signaled(status) {
WaitStatus::Signaled(pid, status::term_signal(status), status::dumped_core(status))
} else if status::stopped(status) {
cfg_if! {
if #[cfg(any(target_os = "linux", target_os = "android"))] {
fn decode_stopped(pid: pid_t, status: i32) -> WaitStatus {
let status_additional = status::stop_additional(status);
if status_additional == 0 {
WaitStatus::Stopped(pid, status::stop_signal(status))
} else {
WaitStatus::PtraceEvent(pid, status::stop_signal(status), status::stop_additional(status))
}
}
} else {
fn decode_stopped(pid: pid_t, status: i32) -> WaitStatus {
WaitStatus::Stopped(pid, status::stop_signal(status))
}
}
}
decode_stopped(pid, status)
} else {
assert!(status::continued(status));
WaitStatus::Continued(pid)
}
}
pub fn waitpid(pid: pid_t, options: Option<WaitPidFlag>) -> Result<WaitStatus> {
use self::WaitStatus::*;
let mut status: i32 = 0;
let option_bits = match options {
Some(bits) => bits.bits(),
None => 0
};
let res = unsafe { ffi::waitpid(pid as pid_t, &mut status as *mut c_int, option_bits) };
Ok(match try!(Errno::result(res)) {
0 => StillAlive,
res => decode(res, status),
})
}
pub fn wait() -> Result<WaitStatus> {
waitpid(-1, None)
}