summaryrefslogtreecommitdiff
path: root/src/sys
diff options
context:
space:
mode:
Diffstat (limited to 'src/sys')
-rw-r--r--src/sys/aio.rs249
-rw-r--r--src/sys/epoll.rs35
-rw-r--r--src/sys/event.rs541
-rw-r--r--src/sys/eventfd.rs6
-rw-r--r--src/sys/ioctl/mod.rs107
-rw-r--r--src/sys/ioctl/platform/bsd.rs36
-rw-r--r--src/sys/ioctl/platform/dragonfly.rs0
-rw-r--r--src/sys/ioctl/platform/freebsd.rs0
-rw-r--r--src/sys/ioctl/platform/ios.rs0
-rw-r--r--src/sys/ioctl/platform/linux.rs72
-rw-r--r--src/sys/ioctl/platform/macos.rs0
-rw-r--r--src/sys/ioctl/platform/netbsd.rs0
-rw-r--r--src/sys/ioctl/platform/openbsd.rs0
-rw-r--r--src/sys/mod.rs6
-rw-r--r--src/sys/select.rs10
-rw-r--r--src/sys/signal.rs180
-rw-r--r--src/sys/socket/addr.rs144
-rw-r--r--src/sys/socket/consts.rs7
-rw-r--r--src/sys/socket/mod.rs28
-rw-r--r--src/sys/time.rs452
20 files changed, 1374 insertions, 499 deletions
diff --git a/src/sys/aio.rs b/src/sys/aio.rs
new file mode 100644
index 00000000..f0fce435
--- /dev/null
+++ b/src/sys/aio.rs
@@ -0,0 +1,249 @@
+use {Error, Errno, Result};
+use std::os::unix::io::RawFd;
+use libc::{c_void, off_t, size_t};
+use libc;
+use std::marker::PhantomData;
+use std::mem;
+use std::ptr::{null, null_mut};
+use sys::signal::*;
+use sys::time::TimeSpec;
+
+/// Mode for `aio_fsync`. Controls whether only data or both data and metadata
+/// are synced.
+#[repr(i32)]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum AioFsyncMode {
+ /// do it like `fsync`
+ O_SYNC = libc::O_SYNC,
+ /// on supported operating systems only, do it like `fdatasync`
+ #[cfg(any(target_os = "openbsd", target_os = "bitrig",
+ target_os = "netbsd", target_os = "macos", target_os = "ios",
+ target_os = "linux"))]
+ O_DSYNC = libc::O_DSYNC
+}
+
+/// When used with `lio_listio`, determines whether a given `aiocb` should be
+/// used for a read operation, a write operation, or ignored. Has no effect for
+/// any other aio functions.
+#[repr(i32)]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum LioOpcode {
+ LIO_NOP = libc::LIO_NOP,
+ LIO_WRITE = libc::LIO_WRITE,
+ LIO_READ = libc::LIO_READ
+}
+
+/// Mode for `lio_listio`.
+#[repr(i32)]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum LioMode {
+ /// Requests that `lio_listio` block until all requested operations have
+ /// been completed
+ LIO_WAIT = libc::LIO_WAIT,
+ /// Requests that `lio_listio` return immediately
+ LIO_NOWAIT = libc::LIO_NOWAIT,
+}
+
+/// Return values for `aio_cancel`
+#[repr(i32)]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum AioCancelStat {
+ /// All outstanding requests were canceled
+ AioCanceled = libc::AIO_CANCELED,
+ /// Some requests were not canceled. Their status should be checked with
+ /// `aio_error`
+ AioNotCanceled = libc::AIO_NOTCANCELED,
+ /// All of the requests have already finished
+ AioAllDone = libc::AIO_ALLDONE,
+}
+
+/// The basic structure used by all aio functions. Each `aiocb` represents one
+/// I/O request.
+#[repr(C)]
+pub struct AioCb<'a> {
+ aiocb: libc::aiocb,
+ phantom: PhantomData<&'a mut [u8]>
+}
+
+impl<'a> AioCb<'a> {
+ /// Constructs a new `AioCb` with no associated buffer.
+ ///
+ /// The resulting `AioCb` structure is suitable for use with `aio_fsync`.
+ /// * `fd` File descriptor. Required for all aio functions.
+ /// * `prio` If POSIX Prioritized IO is supported, then the operation will
+ /// be prioritized at the process's priority level minus `prio`
+ /// * `sigev_notify` Determines how you will be notified of event
+ /// completion.
+ pub fn from_fd(fd: RawFd, prio: ::c_int,
+ sigev_notify: SigevNotify) -> AioCb<'a> {
+ let mut a = AioCb::common_init(fd, prio, sigev_notify);
+ a.aio_offset = 0;
+ a.aio_nbytes = 0;
+ a.aio_buf = null_mut();
+
+ let aiocb = AioCb { aiocb: a, phantom: PhantomData};
+ aiocb
+ }
+
+ /// Constructs a new `AioCb`.
+ ///
+ /// * `fd` File descriptor. Required for all aio functions.
+ /// * `offs` File offset
+ /// * `buf` A memory buffer
+ /// * `prio` If POSIX Prioritized IO is supported, then the operation will
+ /// be prioritized at the process's priority level minus `prio`
+ /// * `sigev_notify` Determines how you will be notified of event
+ /// completion.
+ /// * `opcode` This field is only used for `lio_listio`. It determines
+ /// which operation to use for this individual aiocb
+ pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8],
+ prio: ::c_int, sigev_notify: SigevNotify,
+ opcode: LioOpcode) -> AioCb {
+ let mut a = AioCb::common_init(fd, prio, sigev_notify);
+ a.aio_offset = offs;
+ a.aio_nbytes = buf.len() as size_t;
+ a.aio_buf = buf.as_ptr() as *mut c_void;
+ a.aio_lio_opcode = opcode as ::c_int;
+
+ let aiocb = AioCb { aiocb: a, phantom: PhantomData};
+ aiocb
+ }
+
+ /// Like `from_mut_slice`, but works on constant slices rather than
+ /// mutable slices.
+ ///
+ /// This is technically unsafe, but in practice it's fine
+ /// to use with any aio functions except `aio_read` and `lio_listio` (with
+ /// `opcode` set to `LIO_READ`). This method is useful when writing a const
+ /// buffer with `aio_write`, since from_mut_slice can't work with const
+ /// buffers.
+ // Note: another solution to the problem of writing const buffers would be
+ // to genericize AioCb for both &mut [u8] and &[u8] buffers. aio_read could
+ // take the former and aio_write could take the latter. However, then
+ // lio_listio wouldn't work, because that function needs a slice of AioCb,
+ // and they must all be the same type. We're basically stuck with using an
+ // unsafe function, since aio (as designed in C) is an unsafe API.
+ pub unsafe fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8],
+ prio: ::c_int, sigev_notify: SigevNotify,
+ opcode: LioOpcode) -> AioCb {
+ let mut a = AioCb::common_init(fd, prio, sigev_notify);
+ a.aio_offset = offs;
+ a.aio_nbytes = buf.len() as size_t;
+ a.aio_buf = buf.as_ptr() as *mut c_void;
+ a.aio_lio_opcode = opcode as ::c_int;
+
+ let aiocb = AioCb { aiocb: a, phantom: PhantomData};
+ aiocb
+ }
+
+ fn common_init(fd: RawFd, prio: ::c_int,
+ sigev_notify: SigevNotify) -> libc::aiocb {
+ // Use mem::zeroed instead of explicitly zeroing each field, because the
+ // number and name of reserved fields is OS-dependent. On some OSes,
+ // some reserved fields are used the kernel for state, and must be
+ // explicitly zeroed when allocated.
+ let mut a = unsafe { mem::zeroed::<libc::aiocb>()};
+ a.aio_fildes = fd;
+ a.aio_reqprio = prio;
+ a.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
+ a
+ }
+
+ /// Update the notification settings for an existing `aiocb`
+ pub fn set_sigev_notify(&mut self, sigev_notify: SigevNotify) {
+ self.aiocb.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
+ }
+}
+
+/// Cancels outstanding AIO requests. If `aiocb` is `None`, then all requests
+/// for `fd` will be cancelled. Otherwise, only the given `AioCb` will be
+/// cancelled.
+pub fn aio_cancel(fd: RawFd, aiocb: Option<&mut AioCb>) -> Result<AioCancelStat> {
+ let p: *mut libc::aiocb = match aiocb {
+ None => null_mut(),
+ Some(x) => &mut x.aiocb
+ };
+ match unsafe { libc::aio_cancel(fd, p) } {
+ libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
+ libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
+ libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
+ -1 => Err(Error::last()),
+ _ => panic!("unknown aio_cancel return value")
+ }
+}
+
+/// Retrieve error status of an asynchronous operation. If the request has not
+/// yet completed, returns `EINPROGRESS`. Otherwise, returns `Ok` or any other
+/// error.
+pub fn aio_error(aiocb: &mut AioCb) -> Result<()> {
+ let p: *mut libc::aiocb = &mut aiocb.aiocb;
+ match unsafe { libc::aio_error(p) } {
+ 0 => Ok(()),
+ num if num > 0 => Err(Error::from_errno(Errno::from_i32(num))),
+ -1 => Err(Error::last()),
+ num => panic!("unknown aio_error return value {:?}", num)
+ }
+}
+
+/// An asynchronous version of `fsync`.
+pub fn aio_fsync(mode: AioFsyncMode, aiocb: &mut AioCb) -> Result<()> {
+ let p: *mut libc::aiocb = &mut aiocb.aiocb;
+ Errno::result(unsafe { libc::aio_fsync(mode as ::c_int, p) }).map(drop)
+}
+
+/// Asynchronously reads from a file descriptor into a buffer
+pub fn aio_read(aiocb: &mut AioCb) -> Result<()> {
+ let p: *mut libc::aiocb = &mut aiocb.aiocb;
+ Errno::result(unsafe { libc::aio_read(p) }).map(drop)
+}
+
+/// Retrieve return status of an asynchronous operation. Should only be called
+/// once for each `AioCb`, after `aio_error` indicates that it has completed.
+/// The result the same as for `read`, `write`, of `fsync`.
+pub fn aio_return(aiocb: &mut AioCb) -> Result<isize> {
+ let p: *mut libc::aiocb = &mut aiocb.aiocb;
+ Errno::result(unsafe { libc::aio_return(p) })
+}
+
+/// Suspends the calling process until at least one of the specified `AioCb`s
+/// has completed, a signal is delivered, or the timeout has passed. If
+/// `timeout` is `None`, `aio_suspend` will block indefinitely.
+pub fn aio_suspend(list: &[&AioCb], timeout: Option<TimeSpec>) -> Result<()> {
+ // We must use transmute because Rust doesn't understand that a pointer to a
+ // Struct is the same as a pointer to its first element.
+ let plist = unsafe {
+ mem::transmute::<&[&AioCb], *const [*const libc::aiocb]>(list)
+ };
+ let p = plist as *const *const libc::aiocb;
+ let timep = match timeout {
+ None => null::<libc::timespec>(),
+ Some(x) => x.as_ref() as *const libc::timespec
+ };
+ Errno::result(unsafe {
+ libc::aio_suspend(p, list.len() as i32, timep)
+ }).map(drop)
+}
+
+/// Asynchronously writes from a buffer to a file descriptor
+pub fn aio_write(aiocb: &mut AioCb) -> Result<()> {
+ let p: *mut libc::aiocb = &mut aiocb.aiocb;
+ Errno::result(unsafe { libc::aio_write(p) }).map(drop)
+}
+
+/// Submits multiple asynchronous I/O requests with a single system call. The
+/// order in which the requests are carried out is not specified.
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+pub fn lio_listio(mode: LioMode, list: &[&mut AioCb],
+ sigev_notify: SigevNotify) -> Result<()> {
+ let sigev = SigEvent::new(sigev_notify);
+ let sigevp = &mut sigev.sigevent() as *mut libc::sigevent;
+ // We must use transmute because Rust doesn't understand that a pointer to a
+ // Struct is the same as a pointer to its first element.
+ let plist = unsafe {
+ mem::transmute::<&[&mut AioCb], *const [*mut libc::aiocb]>(list)
+ };
+ let p = plist as *const *mut libc::aiocb;
+ Errno::result(unsafe {
+ libc::lio_listio(mode as i32, p, list.len() as i32, sigevp)
+ }).map(drop)
+}
diff --git a/src/sys/epoll.rs b/src/sys/epoll.rs
index 9774318f..eb28ffb9 100644
--- a/src/sys/epoll.rs
+++ b/src/sys/epoll.rs
@@ -1,6 +1,9 @@
use {Errno, Result};
use libc::{self, c_int};
use std::os::unix::io::RawFd;
+use std::ptr;
+use std::mem;
+use ::Error;
bitflags!(
#[repr(C)]
@@ -23,7 +26,7 @@ bitflags!(
}
);
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Eq, PartialEq)]
#[repr(C)]
pub enum EpollOp {
EpollCtlAdd = 1,
@@ -44,10 +47,14 @@ pub struct EpollEvent {
}
impl EpollEvent {
- pub fn new(events: EpollFlags, data: u64) -> EpollEvent {
+ pub fn new(events: EpollFlags, data: u64) -> Self {
EpollEvent { event: libc::epoll_event { events: events.bits(), u64: data } }
}
+ pub fn empty() -> Self {
+ unsafe { mem::zeroed::<EpollEvent>() }
+ }
+
pub fn events(&self) -> EpollFlags {
EpollFlags::from_bits(self.event.events).unwrap()
}
@@ -57,6 +64,16 @@ impl EpollEvent {
}
}
+impl<'a> Into<&'a mut EpollEvent> for Option<&'a mut EpollEvent> {
+ #[inline]
+ fn into(self) -> &'a mut EpollEvent {
+ match self {
+ Some(epoll_event) => epoll_event,
+ None => unsafe { &mut *ptr::null_mut::<EpollEvent>() }
+ }
+ }
+}
+
#[inline]
pub fn epoll_create() -> Result<RawFd> {
let res = unsafe { libc::epoll_create(1024) };
@@ -72,10 +89,16 @@ pub fn epoll_create1(flags: EpollCreateFlags) -> Result<RawFd> {
}
#[inline]
-pub fn epoll_ctl(epfd: RawFd, op: EpollOp, fd: RawFd, event: &mut EpollEvent) -> Result<()> {
- let res = unsafe { libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event) };
-
- Errno::result(res).map(drop)
+pub fn epoll_ctl<'a, T>(epfd: RawFd, op: EpollOp, fd: RawFd, event: T) -> Result<()>
+ where T: Into<&'a mut EpollEvent>
+{
+ let event: &mut EpollEvent = event.into();
+ if event as *const EpollEvent == ptr::null() && op != EpollOp::EpollCtlDel {
+ Err(Error::Sys(Errno::EINVAL))
+ } else {
+ let res = unsafe { libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event) };
+ Errno::result(res).map(drop)
+ }
}
#[inline]
diff --git a/src/sys/event.rs b/src/sys/event.rs
index 0e94475e..405f38fc 100644
--- a/src/sys/event.rs
+++ b/src/sys/event.rs
@@ -3,280 +3,247 @@
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",
+ target_os = "ios"))]
+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 = "ios",
+ target_os = "dragonfly",
+ target_os = "freebsd"))]
+ EVFILT_FS = libc::EVFILT_FS,
+ #[cfg(target_os = "freebsd")]
+ EVFILT_LIO = libc::EVFILT_LIO,
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ 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 = "ios",
+ target_os = "dragonfly",
+ target_os = "freebsd"))]
+ EVFILT_USER = libc::EVFILT_USER,
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ 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 = "ios",
+ 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(any(target_os = "macos", target_os = "ios"))]
+ EV_FLAG0,
+ EV_FLAG1,
+ #[cfg(target_os = "dragonfly")]
+ EV_NODATA,
+ EV_ONESHOT,
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ EV_OOBAND,
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ 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(any(target_os = "macos", target_os = "ios"))]
+ 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(any(target_os = "macos", target_os = "ios"))]
+ const NOTE_EXIT_REPARENTED = libc::NOTE_EXIT_REPARENTED,
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ const NOTE_EXITSTATUS = libc::NOTE_EXITSTATUS,
+ const NOTE_EXTEND = libc::NOTE_EXTEND,
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ const NOTE_FFAND = libc::NOTE_FFAND,
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ const NOTE_FFCOPY = libc::NOTE_FFCOPY,
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ const NOTE_FFCTRLMASK = libc::NOTE_FFCTRLMASK,
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ const NOTE_FFLAGSMASK = libc::NOTE_FFLAGSMASK,
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ const NOTE_FFNOP = libc::NOTE_FFNOP,
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ 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(any(target_os = "macos", target_os = "ios"))]
+ const NOTE_NONE = libc::NOTE_NONE,
+ #[cfg(any(target_os = "macos", target_os = "ios", 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(any(target_os = "macos", target_os = "ios"))]
+ 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 = "ios", target_os = "freebsd"))]
+ const NOTE_SECONDS = libc::NOTE_SECONDS,
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ 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 = "ios",
+ 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 = "ios", target_os = "freebsd"))]
+ const NOTE_USECONDS = libc::NOTE_USECONDS,
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ const NOTE_VM_ERROR = libc::NOTE_VM_ERROR,
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ const NOTE_VM_PRESSURE = libc::NOTE_VM_PRESSURE,
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ const NOTE_VM_PRESSURE_SUDDEN_TERMINATE = libc::NOTE_VM_PRESSURE_SUDDEN_TERMINATE,
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ 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<RawFd> {
+ 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)
+}
+
+
+// KEvent can't derive Send because on some operating systems, udata is defined
+// as a void*. However, KEvent's public API always treats udata as an intptr_t,
+// which is safe to Send.
+unsafe impl Send for KEvent {
+}
+
+impl KEvent {
+ pub fn new(ident: uintptr_t, filter: EventFilter, flags: EventFlag,
+ fflags:FilterFlag, data: intptr_t, udata: intptr_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<RawFd> {
- let res = unsafe { ffi::kqueue() };
+ pub fn data(&self) -> intptr_t {
+ self.kevent.data
+ }
- Errno::result(res)
+ pub fn udata(&self) -> intptr_t {
+ self.kevent.udata as intptr_t
+ }
}
pub fn kevent(kq: RawFd,
@@ -293,74 +260,70 @@ 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<timespec>) -> Result<usize> {
-
- 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 = "ios",
+ 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<timespec>) -> Result<usize> {
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: intptr_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 : intptr_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::<libc::kevent>() == mem::size_of::<KEvent>());
}
diff --git a/src/sys/eventfd.rs b/src/sys/eventfd.rs
index e6e410ec..8058e207 100644
--- a/src/sys/eventfd.rs
+++ b/src/sys/eventfd.rs
@@ -4,9 +4,9 @@ use {Errno, Result};
libc_bitflags! {
flags EfdFlags: libc::c_int {
- const EFD_CLOEXEC, // Since Linux 2.6.27
- const EFD_NONBLOCK, // Since Linux 2.6.27
- const EFD_SEMAPHORE, // Since Linux 2.6.30
+ EFD_CLOEXEC, // Since Linux 2.6.27
+ EFD_NONBLOCK, // Since Linux 2.6.27
+ EFD_SEMAPHORE, // Since Linux 2.6.30
}
}
diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs
index 4d4d1072..a04e9d39 100644
--- a/src/sys/ioctl/mod.rs
+++ b/src/sys/ioctl/mod.rs
@@ -90,7 +90,7 @@
//! How do I get the magic numbers?
//! ===============================
//!
-//! For Linux, look at your system's headers. For example, `/usr/include/linxu/input.h` has a lot
+//! For Linux, look at your system's headers. For example, `/usr/include/linux/input.h` has a lot
//! of lines defining macros which use `_IOR`, `_IOW`, `_IOC`, and `_IORW`. These macros
//! correspond to the `ior!`, `iow!`, `ioc!`, and `iorw!` macros defined in this crate.
//! Additionally, there is the `ioctl!` macro for creating a wrapper around `ioctl` that is
@@ -104,33 +104,13 @@
#[macro_use]
mod platform;
-#[cfg(target_os = "macos")]
-#[path = "platform/macos.rs"]
-#[macro_use]
-mod platform;
-
-#[cfg(target_os = "ios")]
-#[path = "platform/ios.rs"]
-#[macro_use]
-mod platform;
-
-#[cfg(target_os = "freebsd")]
-#[path = "platform/freebsd.rs"]
-#[macro_use]
-mod platform;
-
-#[cfg(target_os = "netbsd")]
-#[path = "platform/netbsd.rs"]
-#[macro_use]
-mod platform;
-
-#[cfg(target_os = "openbsd")]
-#[path = "platform/openbsd.rs"]
-#[macro_use]
-mod platform;
-
-#[cfg(target_os = "dragonfly")]
-#[path = "platform/dragonfly.rs"]
+#[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+#[path = "platform/bsd.rs"]
#[macro_use]
mod platform;
@@ -145,3 +125,74 @@ extern "C" {
/// A hack to get the macros to work nicely.
#[doc(hidden)]
pub use ::libc as libc;
+
+/// Convert raw ioctl return value to a Nix result
+#[macro_export]
+macro_rules! convert_ioctl_res {
+ ($w:expr) => (
+ {
+ $crate::Errno::result($w)
+ }
+ );
+}
+
+#[macro_export]
+macro_rules! ioctl {
+ ($name:ident with $nr:expr) => (
+ pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
+ data: *mut u8)
+ -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
+ convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong, data))
+ }
+ );
+ (none $name:ident with $ioty:expr, $nr:expr) => (
+ pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int)
+ -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
+ convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::libc::c_ulong))
+ }
+ );
+ (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
+ pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
+ val: *mut $ty)
+ -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
+ convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val))
+ }
+ );
+ (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
+ pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
+ val: *const $ty)
+ -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
+ convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val))
+ }
+ );
+ (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
+ pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
+ val: *mut $ty)
+ -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
+ convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val))
+ }
+ );
+ (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
+ pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
+ val: *mut $ty,
+ len: usize)
+ -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
+ convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val))
+ }
+ );
+ (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
+ pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
+ val: *const $ty,
+ len: usize) -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
+ convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val))
+ }
+ );
+ (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
+ pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
+ val: *mut $ty,
+ len: usize)
+ -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
+ convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val))
+ }
+ );
+}
diff --git a/src/sys/ioctl/platform/bsd.rs b/src/sys/ioctl/platform/bsd.rs
new file mode 100644
index 00000000..57b4d637
--- /dev/null
+++ b/src/sys/ioctl/platform/bsd.rs
@@ -0,0 +1,36 @@
+mod consts {
+ pub const VOID: u32 = 0x20000000;
+ pub const OUT: u32 = 0x40000000;
+ pub const IN: u32 = 0x80000000;
+ pub const INOUT: u32 = (IN|OUT);
+ pub const IOCPARM_MASK: u32 = 0x1fff;
+}
+
+pub use self::consts::*;
+
+#[macro_export]
+macro_rules! ioc {
+ ($inout:expr, $group:expr, $num:expr, $len:expr) => (
+ $inout | (($len as u32 & $crate::sys::ioctl::IOCPARM_MASK) << 16) | (($group as u32) << 8) | ($num as u32)
+ )
+}
+
+#[macro_export]
+macro_rules! io {
+ ($g:expr, $n:expr) => (ioc!($crate::sys::ioctl::VOID, $g, $n, 0))
+}
+
+#[macro_export]
+macro_rules! ior {
+ ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::OUT, $g, $n, $len))
+}
+
+#[macro_export]
+macro_rules! iow {
+ ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::IN, $g, $n, $len))
+}
+
+#[macro_export]
+macro_rules! iorw {
+ ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::INOUT, $g, $n, $len))
+}
diff --git a/src/sys/ioctl/platform/dragonfly.rs b/src/sys/ioctl/platform/dragonfly.rs
deleted file mode 100644
index e69de29b..00000000
--- a/src/sys/ioctl/platform/dragonfly.rs
+++ /dev/null
diff --git a/src/sys/ioctl/platform/freebsd.rs b/src/sys/ioctl/platform/freebsd.rs
deleted file mode 100644
index e69de29b..00000000
--- a/src/sys/ioctl/platform/freebsd.rs
+++ /dev/null
diff --git a/src/sys/ioctl/platform/ios.rs b/src/sys/ioctl/platform/ios.rs
deleted file mode 100644
index e69de29b..00000000
--- a/src/sys/ioctl/platform/ios.rs
+++ /dev/null
diff --git a/src/sys/ioctl/platform/linux.rs b/src/sys/ioctl/platform/linux.rs
index 60311224..aacea459 100644
--- a/src/sys/ioctl/platform/linux.rs
+++ b/src/sys/ioctl/platform/linux.rs
@@ -77,78 +77,6 @@ macro_rules! iorw {
($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz))
}
-/// Convert raw ioctl return value to a Nix result
-#[macro_export]
-macro_rules! convert_ioctl_res {
- ($w:expr) => (
- {
- $crate::Errno::result($w)
- }
- );
-}
-
-/// Declare a wrapper function around an ioctl.
-#[macro_export]
-macro_rules! ioctl {
- (bad $name:ident with $nr:expr) => (
- pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
- data: *mut u8)
- -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
- convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong, data))
- }
- );
- (none $name:ident with $ioty:expr, $nr:expr) => (
- pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int)
- -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
- convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::libc::c_ulong))
- }
- );
- (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
- pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
- val: *mut $ty)
- -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
- convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val))
- }
- );
- (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
- pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
- val: *const $ty)
- -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
- convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val))
- }
- );
- (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
- pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
- val: *mut $ty)
- -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
- convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val))
- }
- );
- (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
- pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
- val: *mut $ty,
- len: usize)
- -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
- convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val))
- }
- );
- (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
- pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
- val: *const $ty,
- len: usize) -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
- convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val))
- }
- );
- (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
- pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
- val: *const $ty,
- len: usize)
- -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
- convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val))
- }
- );
-}
-
/// Extracts the "direction" (read/write/none) from an encoded ioctl command.
#[inline(always)]
pub fn ioc_dir(nr: u32) -> u8 {
diff --git a/src/sys/ioctl/platform/macos.rs b/src/sys/ioctl/platform/macos.rs
deleted file mode 100644
index e69de29b..00000000
--- a/src/sys/ioctl/platform/macos.rs
+++ /dev/null
diff --git a/src/sys/ioctl/platform/netbsd.rs b/src/sys/ioctl/platform/netbsd.rs
deleted file mode 100644
index e69de29b..00000000
--- a/src/sys/ioctl/platform/netbsd.rs
+++ /dev/null
diff --git a/src/sys/ioctl/platform/openbsd.rs b/src/sys/ioctl/platform/openbsd.rs
deleted file mode 100644
index e69de29b..00000000
--- a/src/sys/ioctl/platform/openbsd.rs
+++ /dev/null
diff --git a/src/sys/mod.rs b/src/sys/mod.rs
index 793bc70e..7675f944 100644
--- a/src/sys/mod.rs
+++ b/src/sys/mod.rs
@@ -1,3 +1,7 @@
+#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
+ target_os = "netbsd", target_os = "macos", target_os = "linux"))]
+pub mod aio;
+
#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod epoll;
@@ -12,7 +16,7 @@ pub mod eventfd;
#[cfg(target_os = "linux")]
pub mod memfd;
-#[cfg(not(any(target_os = "ios", target_os = "freebsd", target_os = "dragonfly")))]
+#[macro_use]
pub mod ioctl;
#[cfg(any(target_os = "linux", target_os = "android"))]
diff --git a/src/sys/select.rs b/src/sys/select.rs
index 28b664aa..1d9a76c1 100644
--- a/src/sys/select.rs
+++ b/src/sys/select.rs
@@ -1,6 +1,6 @@
use std::ptr::null_mut;
use std::os::unix::io::RawFd;
-use libc::c_int;
+use libc::{c_int, timeval};
use {Errno, Result};
use sys::time::TimeVal;
@@ -56,8 +56,7 @@ impl FdSet {
}
mod ffi {
- use libc::c_int;
- use sys::time::TimeVal;
+ use libc::{c_int, timeval};
use super::FdSet;
extern {
@@ -65,7 +64,7 @@ mod ffi {
readfds: *mut FdSet,
writefds: *mut FdSet,
errorfds: *mut FdSet,
- timeout: *mut TimeVal) -> c_int;
+ timeout: *mut timeval) -> c_int;
}
}
@@ -77,7 +76,8 @@ pub fn select(nfds: c_int,
let readfds = readfds.map(|set| set as *mut FdSet).unwrap_or(null_mut());
let writefds = writefds.map(|set| set as *mut FdSet).unwrap_or(null_mut());
let errorfds = errorfds.map(|set| set as *mut FdSet).unwrap_or(null_mut());
- let timeout = timeout.map(|tv| tv as *mut TimeVal).unwrap_or(null_mut());
+ let timeout = timeout.map(|tv| tv as *mut TimeVal as *mut timeval)
+ .unwrap_or(null_mut());
let res = unsafe {
ffi::select(nfds, readfds, writefds, errorfds, timeout)
diff --git a/src/sys/signal.rs b/src/sys/signal.rs
index bdc25b47..26cf51fd 100644
--- a/src/sys/signal.rs
+++ b/src/sys/signal.rs
@@ -4,6 +4,8 @@
use libc;
use {Errno, Error, Result};
use std::mem;
+#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+use std::os::unix::io::RawFd;
use std::ptr;
// Currently there is only one definition of c_int in libc, as well as only one
@@ -206,12 +208,12 @@ bitflags!{
}
}
-bitflags!{
- flags SigFlags: libc::c_int {
- const SIG_BLOCK = libc::SIG_BLOCK,
- const SIG_UNBLOCK = libc::SIG_UNBLOCK,
- const SIG_SETMASK = libc::SIG_SETMASK,
- }
+#[repr(i32)]
+#[derive(Clone, Copy, PartialEq)]
+pub enum SigmaskHow {
+ SIG_BLOCK = libc::SIG_BLOCK,
+ SIG_UNBLOCK = libc::SIG_UNBLOCK,
+ SIG_SETMASK = libc::SIG_SETMASK,
}
#[derive(Clone, Copy)]
@@ -268,27 +270,27 @@ impl SigSet {
/// Gets the currently blocked (masked) set of signals for the calling thread.
pub fn thread_get_mask() -> Result<SigSet> {
let mut oldmask: SigSet = unsafe { mem::uninitialized() };
- try!(pthread_sigmask(SigFlags::empty(), None, Some(&mut oldmask)));
+ try!(pthread_sigmask(SigmaskHow::SIG_SETMASK, None, Some(&mut oldmask)));
Ok(oldmask)
}
/// Sets the set of signals as the signal mask for the calling thread.
pub fn thread_set_mask(&self) -> Result<()> {
- pthread_sigmask(SIG_SETMASK, Some(self), None)
+ pthread_sigmask(SigmaskHow::SIG_SETMASK, Some(self), None)
}
/// Adds the set of signals to the signal mask for the calling thread.
pub fn thread_block(&self) -> Result<()> {
- pthread_sigmask(SIG_BLOCK, Some(self), None)
+ pthread_sigmask(SigmaskHow::SIG_BLOCK, Some(self), None)
}
/// Removes the set of signals from the signal mask for the calling thread.
pub fn thread_unblock(&self) -> Result<()> {
- pthread_sigmask(SIG_UNBLOCK, Some(self), None)
+ pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(self), None)
}
/// Sets the set of signals as the signal mask, and returns the old mask.
- pub fn thread_swap_mask(&self, how: SigFlags) -> Result<SigSet> {
+ pub fn thread_swap_mask(&self, how: SigmaskHow) -> Result<SigSet> {
let mut oldmask: SigSet = unsafe { mem::uninitialized() };
try!(pthread_sigmask(how, Some(self), Some(&mut oldmask)));
Ok(oldmask)
@@ -311,7 +313,6 @@ impl AsRef<libc::sigset_t> for SigSet {
}
#[allow(unknown_lints)]
-#[cfg_attr(not(raw_pointer_derive_allowed), allow(raw_pointer_derive))]
#[derive(Clone, Copy, PartialEq)]
pub enum SigHandler {
SigDfl,
@@ -369,7 +370,7 @@ pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigActi
///
/// For more information, visit the [pthread_sigmask](http://man7.org/linux/man-pages/man3/pthread_sigmask.3.html),
/// or [sigprocmask](http://man7.org/linux/man-pages/man2/sigprocmask.2.html) man pages.
-pub fn pthread_sigmask(how: SigFlags,
+pub fn pthread_sigmask(how: SigmaskHow,
set: Option<&SigSet>,
oldset: Option<&mut SigSet>) -> Result<()> {
if set.is_none() && oldset.is_none() {
@@ -378,7 +379,7 @@ pub fn pthread_sigmask(how: SigFlags,
let res = unsafe {
// if set or oldset is None, pass in null pointers instead
- libc::pthread_sigmask(how.bits(),
+ libc::pthread_sigmask(how as libc::c_int,
set.map_or_else(|| ptr::null::<libc::sigset_t>(),
|s| &s.sigset as *const libc::sigset_t),
oldset.map_or_else(|| ptr::null_mut::<libc::sigset_t>(),
@@ -388,8 +389,12 @@ pub fn pthread_sigmask(how: SigFlags,
Errno::result(res).map(drop)
}
-pub fn kill(pid: libc::pid_t, signal: Signal) -> Result<()> {
- let res = unsafe { libc::kill(pid, signal as libc::c_int) };
+pub fn kill<T: Into<Option<Signal>>>(pid: libc::pid_t, signal: T) -> Result<()> {
+ let res = unsafe { libc::kill(pid,
+ match signal.into() {
+ Some(s) => s as libc::c_int,
+ None => 0,
+ }) };
Errno::result(res).map(drop)
}
@@ -400,6 +405,107 @@ pub fn raise(signal: Signal) -> Result<()> {
Errno::result(res).map(drop)
}
+
+#[cfg(target_os = "freebsd")]
+pub type type_of_thread_id = libc::lwpid_t;
+#[cfg(target_os = "linux")]
+pub type type_of_thread_id = libc::pid_t;
+
+/// Used to request asynchronous notification of certain events, for example,
+/// with POSIX AIO, POSIX message queues, and POSIX timers.
+// sigval is actually a union of a int and a void*. But it's never really used
+// as a pointer, because neither libc nor the kernel ever dereference it. nix
+// therefore presents it as an intptr_t, which is how kevent uses it.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum SigevNotify {
+ /// No notification will be delivered
+ SigevNone,
+ /// The signal given by `signal` will be delivered to the process. The
+ /// value in `si_value` will be present in the `si_value` field of the
+ /// `siginfo_t` structure of the queued signal.
+ SigevSignal { signal: Signal, si_value: libc::intptr_t },
+ // Note: SIGEV_THREAD is not implemented because libc::sigevent does not
+ // expose a way to set the union members needed by SIGEV_THREAD.
+ /// A new `kevent` is posted to the kqueue `kq`. The `kevent`'s `udata`
+ /// field will contain the value in `udata`.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ SigevKevent { kq: RawFd, udata: libc::intptr_t },
+ /// The signal `signal` is queued to the thread whose LWP ID is given in
+ /// `thread_id`. The value stored in `si_value` will be present in the
+ /// `si_value` of the `siginfo_t` structure of the queued signal.
+ #[cfg(any(target_os = "freebsd", target_os = "linux"))]
+ SigevThreadId { signal: Signal, thread_id: type_of_thread_id,
+ 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
+}
+
+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::<libc::sigevent>()};
+ 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(any(target_os = "linux", target_os = "freebsd"))]
+ SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID
+ };
+ sev.sigev_signo = match sigev_notify {
+ SigevNotify::SigevSignal{ signal, .. } => signal as ::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 ::c_int,
+ _ => 0
+ };
+ sev.sigev_value.sival_ptr = match sigev_notify {
+ SigevNotify::SigevNone => ptr::null_mut::<libc::c_void>(),
+ SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut ::c_void,
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ SigevNotify::SigevKevent{ udata, .. } => udata as *mut ::c_void,
+ #[cfg(any(target_os = "linux", target_os = "freebsd"))]
+ SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut ::c_void,
+ };
+ SigEvent::set_tid(&mut sev, &sigev_notify);
+ SigEvent{sigevent: sev}
+ }
+
+ #[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) {
+ }
+
+ pub fn sigevent(&self) -> libc::sigevent {
+ self.sigevent
+ }
+}
+
+
#[cfg(test)]
mod tests {
use super::*;
@@ -439,12 +545,46 @@ mod tests {
assert!(two_signals.contains(SIGUSR2));
}
+ // This test doesn't actually test get_mask functionality, see the set_mask test for that.
+ #[test]
+ fn test_thread_signal_get_mask() {
+ assert!(SigSet::thread_get_mask().is_ok());
+ }
+
+ #[test]
+ fn test_thread_signal_set_mask() {
+ let prev_mask = SigSet::thread_get_mask().expect("Failed to get existing signal mask!");
+
+ let mut test_mask = prev_mask;
+ test_mask.add(SIGUSR1);
+
+ assert!(test_mask.thread_set_mask().is_ok());
+ let new_mask = SigSet::thread_get_mask().expect("Failed to get new mask!");
+
+ assert!(new_mask.contains(SIGUSR1));
+ assert!(!new_mask.contains(SIGUSR2));
+
+ prev_mask.thread_set_mask().expect("Failed to revert signal mask!");
+ }
+
#[test]
fn test_thread_signal_block() {
let mut mask = SigSet::empty();
mask.add(SIGUSR1);
assert!(mask.thread_block().is_ok());
+
+ assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
+ }
+
+ #[test]
+ fn test_thread_signal_unblock() {
+ let mut mask = SigSet::empty();
+ mask.add(SIGUSR1);
+
+ assert!(mask.thread_unblock().is_ok());
+
+ assert!(!SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
}
#[test]
@@ -455,13 +595,15 @@ mod tests {
assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
- let mask2 = SigSet::empty();
- mask.add(SIGUSR2);
+ let mut mask2 = SigSet::empty();
+ mask2.add(SIGUSR2);
- let oldmask = mask2.thread_swap_mask(SIG_SETMASK).unwrap();
+ let oldmask = mask2.thread_swap_mask(SigmaskHow::SIG_SETMASK).unwrap();
assert!(oldmask.contains(SIGUSR1));
assert!(!oldmask.contains(SIGUSR2));
+
+ assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR2));
}
// TODO(#251): Re-enable after figuring out flakiness.
diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs
index e3c1401c..5f8b130a 100644
--- a/src/sys/socket/addr.rs
+++ b/src/sys/socket/addr.rs
@@ -7,6 +7,10 @@ use std::path::Path;
use std::os::unix::ffi::OsStrExt;
#[cfg(any(target_os = "linux", target_os = "android"))]
use ::sys::socket::addr::netlink::NetlinkAddr;
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+use std::os::unix::io::RawFd;
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+use ::sys::socket::addr::sys_control::SysControlAddr;
// TODO: uncomment out IpAddr functions: rust-lang/rfcs#988
@@ -26,6 +30,8 @@ pub enum AddressFamily {
Netlink = consts::AF_NETLINK,
#[cfg(any(target_os = "linux", target_os = "android"))]
Packet = consts::AF_PACKET,
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ System = consts::AF_SYSTEM,
}
#[derive(Copy)]
@@ -348,10 +354,12 @@ impl fmt::Display for Ipv6Addr {
*
*/
-/// A wrapper around `sockaddr_un`. We track the length of `sun_path`,
-/// because it may not be null-terminated (unconnected and abstract
-/// sockets). Note that the actual sockaddr length is greater by
-/// `size_of::<sa_family_t>()`.
+/// A wrapper around `sockaddr_un`. We track the length of `sun_path` (excluding
+/// a terminating null), because it may not be null-terminated. For example,
+/// unconnected and Linux abstract sockets are never null-terminated, and POSIX
+/// does not require that `sun_len` include the terminating null even for normal
+/// sockets. Note that the actual sockaddr length is greater by
+/// `offset_of!(libc::sockaddr_un, sun_path)`
#[derive(Copy)]
pub struct UnixAddr(pub libc::sockaddr_un, pub usize);
@@ -365,7 +373,7 @@ impl UnixAddr {
.. mem::zeroed()
};
- let bytes = cstr.to_bytes_with_nul();
+ let bytes = cstr.to_bytes();
if bytes.len() > ret.sun_path.len() {
return Err(Error::Sys(Errno::ENAMETOOLONG));
@@ -416,7 +424,13 @@ impl UnixAddr {
None
} else {
let p = self.sun_path();
- Some(Path::new(<OsStr as OsStrExt>::from_bytes(&p[..p.len()-1])))
+ // POSIX only requires that `sun_len` be at least long enough to
+ // contain the pathname, and it need not be null-terminated. So we
+ // need to create a string that is the shorter of the
+ // null-terminated length or the full length.
+ let ptr = &self.0.sun_path as *const libc::c_char;
+ let reallen = unsafe { libc::strnlen(ptr, p.len()) };
+ Some(Path::new(<OsStr as OsStrExt>::from_bytes(&p[..reallen])))
}
}
}
@@ -467,7 +481,9 @@ pub enum SockAddr {
Inet(InetAddr),
Unix(UnixAddr),
#[cfg(any(target_os = "linux", target_os = "android"))]
- Netlink(NetlinkAddr)
+ Netlink(NetlinkAddr),
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ SysControl(SysControlAddr),
}
impl SockAddr {
@@ -484,6 +500,11 @@ impl SockAddr {
SockAddr::Netlink(NetlinkAddr::new(pid, groups))
}
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ pub fn new_sys_control(sockfd: RawFd, name: &str, unit: u32) -> Result<SockAddr> {
+ SysControlAddr::from_name(sockfd, name, unit).map(|a| SockAddr::SysControl(a))
+ }
+
pub fn family(&self) -> AddressFamily {
match *self {
SockAddr::Inet(InetAddr::V4(..)) => AddressFamily::Inet,
@@ -491,6 +512,8 @@ impl SockAddr {
SockAddr::Unix(..) => AddressFamily::Unix,
#[cfg(any(target_os = "linux", target_os = "android"))]
SockAddr::Netlink(..) => AddressFamily::Netlink,
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ SockAddr::SysControl(..) => AddressFamily::System,
}
}
@@ -502,9 +525,11 @@ impl SockAddr {
match *self {
SockAddr::Inet(InetAddr::V4(ref addr)) => (mem::transmute(addr), mem::size_of::<libc::sockaddr_in>() as libc::socklen_t),
SockAddr::Inet(InetAddr::V6(ref addr)) => (mem::transmute(addr), mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t),
- SockAddr::Unix(UnixAddr(ref addr, len)) => (mem::transmute(addr), (len + mem::size_of::<libc::sa_family_t>()) as libc::socklen_t),
+ SockAddr::Unix(UnixAddr(ref addr, len)) => (mem::transmute(addr), (len + offset_of!(libc::sockaddr_un, sun_path)) as libc::socklen_t),
#[cfg(any(target_os = "linux", target_os = "android"))]
SockAddr::Netlink(NetlinkAddr(ref sa)) => (mem::transmute(sa), mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t),
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ SockAddr::SysControl(SysControlAddr(ref sa)) => (mem::transmute(sa), mem::size_of::<sys_control::sockaddr_ctl>() as libc::socklen_t),
}
}
}
@@ -537,6 +562,8 @@ impl hash::Hash for SockAddr {
SockAddr::Unix(ref a) => a.hash(s),
#[cfg(any(target_os = "linux", target_os = "android"))]
SockAddr::Netlink(ref a) => a.hash(s),
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ SockAddr::SysControl(ref a) => a.hash(s),
}
}
}
@@ -554,6 +581,8 @@ impl fmt::Display for SockAddr {
SockAddr::Unix(ref unix) => unix.fmt(f),
#[cfg(any(target_os = "linux", target_os = "android"))]
SockAddr::Netlink(ref nl) => nl.fmt(f),
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ SockAddr::SysControl(ref sc) => sc.fmt(f),
}
}
}
@@ -612,3 +641,102 @@ pub mod netlink {
}
}
}
+
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+pub mod sys_control {
+ use ::sys::socket::consts;
+ use ::sys::socket::addr::{AddressFamily};
+ use libc::{c_uchar, uint16_t, uint32_t};
+ use std::{fmt, mem};
+ use std::hash::{Hash, Hasher};
+ use std::os::unix::io::RawFd;
+ use {Errno, Error, Result};
+
+ #[repr(C)]
+ pub struct ctl_ioc_info {
+ pub ctl_id: uint32_t,
+ pub ctl_name: [c_uchar; MAX_KCTL_NAME],
+ }
+
+ const CTL_IOC_MAGIC: u8 = 'N' as u8;
+ const CTL_IOC_INFO: u8 = 3;
+ const MAX_KCTL_NAME: usize = 96;
+
+ ioctl!(readwrite ctl_info with CTL_IOC_MAGIC, CTL_IOC_INFO; ctl_ioc_info);
+
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ pub struct sockaddr_ctl {
+ pub sc_len: c_uchar,
+ pub sc_family: c_uchar,
+ pub ss_sysaddr: uint16_t,
+ pub sc_id: uint32_t,
+ pub sc_unit: uint32_t,
+ pub sc_reserved: [uint32_t; 5],
+ }
+
+ #[derive(Copy, Clone)]
+ pub struct SysControlAddr(pub sockaddr_ctl);
+
+ // , PartialEq, Eq, Debug, Hash
+ impl PartialEq for SysControlAddr {
+ fn eq(&self, other: &Self) -> bool {
+ let (inner, other) = (self.0, other.0);
+ (inner.sc_id, inner.sc_unit) ==
+ (other.sc_id, other.sc_unit)
+ }
+ }
+
+ impl Eq for SysControlAddr {}
+
+ impl Hash for SysControlAddr {
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ let inner = self.0;
+ (inner.sc_id, inner.sc_unit).hash(s);
+ }
+ }
+
+
+ impl SysControlAddr {
+ pub fn new(id: u32, unit: u32) -> SysControlAddr {
+ let addr = sockaddr_ctl {
+ sc_len: mem::size_of::<sockaddr_ctl>() as c_uchar,
+ sc_family: AddressFamily::System as c_uchar,
+ ss_sysaddr: consts::AF_SYS_CONTROL as uint16_t,
+ sc_id: id,
+ sc_unit: unit,
+ sc_reserved: [0; 5]
+ };
+
+ SysControlAddr(addr)
+ }
+
+ pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result<SysControlAddr> {
+ if name.len() > MAX_KCTL_NAME {
+ return Err(Error::Sys(Errno::ENAMETOOLONG));
+ }
+
+ let mut ctl_name = [0; MAX_KCTL_NAME];
+ ctl_name[..name.len()].clone_from_slice(name.as_bytes());
+ let mut info = ctl_ioc_info { ctl_id: 0, ctl_name: ctl_name };
+
+ unsafe { try!(ctl_info(sockfd, &mut info)); }
+
+ Ok(SysControlAddr::new(info.ctl_id, unit))
+ }
+
+ pub fn id(&self) -> u32 {
+ self.0.sc_id
+ }
+
+ pub fn unit(&self) -> u32 {
+ self.0.sc_unit
+ }
+ }
+
+ impl fmt::Display for SysControlAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "id: {} unit: {}", self.id(), self.unit())
+ }
+ }
+}
diff --git a/src/sys/socket/consts.rs b/src/sys/socket/consts.rs
index 63eaf28a..3c5efdf7 100644
--- a/src/sys/socket/consts.rs
+++ b/src/sys/socket/consts.rs
@@ -132,6 +132,11 @@ mod os {
pub const AF_INET6: c_int = 28;
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub const AF_INET6: c_int = 30;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ pub const AF_SYSTEM: c_int = 32;
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ pub const AF_SYS_CONTROL: c_int = 2;
pub const SOCK_STREAM: c_int = 1;
pub const SOCK_DGRAM: c_int = 2;
@@ -144,6 +149,8 @@ mod os {
pub const IPPROTO_IPV6: c_int = 41;
pub const IPPROTO_TCP: c_int = 6;
pub const IPPROTO_UDP: c_int = 17;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ pub const SYSPROTO_CONTROL: c_int = 2;
pub const SO_ACCEPTCONN: c_int = 0x0002;
pub const SO_BROADCAST: c_int = 0x0020;
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs
index 69f26aa0..645dfe41 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -50,17 +50,8 @@ pub use self::multicast::{
};
pub use self::consts::*;
-#[cfg(any(not(target_os = "linux"), not(target_arch = "x86")))]
pub use libc::sockaddr_storage;
-// Working around rust-lang/rust#23425
-#[cfg(all(target_os = "linux", target_arch = "x86"))]
-pub struct sockaddr_storage {
- pub ss_family: sa_family_t,
- pub __ss_align: u32,
- pub __ss_pad2: [u8; 120],
-}
-
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(i32)]
pub enum SockType {
@@ -238,8 +229,13 @@ impl<'a> ControlMessage<'a> {
let padlen = cmsg_align(mem::size_of_val(&cmsg)) -
mem::size_of_val(&cmsg);
- let buf2 = &mut &mut buf[padlen..];
- copy_bytes(fds, buf2);
+
+ let mut tmpbuf = &mut [][..];
+ mem::swap(&mut tmpbuf, buf);
+ let (_padding, mut remainder) = tmpbuf.split_at_mut(padlen);
+ mem::swap(buf, &mut remainder);
+
+ copy_bytes(fds, buf);
},
ControlMessage::Unknown(UnknownCmsg(orig_cmsg, bytes)) => {
copy_bytes(orig_cmsg, buf);
@@ -608,6 +604,12 @@ pub fn getsockname(fd: RawFd) -> Result<SockAddr> {
}
}
+/// Return the appropriate SockAddr type from a `sockaddr_storage` of a certain
+/// size. In C this would usually be done by casting. The `len` argument
+/// should be the number of bytes in the sockaddr_storage that are actually
+/// allocated and valid. It must be at least as large as all the useful parts
+/// of the structure. Note that in the case of a `sockaddr_un`, `len` need not
+/// include the terminating null.
pub unsafe fn sockaddr_storage_to_addr(
addr: &sockaddr_storage,
len: usize) -> Result<SockAddr> {
@@ -627,7 +629,9 @@ pub unsafe fn sockaddr_storage_to_addr(
Ok(SockAddr::Inet(InetAddr::V6((*(addr as *const _ as *const sockaddr_in6)))))
}
consts::AF_UNIX => {
- Ok(SockAddr::Unix(UnixAddr(*(addr as *const _ as *const sockaddr_un), len)))
+ let sun = *(addr as *const _ as *const sockaddr_un);
+ let pathlen = len - offset_of!(sockaddr_un, sun_path);
+ Ok(SockAddr::Unix(UnixAddr(sun, pathlen)))
}
#[cfg(any(target_os = "linux", target_os = "android"))]
consts::AF_NETLINK => {
diff --git a/src/sys/time.rs b/src/sys/time.rs
index 1750481c..0d977045 100644
--- a/src/sys/time.rs
+++ b/src/sys/time.rs
@@ -1,55 +1,328 @@
-use std::{fmt, ops};
-use libc::{time_t, suseconds_t};
+use std::{cmp, fmt, ops};
+use libc::{c_long, time_t, suseconds_t, timespec, timeval};
-#[repr(C)]
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
-pub struct TimeVal {
- pub tv_sec: time_t,
- pub tv_usec: suseconds_t,
+pub trait TimeValLike: Sized {
+ #[inline]
+ fn zero() -> Self {
+ Self::seconds(0)
+ }
+
+ #[inline]
+ fn hours(hours: i64) -> Self {
+ let secs = hours.checked_mul(SECS_PER_HOUR)
+ .expect("TimeValLike::hours ouf of bounds");
+ Self::seconds(secs)
+ }
+
+ #[inline]
+ fn minutes(minutes: i64) -> Self {
+ let secs = minutes.checked_mul(SECS_PER_MINUTE)
+ .expect("TimeValLike::minutes out of bounds");
+ Self::seconds(secs)
+ }
+
+ fn seconds(seconds: i64) -> Self;
+ fn milliseconds(milliseconds: i64) -> Self;
+ fn microseconds(microseconds: i64) -> Self;
+ fn nanoseconds(nanoseconds: i64) -> Self;
+
+ #[inline]
+ fn num_hours(&self) -> i64 {
+ self.num_seconds() / 3600
+ }
+
+ #[inline]
+ fn num_minutes(&self) -> i64 {
+ self.num_seconds() / 60
+ }
+
+ fn num_seconds(&self) -> i64;
+ fn num_milliseconds(&self) -> i64;
+ fn num_microseconds(&self) -> i64;
+ fn num_nanoseconds(&self) -> i64;
}
-const MICROS_PER_SEC: i64 = 1_000_000;
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct TimeSpec(timespec);
+
+const NANOS_PER_SEC: i64 = 1_000_000_000;
const SECS_PER_MINUTE: i64 = 60;
const SECS_PER_HOUR: i64 = 3600;
#[cfg(target_pointer_width = "64")]
-const MAX_SECONDS: i64 = (::std::i64::MAX / MICROS_PER_SEC) - 1;
+const TS_MAX_SECONDS: i64 = (::std::i64::MAX / NANOS_PER_SEC) - 1;
#[cfg(target_pointer_width = "32")]
-const MAX_SECONDS: i64 = ::std::isize::MAX as i64;
+const TS_MAX_SECONDS: i64 = ::std::isize::MAX as i64;
-const MIN_SECONDS: i64 = -MAX_SECONDS;
+const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS;
-impl TimeVal {
+
+impl AsRef<timespec> for TimeSpec {
+ fn as_ref(&self) -> &timespec {
+ &self.0
+ }
+}
+
+impl fmt::Debug for TimeSpec {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.debug_struct("TimeSpec")
+ .field("tv_sec", &self.tv_sec())
+ .field("tv_nsec", &self.tv_nsec())
+ .finish()
+ }
+}
+
+impl cmp::PartialEq for TimeSpec {
+ // The implementation of cmp is simplified by assuming that the struct is
+ // normalized. That is, tv_nsec must always be within [0, 1_000_000_000)
+ fn eq(&self, other: &TimeSpec) -> bool {
+ self.tv_sec() == other.tv_sec() && self.tv_nsec() == other.tv_nsec()
+ }
+}
+
+impl cmp::Eq for TimeSpec {}
+
+impl cmp::Ord for TimeSpec {
+ // The implementation of cmp is simplified by assuming that the struct is
+ // normalized. That is, tv_nsec must always be within [0, 1_000_000_000)
+ fn cmp(&self, other: &TimeSpec) -> cmp::Ordering {
+ if self.tv_sec() == other.tv_sec() {
+ self.tv_nsec().cmp(&other.tv_nsec())
+ } else {
+ self.tv_sec().cmp(&other.tv_sec())
+ }
+ }
+}
+
+impl cmp::PartialOrd for TimeSpec {
+ fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl TimeValLike for TimeSpec {
#[inline]
- pub fn zero() -> TimeVal {
- TimeVal::microseconds(0)
+ fn seconds(seconds: i64) -> TimeSpec {
+ assert!(seconds >= TS_MIN_SECONDS && seconds <= TS_MAX_SECONDS,
+ "TimeSpec out of bounds; seconds={}", seconds);
+ TimeSpec(timespec {tv_sec: seconds as time_t, tv_nsec: 0 })
}
#[inline]
- pub fn hours(hours: i64) -> TimeVal {
- let secs = hours.checked_mul(SECS_PER_HOUR)
- .expect("TimeVal::hours ouf of bounds");
+ fn milliseconds(milliseconds: i64) -> TimeSpec {
+ let nanoseconds = milliseconds.checked_mul(1_000_000)
+ .expect("TimeSpec::milliseconds out of bounds");
- TimeVal::seconds(secs)
+ TimeSpec::nanoseconds(nanoseconds)
}
+ /// Makes a new `TimeSpec` with given number of microseconds.
#[inline]
- pub fn minutes(minutes: i64) -> TimeVal {
- let secs = minutes.checked_mul(SECS_PER_MINUTE)
- .expect("TimeVal::minutes out of bounds");
+ fn microseconds(microseconds: i64) -> TimeSpec {
+ let nanoseconds = microseconds.checked_mul(1_000)
+ .expect("TimeSpec::milliseconds out of bounds");
+
+ TimeSpec::nanoseconds(nanoseconds)
+ }
+
+ /// Makes a new `TimeSpec` with given number of nanoseconds.
+ #[inline]
+ fn nanoseconds(nanoseconds: i64) -> TimeSpec {
+ let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
+ assert!(secs >= TS_MIN_SECONDS && secs <= TS_MAX_SECONDS,
+ "TimeSpec out of bounds");
+ TimeSpec(timespec {tv_sec: secs as time_t,
+ tv_nsec: nanos as c_long })
+ }
+
+ fn num_seconds(&self) -> i64 {
+ if self.tv_sec() < 0 && self.tv_nsec() > 0 {
+ (self.tv_sec() + 1) as i64
+ } else {
+ self.tv_sec() as i64
+ }
+ }
+
+ fn num_milliseconds(&self) -> i64 {
+ self.num_nanoseconds() / 1_000_000
+ }
+
+ fn num_microseconds(&self) -> i64 {
+ self.num_nanoseconds() / 1_000_000_000
+ }
+
+ fn num_nanoseconds(&self) -> i64 {
+ let secs = self.num_seconds() * 1_000_000_000;
+ let nsec = self.nanos_mod_sec();
+ secs + nsec as i64
+ }
+}
+
+impl TimeSpec {
+ fn nanos_mod_sec(&self) -> c_long {
+ if self.tv_sec() < 0 && self.tv_nsec() > 0 {
+ self.tv_nsec() - NANOS_PER_SEC as c_long
+ } else {
+ self.tv_nsec()
+ }
+ }
+
+ pub fn tv_sec(&self) -> time_t {
+ self.0.tv_sec
+ }
+
+ pub fn tv_nsec(&self) -> c_long {
+ self.0.tv_nsec
+ }
+}
+
+impl ops::Neg for TimeSpec {
+ type Output = TimeSpec;
+
+ fn neg(self) -> TimeSpec {
+ TimeSpec::nanoseconds(-self.num_nanoseconds())
+ }
+}
+
+impl ops::Add for TimeSpec {
+ type Output = TimeSpec;
+
+ fn add(self, rhs: TimeSpec) -> TimeSpec {
+ TimeSpec::nanoseconds(
+ self.num_nanoseconds() + rhs.num_nanoseconds())
+ }
+}
+
+impl ops::Sub for TimeSpec {
+ type Output = TimeSpec;
+
+ fn sub(self, rhs: TimeSpec) -> TimeSpec {
+ TimeSpec::nanoseconds(
+ self.num_nanoseconds() - rhs.num_nanoseconds())
+ }
+}
+
+impl ops::Mul<i32> for TimeSpec {
+ type Output = TimeSpec;
+
+ fn mul(self, rhs: i32) -> TimeSpec {
+ let usec = self.num_nanoseconds().checked_mul(rhs as i64)
+ .expect("TimeSpec multiply out of bounds");
- TimeVal::seconds(secs)
+ TimeSpec::nanoseconds(usec)
}
+}
+
+impl ops::Div<i32> for TimeSpec {
+ type Output = TimeSpec;
+
+ fn div(self, rhs: i32) -> TimeSpec {
+ let usec = self.num_nanoseconds() / rhs as i64;
+ TimeSpec::nanoseconds(usec)
+ }
+}
+
+impl fmt::Display for TimeSpec {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let (abs, sign) = if self.tv_sec() < 0 {
+ (-*self, "-")
+ } else {
+ (*self, "")
+ };
+ let sec = abs.tv_sec();
+
+ try!(write!(f, "{}", sign));
+
+ if abs.tv_nsec() == 0 {
+ if abs.tv_sec() == 1 {
+ try!(write!(f, "{} second", sec));
+ } else {
+ try!(write!(f, "{} seconds", sec));
+ }
+ } else if abs.tv_nsec() % 1_000_000 == 0 {
+ try!(write!(f, "{}.{:03} seconds", sec, abs.tv_nsec() / 1_000_000));
+ } else if abs.tv_nsec() % 1_000 == 0 {
+ try!(write!(f, "{}.{:06} seconds", sec, abs.tv_nsec() / 1_000));
+ } else {
+ try!(write!(f, "{}.{:09} seconds", sec, abs.tv_nsec()));
+ }
+
+ Ok(())
+ }
+}
+
+
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct TimeVal(timeval);
+
+const MICROS_PER_SEC: i64 = 1_000_000;
+
+#[cfg(target_pointer_width = "64")]
+const TV_MAX_SECONDS: i64 = (::std::i64::MAX / MICROS_PER_SEC) - 1;
+
+#[cfg(target_pointer_width = "32")]
+const TV_MAX_SECONDS: i64 = ::std::isize::MAX as i64;
+
+const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS;
+
+impl AsRef<timeval> for TimeVal {
+ fn as_ref(&self) -> &timeval {
+ &self.0
+ }
+}
+
+impl fmt::Debug for TimeVal {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.debug_struct("TimeVal")
+ .field("tv_sec", &self.tv_sec())
+ .field("tv_usec", &self.tv_usec())
+ .finish()
+ }
+}
+
+impl cmp::PartialEq for TimeVal {
+ // The implementation of cmp is simplified by assuming that the struct is
+ // normalized. That is, tv_usec must always be within [0, 1_000_000)
+ fn eq(&self, other: &TimeVal) -> bool {
+ self.tv_sec() == other.tv_sec() && self.tv_usec() == other.tv_usec()
+ }
+}
+
+impl cmp::Eq for TimeVal {}
+
+impl cmp::Ord for TimeVal {
+ // The implementation of cmp is simplified by assuming that the struct is
+ // normalized. That is, tv_usec must always be within [0, 1_000_000)
+ fn cmp(&self, other: &TimeVal) -> cmp::Ordering {
+ if self.tv_sec() == other.tv_sec() {
+ self.tv_usec().cmp(&other.tv_usec())
+ } else {
+ self.tv_sec().cmp(&other.tv_sec())
+ }
+ }
+}
+
+impl cmp::PartialOrd for TimeVal {
+ fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl TimeValLike for TimeVal {
#[inline]
- pub fn seconds(seconds: i64) -> TimeVal {
- assert!(seconds >= MIN_SECONDS && seconds <= MAX_SECONDS, "TimeVal out of bounds; seconds={}", seconds);
- TimeVal { tv_sec: seconds as time_t, tv_usec: 0 }
+ fn seconds(seconds: i64) -> TimeVal {
+ assert!(seconds >= TV_MIN_SECONDS && seconds <= TV_MAX_SECONDS,
+ "TimeVal out of bounds; seconds={}", seconds);
+ TimeVal(timeval {tv_sec: seconds as time_t, tv_usec: 0 })
}
#[inline]
- pub fn milliseconds(milliseconds: i64) -> TimeVal {
+ fn milliseconds(milliseconds: i64) -> TimeVal {
let microseconds = milliseconds.checked_mul(1_000)
.expect("TimeVal::milliseconds out of bounds");
@@ -58,45 +331,65 @@ impl TimeVal {
/// Makes a new `TimeVal` with given number of microseconds.
#[inline]
- pub fn microseconds(microseconds: i64) -> TimeVal {
+ fn microseconds(microseconds: i64) -> TimeVal {
let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
- assert!(secs >= MIN_SECONDS && secs <= MAX_SECONDS, "TimeVal out of bounds");
- TimeVal { tv_sec: secs as time_t, tv_usec: micros as suseconds_t }
+ assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS,
+ "TimeVal out of bounds");
+ TimeVal(timeval {tv_sec: secs as time_t,
+ tv_usec: micros as suseconds_t })
}
- pub fn num_hours(&self) -> i64 {
- self.num_seconds() / 3600
- }
-
- pub fn num_minutes(&self) -> i64 {
- self.num_seconds() / 60
+ /// Makes a new `TimeVal` with given number of nanoseconds. Some precision
+ /// will be lost
+ #[inline]
+ fn nanoseconds(nanoseconds: i64) -> TimeVal {
+ let microseconds = nanoseconds / 1000;
+ let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
+ assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS,
+ "TimeVal out of bounds");
+ TimeVal(timeval {tv_sec: secs as time_t,
+ tv_usec: micros as suseconds_t })
}
- pub fn num_seconds(&self) -> i64 {
- if self.tv_sec < 0 && self.tv_usec > 0 {
- (self.tv_sec + 1) as i64
+ fn num_seconds(&self) -> i64 {
+ if self.tv_sec() < 0 && self.tv_usec() > 0 {
+ (self.tv_sec() + 1) as i64
} else {
- self.tv_sec as i64
+ self.tv_sec() as i64
}
}
- pub fn num_milliseconds(&self) -> i64 {
+ fn num_milliseconds(&self) -> i64 {
self.num_microseconds() / 1_000
}
- pub fn num_microseconds(&self) -> i64 {
+ fn num_microseconds(&self) -> i64 {
let secs = self.num_seconds() * 1_000_000;
let usec = self.micros_mod_sec();
secs + usec as i64
}
+ fn num_nanoseconds(&self) -> i64 {
+ self.num_microseconds() * 1_000
+ }
+}
+
+impl TimeVal {
fn micros_mod_sec(&self) -> suseconds_t {
- if self.tv_sec < 0 && self.tv_usec > 0 {
- self.tv_usec - MICROS_PER_SEC as suseconds_t
+ if self.tv_sec() < 0 && self.tv_usec() > 0 {
+ self.tv_usec() - MICROS_PER_SEC as suseconds_t
} else {
- self.tv_usec
+ self.tv_usec()
}
}
+
+ pub fn tv_sec(&self) -> time_t {
+ self.0.tv_sec
+ }
+
+ pub fn tv_usec(&self) -> suseconds_t {
+ self.0.tv_usec
+ }
}
impl ops::Neg for TimeVal {
@@ -147,26 +440,26 @@ impl ops::Div<i32> for TimeVal {
impl fmt::Display for TimeVal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let (abs, sign) = if self.tv_sec < 0 {
+ let (abs, sign) = if self.tv_sec() < 0 {
(-*self, "-")
} else {
(*self, "")
};
- let sec = abs.tv_sec;
+ let sec = abs.tv_sec();
try!(write!(f, "{}", sign));
- if abs.tv_usec == 0 {
- if abs.tv_sec == 1 {
+ if abs.tv_usec() == 0 {
+ if abs.tv_sec() == 1 {
try!(write!(f, "{} second", sec));
} else {
try!(write!(f, "{} seconds", sec));
}
- } else if abs.tv_usec % 1000 == 0 {
- try!(write!(f, "{}.{:03} seconds", sec, abs.tv_usec / 1000));
+ } else if abs.tv_usec() % 1000 == 0 {
+ try!(write!(f, "{}.{:03} seconds", sec, abs.tv_usec() / 1000));
} else {
- try!(write!(f, "{}.{:06} seconds", sec, abs.tv_usec));
+ try!(write!(f, "{}.{:06} seconds", sec, abs.tv_usec()));
}
Ok(())
@@ -203,18 +496,64 @@ fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
#[cfg(test)]
mod test {
- use super::TimeVal;
+ use super::{TimeSpec, TimeVal, TimeValLike};
#[test]
- pub fn test_time_val() {
+ pub fn test_timespec() {
+ assert!(TimeSpec::seconds(1) != TimeSpec::zero());
+ assert_eq!(TimeSpec::seconds(1) + TimeSpec::seconds(2),
+ TimeSpec::seconds(3));
+ assert_eq!(TimeSpec::minutes(3) + TimeSpec::seconds(2),
+ TimeSpec::seconds(182));
+ }
+
+ #[test]
+ pub fn test_timespec_neg() {
+ let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123);
+ let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123);
+
+ assert_eq!(a, -b);
+ }
+
+ #[test]
+ pub fn test_timespec_ord() {
+ assert!(TimeSpec::seconds(1) == TimeSpec::nanoseconds(1_000_000_000));
+ assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001));
+ assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999));
+ assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999));
+ assert!(TimeSpec::seconds(-1) > TimeSpec::nanoseconds(-1_000_000_001));
+ }
+
+ #[test]
+ pub fn test_timespec_fmt() {
+ assert_eq!(TimeSpec::zero().to_string(), "0 seconds");
+ assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds");
+ assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds");
+ assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds");
+ assert_eq!(TimeSpec::nanoseconds(42).to_string(), "0.000000042 seconds");
+ assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds");
+ }
+
+ #[test]
+ pub fn test_timeval() {
assert!(TimeVal::seconds(1) != TimeVal::zero());
- assert_eq!(TimeVal::seconds(1) + TimeVal::seconds(2), TimeVal::seconds(3));
+ assert_eq!(TimeVal::seconds(1) + TimeVal::seconds(2),
+ TimeVal::seconds(3));
assert_eq!(TimeVal::minutes(3) + TimeVal::seconds(2),
TimeVal::seconds(182));
}
#[test]
- pub fn test_time_val_neg() {
+ pub fn test_timeval_ord() {
+ assert!(TimeVal::seconds(1) == TimeVal::microseconds(1_000_000));
+ assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001));
+ assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999));
+ assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999));
+ assert!(TimeVal::seconds(-1) > TimeVal::microseconds(-1_000_001));
+ }
+
+ #[test]
+ pub fn test_timeval_neg() {
let a = TimeVal::seconds(1) + TimeVal::microseconds(123);
let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123);
@@ -222,11 +561,12 @@ mod test {
}
#[test]
- pub fn test_time_val_fmt() {
+ pub fn test_timeval_fmt() {
assert_eq!(TimeVal::zero().to_string(), "0 seconds");
assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds");
assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds");
assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds");
+ assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds");
assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds");
}
}