1use nix::sys::signal::{sigprocmask, SigmaskHow};
12use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, Signal as NixSignal};
13use nix::unistd::getpid;
14use std::collections::HashMap;
15use std::sync::atomic::{AtomicBool, AtomicI32, AtomicUsize, Ordering};
16use std::sync::{Mutex, OnceLock};
17
18const MAX_QUEUE_SIZE: usize = 128;
20
21pub mod trap_flags {
23 pub const ZSIG_TRAPPED: u32 = 1; pub const ZSIG_IGNORED: u32 = 2; pub const ZSIG_FUNC: u32 = 4; pub const ZSIG_SHIFT: u32 = 3; }
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31#[repr(i32)]
32pub enum Signal {
33 SIGHUP = libc::SIGHUP,
34 SIGINT = libc::SIGINT,
35 SIGQUIT = libc::SIGQUIT,
36 SIGILL = libc::SIGILL,
37 SIGTRAP = libc::SIGTRAP,
38 SIGABRT = libc::SIGABRT,
39 SIGBUS = libc::SIGBUS,
40 SIGFPE = libc::SIGFPE,
41 SIGKILL = libc::SIGKILL,
42 SIGUSR1 = libc::SIGUSR1,
43 SIGSEGV = libc::SIGSEGV,
44 SIGUSR2 = libc::SIGUSR2,
45 SIGPIPE = libc::SIGPIPE,
46 SIGALRM = libc::SIGALRM,
47 SIGTERM = libc::SIGTERM,
48 SIGCHLD = libc::SIGCHLD,
49 SIGCONT = libc::SIGCONT,
50 SIGSTOP = libc::SIGSTOP,
51 SIGTSTP = libc::SIGTSTP,
52 SIGTTIN = libc::SIGTTIN,
53 SIGTTOU = libc::SIGTTOU,
54 SIGURG = libc::SIGURG,
55 SIGXCPU = libc::SIGXCPU,
56 SIGXFSZ = libc::SIGXFSZ,
57 SIGVTALRM = libc::SIGVTALRM,
58 SIGPROF = libc::SIGPROF,
59 SIGWINCH = libc::SIGWINCH,
60 SIGIO = libc::SIGIO,
61 SIGSYS = libc::SIGSYS,
62}
63
64pub const SIGEXIT: i32 = 0;
66pub const SIGDEBUG: i32 = -1;
67pub const SIGZERR: i32 = -2;
68
69pub static SIGNAL_NAMES: &[(&str, i32)] = &[
71 ("EXIT", SIGEXIT),
72 ("HUP", libc::SIGHUP),
73 ("INT", libc::SIGINT),
74 ("QUIT", libc::SIGQUIT),
75 ("ILL", libc::SIGILL),
76 ("TRAP", libc::SIGTRAP),
77 ("ABRT", libc::SIGABRT),
78 ("BUS", libc::SIGBUS),
79 ("FPE", libc::SIGFPE),
80 ("KILL", libc::SIGKILL),
81 ("USR1", libc::SIGUSR1),
82 ("SEGV", libc::SIGSEGV),
83 ("USR2", libc::SIGUSR2),
84 ("PIPE", libc::SIGPIPE),
85 ("ALRM", libc::SIGALRM),
86 ("TERM", libc::SIGTERM),
87 ("CHLD", libc::SIGCHLD),
88 ("CONT", libc::SIGCONT),
89 ("STOP", libc::SIGSTOP),
90 ("TSTP", libc::SIGTSTP),
91 ("TTIN", libc::SIGTTIN),
92 ("TTOU", libc::SIGTTOU),
93 ("URG", libc::SIGURG),
94 ("XCPU", libc::SIGXCPU),
95 ("XFSZ", libc::SIGXFSZ),
96 ("VTALRM", libc::SIGVTALRM),
97 ("PROF", libc::SIGPROF),
98 ("WINCH", libc::SIGWINCH),
99 ("IO", libc::SIGIO),
100 ("SYS", libc::SIGSYS),
101 ("DEBUG", SIGDEBUG),
102 ("ZERR", SIGZERR),
103 ("ERR", SIGZERR), ];
105
106pub fn sig_by_name(name: &str) -> Option<i32> {
108 let name_upper = name.to_uppercase();
109 let lookup = if name_upper.starts_with("SIG") {
110 &name_upper[3..]
111 } else {
112 &name_upper
113 };
114
115 for (sig_name, sig_num) in SIGNAL_NAMES {
116 if *sig_name == lookup {
117 return Some(*sig_num);
118 }
119 }
120
121 lookup.parse().ok()
123}
124
125pub fn sig_name(sig: i32) -> Option<&'static str> {
127 for (name, num) in SIGNAL_NAMES {
128 if *num == sig {
129 return Some(name);
130 }
131 }
132 None
133}
134
135struct SignalQueue {
137 enabled: AtomicBool,
138 front: AtomicUsize,
139 rear: AtomicUsize,
140 signals: [AtomicI32; MAX_QUEUE_SIZE],
141}
142
143impl SignalQueue {
144 const fn new() -> Self {
145 const INIT: AtomicI32 = AtomicI32::new(0);
146 SignalQueue {
147 enabled: AtomicBool::new(false),
148 front: AtomicUsize::new(0),
149 rear: AtomicUsize::new(0),
150 signals: [INIT; MAX_QUEUE_SIZE],
151 }
152 }
153
154 fn is_enabled(&self) -> bool {
155 self.enabled.load(Ordering::SeqCst)
156 }
157
158 fn enable(&self) {
159 self.enabled.store(true, Ordering::SeqCst);
160 }
161
162 fn disable(&self) {
163 self.enabled.store(false, Ordering::SeqCst);
164 }
165
166 fn push(&self, sig: i32) -> bool {
167 let rear = self.rear.load(Ordering::SeqCst);
168 let new_rear = (rear + 1) % MAX_QUEUE_SIZE;
169 let front = self.front.load(Ordering::SeqCst);
170
171 if new_rear == front {
172 return false; }
174
175 self.signals[new_rear].store(sig, Ordering::SeqCst);
176 self.rear.store(new_rear, Ordering::SeqCst);
177 true
178 }
179
180 fn pop(&self) -> Option<i32> {
181 let front = self.front.load(Ordering::SeqCst);
182 let rear = self.rear.load(Ordering::SeqCst);
183
184 if front == rear {
185 return None; }
187
188 let new_front = (front + 1) % MAX_QUEUE_SIZE;
189 let sig = self.signals[new_front].load(Ordering::SeqCst);
190 self.front.store(new_front, Ordering::SeqCst);
191 Some(sig)
192 }
193}
194
195static SIGNAL_QUEUE: SignalQueue = SignalQueue::new();
196static TRAP_QUEUE: SignalQueue = SignalQueue::new();
197
198static LAST_SIGNAL: AtomicI32 = AtomicI32::new(0);
200
201pub struct TrapHandler {
203 traps: Mutex<HashMap<i32, TrapAction>>,
205 flags: Mutex<HashMap<i32, u32>>,
207 pub num_trapped: AtomicUsize,
209 pub in_trap: AtomicBool,
211 pub in_exit_trap: AtomicBool,
213}
214
215#[derive(Debug, Clone)]
217pub enum TrapAction {
218 Ignore,
220 Code(String),
222 Function(String),
224 Default,
226}
227
228impl Default for TrapHandler {
229 fn default() -> Self {
230 Self::new()
231 }
232}
233
234impl TrapHandler {
235 pub fn new() -> Self {
236 TrapHandler {
237 traps: Mutex::new(HashMap::new()),
238 flags: Mutex::new(HashMap::new()),
239 num_trapped: AtomicUsize::new(0),
240 in_trap: AtomicBool::new(false),
241 in_exit_trap: AtomicBool::new(false),
242 }
243 }
244
245 pub fn set_trap(&self, sig: i32, action: TrapAction) -> Result<(), String> {
247 if sig == libc::SIGKILL || sig == libc::SIGSTOP {
249 return Err(format!("can't trap SIG{}", sig_name(sig).unwrap_or("?")));
250 }
251
252 let mut traps = self.traps.lock().unwrap();
253 let mut flags = self.flags.lock().unwrap();
254
255 let was_trapped = flags
256 .get(&sig)
257 .map(|f| f & trap_flags::ZSIG_TRAPPED != 0)
258 .unwrap_or(false);
259
260 match &action {
261 TrapAction::Ignore => {
262 traps.insert(sig, action);
263 flags.insert(sig, trap_flags::ZSIG_IGNORED);
264 if sig > 0 {
265 self.ignore_signal(sig);
266 }
267 }
268 TrapAction::Code(code) if code.is_empty() => {
269 traps.insert(sig, TrapAction::Ignore);
270 flags.insert(sig, trap_flags::ZSIG_IGNORED);
271 if sig > 0 {
272 self.ignore_signal(sig);
273 }
274 }
275 TrapAction::Code(_) => {
276 if !was_trapped {
277 self.num_trapped.fetch_add(1, Ordering::SeqCst);
278 }
279 traps.insert(sig, action);
280 flags.insert(sig, trap_flags::ZSIG_TRAPPED);
281 if sig > 0 {
282 self.install_handler(sig);
283 }
284 }
285 TrapAction::Function(name) => {
286 if !was_trapped {
287 self.num_trapped.fetch_add(1, Ordering::SeqCst);
288 }
289 traps.insert(sig, TrapAction::Function(name.clone()));
290 flags.insert(sig, trap_flags::ZSIG_TRAPPED | trap_flags::ZSIG_FUNC);
291 if sig > 0 {
292 self.install_handler(sig);
293 }
294 }
295 TrapAction::Default => {
296 if was_trapped {
297 self.num_trapped.fetch_sub(1, Ordering::SeqCst);
298 }
299 traps.remove(&sig);
300 flags.remove(&sig);
301 if sig > 0 {
302 self.default_signal(sig);
303 }
304 }
305 }
306
307 Ok(())
308 }
309
310 pub fn unset_trap(&self, sig: i32) {
312 let _ = self.set_trap(sig, TrapAction::Default);
313 }
314
315 pub fn get_trap(&self, sig: i32) -> Option<TrapAction> {
317 self.traps.lock().unwrap().get(&sig).cloned()
318 }
319
320 pub fn is_trapped(&self, sig: i32) -> bool {
322 self.flags
323 .lock()
324 .unwrap()
325 .get(&sig)
326 .map(|f| f & trap_flags::ZSIG_TRAPPED != 0)
327 .unwrap_or(false)
328 }
329
330 pub fn is_ignored(&self, sig: i32) -> bool {
332 self.flags
333 .lock()
334 .unwrap()
335 .get(&sig)
336 .map(|f| f & trap_flags::ZSIG_IGNORED != 0)
337 .unwrap_or(false)
338 }
339
340 fn install_handler(&self, sig: i32) {
342 unsafe {
343 libc::signal(sig, handler as *const () as usize);
344 }
345 }
346
347 fn ignore_signal(&self, sig: i32) {
349 unsafe {
350 libc::signal(sig, libc::SIG_IGN);
351 }
352 }
353
354 fn default_signal(&self, sig: i32) {
356 unsafe {
357 libc::signal(sig, libc::SIG_DFL);
358 }
359 }
360
361 pub fn list_traps(&self) -> Vec<(i32, TrapAction)> {
363 self.traps
364 .lock()
365 .unwrap()
366 .iter()
367 .map(|(k, v)| (*k, v.clone()))
368 .collect()
369 }
370}
371
372static TRAPS: OnceLock<TrapHandler> = OnceLock::new();
374
375pub fn traps() -> &'static TrapHandler {
377 TRAPS.get_or_init(TrapHandler::new)
378}
379
380static MAIN_PID: AtomicI32 = AtomicI32::new(0);
382
383static SIGCHLD_RECEIVED: AtomicBool = AtomicBool::new(false);
385
386static SIGWINCH_RECEIVED: AtomicBool = AtomicBool::new(false);
388
389fn reraise_if_forked_child(sig: i32) -> bool {
391 if getpid().as_raw() == MAIN_PID.load(Ordering::Relaxed) {
392 return false;
393 }
394 unsafe {
395 libc::signal(sig, libc::SIG_DFL);
396 libc::raise(sig);
397 }
398 true
399}
400
401extern "C" fn handler(sig: i32) {
403 #[cfg(target_os = "macos")]
405 let saved_errno = unsafe { *libc::__error() };
406 #[cfg(not(target_os = "macos"))]
407 let saved_errno = unsafe { *libc::__errno_location() };
408
409 if reraise_if_forked_child(sig) {
411 #[cfg(target_os = "macos")]
412 unsafe {
413 *libc::__error() = saved_errno
414 };
415 #[cfg(not(target_os = "macos"))]
416 unsafe {
417 *libc::__errno_location() = saved_errno
418 };
419 return;
420 }
421
422 LAST_SIGNAL.store(sig, Ordering::SeqCst);
423
424 if sig == libc::SIGCHLD {
426 SIGCHLD_RECEIVED.store(true, Ordering::SeqCst);
427 } else if sig == libc::SIGWINCH {
428 SIGWINCH_RECEIVED.store(true, Ordering::SeqCst);
429 }
430
431 if SIGNAL_QUEUE.is_enabled() {
433 SIGNAL_QUEUE.push(sig);
434 #[cfg(target_os = "macos")]
435 unsafe {
436 *libc::__error() = saved_errno
437 };
438 #[cfg(not(target_os = "macos"))]
439 unsafe {
440 *libc::__errno_location() = saved_errno
441 };
442 return;
443 }
444
445 handle_signal(sig);
447
448 #[cfg(target_os = "macos")]
449 unsafe {
450 *libc::__error() = saved_errno
451 };
452 #[cfg(not(target_os = "macos"))]
453 unsafe {
454 *libc::__errno_location() = saved_errno
455 };
456}
457
458fn handle_signal(sig: i32) {
460 match sig {
461 s if s == libc::SIGCHLD => {
462 }
464 s if s == libc::SIGINT => {
465 if let Some(action) = traps().get_trap(s) {
467 run_trap(s, &action);
468 }
469 }
470 s if s == libc::SIGHUP => {
471 if let Some(action) = traps().get_trap(s) {
473 run_trap(s, &action);
474 }
475 }
476 s if s == libc::SIGWINCH => {
477 if let Some(action) = traps().get_trap(s) {
479 run_trap(s, &action);
480 }
481 }
482 s if s == libc::SIGALRM => {
483 if let Some(action) = traps().get_trap(s) {
485 run_trap(s, &action);
486 }
487 }
488 s if s == libc::SIGPIPE => {
489 if let Some(action) = traps().get_trap(s) {
491 run_trap(s, &action);
492 }
493 }
494 _ => {
495 if let Some(action) = traps().get_trap(sig) {
497 run_trap(sig, &action);
498 }
499 }
500 }
501}
502
503fn run_trap(sig: i32, action: &TrapAction) {
505 match action {
506 TrapAction::Ignore => {}
507 TrapAction::Code(_code) => {
508 traps().in_trap.store(true, Ordering::SeqCst);
510 if sig == SIGEXIT {
511 traps().in_exit_trap.store(true, Ordering::SeqCst);
512 }
513 if sig == SIGEXIT {
515 traps().in_exit_trap.store(false, Ordering::SeqCst);
516 }
517 traps().in_trap.store(false, Ordering::SeqCst);
518 }
519 TrapAction::Function(_name) => {
520 traps().in_trap.store(true, Ordering::SeqCst);
522 traps().in_trap.store(false, Ordering::SeqCst);
524 }
525 TrapAction::Default => {}
526 }
527}
528
529pub fn queue_signals() {
531 SIGNAL_QUEUE.enable();
532}
533
534pub fn unqueue_signals() {
536 SIGNAL_QUEUE.disable();
537 while let Some(sig) = SIGNAL_QUEUE.pop() {
538 handle_signal(sig);
539 }
540}
541
542pub fn queueing_enabled() -> bool {
544 SIGNAL_QUEUE.is_enabled()
545}
546
547pub fn queue_traps() {
549 TRAP_QUEUE.enable();
550}
551
552pub fn unqueue_traps() {
554 TRAP_QUEUE.disable();
555 while let Some(sig) = TRAP_QUEUE.pop() {
556 if let Some(action) = traps().get_trap(sig) {
557 run_trap(sig, &action);
558 }
559 }
560}
561
562pub fn signal_block(sig: i32) {
564 unsafe {
565 let mut set: libc::sigset_t = std::mem::zeroed();
566 libc::sigemptyset(&mut set);
567 libc::sigaddset(&mut set, sig);
568 libc::sigprocmask(libc::SIG_BLOCK, &set, std::ptr::null_mut());
569 }
570}
571
572pub fn signal_unblock(sig: i32) {
574 unsafe {
575 let mut set: libc::sigset_t = std::mem::zeroed();
576 libc::sigemptyset(&mut set);
577 libc::sigaddset(&mut set, sig);
578 libc::sigprocmask(libc::SIG_UNBLOCK, &set, std::ptr::null_mut());
579 }
580}
581
582pub fn hold_intr() {
584 signal_block(libc::SIGINT);
585}
586
587pub fn release_intr() {
589 signal_unblock(libc::SIGINT);
590}
591
592pub fn setup_intr() {
594 unsafe {
595 libc::signal(libc::SIGINT, handler as *const () as usize);
596 }
597}
598
599pub fn last_signal() -> i32 {
601 LAST_SIGNAL.load(Ordering::SeqCst)
602}
603
604pub fn killpg(pgrp: i32, sig: i32) -> i32 {
606 unsafe { libc::killpg(pgrp, sig) }
607}
608
609pub fn kill(pid: i32, sig: i32) -> i32 {
611 unsafe { libc::kill(pid, sig) }
612}
613
614pub fn signal_check_sigchld() -> bool {
616 SIGCHLD_RECEIVED.swap(false, Ordering::SeqCst)
617}
618
619pub fn signal_check_sigwinch() -> bool {
621 SIGWINCH_RECEIVED.swap(false, Ordering::SeqCst)
622}
623
624pub fn signal_clear_cancel() {
626 LAST_SIGNAL.store(0, Ordering::SeqCst);
627}
628
629pub fn signal_check_cancel() -> i32 {
631 let sig = LAST_SIGNAL.load(Ordering::SeqCst);
632 if sig == libc::SIGINT {
633 sig
634 } else {
635 0
636 }
637}
638
639pub fn signal_set_handlers(interactive: bool) {
641 MAIN_PID.store(getpid().as_raw(), Ordering::Relaxed);
642
643 let ignore = SigAction::new(SigHandler::SigIgn, SaFlags::empty(), SigSet::empty());
645 unsafe {
646 let _ = nix::sys::signal::sigaction(NixSignal::SIGPIPE, &ignore);
647 let _ = nix::sys::signal::sigaction(NixSignal::SIGQUIT, &ignore);
648 }
649
650 let sa_handler = SigAction::new(
652 SigHandler::Handler(handler),
653 SaFlags::SA_RESTART,
654 SigSet::empty(),
655 );
656
657 unsafe {
658 let _ = nix::sys::signal::sigaction(NixSignal::SIGINT, &sa_handler);
659 let _ = nix::sys::signal::sigaction(NixSignal::SIGCHLD, &sa_handler);
660 }
661
662 if interactive {
663 unsafe {
665 let _ = nix::sys::signal::sigaction(NixSignal::SIGTSTP, &ignore);
666 let _ = nix::sys::signal::sigaction(NixSignal::SIGTTOU, &ignore);
667 }
668
669 unsafe {
671 let _ = nix::sys::signal::sigaction(NixSignal::SIGWINCH, &sa_handler);
672 }
673
674 unsafe {
676 let _ = nix::sys::signal::sigaction(NixSignal::SIGHUP, &sa_handler);
677 let _ = nix::sys::signal::sigaction(NixSignal::SIGTERM, &sa_handler);
678 }
679 }
680}
681
682pub fn signal_reset_handlers() {
684 let default = SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty());
685
686 let signals = [
687 NixSignal::SIGHUP,
688 NixSignal::SIGINT,
689 NixSignal::SIGQUIT,
690 NixSignal::SIGTERM,
691 NixSignal::SIGCHLD,
692 NixSignal::SIGTSTP,
693 NixSignal::SIGTTIN,
694 NixSignal::SIGTTOU,
695 NixSignal::SIGPIPE,
696 ];
697
698 for sig in signals {
699 unsafe {
700 let _ = nix::sys::signal::sigaction(sig, &default);
701 }
702 }
703}
704
705pub fn signal_unblock_all() {
707 let _ = sigprocmask(SigmaskHow::SIG_SETMASK, Some(&SigSet::empty()), None);
708}
709
710pub fn signal_block_sigchld() -> SigSet {
712 let mut mask = SigSet::empty();
713 mask.add(NixSignal::SIGCHLD);
714 let mut old = SigSet::empty();
715 let _ = sigprocmask(SigmaskHow::SIG_BLOCK, Some(&mask), Some(&mut old));
716 old
717}
718
719pub fn signal_restore_mask(mask: &SigSet) {
721 let _ = sigprocmask(SigmaskHow::SIG_SETMASK, Some(mask), None);
722}
723
724pub fn signal_desc(sig: i32) -> &'static str {
726 match sig {
727 s if s == libc::SIGHUP => "Hangup",
728 s if s == libc::SIGINT => "Interrupt",
729 s if s == libc::SIGQUIT => "Quit",
730 s if s == libc::SIGILL => "Illegal instruction",
731 s if s == libc::SIGTRAP => "Trace trap",
732 s if s == libc::SIGABRT => "Abort",
733 s if s == libc::SIGBUS => "Bus error",
734 s if s == libc::SIGFPE => "Floating point exception",
735 s if s == libc::SIGKILL => "Killed",
736 s if s == libc::SIGUSR1 => "User signal 1",
737 s if s == libc::SIGSEGV => "Segmentation fault",
738 s if s == libc::SIGUSR2 => "User signal 2",
739 s if s == libc::SIGPIPE => "Broken pipe",
740 s if s == libc::SIGALRM => "Alarm clock",
741 s if s == libc::SIGTERM => "Terminated",
742 s if s == libc::SIGCHLD => "Child status changed",
743 s if s == libc::SIGCONT => "Continued",
744 s if s == libc::SIGSTOP => "Stopped (signal)",
745 s if s == libc::SIGTSTP => "Stopped",
746 s if s == libc::SIGTTIN => "Stopped (tty input)",
747 s if s == libc::SIGTTOU => "Stopped (tty output)",
748 s if s == libc::SIGURG => "Urgent I/O condition",
749 s if s == libc::SIGXCPU => "CPU time limit exceeded",
750 s if s == libc::SIGXFSZ => "File size limit exceeded",
751 s if s == libc::SIGVTALRM => "Virtual timer expired",
752 s if s == libc::SIGPROF => "Profiling timer expired",
753 s if s == libc::SIGWINCH => "Window size changed",
754 s if s == libc::SIGIO => "I/O possible",
755 s if s == libc::SIGSYS => "Bad system call",
756 _ => "Unknown signal",
757 }
758}
759
760#[cfg(test)]
761mod tests {
762 use super::*;
763
764 #[test]
765 fn test_sig_by_name() {
766 assert_eq!(sig_by_name("INT"), Some(libc::SIGINT));
767 assert_eq!(sig_by_name("SIGINT"), Some(libc::SIGINT));
768 assert_eq!(sig_by_name("int"), Some(libc::SIGINT));
769 assert_eq!(sig_by_name("HUP"), Some(libc::SIGHUP));
770 assert_eq!(sig_by_name("TERM"), Some(libc::SIGTERM));
771 assert_eq!(sig_by_name("EXIT"), Some(SIGEXIT));
772 assert_eq!(sig_by_name("9"), Some(9));
773 }
774
775 #[test]
776 fn test_sig_name() {
777 assert_eq!(sig_name(libc::SIGINT), Some("INT"));
778 assert_eq!(sig_name(libc::SIGHUP), Some("HUP"));
779 assert_eq!(sig_name(SIGEXIT), Some("EXIT"));
780 }
781
782 #[test]
783 fn test_trap_handler() {
784 let handler = TrapHandler::new();
785
786 assert!(!handler.is_trapped(libc::SIGUSR1));
788
789 handler
791 .set_trap(libc::SIGUSR1, TrapAction::Code("echo trapped".to_string()))
792 .unwrap();
793 assert!(handler.is_trapped(libc::SIGUSR1));
794
795 handler.unset_trap(libc::SIGUSR1);
797 assert!(!handler.is_trapped(libc::SIGUSR1));
798 }
799
800 #[test]
801 fn test_ignore_trap() {
802 let handler = TrapHandler::new();
803
804 handler.set_trap(libc::SIGUSR1, TrapAction::Ignore).unwrap();
805 assert!(handler.is_ignored(libc::SIGUSR1));
806 assert!(!handler.is_trapped(libc::SIGUSR1));
807 }
808
809 #[test]
810 fn test_signal_queue() {
811 queue_signals();
813 assert!(queueing_enabled());
814
815 unqueue_signals();
817 assert!(!queueing_enabled());
818 }
819
820 #[test]
821 fn test_cant_trap_sigkill() {
822 let handler = TrapHandler::new();
823 let result = handler.set_trap(libc::SIGKILL, TrapAction::Code("echo".to_string()));
824 assert!(result.is_err());
825 }
826}
827
828#[cfg(unix)]
834pub fn install_handler(sig: i32) {
835 unsafe {
836 libc::signal(sig, handler_func as *const () as libc::sighandler_t);
837 }
838}
839
840#[cfg(unix)]
841extern "C" fn handler_func(sig: libc::c_int) {
842 unsafe {
844 libc::signal(sig, handler_func as *const () as libc::sighandler_t);
845 }
846 LAST_SIGNAL.store(sig, std::sync::atomic::Ordering::Relaxed);
848}
849
850pub const SIGCOUNT: i32 = 32;
852
853pub const TRAPCOUNT: usize = (SIGCOUNT + 3) as usize;
855
856pub fn is_fatal_signal(sig: i32) -> bool {
858 sig == libc::SIGKILL || sig == libc::SIGSTOP
859}
860
861#[cfg(unix)]
863pub fn signal_block_all() {
864 unsafe {
865 let mut set: libc::sigset_t = std::mem::zeroed();
866 libc::sigfillset(&mut set);
867 libc::sigprocmask(libc::SIG_BLOCK, &set, std::ptr::null_mut());
868 }
869}
870
871#[cfg(unix)]
873pub fn signal_save_mask_raw() -> libc::sigset_t {
874 let mut old: libc::sigset_t = unsafe { std::mem::zeroed() };
875 unsafe {
876 libc::sigprocmask(libc::SIG_BLOCK, std::ptr::null(), &mut old);
877 }
878 old
879}
880
881#[cfg(unix)]
883pub fn signal_default_setup() {
884 unsafe {
885 libc::signal(libc::SIGQUIT, libc::SIG_IGN);
887 libc::signal(libc::SIGPIPE, libc::SIG_IGN);
888
889 install_handler(libc::SIGCHLD);
891
892 install_handler(libc::SIGWINCH);
894
895 install_handler(libc::SIGALRM);
897 }
898}
899
900#[cfg(unix)]
902pub fn signal_suspend() {
903 unsafe {
904 libc::raise(libc::SIGTSTP);
905 }
906}
907
908#[cfg(unix)]
910pub fn signal_wait() -> i32 {
911 let mut set: libc::sigset_t = unsafe { std::mem::zeroed() };
912 let mut sig: libc::c_int = 0;
913 unsafe {
914 libc::sigemptyset(&mut set);
915 libc::sigwait(&set, &mut sig);
916 }
917 sig
918}
919
920#[cfg(unix)]
922pub fn signal_pending(sig: i32) -> bool {
923 unsafe {
924 let mut set: libc::sigset_t = std::mem::zeroed();
925 if libc::sigpending(&mut set) == 0 {
926 libc::sigismember(&set, sig) == 1
927 } else {
928 false
929 }
930 }
931}
932
933#[derive(Debug, Default)]
935pub struct TrapScope {
936 saved_traps: Vec<(i32, TrapAction)>,
937}
938
939impl TrapScope {
940 pub fn new() -> Self {
941 Self::default()
942 }
943
944 pub fn save(&mut self, sig: i32, action: TrapAction) {
946 self.saved_traps.push((sig, action));
947 }
948
949 pub fn saved(&self) -> &[(i32, TrapAction)] {
951 &self.saved_traps
952 }
953}
954
955pub fn signal_names_list() -> Vec<String> {
957 let mut names = Vec::with_capacity(SIGCOUNT as usize + 1);
958 names.push("EXIT".to_string());
959 for i in 1..=SIGCOUNT {
960 if let Some(name) = sig_name(i) {
961 names.push(name.to_string());
962 } else {
963 names.push(format!("SIG{}", i));
964 }
965 }
966 names
967}
968
969#[cfg(unix)]
975pub fn nointr() {
976 unsafe {
977 libc::signal(libc::SIGINT, libc::SIG_IGN);
978 }
979}
980
981#[cfg(unix)]
983pub fn holdintr() {
984 signal_block(libc::SIGINT);
985}
986
987#[cfg(unix)]
989pub fn noholdintr() {
990 signal_unblock(libc::SIGINT);
991}
992
993#[cfg(unix)]
995pub fn signal_mask(sig: i32) -> libc::sigset_t {
996 let mut set: libc::sigset_t = unsafe { std::mem::zeroed() };
997 unsafe {
998 libc::sigemptyset(&mut set);
999 libc::sigaddset(&mut set, sig);
1000 }
1001 set
1002}
1003
1004#[cfg(unix)]
1006pub fn signal_setmask(mask: &libc::sigset_t) {
1007 unsafe {
1008 libc::sigprocmask(libc::SIG_SETMASK, mask, std::ptr::null_mut());
1009 }
1010}
1011
1012#[cfg(unix)]
1014pub fn wait_for_processes() -> Vec<(i32, i32)> {
1015 let mut results = Vec::new();
1016 loop {
1017 let mut status: i32 = 0;
1018 let pid = unsafe { libc::waitpid(-1, &mut status, libc::WNOHANG | libc::WUNTRACED) };
1019 if pid <= 0 {
1020 break;
1021 }
1022 results.push((pid, status));
1023 }
1024 results
1025}
1026
1027#[cfg(unix)]
1029extern "C" fn zhandler(sig: libc::c_int) {
1030 unsafe {
1032 libc::signal(sig, zhandler as *const () as libc::sighandler_t);
1033 }
1034 LAST_SIGNAL.store(sig, std::sync::atomic::Ordering::Relaxed);
1036}
1037
1038#[cfg(unix)]
1040pub fn killrunjobs(sig: i32) {
1041 let _ = sig;
1044}
1045
1046#[cfg(unix)]
1048pub fn killjb(pgrp: i32, sig: i32) -> i32 {
1049 if pgrp > 0 {
1050 unsafe { libc::killpg(pgrp, sig) }
1051 } else {
1052 -1
1053 }
1054}
1055
1056pub fn dosavetrap(sig: i32, handler: &TrapHandler) -> Option<TrapAction> {
1058 handler.get_trap(sig)
1059}
1060
1061pub fn settrap(sig: i32, action: TrapAction) -> Result<(), String> {
1063 let handler = traps();
1064 handler.set_trap(sig, action)
1065}
1066
1067pub fn unsettrap(sig: i32) {
1069 let handler = traps();
1070 handler.unset_trap(sig);
1071}
1072
1073pub fn handletrap(sig: i32) -> Option<String> {
1075 let handler = traps();
1076 if let Some(TrapAction::Code(code)) = handler.get_trap(sig) {
1077 Some(code)
1078 } else {
1079 None
1080 }
1081}
1082
1083pub fn dotrapargs(sig: i32, handler: &TrapHandler) -> Option<String> {
1085 match handler.get_trap(sig) {
1086 Some(TrapAction::Code(code)) => Some(code),
1087 _ => None,
1088 }
1089}
1090
1091pub fn dotrap(sig: i32) -> Option<String> {
1093 let handler = traps();
1094 dotrapargs(sig, handler)
1095}
1096
1097pub fn removetrap(sig: i32) {
1099 unsettrap(sig);
1100 #[cfg(unix)]
1102 unsafe {
1103 libc::signal(sig, libc::SIG_DFL);
1104 }
1105}
1106
1107pub fn rtsigno(offset: i32) -> Option<i32> {
1111 #[cfg(target_os = "linux")]
1112 {
1113 let sigrtmin = 34;
1115 let sigrtmax = 64;
1116 let sig = sigrtmin + offset;
1117 if sig <= sigrtmax {
1118 Some(sig)
1119 } else {
1120 None
1121 }
1122 }
1123 #[cfg(not(target_os = "linux"))]
1124 {
1125 let _ = offset;
1126 None
1127 }
1128}
1129
1130pub fn rtsigname(sig: i32) -> String {
1132 #[cfg(target_os = "linux")]
1133 {
1134 let sigrtmin = 34;
1135 let offset = sig - sigrtmin;
1136 if offset == 0 {
1137 "RTMIN".to_string()
1138 } else if offset > 0 {
1139 format!("RTMIN+{}", offset)
1140 } else {
1141 format!("SIG{}", sig)
1142 }
1143 }
1144 #[cfg(not(target_os = "linux"))]
1145 {
1146 format!("SIG{}", sig)
1147 }
1148}