1mod cond;
37mod state;
38
39pub use self::cond::Condition;
40pub use self::state::{Action, Origin, SetActionError, TrapState};
41use self::state::{EnterSubshellOption, GrandState};
42use crate::Env;
43use crate::signal;
44use crate::source::Location;
45use crate::system::{Disposition, Errno, Signals};
46use std::collections::BTreeMap;
47use std::collections::btree_map::Entry;
48use std::pin::Pin;
49use std::rc::Rc;
50
51pub trait SignalSystem: Signals {
53 fn get_disposition(&self, signal: signal::Number) -> Result<Disposition, Errno>;
59
60 fn set_disposition(
70 &self,
71 signal: signal::Number,
72 disposition: Disposition,
73 ) -> impl Future<Output = Result<Disposition, Errno>> + use<Self>;
74}
75
76impl<S: SignalSystem> SignalSystem for Rc<S> {
78 #[inline]
79 fn get_disposition(&self, signal: signal::Number) -> Result<Disposition, Errno> {
80 (self as &S).get_disposition(signal)
81 }
82 #[inline]
83 fn set_disposition(
84 &self,
85 signal: signal::Number,
86 disposition: Disposition,
87 ) -> impl Future<Output = Result<Disposition, Errno>> + use<S> {
88 (self as &S).set_disposition(signal, disposition)
89 }
90}
91
92#[must_use]
96pub struct Iter<'a> {
97 inner: std::collections::btree_map::Iter<'a, Condition, GrandState>,
98}
99
100impl<'a> Iterator for Iter<'a> {
101 type Item = (&'a Condition, &'a TrapState, Option<&'a TrapState>);
106
107 fn next(&mut self) -> Option<(&'a Condition, &'a TrapState, Option<&'a TrapState>)> {
108 self.inner
109 .next()
110 .map(|(cond, state)| (cond, state.current_state(), state.parent_state()))
111 }
112}
113
114#[derive(Clone, Debug, Default)]
118pub struct TrapSet {
119 traps: BTreeMap<Condition, GrandState>,
120}
121
122impl TrapSet {
123 pub fn get_state<C: Into<Condition>>(
139 &self,
140 cond: C,
141 ) -> (Option<&TrapState>, Option<&TrapState>) {
142 self.get_state_impl(cond.into())
143 }
144
145 fn get_state_impl(&self, cond: Condition) -> (Option<&TrapState>, Option<&TrapState>) {
146 match self.traps.get(&cond) {
147 None => (None, None),
148 Some(state) => (Some(state.current_state()), state.parent_state()),
149 }
150 }
151
152 pub fn peek_state<S: SignalSystem, C: Into<Condition>>(
170 &mut self,
171 system: &S,
172 cond: C,
173 ) -> Result<&TrapState, Errno> {
174 self.peek_state_impl(system, cond.into())
175 }
176
177 fn peek_state_impl<S: SignalSystem>(
178 &mut self,
179 system: &S,
180 cond: Condition,
181 ) -> Result<&TrapState, Errno> {
182 let entry = self.traps.entry(cond);
183 let state = GrandState::insert_from_system_if_vacant(system, entry)?;
184 Ok(state.parent_state().unwrap_or(state.current_state()))
185 }
186
187 pub async fn set_action<S: SignalSystem, C: Into<Condition>>(
206 &mut self,
207 system: &S,
208 cond: C,
209 action: Action,
210 origin: Location,
211 override_ignore: bool,
212 ) -> Result<(), SetActionError> {
213 self.set_action_impl(system, cond.into(), action, origin, override_ignore)
214 .await
215 }
216
217 async fn set_action_impl<S: SignalSystem>(
218 &mut self,
219 system: &S,
220 cond: Condition,
221 action: Action,
222 origin: Location,
223 override_ignore: bool,
224 ) -> Result<(), SetActionError> {
225 if let Condition::Signal(signal) = cond {
226 if signal == S::SIGKILL {
227 return Err(SetActionError::SIGKILL);
228 }
229 if signal == S::SIGSTOP {
230 return Err(SetActionError::SIGSTOP);
231 }
232 }
233
234 self.clear_parent_states();
235
236 let entry = self.traps.entry(cond);
237 GrandState::set_action(system, entry, action, origin, override_ignore).await
238 }
239
240 fn clear_parent_states(&mut self) {
241 for state in self.traps.values_mut() {
242 state.clear_parent_state();
243 }
244 }
245
246 #[inline]
258 pub fn iter(&self) -> Iter<'_> {
259 let inner = self.traps.iter();
260 Iter { inner }
261 }
262
263 pub async fn enter_subshell<S: SignalSystem>(
299 &mut self,
300 system: &S,
301 ignore_sigint_sigquit: bool,
302 keep_internal_dispositions_for_stoppers: bool,
303 ) {
304 self.clear_parent_states();
305
306 for (&cond, state) in &mut self.traps {
307 let option = match cond {
308 Condition::Exit => EnterSubshellOption::ClearInternalDisposition,
309 Condition::Signal(signal) =>
310 {
311 #[allow(clippy::if_same_then_else, reason = "for readability")]
312 if signal == S::SIGCHLD {
313 EnterSubshellOption::KeepInternalDisposition
314 } else if ignore_sigint_sigquit && (signal == S::SIGINT || signal == S::SIGQUIT)
315 {
316 EnterSubshellOption::Ignore
317 } else if keep_internal_dispositions_for_stoppers
318 && (signal == S::SIGTSTP || signal == S::SIGTTIN || signal == S::SIGTTOU)
319 && state.internal_disposition() != Disposition::Default
320 {
321 EnterSubshellOption::Ignore
322 } else {
323 EnterSubshellOption::ClearInternalDisposition
324 }
325 }
326 };
327 state.enter_subshell(system, cond, option).await.ok();
328 }
329
330 if ignore_sigint_sigquit {
331 for signal in [S::SIGINT, S::SIGQUIT] {
332 match self.traps.entry(Condition::Signal(signal)) {
333 Entry::Vacant(vacant) => {
334 GrandState::ignore(system, vacant).await.unwrap_or_default()
335 }
336 Entry::Occupied(_) => {}
338 }
339 }
340 }
341 }
342
343 pub fn catch_signal(&mut self, signal: signal::Number) {
348 if let Some(state) = self.traps.get_mut(&Condition::Signal(signal)) {
349 state.mark_as_caught();
350 }
351 }
352
353 pub fn take_signal_if_caught(&mut self, signal: signal::Number) -> Option<&TrapState> {
357 self.traps
358 .get_mut(&Condition::Signal(signal))
359 .and_then(|state| state.handle_if_caught())
360 }
361
362 pub fn take_caught_signal(&mut self) -> Option<(signal::Number, &TrapState)> {
370 self.traps.iter_mut().find_map(|(&cond, state)| match cond {
371 Condition::Signal(signal) => state.handle_if_caught().map(|trap| (signal, trap)),
372 _ => None,
373 })
374 }
375
376 async fn set_internal_disposition<S: SignalSystem>(
377 &mut self,
378 signal: signal::Number,
379 disposition: Disposition,
380 system: &S,
381 ) -> Result<(), Errno> {
382 let entry = self.traps.entry(Condition::Signal(signal));
383 GrandState::set_internal_disposition(system, entry, disposition).await
384 }
385
386 pub async fn enable_internal_disposition_for_sigchld<S: SignalSystem>(
397 &mut self,
398 system: &S,
399 ) -> Result<(), Errno> {
400 self.set_internal_disposition(S::SIGCHLD, Disposition::Catch, system)
401 .await
402 }
403
404 pub async fn enable_internal_dispositions_for_terminators<S: SignalSystem>(
413 &mut self,
414 system: &S,
415 ) -> Result<(), Errno> {
416 self.set_internal_disposition(S::SIGINT, Disposition::Catch, system)
417 .await?;
418 self.set_internal_disposition(S::SIGTERM, Disposition::Ignore, system)
419 .await?;
420 self.set_internal_disposition(S::SIGQUIT, Disposition::Ignore, system)
421 .await
422 }
423
424 pub async fn enable_internal_dispositions_for_stoppers<S: SignalSystem>(
433 &mut self,
434 system: &S,
435 ) -> Result<(), Errno> {
436 self.set_internal_disposition(S::SIGTSTP, Disposition::Ignore, system)
437 .await?;
438 self.set_internal_disposition(S::SIGTTIN, Disposition::Ignore, system)
439 .await?;
440 self.set_internal_disposition(S::SIGTTOU, Disposition::Ignore, system)
441 .await
442 }
443
444 pub async fn disable_internal_dispositions_for_terminators<S: SignalSystem>(
446 &mut self,
447 system: &S,
448 ) -> Result<(), Errno> {
449 self.set_internal_disposition(S::SIGINT, Disposition::Default, system)
450 .await?;
451 self.set_internal_disposition(S::SIGTERM, Disposition::Default, system)
452 .await?;
453 self.set_internal_disposition(S::SIGQUIT, Disposition::Default, system)
454 .await
455 }
456
457 pub async fn disable_internal_dispositions_for_stoppers<S: SignalSystem>(
459 &mut self,
460 system: &S,
461 ) -> Result<(), Errno> {
462 self.set_internal_disposition(S::SIGTSTP, Disposition::Default, system)
463 .await?;
464 self.set_internal_disposition(S::SIGTTIN, Disposition::Default, system)
465 .await?;
466 self.set_internal_disposition(S::SIGTTOU, Disposition::Default, system)
467 .await
468 }
469
470 pub async fn disable_internal_dispositions<S: SignalSystem>(
476 &mut self,
477 system: &S,
478 ) -> Result<(), Errno> {
479 self.set_internal_disposition(S::SIGCHLD, Disposition::Default, system)
480 .await?;
481 self.disable_internal_dispositions_for_terminators(system)
482 .await?;
483 self.disable_internal_dispositions_for_stoppers(system)
484 .await
485 }
486}
487
488impl<'a> IntoIterator for &'a TrapSet {
489 type Item = (&'a Condition, &'a TrapState, Option<&'a TrapState>);
494 type IntoIter = Iter<'a>;
495
496 #[inline(always)]
497 fn into_iter(self) -> Iter<'a> {
498 self.iter()
499 }
500}
501
502type PinFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
503
504pub struct RunSignalTrapIfCaught<S>(
531 pub for<'a> fn(
532 env: &'a mut Env<S>,
533 signal: signal::Number,
534 ) -> PinFuture<'a, Option<crate::semantics::Result>>,
535);
536
537impl<S> Clone for RunSignalTrapIfCaught<S> {
539 fn clone(&self) -> Self {
540 *self
541 }
542}
543
544impl<S> Copy for RunSignalTrapIfCaught<S> {}
545
546impl<S> std::fmt::Debug for RunSignalTrapIfCaught<S> {
547 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
548 f.debug_tuple("RunSignalTrapIfCaught")
549 .field(&self.0)
550 .finish()
551 }
552}
553
554#[cfg(test)]
555mod tests {
556 use super::*;
557 use crate::job::ProcessState;
558 use crate::system::SendSignal as _;
559 use crate::system::r#virtual::{
560 SIGABRT, SIGALRM, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGIOT,
561 SIGKILL, SIGPIPE, SIGPROF, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTERM, SIGTRAP, SIGTSTP,
562 SIGTTIN, SIGTTOU, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGWINCH, SIGXCPU, SIGXFSZ,
563 };
564 use crate::test_helper::in_virtual_system;
565 use futures_util::FutureExt as _;
566 use std::cell::RefCell;
567 use std::collections::HashMap;
568 use std::future::ready;
569 use std::ops::RangeInclusive;
570
571 #[derive(Default)]
572 pub struct DummySystem(pub RefCell<HashMap<signal::Number, Disposition>>);
573
574 impl Signals for DummySystem {
575 const SIGABRT: signal::Number = SIGABRT;
576 const SIGALRM: signal::Number = SIGALRM;
577 const SIGBUS: signal::Number = SIGBUS;
578 const SIGCHLD: signal::Number = SIGCHLD;
579 const SIGCLD: Option<signal::Number> = None;
580 const SIGCONT: signal::Number = SIGCONT;
581 const SIGEMT: Option<signal::Number> = None;
582 const SIGFPE: signal::Number = SIGFPE;
583 const SIGHUP: signal::Number = SIGHUP;
584 const SIGILL: signal::Number = SIGILL;
585 const SIGINFO: Option<signal::Number> = None;
586 const SIGINT: signal::Number = SIGINT;
587 const SIGIO: Option<signal::Number> = None;
588 const SIGIOT: signal::Number = SIGIOT;
589 const SIGKILL: signal::Number = SIGKILL;
590 const SIGLOST: Option<signal::Number> = None;
591 const SIGPIPE: signal::Number = SIGPIPE;
592 const SIGPOLL: Option<signal::Number> = None;
593 const SIGPROF: signal::Number = SIGPROF;
594 const SIGPWR: Option<signal::Number> = None;
595 const SIGQUIT: signal::Number = SIGQUIT;
596 const SIGSEGV: signal::Number = SIGSEGV;
597 const SIGSTKFLT: Option<signal::Number> = None;
598 const SIGSTOP: signal::Number = SIGSTOP;
599 const SIGSYS: signal::Number = SIGSYS;
600 const SIGTERM: signal::Number = SIGTERM;
601 const SIGTHR: Option<signal::Number> = None;
602 const SIGTRAP: signal::Number = SIGTRAP;
603 const SIGTSTP: signal::Number = SIGTSTP;
604 const SIGTTIN: signal::Number = SIGTTIN;
605 const SIGTTOU: signal::Number = SIGTTOU;
606 const SIGURG: signal::Number = SIGURG;
607 const SIGUSR1: signal::Number = SIGUSR1;
608 const SIGUSR2: signal::Number = SIGUSR2;
609 const SIGVTALRM: signal::Number = SIGVTALRM;
610 const SIGWINCH: signal::Number = SIGWINCH;
611 const SIGXCPU: signal::Number = SIGXCPU;
612 const SIGXFSZ: signal::Number = SIGXFSZ;
613
614 fn sigrt_range(&self) -> Option<RangeInclusive<signal::Number>> {
615 None
616 }
617 }
618
619 impl SignalSystem for DummySystem {
620 fn get_disposition(&self, signal: signal::Number) -> Result<Disposition, Errno> {
621 Ok(self.0.borrow().get(&signal).copied().unwrap_or_default())
622 }
623
624 fn set_disposition(
625 &self,
626 signal: signal::Number,
627 disposition: Disposition,
628 ) -> impl Future<Output = Result<Disposition, Errno>> + use<> {
629 ready(Ok(self
630 .0
631 .borrow_mut()
632 .insert(signal, disposition)
633 .unwrap_or_default()))
634 }
635 }
636
637 #[test]
638 fn default_trap() {
639 let trap_set = TrapSet::default();
640 assert_eq!(trap_set.get_state(SIGCHLD), (None, None));
641 }
642
643 #[test]
644 fn setting_trap_for_two_signals() {
645 let system = DummySystem::default();
646 let mut trap_set = TrapSet::default();
647 let origin_1 = Location::dummy("foo");
648 let result = trap_set
649 .set_action(&system, SIGUSR1, Action::Ignore, origin_1.clone(), false)
650 .now_or_never()
651 .unwrap();
652 assert_eq!(result, Ok(()));
653
654 let command = Action::Command("echo".into());
655 let origin_2 = Location::dummy("bar");
656 let result = trap_set
657 .set_action(&system, SIGUSR2, command.clone(), origin_2.clone(), false)
658 .now_or_never()
659 .unwrap();
660 assert_eq!(result, Ok(()));
661
662 assert_eq!(
663 trap_set.get_state(SIGUSR1),
664 (
665 Some(&TrapState {
666 action: Action::Ignore,
667 origin: Origin::User(origin_1),
668 pending: false
669 }),
670 None
671 )
672 );
673 assert_eq!(
674 trap_set.get_state(SIGUSR2),
675 (
676 Some(&TrapState {
677 action: command,
678 origin: Origin::User(origin_2),
679 pending: false
680 }),
681 None
682 )
683 );
684 assert_eq!(system.0.borrow()[&SIGUSR1], Disposition::Ignore);
685 assert_eq!(system.0.borrow()[&SIGUSR2], Disposition::Catch);
686 }
687
688 #[test]
689 fn setting_trap_for_sigkill() {
690 let system = DummySystem::default();
691 let mut trap_set = TrapSet::default();
692 let origin = Location::dummy("origin");
693 let result = trap_set
694 .set_action(&system, SIGKILL, Action::Ignore, origin, false)
695 .now_or_never()
696 .unwrap();
697 assert_eq!(result, Err(SetActionError::SIGKILL));
698 assert_eq!(trap_set.get_state(SIGKILL), (None, None));
699 assert_eq!(system.0.borrow().get(&SIGKILL), None);
700 }
701
702 #[test]
703 fn setting_trap_for_sigstop() {
704 let system = DummySystem::default();
705 let mut trap_set = TrapSet::default();
706 let origin = Location::dummy("origin");
707 let result = trap_set
708 .set_action(&system, SIGSTOP, Action::Ignore, origin, false)
709 .now_or_never()
710 .unwrap();
711 assert_eq!(result, Err(SetActionError::SIGSTOP));
712 assert_eq!(trap_set.get_state(SIGSTOP), (None, None));
713 assert_eq!(system.0.borrow().get(&SIGSTOP), None);
714 }
715
716 #[test]
717 fn peeking_state_with_default_inherited_disposition() {
718 let system = DummySystem::default();
719 let mut trap_set = TrapSet::default();
720 let result = trap_set.peek_state(&system, SIGCHLD);
721 assert_eq!(
722 result,
723 Ok(&TrapState {
724 action: Action::Default,
725 origin: Origin::Inherited,
726 pending: false
727 })
728 );
729 }
730
731 #[test]
732 fn peeking_state_with_inherited_disposition_of_ignore() {
733 let system = DummySystem::default();
734 system.0.borrow_mut().insert(SIGCHLD, Disposition::Ignore);
735 let mut trap_set = TrapSet::default();
736 let result = trap_set.peek_state(&system, SIGCHLD);
737 assert_eq!(
738 result,
739 Ok(&TrapState {
740 action: Action::Ignore,
741 origin: Origin::Inherited,
742 pending: false
743 })
744 );
745 }
746
747 #[test]
748 fn peeking_state_with_parent_state() {
749 let system = DummySystem::default();
750 let mut trap_set = TrapSet::default();
751 let origin = Location::dummy("foo");
752 let command = Action::Command("echo".into());
753 trap_set
754 .set_action(&system, SIGUSR1, command.clone(), origin.clone(), false)
755 .now_or_never()
756 .unwrap()
757 .unwrap();
758 trap_set
759 .enter_subshell(&system, false, false)
760 .now_or_never()
761 .unwrap();
762
763 let result = trap_set.peek_state(&system, SIGUSR1);
764 assert_eq!(
765 result,
766 Ok(&TrapState {
767 action: command,
768 origin: Origin::User(origin),
769 pending: false
770 })
771 );
772 }
773
774 #[test]
775 fn basic_iteration() {
776 let system = DummySystem::default();
777 let mut trap_set = TrapSet::default();
778 let origin_1 = Location::dummy("foo");
779 trap_set
780 .set_action(&system, SIGUSR1, Action::Ignore, origin_1.clone(), false)
781 .now_or_never()
782 .unwrap()
783 .unwrap();
784 let command = Action::Command("echo".into());
785 let origin_2 = Location::dummy("bar");
786 trap_set
787 .set_action(&system, SIGUSR2, command.clone(), origin_2.clone(), false)
788 .now_or_never()
789 .unwrap()
790 .unwrap();
791
792 let mut i = trap_set.iter();
793 let first = i.next().unwrap();
794 assert_eq!(first.0, &Condition::Signal(SIGUSR1));
795 assert_eq!(first.1.action, Action::Ignore);
796 assert_eq!(first.1.origin, Origin::User(origin_1));
797 assert_eq!(first.2, None);
798 let second = i.next().unwrap();
799 assert_eq!(second.0, &Condition::Signal(SIGUSR2));
800 assert_eq!(second.1.action, command);
801 assert_eq!(second.1.origin, Origin::User(origin_2));
802 assert_eq!(first.2, None);
803 assert_eq!(i.next(), None);
804 }
805
806 #[test]
807 fn iteration_after_entering_subshell() {
808 let system = DummySystem::default();
809 let mut trap_set = TrapSet::default();
810 let origin_1 = Location::dummy("foo");
811 trap_set
812 .set_action(&system, SIGUSR1, Action::Ignore, origin_1.clone(), false)
813 .now_or_never()
814 .unwrap()
815 .unwrap();
816 let command = Action::Command("echo".into());
817 let origin_2 = Location::dummy("bar");
818 trap_set
819 .set_action(&system, SIGUSR2, command.clone(), origin_2.clone(), false)
820 .now_or_never()
821 .unwrap()
822 .unwrap();
823 trap_set
824 .enter_subshell(&system, false, false)
825 .now_or_never()
826 .unwrap();
827
828 let mut i = trap_set.iter();
829 let first = i.next().unwrap();
830 assert_eq!(first.0, &Condition::Signal(SIGUSR1));
831 assert_eq!(first.1.action, Action::Ignore);
832 assert_eq!(first.1.origin, Origin::User(origin_1));
833 assert_eq!(first.2, None);
834 let second = i.next().unwrap();
835 assert_eq!(second.0, &Condition::Signal(SIGUSR2));
836 assert_eq!(second.1.action, Action::Default);
837 assert_eq!(second.1.origin, Origin::Subshell);
838 assert_eq!(second.2.unwrap().action, command);
839 assert_eq!(second.2.unwrap().origin, Origin::User(origin_2));
840 assert_eq!(i.next(), None);
841 }
842
843 #[test]
844 fn iteration_after_setting_trap_in_subshell() {
845 let system = DummySystem::default();
846 let mut trap_set = TrapSet::default();
847 let origin_1 = Location::dummy("foo");
848 let command = Action::Command("echo".into());
849 trap_set
850 .set_action(&system, SIGUSR1, command, origin_1, false)
851 .now_or_never()
852 .unwrap()
853 .unwrap();
854 trap_set
855 .enter_subshell(&system, false, false)
856 .now_or_never()
857 .unwrap();
858 let origin_2 = Location::dummy("bar");
859 let command = Action::Command("ls".into());
860 trap_set
861 .set_action(&system, SIGUSR2, command.clone(), origin_2.clone(), false)
862 .now_or_never()
863 .unwrap()
864 .unwrap();
865
866 let mut i = trap_set.iter();
867 let first = i.next().unwrap();
868 assert_eq!(first.0, &Condition::Signal(SIGUSR1));
869 assert_eq!(first.1.action, Action::Default);
870 assert_eq!(first.1.origin, Origin::Subshell);
871 assert_eq!(first.2, None);
872 let second = i.next().unwrap();
873 assert_eq!(second.0, &Condition::Signal(SIGUSR2));
874 assert_eq!(second.1.action, command);
875 assert_eq!(second.1.origin, Origin::User(origin_2));
876 assert_eq!(second.2, None);
877 assert_eq!(i.next(), None);
878 }
879
880 #[test]
881 fn entering_subshell_resets_command_traps() {
882 let system = DummySystem::default();
883 let mut trap_set = TrapSet::default();
884 let action = Action::Command("".into());
885 let origin = Location::dummy("origin");
886 trap_set
887 .set_action(&system, SIGCHLD, action.clone(), origin.clone(), false)
888 .now_or_never()
889 .unwrap()
890 .unwrap();
891
892 trap_set
893 .enter_subshell(&system, false, false)
894 .now_or_never()
895 .unwrap();
896 assert_eq!(
897 trap_set.get_state(SIGCHLD),
898 (
899 Some(&TrapState {
900 action: Action::Default,
901 origin: Origin::Subshell,
902 pending: false
903 }),
904 Some(&TrapState {
905 action,
906 origin: Origin::User(origin),
907 pending: false
908 })
909 )
910 );
911 assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Default);
912 }
913
914 #[test]
915 fn entering_subshell_keeps_ignore_traps() {
916 let system = DummySystem::default();
917 let mut trap_set = TrapSet::default();
918 let origin = Location::dummy("origin");
919 trap_set
920 .set_action(&system, SIGCHLD, Action::Ignore, origin.clone(), false)
921 .now_or_never()
922 .unwrap()
923 .unwrap();
924
925 trap_set
926 .enter_subshell(&system, false, false)
927 .now_or_never()
928 .unwrap();
929 assert_eq!(
930 trap_set.get_state(SIGCHLD),
931 (
932 Some(&TrapState {
933 action: Action::Ignore,
934 origin: Origin::User(origin),
935 pending: false
936 }),
937 None
938 )
939 );
940 assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Ignore);
941 }
942
943 #[test]
944 fn entering_subshell_with_internal_disposition_for_sigchld() {
945 let system = DummySystem::default();
946 let mut trap_set = TrapSet::default();
947 let action = Action::Command("".into());
948 let origin = Location::dummy("origin");
949 trap_set
950 .set_action(&system, SIGCHLD, action.clone(), origin.clone(), false)
951 .now_or_never()
952 .unwrap()
953 .unwrap();
954 trap_set
955 .enable_internal_disposition_for_sigchld(&system)
956 .now_or_never()
957 .unwrap()
958 .unwrap();
959
960 trap_set
961 .enter_subshell(&system, false, false)
962 .now_or_never()
963 .unwrap();
964 assert_eq!(
965 trap_set.get_state(SIGCHLD),
966 (
967 Some(&TrapState {
968 action: Action::Default,
969 origin: Origin::Subshell,
970 pending: false
971 }),
972 Some(&TrapState {
973 action,
974 origin: Origin::User(origin),
975 pending: false
976 })
977 )
978 );
979 assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Catch);
980 }
981
982 #[test]
983 fn entering_subshell_with_internal_disposition_for_sigint() {
984 let system = DummySystem::default();
985 let mut trap_set = TrapSet::default();
986 let action = Action::Command("".into());
987 let origin = Location::dummy("origin");
988 trap_set
989 .set_action(&system, SIGINT, action.clone(), origin.clone(), false)
990 .now_or_never()
991 .unwrap()
992 .unwrap();
993 trap_set
994 .enable_internal_dispositions_for_terminators(&system)
995 .now_or_never()
996 .unwrap()
997 .unwrap();
998
999 trap_set
1000 .enter_subshell(&system, false, false)
1001 .now_or_never()
1002 .unwrap();
1003 assert_eq!(
1004 trap_set.get_state(SIGINT),
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.borrow()[&SIGINT], Disposition::Default);
1019 }
1020
1021 #[test]
1022 fn entering_subshell_with_internal_disposition_for_sigterm() {
1023 let 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(&system, SIGTERM, action.clone(), origin.clone(), false)
1029 .now_or_never()
1030 .unwrap()
1031 .unwrap();
1032 trap_set
1033 .enable_internal_dispositions_for_terminators(&system)
1034 .now_or_never()
1035 .unwrap()
1036 .unwrap();
1037
1038 trap_set
1039 .enter_subshell(&system, false, false)
1040 .now_or_never()
1041 .unwrap();
1042 assert_eq!(
1043 trap_set.get_state(SIGTERM),
1044 (
1045 Some(&TrapState {
1046 action: Action::Default,
1047 origin: Origin::Subshell,
1048 pending: false
1049 }),
1050 Some(&TrapState {
1051 action,
1052 origin: Origin::User(origin),
1053 pending: false
1054 })
1055 )
1056 );
1057 assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Default);
1058 }
1059
1060 #[test]
1061 fn entering_subshell_with_internal_disposition_for_sigquit() {
1062 let system = DummySystem::default();
1063 let mut trap_set = TrapSet::default();
1064 let action = Action::Command("".into());
1065 let origin = Location::dummy("origin");
1066 trap_set
1067 .set_action(&system, SIGQUIT, action.clone(), origin.clone(), false)
1068 .now_or_never()
1069 .unwrap()
1070 .unwrap();
1071 trap_set
1072 .enable_internal_dispositions_for_terminators(&system)
1073 .now_or_never()
1074 .unwrap()
1075 .unwrap();
1076
1077 trap_set
1078 .enter_subshell(&system, false, false)
1079 .now_or_never()
1080 .unwrap();
1081 assert_eq!(
1082 trap_set.get_state(SIGQUIT),
1083 (
1084 Some(&TrapState {
1085 action: Action::Default,
1086 origin: Origin::Subshell,
1087 pending: false
1088 }),
1089 Some(&TrapState {
1090 action,
1091 origin: Origin::User(origin),
1092 pending: false
1093 })
1094 )
1095 );
1096 assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Default);
1097 }
1098
1099 #[test]
1100 fn entering_subshell_with_internal_disposition_for_sigtstp() {
1101 let system = DummySystem::default();
1102 let mut trap_set = TrapSet::default();
1103 let action = Action::Command("".into());
1104 let origin = Location::dummy("origin");
1105 trap_set
1106 .set_action(&system, SIGTSTP, action.clone(), origin.clone(), false)
1107 .now_or_never()
1108 .unwrap()
1109 .unwrap();
1110 trap_set
1111 .enable_internal_dispositions_for_stoppers(&system)
1112 .now_or_never()
1113 .unwrap()
1114 .unwrap();
1115
1116 trap_set
1117 .enter_subshell(&system, false, false)
1118 .now_or_never()
1119 .unwrap();
1120 assert_eq!(
1121 trap_set.get_state(SIGTSTP),
1122 (
1123 Some(&TrapState {
1124 action: Action::Default,
1125 origin: Origin::Subshell,
1126 pending: false
1127 }),
1128 Some(&TrapState {
1129 action,
1130 origin: Origin::User(origin),
1131 pending: false
1132 })
1133 )
1134 );
1135 assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Default);
1136 }
1137
1138 #[test]
1139 fn entering_subshell_with_internal_disposition_for_sigttin() {
1140 let system = DummySystem::default();
1141 let mut trap_set = TrapSet::default();
1142 let action = Action::Command("".into());
1143 let origin = Location::dummy("origin");
1144 trap_set
1145 .set_action(&system, SIGTTIN, action.clone(), origin.clone(), false)
1146 .now_or_never()
1147 .unwrap()
1148 .unwrap();
1149 trap_set
1150 .enable_internal_dispositions_for_stoppers(&system)
1151 .now_or_never()
1152 .unwrap()
1153 .unwrap();
1154
1155 trap_set
1156 .enter_subshell(&system, false, false)
1157 .now_or_never()
1158 .unwrap();
1159 assert_eq!(
1160 trap_set.get_state(SIGTTIN),
1161 (
1162 Some(&TrapState {
1163 action: Action::Default,
1164 origin: Origin::Subshell,
1165 pending: false
1166 }),
1167 Some(&TrapState {
1168 action,
1169 origin: Origin::User(origin),
1170 pending: false
1171 })
1172 )
1173 );
1174 assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Default);
1175 }
1176
1177 #[test]
1178 fn entering_subshell_with_internal_disposition_for_sigttou() {
1179 let system = DummySystem::default();
1180 let mut trap_set = TrapSet::default();
1181 let action = Action::Command("".into());
1182 let origin = Location::dummy("origin");
1183 trap_set
1184 .set_action(&system, SIGTTOU, action.clone(), origin.clone(), false)
1185 .now_or_never()
1186 .unwrap()
1187 .unwrap();
1188 trap_set
1189 .enable_internal_dispositions_for_stoppers(&system)
1190 .now_or_never()
1191 .unwrap()
1192 .unwrap();
1193
1194 trap_set
1195 .enter_subshell(&system, false, false)
1196 .now_or_never()
1197 .unwrap();
1198 assert_eq!(
1199 trap_set.get_state(SIGTTOU),
1200 (
1201 Some(&TrapState {
1202 action: Action::Default,
1203 origin: Origin::Subshell,
1204 pending: false
1205 }),
1206 Some(&TrapState {
1207 action,
1208 origin: Origin::User(origin),
1209 pending: false
1210 })
1211 )
1212 );
1213 assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Default);
1214 }
1215
1216 #[test]
1217 fn setting_trap_after_entering_subshell_clears_parent_states() {
1218 let system = DummySystem::default();
1219 let mut trap_set = TrapSet::default();
1220 let origin_1 = Location::dummy("foo");
1221 let command = Action::Command("echo 1".into());
1222 trap_set
1223 .set_action(&system, SIGUSR1, command, origin_1, false)
1224 .now_or_never()
1225 .unwrap()
1226 .unwrap();
1227 let origin_2 = Location::dummy("bar");
1228 let command = Action::Command("echo 2".into());
1229 trap_set
1230 .set_action(&system, SIGUSR2, command, origin_2, false)
1231 .now_or_never()
1232 .unwrap()
1233 .unwrap();
1234 trap_set
1235 .enter_subshell(&system, false, false)
1236 .now_or_never()
1237 .unwrap();
1238
1239 let command = Action::Command("echo 9".into());
1240 let origin_3 = Location::dummy("qux");
1241 trap_set
1242 .set_action(&system, SIGUSR1, command.clone(), origin_3.clone(), false)
1243 .now_or_never()
1244 .unwrap()
1245 .unwrap();
1246
1247 assert_eq!(
1248 trap_set.get_state(SIGUSR1),
1249 (
1250 Some(&TrapState {
1251 action: command,
1252 origin: Origin::User(origin_3),
1253 pending: false
1254 }),
1255 None
1256 )
1257 );
1258 assert_eq!(
1259 trap_set.get_state(SIGUSR2),
1260 (
1261 Some(&TrapState {
1262 action: Action::Default,
1263 origin: Origin::Subshell,
1264 pending: false
1265 }),
1266 None
1267 )
1268 );
1269 assert_eq!(system.0.borrow()[&SIGUSR1], Disposition::Catch);
1270 assert_eq!(system.0.borrow()[&SIGUSR2], Disposition::Default);
1271 }
1272
1273 #[test]
1274 fn entering_nested_subshell_clears_parent_states() {
1275 let system = DummySystem::default();
1276 let mut trap_set = TrapSet::default();
1277 let origin_1 = Location::dummy("foo");
1278 let command = Action::Command("echo 1".into());
1279 trap_set
1280 .set_action(&system, SIGUSR1, command, origin_1, false)
1281 .now_or_never()
1282 .unwrap()
1283 .unwrap();
1284 let origin_2 = Location::dummy("bar");
1285 let command = Action::Command("echo 2".into());
1286 trap_set
1287 .set_action(&system, SIGUSR2, command, origin_2, false)
1288 .now_or_never()
1289 .unwrap()
1290 .unwrap();
1291 trap_set
1292 .enter_subshell(&system, false, false)
1293 .now_or_never()
1294 .unwrap();
1295 trap_set
1296 .enter_subshell(&system, false, false)
1297 .now_or_never()
1298 .unwrap();
1299
1300 assert_eq!(
1301 trap_set.get_state(SIGUSR1),
1302 (
1303 Some(&TrapState {
1304 action: Action::Default,
1305 origin: Origin::Subshell,
1306 pending: false
1307 }),
1308 None
1309 )
1310 );
1311 assert_eq!(
1312 trap_set.get_state(SIGUSR2),
1313 (
1314 Some(&TrapState {
1315 action: Action::Default,
1316 origin: Origin::Subshell,
1317 pending: false
1318 }),
1319 None
1320 )
1321 );
1322 assert_eq!(system.0.borrow()[&SIGUSR1], Disposition::Default);
1323 assert_eq!(system.0.borrow()[&SIGUSR2], Disposition::Default);
1324 }
1325
1326 #[test]
1327 fn ignoring_sigint_on_entering_subshell_with_action_set() {
1328 in_virtual_system(|mut env, state| async move {
1329 env.traps
1330 .set_action(
1331 &env.system,
1332 SIGINT,
1333 Action::Command("".into()),
1334 Location::dummy(""),
1335 false,
1336 )
1337 .await
1338 .unwrap();
1339 env.system.kill(env.main_pid, Some(SIGINT)).await.unwrap();
1340 env.traps.enter_subshell(&env.system, true, false).await;
1341
1342 let state = state.borrow();
1343 let process = &state.processes[&env.main_pid];
1344 assert_eq!(process.disposition(SIGINT), Disposition::Ignore);
1345 assert_eq!(process.state(), ProcessState::Running);
1346 })
1347 }
1348
1349 #[test]
1350 fn ignoring_sigquit_on_entering_subshell_with_action_set() {
1351 in_virtual_system(|mut env, state| async move {
1352 env.traps
1353 .set_action(
1354 &env.system,
1355 SIGQUIT,
1356 Action::Command("".into()),
1357 Location::dummy(""),
1358 false,
1359 )
1360 .await
1361 .unwrap();
1362 env.system.kill(env.main_pid, Some(SIGQUIT)).await.unwrap();
1363 env.traps.enter_subshell(&env.system, true, false).await;
1364
1365 let state = state.borrow();
1366 let process = &state.processes[&env.main_pid];
1367 assert_eq!(process.disposition(SIGQUIT), Disposition::Ignore);
1368 assert_eq!(process.state(), ProcessState::Running);
1369 })
1370 }
1371
1372 #[test]
1373 fn ignoring_sigint_and_sigquit_on_entering_subshell_without_action_set() {
1374 let system = DummySystem::default();
1375 let mut trap_set = TrapSet::default();
1376 trap_set
1377 .enter_subshell(&system, true, false)
1378 .now_or_never()
1379 .unwrap();
1380 assert_eq!(system.0.borrow()[&SIGINT], Disposition::Ignore);
1381 assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Ignore);
1382 }
1383
1384 #[test]
1385 fn keeping_stopper_internal_dispositions_ignored() {
1386 in_virtual_system(|mut env, state| async move {
1387 for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
1388 env.traps
1389 .set_action(
1390 &env.system,
1391 signal,
1392 Action::Command("".into()),
1393 Location::dummy(""),
1394 false,
1395 )
1396 .await
1397 .unwrap();
1398 }
1399 env.traps
1400 .enable_internal_dispositions_for_stoppers(&env.system)
1401 .await
1402 .unwrap();
1403 for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
1404 env.system.kill(env.main_pid, Some(signal)).await.unwrap();
1405 }
1406 env.traps.enter_subshell(&env.system, false, true).await;
1407
1408 let state = state.borrow();
1409 let process = &state.processes[&env.main_pid];
1410 assert_eq!(process.disposition(SIGTSTP), Disposition::Ignore);
1411 assert_eq!(process.disposition(SIGTTIN), Disposition::Ignore);
1412 assert_eq!(process.disposition(SIGTTOU), Disposition::Ignore);
1413 assert_eq!(process.state(), ProcessState::Running);
1414 })
1415 }
1416
1417 #[test]
1418 fn no_stopper_internal_dispositions_enabled_to_be_kept_ignored() {
1419 in_virtual_system(|mut env, state| async move {
1420 for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
1421 env.traps
1422 .set_action(
1423 &env.system,
1424 signal,
1425 Action::Command("".into()),
1426 Location::dummy(""),
1427 false,
1428 )
1429 .await
1430 .unwrap();
1431 }
1432 env.traps.enter_subshell(&env.system, false, true).await;
1433
1434 let state = state.borrow();
1435 let process = &state.processes[&env.main_pid];
1436 assert_eq!(process.disposition(SIGTSTP), Disposition::Default);
1437 assert_eq!(process.disposition(SIGTTIN), Disposition::Default);
1438 assert_eq!(process.disposition(SIGTTOU), Disposition::Default);
1439 })
1440 }
1441
1442 #[test]
1443 fn catching_signal() {
1444 let system = DummySystem::default();
1445 let mut trap_set = TrapSet::default();
1446 let command = Action::Command("echo INT".into());
1447 let origin = Location::dummy("origin");
1448 trap_set
1449 .set_action(&system, SIGINT, command, origin, false)
1450 .now_or_never()
1451 .unwrap()
1452 .unwrap();
1453 let command = Action::Command("echo TERM".into());
1454 let origin = Location::dummy("origin");
1455 trap_set
1456 .set_action(&system, SIGTERM, command, origin, false)
1457 .now_or_never()
1458 .unwrap()
1459 .unwrap();
1460
1461 trap_set.catch_signal(SIGCHLD);
1462 trap_set.catch_signal(SIGINT);
1463
1464 let trap_state = trap_set.get_state(SIGINT).0.unwrap();
1465 assert!(trap_state.pending, "trap_state = {trap_state:?}");
1466 let trap_state = trap_set.get_state(SIGTERM).0.unwrap();
1467 assert!(!trap_state.pending, "trap_state = {trap_state:?}");
1468 }
1469
1470 #[test]
1471 fn taking_signal_if_caught() {
1472 let system = DummySystem::default();
1473 let mut trap_set = TrapSet::default();
1474 let command = Action::Command("echo INT".into());
1475 let origin = Location::dummy("origin");
1476 trap_set
1477 .set_action(&system, SIGINT, command, origin, false)
1478 .now_or_never()
1479 .unwrap()
1480 .unwrap();
1481
1482 let result = trap_set.take_signal_if_caught(SIGINT);
1483 assert_eq!(result, None);
1484
1485 trap_set.catch_signal(SIGINT);
1486
1487 let result = trap_set.take_signal_if_caught(SIGINT);
1488 assert!(!result.unwrap().pending);
1489
1490 let result = trap_set.take_signal_if_caught(SIGINT);
1491 assert_eq!(result, None);
1492 }
1493
1494 #[test]
1495 fn taking_caught_signal() {
1496 let system = DummySystem::default();
1497 let mut trap_set = TrapSet::default();
1498 assert_eq!(trap_set.take_caught_signal(), None);
1499
1500 let command = Action::Command("echo INT".into());
1501 let origin = Location::dummy("origin");
1502 trap_set
1503 .set_action(&system, SIGINT, command, origin, false)
1504 .now_or_never()
1505 .unwrap()
1506 .unwrap();
1507 let command = Action::Command("echo TERM".into());
1508 let origin = Location::dummy("origin");
1509 trap_set
1510 .set_action(&system, SIGTERM, command, origin, false)
1511 .now_or_never()
1512 .unwrap()
1513 .unwrap();
1514 let command = Action::Command("echo USR1".into());
1515 let origin = Location::dummy("origin");
1516 trap_set
1517 .set_action(&system, SIGUSR1, command, origin, false)
1518 .now_or_never()
1519 .unwrap()
1520 .unwrap();
1521 assert_eq!(trap_set.take_caught_signal(), None);
1522
1523 trap_set.catch_signal(SIGINT);
1524 trap_set.catch_signal(SIGUSR1);
1525 let result = trap_set.take_caught_signal().unwrap();
1528 match result.0 {
1529 SIGINT => {
1530 assert_eq!(result.1.action, Action::Command("echo INT".into()));
1531 assert!(!result.1.pending);
1532
1533 let result = trap_set.take_caught_signal().unwrap();
1534 assert_eq!(result.0, SIGUSR1);
1535 assert_eq!(result.1.action, Action::Command("echo USR1".into()));
1536 assert!(!result.1.pending);
1537 }
1538 SIGUSR1 => {
1539 assert_eq!(result.1.action, Action::Command("echo USR1".into()));
1540 assert!(!result.1.pending);
1541
1542 let result = trap_set.take_caught_signal().unwrap();
1543 assert_eq!(result.0, SIGINT);
1544 assert_eq!(result.1.action, Action::Command("echo INT".into()));
1545 assert!(!result.1.pending);
1546 }
1547 _ => panic!("wrong signal: {result:?}"),
1548 }
1549
1550 assert_eq!(trap_set.take_caught_signal(), None);
1551 }
1552
1553 #[test]
1554 fn enabling_internal_disposition_for_sigchld() {
1555 let system = DummySystem::default();
1556 let mut trap_set = TrapSet::default();
1557 trap_set
1558 .enable_internal_disposition_for_sigchld(&system)
1559 .now_or_never()
1560 .unwrap()
1561 .unwrap();
1562 assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Catch);
1563 }
1564
1565 #[test]
1566 fn enabling_internal_dispositions_for_terminators() {
1567 let system = DummySystem::default();
1568 let mut trap_set = TrapSet::default();
1569 trap_set
1570 .enable_internal_dispositions_for_terminators(&system)
1571 .now_or_never()
1572 .unwrap()
1573 .unwrap();
1574 assert_eq!(system.0.borrow()[&SIGINT], Disposition::Catch);
1575 assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Ignore);
1576 assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Ignore);
1577 }
1578
1579 #[test]
1580 fn enabling_internal_dispositions_for_stoppers() {
1581 let system = DummySystem::default();
1582 let mut trap_set = TrapSet::default();
1583 trap_set
1584 .enable_internal_dispositions_for_stoppers(&system)
1585 .now_or_never()
1586 .unwrap()
1587 .unwrap();
1588 assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Ignore);
1589 assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Ignore);
1590 assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Ignore);
1591 }
1592
1593 #[test]
1594 fn disabling_internal_dispositions_for_initially_defaulted_signals() {
1595 let system = DummySystem::default();
1596 let mut trap_set = TrapSet::default();
1597 trap_set
1598 .enable_internal_disposition_for_sigchld(&system)
1599 .now_or_never()
1600 .unwrap()
1601 .unwrap();
1602 trap_set
1603 .enable_internal_dispositions_for_terminators(&system)
1604 .now_or_never()
1605 .unwrap()
1606 .unwrap();
1607 trap_set
1608 .enable_internal_dispositions_for_stoppers(&system)
1609 .now_or_never()
1610 .unwrap()
1611 .unwrap();
1612 trap_set
1613 .disable_internal_dispositions(&system)
1614 .now_or_never()
1615 .unwrap()
1616 .unwrap();
1617 assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Default);
1618 assert_eq!(system.0.borrow()[&SIGINT], Disposition::Default);
1619 assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Default);
1620 assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Default);
1621 assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Default);
1622 assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Default);
1623 assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Default);
1624 }
1625
1626 fn ignore_signals(system: &mut DummySystem) {
1627 system.0.borrow_mut().extend(
1628 [SIGCHLD, SIGINT, SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU]
1629 .into_iter()
1630 .map(|signal| (signal, Disposition::Ignore)),
1631 )
1632 }
1633
1634 #[test]
1635 fn disabling_internal_dispositions_for_initially_ignored_signals() {
1636 let mut system = DummySystem::default();
1637 ignore_signals(&mut system);
1638 let mut trap_set = TrapSet::default();
1639 trap_set
1640 .enable_internal_disposition_for_sigchld(&system)
1641 .now_or_never()
1642 .unwrap()
1643 .unwrap();
1644 trap_set
1645 .enable_internal_dispositions_for_terminators(&system)
1646 .now_or_never()
1647 .unwrap()
1648 .unwrap();
1649 trap_set
1650 .enable_internal_dispositions_for_stoppers(&system)
1651 .now_or_never()
1652 .unwrap()
1653 .unwrap();
1654 trap_set
1655 .disable_internal_dispositions(&system)
1656 .now_or_never()
1657 .unwrap()
1658 .unwrap();
1659 assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Ignore);
1660 assert_eq!(system.0.borrow()[&SIGINT], Disposition::Ignore);
1661 assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Ignore);
1662 assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Ignore);
1663 assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Ignore);
1664 assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Ignore);
1665 assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Ignore);
1666 }
1667
1668 #[test]
1669 fn disabling_internal_dispositions_after_enabling_twice() {
1670 let mut system = DummySystem::default();
1671 ignore_signals(&mut system);
1672 let mut trap_set = TrapSet::default();
1673 trap_set
1674 .enable_internal_disposition_for_sigchld(&system)
1675 .now_or_never()
1676 .unwrap()
1677 .unwrap();
1678 trap_set
1679 .enable_internal_disposition_for_sigchld(&system)
1680 .now_or_never()
1681 .unwrap()
1682 .unwrap();
1683 trap_set
1684 .enable_internal_dispositions_for_terminators(&system)
1685 .now_or_never()
1686 .unwrap()
1687 .unwrap();
1688 trap_set
1689 .enable_internal_dispositions_for_terminators(&system)
1690 .now_or_never()
1691 .unwrap()
1692 .unwrap();
1693 trap_set
1694 .enable_internal_dispositions_for_stoppers(&system)
1695 .now_or_never()
1696 .unwrap()
1697 .unwrap();
1698 trap_set
1699 .enable_internal_dispositions_for_stoppers(&system)
1700 .now_or_never()
1701 .unwrap()
1702 .unwrap();
1703 trap_set
1704 .disable_internal_dispositions(&system)
1705 .now_or_never()
1706 .unwrap()
1707 .unwrap();
1708 assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Ignore);
1709 assert_eq!(system.0.borrow()[&SIGINT], Disposition::Ignore);
1710 assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Ignore);
1711 assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Ignore);
1712 assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Ignore);
1713 assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Ignore);
1714 assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Ignore);
1715 }
1716
1717 #[test]
1718 fn disabling_internal_dispositions_without_enabling() {
1719 let mut system = DummySystem::default();
1720 ignore_signals(&mut system);
1721 let mut trap_set = TrapSet::default();
1722 trap_set
1723 .disable_internal_dispositions(&system)
1724 .now_or_never()
1725 .unwrap()
1726 .unwrap();
1727 assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Ignore);
1728 assert_eq!(system.0.borrow()[&SIGINT], Disposition::Ignore);
1729 assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Ignore);
1730 assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Ignore);
1731 assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Ignore);
1732 assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Ignore);
1733 assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Ignore);
1734 }
1735
1736 #[test]
1737 fn reenabling_internal_dispositions() {
1738 let system = DummySystem::default();
1739 let mut trap_set = TrapSet::default();
1740 trap_set
1741 .enable_internal_disposition_for_sigchld(&system)
1742 .now_or_never()
1743 .unwrap()
1744 .unwrap();
1745 trap_set
1746 .enable_internal_disposition_for_sigchld(&system)
1747 .now_or_never()
1748 .unwrap()
1749 .unwrap();
1750 trap_set
1751 .enable_internal_dispositions_for_terminators(&system)
1752 .now_or_never()
1753 .unwrap()
1754 .unwrap();
1755 trap_set
1756 .enable_internal_dispositions_for_terminators(&system)
1757 .now_or_never()
1758 .unwrap()
1759 .unwrap();
1760 trap_set
1761 .enable_internal_dispositions_for_stoppers(&system)
1762 .now_or_never()
1763 .unwrap()
1764 .unwrap();
1765 trap_set
1766 .enable_internal_dispositions_for_stoppers(&system)
1767 .now_or_never()
1768 .unwrap()
1769 .unwrap();
1770 trap_set
1771 .disable_internal_dispositions(&system)
1772 .now_or_never()
1773 .unwrap()
1774 .unwrap();
1775 trap_set
1776 .enable_internal_disposition_for_sigchld(&system)
1777 .now_or_never()
1778 .unwrap()
1779 .unwrap();
1780 trap_set
1781 .enable_internal_dispositions_for_terminators(&system)
1782 .now_or_never()
1783 .unwrap()
1784 .unwrap();
1785 trap_set
1786 .enable_internal_dispositions_for_stoppers(&system)
1787 .now_or_never()
1788 .unwrap()
1789 .unwrap();
1790 assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Catch);
1791 assert_eq!(system.0.borrow()[&SIGINT], Disposition::Catch);
1792 assert_eq!(system.0.borrow()[&SIGTERM], Disposition::Ignore);
1793 assert_eq!(system.0.borrow()[&SIGQUIT], Disposition::Ignore);
1794 assert_eq!(system.0.borrow()[&SIGTSTP], Disposition::Ignore);
1795 assert_eq!(system.0.borrow()[&SIGTTIN], Disposition::Ignore);
1796 assert_eq!(system.0.borrow()[&SIGTTOU], Disposition::Ignore);
1797 }
1798
1799 #[test]
1800 fn setting_trap_to_ignore_after_enabling_internal_disposition() {
1801 let system = DummySystem::default();
1802 let mut trap_set = TrapSet::default();
1803 trap_set
1804 .enable_internal_disposition_for_sigchld(&system)
1805 .now_or_never()
1806 .unwrap()
1807 .unwrap();
1808 let origin = Location::dummy("origin");
1809 let result = trap_set
1810 .set_action(&system, SIGCHLD, Action::Ignore, origin, false)
1811 .now_or_never()
1812 .unwrap();
1813 assert_eq!(result, Ok(()));
1814 assert_eq!(system.0.borrow()[&SIGCHLD], Disposition::Catch);
1815 }
1816
1817 #[test]
1818 fn resetting_trap_from_ignore_no_override_after_enabling_internal_dispositions() {
1819 let mut system = DummySystem::default();
1820 ignore_signals(&mut system);
1821 let mut trap_set = TrapSet::default();
1822 trap_set
1823 .enable_internal_disposition_for_sigchld(&system)
1824 .now_or_never()
1825 .unwrap()
1826 .unwrap();
1827 trap_set
1828 .enable_internal_dispositions_for_terminators(&system)
1829 .now_or_never()
1830 .unwrap()
1831 .unwrap();
1832 trap_set
1833 .enable_internal_dispositions_for_stoppers(&system)
1834 .now_or_never()
1835 .unwrap()
1836 .unwrap();
1837
1838 for signal in [SIGCHLD, SIGINT] {
1839 let origin = Location::dummy("origin");
1840 let result = trap_set
1841 .set_action(&system, signal, Action::Default, origin, false)
1842 .now_or_never()
1843 .unwrap();
1844 assert_eq!(result, Err(SetActionError::InitiallyIgnored));
1845 assert_eq!(system.0.borrow()[&signal], Disposition::Catch);
1846 }
1847 for signal in [SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU] {
1848 let origin = Location::dummy("origin");
1849 let result = trap_set
1850 .set_action(&system, signal, Action::Default, origin, false)
1851 .now_or_never()
1852 .unwrap();
1853 assert_eq!(result, Err(SetActionError::InitiallyIgnored));
1854 assert_eq!(system.0.borrow()[&signal], Disposition::Ignore);
1855 }
1856 }
1857
1858 #[test]
1859 fn resetting_trap_from_ignore_override_after_enabling_internal_dispositions() {
1860 let mut system = DummySystem::default();
1861 ignore_signals(&mut system);
1862 let mut trap_set = TrapSet::default();
1863 trap_set
1864 .enable_internal_disposition_for_sigchld(&system)
1865 .now_or_never()
1866 .unwrap()
1867 .unwrap();
1868 trap_set
1869 .enable_internal_dispositions_for_terminators(&system)
1870 .now_or_never()
1871 .unwrap()
1872 .unwrap();
1873 trap_set
1874 .enable_internal_dispositions_for_stoppers(&system)
1875 .now_or_never()
1876 .unwrap()
1877 .unwrap();
1878
1879 for signal in [SIGCHLD, SIGINT] {
1880 let origin = Location::dummy("origin");
1881 let result = trap_set
1882 .set_action(&system, signal, Action::Ignore, origin.clone(), true)
1883 .now_or_never()
1884 .unwrap();
1885 assert_eq!(result, Ok(()));
1886 assert_eq!(
1887 trap_set.get_state(signal),
1888 (
1889 Some(&TrapState {
1890 action: Action::Ignore,
1891 origin: Origin::User(origin),
1892 pending: false
1893 }),
1894 None
1895 )
1896 );
1897 assert_eq!(system.0.borrow()[&signal], Disposition::Catch);
1898 }
1899 for signal in [SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU] {
1900 let origin = Location::dummy("origin");
1901 let result = trap_set
1902 .set_action(&system, signal, Action::Ignore, origin.clone(), true)
1903 .now_or_never()
1904 .unwrap();
1905 assert_eq!(result, Ok(()));
1906 assert_eq!(
1907 trap_set.get_state(signal),
1908 (
1909 Some(&TrapState {
1910 action: Action::Ignore,
1911 origin: Origin::User(origin),
1912 pending: false
1913 }),
1914 None
1915 )
1916 );
1917 assert_eq!(system.0.borrow()[&signal], Disposition::Ignore);
1918 }
1919 }
1920
1921 #[test]
1922 fn disabling_internal_disposition_with_ignore_trap() {
1923 let signals = [SIGCHLD, SIGINT, SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU];
1924
1925 let system = DummySystem::default();
1926 let mut trap_set = TrapSet::default();
1927 trap_set
1928 .enable_internal_disposition_for_sigchld(&system)
1929 .now_or_never()
1930 .unwrap()
1931 .unwrap();
1932 trap_set
1933 .enable_internal_dispositions_for_terminators(&system)
1934 .now_or_never()
1935 .unwrap()
1936 .unwrap();
1937 let origin = Location::dummy("origin");
1938 for signal in signals {
1939 trap_set
1940 .set_action(&system, signal, Action::Ignore, origin.clone(), false)
1941 .now_or_never()
1942 .unwrap()
1943 .unwrap();
1944 }
1945 trap_set
1946 .disable_internal_dispositions(&system)
1947 .now_or_never()
1948 .unwrap()
1949 .unwrap();
1950
1951 for signal in signals {
1952 assert_eq!(
1953 trap_set.get_state(signal),
1954 (
1955 Some(&TrapState {
1956 action: Action::Ignore,
1957 origin: Origin::User(origin.clone()),
1958 pending: false
1959 }),
1960 None
1961 )
1962 );
1963 assert_eq!(system.0.borrow()[&signal], Disposition::Ignore);
1964 }
1965 }
1966}