Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement nix wrapper for libc::signal #817

Merged
merged 1 commit into from
Jan 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#997](https://github.com/nix-rust/nix/pull/997))
- Added `ptrace::{getregs, setregs}`.
([#1010](https://github.com/nix-rust/nix/pull/1010))
- Added `nix::sys::signal::signal`.
([#817](https://github.com/nix-rust/nix/pull/817))

### Changed
### Fixed
Expand Down
4 changes: 4 additions & 0 deletions src/errno.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ impl ErrnoSentinel for *mut c_void {
fn sentinel() -> Self { (-1 as isize) as *mut c_void }
}

impl ErrnoSentinel for libc::sighandler_t {
fn sentinel() -> Self { libc::SIG_ERR }
}

impl error::Error for Errno {
fn description(&self) -> &str {
self.desc()
Expand Down
73 changes: 73 additions & 0 deletions src/sys/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,79 @@ pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigActi
Errno::result(res).map(|_| SigAction { sigaction: oldact })
}

/// Signal management (see [signal(3p)](http://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
/// # #[macro_use] extern crate lazy_static;
/// # extern crate libc;
/// # extern crate nix;
/// # use std::sync::atomic::{AtomicBool, Ordering};
/// # use nix::sys::signal::{self, Signal, SigHandler};
/// lazy_static! {
/// static ref SIGNALED: AtomicBool = AtomicBool::new(false);
/// }
///
/// extern fn handle_sigint(signal: libc::c_int) {
/// let signal = Signal::from_c_int(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::UnsupportedOperation`] 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),
SigHandler::SigAction(_) => return Err(Error::UnsupportedOperation),
};
Errno::result(res).map(|oldhandler| {
match oldhandler {
libc::SIG_DFL => SigHandler::SigDfl,
libc::SIG_IGN => SigHandler::SigIgn,
f => SigHandler::Handler(mem::transmute(f)),
}
})
}

/// 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.
Expand Down
38 changes: 37 additions & 1 deletion test/sys/test_signal.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use nix::unistd::*;
use libc;
use nix::Error;
use nix::sys::signal::*;
use nix::unistd::*;
use std::sync::atomic::{AtomicBool, Ordering};

#[test]
fn test_kill_none() {
Expand Down Expand Up @@ -60,3 +63,36 @@ fn test_sigprocmask() {
sigprocmask(SigmaskHow::SIG_UNBLOCK, Some(&signal_set), None)
.expect("expect to be able to block signals");
}

lazy_static! {
static ref SIGNALED: AtomicBool = AtomicBool::new(false);
}

extern fn test_sigaction_handler(signal: libc::c_int) {
let signal = Signal::from_c_int(signal).unwrap();
SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
}

extern fn test_sigaction_action(_: libc::c_int, _: *mut libc::siginfo_t, _: *mut libc::c_void) {
}

#[test]
fn test_signal() {
let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");

unsafe { signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap();
raise(Signal::SIGINT).unwrap();
assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), SigHandler::SigIgn);

let handler = SigHandler::Handler(test_sigaction_handler);
assert_eq!(unsafe { signal(Signal::SIGINT, handler) }.unwrap(), SigHandler::SigDfl);
raise(Signal::SIGINT).unwrap();
assert!(SIGNALED.load(Ordering::Relaxed));
assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), handler);

let action_handler = SigHandler::SigAction(test_sigaction_action);
assert_eq!(unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(), Error::UnsupportedOperation);

// Restore default signal handler
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap();
}