diff options
Diffstat (limited to 'src/sys')
-rw-r--r-- | src/sys/aio.rs | 249 | ||||
-rw-r--r-- | src/sys/epoll.rs | 35 | ||||
-rw-r--r-- | src/sys/event.rs | 541 | ||||
-rw-r--r-- | src/sys/eventfd.rs | 6 | ||||
-rw-r--r-- | src/sys/ioctl/mod.rs | 107 | ||||
-rw-r--r-- | src/sys/ioctl/platform/bsd.rs | 36 | ||||
-rw-r--r-- | src/sys/ioctl/platform/dragonfly.rs | 0 | ||||
-rw-r--r-- | src/sys/ioctl/platform/freebsd.rs | 0 | ||||
-rw-r--r-- | src/sys/ioctl/platform/ios.rs | 0 | ||||
-rw-r--r-- | src/sys/ioctl/platform/linux.rs | 72 | ||||
-rw-r--r-- | src/sys/ioctl/platform/macos.rs | 0 | ||||
-rw-r--r-- | src/sys/ioctl/platform/netbsd.rs | 0 | ||||
-rw-r--r-- | src/sys/ioctl/platform/openbsd.rs | 0 | ||||
-rw-r--r-- | src/sys/mod.rs | 6 | ||||
-rw-r--r-- | src/sys/select.rs | 10 | ||||
-rw-r--r-- | src/sys/signal.rs | 180 | ||||
-rw-r--r-- | src/sys/socket/addr.rs | 144 | ||||
-rw-r--r-- | src/sys/socket/consts.rs | 7 | ||||
-rw-r--r-- | src/sys/socket/mod.rs | 28 | ||||
-rw-r--r-- | src/sys/time.rs | 452 |
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) -> ×pec { + &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"); } } |