#![cfg(unix)]
use crate::io::{AsyncRead, PollEvented};
use crate::signal::registry::{globals, EventId, EventInfo, Globals, Init, Storage};
use crate::sync::mpsc::{channel, Receiver};
use libc::c_int;
use mio_uds::UnixStream;
use std::io::{self, Error, ErrorKind, Write};
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;
use std::task::{Context, Poll};
pub(crate) type OsStorage = Vec<SignalInfo>;
const SIGNUM: usize = 33;
impl Init for OsStorage {
fn init() -> Self {
(0..SIGNUM).map(|_| SignalInfo::default()).collect()
}
}
impl Storage for OsStorage {
fn event_info(&self, id: EventId) -> Option<&EventInfo> {
self.get(id).map(|si| &si.event_info)
}
fn for_each<'a, F>(&'a self, f: F)
where
F: FnMut(&'a EventInfo),
{
self.iter().map(|si| &si.event_info).for_each(f)
}
}
#[derive(Debug)]
pub(crate) struct OsExtraData {
sender: UnixStream,
receiver: UnixStream,
}
impl Init for OsExtraData {
fn init() -> Self {
let (receiver, sender) = UnixStream::pair().expect("failed to create UnixStream");
Self { sender, receiver }
}
}
#[derive(Debug, Clone, Copy)]
pub struct SignalKind(c_int);
impl SignalKind {
pub fn from_raw(signum: c_int) -> Self {
Self(signum)
}
pub fn alarm() -> Self {
Self(libc::SIGALRM)
}
pub fn child() -> Self {
Self(libc::SIGCHLD)
}
pub fn hangup() -> Self {
Self(libc::SIGHUP)
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
pub fn info() -> Self {
Self(libc::SIGINFO)
}
pub fn interrupt() -> Self {
Self(libc::SIGINT)
}
pub fn io() -> Self {
Self(libc::SIGIO)
}
pub fn pipe() -> Self {
Self(libc::SIGPIPE)
}
pub fn quit() -> Self {
Self(libc::SIGQUIT)
}
pub fn terminate() -> Self {
Self(libc::SIGTERM)
}
pub fn user_defined1() -> Self {
Self(libc::SIGUSR1)
}
pub fn user_defined2() -> Self {
Self(libc::SIGUSR2)
}
pub fn window_change() -> Self {
Self(libc::SIGWINCH)
}
}
pub(crate) struct SignalInfo {
event_info: EventInfo,
init: Once,
initialized: AtomicBool,
}
impl Default for SignalInfo {
fn default() -> SignalInfo {
SignalInfo {
event_info: Default::default(),
init: Once::new(),
initialized: AtomicBool::new(false),
}
}
}
fn action(globals: Pin<&'static Globals>, signal: c_int) {
globals.record_event(signal as EventId);
let mut sender = &globals.sender;
drop(sender.write(&[1]));
}
fn signal_enable(signal: c_int) -> io::Result<()> {
if signal < 0 || signal_hook_registry::FORBIDDEN.contains(&signal) {
return Err(Error::new(
ErrorKind::Other,
format!("Refusing to register signal {}", signal),
));
}
let globals = globals();
let siginfo = match globals.storage().get(signal as EventId) {
Some(slot) => slot,
None => return Err(io::Error::new(io::ErrorKind::Other, "signal too large")),
};
let mut registered = Ok(());
siginfo.init.call_once(|| {
registered = unsafe {
signal_hook_registry::register(signal, move || action(globals, signal)).map(|_| ())
};
if registered.is_ok() {
siginfo.initialized.store(true, Ordering::Relaxed);
}
});
registered?;
if siginfo.initialized.load(Ordering::Relaxed) {
Ok(())
} else {
Err(Error::new(
ErrorKind::Other,
"Failed to register signal handler",
))
}
}
#[derive(Debug)]
struct Driver {
wakeup: PollEvented<UnixStream>,
}
impl Driver {
fn poll(&mut self, cx: &mut Context<'_>) -> Poll<()> {
self.drain(cx);
globals().broadcast();
Poll::Pending
}
}
impl Driver {
fn new() -> io::Result<Driver> {
let stream = globals().receiver.try_clone()?;
let wakeup = PollEvented::new(stream)?;
Ok(Driver { wakeup })
}
fn drain(&mut self, cx: &mut Context<'_>) {
loop {
match Pin::new(&mut self.wakeup).poll_read(cx, &mut [0; 128]) {
Poll::Ready(Ok(0)) => panic!("EOF on self-pipe"),
Poll::Ready(Ok(_)) => {}
Poll::Ready(Err(e)) => panic!("Bad read on self-pipe: {}", e),
Poll::Pending => break,
}
}
}
}
#[must_use = "streams do nothing unless polled"]
#[derive(Debug)]
pub struct Signal {
driver: Driver,
rx: Receiver<()>,
}
pub fn signal(kind: SignalKind) -> io::Result<Signal> {
let signal = kind.0;
signal_enable(signal)?;
let driver = Driver::new()?;
let (tx, rx) = channel(1);
globals().register_listener(signal as EventId, tx);
Ok(Signal { driver, rx })
}
impl Signal {
pub async fn recv(&mut self) -> Option<()> {
use crate::future::poll_fn;
poll_fn(|cx| self.poll_recv(cx)).await
}
pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> {
let _ = self.driver.poll(cx);
self.rx.poll_recv(cx)
}
}
cfg_stream! {
impl crate::stream::Stream for Signal {
type Item = ();
fn poll_next(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<()>> {
self.poll_recv(cx)
}
}
}
pub(crate) fn ctrl_c() -> io::Result<Signal> {
signal(SignalKind::interrupt())
}
#[cfg(all(test, not(loom)))]
mod tests {
use super::*;
#[test]
fn signal_enable_error_on_invalid_input() {
signal_enable(-1).unwrap_err();
}
#[test]
fn signal_enable_error_on_forbidden_input() {
signal_enable(signal_hook_registry::FORBIDDEN[0]).unwrap_err();
}
}