1 | // Portions of this file are Copyright 2014 The Rust Project Developers. |
2 | // See https://www.rust-lang.org/policies/licenses. |
3 | |
4 | //! Operating system signals. |
5 | |
6 | use crate::errno::Errno; |
7 | use crate::{Error, Result}; |
8 | use cfg_if::cfg_if; |
9 | use std::fmt; |
10 | use std::mem; |
11 | #[cfg (any(target_os = "dragonfly" , target_os = "freebsd" ))] |
12 | use std::os::unix::io::RawFd; |
13 | use std::ptr; |
14 | use std::str::FromStr; |
15 | |
16 | #[cfg (not(any(target_os = "openbsd" , target_os = "redox" )))] |
17 | #[cfg (any(feature = "aio" , feature = "signal" ))] |
18 | pub use self::sigevent::*; |
19 | |
20 | #[cfg (any(feature = "aio" , feature = "process" , feature = "signal" ))] |
21 | libc_enum! { |
22 | /// Types of operating system signals |
23 | // Currently there is only one definition of c_int in libc, as well as only one |
24 | // type for signal constants. |
25 | // We would prefer to use the libc::c_int alias in the repr attribute. Unfortunately |
26 | // this is not (yet) possible. |
27 | #[repr(i32)] |
28 | #[non_exhaustive] |
29 | #[cfg_attr(docsrs, doc(cfg(any(feature = "aio" , feature = "signal" ))))] |
30 | pub enum Signal { |
31 | /// Hangup |
32 | SIGHUP, |
33 | /// Interrupt |
34 | SIGINT, |
35 | /// Quit |
36 | SIGQUIT, |
37 | /// Illegal instruction (not reset when caught) |
38 | SIGILL, |
39 | /// Trace trap (not reset when caught) |
40 | SIGTRAP, |
41 | /// Abort |
42 | SIGABRT, |
43 | /// Bus error |
44 | SIGBUS, |
45 | /// Floating point exception |
46 | SIGFPE, |
47 | /// Kill (cannot be caught or ignored) |
48 | SIGKILL, |
49 | /// User defined signal 1 |
50 | SIGUSR1, |
51 | /// Segmentation violation |
52 | SIGSEGV, |
53 | /// User defined signal 2 |
54 | SIGUSR2, |
55 | /// Write on a pipe with no one to read it |
56 | SIGPIPE, |
57 | /// Alarm clock |
58 | SIGALRM, |
59 | /// Software termination signal from kill |
60 | SIGTERM, |
61 | /// Stack fault (obsolete) |
62 | #[cfg(all(any(target_os = "android" , target_os = "emscripten" , |
63 | target_os = "fuchsia" , target_os = "linux" ), |
64 | not(any(target_arch = "mips" , target_arch = "mips64" , |
65 | target_arch = "sparc64" ))))] |
66 | SIGSTKFLT, |
67 | /// To parent on child stop or exit |
68 | SIGCHLD, |
69 | /// Continue a stopped process |
70 | SIGCONT, |
71 | /// Sendable stop signal not from tty |
72 | SIGSTOP, |
73 | /// Stop signal from tty |
74 | SIGTSTP, |
75 | /// To readers pgrp upon background tty read |
76 | SIGTTIN, |
77 | /// Like TTIN if (tp->t_local<OSTOP) |
78 | SIGTTOU, |
79 | /// Urgent condition on IO channel |
80 | SIGURG, |
81 | /// Exceeded CPU time limit |
82 | SIGXCPU, |
83 | /// Exceeded file size limit |
84 | SIGXFSZ, |
85 | /// Virtual time alarm |
86 | SIGVTALRM, |
87 | /// Profiling time alarm |
88 | SIGPROF, |
89 | /// Window size changes |
90 | SIGWINCH, |
91 | /// Input/output possible signal |
92 | #[cfg(not(target_os = "haiku" ))] |
93 | #[cfg_attr(docsrs, doc(cfg(all())))] |
94 | SIGIO, |
95 | #[cfg(any(target_os = "android" , target_os = "emscripten" , |
96 | target_os = "fuchsia" , target_os = "linux" ))] |
97 | #[cfg_attr(docsrs, doc(cfg(all())))] |
98 | /// Power failure imminent. |
99 | SIGPWR, |
100 | /// Bad system call |
101 | SIGSYS, |
102 | #[cfg(not(any(target_os = "android" , target_os = "emscripten" , |
103 | target_os = "fuchsia" , target_os = "linux" , |
104 | target_os = "redox" , target_os = "haiku" )))] |
105 | #[cfg_attr(docsrs, doc(cfg(all())))] |
106 | /// Emulator trap |
107 | SIGEMT, |
108 | #[cfg(not(any(target_os = "android" , target_os = "emscripten" , |
109 | target_os = "fuchsia" , target_os = "linux" , |
110 | target_os = "redox" , target_os = "haiku" )))] |
111 | #[cfg_attr(docsrs, doc(cfg(all())))] |
112 | /// Information request |
113 | SIGINFO, |
114 | } |
115 | impl TryFrom<i32> |
116 | } |
117 | |
118 | #[cfg (feature = "signal" )] |
119 | impl FromStr for Signal { |
120 | type Err = Error; |
121 | fn from_str(s: &str) -> Result<Signal> { |
122 | Ok(match s { |
123 | "SIGHUP" => Signal::SIGHUP, |
124 | "SIGINT" => Signal::SIGINT, |
125 | "SIGQUIT" => Signal::SIGQUIT, |
126 | "SIGILL" => Signal::SIGILL, |
127 | "SIGTRAP" => Signal::SIGTRAP, |
128 | "SIGABRT" => Signal::SIGABRT, |
129 | "SIGBUS" => Signal::SIGBUS, |
130 | "SIGFPE" => Signal::SIGFPE, |
131 | "SIGKILL" => Signal::SIGKILL, |
132 | "SIGUSR1" => Signal::SIGUSR1, |
133 | "SIGSEGV" => Signal::SIGSEGV, |
134 | "SIGUSR2" => Signal::SIGUSR2, |
135 | "SIGPIPE" => Signal::SIGPIPE, |
136 | "SIGALRM" => Signal::SIGALRM, |
137 | "SIGTERM" => Signal::SIGTERM, |
138 | #[cfg (all( |
139 | any( |
140 | target_os = "android" , |
141 | target_os = "emscripten" , |
142 | target_os = "fuchsia" , |
143 | target_os = "linux" |
144 | ), |
145 | not(any( |
146 | target_arch = "mips" , |
147 | target_arch = "mips64" , |
148 | target_arch = "sparc64" |
149 | )) |
150 | ))] |
151 | "SIGSTKFLT" => Signal::SIGSTKFLT, |
152 | "SIGCHLD" => Signal::SIGCHLD, |
153 | "SIGCONT" => Signal::SIGCONT, |
154 | "SIGSTOP" => Signal::SIGSTOP, |
155 | "SIGTSTP" => Signal::SIGTSTP, |
156 | "SIGTTIN" => Signal::SIGTTIN, |
157 | "SIGTTOU" => Signal::SIGTTOU, |
158 | "SIGURG" => Signal::SIGURG, |
159 | "SIGXCPU" => Signal::SIGXCPU, |
160 | "SIGXFSZ" => Signal::SIGXFSZ, |
161 | "SIGVTALRM" => Signal::SIGVTALRM, |
162 | "SIGPROF" => Signal::SIGPROF, |
163 | "SIGWINCH" => Signal::SIGWINCH, |
164 | #[cfg (not(target_os = "haiku" ))] |
165 | "SIGIO" => Signal::SIGIO, |
166 | #[cfg (any( |
167 | target_os = "android" , |
168 | target_os = "emscripten" , |
169 | target_os = "fuchsia" , |
170 | target_os = "linux" |
171 | ))] |
172 | "SIGPWR" => Signal::SIGPWR, |
173 | "SIGSYS" => Signal::SIGSYS, |
174 | #[cfg (not(any( |
175 | target_os = "android" , |
176 | target_os = "emscripten" , |
177 | target_os = "fuchsia" , |
178 | target_os = "linux" , |
179 | target_os = "redox" , |
180 | target_os = "haiku" |
181 | )))] |
182 | "SIGEMT" => Signal::SIGEMT, |
183 | #[cfg (not(any( |
184 | target_os = "android" , |
185 | target_os = "emscripten" , |
186 | target_os = "fuchsia" , |
187 | target_os = "linux" , |
188 | target_os = "redox" , |
189 | target_os = "haiku" |
190 | )))] |
191 | "SIGINFO" => Signal::SIGINFO, |
192 | _ => return Err(Errno::EINVAL), |
193 | }) |
194 | } |
195 | } |
196 | |
197 | #[cfg (feature = "signal" )] |
198 | impl Signal { |
199 | /// Returns name of signal. |
200 | /// |
201 | /// This function is equivalent to `<Signal as AsRef<str>>::as_ref()`, |
202 | /// with difference that returned string is `'static` |
203 | /// and not bound to `self`'s lifetime. |
204 | pub const fn as_str(self) -> &'static str { |
205 | match self { |
206 | Signal::SIGHUP => "SIGHUP" , |
207 | Signal::SIGINT => "SIGINT" , |
208 | Signal::SIGQUIT => "SIGQUIT" , |
209 | Signal::SIGILL => "SIGILL" , |
210 | Signal::SIGTRAP => "SIGTRAP" , |
211 | Signal::SIGABRT => "SIGABRT" , |
212 | Signal::SIGBUS => "SIGBUS" , |
213 | Signal::SIGFPE => "SIGFPE" , |
214 | Signal::SIGKILL => "SIGKILL" , |
215 | Signal::SIGUSR1 => "SIGUSR1" , |
216 | Signal::SIGSEGV => "SIGSEGV" , |
217 | Signal::SIGUSR2 => "SIGUSR2" , |
218 | Signal::SIGPIPE => "SIGPIPE" , |
219 | Signal::SIGALRM => "SIGALRM" , |
220 | Signal::SIGTERM => "SIGTERM" , |
221 | #[cfg (all( |
222 | any( |
223 | target_os = "android" , |
224 | target_os = "emscripten" , |
225 | target_os = "fuchsia" , |
226 | target_os = "linux" |
227 | ), |
228 | not(any( |
229 | target_arch = "mips" , |
230 | target_arch = "mips64" , |
231 | target_arch = "sparc64" |
232 | )) |
233 | ))] |
234 | Signal::SIGSTKFLT => "SIGSTKFLT" , |
235 | Signal::SIGCHLD => "SIGCHLD" , |
236 | Signal::SIGCONT => "SIGCONT" , |
237 | Signal::SIGSTOP => "SIGSTOP" , |
238 | Signal::SIGTSTP => "SIGTSTP" , |
239 | Signal::SIGTTIN => "SIGTTIN" , |
240 | Signal::SIGTTOU => "SIGTTOU" , |
241 | Signal::SIGURG => "SIGURG" , |
242 | Signal::SIGXCPU => "SIGXCPU" , |
243 | Signal::SIGXFSZ => "SIGXFSZ" , |
244 | Signal::SIGVTALRM => "SIGVTALRM" , |
245 | Signal::SIGPROF => "SIGPROF" , |
246 | Signal::SIGWINCH => "SIGWINCH" , |
247 | #[cfg (not(target_os = "haiku" ))] |
248 | Signal::SIGIO => "SIGIO" , |
249 | #[cfg (any( |
250 | target_os = "android" , |
251 | target_os = "emscripten" , |
252 | target_os = "fuchsia" , |
253 | target_os = "linux" |
254 | ))] |
255 | Signal::SIGPWR => "SIGPWR" , |
256 | Signal::SIGSYS => "SIGSYS" , |
257 | #[cfg (not(any( |
258 | target_os = "android" , |
259 | target_os = "emscripten" , |
260 | target_os = "fuchsia" , |
261 | target_os = "linux" , |
262 | target_os = "redox" , |
263 | target_os = "haiku" |
264 | )))] |
265 | Signal::SIGEMT => "SIGEMT" , |
266 | #[cfg (not(any( |
267 | target_os = "android" , |
268 | target_os = "emscripten" , |
269 | target_os = "fuchsia" , |
270 | target_os = "linux" , |
271 | target_os = "redox" , |
272 | target_os = "haiku" |
273 | )))] |
274 | Signal::SIGINFO => "SIGINFO" , |
275 | } |
276 | } |
277 | } |
278 | |
279 | #[cfg (feature = "signal" )] |
280 | impl AsRef<str> for Signal { |
281 | fn as_ref(&self) -> &str { |
282 | self.as_str() |
283 | } |
284 | } |
285 | |
286 | #[cfg (feature = "signal" )] |
287 | impl fmt::Display for Signal { |
288 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
289 | f.write_str(self.as_ref()) |
290 | } |
291 | } |
292 | |
293 | #[cfg (feature = "signal" )] |
294 | pub use self::Signal::*; |
295 | |
296 | #[cfg (target_os = "redox" )] |
297 | #[cfg (feature = "signal" )] |
298 | const SIGNALS: [Signal; 29] = [ |
299 | SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL, |
300 | SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT, |
301 | SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, |
302 | SIGPROF, SIGWINCH, SIGIO, SIGSYS, |
303 | ]; |
304 | #[cfg (target_os = "haiku" )] |
305 | #[cfg (feature = "signal" )] |
306 | const SIGNALS: [Signal; 28] = [ |
307 | SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL, |
308 | SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT, |
309 | SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, |
310 | SIGPROF, SIGWINCH, SIGSYS, |
311 | ]; |
312 | #[cfg (all( |
313 | any( |
314 | target_os = "linux" , |
315 | target_os = "android" , |
316 | target_os = "emscripten" , |
317 | target_os = "fuchsia" |
318 | ), |
319 | not(any( |
320 | target_arch = "mips" , |
321 | target_arch = "mips64" , |
322 | target_arch = "sparc64" |
323 | )) |
324 | ))] |
325 | #[cfg (feature = "signal" )] |
326 | const SIGNALS: [Signal; 31] = [ |
327 | SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL, |
328 | SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGSTKFLT, SIGCHLD, |
329 | SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, |
330 | SIGVTALRM, SIGPROF, SIGWINCH, SIGIO, SIGPWR, SIGSYS, |
331 | ]; |
332 | #[cfg (all( |
333 | any( |
334 | target_os = "linux" , |
335 | target_os = "android" , |
336 | target_os = "emscripten" , |
337 | target_os = "fuchsia" |
338 | ), |
339 | any(target_arch = "mips" , target_arch = "mips64" , target_arch = "sparc64" ) |
340 | ))] |
341 | #[cfg (feature = "signal" )] |
342 | const SIGNALS: [Signal; 30] = [ |
343 | SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL, |
344 | SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT, |
345 | SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, |
346 | SIGPROF, SIGWINCH, SIGIO, SIGPWR, SIGSYS, |
347 | ]; |
348 | #[cfg (not(any( |
349 | target_os = "linux" , |
350 | target_os = "android" , |
351 | target_os = "fuchsia" , |
352 | target_os = "emscripten" , |
353 | target_os = "redox" , |
354 | target_os = "haiku" |
355 | )))] |
356 | #[cfg (feature = "signal" )] |
357 | const SIGNALS: [Signal; 31] = [ |
358 | SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL, |
359 | SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT, |
360 | SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, |
361 | SIGPROF, SIGWINCH, SIGIO, SIGSYS, SIGEMT, SIGINFO, |
362 | ]; |
363 | |
364 | feature! { |
365 | #![feature = "signal" ] |
366 | |
367 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
368 | /// Iterate through all signals defined by this operating system |
369 | pub struct SignalIterator { |
370 | next: usize, |
371 | } |
372 | |
373 | impl Iterator for SignalIterator { |
374 | type Item = Signal; |
375 | |
376 | fn next(&mut self) -> Option<Signal> { |
377 | if self.next < SIGNALS.len() { |
378 | let next_signal = SIGNALS[self.next]; |
379 | self.next += 1; |
380 | Some(next_signal) |
381 | } else { |
382 | None |
383 | } |
384 | } |
385 | } |
386 | |
387 | impl Signal { |
388 | /// Iterate through all signals defined by this OS |
389 | pub const fn iterator() -> SignalIterator { |
390 | SignalIterator{next: 0} |
391 | } |
392 | } |
393 | |
394 | /// Alias for [`SIGABRT`] |
395 | pub const SIGIOT : Signal = SIGABRT; |
396 | /// Alias for [`SIGIO`] |
397 | #[cfg (not(target_os = "haiku" ))] |
398 | pub const SIGPOLL : Signal = SIGIO; |
399 | /// Alias for [`SIGSYS`] |
400 | pub const SIGUNUSED : Signal = SIGSYS; |
401 | |
402 | cfg_if! { |
403 | if #[cfg(target_os = "redox" )] { |
404 | type SaFlags_t = libc::c_ulong; |
405 | } else if #[cfg(target_env = "uclibc" )] { |
406 | type SaFlags_t = libc::c_ulong; |
407 | } else { |
408 | type SaFlags_t = libc::c_int; |
409 | } |
410 | } |
411 | } |
412 | |
413 | #[cfg (feature = "signal" )] |
414 | libc_bitflags! { |
415 | /// Controls the behavior of a [`SigAction`] |
416 | #[cfg_attr (docsrs, doc(cfg(feature = "signal" )))] |
417 | pub struct SaFlags: SaFlags_t { |
418 | /// When catching a [`Signal::SIGCHLD`] signal, the signal will be |
419 | /// generated only when a child process exits, not when a child process |
420 | /// stops. |
421 | SA_NOCLDSTOP; |
422 | /// When catching a [`Signal::SIGCHLD`] signal, the system will not |
423 | /// create zombie processes when children of the calling process exit. |
424 | SA_NOCLDWAIT; |
425 | /// Further occurrences of the delivered signal are not masked during |
426 | /// the execution of the handler. |
427 | SA_NODEFER; |
428 | /// The system will deliver the signal to the process on a signal stack, |
429 | /// specified by each thread with sigaltstack(2). |
430 | SA_ONSTACK; |
431 | /// The handler is reset back to the default at the moment the signal is |
432 | /// delivered. |
433 | SA_RESETHAND; |
434 | /// Requests that certain system calls restart if interrupted by this |
435 | /// signal. See the man page for complete details. |
436 | SA_RESTART; |
437 | /// This flag is controlled internally by Nix. |
438 | SA_SIGINFO; |
439 | } |
440 | } |
441 | |
442 | #[cfg (feature = "signal" )] |
443 | libc_enum! { |
444 | /// Specifies how certain functions should manipulate a signal mask |
445 | #[repr (i32)] |
446 | #[non_exhaustive ] |
447 | #[cfg_attr (docsrs, doc(cfg(feature = "signal" )))] |
448 | pub enum SigmaskHow { |
449 | /// The new mask is the union of the current mask and the specified set. |
450 | SIG_BLOCK, |
451 | /// The new mask is the intersection of the current mask and the |
452 | /// complement of the specified set. |
453 | SIG_UNBLOCK, |
454 | /// The current mask is replaced by the specified set. |
455 | SIG_SETMASK, |
456 | } |
457 | } |
458 | |
459 | feature! { |
460 | #![feature = "signal" ] |
461 | |
462 | use crate::unistd::Pid; |
463 | use std::iter::Extend; |
464 | use std::iter::FromIterator; |
465 | use std::iter::IntoIterator; |
466 | |
467 | /// Specifies a set of [`Signal`]s that may be blocked, waited for, etc. |
468 | // We are using `transparent` here to be super sure that `SigSet` |
469 | // is represented exactly like the `sigset_t` struct from C. |
470 | #[repr (transparent)] |
471 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
472 | pub struct SigSet { |
473 | sigset: libc::sigset_t |
474 | } |
475 | |
476 | impl SigSet { |
477 | /// Initialize to include all signals. |
478 | #[doc (alias("sigfillset" ))] |
479 | pub fn all() -> SigSet { |
480 | let mut sigset = mem::MaybeUninit::uninit(); |
481 | let _ = unsafe { libc::sigfillset(sigset.as_mut_ptr()) }; |
482 | |
483 | unsafe{ SigSet { sigset: sigset.assume_init() } } |
484 | } |
485 | |
486 | /// Initialize to include nothing. |
487 | #[doc (alias("sigemptyset" ))] |
488 | pub fn empty() -> SigSet { |
489 | let mut sigset = mem::MaybeUninit::uninit(); |
490 | let _ = unsafe { libc::sigemptyset(sigset.as_mut_ptr()) }; |
491 | |
492 | unsafe{ SigSet { sigset: sigset.assume_init() } } |
493 | } |
494 | |
495 | /// Add the specified signal to the set. |
496 | #[doc (alias("sigaddset" ))] |
497 | pub fn add(&mut self, signal: Signal) { |
498 | unsafe { libc::sigaddset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) }; |
499 | } |
500 | |
501 | /// Remove all signals from this set. |
502 | #[doc (alias("sigemptyset" ))] |
503 | pub fn clear(&mut self) { |
504 | unsafe { libc::sigemptyset(&mut self.sigset as *mut libc::sigset_t) }; |
505 | } |
506 | |
507 | /// Remove the specified signal from this set. |
508 | #[doc (alias("sigdelset" ))] |
509 | pub fn remove(&mut self, signal: Signal) { |
510 | unsafe { libc::sigdelset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) }; |
511 | } |
512 | |
513 | /// Return whether this set includes the specified signal. |
514 | #[doc (alias("sigismember" ))] |
515 | pub fn contains(&self, signal: Signal) -> bool { |
516 | let res = unsafe { libc::sigismember(&self.sigset as *const libc::sigset_t, signal as libc::c_int) }; |
517 | |
518 | match res { |
519 | 1 => true, |
520 | 0 => false, |
521 | _ => unreachable!("unexpected value from sigismember" ), |
522 | } |
523 | } |
524 | |
525 | /// Returns an iterator that yields the signals contained in this set. |
526 | pub fn iter(&self) -> SigSetIter<'_> { |
527 | self.into_iter() |
528 | } |
529 | |
530 | /// Gets the currently blocked (masked) set of signals for the calling thread. |
531 | pub fn thread_get_mask() -> Result<SigSet> { |
532 | let mut oldmask = mem::MaybeUninit::uninit(); |
533 | do_pthread_sigmask(SigmaskHow::SIG_SETMASK, None, Some(oldmask.as_mut_ptr()))?; |
534 | Ok(unsafe{ SigSet{sigset: oldmask.assume_init()}}) |
535 | } |
536 | |
537 | /// Sets the set of signals as the signal mask for the calling thread. |
538 | pub fn thread_set_mask(&self) -> Result<()> { |
539 | pthread_sigmask(SigmaskHow::SIG_SETMASK, Some(self), None) |
540 | } |
541 | |
542 | /// Adds the set of signals to the signal mask for the calling thread. |
543 | pub fn thread_block(&self) -> Result<()> { |
544 | pthread_sigmask(SigmaskHow::SIG_BLOCK, Some(self), None) |
545 | } |
546 | |
547 | /// Removes the set of signals from the signal mask for the calling thread. |
548 | pub fn thread_unblock(&self) -> Result<()> { |
549 | pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(self), None) |
550 | } |
551 | |
552 | /// Sets the set of signals as the signal mask, and returns the old mask. |
553 | pub fn thread_swap_mask(&self, how: SigmaskHow) -> Result<SigSet> { |
554 | let mut oldmask = mem::MaybeUninit::uninit(); |
555 | do_pthread_sigmask(how, Some(self), Some(oldmask.as_mut_ptr()))?; |
556 | Ok(unsafe{ SigSet{sigset: oldmask.assume_init()}}) |
557 | } |
558 | |
559 | /// Suspends execution of the calling thread until one of the signals in the |
560 | /// signal mask becomes pending, and returns the accepted signal. |
561 | #[cfg (not(target_os = "redox" ))] // RedoxFS does not yet support sigwait |
562 | #[cfg_attr (docsrs, doc(cfg(all())))] |
563 | pub fn wait(&self) -> Result<Signal> { |
564 | use std::convert::TryFrom; |
565 | |
566 | let mut signum = mem::MaybeUninit::uninit(); |
567 | let res = unsafe { libc::sigwait(&self.sigset as *const libc::sigset_t, signum.as_mut_ptr()) }; |
568 | |
569 | Errno::result(res).map(|_| unsafe { |
570 | Signal::try_from(signum.assume_init()).unwrap() |
571 | }) |
572 | } |
573 | |
574 | /// Converts a `libc::sigset_t` object to a [`SigSet`] without checking whether the |
575 | /// `libc::sigset_t` is already initialized. |
576 | /// |
577 | /// # Safety |
578 | /// |
579 | /// The `sigset` passed in must be a valid an initialized `libc::sigset_t` by calling either |
580 | /// [`sigemptyset(3)`](https://man7.org/linux/man-pages/man3/sigemptyset.3p.html) or |
581 | /// [`sigfillset(3)`](https://man7.org/linux/man-pages/man3/sigfillset.3p.html). |
582 | /// Otherwise, the results are undefined. |
583 | pub unsafe fn from_sigset_t_unchecked(sigset: libc::sigset_t) -> SigSet { |
584 | SigSet { sigset } |
585 | } |
586 | } |
587 | |
588 | impl AsRef<libc::sigset_t> for SigSet { |
589 | fn as_ref(&self) -> &libc::sigset_t { |
590 | &self.sigset |
591 | } |
592 | } |
593 | |
594 | // TODO: Consider specialization for the case where T is &SigSet and libc::sigorset is available. |
595 | impl Extend<Signal> for SigSet { |
596 | fn extend<T>(&mut self, iter: T) |
597 | where T: IntoIterator<Item = Signal> { |
598 | for signal in iter { |
599 | self.add(signal); |
600 | } |
601 | } |
602 | } |
603 | |
604 | impl FromIterator<Signal> for SigSet { |
605 | fn from_iter<T>(iter: T) -> Self |
606 | where T: IntoIterator<Item = Signal> { |
607 | let mut sigset = SigSet::empty(); |
608 | sigset.extend(iter); |
609 | sigset |
610 | } |
611 | } |
612 | |
613 | /// Iterator for a [`SigSet`]. |
614 | /// |
615 | /// Call [`SigSet::iter`] to create an iterator. |
616 | #[derive (Clone, Debug)] |
617 | pub struct SigSetIter<'a> { |
618 | sigset: &'a SigSet, |
619 | inner: SignalIterator, |
620 | } |
621 | |
622 | impl Iterator for SigSetIter<'_> { |
623 | type Item = Signal; |
624 | fn next(&mut self) -> Option<Signal> { |
625 | loop { |
626 | match self.inner.next() { |
627 | None => return None, |
628 | Some(signal) if self.sigset.contains(signal) => return Some(signal), |
629 | Some(_signal) => continue, |
630 | } |
631 | } |
632 | } |
633 | } |
634 | |
635 | impl<'a> IntoIterator for &'a SigSet { |
636 | type Item = Signal; |
637 | type IntoIter = SigSetIter<'a>; |
638 | fn into_iter(self) -> Self::IntoIter { |
639 | SigSetIter { sigset: self, inner: Signal::iterator() } |
640 | } |
641 | } |
642 | |
643 | /// A signal handler. |
644 | #[allow (unknown_lints)] |
645 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
646 | pub enum SigHandler { |
647 | /// Default signal handling. |
648 | SigDfl, |
649 | /// Request that the signal be ignored. |
650 | SigIgn, |
651 | /// Use the given signal-catching function, which takes in the signal. |
652 | Handler(extern fn(libc::c_int)), |
653 | /// Use the given signal-catching function, which takes in the signal, information about how |
654 | /// the signal was generated, and a pointer to the threads `ucontext_t`. |
655 | #[cfg (not(target_os = "redox" ))] |
656 | #[cfg_attr (docsrs, doc(cfg(all())))] |
657 | SigAction(extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)) |
658 | } |
659 | |
660 | /// Action to take on receipt of a signal. Corresponds to `sigaction`. |
661 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
662 | pub struct SigAction { |
663 | sigaction: libc::sigaction |
664 | } |
665 | |
666 | impl SigAction { |
667 | /// Creates a new action. |
668 | /// |
669 | /// The `SA_SIGINFO` bit in the `flags` argument is ignored (it will be set only if `handler` |
670 | /// is the `SigAction` variant). `mask` specifies other signals to block during execution of |
671 | /// the signal-catching function. |
672 | pub fn new(handler: SigHandler, flags: SaFlags, mask: SigSet) -> SigAction { |
673 | unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) { |
674 | (*p).sa_sigaction = match handler { |
675 | SigHandler::SigDfl => libc::SIG_DFL, |
676 | SigHandler::SigIgn => libc::SIG_IGN, |
677 | SigHandler::Handler(f) => f as *const extern fn(libc::c_int) as usize, |
678 | #[cfg (not(target_os = "redox" ))] |
679 | SigHandler::SigAction(f) => f as *const extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void) as usize, |
680 | }; |
681 | } |
682 | |
683 | let mut s = mem::MaybeUninit::<libc::sigaction>::uninit(); |
684 | unsafe { |
685 | let p = s.as_mut_ptr(); |
686 | install_sig(p, handler); |
687 | (*p).sa_flags = match handler { |
688 | #[cfg (not(target_os = "redox" ))] |
689 | SigHandler::SigAction(_) => (flags | SaFlags::SA_SIGINFO).bits(), |
690 | _ => (flags - SaFlags::SA_SIGINFO).bits(), |
691 | }; |
692 | (*p).sa_mask = mask.sigset; |
693 | |
694 | SigAction { sigaction: s.assume_init() } |
695 | } |
696 | } |
697 | |
698 | /// Returns the flags set on the action. |
699 | pub fn flags(&self) -> SaFlags { |
700 | SaFlags::from_bits_truncate(self.sigaction.sa_flags) |
701 | } |
702 | |
703 | /// Returns the set of signals that are blocked during execution of the action's |
704 | /// signal-catching function. |
705 | pub fn mask(&self) -> SigSet { |
706 | SigSet { sigset: self.sigaction.sa_mask } |
707 | } |
708 | |
709 | /// Returns the action's handler. |
710 | pub fn handler(&self) -> SigHandler { |
711 | match self.sigaction.sa_sigaction { |
712 | libc::SIG_DFL => SigHandler::SigDfl, |
713 | libc::SIG_IGN => SigHandler::SigIgn, |
714 | #[cfg (not(target_os = "redox" ))] |
715 | p if self.flags().contains(SaFlags::SA_SIGINFO) => |
716 | SigHandler::SigAction( |
717 | // Safe for one of two reasons: |
718 | // * The SigHandler was created by SigHandler::new, in which |
719 | // case the pointer is correct, or |
720 | // * The SigHandler was created by signal or sigaction, which |
721 | // are unsafe functions, so the caller should've somehow |
722 | // ensured that it is correctly initialized. |
723 | unsafe{ |
724 | *(&p as *const usize |
725 | as *const extern fn(_, _, _)) |
726 | } |
727 | as extern fn(_, _, _)), |
728 | p => SigHandler::Handler( |
729 | // Safe for one of two reasons: |
730 | // * The SigHandler was created by SigHandler::new, in which |
731 | // case the pointer is correct, or |
732 | // * The SigHandler was created by signal or sigaction, which |
733 | // are unsafe functions, so the caller should've somehow |
734 | // ensured that it is correctly initialized. |
735 | unsafe{ |
736 | *(&p as *const usize |
737 | as *const extern fn(libc::c_int)) |
738 | } |
739 | as extern fn(libc::c_int)), |
740 | } |
741 | } |
742 | } |
743 | |
744 | /// Changes the action taken by a process on receipt of a specific signal. |
745 | /// |
746 | /// `signal` can be any signal except `SIGKILL` or `SIGSTOP`. On success, it returns the previous |
747 | /// action for the given signal. If `sigaction` fails, no new signal handler is installed. |
748 | /// |
749 | /// # Safety |
750 | /// |
751 | /// * Signal handlers may be called at any point during execution, which limits |
752 | /// what is safe to do in the body of the signal-catching function. Be certain |
753 | /// to only make syscalls that are explicitly marked safe for signal handlers |
754 | /// and only share global data using atomics. |
755 | /// |
756 | /// * There is also no guarantee that the old signal handler was installed |
757 | /// correctly. If it was installed by this crate, it will be. But if it was |
758 | /// installed by, for example, C code, then there is no guarantee its function |
759 | /// pointer is valid. In that case, this function effectively dereferences a |
760 | /// raw pointer of unknown provenance. |
761 | pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigAction> { |
762 | let mut oldact = mem::MaybeUninit::<libc::sigaction>::uninit(); |
763 | |
764 | let res = libc::sigaction(signal as libc::c_int, |
765 | &sigaction.sigaction as *const libc::sigaction, |
766 | oldact.as_mut_ptr()); |
767 | |
768 | Errno::result(res).map(|_| SigAction { sigaction: oldact.assume_init() }) |
769 | } |
770 | |
771 | /// Signal management (see [signal(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html)) |
772 | /// |
773 | /// Installs `handler` for the given `signal`, returning the previous signal |
774 | /// handler. `signal` should only be used following another call to `signal` or |
775 | /// if the current handler is the default. The return value of `signal` is |
776 | /// undefined after setting the handler with [`sigaction`][SigActionFn]. |
777 | /// |
778 | /// # Safety |
779 | /// |
780 | /// If the pointer to the previous signal handler is invalid, undefined |
781 | /// behavior could be invoked when casting it back to a [`SigAction`][SigActionStruct]. |
782 | /// |
783 | /// # Examples |
784 | /// |
785 | /// Ignore `SIGINT`: |
786 | /// |
787 | /// ```no_run |
788 | /// # use nix::sys::signal::{self, Signal, SigHandler}; |
789 | /// unsafe { signal::signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap(); |
790 | /// ``` |
791 | /// |
792 | /// Use a signal handler to set a flag variable: |
793 | /// |
794 | /// ```no_run |
795 | /// # #[macro_use] extern crate lazy_static; |
796 | /// # use std::convert::TryFrom; |
797 | /// # use std::sync::atomic::{AtomicBool, Ordering}; |
798 | /// # use nix::sys::signal::{self, Signal, SigHandler}; |
799 | /// lazy_static! { |
800 | /// static ref SIGNALED: AtomicBool = AtomicBool::new(false); |
801 | /// } |
802 | /// |
803 | /// extern fn handle_sigint(signal: libc::c_int) { |
804 | /// let signal = Signal::try_from(signal).unwrap(); |
805 | /// SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed); |
806 | /// } |
807 | /// |
808 | /// fn main() { |
809 | /// let handler = SigHandler::Handler(handle_sigint); |
810 | /// unsafe { signal::signal(Signal::SIGINT, handler) }.unwrap(); |
811 | /// } |
812 | /// ``` |
813 | /// |
814 | /// # Errors |
815 | /// |
816 | /// Returns [`Error(Errno::EOPNOTSUPP)`] if `handler` is |
817 | /// [`SigAction`][SigActionStruct]. Use [`sigaction`][SigActionFn] instead. |
818 | /// |
819 | /// `signal` also returns any error from `libc::signal`, such as when an attempt |
820 | /// is made to catch a signal that cannot be caught or to ignore a signal that |
821 | /// cannot be ignored. |
822 | /// |
823 | /// [`Error::UnsupportedOperation`]: ../../enum.Error.html#variant.UnsupportedOperation |
824 | /// [SigActionStruct]: struct.SigAction.html |
825 | /// [sigactionFn]: fn.sigaction.html |
826 | pub unsafe fn signal(signal: Signal, handler: SigHandler) -> Result<SigHandler> { |
827 | let signal = signal as libc::c_int; |
828 | let res = match handler { |
829 | SigHandler::SigDfl => libc::signal(signal, libc::SIG_DFL), |
830 | SigHandler::SigIgn => libc::signal(signal, libc::SIG_IGN), |
831 | SigHandler::Handler(handler) => libc::signal(signal, handler as libc::sighandler_t), |
832 | #[cfg (not(target_os = "redox" ))] |
833 | SigHandler::SigAction(_) => return Err(Errno::ENOTSUP), |
834 | }; |
835 | Errno::result(res).map(|oldhandler| { |
836 | match oldhandler { |
837 | libc::SIG_DFL => SigHandler::SigDfl, |
838 | libc::SIG_IGN => SigHandler::SigIgn, |
839 | p => SigHandler::Handler( |
840 | *(&p as *const usize |
841 | as *const extern fn(libc::c_int)) |
842 | as extern fn(libc::c_int)), |
843 | } |
844 | }) |
845 | } |
846 | |
847 | fn do_pthread_sigmask(how: SigmaskHow, |
848 | set: Option<&SigSet>, |
849 | oldset: Option<*mut libc::sigset_t>) -> Result<()> { |
850 | if set.is_none() && oldset.is_none() { |
851 | return Ok(()) |
852 | } |
853 | |
854 | let res = unsafe { |
855 | // if set or oldset is None, pass in null pointers instead |
856 | libc::pthread_sigmask(how as libc::c_int, |
857 | set.map_or_else(ptr::null::<libc::sigset_t>, |
858 | |s| &s.sigset as *const libc::sigset_t), |
859 | oldset.unwrap_or(ptr::null_mut()) |
860 | ) |
861 | }; |
862 | |
863 | Errno::result(res).map(drop) |
864 | } |
865 | |
866 | /// Manages the signal mask (set of blocked signals) for the calling thread. |
867 | /// |
868 | /// If the `set` parameter is `Some(..)`, then the signal mask will be updated with the signal set. |
869 | /// The `how` flag decides the type of update. If `set` is `None`, `how` will be ignored, |
870 | /// and no modification will take place. |
871 | /// |
872 | /// If the 'oldset' parameter is `Some(..)` then the current signal mask will be written into it. |
873 | /// |
874 | /// If both `set` and `oldset` is `Some(..)`, the current signal mask will be written into oldset, |
875 | /// and then it will be updated with `set`. |
876 | /// |
877 | /// If both `set` and `oldset` is None, this function is a no-op. |
878 | /// |
879 | /// For more information, visit the [`pthread_sigmask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html), |
880 | /// or [`sigprocmask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html) man pages. |
881 | pub fn pthread_sigmask(how: SigmaskHow, |
882 | set: Option<&SigSet>, |
883 | oldset: Option<&mut SigSet>) -> Result<()> |
884 | { |
885 | do_pthread_sigmask(how, set, oldset.map(|os| &mut os.sigset as *mut _ )) |
886 | } |
887 | |
888 | /// Examine and change blocked signals. |
889 | /// |
890 | /// For more information see the [`sigprocmask` man |
891 | /// pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html). |
892 | pub fn sigprocmask(how: SigmaskHow, set: Option<&SigSet>, oldset: Option<&mut SigSet>) -> Result<()> { |
893 | if set.is_none() && oldset.is_none() { |
894 | return Ok(()) |
895 | } |
896 | |
897 | let res = unsafe { |
898 | // if set or oldset is None, pass in null pointers instead |
899 | libc::sigprocmask(how as libc::c_int, |
900 | set.map_or_else(ptr::null::<libc::sigset_t>, |
901 | |s| &s.sigset as *const libc::sigset_t), |
902 | oldset.map_or_else(ptr::null_mut::<libc::sigset_t>, |
903 | |os| &mut os.sigset as *mut libc::sigset_t)) |
904 | }; |
905 | |
906 | Errno::result(res).map(drop) |
907 | } |
908 | |
909 | /// Send a signal to a process |
910 | /// |
911 | /// # Arguments |
912 | /// |
913 | /// * `pid` - Specifies which processes should receive the signal. |
914 | /// - If positive, specifies an individual process. |
915 | /// - If zero, the signal will be sent to all processes whose group |
916 | /// ID is equal to the process group ID of the sender. This is a |
917 | #[cfg_attr (target_os = "fuchsia" , doc = "variant of `killpg`." )] |
918 | #[cfg_attr (not(target_os = "fuchsia" ), doc = "variant of [`killpg`]." )] |
919 | /// - If `-1` and the process has super-user privileges, the signal |
920 | /// is sent to all processes exclusing system processes. |
921 | /// - If less than `-1`, the signal is sent to all processes whose |
922 | /// process group ID is equal to the absolute value of `pid`. |
923 | /// * `signal` - Signal to send. If `None`, error checking is performed |
924 | /// but no signal is actually sent. |
925 | /// |
926 | /// See Also |
927 | /// [`kill(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html) |
928 | pub fn kill<T: Into<Option<Signal>>>(pid: Pid, signal: T) -> Result<()> { |
929 | let res = unsafe { libc::kill(pid.into(), |
930 | match signal.into() { |
931 | Some(s) => s as libc::c_int, |
932 | None => 0, |
933 | }) }; |
934 | |
935 | Errno::result(res).map(drop) |
936 | } |
937 | |
938 | /// Send a signal to a process group |
939 | /// |
940 | /// # Arguments |
941 | /// |
942 | /// * `pgrp` - Process group to signal. If less then or equal 1, the behavior |
943 | /// is platform-specific. |
944 | /// * `signal` - Signal to send. If `None`, `killpg` will only preform error |
945 | /// checking and won't send any signal. |
946 | /// |
947 | /// See Also [killpg(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html). |
948 | #[cfg (not(target_os = "fuchsia" ))] |
949 | pub fn killpg<T: Into<Option<Signal>>>(pgrp: Pid, signal: T) -> Result<()> { |
950 | let res = unsafe { libc::killpg(pgrp.into(), |
951 | match signal.into() { |
952 | Some(s) => s as libc::c_int, |
953 | None => 0, |
954 | }) }; |
955 | |
956 | Errno::result(res).map(drop) |
957 | } |
958 | |
959 | /// Send a signal to the current thread |
960 | /// |
961 | /// See Also [raise(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/raise.html) |
962 | pub fn raise(signal: Signal) -> Result<()> { |
963 | let res = unsafe { libc::raise(signal as libc::c_int) }; |
964 | |
965 | Errno::result(res).map(drop) |
966 | } |
967 | } |
968 | |
969 | feature! { |
970 | #![any(feature = "aio" , feature = "signal" )] |
971 | |
972 | /// Identifies a thread for [`SigevNotify::SigevThreadId`] |
973 | #[cfg (target_os = "freebsd" )] |
974 | pub type type_of_thread_id = libc::lwpid_t; |
975 | /// Identifies a thread for [`SigevNotify::SigevThreadId`] |
976 | #[cfg (target_os = "linux" )] |
977 | pub type type_of_thread_id = libc::pid_t; |
978 | |
979 | /// Specifies the notification method used by a [`SigEvent`] |
980 | // sigval is actually a union of a int and a void*. But it's never really used |
981 | // as a pointer, because neither libc nor the kernel ever dereference it. nix |
982 | // therefore presents it as an intptr_t, which is how kevent uses it. |
983 | #[cfg (not(any(target_os = "openbsd" , target_os = "redox" )))] |
984 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
985 | pub enum SigevNotify { |
986 | /// No notification will be delivered |
987 | SigevNone, |
988 | /// Notify by delivering a signal to the process. |
989 | SigevSignal { |
990 | /// Signal to deliver |
991 | signal: Signal, |
992 | /// Will be present in the `si_value` field of the [`libc::siginfo_t`] |
993 | /// structure of the queued signal. |
994 | si_value: libc::intptr_t |
995 | }, |
996 | // Note: SIGEV_THREAD is not implemented because libc::sigevent does not |
997 | // expose a way to set the union members needed by SIGEV_THREAD. |
998 | /// Notify by delivering an event to a kqueue. |
999 | #[cfg (any(target_os = "dragonfly" , target_os = "freebsd" ))] |
1000 | #[cfg_attr (docsrs, doc(cfg(all())))] |
1001 | SigevKevent { |
1002 | /// File descriptor of the kqueue to notify. |
1003 | kq: RawFd, |
1004 | /// Will be contained in the kevent's `udata` field. |
1005 | udata: libc::intptr_t |
1006 | }, |
1007 | /// Notify by delivering a signal to a thread. |
1008 | #[cfg (any(target_os = "freebsd" , target_os = "linux" ))] |
1009 | #[cfg_attr (docsrs, doc(cfg(all())))] |
1010 | SigevThreadId { |
1011 | /// Signal to send |
1012 | signal: Signal, |
1013 | /// LWP ID of the thread to notify |
1014 | thread_id: type_of_thread_id, |
1015 | /// Will be present in the `si_value` field of the [`libc::siginfo_t`] |
1016 | /// structure of the queued signal. |
1017 | si_value: libc::intptr_t |
1018 | }, |
1019 | } |
1020 | } |
1021 | |
1022 | #[cfg (not(any(target_os = "openbsd" , target_os = "redox" )))] |
1023 | #[cfg_attr (docsrs, doc(cfg(all())))] |
1024 | mod sigevent { |
1025 | feature! { |
1026 | #![any(feature = "aio" , feature = "signal" )] |
1027 | |
1028 | use std::mem; |
1029 | use std::ptr; |
1030 | use super::SigevNotify; |
1031 | #[cfg (any(target_os = "freebsd" , target_os = "linux" ))] |
1032 | use super::type_of_thread_id; |
1033 | |
1034 | /// Used to request asynchronous notification of the completion of certain |
1035 | /// events, such as POSIX AIO and timers. |
1036 | #[repr (C)] |
1037 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
1038 | pub struct SigEvent { |
1039 | sigevent: libc::sigevent |
1040 | } |
1041 | |
1042 | impl SigEvent { |
1043 | /// **Note:** this constructor does not allow the user to set the |
1044 | /// `sigev_notify_kevent_flags` field. That's considered ok because on FreeBSD |
1045 | /// at least those flags don't do anything useful. That field is part of a |
1046 | /// union that shares space with the more genuinely useful fields. |
1047 | /// |
1048 | /// **Note:** This constructor also doesn't allow the caller to set the |
1049 | /// `sigev_notify_function` or `sigev_notify_attributes` fields, which are |
1050 | /// required for `SIGEV_THREAD`. That's considered ok because on no operating |
1051 | /// system is `SIGEV_THREAD` the most efficient way to deliver AIO |
1052 | /// notification. FreeBSD and DragonFly BSD programs should prefer `SIGEV_KEVENT`. |
1053 | /// Linux, Solaris, and portable programs should prefer `SIGEV_THREAD_ID` or |
1054 | /// `SIGEV_SIGNAL`. That field is part of a union that shares space with the |
1055 | /// more genuinely useful `sigev_notify_thread_id` |
1056 | // Allow invalid_value warning on Fuchsia only. |
1057 | // See https://github.com/nix-rust/nix/issues/1441 |
1058 | #[cfg_attr (target_os = "fuchsia" , allow(invalid_value))] |
1059 | pub fn new(sigev_notify: SigevNotify) -> SigEvent { |
1060 | let mut sev = unsafe { mem::MaybeUninit::<libc::sigevent>::zeroed().assume_init() }; |
1061 | sev.sigev_notify = match sigev_notify { |
1062 | SigevNotify::SigevNone => libc::SIGEV_NONE, |
1063 | SigevNotify::SigevSignal{..} => libc::SIGEV_SIGNAL, |
1064 | #[cfg (any(target_os = "dragonfly" , target_os = "freebsd" ))] |
1065 | SigevNotify::SigevKevent{..} => libc::SIGEV_KEVENT, |
1066 | #[cfg (target_os = "freebsd" )] |
1067 | SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, |
1068 | #[cfg (all(target_os = "linux" , target_env = "gnu" , not(target_arch = "mips" )))] |
1069 | SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, |
1070 | #[cfg (all(target_os = "linux" , target_env = "uclibc" ))] |
1071 | SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, |
1072 | #[cfg (any(all(target_os = "linux" , target_env = "musl" ), target_arch = "mips" ))] |
1073 | SigevNotify::SigevThreadId{..} => 4 // No SIGEV_THREAD_ID defined |
1074 | }; |
1075 | sev.sigev_signo = match sigev_notify { |
1076 | SigevNotify::SigevSignal{ signal, .. } => signal as libc::c_int, |
1077 | #[cfg (any(target_os = "dragonfly" , target_os = "freebsd" ))] |
1078 | SigevNotify::SigevKevent{ kq, ..} => kq, |
1079 | #[cfg (any(target_os = "linux" , target_os = "freebsd" ))] |
1080 | SigevNotify::SigevThreadId{ signal, .. } => signal as libc::c_int, |
1081 | _ => 0 |
1082 | }; |
1083 | sev.sigev_value.sival_ptr = match sigev_notify { |
1084 | SigevNotify::SigevNone => ptr::null_mut::<libc::c_void>(), |
1085 | SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut libc::c_void, |
1086 | #[cfg (any(target_os = "dragonfly" , target_os = "freebsd" ))] |
1087 | SigevNotify::SigevKevent{ udata, .. } => udata as *mut libc::c_void, |
1088 | #[cfg (any(target_os = "freebsd" , target_os = "linux" ))] |
1089 | SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut libc::c_void, |
1090 | }; |
1091 | SigEvent::set_tid(&mut sev, &sigev_notify); |
1092 | SigEvent{sigevent: sev} |
1093 | } |
1094 | |
1095 | #[cfg (any(target_os = "freebsd" , target_os = "linux" ))] |
1096 | fn set_tid(sev: &mut libc::sigevent, sigev_notify: &SigevNotify) { |
1097 | sev.sigev_notify_thread_id = match *sigev_notify { |
1098 | SigevNotify::SigevThreadId { thread_id, .. } => thread_id, |
1099 | _ => 0 as type_of_thread_id |
1100 | }; |
1101 | } |
1102 | |
1103 | #[cfg (not(any(target_os = "freebsd" , target_os = "linux" )))] |
1104 | fn set_tid(_sev: &mut libc::sigevent, _sigev_notify: &SigevNotify) { |
1105 | } |
1106 | |
1107 | /// Return a copy of the inner structure |
1108 | pub fn sigevent(&self) -> libc::sigevent { |
1109 | self.sigevent |
1110 | } |
1111 | |
1112 | /// Returns a mutable pointer to the `sigevent` wrapped by `self` |
1113 | pub fn as_mut_ptr(&mut self) -> *mut libc::sigevent { |
1114 | &mut self.sigevent |
1115 | } |
1116 | } |
1117 | |
1118 | impl<'a> From<&'a libc::sigevent> for SigEvent { |
1119 | fn from(sigevent: &libc::sigevent) -> Self { |
1120 | SigEvent{ sigevent: *sigevent } |
1121 | } |
1122 | } |
1123 | } |
1124 | } |
1125 | |
1126 | #[cfg (test)] |
1127 | mod tests { |
1128 | use super::*; |
1129 | #[cfg (not(target_os = "redox" ))] |
1130 | use std::thread; |
1131 | |
1132 | #[test ] |
1133 | fn test_contains() { |
1134 | let mut mask = SigSet::empty(); |
1135 | mask.add(SIGUSR1); |
1136 | |
1137 | assert!(mask.contains(SIGUSR1)); |
1138 | assert!(!mask.contains(SIGUSR2)); |
1139 | |
1140 | let all = SigSet::all(); |
1141 | assert!(all.contains(SIGUSR1)); |
1142 | assert!(all.contains(SIGUSR2)); |
1143 | } |
1144 | |
1145 | #[test ] |
1146 | fn test_clear() { |
1147 | let mut set = SigSet::all(); |
1148 | set.clear(); |
1149 | for signal in Signal::iterator() { |
1150 | assert!(!set.contains(signal)); |
1151 | } |
1152 | } |
1153 | |
1154 | #[test ] |
1155 | fn test_from_str_round_trips() { |
1156 | for signal in Signal::iterator() { |
1157 | assert_eq!(signal.as_ref().parse::<Signal>().unwrap(), signal); |
1158 | assert_eq!(signal.to_string().parse::<Signal>().unwrap(), signal); |
1159 | } |
1160 | } |
1161 | |
1162 | #[test ] |
1163 | fn test_from_str_invalid_value() { |
1164 | let errval = Err(Errno::EINVAL); |
1165 | assert_eq!("NOSIGNAL" .parse::<Signal>(), errval); |
1166 | assert_eq!("kill" .parse::<Signal>(), errval); |
1167 | assert_eq!("9" .parse::<Signal>(), errval); |
1168 | } |
1169 | |
1170 | #[test ] |
1171 | fn test_extend() { |
1172 | let mut one_signal = SigSet::empty(); |
1173 | one_signal.add(SIGUSR1); |
1174 | |
1175 | let mut two_signals = SigSet::empty(); |
1176 | two_signals.add(SIGUSR2); |
1177 | two_signals.extend(&one_signal); |
1178 | |
1179 | assert!(two_signals.contains(SIGUSR1)); |
1180 | assert!(two_signals.contains(SIGUSR2)); |
1181 | } |
1182 | |
1183 | #[test ] |
1184 | #[cfg (not(target_os = "redox" ))] |
1185 | fn test_thread_signal_set_mask() { |
1186 | thread::spawn(|| { |
1187 | let prev_mask = SigSet::thread_get_mask() |
1188 | .expect("Failed to get existing signal mask!" ); |
1189 | |
1190 | let mut test_mask = prev_mask; |
1191 | test_mask.add(SIGUSR1); |
1192 | |
1193 | test_mask.thread_set_mask().expect("assertion failed" ); |
1194 | let new_mask = |
1195 | SigSet::thread_get_mask().expect("Failed to get new mask!" ); |
1196 | |
1197 | assert!(new_mask.contains(SIGUSR1)); |
1198 | assert!(!new_mask.contains(SIGUSR2)); |
1199 | |
1200 | prev_mask |
1201 | .thread_set_mask() |
1202 | .expect("Failed to revert signal mask!" ); |
1203 | }) |
1204 | .join() |
1205 | .unwrap(); |
1206 | } |
1207 | |
1208 | #[test ] |
1209 | #[cfg (not(target_os = "redox" ))] |
1210 | fn test_thread_signal_block() { |
1211 | thread::spawn(|| { |
1212 | let mut mask = SigSet::empty(); |
1213 | mask.add(SIGUSR1); |
1214 | |
1215 | mask.thread_block().expect("assertion failed" ); |
1216 | |
1217 | assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); |
1218 | }) |
1219 | .join() |
1220 | .unwrap(); |
1221 | } |
1222 | |
1223 | #[test ] |
1224 | #[cfg (not(target_os = "redox" ))] |
1225 | fn test_thread_signal_unblock() { |
1226 | thread::spawn(|| { |
1227 | let mut mask = SigSet::empty(); |
1228 | mask.add(SIGUSR1); |
1229 | |
1230 | mask.thread_unblock().expect("assertion failed" ); |
1231 | |
1232 | assert!(!SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); |
1233 | }) |
1234 | .join() |
1235 | .unwrap(); |
1236 | } |
1237 | |
1238 | #[test ] |
1239 | #[cfg (not(target_os = "redox" ))] |
1240 | fn test_thread_signal_swap() { |
1241 | thread::spawn(|| { |
1242 | let mut mask = SigSet::empty(); |
1243 | mask.add(SIGUSR1); |
1244 | mask.thread_block().unwrap(); |
1245 | |
1246 | assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); |
1247 | |
1248 | let mut mask2 = SigSet::empty(); |
1249 | mask2.add(SIGUSR2); |
1250 | |
1251 | let oldmask = |
1252 | mask2.thread_swap_mask(SigmaskHow::SIG_SETMASK).unwrap(); |
1253 | |
1254 | assert!(oldmask.contains(SIGUSR1)); |
1255 | assert!(!oldmask.contains(SIGUSR2)); |
1256 | |
1257 | assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR2)); |
1258 | }) |
1259 | .join() |
1260 | .unwrap(); |
1261 | } |
1262 | |
1263 | #[test ] |
1264 | fn test_from_and_into_iterator() { |
1265 | let sigset = SigSet::from_iter(vec![Signal::SIGUSR1, Signal::SIGUSR2]); |
1266 | let signals = sigset.into_iter().collect::<Vec<Signal>>(); |
1267 | assert_eq!(signals, [Signal::SIGUSR1, Signal::SIGUSR2]); |
1268 | } |
1269 | |
1270 | #[test ] |
1271 | #[cfg (not(target_os = "redox" ))] |
1272 | fn test_sigaction() { |
1273 | thread::spawn(|| { |
1274 | extern "C" fn test_sigaction_handler(_: libc::c_int) {} |
1275 | extern "C" fn test_sigaction_action( |
1276 | _: libc::c_int, |
1277 | _: *mut libc::siginfo_t, |
1278 | _: *mut libc::c_void, |
1279 | ) { |
1280 | } |
1281 | |
1282 | let handler_sig = SigHandler::Handler(test_sigaction_handler); |
1283 | |
1284 | let flags = |
1285 | SaFlags::SA_ONSTACK | SaFlags::SA_RESTART | SaFlags::SA_SIGINFO; |
1286 | |
1287 | let mut mask = SigSet::empty(); |
1288 | mask.add(SIGUSR1); |
1289 | |
1290 | let action_sig = SigAction::new(handler_sig, flags, mask); |
1291 | |
1292 | assert_eq!( |
1293 | action_sig.flags(), |
1294 | SaFlags::SA_ONSTACK | SaFlags::SA_RESTART |
1295 | ); |
1296 | assert_eq!(action_sig.handler(), handler_sig); |
1297 | |
1298 | mask = action_sig.mask(); |
1299 | assert!(mask.contains(SIGUSR1)); |
1300 | assert!(!mask.contains(SIGUSR2)); |
1301 | |
1302 | let handler_act = SigHandler::SigAction(test_sigaction_action); |
1303 | let action_act = SigAction::new(handler_act, flags, mask); |
1304 | assert_eq!(action_act.handler(), handler_act); |
1305 | |
1306 | let action_dfl = SigAction::new(SigHandler::SigDfl, flags, mask); |
1307 | assert_eq!(action_dfl.handler(), SigHandler::SigDfl); |
1308 | |
1309 | let action_ign = SigAction::new(SigHandler::SigIgn, flags, mask); |
1310 | assert_eq!(action_ign.handler(), SigHandler::SigIgn); |
1311 | }) |
1312 | .join() |
1313 | .unwrap(); |
1314 | } |
1315 | |
1316 | #[test ] |
1317 | #[cfg (not(target_os = "redox" ))] |
1318 | fn test_sigwait() { |
1319 | thread::spawn(|| { |
1320 | let mut mask = SigSet::empty(); |
1321 | mask.add(SIGUSR1); |
1322 | mask.add(SIGUSR2); |
1323 | mask.thread_block().unwrap(); |
1324 | |
1325 | raise(SIGUSR1).unwrap(); |
1326 | assert_eq!(mask.wait().unwrap(), SIGUSR1); |
1327 | }) |
1328 | .join() |
1329 | .unwrap(); |
1330 | } |
1331 | |
1332 | #[test ] |
1333 | fn test_from_sigset_t_unchecked() { |
1334 | let src_set = SigSet::empty(); |
1335 | let set = unsafe { SigSet::from_sigset_t_unchecked(src_set.sigset) }; |
1336 | |
1337 | for signal in Signal::iterator() { |
1338 | assert!(!set.contains(signal)); |
1339 | } |
1340 | |
1341 | let src_set = SigSet::all(); |
1342 | let set = unsafe { SigSet::from_sigset_t_unchecked(src_set.sigset) }; |
1343 | |
1344 | for signal in Signal::iterator() { |
1345 | assert!(set.contains(signal)); |
1346 | } |
1347 | } |
1348 | } |
1349 | |