Skip to main content

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