1 /* TOOD: Implement for other kqueue based systems
2  */
3 
4 use crate::{Errno, Result};
5 #[cfg(not(target_os = "netbsd"))]
6 use libc::{timespec, time_t, c_int, c_long, intptr_t, uintptr_t};
7 #[cfg(target_os = "netbsd")]
8 use libc::{timespec, time_t, c_long, intptr_t, uintptr_t, size_t};
9 use std::convert::TryInto;
10 use std::os::unix::io::RawFd;
11 use std::ptr;
12 
13 // Redefine kevent in terms of programmer-friendly enums and bitfields.
14 #[repr(C)]
15 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
16 pub struct KEvent {
17     kevent: libc::kevent,
18 }
19 
20 #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
21           target_os = "ios", target_os = "macos",
22           target_os = "openbsd"))]
23 type type_of_udata = *mut libc::c_void;
24 #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
25           target_os = "ios", target_os = "macos"))]
26 type type_of_data = intptr_t;
27 #[cfg(any(target_os = "netbsd"))]
28 type type_of_udata = intptr_t;
29 #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
30 type type_of_data = i64;
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(target_os = "dragonfly", target_os = "freebsd",
83           target_os = "ios", target_os = "macos",
84           target_os = "openbsd"))]
85 pub type type_of_event_flag = u16;
86 #[cfg(any(target_os = "netbsd"))]
87 pub type type_of_event_flag = u32;
88 libc_bitflags!{
89     pub struct EventFlag: type_of_event_flag {
90         EV_ADD;
91         EV_CLEAR;
92         EV_DELETE;
93         EV_DISABLE;
94         #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
95                   target_os = "ios", target_os = "macos",
96                   target_os = "netbsd", target_os = "openbsd"))]
97         EV_DISPATCH;
98         #[cfg(target_os = "freebsd")]
99         EV_DROP;
100         EV_ENABLE;
101         EV_EOF;
102         EV_ERROR;
103         #[cfg(any(target_os = "macos", target_os = "ios"))]
104         EV_FLAG0;
105         EV_FLAG1;
106         #[cfg(target_os = "dragonfly")]
107         EV_NODATA;
108         EV_ONESHOT;
109         #[cfg(any(target_os = "macos", target_os = "ios"))]
110         EV_OOBAND;
111         #[cfg(any(target_os = "macos", target_os = "ios"))]
112         EV_POLL;
113         #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
114                   target_os = "ios", target_os = "macos",
115                   target_os = "netbsd", target_os = "openbsd"))]
116         EV_RECEIPT;
117         EV_SYSFLAGS;
118     }
119 }
120 
121 libc_bitflags!(
122     pub struct FilterFlag: u32 {
123         #[cfg(any(target_os = "macos", target_os = "ios"))]
124         NOTE_ABSOLUTE;
125         NOTE_ATTRIB;
126         NOTE_CHILD;
127         NOTE_DELETE;
128         #[cfg(target_os = "openbsd")]
129         NOTE_EOF;
130         NOTE_EXEC;
131         NOTE_EXIT;
132         #[cfg(any(target_os = "macos", target_os = "ios"))]
133         NOTE_EXITSTATUS;
134         NOTE_EXTEND;
135         #[cfg(any(target_os = "macos",
136                   target_os = "ios",
137                   target_os = "freebsd",
138                   target_os = "dragonfly"))]
139         NOTE_FFAND;
140         #[cfg(any(target_os = "macos",
141                   target_os = "ios",
142                   target_os = "freebsd",
143                   target_os = "dragonfly"))]
144         NOTE_FFCOPY;
145         #[cfg(any(target_os = "macos",
146                   target_os = "ios",
147                   target_os = "freebsd",
148                   target_os = "dragonfly"))]
149         NOTE_FFCTRLMASK;
150         #[cfg(any(target_os = "macos",
151                   target_os = "ios",
152                   target_os = "freebsd",
153                   target_os = "dragonfly"))]
154         NOTE_FFLAGSMASK;
155         #[cfg(any(target_os = "macos",
156                   target_os = "ios",
157                   target_os = "freebsd",
158                   target_os = "dragonfly"))]
159         NOTE_FFNOP;
160         #[cfg(any(target_os = "macos",
161                   target_os = "ios",
162                   target_os = "freebsd",
163                   target_os = "dragonfly"))]
164         NOTE_FFOR;
165         NOTE_FORK;
166         NOTE_LINK;
167         NOTE_LOWAT;
168         #[cfg(target_os = "freebsd")]
169         NOTE_MSECONDS;
170         #[cfg(any(target_os = "macos", target_os = "ios"))]
171         NOTE_NONE;
172         #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
173         NOTE_NSECONDS;
174         #[cfg(target_os = "dragonfly")]
175         NOTE_OOB;
176         NOTE_PCTRLMASK;
177         NOTE_PDATAMASK;
178         NOTE_RENAME;
179         NOTE_REVOKE;
180         #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
181         NOTE_SECONDS;
182         #[cfg(any(target_os = "macos", target_os = "ios"))]
183         NOTE_SIGNAL;
184         NOTE_TRACK;
185         NOTE_TRACKERR;
186         #[cfg(any(target_os = "macos",
187                   target_os = "ios",
188                   target_os = "freebsd",
189                   target_os = "dragonfly"))]
190         NOTE_TRIGGER;
191         #[cfg(target_os = "openbsd")]
192         NOTE_TRUNCATE;
193         #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
194         NOTE_USECONDS;
195         #[cfg(any(target_os = "macos", target_os = "ios"))]
196         NOTE_VM_ERROR;
197         #[cfg(any(target_os = "macos", target_os = "ios"))]
198         NOTE_VM_PRESSURE;
199         #[cfg(any(target_os = "macos", target_os = "ios"))]
200         NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
201         #[cfg(any(target_os = "macos", target_os = "ios"))]
202         NOTE_VM_PRESSURE_TERMINATE;
203         NOTE_WRITE;
204     }
205 );
206 
kqueue() -> Result<RawFd>207 pub fn kqueue() -> Result<RawFd> {
208     let res = unsafe { libc::kqueue() };
209 
210     Errno::result(res)
211 }
212 
213 
214 // KEvent can't derive Send because on some operating systems, udata is defined
215 // as a void*.  However, KEvent's public API always treats udata as an intptr_t,
216 // which is safe to Send.
217 unsafe impl Send for KEvent {
218 }
219 
220 impl KEvent {
new(ident: uintptr_t, filter: EventFilter, flags: EventFlag, fflags:FilterFlag, data: intptr_t, udata: intptr_t) -> KEvent221     pub fn new(ident: uintptr_t, filter: EventFilter, flags: EventFlag,
222                fflags:FilterFlag, data: intptr_t, udata: intptr_t) -> KEvent {
223         KEvent { kevent: libc::kevent {
224             ident,
225             filter: filter as type_of_event_filter,
226             flags: flags.bits(),
227             fflags: fflags.bits(),
228             data: data as type_of_data,
229             udata: udata as type_of_udata
230         } }
231     }
232 
ident(&self) -> uintptr_t233     pub fn ident(&self) -> uintptr_t {
234         self.kevent.ident
235     }
236 
filter(&self) -> Result<EventFilter>237     pub fn filter(&self) -> Result<EventFilter> {
238         self.kevent.filter.try_into()
239     }
240 
flags(&self) -> EventFlag241     pub fn flags(&self) -> EventFlag {
242         EventFlag::from_bits(self.kevent.flags).unwrap()
243     }
244 
fflags(&self) -> FilterFlag245     pub fn fflags(&self) -> FilterFlag {
246         FilterFlag::from_bits(self.kevent.fflags).unwrap()
247     }
248 
data(&self) -> intptr_t249     pub fn data(&self) -> intptr_t {
250         self.kevent.data as intptr_t
251     }
252 
udata(&self) -> intptr_t253     pub fn udata(&self) -> intptr_t {
254         self.kevent.udata as intptr_t
255     }
256 }
257 
kevent(kq: RawFd, changelist: &[KEvent], eventlist: &mut [KEvent], timeout_ms: usize) -> Result<usize>258 pub fn kevent(kq: RawFd,
259               changelist: &[KEvent],
260               eventlist: &mut [KEvent],
261               timeout_ms: usize) -> Result<usize> {
262 
263     // Convert ms to timespec
264     let timeout = timespec {
265         tv_sec: (timeout_ms / 1000) as time_t,
266         tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long
267     };
268 
269     kevent_ts(kq, changelist, eventlist, Some(timeout))
270 }
271 
272 #[cfg(any(target_os = "macos",
273           target_os = "ios",
274           target_os = "freebsd",
275           target_os = "dragonfly",
276           target_os = "openbsd"))]
277 type type_of_nchanges = c_int;
278 #[cfg(target_os = "netbsd")]
279 type type_of_nchanges = size_t;
280 
kevent_ts(kq: RawFd, changelist: &[KEvent], eventlist: &mut [KEvent], timeout_opt: Option<timespec>) -> Result<usize>281 pub fn kevent_ts(kq: RawFd,
282               changelist: &[KEvent],
283               eventlist: &mut [KEvent],
284               timeout_opt: Option<timespec>) -> Result<usize> {
285 
286     let res = unsafe {
287         libc::kevent(
288             kq,
289             changelist.as_ptr() as *const libc::kevent,
290             changelist.len() as type_of_nchanges,
291             eventlist.as_mut_ptr() as *mut libc::kevent,
292             eventlist.len() as type_of_nchanges,
293             if let Some(ref timeout) = timeout_opt {timeout as *const timespec} else {ptr::null()})
294     };
295 
296     Errno::result(res).map(|r| r as usize)
297 }
298 
299 #[inline]
ev_set(ev: &mut KEvent, ident: usize, filter: EventFilter, flags: EventFlag, fflags: FilterFlag, udata: intptr_t)300 pub fn ev_set(ev: &mut KEvent,
301               ident: usize,
302               filter: EventFilter,
303               flags: EventFlag,
304               fflags: FilterFlag,
305               udata: intptr_t) {
306 
307     ev.kevent.ident  = ident as uintptr_t;
308     ev.kevent.filter = filter as type_of_event_filter;
309     ev.kevent.flags  = flags.bits();
310     ev.kevent.fflags = fflags.bits();
311     ev.kevent.data   = 0;
312     ev.kevent.udata  = udata as type_of_udata;
313 }
314 
315 #[test]
test_struct_kevent()316 fn test_struct_kevent() {
317     use std::mem;
318 
319     let udata : intptr_t = 12345;
320 
321     let actual = KEvent::new(0xdead_beef,
322                              EventFilter::EVFILT_READ,
323                              EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
324                              FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
325                              0x1337,
326                              udata);
327     assert_eq!(0xdead_beef, actual.ident());
328     let filter = actual.kevent.filter;
329     assert_eq!(libc::EVFILT_READ, filter);
330     assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
331     assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
332     assert_eq!(0x1337, actual.data() as type_of_data);
333     assert_eq!(udata as type_of_udata, actual.udata() as type_of_udata);
334     assert_eq!(mem::size_of::<libc::kevent>(), mem::size_of::<KEvent>());
335 }
336 
337 #[test]
test_kevent_filter()338 fn test_kevent_filter() {
339     let udata : intptr_t = 12345;
340 
341     let actual = KEvent::new(0xdead_beef,
342                              EventFilter::EVFILT_READ,
343                              EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
344                              FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
345                              0x1337,
346                              udata);
347     assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
348 }
349