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