rustix/
signal.rs

1//! Signal numbers.
2//!
3//! # Safety
4//!
5//! Some signal numbers are reserved by the libc.
6//! [`Signal::from_raw_unchecked`] and [`Signal::from_raw_nonzero_unchecked`]
7//! allow constructing `Signal` values with arbitrary values. Users must avoid
8//! using reserved values to send, consume, or block any signals or alter any
9//! signal handlers.
10//!
11//! See the individual functions' safety comments for more details.
12#![allow(unsafe_code)]
13
14use crate::backend::c;
15use core::fmt;
16use core::num::NonZeroI32;
17
18/// A signal number for use with [`kill_process`], [`kill_process_group`], and
19/// [`kill_current_process_group`].
20///
21/// For additional constructors such as [`Signal::rt_min`]
22/// (aka `libc::SIGRTMIN`), [`Signal::rt_max`] (aka `libc::SIGRTMAX`),
23/// [`Signal::rt`] (aka `|n| libc::SIGRTMIN() + n`), [`Signal::from_raw`], and
24/// [`Signal::from_raw_nonzero`], see [rustix-libc-wrappers].
25///
26/// # References
27///  - [POSIX]
28///  - [Linux]
29///  - [glibc]
30///
31/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html
32/// [Linux]: https://man7.org/linux/man-pages/man7/signal.7.html
33/// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/Standard-Signals.html
34///
35/// [`kill_process`]: crate::process::kill_process
36/// [`kill_process_group`]: crate::process::kill_process_group
37/// [`kill_current_process_group`]: crate::process::kill_current_process_group
38/// [`Signal::rt_min`]: https://docs.rs/rustix-libc-wrappers/*/rustix_libc_wrappers/trait.SignalExt.html#tymethod.rt_min
39/// [`Signal::rt_max`]: https://docs.rs/rustix-libc-wrappers/*/rustix_libc_wrappers/trait.SignalExt.html#tymethod.rt_max
40/// [`Signal::rt`]: https://docs.rs/rustix-libc-wrappers/*/rustix_libc_wrappers/trait.SignalExt.html#tymethod.rt
41/// [`Signal::from_raw`]: https://docs.rs/rustix-libc-wrappers/*/rustix_libc_wrappers/trait.SignalExt.html#tymethod.from_raw
42/// [`Signal::from_raw_nonzero`]: https://docs.rs/rustix-libc-wrappers/*/rustix_libc_wrappers/trait.SignalExt.html#tymethod.from_raw_nonzero
43/// [rustix-libc-wrappers]: https://docs.rs/rustix-libc-wrappers
44#[doc(alias = "SIGRTMIN")]
45#[doc(alias = "SIGRTMAX")]
46#[derive(Copy, Clone, Eq, PartialEq)]
47#[repr(transparent)]
48pub struct Signal(NonZeroI32);
49
50// SAFETY: The libc-defined signal values are all non-zero.
51#[rustfmt::skip]
52impl Signal {
53    /// `SIGHUP`
54    pub const HUP: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGHUP) });
55    /// `SIGINT`
56    pub const INT: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGINT) });
57    /// `SIGQUIT`
58    pub const QUIT: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGQUIT) });
59    /// `SIGILL`
60    pub const ILL: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGILL) });
61    /// `SIGTRAP`
62    pub const TRAP: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGTRAP) });
63    /// `SIGABRT`, aka `SIGIOT`
64    #[doc(alias = "IOT")]
65    #[doc(alias = "ABRT")]
66    pub const ABORT: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGABRT) });
67    /// `SIGBUS`
68    pub const BUS: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGBUS) });
69    /// `SIGFPE`
70    pub const FPE: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGFPE) });
71    /// `SIGKILL`
72    pub const KILL: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGKILL) });
73    /// `SIGUSR1`
74    #[cfg(not(target_os = "vita"))]
75    pub const USR1: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGUSR1) });
76    /// `SIGSEGV`
77    pub const SEGV: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGSEGV) });
78    /// `SIGUSR2`
79    #[cfg(not(target_os = "vita"))]
80    pub const USR2: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGUSR2) });
81    /// `SIGPIPE`
82    pub const PIPE: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGPIPE) });
83    /// `SIGALRM`
84    #[doc(alias = "ALRM")]
85    pub const ALARM: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGALRM) });
86    /// `SIGTERM`
87    pub const TERM: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGTERM) });
88    /// `SIGSTKFLT`
89    #[cfg(not(any(
90        bsd,
91        solarish,
92        target_os = "aix",
93        target_os = "haiku",
94        target_os = "horizon",
95        target_os = "hurd",
96        target_os = "nto",
97        target_os = "vita",
98        all(
99            linux_kernel,
100            any(
101                target_arch = "mips",
102                target_arch = "mips32r6",
103                target_arch = "mips64",
104                target_arch = "mips64r6",
105                target_arch = "sparc",
106                target_arch = "sparc64"
107            ),
108        )
109    )))]
110    pub const STKFLT: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGSTKFLT) });
111    /// `SIGCHLD`
112    #[cfg(not(target_os = "vita"))]
113    #[doc(alias = "CHLD")]
114    pub const CHILD: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGCHLD) });
115    /// `SIGCONT`
116    #[cfg(not(target_os = "vita"))]
117    pub const CONT: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGCONT) });
118    /// `SIGSTOP`
119    #[cfg(not(target_os = "vita"))]
120    pub const STOP: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGSTOP) });
121    /// `SIGTSTP`
122    #[cfg(not(target_os = "vita"))]
123    pub const TSTP: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGTSTP) });
124    /// `SIGTTIN`
125    #[cfg(not(target_os = "vita"))]
126    pub const TTIN: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGTTIN) });
127    /// `SIGTTOU`
128    #[cfg(not(target_os = "vita"))]
129    pub const TTOU: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGTTOU) });
130    /// `SIGURG`
131    #[cfg(not(target_os = "vita"))]
132    pub const URG: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGURG) });
133    /// `SIGXCPU`
134    #[cfg(not(target_os = "vita"))]
135    pub const XCPU: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGXCPU) });
136    /// `SIGXFSZ`
137    #[cfg(not(target_os = "vita"))]
138    pub const XFSZ: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGXFSZ) });
139    /// `SIGVTALRM`
140    #[cfg(not(target_os = "vita"))]
141    #[doc(alias = "VTALRM")]
142    pub const VTALARM: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGVTALRM) });
143    /// `SIGPROF`
144    #[cfg(not(target_os = "vita"))]
145    pub const PROF: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGPROF) });
146    /// `SIGWINCH`
147    #[cfg(not(target_os = "vita"))]
148    pub const WINCH: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGWINCH) });
149    /// `SIGIO`, aka `SIGPOLL`
150    #[doc(alias = "POLL")]
151    #[cfg(not(any(target_os = "haiku", target_os = "vita")))]
152    pub const IO: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGIO) });
153    /// `SIGPWR`
154    #[cfg(not(any(
155        bsd,
156        target_os = "haiku",
157        target_os = "horizon",
158        target_os = "hurd",
159        target_os = "vita"
160    )))]
161    #[doc(alias = "PWR")]
162    pub const POWER: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGPWR) });
163    /// `SIGSYS`, aka `SIGUNUSED`
164    #[doc(alias = "UNUSED")]
165    pub const SYS: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGSYS) });
166    /// `SIGEMT`
167    #[cfg(any(
168        bsd,
169        solarish,
170        target_os = "aix",
171        target_os = "hermit",
172        all(
173            linux_kernel,
174            any(
175                target_arch = "mips",
176                target_arch = "mips32r6",
177                target_arch = "mips64",
178                target_arch = "mips64r6",
179                target_arch = "sparc",
180                target_arch = "sparc64"
181            )
182        )
183    ))]
184    pub const EMT: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGEMT) });
185    /// `SIGINFO`
186    #[cfg(bsd)]
187    pub const INFO: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGINFO) });
188    /// `SIGTHR`
189    #[cfg(target_os = "freebsd")]
190    #[doc(alias = "LWP")]
191    pub const THR: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGTHR) });
192    /// `SIGLIBRT`
193    #[cfg(target_os = "freebsd")]
194    pub const LIBRT: Self = Self(unsafe { NonZeroI32::new_unchecked(c::SIGLIBRT) });
195}
196
197impl Signal {
198    /// Convert a `Signal` to a raw signal number.
199    ///
200    /// To convert to a `NonZeroI32`, use [`Signal::as_raw_nonzero`].
201    #[inline]
202    pub const fn as_raw(self) -> i32 {
203        self.0.get()
204    }
205
206    /// Convert a `Signal` to a raw non-zero signal number.
207    #[inline]
208    pub const fn as_raw_nonzero(self) -> NonZeroI32 {
209        self.0
210    }
211
212    /// Convert a raw signal number into a `Signal` without checks.
213    ///
214    /// For a safe checked version, see [`Signal::from_raw`] in
215    /// [rustix-libc-wrappers].
216    ///
217    /// # Safety
218    ///
219    /// `sig` must be a valid and non-zero signal number.
220    ///
221    /// And, if `sig` is a signal number reserved by the libc, such as a value
222    /// from the libc [`SIGRTMIN`] to the libc [`SIGRTMAX`], inclusive, then
223    /// the resulting `Signal` must not be used to send, consume, or block any
224    /// signals or alter any signal handlers.
225    ///
226    /// [`Signal::from_raw`]: https://docs.rs/rustix-libc-wrappers/*/rustix_libc_wrappers/trait.SignalExt.html#tymethod.from_raw
227    /// [rustix-libc-wrappers]: https://docs.rs/rustix-libc-wrappers
228    /// [`SIGRTMIN`]: https://docs.rs/libc/*/libc/fn.SIGRTMIN.html
229    /// [`SIGRTMAX`]: https://docs.rs/libc/*/libc/fn.SIGRTMAX.html
230    #[inline]
231    pub const unsafe fn from_raw_unchecked(sig: i32) -> Self {
232        Self::from_raw_nonzero_unchecked(NonZeroI32::new_unchecked(sig))
233    }
234
235    /// Convert a raw non-zero signal number into a `Signal` without checks.
236    ///
237    /// For a safe checked version, see [`Signal::from_raw_nonzero`] in
238    /// [rustix-libc-wrappers].
239    ///
240    /// # Safety
241    ///
242    /// `sig` must be a valid signal number.
243    ///
244    /// And, if `sig` is a signal number reserved by the libc, such as a value
245    /// from [`SIGRTMIN`] to [`SIGRTMAX`] inclusive, then the resulting
246    /// `Signal` must not be used to send, consume, or block any signals or
247    /// alter any signal handlers.
248    ///
249    /// [`Signal::from_raw_nonzero`]: https://docs.rs/rustix-libc-wrappers/*/rustix_libc_wrappers/trait.SignalExt.html#tymethod.from_raw_nonzero
250    /// [rustix-libc-wrappers]: https://docs.rs/rustix-libc-wrappers
251    /// [`SIGRTMIN`]: https://docs.rs/libc/*/libc/fn.SIGRTMIN.html
252    /// [`SIGRTMAX`]: https://docs.rs/libc/*/libc/fn.SIGRTMAX.html
253    #[inline]
254    pub const unsafe fn from_raw_nonzero_unchecked(sig: NonZeroI32) -> Self {
255        Self(sig)
256    }
257}
258
259impl fmt::Debug for Signal {
260    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261        match *self {
262            Self::HUP => "Signal::HUP".fmt(f),
263            Self::INT => "Signal::INT".fmt(f),
264            Self::QUIT => "Signal::QUIT".fmt(f),
265            Self::ILL => "Signal::ILL".fmt(f),
266            Self::TRAP => "Signal::TRAP".fmt(f),
267            Self::ABORT => "Signal::ABORT".fmt(f),
268            Self::BUS => "Signal::BUS".fmt(f),
269            Self::FPE => "Signal::FPE".fmt(f),
270            Self::KILL => "Signal::KILL".fmt(f),
271            #[cfg(not(target_os = "vita"))]
272            Self::USR1 => "Signal::USR1".fmt(f),
273            Self::SEGV => "Signal::SEGV".fmt(f),
274            #[cfg(not(target_os = "vita"))]
275            Self::USR2 => "Signal::USR2".fmt(f),
276            Self::PIPE => "Signal::PIPE".fmt(f),
277            Self::ALARM => "Signal::ALARM".fmt(f),
278            Self::TERM => "Signal::TERM".fmt(f),
279            #[cfg(not(any(
280                bsd,
281                solarish,
282                target_os = "aix",
283                target_os = "haiku",
284                target_os = "horizon",
285                target_os = "hurd",
286                target_os = "nto",
287                target_os = "vita",
288                all(
289                    linux_kernel,
290                    any(
291                        target_arch = "mips",
292                        target_arch = "mips32r6",
293                        target_arch = "mips64",
294                        target_arch = "mips64r6",
295                        target_arch = "sparc",
296                        target_arch = "sparc64"
297                    ),
298                )
299            )))]
300            Self::STKFLT => "Signal::STKFLT".fmt(f),
301            #[cfg(not(target_os = "vita"))]
302            Self::CHILD => "Signal::CHILD".fmt(f),
303            #[cfg(not(target_os = "vita"))]
304            Self::CONT => "Signal::CONT".fmt(f),
305            #[cfg(not(target_os = "vita"))]
306            Self::STOP => "Signal::STOP".fmt(f),
307            #[cfg(not(target_os = "vita"))]
308            Self::TSTP => "Signal::TSTP".fmt(f),
309            #[cfg(not(target_os = "vita"))]
310            Self::TTIN => "Signal::TTIN".fmt(f),
311            #[cfg(not(target_os = "vita"))]
312            Self::TTOU => "Signal::TTOU".fmt(f),
313            #[cfg(not(target_os = "vita"))]
314            Self::URG => "Signal::URG".fmt(f),
315            #[cfg(not(target_os = "vita"))]
316            Self::XCPU => "Signal::XCPU".fmt(f),
317            #[cfg(not(target_os = "vita"))]
318            Self::XFSZ => "Signal::XFSZ".fmt(f),
319            #[cfg(not(target_os = "vita"))]
320            Self::VTALARM => "Signal::VTALARM".fmt(f),
321            #[cfg(not(target_os = "vita"))]
322            Self::PROF => "Signal::PROF".fmt(f),
323            #[cfg(not(target_os = "vita"))]
324            Self::WINCH => "Signal::WINCH".fmt(f),
325            #[cfg(not(any(target_os = "haiku", target_os = "vita")))]
326            Self::IO => "Signal::IO".fmt(f),
327            #[cfg(not(any(
328                bsd,
329                target_os = "haiku",
330                target_os = "horizon",
331                target_os = "hurd",
332                target_os = "vita"
333            )))]
334            Self::POWER => "Signal::POWER".fmt(f),
335            Self::SYS => "Signal::SYS".fmt(f),
336            #[cfg(any(
337                bsd,
338                solarish,
339                target_os = "aix",
340                target_os = "hermit",
341                all(
342                    linux_kernel,
343                    any(
344                        target_arch = "mips",
345                        target_arch = "mips32r6",
346                        target_arch = "mips64",
347                        target_arch = "mips64r6",
348                        target_arch = "sparc",
349                        target_arch = "sparc64"
350                    )
351                )
352            ))]
353            Self::EMT => "Signal::EMT".fmt(f),
354            #[cfg(bsd)]
355            Self::INFO => "Signal::INFO".fmt(f),
356            #[cfg(target_os = "freebsd")]
357            Self::THR => "Signal::THR".fmt(f),
358            #[cfg(target_os = "freebsd")]
359            Self::LIBRT => "Signal::LIBRT".fmt(f),
360
361            n => {
362                "Signal::from_raw(".fmt(f)?;
363                n.as_raw().fmt(f)?;
364                ")".fmt(f)
365            }
366        }
367    }
368}
369
370#[cfg(test)]
371mod tests {
372    use super::*;
373
374    #[test]
375    fn test_basics() {
376        assert_eq!(Signal::HUP.as_raw(), libc::SIGHUP);
377        unsafe {
378            assert_eq!(Signal::from_raw_unchecked(libc::SIGHUP), Signal::HUP);
379            assert_eq!(
380                Signal::from_raw_nonzero_unchecked(NonZeroI32::new(libc::SIGHUP).unwrap()),
381                Signal::HUP
382            );
383        }
384    }
385}