1 //! An API for interfacing with `kqueue`.
2
3 use crate::fd::{AsFd, OwnedFd, RawFd};
4 use crate::pid::Pid;
5 use crate::signal::Signal;
6 use crate::{backend, io};
7
8 use backend::c::{self, intptr_t, kevent as kevent_t, uintptr_t};
9 use backend::event::syscalls;
10
11 use alloc::vec::Vec;
12 use core::mem::zeroed;
13 use core::ptr::slice_from_raw_parts_mut;
14 use core::time::Duration;
15
16 /// A `kqueue` event for use with [`kevent`].
17 #[repr(transparent)]
18 #[derive(Copy, Clone)]
19 pub struct Event {
20 // The layout varies between BSDs and macOS.
21 inner: kevent_t,
22 }
23
24 impl Event {
25 /// Create a new `Event`.
26 #[allow(clippy::needless_update)]
new(filter: EventFilter, flags: EventFlags, udata: isize) -> Event27 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 // On OpenBSD, data is an `i64` and not an `isize`.
77 data as _
78 },
79 udata: {
80 // On NetBSD, udata is an `isize` and not a pointer.
81 // TODO: Strict provenance, prevent int-to-ptr cast.
82 udata as _
83 },
84 ..unsafe { zeroed() }
85 },
86 }
87 }
88
89 /// Get the event flags for this event.
flags(&self) -> EventFlags90 pub fn flags(&self) -> EventFlags {
91 EventFlags::from_bits_retain(self.inner.flags as _)
92 }
93
94 /// Get the user data for this event.
udata(&self) -> isize95 pub fn udata(&self) -> isize {
96 // On NetBSD, udata is an isize and not a pointer.
97 // TODO: Strict provenance, prevent ptr-to-int cast.
98
99 self.inner.udata as _
100 }
101
102 /// Get the raw data for this event.
data(&self) -> i64103 pub fn data(&self) -> i64 {
104 // On some BSDs, data is an `isize` and not an `i64`.
105 self.inner.data as _
106 }
107
108 /// Get the filter of this event.
filter(&self) -> EventFilter109 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 // Unknown timer flags.
140 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 /// Bottom 24 bits of a u32.
159 #[cfg(any(apple, freebsdlike))]
160 const EVFILT_USER_FLAGS: u32 = 0x00ff_ffff;
161
162 /// The possible filters for a `kqueue`.
163 #[repr(i16)]
164 #[non_exhaustive]
165 pub enum EventFilter {
166 /// A read filter.
167 Read(RawFd),
168
169 /// A write filter.
170 Write(RawFd),
171
172 /// An empty filter.
173 #[cfg(target_os = "freebsd")]
174 Empty(RawFd),
175
176 /// A VNode filter.
177 Vnode {
178 /// The file descriptor we looked for events in.
179 vnode: RawFd,
180
181 /// The flags for this event.
182 flags: VnodeEvents,
183 },
184
185 /// A process filter.
186 Proc {
187 /// The process ID we waited on.
188 pid: Pid,
189
190 /// The flags for this event.
191 flags: ProcessEvents,
192 },
193
194 /// A signal filter.
195 Signal {
196 /// The signal number we waited on.
197 signal: Signal,
198
199 /// The number of times the signal has been
200 /// received since the last call to kevent.
201 times: usize,
202 },
203
204 /// A timer filter.
205 Timer {
206 /// The identifier for this event.
207 ident: intptr_t,
208
209 /// The duration for this event.
210 timer: Option<Duration>,
211 },
212
213 /// A user filter.
214 #[cfg(any(apple, freebsdlike))]
215 User {
216 /// The identifier for this event.
217 ident: intptr_t,
218
219 /// The flags for this event.
220 flags: UserFlags,
221
222 /// The user-defined flags for this event.
223 user_flags: UserDefinedFlags,
224 },
225
226 /// This filter is unknown.
227 ///
228 /// # Panics
229 ///
230 /// Passing this into `Event::new()` will result in a panic.
231 Unknown,
232 }
233
234 bitflags::bitflags! {
235 /// The flags for a `kqueue` event specifying actions to perform.
236 #[repr(transparent)]
237 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
238 pub struct EventFlags: u16 {
239 /// Add the event to the `kqueue`.
240 const ADD = c::EV_ADD as _;
241
242 /// Enable the event.
243 const ENABLE = c::EV_ENABLE as _;
244
245 /// Disable the event.
246 const DISABLE = c::EV_DISABLE as _;
247
248 /// Delete the event from the `kqueue`.
249 const DELETE = c::EV_DELETE as _;
250
251 /// TODO
252 const RECEIPT = c::EV_RECEIPT as _;
253
254 /// Clear the event after it is triggered.
255 const ONESHOT = c::EV_ONESHOT as _;
256
257 /// TODO
258 const CLEAR = c::EV_CLEAR as _;
259
260 /// TODO
261 const EOF = c::EV_EOF as _;
262
263 /// TODO
264 const ERROR = c::EV_ERROR as _;
265
266 /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
267 const _ = !0;
268 }
269 }
270
271 bitflags::bitflags! {
272 /// The flags for a virtual node event.
273 #[repr(transparent)]
274 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
275 pub struct VnodeEvents: u32 {
276 /// The file was deleted.
277 const DELETE = c::NOTE_DELETE;
278
279 /// The file was written to.
280 const WRITE = c::NOTE_WRITE;
281
282 /// The file was extended.
283 const EXTEND = c::NOTE_EXTEND;
284
285 /// The file had its attributes changed.
286 const ATTRIBUTES = c::NOTE_ATTRIB;
287
288 /// The file was renamed.
289 const RENAME = c::NOTE_RENAME;
290
291 /// Access to the file was revoked.
292 const REVOKE = c::NOTE_REVOKE;
293
294 /// The link count of the file has changed.
295 const LINK = c::NOTE_LINK;
296
297 /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
298 const _ = !0;
299 }
300 }
301
302 bitflags::bitflags! {
303 /// The flags for a process event.
304 #[repr(transparent)]
305 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
306 pub struct ProcessEvents: u32 {
307 /// The process exited.
308 const EXIT = c::NOTE_EXIT;
309
310 /// The process forked itself.
311 const FORK = c::NOTE_FORK;
312
313 /// The process executed a new process.
314 const EXEC = c::NOTE_EXEC;
315
316 /// Follow the process through `fork` calls (write only).
317 const TRACK = c::NOTE_TRACK;
318
319 /// An error has occurred with following the process.
320 const TRACKERR = c::NOTE_TRACKERR;
321
322 /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
323 const _ = !0;
324 }
325 }
326
327 #[cfg(any(apple, freebsdlike))]
328 bitflags::bitflags! {
329 /// The flags for a user event.
330 #[repr(transparent)]
331 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
332 pub struct UserFlags: u32 {
333 /// Ignore the user input flags.
334 const NOINPUT = c::NOTE_FFNOP;
335
336 /// Bitwise AND `fflags`.
337 const AND = c::NOTE_FFAND;
338
339 /// Bitwise OR `fflags`.
340 const OR = c::NOTE_FFOR;
341
342 /// Copy `fflags`.
343 const COPY = c::NOTE_FFCOPY;
344
345 /// Control mask for operations.
346 const CTRLMASK = c::NOTE_FFCTRLMASK;
347
348 /// User defined flags for masks.
349 const UDFMASK = c::NOTE_FFLAGSMASK;
350
351 /// Trigger the event.
352 const TRIGGER = c::NOTE_TRIGGER;
353
354 /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
355 const _ = !0;
356 }
357 }
358
359 /// User-defined flags.
360 ///
361 /// Only the lower 24 bits are used in this struct.
362 #[repr(transparent)]
363 #[cfg(any(apple, freebsdlike))]
364 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
365 pub struct UserDefinedFlags(u32);
366
367 #[cfg(any(apple, freebsdlike))]
368 impl UserDefinedFlags {
369 /// Create a new `UserDefinedFlags` from a `u32`.
new(flags: u32) -> Self370 pub fn new(flags: u32) -> Self {
371 Self(flags & EVFILT_USER_FLAGS)
372 }
373
374 /// Get the underlying `u32`.
get(self) -> u32375 pub fn get(self) -> u32 {
376 self.0
377 }
378 }
379
380 /// `kqueue()`—Create a new `kqueue` file descriptor.
381 ///
382 /// # References
383 /// - [Apple]
384 /// - [FreeBSD]
385 /// - [OpenBSD]
386 /// - [NetBSD]
387 /// - [DragonFly BSD]
388 ///
389 /// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kqueue.2.html
390 /// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
391 /// [OpenBSD]: https://man.openbsd.org/kqueue.2
392 /// [NetBSD]: https://man.netbsd.org/kqueue.2
393 /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=kqueue§ion=2
kqueue() -> io::Result<OwnedFd>394 pub fn kqueue() -> io::Result<OwnedFd> {
395 syscalls::kqueue()
396 }
397
398 /// `kevent(kqueue, changelist, eventlist, timeout)`—Wait for events on a
399 /// `kqueue`.
400 ///
401 /// Note: in order to receive events, make sure to allocate capacity in the
402 /// eventlist! Otherwise, the function will return immediately.
403 ///
404 /// # Safety
405 ///
406 /// The file descriptors referred to by the `Event` structs must be valid for
407 /// the lifetime of the `kqueue` file descriptor.
408 ///
409 /// # References
410 /// - [Apple]
411 /// - [FreeBSD]
412 /// - [OpenBSD]
413 /// - [NetBSD]
414 /// - [DragonFly BSD]
415 ///
416 /// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kevent.2.html
417 /// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=kevent&sektion=2
418 /// [OpenBSD]: https://man.openbsd.org/kevent.2
419 /// [NetBSD]: https://man.netbsd.org/kevent.2
420 /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=kevent§ion=2
kevent( kqueue: impl AsFd, changelist: &[Event], eventlist: &mut Vec<Event>, timeout: Option<Duration>, ) -> io::Result<usize>421 pub 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 // Populate the event list with events.
433 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 // Update the event list.
444 if let Ok(len) = res {
445 eventlist.set_len(len);
446 }
447
448 res
449 }
450