syscall/
signal.rs

1//!
2//! This file is part of syscall-rs
3//!
4
5use mio::{
6    event::{self},
7    unix::SourceFd,
8    Interest, Registry, Token,
9};
10use std::{fmt, mem, os::unix::prelude::RawFd, str::FromStr};
11
12use crate::{Error, Result};
13
14/// Operating system signal.
15///
16/// Note that we would want to use `lib::c_int` as the repr attribute. This is
17/// not (yet) supported, however.
18#[repr(i32)]
19#[non_exhaustive]
20#[derive(Copy, Clone, PartialEq, Eq, Debug)]
21pub enum Signal {
22    /// Hangup
23    SIGHUP = libc::SIGHUP,
24    /// Interrupt
25    SIGINT = libc::SIGINT,
26    /// Quit
27    SIGQUIT = libc::SIGQUIT,
28    /// Illegal instruction (not reset when caught)
29    SIGILL = libc::SIGILL,
30    /// Trace trap (not reset when caught)
31    SIGTRAP = libc::SIGTRAP,
32    /// Abort
33    SIGABRT = libc::SIGABRT,
34    /// Bus error
35    SIGBUS = libc::SIGBUS,
36    /// Floating point exception
37    SIGFPE = libc::SIGFPE,
38    /// Kill (cannot be caught or ignored)
39    SIGKILL = libc::SIGKILL,
40    /// User defined signal 1
41    SIGUSR1 = libc::SIGUSR1,
42    /// Segmentation violation
43    SIGSEGV = libc::SIGSEGV,
44    /// User defined signal 2
45    SIGUSR2 = libc::SIGUSR2,
46    /// Write on a pipe with no one to read it
47    SIGPIPE = libc::SIGPIPE,
48    /// Alarm clock
49    SIGALRM = libc::SIGALRM,
50    /// Software termination signal from kill
51    SIGTERM = libc::SIGTERM,
52    /// Stack fault (obsolete)
53    SIGSTKFLT = libc::SIGSTKFLT,
54    /// To parent on child stop or exit
55    SIGCHLD = libc::SIGCHLD,
56    /// Continue a stopped process
57    SIGCONT = libc::SIGCONT,
58    /// Sendable stop signal not from tty
59    SIGSTOP = libc::SIGSTOP,
60    /// Stop signal from tty
61    SIGTSTP = libc::SIGTSTP,
62    /// To readers pgrp upon background tty read
63    SIGTTIN = libc::SIGTTIN,
64    /// Like TTIN if (tp->t_local&LTOSTOP)
65    SIGTTOU = libc::SIGTTOU,
66    /// Urgent condition on IO channel
67    SIGURG = libc::SIGURG,
68    /// Exceeded CPU time limit
69    SIGXCPU = libc::SIGXCPU,
70    /// Exceeded file size limit
71    SIGXFSZ = libc::SIGXFSZ,
72    /// Virtual time alarm
73    SIGVTALRM = libc::SIGVTALRM,
74    /// Profiling time alarm
75    SIGPROF = libc::SIGPROF,
76    /// Window size changes
77    SIGWINCH = libc::SIGWINCH,
78    /// Input/output possible signal
79    SIGIO = libc::SIGIO,
80    /// Power failure imminent.
81    SIGPWR = libc::SIGPWR,
82    /// Bad system call
83    SIGSYS = libc::SIGSYS,
84}
85
86impl Signal {
87    /// Return signal name
88    pub const fn as_str(self) -> &'static str {
89        match self {
90            Signal::SIGHUP => "SIGHUP",
91            Signal::SIGINT => "SIGINT",
92            Signal::SIGQUIT => "SIGQUIT",
93            Signal::SIGILL => "SIGILL",
94            Signal::SIGTRAP => "SIGTRAP",
95            Signal::SIGABRT => "SIGABRT",
96            Signal::SIGBUS => "SIGBUS",
97            Signal::SIGFPE => "SIGFPE",
98            Signal::SIGKILL => "SIGKILL",
99            Signal::SIGUSR1 => "SIGUSR1",
100            Signal::SIGSEGV => "SIGSEGV",
101            Signal::SIGUSR2 => "SIGUSR2",
102            Signal::SIGPIPE => "SIGPIPE",
103            Signal::SIGALRM => "SIGALRM",
104            Signal::SIGTERM => "SIGTERM",
105            Signal::SIGSTKFLT => "SIGSTKFLT",
106            Signal::SIGCHLD => "SIGCHLD",
107            Signal::SIGCONT => "SIGCONT",
108            Signal::SIGSTOP => "SIGSTOP",
109            Signal::SIGTSTP => "SIGTSTP",
110            Signal::SIGTTIN => "SIGTTIN",
111            Signal::SIGTTOU => "SIGTTOU",
112            Signal::SIGURG => "SIGURG",
113            Signal::SIGXCPU => "SIGXCPU",
114            Signal::SIGXFSZ => "SIGXFSZ",
115            Signal::SIGVTALRM => "SIGVTALRM",
116            Signal::SIGPROF => "SIGPROF",
117            Signal::SIGWINCH => "SIGWINCH",
118            Signal::SIGIO => "SIGIO",
119            Signal::SIGPWR => "SIGPWR",
120            Signal::SIGSYS => "SIGSYS",
121        }
122    }
123}
124
125impl FromStr for Signal {
126    type Err = Error;
127
128    /// Parse [`Signal`] from a signal name
129    fn from_str(s: &str) -> Result<Signal> {
130        Ok(match s {
131            "SIGHUP" => Signal::SIGHUP,
132            "SIGINT" => Signal::SIGINT,
133            "SIGQUIT" => Signal::SIGQUIT,
134            "SIGILL" => Signal::SIGILL,
135            "SIGTRAP" => Signal::SIGTRAP,
136            "SIGABRT" => Signal::SIGABRT,
137            "SIGBUS" => Signal::SIGBUS,
138            "SIGFPE" => Signal::SIGFPE,
139            "SIGKILL" => Signal::SIGKILL,
140            "SIGUSR1" => Signal::SIGUSR1,
141            "SIGSEGV" => Signal::SIGSEGV,
142            "SIGUSR2" => Signal::SIGUSR2,
143            "SIGPIPE" => Signal::SIGPIPE,
144            "SIGALRM" => Signal::SIGALRM,
145            "SIGTERM" => Signal::SIGTERM,
146            "SIGSTKFLT" => Signal::SIGSTKFLT,
147            "SIGCHLD" => Signal::SIGCHLD,
148            "SIGCONT" => Signal::SIGCONT,
149            "SIGSTOP" => Signal::SIGSTOP,
150            "SIGTSTP" => Signal::SIGTSTP,
151            "SIGTTIN" => Signal::SIGTTIN,
152            "SIGTTOU" => Signal::SIGTTOU,
153            "SIGURG" => Signal::SIGURG,
154            "SIGXCPU" => Signal::SIGXCPU,
155            "SIGXFSZ" => Signal::SIGXFSZ,
156            "SIGVTALRM" => Signal::SIGVTALRM,
157            "SIGPROF" => Signal::SIGPROF,
158            "SIGWINCH" => Signal::SIGWINCH,
159            "SIGIO" => Signal::SIGIO,
160            "SIGPWR" => Signal::SIGPWR,
161            "SIGSYS" => Signal::SIGSYS,
162            _ => {
163                return Err(Error::Syscall(std::io::Error::new(
164                    std::io::ErrorKind::InvalidData,
165                    "invalid signal",
166                )))
167            }
168        })
169    }
170}
171
172impl TryFrom<libc::c_int> for Signal {
173    type Error = Error;
174
175    /// Try to convert `signum` into a [`Signal`]
176    ///
177    /// This function only supports signal numbering for standard signals
178    /// according to the `signal(7)` man page
179    fn try_from(signum: libc::c_int) -> std::result::Result<Self, Self::Error> {
180        if 0 < signum && signum < 32 {
181            Ok(unsafe { mem::transmute(signum) })
182        } else {
183            Err(Error::Syscall(std::io::Error::new(
184                std::io::ErrorKind::InvalidData,
185                "invalid signal number",
186            )))
187        }
188    }
189}
190
191impl AsRef<str> for Signal {
192    fn as_ref(&self) -> &str {
193        self.as_str()
194    }
195}
196
197impl fmt::Display for Signal {
198    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199        f.write_str(self.as_ref())
200    }
201}
202
203/// A group of multiple different [`Signal`]s
204///
205/// This is a `Newtype` for [`libc::sigset_t`] and the related
206/// functions used to manipulate a signal set.
207#[derive(Clone, Copy, Debug, PartialEq, Eq)]
208pub struct SignalSet(libc::sigset_t);
209
210impl SignalSet {
211    /// Initialize a set to contain all signals
212    pub fn fill() -> Result<SignalSet> {
213        let mut set = mem::MaybeUninit::uninit();
214
215        syscall!(sigfillset(set.as_mut_ptr()))?;
216
217        Ok(unsafe { SignalSet(set.assume_init()) })
218    }
219
220    /// Initialize a set to contain no signals
221    ///
222    /// Note that this function will never fail, since `sigemptyset()` has no
223    /// errors defined.
224    pub fn empty() -> Result<SignalSet> {
225        let mut set = mem::MaybeUninit::uninit();
226
227        syscall!(sigemptyset(set.as_mut_ptr()))?;
228
229        Ok(unsafe { SignalSet(set.assume_init()) })
230    }
231
232    /// Add `signal` to a set
233    pub fn add(&mut self, signal: Signal) -> Result<()> {
234        syscall!(sigaddset(
235            &mut self.0 as *mut libc::sigset_t,
236            signal as libc::c_int
237        ))?;
238
239        Ok(())
240    }
241
242    /// Remove `signal` from a set
243    pub fn remove(&mut self, signal: Signal) -> Result<()> {
244        syscall!(sigdelset(
245            &mut self.0 as *mut libc::sigset_t,
246            signal as libc::c_int
247        ))?;
248
249        Ok(())
250    }
251}
252
253impl AsRef<libc::sigset_t> for SignalSet {
254    fn as_ref(&self) -> &libc::sigset_t {
255        &self.0
256    }
257}
258
259impl From<&[Signal]> for SignalSet {
260    /// Return a [`SignalSet`] from a slice of [`Signal`]s
261    ///
262    /// Note that this conversion is save since [`SignalSet::empty()`] never
263    /// fails and the slice of [`Signal`]s contains valid signals only, ever.
264    fn from(signals: &[Signal]) -> Self {
265        *signals.iter().fold(
266            &mut SignalSet::empty().expect("syscall failed"),
267            |set, sig| {
268                set.add(*sig).expect("syscall failed");
269                set
270            },
271        )
272    }
273}
274
275/// Magic value for creating a new signal fd
276const SIGNALFD_NEW: libc::c_int = -1;
277
278/// Special file descriptor from which [`Signal`]s directed to the caller can
279/// be read
280///
281/// Note that a signal fd must **not** derive neither [`Clone`] nor [`Copy`]!
282#[derive(Debug, PartialEq, Eq)]
283pub struct SignalFd(RawFd);
284
285impl SignalFd {
286    /// Return a [`SignalFd`] that will be able to read the signals given in
287    /// the signal set `signals`.
288    pub fn new(signals: SignalSet) -> Result<SignalFd> {
289        let fd = syscall!(signalfd(
290            SIGNALFD_NEW,
291            signals.as_ref() as *const libc::sigset_t,
292            0
293        ))?;
294
295        Ok(SignalFd(fd))
296    }
297
298    /// Read and return a [`Signal`]
299    ///
300    /// Note this function will **block** until a signal could be read.
301    pub fn read_signal(&mut self) -> Result<Signal> {
302        let mut siginfo = mem::MaybeUninit::<libc::signalfd_siginfo>::uninit();
303        let size = mem::size_of_val(&siginfo);
304
305        let num = syscall!(read(
306            self.0,
307            siginfo.as_mut_ptr() as *mut libc::c_void,
308            size
309        ))?;
310
311        // not enough signal info data
312        if num as usize != size {
313            return Err(Error::Syscall(std::io::Error::new(
314                std::io::ErrorKind::InvalidData,
315                "invalid signal",
316            )));
317        }
318
319        let siginfo = unsafe { siginfo.assume_init() };
320        let signum = siginfo.ssi_signo as libc::c_int;
321
322        signum.try_into()
323    }
324}
325
326impl event::Source for SignalFd {
327    fn register(
328        &mut self,
329        registry: &Registry,
330        token: Token,
331        interests: Interest,
332    ) -> std::io::Result<()> {
333        SourceFd(&self.0).register(registry, token, interests)
334    }
335
336    fn reregister(
337        &mut self,
338        registry: &Registry,
339        token: Token,
340        interests: Interest,
341    ) -> std::io::Result<()> {
342        SourceFd(&self.0).reregister(registry, token, interests)
343    }
344
345    fn deregister(&mut self, registry: &Registry) -> std::io::Result<()> {
346        SourceFd(&self.0).deregister(registry)
347    }
348}
349
350impl Drop for SignalFd {
351    /// Drop the [`SignalFd`]
352    ///
353    /// Dropping a signal fd involves calling [`libc::close()`]. Note that
354    /// `close` is special in the sense that all errors except an invalid
355    /// signal fd should simply be ignored.
356    fn drop(&mut self) {
357        unsafe { libc::close(self.0) };
358
359        // an invalid signal fd is the only error we panic for
360        if std::io::Error::last_os_error().raw_os_error().unwrap_or(0) == libc::EBADF {
361            panic!("closing invalid signal fd");
362        }
363    }
364}
365
366/// Add the signals in `set` to the current process signal mask and return the
367/// previous process signal mask.
368///
369/// Note that the process signal mask will be set to the **union** of the current
370/// process signal mask and `set`.
371pub fn signal_block(set: SignalSet) -> Result<SignalSet> {
372    let mut old = SignalSet::empty()?;
373
374    syscall!(sigprocmask(
375        libc::SIG_BLOCK,
376        &set.0 as *const libc::sigset_t,
377        &mut old.0 as &mut libc::sigset_t
378    ))?;
379
380    Ok(old)
381}
382
383/// Set the process signal mask to the signals in `set` and return the previous
384/// process signal mask.
385///
386/// Note that this will also unblock those signals which have previously been
387/// blocked by a call to [`signal_block()`]
388pub fn signal_restore(set: SignalSet) -> Result<SignalSet> {
389    let mut old = SignalSet::empty()?;
390
391    syscall!(sigprocmask(
392        libc::SIG_SETMASK,
393        &set.0 as *const libc::sigset_t,
394        &mut old.0 as &mut libc::sigset_t
395    ))?;
396
397    Ok(old)
398}
399
400#[cfg(test)]
401mod tests {
402    use anyhow::Result;
403
404    use super::{signal_block, signal_restore, Signal, SignalFd, SignalSet};
405
406    #[test]
407    fn signal_set_add() -> Result<()> {
408        let mut set = SignalSet::empty()?;
409
410        set.add(Signal::SIGCHLD)?;
411        set.add(Signal::SIGPIPE)?;
412
413        assert_ne!(set, SignalSet::empty()?);
414        assert_ne!(set, SignalSet::fill()?);
415
416        Ok(())
417    }
418
419    #[test]
420    fn signal_set_remove() -> Result<()> {
421        let mut set = SignalSet::empty()?;
422
423        set.add(Signal::SIGHUP)?;
424        set.add(Signal::SIGQUIT)?;
425
426        set.remove(Signal::SIGHUP)?;
427        set.remove(Signal::SIGQUIT)?;
428
429        assert_eq!(set, SignalSet::empty()?);
430        assert_ne!(set, SignalSet::fill()?);
431
432        Ok(())
433    }
434
435    #[test]
436    fn signal_set_remove_unknown() -> Result<()> {
437        let mut set = SignalSet::empty()?;
438
439        set.remove(Signal::SIGCHLD)?;
440
441        set.add(Signal::SIGHUP)?;
442
443        set.remove(Signal::SIGCHLD)?;
444        set.remove(Signal::SIGHUP)?;
445
446        assert_eq!(set, SignalSet::empty()?);
447        assert_ne!(set, SignalSet::fill()?);
448
449        Ok(())
450    }
451
452    #[test]
453    fn signal_try_from() -> Result<()> {
454        let signum = Signal::SIGQUIT as libc::c_int;
455        let sig: Signal = signum.try_into()?;
456
457        assert_eq!(signum, libc::SIGQUIT);
458        assert_eq!(sig, Signal::SIGQUIT);
459
460        let res: std::result::Result<Signal, _> = (255 as libc::c_int).try_into();
461
462        assert_eq!(
463            format!("{:?}", res.err().unwrap()),
464            "Syscall(Custom { kind: InvalidData, error: \"invalid signal number\" })"
465        );
466
467        Ok(())
468    }
469
470    #[test]
471    fn block_signals() -> Result<()> {
472        let signals = vec![
473            Signal::SIGCHLD,
474            Signal::SIGINT,
475            Signal::SIGQUIT,
476            Signal::SIGTERM,
477        ];
478
479        // nothing has been blocked before
480        let old = signal_block(signals.as_slice().into())?;
481
482        assert_eq!(old, SignalSet::empty()?);
483        assert_ne!(old, SignalSet::fill()?);
484
485        // return the previously blocked signal set
486        let blocked = signal_block(old)?;
487
488        assert_eq!(blocked, signals.as_slice().into());
489
490        Ok(())
491    }
492
493    #[test]
494    fn restore_signals() -> Result<()> {
495        let signals = vec![
496            Signal::SIGCHLD,
497            Signal::SIGINT,
498            Signal::SIGQUIT,
499            Signal::SIGTERM,
500        ];
501
502        let old = signal_block(signals.as_slice().into())?;
503
504        let blocked = signal_restore(old)?;
505
506        assert_eq!(blocked, signals.as_slice().into());
507
508        Ok(())
509    }
510
511    #[test]
512    fn signalfd_new() -> Result<()> {
513        let _ = SignalFd::new(SignalSet::empty()?)?;
514        Ok(())
515    }
516
517    #[test]
518    #[should_panic(expected = "closing invalid signal fd")]
519    fn signalfd_drop_invalid() {
520        let fake = SignalFd(-1);
521        drop(fake);
522    }
523}