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::SharedSystem;
21use super::{FlexFuture, Pid, Result};
22pub use crate::signal::{Name, Number, RawNumber};
23
24/// Trait for managing available signals
25pub trait Signals {
26    /// Tests if a signal number is valid.
27    ///
28    /// This function returns `Some((name, number))` if the signal number refers
29    /// to a valid signal supported by the system. Otherwise, it returns `None`.
30    ///
31    /// Note that one signal number can have multiple names, in which case this
32    /// function returns the name that is considered the most common.
33    #[must_use]
34    fn validate_signal(&self, number: RawNumber) -> Option<(Name, Number)>;
35
36    /// Returns the signal name for the signal number.
37    ///
38    /// This function returns the signal name for the given signal number.
39    ///
40    /// If the signal number is invalid, this function panics. It may occur if
41    /// the number is from a different system or was created without checking
42    /// the validity.
43    ///
44    /// Note that one signal number can have multiple names, in which case this
45    /// function returns the name that is considered the most common.
46    #[must_use]
47    fn signal_name_from_number(&self, number: Number) -> Name {
48        self.validate_signal(number.as_raw()).unwrap().0
49    }
50
51    /// Gets the signal number from the signal name.
52    ///
53    /// This function returns the signal number corresponding to the signal name
54    /// in the system. If the signal name is not supported, it returns `None`.
55    #[must_use]
56    fn signal_number_from_name(&self, name: Name) -> Option<Number>;
57}
58
59/// Operation applied to the signal blocking mask
60///
61/// This enum corresponds to the operations of the `sigprocmask` system call and
62/// is used in the [`Sigmask::sigmask`] method.
63#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
64#[non_exhaustive]
65pub enum SigmaskOp {
66    /// Add signals to the mask (`SIG_BLOCK`)
67    Add,
68    /// Remove signals from the mask (`SIG_UNBLOCK`)
69    Remove,
70    /// Set the mask to the given signals (`SIG_SETMASK`)
71    Set,
72}
73
74/// Trait for managing signal blocking mask
75pub trait Sigmask {
76    /// Gets and/or sets the signal blocking mask.
77    ///
78    /// This is a low-level function used internally by [`SharedSystem`]. You
79    /// should not call this function directly, or you will disrupt the behavior
80    /// of `SharedSystem`. The description below applies if you want to do
81    /// everything yourself without depending on `SharedSystem`.
82    ///
83    /// This is a thin wrapper around the [`sigprocmask` system
84    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_sigmask.html).
85    /// If `op` is `Some`, this function updates the signal blocking mask by
86    /// applying the given `SigmaskOp` and signal set to the current mask. If
87    /// `op` is `None`, this function does not change the mask.
88    /// If `old_mask` is `Some`, this function sets the previous mask to it.
89    fn sigmask(
90        &self,
91        op: Option<(SigmaskOp, &[Number])>,
92        old_mask: Option<&mut Vec<Number>>,
93    ) -> Result<()>;
94}
95
96/// How the shell process responds to a signal
97#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
98pub enum Disposition {
99    /// Perform the default action for the signal.
100    ///
101    /// The default action depends on the signal. For example, `SIGINT` causes
102    /// the process to terminate, and `SIGTSTP` causes the process to stop.
103    #[default]
104    Default,
105    /// Ignore the signal.
106    Ignore,
107    /// Catch the signal.
108    Catch,
109}
110
111/// Trait for managing signal dispositions
112pub trait Sigaction {
113    /// Gets the disposition for a signal.
114    ///
115    /// This is a low-level function used internally by
116    /// [`SharedSystem`]. You should not call this function directly, or you
117    /// will leave the `SharedSystem` instance in an inconsistent state. The
118    /// description below applies if you want to do everything yourself without
119    /// depending on `SharedSystem`.
120    ///
121    /// This is an abstract wrapper around the [`sigaction` system
122    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigaction.html).
123    /// This function returns the current disposition if successful.
124    ///
125    /// To change the disposition, use [`sigaction`](Self::sigaction).
126    fn get_sigaction(&self, signal: Number) -> Result<Disposition>;
127
128    /// Gets and sets the disposition for a signal.
129    ///
130    /// This is a low-level function used internally by [`SharedSystem`]. You
131    /// should not call this function directly, or you will leave the
132    /// `SharedSystem` instance in an inconsistent state. The description below
133    /// applies if you want to do everything yourself without depending on
134    /// `SharedSystem`.
135    ///
136    /// This is an abstract wrapper around the [`sigaction` system
137    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigaction.html).
138    /// This function returns the previous disposition if successful.
139    ///
140    /// When you set the disposition to `Disposition::Catch`, signals sent to
141    /// this process are accumulated in `self` and made available from
142    /// [`caught_signals`](CaughtSignals::caught_signals).
143    ///
144    /// To get the current disposition without changing it, use
145    /// [`get_sigaction`](Self::get_sigaction).
146    fn sigaction(&self, signal: Number, action: Disposition) -> Result<Disposition>;
147}
148
149/// Trait for examining signals caught by the process
150///
151/// Implementors of this trait usually also implement [`Sigaction`] to allow
152/// setting which signals are caught.
153pub trait CaughtSignals {
154    /// Returns signals this process has caught, if any.
155    ///
156    /// This is a low-level function used internally by
157    /// [`SharedSystem::select`]. You should not call this function directly, or
158    /// you will disrupt the behavior of `SharedSystem`. The description below
159    /// applies if you want to do everything yourself without depending on
160    /// `SharedSystem`.
161    ///
162    /// Implementors of this trait usually also implement [`Sigaction`] to allow
163    /// setting which signals are caught.
164    /// To catch a signal, you firstly install a signal handler by calling
165    /// [`Sigaction::sigaction`] with [`Disposition::Catch`]. Once the handler
166    /// is ready, signals sent to the process are accumulated in the
167    /// implementor. Calling this function retrieves the list of caught signals.
168    ///
169    /// This function clears the internal list of caught signals, so a next call
170    /// will return an empty list unless another signal is caught since the
171    /// first call. Because the list size may be limited, you should call this
172    /// function periodically before the list gets full, in which case further
173    /// caught signals are silently ignored.
174    ///
175    /// Note that signals become pending if sent while blocked by
176    /// [`Sigmask::sigmask`]. They must be unblocked so that they are caught and
177    /// made available from this function.
178    fn caught_signals(&self) -> Vec<Number>;
179}
180
181/// Trait for sending signals to processes
182pub trait SendSignal {
183    /// Sends a signal.
184    ///
185    /// This is a thin wrapper around the [`kill` system
186    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/kill.html).
187    /// If `signal` is `None`, permission to send a signal is checked, but no
188    /// signal is sent.
189    ///
190    /// The virtual system version of this function blocks the calling thread if
191    /// the signal stops or terminates the current process, hence returning a
192    /// future. See [`VirtualSystem::kill`] for details.
193    ///
194    /// [`VirtualSystem::kill`]: crate::system::virtual::VirtualSystem::kill
195    fn kill(&self, target: Pid, signal: Option<Number>) -> FlexFuture<Result<()>>;
196
197    /// Sends a signal to the current process.
198    ///
199    /// This is a thin wrapper around the `raise` system call.
200    ///
201    /// The virtual system version of this function blocks the calling thread if
202    /// the signal stops or terminates the current process, hence returning a
203    /// future. See [`VirtualSystem::kill`] for details.
204    ///
205    /// [`VirtualSystem::kill`]: crate::system::virtual::VirtualSystem::kill
206    fn raise(&self, signal: Number) -> FlexFuture<Result<()>>;
207}