diff --git a/CHANGELOG.md b/CHANGELOG.md index 76f7ef5f93..56d4828fed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,17 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added +- Added complete definitions for all kqueue-related constants on all supported + OSes + ([#415](https://github.com/nix-rust/nix/pull/415)) - Added function `epoll_create1` and bitflags `EpollCreateFlags` in `::nix::sys::epoll` in order to support `::libc::epoll_create1`. ([#410](https://github.com/nix-rust/nix/pull/410)) ### Changed +- Changed `KEvent` to an opaque structure that may only be modified by its + constructor and the `ev_set` method. + ([#415](https://github.com/nix-rust/nix/pull/415)) - `pipe2` now calls `libc::pipe2` where available. Previously it was emulated using `pipe`, which meant that setting `O_CLOEXEC` was not atomic. ([#427](https://github.com/nix-rust/nix/pull/427)) @@ -24,6 +30,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#410](https://github.com/nix-rust/nix/pull/410)) ### Fixed +- Fixed using kqueue with `EVFILT_USER` on FreeBSD + ([#415](https://github.com/nix-rust/nix/pull/415)) - Fixed the build on FreeBSD, and fixed the getsockopt, sendmsg, and recvmsg functions on that same OS. ([#397](https://github.com/nix-rust/nix/pull/397)) diff --git a/src/sys/event.rs b/src/sys/event.rs index 0e94475e18..d44963db03 100644 --- a/src/sys/event.rs +++ b/src/sys/event.rs @@ -3,280 +3,230 @@ use {Errno, Result}; #[cfg(not(target_os = "netbsd"))] -use libc::{timespec, time_t, c_int, c_long, uintptr_t}; +use libc::{timespec, time_t, c_int, c_long, intptr_t, uintptr_t}; #[cfg(target_os = "netbsd")] -use libc::{timespec, time_t, c_long, uintptr_t, size_t}; +use libc::{timespec, time_t, c_long, intptr_t, uintptr_t, size_t}; +use libc; use std::os::unix::io::RawFd; use std::ptr; +use std::mem; -pub use self::ffi::kevent as KEvent; - -mod ffi { - pub use libc::{c_int, c_void, uintptr_t, intptr_t, timespec, size_t, int64_t}; - use super::{EventFilter, EventFlag, FilterFlag}; - - #[cfg(not(target_os = "netbsd"))] - #[derive(Clone, Copy)] - #[repr(C)] - pub struct kevent { - pub ident: uintptr_t, // 8 - pub filter: EventFilter, // 2 - pub flags: EventFlag, // 2 - pub fflags: FilterFlag, // 4 - pub data: intptr_t, // 8 - pub udata: usize // 8 - } - - #[cfg(target_os = "netbsd")] - #[derive(Clone, Copy)] - #[repr(C)] - pub struct kevent { - pub ident: uintptr_t, - pub filter: EventFilter, - pub flags: EventFlag, - pub fflags: FilterFlag, - pub data: int64_t, - pub udata: intptr_t - } - - // Bug in rustc, cannot determine that kevent is #[repr(C)] - #[allow(improper_ctypes)] - extern { - pub fn kqueue() -> c_int; - - #[cfg(not(target_os = "netbsd"))] - pub fn kevent( - kq: c_int, - changelist: *const kevent, - nchanges: c_int, - eventlist: *mut kevent, - nevents: c_int, - timeout: *const timespec) -> c_int; - - #[cfg(target_os = "netbsd")] - pub fn kevent( - kq: c_int, - changelist: *const kevent, - nchanges: size_t, - eventlist: *mut kevent, - nevents: size_t, - timeout: *const timespec) -> c_int; - } +// Redefine kevent in terms of programmer-friendly enums and bitfields. +#[derive(Clone, Copy)] +#[repr(C)] +pub struct KEvent { + kevent: libc::kevent, } -#[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] -#[repr(i16)] -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum EventFilter { - EVFILT_READ = -1, - EVFILT_WRITE = -2, - EVFILT_AIO = -3, - EVFILT_VNODE = -4, - EVFILT_PROC = -5, - EVFILT_SIGNAL = -6, - EVFILT_TIMER = -7, - EVFILT_MACHPORT = -8, - EVFILT_FS = -9, - EVFILT_USER = -10, - // -11: unused - EVFILT_VM = -12, - EVFILT_SYSCOUNT = 13 -} +#[cfg(any(target_os = "openbsd", target_os = "freebsd", + target_os = "dragonfly", target_os = "macos"))] +type type_of_udata = *mut ::c_void; +#[cfg(any(target_os = "netbsd"))] +type type_of_udata = intptr_t; -#[cfg(target_os = "dragonfly")] -#[repr(i16)] // u_short +#[cfg(not(target_os = "netbsd"))] +type type_of_event_filter = i16; +#[cfg(not(target_os = "netbsd"))] +#[repr(i16)] #[derive(Clone, Copy, Debug, PartialEq)] pub enum EventFilter { - EVFILT_READ = -1, - EVFILT_WRITE = -2, - EVFILT_AIO = -3, - EVFILT_VNODE = -4, - EVFILT_PROC = -5, - EVFILT_SIGNAL = -6, - EVFILT_TIMER = -7, - EVFILT_EXCEPT = -8, - EVFILT_USER = -9, + EVFILT_AIO = libc::EVFILT_AIO, + #[cfg(target_os = "dragonfly")] + EVFILT_EXCEPT = libc::EVFILT_EXCEPT, + #[cfg(any(target_os = "macos", + target_os = "dragonfly", + target_os = "freebsd"))] + EVFILT_FS = libc::EVFILT_FS, + #[cfg(target_os = "freebsd")] + EVFILT_LIO = libc::EVFILT_LIO, + #[cfg(target_os = "macos")] + EVFILT_MACHPORT = libc::EVFILT_MACHPORT, + EVFILT_PROC = libc::EVFILT_PROC, + EVFILT_READ = libc::EVFILT_READ, + EVFILT_SIGNAL = libc::EVFILT_SIGNAL, + EVFILT_TIMER = libc::EVFILT_TIMER, + #[cfg(any(target_os = "macos", + target_os = "dragonfly", + target_os = "freebsd"))] + EVFILT_USER = libc::EVFILT_USER, + #[cfg(target_os = "macos")] + EVFILT_VM = libc::EVFILT_VM, + EVFILT_VNODE = libc::EVFILT_VNODE, + EVFILT_WRITE = libc::EVFILT_WRITE, } +#[cfg(target_os = "netbsd")] +type type_of_event_filter = i32; #[cfg(target_os = "netbsd")] #[repr(u32)] #[derive(Clone, Copy, Debug, PartialEq)] pub enum EventFilter { - EVFILT_READ = 0, - EVFILT_WRITE = 1, - EVFILT_AIO = 2, - EVFILT_VNODE = 3, - EVFILT_PROC = 4, - EVFILT_SIGNAL = 5, - EVFILT_TIMER = 6, - EVFILT_SYSCOUNT = 7 + EVFILT_READ = libc::EVFILT_READ, + EVFILT_WRITE = libc::EVFILT_WRITE, + EVFILT_AIO = libc::EVFILT_AIO, + EVFILT_VNODE = libc::EVFILT_VNODE, + EVFILT_PROC = libc::EVFILT_PROC, + EVFILT_SIGNAL = libc::EVFILT_SIGNAL, + EVFILT_TIMER = libc::EVFILT_TIMER, } -#[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] -bitflags!( - flags EventFlag: u16 { - const EV_ADD = 0x0001, - const EV_DELETE = 0x0002, - const EV_ENABLE = 0x0004, - const EV_DISABLE = 0x0008, - const EV_RECEIPT = 0x0040, - const EV_ONESHOT = 0x0010, - const EV_CLEAR = 0x0020, - const EV_DISPATCH = 0x0080, - const EV_SYSFLAGS = 0xF000, - const EV_FLAG0 = 0x1000, - const EV_FLAG1 = 0x2000, - const EV_EOF = 0x8000, - const EV_ERROR = 0x4000 +#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly"))] +pub type type_of_event_flag = u16; +#[cfg(any(target_os = "netbsd", target_os = "openbsd"))] +pub type type_of_event_flag = u32; +libc_bitflags!{ + flags EventFlag: type_of_event_flag { + EV_ADD, + EV_CLEAR, + EV_DELETE, + EV_DISABLE, + EV_DISPATCH, + #[cfg(target_os = "freebsd")] + EV_DROP, + EV_ENABLE, + EV_EOF, + EV_ERROR, + #[cfg(target_os = "macos")] + EV_FLAG0, + EV_FLAG1, + #[cfg(target_os = "dragonfly")] + EV_NODATA, + EV_ONESHOT, + #[cfg(target_os = "macos")] + EV_OOBAND, + #[cfg(target_os = "macos")] + EV_POLL, + #[cfg(not(target_os = "openbsd"))] + EV_RECEIPT, + EV_SYSFLAGS, } -); +} -#[cfg(target_os = "dragonfly")] bitflags!( - flags EventFlag: u16 { - const EV_ADD = 0x0001, - const EV_DELETE = 0x0002, - const EV_ENABLE = 0x0004, - const EV_DISABLE = 0x0008, - const EV_RECEIPT = 0x0040, - const EV_ONESHOT = 0x0010, - const EV_CLEAR = 0x0020, - const EV_SYSFLAGS = 0xF000, - const EV_NODATA = 0x1000, - const EV_FLAG1 = 0x2000, - const EV_EOF = 0x8000, - const EV_ERROR = 0x4000 + flags FilterFlag: u32 { + #[cfg(target_os = "macos")] + const NOTE_ABSOLUTE = libc::NOTE_ABSOLUTE, + const NOTE_ATTRIB = libc::NOTE_ATTRIB, + const NOTE_CHILD = libc::NOTE_CHILD, + const NOTE_DELETE = libc::NOTE_DELETE, + #[cfg(target_os = "openbsd")] + const NOTE_EOF = libc::NOTE_EOF, + const NOTE_EXEC = libc::NOTE_EXEC, + const NOTE_EXIT = libc::NOTE_EXIT, + #[cfg(target_os = "macos")] + const NOTE_EXIT_REPARENTED = libc::NOTE_EXIT_REPARENTED, + #[cfg(target_os = "macos")] + const NOTE_EXITSTATUS = libc::NOTE_EXITSTATUS, + const NOTE_EXTEND = libc::NOTE_EXTEND, + #[cfg(any(target_os = "macos", + target_os = "freebsd", + target_os = "dragonfly"))] + const NOTE_FFAND = libc::NOTE_FFAND, + #[cfg(any(target_os = "macos", + target_os = "freebsd", + target_os = "dragonfly"))] + const NOTE_FFCOPY = libc::NOTE_FFCOPY, + #[cfg(any(target_os = "macos", + target_os = "freebsd", + target_os = "dragonfly"))] + const NOTE_FFCTRLMASK = libc::NOTE_FFCTRLMASK, + #[cfg(any(target_os = "macos", + target_os = "freebsd", + target_os = "dragonfly"))] + const NOTE_FFLAGSMASK = libc::NOTE_FFLAGSMASK, + #[cfg(any(target_os = "macos", + target_os = "freebsd", + target_os = "dragonfly"))] + const NOTE_FFNOP = libc::NOTE_FFNOP, + #[cfg(any(target_os = "macos", + target_os = "freebsd", + target_os = "dragonfly"))] + const NOTE_FFOR = libc::NOTE_FFOR, + const NOTE_FORK = libc::NOTE_FORK, + const NOTE_LINK = libc::NOTE_LINK, + const NOTE_LOWAT = libc::NOTE_LOWAT, + #[cfg(target_os = "freebsd")] + const NOTE_MSECONDS = libc::NOTE_MSECONDS, + #[cfg(target_os = "macos")] + const NOTE_NONE = libc::NOTE_NONE, + #[cfg(any(target_os = "macos", target_os = "freebsd"))] + const NOTE_NSECONDS = libc::NOTE_NSECONDS, + #[cfg(target_os = "dragonfly")] + const NOTE_OOB = libc::NOTE_OOB, + const NOTE_PCTRLMASK = libc::NOTE_PCTRLMASK, + const NOTE_PDATAMASK = libc::NOTE_PDATAMASK, + #[cfg(target_os = "macos")] + const NOTE_REAP = libc::NOTE_REAP, + const NOTE_RENAME = libc::NOTE_RENAME, + const NOTE_REVOKE = libc::NOTE_REVOKE, + #[cfg(any(target_os = "macos", target_os = "freebsd"))] + const NOTE_SECONDS = libc::NOTE_SECONDS, + #[cfg(target_os = "macos")] + const NOTE_SIGNAL = libc::NOTE_SIGNAL, + const NOTE_TRACK = libc::NOTE_TRACK, + const NOTE_TRACKERR = libc::NOTE_TRACKERR, + #[cfg(any(target_os = "macos", + target_os = "freebsd", + target_os = "dragonfly"))] + const NOTE_TRIGGER = libc::NOTE_TRIGGER, + #[cfg(target_os = "openbsd")] + const NOTE_TRUNCATE = libc::NOTE_TRUNCATE, + #[cfg(any(target_os = "macos", target_os = "freebsd"))] + const NOTE_USECONDS = libc::NOTE_USECONDS, + #[cfg(target_os = "macos")] + const NOTE_VM_ERROR = libc::NOTE_VM_ERROR, + #[cfg(target_os = "macos")] + const NOTE_VM_PRESSURE = libc::NOTE_VM_PRESSURE, + #[cfg(target_os = "macos")] + const NOTE_VM_PRESSURE_SUDDEN_TERMINATE = libc::NOTE_VM_PRESSURE_SUDDEN_TERMINATE, + #[cfg(target_os = "macos")] + const NOTE_VM_PRESSURE_TERMINATE = libc::NOTE_VM_PRESSURE_TERMINATE, + const NOTE_WRITE = libc::NOTE_WRITE, } ); -#[cfg(target_os = "netbsd")] -bitflags!( - flags EventFlag: u32 { - const EV_ADD = 0x0001, - const EV_DELETE = 0x0002, - const EV_ENABLE = 0x0004, - const EV_DISABLE = 0x0008, - const EV_ONESHOT = 0x0010, - const EV_CLEAR = 0x0020, - const EV_SYSFLAGS = 0xF000, - const EV_NODATA = 0x1000, - const EV_FLAG1 = 0x2000, - const EV_EOF = 0x8000, - const EV_ERROR = 0x4000 - } -); +pub fn kqueue() -> Result { + let res = unsafe { libc::kqueue() }; -#[cfg(not(any(target_os = "dragonfly", target_os="netbsd")))] -bitflags!( - flags FilterFlag: u32 { - const NOTE_TRIGGER = 0x01000000, - const NOTE_FFNOP = 0x00000000, - const NOTE_FFAND = 0x40000000, - const NOTE_FFOR = 0x80000000, - const NOTE_FFCOPY = 0xc0000000, - const NOTE_FFCTRLMASK = 0xc0000000, - const NOTE_FFLAGSMASK = 0x00ffffff, - const NOTE_LOWAT = 0x00000001, - const NOTE_DELETE = 0x00000001, - const NOTE_WRITE = 0x00000002, - const NOTE_EXTEND = 0x00000004, - const NOTE_ATTRIB = 0x00000008, - const NOTE_LINK = 0x00000010, - const NOTE_RENAME = 0x00000020, - const NOTE_REVOKE = 0x00000040, - const NOTE_NONE = 0x00000080, - const NOTE_EXIT = 0x80000000, - const NOTE_FORK = 0x40000000, - const NOTE_EXEC = 0x20000000, - const NOTE_REAP = 0x10000000, - const NOTE_SIGNAL = 0x08000000, - const NOTE_EXITSTATUS = 0x04000000, - const NOTE_RESOURCEEND = 0x02000000, - const NOTE_APPACTIVE = 0x00800000, - const NOTE_APPBACKGROUND = 0x00400000, - const NOTE_APPNONUI = 0x00200000, - const NOTE_APPINACTIVE = 0x00100000, - const NOTE_APPALLSTATES = 0x00f00000, - const NOTE_PDATAMASK = 0x000fffff, - const NOTE_PCTRLMASK = 0xfff00000, - const NOTE_EXIT_REPARENTED = 0x00080000, - const NOTE_VM_PRESSURE = 0x80000000, - const NOTE_VM_PRESSURE_TERMINATE = 0x40000000, - const NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000, - const NOTE_VM_ERROR = 0x10000000, - const NOTE_SECONDS = 0x00000001, - const NOTE_USECONDS = 0x00000002, - const NOTE_NSECONDS = 0x00000004, - const NOTE_ABSOLUTE = 0x00000008, - const NOTE_TRACK = 0x00000001, - const NOTE_TRACKERR = 0x00000002, - const NOTE_CHILD = 0x00000004 + Errno::result(res) +} + +impl KEvent { + pub fn new(ident: uintptr_t, filter: EventFilter, flags: EventFlag, + fflags:FilterFlag, data: intptr_t, udata: uintptr_t) -> KEvent { + KEvent { kevent: libc::kevent { + ident: ident, + filter: filter as type_of_event_filter, + flags: flags.bits(), + fflags: fflags.bits(), + data: data, + udata: udata as type_of_udata + } } } -); -#[cfg(target_os = "dragonfly")] -bitflags!( - flags FilterFlag: u32 { - const NOTE_TRIGGER = 0x01000000, - const NOTE_FFNOP = 0x00000000, - const NOTE_FFAND = 0x40000000, - const NOTE_FFOR = 0x80000000, - const NOTE_FFCOPY = 0xc0000000, - const NOTE_FFCTRLMASK = 0xc0000000, - const NOTE_FFLAGSMASK = 0x00ffffff, - const NOTE_LOWAT = 0x00000001, - const NOTE_DELETE = 0x00000001, - const NOTE_WRITE = 0x00000002, - const NOTE_EXTEND = 0x00000004, - const NOTE_ATTRIB = 0x00000008, - const NOTE_LINK = 0x00000010, - const NOTE_RENAME = 0x00000020, - const NOTE_REVOKE = 0x00000040, - const NOTE_EXIT = 0x80000000, - const NOTE_FORK = 0x40000000, - const NOTE_EXEC = 0x20000000, - const NOTE_SIGNAL = 0x08000000, - const NOTE_PDATAMASK = 0x000fffff, - const NOTE_PCTRLMASK = 0xf0000000, // NOTE: FreeBSD uses 0xfff00000, - const NOTE_TRACK = 0x00000001, - const NOTE_TRACKERR = 0x00000002, - const NOTE_CHILD = 0x00000004 + pub fn ident(&self) -> uintptr_t { + self.kevent.ident } -); -#[cfg(target_os = "netbsd")] -bitflags!( - flags FilterFlag: u32 { - const NOTE_LOWAT = 0x00000001, - const NOTE_DELETE = 0x00000001, - const NOTE_WRITE = 0x00000002, - const NOTE_EXTEND = 0x00000004, - const NOTE_ATTRIB = 0x00000008, - const NOTE_LINK = 0x00000010, - const NOTE_RENAME = 0x00000020, - const NOTE_REVOKE = 0x00000040, - const NOTE_EXIT = 0x80000000, - const NOTE_FORK = 0x40000000, - const NOTE_EXEC = 0x20000000, - const NOTE_SIGNAL = 0x08000000, - const NOTE_PDATAMASK = 0x000fffff, - const NOTE_PCTRLMASK = 0xf0000000, // NOTE: FreeBSD uses 0xfff00000, - const NOTE_TRACK = 0x00000001, - const NOTE_TRACKERR = 0x00000002, - const NOTE_CHILD = 0x00000004 + pub fn filter(&self) -> EventFilter { + unsafe { mem::transmute(self.kevent.filter as type_of_event_filter) } } -); -#[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] -pub const EV_POLL: EventFlag = EV_FLAG0; + pub fn flags(&self) -> EventFlag { + EventFlag::from_bits(self.kevent.flags).unwrap() + } -#[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))] -pub const EV_OOBAND: EventFlag = EV_FLAG1; + pub fn fflags(&self) -> FilterFlag { + FilterFlag::from_bits(self.kevent.fflags).unwrap() + } -pub fn kqueue() -> Result { - let res = unsafe { ffi::kqueue() }; + pub fn data(&self) -> intptr_t { + self.kevent.data + } - Errno::result(res) + pub fn udata(&self) -> uintptr_t { + self.kevent.udata as uintptr_t + } } pub fn kevent(kq: RawFd, @@ -293,74 +243,69 @@ pub fn kevent(kq: RawFd, kevent_ts(kq, changelist, eventlist, Some(timeout)) } -#[cfg(not(target_os = "netbsd"))] -pub fn kevent_ts(kq: RawFd, - changelist: &[KEvent], - eventlist: &mut [KEvent], - timeout_opt: Option) -> Result { - - let res = unsafe { - ffi::kevent( - kq, - changelist.as_ptr(), - changelist.len() as c_int, - eventlist.as_mut_ptr(), - eventlist.len() as c_int, - if let Some(ref timeout) = timeout_opt {timeout as *const timespec} else {ptr::null()}) - }; - - Errno::result(res).map(|r| r as usize) -} - +#[cfg(any(target_os = "macos", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd"))] +type type_of_nchanges = c_int; #[cfg(target_os = "netbsd")] +type type_of_nchanges = size_t; + pub fn kevent_ts(kq: RawFd, changelist: &[KEvent], eventlist: &mut [KEvent], timeout_opt: Option) -> Result { let res = unsafe { - ffi::kevent( + libc::kevent( kq, - changelist.as_ptr(), - changelist.len() as size_t, - eventlist.as_mut_ptr(), - eventlist.len() as size_t, + changelist.as_ptr() as *const libc::kevent, + changelist.len() as type_of_nchanges, + eventlist.as_mut_ptr() as *mut libc::kevent, + eventlist.len() as type_of_nchanges, if let Some(ref timeout) = timeout_opt {timeout as *const timespec} else {ptr::null()}) }; Errno::result(res).map(|r| r as usize) } -#[cfg(not(target_os = "netbsd"))] #[inline] pub fn ev_set(ev: &mut KEvent, ident: usize, filter: EventFilter, flags: EventFlag, fflags: FilterFlag, - udata: usize) { - - ev.ident = ident as uintptr_t; - ev.filter = filter; - ev.flags = flags; - ev.fflags = fflags; - ev.data = 0; - ev.udata = udata; + udata: uintptr_t) { + + ev.kevent.ident = ident as uintptr_t; + ev.kevent.filter = filter as type_of_event_filter; + ev.kevent.flags = flags.bits(); + ev.kevent.fflags = fflags.bits(); + ev.kevent.data = 0; + ev.kevent.udata = udata as type_of_udata; } -#[cfg(target_os = "netbsd")] -#[inline] -pub fn ev_set(ev: &mut KEvent, - ident: usize, - filter: EventFilter, - flags: EventFlag, - fflags: FilterFlag, - udata: isize) { - - ev.ident = ident as uintptr_t; - ev.filter = filter; - ev.flags = flags; - ev.fflags = fflags; - ev.data = 0; - ev.udata = udata; +#[test] +fn test_struct_kevent() { + let udata : uintptr_t = 12345; + + let expected = libc::kevent{ident: 0xdeadbeef, + filter: libc::EVFILT_READ, + flags: libc::EV_DISPATCH | libc::EV_ADD, + fflags: libc::NOTE_CHILD | libc::NOTE_EXIT, + data: 0x1337, + udata: udata as type_of_udata}; + let actual = KEvent::new(0xdeadbeef, + EventFilter::EVFILT_READ, + EV_DISPATCH | EV_ADD, + NOTE_CHILD | NOTE_EXIT, + 0x1337, + udata); + assert!(expected.ident == actual.ident()); + assert!(expected.filter == actual.filter() as type_of_event_filter); + assert!(expected.flags == actual.flags().bits()); + assert!(expected.fflags == actual.fflags().bits()); + assert!(expected.data == actual.data()); + assert!(expected.udata == actual.udata() as type_of_udata); + assert!(mem::size_of::() == mem::size_of::()); }