summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/src/sys/signal.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/nix/src/sys/signal.rs')
-rw-r--r--third_party/rust/nix/src/sys/signal.rs1557
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&LTOSTOP)
+ 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));
+ }
+ }
+}