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