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
389pub fn is_forked_child() -> bool {
394 let mut main = MAIN_PID.load(Ordering::Relaxed);
399 if main == 0 {
400 let cur = getpid().as_raw();
401 match MAIN_PID.compare_exchange(0, cur, Ordering::Relaxed, Ordering::Relaxed) {
402 Ok(_) => main = cur,
403 Err(prev) => main = prev,
404 }
405 }
406 getpid().as_raw() != main
407}
408
409fn reraise_if_forked_child(sig: i32) -> bool {
411 if getpid().as_raw() == MAIN_PID.load(Ordering::Relaxed) {
412 return false;
413 }
414 unsafe {
415 libc::signal(sig, libc::SIG_DFL);
416 libc::raise(sig);
417 }
418 true
419}
420
421extern "C" fn handler(sig: i32) {
423 #[cfg(target_os = "macos")]
425 let saved_errno = unsafe { *libc::__error() };
426 #[cfg(not(target_os = "macos"))]
427 let saved_errno = unsafe { *libc::__errno_location() };
428
429 if reraise_if_forked_child(sig) {
431 #[cfg(target_os = "macos")]
432 unsafe {
433 *libc::__error() = saved_errno
434 };
435 #[cfg(not(target_os = "macos"))]
436 unsafe {
437 *libc::__errno_location() = saved_errno
438 };
439 return;
440 }
441
442 LAST_SIGNAL.store(sig, Ordering::SeqCst);
443
444 if sig == libc::SIGCHLD {
446 SIGCHLD_RECEIVED.store(true, Ordering::SeqCst);
447 } else if sig == libc::SIGWINCH {
448 SIGWINCH_RECEIVED.store(true, Ordering::SeqCst);
449 }
450
451 if SIGNAL_QUEUE.is_enabled() {
453 SIGNAL_QUEUE.push(sig);
454 #[cfg(target_os = "macos")]
455 unsafe {
456 *libc::__error() = saved_errno
457 };
458 #[cfg(not(target_os = "macos"))]
459 unsafe {
460 *libc::__errno_location() = saved_errno
461 };
462 return;
463 }
464
465 handle_signal(sig);
467
468 #[cfg(target_os = "macos")]
469 unsafe {
470 *libc::__error() = saved_errno
471 };
472 #[cfg(not(target_os = "macos"))]
473 unsafe {
474 *libc::__errno_location() = saved_errno
475 };
476}
477
478fn handle_signal(sig: i32) {
480 match sig {
481 s if s == libc::SIGCHLD => {
482 }
484 s if s == libc::SIGINT => {
485 if let Some(action) = traps().get_trap(s) {
487 run_trap(s, &action);
488 }
489 }
490 s if s == libc::SIGHUP => {
491 if let Some(action) = traps().get_trap(s) {
493 run_trap(s, &action);
494 }
495 }
496 s if s == libc::SIGWINCH => {
497 if let Some(action) = traps().get_trap(s) {
499 run_trap(s, &action);
500 }
501 }
502 s if s == libc::SIGALRM => {
503 if let Some(action) = traps().get_trap(s) {
505 run_trap(s, &action);
506 }
507 }
508 s if s == libc::SIGPIPE => {
509 if let Some(action) = traps().get_trap(s) {
511 run_trap(s, &action);
512 }
513 }
514 _ => {
515 if let Some(action) = traps().get_trap(sig) {
517 run_trap(sig, &action);
518 }
519 }
520 }
521}
522
523fn run_trap(sig: i32, action: &TrapAction) {
525 match action {
526 TrapAction::Ignore => {}
527 TrapAction::Code(_code) => {
528 traps().in_trap.store(true, Ordering::SeqCst);
530 if sig == SIGEXIT {
531 traps().in_exit_trap.store(true, Ordering::SeqCst);
532 }
533 if sig == SIGEXIT {
535 traps().in_exit_trap.store(false, Ordering::SeqCst);
536 }
537 traps().in_trap.store(false, Ordering::SeqCst);
538 }
539 TrapAction::Function(_name) => {
540 traps().in_trap.store(true, Ordering::SeqCst);
542 traps().in_trap.store(false, Ordering::SeqCst);
544 }
545 TrapAction::Default => {}
546 }
547}
548
549pub fn queue_signals() {
551 SIGNAL_QUEUE.enable();
552}
553
554pub fn unqueue_signals() {
556 SIGNAL_QUEUE.disable();
557 while let Some(sig) = SIGNAL_QUEUE.pop() {
558 handle_signal(sig);
559 }
560}
561
562pub fn queueing_enabled() -> bool {
564 SIGNAL_QUEUE.is_enabled()
565}
566
567pub fn queue_traps() {
569 TRAP_QUEUE.enable();
570}
571
572pub fn unqueue_traps() {
574 TRAP_QUEUE.disable();
575 while let Some(sig) = TRAP_QUEUE.pop() {
576 if let Some(action) = traps().get_trap(sig) {
577 run_trap(sig, &action);
578 }
579 }
580}
581
582pub fn signal_block(sig: i32) {
584 unsafe {
585 let mut set: libc::sigset_t = std::mem::zeroed();
586 libc::sigemptyset(&mut set);
587 libc::sigaddset(&mut set, sig);
588 libc::sigprocmask(libc::SIG_BLOCK, &set, std::ptr::null_mut());
589 }
590}
591
592pub fn signal_unblock(sig: i32) {
594 unsafe {
595 let mut set: libc::sigset_t = std::mem::zeroed();
596 libc::sigemptyset(&mut set);
597 libc::sigaddset(&mut set, sig);
598 libc::sigprocmask(libc::SIG_UNBLOCK, &set, std::ptr::null_mut());
599 }
600}
601
602pub fn hold_intr() {
604 signal_block(libc::SIGINT);
605}
606
607pub fn release_intr() {
609 signal_unblock(libc::SIGINT);
610}
611
612pub fn setup_intr() {
614 unsafe {
615 libc::signal(libc::SIGINT, handler as *const () as usize);
616 }
617}
618
619pub fn last_signal() -> i32 {
621 LAST_SIGNAL.load(Ordering::SeqCst)
622}
623
624pub fn killpg(pgrp: i32, sig: i32) -> i32 {
626 unsafe { libc::killpg(pgrp, sig) }
627}
628
629pub fn kill(pid: i32, sig: i32) -> i32 {
631 unsafe { libc::kill(pid, sig) }
632}
633
634pub fn signal_check_sigchld() -> bool {
636 SIGCHLD_RECEIVED.swap(false, Ordering::SeqCst)
637}
638
639pub fn signal_check_sigwinch() -> bool {
641 SIGWINCH_RECEIVED.swap(false, Ordering::SeqCst)
642}
643
644pub fn signal_clear_cancel() {
646 LAST_SIGNAL.store(0, Ordering::SeqCst);
647}
648
649pub fn signal_check_cancel() -> i32 {
651 let sig = LAST_SIGNAL.load(Ordering::SeqCst);
652 if sig == libc::SIGINT {
653 sig
654 } else {
655 0
656 }
657}
658
659pub fn signal_set_handlers(interactive: bool) {
661 MAIN_PID.store(getpid().as_raw(), Ordering::Relaxed);
662
663 let ignore = SigAction::new(SigHandler::SigIgn, SaFlags::empty(), SigSet::empty());
665 unsafe {
666 let _ = nix::sys::signal::sigaction(NixSignal::SIGPIPE, &ignore);
667 let _ = nix::sys::signal::sigaction(NixSignal::SIGQUIT, &ignore);
668 }
669
670 let sa_handler = SigAction::new(
672 SigHandler::Handler(handler),
673 SaFlags::SA_RESTART,
674 SigSet::empty(),
675 );
676
677 unsafe {
678 let _ = nix::sys::signal::sigaction(NixSignal::SIGINT, &sa_handler);
679 let _ = nix::sys::signal::sigaction(NixSignal::SIGCHLD, &sa_handler);
680 }
681
682 if interactive {
683 unsafe {
685 let _ = nix::sys::signal::sigaction(NixSignal::SIGTSTP, &ignore);
686 let _ = nix::sys::signal::sigaction(NixSignal::SIGTTOU, &ignore);
687 }
688
689 unsafe {
691 let _ = nix::sys::signal::sigaction(NixSignal::SIGWINCH, &sa_handler);
692 }
693
694 unsafe {
696 let _ = nix::sys::signal::sigaction(NixSignal::SIGHUP, &sa_handler);
697 let _ = nix::sys::signal::sigaction(NixSignal::SIGTERM, &sa_handler);
698 }
699 }
700}
701
702pub fn signal_reset_handlers() {
704 let default = SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty());
705
706 let signals = [
707 NixSignal::SIGHUP,
708 NixSignal::SIGINT,
709 NixSignal::SIGQUIT,
710 NixSignal::SIGTERM,
711 NixSignal::SIGCHLD,
712 NixSignal::SIGTSTP,
713 NixSignal::SIGTTIN,
714 NixSignal::SIGTTOU,
715 NixSignal::SIGPIPE,
716 ];
717
718 for sig in signals {
719 unsafe {
720 let _ = nix::sys::signal::sigaction(sig, &default);
721 }
722 }
723}
724
725pub fn signal_unblock_all() {
727 let _ = sigprocmask(SigmaskHow::SIG_SETMASK, Some(&SigSet::empty()), None);
728}
729
730pub fn signal_block_sigchld() -> SigSet {
732 let mut mask = SigSet::empty();
733 mask.add(NixSignal::SIGCHLD);
734 let mut old = SigSet::empty();
735 let _ = sigprocmask(SigmaskHow::SIG_BLOCK, Some(&mask), Some(&mut old));
736 old
737}
738
739pub fn signal_restore_mask(mask: &SigSet) {
741 let _ = sigprocmask(SigmaskHow::SIG_SETMASK, Some(mask), None);
742}
743
744pub fn signal_desc(sig: i32) -> &'static str {
746 match sig {
747 s if s == libc::SIGHUP => "Hangup",
748 s if s == libc::SIGINT => "Interrupt",
749 s if s == libc::SIGQUIT => "Quit",
750 s if s == libc::SIGILL => "Illegal instruction",
751 s if s == libc::SIGTRAP => "Trace trap",
752 s if s == libc::SIGABRT => "Abort",
753 s if s == libc::SIGBUS => "Bus error",
754 s if s == libc::SIGFPE => "Floating point exception",
755 s if s == libc::SIGKILL => "Killed",
756 s if s == libc::SIGUSR1 => "User signal 1",
757 s if s == libc::SIGSEGV => "Segmentation fault",
758 s if s == libc::SIGUSR2 => "User signal 2",
759 s if s == libc::SIGPIPE => "Broken pipe",
760 s if s == libc::SIGALRM => "Alarm clock",
761 s if s == libc::SIGTERM => "Terminated",
762 s if s == libc::SIGCHLD => "Child status changed",
763 s if s == libc::SIGCONT => "Continued",
764 s if s == libc::SIGSTOP => "Stopped (signal)",
765 s if s == libc::SIGTSTP => "Stopped",
766 s if s == libc::SIGTTIN => "Stopped (tty input)",
767 s if s == libc::SIGTTOU => "Stopped (tty output)",
768 s if s == libc::SIGURG => "Urgent I/O condition",
769 s if s == libc::SIGXCPU => "CPU time limit exceeded",
770 s if s == libc::SIGXFSZ => "File size limit exceeded",
771 s if s == libc::SIGVTALRM => "Virtual timer expired",
772 s if s == libc::SIGPROF => "Profiling timer expired",
773 s if s == libc::SIGWINCH => "Window size changed",
774 s if s == libc::SIGIO => "I/O possible",
775 s if s == libc::SIGSYS => "Bad system call",
776 _ => "Unknown signal",
777 }
778}
779
780#[cfg(test)]
781mod tests {
782 use super::*;
783
784 #[test]
785 fn test_sig_by_name() {
786 assert_eq!(sig_by_name("INT"), Some(libc::SIGINT));
787 assert_eq!(sig_by_name("SIGINT"), Some(libc::SIGINT));
788 assert_eq!(sig_by_name("int"), Some(libc::SIGINT));
789 assert_eq!(sig_by_name("HUP"), Some(libc::SIGHUP));
790 assert_eq!(sig_by_name("TERM"), Some(libc::SIGTERM));
791 assert_eq!(sig_by_name("EXIT"), Some(SIGEXIT));
792 assert_eq!(sig_by_name("9"), Some(9));
793 }
794
795 #[test]
796 fn test_sig_name() {
797 assert_eq!(sig_name(libc::SIGINT), Some("INT"));
798 assert_eq!(sig_name(libc::SIGHUP), Some("HUP"));
799 assert_eq!(sig_name(SIGEXIT), Some("EXIT"));
800 }
801
802 #[test]
803 fn test_trap_handler() {
804 let handler = TrapHandler::new();
805
806 assert!(!handler.is_trapped(libc::SIGUSR1));
808
809 handler
811 .set_trap(libc::SIGUSR1, TrapAction::Code("echo trapped".to_string()))
812 .unwrap();
813 assert!(handler.is_trapped(libc::SIGUSR1));
814
815 handler.unset_trap(libc::SIGUSR1);
817 assert!(!handler.is_trapped(libc::SIGUSR1));
818 }
819
820 #[test]
821 fn test_ignore_trap() {
822 let handler = TrapHandler::new();
823
824 handler.set_trap(libc::SIGUSR1, TrapAction::Ignore).unwrap();
825 assert!(handler.is_ignored(libc::SIGUSR1));
826 assert!(!handler.is_trapped(libc::SIGUSR1));
827 }
828
829 #[test]
830 fn test_signal_queue() {
831 queue_signals();
833 assert!(queueing_enabled());
834
835 unqueue_signals();
837 assert!(!queueing_enabled());
838 }
839
840 #[test]
841 fn test_cant_trap_sigkill() {
842 let handler = TrapHandler::new();
843 let result = handler.set_trap(libc::SIGKILL, TrapAction::Code("echo".to_string()));
844 assert!(result.is_err());
845 }
846}
847
848#[cfg(unix)]
854pub fn install_handler(sig: i32) {
855 unsafe {
856 libc::signal(sig, handler_func as *const () as libc::sighandler_t);
857 }
858}
859
860#[cfg(unix)]
861extern "C" fn handler_func(sig: libc::c_int) {
862 unsafe {
864 libc::signal(sig, handler_func as *const () as libc::sighandler_t);
865 }
866 LAST_SIGNAL.store(sig, std::sync::atomic::Ordering::Relaxed);
868}
869
870pub const SIGCOUNT: i32 = 32;
872
873pub const TRAPCOUNT: usize = (SIGCOUNT + 3) as usize;
875
876pub fn is_fatal_signal(sig: i32) -> bool {
878 sig == libc::SIGKILL || sig == libc::SIGSTOP
879}
880
881#[cfg(unix)]
883pub fn signal_block_all() {
884 unsafe {
885 let mut set: libc::sigset_t = std::mem::zeroed();
886 libc::sigfillset(&mut set);
887 libc::sigprocmask(libc::SIG_BLOCK, &set, std::ptr::null_mut());
888 }
889}
890
891#[cfg(unix)]
893pub fn signal_save_mask_raw() -> libc::sigset_t {
894 let mut old: libc::sigset_t = unsafe { std::mem::zeroed() };
895 unsafe {
896 libc::sigprocmask(libc::SIG_BLOCK, std::ptr::null(), &mut old);
897 }
898 old
899}
900
901#[cfg(unix)]
903pub fn signal_default_setup() {
904 unsafe {
905 libc::signal(libc::SIGQUIT, libc::SIG_IGN);
907 libc::signal(libc::SIGPIPE, libc::SIG_IGN);
908
909 install_handler(libc::SIGCHLD);
911
912 install_handler(libc::SIGWINCH);
914
915 install_handler(libc::SIGALRM);
917 }
918}
919
920#[cfg(unix)]
922pub fn signal_suspend() {
923 unsafe {
924 libc::raise(libc::SIGTSTP);
925 }
926}
927
928#[cfg(unix)]
930pub fn signal_wait() -> i32 {
931 let mut set: libc::sigset_t = unsafe { std::mem::zeroed() };
932 let mut sig: libc::c_int = 0;
933 unsafe {
934 libc::sigemptyset(&mut set);
935 libc::sigwait(&set, &mut sig);
936 }
937 sig
938}
939
940#[cfg(unix)]
942pub fn signal_pending(sig: i32) -> bool {
943 unsafe {
944 let mut set: libc::sigset_t = std::mem::zeroed();
945 if libc::sigpending(&mut set) == 0 {
946 libc::sigismember(&set, sig) == 1
947 } else {
948 false
949 }
950 }
951}
952
953#[derive(Debug, Default)]
955pub struct TrapScope {
956 saved_traps: Vec<(i32, TrapAction)>,
957}
958
959impl TrapScope {
960 pub fn new() -> Self {
961 Self::default()
962 }
963
964 pub fn save(&mut self, sig: i32, action: TrapAction) {
966 self.saved_traps.push((sig, action));
967 }
968
969 pub fn saved(&self) -> &[(i32, TrapAction)] {
971 &self.saved_traps
972 }
973}
974
975pub fn signal_names_list() -> Vec<String> {
977 let mut names = Vec::with_capacity(SIGCOUNT as usize + 1);
978 names.push("EXIT".to_string());
979 for i in 1..=SIGCOUNT {
980 if let Some(name) = sig_name(i) {
981 names.push(name.to_string());
982 } else {
983 names.push(format!("SIG{}", i));
984 }
985 }
986 names
987}
988
989#[cfg(unix)]
995pub fn nointr() {
996 unsafe {
997 libc::signal(libc::SIGINT, libc::SIG_IGN);
998 }
999}
1000
1001#[cfg(unix)]
1003pub fn holdintr() {
1004 signal_block(libc::SIGINT);
1005}
1006
1007#[cfg(unix)]
1009pub fn noholdintr() {
1010 signal_unblock(libc::SIGINT);
1011}
1012
1013#[cfg(unix)]
1015pub fn signal_mask(sig: i32) -> libc::sigset_t {
1016 let mut set: libc::sigset_t = unsafe { std::mem::zeroed() };
1017 unsafe {
1018 libc::sigemptyset(&mut set);
1019 libc::sigaddset(&mut set, sig);
1020 }
1021 set
1022}
1023
1024#[cfg(unix)]
1026pub fn signal_setmask(mask: &libc::sigset_t) {
1027 unsafe {
1028 libc::sigprocmask(libc::SIG_SETMASK, mask, std::ptr::null_mut());
1029 }
1030}
1031
1032#[cfg(unix)]
1034pub fn wait_for_processes() -> Vec<(i32, i32)> {
1035 let mut results = Vec::new();
1036 loop {
1037 let mut status: i32 = 0;
1038 let pid = unsafe { libc::waitpid(-1, &mut status, libc::WNOHANG | libc::WUNTRACED) };
1039 if pid <= 0 {
1040 break;
1041 }
1042 results.push((pid, status));
1043 }
1044 results
1045}
1046
1047#[cfg(unix)]
1049extern "C" fn zhandler(sig: libc::c_int) {
1050 unsafe {
1052 libc::signal(sig, zhandler as *const () as libc::sighandler_t);
1053 }
1054 LAST_SIGNAL.store(sig, std::sync::atomic::Ordering::Relaxed);
1056}
1057
1058#[cfg(unix)]
1060pub fn killrunjobs(sig: i32) {
1061 let _ = sig;
1064}
1065
1066#[cfg(unix)]
1068pub fn killjb(pgrp: i32, sig: i32) -> i32 {
1069 if pgrp > 0 {
1070 unsafe { libc::killpg(pgrp, sig) }
1071 } else {
1072 -1
1073 }
1074}
1075
1076pub fn dosavetrap(sig: i32, handler: &TrapHandler) -> Option<TrapAction> {
1078 handler.get_trap(sig)
1079}
1080
1081pub fn settrap(sig: i32, action: TrapAction) -> Result<(), String> {
1083 let handler = traps();
1084 handler.set_trap(sig, action)
1085}
1086
1087pub fn unsettrap(sig: i32) {
1089 let handler = traps();
1090 handler.unset_trap(sig);
1091}
1092
1093pub fn handletrap(sig: i32) -> Option<String> {
1095 let handler = traps();
1096 if let Some(TrapAction::Code(code)) = handler.get_trap(sig) {
1097 Some(code)
1098 } else {
1099 None
1100 }
1101}
1102
1103pub fn dotrapargs(sig: i32, handler: &TrapHandler) -> Option<String> {
1105 match handler.get_trap(sig) {
1106 Some(TrapAction::Code(code)) => Some(code),
1107 _ => None,
1108 }
1109}
1110
1111pub fn dotrap(sig: i32) -> Option<String> {
1113 let handler = traps();
1114 dotrapargs(sig, handler)
1115}
1116
1117pub fn removetrap(sig: i32) {
1119 unsettrap(sig);
1120 #[cfg(unix)]
1122 unsafe {
1123 libc::signal(sig, libc::SIG_DFL);
1124 }
1125}
1126
1127pub fn rtsigno(offset: i32) -> Option<i32> {
1131 #[cfg(target_os = "linux")]
1132 {
1133 let sigrtmin = 34;
1135 let sigrtmax = 64;
1136 let sig = sigrtmin + offset;
1137 if sig <= sigrtmax {
1138 Some(sig)
1139 } else {
1140 None
1141 }
1142 }
1143 #[cfg(not(target_os = "linux"))]
1144 {
1145 let _ = offset;
1146 None
1147 }
1148}
1149
1150pub fn rtsigname(sig: i32) -> String {
1152 #[cfg(target_os = "linux")]
1153 {
1154 let sigrtmin = 34;
1155 let offset = sig - sigrtmin;
1156 if offset == 0 {
1157 "RTMIN".to_string()
1158 } else if offset > 0 {
1159 format!("RTMIN+{}", offset)
1160 } else {
1161 format!("SIG{}", sig)
1162 }
1163 }
1164 #[cfg(not(target_os = "linux"))]
1165 {
1166 format!("SIG{}", sig)
1167 }
1168}