Skip to content

Commit 7bcd9d2

Browse files
bors[bot]rgardner
andcommitted
Merge #817
817: Implement nix wrapper for libc::signal r=asomers a=rgardner This implements `nix::sys::signal::signal` and adds corresponding documentation and tests. Closes #476 Co-authored-by: Robert Gardner <[email protected]>
2 parents 7f2ac63 + 6bff421 commit 7bcd9d2

File tree

4 files changed

+116
-1
lines changed

4 files changed

+116
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1515
([#997](https://github.com/nix-rust/nix/pull/997))
1616
- Added `ptrace::{getregs, setregs}`.
1717
([#1010](https://github.com/nix-rust/nix/pull/1010))
18+
- Added `nix::sys::signal::signal`.
19+
([#817](https://github.com/nix-rust/nix/pull/817))
1820

1921
### Changed
2022
### Fixed

src/errno.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ impl ErrnoSentinel for *mut c_void {
107107
fn sentinel() -> Self { (-1 as isize) as *mut c_void }
108108
}
109109

110+
impl ErrnoSentinel for libc::sighandler_t {
111+
fn sentinel() -> Self { libc::SIG_ERR }
112+
}
113+
110114
impl error::Error for Errno {
111115
fn description(&self) -> &str {
112116
self.desc()

src/sys/signal.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,79 @@ pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigActi
542542
Errno::result(res).map(|_| SigAction { sigaction: oldact })
543543
}
544544

545+
/// Signal management (see [signal(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html))
546+
///
547+
/// Installs `handler` for the given `signal`, returning the previous signal
548+
/// handler. `signal` should only be used following another call to `signal` or
549+
/// if the current handler is the default. The return value of `signal` is
550+
/// undefined after setting the handler with [`sigaction`][SigActionFn].
551+
///
552+
/// # Safety
553+
///
554+
/// If the pointer to the previous signal handler is invalid, undefined
555+
/// behavior could be invoked when casting it back to a [`SigAction`][SigActionStruct].
556+
///
557+
/// # Examples
558+
///
559+
/// Ignore `SIGINT`:
560+
///
561+
/// ```no_run
562+
/// # use nix::sys::signal::{self, Signal, SigHandler};
563+
/// unsafe { signal::signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap();
564+
/// ```
565+
///
566+
/// Use a signal handler to set a flag variable:
567+
///
568+
/// ```no_run
569+
/// # #[macro_use] extern crate lazy_static;
570+
/// # extern crate libc;
571+
/// # extern crate nix;
572+
/// # use std::sync::atomic::{AtomicBool, Ordering};
573+
/// # use nix::sys::signal::{self, Signal, SigHandler};
574+
/// lazy_static! {
575+
/// static ref SIGNALED: AtomicBool = AtomicBool::new(false);
576+
/// }
577+
///
578+
/// extern fn handle_sigint(signal: libc::c_int) {
579+
/// let signal = Signal::from_c_int(signal).unwrap();
580+
/// SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
581+
/// }
582+
///
583+
/// fn main() {
584+
/// let handler = SigHandler::Handler(handle_sigint);
585+
/// unsafe { signal::signal(Signal::SIGINT, handler) }.unwrap();
586+
/// }
587+
/// ```
588+
///
589+
/// # Errors
590+
///
591+
/// Returns [`Error::UnsupportedOperation`] if `handler` is
592+
/// [`SigAction`][SigActionStruct]. Use [`sigaction`][SigActionFn] instead.
593+
///
594+
/// `signal` also returns any error from `libc::signal`, such as when an attempt
595+
/// is made to catch a signal that cannot be caught or to ignore a signal that
596+
/// cannot be ignored.
597+
///
598+
/// [`Error::UnsupportedOperation`]: ../../enum.Error.html#variant.UnsupportedOperation
599+
/// [SigActionStruct]: struct.SigAction.html
600+
/// [sigactionFn]: fn.sigaction.html
601+
pub unsafe fn signal(signal: Signal, handler: SigHandler) -> Result<SigHandler> {
602+
let signal = signal as libc::c_int;
603+
let res = match handler {
604+
SigHandler::SigDfl => libc::signal(signal, libc::SIG_DFL),
605+
SigHandler::SigIgn => libc::signal(signal, libc::SIG_IGN),
606+
SigHandler::Handler(handler) => libc::signal(signal, handler as libc::sighandler_t),
607+
SigHandler::SigAction(_) => return Err(Error::UnsupportedOperation),
608+
};
609+
Errno::result(res).map(|oldhandler| {
610+
match oldhandler {
611+
libc::SIG_DFL => SigHandler::SigDfl,
612+
libc::SIG_IGN => SigHandler::SigIgn,
613+
f => SigHandler::Handler(mem::transmute(f)),
614+
}
615+
})
616+
}
617+
545618
/// Manages the signal mask (set of blocked signals) for the calling thread.
546619
///
547620
/// If the `set` parameter is `Some(..)`, then the signal mask will be updated with the signal set.

test/sys/test_signal.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
use nix::unistd::*;
1+
use libc;
2+
use nix::Error;
23
use nix::sys::signal::*;
4+
use nix::unistd::*;
5+
use std::sync::atomic::{AtomicBool, Ordering};
36

47
#[test]
58
fn test_kill_none() {
@@ -60,3 +63,36 @@ fn test_sigprocmask() {
6063
sigprocmask(SigmaskHow::SIG_UNBLOCK, Some(&signal_set), None)
6164
.expect("expect to be able to block signals");
6265
}
66+
67+
lazy_static! {
68+
static ref SIGNALED: AtomicBool = AtomicBool::new(false);
69+
}
70+
71+
extern fn test_sigaction_handler(signal: libc::c_int) {
72+
let signal = Signal::from_c_int(signal).unwrap();
73+
SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
74+
}
75+
76+
extern fn test_sigaction_action(_: libc::c_int, _: *mut libc::siginfo_t, _: *mut libc::c_void) {
77+
}
78+
79+
#[test]
80+
fn test_signal() {
81+
let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
82+
83+
unsafe { signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap();
84+
raise(Signal::SIGINT).unwrap();
85+
assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), SigHandler::SigIgn);
86+
87+
let handler = SigHandler::Handler(test_sigaction_handler);
88+
assert_eq!(unsafe { signal(Signal::SIGINT, handler) }.unwrap(), SigHandler::SigDfl);
89+
raise(Signal::SIGINT).unwrap();
90+
assert!(SIGNALED.load(Ordering::Relaxed));
91+
assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), handler);
92+
93+
let action_handler = SigHandler::SigAction(test_sigaction_action);
94+
assert_eq!(unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(), Error::UnsupportedOperation);
95+
96+
// Restore default signal handler
97+
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap();
98+
}

0 commit comments

Comments
 (0)