diff --git a/CHANGELOG.md b/CHANGELOG.md index f1d278d677..8c66fa230a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). It accepts a `None` value for default protocol that was specified with zero using `c_int`. ([#647](https://github.com/nix-rust/nix/pull/647)) +# Fixed +- Fix compilation and tests for OpenBSD targets + ([#688](https://github.com/nix-rust/nix/pull/688)) + ## [0.9.0] 2017-07-23 ### Added diff --git a/nix-test/src/const.c b/nix-test/src/const.c index b0376d48e6..bb70fc9239 100644 --- a/nix-test/src/const.c +++ b/nix-test/src/const.c @@ -209,6 +209,31 @@ get_int_const(const char* err) { GET_CONST(ECANCELED); #endif +#if defined(__OpenBSD__) + GET_CONST(EAUTH); + GET_CONST(EBADRPC); + GET_CONST(ECANCELED); + GET_CONST(EDQUOT); + GET_CONST(EFTYPE); + GET_CONST(EILSEQ); + GET_CONST(EIPSEC); + GET_CONST(EMEDIUMTYPE); + GET_CONST(ENEEDAUTH); + GET_CONST(ENOATTR); + GET_CONST(ENOMEDIUM); + GET_CONST(ENOTSUP); + GET_CONST(EOPNOTSUPP); + GET_CONST(EOVERFLOW); + GET_CONST(EPROCLIM); + GET_CONST(EPROCUNAVAIL); + GET_CONST(EPROGMISMATCH); + GET_CONST(EPROGUNAVAIL); + GET_CONST(EREMOTE); + GET_CONST(ERPCMISMATCH); + GET_CONST(ESTALE); + GET_CONST(EUSERS); +#endif + #ifdef DARWIN GET_CONST(EPWROFF); GET_CONST(EDEVERR); diff --git a/src/sys/event.rs b/src/sys/event.rs index 95b7619eca..9215c65431 100644 --- a/src/sys/event.rs +++ b/src/sys/event.rs @@ -18,17 +18,16 @@ pub struct KEvent { kevent: libc::kevent, } -#[cfg(any(target_os = "openbsd", target_os = "freebsd", - target_os = "dragonfly", target_os = "macos", - target_os = "ios"))] +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "openbsd"))] type type_of_udata = *mut libc::c_void; -#[cfg(any(target_os = "openbsd", target_os = "freebsd", - target_os = "dragonfly", target_os = "macos", - target_os = "ios"))] +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos"))] type type_of_data = libc::intptr_t; #[cfg(any(target_os = "netbsd"))] type type_of_udata = intptr_t; -#[cfg(any(target_os = "netbsd"))] +#[cfg(any(target_os = "netbsd", target_os = "openbsd"))] type type_of_data = libc::int64_t; #[cfg(not(target_os = "netbsd"))] @@ -78,10 +77,11 @@ pub enum EventFilter { EVFILT_TIMER = libc::EVFILT_TIMER, } -#[cfg(any(target_os = "macos", target_os = "ios", - target_os = "freebsd", target_os = "dragonfly"))] +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "openbsd"))] pub type type_of_event_flag = u16; -#[cfg(any(target_os = "netbsd", target_os = "openbsd"))] +#[cfg(any(target_os = "netbsd"))] pub type type_of_event_flag = u32; libc_bitflags!{ pub flags EventFlag: type_of_event_flag { @@ -89,6 +89,14 @@ libc_bitflags!{ EV_CLEAR, EV_DELETE, EV_DISABLE, + // No released version of OpenBSD supports EV_DISPATCH or EV_RECEIPT. + // These have been commited to the -current branch though and are + // expected to be part of the OpenBSD 6.2 release in Nov 2017. + // See: https://marc.info/?l=openbsd-tech&m=149621427511219&w=2 + // https://github.com/rust-lang/libc/pull/613 + #[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "netbsd"))] EV_DISPATCH, #[cfg(target_os = "freebsd")] EV_DROP, @@ -105,7 +113,9 @@ libc_bitflags!{ EV_OOBAND, #[cfg(any(target_os = "macos", target_os = "ios"))] EV_POLL, - #[cfg(not(target_os = "openbsd"))] + #[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "netbsd"))] EV_RECEIPT, EV_SYSFLAGS, } @@ -315,13 +325,13 @@ fn test_struct_kevent() { let expected = libc::kevent{ident: 0xdeadbeef, filter: libc::EVFILT_READ, - flags: libc::EV_DISPATCH | libc::EV_ADD, + flags: libc::EV_ONESHOT | 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, + EV_ONESHOT | EV_ADD, NOTE_CHILD | NOTE_EXIT, 0x1337, udata); @@ -329,7 +339,7 @@ fn test_struct_kevent() { 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.data == actual.data() as type_of_data); assert!(expected.udata == actual.udata() as type_of_udata); assert!(mem::size_of::() == mem::size_of::()); } diff --git a/src/sys/signal.rs b/src/sys/signal.rs index f885af9238..d7e9d91d84 100644 --- a/src/sys/signal.rs +++ b/src/sys/signal.rs @@ -3,13 +3,14 @@ use libc; use {Errno, Error, Result}; -use std::fmt; -use std::fmt::Debug; use std::mem; #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] use std::os::unix::io::RawFd; use std::ptr; +#[cfg(not(target_os = "openbsd"))] +pub use self::sigevent::*; + // Currently there is only one definition of c_int in libc, as well as only one // type for signal constants. // We would prefer to use the libc::c_int alias in the repr attribute. Unfortunately @@ -488,102 +489,114 @@ pub enum SigevNotify { si_value: libc::intptr_t }, } -/// Used to request asynchronous notification of the completion of certain -/// events, such as POSIX AIO and timers. -#[repr(C)] -pub struct SigEvent { - sigevent: libc::sigevent -} +#[cfg(not(target_os = "openbsd"))] +mod sigevent { + use libc; + use std::mem; + use std::ptr; + use std::fmt::{self, Debug}; + use super::SigevNotify; + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + use super::type_of_thread_id; + + /// Used to request asynchronous notification of the completion of certain + /// events, such as POSIX AIO and timers. + #[repr(C)] + pub struct SigEvent { + sigevent: libc::sigevent + } + + impl SigEvent { + /// **Note:** this constructor does not allow the user to set the + /// `sigev_notify_kevent_flags` field. That's considered ok because on FreeBSD + /// at least those flags don't do anything useful. That field is part of a + /// union that shares space with the more genuinely useful fields. + /// + /// **Note:** This constructor also doesn't allow the caller to set the + /// `sigev_notify_function` or `sigev_notify_attributes` fields, which are + /// required for `SIGEV_THREAD`. That's considered ok because on no operating + /// system is `SIGEV_THREAD` the most efficient way to deliver AIO + /// notification. FreeBSD and DragonFly BSD programs should prefer `SIGEV_KEVENT`. + /// Linux, Solaris, and portable programs should prefer `SIGEV_THREAD_ID` or + /// `SIGEV_SIGNAL`. That field is part of a union that shares space with the + /// more genuinely useful `sigev_notify_thread_id` + pub fn new(sigev_notify: SigevNotify) -> SigEvent { + let mut sev = unsafe { mem::zeroed::()}; + sev.sigev_notify = match sigev_notify { + SigevNotify::SigevNone => libc::SIGEV_NONE, + SigevNotify::SigevSignal{..} => libc::SIGEV_SIGNAL, + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + SigevNotify::SigevKevent{..} => libc::SIGEV_KEVENT, + #[cfg(target_os = "freebsd")] + SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, + #[cfg(all(target_os = "linux", target_env = "gnu", not(target_arch = "mips")))] + SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, + #[cfg(any(all(target_os = "linux", target_env = "musl"), target_arch = "mips"))] + SigevNotify::SigevThreadId{..} => 4 // No SIGEV_THREAD_ID defined + }; + sev.sigev_signo = match sigev_notify { + SigevNotify::SigevSignal{ signal, .. } => signal as libc::c_int, + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + SigevNotify::SigevKevent{ kq, ..} => kq, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + SigevNotify::SigevThreadId{ signal, .. } => signal as libc::c_int, + _ => 0 + }; + sev.sigev_value.sival_ptr = match sigev_notify { + SigevNotify::SigevNone => ptr::null_mut::(), + SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut libc::c_void, + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + SigevNotify::SigevKevent{ udata, .. } => udata as *mut libc::c_void, + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut libc::c_void, + }; + SigEvent::set_tid(&mut sev, &sigev_notify); + SigEvent{sigevent: sev} + } -impl SigEvent { - // Note: this constructor does not allow the user to set the - // sigev_notify_kevent_flags field. That's considered ok because on FreeBSD - // at least those flags don't do anything useful. That field is part of a - // union that shares space with the more genuinely useful - // Note: This constructor also doesn't allow the caller to set the - // sigev_notify_function or sigev_notify_attributes fields, which are - // required for SIGEV_THREAD. That's considered ok because on no operating - // system is SIGEV_THREAD the most efficient way to deliver AIO - // notification. FreeBSD and Dragonfly programs should prefer SIGEV_KEVENT. - // Linux, Solaris, and portable programs should prefer SIGEV_THREAD_ID or - // SIGEV_SIGNAL. That field is part of a union that shares space with the - // more genuinely useful sigev_notify_thread_id - pub fn new(sigev_notify: SigevNotify) -> SigEvent { - let mut sev = unsafe { mem::zeroed::()}; - sev.sigev_notify = match sigev_notify { - SigevNotify::SigevNone => libc::SIGEV_NONE, - SigevNotify::SigevSignal{..} => libc::SIGEV_SIGNAL, - #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] - SigevNotify::SigevKevent{..} => libc::SIGEV_KEVENT, - #[cfg(target_os = "freebsd")] - SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, - #[cfg(all(target_os = "linux", target_env = "gnu", not(target_arch = "mips")))] - SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, - #[cfg(any(all(target_os = "linux", target_env = "musl"), target_arch = "mips"))] - SigevNotify::SigevThreadId{..} => 4 // No SIGEV_THREAD_ID defined - }; - sev.sigev_signo = match sigev_notify { - SigevNotify::SigevSignal{ signal, .. } => signal as libc::c_int, - #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] - SigevNotify::SigevKevent{ kq, ..} => kq, - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - SigevNotify::SigevThreadId{ signal, .. } => signal as libc::c_int, - _ => 0 - }; - sev.sigev_value.sival_ptr = match sigev_notify { - SigevNotify::SigevNone => ptr::null_mut::(), - SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut libc::c_void, - #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] - SigevNotify::SigevKevent{ udata, .. } => udata as *mut libc::c_void, - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut libc::c_void, - }; - SigEvent::set_tid(&mut sev, &sigev_notify); - SigEvent{sigevent: sev} - } + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + fn set_tid(sev: &mut libc::sigevent, sigev_notify: &SigevNotify) { + sev.sigev_notify_thread_id = match sigev_notify { + &SigevNotify::SigevThreadId { thread_id, .. } => thread_id, + _ => 0 as type_of_thread_id + }; + } - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - fn set_tid(sev: &mut libc::sigevent, sigev_notify: &SigevNotify) { - sev.sigev_notify_thread_id = match sigev_notify { - &SigevNotify::SigevThreadId { thread_id, .. } => thread_id, - _ => 0 as type_of_thread_id - }; - } + #[cfg(not(any(target_os = "freebsd", target_os = "linux")))] + fn set_tid(_sev: &mut libc::sigevent, _sigev_notify: &SigevNotify) { + } - #[cfg(not(any(target_os = "freebsd", target_os = "linux")))] - fn set_tid(_sev: &mut libc::sigevent, _sigev_notify: &SigevNotify) { + pub fn sigevent(&self) -> libc::sigevent { + self.sigevent + } } - pub fn sigevent(&self) -> libc::sigevent { - self.sigevent - } -} + impl Debug for SigEvent { + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("SigEvent") + .field("sigev_notify", &self.sigevent.sigev_notify) + .field("sigev_signo", &self.sigevent.sigev_signo) + .field("sigev_value", &self.sigevent.sigev_value.sival_ptr) + .field("sigev_notify_thread_id", + &self.sigevent.sigev_notify_thread_id) + .finish() + } -impl Debug for SigEvent { - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("SigEvent") - .field("sigev_notify", &self.sigevent.sigev_notify) - .field("sigev_signo", &self.sigevent.sigev_signo) - .field("sigev_value", &self.sigevent.sigev_value.sival_ptr) - .field("sigev_notify_thread_id", - &self.sigevent.sigev_notify_thread_id) - .finish() - } - - #[cfg(not(any(target_os = "linux", target_os = "freebsd")))] - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("SigEvent") - .field("sigev_notify", &self.sigevent.sigev_notify) - .field("sigev_signo", &self.sigevent.sigev_signo) - .field("sigev_value", &self.sigevent.sigev_value.sival_ptr) - .finish() + #[cfg(not(any(target_os = "freebsd", target_os = "linux")))] + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("SigEvent") + .field("sigev_notify", &self.sigevent.sigev_notify) + .field("sigev_signo", &self.sigevent.sigev_signo) + .field("sigev_value", &self.sigevent.sigev_value.sival_ptr) + .finish() + } } -} -impl<'a> From<&'a libc::sigevent> for SigEvent { - fn from(sigevent: &libc::sigevent) -> Self { - SigEvent{ sigevent: sigevent.clone() } + impl<'a> From<&'a libc::sigevent> for SigEvent { + fn from(sigevent: &libc::sigevent) -> Self { + SigEvent{ sigevent: sigevent.clone() } + } } } diff --git a/src/sys/socket/ffi.rs b/src/sys/socket/ffi.rs index 55a47eb642..265a97c976 100644 --- a/src/sys/socket/ffi.rs +++ b/src/sys/socket/ffi.rs @@ -3,9 +3,12 @@ pub use libc::{socket, listen, bind, accept, connect, setsockopt, sendto, recvfrom, getsockname, getpeername, recv, send}; -use libc::{c_int, c_void, socklen_t, size_t, ssize_t}; +use libc::{c_int, c_void, socklen_t, ssize_t}; -#[cfg(target_os = "macos")] +#[cfg(not(target_os = "macos"))] +use libc::size_t; + +#[cfg(not(target_os = "linux"))] use libc::c_uint; use sys::uio::IoVec; @@ -23,19 +26,27 @@ pub type type_of_cmsg_data = c_uint; #[cfg(not(target_os = "macos"))] pub type type_of_cmsg_data = size_t; +#[cfg(target_os = "linux")] +pub type type_of_msg_iovlen = size_t; + +#[cfg(not(target_os = "linux"))] +pub type type_of_msg_iovlen = c_uint; + // Private because we don't expose any external functions that operate // directly on this type; we just use it internally at FFI boundaries. // Note that in some cases we store pointers in *const fields that the // kernel will proceed to mutate, so users should be careful about the // actual mutability of data pointed to by this structure. +// +// FIXME: Replace these structs with the ones defined in libc #[repr(C)] pub struct msghdr<'a> { pub msg_name: *const c_void, pub msg_namelen: socklen_t, pub msg_iov: *const IoVec<&'a [u8]>, - pub msg_iovlen: size_t, + pub msg_iovlen: type_of_msg_iovlen, pub msg_control: *const c_void, - pub msg_controllen: size_t, + pub msg_controllen: type_of_cmsg_len, pub msg_flags: c_int, } diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 86129fcf00..6ab1684ad0 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -168,7 +168,7 @@ unsafe fn copy_bytes<'a, 'b, T: ?Sized>(src: &T, dst: &'a mut &'b mut [u8]) { } -use self::ffi::{cmsghdr, msghdr, type_of_cmsg_len, type_of_cmsg_data}; +use self::ffi::{cmsghdr, msghdr, type_of_cmsg_data, type_of_msg_iovlen, type_of_cmsg_len}; /// A structure used to make room in a cmsghdr passed to recvmsg. The /// size and alignment match that of a cmsghdr followed by a T, but the @@ -204,11 +204,17 @@ impl<'a> RecvMsg<'a> { /// Iterate over the valid control messages pointed to by this /// msghdr. pub fn cmsgs(&self) -> CmsgIterator { - CmsgIterator(self.cmsg_buffer) + CmsgIterator { + buf: self.cmsg_buffer, + next: 0 + } } } -pub struct CmsgIterator<'a>(&'a [u8]); +pub struct CmsgIterator<'a> { + buf: &'a [u8], + next: usize, +} impl<'a> Iterator for CmsgIterator<'a> { type Item = ControlMessage<'a>; @@ -217,12 +223,11 @@ impl<'a> Iterator for CmsgIterator<'a> { // although we handle the invariants in slightly different places to // get a better iterator interface. fn next(&mut self) -> Option> { - let buf = self.0; let sizeof_cmsghdr = mem::size_of::(); - if buf.len() < sizeof_cmsghdr { + if self.buf.len() < sizeof_cmsghdr { return None; } - let cmsg: &cmsghdr = unsafe { mem::transmute(buf.as_ptr()) }; + let cmsg: &cmsghdr = unsafe { mem::transmute(self.buf.as_ptr()) }; // This check is only in the glibc implementation of CMSG_NXTHDR // (although it claims the kernel header checks this), but such @@ -232,12 +237,20 @@ impl<'a> Iterator for CmsgIterator<'a> { return None; } let len = cmsg_len - sizeof_cmsghdr; + let aligned_cmsg_len = if self.next == 0 { + // CMSG_FIRSTHDR + cmsg_len + } else { + // CMSG_NXTHDR + cmsg_align(cmsg_len) + }; // Advance our internal pointer. - if cmsg_align(cmsg_len) > buf.len() { + if aligned_cmsg_len > self.buf.len() { return None; } - self.0 = &buf[cmsg_align(cmsg_len)..]; + self.buf = &self.buf[aligned_cmsg_len..]; + self.next += 1; match (cmsg.cmsg_level, cmsg.cmsg_type) { (libc::SOL_SOCKET, libc::SCM_RIGHTS) => unsafe { @@ -370,9 +383,9 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<' msg_name: name as *const c_void, msg_namelen: namelen, msg_iov: iov.as_ptr(), - msg_iovlen: iov.len() as size_t, + msg_iovlen: iov.len() as type_of_msg_iovlen, msg_control: cmsg_ptr, - msg_controllen: capacity as size_t, + msg_controllen: capacity as type_of_cmsg_len, msg_flags: 0, }; let ret = unsafe { ffi::sendmsg(fd, &mhdr, flags.bits()) }; @@ -393,9 +406,9 @@ pub fn recvmsg<'a, T>(fd: RawFd, iov: &[IoVec<&mut [u8]>], cmsg_buffer: Option<& msg_name: &mut address as *const _ as *const c_void, msg_namelen: mem::size_of::() as socklen_t, msg_iov: iov.as_ptr() as *const IoVec<&[u8]>, // safe cast to add const-ness - msg_iovlen: iov.len() as size_t, + msg_iovlen: iov.len() as type_of_msg_iovlen, msg_control: msg_control as *const c_void, - msg_controllen: msg_controllen as size_t, + msg_controllen: msg_controllen as type_of_cmsg_len, msg_flags: 0, }; let ret = unsafe { ffi::recvmsg(fd, &mut mhdr, flags.bits()) }; diff --git a/test/test_unistd.rs b/test/test_unistd.rs index 944232caaf..09143c586d 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -244,7 +244,7 @@ fn test_fpathconf_limited() { #[test] fn test_pathconf_limited() { // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test - let path_max = pathconf(".", PathconfVar::PATH_MAX); + let path_max = pathconf("/", PathconfVar::PATH_MAX); assert!(path_max.expect("pathconf failed").expect("PATH_MAX is unlimited") > 0); }