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
//! Providing auxiliary information for signals.

use std::io::Error;
use std::mem;
use std::ptr;

use libc::{c_int, EINVAL};
#[cfg(not(windows))]
use libc::{sigset_t, SIG_UNBLOCK};

use crate::consts::signal::*;
use crate::low_level;

#[derive(Clone, Copy, Debug)]
enum DefaultKind {
    Ignore,
    #[cfg(not(windows))]
    Stop,
    Term,
}

struct Details {
    signal: c_int,
    name: &'static str,
    default_kind: DefaultKind,
}

macro_rules! s {
    ($name: expr, $kind: ident) => {
        Details {
            signal: $name,
            name: stringify!($name),
            default_kind: DefaultKind::$kind,
        }
    };
}

#[cfg(not(windows))]
const DETAILS: &[Details] = &[
    s!(SIGABRT, Term),
    s!(SIGALRM, Term),
    s!(SIGBUS, Term),
    s!(SIGCHLD, Ignore),
    // Technically, continue the process... but this is not done *by* the process.
    s!(SIGCONT, Ignore),
    s!(SIGFPE, Term),
    s!(SIGHUP, Term),
    s!(SIGILL, Term),
    s!(SIGINT, Term),
    #[cfg(any(
        target_os = "freebsd",
        target_os = "dragonfly",
        target_os = "netbsd",
        target_os = "openbsd",
        target_os = "macos"
    ))]
    s!(SIGINFO, Ignore),
    #[cfg(not(target_os = "haiku"))]
    s!(SIGIO, Ignore),
    // Can't override anyway, but...
    s!(SIGKILL, Term),
    s!(SIGPIPE, Term),
    s!(SIGPROF, Term),
    s!(SIGQUIT, Term),
    s!(SIGSEGV, Term),
    // Can't override anyway, but...
    s!(SIGSTOP, Stop),
    s!(SIGSYS, Term),
    s!(SIGTERM, Term),
    s!(SIGTRAP, Term),
    s!(SIGTSTP, Stop),
    s!(SIGTTIN, Stop),
    s!(SIGTTOU, Stop),
    s!(SIGURG, Ignore),
    s!(SIGUSR1, Term),
    s!(SIGUSR2, Term),
    s!(SIGVTALRM, Term),
    s!(SIGWINCH, Ignore),
    s!(SIGXCPU, Term),
    s!(SIGXFSZ, Term),
];

#[cfg(windows)]
const DETAILS: &[Details] = &[
    s!(SIGABRT, Term),
    s!(SIGFPE, Term),
    s!(SIGILL, Term),
    s!(SIGINT, Term),
    s!(SIGSEGV, Term),
    s!(SIGTERM, Term),
];

/// Provides a human-readable name of a signal.
///
/// Note that the name does not have to be known (in case it is some less common, or non-standard
/// signal).
///
/// # Examples
///
/// ```
/// # use signal_hook::low_level::signal_name;
/// assert_eq!("SIGKILL", signal_name(9).unwrap());
/// assert!(signal_name(142).is_none());
/// ```
pub fn signal_name(signal: c_int) -> Option<&'static str> {
    DETAILS.iter().find(|d| d.signal == signal).map(|d| d.name)
}

#[cfg(not(windows))]
fn restore_default(signal: c_int) -> Result<(), Error> {
    unsafe {
        // A C structure, supposed to be memset to 0 before use.
        let mut action: libc::sigaction = mem::zeroed();
        #[cfg(target_os = "aix")]
        {
            action.sa_union.__su_sigaction = mem::transmute::<
                usize,
                extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void),
            >(libc::SIG_DFL);
        }
        #[cfg(not(target_os = "aix"))]
        { action.sa_sigaction = libc::SIG_DFL as _; }
        if libc::sigaction(signal, &action, ptr::null_mut()) == 0 {
            Ok(())
        } else {
            Err(Error::last_os_error())
        }
    }
}

#[cfg(windows)]
fn restore_default(signal: c_int) -> Result<(), Error> {
    unsafe {
        // SIG_DFL = 0, but not in libc :-(
        if libc::signal(signal, 0) == 0 {
            Ok(())
        } else {
            Err(Error::last_os_error())
        }
    }
}

