yash_env/signal.rs
1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2024 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//! Type definitions for signals
18//!
19//! Signals are a method of inter-process communication used to notify a process
20//! that a specific event has occurred. This module provides a list of signals
21//! defined by POSIX with additional support for some non-standard signals.
22//!
23//! This module defines two abstractions for signals: [`Name`] and [`Number`].
24//! The `Name` type identifies a signal by its name, while the `Number` type
25//! represents a signal by its number. This reflects the fact that different
26//! systems may use different signal numbers for the same signal name and that
27//! some exotic signals may not be available on all systems.
28//!
29//! A [`Name`] can represent a single signal name, such as `SIGINT`
30//! ([`Name::Int`]), regardless of whether the signal is available on the
31//! [`System`]. `Name`s are more useful for user-facing applications, as they
32//! are easier to read and understand.
33//!
34//! A [`Number`] represents a signal that is available on the [`System`] by its
35//! signal number. The number is guaranteed to be a positive integer, so it
36//! optimizes the size of `Option<Number>`, etc. `Number`s are used in most
37//! signal-related functions of the [`System`] to efficiently interact with the
38//! underlying system calls.
39//!
40//! All proper signal names start with `"SIG"`. However, the names defined,
41//! parsed, and displayed in this module do not include the `"SIG"` prefix.
42
43use crate::system::Errno;
44#[cfg(doc)]
45use crate::system::System;
46use std::borrow::Cow;
47use std::cmp::Ordering;
48use std::ffi::c_int;
49use std::num::NonZero;
50use std::str::FromStr;
51use strum::{EnumIter, IntoEnumIterator};
52use thiserror::Error;
53
54/// Raw signal number
55///
56/// This is a type alias for the raw signal number used by the underlying
57/// system. POSIX requires valid signal numbers to be positive `c_int` values.
58pub type RawNumber = c_int;
59
60/// Signal name
61///
62/// This enum identifies a signal by its name. It can be used to represent
63/// signals regardless of whether they are available on the [`System`].
64///
65/// Use the [`System::validate_signal`] function to obtain a `Name` from a
66/// signal number. The [`System::signal_number_from_name`] function can be used
67/// to convert a `Name` to a `Number`.
68#[derive(Clone, Copy, Debug, EnumIter, Eq, Hash, PartialEq)]
69#[non_exhaustive]
70pub enum Name {
71 /// `SIGABRT` (process abort signal)
72 Abrt,
73 /// `SIGALRM` (alarm clock)
74 Alrm,
75 /// `SIGBUS` (access to an undefined portion of a memory object)
76 Bus,
77 /// `SIGCHLD` (child process terminated, stopped, or continued)
78 Chld,
79 /// `SIGCLD` (child process terminated, stopped, or continued)
80 Cld,
81 /// `SIGCONT` (continue executing, if stopped)
82 Cont,
83 /// `SIGEMT` (emulation trap)
84 Emt,
85 /// `SIGFPE` (erroneous arithmetic operation)
86 Fpe,
87 /// `SIGHUP` (hangup)
88 Hup,
89 /// `SIGILL` (illegal instruction)
90 Ill,
91 /// `SIGINFO` (status request from keyboard)
92 Info,
93 /// `SIGINT` (interrupt)
94 Int,
95 /// `SIGIO` (I/O is possible on a file descriptor)
96 Io,
97 /// `SIGIOT` (I/O trap)
98 Iot,
99 /// `SIGKILL` (kill)
100 Kill,
101 /// `SIGLOST` (resource lost)
102 Lost,
103 /// `SIGPIPE` (write on a pipe with no one to read it)
104 Pipe,
105 /// `SIGPOLL` (pollable event)
106 Poll,
107 /// `SIGPROF` (profiling timer expired)
108 Prof,
109 /// `SIGPWR` (power failure)
110 Pwr,
111 /// `SIGQUIT` (quit)
112 Quit,
113 /// `SIGSEGV` (invalid memory reference)
114 Segv,
115 /// `SIGSTKFLT` (stack fault)
116 Stkflt,
117 /// `SIGSTOP` (stop executing)
118 Stop,
119 /// `SIGSYS` (bad system call)
120 Sys,
121 /// `SIGTERM` (termination)
122 Term,
123 /// `SIGTHR` (thread interrupt)
124 Thr,
125 /// `SIGTRAP` (trace trap)
126 Trap,
127 /// `SIGTSTP` (stop executing)
128 Tstp,
129 /// `SIGTTIN` (background process attempting read)
130 Ttin,
131 /// `SIGTTOU` (background process attempting write)
132 Ttou,
133 /// `SIGURG` (high bandwidth data is available at a socket)
134 Urg,
135 /// `SIGUSR1` (user-defined signal 1)
136 Usr1,
137 /// `SIGUSR2` (user-defined signal 2)
138 Usr2,
139 /// `SIGVTALRM` (virtual timer expired)
140 Vtalrm,
141 /// `SIGWINCH` (window size change)
142 Winch,
143 /// `SIGXCPU` (CPU time limit exceeded)
144 Xcpu,
145 /// `SIGXFSZ` (file size limit exceeded)
146 Xfsz,
147
148 /// Real-time signal with a number relative to the minimum real-time signal
149 ///
150 /// `Rtmin(n)` represents the real-time signal `SIGRTMIN + n`, where `n` is
151 /// expected to be a non-negative integer between `0` and
152 /// `SIGRTMAX - SIGRTMIN`.
153 Rtmin(RawNumber),
154
155 /// Real-time signal with a number relative to the maximum real-time signal
156 ///
157 /// `Rtmax(n)` represents the real-time signal `SIGRTMAX + n`, where `n` is
158 /// expected to be a non-positive integer between `SIGRTMIN - SIGRTMAX` and
159 /// `0`.
160 Rtmax(RawNumber),
161}
162
163/// Compares two signal names.
164///
165/// The comparison is allowed only between two `Rtmin` values or two `Rtmax`
166/// values. The comparison is based on the numerical value of the signal number.
167impl PartialOrd for Name {
168 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
169 match (self, other) {
170 (Self::Rtmin(a), Self::Rtmin(b)) | (Self::Rtmax(a), Self::Rtmax(b)) => a.partial_cmp(b),
171 _ => None,
172 }
173 }
174}
175
176impl Name {
177 /// Returns an iterator over all signal names.
178 ///
179 /// This is a convenience method that returns an iterator over all signal
180 /// names. The iterator yields all signal names other than `Rtmin` and
181 /// `Rtmax` in the alphabetical order, followed by `Rtmin(0)` and `Rtmax(0)`
182 /// as the last two items.
183 ///
184 /// Note that the iterator works independently of the underlying system and
185 /// does not check whether the signals are available on the system.
186 #[inline(always)]
187 pub fn iter() -> NameIter {
188 <Self as IntoEnumIterator>::iter()
189 }
190
191 /// Returns the name as a string.
192 ///
193 /// For most signals, this function returns a static string that is the
194 /// signal name in uppercase without the `"SIG"` prefix. For real-time
195 /// signals `Rtmin(n)` and `Rtmax(n)` where `n` is non-zero, this function
196 /// returns a dynamically allocated string that is `RTMIN` or `RTMAX`
197 /// followed by the relative number `n`. Examples of the returned strings
198 /// are `"TERM"`, `"RTMIN"`, and `"RTMAX-5"`.
199 ///
200 /// The returned name can be converted back to the signal using the
201 /// [`FromStr`] implementation for `Name`.
202 #[must_use]
203 pub fn as_string(&self) -> Cow<'static, str> {
204 match *self {
205 Self::Abrt => Cow::Borrowed("ABRT"),
206 Self::Alrm => Cow::Borrowed("ALRM"),
207 Self::Bus => Cow::Borrowed("BUS"),
208 Self::Chld => Cow::Borrowed("CHLD"),
209 Self::Cld => Cow::Borrowed("CLD"),
210 Self::Cont => Cow::Borrowed("CONT"),
211 Self::Emt => Cow::Borrowed("EMT"),
212 Self::Fpe => Cow::Borrowed("FPE"),
213 Self::Hup => Cow::Borrowed("HUP"),
214 Self::Ill => Cow::Borrowed("ILL"),
215 Self::Info => Cow::Borrowed("INFO"),
216 Self::Int => Cow::Borrowed("INT"),
217 Self::Io => Cow::Borrowed("IO"),
218 Self::Iot => Cow::Borrowed("IOT"),
219 Self::Kill => Cow::Borrowed("KILL"),
220 Self::Lost => Cow::Borrowed("LOST"),
221 Self::Pipe => Cow::Borrowed("PIPE"),
222 Self::Poll => Cow::Borrowed("POLL"),
223 Self::Prof => Cow::Borrowed("PROF"),
224 Self::Pwr => Cow::Borrowed("PWR"),
225 Self::Quit => Cow::Borrowed("QUIT"),
226 Self::Segv => Cow::Borrowed("SEGV"),
227 Self::Stkflt => Cow::Borrowed("STKFLT"),
228 Self::Stop => Cow::Borrowed("STOP"),
229 Self::Sys => Cow::Borrowed("SYS"),
230 Self::Term => Cow::Borrowed("TERM"),
231 Self::Thr => Cow::Borrowed("THR"),
232 Self::Trap => Cow::Borrowed("TRAP"),
233 Self::Tstp => Cow::Borrowed("TSTP"),
234 Self::Ttin => Cow::Borrowed("TTIN"),
235 Self::Ttou => Cow::Borrowed("TTOU"),
236 Self::Urg => Cow::Borrowed("URG"),
237 Self::Usr1 => Cow::Borrowed("USR1"),
238 Self::Usr2 => Cow::Borrowed("USR2"),
239 Self::Vtalrm => Cow::Borrowed("VTALRM"),
240 Self::Winch => Cow::Borrowed("WINCH"),
241 Self::Xcpu => Cow::Borrowed("XCPU"),
242 Self::Xfsz => Cow::Borrowed("XFSZ"),
243 Self::Rtmin(0) => Cow::Borrowed("RTMIN"),
244 Self::Rtmax(0) => Cow::Borrowed("RTMAX"),
245 Self::Rtmin(n) => Cow::Owned(format!("RTMIN{n:+}")),
246 Self::Rtmax(n) => Cow::Owned(format!("RTMAX{n:+}")),
247 }
248 }
249}
250
251impl std::fmt::Display for Name {
252 /// Writes the signal name to the formatter.
253 ///
254 /// See [`Name::as_string`] for the format of the produced string.
255 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256 self.as_string().fmt(f)
257 }
258}
259
260#[test]
261fn test_name_to_string() {
262 assert_eq!(Name::Term.to_string(), "TERM");
263 assert_eq!(Name::Int.to_string(), "INT");
264 assert_eq!(Name::Rtmin(0).to_string(), "RTMIN");
265 assert_eq!(Name::Rtmax(0).to_string(), "RTMAX");
266 assert_eq!(Name::Rtmin(1).to_string(), "RTMIN+1");
267 assert_eq!(Name::Rtmin(20).to_string(), "RTMIN+20");
268 assert_eq!(Name::Rtmax(-1).to_string(), "RTMAX-1");
269 assert_eq!(Name::Rtmax(-20).to_string(), "RTMAX-20");
270}
271
272/// Error value for an unknown signal name
273///
274/// This error is used by the [`FromStr`] implementation for [`Name`] to
275/// indicate that the input string does not match any known signal name.
276#[derive(Clone, Debug, Eq, Error, Hash, PartialEq)]
277pub struct UnknownNameError;
278
279impl std::fmt::Display for UnknownNameError {
280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 f.write_str("unknown signal name")
282 }
283}
284
285/// Converts an unknown signal name error to `Errno::EINVAL`.
286impl From<UnknownNameError> for Errno {
287 #[inline]
288 fn from(UnknownNameError: UnknownNameError) -> Self {
289 Errno::EINVAL
290 }
291}
292
293/// Parses a signal name from a string.
294///
295/// This function parses a signal name from a string. The input string is
296/// expected to be an uppercase signal name without the `"SIG"` prefix. The
297/// function returns the corresponding signal name if the input string matches a
298/// known signal name. Otherwise, it returns an error.
299///
300/// See [`Name::as_string`] for the format of the input string.
301impl FromStr for Name {
302 type Err = UnknownNameError;
303
304 fn from_str(s: &str) -> Result<Self, UnknownNameError> {
305 match s {
306 "ABRT" => Ok(Self::Abrt),
307 "ALRM" => Ok(Self::Alrm),
308 "BUS" => Ok(Self::Bus),
309 "CHLD" => Ok(Self::Chld),
310 "CLD" => Ok(Self::Cld),
311 "CONT" => Ok(Self::Cont),
312 "EMT" => Ok(Self::Emt),
313 "FPE" => Ok(Self::Fpe),
314 "HUP" => Ok(Self::Hup),
315 "ILL" => Ok(Self::Ill),
316 "INFO" => Ok(Self::Info),
317 "INT" => Ok(Self::Int),
318 "IO" => Ok(Self::Io),
319 "IOT" => Ok(Self::Iot),
320 "KILL" => Ok(Self::Kill),
321 "LOST" => Ok(Self::Lost),
322 "PIPE" => Ok(Self::Pipe),
323 "POLL" => Ok(Self::Poll),
324 "PROF" => Ok(Self::Prof),
325 "PWR" => Ok(Self::Pwr),
326 "QUIT" => Ok(Self::Quit),
327 "SEGV" => Ok(Self::Segv),
328 "STKFLT" => Ok(Self::Stkflt),
329 "STOP" => Ok(Self::Stop),
330 "SYS" => Ok(Self::Sys),
331 "TERM" => Ok(Self::Term),
332 "THR" => Ok(Self::Thr),
333 "TRAP" => Ok(Self::Trap),
334 "TSTP" => Ok(Self::Tstp),
335 "TTIN" => Ok(Self::Ttin),
336 "TTOU" => Ok(Self::Ttou),
337 "URG" => Ok(Self::Urg),
338 "USR1" => Ok(Self::Usr1),
339 "USR2" => Ok(Self::Usr2),
340 "VTALRM" => Ok(Self::Vtalrm),
341 "WINCH" => Ok(Self::Winch),
342 "XCPU" => Ok(Self::Xcpu),
343 "XFSZ" => Ok(Self::Xfsz),
344
345 "RTMIN" => Ok(Self::Rtmin(0)),
346 "RTMAX" => Ok(Self::Rtmax(0)),
347 _ => {
348 if let Some(tail) = s.strip_prefix("RTMIN") {
349 if tail.starts_with(['+', '-']) {
350 if let Ok(n) = tail.parse() {
351 return Ok(Self::Rtmin(n));
352 }
353 }
354 }
355 if let Some(tail) = s.strip_prefix("RTMAX") {
356 if tail.starts_with(['+', '-']) {
357 if let Ok(n) = tail.parse() {
358 return Ok(Self::Rtmax(n));
359 }
360 }
361 }
362 Err(UnknownNameError)
363 }
364 }
365 }
366}
367
368#[test]
369fn test_name_from_str() {
370 assert_eq!("ABRT".parse(), Ok(Name::Abrt));
371 assert_eq!("INT".parse(), Ok(Name::Int));
372 assert_eq!("QUIT".parse(), Ok(Name::Quit));
373
374 assert_eq!("RTMIN".parse(), Ok(Name::Rtmin(0)));
375 assert_eq!("RTMIN+0".parse(), Ok(Name::Rtmin(0)));
376 assert_eq!("RTMIN+1".parse(), Ok(Name::Rtmin(1)));
377
378 assert_eq!("RTMAX".parse(), Ok(Name::Rtmax(0)));
379 assert_eq!("RTMAX-0".parse(), Ok(Name::Rtmax(0)));
380 assert_eq!("RTMAX-1".parse(), Ok(Name::Rtmax(-1)));
381
382 assert_eq!("".parse::<Name>(), Err(UnknownNameError));
383 assert_eq!("FOO".parse::<Name>(), Err(UnknownNameError));
384 assert_eq!("int".parse::<Name>(), Err(UnknownNameError));
385 assert_eq!("RTMIN0".parse::<Name>(), Err(UnknownNameError));
386 assert_eq!("RTMIN+".parse::<Name>(), Err(UnknownNameError));
387 assert_eq!("RTMAX0".parse::<Name>(), Err(UnknownNameError));
388 assert_eq!("RTMAX-".parse::<Name>(), Err(UnknownNameError));
389 assert_eq!("2".parse::<Name>(), Err(UnknownNameError));
390}
391
392/// Signal number
393///
394/// This is a wrapper type for signal numbers. It is guaranteed to be a positive
395/// integer, so it optimizes the size of `Option<Number>`, etc.
396///
397/// To make sure that all `Number`s are valid, you can only obtain a `Number`
398/// from an instance of [`System`]. Use the [`System::validate_signal`] and
399/// [`System::signal_number_from_name`] methods to create a `Number` from a raw
400/// signal number or a signal name, respectively.
401///
402/// Signal numbers are specific to the underlying system. Passing a signal
403/// number obtained from [`RealSystem`] to [`VirtualSystem`] (or vice versa) is
404/// not supported and may result in unexpected behavior, though it is not
405/// checked by the type system.
406///
407/// [`RealSystem`]: crate::system::real::RealSystem
408/// [`VirtualSystem`]: crate::system::virtual::VirtualSystem
409#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
410#[repr(transparent)]
411pub struct Number(NonZero<RawNumber>);
412
413impl Number {
414 /// Returns the raw signal number.
415 #[inline(always)]
416 #[must_use]
417 pub const fn as_raw(self) -> RawNumber {
418 self.0.get()
419 }
420
421 /// Returns the raw signal number as a `NonZero<RawNumber>`.
422 #[inline(always)]
423 #[must_use]
424 pub const fn as_raw_non_zero(self) -> NonZero<RawNumber> {
425 self.0
426 }
427
428 /// Creates a new `Number` from a raw signal number.
429 ///
430 /// This is a backdoor method that allows creating a `Number` from an
431 /// arbitrary raw signal number. The caller must ensure that the raw signal
432 /// number is a valid signal number.
433 ///
434 /// This function is not marked `unsafe` because creating an invalid
435 /// `Number` does not lead to undefined behavior. However, it is not
436 /// recommended to use this function unless you are sure that the raw signal
437 /// number is valid. To make sure that all `Number`s are valid, use the
438 /// [`System::validate_signal`] and [`System::signal_number_from_name`]
439 /// methods instead.
440 #[inline(always)]
441 #[must_use]
442 pub const fn from_raw_unchecked(raw: NonZero<RawNumber>) -> Self {
443 Self(raw)
444 }
445}
446
447impl std::fmt::Display for Number {
448 #[inline(always)]
449 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
450 self.0.fmt(f)
451 }
452}
453impl std::fmt::Binary for Number {
454 #[inline(always)]
455 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
456 self.0.fmt(f)
457 }
458}
459impl std::fmt::Octal for Number {
460 #[inline(always)]
461 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
462 self.0.fmt(f)
463 }
464}
465impl std::fmt::LowerHex for Number {
466 #[inline(always)]
467 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
468 self.0.fmt(f)
469 }
470}
471impl std::fmt::UpperHex for Number {
472 #[inline(always)]
473 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
474 self.0.fmt(f)
475 }
476}