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