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
//! Unix signal handling

use crate::{
    application::{self, Application},
    error::{
        FrameworkError,
        FrameworkErrorKind::{SignalError, ThreadError},
    },
    thread,
};
use libc::c_int;
use signal_hook::iterator::Signals;
use std::{convert::TryFrom, fmt};

/// Unix signal types.
///
/// This includes signals which are useful for Abscissa applications to handle
/// and is not intended to be an exhaustive list.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Signal {
    /// Hangup from controlling terminal/process
    Hup = 1,

    /// Keyboard interrupt
    Int = 2,

    /// Broken pipe
    Pipe = 13,

    /// Timer alarm
    Alrm = 14,

    /// Termination signal
    Term = 15,

    /// Child process terminated
    Chld = 20,

    /// User-defined signal 1
    Usr1 = 30,

    /// User-defined signal 2
    Usr2 = 31,
}

impl Signal {
    /// Get numerical signal code
    pub fn number(self) -> u32 {
        self as u32
    }

    /// Get the signal name
    pub fn name(self) -> &'static str {
        match self {
            Signal::Hup => "SIGHUP",
            Signal::Int => "SIGINT",
            Signal::Pipe => "SIGPIPE",
            Signal::Alrm => "SIGALRM",
            Signal::Term => "SIGTERM",
            Signal::Chld => "SIGCHLD",
            Signal::Usr1 => "SIGUSR1",
            Signal::Usr2 => "SIGUSR2",
        }
    }
}

impl fmt::Display for Signal {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.name())
    }
}

impl TryFrom<u32> for Signal {
    type Error = FrameworkError;

    fn try_from(num: u32) -> Result<Signal, FrameworkError> {
        Ok(match num {
            1 => Signal::Hup,
            2 => Signal::Int,
            13 => Signal::Pipe,
            14 => Signal::Alrm,
            15 => Signal::Term,
            20 => Signal::Chld,
            30 => Signal::Usr1,
            31 => Signal::Usr2,
            other => fail!(SignalError, "unregistered signal number: {}", other),
        })
    }
}

/// Launch the signal handler thread, listening for the given set of signals
pub fn register_handler<A, I>(
    app_lock: &'static application::Lock<A>,
    signals: I,
) -> Result<(), FrameworkError>
where
    A: Application + Send + Sync,
    I: IntoIterator<Item = Signal>,
{
    let mut app = app_lock.write();
    let thread_name = thread::Name::new("absissa::signal");
    let signals = Signals::new(signals.into_iter().map(|s| s.number() as c_int))
        .map_err(|e| err!(ThreadError, "{}", e))?;

    app.state_mut()
        .threads
        .spawn(&thread_name, move || handler_thread(app_lock, signals))
}

/// Signal handler thread
fn handler_thread<A>(app_lock: &'static application::Lock<A>, signals: Signals)
where
    A: Application,
{
    while !thread::should_terminate() {
        for sig_num in &signals {
            let sig = Signal::try_from(sig_num as u32).unwrap();
            debug!("received signal: {}", sig);

            let mut app = app_lock.write();
            app.handle_signal(sig).unwrap_or_else(|e| {
                // TODO(tarcieri): terminate process?
                status_err!("error handling signal: {}", e)
            });
        }
    }
}