use crate::stack::{Stack, pop, push};
use crate::value::Value;
use std::sync::atomic::{AtomicBool, Ordering};
const MAX_SIGNAL: usize = 32;
static SIGNAL_FLAGS: [AtomicBool; MAX_SIGNAL] = [
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
];
#[cfg(unix)]
static SIGNAL_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
#[cfg(unix)]
extern "C" fn flag_signal_handler(sig: libc::c_int) {
let sig_num = sig as usize;
if sig_num < MAX_SIGNAL {
SIGNAL_FLAGS[sig_num].store(true, Ordering::Release);
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigint(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(libc::SIGINT as i64)) }
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigterm(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(libc::SIGTERM as i64)) }
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sighup(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(libc::SIGHUP as i64)) }
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigpipe(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(libc::SIGPIPE as i64)) }
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigusr1(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(libc::SIGUSR1 as i64)) }
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigusr2(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(libc::SIGUSR2 as i64)) }
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigchld(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(libc::SIGCHLD as i64)) }
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigalrm(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(libc::SIGALRM as i64)) }
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigcont(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(libc::SIGCONT as i64)) }
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigint(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(0)) }
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigterm(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(0)) }
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sighup(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(0)) }
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigpipe(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(0)) }
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigusr1(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(0)) }
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigusr2(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(0)) }
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigchld(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(0)) }
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigalrm(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(0)) }
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_sigcont(stack: Stack) -> Stack {
unsafe { push(stack, Value::Int(0)) }
}
#[cfg(unix)]
fn install_signal_handler(sig_num: libc::c_int) -> Result<(), std::io::Error> {
use std::mem::MaybeUninit;
let _guard = SIGNAL_MUTEX
.lock()
.expect("signal: mutex poisoned during handler installation");
unsafe {
let mut action: libc::sigaction = MaybeUninit::zeroed().assume_init();
action.sa_sigaction = flag_signal_handler as *const () as libc::sighandler_t;
action.sa_flags = libc::SA_RESTART;
libc::sigemptyset(&mut action.sa_mask);
let result = libc::sigaction(sig_num, &action, std::ptr::null_mut());
if result != 0 {
return Err(std::io::Error::last_os_error());
}
}
Ok(())
}
#[cfg(unix)]
fn restore_default_handler(sig_num: libc::c_int) -> Result<(), std::io::Error> {
use std::mem::MaybeUninit;
let _guard = SIGNAL_MUTEX
.lock()
.expect("signal: mutex poisoned during handler restoration");
unsafe {
let mut action: libc::sigaction = MaybeUninit::zeroed().assume_init();
action.sa_sigaction = libc::SIG_DFL as libc::sighandler_t;
action.sa_flags = 0;
libc::sigemptyset(&mut action.sa_mask);
let result = libc::sigaction(sig_num, &action, std::ptr::null_mut());
if result != 0 {
return Err(std::io::Error::last_os_error());
}
}
Ok(())
}
#[cfg(unix)]
fn ignore_signal(sig_num: libc::c_int) -> Result<(), std::io::Error> {
use std::mem::MaybeUninit;
let _guard = SIGNAL_MUTEX
.lock()
.expect("signal: mutex poisoned during ignore");
unsafe {
let mut action: libc::sigaction = MaybeUninit::zeroed().assume_init();
action.sa_sigaction = libc::SIG_IGN as libc::sighandler_t;
action.sa_flags = 0;
libc::sigemptyset(&mut action.sa_mask);
let result = libc::sigaction(sig_num, &action, std::ptr::null_mut());
if result != 0 {
return Err(std::io::Error::last_os_error());
}
}
Ok(())
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_trap(stack: Stack) -> Stack {
unsafe {
let (stack, sig_val) = pop(stack);
let sig_num = match sig_val {
Value::Int(n) => {
if n < 0 || n as usize >= MAX_SIGNAL {
panic!("signal.trap: invalid signal number {}", n);
}
n as libc::c_int
}
_ => panic!(
"signal.trap: expected Int (signal number), got {:?}",
sig_val
),
};
if let Err(e) = install_signal_handler(sig_num) {
panic!(
"signal.trap: failed to install handler for signal {}: {}",
sig_num, e
);
}
stack
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_received(stack: Stack) -> Stack {
unsafe {
let (stack, sig_val) = pop(stack);
let sig_num = match sig_val {
Value::Int(n) => {
if n < 0 || n as usize >= MAX_SIGNAL {
panic!("signal.received?: invalid signal number {}", n);
}
n as usize
}
_ => panic!(
"signal.received?: expected Int (signal number), got {:?}",
sig_val
),
};
let was_set = SIGNAL_FLAGS[sig_num].swap(false, Ordering::Acquire);
push(stack, Value::Bool(was_set))
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_pending(stack: Stack) -> Stack {
unsafe {
let (stack, sig_val) = pop(stack);
let sig_num = match sig_val {
Value::Int(n) => {
if n < 0 || n as usize >= MAX_SIGNAL {
panic!("signal.pending?: invalid signal number {}", n);
}
n as usize
}
_ => panic!(
"signal.pending?: expected Int (signal number), got {:?}",
sig_val
),
};
let is_set = SIGNAL_FLAGS[sig_num].load(Ordering::Acquire);
push(stack, Value::Bool(is_set))
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_default(stack: Stack) -> Stack {
unsafe {
let (stack, sig_val) = pop(stack);
let sig_num = match sig_val {
Value::Int(n) => {
if n < 0 || n as usize >= MAX_SIGNAL {
panic!("signal.default: invalid signal number {}", n);
}
n as libc::c_int
}
_ => panic!(
"signal.default: expected Int (signal number), got {:?}",
sig_val
),
};
if let Err(e) = restore_default_handler(sig_num) {
panic!(
"signal.default: failed to restore default handler for signal {}: {}",
sig_num, e
);
}
stack
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_ignore(stack: Stack) -> Stack {
unsafe {
let (stack, sig_val) = pop(stack);
let sig_num = match sig_val {
Value::Int(n) => {
if n < 0 || n as usize >= MAX_SIGNAL {
panic!("signal.ignore: invalid signal number {}", n);
}
n as libc::c_int
}
_ => panic!(
"signal.ignore: expected Int (signal number), got {:?}",
sig_val
),
};
if let Err(e) = ignore_signal(sig_num) {
panic!("signal.ignore: failed to ignore signal {}: {}", sig_num, e);
}
stack
}
}
#[cfg(unix)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_clear(stack: Stack) -> Stack {
unsafe {
let (stack, sig_val) = pop(stack);
let sig_num = match sig_val {
Value::Int(n) => {
if n < 0 || n as usize >= MAX_SIGNAL {
panic!("signal.clear: invalid signal number {}", n);
}
n as usize
}
_ => panic!(
"signal.clear: expected Int (signal number), got {:?}",
sig_val
),
};
SIGNAL_FLAGS[sig_num].store(false, Ordering::Release);
stack
}
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_trap(stack: Stack) -> Stack {
let (stack, _) = unsafe { pop(stack) };
stack
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_default(stack: Stack) -> Stack {
let (stack, _) = unsafe { pop(stack) };
stack
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_ignore(stack: Stack) -> Stack {
let (stack, _) = unsafe { pop(stack) };
stack
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_received(stack: Stack) -> Stack {
let (stack, _) = unsafe { pop(stack) };
unsafe { push(stack, Value::Bool(false)) }
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_pending(stack: Stack) -> Stack {
let (stack, _) = unsafe { pop(stack) };
unsafe { push(stack, Value::Bool(false)) }
}
#[cfg(not(unix))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_signal_clear(stack: Stack) -> Stack {
let (stack, _) = unsafe { pop(stack) };
stack
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
#[test]
#[serial]
fn test_signal_flag_operations() {
const TEST_IDX: usize = 3;
SIGNAL_FLAGS[TEST_IDX].store(false, Ordering::Release);
assert!(!SIGNAL_FLAGS[TEST_IDX].load(Ordering::Acquire));
SIGNAL_FLAGS[TEST_IDX].store(true, Ordering::Release);
assert!(SIGNAL_FLAGS[TEST_IDX].load(Ordering::Acquire));
let was_set = SIGNAL_FLAGS[TEST_IDX].swap(false, Ordering::Acquire);
assert!(was_set);
assert!(!SIGNAL_FLAGS[TEST_IDX].load(Ordering::Acquire));
let was_set = SIGNAL_FLAGS[TEST_IDX].swap(false, Ordering::Acquire);
assert!(!was_set);
SIGNAL_FLAGS[TEST_IDX].store(false, Ordering::Release);
}
#[cfg(unix)]
#[test]
#[serial]
fn test_signal_handler_installation() {
let result = install_signal_handler(libc::SIGUSR1);
assert!(result.is_ok(), "Failed to install SIGUSR1 handler");
let result = restore_default_handler(libc::SIGUSR1);
assert!(result.is_ok(), "Failed to restore SIGUSR1 default handler");
}
#[cfg(unix)]
#[test]
#[serial]
fn test_signal_delivery() {
install_signal_handler(libc::SIGUSR1).expect("Failed to install handler");
SIGNAL_FLAGS[libc::SIGUSR1 as usize].store(false, Ordering::Release);
unsafe {
libc::kill(libc::getpid(), libc::SIGUSR1);
}
std::thread::sleep(std::time::Duration::from_millis(1));
let received = SIGNAL_FLAGS[libc::SIGUSR1 as usize].swap(false, Ordering::Acquire);
assert!(received, "Signal was not received");
restore_default_handler(libc::SIGUSR1).expect("Failed to restore handler");
}
#[cfg(unix)]
#[test]
#[serial]
fn test_invalid_signal_fails() {
let result = install_signal_handler(libc::SIGKILL);
assert!(result.is_err(), "SIGKILL should not be catchable");
let result = install_signal_handler(libc::SIGSTOP);
assert!(result.is_err(), "SIGSTOP should not be catchable");
}
#[cfg(unix)]
#[test]
#[serial]
fn test_signal_ignore() {
let result = ignore_signal(libc::SIGUSR2);
assert!(result.is_ok(), "Failed to ignore SIGUSR2");
let result = restore_default_handler(libc::SIGUSR2);
assert!(result.is_ok(), "Failed to restore SIGUSR2 default handler");
}
#[cfg(unix)]
#[test]
#[serial]
fn test_multiple_signals_independent() {
install_signal_handler(libc::SIGUSR1).expect("Failed to install SIGUSR1");
install_signal_handler(libc::SIGUSR2).expect("Failed to install SIGUSR2");
SIGNAL_FLAGS[libc::SIGUSR1 as usize].store(false, Ordering::Release);
SIGNAL_FLAGS[libc::SIGUSR2 as usize].store(false, Ordering::Release);
unsafe {
libc::kill(libc::getpid(), libc::SIGUSR1);
}
std::thread::sleep(std::time::Duration::from_millis(1));
assert!(SIGNAL_FLAGS[libc::SIGUSR1 as usize].load(Ordering::Acquire));
assert!(!SIGNAL_FLAGS[libc::SIGUSR2 as usize].load(Ordering::Acquire));
restore_default_handler(libc::SIGUSR1).expect("Failed to restore SIGUSR1");
restore_default_handler(libc::SIGUSR2).expect("Failed to restore SIGUSR2");
}
#[cfg(unix)]
#[test]
fn test_signal_constants_valid() {
assert!(libc::SIGINT > 0 && (libc::SIGINT as usize) < MAX_SIGNAL);
assert!(libc::SIGTERM > 0 && (libc::SIGTERM as usize) < MAX_SIGNAL);
assert!(libc::SIGHUP > 0 && (libc::SIGHUP as usize) < MAX_SIGNAL);
assert!(libc::SIGPIPE > 0 && (libc::SIGPIPE as usize) < MAX_SIGNAL);
assert!(libc::SIGUSR1 > 0 && (libc::SIGUSR1 as usize) < MAX_SIGNAL);
assert!(libc::SIGUSR2 > 0 && (libc::SIGUSR2 as usize) < MAX_SIGNAL);
}
}