456 lines
12 KiB
Rust
456 lines
12 KiB
Rust
use nix::errno::Errno;
|
|
use nix::sys::signal::*;
|
|
use nix::unistd::*;
|
|
use std::hash::{Hash, Hasher};
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
#[cfg(not(target_os = "redox"))]
|
|
use std::thread;
|
|
|
|
#[test]
|
|
fn test_kill_none() {
|
|
kill(getpid(), None).expect("Should be able to send signal to myself.");
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(target_os = "fuchsia"))]
|
|
fn test_killpg_none() {
|
|
killpg(getpgrp(), None)
|
|
.expect("Should be able to send signal to my process group.");
|
|
}
|
|
|
|
#[test]
|
|
fn test_old_sigaction_flags() {
|
|
let _m = crate::SIGNAL_MTX.lock();
|
|
|
|
extern "C" fn handler(_: ::libc::c_int) {}
|
|
let act = SigAction::new(
|
|
SigHandler::Handler(handler),
|
|
SaFlags::empty(),
|
|
SigSet::empty(),
|
|
);
|
|
let oact = unsafe { sigaction(SIGINT, &act) }.unwrap();
|
|
let _flags = oact.flags();
|
|
let oact = unsafe { sigaction(SIGINT, &act) }.unwrap();
|
|
let _flags = oact.flags();
|
|
}
|
|
|
|
#[test]
|
|
fn test_sigprocmask_noop() {
|
|
sigprocmask(SigmaskHow::SIG_BLOCK, None, None)
|
|
.expect("this should be an effective noop");
|
|
}
|
|
|
|
#[test]
|
|
fn test_sigprocmask() {
|
|
let _m = crate::SIGNAL_MTX.lock();
|
|
|
|
// This needs to be a signal that rust doesn't use in the test harness.
|
|
const SIGNAL: Signal = Signal::SIGCHLD;
|
|
|
|
let mut old_signal_set = SigSet::empty();
|
|
sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
|
|
.expect("expect to be able to retrieve old signals");
|
|
|
|
// Make sure the old set doesn't contain the signal, otherwise the following
|
|
// test don't make sense.
|
|
assert!(
|
|
!old_signal_set.contains(SIGNAL),
|
|
"the {SIGNAL:?} signal is already blocked, please change to a \
|
|
different one"
|
|
);
|
|
|
|
// Now block the signal.
|
|
let mut signal_set = SigSet::empty();
|
|
signal_set.add(SIGNAL);
|
|
sigprocmask(SigmaskHow::SIG_BLOCK, Some(&signal_set), None)
|
|
.expect("expect to be able to block signals");
|
|
|
|
// And test it again, to make sure the change was effective.
|
|
old_signal_set.clear();
|
|
sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
|
|
.expect("expect to be able to retrieve old signals");
|
|
assert!(
|
|
old_signal_set.contains(SIGNAL),
|
|
"expected the {SIGNAL:?} to be blocked"
|
|
);
|
|
|
|
// Reset the signal.
|
|
sigprocmask(SigmaskHow::SIG_UNBLOCK, Some(&signal_set), None)
|
|
.expect("expect to be able to block signals");
|
|
}
|
|
|
|
static SIGNALED: AtomicBool = AtomicBool::new(false);
|
|
|
|
extern "C" fn test_sigaction_handler(signal: libc::c_int) {
|
|
let signal = Signal::try_from(signal).unwrap();
|
|
SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
|
|
}
|
|
|
|
#[cfg(not(target_os = "redox"))]
|
|
extern "C" fn test_sigaction_action(
|
|
_: libc::c_int,
|
|
_: *mut libc::siginfo_t,
|
|
_: *mut libc::c_void,
|
|
) {
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(target_os = "redox"))]
|
|
fn test_signal_sigaction() {
|
|
let _m = crate::SIGNAL_MTX.lock();
|
|
|
|
let action_handler = SigHandler::SigAction(test_sigaction_action);
|
|
assert_eq!(
|
|
unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(),
|
|
Errno::ENOTSUP
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_signal() {
|
|
let _m = crate::SIGNAL_MTX.lock();
|
|
|
|
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));
|
|
|
|
#[cfg(not(solarish))]
|
|
assert_eq!(
|
|
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
|
|
handler
|
|
);
|
|
|
|
// System V based OSes (e.g. illumos and Solaris) always resets the
|
|
// disposition to SIG_DFL prior to calling the signal handler
|
|
#[cfg(solarish)]
|
|
assert_eq!(
|
|
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
|
|
SigHandler::SigDfl
|
|
);
|
|
|
|
// Restore default signal handler
|
|
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap();
|
|
}
|
|
|
|
#[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() {
|
|
let _m = crate::SIGNAL_MTX.lock();
|
|
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();
|
|
}
|
|
|
|
#[cfg(any(
|
|
bsd,
|
|
linux_android,
|
|
solarish,
|
|
target_os = "haiku",
|
|
target_os = "hurd",
|
|
target_os = "aix",
|
|
target_os = "fuchsia"
|
|
))]
|
|
#[test]
|
|
fn test_sigsuspend() {
|
|
// This test change signal handler
|
|
let _m = crate::SIGNAL_MTX.lock();
|
|
static SIGNAL_RECIEVED: AtomicBool = AtomicBool::new(false);
|
|
extern "C" fn test_sigsuspend_handler(_: libc::c_int) {
|
|
assert!(!SIGNAL_RECIEVED.swap(true, Ordering::SeqCst));
|
|
}
|
|
thread::spawn(|| {
|
|
const SIGNAL: Signal = Signal::SIGUSR1;
|
|
|
|
// Add signal mask to this thread
|
|
let mut signal_set = SigSet::empty();
|
|
signal_set.add(SIGNAL);
|
|
signal_set.thread_block().unwrap();
|
|
|
|
// Set signal handler and save old one.
|
|
let act = SigAction::new(
|
|
SigHandler::Handler(test_sigsuspend_handler),
|
|
SaFlags::empty(),
|
|
SigSet::empty(),
|
|
);
|
|
let old_act = unsafe { sigaction(SIGNAL, &act) }
|
|
.expect("expect to be able to set new action and get old action");
|
|
|
|
raise(SIGNAL).expect("expect be able to send signal");
|
|
// Now `SIGNAL` was sended but it is blocked.
|
|
let mut not_wait_set = SigSet::all();
|
|
not_wait_set.remove(SIGNAL);
|
|
// signal handler must run in SigSet::suspend()
|
|
assert!(!SIGNAL_RECIEVED.load(Ordering::SeqCst));
|
|
not_wait_set.suspend().unwrap();
|
|
assert!(SIGNAL_RECIEVED.load(Ordering::SeqCst));
|
|
|
|
// Restore the signal handler.
|
|
unsafe { sigaction(SIGNAL, &old_act) }
|
|
.expect("expect to be able to restore old action ");
|
|
})
|
|
.join()
|
|
.unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_from_sigset_t_unchecked() {
|
|
let src_set = SigSet::empty();
|
|
let set = unsafe { SigSet::from_sigset_t_unchecked(*src_set.as_ref()) };
|
|
|
|
for signal in Signal::iterator() {
|
|
assert!(!set.contains(signal));
|
|
}
|
|
|
|
let src_set = SigSet::all();
|
|
let set = unsafe { SigSet::from_sigset_t_unchecked(*src_set.as_ref()) };
|
|
|
|
for signal in Signal::iterator() {
|
|
assert!(set.contains(signal));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_eq_empty() {
|
|
let set0 = SigSet::empty();
|
|
let set1 = SigSet::empty();
|
|
assert_eq!(set0, set1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_eq_all() {
|
|
let set0 = SigSet::all();
|
|
let set1 = SigSet::all();
|
|
assert_eq!(set0, set1);
|
|
}
|
|
|
|
#[test]
|
|
fn test_hash_empty() {
|
|
use std::collections::hash_map::DefaultHasher;
|
|
|
|
let set0 = SigSet::empty();
|
|
let mut h0 = DefaultHasher::new();
|
|
set0.hash(&mut h0);
|
|
|
|
let set1 = SigSet::empty();
|
|
let mut h1 = DefaultHasher::new();
|
|
set1.hash(&mut h1);
|
|
|
|
assert_eq!(h0.finish(), h1.finish());
|
|
}
|
|
|
|
#[test]
|
|
fn test_hash_all() {
|
|
use std::collections::hash_map::DefaultHasher;
|
|
|
|
let set0 = SigSet::all();
|
|
let mut h0 = DefaultHasher::new();
|
|
set0.hash(&mut h0);
|
|
|
|
let set1 = SigSet::all();
|
|
let mut h1 = DefaultHasher::new();
|
|
set1.hash(&mut h1);
|
|
|
|
assert_eq!(h0.finish(), h1.finish());
|
|
}
|