/// Emulates the behaviour of a default handler for the provided signal.
///
/// This function does its best to provide the same action as the default handler would do, without
/// disrupting the rest of the handling of such signal in the application. It is also
/// async-signal-safe.
///
/// This function necessarily looks up the appropriate action in a table. That means it is possible
/// your system has a signal that is not known to this function. In such case an error is returned
/// (equivalent of `EINVAL`).
///
/// See also the [`register_conditional_default`][crate::flag::register_conditional_default].
///
/// # Warning
///
/// There's a short race condition in case of signals that terminate (either with or without a core
/// dump). The emulation first resets the signal handler back to default (as the application is
/// going to end, it's not a problem) and invokes it. But if some other thread installs a signal
/// handler in the meantime (without assistance from `signal-hook`), it can happen this will be
/// invoked by the re-raised signal.
///
/// This function will still terminate the application (there's a fallback on `abort`), the risk is
/// invoking the newly installed signal handler. Note that manipulating the low-level signals is
/// always racy in a multi-threaded program, therefore the described situation is already
/// discouraged.
///
/// If you are uneasy about such race condition, the recommendation is to run relevant termination
/// routine manually ([`exit`][super::exit] or [`abort`][super::abort]); they always do what they
/// say, but slightly differ in externally observable behaviour from termination by a signal (the
/// exit code will specify that the application exited, not that it terminated with a signal in the
/// first case, and `abort` terminates on `SIGABRT`, so the detected termination signal may be
/// different).
pub fn emulate_default_handler(signal: c_int) -> Result<(), Error> {
    #[cfg(not(windows))]
    {
        if signal == SIGSTOP || signal == SIGKILL {
            return low_level::raise(signal);
        }
    }
    let kind = DETAILS
        .iter()
        .find(|d| d.signal == signal)
        .map(|d| d.default_kind)
        .ok_or_else(|| Error::from_raw_os_error(EINVAL))?;
    match kind {
        DefaultKind::Ignore => Ok(()),
        #[cfg(not(windows))]
        DefaultKind::Stop => low_level::raise(SIGSTOP),
        DefaultKind::Term => {
            if let Ok(()) = restore_default(signal) {
                #[cfg(not(windows))]
                unsafe {
                    #[allow(deprecated)]
                    let mut newsigs: sigset_t = mem::zeroed();

                    // Some android versions don't have the sigemptyset and sigaddset.
                    // Unfortunately, we don't have an access to the android _version_. We just
                    // know that 64bit versions are all OK, so this is a best-effort guess.
                    //
                    // For the affected/guessed versions, we provide our own implementation. We
                    // hope it to be correct (it's inspired by a libc implementation and we assume
                    // the kernel uses the same format ‒ it's unlikely to be different both because
                    // of compatibility and because there's really nothing to invent about a
                    // bitarray).
                    //
                    // We use the proper way for other systems.
                    #[cfg(all(target_os = "android", target_pointer_width = "32"))]
                    unsafe fn prepare_sigset(set: *mut sigset_t, mut signal: c_int) {
                        signal -= 1;
                        let set_raw: *mut libc::c_ulong = set.cast();
                        let size = mem::size_of::<libc::c_ulong>();
                        assert_eq!(set_raw as usize % mem::align_of::<libc::c_ulong>(), 0);
                        let pos = signal as usize / size;
                        assert!(pos < mem::size_of::<sigset_t>() / size);
                        let bit = 1 << (signal as usize % size);
                        set_raw.add(pos).write(bit);
                    }

                    #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
                    unsafe fn prepare_sigset(set: *mut sigset_t, signal: c_int) {
                        libc::sigemptyset(set);
                        libc::sigaddset(set, signal);
                    }

                    prepare_sigset(&mut newsigs, signal);
                    // Ignore the result, if it doesn't work, we try anyway
                    // Also, sigprocmask is unspecified, but available on more systems. And we want
                    // to just enable _something_. And if it doesn't work, we'll terminate
                    // anyway... It's not UB, so we are good.
                    libc::sigprocmask(SIG_UNBLOCK, &newsigs, ptr::null_mut());
                }
                let _ = low_level::raise(signal);
            }
            // Fallback if anything failed or someone managed to put some other action in in
            // between.
            unsafe { libc::abort() }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn existing() {
        assert_eq!("SIGTERM", signal_name(SIGTERM).unwrap());
    }

    #[test]
    fn unknown() {
        assert!(signal_name(128).is_none());
    }
}