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}