1use crate::fd::{AsFd, OwnedFd, RawFd};
4use crate::pid::Pid;
5use crate::signal::Signal;
6use crate::{backend, io};
7
8use backend::c::{self, intptr_t, kevent as kevent_t, uintptr_t};
9use backend::event::syscalls;
10
11use alloc::vec::Vec;
12use core::mem::zeroed;
13use core::ptr::slice_from_raw_parts_mut;
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: isize) -> 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: _ } => (signal as _, 0, c::EVFILT_SIGNAL, 0),
38 EventFilter::Timer { ident, timer } => {
39 #[cfg(any(apple, target_os = "freebsd", target_os = "netbsd"))]
40 let (data, fflags) = match timer {
41 Some(timer) => {
42 if timer.subsec_millis() == 0 {
43 (timer.as_secs() as _, c::NOTE_SECONDS)
44 } else if timer.subsec_nanos() == 0 {
45 (timer.as_micros() as _, c::NOTE_USECONDS)
46 } else {
47 (timer.as_nanos() as _, c::NOTE_NSECONDS)
48 }
49 }
50 None => (intptr_t::MAX, c::NOTE_SECONDS),
51 };
52 #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
53 let (data, fflags) = match timer {
54 Some(timer) => (timer.as_millis() as _, 0),
55 None => (intptr_t::MAX, 0),
56 };
57
58 (ident as _, data, c::EVFILT_TIMER, fflags)
59 }
60 #[cfg(any(apple, freebsdlike))]
61 EventFilter::User {
62 ident,
63 flags,
64 user_flags,
65 } => (ident as _, 0, c::EVFILT_USER, flags.bits() | user_flags.0),
66 EventFilter::Unknown => panic!("unknown filter"),
67 };
68
69 Event {
70 inner: kevent_t {
71 ident,
72 filter: filter as _,
73 flags: flags.bits() as _,
74 fflags,
75 data: {
76 data as _
78 },
79 udata: {
80 udata as _
83 },
84 ..unsafe { zeroed() }
85 },
86 }
87 }
88
89 pub fn flags(&self) -> EventFlags {
91 EventFlags::from_bits_retain(self.inner.flags as _)
92 }
93
94 pub fn udata(&self) -> isize {
96 self.inner.udata as _
100 }
101
102 pub fn data(&self) -> i64 {
104 self.inner.data as _
106 }
107
108 pub fn filter(&self) -> EventFilter {
110 match self.inner.filter as _ {
111 c::EVFILT_READ => EventFilter::Read(self.inner.ident as _),
112 c::EVFILT_WRITE => EventFilter::Write(self.inner.ident as _),
113 #[cfg(target_os = "freebsd")]
114 c::EVFILT_EMPTY => EventFilter::Empty(self.inner.ident as _),
115 c::EVFILT_VNODE => EventFilter::Vnode {
116 vnode: self.inner.ident as _,
117 flags: VnodeEvents::from_bits_retain(self.inner.fflags),
118 },
119 c::EVFILT_PROC => EventFilter::Proc {
120 pid: Pid::from_raw(self.inner.ident as _).unwrap(),
121 flags: ProcessEvents::from_bits_retain(self.inner.fflags),
122 },
123 c::EVFILT_SIGNAL => EventFilter::Signal {
124 signal: Signal::from_raw(self.inner.ident as _).unwrap(),
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 const NOINPUT = c::NOTE_FFNOP;
335
336 const AND = c::NOTE_FFAND;
338
339 const OR = c::NOTE_FFOR;
341
342 const COPY = c::NOTE_FFCOPY;
344
345 const CTRLMASK = c::NOTE_FFCTRLMASK;
347
348 const UDFMASK = c::NOTE_FFLAGSMASK;
350
351 const TRIGGER = c::NOTE_TRIGGER;
353
354 const _ = !0;
356 }
357}
358
359#[repr(transparent)]
363#[cfg(any(apple, freebsdlike))]
364#[derive(Clone, Copy, Debug, Eq, PartialEq)]
365pub struct UserDefinedFlags(u32);
366
367#[cfg(any(apple, freebsdlike))]
368impl UserDefinedFlags {
369 pub fn new(flags: u32) -> Self {
371 Self(flags & EVFILT_USER_FLAGS)
372 }
373
374 pub fn get(self) -> u32 {
376 self.0
377 }
378}
379
380pub fn kqueue() -> io::Result<OwnedFd> {
395 syscalls::kqueue()
396}
397
398pub unsafe fn kevent(
422 kqueue: impl AsFd,
423 changelist: &[Event],
424 eventlist: &mut Vec<Event>,
425 timeout: Option<Duration>,
426) -> io::Result<usize> {
427 let timeout = timeout.map(|timeout| backend::c::timespec {
428 tv_sec: timeout.as_secs() as _,
429 tv_nsec: timeout.subsec_nanos() as _,
430 });
431
432 eventlist.set_len(0);
434 let out_slice = slice_from_raw_parts_mut(eventlist.as_mut_ptr().cast(), eventlist.capacity());
435 let res = syscalls::kevent(
436 kqueue.as_fd(),
437 changelist,
438 &mut *out_slice,
439 timeout.as_ref(),
440 )
441 .map(|res| res as _);
442
443 if let Ok(len) = res {
445 eventlist.set_len(len);
446 }
447
448 res
449}