#[cfg(all(
not(feature = "libc-signal-mode"),
not(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "riscv64"
))
))]
compile_error!(
"varta-watch on Linux currently supports x86_64, aarch64, and riscv64 only — \
add KernelSigAction + rt_sigaction_raw arms for this architecture \
(see book/src/architecture/signal-install.md for the extension recipe). \
Alternatively, enable the `libc-signal-mode` Cargo feature to skip the \
direct-syscall path entirely."
);
#[cfg(not(feature = "libc-signal-mode"))]
pub(super) mod direct;
#[cfg(not(feature = "libc-signal-mode"))]
pub(super) mod kernel_abi;
pub(super) mod libc_wrapper;
#[cfg(not(feature = "libc-signal-mode"))]
pub(super) mod syscall;
#[cfg(not(feature = "libc-signal-mode"))]
pub(super) mod trampoline;
use std::io;
use super::mode::SignalHandlerMode;
#[cfg(not(feature = "libc-signal-mode"))]
use kernel_abi::KernelSigAction;
#[cfg(not(feature = "libc-signal-mode"))]
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(feature = "libc-signal-mode"))]
use syscall::rt_sigaction_raw;
#[cfg(not(feature = "libc-signal-mode"))]
pub const SA_RESTART: u64 = 0x1000_0000;
#[cfg(all(not(feature = "libc-signal-mode"), target_arch = "x86_64"))]
pub const SA_RESTORER: u64 = 0x0400_0000;
#[cfg(all(not(feature = "libc-signal-mode"), not(target_arch = "x86_64")))]
#[allow(dead_code)]
pub const SA_RESTORER: u64 = 0;
pub(super) unsafe fn install(
mode: SignalHandlerMode,
handler: extern "C" fn(i32),
) -> io::Result<()> {
#[cfg(not(feature = "libc-signal-mode"))]
match mode {
SignalHandlerMode::Direct => {
unsafe { direct::install(handler) }?;
unsafe { verify_live_delivery() }?;
}
SignalHandlerMode::Libc => {
unsafe { libc_wrapper::install(handler) }?;
}
}
#[cfg(feature = "libc-signal-mode")]
{
let _ = mode; unsafe { libc_wrapper::install(handler) }?;
}
Ok(())
}
#[cfg(not(feature = "libc-signal-mode"))]
static SMOKE_TEST_FIRED: AtomicBool = AtomicBool::new(false);
#[cfg(not(feature = "libc-signal-mode"))]
extern "C" fn smoke_handler(_: i32) {
SMOKE_TEST_FIRED.store(true, Ordering::Release);
}
#[cfg(not(feature = "libc-signal-mode"))]
const SIGUSR1: i32 = 10;
#[cfg(not(feature = "libc-signal-mode"))]
extern "C" {
fn getpid() -> i32;
fn kill(pid: i32, sig: i32) -> i32;
}
#[cfg(not(feature = "libc-signal-mode"))]
unsafe fn verify_live_delivery() -> io::Result<()> {
SMOKE_TEST_FIRED.store(false, Ordering::SeqCst);
let mut old = std::mem::MaybeUninit::<KernelSigAction>::zeroed();
unsafe { direct::install_one_direct(SIGUSR1, smoke_handler, old.as_mut_ptr()) }?;
let pid = unsafe { getpid() };
unsafe { kill(pid, SIGUSR1) };
let deadline = std::time::Instant::now() + std::time::Duration::from_millis(50);
loop {
if SMOKE_TEST_FIRED.load(Ordering::Acquire) {
break;
}
if std::time::Instant::now() >= deadline {
let old_init = unsafe { old.assume_init() };
unsafe { rt_sigaction_raw(SIGUSR1, &old_init, std::ptr::null_mut()) };
return Err(io::Error::new(
io::ErrorKind::Other,
"signal smoke test: SIGUSR1 not delivered within 50 ms — \
trampoline ABI broken or kernel signal delivery disabled?",
));
}
std::thread::sleep(std::time::Duration::from_millis(1));
}
let old_init = unsafe { old.assume_init() };
let rc = unsafe { rt_sigaction_raw(SIGUSR1, &old_init, std::ptr::null_mut()) };
if rc < 0 {
return Err(io::Error::from_raw_os_error(-rc as i32));
}
Ok(())
}
#[cfg(all(any(test, feature = "test-hooks"), not(feature = "libc-signal-mode")))]
pub(crate) mod test_abi {
pub use super::kernel_abi::KernelSigAction;
pub use super::syscall::rt_sigaction_raw;
#[cfg(target_arch = "x86_64")]
pub use super::trampoline::varta_signal_restorer;
pub use super::{SA_RESTART, SA_RESTORER};
}