| 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() { |
| 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 = "fushsia" |
| ))] |
| #[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()); |
| } |