rustix/event/
kqueue.rs

1//! An API for interfacing with `kqueue`.
2
3use 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/// A `kqueue` event for use with [`kevent`].
17#[repr(transparent)]
18#[derive(Copy, Clone)]
19pub struct Event {
20    // The layout varies between BSDs and macOS.
21    inner: kevent_t,
22}
23
24impl Event {
25    /// Create a new `Event`.
26    #[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                    // On OpenBSD, data is an `i64` and not an `isize`.
79                    data as _
80                },
81                udata: {
82                    // On NetBSD, udata is an `isize` and not a pointer.
83                    udata as _
84                },
85                ..unsafe { zeroed() }
86            },
87        }
88    }
89
90    /// Get the event flags for this event.
91    pub fn flags(&self) -> EventFlags {
92        EventFlags::from_bits_retain(self.inner.flags as _)
93    }
94
95    /// Get the user data for this event.
96    pub fn udata(&self) -> *mut c::c_void {
97        // On NetBSD, udata is an isize and not a pointer.
98        self.inner.udata as _
99    }
100
101    /// Get the raw data for this event.
102    pub fn data(&self) -> i64 {
103        // On some BSDs, data is an `isize` and not an `i64`.
104        self.inner.data as _
105    }
106
107    /// Get the filter of this event.
108    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                // SAFETY: `EventFilter::new` requires a valid `Signal`.
124                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                            // 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))]
160const EVFILT_USER_FLAGS: u32 = 0x00ff_ffff;
161
162/// The possible filters for a `kqueue`.
163#[repr(i16)]
164#[non_exhaustive]
165pub 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 received since the last
200        /// 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
234bitflags::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
271bitflags::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
302bitflags::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))]
328bitflags::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        #[doc(alias = "NOP")]
335        const NOINPUT = c::NOTE_FFNOP;
336
337        /// Bitwise AND `fflags`.
338        const AND = c::NOTE_FFAND;
339
340        /// Bitwise OR `fflags`.
341        const OR = c::NOTE_FFOR;
342
343        /// Copy `fflags`.
344        const COPY = c::NOTE_FFCOPY;
345
346        /// Control mask for operations.
347        const CTRLMASK = c::NOTE_FFCTRLMASK;
348
349        /// User defined flags for masks.
350        const UDFMASK = c::NOTE_FFLAGSMASK;
351
352        /// Trigger the event.
353        const TRIGGER = c::NOTE_TRIGGER;
354
355        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
356        const _ = !0;
357    }
358}
359
360/// User-defined flags.
361///
362/// Only the lower 24 bits are used in this struct.
363#[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    /// Create a new `UserDefinedFlags` from a `u32`.
371    pub fn new(flags: u32) -> Self {
372        Self(flags & EVFILT_USER_FLAGS)
373    }
374
375    /// Get the underlying `u32`.
376    pub fn get(self) -> u32 {
377        self.0
378    }
379}
380
381/// `kqueue()`—Create a new `kqueue` file descriptor.
382///
383/// # References
384///  - [Apple]
385///  - [FreeBSD]
386///  - [OpenBSD]
387///  - [NetBSD]
388///  - [DragonFly BSD]
389///
390/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kqueue.2.html
391/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
392/// [OpenBSD]: https://man.openbsd.org/kqueue.2
393/// [NetBSD]: https://man.netbsd.org/kqueue.2
394/// [DragonFly BSD]: https://man.dragonflybsd.org/?command=kqueue&section=2
395pub fn kqueue() -> io::Result<OwnedFd> {
396    syscalls::kqueue()
397}
398
399/// `kevent(kqueue, changelist, eventlist, timeout)`—Wait for events on a
400/// `kqueue`.
401///
402/// If an unsupported timeout is passed, this function fails with
403/// [`io::Errno::INVAL`].
404///
405/// # Safety
406///
407/// The file descriptors referred to by the `Event` structs must be valid for
408/// the lifetime of the `kqueue` file descriptor.
409///
410/// # References
411///  - [Apple]
412///  - [FreeBSD]
413///  - [OpenBSD]
414///  - [NetBSD]
415///  - [DragonFly BSD]
416///
417/// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kevent.2.html
418/// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=kevent&sektion=2
419/// [OpenBSD]: https://man.openbsd.org/kevent.2
420/// [NetBSD]: https://man.netbsd.org/kevent.2
421/// [DragonFly BSD]: https://man.dragonflybsd.org/?command=kevent&section=2
422pub 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    // Populate the event list with events.
429    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
435/// `kevent(kqueue, changelist, eventlist, timeout)`—Wait for events on a
436/// `kqueue`.
437///
438/// This is a wrapper around [`kevent_timespec`] which takes a `Duration`
439/// instead of a `Timespec` for the timemout value. `Timespec` has a signed
440/// `i64` seconds field; if converting `Duration` to `Timespec` overflows,
441/// `None` is passed as the timeout instead, such such a large timeout would
442/// be effectively infinite in practice.
443///
444/// # Safety
445///
446/// The file descriptors referred to by the `Event` structs must be valid for
447/// the lifetime of the `kqueue` file descriptor.
448pub 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}