1mod cond;
37mod state;
38
39pub use self::cond::Condition;
40pub use self::state::{Action, Origin, SetActionError, TrapState};
41use self::state::{EnterSubshellOption, GrandState};
42use crate::Env;
43use crate::signal;
44use crate::source::Location;
45use crate::system::{Disposition, Errno, Signals};
46#[cfg(doc)]
47use crate::system::{SharedSystem, System};
48use std::collections::BTreeMap;
49use std::collections::btree_map::Entry;
50use std::pin::Pin;
51
52pub trait SignalSystem: Signals {
54 fn get_disposition(&self, signal: signal::Number) -> Result<Disposition, Errno>;
60
61 fn set_disposition(
66 &mut self,
67 signal: signal::Number,
68 disposition: Disposition,
69 ) -> Result<Disposition, Errno>;
70}
71
72#[must_use]
76pub struct Iter<'a> {
77 inner: std::collections::btree_map::Iter<'a, Condition, GrandState>,
78}
79
80impl<'a> Iterator for Iter<'a> {
81 type Item = (&'a Condition, &'a TrapState, Option<&'a TrapState>);
86
87 fn next(&mut self) -> Option<(&'a Condition, &'a TrapState, Option<&'a TrapState>)> {
88 self.inner
89 .next()
90 .map(|(cond, state)| (cond, state.current_state(), state.parent_state()))
91 }
92}
93
94#[derive(Clone, Debug, Default)]
98pub struct TrapSet {
99 traps: BTreeMap<Condition, GrandState>,
100}
101
102impl TrapSet {
103 pub fn get_state<C: Into<Condition>>(
119 &self,
120 cond: C,
121 ) -> (Option<&TrapState>, Option<&TrapState>) {
122 self.get_state_impl(cond.into())
123 }
124
125 fn get_state_impl(&self, cond: Condition) -> (Option<&TrapState>, Option<&TrapState>) {
126 match self.traps.get(&cond) {
127 None => (None, None),
128 Some(state) => (Some(state.current_state()), state.parent_state()),
129 }
130 }
131
132 pub fn peek_state<S: SignalSystem, C: Into<Condition>>(
150 &mut self,
151 system: &S,
152 cond: C,
153 ) -> Result<&TrapState, Errno> {
154 self.peek_state_impl(system, cond.into())
155 }
156
157 fn peek_state_impl<S: SignalSystem>(
158 &mut self,
159 system: &S,
160 cond: Condition,
161 ) -> Result<&TrapState, Errno> {
162 let entry = self.traps.entry(cond);
163 let state = GrandState::insert_from_system_if_vacant(system, entry)?;
164 Ok(state.parent_state().unwrap_or(state.current_state()))
165 }
166
167 pub fn set_action<S: SignalSystem, C: Into<Condition>>(
186 &mut self,
187 system: &mut S,
188 cond: C,
189 action: Action,
190 origin: Location,
191 override_ignore: bool,
192 ) -> Result<(), SetActionError> {
193 self.set_action_impl(system, cond.into(), action, origin, override_ignore)
194 }
195
196 fn set_action_impl<S: SignalSystem>(
197 &mut self,
198 system: &mut S,
199 cond: Condition,
200 action: Action,
201 origin: Location,
202 override_ignore: bool,
203 ) -> Result<(), SetActionError> {
204 if let Condition::Signal(signal) = cond {
205 if signal == S::SIGKILL {
206 return Err(SetActionError::SIGKILL);
207 }
208 if signal == S::SIGSTOP {
209 return Err(SetActionError::SIGSTOP);
210 }
211 }
212
213 self.clear_parent_states();
214
215 let entry = self.traps.entry(cond);
216 GrandState::set_action(system, entry, action, origin, override_ignore)
217 }
218
219 fn clear_parent_states(&mut self) {
220 for state in self.traps.values_mut() {
221 state.clear_parent_state();
222 }
223 }
224
225 #[inline]
237 pub fn iter(&self) -> Iter<'_> {
238 let inner = self.traps.iter();
239 Iter { inner }
240 }
241
242 pub fn enter_subshell<S: SignalSystem>(
278 &mut self,
279 system: &mut S,
280 ignore_sigint_sigquit: bool,
281 keep_internal_dispositions_for_stoppers: bool,
282 ) {
283 self.clear_parent_states();
284
285 for (&cond, state) in &mut self.traps {
286 let option = match cond {
287 Condition::Exit => EnterSubshellOption::ClearInternalDisposition,
288 Condition::Signal(signal) =>
289 {
290 #[allow(clippy::if_same_then_else)]
291 if signal == S::SIGCHLD {
292 EnterSubshellOption::KeepInternalDisposition
293 } else if ignore_sigint_sigquit && (signal == S::SIGINT || signal == S::SIGQUIT)
294 {
295 EnterSubshellOption::Ignore
296 } else if keep_internal_dispositions_for_stoppers
297 && (signal == S::SIGTSTP || signal == S::SIGTTIN || signal == S::SIGTTOU)
298 && state.internal_disposition() != Disposition::Default
299 {
300 EnterSubshellOption::Ignore
301 } else {
302 EnterSubshellOption::ClearInternalDisposition
303 }
304 }
305 };
306 _ = state.enter_subshell(system, cond, option);
307 }
308
309 if ignore_sigint_sigquit {
310 for signal in [S::SIGINT, S::SIGQUIT] {
311 match self.traps.entry(Condition::Signal(signal)) {
312 Entry::Vacant(vacant) => _ = GrandState::ignore(system, vacant),
313 Entry::Occupied(_) => {}
315 }
316 }
317 }
318 }
319
320 pub fn catch_signal(&mut self, signal: signal::Number) {
325 if let Some(state) = self.traps.get_mut(&Condition::Signal(signal)) {
326 state.mark_as_caught();
327 }
328 }
329
330 pub fn take_signal_if_caught(&mut self, signal: signal::Number) -> Option<&TrapState> {
334 self.traps
335 .get_mut(&Condition::Signal(signal))
336 .and_then(|state| state.handle_if_caught())
337 }
338
339 pub fn take_caught_signal(&mut self) -> Option<(signal::Number, &TrapState)> {
347 self.traps.iter_mut().find_map(|(&cond, state)| match cond {
348 Condition::Signal(signal) => state.handle_if_caught().map(|trap| (signal, trap)),
349 _ => None,
350 })
351 }
352
353 fn set_internal_disposition<S: SignalSystem>(
354 &mut self,
355 signal: signal::Number,
356 disposition: Disposition,
357 system: &mut S,
358 ) -> Result<(), Errno> {
359 let entry = self.traps.entry(Condition::Signal(signal));
360 GrandState::set_internal_disposition(system, entry, disposition)
361 }
362
363 pub fn enable_internal_disposition_for_sigchld<S: SignalSystem>(
373 &mut self,
374 system: &mut S,
375 ) -> Result<(), Errno> {
376 self.set_internal_disposition(S::SIGCHLD, Disposition::Catch, system)
377 }
378
379 pub fn enable_internal_dispositions_for_terminators<S: SignalSystem>(
388 &mut self,
389 system: &mut S,
390 ) -> Result<(), Errno> {
391 self.set_internal_disposition(S::SIGINT, Disposition::Catch, system)?;
392 self.set_internal_disposition(S::SIGTERM, Disposition::Ignore, system)?;
393 self.set_internal_disposition(S::SIGQUIT, Disposition::Ignore, system)
394 }
395
396 pub fn enable_internal_dispositions_for_stoppers<S: SignalSystem>(
405 &mut self,
406 system: &mut S,
407 ) -> Result<(), Errno> {
408 self.set_internal_disposition(S::SIGTSTP, Disposition::Ignore, system)?;
409 self.set_internal_disposition(S::SIGTTIN, Disposition::Ignore, system)?;
410 self.set_internal_disposition(S::SIGTTOU, Disposition::Ignore, system)
411 }
412
413 pub fn disable_internal_dispositions_for_terminators<S: SignalSystem>(
415 &mut self,
416 system: &mut S,
417 ) -> Result<(), Errno> {
418 self.set_internal_disposition(S::SIGINT, Disposition::Default, system)?;
419 self.set_internal_disposition(S::SIGTERM, Disposition::Default, system)?;
420 self.set_internal_disposition(S::SIGQUIT, Disposition::Default, system)
421 }
422
423 pub fn disable_internal_dispositions_for_stoppers<S: SignalSystem>(
425 &mut self,
426 system: &mut S,
427 ) -> Result<(), Errno> {
428 self.set_internal_disposition(S::SIGTSTP, Disposition::Default, system)?;
429 self.set_internal_disposition(S::SIGTTIN, Disposition::Default, system)?;
430 self.set_internal_disposition(S::SIGTTOU, Disposition::Default, system)
431 }
432
433 pub fn disable_internal_dispositions<S: SignalSystem>(
439 &mut self,
440 system: &mut S,
441 ) -> Result<(), Errno> {
442 self.set_internal_disposition(S::SIGCHLD, Disposition::Default, system)?;
443 self.disable_internal_dispositions_for_terminators(system)?;
444 self.disable_internal_dispositions_for_stoppers(system)
445 }
446}
447
448impl<'a> IntoIterator for &'a TrapSet {
449 type Item = (&'a Condition, &'a TrapState, Option<&'a TrapState>);
454 type IntoIter = Iter<'a>;
455
456 #[inline(always)]
457 fn into_iter(self) -> Iter<'a> {
458 self.iter()
459 }
460}
461
462type PinFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
463
464pub struct RunSignalTrapIfCaught<S>(
491 pub for<'a> fn(
492 env: &'a mut Env<S>,
493 signal: signal::Number,
494 ) -> PinFuture<'a, Option<crate::semantics::Result>>,
495);
496
497impl<S> Clone for RunSignalTrapIfCaught<S> {
499 fn clone(&self) -> Self {
500 *self
501 }
502}
503
504impl<S> Copy for RunSignalTrapIfCaught<S> {}
505
506impl<S> std::fmt::Debug for RunSignalTrapIfCaught<S> {
507 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
508 f.debug_tuple("RunSignalTrapIfCaught")
509 .field(&self.0)
510 .finish()
511 }
512}
513
514#[cfg(test)]
515mod tests {
516 use super::*;
517 use crate::job::ProcessState;
518 use crate::system::SendSignal as _;
519 use crate::system::r#virtual::{
520 SIGABRT, SIGALRM, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGIOT,
521 SIGKILL, SIGPIPE, SIGPROF, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTERM, SIGTRAP, SIGTSTP,
522 SIGTTIN, SIGTTOU, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGWINCH, SIGXCPU, SIGXFSZ,
523 };
524 use crate::tests::in_virtual_system;
525 use std::collections::HashMap;
526 use std::ops::RangeInclusive;
527
528 #[derive(Default)]
529 pub struct DummySystem(pub HashMap<signal::Number, Disposition>);
530
531 impl Signals for DummySystem {
532 const SIGABRT: signal::Number = SIGABRT;
533 const SIGALRM: signal::Number = SIGALRM;
534 const SIGBUS: signal::Number = SIGBUS;
535 const SIGCHLD: signal::Number = SIGCHLD;
536 const SIGCLD: Option<signal::Number> = None;
537 const SIGCONT: signal::Number = SIGCONT;
538 const SIGEMT: Option<signal::Number> = None;
539 const SIGFPE: signal::Number = SIGFPE;
540 const SIGHUP: signal::Number = SIGHUP;
541 const SIGILL: signal::Number = SIGILL;
542 const SIGINFO: Option<signal::Number> = None;
543 const SIGINT: signal::Number = SIGINT;
544 const SIGIO: Option<signal::Number> = None;
545 const SIGIOT: signal::Number = SIGIOT;
546 const SIGKILL: signal::Number = SIGKILL;
547 const SIGLOST: Option<signal::Number> = None;
548 const SIGPIPE: signal::Number = SIGPIPE;
549 const SIGPOLL: Option<signal::Number> = None;
550 const SIGPROF: signal::Number = SIGPROF;
551 const SIGPWR: Option<signal::Number> = None;
552 const SIGQUIT: signal::Number = SIGQUIT;
553 const SIGSEGV: signal::Number = SIGSEGV;
554 const SIGSTKFLT: Option<signal::Number> = None;
555 const SIGSTOP: signal::Number = SIGSTOP;
556 const SIGSYS: signal::Number = SIGSYS;
557 const SIGTERM: signal::Number = SIGTERM;
558 const SIGTHR: Option<signal::Number> = None;
559 const SIGTRAP: signal::Number = SIGTRAP;
560 const SIGTSTP: signal::Number = SIGTSTP;
561 const SIGTTIN: signal::Number = SIGTTIN;
562 const SIGTTOU: signal::Number = SIGTTOU;
563 const SIGURG: signal::Number = SIGURG;
564 const SIGUSR1: signal::Number = SIGUSR1;
565 const SIGUSR2: signal::Number = SIGUSR2;
566 const SIGVTALRM: signal::Number = SIGVTALRM;
567 const SIGWINCH: signal::Number = SIGWINCH;
568 const SIGXCPU: signal::Number = SIGXCPU;
569 const SIGXFSZ: signal::Number = SIGXFSZ;
570
571 fn sigrt_range(&self) -> Option<RangeInclusive<signal::Number>> {
572 None
573 }
574 }
575
576 impl SignalSystem for DummySystem {
577 fn get_disposition(&self, signal: signal::Number) -> Result<Disposition, Errno> {
578 Ok(self.0.get(&signal).copied().unwrap_or_default())
579 }
580
581 fn set_disposition(
582 &mut self,
583 signal: signal::Number,
584 disposition: Disposition,
585 ) -> Result<Disposition, Errno> {
586 Ok(self.0.insert(signal, disposition).unwrap_or_default())
587 }
588 }
589
590 #[test]
591 fn default_trap() {
592 let trap_set = TrapSet::default();
593 assert_eq!(trap_set.get_state(SIGCHLD), (None, None));
594 }
595
596 #[test]
597 fn setting_trap_for_two_signals() {
598 let mut system = DummySystem::default();
599 let mut trap_set = TrapSet::default();
600 let origin_1 = Location::dummy("foo");
601 let result = trap_set.set_action(
602 &mut system,
603 SIGUSR1,
604 Action::Ignore,
605 origin_1.clone(),
606 false,
607 );
608 assert_eq!(result, Ok(()));
609
610 let command = Action::Command("echo".into());
611 let origin_2 = Location::dummy("bar");
612 let result = trap_set.set_action(
613 &mut system,
614 SIGUSR2,
615 command.clone(),
616 origin_2.clone(),
617 false,
618 );
619 assert_eq!(result, Ok(()));
620
621 assert_eq!(
622 trap_set.get_state(SIGUSR1),
623 (
624 Some(&TrapState {
625 action: Action::Ignore,
626 origin: Origin::User(origin_1),
627 pending: false
628 }),
629 None
630 )
631 );
632 assert_eq!(
633 trap_set.get_state(SIGUSR2),
634 (
635 Some(&TrapState {
636 action: command,
637 origin: Origin::User(origin_2),
638 pending: false
639 }),
640 None
641 )
642 );
643 assert_eq!(system.0[&SIGUSR1], Disposition::Ignore);
644 assert_eq!(system.0[&SIGUSR2], Disposition::Catch);
645 }
646
647 #[test]
648 fn setting_trap_for_sigkill() {
649 let mut system = DummySystem::default();
650 let mut trap_set = TrapSet::default();
651 let origin = Location::dummy("origin");
652 let result = trap_set.set_action(&mut system, SIGKILL, Action::Ignore, origin, false);
653 assert_eq!(result, Err(SetActionError::SIGKILL));
654 assert_eq!(trap_set.get_state(SIGKILL), (None, None));
655 assert_eq!(system.0.get(&SIGKILL), None);
656 }
657
658 #[test]
659 fn setting_trap_for_sigstop() {
660 let mut system = DummySystem::default();
661 let mut trap_set = TrapSet::default();
662 let origin = Location::dummy("origin");
663 let result = trap_set.set_action(&mut system, SIGSTOP, Action::Ignore, origin, false);
664 assert_eq!(result, Err(SetActionError::SIGSTOP));
665 assert_eq!(trap_set.get_state(SIGSTOP), (None, None));
666 assert_eq!(system.0.get(&SIGSTOP), None);
667 }
668
669 #[test]
670 fn peeking_state_with_default_inherited_disposition() {
671 let system = DummySystem::default();
672 let mut trap_set = TrapSet::default();
673 let result = trap_set.peek_state(&system, SIGCHLD);
674 assert_eq!(
675 result,
676 Ok(&TrapState {
677 action: Action::Default,
678 origin: Origin::Inherited,
679 pending: false
680 })
681 );
682 }
683
684 #[test]
685 fn peeking_state_with_inherited_disposition_of_ignore() {
686 let mut system = DummySystem::default();
687 system.0.insert(SIGCHLD, Disposition::Ignore);
688 let mut trap_set = TrapSet::default();
689 let result = trap_set.peek_state(&system, SIGCHLD);
690 assert_eq!(
691 result,
692 Ok(&TrapState {
693 action: Action::Ignore,
694 origin: Origin::Inherited,
695 pending: false
696 })
697 );
698 }
699
700 #[test]
701 fn peeking_state_with_parent_state() {
702 let mut system = DummySystem::default();
703 let mut trap_set = TrapSet::default();
704 let origin = Location::dummy("foo");
705 let command = Action::Command("echo".into());
706 trap_set
707 .set_action(&mut system, SIGUSR1, command.clone(), origin.clone(), false)
708 .unwrap();
709 trap_set.enter_subshell(&mut system, false, false);
710
711 let result = trap_set.peek_state(&system, SIGUSR1);
712 assert_eq!(
713 result,
714 Ok(&TrapState {
715 action: command,
716 origin: Origin::User(origin),
717 pending: false
718 })
719 );
720 }
721
722 #[test]
723 fn basic_iteration() {
724 let mut system = DummySystem::default();
725 let mut trap_set = TrapSet::default();
726 let origin_1 = Location::dummy("foo");
727 trap_set
728 .set_action(
729 &mut system,
730 SIGUSR1,
731 Action::Ignore,
732 origin_1.clone(),
733 false,
734 )
735 .unwrap();
736 let command = Action::Command("echo".into());
737 let origin_2 = Location::dummy("bar");
738 trap_set
739 .set_action(
740 &mut system,
741 SIGUSR2,
742 command.clone(),
743 origin_2.clone(),
744 false,
745 )
746 .unwrap();
747
748 let mut i = trap_set.iter();
749 let first = i.next().unwrap();
750 assert_eq!(first.0, &Condition::Signal(SIGUSR1));
751 assert_eq!(first.1.action, Action::Ignore);
752 assert_eq!(first.1.origin, Origin::User(origin_1));
753 assert_eq!(first.2, None);
754 let second = i.next().unwrap();
755 assert_eq!(second.0, &Condition::Signal(SIGUSR2));
756 assert_eq!(second.1.action, command);
757 assert_eq!(second.1.origin, Origin::User(origin_2));
758 assert_eq!(first.2, None);
759 assert_eq!(i.next(), None);
760 }
761
762 #[test]
763 fn iteration_after_entering_subshell() {
764 let mut system = DummySystem::default();
765 let mut trap_set = TrapSet::default();
766 let origin_1 = Location::dummy("foo");
767 trap_set
768 .set_action(
769 &mut system,
770 SIGUSR1,
771 Action::Ignore,
772 origin_1.clone(),
773 false,
774 )
775 .unwrap();
776 let command = Action::Command("echo".into());
777 let origin_2 = Location::dummy("bar");
778 trap_set
779 .set_action(
780 &mut system,
781 SIGUSR2,
782 command.clone(),
783 origin_2.clone(),
784 false,
785 )
786 .unwrap();
787 trap_set.enter_subshell(&mut system, false, false);
788
789 let mut i = trap_set.iter();
790 let first = i.next().unwrap();
791 assert_eq!(first.0, &Condition::Signal(SIGUSR1));
792 assert_eq!(first.1.action, Action::Ignore);
793 assert_eq!(first.1.origin, Origin::User(origin_1));
794 assert_eq!(first.2, None);
795 let second = i.next().unwrap();
796 assert_eq!(second.0, &Condition::Signal(SIGUSR2));
797 assert_eq!(second.1.action, Action::Default);
798 assert_eq!(second.1.origin, Origin::Subshell);
799 assert_eq!(second.2.unwrap().action, command);
800 assert_eq!(second.2.unwrap().origin, Origin::User(origin_2));
801 assert_eq!(i.next(), None);
802 }
803
804 #[test]
805 fn iteration_after_setting_trap_in_subshell() {
806 let mut system = DummySystem::default();
807 let mut trap_set = TrapSet::default();
808 let origin_1 = Location::dummy("foo");
809 let command = Action::Command("echo".into());
810 trap_set
811 .set_action(&mut system, SIGUSR1, command, origin_1, false)
812 .unwrap();
813 trap_set.enter_subshell(&mut system, false, false);
814 let origin_2 = Location::dummy("bar");
815 let command = Action::Command("ls".into());
816 trap_set
817 .set_action(
818 &mut system,
819 SIGUSR2,
820 command.clone(),
821 origin_2.clone(),
822 false,
823 )
824 .unwrap();
825
826 let mut i = trap_set.iter();
827 let first = i.next().unwrap();
828 assert_eq!(first.0, &Condition::Signal(SIGUSR1));
829 assert_eq!(first.1.action, Action::Default);
830 assert_eq!(first.1.origin, Origin::Subshell);
831 assert_eq!(first.2, None);
832 let second = i.next().unwrap();
833 assert_eq!(second.0, &Condition::Signal(SIGUSR2));
834 assert_eq!(second.1.action, command);
835 assert_eq!(second.1.origin, Origin::User(origin_2));
836 assert_eq!(second.2, None);
837 assert_eq!(i.next(), None);
838 }
839
840 #[test]
841 fn entering_subshell_resets_command_traps() {
842 let mut system = DummySystem::default();
843 let mut trap_set = TrapSet::default();
844 let action = Action::Command("".into());
845 let origin = Location::dummy("origin");
846 trap_set
847 .set_action(&mut system, SIGCHLD, action.clone(), origin.clone(), false)
848 .unwrap();
849
850 trap_set.enter_subshell(&mut system, false, false);
851 assert_eq!(
852 trap_set.get_state(SIGCHLD),
853 (
854 Some(&TrapState {
855 action: Action::Default,
856 origin: Origin::Subshell,
857 pending: false
858 }),
859 Some(&TrapState {
860 action,
861 origin: Origin::User(origin),
862 pending: false
863 })
864 )
865 );
866 assert_eq!(system.0[&SIGCHLD], Disposition::Default);
867 }
868
869 #[test]
870 fn entering_subshell_keeps_ignore_traps() {
871 let mut system = DummySystem::default();
872 let mut trap_set = TrapSet::default();
873 let origin = Location::dummy("origin");
874 trap_set
875 .set_action(&mut system, SIGCHLD, Action::Ignore, origin.clone(), false)
876 .unwrap();
877
878 trap_set.enter_subshell(&mut system, false, false);
879 assert_eq!(
880 trap_set.get_state(SIGCHLD),
881 (
882 Some(&TrapState {
883 action: Action::Ignore,
884 origin: Origin::User(origin),
885 pending: false
886 }),
887 None
888 )
889 );
890 assert_eq!(system.0[&SIGCHLD], Disposition::Ignore);
891 }
892
893 #[test]
894 fn entering_subshell_with_internal_disposition_for_sigchld() {
895 let mut system = DummySystem::default();
896 let mut trap_set = TrapSet::default();
897 let action = Action::Command("".into());
898 let origin = Location::dummy("origin");
899 trap_set
900 .set_action(&mut system, SIGCHLD, action.clone(), origin.clone(), false)
901 .unwrap();
902 trap_set
903 .enable_internal_disposition_for_sigchld(&mut system)
904 .unwrap();
905
906 trap_set.enter_subshell(&mut system, false, false);
907 assert_eq!(
908 trap_set.get_state(SIGCHLD),
909 (
910 Some(&TrapState {
911 action: Action::Default,
912 origin: Origin::Subshell,
913 pending: false
914 }),
915 Some(&TrapState {
916 action,
917 origin: Origin::User(origin),
918 pending: false
919 })
920 )
921 );
922 assert_eq!(system.0[&SIGCHLD], Disposition::Catch);
923 }
924
925 #[test]
926 fn entering_subshell_with_internal_disposition_for_sigint() {
927 let mut system = DummySystem::default();
928 let mut trap_set = TrapSet::default();
929 let action = Action::Command("".into());
930 let origin = Location::dummy("origin");
931 trap_set
932 .set_action(&mut system, SIGINT, action.clone(), origin.clone(), false)
933 .unwrap();
934 trap_set
935 .enable_internal_dispositions_for_terminators(&mut system)
936 .unwrap();
937
938 trap_set.enter_subshell(&mut system, false, false);
939 assert_eq!(
940 trap_set.get_state(SIGINT),
941 (
942 Some(&TrapState {
943 action: Action::Default,
944 origin: Origin::Subshell,
945 pending: false
946 }),
947 Some(&TrapState {
948 action,
949 origin: Origin::User(origin),
950 pending: false
951 })
952 )
953 );
954 assert_eq!(system.0[&SIGINT], Disposition::Default);
955 }
956
957 #[test]
958 fn entering_subshell_with_internal_disposition_for_sigterm() {
959 let mut system = DummySystem::default();
960 let mut trap_set = TrapSet::default();
961 let action = Action::Command("".into());
962 let origin = Location::dummy("origin");
963 trap_set
964 .set_action(&mut system, SIGTERM, action.clone(), origin.clone(), false)
965 .unwrap();
966 trap_set
967 .enable_internal_dispositions_for_terminators(&mut system)
968 .unwrap();
969
970 trap_set.enter_subshell(&mut system, false, false);
971 assert_eq!(
972 trap_set.get_state(SIGTERM),
973 (
974 Some(&TrapState {
975 action: Action::Default,
976 origin: Origin::Subshell,
977 pending: false
978 }),
979 Some(&TrapState {
980 action,
981 origin: Origin::User(origin),
982 pending: false
983 })
984 )
985 );
986 assert_eq!(system.0[&SIGTERM], Disposition::Default);
987 }
988
989 #[test]
990 fn entering_subshell_with_internal_disposition_for_sigquit() {
991 let mut system = DummySystem::default();
992 let mut trap_set = TrapSet::default();
993 let action = Action::Command("".into());
994 let origin = Location::dummy("origin");
995 trap_set
996 .set_action(&mut system, SIGQUIT, action.clone(), origin.clone(), false)
997 .unwrap();
998 trap_set
999 .enable_internal_dispositions_for_terminators(&mut system)
1000 .unwrap();
1001
1002 trap_set.enter_subshell(&mut system, false, false);
1003 assert_eq!(
1004 trap_set.get_state(SIGQUIT),
1005 (
1006 Some(&TrapState {
1007 action: Action::Default,
1008 origin: Origin::Subshell,
1009 pending: false
1010 }),
1011 Some(&TrapState {
1012 action,
1013 origin: Origin::User(origin),
1014 pending: false
1015 })
1016 )
1017 );
1018 assert_eq!(system.0[&SIGQUIT], Disposition::Default);
1019 }
1020
1021 #[test]
1022 fn entering_subshell_with_internal_disposition_for_sigtstp() {
1023 let mut system = DummySystem::default();
1024 let mut trap_set = TrapSet::default();
1025 let action = Action::Command("".into());
1026 let origin = Location::dummy("origin");
1027 trap_set
1028 .set_action(&mut system, SIGTSTP, action.clone(), origin.clone(), false)
1029 .unwrap();
1030 trap_set
1031 .enable_internal_dispositions_for_terminators(&mut system)
1032 .unwrap();
1033
1034 trap_set.enter_subshell(&mut system, false, false);
1035 assert_eq!(
1036 trap_set.get_state(SIGTSTP),
1037 (
1038 Some(&TrapState {
1039 action: Action::Default,
1040 origin: Origin::Subshell,
1041 pending: false
1042 }),
1043 Some(&TrapState {
1044 action,
1045 origin: Origin::User(origin),
1046 pending: false
1047 })
1048 )
1049 );
1050 assert_eq!(system.0[&SIGTSTP], Disposition::Default);
1051 }
1052
1053 #[test]
1054 fn entering_subshell_with_internal_disposition_for_sigttin() {
1055 let mut system = DummySystem::default();
1056 let mut trap_set = TrapSet::default();
1057 let action = Action::Command("".into());
1058 let origin = Location::dummy("origin");
1059 trap_set
1060 .set_action(&mut system, SIGTTIN, action.clone(), origin.clone(), false)
1061 .unwrap();
1062 trap_set
1063 .enable_internal_dispositions_for_terminators(&mut system)
1064 .unwrap();
1065
1066 trap_set.enter_subshell(&mut system, false, false);
1067 assert_eq!(
1068 trap_set.get_state(SIGTTIN),
1069 (
1070 Some(&TrapState {
1071 action: Action::Default,
1072 origin: Origin::Subshell,
1073 pending: false
1074 }),
1075 Some(&TrapState {
1076 action,
1077 origin: Origin::User(origin),
1078 pending: false
1079 })
1080 )
1081 );
1082 assert_eq!(system.0[&SIGTTIN], Disposition::Default);
1083 }
1084
1085 #[test]
1086 fn entering_subshell_with_internal_disposition_for_sigttou() {
1087 let mut system = DummySystem::default();
1088 let mut trap_set = TrapSet::default();
1089 let action = Action::Command("".into());
1090 let origin = Location::dummy("origin");
1091 trap_set
1092 .set_action(&mut system, SIGTTOU, action.clone(), origin.clone(), false)
1093 .unwrap();
1094 trap_set
1095 .enable_internal_dispositions_for_terminators(&mut system)
1096 .unwrap();
1097
1098 trap_set.enter_subshell(&mut system, false, false);
1099 assert_eq!(
1100 trap_set.get_state(SIGTTOU),
1101 (
1102 Some(&TrapState {
1103 action: Action::Default,
1104 origin: Origin::Subshell,
1105 pending: false
1106 }),
1107 Some(&TrapState {
1108 action,
1109 origin: Origin::User(origin),
1110 pending: false
1111 })
1112 )
1113 );
1114 assert_eq!(system.0[&SIGTTOU], Disposition::Default);
1115 }
1116
1117 #[test]
1118 fn setting_trap_after_entering_subshell_clears_parent_states() {
1119 let mut system = DummySystem::default();
1120 let mut trap_set = TrapSet::default();
1121 let origin_1 = Location::dummy("foo");
1122 let command = Action::Command("echo 1".into());
1123 trap_set
1124 .set_action(&mut system, SIGUSR1, command, origin_1, false)
1125 .unwrap();
1126 let origin_2 = Location::dummy("bar");
1127 let command = Action::Command("echo 2".into());
1128 trap_set
1129 .set_action(&mut system, SIGUSR2, command, origin_2, false)
1130 .unwrap();
1131 trap_set.enter_subshell(&mut system, false, false);
1132
1133 let command = Action::Command("echo 9".into());
1134 let origin_3 = Location::dummy("qux");
1135 trap_set
1136 .set_action(
1137 &mut system,
1138 SIGUSR1,
1139 command.clone(),
1140 origin_3.clone(),
1141 false,
1142 )
1143 .unwrap();
1144
1145 assert_eq!(
1146 trap_set.get_state(SIGUSR1),
1147 (
1148 Some(&TrapState {
1149 action: command,
1150 origin: Origin::User(origin_3),
1151 pending: false
1152 }),
1153 None
1154 )
1155 );
1156 assert_eq!(
1157 trap_set.get_state(SIGUSR2),
1158 (
1159 Some(&TrapState {
1160 action: Action::Default,
1161 origin: Origin::Subshell,
1162 pending: false
1163 }),
1164 None
1165 )
1166 );
1167 assert_eq!(system.0[&SIGUSR1], Disposition::Catch);
1168 assert_eq!(system.0[&SIGUSR2], Disposition::Default);
1169 }
1170
1171 #[test]
1172 fn entering_nested_subshell_clears_parent_states() {
1173 let mut system = DummySystem::default();
1174 let mut trap_set = TrapSet::default();
1175 let origin_1 = Location::dummy("foo");
1176 let command = Action::Command("echo 1".into());
1177 trap_set
1178 .set_action(&mut system, SIGUSR1, command, origin_1, false)
1179 .unwrap();
1180 let origin_2 = Location::dummy("bar");
1181 let command = Action::Command("echo 2".into());
1182 trap_set
1183 .set_action(&mut system, SIGUSR2, command, origin_2, false)
1184 .unwrap();
1185 trap_set.enter_subshell(&mut system, false, false);
1186 trap_set.enter_subshell(&mut system, false, false);
1187
1188 assert_eq!(
1189 trap_set.get_state(SIGUSR1),
1190 (
1191 Some(&TrapState {
1192 action: Action::Default,
1193 origin: Origin::Subshell,
1194 pending: false
1195 }),
1196 None
1197 )
1198 );
1199 assert_eq!(
1200 trap_set.get_state(SIGUSR2),
1201 (
1202 Some(&TrapState {
1203 action: Action::Default,
1204 origin: Origin::Subshell,
1205 pending: false
1206 }),
1207 None
1208 )
1209 );
1210 assert_eq!(system.0[&SIGUSR1], Disposition::Default);
1211 assert_eq!(system.0[&SIGUSR2], Disposition::Default);
1212 }
1213
1214 #[test]
1215 fn ignoring_sigint_on_entering_subshell_with_action_set() {
1216 in_virtual_system(|mut env, state| async move {
1217 env.traps
1218 .set_action(
1219 &mut env.system,
1220 SIGINT,
1221 Action::Command("".into()),
1222 Location::dummy(""),
1223 false,
1224 )
1225 .unwrap();
1226 env.system.kill(env.main_pid, Some(SIGINT)).await.unwrap();
1227 env.traps.enter_subshell(&mut env.system, true, false);
1228
1229 let state = state.borrow();
1230 let process = &state.processes[&env.main_pid];
1231 assert_eq!(process.disposition(SIGINT), Disposition::Ignore);
1232 assert_eq!(process.state(), ProcessState::Running);
1233 })
1234 }
1235
1236 #[test]
1237 fn ignoring_sigquit_on_entering_subshell_with_action_set() {
1238 in_virtual_system(|mut env, state| async move {
1239 env.traps
1240 .set_action(
1241 &mut env.system,
1242 SIGQUIT,
1243 Action::Command("".into()),
1244 Location::dummy(""),
1245 false,
1246 )
1247 .unwrap();
1248 env.system.kill(env.main_pid, Some(SIGQUIT)).await.unwrap();
1249 env.traps.enter_subshell(&mut env.system, true, false);
1250
1251 let state = state.borrow();
1252 let process = &state.processes[&env.main_pid];
1253 assert_eq!(process.disposition(SIGQUIT), Disposition::Ignore);
1254 assert_eq!(process.state(), ProcessState::Running);
1255 })
1256 }
1257
1258 #[test]
1259 fn ignoring_sigint_and_sigquit_on_entering_subshell_without_action_set() {
1260 let mut system = DummySystem::default();
1261 let mut trap_set = TrapSet::default();
1262 trap_set.enter_subshell(&mut system, true, false);
1263 assert_eq!(system.0[&SIGINT], Disposition::Ignore);
1264 assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1265 }
1266
1267 #[test]
1268 fn keeping_stopper_internal_dispositions_ignored() {
1269 in_virtual_system(|mut env, state| async move {
1270 for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
1271 env.traps
1272 .set_action(
1273 &mut env.system,
1274 signal,
1275 Action::Command("".into()),
1276 Location::dummy(""),
1277 false,
1278 )
1279 .unwrap();
1280 }
1281 env.traps
1282 .enable_internal_dispositions_for_stoppers(&mut env.system)
1283 .unwrap();
1284 for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
1285 env.system.kill(env.main_pid, Some(signal)).await.unwrap();
1286 }
1287 env.traps.enter_subshell(&mut env.system, false, true);
1288
1289 let state = state.borrow();
1290 let process = &state.processes[&env.main_pid];
1291 assert_eq!(process.disposition(SIGTSTP), Disposition::Ignore);
1292 assert_eq!(process.disposition(SIGTTIN), Disposition::Ignore);
1293 assert_eq!(process.disposition(SIGTTOU), Disposition::Ignore);
1294 assert_eq!(process.state(), ProcessState::Running);
1295 })
1296 }
1297
1298 #[test]
1299 fn no_stopper_internal_dispositions_enabled_to_be_kept_ignored() {
1300 in_virtual_system(|mut env, state| async move {
1301 for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
1302 env.traps
1303 .set_action(
1304 &mut env.system,
1305 signal,
1306 Action::Command("".into()),
1307 Location::dummy(""),
1308 false,
1309 )
1310 .unwrap();
1311 }
1312 env.traps.enter_subshell(&mut env.system, false, true);
1313
1314 let state = state.borrow();
1315 let process = &state.processes[&env.main_pid];
1316 assert_eq!(process.disposition(SIGTSTP), Disposition::Default);
1317 assert_eq!(process.disposition(SIGTTIN), Disposition::Default);
1318 assert_eq!(process.disposition(SIGTTOU), Disposition::Default);
1319 })
1320 }
1321
1322 #[test]
1323 fn catching_signal() {
1324 let mut system = DummySystem::default();
1325 let mut trap_set = TrapSet::default();
1326 let command = Action::Command("echo INT".into());
1327 let origin = Location::dummy("origin");
1328 trap_set
1329 .set_action(&mut system, SIGINT, command, origin, false)
1330 .unwrap();
1331 let command = Action::Command("echo TERM".into());
1332 let origin = Location::dummy("origin");
1333 trap_set
1334 .set_action(&mut system, SIGTERM, command, origin, false)
1335 .unwrap();
1336
1337 trap_set.catch_signal(SIGCHLD);
1338 trap_set.catch_signal(SIGINT);
1339
1340 let trap_state = trap_set.get_state(SIGINT).0.unwrap();
1341 assert!(trap_state.pending, "trap_state = {trap_state:?}");
1342 let trap_state = trap_set.get_state(SIGTERM).0.unwrap();
1343 assert!(!trap_state.pending, "trap_state = {trap_state:?}");
1344 }
1345
1346 #[test]
1347 fn taking_signal_if_caught() {
1348 let mut system = DummySystem::default();
1349 let mut trap_set = TrapSet::default();
1350 let command = Action::Command("echo INT".into());
1351 let origin = Location::dummy("origin");
1352 trap_set
1353 .set_action(&mut system, SIGINT, command, origin, false)
1354 .unwrap();
1355
1356 let result = trap_set.take_signal_if_caught(SIGINT);
1357 assert_eq!(result, None);
1358
1359 trap_set.catch_signal(SIGINT);
1360
1361 let result = trap_set.take_signal_if_caught(SIGINT);
1362 assert!(!result.unwrap().pending);
1363
1364 let result = trap_set.take_signal_if_caught(SIGINT);
1365 assert_eq!(result, None);
1366 }
1367
1368 #[test]
1369 fn taking_caught_signal() {
1370 let mut system = DummySystem::default();
1371 let mut trap_set = TrapSet::default();
1372 assert_eq!(trap_set.take_caught_signal(), None);
1373
1374 let command = Action::Command("echo INT".into());
1375 let origin = Location::dummy("origin");
1376 trap_set
1377 .set_action(&mut system, SIGINT, command, origin, false)
1378 .unwrap();
1379 let command = Action::Command("echo TERM".into());
1380 let origin = Location::dummy("origin");
1381 trap_set
1382 .set_action(&mut system, SIGTERM, command, origin, false)
1383 .unwrap();
1384 let command = Action::Command("echo USR1".into());
1385 let origin = Location::dummy("origin");
1386 trap_set
1387 .set_action(&mut system, SIGUSR1, command, origin, false)
1388 .unwrap();
1389 assert_eq!(trap_set.take_caught_signal(), None);
1390
1391 trap_set.catch_signal(SIGINT);
1392 trap_set.catch_signal(SIGUSR1);
1393 let result = trap_set.take_caught_signal().unwrap();
1396 match result.0 {
1397 SIGINT => {
1398 assert_eq!(result.1.action, Action::Command("echo INT".into()));
1399 assert!(!result.1.pending);
1400
1401 let result = trap_set.take_caught_signal().unwrap();
1402 assert_eq!(result.0, SIGUSR1);
1403 assert_eq!(result.1.action, Action::Command("echo USR1".into()));
1404 assert!(!result.1.pending);
1405 }
1406 SIGUSR1 => {
1407 assert_eq!(result.1.action, Action::Command("echo USR1".into()));
1408 assert!(!result.1.pending);
1409
1410 let result = trap_set.take_caught_signal().unwrap();
1411 assert_eq!(result.0, SIGINT);
1412 assert_eq!(result.1.action, Action::Command("echo INT".into()));
1413 assert!(!result.1.pending);
1414 }
1415 _ => panic!("wrong signal: {result:?}"),
1416 }
1417
1418 assert_eq!(trap_set.take_caught_signal(), None);
1419 }
1420
1421 #[test]
1422 fn enabling_internal_disposition_for_sigchld() {
1423 let mut system = DummySystem::default();
1424 let mut trap_set = TrapSet::default();
1425 trap_set
1426 .enable_internal_disposition_for_sigchld(&mut system)
1427 .unwrap();
1428 assert_eq!(system.0[&SIGCHLD], Disposition::Catch);
1429 }
1430
1431 #[test]
1432 fn enabling_internal_dispositions_for_terminators() {
1433 let mut system = DummySystem::default();
1434 let mut trap_set = TrapSet::default();
1435 trap_set
1436 .enable_internal_dispositions_for_terminators(&mut system)
1437 .unwrap();
1438 assert_eq!(system.0[&SIGINT], Disposition::Catch);
1439 assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1440 assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1441 }
1442
1443 #[test]
1444 fn enabling_internal_dispositions_for_stoppers() {
1445 let mut system = DummySystem::default();
1446 let mut trap_set = TrapSet::default();
1447 trap_set
1448 .enable_internal_dispositions_for_stoppers(&mut system)
1449 .unwrap();
1450 assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1451 assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1452 assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1453 }
1454
1455 #[test]
1456 fn disabling_internal_dispositions_for_initially_defaulted_signals() {
1457 let mut system = DummySystem::default();
1458 let mut trap_set = TrapSet::default();
1459 trap_set
1460 .enable_internal_disposition_for_sigchld(&mut system)
1461 .unwrap();
1462 trap_set
1463 .enable_internal_dispositions_for_terminators(&mut system)
1464 .unwrap();
1465 trap_set
1466 .enable_internal_dispositions_for_stoppers(&mut system)
1467 .unwrap();
1468 trap_set.disable_internal_dispositions(&mut system).unwrap();
1469 assert_eq!(system.0[&SIGCHLD], Disposition::Default);
1470 assert_eq!(system.0[&SIGINT], Disposition::Default);
1471 assert_eq!(system.0[&SIGTERM], Disposition::Default);
1472 assert_eq!(system.0[&SIGQUIT], Disposition::Default);
1473 assert_eq!(system.0[&SIGTSTP], Disposition::Default);
1474 assert_eq!(system.0[&SIGTTIN], Disposition::Default);
1475 assert_eq!(system.0[&SIGTTOU], Disposition::Default);
1476 }
1477
1478 fn ignore_signals(system: &mut DummySystem) {
1479 system.0.extend(
1480 [SIGCHLD, SIGINT, SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU]
1481 .into_iter()
1482 .map(|signal| (signal, Disposition::Ignore)),
1483 )
1484 }
1485
1486 #[test]
1487 fn disabling_internal_dispositions_for_initially_ignored_signals() {
1488 let mut system = DummySystem::default();
1489 ignore_signals(&mut system);
1490 let mut trap_set = TrapSet::default();
1491 trap_set
1492 .enable_internal_disposition_for_sigchld(&mut system)
1493 .unwrap();
1494 trap_set
1495 .enable_internal_dispositions_for_terminators(&mut system)
1496 .unwrap();
1497 trap_set
1498 .enable_internal_dispositions_for_stoppers(&mut system)
1499 .unwrap();
1500 trap_set.disable_internal_dispositions(&mut system).unwrap();
1501 assert_eq!(system.0[&SIGCHLD], Disposition::Ignore);
1502 assert_eq!(system.0[&SIGINT], Disposition::Ignore);
1503 assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1504 assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1505 assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1506 assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1507 assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1508 }
1509
1510 #[test]
1511 fn disabling_internal_dispositions_after_enabling_twice() {
1512 let mut system = DummySystem::default();
1513 ignore_signals(&mut system);
1514 let mut trap_set = TrapSet::default();
1515 trap_set
1516 .enable_internal_disposition_for_sigchld(&mut system)
1517 .unwrap();
1518 trap_set
1519 .enable_internal_disposition_for_sigchld(&mut system)
1520 .unwrap();
1521 trap_set
1522 .enable_internal_dispositions_for_terminators(&mut system)
1523 .unwrap();
1524 trap_set
1525 .enable_internal_dispositions_for_terminators(&mut system)
1526 .unwrap();
1527 trap_set
1528 .enable_internal_dispositions_for_stoppers(&mut system)
1529 .unwrap();
1530 trap_set
1531 .enable_internal_dispositions_for_stoppers(&mut system)
1532 .unwrap();
1533 trap_set.disable_internal_dispositions(&mut system).unwrap();
1534 assert_eq!(system.0[&SIGCHLD], Disposition::Ignore);
1535 assert_eq!(system.0[&SIGINT], Disposition::Ignore);
1536 assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1537 assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1538 assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1539 assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1540 assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1541 }
1542
1543 #[test]
1544 fn disabling_internal_dispositions_without_enabling() {
1545 let mut system = DummySystem::default();
1546 ignore_signals(&mut system);
1547 let mut trap_set = TrapSet::default();
1548 trap_set.disable_internal_dispositions(&mut system).unwrap();
1549 assert_eq!(system.0[&SIGCHLD], Disposition::Ignore);
1550 assert_eq!(system.0[&SIGINT], Disposition::Ignore);
1551 assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1552 assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1553 assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1554 assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1555 assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1556 }
1557
1558 #[test]
1559 fn reenabling_internal_dispositions() {
1560 let mut system = DummySystem::default();
1561 let mut trap_set = TrapSet::default();
1562 trap_set
1563 .enable_internal_disposition_for_sigchld(&mut system)
1564 .unwrap();
1565 trap_set
1566 .enable_internal_disposition_for_sigchld(&mut system)
1567 .unwrap();
1568 trap_set
1569 .enable_internal_dispositions_for_terminators(&mut system)
1570 .unwrap();
1571 trap_set
1572 .enable_internal_dispositions_for_terminators(&mut system)
1573 .unwrap();
1574 trap_set
1575 .enable_internal_dispositions_for_stoppers(&mut system)
1576 .unwrap();
1577 trap_set
1578 .enable_internal_dispositions_for_stoppers(&mut system)
1579 .unwrap();
1580 trap_set.disable_internal_dispositions(&mut system).unwrap();
1581 trap_set
1582 .enable_internal_disposition_for_sigchld(&mut system)
1583 .unwrap();
1584 trap_set
1585 .enable_internal_dispositions_for_terminators(&mut system)
1586 .unwrap();
1587 trap_set
1588 .enable_internal_dispositions_for_stoppers(&mut system)
1589 .unwrap();
1590 assert_eq!(system.0[&SIGCHLD], Disposition::Catch);
1591 assert_eq!(system.0[&SIGINT], Disposition::Catch);
1592 assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1593 assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1594 assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1595 assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1596 assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1597 }
1598
1599 #[test]
1600 fn setting_trap_to_ignore_after_enabling_internal_disposition() {
1601 let mut system = DummySystem::default();
1602 let mut trap_set = TrapSet::default();
1603 trap_set
1604 .enable_internal_disposition_for_sigchld(&mut system)
1605 .unwrap();
1606 let origin = Location::dummy("origin");
1607 let result = trap_set.set_action(&mut system, SIGCHLD, Action::Ignore, origin, false);
1608 assert_eq!(result, Ok(()));
1609 assert_eq!(system.0[&SIGCHLD], Disposition::Catch);
1610 }
1611
1612 #[test]
1613 fn resetting_trap_from_ignore_no_override_after_enabling_internal_dispositions() {
1614 let mut system = DummySystem::default();
1615 ignore_signals(&mut system);
1616 let mut trap_set = TrapSet::default();
1617 trap_set
1618 .enable_internal_disposition_for_sigchld(&mut system)
1619 .unwrap();
1620 trap_set
1621 .enable_internal_dispositions_for_terminators(&mut system)
1622 .unwrap();
1623 trap_set
1624 .enable_internal_dispositions_for_stoppers(&mut system)
1625 .unwrap();
1626
1627 for signal in [SIGCHLD, SIGINT] {
1628 let origin = Location::dummy("origin");
1629 let result = trap_set.set_action(&mut system, signal, Action::Default, origin, false);
1630 assert_eq!(result, Err(SetActionError::InitiallyIgnored));
1631 assert_eq!(system.0[&signal], Disposition::Catch);
1632 }
1633 for signal in [SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU] {
1634 let origin = Location::dummy("origin");
1635 let result = trap_set.set_action(&mut system, signal, Action::Default, origin, false);
1636 assert_eq!(result, Err(SetActionError::InitiallyIgnored));
1637 assert_eq!(system.0[&signal], Disposition::Ignore);
1638 }
1639 }
1640
1641 #[test]
1642 fn resetting_trap_from_ignore_override_after_enabling_internal_dispositions() {
1643 let mut system = DummySystem::default();
1644 ignore_signals(&mut system);
1645 let mut trap_set = TrapSet::default();
1646 trap_set
1647 .enable_internal_disposition_for_sigchld(&mut system)
1648 .unwrap();
1649 trap_set
1650 .enable_internal_dispositions_for_terminators(&mut system)
1651 .unwrap();
1652 trap_set
1653 .enable_internal_dispositions_for_stoppers(&mut system)
1654 .unwrap();
1655
1656 for signal in [SIGCHLD, SIGINT] {
1657 let origin = Location::dummy("origin");
1658 let result =
1659 trap_set.set_action(&mut system, signal, Action::Ignore, origin.clone(), true);
1660 assert_eq!(result, Ok(()));
1661 assert_eq!(
1662 trap_set.get_state(signal),
1663 (
1664 Some(&TrapState {
1665 action: Action::Ignore,
1666 origin: Origin::User(origin),
1667 pending: false
1668 }),
1669 None
1670 )
1671 );
1672 assert_eq!(system.0[&signal], Disposition::Catch);
1673 }
1674 for signal in [SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU] {
1675 let origin = Location::dummy("origin");
1676 let result =
1677 trap_set.set_action(&mut system, signal, Action::Ignore, origin.clone(), true);
1678 assert_eq!(result, Ok(()));
1679 assert_eq!(
1680 trap_set.get_state(signal),
1681 (
1682 Some(&TrapState {
1683 action: Action::Ignore,
1684 origin: Origin::User(origin),
1685 pending: false
1686 }),
1687 None
1688 )
1689 );
1690 assert_eq!(system.0[&signal], Disposition::Ignore);
1691 }
1692 }
1693
1694 #[test]
1695 fn disabling_internal_disposition_with_ignore_trap() {
1696 let signals = [SIGCHLD, SIGINT, SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU];
1697
1698 let mut system = DummySystem::default();
1699 let mut trap_set = TrapSet::default();
1700 trap_set
1701 .enable_internal_disposition_for_sigchld(&mut system)
1702 .unwrap();
1703 trap_set
1704 .enable_internal_dispositions_for_terminators(&mut system)
1705 .unwrap();
1706 let origin = Location::dummy("origin");
1707 for signal in signals {
1708 trap_set
1709 .set_action(&mut system, signal, Action::Ignore, origin.clone(), false)
1710 .unwrap();
1711 }
1712 trap_set.disable_internal_dispositions(&mut system).unwrap();
1713
1714 for signal in signals {
1715 assert_eq!(
1716 trap_set.get_state(signal),
1717 (
1718 Some(&TrapState {
1719 action: Action::Ignore,
1720 origin: Origin::User(origin.clone()),
1721 pending: false
1722 }),
1723 None
1724 )
1725 );
1726 assert_eq!(system.0[&signal], Disposition::Ignore);
1727 }
1728 }
1729}