1mod cond;
37mod state;
38
39pub use self::cond::Condition;
40pub use self::state::{Action, 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::btree_map::Entry;
47use std::collections::BTreeMap;
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 set_disposition(
82 &mut self,
83 signal: signal::Number,
84 disposition: Disposition,
85 ) -> Result<Disposition, Errno>;
86}
87
88#[must_use]
92pub struct Iter<'a> {
93 inner: std::collections::btree_map::Iter<'a, Condition, GrandState>,
94}
95
96impl<'a> Iterator for Iter<'a> {
97 type Item = (&'a Condition, Option<&'a TrapState>, Option<&'a TrapState>);
98
99 fn next(&mut self) -> Option<(&'a Condition, Option<&'a TrapState>, Option<&'a TrapState>)> {
100 loop {
101 let (cond, state) = self.inner.next()?;
102 let (current, parent) = state.get_state();
103 if current.is_some() || parent.is_some() {
104 return Some((cond, current, parent));
105 }
106 }
107 }
108}
109
110#[derive(Clone, Debug, Default)]
114pub struct TrapSet {
115 traps: BTreeMap<Condition, GrandState>,
116}
117
118impl TrapSet {
119 pub fn get_state<C: Into<Condition>>(
128 &self,
129 cond: C,
130 ) -> (Option<&TrapState>, Option<&TrapState>) {
131 self.get_state_impl(cond.into())
132 }
133
134 fn get_state_impl(&self, cond: Condition) -> (Option<&TrapState>, Option<&TrapState>) {
135 match self.traps.get(&cond) {
136 None => (None, None),
137 Some(state) => state.get_state(),
138 }
139 }
140
141 pub fn set_action<S: SignalSystem, C: Into<Condition>>(
160 &mut self,
161 system: &mut S,
162 cond: C,
163 action: Action,
164 origin: Location,
165 override_ignore: bool,
166 ) -> Result<(), SetActionError> {
167 self.set_action_impl(system, cond.into(), action, origin, override_ignore)
168 }
169
170 fn set_action_impl<S: SignalSystem>(
171 &mut self,
172 system: &mut S,
173 cond: Condition,
174 action: Action,
175 origin: Location,
176 override_ignore: bool,
177 ) -> Result<(), SetActionError> {
178 if let Condition::Signal(number) = cond {
179 match system.signal_name_from_number(number) {
180 signal::Name::Kill => return Err(SetActionError::SIGKILL),
181 signal::Name::Stop => return Err(SetActionError::SIGSTOP),
182 _ => {}
183 }
184 }
185
186 self.clear_parent_settings();
187
188 let entry = self.traps.entry(cond);
189 GrandState::set_action(system, entry, action, origin, override_ignore)
190 }
191
192 fn clear_parent_settings(&mut self) {
193 for state in self.traps.values_mut() {
194 state.clear_parent_setting();
195 }
196 }
197
198 #[inline]
204 pub fn iter(&self) -> Iter<'_> {
205 let inner = self.traps.iter();
206 Iter { inner }
207 }
208
209 pub fn enter_subshell<S: SignalSystem>(
245 &mut self,
246 system: &mut S,
247 ignore_sigint_sigquit: bool,
248 keep_internal_dispositions_for_stoppers: bool,
249 ) {
250 self.clear_parent_settings();
251
252 for (&cond, state) in &mut self.traps {
253 let option = match cond {
254 Condition::Exit => EnterSubshellOption::ClearInternalDisposition,
255 Condition::Signal(number) => {
256 use signal::Name::*;
257 match system.signal_name_from_number(number) {
258 Chld => EnterSubshellOption::KeepInternalDisposition,
259 Int | Quit if ignore_sigint_sigquit => EnterSubshellOption::Ignore,
260 Tstp | Ttin | Ttou
261 if keep_internal_dispositions_for_stoppers
262 && state.internal_disposition() != Disposition::Default =>
263 {
264 EnterSubshellOption::Ignore
265 }
266 _ => EnterSubshellOption::ClearInternalDisposition,
267 }
268 }
269 };
270 _ = state.enter_subshell(system, cond, option);
271 }
272
273 if ignore_sigint_sigquit {
274 for name in [signal::Name::Int, signal::Name::Quit] {
275 let number = system
276 .signal_number_from_name(name)
277 .unwrap_or_else(|| panic!("missing support for signal {name}"));
278 match self.traps.entry(Condition::Signal(number)) {
279 Entry::Vacant(vacant) => _ = GrandState::ignore(system, vacant),
280 Entry::Occupied(_) => {}
282 }
283 }
284 }
285 }
286
287 pub fn catch_signal(&mut self, signal: signal::Number) {
292 if let Some(state) = self.traps.get_mut(&Condition::Signal(signal)) {
293 state.mark_as_caught();
294 }
295 }
296
297 pub fn take_signal_if_caught(&mut self, signal: signal::Number) -> Option<&TrapState> {
301 self.traps
302 .get_mut(&signal.into())
303 .and_then(|state| state.handle_if_caught())
304 }
305
306 pub fn take_caught_signal(&mut self) -> Option<(signal::Number, &TrapState)> {
314 self.traps.iter_mut().find_map(|(&cond, state)| match cond {
315 Condition::Signal(signal) => state.handle_if_caught().map(|trap| (signal, trap)),
316 _ => None,
317 })
318 }
319
320 fn set_internal_disposition<S: SignalSystem>(
321 &mut self,
322 signal: signal::Name,
323 disposition: Disposition,
324 system: &mut S,
325 ) -> Result<(), Errno> {
326 let number = system
327 .signal_number_from_name(signal)
328 .unwrap_or_else(|| panic!("missing support for signal {signal}"));
329 let entry = self.traps.entry(Condition::Signal(number));
330 GrandState::set_internal_disposition(system, entry, disposition)
331 }
332
333 pub fn enable_internal_disposition_for_sigchld<S: SignalSystem>(
343 &mut self,
344 system: &mut S,
345 ) -> Result<(), Errno> {
346 self.set_internal_disposition(signal::Name::Chld, Disposition::Catch, system)
347 }
348
349 pub fn enable_internal_dispositions_for_terminators<S: SignalSystem>(
358 &mut self,
359 system: &mut S,
360 ) -> Result<(), Errno> {
361 self.set_internal_disposition(signal::Name::Int, Disposition::Catch, system)?;
362 self.set_internal_disposition(signal::Name::Term, Disposition::Ignore, system)?;
363 self.set_internal_disposition(signal::Name::Quit, Disposition::Ignore, system)
364 }
365
366 pub fn enable_internal_dispositions_for_stoppers<S: SignalSystem>(
375 &mut self,
376 system: &mut S,
377 ) -> Result<(), Errno> {
378 self.set_internal_disposition(signal::Name::Tstp, Disposition::Ignore, system)?;
379 self.set_internal_disposition(signal::Name::Ttin, Disposition::Ignore, system)?;
380 self.set_internal_disposition(signal::Name::Ttou, Disposition::Ignore, system)
381 }
382
383 pub fn disable_internal_dispositions_for_terminators<S: SignalSystem>(
385 &mut self,
386 system: &mut S,
387 ) -> Result<(), Errno> {
388 self.set_internal_disposition(signal::Name::Int, Disposition::Default, system)?;
389 self.set_internal_disposition(signal::Name::Term, Disposition::Default, system)?;
390 self.set_internal_disposition(signal::Name::Quit, Disposition::Default, system)
391 }
392
393 pub fn disable_internal_dispositions_for_stoppers<S: SignalSystem>(
395 &mut self,
396 system: &mut S,
397 ) -> Result<(), Errno> {
398 self.set_internal_disposition(signal::Name::Tstp, Disposition::Default, system)?;
399 self.set_internal_disposition(signal::Name::Ttin, Disposition::Default, system)?;
400 self.set_internal_disposition(signal::Name::Ttou, Disposition::Default, system)
401 }
402
403 pub fn disable_internal_dispositions<S: SignalSystem>(
409 &mut self,
410 system: &mut S,
411 ) -> Result<(), Errno> {
412 self.set_internal_disposition(signal::Name::Chld, Disposition::Default, system)?;
413 self.disable_internal_dispositions_for_terminators(system)?;
414 self.disable_internal_dispositions_for_stoppers(system)
415 }
416}
417
418impl<'a> IntoIterator for &'a TrapSet {
419 type Item = (&'a Condition, Option<&'a TrapState>, Option<&'a TrapState>);
420 type IntoIter = Iter<'a>;
421
422 #[inline(always)]
423 fn into_iter(self) -> Iter<'a> {
424 self.iter()
425 }
426}
427
428#[cfg(test)]
429mod tests {
430 use super::*;
431 use crate::job::ProcessState;
432 use crate::system::r#virtual::VirtualSystem;
433 use crate::system::r#virtual::{
434 SIGCHLD, SIGINT, SIGKILL, SIGQUIT, SIGSTOP, SIGTERM, SIGTSTP, SIGTTIN, SIGTTOU, SIGUSR1,
435 SIGUSR2,
436 };
437 use crate::system::System as _;
438 use crate::system::SystemEx as _;
439 use crate::tests::in_virtual_system;
440 use std::collections::HashMap;
441
442 #[derive(Default)]
443 pub struct DummySystem(pub HashMap<signal::Number, Disposition>);
444
445 impl SignalSystem for DummySystem {
446 fn signal_name_from_number(&self, number: signal::Number) -> signal::Name {
447 VirtualSystem::new().signal_name_from_number(number)
448 }
449
450 fn signal_number_from_name(&self, name: signal::Name) -> Option<signal::Number> {
451 VirtualSystem::new().signal_number_from_name(name)
452 }
453
454 fn set_disposition(
455 &mut self,
456 signal: signal::Number,
457 disposition: Disposition,
458 ) -> Result<Disposition, Errno> {
459 Ok(self
460 .0
461 .insert(signal, disposition)
462 .unwrap_or(Disposition::Default))
463 }
464 }
465
466 #[test]
467 fn default_trap() {
468 let trap_set = TrapSet::default();
469 assert_eq!(trap_set.get_state(SIGCHLD), (None, None));
470 }
471
472 #[test]
473 fn setting_trap_for_two_signals() {
474 let mut system = DummySystem::default();
475 let mut trap_set = TrapSet::default();
476 let origin_1 = Location::dummy("foo");
477 let result = trap_set.set_action(
478 &mut system,
479 SIGUSR1,
480 Action::Ignore,
481 origin_1.clone(),
482 false,
483 );
484 assert_eq!(result, Ok(()));
485
486 let command = Action::Command("echo".into());
487 let origin_2 = Location::dummy("bar");
488 let result = trap_set.set_action(
489 &mut system,
490 SIGUSR2,
491 command.clone(),
492 origin_2.clone(),
493 false,
494 );
495 assert_eq!(result, Ok(()));
496
497 assert_eq!(
498 trap_set.get_state(SIGUSR1),
499 (
500 Some(&TrapState {
501 action: Action::Ignore,
502 origin: origin_1,
503 pending: false
504 }),
505 None
506 )
507 );
508 assert_eq!(
509 trap_set.get_state(SIGUSR2),
510 (
511 Some(&TrapState {
512 action: command,
513 origin: origin_2,
514 pending: false
515 }),
516 None
517 )
518 );
519 assert_eq!(system.0[&SIGUSR1], Disposition::Ignore);
520 assert_eq!(system.0[&SIGUSR2], Disposition::Catch);
521 }
522
523 #[test]
524 fn setting_trap_for_sigkill() {
525 let mut system = DummySystem::default();
526 let mut trap_set = TrapSet::default();
527 let origin = Location::dummy("origin");
528 let result = trap_set.set_action(&mut system, SIGKILL, Action::Ignore, origin, false);
529 assert_eq!(result, Err(SetActionError::SIGKILL));
530 assert_eq!(trap_set.get_state(SIGKILL), (None, None));
531 assert_eq!(system.0.get(&SIGKILL), None);
532 }
533
534 #[test]
535 fn setting_trap_for_sigstop() {
536 let mut system = DummySystem::default();
537 let mut trap_set = TrapSet::default();
538 let origin = Location::dummy("origin");
539 let result = trap_set.set_action(&mut system, SIGSTOP, Action::Ignore, origin, false);
540 assert_eq!(result, Err(SetActionError::SIGSTOP));
541 assert_eq!(trap_set.get_state(SIGSTOP), (None, None));
542 assert_eq!(system.0.get(&SIGSTOP), None);
543 }
544
545 #[test]
546 fn basic_iteration() {
547 let mut system = DummySystem::default();
548 let mut trap_set = TrapSet::default();
549 let origin_1 = Location::dummy("foo");
550 trap_set
551 .set_action(
552 &mut system,
553 SIGUSR1,
554 Action::Ignore,
555 origin_1.clone(),
556 false,
557 )
558 .unwrap();
559 let command = Action::Command("echo".into());
560 let origin_2 = Location::dummy("bar");
561 trap_set
562 .set_action(
563 &mut system,
564 SIGUSR2,
565 command.clone(),
566 origin_2.clone(),
567 false,
568 )
569 .unwrap();
570
571 let mut i = trap_set.iter();
572 let first = i.next().unwrap();
573 assert_eq!(first.0, &Condition::Signal(SIGUSR1));
574 assert_eq!(first.1.unwrap().action, Action::Ignore);
575 assert_eq!(first.1.unwrap().origin, origin_1);
576 assert_eq!(first.2, None);
577 let second = i.next().unwrap();
578 assert_eq!(second.0, &Condition::Signal(SIGUSR2));
579 assert_eq!(second.1.unwrap().action, command);
580 assert_eq!(second.1.unwrap().origin, origin_2);
581 assert_eq!(first.2, None);
582 assert_eq!(i.next(), None);
583 }
584
585 #[test]
586 fn iteration_after_entering_subshell() {
587 let mut system = DummySystem::default();
588 let mut trap_set = TrapSet::default();
589 let origin_1 = Location::dummy("foo");
590 trap_set
591 .set_action(
592 &mut system,
593 SIGUSR1,
594 Action::Ignore,
595 origin_1.clone(),
596 false,
597 )
598 .unwrap();
599 let command = Action::Command("echo".into());
600 let origin_2 = Location::dummy("bar");
601 trap_set
602 .set_action(
603 &mut system,
604 SIGUSR2,
605 command.clone(),
606 origin_2.clone(),
607 false,
608 )
609 .unwrap();
610 trap_set.enter_subshell(&mut system, false, false);
611
612 let mut i = trap_set.iter();
613 let first = i.next().unwrap();
614 assert_eq!(first.0, &Condition::Signal(SIGUSR1));
615 assert_eq!(first.1.unwrap().action, Action::Ignore);
616 assert_eq!(first.1.unwrap().origin, origin_1);
617 assert_eq!(first.2, None);
618 let second = i.next().unwrap();
619 assert_eq!(second.0, &Condition::Signal(SIGUSR2));
620 assert_eq!(second.1, None);
621 assert_eq!(second.2.unwrap().action, command);
622 assert_eq!(second.2.unwrap().origin, origin_2);
623 assert_eq!(i.next(), None);
624 }
625
626 #[test]
627 fn iteration_after_setting_trap_in_subshell() {
628 let mut system = DummySystem::default();
629 let mut trap_set = TrapSet::default();
630 let origin_1 = Location::dummy("foo");
631 let command = Action::Command("echo".into());
632 trap_set
633 .set_action(&mut system, SIGUSR1, command, origin_1, false)
634 .unwrap();
635 trap_set.enter_subshell(&mut system, false, false);
636 let origin_2 = Location::dummy("bar");
637 let command = Action::Command("ls".into());
638 trap_set
639 .set_action(
640 &mut system,
641 SIGUSR2,
642 command.clone(),
643 origin_2.clone(),
644 false,
645 )
646 .unwrap();
647
648 let mut i = trap_set.iter();
649 let first = i.next().unwrap();
650 assert_eq!(first.0, &Condition::Signal(SIGUSR2));
651 assert_eq!(first.1.unwrap().action, command);
652 assert_eq!(first.1.unwrap().origin, origin_2);
653 assert_eq!(first.2, None);
654 assert_eq!(i.next(), None);
655 }
656
657 #[test]
658 fn entering_subshell_resets_command_traps() {
659 let mut system = DummySystem::default();
660 let mut trap_set = TrapSet::default();
661 let action = Action::Command("".into());
662 let origin = Location::dummy("origin");
663 trap_set
664 .set_action(&mut system, SIGCHLD, action.clone(), origin.clone(), false)
665 .unwrap();
666
667 trap_set.enter_subshell(&mut system, false, false);
668 assert_eq!(
669 trap_set.get_state(SIGCHLD),
670 (
671 None,
672 Some(&TrapState {
673 action,
674 origin,
675 pending: false
676 })
677 )
678 );
679 assert_eq!(system.0[&SIGCHLD], Disposition::Default);
680 }
681
682 #[test]
683 fn entering_subshell_keeps_ignore_traps() {
684 let mut system = DummySystem::default();
685 let mut trap_set = TrapSet::default();
686 let origin = Location::dummy("origin");
687 trap_set
688 .set_action(&mut system, SIGCHLD, Action::Ignore, origin.clone(), false)
689 .unwrap();
690
691 trap_set.enter_subshell(&mut system, false, false);
692 assert_eq!(
693 trap_set.get_state(SIGCHLD),
694 (
695 Some(&TrapState {
696 action: Action::Ignore,
697 origin,
698 pending: false
699 }),
700 None
701 )
702 );
703 assert_eq!(system.0[&SIGCHLD], Disposition::Ignore);
704 }
705
706 #[test]
707 fn entering_subshell_with_internal_disposition_for_sigchld() {
708 let mut system = DummySystem::default();
709 let mut trap_set = TrapSet::default();
710 let action = Action::Command("".into());
711 let origin = Location::dummy("origin");
712 trap_set
713 .set_action(&mut system, SIGCHLD, action.clone(), origin.clone(), false)
714 .unwrap();
715 trap_set
716 .enable_internal_disposition_for_sigchld(&mut system)
717 .unwrap();
718
719 trap_set.enter_subshell(&mut system, false, false);
720 assert_eq!(
721 trap_set.get_state(SIGCHLD),
722 (
723 None,
724 Some(&TrapState {
725 action,
726 origin,
727 pending: false
728 })
729 )
730 );
731 assert_eq!(system.0[&SIGCHLD], Disposition::Catch);
732 }
733
734 #[test]
735 fn entering_subshell_with_internal_disposition_for_sigint() {
736 let mut system = DummySystem::default();
737 let mut trap_set = TrapSet::default();
738 let action = Action::Command("".into());
739 let origin = Location::dummy("origin");
740 trap_set
741 .set_action(&mut system, SIGINT, action.clone(), origin.clone(), false)
742 .unwrap();
743 trap_set
744 .enable_internal_dispositions_for_terminators(&mut system)
745 .unwrap();
746
747 trap_set.enter_subshell(&mut system, false, false);
748 assert_eq!(
749 trap_set.get_state(SIGINT),
750 (
751 None,
752 Some(&TrapState {
753 action,
754 origin,
755 pending: false
756 })
757 )
758 );
759 assert_eq!(system.0[&SIGINT], Disposition::Default);
760 }
761
762 #[test]
763 fn entering_subshell_with_internal_disposition_for_sigterm() {
764 let mut system = DummySystem::default();
765 let mut trap_set = TrapSet::default();
766 let action = Action::Command("".into());
767 let origin = Location::dummy("origin");
768 trap_set
769 .set_action(&mut system, SIGTERM, action.clone(), origin.clone(), false)
770 .unwrap();
771 trap_set
772 .enable_internal_dispositions_for_terminators(&mut system)
773 .unwrap();
774
775 trap_set.enter_subshell(&mut system, false, false);
776 assert_eq!(
777 trap_set.get_state(SIGTERM),
778 (
779 None,
780 Some(&TrapState {
781 action,
782 origin,
783 pending: false
784 })
785 )
786 );
787 assert_eq!(system.0[&SIGTERM], Disposition::Default);
788 }
789
790 #[test]
791 fn entering_subshell_with_internal_disposition_for_sigquit() {
792 let mut system = DummySystem::default();
793 let mut trap_set = TrapSet::default();
794 let action = Action::Command("".into());
795 let origin = Location::dummy("origin");
796 trap_set
797 .set_action(&mut system, SIGQUIT, action.clone(), origin.clone(), false)
798 .unwrap();
799 trap_set
800 .enable_internal_dispositions_for_terminators(&mut system)
801 .unwrap();
802
803 trap_set.enter_subshell(&mut system, false, false);
804 assert_eq!(
805 trap_set.get_state(SIGQUIT),
806 (
807 None,
808 Some(&TrapState {
809 action,
810 origin,
811 pending: false
812 })
813 )
814 );
815 assert_eq!(system.0[&SIGQUIT], Disposition::Default);
816 }
817
818 #[test]
819 fn entering_subshell_with_internal_disposition_for_sigtstp() {
820 let mut system = DummySystem::default();
821 let mut trap_set = TrapSet::default();
822 let action = Action::Command("".into());
823 let origin = Location::dummy("origin");
824 trap_set
825 .set_action(&mut system, SIGTSTP, action.clone(), origin.clone(), false)
826 .unwrap();
827 trap_set
828 .enable_internal_dispositions_for_terminators(&mut system)
829 .unwrap();
830
831 trap_set.enter_subshell(&mut system, false, false);
832 assert_eq!(
833 trap_set.get_state(SIGTSTP),
834 (
835 None,
836 Some(&TrapState {
837 action,
838 origin,
839 pending: false
840 })
841 )
842 );
843 assert_eq!(system.0[&SIGTSTP], Disposition::Default);
844 }
845
846 #[test]
847 fn entering_subshell_with_internal_disposition_for_sigttin() {
848 let mut system = DummySystem::default();
849 let mut trap_set = TrapSet::default();
850 let action = Action::Command("".into());
851 let origin = Location::dummy("origin");
852 trap_set
853 .set_action(&mut system, SIGTTIN, action.clone(), origin.clone(), false)
854 .unwrap();
855 trap_set
856 .enable_internal_dispositions_for_terminators(&mut system)
857 .unwrap();
858
859 trap_set.enter_subshell(&mut system, false, false);
860 assert_eq!(
861 trap_set.get_state(SIGTTIN),
862 (
863 None,
864 Some(&TrapState {
865 action,
866 origin,
867 pending: false
868 })
869 )
870 );
871 assert_eq!(system.0[&SIGTTIN], Disposition::Default);
872 }
873
874 #[test]
875 fn entering_subshell_with_internal_disposition_for_sigttou() {
876 let mut system = DummySystem::default();
877 let mut trap_set = TrapSet::default();
878 let action = Action::Command("".into());
879 let origin = Location::dummy("origin");
880 trap_set
881 .set_action(&mut system, SIGTTOU, action.clone(), origin.clone(), false)
882 .unwrap();
883 trap_set
884 .enable_internal_dispositions_for_terminators(&mut system)
885 .unwrap();
886
887 trap_set.enter_subshell(&mut system, false, false);
888 assert_eq!(
889 trap_set.get_state(SIGTTOU),
890 (
891 None,
892 Some(&TrapState {
893 action,
894 origin,
895 pending: false
896 })
897 )
898 );
899 assert_eq!(system.0[&SIGTTOU], Disposition::Default);
900 }
901
902 #[test]
903 fn setting_trap_after_entering_subshell_clears_parent_states() {
904 let mut system = DummySystem::default();
905 let mut trap_set = TrapSet::default();
906 let origin_1 = Location::dummy("foo");
907 let command = Action::Command("echo 1".into());
908 trap_set
909 .set_action(&mut system, SIGUSR1, command, origin_1, false)
910 .unwrap();
911 let origin_2 = Location::dummy("bar");
912 let command = Action::Command("echo 2".into());
913 trap_set
914 .set_action(&mut system, SIGUSR2, command, origin_2, false)
915 .unwrap();
916 trap_set.enter_subshell(&mut system, false, false);
917
918 let command = Action::Command("echo 9".into());
919 let origin_3 = Location::dummy("qux");
920 trap_set
921 .set_action(
922 &mut system,
923 SIGUSR1,
924 command.clone(),
925 origin_3.clone(),
926 false,
927 )
928 .unwrap();
929
930 assert_eq!(
931 trap_set.get_state(SIGUSR1),
932 (
933 Some(&TrapState {
934 action: command,
935 origin: origin_3,
936 pending: false
937 }),
938 None
939 )
940 );
941 assert_eq!(trap_set.get_state(SIGUSR2), (None, None));
942 assert_eq!(system.0[&SIGUSR1], Disposition::Catch);
943 assert_eq!(system.0[&SIGUSR2], Disposition::Default);
944 }
945
946 #[test]
947 fn entering_nested_subshell_clears_parent_states() {
948 let mut system = DummySystem::default();
949 let mut trap_set = TrapSet::default();
950 let origin_1 = Location::dummy("foo");
951 let command = Action::Command("echo 1".into());
952 trap_set
953 .set_action(&mut system, SIGUSR1, command, origin_1, false)
954 .unwrap();
955 let origin_2 = Location::dummy("bar");
956 let command = Action::Command("echo 2".into());
957 trap_set
958 .set_action(&mut system, SIGUSR2, command, origin_2, false)
959 .unwrap();
960 trap_set.enter_subshell(&mut system, false, false);
961 trap_set.enter_subshell(&mut system, false, false);
962
963 assert_eq!(trap_set.get_state(SIGUSR1), (None, None));
964 assert_eq!(trap_set.get_state(SIGUSR2), (None, None));
965 assert_eq!(system.0[&SIGUSR1], Disposition::Default);
966 assert_eq!(system.0[&SIGUSR2], Disposition::Default);
967 }
968
969 #[test]
970 fn ignoring_sigint_on_entering_subshell_with_action_set() {
971 in_virtual_system(|mut env, state| async move {
972 env.traps
973 .set_action(
974 &mut env.system,
975 SIGINT,
976 Action::Command("".into()),
977 Location::dummy(""),
978 false,
979 )
980 .unwrap();
981 env.system.kill(env.main_pid, Some(SIGINT)).await.unwrap();
982 env.traps.enter_subshell(&mut env.system, true, false);
983
984 let state = state.borrow();
985 let process = &state.processes[&env.main_pid];
986 assert_eq!(process.disposition(SIGINT), Disposition::Ignore);
987 assert_eq!(process.state(), ProcessState::Running);
988 })
989 }
990
991 #[test]
992 fn ignoring_sigquit_on_entering_subshell_with_action_set() {
993 in_virtual_system(|mut env, state| async move {
994 env.traps
995 .set_action(
996 &mut env.system,
997 SIGQUIT,
998 Action::Command("".into()),
999 Location::dummy(""),
1000 false,
1001 )
1002 .unwrap();
1003 env.system.kill(env.main_pid, Some(SIGQUIT)).await.unwrap();
1004 env.traps.enter_subshell(&mut env.system, true, false);
1005
1006 let state = state.borrow();
1007 let process = &state.processes[&env.main_pid];
1008 assert_eq!(process.disposition(SIGQUIT), Disposition::Ignore);
1009 assert_eq!(process.state(), ProcessState::Running);
1010 })
1011 }
1012
1013 #[test]
1014 fn ignoring_sigint_and_sigquit_on_entering_subshell_without_action_set() {
1015 let mut system = DummySystem::default();
1016 let mut trap_set = TrapSet::default();
1017 trap_set.enter_subshell(&mut system, true, false);
1018 assert_eq!(system.0[&SIGINT], Disposition::Ignore);
1019 assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1020 }
1021
1022 #[test]
1023 fn keeping_stopper_internal_dispositions_ignored() {
1024 in_virtual_system(|mut env, state| async move {
1025 for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
1026 env.traps
1027 .set_action(
1028 &mut env.system,
1029 signal,
1030 Action::Command("".into()),
1031 Location::dummy(""),
1032 false,
1033 )
1034 .unwrap();
1035 }
1036 env.traps
1037 .enable_internal_dispositions_for_stoppers(&mut env.system)
1038 .unwrap();
1039 for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
1040 env.system.kill(env.main_pid, Some(signal)).await.unwrap();
1041 }
1042 env.traps.enter_subshell(&mut env.system, false, true);
1043
1044 let state = state.borrow();
1045 let process = &state.processes[&env.main_pid];
1046 assert_eq!(process.disposition(SIGTSTP), Disposition::Ignore);
1047 assert_eq!(process.disposition(SIGTTIN), Disposition::Ignore);
1048 assert_eq!(process.disposition(SIGTTOU), Disposition::Ignore);
1049 assert_eq!(process.state(), ProcessState::Running);
1050 })
1051 }
1052
1053 #[test]
1054 fn no_stopper_internal_dispositions_enabled_to_be_kept_ignored() {
1055 in_virtual_system(|mut env, state| async move {
1056 for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
1057 env.traps
1058 .set_action(
1059 &mut env.system,
1060 signal,
1061 Action::Command("".into()),
1062 Location::dummy(""),
1063 false,
1064 )
1065 .unwrap();
1066 }
1067 env.traps.enter_subshell(&mut env.system, false, true);
1068
1069 let state = state.borrow();
1070 let process = &state.processes[&env.main_pid];
1071 assert_eq!(process.disposition(SIGTSTP), Disposition::Default);
1072 assert_eq!(process.disposition(SIGTTIN), Disposition::Default);
1073 assert_eq!(process.disposition(SIGTTOU), Disposition::Default);
1074 })
1075 }
1076
1077 #[test]
1078 fn catching_signal() {
1079 let mut system = DummySystem::default();
1080 let mut trap_set = TrapSet::default();
1081 let command = Action::Command("echo INT".into());
1082 let origin = Location::dummy("origin");
1083 trap_set
1084 .set_action(&mut system, SIGINT, command, origin, false)
1085 .unwrap();
1086 let command = Action::Command("echo TERM".into());
1087 let origin = Location::dummy("origin");
1088 trap_set
1089 .set_action(&mut system, SIGTERM, command, origin, false)
1090 .unwrap();
1091
1092 trap_set.catch_signal(SIGCHLD);
1093 trap_set.catch_signal(SIGINT);
1094
1095 let trap_state = trap_set.get_state(SIGINT).0.unwrap();
1096 assert!(trap_state.pending, "trap_state = {trap_state:?}");
1097 let trap_state = trap_set.get_state(SIGTERM).0.unwrap();
1098 assert!(!trap_state.pending, "trap_state = {trap_state:?}");
1099 }
1100
1101 #[test]
1102 fn taking_signal_if_caught() {
1103 let mut system = DummySystem::default();
1104 let mut trap_set = TrapSet::default();
1105 let command = Action::Command("echo INT".into());
1106 let origin = Location::dummy("origin");
1107 trap_set
1108 .set_action(&mut system, SIGINT, command, origin, false)
1109 .unwrap();
1110
1111 let result = trap_set.take_signal_if_caught(SIGINT);
1112 assert_eq!(result, None);
1113
1114 trap_set.catch_signal(SIGINT);
1115
1116 let result = trap_set.take_signal_if_caught(SIGINT);
1117 assert!(!result.unwrap().pending);
1118
1119 let result = trap_set.take_signal_if_caught(SIGINT);
1120 assert_eq!(result, None);
1121 }
1122
1123 #[test]
1124 fn taking_caught_signal() {
1125 let mut system = DummySystem::default();
1126 let mut trap_set = TrapSet::default();
1127 assert_eq!(trap_set.take_caught_signal(), None);
1128
1129 let command = Action::Command("echo INT".into());
1130 let origin = Location::dummy("origin");
1131 trap_set
1132 .set_action(&mut system, SIGINT, command, origin, false)
1133 .unwrap();
1134 let command = Action::Command("echo TERM".into());
1135 let origin = Location::dummy("origin");
1136 trap_set
1137 .set_action(&mut system, SIGTERM, command, origin, false)
1138 .unwrap();
1139 let command = Action::Command("echo USR1".into());
1140 let origin = Location::dummy("origin");
1141 trap_set
1142 .set_action(&mut system, SIGUSR1, command, origin, false)
1143 .unwrap();
1144 assert_eq!(trap_set.take_caught_signal(), None);
1145
1146 trap_set.catch_signal(SIGINT);
1147 trap_set.catch_signal(SIGUSR1);
1148 let result = trap_set.take_caught_signal().unwrap();
1151 match result.0 {
1152 SIGINT => {
1153 assert_eq!(result.1.action, Action::Command("echo INT".into()));
1154 assert!(!result.1.pending);
1155
1156 let result = trap_set.take_caught_signal().unwrap();
1157 assert_eq!(result.0, SIGUSR1);
1158 assert_eq!(result.1.action, Action::Command("echo USR1".into()));
1159 assert!(!result.1.pending);
1160 }
1161 SIGUSR1 => {
1162 assert_eq!(result.1.action, Action::Command("echo USR1".into()));
1163 assert!(!result.1.pending);
1164
1165 let result = trap_set.take_caught_signal().unwrap();
1166 assert_eq!(result.0, SIGINT);
1167 assert_eq!(result.1.action, Action::Command("echo INT".into()));
1168 assert!(!result.1.pending);
1169 }
1170 _ => panic!("wrong signal: {result:?}"),
1171 }
1172
1173 assert_eq!(trap_set.take_caught_signal(), None);
1174 }
1175
1176 #[test]
1177 fn enabling_internal_disposition_for_sigchld() {
1178 let mut system = DummySystem::default();
1179 let mut trap_set = TrapSet::default();
1180 trap_set
1181 .enable_internal_disposition_for_sigchld(&mut system)
1182 .unwrap();
1183 assert_eq!(system.0[&SIGCHLD], Disposition::Catch);
1184 }
1185
1186 #[test]
1187 fn enabling_internal_dispositions_for_terminators() {
1188 let mut system = DummySystem::default();
1189 let mut trap_set = TrapSet::default();
1190 trap_set
1191 .enable_internal_dispositions_for_terminators(&mut system)
1192 .unwrap();
1193 assert_eq!(system.0[&SIGINT], Disposition::Catch);
1194 assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1195 assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1196 }
1197
1198 #[test]
1199 fn enabling_internal_dispositions_for_stoppers() {
1200 let mut system = DummySystem::default();
1201 let mut trap_set = TrapSet::default();
1202 trap_set
1203 .enable_internal_dispositions_for_stoppers(&mut system)
1204 .unwrap();
1205 assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1206 assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1207 assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1208 }
1209
1210 #[test]
1211 fn disabling_internal_dispositions_for_initially_defaulted_signals() {
1212 let mut system = DummySystem::default();
1213 let mut trap_set = TrapSet::default();
1214 trap_set
1215 .enable_internal_disposition_for_sigchld(&mut system)
1216 .unwrap();
1217 trap_set
1218 .enable_internal_dispositions_for_terminators(&mut system)
1219 .unwrap();
1220 trap_set
1221 .enable_internal_dispositions_for_stoppers(&mut system)
1222 .unwrap();
1223 trap_set.disable_internal_dispositions(&mut system).unwrap();
1224 assert_eq!(system.0[&SIGCHLD], Disposition::Default);
1225 assert_eq!(system.0[&SIGINT], Disposition::Default);
1226 assert_eq!(system.0[&SIGTERM], Disposition::Default);
1227 assert_eq!(system.0[&SIGQUIT], Disposition::Default);
1228 assert_eq!(system.0[&SIGTSTP], Disposition::Default);
1229 assert_eq!(system.0[&SIGTTIN], Disposition::Default);
1230 assert_eq!(system.0[&SIGTTOU], Disposition::Default);
1231 }
1232
1233 fn ignore_signals(system: &mut DummySystem) {
1234 system.0.extend(
1235 [SIGCHLD, SIGINT, SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU]
1236 .into_iter()
1237 .map(|signal| (signal, Disposition::Ignore)),
1238 )
1239 }
1240
1241 #[test]
1242 fn disabling_internal_dispositions_for_initially_ignored_signals() {
1243 let mut system = DummySystem::default();
1244 ignore_signals(&mut system);
1245 let mut trap_set = TrapSet::default();
1246 trap_set
1247 .enable_internal_disposition_for_sigchld(&mut system)
1248 .unwrap();
1249 trap_set
1250 .enable_internal_dispositions_for_terminators(&mut system)
1251 .unwrap();
1252 trap_set
1253 .enable_internal_dispositions_for_stoppers(&mut system)
1254 .unwrap();
1255 trap_set.disable_internal_dispositions(&mut system).unwrap();
1256 assert_eq!(system.0[&SIGCHLD], Disposition::Ignore);
1257 assert_eq!(system.0[&SIGINT], Disposition::Ignore);
1258 assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1259 assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1260 assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1261 assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1262 assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1263 }
1264
1265 #[test]
1266 fn disabling_internal_dispositions_after_enabling_twice() {
1267 let mut system = DummySystem::default();
1268 ignore_signals(&mut system);
1269 let mut trap_set = TrapSet::default();
1270 trap_set
1271 .enable_internal_disposition_for_sigchld(&mut system)
1272 .unwrap();
1273 trap_set
1274 .enable_internal_disposition_for_sigchld(&mut system)
1275 .unwrap();
1276 trap_set
1277 .enable_internal_dispositions_for_terminators(&mut system)
1278 .unwrap();
1279 trap_set
1280 .enable_internal_dispositions_for_terminators(&mut system)
1281 .unwrap();
1282 trap_set
1283 .enable_internal_dispositions_for_stoppers(&mut system)
1284 .unwrap();
1285 trap_set
1286 .enable_internal_dispositions_for_stoppers(&mut system)
1287 .unwrap();
1288 trap_set.disable_internal_dispositions(&mut system).unwrap();
1289 assert_eq!(system.0[&SIGCHLD], Disposition::Ignore);
1290 assert_eq!(system.0[&SIGINT], Disposition::Ignore);
1291 assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1292 assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1293 assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1294 assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1295 assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1296 }
1297
1298 #[test]
1299 fn disabling_internal_dispositions_without_enabling() {
1300 let mut system = DummySystem::default();
1301 ignore_signals(&mut system);
1302 let mut trap_set = TrapSet::default();
1303 trap_set.disable_internal_dispositions(&mut system).unwrap();
1304 assert_eq!(system.0[&SIGCHLD], Disposition::Ignore);
1305 assert_eq!(system.0[&SIGINT], Disposition::Ignore);
1306 assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1307 assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1308 assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1309 assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1310 assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1311 }
1312
1313 #[test]
1314 fn reenabling_internal_dispositions() {
1315 let mut system = DummySystem::default();
1316 let mut trap_set = TrapSet::default();
1317 trap_set
1318 .enable_internal_disposition_for_sigchld(&mut system)
1319 .unwrap();
1320 trap_set
1321 .enable_internal_disposition_for_sigchld(&mut system)
1322 .unwrap();
1323 trap_set
1324 .enable_internal_dispositions_for_terminators(&mut system)
1325 .unwrap();
1326 trap_set
1327 .enable_internal_dispositions_for_terminators(&mut system)
1328 .unwrap();
1329 trap_set
1330 .enable_internal_dispositions_for_stoppers(&mut system)
1331 .unwrap();
1332 trap_set
1333 .enable_internal_dispositions_for_stoppers(&mut system)
1334 .unwrap();
1335 trap_set.disable_internal_dispositions(&mut system).unwrap();
1336 trap_set
1337 .enable_internal_disposition_for_sigchld(&mut system)
1338 .unwrap();
1339 trap_set
1340 .enable_internal_dispositions_for_terminators(&mut system)
1341 .unwrap();
1342 trap_set
1343 .enable_internal_dispositions_for_stoppers(&mut system)
1344 .unwrap();
1345 assert_eq!(system.0[&SIGCHLD], Disposition::Catch);
1346 assert_eq!(system.0[&SIGINT], Disposition::Catch);
1347 assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1348 assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1349 assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1350 assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1351 assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1352 }
1353
1354 #[test]
1355 fn setting_trap_to_ignore_after_enabling_internal_disposition() {
1356 let mut system = DummySystem::default();
1357 let mut trap_set = TrapSet::default();
1358 trap_set
1359 .enable_internal_disposition_for_sigchld(&mut system)
1360 .unwrap();
1361 let origin = Location::dummy("origin");
1362 let result = trap_set.set_action(&mut system, SIGCHLD, Action::Ignore, origin, false);
1363 assert_eq!(result, Ok(()));
1364 assert_eq!(system.0[&SIGCHLD], Disposition::Catch);
1365 }
1366
1367 #[test]
1368 fn resetting_trap_from_ignore_no_override_after_enabling_internal_dispositions() {
1369 let mut system = DummySystem::default();
1370 ignore_signals(&mut system);
1371 let mut trap_set = TrapSet::default();
1372 trap_set
1373 .enable_internal_disposition_for_sigchld(&mut system)
1374 .unwrap();
1375 trap_set
1376 .enable_internal_dispositions_for_terminators(&mut system)
1377 .unwrap();
1378 trap_set
1379 .enable_internal_dispositions_for_stoppers(&mut system)
1380 .unwrap();
1381
1382 for signal in [SIGCHLD, SIGINT] {
1383 let origin = Location::dummy("origin");
1384 let result = trap_set.set_action(&mut system, signal, Action::Default, origin, false);
1385 assert_eq!(result, Err(SetActionError::InitiallyIgnored));
1386 assert_eq!(system.0[&signal], Disposition::Catch);
1387 }
1388 for signal in [SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU] {
1389 let origin = Location::dummy("origin");
1390 let result = trap_set.set_action(&mut system, signal, Action::Default, origin, false);
1391 assert_eq!(result, Err(SetActionError::InitiallyIgnored));
1392 assert_eq!(system.0[&signal], Disposition::Ignore);
1393 }
1394 }
1395
1396 #[test]
1397 fn resetting_trap_from_ignore_override_after_enabling_internal_dispositions() {
1398 let mut system = DummySystem::default();
1399 ignore_signals(&mut system);
1400 let mut trap_set = TrapSet::default();
1401 trap_set
1402 .enable_internal_disposition_for_sigchld(&mut system)
1403 .unwrap();
1404 trap_set
1405 .enable_internal_dispositions_for_terminators(&mut system)
1406 .unwrap();
1407 trap_set
1408 .enable_internal_dispositions_for_stoppers(&mut system)
1409 .unwrap();
1410
1411 for signal in [SIGCHLD, SIGINT] {
1412 let origin = Location::dummy("origin");
1413 let result =
1414 trap_set.set_action(&mut system, signal, Action::Ignore, origin.clone(), true);
1415 assert_eq!(result, Ok(()));
1416 assert_eq!(
1417 trap_set.get_state(signal),
1418 (
1419 Some(&TrapState {
1420 action: Action::Ignore,
1421 origin,
1422 pending: false
1423 }),
1424 None
1425 )
1426 );
1427 assert_eq!(system.0[&signal], Disposition::Catch);
1428 }
1429 for signal in [SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU] {
1430 let origin = Location::dummy("origin");
1431 let result =
1432 trap_set.set_action(&mut system, signal, Action::Ignore, origin.clone(), true);
1433 assert_eq!(result, Ok(()));
1434 assert_eq!(
1435 trap_set.get_state(signal),
1436 (
1437 Some(&TrapState {
1438 action: Action::Ignore,
1439 origin,
1440 pending: false
1441 }),
1442 None
1443 )
1444 );
1445 assert_eq!(system.0[&signal], Disposition::Ignore);
1446 }
1447 }
1448
1449 #[test]
1450 fn disabling_internal_disposition_with_ignore_trap() {
1451 let signals = [SIGCHLD, SIGINT, SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU];
1452
1453 let mut system = DummySystem::default();
1454 let mut trap_set = TrapSet::default();
1455 trap_set
1456 .enable_internal_disposition_for_sigchld(&mut system)
1457 .unwrap();
1458 trap_set
1459 .enable_internal_dispositions_for_terminators(&mut system)
1460 .unwrap();
1461 let origin = Location::dummy("origin");
1462 for signal in signals {
1463 trap_set
1464 .set_action(&mut system, signal, Action::Ignore, origin.clone(), false)
1465 .unwrap();
1466 }
1467 trap_set.disable_internal_dispositions(&mut system).unwrap();
1468
1469 for signal in signals {
1470 assert_eq!(
1471 trap_set.get_state(signal),
1472 (
1473 Some(&TrapState {
1474 action: Action::Ignore,
1475 origin: origin.clone(),
1476 pending: false
1477 }),
1478 None
1479 )
1480 );
1481 assert_eq!(system.0[&signal], Disposition::Ignore);
1482 }
1483 }
1484}