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