• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* TOOD: Implement for other kqueue based systems
2  */
3 
4 use crate::{Errno, Result};
5 #[cfg(not(target_os = "netbsd"))]
6 use libc::{c_int, c_long, intptr_t, time_t, timespec, uintptr_t};
7 #[cfg(target_os = "netbsd")]
8 use libc::{c_long, intptr_t, size_t, time_t, timespec, uintptr_t};
9 use std::convert::TryInto;
10 use std::mem;
11 use std::os::unix::io::RawFd;
12 use 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)]
17 pub 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 ))]
28 type type_of_udata = *mut libc::c_void;
29 #[cfg(any(target_os = "netbsd"))]
30 type type_of_udata = intptr_t;
31 
32 #[cfg(target_os = "netbsd")]
33 type type_of_event_filter = u32;
34 #[cfg(not(target_os = "netbsd"))]
35 type type_of_event_filter = i16;
36 libc_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 ))]
89 pub type type_of_event_flag = u16;
90 #[cfg(any(target_os = "netbsd"))]
91 pub type type_of_event_flag = u32;
92 libc_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 
124 libc_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 
kqueue() -> Result<RawFd>210 pub 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.
219 unsafe impl Send for KEvent {}
220 
221 impl KEvent {
222     #[allow(clippy::needless_update)] // Not needless on all platforms.
new( ident: uintptr_t, filter: EventFilter, flags: EventFlag, fflags: FilterFlag, data: intptr_t, udata: intptr_t, ) -> KEvent223     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 
ident(&self) -> uintptr_t245     pub fn ident(&self) -> uintptr_t {
246         self.kevent.ident
247     }
248 
filter(&self) -> Result<EventFilter>249     pub fn filter(&self) -> Result<EventFilter> {
250         self.kevent.filter.try_into()
251     }
252 
flags(&self) -> EventFlag253     pub fn flags(&self) -> EventFlag {
254         EventFlag::from_bits(self.kevent.flags).unwrap()
255     }
256 
fflags(&self) -> FilterFlag257     pub fn fflags(&self) -> FilterFlag {
258         FilterFlag::from_bits(self.kevent.fflags).unwrap()
259     }
260 
data(&self) -> intptr_t261     pub fn data(&self) -> intptr_t {
262         self.kevent.data as intptr_t
263     }
264 
udata(&self) -> intptr_t265     pub fn udata(&self) -> intptr_t {
266         self.kevent.udata as intptr_t
267     }
268 }
269 
kevent( kq: RawFd, changelist: &[KEvent], eventlist: &mut [KEvent], timeout_ms: usize, ) -> Result<usize>270 pub 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 ))]
292 type type_of_nchanges = c_int;
293 #[cfg(target_os = "netbsd")]
294 type type_of_nchanges = size_t;
295 
kevent_ts( kq: RawFd, changelist: &[KEvent], eventlist: &mut [KEvent], timeout_opt: Option<timespec>, ) -> Result<usize>296 pub 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]
ev_set( ev: &mut KEvent, ident: usize, filter: EventFilter, flags: EventFlag, fflags: FilterFlag, udata: intptr_t, )321 pub 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]
test_struct_kevent()338 fn 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]
test_kevent_filter()362 fn 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