use std::fs::File;
use std::io::{ErrorKind, Result};
use std::os::fd::{FromRawFd, OwnedFd};
use std::os::unix::io::AsRawFd;
use std::ptr::{null, null_mut};
use mio::event::Source;
use mio::{Interest, Registry, Token};
use crate::ffi;
#[derive(Debug)]
pub struct Notifier {
fd: OwnedFd,
registered: Option<(Token, OwnedFd)>,
}
impl Notifier {
pub fn new() -> Result<Self> {
Ok(Notifier {
fd: File::open("/dev/null")?.into(),
registered: None,
})
}
pub fn notify(&self) -> Result<()> {
let Some((token, kqfd)) = &self.registered else {
return Err(ErrorKind::NotFound.into());
};
let event = libc::kevent {
ident: self.fd.as_raw_fd() as _,
filter: libc::EVFILT_USER,
flags: libc::EV_ADD | libc::EV_RECEIPT,
fflags: libc::NOTE_TRIGGER,
data: 0,
udata: token.0 as _,
};
ffi!(unsafe { libc::kevent(kqfd.as_raw_fd(), &event, 1, null_mut(), 0, null()) })?;
Ok(())
}
}
impl Source for Notifier {
fn register(&mut self, registry: &Registry, token: Token, _: Interest) -> Result<()> {
let event = libc::kevent {
ident: self.fd.as_raw_fd() as _,
filter: libc::EVFILT_USER,
flags: libc::EV_ADD | libc::EV_CLEAR | libc::EV_RECEIPT,
fflags: 0,
data: 0,
udata: null_mut(),
};
let kqfd = registry.as_raw_fd();
let kqfd = ffi!(unsafe { libc::dup(kqfd) })?;
let kqfd = unsafe { OwnedFd::from_raw_fd(kqfd) };
ffi!(unsafe { libc::kevent(kqfd.as_raw_fd(), &event, 1, null_mut(), 0, null()) })?;
self.registered = Some((token, kqfd));
Ok(())
}
fn deregister(&mut self, registry: &Registry) -> Result<()> {
let event = libc::kevent {
ident: self.fd.as_raw_fd() as _,
filter: libc::EVFILT_USER,
flags: libc::EV_DELETE | libc::EV_RECEIPT,
fflags: 0,
data: 0,
udata: null_mut(),
};
let kqfd = registry.as_raw_fd();
ffi!(unsafe { libc::kevent(kqfd, &event, 1, null_mut(), 0, null()) })?;
self.registered = None;
Ok(())
}
fn reregister(&mut self, _: &Registry, _: Token, _: Interest) -> Result<()> {
Err(ErrorKind::Unsupported.into())
}
}