diff options
Diffstat (limited to 'third_party/rust/nix/src/sys/signal.rs')
-rw-r--r-- | third_party/rust/nix/src/sys/signal.rs | 1557 |
1 files changed, 1557 insertions, 0 deletions
diff --git a/third_party/rust/nix/src/sys/signal.rs b/third_party/rust/nix/src/sys/signal.rs new file mode 100644 index 0000000000..c946e4a0b1 --- /dev/null +++ b/third_party/rust/nix/src/sys/signal.rs @@ -0,0 +1,1557 @@ +// Portions of this file are Copyright 2014 The Rust Project Developers. +// See https://www.rust-lang.org/policies/licenses. + +//! Operating system signals. + +use crate::errno::Errno; +use crate::{Error, Result}; +use cfg_if::cfg_if; +use std::fmt; +use std::mem; +#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] +use std::os::unix::io::RawFd; +use std::ptr; +use std::str::FromStr; + +#[cfg(not(any( + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox" +)))] +#[cfg(any(feature = "aio", feature = "signal"))] +pub use self::sigevent::*; + +#[cfg(any(feature = "aio", feature = "process", feature = "signal"))] +libc_enum! { + /// Types of operating system signals + // Currently there is only one definition of c_int in libc, as well as only one + // type for signal constants. + // We would prefer to use the libc::c_int alias in the repr attribute. Unfortunately + // this is not (yet) possible. + #[repr(i32)] + #[non_exhaustive] + #[cfg_attr(docsrs, doc(cfg(any(feature = "aio", feature = "signal"))))] + pub enum Signal { + /// Hangup + SIGHUP, + /// Interrupt + SIGINT, + /// Quit + SIGQUIT, + /// Illegal instruction (not reset when caught) + SIGILL, + /// Trace trap (not reset when caught) + SIGTRAP, + /// Abort + SIGABRT, + /// Bus error + SIGBUS, + /// Floating point exception + SIGFPE, + /// Kill (cannot be caught or ignored) + SIGKILL, + /// User defined signal 1 + SIGUSR1, + /// Segmentation violation + SIGSEGV, + /// User defined signal 2 + SIGUSR2, + /// Write on a pipe with no one to read it + SIGPIPE, + /// Alarm clock + SIGALRM, + /// Software termination signal from kill + SIGTERM, + /// Stack fault (obsolete) + #[cfg(all(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux"), + not(any(target_arch = "mips", target_arch = "mips64", + target_arch = "sparc64"))))] + SIGSTKFLT, + /// To parent on child stop or exit + SIGCHLD, + /// Continue a stopped process + SIGCONT, + /// Sendable stop signal not from tty + SIGSTOP, + /// Stop signal from tty + SIGTSTP, + /// To readers pgrp upon background tty read + SIGTTIN, + /// Like TTIN if (tp->t_local<OSTOP) + SIGTTOU, + /// Urgent condition on IO channel + SIGURG, + /// Exceeded CPU time limit + SIGXCPU, + /// Exceeded file size limit + SIGXFSZ, + /// Virtual time alarm + SIGVTALRM, + /// Profiling time alarm + SIGPROF, + /// Window size changes + SIGWINCH, + /// Input/output possible signal + #[cfg(not(target_os = "haiku"))] + #[cfg_attr(docsrs, doc(cfg(all())))] + SIGIO, + #[cfg(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux", + target_os = "aix"))] + #[cfg_attr(docsrs, doc(cfg(all())))] + /// Power failure imminent. + SIGPWR, + /// Bad system call + SIGSYS, + #[cfg(not(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux", + target_os = "redox", target_os = "haiku")))] + #[cfg_attr(docsrs, doc(cfg(all())))] + /// Emulator trap + SIGEMT, + #[cfg(not(any(target_os = "android", target_os = "emscripten", + target_os = "fuchsia", target_os = "linux", + target_os = "redox", target_os = "haiku", + target_os = "aix")))] + #[cfg_attr(docsrs, doc(cfg(all())))] + /// Information request + SIGINFO, + } + impl TryFrom<i32> +} + +#[cfg(feature = "signal")] +impl FromStr for Signal { + type Err = Error; + fn from_str(s: &str) -> Result<Signal> { + Ok(match s { + "SIGHUP" => Signal::SIGHUP, + "SIGINT" => Signal::SIGINT, + "SIGQUIT" => Signal::SIGQUIT, + "SIGILL" => Signal::SIGILL, + "SIGTRAP" => Signal::SIGTRAP, + "SIGABRT" => Signal::SIGABRT, + "SIGBUS" => Signal::SIGBUS, + "SIGFPE" => Signal::SIGFPE, + "SIGKILL" => Signal::SIGKILL, + "SIGUSR1" => Signal::SIGUSR1, + "SIGSEGV" => Signal::SIGSEGV, + "SIGUSR2" => Signal::SIGUSR2, + "SIGPIPE" => Signal::SIGPIPE, + "SIGALRM" => Signal::SIGALRM, + "SIGTERM" => Signal::SIGTERM, + #[cfg(all( + any( + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "linux" + ), + not(any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "sparc64" + )) + ))] + "SIGSTKFLT" => Signal::SIGSTKFLT, + "SIGCHLD" => Signal::SIGCHLD, + "SIGCONT" => Signal::SIGCONT, + "SIGSTOP" => Signal::SIGSTOP, + "SIGTSTP" => Signal::SIGTSTP, + "SIGTTIN" => Signal::SIGTTIN, + "SIGTTOU" => Signal::SIGTTOU, + "SIGURG" => Signal::SIGURG, + "SIGXCPU" => Signal::SIGXCPU, + "SIGXFSZ" => Signal::SIGXFSZ, + "SIGVTALRM" => Signal::SIGVTALRM, + "SIGPROF" => Signal::SIGPROF, + "SIGWINCH" => Signal::SIGWINCH, + #[cfg(not(target_os = "haiku"))] + "SIGIO" => Signal::SIGIO, + #[cfg(any( + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "linux" + ))] + "SIGPWR" => Signal::SIGPWR, + "SIGSYS" => Signal::SIGSYS, + #[cfg(not(any( + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "linux", + target_os = "redox", + target_os = "haiku" + )))] + "SIGEMT" => Signal::SIGEMT, + #[cfg(not(any( + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "linux", + target_os = "redox", + target_os = "aix", + target_os = "haiku" + )))] + "SIGINFO" => Signal::SIGINFO, + _ => return Err(Errno::EINVAL), + }) + } +} + +#[cfg(feature = "signal")] +impl Signal { + /// Returns name of signal. + /// + /// This function is equivalent to `<Signal as AsRef<str>>::as_ref()`, + /// with difference that returned string is `'static` + /// and not bound to `self`'s lifetime. + pub const fn as_str(self) -> &'static str { + match self { + Signal::SIGHUP => "SIGHUP", + Signal::SIGINT => "SIGINT", + Signal::SIGQUIT => "SIGQUIT", + Signal::SIGILL => "SIGILL", + Signal::SIGTRAP => "SIGTRAP", + Signal::SIGABRT => "SIGABRT", + Signal::SIGBUS => "SIGBUS", + Signal::SIGFPE => "SIGFPE", + Signal::SIGKILL => "SIGKILL", + Signal::SIGUSR1 => "SIGUSR1", + Signal::SIGSEGV => "SIGSEGV", + Signal::SIGUSR2 => "SIGUSR2", + Signal::SIGPIPE => "SIGPIPE", + Signal::SIGALRM => "SIGALRM", + Signal::SIGTERM => "SIGTERM", + #[cfg(all( + any( + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "linux" + ), + not(any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "sparc64" + )) + ))] + Signal::SIGSTKFLT => "SIGSTKFLT", + Signal::SIGCHLD => "SIGCHLD", + Signal::SIGCONT => "SIGCONT", + Signal::SIGSTOP => "SIGSTOP", + Signal::SIGTSTP => "SIGTSTP", + Signal::SIGTTIN => "SIGTTIN", + Signal::SIGTTOU => "SIGTTOU", + Signal::SIGURG => "SIGURG", + Signal::SIGXCPU => "SIGXCPU", + Signal::SIGXFSZ => "SIGXFSZ", + Signal::SIGVTALRM => "SIGVTALRM", + Signal::SIGPROF => "SIGPROF", + Signal::SIGWINCH => "SIGWINCH", + #[cfg(not(target_os = "haiku"))] + Signal::SIGIO => "SIGIO", + #[cfg(any( + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "aix", + target_os = "linux" + ))] + Signal::SIGPWR => "SIGPWR", + Signal::SIGSYS => "SIGSYS", + #[cfg(not(any( + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "linux", + target_os = "redox", + target_os = "haiku" + )))] + Signal::SIGEMT => "SIGEMT", + #[cfg(not(any( + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "linux", + target_os = "redox", + target_os = "aix", + target_os = "haiku" + )))] + Signal::SIGINFO => "SIGINFO", + } + } +} + +#[cfg(feature = "signal")] +impl AsRef<str> for Signal { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +#[cfg(feature = "signal")] +impl fmt::Display for Signal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.as_ref()) + } +} + +#[cfg(feature = "signal")] +pub use self::Signal::*; + +#[cfg(target_os = "redox")] +#[cfg(feature = "signal")] +const SIGNALS: [Signal; 29] = [ + 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, +]; +#[cfg(target_os = "haiku")] +#[cfg(feature = "signal")] +const SIGNALS: [Signal; 28] = [ + 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, SIGSYS, +]; +#[cfg(all( + any( + target_os = "linux", + target_os = "android", + target_os = "emscripten", + target_os = "fuchsia" + ), + not(any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "sparc64" + )) +))] +#[cfg(feature = "signal")] +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_os = "fuchsia" + ), + any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64") +))] +#[cfg(feature = "signal")] +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(target_os = "aix")] +#[cfg(feature = "signal")] +const SIGNALS: [Signal; 30] = [ + SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGEMT, SIGFPE, SIGKILL, SIGSEGV, + SIGSYS, SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2, SIGPWR, SIGWINCH, + SIGURG, SIGPOLL, SIGIO, SIGSTOP, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU, + SIGVTALRM, SIGPROF, SIGXCPU, SIGXFSZ, SIGTRAP, +]; +#[cfg(not(any( + target_os = "linux", + target_os = "android", + target_os = "fuchsia", + target_os = "emscripten", + target_os = "aix", + target_os = "redox", + target_os = "haiku" +)))] +#[cfg(feature = "signal")] +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, +]; + +feature! { +#![feature = "signal"] + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +/// Iterate through all signals defined by this operating system +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 { + /// Iterate through all signals defined by this OS + pub const fn iterator() -> SignalIterator { + SignalIterator{next: 0} + } +} + +/// Alias for [`SIGABRT`] +pub const SIGIOT : Signal = SIGABRT; +/// Alias for [`SIGIO`] +#[cfg(not(target_os = "haiku"))] +pub const SIGPOLL : Signal = SIGIO; +/// Alias for [`SIGSYS`] +pub const SIGUNUSED : Signal = SIGSYS; + +cfg_if! { + if #[cfg(target_os = "redox")] { + type SaFlags_t = libc::c_ulong; + } else if #[cfg(target_env = "uclibc")] { + type SaFlags_t = libc::c_ulong; + } else { + type SaFlags_t = libc::c_int; + } +} +} + +#[cfg(feature = "signal")] +libc_bitflags! { + /// Controls the behavior of a [`SigAction`] + #[cfg_attr(docsrs, doc(cfg(feature = "signal")))] + pub struct SaFlags: SaFlags_t { + /// When catching a [`Signal::SIGCHLD`] signal, the signal will be + /// generated only when a child process exits, not when a child process + /// stops. + SA_NOCLDSTOP; + /// When catching a [`Signal::SIGCHLD`] signal, the system will not + /// create zombie processes when children of the calling process exit. + SA_NOCLDWAIT; + /// Further occurrences of the delivered signal are not masked during + /// the execution of the handler. + SA_NODEFER; + /// The system will deliver the signal to the process on a signal stack, + /// specified by each thread with sigaltstack(2). + SA_ONSTACK; + /// The handler is reset back to the default at the moment the signal is + /// delivered. + SA_RESETHAND; + /// Requests that certain system calls restart if interrupted by this + /// signal. See the man page for complete details. + SA_RESTART; + /// This flag is controlled internally by Nix. + SA_SIGINFO; + } +} + +#[cfg(feature = "signal")] +libc_enum! { + /// Specifies how certain functions should manipulate a signal mask + #[repr(i32)] + #[non_exhaustive] + #[cfg_attr(docsrs, doc(cfg(feature = "signal")))] + pub enum SigmaskHow { + /// The new mask is the union of the current mask and the specified set. + SIG_BLOCK, + /// The new mask is the intersection of the current mask and the + /// complement of the specified set. + SIG_UNBLOCK, + /// The current mask is replaced by the specified set. + SIG_SETMASK, + } +} + +feature! { +#![feature = "signal"] + +use crate::unistd::Pid; +use std::iter::Extend; +use std::iter::FromIterator; +use std::iter::IntoIterator; + +/// Specifies a set of [`Signal`]s that may be blocked, waited for, etc. +// We are using `transparent` here to be super sure that `SigSet` +// is represented exactly like the `sigset_t` struct from C. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct SigSet { + sigset: libc::sigset_t +} + +impl SigSet { + /// Initialize to include all signals. + #[doc(alias("sigfillset"))] + pub fn all() -> SigSet { + let mut sigset = mem::MaybeUninit::uninit(); + let _ = unsafe { libc::sigfillset(sigset.as_mut_ptr()) }; + + unsafe{ SigSet { sigset: sigset.assume_init() } } + } + + /// Initialize to include nothing. + #[doc(alias("sigemptyset"))] + pub fn empty() -> SigSet { + let mut sigset = mem::MaybeUninit::uninit(); + let _ = unsafe { libc::sigemptyset(sigset.as_mut_ptr()) }; + + unsafe{ SigSet { sigset: sigset.assume_init() } } + } + + /// Add the specified signal to the set. + #[doc(alias("sigaddset"))] + pub fn add(&mut self, signal: Signal) { + unsafe { libc::sigaddset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) }; + } + + /// Remove all signals from this set. + #[doc(alias("sigemptyset"))] + pub fn clear(&mut self) { + unsafe { libc::sigemptyset(&mut self.sigset as *mut libc::sigset_t) }; + } + + /// Remove the specified signal from this set. + #[doc(alias("sigdelset"))] + pub fn remove(&mut self, signal: Signal) { + unsafe { libc::sigdelset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) }; + } + + /// Return whether this set includes the specified signal. + #[doc(alias("sigismember"))] + 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"), + } + } + + /// Returns an iterator that yields the signals contained in this set. + pub fn iter(&self) -> SigSetIter<'_> { + self.into_iter() + } + + /// Gets the currently blocked (masked) set of signals for the calling thread. + pub fn thread_get_mask() -> Result<SigSet> { + let mut oldmask = mem::MaybeUninit::uninit(); + do_pthread_sigmask(SigmaskHow::SIG_SETMASK, None, Some(oldmask.as_mut_ptr()))?; + Ok(unsafe{ SigSet{sigset: oldmask.assume_init()}}) + } + + /// Sets the set of signals as the signal mask for the calling thread. + pub fn thread_set_mask(&self) -> Result<()> { + pthread_sigmask(SigmaskHow::SIG_SETMASK, Some(self), None) + } + + /// Adds the set of signals to the signal mask for the calling thread. + pub fn thread_block(&self) -> Result<()> { + pthread_sigmask(SigmaskHow::SIG_BLOCK, Some(self), None) + } + + /// Removes the set of signals from the signal mask for the calling thread. + pub fn thread_unblock(&self) -> Result<()> { + pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(self), None) + } + + /// Sets the set of signals as the signal mask, and returns the old mask. + pub fn thread_swap_mask(&self, how: SigmaskHow) -> Result<SigSet> { + let mut oldmask = mem::MaybeUninit::uninit(); + do_pthread_sigmask(how, Some(self), Some(oldmask.as_mut_ptr()))?; + Ok(unsafe{ SigSet{sigset: oldmask.assume_init()}}) + } + + /// Suspends execution of the calling thread until one of the signals in the + /// signal mask becomes pending, and returns the accepted signal. + #[cfg(not(target_os = "redox"))] // RedoxFS does not yet support sigwait + #[cfg_attr(docsrs, doc(cfg(all())))] + pub fn wait(&self) -> Result<Signal> { + use std::convert::TryFrom; + + let mut signum = mem::MaybeUninit::uninit(); + let res = unsafe { libc::sigwait(&self.sigset as *const libc::sigset_t, signum.as_mut_ptr()) }; + + Errno::result(res).map(|_| unsafe { + Signal::try_from(signum.assume_init()).unwrap() + }) + } + + /// Converts a `libc::sigset_t` object to a [`SigSet`] without checking whether the + /// `libc::sigset_t` is already initialized. + /// + /// # Safety + /// + /// The `sigset` passed in must be a valid an initialized `libc::sigset_t` by calling either + /// [`sigemptyset(3)`](https://man7.org/linux/man-pages/man3/sigemptyset.3p.html) or + /// [`sigfillset(3)`](https://man7.org/linux/man-pages/man3/sigfillset.3p.html). + /// Otherwise, the results are undefined. + pub unsafe fn from_sigset_t_unchecked(sigset: libc::sigset_t) -> SigSet { + SigSet { sigset } + } +} + +impl AsRef<libc::sigset_t> for SigSet { + fn as_ref(&self) -> &libc::sigset_t { + &self.sigset + } +} + +// TODO: Consider specialization for the case where T is &SigSet and libc::sigorset is available. +impl Extend<Signal> for SigSet { + fn extend<T>(&mut self, iter: T) + where T: IntoIterator<Item = Signal> { + for signal in iter { + self.add(signal); + } + } +} + +impl FromIterator<Signal> for SigSet { + fn from_iter<T>(iter: T) -> Self + where T: IntoIterator<Item = Signal> { + let mut sigset = SigSet::empty(); + sigset.extend(iter); + sigset + } +} + +/// Iterator for a [`SigSet`]. +/// +/// Call [`SigSet::iter`] to create an iterator. +#[derive(Clone, Debug)] +pub struct SigSetIter<'a> { + sigset: &'a SigSet, + inner: SignalIterator, +} + +impl Iterator for SigSetIter<'_> { + type Item = Signal; + fn next(&mut self) -> Option<Signal> { + loop { + match self.inner.next() { + None => return None, + Some(signal) if self.sigset.contains(signal) => return Some(signal), + Some(_signal) => continue, + } + } + } +} + +impl<'a> IntoIterator for &'a SigSet { + type Item = Signal; + type IntoIter = SigSetIter<'a>; + fn into_iter(self) -> Self::IntoIter { + SigSetIter { sigset: self, inner: Signal::iterator() } + } +} + +/// A signal handler. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SigHandler { + /// Default signal handling. + SigDfl, + /// Request that the signal be ignored. + SigIgn, + /// Use the given signal-catching function, which takes in the signal. + Handler(extern fn(libc::c_int)), + /// Use the given signal-catching function, which takes in the signal, information about how + /// the signal was generated, and a pointer to the threads `ucontext_t`. + #[cfg(not(target_os = "redox"))] + #[cfg_attr(docsrs, doc(cfg(all())))] + SigAction(extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)) +} + +/// Action to take on receipt of a signal. Corresponds to `sigaction`. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct SigAction { + sigaction: libc::sigaction +} + +impl SigAction { + /// Creates a new action. + /// + /// The `SA_SIGINFO` bit in the `flags` argument is ignored (it will be set only if `handler` + /// is the `SigAction` variant). `mask` specifies other signals to block during execution of + /// the signal-catching function. + pub fn new(handler: SigHandler, flags: SaFlags, mask: SigSet) -> SigAction { + #[cfg(not(target_os = "aix"))] + unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) { + (*p).sa_sigaction = match handler { + SigHandler::SigDfl => libc::SIG_DFL, + SigHandler::SigIgn => libc::SIG_IGN, + SigHandler::Handler(f) => f as *const extern fn(libc::c_int) as usize, + #[cfg(not(target_os = "redox"))] + SigHandler::SigAction(f) => f as *const extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void) as usize, + }; + } + + #[cfg(target_os = "aix")] + unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) { + (*p).sa_union.__su_sigaction = match handler { + SigHandler::SigDfl => mem::transmute::<usize, extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)>(libc::SIG_DFL), + SigHandler::SigIgn => mem::transmute::<usize, extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)>(libc::SIG_IGN), + SigHandler::Handler(f) => mem::transmute::<extern "C" fn(i32), extern "C" fn(i32, *mut libc::siginfo_t, *mut libc::c_void)>(f), + SigHandler::SigAction(f) => f, + }; + } + + let mut s = mem::MaybeUninit::<libc::sigaction>::uninit(); + unsafe { + let p = s.as_mut_ptr(); + install_sig(p, handler); + (*p).sa_flags = match handler { + #[cfg(not(target_os = "redox"))] + SigHandler::SigAction(_) => (flags | SaFlags::SA_SIGINFO).bits(), + _ => (flags - SaFlags::SA_SIGINFO).bits(), + }; + (*p).sa_mask = mask.sigset; + + SigAction { sigaction: s.assume_init() } + } + } + + /// Returns the flags set on the action. + pub fn flags(&self) -> SaFlags { + SaFlags::from_bits_truncate(self.sigaction.sa_flags) + } + + /// Returns the set of signals that are blocked during execution of the action's + /// signal-catching function. + pub fn mask(&self) -> SigSet { + SigSet { sigset: self.sigaction.sa_mask } + } + + /// Returns the action's handler. + #[cfg(not(target_os = "aix"))] + pub fn handler(&self) -> SigHandler { + match self.sigaction.sa_sigaction { + libc::SIG_DFL => SigHandler::SigDfl, + libc::SIG_IGN => SigHandler::SigIgn, + #[cfg(not(target_os = "redox"))] + p if self.flags().contains(SaFlags::SA_SIGINFO) => + SigHandler::SigAction( + // Safe for one of two reasons: + // * The SigHandler was created by SigHandler::new, in which + // case the pointer is correct, or + // * The SigHandler was created by signal or sigaction, which + // are unsafe functions, so the caller should've somehow + // ensured that it is correctly initialized. + unsafe{ + *(&p as *const usize + as *const extern fn(_, _, _)) + } + as extern fn(_, _, _)), + p => SigHandler::Handler( + // Safe for one of two reasons: + // * The SigHandler was created by SigHandler::new, in which + // case the pointer is correct, or + // * The SigHandler was created by signal or sigaction, which + // are unsafe functions, so the caller should've somehow + // ensured that it is correctly initialized. + unsafe{ + *(&p as *const usize + as *const extern fn(libc::c_int)) + } + as extern fn(libc::c_int)), + } + } + + /// Returns the action's handler. + #[cfg(target_os = "aix")] + pub fn handler(&self) -> SigHandler { + unsafe { + match self.sigaction.sa_union.__su_sigaction as usize { + libc::SIG_DFL => SigHandler::SigDfl, + libc::SIG_IGN => SigHandler::SigIgn, + p if self.flags().contains(SaFlags::SA_SIGINFO) => + SigHandler::SigAction( + *(&p as *const usize + as *const extern fn(_, _, _)) + as extern fn(_, _, _)), + p => SigHandler::Handler( + *(&p as *const usize + as *const extern fn(libc::c_int)) + as extern fn(libc::c_int)), + } + } + } +} + +/// Changes the action taken by a process on receipt of a specific signal. +/// +/// `signal` can be any signal except `SIGKILL` or `SIGSTOP`. On success, it returns the previous +/// action for the given signal. If `sigaction` fails, no new signal handler is installed. +/// +/// # Safety +/// +/// * Signal handlers may be called at any point during execution, which limits +/// what is safe to do in the body of the signal-catching function. Be certain +/// to only make syscalls that are explicitly marked safe for signal handlers +/// and only share global data using atomics. +/// +/// * There is also no guarantee that the old signal handler was installed +/// correctly. If it was installed by this crate, it will be. But if it was +/// installed by, for example, C code, then there is no guarantee its function +/// pointer is valid. In that case, this function effectively dereferences a +/// raw pointer of unknown provenance. +pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigAction> { + let mut oldact = mem::MaybeUninit::<libc::sigaction>::uninit(); + + let res = libc::sigaction(signal as libc::c_int, + &sigaction.sigaction as *const libc::sigaction, + oldact.as_mut_ptr()); + + Errno::result(res).map(|_| SigAction { sigaction: oldact.assume_init() }) +} + +/// Signal management (see [signal(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html)) +/// +/// Installs `handler` for the given `signal`, returning the previous signal +/// handler. `signal` should only be used following another call to `signal` or +/// if the current handler is the default. The return value of `signal` is +/// undefined after setting the handler with [`sigaction`][SigActionFn]. +/// +/// # Safety +/// +/// If the pointer to the previous signal handler is invalid, undefined +/// behavior could be invoked when casting it back to a [`SigAction`][SigActionStruct]. +/// +/// # Examples +/// +/// Ignore `SIGINT`: +/// +/// ```no_run +/// # use nix::sys::signal::{self, Signal, SigHandler}; +/// unsafe { signal::signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap(); +/// ``` +/// +/// Use a signal handler to set a flag variable: +/// +/// ```no_run +/// # use std::convert::TryFrom; +/// # use std::sync::atomic::{AtomicBool, Ordering}; +/// # use nix::sys::signal::{self, Signal, SigHandler}; +/// static SIGNALED: AtomicBool = AtomicBool::new(false); +/// +/// extern fn handle_sigint(signal: libc::c_int) { +/// let signal = Signal::try_from(signal).unwrap(); +/// SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed); +/// } +/// +/// fn main() { +/// let handler = SigHandler::Handler(handle_sigint); +/// unsafe { signal::signal(Signal::SIGINT, handler) }.unwrap(); +/// } +/// ``` +/// +/// # Errors +/// +/// Returns [`Error(Errno::EOPNOTSUPP)`] if `handler` is +/// [`SigAction`][SigActionStruct]. Use [`sigaction`][SigActionFn] instead. +/// +/// `signal` also returns any error from `libc::signal`, such as when an attempt +/// is made to catch a signal that cannot be caught or to ignore a signal that +/// cannot be ignored. +/// +/// [`Error::UnsupportedOperation`]: ../../enum.Error.html#variant.UnsupportedOperation +/// [SigActionStruct]: struct.SigAction.html +/// [sigactionFn]: fn.sigaction.html +pub unsafe fn signal(signal: Signal, handler: SigHandler) -> Result<SigHandler> { + let signal = signal as libc::c_int; + let res = match handler { + SigHandler::SigDfl => libc::signal(signal, libc::SIG_DFL), + SigHandler::SigIgn => libc::signal(signal, libc::SIG_IGN), + SigHandler::Handler(handler) => libc::signal(signal, handler as libc::sighandler_t), + #[cfg(not(target_os = "redox"))] + SigHandler::SigAction(_) => return Err(Errno::ENOTSUP), + }; + Errno::result(res).map(|oldhandler| { + match oldhandler { + libc::SIG_DFL => SigHandler::SigDfl, + libc::SIG_IGN => SigHandler::SigIgn, + p => SigHandler::Handler( + *(&p as *const usize + as *const extern fn(libc::c_int)) + as extern fn(libc::c_int)), + } + }) +} + +fn do_pthread_sigmask(how: SigmaskHow, + set: Option<&SigSet>, + oldset: Option<*mut libc::sigset_t>) -> Result<()> { + if set.is_none() && oldset.is_none() { + return Ok(()) + } + + let res = unsafe { + // if set or oldset is None, pass in null pointers instead + 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.unwrap_or(ptr::null_mut()) + ) + }; + + Errno::result(res).map(drop) +} + +/// Manages the signal mask (set of blocked signals) for the calling thread. +/// +/// If the `set` parameter is `Some(..)`, then the signal mask will be updated with the signal set. +/// The `how` flag decides the type of update. If `set` is `None`, `how` will be ignored, +/// and no modification will take place. +/// +/// If the 'oldset' parameter is `Some(..)` then the current signal mask will be written into it. +/// +/// If both `set` and `oldset` is `Some(..)`, the current signal mask will be written into oldset, +/// and then it will be updated with `set`. +/// +/// If both `set` and `oldset` is None, this function is a no-op. +/// +/// For more information, visit the [`pthread_sigmask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html), +/// or [`sigprocmask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html) man pages. +pub fn pthread_sigmask(how: SigmaskHow, + set: Option<&SigSet>, + oldset: Option<&mut SigSet>) -> Result<()> +{ + do_pthread_sigmask(how, set, oldset.map(|os| &mut os.sigset as *mut _ )) +} + +/// Examine and change blocked signals. +/// +/// For more information see the [`sigprocmask` man +/// pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html). +pub fn sigprocmask(how: SigmaskHow, set: Option<&SigSet>, oldset: Option<&mut SigSet>) -> Result<()> { + if set.is_none() && oldset.is_none() { + return Ok(()) + } + + let res = unsafe { + // if set or oldset is None, pass in null pointers instead + libc::sigprocmask(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) +} + +/// Send a signal to a process +/// +/// # Arguments +/// +/// * `pid` - Specifies which processes should receive the signal. +/// - If positive, specifies an individual process. +/// - If zero, the signal will be sent to all processes whose group +/// ID is equal to the process group ID of the sender. This is a +#[cfg_attr(target_os = "fuchsia", doc = "variant of `killpg`.")] +#[cfg_attr(not(target_os = "fuchsia"), doc = "variant of [`killpg`].")] +/// - If `-1` and the process has super-user privileges, the signal +/// is sent to all processes exclusing system processes. +/// - If less than `-1`, the signal is sent to all processes whose +/// process group ID is equal to the absolute value of `pid`. +/// * `signal` - Signal to send. If `None`, error checking is performed +/// but no signal is actually sent. +/// +/// See Also +/// [`kill(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html) +pub fn kill<T: Into<Option<Signal>>>(pid: Pid, signal: T) -> Result<()> { + let res = unsafe { libc::kill(pid.into(), + match signal.into() { + Some(s) => s as libc::c_int, + None => 0, + }) }; + + Errno::result(res).map(drop) +} + +/// Send a signal to a process group +/// +/// # Arguments +/// +/// * `pgrp` - Process group to signal. If less then or equal 1, the behavior +/// is platform-specific. +/// * `signal` - Signal to send. If `None`, `killpg` will only preform error +/// checking and won't send any signal. +/// +/// See Also [killpg(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html). +#[cfg(not(target_os = "fuchsia"))] +pub fn killpg<T: Into<Option<Signal>>>(pgrp: Pid, signal: T) -> Result<()> { + let res = unsafe { libc::killpg(pgrp.into(), + match signal.into() { + Some(s) => s as libc::c_int, + None => 0, + }) }; + + Errno::result(res).map(drop) +} + +/// Send a signal to the current thread +/// +/// See Also [raise(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/raise.html) +pub fn raise(signal: Signal) -> Result<()> { + let res = unsafe { libc::raise(signal as libc::c_int) }; + + Errno::result(res).map(drop) +} +} + +feature! { +#![any(feature = "aio", feature = "signal")] + +/// Identifies a thread for [`SigevNotify::SigevThreadId`] +#[cfg(target_os = "freebsd")] +pub type type_of_thread_id = libc::lwpid_t; +/// Identifies a thread for [`SigevNotify::SigevThreadId`] +#[cfg(any(target_env = "gnu", target_env = "uclibc"))] +pub type type_of_thread_id = libc::pid_t; + +/// Specifies the notification method used by a [`SigEvent`] +// sigval is actually a union of a int and a void*. But it's never really used +// as a pointer, because neither libc nor the kernel ever dereference it. nix +// therefore presents it as an intptr_t, which is how kevent uses it. +#[cfg(not(any(target_os = "fuchsia", target_os = "openbsd", target_os = "redox")))] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SigevNotify { + /// No notification will be delivered + SigevNone, + /// Notify by delivering a signal to the process. + SigevSignal { + /// Signal to deliver + signal: Signal, + /// Will be present in the `si_value` field of the [`libc::siginfo_t`] + /// structure of the queued signal. + si_value: libc::intptr_t + }, + // Note: SIGEV_THREAD is not implemented, but could be if desired. + /// Notify by delivering an event to a kqueue. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + #[cfg_attr(docsrs, doc(cfg(all())))] + SigevKevent { + /// File descriptor of the kqueue to notify. + kq: RawFd, + /// Will be contained in the kevent's `udata` field. + udata: libc::intptr_t + }, + /// Notify by delivering an event to a kqueue, with optional event flags set + #[cfg(target_os = "freebsd")] + #[cfg_attr(docsrs, doc(cfg(all())))] + #[cfg(feature = "event")] + SigevKeventFlags { + /// File descriptor of the kqueue to notify. + kq: RawFd, + /// Will be contained in the kevent's `udata` field. + udata: libc::intptr_t, + /// Flags that will be set on the delivered event. See `kevent(2)`. + flags: crate::sys::event::EventFlag + }, + /// Notify by delivering a signal to a thread. + #[cfg(any( + target_os = "freebsd", + target_env = "gnu", + target_env = "uclibc", + ))] + #[cfg_attr(docsrs, doc(cfg(all())))] + SigevThreadId { + /// Signal to send + signal: Signal, + /// LWP ID of the thread to notify + thread_id: type_of_thread_id, + /// Will be present in the `si_value` field of the [`libc::siginfo_t`] + /// structure of the queued signal. + si_value: libc::intptr_t + }, +} +} + +#[cfg(not(any( + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox" +)))] +#[cfg_attr(docsrs, doc(cfg(all())))] +mod sigevent { + feature! { + #![any(feature = "aio", feature = "signal")] + + use std::mem; + use super::SigevNotify; + + #[cfg(target_os = "freebsd")] + pub(crate) use ffi::sigevent as libc_sigevent; + #[cfg(not(target_os = "freebsd"))] + pub(crate) use libc::sigevent as libc_sigevent; + + // For FreeBSD only, we define the C structure here. Because the structure + // defined in libc isn't correct. The real sigevent contains union fields, + // but libc could not represent those when sigevent was originally added, so + // instead libc simply defined the most useful field. Now that Rust can + // represent unions, there's a PR to libc to fix it. However, it's stuck + // forever due to backwards compatibility concerns. Even though there's a + // workaround, libc refuses to merge it. I think it's just too complicated + // for them to want to think about right now, because that project is + // short-staffed. So we define it here instead, so we won't have to wait on + // libc. + // https://github.com/rust-lang/libc/pull/2813 + #[cfg(target_os = "freebsd")] + mod ffi { + use std::{fmt, hash}; + + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + #[repr(C)] + pub struct __c_anonymous_sigev_thread { + pub _function: *mut libc::c_void, // Actually a function pointer + pub _attribute: *mut libc::pthread_attr_t, + } + #[derive(Clone, Copy)] + // This will never be used on its own, and its parent has a Debug impl, + // so it doesn't need one. + #[allow(missing_debug_implementations)] + #[repr(C)] + pub union __c_anonymous_sigev_un { + pub _threadid: libc::__lwpid_t, + pub _sigev_thread: __c_anonymous_sigev_thread, + pub _kevent_flags: libc::c_ushort, + __spare__: [libc::c_long; 8], + } + + #[derive(Clone, Copy)] + #[repr(C)] + pub struct sigevent { + pub sigev_notify: libc::c_int, + pub sigev_signo: libc::c_int, + pub sigev_value: libc::sigval, + pub _sigev_un: __c_anonymous_sigev_un, + } + + impl fmt::Debug for sigevent { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut ds = f.debug_struct("sigevent"); + ds.field("sigev_notify", &self.sigev_notify) + .field("sigev_signo", &self.sigev_signo) + .field("sigev_value", &self.sigev_value); + // Safe because we check the sigev_notify discriminant + unsafe { + match self.sigev_notify { + libc::SIGEV_KEVENT => { + ds.field("sigev_notify_kevent_flags", &self._sigev_un._kevent_flags); + } + libc::SIGEV_THREAD_ID => { + ds.field("sigev_notify_thread_id", &self._sigev_un._threadid); + } + libc::SIGEV_THREAD => { + ds.field("sigev_notify_function", &self._sigev_un._sigev_thread._function); + ds.field("sigev_notify_attributes", &self._sigev_un._sigev_thread._attribute); + } + _ => () + }; + } + ds.finish() + } + } + + impl PartialEq for sigevent { + fn eq(&self, other: &Self) -> bool { + let mut equals = self.sigev_notify == other.sigev_notify; + equals &= self.sigev_signo == other.sigev_signo; + equals &= self.sigev_value == other.sigev_value; + // Safe because we check the sigev_notify discriminant + unsafe { + match self.sigev_notify { + libc::SIGEV_KEVENT => { + equals &= self._sigev_un._kevent_flags == other._sigev_un._kevent_flags; + } + libc::SIGEV_THREAD_ID => { + equals &= self._sigev_un._threadid == other._sigev_un._threadid; + } + libc::SIGEV_THREAD => { + equals &= self._sigev_un._sigev_thread == other._sigev_un._sigev_thread; + } + _ => /* The union field is don't care */ () + } + } + equals + } + } + + impl Eq for sigevent {} + + impl hash::Hash for sigevent { + fn hash<H: hash::Hasher>(&self, s: &mut H) { + self.sigev_notify.hash(s); + self.sigev_signo.hash(s); + self.sigev_value.hash(s); + // Safe because we check the sigev_notify discriminant + unsafe { + match self.sigev_notify { + libc::SIGEV_KEVENT => { + self._sigev_un._kevent_flags.hash(s); + } + libc::SIGEV_THREAD_ID => { + self._sigev_un._threadid.hash(s); + } + libc::SIGEV_THREAD => { + self._sigev_un._sigev_thread.hash(s); + } + _ => /* The union field is don't care */ () + } + } + } + } + } + + /// Used to request asynchronous notification of the completion of certain + /// events, such as POSIX AIO and timers. + #[repr(C)] + #[derive(Clone, Debug, Eq, Hash, PartialEq)] + // It can't be Copy on all platforms. + #[allow(missing_copy_implementations)] + pub struct SigEvent { + sigevent: libc_sigevent + } + + impl SigEvent { + /// **Note:** this constructor does not allow the user to set the + /// `sigev_notify_kevent_flags` field. That's considered ok because on FreeBSD + /// at least those flags don't do anything useful. That field is part of a + /// union that shares space with the more genuinely useful fields. + /// + /// **Note:** This constructor also doesn't allow the caller to set the + /// `sigev_notify_function` or `sigev_notify_attributes` fields, which are + /// required for `SIGEV_THREAD`. That's considered ok because on no operating + /// system is `SIGEV_THREAD` the most efficient way to deliver AIO + /// notification. FreeBSD and DragonFly BSD programs should prefer `SIGEV_KEVENT`. + /// Linux, Solaris, and portable programs should prefer `SIGEV_THREAD_ID` or + /// `SIGEV_SIGNAL`. That field is part of a union that shares space with the + /// more genuinely useful `sigev_notify_thread_id` + pub fn new(sigev_notify: SigevNotify) -> SigEvent { + let mut sev: libc_sigevent = unsafe { mem::zeroed() }; + match sigev_notify { + SigevNotify::SigevNone => { + sev.sigev_notify = libc::SIGEV_NONE; + }, + SigevNotify::SigevSignal{signal, si_value} => { + sev.sigev_notify = libc::SIGEV_SIGNAL; + sev.sigev_signo = signal as libc::c_int; + sev.sigev_value.sival_ptr = si_value as *mut libc::c_void + }, + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + SigevNotify::SigevKevent{kq, udata} => { + sev.sigev_notify = libc::SIGEV_KEVENT; + sev.sigev_signo = kq; + sev.sigev_value.sival_ptr = udata as *mut libc::c_void; + }, + #[cfg(target_os = "freebsd")] + #[cfg(feature = "event")] + SigevNotify::SigevKeventFlags{kq, udata, flags} => { + sev.sigev_notify = libc::SIGEV_KEVENT; + sev.sigev_signo = kq; + sev.sigev_value.sival_ptr = udata as *mut libc::c_void; + sev._sigev_un._kevent_flags = flags.bits(); + }, + #[cfg(target_os = "freebsd")] + SigevNotify::SigevThreadId{signal, thread_id, si_value} => { + sev.sigev_notify = libc::SIGEV_THREAD_ID; + sev.sigev_signo = signal as libc::c_int; + sev.sigev_value.sival_ptr = si_value as *mut libc::c_void; + sev._sigev_un._threadid = thread_id; + } + #[cfg(any(target_env = "gnu", target_env = "uclibc"))] + SigevNotify::SigevThreadId{signal, thread_id, si_value} => { + sev.sigev_notify = libc::SIGEV_THREAD_ID; + sev.sigev_signo = signal as libc::c_int; + sev.sigev_value.sival_ptr = si_value as *mut libc::c_void; + sev.sigev_notify_thread_id = thread_id; + } + } + SigEvent{sigevent: sev} + } + + /// Return a copy of the inner structure + #[cfg(target_os = "freebsd")] + pub fn sigevent(&self) -> libc::sigevent { + // Safe because they're really the same structure. See + // https://github.com/rust-lang/libc/pull/2813 + unsafe { + mem::transmute::<libc_sigevent, libc::sigevent>(self.sigevent) + } + } + + /// Return a copy of the inner structure + #[cfg(not(target_os = "freebsd"))] + pub fn sigevent(&self) -> libc::sigevent { + self.sigevent + } + + /// Returns a mutable pointer to the `sigevent` wrapped by `self` + #[cfg(target_os = "freebsd")] + pub fn as_mut_ptr(&mut self) -> *mut libc::sigevent { + // Safe because they're really the same structure. See + // https://github.com/rust-lang/libc/pull/2813 + &mut self.sigevent as *mut libc_sigevent as *mut libc::sigevent + } + + /// Returns a mutable pointer to the `sigevent` wrapped by `self` + #[cfg(not(target_os = "freebsd"))] + pub fn as_mut_ptr(&mut self) -> *mut libc::sigevent { + &mut self.sigevent + } + } + + impl<'a> From<&'a libc::sigevent> for SigEvent { + #[cfg(target_os = "freebsd")] + fn from(sigevent: &libc::sigevent) -> Self { + // Safe because they're really the same structure. See + // https://github.com/rust-lang/libc/pull/2813 + let sigevent = unsafe { + mem::transmute::<libc::sigevent, libc_sigevent>(*sigevent) + }; + SigEvent{ sigevent } + } + #[cfg(not(target_os = "freebsd"))] + fn from(sigevent: &libc::sigevent) -> Self { + SigEvent{ sigevent: *sigevent } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[cfg(not(target_os = "redox"))] + use std::thread; + + #[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_from_str_round_trips() { + for signal in Signal::iterator() { + assert_eq!(signal.as_ref().parse::<Signal>().unwrap(), signal); + assert_eq!(signal.to_string().parse::<Signal>().unwrap(), signal); + } + } + + #[test] + fn test_from_str_invalid_value() { + let errval = Err(Errno::EINVAL); + assert_eq!("NOSIGNAL".parse::<Signal>(), errval); + assert_eq!("kill".parse::<Signal>(), errval); + assert_eq!("9".parse::<Signal>(), errval); + } + + #[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] + #[cfg(not(target_os = "redox"))] + fn test_thread_signal_set_mask() { + thread::spawn(|| { + let prev_mask = SigSet::thread_get_mask() + .expect("Failed to get existing signal mask!"); + + let mut test_mask = prev_mask; + test_mask.add(SIGUSR1); + + test_mask.thread_set_mask().expect("assertion failed"); + 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!"); + }) + .join() + .unwrap(); + } + + #[test] + #[cfg(not(target_os = "redox"))] + fn test_thread_signal_block() { + thread::spawn(|| { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + + mask.thread_block().expect("assertion failed"); + + assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); + }) + .join() + .unwrap(); + } + + #[test] + #[cfg(not(target_os = "redox"))] + fn test_thread_signal_unblock() { + thread::spawn(|| { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + + mask.thread_unblock().expect("assertion failed"); + + assert!(!SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); + }) + .join() + .unwrap(); + } + + #[test] + #[cfg(not(target_os = "redox"))] + fn test_thread_signal_swap() { + thread::spawn(|| { + 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)); + }) + .join() + .unwrap(); + } + + #[test] + fn test_from_and_into_iterator() { + let sigset = SigSet::from_iter(vec![Signal::SIGUSR1, Signal::SIGUSR2]); + let signals = sigset.into_iter().collect::<Vec<Signal>>(); + assert_eq!(signals, [Signal::SIGUSR1, Signal::SIGUSR2]); + } + + #[test] + #[cfg(not(target_os = "redox"))] + fn test_sigaction() { + thread::spawn(|| { + extern "C" fn test_sigaction_handler(_: libc::c_int) {} + extern "C" fn test_sigaction_action( + _: libc::c_int, + _: *mut libc::siginfo_t, + _: *mut libc::c_void, + ) { + } + + let handler_sig = SigHandler::Handler(test_sigaction_handler); + + let flags = + SaFlags::SA_ONSTACK | SaFlags::SA_RESTART | SaFlags::SA_SIGINFO; + + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + + let action_sig = SigAction::new(handler_sig, flags, mask); + + assert_eq!( + action_sig.flags(), + SaFlags::SA_ONSTACK | SaFlags::SA_RESTART + ); + assert_eq!(action_sig.handler(), handler_sig); + + mask = action_sig.mask(); + assert!(mask.contains(SIGUSR1)); + assert!(!mask.contains(SIGUSR2)); + + let handler_act = SigHandler::SigAction(test_sigaction_action); + let action_act = SigAction::new(handler_act, flags, mask); + assert_eq!(action_act.handler(), handler_act); + + let action_dfl = SigAction::new(SigHandler::SigDfl, flags, mask); + assert_eq!(action_dfl.handler(), SigHandler::SigDfl); + + let action_ign = SigAction::new(SigHandler::SigIgn, flags, mask); + assert_eq!(action_ign.handler(), SigHandler::SigIgn); + }) + .join() + .unwrap(); + } + + #[test] + #[cfg(not(target_os = "redox"))] + fn test_sigwait() { + thread::spawn(|| { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + mask.add(SIGUSR2); + mask.thread_block().unwrap(); + + raise(SIGUSR1).unwrap(); + assert_eq!(mask.wait().unwrap(), SIGUSR1); + }) + .join() + .unwrap(); + } + + #[test] + fn test_from_sigset_t_unchecked() { + let src_set = SigSet::empty(); + let set = unsafe { SigSet::from_sigset_t_unchecked(src_set.sigset) }; + + for signal in Signal::iterator() { + assert!(!set.contains(signal)); + } + + let src_set = SigSet::all(); + let set = unsafe { SigSet::from_sigset_t_unchecked(src_set.sigset) }; + + for signal in Signal::iterator() { + assert!(set.contains(signal)); + } + } +} |