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