yash_env/
trap.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2021 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Signal and other event handling settings.
18//!
19//! The trap is a mechanism of the shell that allows you to configure event
20//! handlers for specific situations. A [`TrapSet`] is a mapping from [`Condition`]s to
21//! [`Action`]s. When the mapping is modified, it updates the corresponding signal
22//! disposition in the underlying system through a [`SignalSystem`] implementor.
23//! Methods of `TrapSet` expect they are passed the same system instance in
24//! every call to keep it in a correct state.
25//!
26//! `TrapSet` manages two types of signal handling configurations. One is
27//! user-defined traps, which the user explicitly configures with the trap
28//! built-in. The other is internal dispositions, which the shell implicitly
29//! installs to the system to implement additional actions it needs to perform.
30//! `TrapSet` merges the two configurations into a single [`Disposition`] for
31//! each signal and sets it to the system.
32//!
33//! No signal disposition is involved for conditions other than signals, and the
34//! trap set serves only as a storage for action settings.
35
36mod 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
50/// System interface for signal handling configuration
51pub trait SignalSystem {
52    /// Returns the name of a signal from its number.
53    #[must_use]
54    fn signal_name_from_number(&self, number: signal::Number) -> signal::Name;
55
56    /// Returns the signal number from its name.
57    ///
58    /// This function returns the signal number corresponding to the signal name
59    /// in the system. If the signal name is not supported, it returns `None`.
60    ///
61    /// Note that the `TrapSet` implementation assumes that the system supports
62    /// all the following signals:
63    ///
64    /// - `SIGCHLD`
65    /// - `SIGINT`
66    /// - `SIGTERM`
67    /// - `SIGQUIT`
68    /// - `SIGTSTP`
69    /// - `SIGTTIN`
70    /// - `SIGTTOU`
71    ///
72    /// If this method returns `None` for any of these signals, `TrapSet` will
73    /// panic.
74    #[must_use]
75    fn signal_number_from_name(&self, name: signal::Name) -> Option<signal::Number>;
76
77    /// Sets how a signal is handled.
78    ///
79    /// This function updates the signal blocking mask and the disposition for
80    /// the specified signal, and returns the previous disposition.
81    fn set_disposition(
82        &mut self,
83        signal: signal::Number,
84        disposition: Disposition,
85    ) -> Result<Disposition, Errno>;
86}
87
88/// Iterator of trap actions configured in a [trap set](TrapSet).
89///
90/// [`TrapSet::iter`] returns this type of iterator.
91#[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/// Collection of event handling settings.
111///
112/// See the [module documentation](self) for details.
113#[derive(Clone, Debug, Default)]
114pub struct TrapSet {
115    traps: BTreeMap<Condition, GrandState>,
116}
117
118impl TrapSet {
119    /// Returns the current state for a condition.
120    ///
121    /// This function returns a pair of optional trap states. The first is the
122    /// currently configured trap action, and the second is the action set
123    /// before [`enter_subshell`](Self::enter_subshell) was called.
124    ///
125    /// This function does not reflect the initial signal actions the shell
126    /// inherited on startup.
127    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    /// Sets a trap action for a condition.
142    ///
143    /// If the condition is a signal, this function installs a signal handler to
144    /// the specified underlying system.
145    ///
146    /// If `override_ignore` is `false`, you cannot set a trap for a signal that
147    /// has been ignored since the shell startup. An interactive shell should
148    /// set `override_ignore` to `true` to bypass this restriction.
149    ///
150    /// You can never set a trap for `SIGKILL` or `SIGSTOP`.
151    ///
152    /// `origin` should be the location of the command performing this trap
153    /// update. It is only informative: It does not affect the signal handling
154    /// behavior and can be referenced later by [`get_state`](Self::get_state).
155    ///
156    /// This function clears all parent states remembered when [entering a
157    /// subshell](Self::enter_subshell), not only for the specified condition
158    /// but also for all other conditions.
159    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    /// Returns an iterator over the signal actions configured in this trap set.
199    ///
200    /// The iterator yields tuples of the signal, the currently configured trap
201    /// action, and the action set before
202    /// [`enter_subshell`](Self::enter_subshell) was called.
203    #[inline]
204    pub fn iter(&self) -> Iter<'_> {
205        let inner = self.traps.iter();
206        Iter { inner }
207    }
208
209    /// Updates signal dispositions on entering a subshell.
210    ///
211    /// ## Resetting non-ignore traps
212    ///
213    /// POSIX requires that traps other than `Action::Ignore` be reset when
214    /// entering a subshell. This function achieves that effect.
215    ///
216    /// The trap set will remember the original trap states as the parent
217    /// states. You can get them from the second return value of
218    /// [`get_state`](Self::get_state) or the third item of tuples yielded by an
219    /// [iterator](Self::iter).
220    ///
221    /// Note that trap actions other than `Trap::Command` remain as before.
222    ///
223    /// ## Clearing internal dispositions
224    ///
225    /// Internal dispositions that have been installed are cleared except for
226    /// the SIGCHLD signal.
227    ///
228    /// ## Ignoring SIGINT and SIGQUIT
229    ///
230    /// If `ignore_sigint_sigquit` is true, this function sets the dispositions
231    /// for SIGINT and SIGQUIT to `Ignore`.
232    ///
233    /// ## Ignoring SIGTSTP, SIGTTIN, and SIGTTOU
234    ///
235    /// If `keep_internal_dispositions_for_stoppers` is true and the internal
236    /// dispositions have been [enabled for SIGTSTP, SIGTTIN, and
237    /// SIGTTOU](Self::enable_internal_dispositions_for_stoppers), this function
238    /// leaves the dispositions for those signals set to `Ignore`.
239    ///
240    /// ## Errors
241    ///
242    /// This function ignores any errors that may occur when setting signal
243    /// dispositions.
244    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                    // If the entry is occupied, the signal is already ignored in the loop above.
281                    Entry::Occupied(_) => {}
282                }
283            }
284        }
285    }
286
287    /// Sets the `pending` flag of the [`TrapState`] for the specified signal.
288    ///
289    /// This function does nothing if no trap action has been
290    /// [set](Self::set_action) for the signal.
291    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    /// Resets the `pending` flag of the [`TrapState`] for the specified signal.
298    ///
299    /// Returns the [`TrapState`] if the flag was set.
300    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    /// Returns a signal that has been [caught](Self::catch_signal).
307    ///
308    /// This function clears the `pending` flag of the [`TrapState`] for the
309    /// specified signal.
310    ///
311    /// If there is more than one caught signal, it is unspecified which one of
312    /// them is returned. If there is no caught signal, `None` is returned.
313    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    /// Installs the internal disposition for `SIGCHLD`.
334    ///
335    /// You should install the internal disposition for `SIGCHLD` by using this
336    /// function before waiting for `SIGCHLD` with [`System::wait`] and
337    /// [`SharedSystem::wait_for_signal`]. The disposition allows catching
338    /// `SIGCHLD`.
339    ///
340    /// This function remembers that the disposition has been installed, so a
341    /// second call to the function will be a no-op.
342    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    /// Installs the internal dispositions for `SIGINT`, `SIGTERM`, and `SIGQUIT`.
350    ///
351    /// An interactive shell should install the internal dispositions for these
352    /// signals by using this function. The dispositions catch `SIGINT` and
353    /// ignore `SIGTERM` and `SIGQUIT`.
354    ///
355    /// This function remembers that the dispositions have been installed, so a
356    /// second call to the function will be a no-op.
357    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    /// Installs the internal dispositions for `SIGTSTP`, `SIGTTIN`, and `SIGTTOU`.
367    ///
368    /// An interactive job-controlling shell should install the internal
369    /// dispositions for these signals by using this function. The dispositions
370    /// ignore the signals.
371    ///
372    /// This function remembers that the dispositions have been installed, so a
373    /// second call to the function will be a no-op.
374    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    /// Uninstalls the internal dispositions for `SIGINT`, `SIGTERM`, and `SIGQUIT`.
384    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    /// Uninstalls the internal dispositions for `SIGTSTP`, `SIGTTIN`, and `SIGTTOU`.
394    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    /// Uninstalls all internal dispositions.
404    ///
405    /// This function removes all internal dispositions that have been
406    /// previously installed by `self`, except for the `SIGCHLD` signal.
407    /// Dispositions for any existing user-defined traps are not affected.
408    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        // The order in which take_caught_signal returns the two signals is
1149        // unspecified, so we accept both the orders.
1150        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}