use crate::{Errno, Result};
#[cfg(not(target_os = "netbsd"))]
use libc::{c_int, c_long, intptr_t, time_t, timespec, uintptr_t};
#[cfg(target_os = "netbsd")]
use libc::{c_long, intptr_t, size_t, time_t, timespec, uintptr_t};
use std::convert::TryInto;
use std::mem;
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd};
use std::ptr;
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct KEvent {
kevent: libc::kevent,
}
#[repr(transparent)]
#[derive(Debug)]
pub struct Kqueue(OwnedFd);
impl Kqueue {
pub fn new() -> Result<Self> {
let res = unsafe { libc::kqueue() };
Errno::result(res).map(|fd| unsafe { Self(OwnedFd::from_raw_fd(fd)) })
}
pub fn kevent(
&self,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_opt: Option<timespec>,
) -> Result<usize> {
let res = unsafe {
libc::kevent(
self.0.as_raw_fd(),
changelist.as_ptr() as *const libc::kevent,
changelist.len() as type_of_nchanges,
eventlist.as_mut_ptr() as *mut libc::kevent,
eventlist.len() as type_of_nchanges,
if let Some(ref timeout) = timeout_opt {
timeout as *const timespec
} else {
ptr::null()
},
)
};
Errno::result(res).map(|r| r as usize)
}
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "openbsd"
))]
type type_of_udata = *mut libc::c_void;
#[cfg(target_os = "netbsd")]
type type_of_udata = intptr_t;
#[cfg(target_os = "netbsd")]
type type_of_event_filter = u32;
#[cfg(not(target_os = "netbsd"))]
type type_of_event_filter = i16;
libc_enum! {
#[cfg_attr(target_os = "netbsd", repr(u32))]
#[cfg_attr(not(target_os = "netbsd"), repr(i16))]
#[non_exhaustive]
pub enum EventFilter {
EVFILT_AIO,
#[cfg(target_os = "freebsd")]
EVFILT_EMPTY,
#[cfg(target_os = "dragonfly")]
EVFILT_EXCEPT,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"))]
EVFILT_FS,
#[cfg(target_os = "freebsd")]
EVFILT_LIO,
#[cfg(any(target_os = "ios", target_os = "macos"))]
EVFILT_MACHPORT,
EVFILT_PROC,
#[cfg(target_os = "freebsd")]
EVFILT_PROCDESC,
EVFILT_READ,
#[cfg(target_os = "freebsd")]
#[doc(hidden)]
#[deprecated(since = "0.27.0", note = "Never fully implemented by the OS")]
EVFILT_SENDFILE,
EVFILT_SIGNAL,
EVFILT_TIMER,
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"))]
EVFILT_USER,
#[cfg(any(target_os = "ios", target_os = "macos"))]
EVFILT_VM,
EVFILT_VNODE,
EVFILT_WRITE,
}
impl TryFrom<type_of_event_filter>
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "openbsd"
))]
#[doc(hidden)]
pub type type_of_event_flag = u16;
#[cfg(target_os = "netbsd")]
#[doc(hidden)]
pub type type_of_event_flag = u32;
libc_bitflags! {
pub struct EventFlag: type_of_event_flag {
#[allow(missing_docs)]
EV_ADD;
#[allow(missing_docs)]
EV_CLEAR;
#[allow(missing_docs)]
EV_DELETE;
#[allow(missing_docs)]
EV_DISABLE;
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
target_os = "ios", target_os = "macos",
target_os = "netbsd", target_os = "openbsd"))]
#[allow(missing_docs)]
EV_DISPATCH;
#[cfg(target_os = "freebsd")]
#[allow(missing_docs)]
EV_DROP;
#[allow(missing_docs)]
EV_ENABLE;
#[allow(missing_docs)]
EV_EOF;
#[allow(missing_docs)]
EV_ERROR;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(missing_docs)]
EV_FLAG0;
#[allow(missing_docs)]
EV_FLAG1;
#[cfg(target_os = "dragonfly")]
#[allow(missing_docs)]
EV_NODATA;
#[allow(missing_docs)]
EV_ONESHOT;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(missing_docs)]
EV_OOBAND;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(missing_docs)]
EV_POLL;
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
target_os = "ios", target_os = "macos",
target_os = "netbsd", target_os = "openbsd"))]
#[allow(missing_docs)]
EV_RECEIPT;
}
}
libc_bitflags!(
#[allow(missing_docs)]
pub struct FilterFlag: u32 {
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(missing_docs)]
NOTE_ABSOLUTE;
#[allow(missing_docs)]
NOTE_ATTRIB;
#[allow(missing_docs)]
NOTE_CHILD;
#[allow(missing_docs)]
NOTE_DELETE;
#[cfg(target_os = "openbsd")]
#[allow(missing_docs)]
NOTE_EOF;
#[allow(missing_docs)]
NOTE_EXEC;
#[allow(missing_docs)]
NOTE_EXIT;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(missing_docs)]
NOTE_EXITSTATUS;
#[allow(missing_docs)]
NOTE_EXTEND;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[allow(missing_docs)]
NOTE_FFAND;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[allow(missing_docs)]
NOTE_FFCOPY;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[allow(missing_docs)]
NOTE_FFCTRLMASK;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[allow(missing_docs)]
NOTE_FFLAGSMASK;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[allow(missing_docs)]
NOTE_FFNOP;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[allow(missing_docs)]
NOTE_FFOR;
#[allow(missing_docs)]
NOTE_FORK;
#[allow(missing_docs)]
NOTE_LINK;
#[allow(missing_docs)]
NOTE_LOWAT;
#[cfg(target_os = "freebsd")]
#[allow(missing_docs)]
NOTE_MSECONDS;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(missing_docs)]
NOTE_NONE;
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
#[allow(missing_docs)]
NOTE_NSECONDS;
#[cfg(target_os = "dragonfly")]
#[allow(missing_docs)]
NOTE_OOB;
#[allow(missing_docs)]
NOTE_PCTRLMASK;
#[allow(missing_docs)]
NOTE_PDATAMASK;
#[allow(missing_docs)]
NOTE_RENAME;
#[allow(missing_docs)]
NOTE_REVOKE;
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
#[allow(missing_docs)]
NOTE_SECONDS;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(missing_docs)]
NOTE_SIGNAL;
#[allow(missing_docs)]
NOTE_TRACK;
#[allow(missing_docs)]
NOTE_TRACKERR;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly"))]
#[allow(missing_docs)]
NOTE_TRIGGER;
#[cfg(target_os = "openbsd")]
#[allow(missing_docs)]
NOTE_TRUNCATE;
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
#[allow(missing_docs)]
NOTE_USECONDS;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(missing_docs)]
NOTE_VM_ERROR;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(missing_docs)]
NOTE_VM_PRESSURE;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(missing_docs)]
NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(missing_docs)]
NOTE_VM_PRESSURE_TERMINATE;
#[allow(missing_docs)]
NOTE_WRITE;
}
);
#[allow(missing_docs)]
#[deprecated(since = "0.27.0", note = "Use KEvent::new instead")]
pub fn kqueue() -> Result<Kqueue> {
Kqueue::new()
}
unsafe impl Send for KEvent {}
impl KEvent {
#[allow(clippy::needless_update)] pub fn new(
ident: uintptr_t,
filter: EventFilter,
flags: EventFlag,
fflags: FilterFlag,
data: intptr_t,
udata: intptr_t,
) -> KEvent {
KEvent {
kevent: libc::kevent {
ident,
filter: filter as type_of_event_filter,
flags: flags.bits(),
fflags: fflags.bits(),
data: data as _,
udata: udata as type_of_udata,
..unsafe { mem::zeroed() }
},
}
}
pub fn ident(&self) -> uintptr_t {
self.kevent.ident
}
pub fn filter(&self) -> Result<EventFilter> {
self.kevent.filter.try_into()
}
pub fn flags(&self) -> EventFlag {
EventFlag::from_bits(self.kevent.flags).unwrap()
}
pub fn fflags(&self) -> FilterFlag {
FilterFlag::from_bits(self.kevent.fflags).unwrap()
}
pub fn data(&self) -> intptr_t {
self.kevent.data as intptr_t
}
pub fn udata(&self) -> intptr_t {
self.kevent.udata as intptr_t
}
}
#[allow(missing_docs)]
#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
pub fn kevent(
kq: &Kqueue,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_ms: usize,
) -> Result<usize> {
let timeout = timespec {
tv_sec: (timeout_ms / 1000) as time_t,
tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long,
};
kq.kevent(changelist, eventlist, Some(timeout))
}
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd"
))]
type type_of_nchanges = c_int;
#[cfg(target_os = "netbsd")]
type type_of_nchanges = size_t;
#[allow(missing_docs)]
#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
pub fn kevent_ts(
kq: &Kqueue,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_opt: Option<timespec>,
) -> Result<usize> {
kq.kevent(changelist, eventlist, timeout_opt)
}
#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
#[inline]
pub fn ev_set(
ev: &mut KEvent,
ident: usize,
filter: EventFilter,
flags: EventFlag,
fflags: FilterFlag,
udata: intptr_t,
) {
ev.kevent.ident = ident as uintptr_t;
ev.kevent.filter = filter as type_of_event_filter;
ev.kevent.flags = flags.bits();
ev.kevent.fflags = fflags.bits();
ev.kevent.data = 0;
ev.kevent.udata = udata as type_of_udata;
}
#[test]
fn test_struct_kevent() {
use std::mem;
let udata: intptr_t = 12345;
let actual = KEvent::new(
0xdead_beef,
EventFilter::EVFILT_READ,
EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
0x1337,
udata,
);
assert_eq!(0xdead_beef, actual.ident());
let filter = actual.kevent.filter;
assert_eq!(libc::EVFILT_READ, filter);
assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
assert_eq!(0x1337, actual.data());
assert_eq!(udata as type_of_udata, actual.udata() as type_of_udata);
assert_eq!(mem::size_of::<libc::kevent>(), mem::size_of::<KEvent>());
}
#[test]
fn test_kevent_filter() {
let udata: intptr_t = 12345;
let actual = KEvent::new(
0xdead_beef,
EventFilter::EVFILT_READ,
EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
0x1337,
udata,
);
assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
}