From 2abaad5fbce61ec566a244c1d2eae2bc3aa5fd5b Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Thu, 21 Aug 2014 01:13:57 -0700 Subject: Bind kqueue + misc cleanup --- src/fcntl.rs | 106 +++++++++++++++++++++++++++++++++++++- src/features.rs | 15 ++++++ src/lib.rs | 3 ++ src/sys/event.rs | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/sys/mod.rs | 4 ++ src/sys/socket.rs | 18 +++++-- 6 files changed, 290 insertions(+), 4 deletions(-) create mode 100644 src/sys/event.rs diff --git a/src/fcntl.rs b/src/fcntl.rs index ccaee9bc..80290aec 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -4,11 +4,66 @@ use libc::{c_int, mode_t}; use errno::{SysResult, SysError}; pub use self::consts::*; +pub use self::ffi::flock; pub type Fd = c_int; mod ffi { - pub use libc::open; + pub use libc::{open, fcntl}; + pub use self::os::*; + + #[cfg(target_os = "linux")] + mod os { + use libc::{c_int, c_short, off_t, pid_t}; + + pub struct flock { + pub l_type: c_short, + pub l_whence: c_short, + pub l_start: off_t, + pub l_len: off_t, + pub l_pid: pid_t, + + // not actually here, but brings in line with freebsd + pub l_sysid: c_int, + } + + pub static F_DUPFD: c_int = 0; + pub static F_DUPFD_CLOEXEC: c_int = 1030; + pub static F_GETFD: c_int = 1; + pub static F_SETFD: c_int = 2; + pub static F_GETFL: c_int = 3; + pub static F_SETFL: c_int = 4; + pub static F_SETLK: c_int = 6; + pub static F_SETLKW: c_int = 7; + pub static F_GETLK: c_int = 5; + } + + #[cfg(target_os = "macos")] + #[cfg(target_os = "ios")] + mod os { + use libc::{c_int, c_short, off_t, pid_t}; + + pub struct flock { + pub l_start: off_t, + pub l_len: off_t, + pub l_pid: pid_t, + pub l_type: c_short, + pub l_whence: c_short, + + // not actually here, but brings in line with freebsd + pub l_sysid: c_int, + } + + pub static F_DUPFD: c_int = 0; + pub static F_DUPFD_CLOEXEC: c_int = 67; + pub static F_GETFD: c_int = 1; + pub static F_SETFD: c_int = 2; + pub static F_GETFL: c_int = 3; + pub static F_SETFL: c_int = 4; + pub static F_SETLK: c_int = 8; + pub static F_SETLKW: c_int = 9; + pub static F_GETLK: c_int = 7; + } } pub fn open(path: &Path, oflag: OFlag, mode: FilePermission) -> SysResult { @@ -21,6 +76,43 @@ pub fn open(path: &Path, oflag: OFlag, mode: FilePermission) -> SysResult { Ok(fd) } +pub enum FcntlArg<'a> { + F_DUPFD(Fd), + F_DUPFD_CLOEXEC(Fd), + F_GETFD, + F_SETFD(FdFlag), // FD_FLAGS + F_GETFL, + F_SETFL(OFlag), // O_NONBLOCK + F_SETLK(&'a flock), + F_SETLKW(&'a flock), + F_GETLK(&'a mut flock), + #[cfg(target_os = "linux")] + F_OFD_SETLK(&'a flock), + #[cfg(target_os = "linux")] + F_OFD_SETLKW(&'a flock), + #[cfg(target_os = "linux")] + F_OFD_GETLK(&'a mut flock) + + // TODO: Rest of flags +} + +// TODO: Figure out how to handle value fcntl returns +pub fn fcntl(fd: Fd, arg: FcntlArg) -> SysResult<()> { + let res = unsafe { + match arg { + F_SETFD(flag) => ffi::fcntl(fd, ffi::F_SETFD, flag.bits()), + F_SETFL(flag) => ffi::fcntl(fd, ffi::F_SETFL, flag.bits()), + _ => unimplemented!() + } + }; + + if res < 0 { + return Err(SysError::last()); + } + + Ok(()) +} + #[cfg(target_os = "linux")] mod consts { use libc::c_int; @@ -50,6 +142,12 @@ mod consts { static O_NDELAY = O_NONBLOCK.bits } ) + + bitflags!( + flags FdFlag: c_int { + static FD_CLOEXEC = 1 + } + ) } #[cfg(target_os = "macos")] @@ -82,4 +180,10 @@ mod consts { static O_NDELAY = O_NONBLOCK.bits } ) + + bitflags!( + flags FdFlag: c_int { + static FD_CLOEXEC = 1 + } + ) } diff --git a/src/features.rs b/src/features.rs index 8b137891..db8bddf0 100644 --- a/src/features.rs +++ b/src/features.rs @@ -1 +1,16 @@ +pub use self::os::*; +#[cfg(target_os = "linux")] +mod os { + pub fn atomic_cloexec() -> bool { + true // TODO: Not on all kernel versions + } +} + +#[cfg(target_os = "macos")] +#[cfg(target_os = "ios")] +mod os { + pub fn atomic_cloexec() -> bool { + false + } +} diff --git a/src/lib.rs b/src/lib.rs index 96579a1b..e679f7a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![crate_name = "nix"] #![feature(globs)] +#![allow(non_camel_case_types)] extern crate libc; @@ -11,6 +12,8 @@ pub use errno::{SysResult, SysError}; pub mod errno; #[cfg(target_os = "linux")] +#[cfg(target_os = "macos")] +#[cfg(target_os = "ios")] pub mod features; #[cfg(target_os = "linux")] diff --git a/src/sys/event.rs b/src/sys/event.rs new file mode 100644 index 00000000..335f9ad8 --- /dev/null +++ b/src/sys/event.rs @@ -0,0 +1,148 @@ +use libc::{timespec, time_t, c_int, c_long}; +use errno::{SysResult, SysError}; +use fcntl::Fd; + +pub use self::ffi::kevent as KEvent; + +mod ffi { + pub use libc::{c_int, c_void, timespec}; + + // Packed to 32 bytes + pub struct kevent { + pub ident: uint, // 8 + pub filter: i16, // 2 + pub flags: u16, // 2 + pub fflags: u32, // 4 + pub data: int, // 8 + pub udata: *mut c_void // 8 + } + + extern { + pub fn kqueue() -> c_int; + + pub fn kevent( + kq: c_int, + changelist: *const kevent, + nchanges: c_int, + eventlist: *mut kevent, + nevents: c_int, + timeout: *const timespec) -> c_int; + } +} + +#[repr(C)] +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 +} + +bitflags!( + flags EventFlag: u16 { + static EV_ADD = 0x0001, + static EV_DELETE = 0x0002, + static EV_ENABLE = 0x0004, + static EV_DISABLE = 0x0008, + static EV_RECEIPT = 0x0040, + static EV_ONESHOT = 0x0010, + static EV_CLEAR = 0x0020, + static EV_DISPATCH = 0x0080, + static EV_SYSFLAGS = 0xF000, + static EV_FLAG0 = 0x1000, + static EV_FLAG1 = 0x2000, + static EV_EOF = 0x8000, + static EV_ERROR = 0x4000 + } +) + +bitflags!( + flags FilterFlag: u32 { + static NOTE_TRIGGER = 0x01000000, + static NOTE_FFNOP = 0x00000000, + static NOTE_FFAND = 0x40000000, + static NOTE_FFOR = 0x80000000, + static NOTE_FFCOPY = 0xc0000000, + static NOTE_FFCTRLMASK = 0xc0000000, + static NOTE_FFLAGSMASK = 0x00ffffff, + static NOTE_LOWAT = 0x00000001, + static NOTE_DELETE = 0x00000001, + static NOTE_WRITE = 0x00000002, + static NOTE_EXTEND = 0x00000004, + static NOTE_ATTRIB = 0x00000008, + static NOTE_LINK = 0x00000010, + static NOTE_RENAME = 0x00000020, + static NOTE_REVOKE = 0x00000040, + static NOTE_NONE = 0x00000080, + static NOTE_EXIT = 0x80000000, + static NOTE_FORK = 0x40000000, + static NOTE_EXEC = 0x20000000, + static NOTE_REAP = 0x10000000, + static NOTE_SIGNAL = 0x08000000, + static NOTE_EXITSTATUS = 0x04000000, + static NOTE_RESOURCEEND = 0x02000000, + static NOTE_APPACTIVE = 0x00800000, + static NOTE_APPBACKGROUND = 0x00400000, + static NOTE_APPNONUI = 0x00200000, + static NOTE_APPINACTIVE = 0x00100000, + static NOTE_APPALLSTATES = 0x00f00000, + static NOTE_PDATAMASK = 0x000fffff, + static NOTE_PCTRLMASK = 0xfff00000, + static NOTE_EXIT_REPARENTED = 0x00080000, + static NOTE_VM_PRESSURE = 0x80000000, + static NOTE_VM_PRESSURE_TERMINATE = 0x40000000, + static NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000, + static NOTE_VM_ERROR = 0x10000000, + static NOTE_SECONDS = 0x00000001, + static NOTE_USECONDS = 0x00000002, + static NOTE_NSECONDS = 0x00000004, + static NOTE_ABSOLUTE = 0x00000008, + static NOTE_TRACK = 0x00000001, + static NOTE_TRACKERR = 0x00000002, + static NOTE_CHILD = 0x00000004 + } +) + +pub static EV_POLL: EventFlag = EV_FLAG0; +pub static EV_OOBAND: EventFlag = EV_FLAG1; + +pub fn kqueue() -> Fd { + unsafe { ffi::kqueue() } +} + +pub fn kevent(kq: Fd, + changelist: &[KEvent], + eventlist: &mut [KEvent], + timeout_ms: uint) -> SysResult { + + // Convert ms to timespec + let timeout = timespec { + tv_sec: (timeout_ms / 1000) as time_t, + tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long + }; + + let res = unsafe { + ffi::kevent( + kq, + changelist.as_ptr(), + changelist.len() as c_int, + eventlist.as_mut_ptr(), + eventlist.len() as c_int, + &timeout as *const timespec) + }; + + if res < 0 { + return Err(SysError::last()); + } + + return Ok(res as uint) +} diff --git a/src/sys/mod.rs b/src/sys/mod.rs index ccab75fd..9e99458b 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -2,6 +2,10 @@ #[cfg(target_os = "linux")] pub mod epoll; +#[cfg(target_os = "macos")] +#[cfg(target_os = "ios")] +pub mod event; + #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] #[cfg(target_os = "ios")] diff --git a/src/sys/socket.rs b/src/sys/socket.rs index 46db7ab4..e714834b 100644 --- a/src/sys/socket.rs +++ b/src/sys/socket.rs @@ -1,7 +1,8 @@ use std::{mem, ptr}; use libc::{c_int, socklen_t}; -use fcntl::Fd; +use fcntl::{Fd, fcntl, F_SETFL, F_SETFD, FD_CLOEXEC, O_NONBLOCK}; use errno::{SysResult, SysError, from_ffi}; +use features; pub use libc::{in_addr, sockaddr_in, sockaddr_in6, sockaddr_un, sa_family_t}; @@ -167,14 +168,25 @@ mod consts { pub static SO_RESTRICT_DENYSET: SockOpt = 0x80000000; } -pub fn socket(domain: AddressFamily, ty: SockType, flags: SockFlag) -> SysResult { +pub fn socket(domain: AddressFamily, mut ty: SockType, flags: SockFlag) -> SysResult { + let feat_atomic = features::atomic_cloexec(); // TODO: detect + + if feat_atomic { + ty = ty | flags.bits(); + } + // TODO: Check the kernel version - let res = unsafe { ffi::socket(domain, ty | flags.bits(), 0) }; + let res = unsafe { ffi::socket(domain, ty, 0) }; if res < 0 { return Err(SysError::last()); } + if !feat_atomic { + try!(fcntl(res, F_SETFD(FD_CLOEXEC))); + try!(fcntl(res, F_SETFL(O_NONBLOCK))); + } + Ok(res) } -- cgit v1.2.3