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