use libc;
use {Errno, Error, Result};
use std::fmt;
use std::fmt::Debug;
use std::mem;
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
use std::os::unix::io::RawFd;
use std::ptr;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
pub enum Signal {
SIGHUP = libc::SIGHUP,
SIGINT = libc::SIGINT,
SIGQUIT = libc::SIGQUIT,
SIGILL = libc::SIGILL,
SIGTRAP = libc::SIGTRAP,
SIGABRT = libc::SIGABRT,
SIGBUS = libc::SIGBUS,
SIGFPE = libc::SIGFPE,
SIGKILL = libc::SIGKILL,
SIGUSR1 = libc::SIGUSR1,
SIGSEGV = libc::SIGSEGV,
SIGUSR2 = libc::SIGUSR2,
SIGPIPE = libc::SIGPIPE,
SIGALRM = libc::SIGALRM,
SIGTERM = libc::SIGTERM,
#[cfg(all(any(target_os = "linux", target_os = "android", target_os = "emscripten"), not(target_arch = "mips")))]
SIGSTKFLT = libc::SIGSTKFLT,
SIGCHLD = libc::SIGCHLD,
SIGCONT = libc::SIGCONT,
SIGSTOP = libc::SIGSTOP,
SIGTSTP = libc::SIGTSTP,
SIGTTIN = libc::SIGTTIN,
SIGTTOU = libc::SIGTTOU,
SIGURG = libc::SIGURG,
SIGXCPU = libc::SIGXCPU,
SIGXFSZ = libc::SIGXFSZ,
SIGVTALRM = libc::SIGVTALRM,
SIGPROF = libc::SIGPROF,
SIGWINCH = libc::SIGWINCH,
SIGIO = libc::SIGIO,
#[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))]
SIGPWR = libc::SIGPWR,
SIGSYS = libc::SIGSYS,
#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "emscripten")))]
SIGEMT = libc::SIGEMT,
#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "emscripten")))]
SIGINFO = libc::SIGINFO,
}
pub use self::Signal::*;
#[cfg(all(any(target_os = "linux", target_os = "android", target_os = "emscripten"), not(target_arch = "mips")))]
const SIGNALS: [Signal; 31] = [
SIGHUP,
SIGINT,
SIGQUIT,
SIGILL,
SIGTRAP,
SIGABRT,
SIGBUS,
SIGFPE,
SIGKILL,
SIGUSR1,
SIGSEGV,
SIGUSR2,
SIGPIPE,
SIGALRM,
SIGTERM,
SIGSTKFLT,
SIGCHLD,
SIGCONT,
SIGSTOP,
SIGTSTP,
SIGTTIN,
SIGTTOU,
SIGURG,
SIGXCPU,
SIGXFSZ,
SIGVTALRM,
SIGPROF,
SIGWINCH,
SIGIO,
SIGPWR,
SIGSYS];
#[cfg(all(any(target_os = "linux", target_os = "android", target_os = "emscripten"), target_arch = "mips"))]
const SIGNALS: [Signal; 30] = [
SIGHUP,
SIGINT,
SIGQUIT,
SIGILL,
SIGTRAP,
SIGABRT,
SIGBUS,
SIGFPE,
SIGKILL,
SIGUSR1,
SIGSEGV,
SIGUSR2,
SIGPIPE,
SIGALRM,
SIGTERM,
SIGCHLD,
SIGCONT,
SIGSTOP,
SIGTSTP,
SIGTTIN,
SIGTTOU,
SIGURG,
SIGXCPU,
SIGXFSZ,
SIGVTALRM,
SIGPROF,
SIGWINCH,
SIGIO,
SIGPWR,
SIGSYS];
#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "emscripten")))]
const SIGNALS: [Signal; 31] = [
SIGHUP,
SIGINT,
SIGQUIT,
SIGILL,
SIGTRAP,
SIGABRT,
SIGBUS,
SIGFPE,
SIGKILL,
SIGUSR1,
SIGSEGV,
SIGUSR2,
SIGPIPE,
SIGALRM,
SIGTERM,
SIGCHLD,
SIGCONT,
SIGSTOP,
SIGTSTP,
SIGTTIN,
SIGTTOU,
SIGURG,
SIGXCPU,
SIGXFSZ,
SIGVTALRM,
SIGPROF,
SIGWINCH,
SIGIO,
SIGSYS,
SIGEMT,
SIGINFO];
pub const NSIG: libc::c_int = 32;
pub struct SignalIterator {
next: usize,
}
impl Iterator for SignalIterator {
type Item = Signal;
fn next(&mut self) -> Option<Signal> {
if self.next < SIGNALS.len() {
let next_signal = SIGNALS[self.next];
self.next += 1;
Some(next_signal)
} else {
None
}
}
}
impl Signal {
pub fn iterator() -> SignalIterator {
SignalIterator{next: 0}
}
#[inline]
pub fn from_c_int(signum: libc::c_int) -> Result<Signal> {
match 0 < signum && signum < NSIG {
true => Ok(unsafe { mem::transmute(signum) }),
false => Err(Error::invalid_argument()),
}
}
}
pub const SIGIOT : Signal = SIGABRT;
pub const SIGPOLL : Signal = SIGIO;
pub const SIGUNUSED : Signal = SIGSYS;
libc_bitflags!{
pub flags SaFlags: libc::c_int {
SA_NOCLDSTOP,
SA_NOCLDWAIT,
SA_NODEFER,
SA_ONSTACK,
SA_RESETHAND,
SA_RESTART,
SA_SIGINFO,
}
}
#[repr(i32)]
#[derive(Clone, Copy, PartialEq)]
pub enum SigmaskHow {
SIG_BLOCK = libc::SIG_BLOCK,
SIG_UNBLOCK = libc::SIG_UNBLOCK,
SIG_SETMASK = libc::SIG_SETMASK,
}
#[derive(Clone, Copy)]
pub struct SigSet {
sigset: libc::sigset_t
}
impl SigSet {
pub fn all() -> SigSet {
let mut sigset: libc::sigset_t = unsafe { mem::uninitialized() };
let _ = unsafe { libc::sigfillset(&mut sigset as *mut libc::sigset_t) };
SigSet { sigset: sigset }
}
pub fn empty() -> SigSet {
let mut sigset: libc::sigset_t = unsafe { mem::uninitialized() };
let _ = unsafe { libc::sigemptyset(&mut sigset as *mut libc::sigset_t) };
SigSet { sigset: sigset }
}
pub fn add(&mut self, signal: Signal) {
unsafe { libc::sigaddset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) };
}
pub fn clear(&mut self) {
unsafe { libc::sigemptyset(&mut self.sigset as *mut libc::sigset_t) };
}
pub fn remove(&mut self, signal: Signal) {
unsafe { libc::sigdelset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) };
}
pub fn contains(&self, signal: Signal) -> bool {
let res = unsafe { libc::sigismember(&self.sigset as *const libc::sigset_t, signal as libc::c_int) };
match res {
1 => true,
0 => false,
_ => unreachable!("unexpected value from sigismember"),
}
}
pub fn extend(&mut self, other: &SigSet) {
for signal in Signal::iterator() {
if other.contains(signal) {
self.add(signal);
}
}
}
pub fn thread_get_mask() -> Result<SigSet> {
let mut oldmask: SigSet = unsafe { mem::uninitialized() };
try!(pthread_sigmask(SigmaskHow::SIG_SETMASK, None, Some(&mut oldmask)));
Ok(oldmask)
}
pub fn thread_set_mask(&self) -> Result<()> {
pthread_sigmask(SigmaskHow::SIG_SETMASK, Some(self), None)
}
pub fn thread_block(&self) -> Result<()> {
pthread_sigmask(SigmaskHow::SIG_BLOCK, Some(self), None)
}
pub fn thread_unblock(&self) -> Result<()> {
pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(self), None)
}
pub fn thread_swap_mask(&self, how: SigmaskHow) -> Result<SigSet> {
let mut oldmask: SigSet = unsafe { mem::uninitialized() };
try!(pthread_sigmask(how, Some(self), Some(&mut oldmask)));
Ok(oldmask)
}
pub fn wait(&self) -> Result<Signal> {
let mut signum: libc::c_int = unsafe { mem::uninitialized() };
let res = unsafe { libc::sigwait(&self.sigset as *const libc::sigset_t, &mut signum) };
Errno::result(res).map(|_| Signal::from_c_int(signum).unwrap())
}
}
impl AsRef<libc::sigset_t> for SigSet {
fn as_ref(&self) -> &libc::sigset_t {
&self.sigset
}
}
#[allow(unknown_lints)]
#[derive(Clone, Copy, PartialEq)]
pub enum SigHandler {
SigDfl,
SigIgn,
Handler(extern fn(libc::c_int)),
SigAction(extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void))
}
pub struct SigAction {
sigaction: libc::sigaction
}
impl SigAction {
pub fn new(handler: SigHandler, flags: SaFlags, mask: SigSet) -> SigAction {
let mut s = unsafe { mem::uninitialized::<libc::sigaction>() };
s.sa_sigaction = match handler {
SigHandler::SigDfl => unsafe { mem::transmute(libc::SIG_DFL) },
SigHandler::SigIgn => unsafe { mem::transmute(libc::SIG_IGN) },
SigHandler::Handler(f) => unsafe { mem::transmute(f) },
SigHandler::SigAction(f) => unsafe { mem::transmute(f) },
};
s.sa_flags = match handler {
SigHandler::SigAction(_) => (flags | SA_SIGINFO).bits(),
_ => (flags - SA_SIGINFO).bits(),
};
s.sa_mask = mask.sigset;
SigAction { sigaction: s }
}
}
pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigAction> {
let mut oldact = mem::uninitialized::<libc::sigaction>();
let res =
libc::sigaction(signal as libc::c_int, &sigaction.sigaction as *const libc::sigaction, &mut oldact as *mut libc::sigaction);
Errno::result(res).map(|_| SigAction { sigaction: oldact })
}
pub fn pthread_sigmask(how: SigmaskHow,
set: Option<&SigSet>,
oldset: Option<&mut SigSet>) -> Result<()> {
if set.is_none() && oldset.is_none() {
return Ok(())
}
let res = unsafe {
libc::pthread_sigmask(how as libc::c_int,
set.map_or_else(|| ptr::null::<libc::sigset_t>(),
|s| &s.sigset as *const libc::sigset_t),
oldset.map_or_else(|| ptr::null_mut::<libc::sigset_t>(),
|os| &mut os.sigset as *mut libc::sigset_t))
};
Errno::result(res).map(drop)
}
pub fn kill<T: Into<Option<Signal>>>(pid: libc::pid_t, signal: T) -> Result<()> {
let res = unsafe { libc::kill(pid,
match signal.into() {
Some(s) => s as libc::c_int,
None => 0,
}) };
Errno::result(res).map(drop)
}
pub fn raise(signal: Signal) -> Result<()> {
let res = unsafe { libc::raise(signal as libc::c_int) };
Errno::result(res).map(drop)
}
#[cfg(target_os = "freebsd")]
pub type type_of_thread_id = libc::lwpid_t;
#[cfg(target_os = "linux")]
pub type type_of_thread_id = libc::pid_t;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum SigevNotify {
SigevNone,
SigevSignal { signal: Signal, si_value: libc::intptr_t },
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
SigevKevent { kq: RawFd, udata: libc::intptr_t },
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
SigevThreadId { signal: Signal, thread_id: type_of_thread_id,
si_value: libc::intptr_t },
}
#[repr(C)]
pub struct SigEvent {
sigevent: libc::sigevent
}
impl SigEvent {
pub fn new(sigev_notify: SigevNotify) -> SigEvent {
let mut sev = unsafe { mem::zeroed::<libc::sigevent>()};
sev.sigev_notify = match sigev_notify {
SigevNotify::SigevNone => libc::SIGEV_NONE,
SigevNotify::SigevSignal{..} => libc::SIGEV_SIGNAL,
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
SigevNotify::SigevKevent{..} => libc::SIGEV_KEVENT,
#[cfg(target_os = "freebsd")]
SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID,
#[cfg(all(target_os = "linux", target_env = "gnu", not(target_arch = "mips")))]
SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID,
#[cfg(any(all(target_os = "linux", target_env = "musl"), target_arch = "mips"))]
SigevNotify::SigevThreadId{..} => 4 };
sev.sigev_signo = match sigev_notify {
SigevNotify::SigevSignal{ signal, .. } => signal as ::c_int,
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
SigevNotify::SigevKevent{ kq, ..} => kq,
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
SigevNotify::SigevThreadId{ signal, .. } => signal as ::c_int,
_ => 0
};
sev.sigev_value.sival_ptr = match sigev_notify {
SigevNotify::SigevNone => ptr::null_mut::<libc::c_void>(),
SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut ::c_void,
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
SigevNotify::SigevKevent{ udata, .. } => udata as *mut ::c_void,
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut ::c_void,
};
SigEvent::set_tid(&mut sev, &sigev_notify);
SigEvent{sigevent: sev}
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
fn set_tid(sev: &mut libc::sigevent, sigev_notify: &SigevNotify) {
sev.sigev_notify_thread_id = match sigev_notify {
&SigevNotify::SigevThreadId { thread_id, .. } => thread_id,
_ => 0 as type_of_thread_id
};
}
#[cfg(not(any(target_os = "freebsd", target_os = "linux")))]
fn set_tid(_sev: &mut libc::sigevent, _sigev_notify: &SigevNotify) {
}
pub fn sigevent(&self) -> libc::sigevent {
self.sigevent
}
}
impl Debug for SigEvent {
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("SigEvent")
.field("sigev_notify", &self.sigevent.sigev_notify)
.field("sigev_signo", &self.sigevent.sigev_signo)
.field("sigev_value", &self.sigevent.sigev_value.sival_ptr)
.field("sigev_notify_thread_id",
&self.sigevent.sigev_notify_thread_id)
.finish()
}
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("SigEvent")
.field("sigev_notify", &self.sigevent.sigev_notify)
.field("sigev_signo", &self.sigevent.sigev_signo)
.field("sigev_value", &self.sigevent.sigev_value.sival_ptr)
.finish()
}
}
impl<'a> From<&'a libc::sigevent> for SigEvent {
fn from(sigevent: &libc::sigevent) -> Self {
SigEvent{ sigevent: sigevent.clone() }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_contains() {
let mut mask = SigSet::empty();
mask.add(SIGUSR1);
assert!(mask.contains(SIGUSR1));
assert!(!mask.contains(SIGUSR2));
let all = SigSet::all();
assert!(all.contains(SIGUSR1));
assert!(all.contains(SIGUSR2));
}
#[test]
fn test_clear() {
let mut set = SigSet::all();
set.clear();
for signal in Signal::iterator() {
assert!(!set.contains(signal));
}
}
#[test]
fn test_extend() {
let mut one_signal = SigSet::empty();
one_signal.add(SIGUSR1);
let mut two_signals = SigSet::empty();
two_signals.add(SIGUSR2);
two_signals.extend(&one_signal);
assert!(two_signals.contains(SIGUSR1));
assert!(two_signals.contains(SIGUSR2));
}
#[test]
fn test_thread_signal_get_mask() {
assert!(SigSet::thread_get_mask().is_ok());
}
#[test]
fn test_thread_signal_set_mask() {
let prev_mask = SigSet::thread_get_mask().expect("Failed to get existing signal mask!");
let mut test_mask = prev_mask;
test_mask.add(SIGUSR1);
assert!(test_mask.thread_set_mask().is_ok());
let new_mask = SigSet::thread_get_mask().expect("Failed to get new mask!");
assert!(new_mask.contains(SIGUSR1));
assert!(!new_mask.contains(SIGUSR2));
prev_mask.thread_set_mask().expect("Failed to revert signal mask!");
}
#[test]
fn test_thread_signal_block() {
let mut mask = SigSet::empty();
mask.add(SIGUSR1);
assert!(mask.thread_block().is_ok());
assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
}
#[test]
fn test_thread_signal_unblock() {
let mut mask = SigSet::empty();
mask.add(SIGUSR1);
assert!(mask.thread_unblock().is_ok());
assert!(!SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
}
#[test]
fn test_thread_signal_swap() {
let mut mask = SigSet::empty();
mask.add(SIGUSR1);
mask.thread_block().unwrap();
assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
let mut mask2 = SigSet::empty();
mask2.add(SIGUSR2);
let oldmask = mask2.thread_swap_mask(SigmaskHow::SIG_SETMASK).unwrap();
assert!(oldmask.contains(SIGUSR1));
assert!(!oldmask.contains(SIGUSR2));
assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR2));
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
#[test]
fn test_sigwait() {
let mut mask = SigSet::empty();
mask.add(SIGUSR1);
mask.add(SIGUSR2);
mask.thread_block().unwrap();
raise(SIGUSR1).unwrap();
assert_eq!(mask.wait().unwrap(), SIGUSR1);
}
}