1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
// devela::sys::os::linux::process::signal::set
//
//! Defines [`LinuxSignalSet`], [`LinuxSigset`].
//
use crate::{c_int, c_size_t};
crate::enumset! {
#[doc = crate::_tags!(linux signal member)]
/// A linux signal
#[doc = crate::_doc_meta!{
location("sys/os/linux/process"),
test_size_of(LinuxSignal = 1|8; niche Option),
}]
/// The enum discriminants are zero-based semantic indices, not raw Linux
/// signal numbers. Use [`as_c_int`][Self::as_c_int] for the raw signal number.
//
// For the terminal, the key signals are:
// - SIGINT // Ctrl+C
// - SIGQUIT // Ctrl+\
// - SIGTSTP // Ctrl+Z
// - SIGTTIN // background terminal read
// - SIGTTOU // background terminal write
// - SIGWINCH // terminal resize
// - SIGHUP // terminal/session hangup
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum LinuxSignal(
#[doc = crate::_tags!(linux signal set)]
/// A compact semantic set of standard Linux signals.
#[doc = crate::_doc_meta!{
location("sys/os/linux/process"),
test_size_of(LinuxSignalSet = 4|32),
}]
/// This is not the raw Linux `sigset_t` mask. Signal bits are stored
/// compactly from zero: `SIGHUP` is bit `0`, `SIGINT` is bit `1`, …
pub LinuxSignalSet: u32
) {
/// Terminal hangup or controlling process ended.
SIGHUP // = 0
/// Keyboard interrupt, usually `Ctrl+C`.
SIGINT, // = 1
/// Keyboard quit, usually `Ctrl+\`; often produces a core dump.
SIGQUIT, // = 2
/// Illegal CPU instruction.
SIGILL, // = 3
/// Trace or breakpoint trap, commonly used by debuggers.
SIGTRAP, // = 4
/// Process abort, usually from `abort()`.
SIGABRT, // = 5
/// Bus error, usually invalid or misaligned memory access.
SIGBUS, // = 6
/// Floating-point exception, such as division by zero in some contexts.
SIGFPE, // = 7
/// Uncatchable forced termination.
SIGKILL, // = 8
/// User-defined signal 1.
SIGUSR1, // = 9
/// Invalid memory reference, usually a segmentation fault.
SIGSEGV, // = 10
/// User-defined signal 2.
SIGUSR2, // = 11
/// Broken pipe, usually writing to a pipe or socket with no reader.
SIGPIPE, // = 12
/// Alarm timer expired, usually from `alarm()`.
SIGALRM, // = 13
/// Polite termination request, commonly sent by `kill` or service managers.
SIGTERM, // = 14
/// Stack fault signal, obsolete on most modern Linux systems.
SIGSTKFLT, // = 15
/// Child process stopped, continued, or exited.
SIGCHLD, // = 16
/// Continue a stopped process.
SIGCONT, // = 17
/// Uncatchable stop signal.
SIGSTOP, // = 18
/// Terminal stop, usually `Ctrl+Z`.
SIGTSTP, // = 19
/// Background process tried to read from the terminal.
SIGTTIN, // = 20
/// Background process tried to write to the terminal.
SIGTTOU, // = 21
/// Urgent condition on a socket.
SIGURG, // = 22
/// CPU time limit exceeded.
SIGXCPU, // = 23
/// File size limit exceeded.
SIGXFSZ, // = 24
/// Virtual interval timer expired.
SIGVTALRM, // = 25
/// Profiling timer expired.
SIGPROF, // = 26
/// Terminal or window size changed.
SIGWINCH, // = 27
/// Asynchronous I/O event is available.
SIGIO, // = 28
/// Power failure or power-management event.
SIGPWR, // = 29
/// Bad system call, often from seccomp filtering.
SIGSYS, // = 30
}
// Constants
impl enum {
/// All supported standard Linux signals, in signal-number order.
pub const ALL: [Self; Self::VARIANTS] = [
Self::SIGHUP, Self::SIGINT, Self::SIGQUIT, Self::SIGILL,
Self::SIGTRAP, Self::SIGABRT, Self::SIGBUS, Self::SIGFPE,
Self::SIGKILL, Self::SIGUSR1, Self::SIGSEGV, Self::SIGUSR2,
Self::SIGPIPE, Self::SIGALRM, Self::SIGTERM, Self::SIGSTKFLT,
Self::SIGCHLD, Self::SIGCONT, Self::SIGSTOP, Self::SIGTSTP,
Self::SIGTTIN, Self::SIGTTOU, Self::SIGURG, Self::SIGXCPU,
Self::SIGXFSZ, Self::SIGVTALRM, Self::SIGPROF, Self::SIGWINCH,
Self::SIGIO, Self::SIGPWR, Self::SIGSYS,
];
/// Lowest supported standard Linux signal number.
pub const MIN_NUMBER: c_int = 1;
/// Highest supported standard Linux signal number.
pub const MAX_NUMBER: c_int = Self::SIGSYS.as_c_int();
/// Number of slots needed when indexing directly by raw signal number.
///
/// Slot `0` is unused.
pub(crate) const TABLE_LEN: usize = Self::MAX_NUMBER as usize + 1;
const __: () = {
assert!(LinuxSignal::SIGHUP.as_c_int() == 1);
assert!(LinuxSignal::SIGINT.as_c_int() == 2);
assert!(LinuxSignal::SIGWINCH.as_c_int() == 28);
assert!(LinuxSignal::SIGSYS.as_c_int() == 31);
assert!(LinuxSignal::SIGHUP.as_index() == 0);
assert!(LinuxSignal::SIGINT.as_index() == 1);
assert!(LinuxSignal::SIGWINCH.as_index() == 27);
assert!(LinuxSignal::SIGSYS.as_index() == 30);
assert!(LinuxSignal::MAX_NUMBER == 31);
assert!(LinuxSignal::TABLE_LEN == 32);
};
}
// Methods
impl enum {
/// Returns the raw Linux signal number.
#[must_use]
pub const fn as_c_int(self) -> c_int { self as c_int + 1 }
/// Returns the zero-based set/mask index for this signal.
#[must_use]
pub const fn as_index(self) -> usize { self as usize }
/// Returns a signal from a raw Linux signal number.
#[must_use]
pub const fn from_c_int(signal: c_int) -> Option<Self> {
match signal {
1 => Some(Self::SIGHUP),
2 => Some(Self::SIGINT),
3 => Some(Self::SIGQUIT),
4 => Some(Self::SIGILL),
5 => Some(Self::SIGTRAP),
6 => Some(Self::SIGABRT),
7 => Some(Self::SIGBUS),
8 => Some(Self::SIGFPE),
9 => Some(Self::SIGKILL),
10 => Some(Self::SIGUSR1),
11 => Some(Self::SIGSEGV),
12 => Some(Self::SIGUSR2),
13 => Some(Self::SIGPIPE),
14 => Some(Self::SIGALRM),
15 => Some(Self::SIGTERM),
16 => Some(Self::SIGSTKFLT),
17 => Some(Self::SIGCHLD),
18 => Some(Self::SIGCONT),
19 => Some(Self::SIGSTOP),
20 => Some(Self::SIGTSTP),
21 => Some(Self::SIGTTIN),
22 => Some(Self::SIGTTOU),
23 => Some(Self::SIGURG),
24 => Some(Self::SIGXCPU),
25 => Some(Self::SIGXFSZ),
26 => Some(Self::SIGVTALRM),
27 => Some(Self::SIGPROF),
28 => Some(Self::SIGWINCH),
29 => Some(Self::SIGIO),
30 => Some(Self::SIGPWR),
31 => Some(Self::SIGSYS),
_ => None,
}
}
/// Returns whether this signal can be caught by a user handler.
#[must_use]
pub const fn is_catchable(self) -> bool {
!matches!(self, Self::SIGKILL | Self::SIGSTOP)
}
/// Returns whether the raw signal number is supported.
#[must_use]
pub const fn is_standard_number(signal: c_int) -> bool {
Self::from_c_int(signal).is_some()
}
/// Returns whether the raw signal number can be caught by a user handler.
#[must_use]
pub const fn is_catchable_number(signal: c_int) -> bool {
match Self::from_c_int(signal) {
Some(signal) => signal.is_catchable(),
None => false,
}
}
}
// Aliases
impl enum {
/// IOT trap. Compatibility alias for [`SIGABRT`][Self::SIGABRT].
pub const SIGIOT: Self = Self::SIGABRT;
/// Compatibility alias for [`SIGCHLD`][Self::SIGCHLD].
pub const SIGCLD: Self = Self::SIGCHLD;
/// Pollable event. Compatibility alias for [`SIGIO`][Self::SIGIO].
pub const SIGPOLL: Self = Self::SIGIO;
// /// Compatibility alias for [`SIGPWR`][Self::SIGPWR].
// ///
// /// This is not the BSD terminal status signal commonly associated with `Ctrl+T`.
// pub const SIGINFO: Self = Self::SIGPWR;
// /// Obsolete compatibility alias for [`SIGSYS`][Self::SIGSYS].
// pub const SIGUNUSED: Self = Self::SIGSYS;
}
impl set {
/// Returns a singleton set containing `signal`.
#[must_use]
pub const fn from_signal(signal: LinuxSignal) -> Self {
signal.to_set()
}
/// Returns the only signal in this set, if it contains exactly one.
#[must_use]
pub const fn to_signal(self) -> Option<LinuxSignal> {
let bits = self.bits();
if bits != 0 && bits.is_power_of_two() {
LinuxSignal::from_c_int(bits.trailing_zeros() as c_int + 1)
} else {
None
}
}
/// Calls `f` for each signal in this set.
pub fn for_each(self, mut f: impl FnMut(LinuxSignal)) {
for signal in LinuxSignal::ALL {
if signal.is_in(self) { f(signal); }
}
}
/// Calls `f` for each catchable signal in this set.
pub fn for_each_catchable(self, f: impl FnMut(LinuxSignal)) {
self.catchable_only().for_each(f);
}
/// Returns whether all signals in this set are catchable.
#[must_use]
pub const fn all_catchable(self) -> bool {
!self.has(Self::SIGKILL) && !self.has(Self::SIGSTOP)
}
/// Returns this set without without signals that cannot be caught.
#[must_use]
pub const fn catchable_only(self) -> Self {
self.without(Self::SIGKILL).without(Self::SIGSTOP)
}
}
}
impl From<LinuxSignalSet> for LinuxSigset {
fn from(signals: LinuxSignalSet) -> Self {
Self::from_signal_set(signals)
}
}
impl From<LinuxSigset> for LinuxSignalSet {
fn from(mask: LinuxSigset) -> Self {
mask.to_signal_set()
}
}
#[doc = crate::_tags!(linux signal set abi)]
/// A raw Linux kernel signal mask.
#[doc = crate::_doc_meta!{
location("sys/os/linux/process"),
#[cfg(target_pointer_width = "32")]
test_size_of(LinuxSigset = 4|32),
#[cfg(target_pointer_width = "64")]
test_size_of(LinuxSigset = 8|64),
}]
/// This is the mask representation passed to raw Linux signal syscalls.
///
/// Signal numbers start at `1`, while mask bits start at `0`,
/// so signal `n` is stored in bit `n - 1`.
///
/// For the semantic Devela set, use [`LinuxSignalSet`].
//
// This one resembles the [`sigset_t`] structure from libc.
// [`sigset_t`]: https://man7.org/linux/man-pages/man7/system_data_types.7.html
#[repr(C)]
#[must_use]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LinuxSigset {
/// Raw signal mask words.
pub sig: [c_size_t; Self::WORDS],
}
#[rustfmt::skip]
impl LinuxSigset {
/// Number of bits in one raw mask word.
pub const WORD_BITS: usize = usize::BITS as usize;
/// Highest supported standard Linux signal number.
pub const MAX_SIGNAL: usize = LinuxSignal::MAX_NUMBER as usize;
/// Number of raw mask words needed for the supported signal range.
pub const WORDS: usize = Self::MAX_SIGNAL.div_ceil(Self::WORD_BITS);
/// Returns an empty signal mask.
pub const fn empty() -> Self { Self { sig: [0; Self::WORDS] } }
/// Returns the size in bytes passed to raw Linux signal syscalls.
#[must_use]
pub const fn size() -> c_size_t { size_of::<Self>() }
/// Returns a raw mask from a semantic signal set.
pub const fn from_signal_set(signals: LinuxSignalSet) -> Self {
Self { sig: [signals.bits() as usize; Self::WORDS] }
}
/// Returns this raw mask as a semantic signal set.
#[must_use]
pub const fn to_signal_set(self) -> LinuxSignalSet {
LinuxSignalSet::from_bits(self.sig[0] as u32)
}
/// Returns a copy with `signal` added.
pub const fn with_signal(mut self, signal: LinuxSignal) -> Self {
let bit = signal.as_index();
let word = bit / Self::WORD_BITS;
let shift = bit % Self::WORD_BITS;
self.sig[word] |= 1usize << shift;
self
}
/// Returns a copy with `signal` removed.
///
/// Ignores unsupported signal numbers.
pub const fn without_signal(mut self, signal: LinuxSignal) -> Self {
let bit = signal.as_index();
let word = bit / Self::WORD_BITS;
let shift = bit % Self::WORD_BITS;
self.sig[word] &= !(1usize << shift);
self
}
/// Returns whether this raw mask contains `signal`.
#[must_use]
pub const fn has_signal(self, signal: LinuxSignal) -> bool {
let bit = signal.as_index();
let word = bit / Self::WORD_BITS;
let shift = bit % Self::WORD_BITS;
self.sig[word] & (1usize << shift) != 0
}
/// Adds `signal` to this raw mask.
pub fn insert_signal(&mut self, signal: LinuxSignal) {
*self = self.with_signal(signal);
}
/// Removes `signal` from this raw mask.
///
/// # Panics
/// Panics if `signal` is not a supported standard Linux signal.
pub fn remove_signal(&mut self, signal: LinuxSignal) {
*self = self.without_signal(signal);
}
/// Returns a copy with the raw signal number added, if supported.
pub const fn with_signal_number(self, signal: c_int) -> Self {
match LinuxSignal::from_c_int(signal) {
Some(signal) => self.with_signal(signal),
None => self,
}
}
}