mirror of
https://github.com/fish-shell/fish-shell
synced 2024-11-14 17:07:44 +00:00
Add Signal newtype
Signal is a newtype around NonZeroI32. We could use NonZeroU8 since all signal values comfortably fit, but using i32 lets us avoid a fallible attempt at narrowing values returned from the system as integers to the narrower u8 type. Known signals are explicitly defined as constants and can be matched against with equality or with pattern matching in a `match` block. Unknown signal values are passed-through without causing any issues. We're using per-OS targeting to enable certain libc SIGXXX values - we could change this to dynamically detecting what's available in build.rs but then it might not match what libc exposes, still giving us build failures.
This commit is contained in:
parent
2e66bb19da
commit
1f4c233dfb
1 changed files with 299 additions and 0 deletions
|
@ -1,3 +1,6 @@
|
|||
use std::borrow::Cow;
|
||||
use std::num::NonZeroI32;
|
||||
|
||||
use crate::ffi;
|
||||
use crate::topic_monitor::{generation_t, invalid_generations, topic_monitor_principal, topic_t};
|
||||
use crate::wchar::wstr;
|
||||
|
@ -43,6 +46,7 @@ impl sigchecker_t {
|
|||
}
|
||||
}
|
||||
|
||||
#[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));
|
||||
|
@ -50,6 +54,7 @@ pub fn wcs2sig(s: &wstr) -> Option<usize> {
|
|||
sig.0.try_into().ok()
|
||||
}
|
||||
|
||||
#[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));
|
||||
|
@ -58,6 +63,7 @@ pub fn sig2wcs(sig: i32) -> &'static wstr {
|
|||
wstr::from_ucstr(s).expect("signal name should be valid utf-32")
|
||||
}
|
||||
|
||||
#[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));
|
||||
|
@ -65,3 +71,296 @@ pub fn signal_get_desc(sig: i32) -> &'static wstr {
|
|||
|
||||
wstr::from_ucstr(s).expect("signal description should be valid utf-32")
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
|
||||
/// A wrapper around the system signal code.
|
||||
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(target_os = "freebsd", 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(target_os = "freebsd", 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 {
|
||||
const UNKNOWN_SIG_NAME: &'static str = "SIG???";
|
||||
|
||||
/// Creates a new `Signal` to represent the passed system signal code `sig`.
|
||||
/// Panics if `sig` is zero.
|
||||
pub const fn new(sig: i32) -> Self {
|
||||
match NonZeroI32::new(sig) {
|
||||
None => panic!("Invalid zero signal value!"),
|
||||
Some(result) => Signal(result),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn name(&self) -> &'static str {
|
||||
match *self {
|
||||
Signal::SIGHUP => "SIGHUP",
|
||||
Signal::SIGINT => "SIGINT",
|
||||
Signal::SIGQUIT => "SIGQUIT",
|
||||
Signal::SIGILL => "SIGILL",
|
||||
Signal::SIGTRAP => "SIGTRAP",
|
||||
Signal::SIGABRT => "SIGABRT",
|
||||
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
|
||||
Signal::SIGEMT => "SIGEMT",
|
||||
Signal::SIGFPE => "SIGFPE",
|
||||
Signal::SIGKILL => "SIGKILL",
|
||||
Signal::SIGBUS => "SIGBUS",
|
||||
Signal::SIGSEGV => "SIGSEGV",
|
||||
Signal::SIGSYS => "SIGSYS",
|
||||
Signal::SIGPIPE => "SIGPIPE",
|
||||
Signal::SIGALRM => "SIGALRM",
|
||||
Signal::SIGTERM => "SIGTERM",
|
||||
Signal::SIGURG => "SIGURG",
|
||||
Signal::SIGSTOP => "SIGSTOP",
|
||||
Signal::SIGTSTP => "SIGTSTP",
|
||||
Signal::SIGCONT => "SIGCONT",
|
||||
Signal::SIGCHLD => "SIGCHLD",
|
||||
Signal::SIGTTIN => "SIGTTIN",
|
||||
Signal::SIGTTOU => "SIGTTOU",
|
||||
Signal::SIGIO => "SIGIO",
|
||||
Signal::SIGXCPU => "SIGXCPU",
|
||||
Signal::SIGXFSZ => "SIGXFSZ",
|
||||
Signal::SIGVTALRM => "SIGVTALRM",
|
||||
Signal::SIGPROF => "SIGPROF",
|
||||
Signal::SIGWINCH => "SIGWINCH",
|
||||
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
|
||||
Signal::SIGINFO => "SIGINFO",
|
||||
Signal::SIGUSR1 => "SIGUSR1",
|
||||
Signal::SIGUSR2 => "SIGUSR2",
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
Signal::SIGTHR => "SIGTHR",
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
Signal::SIGLIBRT => "SIGLIBRT",
|
||||
#[cfg(target_os = "linux")]
|
||||
Signal::SIGSTKFLT => "SIGSTKFLT",
|
||||
#[cfg(target_os = "linux")]
|
||||
Signal::SIGPWR => "SIGPWR",
|
||||
Signal(_) => Self::UNKNOWN_SIG_NAME,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn code(&self) -> i32 {
|
||||
self.0.into()
|
||||
}
|
||||
|
||||
pub const fn desc(&self) -> &'static str {
|
||||
match *self {
|
||||
Signal::SIGHUP => "Terminal hung up",
|
||||
Signal::SIGINT => "Quit request from job control (^C)",
|
||||
Signal::SIGQUIT => "Quit request from job control with core dump (^\\)",
|
||||
Signal::SIGILL => "Illegal instruction",
|
||||
Signal::SIGTRAP => "Trace or breakpoint trap",
|
||||
Signal::SIGABRT => "Abort",
|
||||
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
|
||||
Signal::SIGEMT => "Emulator trap",
|
||||
Signal::SIGFPE => "Floating point exception",
|
||||
Signal::SIGKILL => "Forced quit",
|
||||
Signal::SIGBUS => "Misaligned address error",
|
||||
Signal::SIGSEGV => "Address boundary error",
|
||||
Signal::SIGSYS => "Bad system call",
|
||||
Signal::SIGPIPE => "Broken pipe",
|
||||
Signal::SIGALRM => "Timer expired",
|
||||
Signal::SIGTERM => "Polite quit request",
|
||||
Signal::SIGURG => "Urgent socket condition",
|
||||
Signal::SIGSTOP => "Forced stop",
|
||||
Signal::SIGTSTP => "Stop request from job control (^Z)",
|
||||
Signal::SIGCONT => "Continue previously stopped process",
|
||||
Signal::SIGCHLD => "Child process status changed",
|
||||
Signal::SIGTTIN => "Stop from terminal input",
|
||||
Signal::SIGTTOU => "Stop from terminal output",
|
||||
Signal::SIGIO => "I/O on asynchronous file descriptior is possible",
|
||||
Signal::SIGXCPU => "CPU time limit exceeded",
|
||||
Signal::SIGXFSZ => "File size limit exceeded",
|
||||
Signal::SIGVTALRM => "Virtual timer expired",
|
||||
Signal::SIGPROF => "Profiling timer expired",
|
||||
Signal::SIGWINCH => "Window size change",
|
||||
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
|
||||
Signal::SIGINFO => "Information request",
|
||||
Signal::SIGUSR1 => "User-defined signal 1",
|
||||
Signal::SIGUSR2 => "User-defined signal 2",
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
Signal::SIGTHR => "Thread interrupt",
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
Signal::SIGLIBRT => "Real-time library interrupt",
|
||||
#[cfg(target_os = "linux")]
|
||||
Signal::SIGSTKFLT => "Stack fault",
|
||||
#[cfg(target_os = "linux")]
|
||||
Signal::SIGPWR => "Power failure",
|
||||
Signal(_) => "Unknown",
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(target_os = "freebsd", 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(target_os = "freebsd", 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Signal {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("{}({})", self.name(), self.code()))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Signal {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let name = self.name();
|
||||
if name != Self::UNKNOWN_SIG_NAME {
|
||||
f.write_str(name)
|
||||
} else {
|
||||
f.write_fmt(format_args!("Unrecognized Signal {}", self.code()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signal> for i32 {
|
||||
fn from(value: Signal) -> Self {
|
||||
value.code()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signal> for usize {
|
||||
fn from(value: Signal) -> Self {
|
||||
value.code() as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signal> for NonZeroI32 {
|
||||
fn from(value: Signal) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signal_name() {
|
||||
let sig = Signal::SIGINT;
|
||||
assert_eq!(sig.name(), "SIGINT");
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue