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, Origin, SetActionError, TrapState};
41use self::state::{EnterSubshellOption, GrandState};
42use crate::signal;
43use crate::system::{Disposition, Errno};
44#[cfg(doc)]
45use crate::system::{SharedSystem, System};
46use std::collections::BTreeMap;
47use std::collections::btree_map::Entry;
48use yash_syntax::source::Location;
49
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    /// Returns the current disposition for a signal.
78    ///
79    /// This function returns the current disposition for the specified signal like
80    /// [`set_disposition`](Self::set_disposition) does, but does not change the
81    /// disposition.
82    fn get_disposition(&self, signal: signal::Number) -> Result<Disposition, Errno>;
83
84    /// Sets how a signal is handled.
85    ///
86    /// This function updates the signal blocking mask and the disposition for
87    /// the specified signal, and returns the previous disposition.
88    fn set_disposition(
89        &mut self,
90        signal: signal::Number,
91        disposition: Disposition,
92    ) -> Result<Disposition, Errno>;
93}
94
95/// Iterator of trap actions configured in a [trap set](TrapSet).
96///
97/// [`TrapSet::iter`] returns this type of iterator.
98#[must_use]
99pub struct Iter<'a> {
100    inner: std::collections::btree_map::Iter<'a, Condition, GrandState>,
101}
102
103impl<'a> Iterator for Iter<'a> {
104    /// Type of items yielded by this iterator
105    ///
106    /// Each item is a tuple of a condition, the currently configured trap state,
107    /// and the parent state that was active before entering a subshell.
108    type Item = (&'a Condition, &'a TrapState, Option<&'a TrapState>);
109
110    fn next(&mut self) -> Option<(&'a Condition, &'a TrapState, Option<&'a TrapState>)> {
111        self.inner
112            .next()
113            .map(|(cond, state)| (cond, state.current_state(), state.parent_state()))
114    }
115}
116
117/// Collection of event handling settings.
118///
119/// See the [module documentation](self) for details.
120#[derive(Clone, Debug, Default)]
121pub struct TrapSet {
122    traps: BTreeMap<Condition, GrandState>,
123}
124
125impl TrapSet {
126    /// Returns the current and parent states for a condition.
127    ///
128    /// This function returns a pair of optional trap states. The first is the
129    /// currently configured trap action, and the second is the action set
130    /// before [`enter_subshell`](Self::enter_subshell) was called.
131    ///
132    /// The current state (the first return value) is `None` if the state is
133    /// unknown (because no trap action or internal disposition has been set for
134    /// the signal specified by the condition). To get the current signal
135    /// disposition in this case, use [`peek_state`](Self::peek_state).
136    ///
137    /// The parent state (the second return value) is `None` if the
138    /// shell environment has not [entered a subshell](Self::enter_subshell) or
139    /// a [trap action has been modified](Self::set_action) after entering the
140    /// subshell.
141    pub fn get_state<C: Into<Condition>>(
142        &self,
143        cond: C,
144    ) -> (Option<&TrapState>, Option<&TrapState>) {
145        self.get_state_impl(cond.into())
146    }
147
148    fn get_state_impl(&self, cond: Condition) -> (Option<&TrapState>, Option<&TrapState>) {
149        match self.traps.get(&cond) {
150            None => (None, None),
151            Some(state) => (Some(state.current_state()), state.parent_state()),
152        }
153    }
154
155    /// Returns the current state for a condition.
156    ///
157    /// This function returns the trap action for the specified condition that
158    /// is to be included in the output of the `trap` built-in.
159    ///
160    /// If the current state is not yet known (because no trap action or
161    /// internal disposition has been set for the signal specified by the
162    /// condition), it calls [`SignalSystem::get_disposition`] to get the
163    /// current signal disposition and saves it as the current state.
164    ///
165    /// If the shell environment has [entered a subshell](Self::enter_subshell)
166    /// and no [trap action has been modified](Self::set_action) after entering
167    /// the subshell, the state that was active before entering the subshell is
168    /// returned.
169    ///
170    /// See also [`get_state`](Self::get_state), which returns `None` if the
171    /// current state is not yet known.
172    pub fn peek_state<S: SignalSystem, C: Into<Condition>>(
173        &mut self,
174        system: &S,
175        cond: C,
176    ) -> Result<&TrapState, Errno> {
177        self.peek_state_impl(system, cond.into())
178    }
179
180    fn peek_state_impl<S: SignalSystem>(
181        &mut self,
182        system: &S,
183        cond: Condition,
184    ) -> Result<&TrapState, Errno> {
185        let entry = self.traps.entry(cond);
186        let state = GrandState::insert_from_system_if_vacant(system, entry)?;
187        Ok(state.parent_state().unwrap_or(state.current_state()))
188    }
189
190    /// Sets a trap action for a condition.
191    ///
192    /// If the condition is a signal, this function installs a signal handler to
193    /// the specified underlying system.
194    ///
195    /// If `override_ignore` is `false`, you cannot set a trap for a signal that
196    /// has been ignored since the shell startup. An interactive shell should
197    /// set `override_ignore` to `true` to bypass this restriction.
198    ///
199    /// You can never set a trap for `SIGKILL` or `SIGSTOP`.
200    ///
201    /// `origin` should be the location of the command performing this trap
202    /// update. It is only informative: It does not affect the signal handling
203    /// behavior and can be referenced later by [`get_state`](Self::get_state).
204    ///
205    /// This function clears all parent states remembered when [entering a
206    /// subshell](Self::enter_subshell), not only for the specified condition
207    /// but also for all other conditions.
208    pub fn set_action<S: SignalSystem, C: Into<Condition>>(
209        &mut self,
210        system: &mut S,
211        cond: C,
212        action: Action,
213        origin: Location,
214        override_ignore: bool,
215    ) -> Result<(), SetActionError> {
216        self.set_action_impl(system, cond.into(), action, origin, override_ignore)
217    }
218
219    fn set_action_impl<S: SignalSystem>(
220        &mut self,
221        system: &mut S,
222        cond: Condition,
223        action: Action,
224        origin: Location,
225        override_ignore: bool,
226    ) -> Result<(), SetActionError> {
227        if let Condition::Signal(number) = cond {
228            match system.signal_name_from_number(number) {
229                signal::Name::Kill => return Err(SetActionError::SIGKILL),
230                signal::Name::Stop => return Err(SetActionError::SIGSTOP),
231                _ => {}
232            }
233        }
234
235        self.clear_parent_states();
236
237        let entry = self.traps.entry(cond);
238        GrandState::set_action(system, entry, action, origin, override_ignore)
239    }
240
241    fn clear_parent_states(&mut self) {
242        for state in self.traps.values_mut() {
243            state.clear_parent_state();
244        }
245    }
246
247    /// Returns an iterator over the signal actions configured in this trap set.
248    ///
249    /// The iterator yields tuples of the signal, the currently configured trap
250    /// action, and the action set before
251    /// [`enter_subshell`](Self::enter_subshell) was called.
252    ///
253    /// The iterator only emits conditions for which a trap action has been
254    /// [set](Self::set_action) or [peeked](Self::peek_state). In other words,
255    /// it does not include conditions for which the current state is not yet
256    /// known. To get the state for all conditions, use
257    /// [`peek_state`](Self::peek_state) for each condition.
258    #[inline]
259    pub fn iter(&self) -> Iter<'_> {
260        let inner = self.traps.iter();
261        Iter { inner }
262    }
263
264    /// Updates signal dispositions on entering a subshell.
265    ///
266    /// ## Resetting non-ignore traps
267    ///
268    /// POSIX requires that traps other than `Action::Ignore` be reset when
269    /// entering a subshell. This function achieves that effect.
270    ///
271    /// The trap set will remember the original trap states as the parent
272    /// states. You can get them from the second return value of
273    /// [`get_state`](Self::get_state) or the third item of tuples yielded by an
274    /// [iterator](Self::iter).
275    ///
276    /// Note that trap actions other than `Trap::Command` remain as before.
277    ///
278    /// ## Clearing internal dispositions
279    ///
280    /// Internal dispositions that have been installed are cleared except for
281    /// the SIGCHLD signal.
282    ///
283    /// ## Ignoring SIGINT and SIGQUIT
284    ///
285    /// If `ignore_sigint_sigquit` is true, this function sets the dispositions
286    /// for SIGINT and SIGQUIT to `Ignore`.
287    ///
288    /// ## Ignoring SIGTSTP, SIGTTIN, and SIGTTOU
289    ///
290    /// If `keep_internal_dispositions_for_stoppers` is true and the internal
291    /// dispositions have been [enabled for SIGTSTP, SIGTTIN, and
292    /// SIGTTOU](Self::enable_internal_dispositions_for_stoppers), this function
293    /// leaves the dispositions for those signals set to `Ignore`.
294    ///
295    /// ## Errors
296    ///
297    /// This function ignores any errors that may occur when setting signal
298    /// dispositions.
299    pub fn enter_subshell<S: SignalSystem>(
300        &mut self,
301        system: &mut S,
302        ignore_sigint_sigquit: bool,
303        keep_internal_dispositions_for_stoppers: bool,
304    ) {
305        self.clear_parent_states();
306
307        for (&cond, state) in &mut self.traps {
308            let option = match cond {
309                Condition::Exit => EnterSubshellOption::ClearInternalDisposition,
310                Condition::Signal(number) => {
311                    use signal::Name::*;
312                    match system.signal_name_from_number(number) {
313                        Chld => EnterSubshellOption::KeepInternalDisposition,
314                        Int | Quit if ignore_sigint_sigquit => EnterSubshellOption::Ignore,
315                        Tstp | Ttin | Ttou
316                            if keep_internal_dispositions_for_stoppers
317                                && state.internal_disposition() != Disposition::Default =>
318                        {
319                            EnterSubshellOption::Ignore
320                        }
321                        _ => EnterSubshellOption::ClearInternalDisposition,
322                    }
323                }
324            };
325            _ = state.enter_subshell(system, cond, option);
326        }
327
328        if ignore_sigint_sigquit {
329            for name in [signal::Name::Int, signal::Name::Quit] {
330                let number = system
331                    .signal_number_from_name(name)
332                    .unwrap_or_else(|| panic!("missing support for signal {name}"));
333                match self.traps.entry(Condition::Signal(number)) {
334                    Entry::Vacant(vacant) => _ = GrandState::ignore(system, vacant),
335                    // If the entry is occupied, the signal is already ignored in the loop above.
336                    Entry::Occupied(_) => {}
337                }
338            }
339        }
340    }
341
342    /// Sets the `pending` flag of the [`TrapState`] for the specified signal.
343    ///
344    /// This function does nothing if no trap action has been
345    /// [set](Self::set_action) for the signal.
346    pub fn catch_signal(&mut self, signal: signal::Number) {
347        if let Some(state) = self.traps.get_mut(&Condition::Signal(signal)) {
348            state.mark_as_caught();
349        }
350    }
351
352    /// Resets the `pending` flag of the [`TrapState`] for the specified signal.
353    ///
354    /// Returns the [`TrapState`] if the flag was set.
355    pub fn take_signal_if_caught(&mut self, signal: signal::Number) -> Option<&TrapState> {
356        self.traps
357            .get_mut(&signal.into())
358            .and_then(|state| state.handle_if_caught())
359    }
360
361    /// Returns a signal that has been [caught](Self::catch_signal).
362    ///
363    /// This function clears the `pending` flag of the [`TrapState`] for the
364    /// specified signal.
365    ///
366    /// If there is more than one caught signal, it is unspecified which one of
367    /// them is returned. If there is no caught signal, `None` is returned.
368    pub fn take_caught_signal(&mut self) -> Option<(signal::Number, &TrapState)> {
369        self.traps.iter_mut().find_map(|(&cond, state)| match cond {
370            Condition::Signal(signal) => state.handle_if_caught().map(|trap| (signal, trap)),
371            _ => None,
372        })
373    }
374
375    fn set_internal_disposition<S: SignalSystem>(
376        &mut self,
377        signal: signal::Name,
378        disposition: Disposition,
379        system: &mut S,
380    ) -> Result<(), Errno> {
381        let number = system
382            .signal_number_from_name(signal)
383            .unwrap_or_else(|| panic!("missing support for signal {signal}"));
384        let entry = self.traps.entry(Condition::Signal(number));
385        GrandState::set_internal_disposition(system, entry, disposition)
386    }
387
388    /// Installs the internal disposition for `SIGCHLD`.
389    ///
390    /// You should install the internal disposition for `SIGCHLD` by using this
391    /// function before waiting for `SIGCHLD` with [`System::wait`] and
392    /// [`SharedSystem::wait_for_signal`]. The disposition allows catching
393    /// `SIGCHLD`.
394    ///
395    /// This function remembers that the disposition has been installed, so a
396    /// second call to the function will be a no-op.
397    pub fn enable_internal_disposition_for_sigchld<S: SignalSystem>(
398        &mut self,
399        system: &mut S,
400    ) -> Result<(), Errno> {
401        self.set_internal_disposition(signal::Name::Chld, Disposition::Catch, system)
402    }
403
404    /// Installs the internal dispositions for `SIGINT`, `SIGTERM`, and `SIGQUIT`.
405    ///
406    /// An interactive shell should install the internal dispositions for these
407    /// signals by using this function. The dispositions catch `SIGINT` and
408    /// ignore `SIGTERM` and `SIGQUIT`.
409    ///
410    /// This function remembers that the dispositions have been installed, so a
411    /// second call to the function will be a no-op.
412    pub fn enable_internal_dispositions_for_terminators<S: SignalSystem>(
413        &mut self,
414        system: &mut S,
415    ) -> Result<(), Errno> {
416        self.set_internal_disposition(signal::Name::Int, Disposition::Catch, system)?;
417        self.set_internal_disposition(signal::Name::Term, Disposition::Ignore, system)?;
418        self.set_internal_disposition(signal::Name::Quit, Disposition::Ignore, system)
419    }
420
421    /// Installs the internal dispositions for `SIGTSTP`, `SIGTTIN`, and `SIGTTOU`.
422    ///
423    /// An interactive job-controlling shell should install the internal
424    /// dispositions for these signals by using this function. The dispositions
425    /// ignore the signals.
426    ///
427    /// This function remembers that the dispositions have been installed, so a
428    /// second call to the function will be a no-op.
429    pub fn enable_internal_dispositions_for_stoppers<S: SignalSystem>(
430        &mut self,
431        system: &mut S,
432    ) -> Result<(), Errno> {
433        self.set_internal_disposition(signal::Name::Tstp, Disposition::Ignore, system)?;
434        self.set_internal_disposition(signal::Name::Ttin, Disposition::Ignore, system)?;
435        self.set_internal_disposition(signal::Name::Ttou, Disposition::Ignore, system)
436    }
437
438    /// Uninstalls the internal dispositions for `SIGINT`, `SIGTERM`, and `SIGQUIT`.
439    pub fn disable_internal_dispositions_for_terminators<S: SignalSystem>(
440        &mut self,
441        system: &mut S,
442    ) -> Result<(), Errno> {
443        self.set_internal_disposition(signal::Name::Int, Disposition::Default, system)?;
444        self.set_internal_disposition(signal::Name::Term, Disposition::Default, system)?;
445        self.set_internal_disposition(signal::Name::Quit, Disposition::Default, system)
446    }
447
448    /// Uninstalls the internal dispositions for `SIGTSTP`, `SIGTTIN`, and `SIGTTOU`.
449    pub fn disable_internal_dispositions_for_stoppers<S: SignalSystem>(
450        &mut self,
451        system: &mut S,
452    ) -> Result<(), Errno> {
453        self.set_internal_disposition(signal::Name::Tstp, Disposition::Default, system)?;
454        self.set_internal_disposition(signal::Name::Ttin, Disposition::Default, system)?;
455        self.set_internal_disposition(signal::Name::Ttou, Disposition::Default, system)
456    }
457
458    /// Uninstalls all internal dispositions.
459    ///
460    /// This function removes all internal dispositions that have been
461    /// previously installed by `self`, except for the `SIGCHLD` signal.
462    /// Dispositions for any existing user-defined traps are not affected.
463    pub fn disable_internal_dispositions<S: SignalSystem>(
464        &mut self,
465        system: &mut S,
466    ) -> Result<(), Errno> {
467        self.set_internal_disposition(signal::Name::Chld, Disposition::Default, system)?;
468        self.disable_internal_dispositions_for_terminators(system)?;
469        self.disable_internal_dispositions_for_stoppers(system)
470    }
471}
472
473impl<'a> IntoIterator for &'a TrapSet {
474    /// Type of items yielded by this iterator
475    ///
476    /// Each item is a tuple of a condition, the currently configured trap state,
477    /// and the parent state that was active before entering a subshell.
478    type Item = (&'a Condition, &'a TrapState, Option<&'a TrapState>);
479    type IntoIter = Iter<'a>;
480
481    #[inline(always)]
482    fn into_iter(self) -> Iter<'a> {
483        self.iter()
484    }
485}
486
487#[cfg(test)]
488mod tests {
489    use super::*;
490    use crate::job::ProcessState;
491    use crate::system::System as _;
492    use crate::system::SystemEx as _;
493    use crate::system::r#virtual::VirtualSystem;
494    use crate::system::r#virtual::{
495        SIGCHLD, SIGINT, SIGKILL, SIGQUIT, SIGSTOP, SIGTERM, SIGTSTP, SIGTTIN, SIGTTOU, SIGUSR1,
496        SIGUSR2,
497    };
498    use crate::tests::in_virtual_system;
499    use std::collections::HashMap;
500
501    #[derive(Default)]
502    pub struct DummySystem(pub HashMap<signal::Number, Disposition>);
503
504    impl SignalSystem for DummySystem {
505        fn signal_name_from_number(&self, number: signal::Number) -> signal::Name {
506            VirtualSystem::new().signal_name_from_number(number)
507        }
508
509        fn signal_number_from_name(&self, name: signal::Name) -> Option<signal::Number> {
510            VirtualSystem::new().signal_number_from_name(name)
511        }
512
513        fn get_disposition(&self, signal: signal::Number) -> Result<Disposition, Errno> {
514            Ok(self.0.get(&signal).copied().unwrap_or_default())
515        }
516
517        fn set_disposition(
518            &mut self,
519            signal: signal::Number,
520            disposition: Disposition,
521        ) -> Result<Disposition, Errno> {
522            Ok(self.0.insert(signal, disposition).unwrap_or_default())
523        }
524    }
525
526    #[test]
527    fn default_trap() {
528        let trap_set = TrapSet::default();
529        assert_eq!(trap_set.get_state(SIGCHLD), (None, None));
530    }
531
532    #[test]
533    fn setting_trap_for_two_signals() {
534        let mut system = DummySystem::default();
535        let mut trap_set = TrapSet::default();
536        let origin_1 = Location::dummy("foo");
537        let result = trap_set.set_action(
538            &mut system,
539            SIGUSR1,
540            Action::Ignore,
541            origin_1.clone(),
542            false,
543        );
544        assert_eq!(result, Ok(()));
545
546        let command = Action::Command("echo".into());
547        let origin_2 = Location::dummy("bar");
548        let result = trap_set.set_action(
549            &mut system,
550            SIGUSR2,
551            command.clone(),
552            origin_2.clone(),
553            false,
554        );
555        assert_eq!(result, Ok(()));
556
557        assert_eq!(
558            trap_set.get_state(SIGUSR1),
559            (
560                Some(&TrapState {
561                    action: Action::Ignore,
562                    origin: Origin::User(origin_1),
563                    pending: false
564                }),
565                None
566            )
567        );
568        assert_eq!(
569            trap_set.get_state(SIGUSR2),
570            (
571                Some(&TrapState {
572                    action: command,
573                    origin: Origin::User(origin_2),
574                    pending: false
575                }),
576                None
577            )
578        );
579        assert_eq!(system.0[&SIGUSR1], Disposition::Ignore);
580        assert_eq!(system.0[&SIGUSR2], Disposition::Catch);
581    }
582
583    #[test]
584    fn setting_trap_for_sigkill() {
585        let mut system = DummySystem::default();
586        let mut trap_set = TrapSet::default();
587        let origin = Location::dummy("origin");
588        let result = trap_set.set_action(&mut system, SIGKILL, Action::Ignore, origin, false);
589        assert_eq!(result, Err(SetActionError::SIGKILL));
590        assert_eq!(trap_set.get_state(SIGKILL), (None, None));
591        assert_eq!(system.0.get(&SIGKILL), None);
592    }
593
594    #[test]
595    fn setting_trap_for_sigstop() {
596        let mut system = DummySystem::default();
597        let mut trap_set = TrapSet::default();
598        let origin = Location::dummy("origin");
599        let result = trap_set.set_action(&mut system, SIGSTOP, Action::Ignore, origin, false);
600        assert_eq!(result, Err(SetActionError::SIGSTOP));
601        assert_eq!(trap_set.get_state(SIGSTOP), (None, None));
602        assert_eq!(system.0.get(&SIGSTOP), None);
603    }
604
605    #[test]
606    fn peeking_state_with_default_inherited_disposition() {
607        let system = DummySystem::default();
608        let mut trap_set = TrapSet::default();
609        let result = trap_set.peek_state(&system, SIGCHLD);
610        assert_eq!(
611            result,
612            Ok(&TrapState {
613                action: Action::Default,
614                origin: Origin::Inherited,
615                pending: false
616            })
617        );
618    }
619
620    #[test]
621    fn peeking_state_with_inherited_disposition_of_ignore() {
622        let mut system = DummySystem::default();
623        system.0.insert(SIGCHLD, Disposition::Ignore);
624        let mut trap_set = TrapSet::default();
625        let result = trap_set.peek_state(&system, SIGCHLD);
626        assert_eq!(
627            result,
628            Ok(&TrapState {
629                action: Action::Ignore,
630                origin: Origin::Inherited,
631                pending: false
632            })
633        );
634    }
635
636    #[test]
637    fn peeking_state_with_parent_state() {
638        let mut system = DummySystem::default();
639        let mut trap_set = TrapSet::default();
640        let origin = Location::dummy("foo");
641        let command = Action::Command("echo".into());
642        trap_set
643            .set_action(&mut system, SIGUSR1, command.clone(), origin.clone(), false)
644            .unwrap();
645        trap_set.enter_subshell(&mut system, false, false);
646
647        let result = trap_set.peek_state(&system, SIGUSR1);
648        assert_eq!(
649            result,
650            Ok(&TrapState {
651                action: command,
652                origin: Origin::User(origin),
653                pending: false
654            })
655        );
656    }
657
658    #[test]
659    fn basic_iteration() {
660        let mut system = DummySystem::default();
661        let mut trap_set = TrapSet::default();
662        let origin_1 = Location::dummy("foo");
663        trap_set
664            .set_action(
665                &mut system,
666                SIGUSR1,
667                Action::Ignore,
668                origin_1.clone(),
669                false,
670            )
671            .unwrap();
672        let command = Action::Command("echo".into());
673        let origin_2 = Location::dummy("bar");
674        trap_set
675            .set_action(
676                &mut system,
677                SIGUSR2,
678                command.clone(),
679                origin_2.clone(),
680                false,
681            )
682            .unwrap();
683
684        let mut i = trap_set.iter();
685        let first = i.next().unwrap();
686        assert_eq!(first.0, &Condition::Signal(SIGUSR1));
687        assert_eq!(first.1.action, Action::Ignore);
688        assert_eq!(first.1.origin, Origin::User(origin_1));
689        assert_eq!(first.2, None);
690        let second = i.next().unwrap();
691        assert_eq!(second.0, &Condition::Signal(SIGUSR2));
692        assert_eq!(second.1.action, command);
693        assert_eq!(second.1.origin, Origin::User(origin_2));
694        assert_eq!(first.2, None);
695        assert_eq!(i.next(), None);
696    }
697
698    #[test]
699    fn iteration_after_entering_subshell() {
700        let mut system = DummySystem::default();
701        let mut trap_set = TrapSet::default();
702        let origin_1 = Location::dummy("foo");
703        trap_set
704            .set_action(
705                &mut system,
706                SIGUSR1,
707                Action::Ignore,
708                origin_1.clone(),
709                false,
710            )
711            .unwrap();
712        let command = Action::Command("echo".into());
713        let origin_2 = Location::dummy("bar");
714        trap_set
715            .set_action(
716                &mut system,
717                SIGUSR2,
718                command.clone(),
719                origin_2.clone(),
720                false,
721            )
722            .unwrap();
723        trap_set.enter_subshell(&mut system, false, false);
724
725        let mut i = trap_set.iter();
726        let first = i.next().unwrap();
727        assert_eq!(first.0, &Condition::Signal(SIGUSR1));
728        assert_eq!(first.1.action, Action::Ignore);
729        assert_eq!(first.1.origin, Origin::User(origin_1));
730        assert_eq!(first.2, None);
731        let second = i.next().unwrap();
732        assert_eq!(second.0, &Condition::Signal(SIGUSR2));
733        assert_eq!(second.1.action, Action::Default);
734        assert_eq!(second.1.origin, Origin::Subshell);
735        assert_eq!(second.2.unwrap().action, command);
736        assert_eq!(second.2.unwrap().origin, Origin::User(origin_2));
737        assert_eq!(i.next(), None);
738    }
739
740    #[test]
741    fn iteration_after_setting_trap_in_subshell() {
742        let mut system = DummySystem::default();
743        let mut trap_set = TrapSet::default();
744        let origin_1 = Location::dummy("foo");
745        let command = Action::Command("echo".into());
746        trap_set
747            .set_action(&mut system, SIGUSR1, command, origin_1, false)
748            .unwrap();
749        trap_set.enter_subshell(&mut system, false, false);
750        let origin_2 = Location::dummy("bar");
751        let command = Action::Command("ls".into());
752        trap_set
753            .set_action(
754                &mut system,
755                SIGUSR2,
756                command.clone(),
757                origin_2.clone(),
758                false,
759            )
760            .unwrap();
761
762        let mut i = trap_set.iter();
763        let first = i.next().unwrap();
764        assert_eq!(first.0, &Condition::Signal(SIGUSR1));
765        assert_eq!(first.1.action, Action::Default);
766        assert_eq!(first.1.origin, Origin::Subshell);
767        assert_eq!(first.2, None);
768        let second = i.next().unwrap();
769        assert_eq!(second.0, &Condition::Signal(SIGUSR2));
770        assert_eq!(second.1.action, command);
771        assert_eq!(second.1.origin, Origin::User(origin_2));
772        assert_eq!(second.2, None);
773        assert_eq!(i.next(), None);
774    }
775
776    #[test]
777    fn entering_subshell_resets_command_traps() {
778        let mut system = DummySystem::default();
779        let mut trap_set = TrapSet::default();
780        let action = Action::Command("".into());
781        let origin = Location::dummy("origin");
782        trap_set
783            .set_action(&mut system, SIGCHLD, action.clone(), origin.clone(), false)
784            .unwrap();
785
786        trap_set.enter_subshell(&mut system, false, false);
787        assert_eq!(
788            trap_set.get_state(SIGCHLD),
789            (
790                Some(&TrapState {
791                    action: Action::Default,
792                    origin: Origin::Subshell,
793                    pending: false
794                }),
795                Some(&TrapState {
796                    action,
797                    origin: Origin::User(origin),
798                    pending: false
799                })
800            )
801        );
802        assert_eq!(system.0[&SIGCHLD], Disposition::Default);
803    }
804
805    #[test]
806    fn entering_subshell_keeps_ignore_traps() {
807        let mut system = DummySystem::default();
808        let mut trap_set = TrapSet::default();
809        let origin = Location::dummy("origin");
810        trap_set
811            .set_action(&mut system, SIGCHLD, Action::Ignore, origin.clone(), false)
812            .unwrap();
813
814        trap_set.enter_subshell(&mut system, false, false);
815        assert_eq!(
816            trap_set.get_state(SIGCHLD),
817            (
818                Some(&TrapState {
819                    action: Action::Ignore,
820                    origin: Origin::User(origin),
821                    pending: false
822                }),
823                None
824            )
825        );
826        assert_eq!(system.0[&SIGCHLD], Disposition::Ignore);
827    }
828
829    #[test]
830    fn entering_subshell_with_internal_disposition_for_sigchld() {
831        let mut system = DummySystem::default();
832        let mut trap_set = TrapSet::default();
833        let action = Action::Command("".into());
834        let origin = Location::dummy("origin");
835        trap_set
836            .set_action(&mut system, SIGCHLD, action.clone(), origin.clone(), false)
837            .unwrap();
838        trap_set
839            .enable_internal_disposition_for_sigchld(&mut system)
840            .unwrap();
841
842        trap_set.enter_subshell(&mut system, false, false);
843        assert_eq!(
844            trap_set.get_state(SIGCHLD),
845            (
846                Some(&TrapState {
847                    action: Action::Default,
848                    origin: Origin::Subshell,
849                    pending: false
850                }),
851                Some(&TrapState {
852                    action,
853                    origin: Origin::User(origin),
854                    pending: false
855                })
856            )
857        );
858        assert_eq!(system.0[&SIGCHLD], Disposition::Catch);
859    }
860
861    #[test]
862    fn entering_subshell_with_internal_disposition_for_sigint() {
863        let mut system = DummySystem::default();
864        let mut trap_set = TrapSet::default();
865        let action = Action::Command("".into());
866        let origin = Location::dummy("origin");
867        trap_set
868            .set_action(&mut system, SIGINT, action.clone(), origin.clone(), false)
869            .unwrap();
870        trap_set
871            .enable_internal_dispositions_for_terminators(&mut system)
872            .unwrap();
873
874        trap_set.enter_subshell(&mut system, false, false);
875        assert_eq!(
876            trap_set.get_state(SIGINT),
877            (
878                Some(&TrapState {
879                    action: Action::Default,
880                    origin: Origin::Subshell,
881                    pending: false
882                }),
883                Some(&TrapState {
884                    action,
885                    origin: Origin::User(origin),
886                    pending: false
887                })
888            )
889        );
890        assert_eq!(system.0[&SIGINT], Disposition::Default);
891    }
892
893    #[test]
894    fn entering_subshell_with_internal_disposition_for_sigterm() {
895        let mut system = DummySystem::default();
896        let mut trap_set = TrapSet::default();
897        let action = Action::Command("".into());
898        let origin = Location::dummy("origin");
899        trap_set
900            .set_action(&mut system, SIGTERM, action.clone(), origin.clone(), false)
901            .unwrap();
902        trap_set
903            .enable_internal_dispositions_for_terminators(&mut system)
904            .unwrap();
905
906        trap_set.enter_subshell(&mut system, false, false);
907        assert_eq!(
908            trap_set.get_state(SIGTERM),
909            (
910                Some(&TrapState {
911                    action: Action::Default,
912                    origin: Origin::Subshell,
913                    pending: false
914                }),
915                Some(&TrapState {
916                    action,
917                    origin: Origin::User(origin),
918                    pending: false
919                })
920            )
921        );
922        assert_eq!(system.0[&SIGTERM], Disposition::Default);
923    }
924
925    #[test]
926    fn entering_subshell_with_internal_disposition_for_sigquit() {
927        let mut system = DummySystem::default();
928        let mut trap_set = TrapSet::default();
929        let action = Action::Command("".into());
930        let origin = Location::dummy("origin");
931        trap_set
932            .set_action(&mut system, SIGQUIT, action.clone(), origin.clone(), false)
933            .unwrap();
934        trap_set
935            .enable_internal_dispositions_for_terminators(&mut system)
936            .unwrap();
937
938        trap_set.enter_subshell(&mut system, false, false);
939        assert_eq!(
940            trap_set.get_state(SIGQUIT),
941            (
942                Some(&TrapState {
943                    action: Action::Default,
944                    origin: Origin::Subshell,
945                    pending: false
946                }),
947                Some(&TrapState {
948                    action,
949                    origin: Origin::User(origin),
950                    pending: false
951                })
952            )
953        );
954        assert_eq!(system.0[&SIGQUIT], Disposition::Default);
955    }
956
957    #[test]
958    fn entering_subshell_with_internal_disposition_for_sigtstp() {
959        let mut system = DummySystem::default();
960        let mut trap_set = TrapSet::default();
961        let action = Action::Command("".into());
962        let origin = Location::dummy("origin");
963        trap_set
964            .set_action(&mut system, SIGTSTP, action.clone(), origin.clone(), false)
965            .unwrap();
966        trap_set
967            .enable_internal_dispositions_for_terminators(&mut system)
968            .unwrap();
969
970        trap_set.enter_subshell(&mut system, false, false);
971        assert_eq!(
972            trap_set.get_state(SIGTSTP),
973            (
974                Some(&TrapState {
975                    action: Action::Default,
976                    origin: Origin::Subshell,
977                    pending: false
978                }),
979                Some(&TrapState {
980                    action,
981                    origin: Origin::User(origin),
982                    pending: false
983                })
984            )
985        );
986        assert_eq!(system.0[&SIGTSTP], Disposition::Default);
987    }
988
989    #[test]
990    fn entering_subshell_with_internal_disposition_for_sigttin() {
991        let mut system = DummySystem::default();
992        let mut trap_set = TrapSet::default();
993        let action = Action::Command("".into());
994        let origin = Location::dummy("origin");
995        trap_set
996            .set_action(&mut system, SIGTTIN, action.clone(), origin.clone(), false)
997            .unwrap();
998        trap_set
999            .enable_internal_dispositions_for_terminators(&mut system)
1000            .unwrap();
1001
1002        trap_set.enter_subshell(&mut system, false, false);
1003        assert_eq!(
1004            trap_set.get_state(SIGTTIN),
1005            (
1006                Some(&TrapState {
1007                    action: Action::Default,
1008                    origin: Origin::Subshell,
1009                    pending: false
1010                }),
1011                Some(&TrapState {
1012                    action,
1013                    origin: Origin::User(origin),
1014                    pending: false
1015                })
1016            )
1017        );
1018        assert_eq!(system.0[&SIGTTIN], Disposition::Default);
1019    }
1020
1021    #[test]
1022    fn entering_subshell_with_internal_disposition_for_sigttou() {
1023        let mut system = DummySystem::default();
1024        let mut trap_set = TrapSet::default();
1025        let action = Action::Command("".into());
1026        let origin = Location::dummy("origin");
1027        trap_set
1028            .set_action(&mut system, SIGTTOU, action.clone(), origin.clone(), false)
1029            .unwrap();
1030        trap_set
1031            .enable_internal_dispositions_for_terminators(&mut system)
1032            .unwrap();
1033
1034        trap_set.enter_subshell(&mut system, false, false);
1035        assert_eq!(
1036            trap_set.get_state(SIGTTOU),
1037            (
1038                Some(&TrapState {
1039                    action: Action::Default,
1040                    origin: Origin::Subshell,
1041                    pending: false
1042                }),
1043                Some(&TrapState {
1044                    action,
1045                    origin: Origin::User(origin),
1046                    pending: false
1047                })
1048            )
1049        );
1050        assert_eq!(system.0[&SIGTTOU], Disposition::Default);
1051    }
1052
1053    #[test]
1054    fn setting_trap_after_entering_subshell_clears_parent_states() {
1055        let mut system = DummySystem::default();
1056        let mut trap_set = TrapSet::default();
1057        let origin_1 = Location::dummy("foo");
1058        let command = Action::Command("echo 1".into());
1059        trap_set
1060            .set_action(&mut system, SIGUSR1, command, origin_1, false)
1061            .unwrap();
1062        let origin_2 = Location::dummy("bar");
1063        let command = Action::Command("echo 2".into());
1064        trap_set
1065            .set_action(&mut system, SIGUSR2, command, origin_2, false)
1066            .unwrap();
1067        trap_set.enter_subshell(&mut system, false, false);
1068
1069        let command = Action::Command("echo 9".into());
1070        let origin_3 = Location::dummy("qux");
1071        trap_set
1072            .set_action(
1073                &mut system,
1074                SIGUSR1,
1075                command.clone(),
1076                origin_3.clone(),
1077                false,
1078            )
1079            .unwrap();
1080
1081        assert_eq!(
1082            trap_set.get_state(SIGUSR1),
1083            (
1084                Some(&TrapState {
1085                    action: command,
1086                    origin: Origin::User(origin_3),
1087                    pending: false
1088                }),
1089                None
1090            )
1091        );
1092        assert_eq!(
1093            trap_set.get_state(SIGUSR2),
1094            (
1095                Some(&TrapState {
1096                    action: Action::Default,
1097                    origin: Origin::Subshell,
1098                    pending: false
1099                }),
1100                None
1101            )
1102        );
1103        assert_eq!(system.0[&SIGUSR1], Disposition::Catch);
1104        assert_eq!(system.0[&SIGUSR2], Disposition::Default);
1105    }
1106
1107    #[test]
1108    fn entering_nested_subshell_clears_parent_states() {
1109        let mut system = DummySystem::default();
1110        let mut trap_set = TrapSet::default();
1111        let origin_1 = Location::dummy("foo");
1112        let command = Action::Command("echo 1".into());
1113        trap_set
1114            .set_action(&mut system, SIGUSR1, command, origin_1, false)
1115            .unwrap();
1116        let origin_2 = Location::dummy("bar");
1117        let command = Action::Command("echo 2".into());
1118        trap_set
1119            .set_action(&mut system, SIGUSR2, command, origin_2, false)
1120            .unwrap();
1121        trap_set.enter_subshell(&mut system, false, false);
1122        trap_set.enter_subshell(&mut system, false, false);
1123
1124        assert_eq!(
1125            trap_set.get_state(SIGUSR1),
1126            (
1127                Some(&TrapState {
1128                    action: Action::Default,
1129                    origin: Origin::Subshell,
1130                    pending: false
1131                }),
1132                None
1133            )
1134        );
1135        assert_eq!(
1136            trap_set.get_state(SIGUSR2),
1137            (
1138                Some(&TrapState {
1139                    action: Action::Default,
1140                    origin: Origin::Subshell,
1141                    pending: false
1142                }),
1143                None
1144            )
1145        );
1146        assert_eq!(system.0[&SIGUSR1], Disposition::Default);
1147        assert_eq!(system.0[&SIGUSR2], Disposition::Default);
1148    }
1149
1150    #[test]
1151    fn ignoring_sigint_on_entering_subshell_with_action_set() {
1152        in_virtual_system(|mut env, state| async move {
1153            env.traps
1154                .set_action(
1155                    &mut env.system,
1156                    SIGINT,
1157                    Action::Command("".into()),
1158                    Location::dummy(""),
1159                    false,
1160                )
1161                .unwrap();
1162            env.system.kill(env.main_pid, Some(SIGINT)).await.unwrap();
1163            env.traps.enter_subshell(&mut env.system, true, false);
1164
1165            let state = state.borrow();
1166            let process = &state.processes[&env.main_pid];
1167            assert_eq!(process.disposition(SIGINT), Disposition::Ignore);
1168            assert_eq!(process.state(), ProcessState::Running);
1169        })
1170    }
1171
1172    #[test]
1173    fn ignoring_sigquit_on_entering_subshell_with_action_set() {
1174        in_virtual_system(|mut env, state| async move {
1175            env.traps
1176                .set_action(
1177                    &mut env.system,
1178                    SIGQUIT,
1179                    Action::Command("".into()),
1180                    Location::dummy(""),
1181                    false,
1182                )
1183                .unwrap();
1184            env.system.kill(env.main_pid, Some(SIGQUIT)).await.unwrap();
1185            env.traps.enter_subshell(&mut env.system, true, false);
1186
1187            let state = state.borrow();
1188            let process = &state.processes[&env.main_pid];
1189            assert_eq!(process.disposition(SIGQUIT), Disposition::Ignore);
1190            assert_eq!(process.state(), ProcessState::Running);
1191        })
1192    }
1193
1194    #[test]
1195    fn ignoring_sigint_and_sigquit_on_entering_subshell_without_action_set() {
1196        let mut system = DummySystem::default();
1197        let mut trap_set = TrapSet::default();
1198        trap_set.enter_subshell(&mut system, true, false);
1199        assert_eq!(system.0[&SIGINT], Disposition::Ignore);
1200        assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1201    }
1202
1203    #[test]
1204    fn keeping_stopper_internal_dispositions_ignored() {
1205        in_virtual_system(|mut env, state| async move {
1206            for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
1207                env.traps
1208                    .set_action(
1209                        &mut env.system,
1210                        signal,
1211                        Action::Command("".into()),
1212                        Location::dummy(""),
1213                        false,
1214                    )
1215                    .unwrap();
1216            }
1217            env.traps
1218                .enable_internal_dispositions_for_stoppers(&mut env.system)
1219                .unwrap();
1220            for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
1221                env.system.kill(env.main_pid, Some(signal)).await.unwrap();
1222            }
1223            env.traps.enter_subshell(&mut env.system, false, true);
1224
1225            let state = state.borrow();
1226            let process = &state.processes[&env.main_pid];
1227            assert_eq!(process.disposition(SIGTSTP), Disposition::Ignore);
1228            assert_eq!(process.disposition(SIGTTIN), Disposition::Ignore);
1229            assert_eq!(process.disposition(SIGTTOU), Disposition::Ignore);
1230            assert_eq!(process.state(), ProcessState::Running);
1231        })
1232    }
1233
1234    #[test]
1235    fn no_stopper_internal_dispositions_enabled_to_be_kept_ignored() {
1236        in_virtual_system(|mut env, state| async move {
1237            for signal in [SIGTSTP, SIGTTIN, SIGTTOU] {
1238                env.traps
1239                    .set_action(
1240                        &mut env.system,
1241                        signal,
1242                        Action::Command("".into()),
1243                        Location::dummy(""),
1244                        false,
1245                    )
1246                    .unwrap();
1247            }
1248            env.traps.enter_subshell(&mut env.system, false, true);
1249
1250            let state = state.borrow();
1251            let process = &state.processes[&env.main_pid];
1252            assert_eq!(process.disposition(SIGTSTP), Disposition::Default);
1253            assert_eq!(process.disposition(SIGTTIN), Disposition::Default);
1254            assert_eq!(process.disposition(SIGTTOU), Disposition::Default);
1255        })
1256    }
1257
1258    #[test]
1259    fn catching_signal() {
1260        let mut system = DummySystem::default();
1261        let mut trap_set = TrapSet::default();
1262        let command = Action::Command("echo INT".into());
1263        let origin = Location::dummy("origin");
1264        trap_set
1265            .set_action(&mut system, SIGINT, command, origin, false)
1266            .unwrap();
1267        let command = Action::Command("echo TERM".into());
1268        let origin = Location::dummy("origin");
1269        trap_set
1270            .set_action(&mut system, SIGTERM, command, origin, false)
1271            .unwrap();
1272
1273        trap_set.catch_signal(SIGCHLD);
1274        trap_set.catch_signal(SIGINT);
1275
1276        let trap_state = trap_set.get_state(SIGINT).0.unwrap();
1277        assert!(trap_state.pending, "trap_state = {trap_state:?}");
1278        let trap_state = trap_set.get_state(SIGTERM).0.unwrap();
1279        assert!(!trap_state.pending, "trap_state = {trap_state:?}");
1280    }
1281
1282    #[test]
1283    fn taking_signal_if_caught() {
1284        let mut system = DummySystem::default();
1285        let mut trap_set = TrapSet::default();
1286        let command = Action::Command("echo INT".into());
1287        let origin = Location::dummy("origin");
1288        trap_set
1289            .set_action(&mut system, SIGINT, command, origin, false)
1290            .unwrap();
1291
1292        let result = trap_set.take_signal_if_caught(SIGINT);
1293        assert_eq!(result, None);
1294
1295        trap_set.catch_signal(SIGINT);
1296
1297        let result = trap_set.take_signal_if_caught(SIGINT);
1298        assert!(!result.unwrap().pending);
1299
1300        let result = trap_set.take_signal_if_caught(SIGINT);
1301        assert_eq!(result, None);
1302    }
1303
1304    #[test]
1305    fn taking_caught_signal() {
1306        let mut system = DummySystem::default();
1307        let mut trap_set = TrapSet::default();
1308        assert_eq!(trap_set.take_caught_signal(), None);
1309
1310        let command = Action::Command("echo INT".into());
1311        let origin = Location::dummy("origin");
1312        trap_set
1313            .set_action(&mut system, SIGINT, command, origin, false)
1314            .unwrap();
1315        let command = Action::Command("echo TERM".into());
1316        let origin = Location::dummy("origin");
1317        trap_set
1318            .set_action(&mut system, SIGTERM, command, origin, false)
1319            .unwrap();
1320        let command = Action::Command("echo USR1".into());
1321        let origin = Location::dummy("origin");
1322        trap_set
1323            .set_action(&mut system, SIGUSR1, command, origin, false)
1324            .unwrap();
1325        assert_eq!(trap_set.take_caught_signal(), None);
1326
1327        trap_set.catch_signal(SIGINT);
1328        trap_set.catch_signal(SIGUSR1);
1329        // The order in which take_caught_signal returns the two signals is
1330        // unspecified, so we accept both the orders.
1331        let result = trap_set.take_caught_signal().unwrap();
1332        match result.0 {
1333            SIGINT => {
1334                assert_eq!(result.1.action, Action::Command("echo INT".into()));
1335                assert!(!result.1.pending);
1336
1337                let result = trap_set.take_caught_signal().unwrap();
1338                assert_eq!(result.0, SIGUSR1);
1339                assert_eq!(result.1.action, Action::Command("echo USR1".into()));
1340                assert!(!result.1.pending);
1341            }
1342            SIGUSR1 => {
1343                assert_eq!(result.1.action, Action::Command("echo USR1".into()));
1344                assert!(!result.1.pending);
1345
1346                let result = trap_set.take_caught_signal().unwrap();
1347                assert_eq!(result.0, SIGINT);
1348                assert_eq!(result.1.action, Action::Command("echo INT".into()));
1349                assert!(!result.1.pending);
1350            }
1351            _ => panic!("wrong signal: {result:?}"),
1352        }
1353
1354        assert_eq!(trap_set.take_caught_signal(), None);
1355    }
1356
1357    #[test]
1358    fn enabling_internal_disposition_for_sigchld() {
1359        let mut system = DummySystem::default();
1360        let mut trap_set = TrapSet::default();
1361        trap_set
1362            .enable_internal_disposition_for_sigchld(&mut system)
1363            .unwrap();
1364        assert_eq!(system.0[&SIGCHLD], Disposition::Catch);
1365    }
1366
1367    #[test]
1368    fn enabling_internal_dispositions_for_terminators() {
1369        let mut system = DummySystem::default();
1370        let mut trap_set = TrapSet::default();
1371        trap_set
1372            .enable_internal_dispositions_for_terminators(&mut system)
1373            .unwrap();
1374        assert_eq!(system.0[&SIGINT], Disposition::Catch);
1375        assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1376        assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1377    }
1378
1379    #[test]
1380    fn enabling_internal_dispositions_for_stoppers() {
1381        let mut system = DummySystem::default();
1382        let mut trap_set = TrapSet::default();
1383        trap_set
1384            .enable_internal_dispositions_for_stoppers(&mut system)
1385            .unwrap();
1386        assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1387        assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1388        assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1389    }
1390
1391    #[test]
1392    fn disabling_internal_dispositions_for_initially_defaulted_signals() {
1393        let mut system = DummySystem::default();
1394        let mut trap_set = TrapSet::default();
1395        trap_set
1396            .enable_internal_disposition_for_sigchld(&mut system)
1397            .unwrap();
1398        trap_set
1399            .enable_internal_dispositions_for_terminators(&mut system)
1400            .unwrap();
1401        trap_set
1402            .enable_internal_dispositions_for_stoppers(&mut system)
1403            .unwrap();
1404        trap_set.disable_internal_dispositions(&mut system).unwrap();
1405        assert_eq!(system.0[&SIGCHLD], Disposition::Default);
1406        assert_eq!(system.0[&SIGINT], Disposition::Default);
1407        assert_eq!(system.0[&SIGTERM], Disposition::Default);
1408        assert_eq!(system.0[&SIGQUIT], Disposition::Default);
1409        assert_eq!(system.0[&SIGTSTP], Disposition::Default);
1410        assert_eq!(system.0[&SIGTTIN], Disposition::Default);
1411        assert_eq!(system.0[&SIGTTOU], Disposition::Default);
1412    }
1413
1414    fn ignore_signals(system: &mut DummySystem) {
1415        system.0.extend(
1416            [SIGCHLD, SIGINT, SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU]
1417                .into_iter()
1418                .map(|signal| (signal, Disposition::Ignore)),
1419        )
1420    }
1421
1422    #[test]
1423    fn disabling_internal_dispositions_for_initially_ignored_signals() {
1424        let mut system = DummySystem::default();
1425        ignore_signals(&mut system);
1426        let mut trap_set = TrapSet::default();
1427        trap_set
1428            .enable_internal_disposition_for_sigchld(&mut system)
1429            .unwrap();
1430        trap_set
1431            .enable_internal_dispositions_for_terminators(&mut system)
1432            .unwrap();
1433        trap_set
1434            .enable_internal_dispositions_for_stoppers(&mut system)
1435            .unwrap();
1436        trap_set.disable_internal_dispositions(&mut system).unwrap();
1437        assert_eq!(system.0[&SIGCHLD], Disposition::Ignore);
1438        assert_eq!(system.0[&SIGINT], Disposition::Ignore);
1439        assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1440        assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1441        assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1442        assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1443        assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1444    }
1445
1446    #[test]
1447    fn disabling_internal_dispositions_after_enabling_twice() {
1448        let mut system = DummySystem::default();
1449        ignore_signals(&mut system);
1450        let mut trap_set = TrapSet::default();
1451        trap_set
1452            .enable_internal_disposition_for_sigchld(&mut system)
1453            .unwrap();
1454        trap_set
1455            .enable_internal_disposition_for_sigchld(&mut system)
1456            .unwrap();
1457        trap_set
1458            .enable_internal_dispositions_for_terminators(&mut system)
1459            .unwrap();
1460        trap_set
1461            .enable_internal_dispositions_for_terminators(&mut system)
1462            .unwrap();
1463        trap_set
1464            .enable_internal_dispositions_for_stoppers(&mut system)
1465            .unwrap();
1466        trap_set
1467            .enable_internal_dispositions_for_stoppers(&mut system)
1468            .unwrap();
1469        trap_set.disable_internal_dispositions(&mut system).unwrap();
1470        assert_eq!(system.0[&SIGCHLD], Disposition::Ignore);
1471        assert_eq!(system.0[&SIGINT], Disposition::Ignore);
1472        assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1473        assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1474        assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1475        assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1476        assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1477    }
1478
1479    #[test]
1480    fn disabling_internal_dispositions_without_enabling() {
1481        let mut system = DummySystem::default();
1482        ignore_signals(&mut system);
1483        let mut trap_set = TrapSet::default();
1484        trap_set.disable_internal_dispositions(&mut system).unwrap();
1485        assert_eq!(system.0[&SIGCHLD], Disposition::Ignore);
1486        assert_eq!(system.0[&SIGINT], Disposition::Ignore);
1487        assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1488        assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1489        assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1490        assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1491        assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1492    }
1493
1494    #[test]
1495    fn reenabling_internal_dispositions() {
1496        let mut system = DummySystem::default();
1497        let mut trap_set = TrapSet::default();
1498        trap_set
1499            .enable_internal_disposition_for_sigchld(&mut system)
1500            .unwrap();
1501        trap_set
1502            .enable_internal_disposition_for_sigchld(&mut system)
1503            .unwrap();
1504        trap_set
1505            .enable_internal_dispositions_for_terminators(&mut system)
1506            .unwrap();
1507        trap_set
1508            .enable_internal_dispositions_for_terminators(&mut system)
1509            .unwrap();
1510        trap_set
1511            .enable_internal_dispositions_for_stoppers(&mut system)
1512            .unwrap();
1513        trap_set
1514            .enable_internal_dispositions_for_stoppers(&mut system)
1515            .unwrap();
1516        trap_set.disable_internal_dispositions(&mut system).unwrap();
1517        trap_set
1518            .enable_internal_disposition_for_sigchld(&mut system)
1519            .unwrap();
1520        trap_set
1521            .enable_internal_dispositions_for_terminators(&mut system)
1522            .unwrap();
1523        trap_set
1524            .enable_internal_dispositions_for_stoppers(&mut system)
1525            .unwrap();
1526        assert_eq!(system.0[&SIGCHLD], Disposition::Catch);
1527        assert_eq!(system.0[&SIGINT], Disposition::Catch);
1528        assert_eq!(system.0[&SIGTERM], Disposition::Ignore);
1529        assert_eq!(system.0[&SIGQUIT], Disposition::Ignore);
1530        assert_eq!(system.0[&SIGTSTP], Disposition::Ignore);
1531        assert_eq!(system.0[&SIGTTIN], Disposition::Ignore);
1532        assert_eq!(system.0[&SIGTTOU], Disposition::Ignore);
1533    }
1534
1535    #[test]
1536    fn setting_trap_to_ignore_after_enabling_internal_disposition() {
1537        let mut system = DummySystem::default();
1538        let mut trap_set = TrapSet::default();
1539        trap_set
1540            .enable_internal_disposition_for_sigchld(&mut system)
1541            .unwrap();
1542        let origin = Location::dummy("origin");
1543        let result = trap_set.set_action(&mut system, SIGCHLD, Action::Ignore, origin, false);
1544        assert_eq!(result, Ok(()));
1545        assert_eq!(system.0[&SIGCHLD], Disposition::Catch);
1546    }
1547
1548    #[test]
1549    fn resetting_trap_from_ignore_no_override_after_enabling_internal_dispositions() {
1550        let mut system = DummySystem::default();
1551        ignore_signals(&mut system);
1552        let mut trap_set = TrapSet::default();
1553        trap_set
1554            .enable_internal_disposition_for_sigchld(&mut system)
1555            .unwrap();
1556        trap_set
1557            .enable_internal_dispositions_for_terminators(&mut system)
1558            .unwrap();
1559        trap_set
1560            .enable_internal_dispositions_for_stoppers(&mut system)
1561            .unwrap();
1562
1563        for signal in [SIGCHLD, SIGINT] {
1564            let origin = Location::dummy("origin");
1565            let result = trap_set.set_action(&mut system, signal, Action::Default, origin, false);
1566            assert_eq!(result, Err(SetActionError::InitiallyIgnored));
1567            assert_eq!(system.0[&signal], Disposition::Catch);
1568        }
1569        for signal in [SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU] {
1570            let origin = Location::dummy("origin");
1571            let result = trap_set.set_action(&mut system, signal, Action::Default, origin, false);
1572            assert_eq!(result, Err(SetActionError::InitiallyIgnored));
1573            assert_eq!(system.0[&signal], Disposition::Ignore);
1574        }
1575    }
1576
1577    #[test]
1578    fn resetting_trap_from_ignore_override_after_enabling_internal_dispositions() {
1579        let mut system = DummySystem::default();
1580        ignore_signals(&mut system);
1581        let mut trap_set = TrapSet::default();
1582        trap_set
1583            .enable_internal_disposition_for_sigchld(&mut system)
1584            .unwrap();
1585        trap_set
1586            .enable_internal_dispositions_for_terminators(&mut system)
1587            .unwrap();
1588        trap_set
1589            .enable_internal_dispositions_for_stoppers(&mut system)
1590            .unwrap();
1591
1592        for signal in [SIGCHLD, SIGINT] {
1593            let origin = Location::dummy("origin");
1594            let result =
1595                trap_set.set_action(&mut system, signal, Action::Ignore, origin.clone(), true);
1596            assert_eq!(result, Ok(()));
1597            assert_eq!(
1598                trap_set.get_state(signal),
1599                (
1600                    Some(&TrapState {
1601                        action: Action::Ignore,
1602                        origin: Origin::User(origin),
1603                        pending: false
1604                    }),
1605                    None
1606                )
1607            );
1608            assert_eq!(system.0[&signal], Disposition::Catch);
1609        }
1610        for signal in [SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU] {
1611            let origin = Location::dummy("origin");
1612            let result =
1613                trap_set.set_action(&mut system, signal, Action::Ignore, origin.clone(), true);
1614            assert_eq!(result, Ok(()));
1615            assert_eq!(
1616                trap_set.get_state(signal),
1617                (
1618                    Some(&TrapState {
1619                        action: Action::Ignore,
1620                        origin: Origin::User(origin),
1621                        pending: false
1622                    }),
1623                    None
1624                )
1625            );
1626            assert_eq!(system.0[&signal], Disposition::Ignore);
1627        }
1628    }
1629
1630    #[test]
1631    fn disabling_internal_disposition_with_ignore_trap() {
1632        let signals = [SIGCHLD, SIGINT, SIGTERM, SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU];
1633
1634        let mut system = DummySystem::default();
1635        let mut trap_set = TrapSet::default();
1636        trap_set
1637            .enable_internal_disposition_for_sigchld(&mut system)
1638            .unwrap();
1639        trap_set
1640            .enable_internal_dispositions_for_terminators(&mut system)
1641            .unwrap();
1642        let origin = Location::dummy("origin");
1643        for signal in signals {
1644            trap_set
1645                .set_action(&mut system, signal, Action::Ignore, origin.clone(), false)
1646                .unwrap();
1647        }
1648        trap_set.disable_internal_dispositions(&mut system).unwrap();
1649
1650        for signal in signals {
1651            assert_eq!(
1652                trap_set.get_state(signal),
1653                (
1654                    Some(&TrapState {
1655                        action: Action::Ignore,
1656                        origin: Origin::User(origin.clone()),
1657                        pending: false
1658                    }),
1659                    None
1660                )
1661            );
1662            assert_eq!(system.0[&signal], Disposition::Ignore);
1663        }
1664    }
1665}