1/* TOOD: Implement for other kqueue based systems 2 */ 3 4use crate::{Errno, Result}; 5#[cfg(not(target_os = "netbsd"))] 6use libc::{c_int, c_long, intptr_t, time_t, timespec, uintptr_t}; 7#[cfg(target_os = "netbsd")] 8use libc::{c_long, intptr_t, size_t, time_t, timespec, uintptr_t}; 9use std::convert::TryInto; 10use std::mem; 11use std::os::unix::io::RawFd; 12use 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)] 17pub 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))] 28type type_of_udata = *mut libc::c_void; 29#[cfg(any(target_os = "netbsd"))] 30type type_of_udata = intptr_t; 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 #[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))] 89pub type type_of_event_flag = u16; 90#[cfg(any(target_os = "netbsd"))] 91pub type type_of_event_flag = u32; 92libc_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 124libc_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 210pub 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. 219unsafe impl Send for KEvent {} 220 221impl KEvent { 222 #[allow(clippy::needless_update)] // Not needless on all platforms. 223 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 245 pub fn ident(&self) -> uintptr_t { 246 self.kevent.ident 247 } 248 249 pub fn filter(&self) -> Result<EventFilter> { 250 self.kevent.filter.try_into() 251 } 252 253 pub fn flags(&self) -> EventFlag { 254 EventFlag::from_bits(self.kevent.flags).unwrap() 255 } 256 257 pub fn fflags(&self) -> FilterFlag { 258 FilterFlag::from_bits(self.kevent.fflags).unwrap() 259 } 260 261 pub fn data(&self) -> intptr_t { 262 self.kevent.data as intptr_t 263 } 264 265 pub fn udata(&self) -> intptr_t { 266 self.kevent.udata as intptr_t 267 } 268} 269 270pub 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))] 292type type_of_nchanges = c_int; 293#[cfg(target_os = "netbsd")] 294type type_of_nchanges = size_t; 295 296pub 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] 321pub 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] 338fn 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] 362fn 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