blob: 2f8b5fa88823d05192bfadb3c1d3fd6e3b7ef42e [file] [log] [blame]
Andrew Walbran12f61402020-10-14 11:10:53 +01001// Portions of this file are Copyright 2014 The Rust Project Developers.
2// See http://rust-lang.org/COPYRIGHT.
3
4///! Operating system signals.
5
6use crate::{Error, Result};
7use crate::errno::Errno;
8use crate::unistd::Pid;
9use std::convert::TryFrom;
10use std::mem;
11use std::fmt;
12use std::str::FromStr;
13#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
14use std::os::unix::io::RawFd;
15use std::ptr;
16
17#[cfg(not(any(target_os = "openbsd", target_os = "redox")))]
18pub use self::sigevent::*;
19
20libc_enum!{
21 // Currently there is only one definition of c_int in libc, as well as only one
22 // type for signal constants.
23 // We would prefer to use the libc::c_int alias in the repr attribute. Unfortunately
24 // this is not (yet) possible.
25 #[repr(i32)]
26 pub enum Signal {
27 SIGHUP,
28 SIGINT,
29 SIGQUIT,
30 SIGILL,
31 SIGTRAP,
32 SIGABRT,
33 SIGBUS,
34 SIGFPE,
35 SIGKILL,
36 SIGUSR1,
37 SIGSEGV,
38 SIGUSR2,
39 SIGPIPE,
40 SIGALRM,
41 SIGTERM,
Joel Galenson4727c112021-04-02 12:22:24 -070042 #[cfg(all(any(target_os = "android", target_os = "emscripten",
43 target_os = "fuchsia", target_os = "linux"),
44 not(any(target_arch = "mips", target_arch = "mips64",
45 target_arch = "sparc64"))))]
Andrew Walbran12f61402020-10-14 11:10:53 +010046 SIGSTKFLT,
47 SIGCHLD,
48 SIGCONT,
49 SIGSTOP,
50 SIGTSTP,
51 SIGTTIN,
52 SIGTTOU,
53 SIGURG,
54 SIGXCPU,
55 SIGXFSZ,
56 SIGVTALRM,
57 SIGPROF,
58 SIGWINCH,
59 SIGIO,
Joel Galenson4727c112021-04-02 12:22:24 -070060 #[cfg(any(target_os = "android", target_os = "emscripten",
61 target_os = "fuchsia", target_os = "linux"))]
Andrew Walbran12f61402020-10-14 11:10:53 +010062 SIGPWR,
63 SIGSYS,
64 #[cfg(not(any(target_os = "android", target_os = "emscripten",
Joel Galenson4727c112021-04-02 12:22:24 -070065 target_os = "fuchsia", target_os = "linux",
66 target_os = "redox")))]
Andrew Walbran12f61402020-10-14 11:10:53 +010067 SIGEMT,
68 #[cfg(not(any(target_os = "android", target_os = "emscripten",
Joel Galenson4727c112021-04-02 12:22:24 -070069 target_os = "fuchsia", target_os = "linux",
70 target_os = "redox")))]
Andrew Walbran12f61402020-10-14 11:10:53 +010071 SIGINFO,
72 }
73}
74
75impl FromStr for Signal {
76 type Err = Error;
77 fn from_str(s: &str) -> Result<Signal> {
78 Ok(match s {
79 "SIGHUP" => Signal::SIGHUP,
80 "SIGINT" => Signal::SIGINT,
81 "SIGQUIT" => Signal::SIGQUIT,
82 "SIGILL" => Signal::SIGILL,
83 "SIGTRAP" => Signal::SIGTRAP,
84 "SIGABRT" => Signal::SIGABRT,
85 "SIGBUS" => Signal::SIGBUS,
86 "SIGFPE" => Signal::SIGFPE,
87 "SIGKILL" => Signal::SIGKILL,
88 "SIGUSR1" => Signal::SIGUSR1,
89 "SIGSEGV" => Signal::SIGSEGV,
90 "SIGUSR2" => Signal::SIGUSR2,
91 "SIGPIPE" => Signal::SIGPIPE,
92 "SIGALRM" => Signal::SIGALRM,
93 "SIGTERM" => Signal::SIGTERM,
Joel Galenson4727c112021-04-02 12:22:24 -070094 #[cfg(all(any(target_os = "android", target_os = "emscripten",
95 target_os = "fuchsia", target_os = "linux"),
96 not(any(target_arch = "mips", target_arch = "mips64",
97 target_arch = "sparc64"))))]
Andrew Walbran12f61402020-10-14 11:10:53 +010098 "SIGSTKFLT" => Signal::SIGSTKFLT,
99 "SIGCHLD" => Signal::SIGCHLD,
100 "SIGCONT" => Signal::SIGCONT,
101 "SIGSTOP" => Signal::SIGSTOP,
102 "SIGTSTP" => Signal::SIGTSTP,
103 "SIGTTIN" => Signal::SIGTTIN,
104 "SIGTTOU" => Signal::SIGTTOU,
105 "SIGURG" => Signal::SIGURG,
106 "SIGXCPU" => Signal::SIGXCPU,
107 "SIGXFSZ" => Signal::SIGXFSZ,
108 "SIGVTALRM" => Signal::SIGVTALRM,
109 "SIGPROF" => Signal::SIGPROF,
110 "SIGWINCH" => Signal::SIGWINCH,
111 "SIGIO" => Signal::SIGIO,
Joel Galenson4727c112021-04-02 12:22:24 -0700112 #[cfg(any(target_os = "android", target_os = "emscripten",
113 target_os = "fuchsia", target_os = "linux"))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100114 "SIGPWR" => Signal::SIGPWR,
115 "SIGSYS" => Signal::SIGSYS,
116 #[cfg(not(any(target_os = "android", target_os = "emscripten",
Joel Galenson4727c112021-04-02 12:22:24 -0700117 target_os = "fuchsia", target_os = "linux",
118 target_os = "redox")))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100119 "SIGEMT" => Signal::SIGEMT,
120 #[cfg(not(any(target_os = "android", target_os = "emscripten",
Joel Galenson4727c112021-04-02 12:22:24 -0700121 target_os = "fuchsia", target_os = "linux",
122 target_os = "redox")))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100123 "SIGINFO" => Signal::SIGINFO,
124 _ => return Err(Error::invalid_argument()),
125 })
126 }
127}
128
129impl Signal {
130 /// Returns name of signal.
131 ///
132 /// This function is equivalent to `<Signal as AsRef<str>>::as_ref()`,
133 /// with difference that returned string is `'static`
134 /// and not bound to `self`'s lifetime.
135 pub fn as_str(self) -> &'static str {
136 match self {
137 Signal::SIGHUP => "SIGHUP",
138 Signal::SIGINT => "SIGINT",
139 Signal::SIGQUIT => "SIGQUIT",
140 Signal::SIGILL => "SIGILL",
141 Signal::SIGTRAP => "SIGTRAP",
142 Signal::SIGABRT => "SIGABRT",
143 Signal::SIGBUS => "SIGBUS",
144 Signal::SIGFPE => "SIGFPE",
145 Signal::SIGKILL => "SIGKILL",
146 Signal::SIGUSR1 => "SIGUSR1",
147 Signal::SIGSEGV => "SIGSEGV",
148 Signal::SIGUSR2 => "SIGUSR2",
149 Signal::SIGPIPE => "SIGPIPE",
150 Signal::SIGALRM => "SIGALRM",
151 Signal::SIGTERM => "SIGTERM",
Joel Galenson4727c112021-04-02 12:22:24 -0700152 #[cfg(all(any(target_os = "android", target_os = "emscripten",
153 target_os = "fuchsia", target_os = "linux"),
Andrew Walbran12f61402020-10-14 11:10:53 +0100154 not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))]
155 Signal::SIGSTKFLT => "SIGSTKFLT",
156 Signal::SIGCHLD => "SIGCHLD",
157 Signal::SIGCONT => "SIGCONT",
158 Signal::SIGSTOP => "SIGSTOP",
159 Signal::SIGTSTP => "SIGTSTP",
160 Signal::SIGTTIN => "SIGTTIN",
161 Signal::SIGTTOU => "SIGTTOU",
162 Signal::SIGURG => "SIGURG",
163 Signal::SIGXCPU => "SIGXCPU",
164 Signal::SIGXFSZ => "SIGXFSZ",
165 Signal::SIGVTALRM => "SIGVTALRM",
166 Signal::SIGPROF => "SIGPROF",
167 Signal::SIGWINCH => "SIGWINCH",
168 Signal::SIGIO => "SIGIO",
Joel Galenson4727c112021-04-02 12:22:24 -0700169 #[cfg(any(target_os = "android", target_os = "emscripten",
170 target_os = "fuchsia", target_os = "linux"))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100171 Signal::SIGPWR => "SIGPWR",
172 Signal::SIGSYS => "SIGSYS",
173 #[cfg(not(any(target_os = "android", target_os = "emscripten",
Joel Galenson4727c112021-04-02 12:22:24 -0700174 target_os = "fuchsia", target_os = "linux",
175 target_os = "redox")))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100176 Signal::SIGEMT => "SIGEMT",
177 #[cfg(not(any(target_os = "android", target_os = "emscripten",
Joel Galenson4727c112021-04-02 12:22:24 -0700178 target_os = "fuchsia", target_os = "linux",
179 target_os = "redox")))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100180 Signal::SIGINFO => "SIGINFO",
181 }
182 }
183}
184
185impl AsRef<str> for Signal {
186 fn as_ref(&self) -> &str {
187 self.as_str()
188 }
189}
190
191impl fmt::Display for Signal {
192 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
193 f.write_str(self.as_ref())
194 }
195}
196
197pub use self::Signal::*;
198
199#[cfg(target_os = "redox")]
200const SIGNALS: [Signal; 29] = [
201 SIGHUP,
202 SIGINT,
203 SIGQUIT,
204 SIGILL,
205 SIGTRAP,
206 SIGABRT,
207 SIGBUS,
208 SIGFPE,
209 SIGKILL,
210 SIGUSR1,
211 SIGSEGV,
212 SIGUSR2,
213 SIGPIPE,
214 SIGALRM,
215 SIGTERM,
216 SIGCHLD,
217 SIGCONT,
218 SIGSTOP,
219 SIGTSTP,
220 SIGTTIN,
221 SIGTTOU,
222 SIGURG,
223 SIGXCPU,
224 SIGXFSZ,
225 SIGVTALRM,
226 SIGPROF,
227 SIGWINCH,
228 SIGIO,
229 SIGSYS];
Joel Galenson4727c112021-04-02 12:22:24 -0700230#[cfg(all(any(target_os = "linux", target_os = "android",
231 target_os = "emscripten", target_os = "fuchsia"),
232 not(any(target_arch = "mips", target_arch = "mips64",
233 target_arch = "sparc64"))))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100234const SIGNALS: [Signal; 31] = [
235 SIGHUP,
236 SIGINT,
237 SIGQUIT,
238 SIGILL,
239 SIGTRAP,
240 SIGABRT,
241 SIGBUS,
242 SIGFPE,
243 SIGKILL,
244 SIGUSR1,
245 SIGSEGV,
246 SIGUSR2,
247 SIGPIPE,
248 SIGALRM,
249 SIGTERM,
250 SIGSTKFLT,
251 SIGCHLD,
252 SIGCONT,
253 SIGSTOP,
254 SIGTSTP,
255 SIGTTIN,
256 SIGTTOU,
257 SIGURG,
258 SIGXCPU,
259 SIGXFSZ,
260 SIGVTALRM,
261 SIGPROF,
262 SIGWINCH,
263 SIGIO,
264 SIGPWR,
265 SIGSYS];
Joel Galenson4727c112021-04-02 12:22:24 -0700266#[cfg(all(any(target_os = "linux", target_os = "android",
267 target_os = "emscripten", target_os = "fuchsia"),
268 any(target_arch = "mips", target_arch = "mips64",
269 target_arch = "sparc64")))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100270const SIGNALS: [Signal; 30] = [
271 SIGHUP,
272 SIGINT,
273 SIGQUIT,
274 SIGILL,
275 SIGTRAP,
276 SIGABRT,
277 SIGBUS,
278 SIGFPE,
279 SIGKILL,
280 SIGUSR1,
281 SIGSEGV,
282 SIGUSR2,
283 SIGPIPE,
284 SIGALRM,
285 SIGTERM,
286 SIGCHLD,
287 SIGCONT,
288 SIGSTOP,
289 SIGTSTP,
290 SIGTTIN,
291 SIGTTOU,
292 SIGURG,
293 SIGXCPU,
294 SIGXFSZ,
295 SIGVTALRM,
296 SIGPROF,
297 SIGWINCH,
298 SIGIO,
299 SIGPWR,
300 SIGSYS];
301#[cfg(not(any(target_os = "linux", target_os = "android",
Joel Galenson4727c112021-04-02 12:22:24 -0700302 target_os = "fuchsia", target_os = "emscripten",
303 target_os = "redox")))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100304const SIGNALS: [Signal; 31] = [
305 SIGHUP,
306 SIGINT,
307 SIGQUIT,
308 SIGILL,
309 SIGTRAP,
310 SIGABRT,
311 SIGBUS,
312 SIGFPE,
313 SIGKILL,
314 SIGUSR1,
315 SIGSEGV,
316 SIGUSR2,
317 SIGPIPE,
318 SIGALRM,
319 SIGTERM,
320 SIGCHLD,
321 SIGCONT,
322 SIGSTOP,
323 SIGTSTP,
324 SIGTTIN,
325 SIGTTOU,
326 SIGURG,
327 SIGXCPU,
328 SIGXFSZ,
329 SIGVTALRM,
330 SIGPROF,
331 SIGWINCH,
332 SIGIO,
333 SIGSYS,
334 SIGEMT,
335 SIGINFO];
336
337pub const NSIG: libc::c_int = 32;
338
339#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
340pub struct SignalIterator {
341 next: usize,
342}
343
344impl Iterator for SignalIterator {
345 type Item = Signal;
346
347 fn next(&mut self) -> Option<Signal> {
348 if self.next < SIGNALS.len() {
349 let next_signal = SIGNALS[self.next];
350 self.next += 1;
351 Some(next_signal)
352 } else {
353 None
354 }
355 }
356}
357
358impl Signal {
359 pub fn iterator() -> SignalIterator {
360 SignalIterator{next: 0}
361 }
362}
363
364impl TryFrom<libc::c_int> for Signal {
365 type Error = Error;
366
367 fn try_from(signum: libc::c_int) -> Result<Signal> {
368 if 0 < signum && signum < NSIG {
369 Ok(unsafe { mem::transmute(signum) })
370 } else {
371 Err(Error::invalid_argument())
372 }
373 }
374}
375
376pub const SIGIOT : Signal = SIGABRT;
377pub const SIGPOLL : Signal = SIGIO;
378pub const SIGUNUSED : Signal = SIGSYS;
379
380#[cfg(not(target_os = "redox"))]
381type SaFlags_t = libc::c_int;
382#[cfg(target_os = "redox")]
383type SaFlags_t = libc::c_ulong;
384
385libc_bitflags!{
386 pub struct SaFlags: SaFlags_t {
387 SA_NOCLDSTOP;
388 SA_NOCLDWAIT;
389 SA_NODEFER;
390 SA_ONSTACK;
391 SA_RESETHAND;
392 SA_RESTART;
393 SA_SIGINFO;
394 }
395}
396
397libc_enum! {
398 #[repr(i32)]
399 pub enum SigmaskHow {
400 SIG_BLOCK,
401 SIG_UNBLOCK,
402 SIG_SETMASK,
403 }
404}
405
406#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
407pub struct SigSet {
408 sigset: libc::sigset_t
409}
410
411
412impl SigSet {
413 pub fn all() -> SigSet {
414 let mut sigset = mem::MaybeUninit::uninit();
415 let _ = unsafe { libc::sigfillset(sigset.as_mut_ptr()) };
416
417 unsafe{ SigSet { sigset: sigset.assume_init() } }
418 }
419
420 pub fn empty() -> SigSet {
421 let mut sigset = mem::MaybeUninit::uninit();
422 let _ = unsafe { libc::sigemptyset(sigset.as_mut_ptr()) };
423
424 unsafe{ SigSet { sigset: sigset.assume_init() } }
425 }
426
427 pub fn add(&mut self, signal: Signal) {
428 unsafe { libc::sigaddset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) };
429 }
430
431 pub fn clear(&mut self) {
432 unsafe { libc::sigemptyset(&mut self.sigset as *mut libc::sigset_t) };
433 }
434
435 pub fn remove(&mut self, signal: Signal) {
436 unsafe { libc::sigdelset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) };
437 }
438
439 pub fn contains(&self, signal: Signal) -> bool {
440 let res = unsafe { libc::sigismember(&self.sigset as *const libc::sigset_t, signal as libc::c_int) };
441
442 match res {
443 1 => true,
444 0 => false,
445 _ => unreachable!("unexpected value from sigismember"),
446 }
447 }
448
449 pub fn extend(&mut self, other: &SigSet) {
450 for signal in Signal::iterator() {
451 if other.contains(signal) {
452 self.add(signal);
453 }
454 }
455 }
456
457 /// Gets the currently blocked (masked) set of signals for the calling thread.
458 pub fn thread_get_mask() -> Result<SigSet> {
459 let mut oldmask = mem::MaybeUninit::uninit();
460 do_pthread_sigmask(SigmaskHow::SIG_SETMASK, None, Some(oldmask.as_mut_ptr()))?;
461 Ok(unsafe{ SigSet{sigset: oldmask.assume_init()}})
462 }
463
464 /// Sets the set of signals as the signal mask for the calling thread.
465 pub fn thread_set_mask(&self) -> Result<()> {
466 pthread_sigmask(SigmaskHow::SIG_SETMASK, Some(self), None)
467 }
468
469 /// Adds the set of signals to the signal mask for the calling thread.
470 pub fn thread_block(&self) -> Result<()> {
471 pthread_sigmask(SigmaskHow::SIG_BLOCK, Some(self), None)
472 }
473
474 /// Removes the set of signals from the signal mask for the calling thread.
475 pub fn thread_unblock(&self) -> Result<()> {
476 pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(self), None)
477 }
478
479 /// Sets the set of signals as the signal mask, and returns the old mask.
480 pub fn thread_swap_mask(&self, how: SigmaskHow) -> Result<SigSet> {
481 let mut oldmask = mem::MaybeUninit::uninit();
482 do_pthread_sigmask(how, Some(self), Some(oldmask.as_mut_ptr()))?;
483 Ok(unsafe{ SigSet{sigset: oldmask.assume_init()}})
484 }
485
486 /// Suspends execution of the calling thread until one of the signals in the
487 /// signal mask becomes pending, and returns the accepted signal.
488 #[cfg(not(target_os = "redox"))] // RedoxFS does not yet support sigwait
489 pub fn wait(&self) -> Result<Signal> {
490 let mut signum = mem::MaybeUninit::uninit();
491 let res = unsafe { libc::sigwait(&self.sigset as *const libc::sigset_t, signum.as_mut_ptr()) };
492
493 Errno::result(res).map(|_| unsafe {
494 Signal::try_from(signum.assume_init()).unwrap()
495 })
496 }
497}
498
499impl AsRef<libc::sigset_t> for SigSet {
500 fn as_ref(&self) -> &libc::sigset_t {
501 &self.sigset
502 }
503}
504
505/// A signal handler.
506#[allow(unknown_lints)]
507#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
508pub enum SigHandler {
509 /// Default signal handling.
510 SigDfl,
511 /// Request that the signal be ignored.
512 SigIgn,
513 /// Use the given signal-catching function, which takes in the signal.
514 Handler(extern fn(libc::c_int)),
515 /// Use the given signal-catching function, which takes in the signal, information about how
516 /// the signal was generated, and a pointer to the threads `ucontext_t`.
517 #[cfg(not(target_os = "redox"))]
518 SigAction(extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void))
519}
520
521/// Action to take on receipt of a signal. Corresponds to `sigaction`.
522#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
523pub struct SigAction {
524 sigaction: libc::sigaction
525}
526
527impl SigAction {
528 /// Creates a new action.
529 ///
530 /// The `SA_SIGINFO` bit in the `flags` argument is ignored (it will be set only if `handler`
531 /// is the `SigAction` variant). `mask` specifies other signals to block during execution of
532 /// the signal-catching function.
533 pub fn new(handler: SigHandler, flags: SaFlags, mask: SigSet) -> SigAction {
534 #[cfg(target_os = "redox")]
535 unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) {
536 (*p).sa_handler = match handler {
537 SigHandler::SigDfl => libc::SIG_DFL,
538 SigHandler::SigIgn => libc::SIG_IGN,
539 SigHandler::Handler(f) => f as *const extern fn(libc::c_int) as usize,
540 };
541 }
542
543 #[cfg(not(target_os = "redox"))]
544 unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) {
545 (*p).sa_sigaction = match handler {
546 SigHandler::SigDfl => libc::SIG_DFL,
547 SigHandler::SigIgn => libc::SIG_IGN,
548 SigHandler::Handler(f) => f as *const extern fn(libc::c_int) as usize,
549 SigHandler::SigAction(f) => f as *const extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void) as usize,
550 };
551 }
552
553 let mut s = mem::MaybeUninit::<libc::sigaction>::uninit();
554 unsafe {
555 let p = s.as_mut_ptr();
556 install_sig(p, handler);
557 (*p).sa_flags = match handler {
558 #[cfg(not(target_os = "redox"))]
559 SigHandler::SigAction(_) => (flags | SaFlags::SA_SIGINFO).bits(),
560 _ => (flags - SaFlags::SA_SIGINFO).bits(),
561 };
562 (*p).sa_mask = mask.sigset;
563
564 SigAction { sigaction: s.assume_init() }
565 }
566 }
567
568 /// Returns the flags set on the action.
569 pub fn flags(&self) -> SaFlags {
570 SaFlags::from_bits_truncate(self.sigaction.sa_flags)
571 }
572
573 /// Returns the set of signals that are blocked during execution of the action's
574 /// signal-catching function.
575 pub fn mask(&self) -> SigSet {
576 SigSet { sigset: self.sigaction.sa_mask }
577 }
578
579 /// Returns the action's handler.
580 #[cfg(not(target_os = "redox"))]
581 pub fn handler(&self) -> SigHandler {
582 match self.sigaction.sa_sigaction {
583 libc::SIG_DFL => SigHandler::SigDfl,
584 libc::SIG_IGN => SigHandler::SigIgn,
585 f if self.flags().contains(SaFlags::SA_SIGINFO) =>
586 SigHandler::SigAction( unsafe { mem::transmute(f) } ),
587 f => SigHandler::Handler( unsafe { mem::transmute(f) } ),
588 }
589 }
590
591 /// Returns the action's handler.
592 #[cfg(target_os = "redox")]
593 pub fn handler(&self) -> SigHandler {
594 match self.sigaction.sa_handler {
595 libc::SIG_DFL => SigHandler::SigDfl,
596 libc::SIG_IGN => SigHandler::SigIgn,
597 f => SigHandler::Handler( unsafe { mem::transmute(f) } ),
598 }
599 }
600}
601
602/// Changes the action taken by a process on receipt of a specific signal.
603///
604/// `signal` can be any signal except `SIGKILL` or `SIGSTOP`. On success, it returns the previous
605/// action for the given signal. If `sigaction` fails, no new signal handler is installed.
606///
607/// # Safety
608///
609/// Signal handlers may be called at any point during execution, which limits what is safe to do in
610/// the body of the signal-catching function. Be certain to only make syscalls that are explicitly
611/// marked safe for signal handlers and only share global data using atomics.
612pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigAction> {
613 let mut oldact = mem::MaybeUninit::<libc::sigaction>::uninit();
614
615 let res = libc::sigaction(signal as libc::c_int,
616 &sigaction.sigaction as *const libc::sigaction,
617 oldact.as_mut_ptr());
618
619 Errno::result(res).map(|_| SigAction { sigaction: oldact.assume_init() })
620}
621
622/// Signal management (see [signal(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html))
623///
624/// Installs `handler` for the given `signal`, returning the previous signal
625/// handler. `signal` should only be used following another call to `signal` or
626/// if the current handler is the default. The return value of `signal` is
627/// undefined after setting the handler with [`sigaction`][SigActionFn].
628///
629/// # Safety
630///
631/// If the pointer to the previous signal handler is invalid, undefined
632/// behavior could be invoked when casting it back to a [`SigAction`][SigActionStruct].
633///
634/// # Examples
635///
636/// Ignore `SIGINT`:
637///
638/// ```no_run
639/// # use nix::sys::signal::{self, Signal, SigHandler};
640/// unsafe { signal::signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap();
641/// ```
642///
643/// Use a signal handler to set a flag variable:
644///
645/// ```no_run
646/// # #[macro_use] extern crate lazy_static;
647/// # use std::convert::TryFrom;
648/// # use std::sync::atomic::{AtomicBool, Ordering};
649/// # use nix::sys::signal::{self, Signal, SigHandler};
650/// lazy_static! {
651/// static ref SIGNALED: AtomicBool = AtomicBool::new(false);
652/// }
653///
654/// extern fn handle_sigint(signal: libc::c_int) {
655/// let signal = Signal::try_from(signal).unwrap();
656/// SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
657/// }
658///
659/// fn main() {
660/// let handler = SigHandler::Handler(handle_sigint);
661/// unsafe { signal::signal(Signal::SIGINT, handler) }.unwrap();
662/// }
663/// ```
664///
665/// # Errors
666///
667/// Returns [`Error::UnsupportedOperation`] if `handler` is
668/// [`SigAction`][SigActionStruct]. Use [`sigaction`][SigActionFn] instead.
669///
670/// `signal` also returns any error from `libc::signal`, such as when an attempt
671/// is made to catch a signal that cannot be caught or to ignore a signal that
672/// cannot be ignored.
673///
674/// [`Error::UnsupportedOperation`]: ../../enum.Error.html#variant.UnsupportedOperation
675/// [SigActionStruct]: struct.SigAction.html
676/// [sigactionFn]: fn.sigaction.html
677pub unsafe fn signal(signal: Signal, handler: SigHandler) -> Result<SigHandler> {
678 let signal = signal as libc::c_int;
679 let res = match handler {
680 SigHandler::SigDfl => libc::signal(signal, libc::SIG_DFL),
681 SigHandler::SigIgn => libc::signal(signal, libc::SIG_IGN),
682 SigHandler::Handler(handler) => libc::signal(signal, handler as libc::sighandler_t),
683 #[cfg(not(target_os = "redox"))]
684 SigHandler::SigAction(_) => return Err(Error::UnsupportedOperation),
685 };
686 Errno::result(res).map(|oldhandler| {
687 match oldhandler {
688 libc::SIG_DFL => SigHandler::SigDfl,
689 libc::SIG_IGN => SigHandler::SigIgn,
690 f => SigHandler::Handler(mem::transmute(f)),
691 }
692 })
693}
694
695fn do_pthread_sigmask(how: SigmaskHow,
696 set: Option<&SigSet>,
697 oldset: Option<*mut libc::sigset_t>) -> Result<()> {
698 if set.is_none() && oldset.is_none() {
699 return Ok(())
700 }
701
702 let res = unsafe {
703 // if set or oldset is None, pass in null pointers instead
704 libc::pthread_sigmask(how as libc::c_int,
705 set.map_or_else(ptr::null::<libc::sigset_t>,
706 |s| &s.sigset as *const libc::sigset_t),
707 oldset.unwrap_or(ptr::null_mut())
708 )
709 };
710
711 Errno::result(res).map(drop)
712}
713
714/// Manages the signal mask (set of blocked signals) for the calling thread.
715///
716/// If the `set` parameter is `Some(..)`, then the signal mask will be updated with the signal set.
717/// The `how` flag decides the type of update. If `set` is `None`, `how` will be ignored,
718/// and no modification will take place.
719///
720/// If the 'oldset' parameter is `Some(..)` then the current signal mask will be written into it.
721///
722/// If both `set` and `oldset` is `Some(..)`, the current signal mask will be written into oldset,
723/// and then it will be updated with `set`.
724///
725/// If both `set` and `oldset` is None, this function is a no-op.
726///
727/// For more information, visit the [`pthread_sigmask`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html),
728/// or [`sigprocmask`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html) man pages.
729pub fn pthread_sigmask(how: SigmaskHow,
730 set: Option<&SigSet>,
731 oldset: Option<&mut SigSet>) -> Result<()>
732{
733 do_pthread_sigmask(how, set, oldset.map(|os| &mut os.sigset as *mut _ ))
734}
735
736/// Examine and change blocked signals.
737///
738/// For more informations see the [`sigprocmask` man
739/// pages](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html).
740pub fn sigprocmask(how: SigmaskHow, set: Option<&SigSet>, oldset: Option<&mut SigSet>) -> Result<()> {
741 if set.is_none() && oldset.is_none() {
742 return Ok(())
743 }
744
745 let res = unsafe {
746 // if set or oldset is None, pass in null pointers instead
747 libc::sigprocmask(how as libc::c_int,
748 set.map_or_else(ptr::null::<libc::sigset_t>,
749 |s| &s.sigset as *const libc::sigset_t),
750 oldset.map_or_else(ptr::null_mut::<libc::sigset_t>,
751 |os| &mut os.sigset as *mut libc::sigset_t))
752 };
753
754 Errno::result(res).map(drop)
755}
756
757pub fn kill<T: Into<Option<Signal>>>(pid: Pid, signal: T) -> Result<()> {
758 let res = unsafe { libc::kill(pid.into(),
759 match signal.into() {
760 Some(s) => s as libc::c_int,
761 None => 0,
762 }) };
763
764 Errno::result(res).map(drop)
765}
766
767/// Send a signal to a process group [(see
768/// killpg(3))](http://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html).
769///
770/// If `pgrp` less then or equal 1, the behavior is platform-specific.
771/// If `signal` is `None`, `killpg` will only preform error checking and won't
772/// send any signal.
Joel Galenson4727c112021-04-02 12:22:24 -0700773#[cfg(not(target_os = "fuchsia"))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100774pub fn killpg<T: Into<Option<Signal>>>(pgrp: Pid, signal: T) -> Result<()> {
775 let res = unsafe { libc::killpg(pgrp.into(),
776 match signal.into() {
777 Some(s) => s as libc::c_int,
778 None => 0,
779 }) };
780
781 Errno::result(res).map(drop)
782}
783
784pub fn raise(signal: Signal) -> Result<()> {
785 let res = unsafe { libc::raise(signal as libc::c_int) };
786
787 Errno::result(res).map(drop)
788}
789
790
791#[cfg(target_os = "freebsd")]
792pub type type_of_thread_id = libc::lwpid_t;
793#[cfg(target_os = "linux")]
794pub type type_of_thread_id = libc::pid_t;
795
796/// Used to request asynchronous notification of certain events, for example,
797/// with POSIX AIO, POSIX message queues, and POSIX timers.
798// sigval is actually a union of a int and a void*. But it's never really used
799// as a pointer, because neither libc nor the kernel ever dereference it. nix
800// therefore presents it as an intptr_t, which is how kevent uses it.
801#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
802pub enum SigevNotify {
803 /// No notification will be delivered
804 SigevNone,
805 /// The signal given by `signal` will be delivered to the process. The
806 /// value in `si_value` will be present in the `si_value` field of the
807 /// `siginfo_t` structure of the queued signal.
808 SigevSignal { signal: Signal, si_value: libc::intptr_t },
809 // Note: SIGEV_THREAD is not implemented because libc::sigevent does not
810 // expose a way to set the union members needed by SIGEV_THREAD.
811 /// A new `kevent` is posted to the kqueue `kq`. The `kevent`'s `udata`
812 /// field will contain the value in `udata`.
813 #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
814 SigevKevent { kq: RawFd, udata: libc::intptr_t },
815 /// The signal `signal` is queued to the thread whose LWP ID is given in
816 /// `thread_id`. The value stored in `si_value` will be present in the
817 /// `si_value` of the `siginfo_t` structure of the queued signal.
818 #[cfg(any(target_os = "freebsd", target_os = "linux"))]
819 SigevThreadId { signal: Signal, thread_id: type_of_thread_id,
820 si_value: libc::intptr_t },
821}
822
823#[cfg(not(any(target_os = "openbsd", target_os = "redox")))]
824mod sigevent {
825 use std::mem;
826 use std::ptr;
827 use super::SigevNotify;
828 #[cfg(any(target_os = "freebsd", target_os = "linux"))]
829 use super::type_of_thread_id;
830
831 /// Used to request asynchronous notification of the completion of certain
832 /// events, such as POSIX AIO and timers.
833 #[repr(C)]
834 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
835 pub struct SigEvent {
836 sigevent: libc::sigevent
837 }
838
839 impl SigEvent {
840 /// **Note:** this constructor does not allow the user to set the
841 /// `sigev_notify_kevent_flags` field. That's considered ok because on FreeBSD
842 /// at least those flags don't do anything useful. That field is part of a
843 /// union that shares space with the more genuinely useful fields.
844 ///
845 /// **Note:** This constructor also doesn't allow the caller to set the
846 /// `sigev_notify_function` or `sigev_notify_attributes` fields, which are
847 /// required for `SIGEV_THREAD`. That's considered ok because on no operating
848 /// system is `SIGEV_THREAD` the most efficient way to deliver AIO
849 /// notification. FreeBSD and DragonFly BSD programs should prefer `SIGEV_KEVENT`.
850 /// Linux, Solaris, and portable programs should prefer `SIGEV_THREAD_ID` or
851 /// `SIGEV_SIGNAL`. That field is part of a union that shares space with the
852 /// more genuinely useful `sigev_notify_thread_id`
853 pub fn new(sigev_notify: SigevNotify) -> SigEvent {
Joel Galenson4727c112021-04-02 12:22:24 -0700854 // NB: This uses MaybeUninit rather than mem::zeroed because libc::sigevent contains a
855 // function pointer on Fuchsia as of https://github.com/rust-lang/libc/commit/2f59370,
856 // and function pointers must not be null.
857 let mut sev = unsafe { mem::MaybeUninit::<libc::sigevent>::zeroed().assume_init() };
Andrew Walbran12f61402020-10-14 11:10:53 +0100858 sev.sigev_notify = match sigev_notify {
859 SigevNotify::SigevNone => libc::SIGEV_NONE,
860 SigevNotify::SigevSignal{..} => libc::SIGEV_SIGNAL,
861 #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
862 SigevNotify::SigevKevent{..} => libc::SIGEV_KEVENT,
863 #[cfg(target_os = "freebsd")]
864 SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID,
865 #[cfg(all(target_os = "linux", target_env = "gnu", not(target_arch = "mips")))]
866 SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID,
867 #[cfg(any(all(target_os = "linux", target_env = "musl"), target_arch = "mips"))]
868 SigevNotify::SigevThreadId{..} => 4 // No SIGEV_THREAD_ID defined
869 };
870 sev.sigev_signo = match sigev_notify {
871 SigevNotify::SigevSignal{ signal, .. } => signal as libc::c_int,
872 #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
873 SigevNotify::SigevKevent{ kq, ..} => kq,
874 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
875 SigevNotify::SigevThreadId{ signal, .. } => signal as libc::c_int,
876 _ => 0
877 };
878 sev.sigev_value.sival_ptr = match sigev_notify {
879 SigevNotify::SigevNone => ptr::null_mut::<libc::c_void>(),
880 SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut libc::c_void,
881 #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
882 SigevNotify::SigevKevent{ udata, .. } => udata as *mut libc::c_void,
883 #[cfg(any(target_os = "freebsd", target_os = "linux"))]
884 SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut libc::c_void,
885 };
886 SigEvent::set_tid(&mut sev, &sigev_notify);
887 SigEvent{sigevent: sev}
888 }
889
890 #[cfg(any(target_os = "freebsd", target_os = "linux"))]
891 fn set_tid(sev: &mut libc::sigevent, sigev_notify: &SigevNotify) {
892 sev.sigev_notify_thread_id = match *sigev_notify {
893 SigevNotify::SigevThreadId { thread_id, .. } => thread_id,
894 _ => 0 as type_of_thread_id
895 };
896 }
897
898 #[cfg(not(any(target_os = "freebsd", target_os = "linux")))]
899 fn set_tid(_sev: &mut libc::sigevent, _sigev_notify: &SigevNotify) {
900 }
901
902 pub fn sigevent(&self) -> libc::sigevent {
903 self.sigevent
904 }
905 }
906
907 impl<'a> From<&'a libc::sigevent> for SigEvent {
908 fn from(sigevent: &libc::sigevent) -> Self {
909 SigEvent{ sigevent: *sigevent }
910 }
911 }
912}
913
914#[cfg(test)]
915mod tests {
916 #[cfg(not(target_os = "redox"))]
917 use std::thread;
918 use super::*;
919
920 #[test]
921 fn test_contains() {
922 let mut mask = SigSet::empty();
923 mask.add(SIGUSR1);
924
925 assert!(mask.contains(SIGUSR1));
926 assert!(!mask.contains(SIGUSR2));
927
928 let all = SigSet::all();
929 assert!(all.contains(SIGUSR1));
930 assert!(all.contains(SIGUSR2));
931 }
932
933 #[test]
934 fn test_clear() {
935 let mut set = SigSet::all();
936 set.clear();
937 for signal in Signal::iterator() {
938 assert!(!set.contains(signal));
939 }
940 }
941
942 #[test]
943 fn test_from_str_round_trips() {
944 for signal in Signal::iterator() {
945 assert_eq!(signal.as_ref().parse::<Signal>().unwrap(), signal);
946 assert_eq!(signal.to_string().parse::<Signal>().unwrap(), signal);
947 }
948 }
949
950 #[test]
951 fn test_from_str_invalid_value() {
952 let errval = Err(Error::Sys(Errno::EINVAL));
953 assert_eq!("NOSIGNAL".parse::<Signal>(), errval);
954 assert_eq!("kill".parse::<Signal>(), errval);
955 assert_eq!("9".parse::<Signal>(), errval);
956 }
957
958 #[test]
959 fn test_extend() {
960 let mut one_signal = SigSet::empty();
961 one_signal.add(SIGUSR1);
962
963 let mut two_signals = SigSet::empty();
964 two_signals.add(SIGUSR2);
965 two_signals.extend(&one_signal);
966
967 assert!(two_signals.contains(SIGUSR1));
968 assert!(two_signals.contains(SIGUSR2));
969 }
970
971 #[test]
972 #[cfg(not(target_os = "redox"))]
973 fn test_thread_signal_set_mask() {
974 thread::spawn(|| {
975 let prev_mask = SigSet::thread_get_mask()
976 .expect("Failed to get existing signal mask!");
977
978 let mut test_mask = prev_mask;
979 test_mask.add(SIGUSR1);
980
981 assert!(test_mask.thread_set_mask().is_ok());
982 let new_mask = SigSet::thread_get_mask()
983 .expect("Failed to get new mask!");
984
985 assert!(new_mask.contains(SIGUSR1));
986 assert!(!new_mask.contains(SIGUSR2));
987
988 prev_mask.thread_set_mask().expect("Failed to revert signal mask!");
989 }).join().unwrap();
990 }
991
992 #[test]
993 #[cfg(not(target_os = "redox"))]
994 fn test_thread_signal_block() {
995 thread::spawn(|| {
996 let mut mask = SigSet::empty();
997 mask.add(SIGUSR1);
998
999 assert!(mask.thread_block().is_ok());
1000
1001 assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
1002 }).join().unwrap();
1003 }
1004
1005 #[test]
1006 #[cfg(not(target_os = "redox"))]
1007 fn test_thread_signal_unblock() {
1008 thread::spawn(|| {
1009 let mut mask = SigSet::empty();
1010 mask.add(SIGUSR1);
1011
1012 assert!(mask.thread_unblock().is_ok());
1013
1014 assert!(!SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
1015 }).join().unwrap();
1016 }
1017
1018 #[test]
1019 #[cfg(not(target_os = "redox"))]
1020 fn test_thread_signal_swap() {
1021 thread::spawn(|| {
1022 let mut mask = SigSet::empty();
1023 mask.add(SIGUSR1);
1024 mask.thread_block().unwrap();
1025
1026 assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
1027
1028 let mut mask2 = SigSet::empty();
1029 mask2.add(SIGUSR2);
1030
1031 let oldmask = mask2.thread_swap_mask(SigmaskHow::SIG_SETMASK)
1032 .unwrap();
1033
1034 assert!(oldmask.contains(SIGUSR1));
1035 assert!(!oldmask.contains(SIGUSR2));
1036
1037 assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR2));
1038 }).join().unwrap();
1039 }
1040
1041 #[test]
1042 #[cfg(not(target_os = "redox"))]
1043 fn test_sigaction() {
1044 thread::spawn(|| {
1045 extern fn test_sigaction_handler(_: libc::c_int) {}
1046 extern fn test_sigaction_action(_: libc::c_int,
1047 _: *mut libc::siginfo_t, _: *mut libc::c_void) {}
1048
1049 let handler_sig = SigHandler::Handler(test_sigaction_handler);
1050
1051 let flags = SaFlags::SA_ONSTACK | SaFlags::SA_RESTART |
1052 SaFlags::SA_SIGINFO;
1053
1054 let mut mask = SigSet::empty();
1055 mask.add(SIGUSR1);
1056
1057 let action_sig = SigAction::new(handler_sig, flags, mask);
1058
1059 assert_eq!(action_sig.flags(),
1060 SaFlags::SA_ONSTACK | SaFlags::SA_RESTART);
1061 assert_eq!(action_sig.handler(), handler_sig);
1062
1063 mask = action_sig.mask();
1064 assert!(mask.contains(SIGUSR1));
1065 assert!(!mask.contains(SIGUSR2));
1066
1067 let handler_act = SigHandler::SigAction(test_sigaction_action);
1068 let action_act = SigAction::new(handler_act, flags, mask);
1069 assert_eq!(action_act.handler(), handler_act);
1070
1071 let action_dfl = SigAction::new(SigHandler::SigDfl, flags, mask);
1072 assert_eq!(action_dfl.handler(), SigHandler::SigDfl);
1073
1074 let action_ign = SigAction::new(SigHandler::SigIgn, flags, mask);
1075 assert_eq!(action_ign.handler(), SigHandler::SigIgn);
1076 }).join().unwrap();
1077 }
1078
1079 #[test]
1080 #[cfg(not(target_os = "redox"))]
1081 fn test_sigwait() {
1082 thread::spawn(|| {
1083 let mut mask = SigSet::empty();
1084 mask.add(SIGUSR1);
1085 mask.add(SIGUSR2);
1086 mask.thread_block().unwrap();
1087
1088 raise(SIGUSR1).unwrap();
1089 assert_eq!(mask.wait().unwrap(), SIGUSR1);
1090 }).join().unwrap();
1091 }
1092}