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}