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