use nix::sys::signal::{sigprocmask, SigmaskHow};
use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, Signal as NixSignal};
use nix::unistd::getpid;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicUsize, Ordering};
use std::sync::{Mutex, OnceLock};
const MAX_QUEUE_SIZE: usize = 128;
pub mod trap_flags {
pub const ZSIG_TRAPPED: u32 = 1; pub const ZSIG_IGNORED: u32 = 2; pub const ZSIG_FUNC: u32 = 4; pub const ZSIG_SHIFT: u32 = 3; }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum Signal {
SIGHUP = libc::SIGHUP,
SIGINT = libc::SIGINT,
SIGQUIT = libc::SIGQUIT,
SIGILL = libc::SIGILL,
SIGTRAP = libc::SIGTRAP,
SIGABRT = libc::SIGABRT,
SIGBUS = libc::SIGBUS,
SIGFPE = libc::SIGFPE,
SIGKILL = libc::SIGKILL,
SIGUSR1 = libc::SIGUSR1,
SIGSEGV = libc::SIGSEGV,
SIGUSR2 = libc::SIGUSR2,
SIGPIPE = libc::SIGPIPE,
SIGALRM = libc::SIGALRM,
SIGTERM = libc::SIGTERM,
SIGCHLD = libc::SIGCHLD,
SIGCONT = libc::SIGCONT,
SIGSTOP = libc::SIGSTOP,
SIGTSTP = libc::SIGTSTP,
SIGTTIN = libc::SIGTTIN,
SIGTTOU = libc::SIGTTOU,
SIGURG = libc::SIGURG,
SIGXCPU = libc::SIGXCPU,
SIGXFSZ = libc::SIGXFSZ,
SIGVTALRM = libc::SIGVTALRM,
SIGPROF = libc::SIGPROF,
SIGWINCH = libc::SIGWINCH,
SIGIO = libc::SIGIO,
SIGSYS = libc::SIGSYS,
}
pub const SIGEXIT: i32 = 0;
pub const SIGDEBUG: i32 = -1;
pub const SIGZERR: i32 = -2;
pub static SIGNAL_NAMES: &[(&str, i32)] = &[
("EXIT", SIGEXIT),
("HUP", libc::SIGHUP),
("INT", libc::SIGINT),
("QUIT", libc::SIGQUIT),
("ILL", libc::SIGILL),
("TRAP", libc::SIGTRAP),
("ABRT", libc::SIGABRT),
("BUS", libc::SIGBUS),
("FPE", libc::SIGFPE),
("KILL", libc::SIGKILL),
("USR1", libc::SIGUSR1),
("SEGV", libc::SIGSEGV),
("USR2", libc::SIGUSR2),
("PIPE", libc::SIGPIPE),
("ALRM", libc::SIGALRM),
("TERM", libc::SIGTERM),
("CHLD", libc::SIGCHLD),
("CONT", libc::SIGCONT),
("STOP", libc::SIGSTOP),
("TSTP", libc::SIGTSTP),
("TTIN", libc::SIGTTIN),
("TTOU", libc::SIGTTOU),
("URG", libc::SIGURG),
("XCPU", libc::SIGXCPU),
("XFSZ", libc::SIGXFSZ),
("VTALRM", libc::SIGVTALRM),
("PROF", libc::SIGPROF),
("WINCH", libc::SIGWINCH),
("IO", libc::SIGIO),
("SYS", libc::SIGSYS),
("DEBUG", SIGDEBUG),
("ZERR", SIGZERR),
("ERR", SIGZERR), ];
pub fn sig_by_name(name: &str) -> Option<i32> {
let name_upper = name.to_uppercase();
let lookup = if name_upper.starts_with("SIG") {
&name_upper[3..]
} else {
&name_upper
};
for (sig_name, sig_num) in SIGNAL_NAMES {
if *sig_name == lookup {
return Some(*sig_num);
}
}
lookup.parse().ok()
}
pub fn sig_name(sig: i32) -> Option<&'static str> {
for (name, num) in SIGNAL_NAMES {
if *num == sig {
return Some(name);
}
}
None
}
struct SignalQueue {
enabled: AtomicBool,
front: AtomicUsize,
rear: AtomicUsize,
signals: [AtomicI32; MAX_QUEUE_SIZE],
}
impl SignalQueue {
const fn new() -> Self {
const INIT: AtomicI32 = AtomicI32::new(0);
SignalQueue {
enabled: AtomicBool::new(false),
front: AtomicUsize::new(0),
rear: AtomicUsize::new(0),
signals: [INIT; MAX_QUEUE_SIZE],
}
}
fn is_enabled(&self) -> bool {
self.enabled.load(Ordering::SeqCst)
}
fn enable(&self) {
self.enabled.store(true, Ordering::SeqCst);
}
fn disable(&self) {
self.enabled.store(false, Ordering::SeqCst);
}
fn push(&self, sig: i32) -> bool {
let rear = self.rear.load(Ordering::SeqCst);
let new_rear = (rear + 1) % MAX_QUEUE_SIZE;
let front = self.front.load(Ordering::SeqCst);
if new_rear == front {
return false; }
self.signals[new_rear].store(sig, Ordering::SeqCst);
self.rear.store(new_rear, Ordering::SeqCst);
true
}
fn pop(&self) -> Option<i32> {
let front = self.front.load(Ordering::SeqCst);
let rear = self.rear.load(Ordering::SeqCst);
if front == rear {
return None; }
let new_front = (front + 1) % MAX_QUEUE_SIZE;
let sig = self.signals[new_front].load(Ordering::SeqCst);
self.front.store(new_front, Ordering::SeqCst);
Some(sig)
}
}
static SIGNAL_QUEUE: SignalQueue = SignalQueue::new();
static TRAP_QUEUE: SignalQueue = SignalQueue::new();
static LAST_SIGNAL: AtomicI32 = AtomicI32::new(0);
pub struct TrapHandler {
traps: Mutex<HashMap<i32, TrapAction>>,
flags: Mutex<HashMap<i32, u32>>,
pub num_trapped: AtomicUsize,
pub in_trap: AtomicBool,
pub in_exit_trap: AtomicBool,
}
#[derive(Debug, Clone)]
pub enum TrapAction {
Ignore,
Code(String),
Function(String),
Default,
}
impl Default for TrapHandler {
fn default() -> Self {
Self::new()
}
}
impl TrapHandler {
pub fn new() -> Self {
TrapHandler {
traps: Mutex::new(HashMap::new()),
flags: Mutex::new(HashMap::new()),
num_trapped: AtomicUsize::new(0),
in_trap: AtomicBool::new(false),
in_exit_trap: AtomicBool::new(false),
}
}
pub fn set_trap(&self, sig: i32, action: TrapAction) -> Result<(), String> {
if sig == libc::SIGKILL || sig == libc::SIGSTOP {
return Err(format!("can't trap SIG{}", sig_name(sig).unwrap_or("?")));
}
let mut traps = self.traps.lock().unwrap();
let mut flags = self.flags.lock().unwrap();
let was_trapped = flags
.get(&sig)
.map(|f| f & trap_flags::ZSIG_TRAPPED != 0)
.unwrap_or(false);
match &action {
TrapAction::Ignore => {
traps.insert(sig, action);
flags.insert(sig, trap_flags::ZSIG_IGNORED);
if sig > 0 {
self.ignore_signal(sig);
}
}
TrapAction::Code(code) if code.is_empty() => {
traps.insert(sig, TrapAction::Ignore);
flags.insert(sig, trap_flags::ZSIG_IGNORED);
if sig > 0 {
self.ignore_signal(sig);
}
}
TrapAction::Code(_) => {
if !was_trapped {
self.num_trapped.fetch_add(1, Ordering::SeqCst);
}
traps.insert(sig, action);
flags.insert(sig, trap_flags::ZSIG_TRAPPED);
if sig > 0 {
self.install_handler(sig);
}
}
TrapAction::Function(name) => {
if !was_trapped {
self.num_trapped.fetch_add(1, Ordering::SeqCst);
}
traps.insert(sig, TrapAction::Function(name.clone()));
flags.insert(sig, trap_flags::ZSIG_TRAPPED | trap_flags::ZSIG_FUNC);
if sig > 0 {
self.install_handler(sig);
}
}
TrapAction::Default => {
if was_trapped {
self.num_trapped.fetch_sub(1, Ordering::SeqCst);
}
traps.remove(&sig);
flags.remove(&sig);
if sig > 0 {
self.default_signal(sig);
}
}
}
Ok(())
}
pub fn unset_trap(&self, sig: i32) {
let _ = self.set_trap(sig, TrapAction::Default);
}
pub fn get_trap(&self, sig: i32) -> Option<TrapAction> {
self.traps.lock().unwrap().get(&sig).cloned()
}
pub fn is_trapped(&self, sig: i32) -> bool {
self.flags
.lock()
.unwrap()
.get(&sig)
.map(|f| f & trap_flags::ZSIG_TRAPPED != 0)
.unwrap_or(false)
}
pub fn is_ignored(&self, sig: i32) -> bool {
self.flags
.lock()
.unwrap()
.get(&sig)
.map(|f| f & trap_flags::ZSIG_IGNORED != 0)
.unwrap_or(false)
}
fn install_handler(&self, sig: i32) {
unsafe {
libc::signal(sig, handler as *const () as usize);
}
}
fn ignore_signal(&self, sig: i32) {
unsafe {
libc::signal(sig, libc::SIG_IGN);
}
}
fn default_signal(&self, sig: i32) {
unsafe {
libc::signal(sig, libc::SIG_DFL);
}
}
pub fn list_traps(&self) -> Vec<(i32, TrapAction)> {
self.traps
.lock()
.unwrap()
.iter()
.map(|(k, v)| (*k, v.clone()))
.collect()
}
}
static TRAPS: OnceLock<TrapHandler> = OnceLock::new();
pub fn traps() -> &'static TrapHandler {
TRAPS.get_or_init(TrapHandler::new)
}
static MAIN_PID: AtomicI32 = AtomicI32::new(0);
static SIGCHLD_RECEIVED: AtomicBool = AtomicBool::new(false);
static SIGWINCH_RECEIVED: AtomicBool = AtomicBool::new(false);
fn reraise_if_forked_child(sig: i32) -> bool {
if getpid().as_raw() == MAIN_PID.load(Ordering::Relaxed) {
return false;
}
unsafe {
libc::signal(sig, libc::SIG_DFL);
libc::raise(sig);
}
true
}
extern "C" fn handler(sig: i32) {
#[cfg(target_os = "macos")]
let saved_errno = unsafe { *libc::__error() };
#[cfg(not(target_os = "macos"))]
let saved_errno = unsafe { *libc::__errno_location() };
if reraise_if_forked_child(sig) {
#[cfg(target_os = "macos")]
unsafe {
*libc::__error() = saved_errno
};
#[cfg(not(target_os = "macos"))]
unsafe {
*libc::__errno_location() = saved_errno
};
return;
}
LAST_SIGNAL.store(sig, Ordering::SeqCst);
if sig == libc::SIGCHLD {
SIGCHLD_RECEIVED.store(true, Ordering::SeqCst);
} else if sig == libc::SIGWINCH {
SIGWINCH_RECEIVED.store(true, Ordering::SeqCst);
}
if SIGNAL_QUEUE.is_enabled() {
SIGNAL_QUEUE.push(sig);
#[cfg(target_os = "macos")]
unsafe {
*libc::__error() = saved_errno
};
#[cfg(not(target_os = "macos"))]
unsafe {
*libc::__errno_location() = saved_errno
};
return;
}
handle_signal(sig);
#[cfg(target_os = "macos")]
unsafe {
*libc::__error() = saved_errno
};
#[cfg(not(target_os = "macos"))]
unsafe {
*libc::__errno_location() = saved_errno
};
}
fn handle_signal(sig: i32) {
match sig {
s if s == libc::SIGCHLD => {
}
s if s == libc::SIGINT => {
if let Some(action) = traps().get_trap(s) {
run_trap(s, &action);
}
}
s if s == libc::SIGHUP => {
if let Some(action) = traps().get_trap(s) {
run_trap(s, &action);
}
}
s if s == libc::SIGWINCH => {
if let Some(action) = traps().get_trap(s) {
run_trap(s, &action);
}
}
s if s == libc::SIGALRM => {
if let Some(action) = traps().get_trap(s) {
run_trap(s, &action);
}
}
s if s == libc::SIGPIPE => {
if let Some(action) = traps().get_trap(s) {
run_trap(s, &action);
}
}
_ => {
if let Some(action) = traps().get_trap(sig) {
run_trap(sig, &action);
}
}
}
}
fn run_trap(sig: i32, action: &TrapAction) {
match action {
TrapAction::Ignore => {}
TrapAction::Code(_code) => {
traps().in_trap.store(true, Ordering::SeqCst);
if sig == SIGEXIT {
traps().in_exit_trap.store(true, Ordering::SeqCst);
}
if sig == SIGEXIT {
traps().in_exit_trap.store(false, Ordering::SeqCst);
}
traps().in_trap.store(false, Ordering::SeqCst);
}
TrapAction::Function(_name) => {
traps().in_trap.store(true, Ordering::SeqCst);
traps().in_trap.store(false, Ordering::SeqCst);
}
TrapAction::Default => {}
}
}
pub fn queue_signals() {
SIGNAL_QUEUE.enable();
}
pub fn unqueue_signals() {
SIGNAL_QUEUE.disable();
while let Some(sig) = SIGNAL_QUEUE.pop() {
handle_signal(sig);
}
}
pub fn queueing_enabled() -> bool {
SIGNAL_QUEUE.is_enabled()
}
pub fn queue_traps() {
TRAP_QUEUE.enable();
}
pub fn unqueue_traps() {
TRAP_QUEUE.disable();
while let Some(sig) = TRAP_QUEUE.pop() {
if let Some(action) = traps().get_trap(sig) {
run_trap(sig, &action);
}
}
}
pub fn signal_block(sig: i32) {
unsafe {
let mut set: libc::sigset_t = std::mem::zeroed();
libc::sigemptyset(&mut set);
libc::sigaddset(&mut set, sig);
libc::sigprocmask(libc::SIG_BLOCK, &set, std::ptr::null_mut());
}
}
pub fn signal_unblock(sig: i32) {
unsafe {
let mut set: libc::sigset_t = std::mem::zeroed();
libc::sigemptyset(&mut set);
libc::sigaddset(&mut set, sig);
libc::sigprocmask(libc::SIG_UNBLOCK, &set, std::ptr::null_mut());
}
}
pub fn hold_intr() {
signal_block(libc::SIGINT);
}
pub fn release_intr() {
signal_unblock(libc::SIGINT);
}
pub fn setup_intr() {
unsafe {
libc::signal(libc::SIGINT, handler as *const () as usize);
}
}
pub fn last_signal() -> i32 {
LAST_SIGNAL.load(Ordering::SeqCst)
}
pub fn killpg(pgrp: i32, sig: i32) -> i32 {
unsafe { libc::killpg(pgrp, sig) }
}
pub fn kill(pid: i32, sig: i32) -> i32 {
unsafe { libc::kill(pid, sig) }
}
pub fn signal_check_sigchld() -> bool {
SIGCHLD_RECEIVED.swap(false, Ordering::SeqCst)
}
pub fn signal_check_sigwinch() -> bool {
SIGWINCH_RECEIVED.swap(false, Ordering::SeqCst)
}
pub fn signal_clear_cancel() {
LAST_SIGNAL.store(0, Ordering::SeqCst);
}
pub fn signal_check_cancel() -> i32 {
let sig = LAST_SIGNAL.load(Ordering::SeqCst);
if sig == libc::SIGINT {
sig
} else {
0
}
}
pub fn signal_set_handlers(interactive: bool) {
MAIN_PID.store(getpid().as_raw(), Ordering::Relaxed);
let ignore = SigAction::new(SigHandler::SigIgn, SaFlags::empty(), SigSet::empty());
unsafe {
let _ = nix::sys::signal::sigaction(NixSignal::SIGPIPE, &ignore);
let _ = nix::sys::signal::sigaction(NixSignal::SIGQUIT, &ignore);
}
let sa_handler = SigAction::new(
SigHandler::Handler(handler),
SaFlags::SA_RESTART,
SigSet::empty(),
);
unsafe {
let _ = nix::sys::signal::sigaction(NixSignal::SIGINT, &sa_handler);
let _ = nix::sys::signal::sigaction(NixSignal::SIGCHLD, &sa_handler);
}
if interactive {
unsafe {
let _ = nix::sys::signal::sigaction(NixSignal::SIGTSTP, &ignore);
let _ = nix::sys::signal::sigaction(NixSignal::SIGTTOU, &ignore);
}
unsafe {
let _ = nix::sys::signal::sigaction(NixSignal::SIGWINCH, &sa_handler);
}
unsafe {
let _ = nix::sys::signal::sigaction(NixSignal::SIGHUP, &sa_handler);
let _ = nix::sys::signal::sigaction(NixSignal::SIGTERM, &sa_handler);
}
}
}
pub fn signal_reset_handlers() {
let default = SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty());
let signals = [
NixSignal::SIGHUP,
NixSignal::SIGINT,
NixSignal::SIGQUIT,
NixSignal::SIGTERM,
NixSignal::SIGCHLD,
NixSignal::SIGTSTP,
NixSignal::SIGTTIN,
NixSignal::SIGTTOU,
NixSignal::SIGPIPE,
];
for sig in signals {
unsafe {
let _ = nix::sys::signal::sigaction(sig, &default);
}
}
}
pub fn signal_unblock_all() {
let _ = sigprocmask(SigmaskHow::SIG_SETMASK, Some(&SigSet::empty()), None);
}
pub fn signal_block_sigchld() -> SigSet {
let mut mask = SigSet::empty();
mask.add(NixSignal::SIGCHLD);
let mut old = SigSet::empty();
let _ = sigprocmask(SigmaskHow::SIG_BLOCK, Some(&mask), Some(&mut old));
old
}
pub fn signal_restore_mask(mask: &SigSet) {
let _ = sigprocmask(SigmaskHow::SIG_SETMASK, Some(mask), None);
}
pub fn signal_desc(sig: i32) -> &'static str {
match sig {
s if s == libc::SIGHUP => "Hangup",
s if s == libc::SIGINT => "Interrupt",
s if s == libc::SIGQUIT => "Quit",
s if s == libc::SIGILL => "Illegal instruction",
s if s == libc::SIGTRAP => "Trace trap",
s if s == libc::SIGABRT => "Abort",
s if s == libc::SIGBUS => "Bus error",
s if s == libc::SIGFPE => "Floating point exception",
s if s == libc::SIGKILL => "Killed",
s if s == libc::SIGUSR1 => "User signal 1",
s if s == libc::SIGSEGV => "Segmentation fault",
s if s == libc::SIGUSR2 => "User signal 2",
s if s == libc::SIGPIPE => "Broken pipe",
s if s == libc::SIGALRM => "Alarm clock",
s if s == libc::SIGTERM => "Terminated",
s if s == libc::SIGCHLD => "Child status changed",
s if s == libc::SIGCONT => "Continued",
s if s == libc::SIGSTOP => "Stopped (signal)",
s if s == libc::SIGTSTP => "Stopped",
s if s == libc::SIGTTIN => "Stopped (tty input)",
s if s == libc::SIGTTOU => "Stopped (tty output)",
s if s == libc::SIGURG => "Urgent I/O condition",
s if s == libc::SIGXCPU => "CPU time limit exceeded",
s if s == libc::SIGXFSZ => "File size limit exceeded",
s if s == libc::SIGVTALRM => "Virtual timer expired",
s if s == libc::SIGPROF => "Profiling timer expired",
s if s == libc::SIGWINCH => "Window size changed",
s if s == libc::SIGIO => "I/O possible",
s if s == libc::SIGSYS => "Bad system call",
_ => "Unknown signal",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sig_by_name() {
assert_eq!(sig_by_name("INT"), Some(libc::SIGINT));
assert_eq!(sig_by_name("SIGINT"), Some(libc::SIGINT));
assert_eq!(sig_by_name("int"), Some(libc::SIGINT));
assert_eq!(sig_by_name("HUP"), Some(libc::SIGHUP));
assert_eq!(sig_by_name("TERM"), Some(libc::SIGTERM));
assert_eq!(sig_by_name("EXIT"), Some(SIGEXIT));
assert_eq!(sig_by_name("9"), Some(9));
}
#[test]
fn test_sig_name() {
assert_eq!(sig_name(libc::SIGINT), Some("INT"));
assert_eq!(sig_name(libc::SIGHUP), Some("HUP"));
assert_eq!(sig_name(SIGEXIT), Some("EXIT"));
}
#[test]
fn test_trap_handler() {
let handler = TrapHandler::new();
assert!(!handler.is_trapped(libc::SIGUSR1));
handler
.set_trap(libc::SIGUSR1, TrapAction::Code("echo trapped".to_string()))
.unwrap();
assert!(handler.is_trapped(libc::SIGUSR1));
handler.unset_trap(libc::SIGUSR1);
assert!(!handler.is_trapped(libc::SIGUSR1));
}
#[test]
fn test_ignore_trap() {
let handler = TrapHandler::new();
handler.set_trap(libc::SIGUSR1, TrapAction::Ignore).unwrap();
assert!(handler.is_ignored(libc::SIGUSR1));
assert!(!handler.is_trapped(libc::SIGUSR1));
}
#[test]
fn test_signal_queue() {
queue_signals();
assert!(queueing_enabled());
unqueue_signals();
assert!(!queueing_enabled());
}
#[test]
fn test_cant_trap_sigkill() {
let handler = TrapHandler::new();
let result = handler.set_trap(libc::SIGKILL, TrapAction::Code("echo".to_string()));
assert!(result.is_err());
}
}
#[cfg(unix)]
pub fn install_handler(sig: i32) {
unsafe {
libc::signal(sig, handler_func as *const () as libc::sighandler_t);
}
}
#[cfg(unix)]
extern "C" fn handler_func(sig: libc::c_int) {
unsafe {
libc::signal(sig, handler_func as *const () as libc::sighandler_t);
}
LAST_SIGNAL.store(sig, std::sync::atomic::Ordering::Relaxed);
}
pub const SIGCOUNT: i32 = 32;
pub const TRAPCOUNT: usize = (SIGCOUNT + 3) as usize;
pub fn is_fatal_signal(sig: i32) -> bool {
sig == libc::SIGKILL || sig == libc::SIGSTOP
}
#[cfg(unix)]
pub fn signal_block_all() {
unsafe {
let mut set: libc::sigset_t = std::mem::zeroed();
libc::sigfillset(&mut set);
libc::sigprocmask(libc::SIG_BLOCK, &set, std::ptr::null_mut());
}
}
#[cfg(unix)]
pub fn signal_save_mask_raw() -> libc::sigset_t {
let mut old: libc::sigset_t = unsafe { std::mem::zeroed() };
unsafe {
libc::sigprocmask(libc::SIG_BLOCK, std::ptr::null(), &mut old);
}
old
}
#[cfg(unix)]
pub fn signal_default_setup() {
unsafe {
libc::signal(libc::SIGQUIT, libc::SIG_IGN);
libc::signal(libc::SIGPIPE, libc::SIG_IGN);
install_handler(libc::SIGCHLD);
install_handler(libc::SIGWINCH);
install_handler(libc::SIGALRM);
}
}
#[cfg(unix)]
pub fn signal_suspend() {
unsafe {
libc::raise(libc::SIGTSTP);
}
}
#[cfg(unix)]
pub fn signal_wait() -> i32 {
let mut set: libc::sigset_t = unsafe { std::mem::zeroed() };
let mut sig: libc::c_int = 0;
unsafe {
libc::sigemptyset(&mut set);
libc::sigwait(&set, &mut sig);
}
sig
}
#[cfg(unix)]
pub fn signal_pending(sig: i32) -> bool {
unsafe {
let mut set: libc::sigset_t = std::mem::zeroed();
if libc::sigpending(&mut set) == 0 {
libc::sigismember(&set, sig) == 1
} else {
false
}
}
}
#[derive(Debug, Default)]
pub struct TrapScope {
saved_traps: Vec<(i32, TrapAction)>,
}
impl TrapScope {
pub fn new() -> Self {
Self::default()
}
pub fn save(&mut self, sig: i32, action: TrapAction) {
self.saved_traps.push((sig, action));
}
pub fn saved(&self) -> &[(i32, TrapAction)] {
&self.saved_traps
}
}
pub fn signal_names_list() -> Vec<String> {
let mut names = Vec::with_capacity(SIGCOUNT as usize + 1);
names.push("EXIT".to_string());
for i in 1..=SIGCOUNT {
if let Some(name) = sig_name(i) {
names.push(name.to_string());
} else {
names.push(format!("SIG{}", i));
}
}
names
}
#[cfg(unix)]
pub fn nointr() {
unsafe {
libc::signal(libc::SIGINT, libc::SIG_IGN);
}
}
#[cfg(unix)]
pub fn holdintr() {
signal_block(libc::SIGINT);
}
#[cfg(unix)]
pub fn noholdintr() {
signal_unblock(libc::SIGINT);
}
#[cfg(unix)]
pub fn signal_mask(sig: i32) -> libc::sigset_t {
let mut set: libc::sigset_t = unsafe { std::mem::zeroed() };
unsafe {
libc::sigemptyset(&mut set);
libc::sigaddset(&mut set, sig);
}
set
}
#[cfg(unix)]
pub fn signal_setmask(mask: &libc::sigset_t) {
unsafe {
libc::sigprocmask(libc::SIG_SETMASK, mask, std::ptr::null_mut());
}
}
#[cfg(unix)]
pub fn wait_for_processes() -> Vec<(i32, i32)> {
let mut results = Vec::new();
loop {
let mut status: i32 = 0;
let pid = unsafe { libc::waitpid(-1, &mut status, libc::WNOHANG | libc::WUNTRACED) };
if pid <= 0 {
break;
}
results.push((pid, status));
}
results
}
#[cfg(unix)]
extern "C" fn zhandler(sig: libc::c_int) {
unsafe {
libc::signal(sig, zhandler as *const () as libc::sighandler_t);
}
LAST_SIGNAL.store(sig, std::sync::atomic::Ordering::Relaxed);
}
#[cfg(unix)]
pub fn killrunjobs(sig: i32) {
let _ = sig;
}
#[cfg(unix)]
pub fn killjb(pgrp: i32, sig: i32) -> i32 {
if pgrp > 0 {
unsafe { libc::killpg(pgrp, sig) }
} else {
-1
}
}
pub fn dosavetrap(sig: i32, handler: &TrapHandler) -> Option<TrapAction> {
handler.get_trap(sig)
}
pub fn settrap(sig: i32, action: TrapAction) -> Result<(), String> {
let handler = traps();
handler.set_trap(sig, action)
}
pub fn unsettrap(sig: i32) {
let handler = traps();
handler.unset_trap(sig);
}
pub fn handletrap(sig: i32) -> Option<String> {
let handler = traps();
if let Some(TrapAction::Code(code)) = handler.get_trap(sig) {
Some(code)
} else {
None
}
}
pub fn dotrapargs(sig: i32, handler: &TrapHandler) -> Option<String> {
match handler.get_trap(sig) {
Some(TrapAction::Code(code)) => Some(code),
_ => None,
}
}
pub fn dotrap(sig: i32) -> Option<String> {
let handler = traps();
dotrapargs(sig, handler)
}
pub fn removetrap(sig: i32) {
unsettrap(sig);
#[cfg(unix)]
unsafe {
libc::signal(sig, libc::SIG_DFL);
}
}
pub fn rtsigno(offset: i32) -> Option<i32> {
#[cfg(target_os = "linux")]
{
let sigrtmin = 34;
let sigrtmax = 64;
let sig = sigrtmin + offset;
if sig <= sigrtmax {
Some(sig)
} else {
None
}
}
#[cfg(not(target_os = "linux"))]
{
let _ = offset;
None
}
}
pub fn rtsigname(sig: i32) -> String {
#[cfg(target_os = "linux")]
{
let sigrtmin = 34;
let offset = sig - sigrtmin;
if offset == 0 {
"RTMIN".to_string()
} else if offset > 0 {
format!("RTMIN+{}", offset)
} else {
format!("SIG{}", sig)
}
}
#[cfg(not(target_os = "linux"))]
{
format!("SIG{}", sig)
}
}