|
|
|
@ -1,13 +1,290 @@
|
|
|
|
|
use std::borrow::Cow;
|
|
|
|
|
use std::num::NonZeroI32;
|
|
|
|
|
|
|
|
|
|
use crate::ffi;
|
|
|
|
|
use crate::common::{exit_without_destructors, restore_term_foreground_process_group_for_exit};
|
|
|
|
|
use crate::event::{enqueue_signal, is_signal_observed};
|
|
|
|
|
use crate::termsize::termsize_handle_winch;
|
|
|
|
|
use crate::topic_monitor::{generation_t, invalid_generations, topic_monitor_principal, topic_t};
|
|
|
|
|
use crate::wchar::wstr;
|
|
|
|
|
use crate::wchar_ffi::c_str;
|
|
|
|
|
use widestring::U32CStr;
|
|
|
|
|
use crate::wchar::{wstr, WExt, L};
|
|
|
|
|
use crate::wutil::fish_wcstoi;
|
|
|
|
|
use crate::wutil::{wgettext, wgettext_str, wperror};
|
|
|
|
|
use errno::{errno, set_errno};
|
|
|
|
|
use std::sync::atomic::{AtomicI32, Ordering};
|
|
|
|
|
use widestring_suffix::widestrs;
|
|
|
|
|
|
|
|
|
|
/// Store the "main" pid. This allows us to reliably determine if we are in a forked child.
|
|
|
|
|
static MAIN_PID: AtomicI32 = AtomicI32::new(0);
|
|
|
|
|
|
|
|
|
|
/// It's possible that we receive a signal after we have forked, but before we have reset the signal
|
|
|
|
|
/// handlers (or even run the pthread_atfork calls). In that event we will do something dumb like
|
|
|
|
|
/// swallow SIGINT. Ensure that doesn't happen. Check if we are the main fish process; if not, reset
|
|
|
|
|
/// and re-raise the signal. \return whether we re-raised the signal.
|
|
|
|
|
fn reraise_if_forked_child(sig: i32) -> bool {
|
|
|
|
|
// Don't use is_forked_child: it relies on atfork handlers which may have not yet run.
|
|
|
|
|
// Safety: getpid() is async-signal-safe.
|
|
|
|
|
let pid = unsafe { libc::getpid() };
|
|
|
|
|
if pid == MAIN_PID.load(Ordering::Relaxed) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Safety: signal() and raise() are async-signal-safe.
|
|
|
|
|
unsafe {
|
|
|
|
|
libc::signal(sig, libc::SIG_DFL);
|
|
|
|
|
libc::raise(sig);
|
|
|
|
|
}
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The cancellation signal we have received.
|
|
|
|
|
/// Of course this is modified from a signal handler.
|
|
|
|
|
static CANCELLATION_SIGNAL: AtomicI32 = AtomicI32::new(0);
|
|
|
|
|
|
|
|
|
|
pub fn signal_clear_cancel() {
|
|
|
|
|
CANCELLATION_SIGNAL.store(0, Ordering::Relaxed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn signal_check_cancel() -> i32 {
|
|
|
|
|
CANCELLATION_SIGNAL.load(Ordering::Relaxed)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Declare these as an extern C functions and call them directly,
|
|
|
|
|
// in case the autocxx ffi allocates or does something else signal-unfriendly.
|
|
|
|
|
extern "C" {
|
|
|
|
|
fn reader_sighup();
|
|
|
|
|
fn reader_handle_sigint();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The single signal handler. By centralizing signal handling we ensure that we can never install
|
|
|
|
|
/// the "wrong" signal handler (see #5969).
|
|
|
|
|
extern "C" fn fish_signal_handler(
|
|
|
|
|
sig: i32,
|
|
|
|
|
_info: *mut libc::siginfo_t,
|
|
|
|
|
_context: *mut libc::c_void,
|
|
|
|
|
) {
|
|
|
|
|
// Ensure we preserve errno.
|
|
|
|
|
let saved_errno = errno();
|
|
|
|
|
|
|
|
|
|
// Check if we are a forked child.
|
|
|
|
|
if reraise_if_forked_child(sig) {
|
|
|
|
|
set_errno(saved_errno);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if fish script cares about this.
|
|
|
|
|
let observed = is_signal_observed(sig);
|
|
|
|
|
if observed {
|
|
|
|
|
enqueue_signal(sig);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Do some signal-specific stuff.
|
|
|
|
|
match sig {
|
|
|
|
|
libc::SIGWINCH => {
|
|
|
|
|
// Respond to a winch signal by telling the termsize container.
|
|
|
|
|
termsize_handle_winch();
|
|
|
|
|
}
|
|
|
|
|
libc::SIGHUP => {
|
|
|
|
|
// Exit unless the signal was trapped.
|
|
|
|
|
if !observed {
|
|
|
|
|
unsafe { reader_sighup() };
|
|
|
|
|
}
|
|
|
|
|
topic_monitor_principal().post(topic_t::sighupint);
|
|
|
|
|
}
|
|
|
|
|
libc::SIGTERM => {
|
|
|
|
|
// Handle sigterm. The only thing we do is restore the front process ID, then die.
|
|
|
|
|
if !observed {
|
|
|
|
|
restore_term_foreground_process_group_for_exit();
|
|
|
|
|
// Safety: signal() and raise() are async-signal-safe.
|
|
|
|
|
unsafe {
|
|
|
|
|
libc::signal(libc::SIGTERM, libc::SIG_DFL);
|
|
|
|
|
libc::raise(libc::SIGTERM);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
libc::SIGINT => {
|
|
|
|
|
// Cancel unless the signal was trapped.
|
|
|
|
|
if !observed {
|
|
|
|
|
CANCELLATION_SIGNAL.store(libc::SIGINT, Ordering::Relaxed);
|
|
|
|
|
}
|
|
|
|
|
unsafe { reader_handle_sigint() };
|
|
|
|
|
topic_monitor_principal().post(topic_t::sighupint);
|
|
|
|
|
}
|
|
|
|
|
libc::SIGCHLD => {
|
|
|
|
|
// A child process stopped or exited.
|
|
|
|
|
topic_monitor_principal().post(topic_t::sigchld);
|
|
|
|
|
}
|
|
|
|
|
libc::SIGALRM => {
|
|
|
|
|
// We have a sigalarm handler that does nothing. This is used in the signal torture
|
|
|
|
|
// test, to verify that we behave correctly when receiving lots of irrelevant signals.
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_errno(saved_errno);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn signal_reset_handlers() {
|
|
|
|
|
let mut act: libc::sigaction = unsafe { std::mem::zeroed() };
|
|
|
|
|
unsafe { libc::sigemptyset(&mut act.sa_mask) };
|
|
|
|
|
act.sa_flags = 0;
|
|
|
|
|
act.sa_sigaction = libc::SIG_DFL;
|
|
|
|
|
|
|
|
|
|
for data in SIGNAL_TABLE.iter() {
|
|
|
|
|
if data.signal == libc::SIGHUP {
|
|
|
|
|
let mut oact: libc::sigaction = unsafe { std::mem::zeroed() };
|
|
|
|
|
unsafe { libc::sigaction(libc::SIGHUP, std::ptr::null(), &mut oact) };
|
|
|
|
|
if oact.sa_sigaction == libc::SIG_IGN {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
unsafe {
|
|
|
|
|
libc::sigaction(data.signal.code(), &act, std::ptr::null_mut());
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wrapper around sigaction.
|
|
|
|
|
fn sigaction(sig: i32, act: &libc::sigaction, oact: *mut libc::sigaction) -> libc::c_int {
|
|
|
|
|
// Note: historically many call sites have ignored return value of sigaction here.
|
|
|
|
|
unsafe { libc::sigaction(sig, act, oact) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_interactive_handlers() {
|
|
|
|
|
let signal_handler: usize = fish_signal_handler as usize;
|
|
|
|
|
let mut act: libc::sigaction = unsafe { std::mem::zeroed() };
|
|
|
|
|
let mut oact: libc::sigaction = unsafe { std::mem::zeroed() };
|
|
|
|
|
act.sa_flags = 0;
|
|
|
|
|
oact.sa_flags = 0;
|
|
|
|
|
unsafe { libc::sigemptyset(&mut act.sa_mask) };
|
|
|
|
|
|
|
|
|
|
let nullptr = std::ptr::null_mut();
|
|
|
|
|
|
|
|
|
|
// Interactive mode. Ignore interactive signals. We are a shell, we know what is best for
|
|
|
|
|
// the user.
|
|
|
|
|
act.sa_sigaction = libc::SIG_IGN;
|
|
|
|
|
sigaction(libc::SIGTSTP, &act, nullptr);
|
|
|
|
|
sigaction(libc::SIGTTOU, &act, nullptr);
|
|
|
|
|
|
|
|
|
|
// We don't ignore SIGTTIN because we might send it to ourself.
|
|
|
|
|
act.sa_sigaction = signal_handler;
|
|
|
|
|
act.sa_flags = libc::SA_SIGINFO;
|
|
|
|
|
sigaction(libc::SIGTTIN, &act, nullptr);
|
|
|
|
|
|
|
|
|
|
// SIGTERM restores the terminal controlling process before dying.
|
|
|
|
|
act.sa_sigaction = signal_handler;
|
|
|
|
|
act.sa_flags = libc::SA_SIGINFO;
|
|
|
|
|
sigaction(libc::SIGTERM, &act, nullptr);
|
|
|
|
|
|
|
|
|
|
unsafe { libc::sigaction(libc::SIGHUP, nullptr, &mut oact) };
|
|
|
|
|
if oact.sa_sigaction == libc::SIG_DFL {
|
|
|
|
|
act.sa_sigaction = signal_handler;
|
|
|
|
|
act.sa_flags = libc::SA_SIGINFO;
|
|
|
|
|
sigaction(libc::SIGHUP, &act, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SIGALARM as part of our signal torture test
|
|
|
|
|
act.sa_sigaction = signal_handler;
|
|
|
|
|
act.sa_flags = libc::SA_SIGINFO;
|
|
|
|
|
sigaction(libc::SIGALRM, &act, nullptr);
|
|
|
|
|
|
|
|
|
|
act.sa_sigaction = signal_handler;
|
|
|
|
|
act.sa_flags = libc::SA_SIGINFO;
|
|
|
|
|
sigaction(libc::SIGWINCH, &act, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Sets up appropriate signal handlers.
|
|
|
|
|
fn signal_set_handlers(interactive: bool) {
|
|
|
|
|
use libc::SIG_IGN;
|
|
|
|
|
let nullptr = std::ptr::null_mut();
|
|
|
|
|
let mut act: libc::sigaction = unsafe { std::mem::zeroed() };
|
|
|
|
|
|
|
|
|
|
act.sa_flags = 0;
|
|
|
|
|
unsafe { libc::sigemptyset(&mut act.sa_mask) };
|
|
|
|
|
|
|
|
|
|
// Ignore SIGPIPE. We'll detect failed writes and deal with them appropriately. We don't want
|
|
|
|
|
// this signal interrupting other syscalls or terminating us.
|
|
|
|
|
act.sa_sigaction = SIG_IGN;
|
|
|
|
|
sigaction(libc::SIGPIPE, &act, nullptr);
|
|
|
|
|
|
|
|
|
|
// Ignore SIGQUIT.
|
|
|
|
|
act.sa_sigaction = SIG_IGN;
|
|
|
|
|
sigaction(libc::SIGQUIT, &act, nullptr);
|
|
|
|
|
|
|
|
|
|
// Apply our SIGINT handler.
|
|
|
|
|
act.sa_sigaction = fish_signal_handler as usize;
|
|
|
|
|
act.sa_flags = libc::SA_SIGINFO;
|
|
|
|
|
sigaction(libc::SIGINT, &act, nullptr);
|
|
|
|
|
|
|
|
|
|
// Whether or not we're interactive we want SIGCHLD to not interrupt restartable syscalls.
|
|
|
|
|
act.sa_sigaction = fish_signal_handler as usize;
|
|
|
|
|
act.sa_flags = libc::SA_SIGINFO | libc::SA_RESTART;
|
|
|
|
|
if sigaction(libc::SIGCHLD, &act, nullptr) != 0 {
|
|
|
|
|
wperror(L!("sigaction"));
|
|
|
|
|
exit_without_destructors(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if interactive {
|
|
|
|
|
set_interactive_handlers();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if cfg!(feature = "FISH_TSAN_WORKAROUNDS") {
|
|
|
|
|
// Work around the following TSAN bug:
|
|
|
|
|
// The structure containing signal information for a thread is lazily allocated by TSAN.
|
|
|
|
|
// It is possible for the same thread to receive two allocations, if the signal handler
|
|
|
|
|
// races with other allocation paths (e.g. a blocking call). This results in the first signal
|
|
|
|
|
// being potentially dropped.
|
|
|
|
|
// The workaround is to send ourselves a SIGCHLD signal now, to force the allocation to happen.
|
|
|
|
|
// As no child is associated with this signal, it is OK if it is dropped, so long as the
|
|
|
|
|
// allocation happens.
|
|
|
|
|
unsafe { libc::kill(libc::getpid(), libc::SIGCHLD) };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn signal_handle(sig: libc::c_int) {
|
|
|
|
|
let mut act: libc::sigaction = unsafe { std::mem::zeroed() };
|
|
|
|
|
|
|
|
|
|
// These should always be handled.
|
|
|
|
|
if sig == libc::SIGINT
|
|
|
|
|
|| sig == libc::SIGQUIT
|
|
|
|
|
|| sig == libc::SIGTSTP
|
|
|
|
|
|| sig == libc::SIGTTIN
|
|
|
|
|
|| sig == libc::SIGTTOU
|
|
|
|
|
|| sig == libc::SIGCHLD
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
act.sa_flags = 0;
|
|
|
|
|
unsafe { libc::sigemptyset(&mut act.sa_mask) };
|
|
|
|
|
act.sa_flags = libc::SA_SIGINFO;
|
|
|
|
|
act.sa_sigaction = fish_signal_handler as usize;
|
|
|
|
|
sigaction(sig, &act, std::ptr::null_mut());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_signals_with_handlers(set: &mut libc::sigset_t) {
|
|
|
|
|
unsafe { libc::sigemptyset(set) };
|
|
|
|
|
for data in SIGNAL_TABLE.iter() {
|
|
|
|
|
let mut act: libc::sigaction = unsafe { std::mem::zeroed() };
|
|
|
|
|
unsafe { libc::sigaction(data.signal.code(), std::ptr::null(), &mut act) };
|
|
|
|
|
// If SIGHUP is being ignored (e.g., because were were run via `nohup`) don't reset it.
|
|
|
|
|
// We don't special case other signals because if they're being ignored that shouldn't
|
|
|
|
|
// affect processes we spawn. They should get the default behavior for those signals.
|
|
|
|
|
if data.signal == libc::SIGHUP && act.sa_sigaction == libc::SIG_IGN {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if act.sa_sigaction != libc::SIG_DFL {
|
|
|
|
|
unsafe { libc::sigaddset(set, data.signal.code()) };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Ensure we did not inherit any blocked signals. See issue #3964.
|
|
|
|
|
pub fn signal_unblock_all() {
|
|
|
|
|
unsafe {
|
|
|
|
|
let mut iset: libc::sigset_t = std::mem::zeroed();
|
|
|
|
|
libc::sigemptyset(&mut iset);
|
|
|
|
|
libc::sigprocmask(libc::SIG_SETMASK, &iset, std::ptr::null_mut());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A Sigchecker can be used to check if a SIGINT (or SIGHUP) has been delivered.
|
|
|
|
|
pub struct Sigchecker {
|
|
|
|
|
topic: topic_t,
|
|
|
|
@ -47,30 +324,102 @@ impl Sigchecker {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[deprecated(note = "Use [`Signal::parse()`] instead.")]
|
|
|
|
|
/// Get the integer signal value representing the specified signal.
|
|
|
|
|
pub fn wcs2sig(s: &wstr) -> Option<usize> {
|
|
|
|
|
let sig = ffi::wcs2sig(c_str!(s));
|
|
|
|
|
|
|
|
|
|
sig.0.try_into().ok()
|
|
|
|
|
/// Struct describing an entry for the lookup table used to convert between signal names and signal
|
|
|
|
|
/// ids, etc.
|
|
|
|
|
struct LookupEntry {
|
|
|
|
|
signal: Signal,
|
|
|
|
|
name: &'static wstr,
|
|
|
|
|
desc: &'static wstr, // Note: this needs to be translated via gettext before presenting it to the user.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[deprecated(note = "Use [`Signal::name()`] instead.")]
|
|
|
|
|
/// Get string representation of a signal.
|
|
|
|
|
pub fn sig2wcs(sig: i32) -> &'static wstr {
|
|
|
|
|
let s = ffi::sig2wcs(ffi::c_int(sig));
|
|
|
|
|
let s = unsafe { U32CStr::from_ptr_str(s) };
|
|
|
|
|
|
|
|
|
|
wstr::from_ucstr(s).expect("signal name should be valid utf-32")
|
|
|
|
|
impl LookupEntry {
|
|
|
|
|
const fn new(signal: i32, name: &'static wstr, desc: &'static wstr) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
signal: Signal::new(signal),
|
|
|
|
|
name,
|
|
|
|
|
desc,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[deprecated(note = "Use [`Signal::desc()`] instead.")]
|
|
|
|
|
/// Returns a description of the specified signal.
|
|
|
|
|
pub fn signal_get_desc(sig: i32) -> &'static wstr {
|
|
|
|
|
let s = ffi::signal_get_desc(ffi::c_int(sig));
|
|
|
|
|
let s = unsafe { U32CStr::from_ptr_str(s) };
|
|
|
|
|
// Lookup table used to convert between signal names and signal ids, etc.
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
|
#[widestrs]
|
|
|
|
|
const SIGNAL_TABLE : &[LookupEntry] = &[
|
|
|
|
|
LookupEntry::new(libc::SIGHUP, "SIGHUP"L, "Terminal hung up"L),
|
|
|
|
|
LookupEntry::new(libc::SIGINT, "SIGINT"L, "Quit request from job control (^C)"L),
|
|
|
|
|
LookupEntry::new(libc::SIGQUIT, "SIGQUIT"L, "Quit request from job control with core dump (^\\)"L),
|
|
|
|
|
LookupEntry::new(libc::SIGILL, "SIGILL"L, "Illegal instruction"L),
|
|
|
|
|
LookupEntry::new(libc::SIGTRAP, "SIGTRAP"L, "Trace or breakpoint trap"L),
|
|
|
|
|
LookupEntry::new(libc::SIGABRT, "SIGABRT"L, "Abort"L),
|
|
|
|
|
LookupEntry::new(libc::SIGBUS, "SIGBUS"L, "Misaligned address error"L),
|
|
|
|
|
LookupEntry::new(libc::SIGFPE, "SIGFPE"L, "Floating point exception"L),
|
|
|
|
|
LookupEntry::new(libc::SIGKILL, "SIGKILL"L, "Forced quit"L),
|
|
|
|
|
LookupEntry::new(libc::SIGUSR1, "SIGUSR1"L, "User defined signal 1"L),
|
|
|
|
|
LookupEntry::new(libc::SIGUSR2, "SIGUSR2"L, "User defined signal 2"L),
|
|
|
|
|
LookupEntry::new(libc::SIGSEGV, "SIGSEGV"L, "Address boundary error"L),
|
|
|
|
|
LookupEntry::new(libc::SIGPIPE, "SIGPIPE"L, "Broken pipe"L),
|
|
|
|
|
LookupEntry::new(libc::SIGALRM, "SIGALRM"L, "Timer expired"L),
|
|
|
|
|
LookupEntry::new(libc::SIGTERM, "SIGTERM"L, "Polite quit request"L),
|
|
|
|
|
LookupEntry::new(libc::SIGCHLD, "SIGCHLD"L, "Child process status changed"L),
|
|
|
|
|
LookupEntry::new(libc::SIGCONT, "SIGCONT"L, "Continue previously stopped process"L),
|
|
|
|
|
LookupEntry::new(libc::SIGSTOP, "SIGSTOP"L, "Forced stop"L),
|
|
|
|
|
LookupEntry::new(libc::SIGTSTP, "SIGTSTP"L, "Stop request from job control (^Z)"L),
|
|
|
|
|
LookupEntry::new(libc::SIGTTIN, "SIGTTIN"L, "Stop from terminal input"L),
|
|
|
|
|
LookupEntry::new(libc::SIGTTOU, "SIGTTOU"L, "Stop from terminal output"L),
|
|
|
|
|
LookupEntry::new(libc::SIGURG, "SIGURG"L, "Urgent socket condition"L),
|
|
|
|
|
LookupEntry::new(libc::SIGXCPU, "SIGXCPU"L, "CPU time limit exceeded"L),
|
|
|
|
|
LookupEntry::new(libc::SIGXFSZ, "SIGXFSZ"L, "File size limit exceeded"L),
|
|
|
|
|
LookupEntry::new(libc::SIGVTALRM, "SIGVTALRM"L, "Virtual timefr expired"L),
|
|
|
|
|
LookupEntry::new(libc::SIGPROF, "SIGPROF"L, "Profiling timer expired"L),
|
|
|
|
|
LookupEntry::new(libc::SIGWINCH, "SIGWINCH"L, "Window size change"L),
|
|
|
|
|
LookupEntry::new(libc::SIGIO, "SIGIO"L, "I/O on asynchronous file descriptor is possible"L),
|
|
|
|
|
LookupEntry::new(libc::SIGSYS, "SIGSYS"L, "Bad system call"L),
|
|
|
|
|
LookupEntry::new(libc::SIGIOT, "SIGIOT"L, "Abort (Alias for SIGABRT)"L),
|
|
|
|
|
|
|
|
|
|
wstr::from_ucstr(s).expect("signal description should be valid utf-32")
|
|
|
|
|
#[cfg(any(feature = "bsd", target_os = "macos"))]
|
|
|
|
|
LookupEntry::new(libc::SIGEMT, "SIGEMT"L, "Unused signal"L),
|
|
|
|
|
|
|
|
|
|
#[cfg(any(feature = "bsd", target_os = "macos"))]
|
|
|
|
|
LookupEntry::new(libc::SIGINFO, "SIGINFO"L, "Information request"L),
|
|
|
|
|
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
LookupEntry::new(libc::SIGSTKFLT, "SISTKFLT"L, "Stack fault"L),
|
|
|
|
|
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
LookupEntry::new(libc::SIGIOT, "SIGIOT"L, "Abort (Alias for SIGABRT)"L),
|
|
|
|
|
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
#[allow(deprecated)]
|
|
|
|
|
LookupEntry::new(libc::SIGUNUSED, "SIGUNUSED"L, "Unused signal"L),
|
|
|
|
|
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
LookupEntry::new(libc::SIGPWR, "SIGPWR"L, "Power failure"L),
|
|
|
|
|
|
|
|
|
|
// TODO: determine whether SIGWIND is defined on any platform.
|
|
|
|
|
//LookupEntry::new(libc::SIGWIND, "SIGWIND"L, "Window size change"L),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// Return true if two strings are equal, ignoring ASCII case.
|
|
|
|
|
fn equals_ascii_icase(left: &wstr, right: &wstr) -> bool {
|
|
|
|
|
if left.len() != right.len() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
for (lc, rc) in left.chars().zip(right.chars()) {
|
|
|
|
|
if lc.to_ascii_lowercase() != rc.to_ascii_lowercase() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Test if \c name is a string describing the signal named \c canonical.
|
|
|
|
|
fn match_signal_name(canonical: &wstr, mut name: &wstr) -> bool {
|
|
|
|
|
// Skip the "SIG" prefix if it exists.
|
|
|
|
|
if name.char_count() >= 3 && equals_ascii_icase(name.slice_to(3), L!("sig")) {
|
|
|
|
|
name = name.slice_from(3)
|
|
|
|
|
}
|
|
|
|
|
equals_ascii_icase(canonical.slice_from(3), name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
|
|
|
@ -78,71 +427,6 @@ pub fn signal_get_desc(sig: i32) -> &'static wstr {
|
|
|
|
|
pub struct Signal(NonZeroI32);
|
|
|
|
|
|
|
|
|
|
impl Signal {
|
|
|
|
|
pub const SIGHUP: Signal = Signal::new(libc::SIGHUP);
|
|
|
|
|
pub const SIGINT: Signal = Signal::new(libc::SIGINT);
|
|
|
|
|
pub const SIGQUIT: Signal = Signal::new(libc::SIGQUIT);
|
|
|
|
|
pub const SIGILL: Signal = Signal::new(libc::SIGILL);
|
|
|
|
|
pub const SIGTRAP: Signal = Signal::new(libc::SIGTRAP);
|
|
|
|
|
pub const SIGABRT: Signal = Signal::new(libc::SIGABRT);
|
|
|
|
|
/// Available on BSD and macOS only.
|
|
|
|
|
#[cfg(any(feature = "bsd", target_os = "macos"))]
|
|
|
|
|
pub const SIGEMT: Signal = Signal::new(libc::SIGEMT);
|
|
|
|
|
pub const SIGFPE: Signal = Signal::new(libc::SIGFPE);
|
|
|
|
|
pub const SIGKILL: Signal = Signal::new(libc::SIGKILL);
|
|
|
|
|
pub const SIGBUS: Signal = Signal::new(libc::SIGBUS);
|
|
|
|
|
pub const SIGSEGV: Signal = Signal::new(libc::SIGSEGV);
|
|
|
|
|
pub const SIGSYS: Signal = Signal::new(libc::SIGSYS);
|
|
|
|
|
pub const SIGPIPE: Signal = Signal::new(libc::SIGPIPE);
|
|
|
|
|
pub const SIGALRM: Signal = Signal::new(libc::SIGALRM);
|
|
|
|
|
pub const SIGTERM: Signal = Signal::new(libc::SIGTERM);
|
|
|
|
|
pub const SIGURG: Signal = Signal::new(libc::SIGURG);
|
|
|
|
|
pub const SIGSTOP: Signal = Signal::new(libc::SIGSTOP);
|
|
|
|
|
pub const SIGTSTP: Signal = Signal::new(libc::SIGTSTP);
|
|
|
|
|
pub const SIGCONT: Signal = Signal::new(libc::SIGCONT);
|
|
|
|
|
pub const SIGCHLD: Signal = Signal::new(libc::SIGCHLD);
|
|
|
|
|
pub const SIGTTIN: Signal = Signal::new(libc::SIGTTIN);
|
|
|
|
|
pub const SIGTTOU: Signal = Signal::new(libc::SIGTTOU);
|
|
|
|
|
pub const SIGIO: Signal = Signal::new(libc::SIGIO);
|
|
|
|
|
pub const SIGXCPU: Signal = Signal::new(libc::SIGXCPU);
|
|
|
|
|
pub const SIGXFSZ: Signal = Signal::new(libc::SIGXFSZ);
|
|
|
|
|
pub const SIGVTALRM: Signal = Signal::new(libc::SIGVTALRM);
|
|
|
|
|
pub const SIGPROF: Signal = Signal::new(libc::SIGPROF);
|
|
|
|
|
pub const SIGWINCH: Signal = Signal::new(libc::SIGWINCH);
|
|
|
|
|
/// Available on BSD and macOS only.
|
|
|
|
|
#[cfg(any(feature = "bsd", target_os = "macos"))]
|
|
|
|
|
pub const SIGINFO: Signal = Signal::new(libc::SIGINFO);
|
|
|
|
|
pub const SIGUSR1: Signal = Signal::new(libc::SIGUSR1);
|
|
|
|
|
pub const SIGUSR2: Signal = Signal::new(libc::SIGUSR2);
|
|
|
|
|
/// Available on BSD only.
|
|
|
|
|
#[cfg(any(target_os = "freebsd"))]
|
|
|
|
|
pub const SIGTHR: Signal = Signal::new(32); // Not exposed by libc crate
|
|
|
|
|
/// Available on BSD only.
|
|
|
|
|
#[cfg(any(target_os = "freebsd"))]
|
|
|
|
|
pub const SIGLIBRT: Signal = Signal::new(33); // Not exposed by libc crate
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
/// Available on Linux only.
|
|
|
|
|
pub const SIGSTKFLT: Signal = Signal::new(libc::SIGSTKFLT);
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
/// Available on Linux only.
|
|
|
|
|
pub const SIGPWR: Signal = Signal::new(libc::SIGPWR);
|
|
|
|
|
|
|
|
|
|
// Signals aliased to other signals
|
|
|
|
|
/// Available on Linux only. Use [`Signal::SIGIO`] instead.
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
pub const SIGPOLL: Signal = Signal::SIGIO;
|
|
|
|
|
/// Available on Linux only. Use [`Signal::SIGSYS`] instead.
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
#[deprecated(note = "Use SIGSYS instead")]
|
|
|
|
|
pub const SIGUNUSED: Signal = Signal::SIGSYS;
|
|
|
|
|
/// Available on Linux only. Alias for [`Signal::SIGABRT`].
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
pub const SIGIOT: Signal = Signal::SIGABRT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Signal {
|
|
|
|
|
#[widestrs]
|
|
|
|
|
const UNKNOWN_SIG_NAME: &'static wstr = "SIG???"L;
|
|
|
|
|
|
|
|
|
|
/// Creates a new `Signal` to represent the passed system signal code `sig`.
|
|
|
|
|
/// Panics if `sig` is zero.
|
|
|
|
|
pub const fn new(sig: i32) -> Self {
|
|
|
|
@ -152,167 +436,58 @@ impl Signal {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[widestrs]
|
|
|
|
|
pub const fn name(&self) -> &'static wstr {
|
|
|
|
|
match *self {
|
|
|
|
|
Signal::SIGHUP => "SIGHUP"L,
|
|
|
|
|
Signal::SIGINT => "SIGINT"L,
|
|
|
|
|
Signal::SIGQUIT => "SIGQUIT"L,
|
|
|
|
|
Signal::SIGILL => "SIGILL"L,
|
|
|
|
|
Signal::SIGTRAP => "SIGTRAP"L,
|
|
|
|
|
Signal::SIGABRT => "SIGABRT"L,
|
|
|
|
|
#[cfg(any(feature = "bsd", target_os = "macos"))]
|
|
|
|
|
Signal::SIGEMT => "SIGEMT"L,
|
|
|
|
|
Signal::SIGFPE => "SIGFPE"L,
|
|
|
|
|
Signal::SIGKILL => "SIGKILL"L,
|
|
|
|
|
Signal::SIGBUS => "SIGBUS"L,
|
|
|
|
|
Signal::SIGSEGV => "SIGSEGV"L,
|
|
|
|
|
Signal::SIGSYS => "SIGSYS"L,
|
|
|
|
|
Signal::SIGPIPE => "SIGPIPE"L,
|
|
|
|
|
Signal::SIGALRM => "SIGALRM"L,
|
|
|
|
|
Signal::SIGTERM => "SIGTERM"L,
|
|
|
|
|
Signal::SIGURG => "SIGURG"L,
|
|
|
|
|
Signal::SIGSTOP => "SIGSTOP"L,
|
|
|
|
|
Signal::SIGTSTP => "SIGTSTP"L,
|
|
|
|
|
Signal::SIGCONT => "SIGCONT"L,
|
|
|
|
|
Signal::SIGCHLD => "SIGCHLD"L,
|
|
|
|
|
Signal::SIGTTIN => "SIGTTIN"L,
|
|
|
|
|
Signal::SIGTTOU => "SIGTTOU"L,
|
|
|
|
|
Signal::SIGIO => "SIGIO"L,
|
|
|
|
|
Signal::SIGXCPU => "SIGXCPU"L,
|
|
|
|
|
Signal::SIGXFSZ => "SIGXFSZ"L,
|
|
|
|
|
Signal::SIGVTALRM => "SIGVTALRM"L,
|
|
|
|
|
Signal::SIGPROF => "SIGPROF"L,
|
|
|
|
|
Signal::SIGWINCH => "SIGWINCH"L,
|
|
|
|
|
#[cfg(any(feature = "bsd", target_os = "macos"))]
|
|
|
|
|
Signal::SIGINFO => "SIGINFO"L,
|
|
|
|
|
Signal::SIGUSR1 => "SIGUSR1"L,
|
|
|
|
|
Signal::SIGUSR2 => "SIGUSR2"L,
|
|
|
|
|
#[cfg(any(target_os = "freebsd"))]
|
|
|
|
|
Signal::SIGTHR => "SIGTHR"L,
|
|
|
|
|
#[cfg(any(target_os = "freebsd"))]
|
|
|
|
|
Signal::SIGLIBRT => "SIGLIBRT"L,
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
Signal::SIGSTKFLT => "SIGSTKFLT"L,
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
Signal::SIGPWR => "SIGPWR"L,
|
|
|
|
|
Signal(_) => Self::UNKNOWN_SIG_NAME,
|
|
|
|
|
/// Return the LookupEntry for ourself.
|
|
|
|
|
fn get_lookup_entry(&self) -> Option<&'static LookupEntry> {
|
|
|
|
|
SIGNAL_TABLE
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|entry| entry.signal == self.code())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Previously sig2wcs().
|
|
|
|
|
pub fn name(&self) -> &'static wstr {
|
|
|
|
|
match self.get_lookup_entry() {
|
|
|
|
|
Some(entry) => entry.name,
|
|
|
|
|
None => wgettext!("Unknown"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Previously signal_get_desc().
|
|
|
|
|
pub fn desc(&self) -> &'static wstr {
|
|
|
|
|
match self.get_lookup_entry() {
|
|
|
|
|
Some(entry) => wgettext_str(entry.desc),
|
|
|
|
|
None => wgettext!("Unknown"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn code(&self) -> i32 {
|
|
|
|
|
self.0.into()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[widestrs]
|
|
|
|
|
pub const fn desc(&self) -> &'static wstr {
|
|
|
|
|
match *self {
|
|
|
|
|
Signal::SIGHUP => "Terminal hung up"L,
|
|
|
|
|
Signal::SIGINT => "Quit request from job control (^C)"L,
|
|
|
|
|
Signal::SIGQUIT => "Quit request from job control with core dump (^\\)"L,
|
|
|
|
|
Signal::SIGILL => "Illegal instruction"L,
|
|
|
|
|
Signal::SIGTRAP => "Trace or breakpoint trap"L,
|
|
|
|
|
Signal::SIGABRT => "Abort"L,
|
|
|
|
|
#[cfg(any(feature = "bsd", target_os = "macos"))]
|
|
|
|
|
Signal::SIGEMT => "Emulator trap"L,
|
|
|
|
|
Signal::SIGFPE => "Floating point exception"L,
|
|
|
|
|
Signal::SIGKILL => "Forced quit"L,
|
|
|
|
|
Signal::SIGBUS => "Misaligned address error"L,
|
|
|
|
|
Signal::SIGSEGV => "Address boundary error"L,
|
|
|
|
|
Signal::SIGSYS => "Bad system call"L,
|
|
|
|
|
Signal::SIGPIPE => "Broken pipe"L,
|
|
|
|
|
Signal::SIGALRM => "Timer expired"L,
|
|
|
|
|
Signal::SIGTERM => "Polite quit request"L,
|
|
|
|
|
Signal::SIGURG => "Urgent socket condition"L,
|
|
|
|
|
Signal::SIGSTOP => "Forced stop"L,
|
|
|
|
|
Signal::SIGTSTP => "Stop request from job control (^Z)"L,
|
|
|
|
|
Signal::SIGCONT => "Continue previously stopped process"L,
|
|
|
|
|
Signal::SIGCHLD => "Child process status changed"L,
|
|
|
|
|
Signal::SIGTTIN => "Stop from terminal input"L,
|
|
|
|
|
Signal::SIGTTOU => "Stop from terminal output"L,
|
|
|
|
|
Signal::SIGIO => "I/O on asynchronous file descriptior is possible"L,
|
|
|
|
|
Signal::SIGXCPU => "CPU time limit exceeded"L,
|
|
|
|
|
Signal::SIGXFSZ => "File size limit exceeded"L,
|
|
|
|
|
Signal::SIGVTALRM => "Virtual timer expired"L,
|
|
|
|
|
Signal::SIGPROF => "Profiling timer expired"L,
|
|
|
|
|
Signal::SIGWINCH => "Window size change"L,
|
|
|
|
|
#[cfg(any(feature = "bsd", target_os = "macos"))]
|
|
|
|
|
Signal::SIGINFO => "Information request"L,
|
|
|
|
|
Signal::SIGUSR1 => "User-defined signal 1"L,
|
|
|
|
|
Signal::SIGUSR2 => "User-defined signal 2"L,
|
|
|
|
|
#[cfg(any(target_os = "freebsd"))]
|
|
|
|
|
Signal::SIGTHR => "Thread interrupt"L,
|
|
|
|
|
#[cfg(any(target_os = "freebsd"))]
|
|
|
|
|
Signal::SIGLIBRT => "Real-time library interrupt"L,
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
Signal::SIGSTKFLT => "Stack fault"L,
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
Signal::SIGPWR => "Power failure"L,
|
|
|
|
|
Signal(_) => "Unknown"L,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Parses a string into the equivalent [`Signal`] sharing the same name.
|
|
|
|
|
///
|
|
|
|
|
/// Accepts both `SIGABC` and `ABC` to match against `Signal::SIGABC`. If the signal name is not
|
|
|
|
|
/// recognized, `None` is returned.
|
|
|
|
|
pub fn parse(name: &str) -> Option<Signal> {
|
|
|
|
|
let mut chars = name.chars();
|
|
|
|
|
let name = loop {
|
|
|
|
|
match chars.next() {
|
|
|
|
|
None => break Cow::Borrowed(name),
|
|
|
|
|
Some(c) if !c.is_ascii() => return None,
|
|
|
|
|
Some(c) if !c.is_ascii_uppercase() => break Cow::Owned(name.to_ascii_uppercase()),
|
|
|
|
|
_ => (),
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let name = name.strip_prefix("SIG").unwrap_or(name.as_ref());
|
|
|
|
|
match name {
|
|
|
|
|
"HUP" => Some(Signal::SIGHUP),
|
|
|
|
|
"INT" => Some(Signal::SIGINT),
|
|
|
|
|
"QUIT" => Some(Signal::SIGQUIT),
|
|
|
|
|
"ILL" => Some(Signal::SIGILL),
|
|
|
|
|
"TRAP" => Some(Signal::SIGTRAP),
|
|
|
|
|
"ABRT" => Some(Signal::SIGABRT),
|
|
|
|
|
#[cfg(any(feature = "bsd", target_os = "macos"))]
|
|
|
|
|
"EMT" => Some(Signal::SIGEMT),
|
|
|
|
|
"FPE" => Some(Signal::SIGFPE),
|
|
|
|
|
"KILL" => Some(Signal::SIGKILL),
|
|
|
|
|
"BUS" => Some(Signal::SIGBUS),
|
|
|
|
|
"SEGV" => Some(Signal::SIGSEGV),
|
|
|
|
|
"SYS" => Some(Signal::SIGSYS),
|
|
|
|
|
"PIPE" => Some(Signal::SIGPIPE),
|
|
|
|
|
"ALRM" => Some(Signal::SIGALRM),
|
|
|
|
|
"TERM" => Some(Signal::SIGTERM),
|
|
|
|
|
"URG" => Some(Signal::SIGURG),
|
|
|
|
|
"STOP" => Some(Signal::SIGSTOP),
|
|
|
|
|
"TSTP" => Some(Signal::SIGTSTP),
|
|
|
|
|
"CONT" => Some(Signal::SIGCONT),
|
|
|
|
|
"CHLD" => Some(Signal::SIGCHLD),
|
|
|
|
|
"TTIN" => Some(Signal::SIGTTIN),
|
|
|
|
|
"TTOU" => Some(Signal::SIGTTOU),
|
|
|
|
|
"IO" => Some(Signal::SIGIO),
|
|
|
|
|
"XCPU" => Some(Signal::SIGXCPU),
|
|
|
|
|
"XFSZ" => Some(Signal::SIGXFSZ),
|
|
|
|
|
"VTALRM" => Some(Signal::SIGVTALRM),
|
|
|
|
|
"PROF" => Some(Signal::SIGPROF),
|
|
|
|
|
"WINCH" => Some(Signal::SIGWINCH),
|
|
|
|
|
#[cfg(any(feature = "bsd", target_os = "macos"))]
|
|
|
|
|
"INFO" => Some(Signal::SIGINFO),
|
|
|
|
|
"USR1" => Some(Signal::SIGUSR1),
|
|
|
|
|
"USR2" => Some(Signal::SIGUSR2),
|
|
|
|
|
#[cfg(any(target_os = "freebsd"))]
|
|
|
|
|
"THR" => Some(Signal::SIGTHR),
|
|
|
|
|
#[cfg(any(target_os = "freebsd"))]
|
|
|
|
|
"LIBRT" => Some(Signal::SIGLIBRT),
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
"STKFLT" => Some(Signal::SIGSTKFLT),
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
"PWR" => Some(Signal::SIGPWR),
|
|
|
|
|
_ => None,
|
|
|
|
|
/// This also accepts integer codes via fish_wcstoi().
|
|
|
|
|
/// Previously sig2wcs().
|
|
|
|
|
pub fn parse(name: &wstr) -> Option<Signal> {
|
|
|
|
|
for entry in SIGNAL_TABLE.iter() {
|
|
|
|
|
if match_signal_name(entry.name, name) {
|
|
|
|
|
return Some(entry.signal);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Ok(num) = fish_wcstoi(name) {
|
|
|
|
|
if num > 0 {
|
|
|
|
|
return Some(Signal::new(num));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Allow signals to be compared against i32.
|
|
|
|
|
impl PartialEq<i32> for Signal {
|
|
|
|
|
fn eq(&self, other: &i32) -> bool {
|
|
|
|
|
self.code() == *other
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<Signal> for i32 {
|
|
|
|
@ -333,24 +508,32 @@ impl From<Signal> for NonZeroI32 {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn signal_name() {
|
|
|
|
|
let sig = Signal::SIGINT;
|
|
|
|
|
assert_eq!(sig.name(), "SIGINT");
|
|
|
|
|
}
|
|
|
|
|
// Need to use add_test for wgettext support.
|
|
|
|
|
use crate::ffi_tests::add_test;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parse_signal() {
|
|
|
|
|
assert_eq!(Signal::parse("SIGHUP"), Some(Signal::SIGHUP));
|
|
|
|
|
assert_eq!(Signal::parse("sigwinch"), Some(Signal::SIGWINCH));
|
|
|
|
|
assert_eq!(Signal::parse("TSTP"), Some(Signal::SIGTSTP));
|
|
|
|
|
assert_eq!(Signal::parse("TstP"), Some(Signal::SIGTSTP));
|
|
|
|
|
assert_eq!(Signal::parse("sigCONT"), Some(Signal::SIGCONT));
|
|
|
|
|
assert_eq!(Signal::parse("SIGFOO"), None);
|
|
|
|
|
assert_eq!(Signal::parse(""), None);
|
|
|
|
|
assert_eq!(Signal::parse("SIG"), None);
|
|
|
|
|
assert_eq!(Signal::parse("سلام"), None);
|
|
|
|
|
}
|
|
|
|
|
add_test!("test_signal_name", || {
|
|
|
|
|
let sig = Signal::new(libc::SIGINT);
|
|
|
|
|
assert_eq!(sig.name(), "SIGINT");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
|
add_test!("test_signal_parse", || {
|
|
|
|
|
use crate::wchar_ext::ToWString;
|
|
|
|
|
assert_eq!(Signal::parse(L!("SIGHUP")), Some(Signal::new(libc::SIGHUP)));
|
|
|
|
|
assert_eq!(Signal::parse(L!("sigwinch")), Some(Signal::new(libc::SIGWINCH)));
|
|
|
|
|
assert_eq!(Signal::parse(L!("TSTP")), Some(Signal::new(libc::SIGTSTP)));
|
|
|
|
|
assert_eq!(Signal::parse(L!("TstP")), Some(Signal::new(libc::SIGTSTP)));
|
|
|
|
|
assert_eq!(Signal::parse(L!("sigCONT")), Some(Signal::new(libc::SIGCONT)));
|
|
|
|
|
assert_eq!(Signal::parse(L!("SIGFOO")), None);
|
|
|
|
|
assert_eq!(Signal::parse(L!("")), None);
|
|
|
|
|
assert_eq!(Signal::parse(L!("SIG")), None);
|
|
|
|
|
assert_eq!(Signal::parse(L!("سلام")), None);
|
|
|
|
|
|
|
|
|
|
assert_eq!(Signal::parse(&libc::SIGINT.to_wstring()), Some(Signal::new(libc::SIGINT)));
|
|
|
|
|
assert_eq!(Signal::parse(L!("0")), None);
|
|
|
|
|
assert_eq!(Signal::parse(L!("-0")), None);
|
|
|
|
|
assert_eq!(Signal::parse(L!("-1")), None);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
|
|
|
|