summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml13
-rw-r--r--CHANGELOG.md60
-rw-r--r--Cargo.toml11
-rw-r--r--README.md4
-rw-r--r--RELEASE_PROCEDURE.md6
-rw-r--r--build.rs10
-rw-r--r--nix-test/src/sizes.c6
-rw-r--r--src/lib.rs13
-rw-r--r--src/macros.rs8
-rw-r--r--src/sched.rs4
-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
-rw-r--r--src/unistd.rs330
-rw-r--r--test/sys/mod.rs7
-rw-r--r--test/sys/test_aio.rs374
-rw-r--r--test/sys/test_epoll.rs24
-rw-r--r--test/sys/test_ioctl.rs123
-rw-r--r--test/sys/test_select.rs4
-rw-r--r--test/sys/test_signal.rs7
-rw-r--r--test/sys/test_socket.rs75
-rw-r--r--test/sys/test_uio.rs9
-rw-r--r--test/sys/test_wait.rs2
-rw-r--r--test/test.rs24
-rw-r--r--test/test_mount.rs6
-rw-r--r--test/test_unistd.rs31
43 files changed, 2380 insertions, 644 deletions
diff --git a/.travis.yml b/.travis.yml
index cf8a20cb..e08e05e4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,9 +16,7 @@ addons:
- binutils-dev
rust:
- - 1.2.0 # Oldest supported version
- - 1.7.0
- - 1.8.0
+ - 1.7.0 # Oldest supported version
- stable
- beta
- nightly
@@ -48,12 +46,6 @@ matrix:
- os: osx
env: ARCH=i686
rust: stable
- - os: osx
- env: ARCH=x86_64
- rust: 1.2.0
- - os: osx
- env: ARCH=i686
- rust: 1.2.0
# Docker builds for other targets
- os: linux
env: TARGET=aarch64-unknown-linux-gnu DOCKER_IMAGE=posborne/rust-cross:arm
@@ -77,6 +69,9 @@ matrix:
sudo: true
allow_failures:
- rust: nightly
+ # We need to upgrade the lowest supported version. However, the build
+ # infrastructure for arm/mips/android is not ready yet.
+ - rust: 1.7.0
- env: TARGET=mips-unknown-linux-gnu DOCKER_IMAGE=posborne/rust-cross:mips
- env: TARGET=mipsel-unknown-linux-gnu DOCKER_IMAGE=posborne/rust-cross:mips
- env: TARGET=arm-linux-androideabi DOCKER_IMAGE=posborne/rust-cross:android
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76f7ef5f..335098bf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,11 +6,55 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Added
+- Added support for POSIX AIO
+ ([#483](https://github.com/nix-rust/nix/pull/483))
+- Added support for XNU system control sockets
+ ([#478](https://github.com/nix-rust/nix/pull/478))
+- Added support for `ioctl` calls on BSD platforms
+ ([#478](https://github.com/nix-rust/nix/pull/478))
+- Added struct `TimeSpec`
+ ([#475](https://github.com/nix-rust/nix/pull/475))
+ ([#483](https://github.com/nix-rust/nix/pull/483))
+- Added complete definitions for all kqueue-related constants on all supported
+ OSes
+ ([#415](https://github.com/nix-rust/nix/pull/415))
- Added function `epoll_create1` and bitflags `EpollCreateFlags` in
`::nix::sys::epoll` in order to support `::libc::epoll_create1`.
([#410](https://github.com/nix-rust/nix/pull/410))
+- Added `setresuid` and `setresgid` for Linux in `::nix::unistd`
+ ([#448](https://github.com/nix-rust/nix/pull/448))
+- Added `getpgid` in `::nix::unistd`
+ ([#433](https://github.com/nix-rust/nix/pull/433))
+- Added `tcgetpgrp` and `tcsetpgrp` in `::nix::unistd`
+ ([#451](https://github.com/nix-rust/nix/pull/451))
+- Added `CLONE_NEWCGROUP` in `::nix::sched`
+ ([#457](https://github.com/nix-rust/nix/pull/457))
+- Added `getpgrp` in `::nix::unistd`
+ ([#491](https://github.com/nix-rust/nix/pull/491))
+- Added `fchdir` in `::nix::unistd`
+ ([#497](https://github.com/nix-rust/nix/pull/497))
### Changed
+- `epoll_ctl` now could accept None as argument `event`
+ when op is `EpollOp::EpollCtlDel`.
+ ([#480](https://github.com/nix-rust/nix/pull/480))
+- Removed the `bad` keyword from the `ioctl!` macro
+ ([#478](https://github.com/nix-rust/nix/pull/478))
+- Changed `TimeVal` into an opaque Newtype
+ ([#475](https://github.com/nix-rust/nix/pull/475))
+- `kill`'s signature, defined in `::nix::sys::signal`, changed, so that the
+ signal parameter has type `T: Into<Option<Signal>>`. `None` as an argument
+ for that parameter will result in a 0 passed to libc's `kill`, while a
+ `Some`-argument will result in the previous behavior for the contained
+ `Signal`.
+ ([#445](https://github.com/nix-rust/nix/pull/445))
+- The minimum supported version of rustc is now 1.7.0.
+ ([#444](https://github.com/nix-rust/nix/pull/444))
+- Changed `KEvent` to an opaque structure that may only be modified by its
+ constructor and the `ev_set` method.
+ ([#415](https://github.com/nix-rust/nix/pull/415))
+ ([#442](https://github.com/nix-rust/nix/pull/442))
+ ([#463](https://github.com/nix-rust/nix/pull/463))
- `pipe2` now calls `libc::pipe2` where available. Previously it was emulated
using `pipe`, which meant that setting `O_CLOEXEC` was not atomic.
([#427](https://github.com/nix-rust/nix/pull/427))
@@ -22,13 +66,29 @@ This project adheres to [Semantic Versioning](http://semver.org/).
accessible with the new method `events()` of `EpollEvent`. Instances of
`EpollEvent` can be constructed using the new method `new()` of EpollEvent.
([#410](https://github.com/nix-rust/nix/pull/410))
+- `SigFlags` in `::nix::sys::signal` has be renamed to `SigmaskHow` and its type
+ has changed from `bitflags` to `enum` in order to conform to our conventions.
+ ([#460](https://github.com/nix-rust/nix/pull/460))
+- `sethostname` now takes a `&str` instead of a `&[u8]` as this provides an API
+ that makes more sense in normal, correct usage of the API.
+- `gethostname` previously did not expose the actual length of the hostname
+ written from the underlying system call at all. This has been updated to
+ return a `&CStr` within the provided buffer that is always properly
+ NUL-terminated (this is not guaranteed by the call with all platforms/libc
+ implementations).
### Fixed
+- Fixed multiple issues with Unix domain sockets on non-Linux OSes
+ ([#474](https://github.com/nix-rust/nix/pull/415))
+- Fixed using kqueue with `EVFILT_USER` on FreeBSD
+ ([#415](https://github.com/nix-rust/nix/pull/415))
- Fixed the build on FreeBSD, and fixed the getsockopt, sendmsg, and recvmsg
functions on that same OS.
([#397](https://github.com/nix-rust/nix/pull/397))
- Fixed an off-by-one bug in `UnixAddr::new_abstract` in `::nix::sys::socket`.
([#429](https://github.com/nix-rust/nix/pull/429))
+- Fixed clone passing a potentially unaligned stack.
+ ([#490](https://github.com/nix-rust/nix/pull/490))
## [0.7.0] 2016-09-09
diff --git a/Cargo.toml b/Cargo.toml
index 9302181c..43b074ee 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,11 +2,12 @@
name = "nix"
description = "Rust friendly bindings to *nix APIs"
-version = "0.7.1-pre"
-authors = ["Carl Lerche <me@carllerche.com>"]
+version = "0.8.0"
+authors = ["The nix-rust Project Developers"]
homepage = "https://github.com/nix-rust/nix"
repository = "https://github.com/nix-rust/nix"
license = "MIT"
+categories = ["os::unix-apis"]
exclude = [
".gitignore",
".travis.yml",
@@ -14,8 +15,6 @@ exclude = [
"test/**/*"
]
-build = "build.rs"
-
[features]
eventfd = []
execvpe = []
@@ -28,10 +27,6 @@ bitflags = "0.4"
cfg-if = "0.1.0"
void = "1.0.2"
-[build-dependencies]
-rustc_version = "0.1.7"
-semver = "0.1.20" # Old version for compatibility with rustc_version.
-
[dev-dependencies]
rand = "0.3.8"
tempdir = "0.3"
diff --git a/README.md b/README.md
index 20753af7..2a3dd24c 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,9 @@
[![Build Status](https://travis-ci.org/nix-rust/nix.svg?branch=master)](https://travis-ci.org/nix-rust/nix)
[![crates.io](http://meritbadge.herokuapp.com/nix)](https://crates.io/crates/nix)
-[Documentation](https://nix-rust.github.io/nix/nix/index.html)
+[Documentation (Releases)](https://docs.rs/nix/)
+
+[Documentation (Development)](https://nix-rust.github.io/nix/nix/index.html)
Nix seeks to provide friendly bindings to various *nix platform APIs (Linux, Darwin,
...). The goal is to not provide a 100% unified interface, but to unify
diff --git a/RELEASE_PROCEDURE.md b/RELEASE_PROCEDURE.md
index 14496f23..9502765c 100644
--- a/RELEASE_PROCEDURE.md
+++ b/RELEASE_PROCEDURE.md
@@ -34,8 +34,8 @@ After the release a commit with the following changes is added to the master
branch.
- Add a new Unreleased section header to CHANGELOG.md.
-- In `Cargo.toml`, update the version to the next `-dev` version, e.g.
- `v0.8.4-dev`.
+- In `Cargo.toml`, update the version to the nextversion, e.g.
+ `v0.8.4`.
- In `Cargo.tml`, revert the libc dependency to its git master branch.
-- Commit with a message like "Bump to v0.8.4-dev"
+- Commit with a message like "Bump to v0.8.4"
- Make a pull request.
diff --git a/build.rs b/build.rs
deleted file mode 100644
index 084bb0d7..00000000
--- a/build.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-extern crate rustc_version;
-extern crate semver;
-
-use semver::Version;
-
-fn main() {
- if rustc_version::version() >= Version::parse("1.6.0").unwrap() {
- println!("cargo:rustc-cfg=raw_pointer_derive_allowed");
- }
-}
diff --git a/nix-test/src/sizes.c b/nix-test/src/sizes.c
index 97a9b4a3..ca862003 100644
--- a/nix-test/src/sizes.c
+++ b/nix-test/src/sizes.c
@@ -1,5 +1,7 @@
-#include "sys/socket.h"
-#include "sys/uio.h"
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <string.h>
#define SIZE_OF_T(TYPE) \
do { \
diff --git a/src/lib.rs b/src/lib.rs
index 8dbf9fe0..ab8339ef 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -77,8 +77,16 @@ use std::fmt;
use std::error;
use libc::PATH_MAX;
+/// Nix Result Type
pub type Result<T> = result::Result<T, Error>;
+/// Nix Error Type
+///
+/// The nix error type provides a common way of dealing with
+/// various system system/libc calls that might fail. Each
+/// error has a corresponding errno (usually the one from the
+/// underlying OS) to which it can be mapped in addition to
+/// implementing other common traits.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Error {
Sys(errno::Errno),
@@ -86,18 +94,23 @@ pub enum Error {
}
impl Error {
+
+ /// Create a nix Error from a given errno
pub fn from_errno(errno: errno::Errno) -> Error {
Error::Sys(errno)
}
+ /// Get the current errno and convert it to a nix Error
pub fn last() -> Error {
Error::Sys(errno::Errno::last())
}
+ /// Create a new invalid argument error (`EINVAL`)
pub fn invalid_argument() -> Error {
Error::Sys(errno::EINVAL)
}
+ /// Get the errno associated with this error
pub fn errno(&self) -> errno::Errno {
match *self {
Error::Sys(errno) => errno,
diff --git a/src/macros.rs b/src/macros.rs
index e6d58b10..4954bed6 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -186,3 +186,11 @@ macro_rules! libc_bitflags {
}
};
}
+
+/// A Rust version of the familiar C `offset_of` macro. It returns the byte
+/// offset of `field` within struct `ty`
+macro_rules! offset_of {
+ ($ty:ty, $field:ident) => {
+ &(*(0 as *const $ty)).$field as *const _ as usize
+ }
+}
diff --git a/src/sched.rs b/src/sched.rs
index 91a7c42a..66b4ed4f 100644
--- a/src/sched.rs
+++ b/src/sched.rs
@@ -24,6 +24,7 @@ libc_bitflags!{
CLONE_DETACHED,
CLONE_UNTRACED,
CLONE_CHILD_SETTID,
+ CLONE_NEWCGROUP,
CLONE_NEWUTS,
CLONE_NEWIPC,
CLONE_NEWUSER,
@@ -113,8 +114,9 @@ pub fn clone(mut cb: CloneCb,
let res = unsafe {
let combined = flags.bits() | signal.unwrap_or(0);
let ptr = stack.as_mut_ptr().offset(stack.len() as isize);
+ let ptr_aligned = ptr.offset((ptr as usize % 16) as isize * -1);
ffi::clone(mem::transmute(callback as extern "C" fn(*mut Box<::std::ops::FnMut() -> isize>) -> i32),
- ptr as *mut c_void,
+ ptr_aligned as *mut c_void,
combined,
&mut cb)
};
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");
}
}
diff --git a/src/unistd.rs b/src/unistd.rs
index 2c4a92c3..187154bd 100644
--- a/src/unistd.rs
+++ b/src/unistd.rs
@@ -1,12 +1,12 @@
-//! Standard symbolic constants and types
-//!
+//! Safe wrappers around functions found in libc "unistd.h" header
+
use {Errno, Error, Result, NixPath};
use fcntl::{fcntl, OFlag, O_CLOEXEC, FD_CLOEXEC};
use fcntl::FcntlArg::F_SETFD;
use libc::{self, c_char, c_void, c_int, c_uint, size_t, pid_t, off_t, uid_t, gid_t, mode_t};
use std::mem;
-use std::ffi::{CString, CStr, OsString};
-use std::os::unix::ffi::{OsStringExt};
+use std::ffi::{CString, CStr, OsString, OsStr};
+use std::os::unix::ffi::{OsStringExt, OsStrExt};
use std::os::unix::io::RawFd;
use std::path::{PathBuf};
use void::Void;
@@ -15,15 +15,20 @@ use sys::stat::Mode;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use self::linux::*;
+/// Represents the successful result of calling `fork`
+///
+/// When `fork` is called, the process continues execution in the parent process
+/// and in the new child. This return type can be examined to determine whether
+/// you are now executing in the parent process or in the child.
#[derive(Clone, Copy)]
pub enum ForkResult {
- Parent {
- child: pid_t
- },
- Child
+ Parent { child: pid_t },
+ Child,
}
impl ForkResult {
+
+ /// Return `true` if this is the child process of the `fork()`
#[inline]
pub fn is_child(&self) -> bool {
match *self {
@@ -32,12 +37,40 @@ impl ForkResult {
}
}
+ /// Returns `true` if this is the parent process of the `fork()`
#[inline]
pub fn is_parent(&self) -> bool {
!self.is_child()
}
}
+/// Create a new child process duplicating the parent process ([see
+/// fork(2)](http://man7.org/linux/man-pages/man2/fork.2.html)).
+///
+/// After calling the fork system call (successfully) two processes will
+/// be created that are identical with the exception of their pid and the
+/// return value of this function. As an example:
+///
+/// ```no_run
+/// use nix::unistd::{fork, ForkResult};
+///
+/// match fork() {
+/// Ok(ForkResult::Parent { child, .. }) => {
+/// println!("Continuing execution in parent process, new child has pid: {}", child);
+/// }
+/// Ok(ForkResult::Child) => println!("I'm a new child process"),
+/// Err(e) => println!("Fork failed"),
+/// }
+/// ```
+///
+/// This will print something like the following (order indeterministic). The
+/// thing to note is that you end up with two processes continuing execution
+/// immediately after the fork call but with different match arms.
+///
+/// ```text
+/// Continuing execution in parent process, new child has pid: 1234
+/// I'm a new child process
+/// ```
#[inline]
pub fn fork() -> Result<ForkResult> {
use self::ForkResult::*;
@@ -45,30 +78,116 @@ pub fn fork() -> Result<ForkResult> {
Errno::result(res).map(|res| match res {
0 => Child,
- res => Parent { child: res }
+ res => Parent { child: res },
})
}
+/// Get the pid of this process (see
+/// [getpid(2)](http://man7.org/linux/man-pages/man2/getpid.2.html)).
+///
+/// Since you are running code, there is always a pid to return, so there
+/// is no error case that needs to be handled.
#[inline]
pub fn getpid() -> pid_t {
- unsafe { libc::getpid() } // no error handling, according to man page: "These functions are always successful."
+ unsafe { libc::getpid() }
}
+
+/// Get the pid of this processes' parent (see
+/// [getpid(2)](http://man7.org/linux/man-pages/man2/getpid.2.html)).
+///
+/// There is always a parent pid to return, so there is no error case that needs
+/// to be handled.
#[inline]
pub fn getppid() -> pid_t {
unsafe { libc::getppid() } // no error handling, according to man page: "These functions are always successful."
}
+
+/// Set a process group ID (see
+/// [setpgid(2)](http://man7.org/linux/man-pages/man2/setpgid.2.html)).
+///
+/// Set the process group id (PGID) of a particular process. If a pid of zero
+/// is specified, then the pid of the calling process is used. Process groups
+/// may be used to group together a set of processes in order for the OS to
+/// apply some operations across the group.
+///
+/// `setsid()` may be used to create a new process group.
#[inline]
pub fn setpgid(pid: pid_t, pgid: pid_t) -> Result<()> {
let res = unsafe { libc::setpgid(pid, pgid) };
Errno::result(res).map(drop)
}
+#[inline]
+pub fn getpgid(pid: Option<pid_t>) -> Result<pid_t> {
+ let res = unsafe { libc::getpgid(pid.unwrap_or(0 as pid_t)) };
+ Errno::result(res)
+}
+
+/// Create new session and set process group id (see
+/// [setsid(2)](http://man7.org/linux/man-pages/man2/setsid.2.html)).
+#[inline]
+pub fn setsid() -> Result<pid_t> {
+ Errno::result(unsafe { libc::setsid() })
+}
+
+
+/// Get the terminal foreground process group (see
+/// [tcgetpgrp(3)](http://man7.org/linux/man-pages/man3/tcgetpgrp.3.html)).
+///
+/// Get the group process id (GPID) of the foreground process group on the
+/// terminal associated to file descriptor (FD).
+#[inline]
+pub fn tcgetpgrp(fd: c_int) -> Result<pid_t> {
+ let res = unsafe { libc::tcgetpgrp(fd) };
+ Errno::result(res)
+}
+/// Set the terminal foreground process group (see
+/// [tcgetpgrp(3)](http://man7.org/linux/man-pages/man3/tcgetpgrp.3.html)).
+///
+/// Get the group process id (PGID) to the foreground process group on the
+/// terminal associated to file descriptor (FD).
+#[inline]
+pub fn tcsetpgrp(fd: c_int, pgrp: pid_t) -> Result<()> {
+ let res = unsafe { libc::tcsetpgrp(fd, pgrp) };
+ Errno::result(res).map(drop)
+}
+
+
+/// Get the group id of the calling process (see
+///[getpgrp(3)](http://man7.org/linux/man-pages/man3/getpgrp.3p.html)).
+///
+/// Get the process group id (PGID) of the calling process.
+/// According to the man page it is always successful.
+#[inline]
+pub fn getpgrp() -> pid_t {
+ unsafe { libc::getpgrp() }
+}
+/// Get the caller's thread ID (see
+/// [gettid(2)](http://man7.org/linux/man-pages/man2/gettid.2.html).
+///
+/// This function is only available on Linux based systems. In a single
+/// threaded process, the main thread will have the same ID as the process. In
+/// a multithreaded process, each thread will have a unique thread id but the
+/// same process ID.
+///
+/// No error handling is required as a thread id should always exist for any
+/// process, even if threads are not being used.
#[cfg(any(target_os = "linux", target_os = "android"))]
#[inline]
pub fn gettid() -> pid_t {
- unsafe { libc::syscall(libc::SYS_gettid) as pid_t } // no error handling, according to man page: "These functions are always successful."
+ unsafe { libc::syscall(libc::SYS_gettid) as pid_t }
}
+/// Create a copy of the specified file descriptor (see
+/// [dup(2)](http://man7.org/linux/man-pages/man2/dup.2.html)).
+///
+/// The new file descriptor will be have a new index but refer to the same
+/// resource as the old file descriptor and the old and new file descriptors may
+/// be used interchangeably. The new and old file descriptor share the same
+/// underlying resource, offset, and file status flags. The actual index used
+/// for the file descriptor will be the lowest fd index that is available.
+///
+/// The two file descriptors do not share file descriptor flags (e.g. `FD_CLOEXEC`).
#[inline]
pub fn dup(oldfd: RawFd) -> Result<RawFd> {
let res = unsafe { libc::dup(oldfd) };
@@ -76,6 +195,12 @@ pub fn dup(oldfd: RawFd) -> Result<RawFd> {
Errno::result(res)
}
+/// Create a copy of the specified file descriptor using the specified fd (see
+/// [dup(2)](http://man7.org/linux/man-pages/man2/dup.2.html)).
+///
+/// This function behaves similar to `dup()` except that it will try to use the
+/// specified fd instead of allocating a new one. See the man pages for more
+/// detail on the exact behavior of this function.
#[inline]
pub fn dup2(oldfd: RawFd, newfd: RawFd) -> Result<RawFd> {
let res = unsafe { libc::dup2(oldfd, newfd) };
@@ -83,6 +208,11 @@ pub fn dup2(oldfd: RawFd, newfd: RawFd) -> Result<RawFd> {
Errno::result(res)
}
+/// Create a new copy of the specified file descriptor using the specified fd
+/// and flags (see [dup(2)](http://man7.org/linux/man-pages/man2/dup.2.html)).
+///
+/// This function behaves similar to `dup2()` but allows for flags to be
+/// specified.
pub fn dup3(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
dup3_polyfill(oldfd, newfd, flags)
}
@@ -105,6 +235,11 @@ fn dup3_polyfill(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
Ok(fd)
}
+/// Change the current working directory of the calling process (see
+/// [chdir(2)](http://man7.org/linux/man-pages/man2/chdir.2.html)).
+///
+/// This function may fail in a number of different scenarios. See the man
+/// pages for additional details on possible failure cases.
#[inline]
pub fn chdir<P: ?Sized + NixPath>(path: &P) -> Result<()> {
let res = try!(path.with_nix_path(|cstr| {
@@ -114,6 +249,19 @@ pub fn chdir<P: ?Sized + NixPath>(path: &P) -> Result<()> {
Errno::result(res).map(drop)
}
+/// Change the current working directory of the process to the one
+/// given as an open file descriptor (see
+/// [fchdir(2)](http://man7.org/linux/man-pages/man2/fchdir.2.html)).
+///
+/// This function may fail in a number of different scenarios. See the man
+/// pages for additional details on possible failure cases.
+#[inline]
+pub fn fchdir(dirfd: RawFd) -> Result<()> {
+ let res = unsafe { libc::fchdir(dirfd) };
+
+ Errno::result(res).map(drop)
+}
+
/// Creates new directory `path` with access rights `mode`.
///
/// # Errors
@@ -138,12 +286,12 @@ pub fn chdir<P: ?Sized + NixPath>(path: &P) -> Result<()> {
/// use tempdir::TempDir;
///
/// fn main() {
-/// let mut tmp_dir = TempDir::new("test_mkdir").unwrap().into_path();
-/// tmp_dir.push("new_dir");
+/// let tmp_dir1 = TempDir::new("test_mkdir").unwrap();
+/// let tmp_dir2 = tmp_dir1.path().join("new_dir");
///
/// // create new directory and give read, write and execute rights to the owner
-/// match unistd::mkdir(&tmp_dir, stat::S_IRWXU) {
-/// Ok(_) => println!("created {:?}", tmp_dir),
+/// match unistd::mkdir(&tmp_dir2, stat::S_IRWXU) {
+/// Ok(_) => println!("created {:?}", tmp_dir2),
/// Err(err) => println!("Error creating directory: {}", err),
/// }
/// }
@@ -208,6 +356,17 @@ pub fn getcwd() -> Result<PathBuf> {
}
}
+/// Change the ownership of the file at `path` to be owned by the specified
+/// `owner` (user) and `group` (see
+/// [chown(2)](http://man7.org/linux/man-pages/man2/lchown.2.html)).
+///
+/// The owner/group for the provided path name will not be modified if `None` is
+/// provided for that argument. Ownership change will be attempted for the path
+/// only if `Some` owner/group is provided.
+///
+/// This call may fail under a number of different situations. See [the man
+/// pages](http://man7.org/linux/man-pages/man2/lchown.2.html#ERRORS) for
+/// additional details.
#[inline]
pub fn chown<P: ?Sized + NixPath>(path: &P, owner: Option<uid_t>, group: Option<gid_t>) -> Result<()> {
let res = try!(path.with_nix_path(|cstr| {
@@ -231,6 +390,12 @@ fn to_exec_array(args: &[CString]) -> Vec<*const c_char> {
args_p
}
+/// Replace the current process image with a new one (see
+/// [exec(3)](http://man7.org/linux/man-pages/man3/exec.3.html)).
+///
+/// See the `::nix::unistd::execve` system call for additional details. `execv`
+/// performs the same action but does not allow for customization of the
+/// environment for the new process.
#[inline]
pub fn execv(path: &CString, argv: &[CString]) -> Result<Void> {
let args_p = to_exec_array(argv);
@@ -242,6 +407,24 @@ pub fn execv(path: &CString, argv: &[CString]) -> Result<Void> {
Err(Error::Sys(Errno::last()))
}
+
+/// Replace the current process image with a new one (see
+/// [execve(2)](http://man7.org/linux/man-pages/man2/execve.2.html)).
+///
+/// The execve system call allows for another process to be "called" which will
+/// replace the current process image. That is, this process becomes the new
+/// command that is run. On success, this function will not return. Instead,
+/// the new program will run until it exits.
+///
+/// If an error occurs, this function will return with an indication of the
+/// cause of failure. See
+/// [execve(2)#errors](http://man7.org/linux/man-pages/man2/execve.2.html#ERRORS)
+/// for a list of potential problems that maight cause execv to fail.
+///
+/// `::nix::unistd::execv` and `::nix::unistd::execve` take as arguments a slice
+/// of `::std::ffi::CString`s for `args` and `env` (for `execve`). Each element
+/// in the `args` list is an argument to the new process. Each element in the
+/// `env` list should be a string in the form "key=value".
#[inline]
pub fn execve(path: &CString, args: &[CString], env: &[CString]) -> Result<Void> {
let args_p = to_exec_array(args);
@@ -254,6 +437,15 @@ pub fn execve(path: &CString, args: &[CString], env: &[CString]) -> Result<Void>
Err(Error::Sys(Errno::last()))
}
+/// Replace the current process image with a new one and replicate shell `PATH`
+/// searching behavior (see
+/// [exec(3)](http://man7.org/linux/man-pages/man3/exec.3.html)).
+///
+/// See `::nix::unistd::execve` for additoinal details. `execvp` behaves the
+/// same as execv except that it will examine the `PATH` environment variables
+/// for file names not specified with a leading slash. For example, `execv`
+/// would not work if "bash" was specified for the path argument, but `execvp`
+/// would assuming that a bash executable was on the system `PATH`.
#[inline]
pub fn execvp(filename: &CString, args: &[CString]) -> Result<Void> {
let args_p = to_exec_array(args);
@@ -265,12 +457,50 @@ pub fn execvp(filename: &CString, args: &[CString]) -> Result<Void> {
Err(Error::Sys(Errno::last()))
}
+/// Daemonize this process by detaching from the controlling terminal (see
+/// [daemon(3)](http://man7.org/linux/man-pages/man3/daemon.3.html)).
+///
+/// When a process is launched it is typically associated with a parent and it,
+/// in turn, by its controlling terminal/process. In order for a process to run
+/// in the "background" it must daemonize itself by detaching itself. Under
+/// posix, this is done by doing the following:
+///
+/// 1. Parent process (this one) forks
+/// 2. Parent process exits
+/// 3. Child process continues to run.
+///
+/// `nochdir`:
+///
+/// * `nochdir = true`: The current working directory after daemonizing will
+/// be the current working directory.
+/// * `nochdir = false`: The current working directory after daemonizing will
+/// be the root direcory, `/`.
+///
+/// `noclose`:
+///
+/// * `noclose = true`: The process' current stdin, stdout, and stderr file
+/// descriptors will remain identical after daemonizing.
+/// * `noclose = false`: The process' stdin, stdout, and stderr will point to
+/// `/dev/null` after daemonizing.
+///
+/// The underlying implementation (in libc) calls both
+/// [fork(2)](http://man7.org/linux/man-pages/man2/fork.2.html) and
+/// [setsid(2)](http://man7.org/linux/man-pages/man2/setsid.2.html) and, as
+/// such, error that could be returned by either of those functions could also
+/// show up as errors here.
pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> {
let res = unsafe { libc::daemon(nochdir as c_int, noclose as c_int) };
Errno::result(res).map(drop)
}
-pub fn sethostname(name: &[u8]) -> Result<()> {
+/// Set the system host name (see
+/// [gethostname(2)](http://man7.org/linux/man-pages/man2/gethostname.2.html)).
+///
+/// Given a name, attempt to update the system host name to the given string.
+/// On some systems, the host name is limited to as few as 64 bytes. An error
+/// will be return if the name is not valid or the current process does not have
+/// permissions to update the host name.
+pub fn sethostname<S: AsRef<OsStr>>(name: S) -> Result<()> {
// Handle some differences in type of the len arg across platforms.
cfg_if! {
if #[cfg(any(target_os = "dragonfly",
@@ -282,19 +512,42 @@ pub fn sethostname(name: &[u8]) -> Result<()> {
type sethostname_len_t = size_t;
}
}
- let ptr = name.as_ptr() as *const c_char;
- let len = name.len() as sethostname_len_t;
+ let ptr = name.as_ref().as_bytes().as_ptr() as *const c_char;
+ let len = name.as_ref().len() as sethostname_len_t;
let res = unsafe { libc::sethostname(ptr, len) };
Errno::result(res).map(drop)
}
-pub fn gethostname(name: &mut [u8]) -> Result<()> {
- let ptr = name.as_mut_ptr() as *mut c_char;
- let len = name.len() as size_t;
+/// Get the host name and store it in the provided buffer, returning a pointer
+/// the CStr in that buffer on success (see
+/// [gethostname(2)](http://man7.org/linux/man-pages/man2/gethostname.2.html)).
+///
+/// This function call attempts to get the host name for the running system and
+/// store it in a provided buffer. The buffer will be populated with bytes up
+/// to the length of the provided slice including a NUL terminating byte. If
+/// the hostname is longer than the length provided, no error will be provided.
+/// The posix specification does not specify whether implementations will
+/// null-terminate in this case, but the nix implementation will ensure that the
+/// buffer is null terminated in this case.
+///
+/// ```no_run
+/// use nix::unistd;
+///
+/// let mut buf = [0u8; 64];
+/// let hostname_cstr = unistd::gethostname(&mut buf).expect("Failed getting hostname");
+/// let hostname = hostname_cstr.to_str().expect("Hostname wasn't valid UTF-8");
+/// println!("Hostname: {}", hostname);
+/// ```
+pub fn gethostname<'a>(buffer: &'a mut [u8]) -> Result<&'a CStr> {
+ let ptr = buffer.as_mut_ptr() as *mut c_char;
+ let len = buffer.len() as size_t;
let res = unsafe { libc::gethostname(ptr, len) };
- Errno::result(res).map(drop)
+ Errno::result(res).map(|_| {
+ buffer[len - 1] = 0; // ensure always null-terminated
+ unsafe { CStr::from_ptr(buffer.as_ptr() as *const c_char) }
+ })
}
pub fn close(fd: RawFd) -> Result<()> {
@@ -568,6 +821,7 @@ pub fn mkstemp<P: ?Sized + NixPath>(template: &P) -> Result<(RawFd, PathBuf)> {
#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux {
+ use libc::{self, uid_t, gid_t};
use sys::syscall::{syscall, SYSPIVOTROOT};
use {Errno, Result, NixPath};
@@ -587,6 +841,38 @@ mod linux {
Errno::result(res).map(drop)
}
+ /// Sets the real, effective, and saved uid.
+ /// ([see setresuid(2)](http://man7.org/linux/man-pages/man2/setresuid.2.html))
+ ///
+ /// * `ruid`: real user id
+ /// * `euid`: effective user id
+ /// * `suid`: saved user id
+ /// * returns: Ok or libc error code.
+ ///
+ /// Err is returned if the user doesn't have permission to set this UID.
+ #[inline]
+ pub fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> Result<()> {
+ let res = unsafe { libc::setresuid(ruid, euid, suid) };
+
+ Errno::result(res).map(drop)
+ }
+
+ /// Sets the real, effective, and saved gid.
+ /// ([see setresuid(2)](http://man7.org/linux/man-pages/man2/setresuid.2.html))
+ ///
+ /// * `rgid`: real user id
+ /// * `egid`: effective user id
+ /// * `sgid`: saved user id
+ /// * returns: Ok or libc error code.
+ ///
+ /// Err is returned if the user doesn't have permission to set this GID.
+ #[inline]
+ pub fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> Result<()> {
+ let res = unsafe { libc::setresgid(rgid, egid, sgid) };
+
+ Errno::result(res).map(drop)
+ }
+
#[inline]
#[cfg(feature = "execvpe")]
pub fn execvpe(filename: &CString, args: &[CString], env: &[CString]) -> Result<()> {
diff --git a/test/sys/mod.rs b/test/sys/mod.rs
index a5f3351d..5e5eed41 100644
--- a/test/sys/mod.rs
+++ b/test/sys/mod.rs
@@ -1,3 +1,7 @@
+mod test_signal;
+#[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
+ target_os = "netbsd", target_os = "macos", target_os = "linux"))]
+mod test_aio;
mod test_socket;
mod test_sockopt;
mod test_termios;
@@ -5,3 +9,6 @@ mod test_ioctl;
mod test_wait;
mod test_select;
mod test_uio;
+
+#[cfg(target_os = "linux")]
+mod test_epoll;
diff --git a/test/sys/test_aio.rs b/test/sys/test_aio.rs
new file mode 100644
index 00000000..825282f1
--- /dev/null
+++ b/test/sys/test_aio.rs
@@ -0,0 +1,374 @@
+use libc::c_int;
+use nix::{Error, Result};
+use nix::errno::*;
+use nix::sys::aio::*;
+use nix::sys::signal::*;
+use nix::sys::time::{TimeSpec, TimeValLike};
+use std::io::{Write, Read, Seek, SeekFrom};
+use std::os::unix::io::AsRawFd;
+use std::{thread, time};
+use tempfile::tempfile;
+
+// Helper that polls an AioCb for completion or error
+fn poll_aio(mut aiocb: &mut AioCb) -> Result<()> {
+ loop {
+ let err = aio_error(&mut aiocb);
+ if err != Err(Error::from(Errno::EINPROGRESS)) { return err; };
+ thread::sleep(time::Duration::from_millis(10));
+ }
+}
+
+// Tests aio_cancel. We aren't trying to test the OS's implementation, only our
+// bindings. So it's sufficient to check that aio_cancel returned any
+// AioCancelStat value.
+#[test]
+fn test_aio_cancel() {
+ let mut wbuf = "CDEF".to_string().into_bytes();
+
+ let f = tempfile().unwrap();
+ let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
+ 0, //offset
+ &mut wbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ aio_write(&mut aiocb).unwrap();
+ let err = aio_error(&mut aiocb);
+ assert!(err == Ok(()) || err == Err(Error::from(Errno::EINPROGRESS)));
+
+ let cancelstat = aio_cancel(f.as_raw_fd(), Some(&mut aiocb));
+ assert!(cancelstat.is_ok());
+
+ // Wait for aiocb to complete, but don't care whether it succeeded
+ let _ = poll_aio(&mut aiocb);
+ let _ = aio_return(&mut aiocb);
+}
+
+// Tests using aio_cancel for all outstanding IOs.
+#[test]
+fn test_aio_cancel_all() {
+ let mut wbuf = "CDEF".to_string().into_bytes();
+
+ let f = tempfile().unwrap();
+ let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
+ 0, //offset
+ &mut wbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ aio_write(&mut aiocb).unwrap();
+ let err = aio_error(&mut aiocb);
+ assert!(err == Ok(()) || err == Err(Error::from(Errno::EINPROGRESS)));
+
+ let cancelstat = aio_cancel(f.as_raw_fd(), None);
+ assert!(cancelstat.is_ok());
+
+ // Wait for aiocb to complete, but don't care whether it succeeded
+ let _ = poll_aio(&mut aiocb);
+ let _ = aio_return(&mut aiocb);
+}
+
+#[test]
+fn test_aio_fsync() {
+ const INITIAL: &'static [u8] = b"abcdef123456";
+ let mut f = tempfile().unwrap();
+ f.write(INITIAL).unwrap();
+ let mut aiocb = AioCb::from_fd( f.as_raw_fd(),
+ 0, //priority
+ SigevNotify::SigevNone);
+ let err = aio_fsync(AioFsyncMode::O_SYNC, &mut aiocb);
+ assert!(err.is_ok());
+ poll_aio(&mut aiocb).unwrap();
+ aio_return(&mut aiocb).unwrap();
+}
+
+
+#[test]
+fn test_aio_suspend() {
+ const INITIAL: &'static [u8] = b"abcdef123456";
+ const WBUF: &'static [u8] = b"CDEF";
+ let timeout = TimeSpec::seconds(10);
+ let mut rbuf = vec![0; 4];
+ let mut f = tempfile().unwrap();
+ f.write(INITIAL).unwrap();
+
+ let mut wcb = unsafe {
+ AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ &mut WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_WRITE)
+ };
+
+ let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
+ 8, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_READ);
+ aio_write(&mut wcb).unwrap();
+ aio_read(&mut rcb).unwrap();
+ loop {
+ {
+ let cbbuf = [&wcb, &rcb];
+ assert!(aio_suspend(&cbbuf[..], Some(timeout)).is_ok());
+ }
+ if aio_error(&mut rcb) != Err(Error::from(Errno::EINPROGRESS)) &&
+ aio_error(&mut wcb) != Err(Error::from(Errno::EINPROGRESS)) {
+ break
+ }
+ }
+
+ assert!(aio_return(&mut wcb).unwrap() as usize == WBUF.len());
+ assert!(aio_return(&mut rcb).unwrap() as usize == WBUF.len());
+}
+
+// Test a simple aio operation with no completion notification. We must poll
+// for completion
+#[test]
+fn test_aio_read() {
+ const INITIAL: &'static [u8] = b"abcdef123456";
+ let mut rbuf = vec![0; 4];
+ const EXPECT: &'static [u8] = b"cdef";
+ let mut f = tempfile().unwrap();
+ f.write(INITIAL).unwrap();
+ {
+ let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
+ 2, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP);
+ aio_read(&mut aiocb).unwrap();
+
+ let err = poll_aio(&mut aiocb);
+ assert!(err == Ok(()));
+ assert!(aio_return(&mut aiocb).unwrap() as usize == EXPECT.len());
+ }
+
+ assert!(rbuf == EXPECT);
+}
+
+// Test a simple aio operation with no completion notification. We must poll
+// for completion. Unlike test_aio_read, this test uses AioCb::from_slice
+#[test]
+fn test_aio_write() {
+ const INITIAL: &'static [u8] = b"abcdef123456";
+ const WBUF: &'static [u8] = b"CDEF"; //"CDEF".to_string().into_bytes();
+ let mut rbuf = Vec::new();
+ const EXPECT: &'static [u8] = b"abCDEF123456";
+
+ let mut f = tempfile().unwrap();
+ f.write(INITIAL).unwrap();
+ let mut aiocb = unsafe {
+ AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ &WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_NOP)
+ };
+ aio_write(&mut aiocb).unwrap();
+
+ let err = poll_aio(&mut aiocb);
+ assert!(err == Ok(()));
+ assert!(aio_return(&mut aiocb).unwrap() as usize == WBUF.len());
+
+ f.seek(SeekFrom::Start(0)).unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert!(len == EXPECT.len());
+ assert!(rbuf == EXPECT);
+}
+
+// XXX: should be sig_atomic_t, but rust's libc doesn't define that yet
+static mut signaled: i32 = 0;
+
+extern fn sigfunc(_: c_int) {
+ // It's a pity that Rust can't understand that static mutable sig_atomic_t
+ // variables can be safely accessed
+ unsafe { signaled = 1 };
+}
+
+// Test an aio operation with completion delivered by a signal
+#[test]
+fn test_aio_write_sigev_signal() {
+ let sa = SigAction::new(SigHandler::Handler(sigfunc),
+ SA_RESETHAND,
+ SigSet::empty());
+ unsafe {signaled = 0 };
+ unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
+
+ const INITIAL: &'static [u8] = b"abcdef123456";
+ const WBUF: &'static [u8] = b"CDEF";
+ let mut rbuf = Vec::new();
+ const EXPECT: &'static [u8] = b"abCDEF123456";
+
+ let mut f = tempfile().unwrap();
+ f.write(INITIAL).unwrap();
+ let mut aiocb = unsafe {
+ AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ &WBUF,
+ 0, //priority
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 0 //TODO: validate in sigfunc
+ },
+ LioOpcode::LIO_NOP)
+ };
+ aio_write(&mut aiocb).unwrap();
+ while unsafe { signaled == 0 } {
+ thread::sleep(time::Duration::from_millis(10));
+ }
+
+ assert!(aio_return(&mut aiocb).unwrap() as usize == WBUF.len());
+ f.seek(SeekFrom::Start(0)).unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert!(len == EXPECT.len());
+ assert!(rbuf == EXPECT);
+}
+
+// Test lio_listio with LIO_WAIT, so all AIO ops should be complete by the time
+// lio_listio returns.
+#[test]
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+fn test_lio_listio_wait() {
+ const INITIAL: &'static [u8] = b"abcdef123456";
+ const WBUF: &'static [u8] = b"CDEF";
+ let mut rbuf = vec![0; 4];
+ let mut rbuf2 = Vec::new();
+ const EXPECT: &'static [u8] = b"abCDEF123456";
+ let mut f = tempfile().unwrap();
+
+ f.write(INITIAL).unwrap();
+
+ {
+ let mut wcb = unsafe {
+ AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ &WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_WRITE)
+ };
+
+ let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
+ 8, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_READ);
+ let err = lio_listio(LioMode::LIO_WAIT, &[&mut wcb, &mut rcb], SigevNotify::SigevNone);
+ err.expect("lio_listio failed");
+
+ assert!(aio_return(&mut wcb).unwrap() as usize == WBUF.len());
+ assert!(aio_return(&mut rcb).unwrap() as usize == WBUF.len());
+ }
+ assert!(rbuf == b"3456");
+
+ f.seek(SeekFrom::Start(0)).unwrap();
+ let len = f.read_to_end(&mut rbuf2).unwrap();
+ assert!(len == EXPECT.len());
+ assert!(rbuf2 == EXPECT);
+}
+
+// Test lio_listio with LIO_NOWAIT and no SigEvent, so we must use some other
+// mechanism to check for the individual AioCb's completion.
+#[test]
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+fn test_lio_listio_nowait() {
+ const INITIAL: &'static [u8] = b"abcdef123456";
+ const WBUF: &'static [u8] = b"CDEF";
+ let mut rbuf = vec![0; 4];
+ let mut rbuf2 = Vec::new();
+ const EXPECT: &'static [u8] = b"abCDEF123456";
+ let mut f = tempfile().unwrap();
+
+ f.write(INITIAL).unwrap();
+
+ {
+ let mut wcb = unsafe {
+ AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ &WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_WRITE)
+ };
+
+ let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
+ 8, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_READ);
+ let err = lio_listio(LioMode::LIO_NOWAIT, &[&mut wcb, &mut rcb], SigevNotify::SigevNone);
+ err.expect("lio_listio failed");
+
+ poll_aio(&mut wcb).unwrap();
+ poll_aio(&mut rcb).unwrap();
+ assert!(aio_return(&mut wcb).unwrap() as usize == WBUF.len());
+ assert!(aio_return(&mut rcb).unwrap() as usize == WBUF.len());
+ }
+ assert!(rbuf == b"3456");
+
+ f.seek(SeekFrom::Start(0)).unwrap();
+ let len = f.read_to_end(&mut rbuf2).unwrap();
+ assert!(len == EXPECT.len());
+ assert!(rbuf2 == EXPECT);
+}
+
+// Test lio_listio with LIO_NOWAIT and a SigEvent to indicate when all AioCb's
+// are complete.
+#[test]
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+fn test_lio_listio_signal() {
+ const INITIAL: &'static [u8] = b"abcdef123456";
+ const WBUF: &'static [u8] = b"CDEF";
+ let mut rbuf = vec![0; 4];
+ let mut rbuf2 = Vec::new();
+ const EXPECT: &'static [u8] = b"abCDEF123456";
+ let mut f = tempfile().unwrap();
+ let sa = SigAction::new(SigHandler::Handler(sigfunc),
+ SA_RESETHAND,
+ SigSet::empty());
+ let sigev_notify = SigevNotify::SigevSignal { signal: Signal::SIGUSR2,
+ si_value: 0 };
+
+ f.write(INITIAL).unwrap();
+
+ {
+ let mut wcb = unsafe {
+ AioCb::from_slice( f.as_raw_fd(),
+ 2, //offset
+ &WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_WRITE)
+ };
+
+ let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
+ 8, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ LioOpcode::LIO_READ);
+ unsafe {signaled = 0 };
+ unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
+ let err = lio_listio(LioMode::LIO_NOWAIT, &[&mut wcb, &mut rcb], sigev_notify);
+ err.expect("lio_listio failed");
+ while unsafe { signaled == 0 } {
+ thread::sleep(time::Duration::from_millis(10));
+ }
+
+ assert!(aio_return(&mut wcb).unwrap() as usize == WBUF.len());
+ assert!(aio_return(&mut rcb).unwrap() as usize == WBUF.len());
+ }
+ assert!(rbuf == b"3456");
+
+ f.seek(SeekFrom::Start(0)).unwrap();
+ let len = f.read_to_end(&mut rbuf2).unwrap();
+ assert!(len == EXPECT.len());
+ assert!(rbuf2 == EXPECT);
+}
diff --git a/test/sys/test_epoll.rs b/test/sys/test_epoll.rs
new file mode 100644
index 00000000..a73fea6d
--- /dev/null
+++ b/test/sys/test_epoll.rs
@@ -0,0 +1,24 @@
+use nix::sys::epoll::{EpollCreateFlags, EpollOp, EpollEvent};
+use nix::sys::epoll::{EPOLLIN, EPOLLERR};
+use nix::sys::epoll::{epoll_create1, epoll_ctl};
+use nix::{Error, Errno};
+
+#[test]
+pub fn test_epoll_errno() {
+ let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
+ let result = epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None);
+ assert!(result.is_err());
+ assert_eq!(result.unwrap_err(), Error::Sys(Errno::ENOENT));
+
+ let result = epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, None);
+ assert!(result.is_err());
+ assert_eq!(result.unwrap_err(), Error::Sys(Errno::EINVAL));
+}
+
+#[test]
+pub fn test_epoll_ctl() {
+ let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
+ let mut event = EpollEvent::new(EPOLLIN | EPOLLERR, 1);
+ epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, &mut event).unwrap();
+ epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None).unwrap();
+}
diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs
index 94eca447..1d9fbcc8 100644
--- a/test/sys/test_ioctl.rs
+++ b/test/sys/test_ioctl.rs
@@ -1,9 +1,7 @@
#![allow(dead_code)]
-#![cfg(target_os = "linux")] // no ioctl support for osx yet
-
// Simple tests to ensure macro generated fns compile
-ioctl!(bad do_bad with 0x1234);
+ioctl!(do_bad with 0x1234);
ioctl!(none do_none with 0, 0);
ioctl!(read read_test with 0, 0; u32);
ioctl!(write write_test with 0, 0; u64);
@@ -15,44 +13,97 @@ ioctl!(readwrite buf readwritebuf_test with 0, 0; u32);
// See C code for source of values for op calculations:
// https://gist.github.com/posborne/83ea6880770a1aef332e
-#[test]
-fn test_op_none() {
- assert_eq!(io!(b'q', 10), 0x0000710A);
- assert_eq!(io!(b'a', 255), 0x000061FF);
-}
+#[cfg(any(target_os = "linux", target_os = "android"))]
+mod linux {
+ #[test]
+ fn test_op_none() {
+ assert_eq!(io!(b'q', 10), 0x0000710A);
+ assert_eq!(io!(b'a', 255), 0x000061FF);
+ }
-#[test]
-fn test_op_write() {
- assert_eq!(iow!(b'z', 10, 1), 0x40017A0A);
- assert_eq!(iow!(b'z', 10, 512), 0x42007A0A);
-}
+ #[test]
+ fn test_op_write() {
+ assert_eq!(iow!(b'z', 10, 1), 0x40017A0A);
+ assert_eq!(iow!(b'z', 10, 512), 0x42007A0A);
+ }
-#[cfg(target_pointer_width = "64")]
-#[test]
-fn test_op_write_64() {
- assert_eq!(iow!(b'z', 10, (1 as u64) << 32), 0x40007A0A);
-}
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_write_64() {
+ assert_eq!(iow!(b'z', 10, (1 as u64) << 32), 0x40007A0A);
+ }
-#[test]
-fn test_op_read() {
- assert_eq!(ior!(b'z', 10, 1), 0x80017A0A);
- assert_eq!(ior!(b'z', 10, 512), 0x82007A0A);
-}
+ #[test]
+ fn test_op_read() {
+ assert_eq!(ior!(b'z', 10, 1), 0x80017A0A);
+ assert_eq!(ior!(b'z', 10, 512), 0x82007A0A);
+ }
-#[cfg(target_pointer_width = "64")]
-#[test]
-fn test_op_read_64() {
- assert_eq!(ior!(b'z', 10, (1 as u64) << 32), 0x80007A0A);
-}
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_read_64() {
+ assert_eq!(ior!(b'z', 10, (1 as u64) << 32), 0x80007A0A);
+ }
+
+ #[test]
+ fn test_op_read_write() {
+ assert_eq!(iorw!(b'z', 10, 1), 0xC0017A0A);
+ assert_eq!(iorw!(b'z', 10, 512), 0xC2007A0A);
+ }
-#[test]
-fn test_op_read_write() {
- assert_eq!(iorw!(b'z', 10, 1), 0xC0017A0A);
- assert_eq!(iorw!(b'z', 10, 512), 0xC2007A0A);
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_read_write_64() {
+ assert_eq!(iorw!(b'z', 10, (1 as u64) << 32), 0xC0007A0A);
+ }
}
-#[cfg(target_pointer_width = "64")]
-#[test]
-fn test_op_read_write_64() {
- assert_eq!(iorw!(b'z', 10, (1 as u64) << 32), 0xC0007A0A);
+#[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+mod bsd {
+ #[test]
+ fn test_op_none() {
+ assert_eq!(io!(b'q', 10), 0x2000710A);
+ assert_eq!(io!(b'a', 255), 0x200061FF);
+ }
+
+ #[test]
+ fn test_op_write() {
+ assert_eq!(iow!(b'z', 10, 1), 0x80017A0A);
+ assert_eq!(iow!(b'z', 10, 512), 0x82007A0A);
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_write_64() {
+ assert_eq!(iow!(b'z', 10, (1 as u64) << 32), 0x80007A0A);
+ }
+
+ #[test]
+ fn test_op_read() {
+ assert_eq!(ior!(b'z', 10, 1), 0x40017A0A);
+ assert_eq!(ior!(b'z', 10, 512), 0x42007A0A);
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_read_64() {
+ assert_eq!(ior!(b'z', 10, (1 as u64) << 32), 0x40007A0A);
+ }
+
+ #[test]
+ fn test_op_read_write() {
+ assert_eq!(iorw!(b'z', 10, 1), 0xC0017A0A);
+ assert_eq!(iorw!(b'z', 10, 512), 0xC2007A0A);
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_read_write_64() {
+ assert_eq!(iorw!(b'z', 10, (1 as u64) << 32), 0xC0007A0A);
+ }
}
diff --git a/test/sys/test_select.rs b/test/sys/test_select.rs
index 68908670..53990f65 100644
--- a/test/sys/test_select.rs
+++ b/test/sys/test_select.rs
@@ -1,5 +1,5 @@
use nix::sys::select::{FdSet, FD_SETSIZE, select};
-use nix::sys::time::TimeVal;
+use nix::sys::time::{TimeVal, TimeValLike};
use nix::unistd::{write, pipe};
#[test]
@@ -41,7 +41,7 @@ fn test_select() {
fd_set.insert(r1);
fd_set.insert(r2);
- let mut timeout = TimeVal::seconds(1);
+ let mut timeout = TimeVal::seconds(10);
assert_eq!(1, select(r2 + 1,
Some(&mut fd_set),
None,
diff --git a/test/sys/test_signal.rs b/test/sys/test_signal.rs
new file mode 100644
index 00000000..4084a0da
--- /dev/null
+++ b/test/sys/test_signal.rs
@@ -0,0 +1,7 @@
+use nix::unistd::*;
+use nix::sys::signal::*;
+
+#[test]
+fn test_kill_none() {
+ kill(getpid(), None).ok().expect("Should be able to send signal to myself.");
+}
diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs
index bada1120..b5465aa0 100644
--- a/test/sys/test_socket.rs
+++ b/test/sys/test_socket.rs
@@ -3,8 +3,7 @@ use std::mem;
use std::net::{self, Ipv6Addr, SocketAddr, SocketAddrV6};
use std::path::Path;
use std::str::FromStr;
-use std::os::unix::io::{AsRawFd, RawFd};
-use ports::localhost;
+use std::os::unix::io::RawFd;
use libc::c_char;
#[test]
@@ -64,13 +63,18 @@ pub fn test_path_to_sock_addr() {
#[test]
pub fn test_getsockname() {
- use std::net::TcpListener;
-
- let addr = localhost();
- let sock = TcpListener::bind(&*addr).unwrap();
- let res = getsockname(sock.as_raw_fd()).unwrap();
-
- assert_eq!(addr, res.to_str());
+ use nix::sys::socket::{socket, AddressFamily, SockType, SockFlag};
+ use nix::sys::socket::{bind, SockAddr};
+ use tempdir::TempDir;
+
+ let tempdir = TempDir::new("test_getsockname").unwrap();
+ let sockname = tempdir.path().join("sock");
+ let sock = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(),
+ 0).expect("socket failed");
+ let sockaddr = SockAddr::new_unix(&sockname).unwrap();
+ bind(sock, &sockaddr).expect("bind failed");
+ assert_eq!(sockaddr.to_str(),
+ getsockname(sock).expect("getsockname failed").to_str());
}
#[test]
@@ -140,3 +144,56 @@ pub fn test_scm_rights() {
close(received_r).unwrap();
close(w).unwrap();
}
+
+// Test creating and using named unix domain sockets
+#[test]
+pub fn test_unixdomain() {
+ use nix::sys::socket::{AddressFamily, SockType, SockFlag};
+ use nix::sys::socket::{bind, socket, connect, listen, accept, SockAddr};
+ use nix::unistd::{read, write, close};
+ use std::thread;
+ use tempdir::TempDir;
+
+ let tempdir = TempDir::new("test_unixdomain").unwrap();
+ let sockname = tempdir.path().join("sock");
+ let s1 = socket(AddressFamily::Unix, SockType::Stream,
+ SockFlag::empty(), 0).expect("socket failed");
+ let sockaddr = SockAddr::new_unix(&sockname).unwrap();
+ bind(s1, &sockaddr).expect("bind failed");
+ listen(s1, 10).expect("listen failed");
+
+ let thr = thread::spawn(move || {
+ let s2 = socket(AddressFamily::Unix, SockType::Stream,
+ SockFlag::empty(), 0).expect("socket failed");
+ connect(s2, &sockaddr).expect("connect failed");
+ write(s2, b"hello").expect("write failed");
+ close(s2).unwrap();
+ });
+
+ let s3 = accept(s1).expect("accept failed");
+
+ let mut buf = [0;5];
+ read(s3, &mut buf).unwrap();
+ close(s3).unwrap();
+ close(s1).unwrap();
+ thr.join().unwrap();
+
+ assert_eq!(&buf[..], b"hello");
+}
+
+// Test creating and using named system control sockets
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+#[test]
+pub fn test_syscontrol() {
+ use nix::{Errno, Error};
+ use nix::sys::socket::{AddressFamily, SockType, SockFlag};
+ use nix::sys::socket::{socket, SockAddr};
+ use nix::sys::socket::SYSPROTO_CONTROL;
+
+ let fd = socket(AddressFamily::System, SockType::Datagram, SockFlag::empty(), SYSPROTO_CONTROL).expect("socket failed");
+ let _sockaddr = SockAddr::new_sys_control(fd, "com.apple.net.utun_control", 0).expect("resolving sys_control name failed");
+ assert_eq!(SockAddr::new_sys_control(fd, "foo.bar.lol", 0).err(), Some(Error::Sys(Errno::ENOENT)));
+
+ // requires root privileges
+ // connect(fd, &sockaddr).expect("connect failed");
+}
diff --git a/test/sys/test_uio.rs b/test/sys/test_uio.rs
index 27de48be..90cda56f 100644
--- a/test/sys/test_uio.rs
+++ b/test/sys/test_uio.rs
@@ -2,10 +2,11 @@ use nix::sys::uio::*;
use nix::unistd::*;
use rand::{thread_rng, Rng};
use std::{cmp, iter};
-use std::fs::{OpenOptions, remove_file};
+use std::fs::{OpenOptions};
use std::os::unix::io::AsRawFd;
use tempdir::TempDir;
+use tempfile::tempfile;
#[test]
fn test_writev() {
@@ -99,9 +100,7 @@ fn test_readv() {
fn test_pwrite() {
use std::io::Read;
- let path = "pwrite_test_file";
- let mut file = OpenOptions::new().write(true).read(true).create(true)
- .truncate(true).open(path).unwrap();
+ let mut file = tempfile().unwrap();
let buf = [1u8;8];
assert_eq!(Ok(8), pwrite(file.as_raw_fd(), &buf, 8));
let mut file_content = Vec::new();
@@ -109,8 +108,6 @@ fn test_pwrite() {
let mut expected = vec![0u8;8];
expected.extend(vec![1;8]);
assert_eq!(file_content, expected);
-
- remove_file(path).unwrap();
}
#[test]
diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs
index c2112bac..d8ac94e4 100644
--- a/test/sys/test_wait.rs
+++ b/test/sys/test_wait.rs
@@ -9,7 +9,7 @@ fn test_wait_signal() {
match fork() {
Ok(Child) => pause().unwrap_or(()),
Ok(Parent { child }) => {
- kill(child, SIGKILL).ok().expect("Error: Kill Failed");
+ kill(child, Some(SIGKILL)).ok().expect("Error: Kill Failed");
assert_eq!(waitpid(child, None), Ok(WaitStatus::Signaled(child, SIGKILL, false)));
},
// panic, fork should never fail unless there is a serious problem with the OS
diff --git a/test/test.rs b/test/test.rs
index 045f8f18..d10970d6 100644
--- a/test/test.rs
+++ b/test/test.rs
@@ -22,30 +22,6 @@ mod test_mq;
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod test_poll;
-mod ports {
- use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
- use std::sync::atomic::Ordering::SeqCst;
-
- // Helper for getting a unique port for the task run
- // TODO: Reuse ports to not spam the system
- static mut NEXT_PORT: AtomicUsize = ATOMIC_USIZE_INIT;
- const FIRST_PORT: usize = 18080;
-
- pub fn next_port() -> usize {
- unsafe {
- // If the atomic was never used, set it to the initial port
- NEXT_PORT.compare_and_swap(0, FIRST_PORT, SeqCst);
-
- // Get and increment the port list
- NEXT_PORT.fetch_add(1, SeqCst)
- }
- }
-
- pub fn localhost() -> String {
- format!("127.0.0.1:{}", next_port())
- }
-}
-
use nixtest::assert_size_of;
#[test]
diff --git a/test/test_mount.rs b/test/test_mount.rs
index 7ddbce9f..42670216 100644
--- a/test/test_mount.rs
+++ b/test/test_mount.rs
@@ -128,12 +128,6 @@ exit 23";
}
pub fn test_mount_bind() {
- use std::env;
- if env::var("CI").is_ok() && env::var("TRAVIS").is_ok() {
- print!("Travis does not allow bind mounts, skipping.");
- return;
- }
-
let tempdir = TempDir::new("nix-test_mount")
.unwrap_or_else(|e| panic!("tempdir failed: {}", e));
let file_name = "test";
diff --git a/test/test_unistd.rs b/test/test_unistd.rs
index 856693f6..76ab442a 100644
--- a/test/test_unistd.rs
+++ b/test/test_unistd.rs
@@ -6,6 +6,7 @@ use nix::sys::wait::*;
use nix::sys::stat;
use std::iter;
use std::ffi::CString;
+use std::fs::File;
use std::io::{Write, Read};
use std::os::unix::prelude::*;
use std::env::current_dir;
@@ -142,20 +143,40 @@ macro_rules! execve_test_factory(
);
#[test]
+fn test_fchdir() {
+ let tmpdir = TempDir::new("test_fchdir").unwrap();
+ let tmpdir_path = tmpdir.path().canonicalize().unwrap();
+ let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd();
+ let olddir_path = getcwd().unwrap();
+ let olddir_fd = File::open(&olddir_path).unwrap().into_raw_fd();
+
+ assert!(fchdir(tmpdir_fd).is_ok());
+ assert_eq!(getcwd().unwrap(), tmpdir_path);
+
+ assert!(fchdir(olddir_fd).is_ok());
+ assert_eq!(getcwd().unwrap(), olddir_path);
+
+ assert!(close(olddir_fd).is_ok());
+ assert!(close(tmpdir_fd).is_ok());
+}
+
+#[test]
fn test_getcwd() {
- let mut tmp_dir = TempDir::new("test_getcwd").unwrap().into_path();
- assert!(chdir(tmp_dir.as_path()).is_ok());
+ let tmp_dir = TempDir::new("test_getcwd").unwrap();
+ assert!(chdir(tmp_dir.path()).is_ok());
assert_eq!(getcwd().unwrap(), current_dir().unwrap());
// make path 500 chars longer so that buffer doubling in getcwd kicks in.
// Note: One path cannot be longer than 255 bytes (NAME_MAX)
// whole path cannot be longer than PATH_MAX (usually 4096 on linux, 1024 on macos)
+ let mut inner_tmp_dir = tmp_dir.path().to_path_buf();
for _ in 0..5 {
let newdir = iter::repeat("a").take(100).collect::<String>();
- tmp_dir.push(newdir);
- assert!(mkdir(tmp_dir.as_path(), stat::S_IRWXU).is_ok());
+ //inner_tmp_dir = inner_tmp_dir.join(newdir).path();
+ inner_tmp_dir.push(newdir);
+ assert!(mkdir(inner_tmp_dir.as_path(), stat::S_IRWXU).is_ok());
}
- assert!(chdir(tmp_dir.as_path()).is_ok());
+ assert!(chdir(inner_tmp_dir.as_path()).is_ok());
assert_eq!(getcwd().unwrap(), current_dir().unwrap());
}