xref: /third_party/rust/crates/nix/src/sys/event.rs (revision 3da5c369)
1/* TOOD: Implement for other kqueue based systems
2 */
3
4use crate::{Errno, Result};
5#[cfg(not(target_os = "netbsd"))]
6use libc::{c_int, c_long, intptr_t, time_t, timespec, uintptr_t};
7#[cfg(target_os = "netbsd")]
8use libc::{c_long, intptr_t, size_t, time_t, timespec, uintptr_t};
9use std::convert::TryInto;
10use std::mem;
11use std::os::unix::io::RawFd;
12use std::ptr;
13
14// Redefine kevent in terms of programmer-friendly enums and bitfields.
15#[repr(C)]
16#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
17pub struct KEvent {
18    kevent: libc::kevent,
19}
20
21#[cfg(any(
22    target_os = "dragonfly",
23    target_os = "freebsd",
24    target_os = "ios",
25    target_os = "macos",
26    target_os = "openbsd"
27))]
28type type_of_udata = *mut libc::c_void;
29#[cfg(any(target_os = "netbsd"))]
30type type_of_udata = intptr_t;
31
32#[cfg(target_os = "netbsd")]
33type type_of_event_filter = u32;
34#[cfg(not(target_os = "netbsd"))]
35type type_of_event_filter = i16;
36libc_enum! {
37    #[cfg_attr(target_os = "netbsd", repr(u32))]
38    #[cfg_attr(not(target_os = "netbsd"), repr(i16))]
39    #[non_exhaustive]
40    pub enum EventFilter {
41        EVFILT_AIO,
42        /// Returns whenever there is no remaining data in the write buffer
43        #[cfg(target_os = "freebsd")]
44        EVFILT_EMPTY,
45        #[cfg(target_os = "dragonfly")]
46        EVFILT_EXCEPT,
47        #[cfg(any(target_os = "dragonfly",
48                  target_os = "freebsd",
49                  target_os = "ios",
50                  target_os = "macos"))]
51        EVFILT_FS,
52        #[cfg(target_os = "freebsd")]
53        EVFILT_LIO,
54        #[cfg(any(target_os = "ios", target_os = "macos"))]
55        EVFILT_MACHPORT,
56        EVFILT_PROC,
57        /// Returns events associated with the process referenced by a given
58        /// process descriptor, created by `pdfork()`. The events to monitor are:
59        ///
60        /// - NOTE_EXIT: the process has exited. The exit status will be stored in data.
61        #[cfg(target_os = "freebsd")]
62        EVFILT_PROCDESC,
63        EVFILT_READ,
64        /// Returns whenever an asynchronous `sendfile()` call completes.
65        #[cfg(target_os = "freebsd")]
66        EVFILT_SENDFILE,
67        EVFILT_SIGNAL,
68        EVFILT_TIMER,
69        #[cfg(any(target_os = "dragonfly",
70                  target_os = "freebsd",
71                  target_os = "ios",
72                  target_os = "macos"))]
73        EVFILT_USER,
74        #[cfg(any(target_os = "ios", target_os = "macos"))]
75        EVFILT_VM,
76        EVFILT_VNODE,
77        EVFILT_WRITE,
78    }
79    impl TryFrom<type_of_event_filter>
80}
81
82#[cfg(any(
83    target_os = "dragonfly",
84    target_os = "freebsd",
85    target_os = "ios",
86    target_os = "macos",
87    target_os = "openbsd"
88))]
89pub type type_of_event_flag = u16;
90#[cfg(any(target_os = "netbsd"))]
91pub type type_of_event_flag = u32;
92libc_bitflags! {
93    pub struct EventFlag: type_of_event_flag {
94        EV_ADD;
95        EV_CLEAR;
96        EV_DELETE;
97        EV_DISABLE;
98        #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
99                  target_os = "ios", target_os = "macos",
100                  target_os = "netbsd", target_os = "openbsd"))]
101        EV_DISPATCH;
102        #[cfg(target_os = "freebsd")]
103        EV_DROP;
104        EV_ENABLE;
105        EV_EOF;
106        EV_ERROR;
107        #[cfg(any(target_os = "macos", target_os = "ios"))]
108        EV_FLAG0;
109        EV_FLAG1;
110        #[cfg(target_os = "dragonfly")]
111        EV_NODATA;
112        EV_ONESHOT;
113        #[cfg(any(target_os = "macos", target_os = "ios"))]
114        EV_OOBAND;
115        #[cfg(any(target_os = "macos", target_os = "ios"))]
116        EV_POLL;
117        #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
118                  target_os = "ios", target_os = "macos",
119                  target_os = "netbsd", target_os = "openbsd"))]
120        EV_RECEIPT;
121    }
122}
123
124libc_bitflags!(
125    pub struct FilterFlag: u32 {
126        #[cfg(any(target_os = "macos", target_os = "ios"))]
127        NOTE_ABSOLUTE;
128        NOTE_ATTRIB;
129        NOTE_CHILD;
130        NOTE_DELETE;
131        #[cfg(target_os = "openbsd")]
132        NOTE_EOF;
133        NOTE_EXEC;
134        NOTE_EXIT;
135        #[cfg(any(target_os = "macos", target_os = "ios"))]
136        NOTE_EXITSTATUS;
137        NOTE_EXTEND;
138        #[cfg(any(target_os = "macos",
139                  target_os = "ios",
140                  target_os = "freebsd",
141                  target_os = "dragonfly"))]
142        NOTE_FFAND;
143        #[cfg(any(target_os = "macos",
144                  target_os = "ios",
145                  target_os = "freebsd",
146                  target_os = "dragonfly"))]
147        NOTE_FFCOPY;
148        #[cfg(any(target_os = "macos",
149                  target_os = "ios",
150                  target_os = "freebsd",
151                  target_os = "dragonfly"))]
152        NOTE_FFCTRLMASK;
153        #[cfg(any(target_os = "macos",
154                  target_os = "ios",
155                  target_os = "freebsd",
156                  target_os = "dragonfly"))]
157        NOTE_FFLAGSMASK;
158        #[cfg(any(target_os = "macos",
159                  target_os = "ios",
160                  target_os = "freebsd",
161                  target_os = "dragonfly"))]
162        NOTE_FFNOP;
163        #[cfg(any(target_os = "macos",
164                  target_os = "ios",
165                  target_os = "freebsd",
166                  target_os = "dragonfly"))]
167        NOTE_FFOR;
168        NOTE_FORK;
169        NOTE_LINK;
170        NOTE_LOWAT;
171        #[cfg(target_os = "freebsd")]
172        NOTE_MSECONDS;
173        #[cfg(any(target_os = "macos", target_os = "ios"))]
174        NOTE_NONE;
175        #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
176        NOTE_NSECONDS;
177        #[cfg(target_os = "dragonfly")]
178        NOTE_OOB;
179        NOTE_PCTRLMASK;
180        NOTE_PDATAMASK;
181        NOTE_RENAME;
182        NOTE_REVOKE;
183        #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
184        NOTE_SECONDS;
185        #[cfg(any(target_os = "macos", target_os = "ios"))]
186        NOTE_SIGNAL;
187        NOTE_TRACK;
188        NOTE_TRACKERR;
189        #[cfg(any(target_os = "macos",
190                  target_os = "ios",
191                  target_os = "freebsd",
192                  target_os = "dragonfly"))]
193        NOTE_TRIGGER;
194        #[cfg(target_os = "openbsd")]
195        NOTE_TRUNCATE;
196        #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
197        NOTE_USECONDS;
198        #[cfg(any(target_os = "macos", target_os = "ios"))]
199        NOTE_VM_ERROR;
200        #[cfg(any(target_os = "macos", target_os = "ios"))]
201        NOTE_VM_PRESSURE;
202        #[cfg(any(target_os = "macos", target_os = "ios"))]
203        NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
204        #[cfg(any(target_os = "macos", target_os = "ios"))]
205        NOTE_VM_PRESSURE_TERMINATE;
206        NOTE_WRITE;
207    }
208);
209
210pub fn kqueue() -> Result<RawFd> {
211    let res = unsafe { libc::kqueue() };
212
213    Errno::result(res)
214}
215
216// KEvent can't derive Send because on some operating systems, udata is defined
217// as a void*.  However, KEvent's public API always treats udata as an intptr_t,
218// which is safe to Send.
219unsafe impl Send for KEvent {}
220
221impl KEvent {
222    #[allow(clippy::needless_update)] // Not needless on all platforms.
223    pub fn new(
224        ident: uintptr_t,
225        filter: EventFilter,
226        flags: EventFlag,
227        fflags: FilterFlag,
228        data: intptr_t,
229        udata: intptr_t,
230    ) -> KEvent {
231        KEvent {
232            kevent: libc::kevent {
233                ident,
234                filter: filter as type_of_event_filter,
235                flags: flags.bits(),
236                fflags: fflags.bits(),
237                // data can be either i64 or intptr_t, depending on platform
238                data: data as _,
239                udata: udata as type_of_udata,
240                ..unsafe { mem::zeroed() }
241            },
242        }
243    }
244
245    pub fn ident(&self) -> uintptr_t {
246        self.kevent.ident
247    }
248
249    pub fn filter(&self) -> Result<EventFilter> {
250        self.kevent.filter.try_into()
251    }
252
253    pub fn flags(&self) -> EventFlag {
254        EventFlag::from_bits(self.kevent.flags).unwrap()
255    }
256
257    pub fn fflags(&self) -> FilterFlag {
258        FilterFlag::from_bits(self.kevent.fflags).unwrap()
259    }
260
261    pub fn data(&self) -> intptr_t {
262        self.kevent.data as intptr_t
263    }
264
265    pub fn udata(&self) -> intptr_t {
266        self.kevent.udata as intptr_t
267    }
268}
269
270pub fn kevent(
271    kq: RawFd,
272    changelist: &[KEvent],
273    eventlist: &mut [KEvent],
274    timeout_ms: usize,
275) -> Result<usize> {
276    // Convert ms to timespec
277    let timeout = timespec {
278        tv_sec: (timeout_ms / 1000) as time_t,
279        tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long,
280    };
281
282    kevent_ts(kq, changelist, eventlist, Some(timeout))
283}
284
285#[cfg(any(
286    target_os = "macos",
287    target_os = "ios",
288    target_os = "freebsd",
289    target_os = "dragonfly",
290    target_os = "openbsd"
291))]
292type type_of_nchanges = c_int;
293#[cfg(target_os = "netbsd")]
294type type_of_nchanges = size_t;
295
296pub fn kevent_ts(
297    kq: RawFd,
298    changelist: &[KEvent],
299    eventlist: &mut [KEvent],
300    timeout_opt: Option<timespec>,
301) -> Result<usize> {
302    let res = unsafe {
303        libc::kevent(
304            kq,
305            changelist.as_ptr() as *const libc::kevent,
306            changelist.len() as type_of_nchanges,
307            eventlist.as_mut_ptr() as *mut libc::kevent,
308            eventlist.len() as type_of_nchanges,
309            if let Some(ref timeout) = timeout_opt {
310                timeout as *const timespec
311            } else {
312                ptr::null()
313            },
314        )
315    };
316
317    Errno::result(res).map(|r| r as usize)
318}
319
320#[inline]
321pub fn ev_set(
322    ev: &mut KEvent,
323    ident: usize,
324    filter: EventFilter,
325    flags: EventFlag,
326    fflags: FilterFlag,
327    udata: intptr_t,
328) {
329    ev.kevent.ident = ident as uintptr_t;
330    ev.kevent.filter = filter as type_of_event_filter;
331    ev.kevent.flags = flags.bits();
332    ev.kevent.fflags = fflags.bits();
333    ev.kevent.data = 0;
334    ev.kevent.udata = udata as type_of_udata;
335}
336
337#[test]
338fn test_struct_kevent() {
339    use std::mem;
340
341    let udata: intptr_t = 12345;
342
343    let actual = KEvent::new(
344        0xdead_beef,
345        EventFilter::EVFILT_READ,
346        EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
347        FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
348        0x1337,
349        udata,
350    );
351    assert_eq!(0xdead_beef, actual.ident());
352    let filter = actual.kevent.filter;
353    assert_eq!(libc::EVFILT_READ, filter);
354    assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
355    assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
356    assert_eq!(0x1337, actual.data());
357    assert_eq!(udata as type_of_udata, actual.udata() as type_of_udata);
358    assert_eq!(mem::size_of::<libc::kevent>(), mem::size_of::<KEvent>());
359}
360
361#[test]
362fn test_kevent_filter() {
363    let udata: intptr_t = 12345;
364
365    let actual = KEvent::new(
366        0xdead_beef,
367        EventFilter::EVFILT_READ,
368        EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
369        FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
370        0x1337,
371        udata,
372    );
373    assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
374}
375