use std::{
ffi::c_void,
io,
mem::MaybeUninit,
os::{
fd::{AsFd, AsRawFd, BorrowedFd},
unix::net::UnixStream,
},
sync::OnceLock,
};
use crate::{cutils::cerr, log::dev_error};
use super::{
SignalNumber, SignalsState,
handler::{SignalHandler, SignalHandlerBehavior},
info::SignalInfo,
signal_name,
};
static STREAM: OnceLock<SignalStream> = OnceLock::new();
pub(super) unsafe fn send_siginfo(
_signal: SignalNumber,
info: *const SignalInfo,
_context: *const c_void,
) {
if let Some(tx) = STREAM.get().map(|stream| stream.tx.as_raw_fd()) {
unsafe { libc::send(tx, info.cast(), SignalInfo::SIZE, libc::MSG_DONTWAIT) };
}
}
pub(crate) struct SignalStream {
rx: UnixStream,
tx: UnixStream,
}
impl SignalStream {
#[track_caller]
pub(crate) fn init() -> io::Result<&'static Self> {
let (rx, tx) = UnixStream::pair().map_err(|err| {
dev_error!("cannot create socket pair for `SignalStream`: {err}");
err
})?;
if STREAM.set(Self { rx, tx }).is_err() {
panic!("`SignalStream` has already been initialized");
};
Ok(STREAM.get().unwrap())
}
pub(crate) fn recv(&self) -> io::Result<SignalInfo> {
let mut info = MaybeUninit::<SignalInfo>::uninit();
let fd = self.rx.as_raw_fd();
let bytes = cerr(unsafe { libc::recv(fd, info.as_mut_ptr().cast(), SignalInfo::SIZE, 0) })?;
if bytes as usize != SignalInfo::SIZE {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"Not enough bytes when receiving `siginfo_t`",
));
}
Ok(unsafe { info.assume_init() })
}
}
#[track_caller]
pub(crate) fn register_handlers<const N: usize>(
signals: [SignalNumber; N],
original_signals: &mut SignalsState,
) -> io::Result<[SignalHandler; N]> {
let mut handlers = signals.map(|signal| (signal, MaybeUninit::uninit()));
for (signal, handler) in &mut handlers {
*handler =
SignalHandler::register(*signal, SignalHandlerBehavior::Stream, original_signals)
.map(MaybeUninit::new)
.map_err(|err| {
let name = signal_name(*signal);
dev_error!("cannot setup handler for {name}: {err}");
err
})?;
}
Ok(handlers.map(|(_, handler)| unsafe { handler.assume_init() }))
}
impl AsFd for SignalStream {
fn as_fd(&self) -> BorrowedFd<'_> {
self.rx.as_fd()
}
}