1use crate::buffer::Buffer;
4use crate::fd::{AsFd, OwnedFd, RawFd};
5use crate::pid::Pid;
6use crate::signal::Signal;
7use crate::timespec::Timespec;
8use crate::{backend, io};
9
10use backend::c::{self, intptr_t, kevent as kevent_t, uintptr_t};
11use backend::event::syscalls;
12
13use core::mem::zeroed;
14use core::time::Duration;
15
16#[repr(transparent)]
18#[derive(Copy, Clone)]
19pub struct Event {
20 inner: kevent_t,
22}
23
24impl Event {
25 #[allow(clippy::needless_update)]
27 pub fn new(filter: EventFilter, flags: EventFlags, udata: *mut c::c_void) -> Event {
28 let (ident, data, filter, fflags) = match filter {
29 EventFilter::Read(fd) => (fd as uintptr_t, 0, c::EVFILT_READ, 0),
30 EventFilter::Write(fd) => (fd as _, 0, c::EVFILT_WRITE, 0),
31 #[cfg(target_os = "freebsd")]
32 EventFilter::Empty(fd) => (fd as _, 0, c::EVFILT_EMPTY, 0),
33 EventFilter::Vnode { vnode, flags } => (vnode as _, 0, c::EVFILT_VNODE, flags.bits()),
34 EventFilter::Proc { pid, flags } => {
35 (Pid::as_raw(Some(pid)) as _, 0, c::EVFILT_PROC, flags.bits())
36 }
37 EventFilter::Signal { signal, times: _ } => {
38 (signal.as_raw() as _, 0, c::EVFILT_SIGNAL, 0)
39 }
40 EventFilter::Timer { ident, timer } => {
41 #[cfg(any(apple, target_os = "freebsd", target_os = "netbsd"))]
42 let (data, fflags) = match timer {
43 Some(timer) => {
44 if timer.subsec_millis() == 0 {
45 (timer.as_secs() as _, c::NOTE_SECONDS)
46 } else if timer.subsec_nanos() == 0 {
47 (timer.as_micros() as _, c::NOTE_USECONDS)
48 } else {
49 (timer.as_nanos() as _, c::NOTE_NSECONDS)
50 }
51 }
52 None => (intptr_t::MAX, c::NOTE_SECONDS),
53 };
54 #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
55 let (data, fflags) = match timer {
56 Some(timer) => (timer.as_millis() as _, 0),
57 None => (intptr_t::MAX, 0),
58 };
59
60 (ident as _, data, c::EVFILT_TIMER, fflags)
61 }
62 #[cfg(any(apple, freebsdlike))]
63 EventFilter::User {
64 ident,
65 flags,
66 user_flags,
67 } => (ident as _, 0, c::EVFILT_USER, flags.bits() | user_flags.0),
68 EventFilter::Unknown => panic!("unknown filter"),
69 };
70
71 Event {
72 inner: kevent_t {
73 ident,
74 filter: filter as _,
75 flags: flags.bits() as _,
76 fflags,
77 data: {
78 data as _
80 },
81 udata: {
82 udata as _
84 },
85 ..unsafe { zeroed() }
86 },
87 }
88 }
89
90 pub fn flags(&self) -> EventFlags {
92 EventFlags::from_bits_retain(self.inner.flags as _)
93 }
94
95 pub fn udata(&self) -> *mut c::c_void {
97 self.inner.udata as _
99 }
100
101 pub fn data(&self) -> i64 {
103 self.inner.data as _
105 }
106
107 pub fn filter(&self) -> EventFilter {
109 match self.inner.filter as _ {
110 c::EVFILT_READ => EventFilter::Read(self.inner.ident as _),
111 c::EVFILT_WRITE => EventFilter::Write(self.inner.ident as _),
112 #[cfg(target_os = "freebsd")]
113 c::EVFILT_EMPTY => EventFilter::Empty(self.inner.ident as _),
114 c::EVFILT_VNODE => EventFilter::Vnode {
115 vnode: self.inner.ident as _,
116 flags: VnodeEvents::from_bits_retain(self.inner.fflags),
117 },
118 c::EVFILT_PROC => EventFilter::Proc {
119 pid: Pid::from_raw(self.inner.ident as _).unwrap(),
120 flags: ProcessEvents::from_bits_retain(self.inner.fflags),
121 },
122 c::EVFILT_SIGNAL => EventFilter::Signal {
123 signal: unsafe { Signal::from_raw_unchecked(self.inner.ident as _) },
125 times: self.inner.data as _,
126 },
127 c::EVFILT_TIMER => EventFilter::Timer {
128 ident: self.inner.ident as _,
129 timer: {
130 let (data, fflags) = (self.inner.data, self.inner.fflags);
131 #[cfg(not(any(apple, target_os = "freebsd", target_os = "netbsd")))]
132 let _ = fflags;
133 #[cfg(any(apple, target_os = "freebsd", target_os = "netbsd"))]
134 match fflags as _ {
135 c::NOTE_SECONDS => Some(Duration::from_secs(data as _)),
136 c::NOTE_USECONDS => Some(Duration::from_micros(data as _)),
137 c::NOTE_NSECONDS => Some(Duration::from_nanos(data as _)),
138 _ => {
139 None
141 }
142 }
143 #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
144 Some(Duration::from_millis(data as _))
145 },
146 },
147 #[cfg(any(apple, freebsdlike))]
148 c::EVFILT_USER => EventFilter::User {
149 ident: self.inner.ident as _,
150 flags: UserFlags::from_bits_retain(self.inner.fflags),
151 user_flags: UserDefinedFlags(self.inner.fflags & EVFILT_USER_FLAGS),
152 },
153 _ => EventFilter::Unknown,
154 }
155 }
156}
157
158#[cfg(any(apple, freebsdlike))]
160const EVFILT_USER_FLAGS: u32 = 0x00ff_ffff;
161
162#[repr(i16)]
164#[non_exhaustive]
165pub enum EventFilter {
166 Read(RawFd),
168
169 Write(RawFd),
171
172 #[cfg(target_os = "freebsd")]
174 Empty(RawFd),
175
176 Vnode {
178 vnode: RawFd,
180
181 flags: VnodeEvents,
183 },
184
185 Proc {
187 pid: Pid,
189
190 flags: ProcessEvents,
192 },
193
194 Signal {
196 signal: Signal,
198
199 times: usize,
202 },
203
204 Timer {
206 ident: intptr_t,
208
209 timer: Option<Duration>,
211 },
212
213 #[cfg(any(apple, freebsdlike))]
215 User {
216 ident: intptr_t,
218
219 flags: UserFlags,
221
222 user_flags: UserDefinedFlags,
224 },
225
226 Unknown,
232}
233
234bitflags::bitflags! {
235 #[repr(transparent)]
237 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
238 pub struct EventFlags: u16 {
239 const ADD = c::EV_ADD as _;
241
242 const ENABLE = c::EV_ENABLE as _;
244
245 const DISABLE = c::EV_DISABLE as _;
247
248 const DELETE = c::EV_DELETE as _;
250
251 const RECEIPT = c::EV_RECEIPT as _;
253
254 const ONESHOT = c::EV_ONESHOT as _;
256
257 const CLEAR = c::EV_CLEAR as _;
259
260 const EOF = c::EV_EOF as _;
262
263 const ERROR = c::EV_ERROR as _;
265
266 const _ = !0;
268 }
269}
270
271bitflags::bitflags! {
272 #[repr(transparent)]
274 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
275 pub struct VnodeEvents: u32 {
276 const DELETE = c::NOTE_DELETE;
278
279 const WRITE = c::NOTE_WRITE;
281
282 const EXTEND = c::NOTE_EXTEND;
284
285 const ATTRIBUTES = c::NOTE_ATTRIB;
287
288 const RENAME = c::NOTE_RENAME;
290
291 const REVOKE = c::NOTE_REVOKE;
293
294 const LINK = c::NOTE_LINK;
296
297 const _ = !0;
299 }
300}
301
302bitflags::bitflags! {
303 #[repr(transparent)]
305 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
306 pub struct ProcessEvents: u32 {
307 const EXIT = c::NOTE_EXIT;
309
310 const FORK = c::NOTE_FORK;
312
313 const EXEC = c::NOTE_EXEC;
315
316 const TRACK = c::NOTE_TRACK;
318
319 const TRACKERR = c::NOTE_TRACKERR;
321
322 const _ = !0;
324 }
325}
326
327#[cfg(any(apple, freebsdlike))]
328bitflags::bitflags! {
329 #[repr(transparent)]
331 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
332 pub struct UserFlags: u32 {
333 #[doc(alias = "NOP")]
335 const NOINPUT = c::NOTE_FFNOP;
336
337 const AND = c::NOTE_FFAND;
339
340 const OR = c::NOTE_FFOR;
342
343 const COPY = c::NOTE_FFCOPY;
345
346 const CTRLMASK = c::NOTE_FFCTRLMASK;
348
349 const UDFMASK = c::NOTE_FFLAGSMASK;
351
352 const TRIGGER = c::NOTE_TRIGGER;
354
355 const _ = !0;
357 }
358}
359
360#[repr(transparent)]
364#[cfg(any(apple, freebsdlike))]
365#[derive(Clone, Copy, Debug, Eq, PartialEq)]
366pub struct UserDefinedFlags(u32);
367
368#[cfg(any(apple, freebsdlike))]
369impl UserDefinedFlags {
370 pub fn new(flags: u32) -> Self {
372 Self(flags & EVFILT_USER_FLAGS)
373 }
374
375 pub fn get(self) -> u32 {
377 self.0
378 }
379}
380
381pub fn kqueue() -> io::Result<OwnedFd> {
396 syscalls::kqueue()
397}
398
399pub unsafe fn kevent_timespec<Fd: AsFd, Buf: Buffer<Event>>(
423 kqueue: Fd,
424 changelist: &[Event],
425 mut eventlist: Buf,
426 timeout: Option<&Timespec>,
427) -> io::Result<Buf::Output> {
428 let len = syscalls::kevent(kqueue.as_fd(), changelist, eventlist.parts_mut(), timeout)
430 .map(|res| res as _)?;
431
432 Ok(eventlist.assume_init(len))
433}
434
435pub unsafe fn kevent<Fd: AsFd, Buf: Buffer<Event>>(
449 kqueue: Fd,
450 changelist: &[Event],
451 eventlist: Buf,
452 timeout: Option<Duration>,
453) -> io::Result<Buf::Output> {
454 let timeout = match timeout {
455 Some(timeout) => match timeout.as_secs().try_into() {
456 Ok(tv_sec) => Some(Timespec {
457 tv_sec,
458 tv_nsec: timeout.subsec_nanos() as _,
459 }),
460 Err(_) => None,
461 },
462 None => None,
463 };
464
465 kevent_timespec(kqueue, changelist, eventlist, timeout.as_ref())
466}