Skip to main content

yash_env/system/
signal.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2025 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-related functionality for the system module
18
19#[cfg(doc)]
20use super::Concurrent;
21use super::{Pid, Result};
22pub use crate::signal::{Name, Number, RawNumber};
23use std::borrow::Cow;
24use std::fmt::Debug;
25use std::num::NonZero;
26use std::ops::RangeInclusive;
27use std::rc::Rc;
28
29/// Trait for managing available signals
30pub trait Signals {
31    /// The signal number for `SIGABRT`
32    const SIGABRT: Number;
33    /// The signal number for `SIGALRM`
34    const SIGALRM: Number;
35    /// The signal number for `SIGBUS`
36    const SIGBUS: Number;
37    /// The signal number for `SIGCHLD`
38    const SIGCHLD: Number;
39    /// The signal number for `SIGCLD`, if available on the system
40    const SIGCLD: Option<Number>;
41    /// The signal number for `SIGCONT`
42    const SIGCONT: Number;
43    /// The signal number for `SIGEMT`, if available on the system
44    const SIGEMT: Option<Number>;
45    /// The signal number for `SIGFPE`
46    const SIGFPE: Number;
47    /// The signal number for `SIGHUP`
48    const SIGHUP: Number;
49    /// The signal number for `SIGILL`
50    const SIGILL: Number;
51    /// The signal number for `SIGINFO`, if available on the system
52    const SIGINFO: Option<Number>;
53    /// The signal number for `SIGINT`
54    const SIGINT: Number;
55    /// The signal number for `SIGIO`, if available on the system
56    const SIGIO: Option<Number>;
57    /// The signal number for `SIGIOT`
58    const SIGIOT: Number;
59    /// The signal number for `SIGKILL`
60    const SIGKILL: Number;
61    /// The signal number for `SIGLOST`, if available on the system
62    const SIGLOST: Option<Number>;
63    /// The signal number for `SIGPIPE`
64    const SIGPIPE: Number;
65    /// The signal number for `SIGPOLL`, if available on the system
66    const SIGPOLL: Option<Number>;
67    /// The signal number for `SIGPROF`
68    const SIGPROF: Number;
69    /// The signal number for `SIGPWR`, if available on the system
70    const SIGPWR: Option<Number>;
71    /// The signal number for `SIGQUIT`
72    const SIGQUIT: Number;
73    /// The signal number for `SIGSEGV`
74    const SIGSEGV: Number;
75    /// The signal number for `SIGSTKFLT`, if available on the system
76    const SIGSTKFLT: Option<Number>;
77    /// The signal number for `SIGSTOP`
78    const SIGSTOP: Number;
79    /// The signal number for `SIGSYS`
80    const SIGSYS: Number;
81    /// The signal number for `SIGTERM`
82    const SIGTERM: Number;
83    /// The signal number for `SIGTHR`, if available on the system
84    const SIGTHR: Option<Number>;
85    /// The signal number for `SIGTRAP`
86    const SIGTRAP: Number;
87    /// The signal number for `SIGTSTP`
88    const SIGTSTP: Number;
89    /// The signal number for `SIGTTIN`
90    const SIGTTIN: Number;
91    /// The signal number for `SIGTTOU`
92    const SIGTTOU: Number;
93    /// The signal number for `SIGURG`
94    const SIGURG: Number;
95    /// The signal number for `SIGUSR1`
96    const SIGUSR1: Number;
97    /// The signal number for `SIGUSR2`
98    const SIGUSR2: Number;
99    /// The signal number for `SIGVTALRM`
100    const SIGVTALRM: Number;
101    /// The signal number for `SIGWINCH`
102    const SIGWINCH: Number;
103    /// The signal number for `SIGXCPU`
104    const SIGXCPU: Number;
105    /// The signal number for `SIGXFSZ`
106    const SIGXFSZ: Number;
107
108    /// Returns the range of real-time signals supported by the system.
109    ///
110    /// If the system does not support real-time signals, returns `None`.
111    ///
112    /// The range is provided as a method rather than associated constants
113    /// because some systems determine the range at runtime.
114    #[must_use]
115    fn sigrt_range(&self) -> Option<RangeInclusive<Number>>;
116
117    /// List of all signal names and their numbers, excluding real-time signals
118    ///
119    /// This list contains all named signals declared in this trait, except for
120    /// real-time signals. Each entry is a tuple of the signal name (without the
121    /// `SIG` prefix) and its corresponding signal number. If a signal is not
122    /// available on the system, its number is `None`.
123    ///
124    /// The signals are listed in alphabetical order by name (without the `SIG`
125    /// prefix). Implementations that override this constant must preserve this
126    /// ordering because the default implementation of
127    /// [`str2sig`](Self::str2sig) relies on it to perform a binary search.
128    const NAMED_SIGNALS: &'static [(&'static str, Option<Number>)] = &[
129        ("ABRT", Some(Self::SIGABRT)),
130        ("ALRM", Some(Self::SIGALRM)),
131        ("BUS", Some(Self::SIGBUS)),
132        ("CHLD", Some(Self::SIGCHLD)),
133        ("CLD", Self::SIGCLD),
134        ("CONT", Some(Self::SIGCONT)),
135        ("EMT", Self::SIGEMT),
136        ("FPE", Some(Self::SIGFPE)),
137        ("HUP", Some(Self::SIGHUP)),
138        ("ILL", Some(Self::SIGILL)),
139        ("INFO", Self::SIGINFO),
140        ("INT", Some(Self::SIGINT)),
141        ("IO", Self::SIGIO),
142        ("IOT", Some(Self::SIGIOT)),
143        ("KILL", Some(Self::SIGKILL)),
144        ("LOST", Self::SIGLOST),
145        ("PIPE", Some(Self::SIGPIPE)),
146        ("POLL", Self::SIGPOLL),
147        ("PROF", Some(Self::SIGPROF)),
148        ("PWR", Self::SIGPWR),
149        ("QUIT", Some(Self::SIGQUIT)),
150        ("SEGV", Some(Self::SIGSEGV)),
151        ("STKFLT", Self::SIGSTKFLT),
152        ("STOP", Some(Self::SIGSTOP)),
153        ("SYS", Some(Self::SIGSYS)),
154        ("TERM", Some(Self::SIGTERM)),
155        ("THR", Self::SIGTHR),
156        ("TRAP", Some(Self::SIGTRAP)),
157        ("TSTP", Some(Self::SIGTSTP)),
158        ("TTIN", Some(Self::SIGTTIN)),
159        ("TTOU", Some(Self::SIGTTOU)),
160        ("URG", Some(Self::SIGURG)),
161        ("USR1", Some(Self::SIGUSR1)),
162        ("USR2", Some(Self::SIGUSR2)),
163        ("VTALRM", Some(Self::SIGVTALRM)),
164        ("WINCH", Some(Self::SIGWINCH)),
165        ("XCPU", Some(Self::SIGXCPU)),
166        ("XFSZ", Some(Self::SIGXFSZ)),
167    ];
168
169    /// Returns an iterator over all real-time signals supported by the system.
170    ///
171    /// The iterator yields signal numbers in ascending order. If the system
172    /// does not support real-time signals, the iterator yields no items.
173    fn iter_sigrt(&self) -> impl DoubleEndedIterator<Item = Number> + use<Self> {
174        let range = match self.sigrt_range() {
175            Some(range) => range.start().as_raw()..=range.end().as_raw(),
176            #[allow(clippy::reversed_empty_ranges, reason = "false positive")]
177            None => 0..=-1,
178        };
179        // If NonZero implemented Step, we could use range.map(...)
180        range.filter_map(|raw| NonZero::new(raw).map(Number::from_raw_unchecked))
181    }
182
183    /// Tests if a signal number is valid and returns its signal number.
184    ///
185    /// This function returns `Some(number)` if the signal number refers to a valid
186    /// signal supported by the system. Otherwise, it returns `None`.
187    #[must_use]
188    fn to_signal_number<N: Into<RawNumber>>(&self, number: N) -> Option<Number> {
189        fn inner<S: Signals + ?Sized>(system: &S, raw_number: RawNumber) -> Option<Number> {
190            let non_zero = NonZero::new(raw_number)?;
191            let number = Number::from_raw_unchecked(non_zero);
192            (S::NAMED_SIGNALS
193                .iter()
194                .any(|signal| signal.1 == Some(number))
195                || system
196                    .sigrt_range()
197                    .is_some_and(|range| range.contains(&number)))
198            .then_some(number)
199        }
200        inner(self, number.into())
201    }
202
203    /// Converts a signal number to its string representation.
204    ///
205    /// This function returns `Some(name)` if the signal number refers to a valid
206    /// signal supported by the system. Otherwise, it returns `None`.
207    ///
208    /// The returned name does not include the `SIG` prefix.
209    /// Note that one signal number can have multiple names, in which case it is
210    /// unspecified which name is returned.
211    #[must_use]
212    fn sig2str<N: Into<RawNumber>>(&self, signal: N) -> Option<Cow<'static, str>> {
213        fn inner<S: Signals + ?Sized>(
214            system: &S,
215            raw_number: RawNumber,
216        ) -> Option<Cow<'static, str>> {
217            let number = Number::from_raw_unchecked(NonZero::new(raw_number)?);
218            // The signals below are ordered roughly by frequency of use
219            // so that common names are preferred for signals with multiple names.
220            match () {
221                () if number == S::SIGABRT => Some(Cow::Borrowed("ABRT")),
222                () if number == S::SIGALRM => Some(Cow::Borrowed("ALRM")),
223                () if number == S::SIGBUS => Some(Cow::Borrowed("BUS")),
224                () if number == S::SIGCHLD => Some(Cow::Borrowed("CHLD")),
225                () if number == S::SIGCONT => Some(Cow::Borrowed("CONT")),
226                () if number == S::SIGFPE => Some(Cow::Borrowed("FPE")),
227                () if number == S::SIGHUP => Some(Cow::Borrowed("HUP")),
228                () if number == S::SIGILL => Some(Cow::Borrowed("ILL")),
229                () if number == S::SIGINT => Some(Cow::Borrowed("INT")),
230                () if number == S::SIGKILL => Some(Cow::Borrowed("KILL")),
231                () if number == S::SIGPIPE => Some(Cow::Borrowed("PIPE")),
232                () if number == S::SIGQUIT => Some(Cow::Borrowed("QUIT")),
233                () if number == S::SIGSEGV => Some(Cow::Borrowed("SEGV")),
234                () if number == S::SIGSTOP => Some(Cow::Borrowed("STOP")),
235                () if number == S::SIGTERM => Some(Cow::Borrowed("TERM")),
236                () if number == S::SIGTSTP => Some(Cow::Borrowed("TSTP")),
237                () if number == S::SIGTTIN => Some(Cow::Borrowed("TTIN")),
238                () if number == S::SIGTTOU => Some(Cow::Borrowed("TTOU")),
239                () if number == S::SIGUSR1 => Some(Cow::Borrowed("USR1")),
240                () if number == S::SIGUSR2 => Some(Cow::Borrowed("USR2")),
241                () if Some(number) == S::SIGPOLL => Some(Cow::Borrowed("POLL")),
242                () if number == S::SIGPROF => Some(Cow::Borrowed("PROF")),
243                () if number == S::SIGSYS => Some(Cow::Borrowed("SYS")),
244                () if number == S::SIGTRAP => Some(Cow::Borrowed("TRAP")),
245                () if number == S::SIGURG => Some(Cow::Borrowed("URG")),
246                () if number == S::SIGVTALRM => Some(Cow::Borrowed("VTALRM")),
247                () if number == S::SIGWINCH => Some(Cow::Borrowed("WINCH")),
248                () if number == S::SIGXCPU => Some(Cow::Borrowed("XCPU")),
249                () if number == S::SIGXFSZ => Some(Cow::Borrowed("XFSZ")),
250                () if Some(number) == S::SIGEMT => Some(Cow::Borrowed("EMT")),
251                () if Some(number) == S::SIGINFO => Some(Cow::Borrowed("INFO")),
252                () if Some(number) == S::SIGIO => Some(Cow::Borrowed("IO")),
253                () if Some(number) == S::SIGLOST => Some(Cow::Borrowed("LOST")),
254                () if Some(number) == S::SIGPWR => Some(Cow::Borrowed("PWR")),
255                () if Some(number) == S::SIGSTKFLT => Some(Cow::Borrowed("STKFLT")),
256                () if Some(number) == S::SIGTHR => Some(Cow::Borrowed("THR")),
257                _ => {
258                    let range = system.sigrt_range()?;
259                    if number == *range.start() {
260                        Some(Cow::Borrowed("RTMIN"))
261                    } else if number == *range.end() {
262                        Some(Cow::Borrowed("RTMAX"))
263                    } else if range.contains(&number) {
264                        let rtmin = range.start().as_raw();
265                        let rtmax = range.end().as_raw();
266                        if raw_number <= rtmin.midpoint(rtmax) {
267                            let offset = raw_number - rtmin;
268                            Some(Cow::Owned(format!("RTMIN+{}", offset)))
269                        } else {
270                            let offset = rtmax - raw_number;
271                            Some(Cow::Owned(format!("RTMAX-{}", offset)))
272                        }
273                    } else {
274                        None
275                    }
276                }
277            }
278        }
279        inner(self, signal.into())
280    }
281
282    /// Converts a string representation of a signal to its signal number.
283    ///
284    /// This function returns `Some(number)` if the signal name is supported by
285    /// the system. Otherwise, it returns `None`.
286    ///
287    /// The input name should not include the `SIG` prefix, and is case-sensitive.
288    #[must_use]
289    fn str2sig(&self, name: &str) -> Option<Number> {
290        // Binary search on NAMED_SIGNALS
291        if let Ok(index) = Self::NAMED_SIGNALS.binary_search_by_key(&name, |s| s.0) {
292            return Self::NAMED_SIGNALS[index].1;
293        }
294
295        // Handle real-time signals
296        enum BaseName {
297            Rtmin,
298            Rtmax,
299        }
300        #[allow(clippy::question_mark, reason = "keeps the if/else branches symmetric")]
301        let (basename, suffix) = if let Some(suffix) = name.strip_prefix("RTMIN") {
302            (BaseName::Rtmin, suffix)
303        } else if let Some(suffix) = name.strip_prefix("RTMAX") {
304            (BaseName::Rtmax, suffix)
305        } else {
306            return None;
307        };
308        if !suffix.is_empty() && !suffix.starts_with(['+', '-']) {
309            return None;
310        }
311        let range = self.sigrt_range()?;
312        let base_raw = match basename {
313            BaseName::Rtmin => range.start().as_raw(),
314            BaseName::Rtmax => range.end().as_raw(),
315        };
316        let raw_number = if suffix.is_empty() {
317            base_raw
318        } else {
319            let offset: RawNumber = suffix.parse().ok()?;
320            base_raw.checked_add(offset)?
321        };
322        let number = Number::from_raw_unchecked(NonZero::new(raw_number)?);
323        range.contains(&number).then_some(number)
324    }
325
326    /// Tests if a signal number is valid and returns its name and number.
327    ///
328    /// This function returns `Some((name, number))` if the signal number refers
329    /// to a valid signal supported by the system. Otherwise, it returns `None`.
330    ///
331    /// Note that one signal number can have multiple names, in which case this
332    /// function returns the name that is considered the most common.
333    ///
334    /// If you only need to tell whether a signal number is valid, use
335    /// [`to_signal_number`](Self::to_signal_number), which is more efficient.
336    #[must_use]
337    fn validate_signal(&self, number: RawNumber) -> Option<(Name, Number)> {
338        let number = Number::from_raw_unchecked(NonZero::new(number)?);
339        let str_name = self.sig2str(number)?;
340        Some((str_name.parse().ok()?, number))
341    }
342
343    /// Returns the signal name for the signal number.
344    ///
345    /// This function returns the signal name for the given signal number.
346    ///
347    /// If the signal number is invalid, this function panics. It may occur if
348    /// the number is from a different system or was created without checking
349    /// the validity.
350    ///
351    /// Note that one signal number can have multiple names, in which case this
352    /// function returns the name that is considered the most common.
353    #[must_use]
354    fn signal_name_from_number(&self, number: Number) -> Name {
355        self.validate_signal(number.as_raw()).unwrap().0
356    }
357
358    /// Gets the signal number from the signal name.
359    ///
360    /// This function returns the signal number corresponding to the signal name
361    /// in the system. If the signal name is not supported, it returns `None`.
362    #[must_use]
363    fn signal_number_from_name(&self, name: Name) -> Option<Number> {
364        self.str2sig(&name.as_string())
365    }
366}
367
368/// Delegates the `Signals` trait to the contained instance of `S`
369impl<S: Signals> Signals for Rc<S> {
370    const SIGABRT: Number = S::SIGABRT;
371    const SIGALRM: Number = S::SIGALRM;
372    const SIGBUS: Number = S::SIGBUS;
373    const SIGCHLD: Number = S::SIGCHLD;
374    const SIGCLD: Option<Number> = S::SIGCLD;
375    const SIGCONT: Number = S::SIGCONT;
376    const SIGEMT: Option<Number> = S::SIGEMT;
377    const SIGFPE: Number = S::SIGFPE;
378    const SIGHUP: Number = S::SIGHUP;
379    const SIGILL: Number = S::SIGILL;
380    const SIGINFO: Option<Number> = S::SIGINFO;
381    const SIGINT: Number = S::SIGINT;
382    const SIGIO: Option<Number> = S::SIGIO;
383    const SIGIOT: Number = S::SIGIOT;
384    const SIGKILL: Number = S::SIGKILL;
385    const SIGLOST: Option<Number> = S::SIGLOST;
386    const SIGPIPE: Number = S::SIGPIPE;
387    const SIGPOLL: Option<Number> = S::SIGPOLL;
388    const SIGPROF: Number = S::SIGPROF;
389    const SIGPWR: Option<Number> = S::SIGPWR;
390    const SIGQUIT: Number = S::SIGQUIT;
391    const SIGSEGV: Number = S::SIGSEGV;
392    const SIGSTKFLT: Option<Number> = S::SIGSTKFLT;
393    const SIGSTOP: Number = S::SIGSTOP;
394    const SIGSYS: Number = S::SIGSYS;
395    const SIGTERM: Number = S::SIGTERM;
396    const SIGTHR: Option<Number> = S::SIGTHR;
397    const SIGTRAP: Number = S::SIGTRAP;
398    const SIGTSTP: Number = S::SIGTSTP;
399    const SIGTTIN: Number = S::SIGTTIN;
400    const SIGTTOU: Number = S::SIGTTOU;
401    const SIGURG: Number = S::SIGURG;
402    const SIGUSR1: Number = S::SIGUSR1;
403    const SIGUSR2: Number = S::SIGUSR2;
404    const SIGVTALRM: Number = S::SIGVTALRM;
405    const SIGWINCH: Number = S::SIGWINCH;
406    const SIGXCPU: Number = S::SIGXCPU;
407    const SIGXFSZ: Number = S::SIGXFSZ;
408
409    #[inline]
410    fn sigrt_range(&self) -> Option<RangeInclusive<Number>> {
411        (self as &S).sigrt_range()
412    }
413
414    const NAMED_SIGNALS: &'static [(&'static str, Option<Number>)] = S::NAMED_SIGNALS;
415
416    #[inline]
417    fn iter_sigrt(&self) -> impl DoubleEndedIterator<Item = Number> + use<S> {
418        (self as &S).iter_sigrt()
419    }
420    #[inline]
421    fn to_signal_number<N: Into<RawNumber>>(&self, number: N) -> Option<Number> {
422        (self as &S).to_signal_number(number)
423    }
424    #[inline]
425    fn sig2str<N: Into<RawNumber>>(&self, signal: N) -> Option<Cow<'static, str>> {
426        (self as &S).sig2str(signal)
427    }
428    #[inline]
429    fn str2sig(&self, name: &str) -> Option<Number> {
430        (self as &S).str2sig(name)
431    }
432    #[inline]
433    fn validate_signal(&self, number: RawNumber) -> Option<(Name, Number)> {
434        (self as &S).validate_signal(number)
435    }
436    #[inline]
437    fn signal_name_from_number(&self, number: Number) -> Name {
438        (self as &S).signal_name_from_number(number)
439    }
440    #[inline]
441    fn signal_number_from_name(&self, name: Name) -> Option<Number> {
442        (self as &S).signal_number_from_name(name)
443    }
444}
445
446/// Operation applied to the signal blocking mask
447///
448/// This enum corresponds to the operations of the `sigprocmask` system call and
449/// is used in the [`Sigmask::sigmask`] method.
450#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
451#[non_exhaustive]
452pub enum SigmaskOp {
453    /// Add signals to the mask (`SIG_BLOCK`)
454    Add,
455    /// Remove signals from the mask (`SIG_UNBLOCK`)
456    Remove,
457    /// Set the mask to the given signals (`SIG_SETMASK`)
458    Set,
459}
460
461/// Interface for signal sets
462///
463/// This trait is implemented by types that represent sets of signals,
464/// abstracting the functionality of the `sigset_t` type in POSIX.
465///
466/// Implementors of this trait must also implement the `Default` trait, where
467/// the default value must be an empty signal set.
468pub trait Sigset: Clone + Default + 'static {
469    /// Creates a new, empty signal set.
470    ///
471    /// The provided implementation simply calls [`Default::default`].
472    #[inline(always)]
473    #[must_use]
474    fn new() -> Self {
475        Default::default()
476    }
477
478    /// Creates a new signal set containing all signals.
479    #[must_use]
480    fn full() -> Self;
481
482    /// Adds a signal to the set.
483    fn insert(&mut self, signal: Number) -> Result<()>;
484
485    /// Removes a signal from the set.
486    fn remove(&mut self, signal: Number) -> Result<()>;
487
488    /// Checks if a signal is in the set.
489    fn contains(&self, signal: Number) -> Result<bool>;
490
491    /// Creates a signal set from an iterator of signal numbers.
492    ///
493    /// This method is a convenient way to create a signal set from a list of
494    /// signal numbers. It iterates over the provided signal numbers and
495    /// [inserts](Self::insert) them to a new signal set. If any call to
496    /// `insert` fails, this method returns the error immediately. Otherwise, it
497    /// returns the resulting signal set.
498    fn from_signals<I>(iter: I) -> Result<Self>
499    where
500        I: IntoIterator<Item = Number>,
501    {
502        let mut set = Self::new();
503        for signal in iter {
504            set.insert(signal)?;
505        }
506        Ok(set)
507    }
508}
509
510/// Trait for managing signal blocking mask
511pub trait Sigmask: Signals {
512    /// The type representing a set of signals for this system
513    ///
514    /// This type is used in the [`sigmask`](Self::sigmask) method to specify
515    /// which signals to block or unblock.
516    type Sigset: Sigset + Debug;
517
518    /// Gets and/or sets the signal blocking mask.
519    ///
520    /// This trait is usually not used directly. Instead, it is used by
521    /// [`Concurrent`] to configure signal handling behavior of the process.
522    ///
523    /// This is a thin wrapper around the [`sigprocmask` system
524    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_sigmask.html).
525    /// If `op` is `Some`, this function updates the signal blocking mask by
526    /// applying the given `SigmaskOp` and signal set to the current mask. If
527    /// `op` is `None`, this function does not change the mask.
528    /// If `old_mask` is `Some`, this function sets the previous mask to it.
529    ///
530    /// The return type is a future so that
531    /// [virtual systems](crate::system::virtual) can simulate termination or
532    /// suspension of the process that may be caused by a signal delivered as a
533    /// result of changing (for example, unblocking) the signal mask. In the
534    /// [real system](super::real), this function does not work asynchronously
535    /// and returns a ready `Future` with the result of the underlying system
536    /// call. See the [module-level documentation](super) for details.
537    fn sigmask(
538        &self,
539        op: Option<(SigmaskOp, &Self::Sigset)>,
540        old_mask: Option<&mut Self::Sigset>,
541    ) -> impl Future<Output = Result<()>> + use<Self>;
542}
543
544/// Delegates the `Sigmask` trait to the contained instance of `S`
545impl<S: Sigmask> Sigmask for Rc<S> {
546    type Sigset = S::Sigset;
547
548    #[inline]
549    fn sigmask(
550        &self,
551        op: Option<(SigmaskOp, &Self::Sigset)>,
552        old_mask: Option<&mut Self::Sigset>,
553    ) -> impl Future<Output = Result<()>> + use<S> {
554        (self as &S).sigmask(op, old_mask)
555    }
556}
557
558/// How the shell process responds to a signal
559#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
560pub enum Disposition {
561    /// Perform the default action for the signal.
562    ///
563    /// The default action depends on the signal. For example, `SIGINT` causes
564    /// the process to terminate, and `SIGTSTP` causes the process to stop.
565    #[default]
566    Default,
567    /// Ignore the signal.
568    Ignore,
569    /// Catch the signal.
570    Catch,
571}
572
573/// Trait for getting signal dispositions
574pub trait GetSigaction: Signals {
575    /// Gets the disposition for a signal.
576    ///
577    /// This is an abstract wrapper around the [`sigaction` system
578    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigaction.html).
579    /// This function returns the current disposition if successful.
580    ///
581    /// To change the disposition, use [`Sigaction::sigaction`].
582    fn get_sigaction(&self, signal: Number) -> Result<Disposition>;
583}
584
585/// Delegates the `GetSigaction` trait to the contained instance of `S`
586impl<S: GetSigaction> GetSigaction for Rc<S> {
587    #[inline]
588    fn get_sigaction(&self, signal: Number) -> Result<Disposition> {
589        (self as &S).get_sigaction(signal)
590    }
591}
592
593/// Trait for managing signal dispositions
594pub trait Sigaction: GetSigaction {
595    /// Gets and sets the disposition for a signal.
596    ///
597    /// This trait is usually not used directly. Instead, it is used by
598    /// [`Concurrent`] to configure signal handling behavior of the process.
599    ///
600    /// This is an abstract wrapper around the [`sigaction` system
601    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigaction.html).
602    /// This function returns the previous disposition if successful.
603    ///
604    /// When you set the disposition to `Disposition::Catch`, signals sent to
605    /// this process are accumulated in `self` and made available from
606    /// [`caught_signals`](CaughtSignals::caught_signals).
607    ///
608    /// To get the current disposition without changing it, use
609    /// [`GetSigaction::get_sigaction`].
610    fn sigaction(&self, signal: Number, action: Disposition) -> Result<Disposition>;
611}
612
613/// Delegates the `Sigaction` trait to the contained instance of `S`
614impl<S: Sigaction> Sigaction for Rc<S> {
615    #[inline]
616    fn sigaction(&self, signal: Number, action: Disposition) -> Result<Disposition> {
617        (self as &S).sigaction(signal, action)
618    }
619}
620
621/// Trait for examining signals caught by the process
622///
623/// Implementors of this trait usually also implement [`Sigaction`] to allow
624/// setting which signals are caught.
625pub trait CaughtSignals: Signals {
626    /// Returns signals this process has caught, if any.
627    ///
628    /// This trait is usually not used directly. Instead, it is used by
629    /// [`Concurrent`] to collect signals caught by the process.
630    ///
631    /// Implementors of this trait usually also implement [`Sigaction`] to allow
632    /// setting which signals are caught.
633    /// To catch a signal, you firstly install a signal handler by calling
634    /// [`Sigaction::sigaction`] with [`Disposition::Catch`]. Once the handler
635    /// is ready, signals sent to the process are accumulated in the
636    /// implementor. Calling this function retrieves the list of caught signals.
637    ///
638    /// This function clears the internal list of caught signals, so a next call
639    /// will return an empty list unless another signal is caught since the
640    /// first call. Because the list size may be limited, you should call this
641    /// function periodically before the list gets full, in which case further
642    /// caught signals are silently ignored.
643    ///
644    /// Note that signals become pending if sent while blocked by
645    /// [`Sigmask::sigmask`]. They must be unblocked so that they are caught and
646    /// made available from this function.
647    fn caught_signals(&self) -> Vec<Number>;
648}
649
650/// Delegates the `CaughtSignals` trait to the contained instance of `S`
651impl<S: CaughtSignals> CaughtSignals for Rc<S> {
652    #[inline]
653    fn caught_signals(&self) -> Vec<Number> {
654        (self as &S).caught_signals()
655    }
656}
657
658/// Trait for sending signals to processes
659pub trait SendSignal: Signals {
660    /// Sends a signal.
661    ///
662    /// This is a thin wrapper around the [`kill` system
663    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/kill.html).
664    /// If `signal` is `None`, permission to send a signal is checked, but no
665    /// signal is sent.
666    ///
667    /// The virtual system version of this function blocks the calling thread if
668    /// the signal stops or terminates the current process, hence returning a
669    /// future. See [`VirtualSystem::kill`] for details.
670    ///
671    /// [`VirtualSystem::kill`]: crate::system::virtual::VirtualSystem::kill
672    fn kill(
673        &self,
674        target: Pid,
675        signal: Option<Number>,
676    ) -> impl Future<Output = Result<()>> + use<Self>;
677
678    /// Sends a signal to the current process.
679    ///
680    /// This is a thin wrapper around the `raise` system call.
681    ///
682    /// The virtual system version of this function blocks the calling thread if
683    /// the signal stops or terminates the current process, hence returning a
684    /// future. See [`VirtualSystem::kill`] for details.
685    ///
686    /// [`VirtualSystem::kill`]: crate::system::virtual::VirtualSystem::kill
687    fn raise(&self, signal: Number) -> impl Future<Output = Result<()>> + use<Self>;
688}
689
690/// Delegates the `SendSignal` trait to the contained instance of `S`
691impl<S: SendSignal> SendSignal for Rc<S> {
692    #[inline]
693    fn kill(
694        &self,
695        target: Pid,
696        signal: Option<Number>,
697    ) -> impl Future<Output = Result<()>> + use<S> {
698        (self as &S).kill(target, signal)
699    }
700    #[inline]
701    fn raise(&self, signal: Number) -> impl Future<Output = Result<()>> + use<S> {
702        (self as &S).raise(signal)
703    }
704}