1 /* TOOD: Implement for other kqueue based systems
2  */
3 
4 use {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 libc;
10 use std::os::unix::io::RawFd;
11 use std::ptr;
12 use std::mem;
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(target_os = "dragonfly", target_os = "freebsd",
22           target_os = "ios", target_os = "macos",
23           target_os = "openbsd"))]
24 type type_of_udata = *mut libc::c_void;
25 #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
26           target_os = "ios", target_os = "macos"))]
27 type type_of_data = intptr_t;
28 #[cfg(any(target_os = "netbsd"))]
29 type type_of_udata = intptr_t;
30 #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
31 type type_of_data = libc::int64_t;
32 
33 #[cfg(target_os = "netbsd")]
34 type type_of_event_filter = u32;
35 #[cfg(not(target_os = "netbsd"))]
36 type type_of_event_filter = i16;
37 libc_enum! {
38     #[cfg_attr(target_os = "netbsd", repr(u32))]
39     #[cfg_attr(not(target_os = "netbsd"), repr(i16))]
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 }
80 
81 #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
82           target_os = "ios", target_os = "macos",
83           target_os = "openbsd"))]
84 pub type type_of_event_flag = u16;
85 #[cfg(any(target_os = "netbsd"))]
86 pub type type_of_event_flag = u32;
87 libc_bitflags!{
88     pub struct EventFlag: type_of_event_flag {
89         EV_ADD;
90         EV_CLEAR;
91         EV_DELETE;
92         EV_DISABLE;
93         // No released version of OpenBSD supports EV_DISPATCH or EV_RECEIPT.
94         // These have been commited to the -current branch though and are
95         // expected to be part of the OpenBSD 6.2 release in Nov 2017.
96         // See: https://marc.info/?l=openbsd-tech&m=149621427511219&w=2
97         // https://github.com/rust-lang/libc/pull/613
98         #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
99                   target_os = "ios", target_os = "macos",
100                   target_os = "netbsd"))]
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"))]
120         EV_RECEIPT;
121         EV_SYSFLAGS;
122     }
123 }
124 
125 libc_bitflags!(
126     pub struct FilterFlag: u32 {
127         #[cfg(any(target_os = "macos", target_os = "ios"))]
128         NOTE_ABSOLUTE;
129         NOTE_ATTRIB;
130         NOTE_CHILD;
131         NOTE_DELETE;
132         #[cfg(target_os = "openbsd")]
133         NOTE_EOF;
134         NOTE_EXEC;
135         NOTE_EXIT;
136         #[cfg(any(target_os = "macos", target_os = "ios"))]
137         #[deprecated( since="0.14.0", note="Deprecated since OSX 10.9")]
138         #[allow(deprecated)]
139         NOTE_EXIT_REPARENTED;
140         #[cfg(any(target_os = "macos", target_os = "ios"))]
141         NOTE_EXITSTATUS;
142         NOTE_EXTEND;
143         #[cfg(any(target_os = "macos",
144                   target_os = "ios",
145                   target_os = "freebsd",
146                   target_os = "dragonfly"))]
147         NOTE_FFAND;
148         #[cfg(any(target_os = "macos",
149                   target_os = "ios",
150                   target_os = "freebsd",
151                   target_os = "dragonfly"))]
152         NOTE_FFCOPY;
153         #[cfg(any(target_os = "macos",
154                   target_os = "ios",
155                   target_os = "freebsd",
156                   target_os = "dragonfly"))]
157         NOTE_FFCTRLMASK;
158         #[cfg(any(target_os = "macos",
159                   target_os = "ios",
160                   target_os = "freebsd",
161                   target_os = "dragonfly"))]
162         NOTE_FFLAGSMASK;
163         #[cfg(any(target_os = "macos",
164                   target_os = "ios",
165                   target_os = "freebsd",
166                   target_os = "dragonfly"))]
167         NOTE_FFNOP;
168         #[cfg(any(target_os = "macos",
169                   target_os = "ios",
170                   target_os = "freebsd",
171                   target_os = "dragonfly"))]
172         NOTE_FFOR;
173         NOTE_FORK;
174         NOTE_LINK;
175         NOTE_LOWAT;
176         #[cfg(target_os = "freebsd")]
177         NOTE_MSECONDS;
178         #[cfg(any(target_os = "macos", target_os = "ios"))]
179         NOTE_NONE;
180         #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
181         NOTE_NSECONDS;
182         #[cfg(target_os = "dragonfly")]
183         NOTE_OOB;
184         NOTE_PCTRLMASK;
185         NOTE_PDATAMASK;
186         #[cfg(any(target_os = "macos", target_os = "ios"))]
187         #[cfg(any(target_os = "macos", target_os = "ios"))]
188         #[deprecated( since="0.14.0", note="Deprecated since OSX 10.9")]
189         #[allow(deprecated)]
190         NOTE_REAP;
191         NOTE_RENAME;
192         NOTE_REVOKE;
193         #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
194         NOTE_SECONDS;
195         #[cfg(any(target_os = "macos", target_os = "ios"))]
196         NOTE_SIGNAL;
197         NOTE_TRACK;
198         NOTE_TRACKERR;
199         #[cfg(any(target_os = "macos",
200                   target_os = "ios",
201                   target_os = "freebsd",
202                   target_os = "dragonfly"))]
203         NOTE_TRIGGER;
204         #[cfg(target_os = "openbsd")]
205         NOTE_TRUNCATE;
206         #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
207         NOTE_USECONDS;
208         #[cfg(any(target_os = "macos", target_os = "ios"))]
209         NOTE_VM_ERROR;
210         #[cfg(any(target_os = "macos", target_os = "ios"))]
211         NOTE_VM_PRESSURE;
212         #[cfg(any(target_os = "macos", target_os = "ios"))]
213         NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
214         #[cfg(any(target_os = "macos", target_os = "ios"))]
215         NOTE_VM_PRESSURE_TERMINATE;
216         NOTE_WRITE;
217     }
218 );
219 
kqueue() -> Result<RawFd>220 pub fn kqueue() -> Result<RawFd> {
221     let res = unsafe { libc::kqueue() };
222 
223     Errno::result(res)
224 }
225 
226 
227 // KEvent can't derive Send because on some operating systems, udata is defined
228 // as a void*.  However, KEvent's public API always treats udata as an intptr_t,
229 // which is safe to Send.
230 unsafe impl Send for KEvent {
231 }
232 
233 impl KEvent {
new(ident: uintptr_t, filter: EventFilter, flags: EventFlag, fflags:FilterFlag, data: intptr_t, udata: intptr_t) -> KEvent234     pub fn new(ident: uintptr_t, filter: EventFilter, flags: EventFlag,
235                fflags:FilterFlag, data: intptr_t, udata: intptr_t) -> KEvent {
236         KEvent { kevent: libc::kevent {
237             ident,
238             filter: filter as type_of_event_filter,
239             flags: flags.bits(),
240             fflags: fflags.bits(),
241             data: data as type_of_data,
242             udata: udata as type_of_udata
243         } }
244     }
245 
ident(&self) -> uintptr_t246     pub fn ident(&self) -> uintptr_t {
247         self.kevent.ident
248     }
249 
filter(&self) -> EventFilter250     pub fn filter(&self) -> EventFilter {
251         unsafe { mem::transmute(self.kevent.filter as type_of_event_filter) }
252     }
253 
flags(&self) -> EventFlag254     pub fn flags(&self) -> EventFlag {
255         EventFlag::from_bits(self.kevent.flags).unwrap()
256     }
257 
fflags(&self) -> FilterFlag258     pub fn fflags(&self) -> FilterFlag {
259         FilterFlag::from_bits(self.kevent.fflags).unwrap()
260     }
261 
data(&self) -> intptr_t262     pub fn data(&self) -> intptr_t {
263         self.kevent.data as intptr_t
264     }
265 
udata(&self) -> intptr_t266     pub fn udata(&self) -> intptr_t {
267         self.kevent.udata as intptr_t
268     }
269 }
270 
kevent(kq: RawFd, changelist: &[KEvent], eventlist: &mut [KEvent], timeout_ms: usize) -> Result<usize>271 pub fn kevent(kq: RawFd,
272               changelist: &[KEvent],
273               eventlist: &mut [KEvent],
274               timeout_ms: usize) -> Result<usize> {
275 
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(target_os = "macos",
286           target_os = "ios",
287           target_os = "freebsd",
288           target_os = "dragonfly",
289           target_os = "openbsd"))]
290 type type_of_nchanges = c_int;
291 #[cfg(target_os = "netbsd")]
292 type type_of_nchanges = size_t;
293 
kevent_ts(kq: RawFd, changelist: &[KEvent], eventlist: &mut [KEvent], timeout_opt: Option<timespec>) -> Result<usize>294 pub fn kevent_ts(kq: RawFd,
295               changelist: &[KEvent],
296               eventlist: &mut [KEvent],
297               timeout_opt: Option<timespec>) -> Result<usize> {
298 
299     let res = unsafe {
300         libc::kevent(
301             kq,
302             changelist.as_ptr() as *const libc::kevent,
303             changelist.len() as type_of_nchanges,
304             eventlist.as_mut_ptr() as *mut libc::kevent,
305             eventlist.len() as type_of_nchanges,
306             if let Some(ref timeout) = timeout_opt {timeout as *const timespec} else {ptr::null()})
307     };
308 
309     Errno::result(res).map(|r| r as usize)
310 }
311 
312 #[inline]
ev_set(ev: &mut KEvent, ident: usize, filter: EventFilter, flags: EventFlag, fflags: FilterFlag, udata: intptr_t)313 pub fn ev_set(ev: &mut KEvent,
314               ident: usize,
315               filter: EventFilter,
316               flags: EventFlag,
317               fflags: FilterFlag,
318               udata: intptr_t) {
319 
320     ev.kevent.ident  = ident as uintptr_t;
321     ev.kevent.filter = filter as type_of_event_filter;
322     ev.kevent.flags  = flags.bits();
323     ev.kevent.fflags = fflags.bits();
324     ev.kevent.data   = 0;
325     ev.kevent.udata  = udata as type_of_udata;
326 }
327 
328 #[test]
test_struct_kevent()329 fn test_struct_kevent() {
330     let udata : intptr_t = 12345;
331 
332     let actual = KEvent::new(0xdead_beef,
333                              EventFilter::EVFILT_READ,
334                              EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
335                              FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
336                              0x1337,
337                              udata);
338     assert_eq!(0xdead_beef, actual.ident());
339     assert_eq!(libc::EVFILT_READ, actual.filter() as type_of_event_filter);
340     assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
341     assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
342     assert_eq!(0x1337, actual.data() as type_of_data);
343     assert_eq!(udata as type_of_udata, actual.udata() as type_of_udata);
344     assert_eq!(mem::size_of::<libc::kevent>(), mem::size_of::<KEvent>());
345 }
346