1 use nix::errno::Errno;
2 use nix::sys::signal::*;
3 use nix::unistd::*;
4 use std::hash::{Hash, Hasher};
5 use std::sync::atomic::{AtomicBool, Ordering};
6 #[cfg(not(target_os = "redox"))]
7 use std::thread;
8
9 #[test]
test_kill_none()10 fn test_kill_none() {
11 kill(getpid(), None).expect("Should be able to send signal to myself.");
12 }
13
14 #[test]
15 #[cfg(not(target_os = "fuchsia"))]
test_killpg_none()16 fn test_killpg_none() {
17 killpg(getpgrp(), None)
18 .expect("Should be able to send signal to my process group.");
19 }
20
21 #[test]
test_old_sigaction_flags()22 fn test_old_sigaction_flags() {
23 let _m = crate::SIGNAL_MTX.lock();
24
25 extern "C" fn handler(_: ::libc::c_int) {}
26 let act = SigAction::new(
27 SigHandler::Handler(handler),
28 SaFlags::empty(),
29 SigSet::empty(),
30 );
31 let oact = unsafe { sigaction(SIGINT, &act) }.unwrap();
32 let _flags = oact.flags();
33 let oact = unsafe { sigaction(SIGINT, &act) }.unwrap();
34 let _flags = oact.flags();
35 }
36
37 #[test]
test_sigprocmask_noop()38 fn test_sigprocmask_noop() {
39 sigprocmask(SigmaskHow::SIG_BLOCK, None, None)
40 .expect("this should be an effective noop");
41 }
42
43 #[test]
test_sigprocmask()44 fn test_sigprocmask() {
45 let _m = crate::SIGNAL_MTX.lock();
46
47 // This needs to be a signal that rust doesn't use in the test harness.
48 const SIGNAL: Signal = Signal::SIGCHLD;
49
50 let mut old_signal_set = SigSet::empty();
51 sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
52 .expect("expect to be able to retrieve old signals");
53
54 // Make sure the old set doesn't contain the signal, otherwise the following
55 // test don't make sense.
56 assert!(
57 !old_signal_set.contains(SIGNAL),
58 "the {SIGNAL:?} signal is already blocked, please change to a \
59 different one"
60 );
61
62 // Now block the signal.
63 let mut signal_set = SigSet::empty();
64 signal_set.add(SIGNAL);
65 sigprocmask(SigmaskHow::SIG_BLOCK, Some(&signal_set), None)
66 .expect("expect to be able to block signals");
67
68 // And test it again, to make sure the change was effective.
69 old_signal_set.clear();
70 sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
71 .expect("expect to be able to retrieve old signals");
72 assert!(
73 old_signal_set.contains(SIGNAL),
74 "expected the {SIGNAL:?} to be blocked"
75 );
76
77 // Reset the signal.
78 sigprocmask(SigmaskHow::SIG_UNBLOCK, Some(&signal_set), None)
79 .expect("expect to be able to block signals");
80 }
81
82 static SIGNALED: AtomicBool = AtomicBool::new(false);
83
test_sigaction_handler(signal: libc::c_int)84 extern "C" fn test_sigaction_handler(signal: libc::c_int) {
85 let signal = Signal::try_from(signal).unwrap();
86 SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
87 }
88
89 #[cfg(not(target_os = "redox"))]
test_sigaction_action( _: libc::c_int, _: *mut libc::siginfo_t, _: *mut libc::c_void, )90 extern "C" fn test_sigaction_action(
91 _: libc::c_int,
92 _: *mut libc::siginfo_t,
93 _: *mut libc::c_void,
94 ) {
95 }
96
97 #[test]
98 #[cfg(not(target_os = "redox"))]
test_signal_sigaction()99 fn test_signal_sigaction() {
100 let _m = crate::SIGNAL_MTX.lock();
101
102 let action_handler = SigHandler::SigAction(test_sigaction_action);
103 assert_eq!(
104 unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(),
105 Errno::ENOTSUP
106 );
107 }
108
109 #[test]
test_signal()110 fn test_signal() {
111 let _m = crate::SIGNAL_MTX.lock();
112
113 unsafe { signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap();
114 raise(Signal::SIGINT).unwrap();
115 assert_eq!(
116 unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
117 SigHandler::SigIgn
118 );
119
120 let handler = SigHandler::Handler(test_sigaction_handler);
121 assert_eq!(
122 unsafe { signal(Signal::SIGINT, handler) }.unwrap(),
123 SigHandler::SigDfl
124 );
125 raise(Signal::SIGINT).unwrap();
126 assert!(SIGNALED.load(Ordering::Relaxed));
127
128 #[cfg(not(solarish))]
129 assert_eq!(
130 unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
131 handler
132 );
133
134 // System V based OSes (e.g. illumos and Solaris) always resets the
135 // disposition to SIG_DFL prior to calling the signal handler
136 #[cfg(solarish)]
137 assert_eq!(
138 unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
139 SigHandler::SigDfl
140 );
141
142 // Restore default signal handler
143 unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap();
144 }
145
146 #[test]
test_contains()147 fn test_contains() {
148 let mut mask = SigSet::empty();
149 mask.add(SIGUSR1);
150
151 assert!(mask.contains(SIGUSR1));
152 assert!(!mask.contains(SIGUSR2));
153
154 let all = SigSet::all();
155 assert!(all.contains(SIGUSR1));
156 assert!(all.contains(SIGUSR2));
157 }
158
159 #[test]
test_clear()160 fn test_clear() {
161 let mut set = SigSet::all();
162 set.clear();
163 for signal in Signal::iterator() {
164 assert!(!set.contains(signal));
165 }
166 }
167
168 #[test]
test_from_str_round_trips()169 fn test_from_str_round_trips() {
170 for signal in Signal::iterator() {
171 assert_eq!(signal.as_ref().parse::<Signal>().unwrap(), signal);
172 assert_eq!(signal.to_string().parse::<Signal>().unwrap(), signal);
173 }
174 }
175
176 #[test]
test_from_str_invalid_value()177 fn test_from_str_invalid_value() {
178 let errval = Err(Errno::EINVAL);
179 assert_eq!("NOSIGNAL".parse::<Signal>(), errval);
180 assert_eq!("kill".parse::<Signal>(), errval);
181 assert_eq!("9".parse::<Signal>(), errval);
182 }
183
184 #[test]
test_extend()185 fn test_extend() {
186 let mut one_signal = SigSet::empty();
187 one_signal.add(SIGUSR1);
188
189 let mut two_signals = SigSet::empty();
190 two_signals.add(SIGUSR2);
191 two_signals.extend(&one_signal);
192
193 assert!(two_signals.contains(SIGUSR1));
194 assert!(two_signals.contains(SIGUSR2));
195 }
196
197 #[test]
198 #[cfg(not(target_os = "redox"))]
test_thread_signal_set_mask()199 fn test_thread_signal_set_mask() {
200 thread::spawn(|| {
201 let prev_mask = SigSet::thread_get_mask()
202 .expect("Failed to get existing signal mask!");
203
204 let mut test_mask = prev_mask;
205 test_mask.add(SIGUSR1);
206
207 test_mask.thread_set_mask().expect("assertion failed");
208 let new_mask =
209 SigSet::thread_get_mask().expect("Failed to get new mask!");
210
211 assert!(new_mask.contains(SIGUSR1));
212 assert!(!new_mask.contains(SIGUSR2));
213
214 prev_mask
215 .thread_set_mask()
216 .expect("Failed to revert signal mask!");
217 })
218 .join()
219 .unwrap();
220 }
221
222 #[test]
223 #[cfg(not(target_os = "redox"))]
test_thread_signal_block()224 fn test_thread_signal_block() {
225 thread::spawn(|| {
226 let mut mask = SigSet::empty();
227 mask.add(SIGUSR1);
228
229 mask.thread_block().expect("assertion failed");
230
231 assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
232 })
233 .join()
234 .unwrap();
235 }
236
237 #[test]
238 #[cfg(not(target_os = "redox"))]
test_thread_signal_unblock()239 fn test_thread_signal_unblock() {
240 thread::spawn(|| {
241 let mut mask = SigSet::empty();
242 mask.add(SIGUSR1);
243
244 mask.thread_unblock().expect("assertion failed");
245
246 assert!(!SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
247 })
248 .join()
249 .unwrap();
250 }
251
252 #[test]
253 #[cfg(not(target_os = "redox"))]
test_thread_signal_swap()254 fn test_thread_signal_swap() {
255 thread::spawn(|| {
256 let mut mask = SigSet::empty();
257 mask.add(SIGUSR1);
258 mask.thread_block().unwrap();
259
260 assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
261
262 let mut mask2 = SigSet::empty();
263 mask2.add(SIGUSR2);
264
265 let oldmask = mask2.thread_swap_mask(SigmaskHow::SIG_SETMASK).unwrap();
266
267 assert!(oldmask.contains(SIGUSR1));
268 assert!(!oldmask.contains(SIGUSR2));
269
270 assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR2));
271 })
272 .join()
273 .unwrap();
274 }
275
276 #[test]
test_from_and_into_iterator()277 fn test_from_and_into_iterator() {
278 let sigset = SigSet::from_iter(vec![Signal::SIGUSR1, Signal::SIGUSR2]);
279 let signals = sigset.into_iter().collect::<Vec<Signal>>();
280 assert_eq!(signals, [Signal::SIGUSR1, Signal::SIGUSR2]);
281 }
282
283 #[test]
284 #[cfg(not(target_os = "redox"))]
test_sigaction()285 fn test_sigaction() {
286 let _m = crate::SIGNAL_MTX.lock();
287 thread::spawn(|| {
288 extern "C" fn test_sigaction_handler(_: libc::c_int) {}
289 extern "C" fn test_sigaction_action(
290 _: libc::c_int,
291 _: *mut libc::siginfo_t,
292 _: *mut libc::c_void,
293 ) {
294 }
295
296 let handler_sig = SigHandler::Handler(test_sigaction_handler);
297
298 let flags =
299 SaFlags::SA_ONSTACK | SaFlags::SA_RESTART | SaFlags::SA_SIGINFO;
300
301 let mut mask = SigSet::empty();
302 mask.add(SIGUSR1);
303
304 let action_sig = SigAction::new(handler_sig, flags, mask);
305
306 assert_eq!(
307 action_sig.flags(),
308 SaFlags::SA_ONSTACK | SaFlags::SA_RESTART
309 );
310 assert_eq!(action_sig.handler(), handler_sig);
311
312 mask = action_sig.mask();
313 assert!(mask.contains(SIGUSR1));
314 assert!(!mask.contains(SIGUSR2));
315
316 let handler_act = SigHandler::SigAction(test_sigaction_action);
317 let action_act = SigAction::new(handler_act, flags, mask);
318 assert_eq!(action_act.handler(), handler_act);
319
320 let action_dfl = SigAction::new(SigHandler::SigDfl, flags, mask);
321 assert_eq!(action_dfl.handler(), SigHandler::SigDfl);
322
323 let action_ign = SigAction::new(SigHandler::SigIgn, flags, mask);
324 assert_eq!(action_ign.handler(), SigHandler::SigIgn);
325 })
326 .join()
327 .unwrap();
328 }
329
330 #[test]
331 #[cfg(not(target_os = "redox"))]
test_sigwait()332 fn test_sigwait() {
333 thread::spawn(|| {
334 let mut mask = SigSet::empty();
335 mask.add(SIGUSR1);
336 mask.add(SIGUSR2);
337 mask.thread_block().unwrap();
338
339 raise(SIGUSR1).unwrap();
340 assert_eq!(mask.wait().unwrap(), SIGUSR1);
341 })
342 .join()
343 .unwrap();
344 }
345
346 #[cfg(any(
347 bsd,
348 linux_android,
349 solarish,
350 target_os = "haiku",
351 target_os = "hurd",
352 target_os = "aix",
353 target_os = "fuchsia"
354 ))]
355 #[test]
test_sigsuspend()356 fn test_sigsuspend() {
357 // This test change signal handler
358 let _m = crate::SIGNAL_MTX.lock();
359 static SIGNAL_RECIEVED: AtomicBool = AtomicBool::new(false);
360 extern "C" fn test_sigsuspend_handler(_: libc::c_int) {
361 assert!(!SIGNAL_RECIEVED.swap(true, Ordering::SeqCst));
362 }
363 thread::spawn(|| {
364 const SIGNAL: Signal = Signal::SIGUSR1;
365
366 // Add signal mask to this thread
367 let mut signal_set = SigSet::empty();
368 signal_set.add(SIGNAL);
369 signal_set.thread_block().unwrap();
370
371 // Set signal handler and save old one.
372 let act = SigAction::new(
373 SigHandler::Handler(test_sigsuspend_handler),
374 SaFlags::empty(),
375 SigSet::empty(),
376 );
377 let old_act = unsafe { sigaction(SIGNAL, &act) }
378 .expect("expect to be able to set new action and get old action");
379
380 raise(SIGNAL).expect("expect be able to send signal");
381 // Now `SIGNAL` was sended but it is blocked.
382 let mut not_wait_set = SigSet::all();
383 not_wait_set.remove(SIGNAL);
384 // signal handler must run in SigSet::suspend()
385 assert!(!SIGNAL_RECIEVED.load(Ordering::SeqCst));
386 not_wait_set.suspend().unwrap();
387 assert!(SIGNAL_RECIEVED.load(Ordering::SeqCst));
388
389 // Restore the signal handler.
390 unsafe { sigaction(SIGNAL, &old_act) }
391 .expect("expect to be able to restore old action ");
392 })
393 .join()
394 .unwrap();
395 }
396
397 #[test]
test_from_sigset_t_unchecked()398 fn test_from_sigset_t_unchecked() {
399 let src_set = SigSet::empty();
400 let set = unsafe { SigSet::from_sigset_t_unchecked(*src_set.as_ref()) };
401
402 for signal in Signal::iterator() {
403 assert!(!set.contains(signal));
404 }
405
406 let src_set = SigSet::all();
407 let set = unsafe { SigSet::from_sigset_t_unchecked(*src_set.as_ref()) };
408
409 for signal in Signal::iterator() {
410 assert!(set.contains(signal));
411 }
412 }
413
414 #[test]
test_eq_empty()415 fn test_eq_empty() {
416 let set0 = SigSet::empty();
417 let set1 = SigSet::empty();
418 assert_eq!(set0, set1);
419 }
420
421 #[test]
test_eq_all()422 fn test_eq_all() {
423 let set0 = SigSet::all();
424 let set1 = SigSet::all();
425 assert_eq!(set0, set1);
426 }
427
428 #[test]
test_hash_empty()429 fn test_hash_empty() {
430 use std::collections::hash_map::DefaultHasher;
431
432 let set0 = SigSet::empty();
433 let mut h0 = DefaultHasher::new();
434 set0.hash(&mut h0);
435
436 let set1 = SigSet::empty();
437 let mut h1 = DefaultHasher::new();
438 set1.hash(&mut h1);
439
440 assert_eq!(h0.finish(), h1.finish());
441 }
442
443 #[test]
test_hash_all()444 fn test_hash_all() {
445 use std::collections::hash_map::DefaultHasher;
446
447 let set0 = SigSet::all();
448 let mut h0 = DefaultHasher::new();
449 set0.hash(&mut h0);
450
451 let set1 = SigSet::all();
452 let mut h1 = DefaultHasher::new();
453 set1.hash(&mut h1);
454
455 assert_eq!(h0.finish(), h1.finish());
456 }
457