diff options
-rw-r--r-- | .travis.yml | 92 | ||||
-rw-r--r-- | CHANGELOG.md | 132 | ||||
-rw-r--r-- | CONTRIBUTING.md | 7 | ||||
-rw-r--r-- | CONVENTIONS.md | 5 | ||||
-rw-r--r-- | Cargo.toml | 6 | ||||
-rw-r--r-- | README.md | 40 | ||||
-rw-r--r-- | RELEASE_PROCEDURE.md | 41 | ||||
-rw-r--r-- | ci/run-travis.sh | 1 | ||||
-rwxr-xr-x | ci/run.sh | 2 | ||||
-rw-r--r-- | src/fcntl.rs | 4 | ||||
-rw-r--r-- | src/lib.rs | 4 | ||||
-rw-r--r-- | src/mqueue.rs | 178 | ||||
-rw-r--r-- | src/poll.rs | 92 | ||||
-rw-r--r-- | src/sched.rs | 226 | ||||
-rw-r--r-- | src/sys/event.rs | 2 | ||||
-rw-r--r-- | src/sys/eventfd.rs | 26 | ||||
-rw-r--r-- | src/sys/mman.rs | 2 | ||||
-rw-r--r-- | src/sys/mod.rs | 3 | ||||
-rw-r--r-- | src/sys/reboot.rs | 43 | ||||
-rw-r--r-- | src/sys/select.rs | 1 | ||||
-rw-r--r-- | src/sys/signal.rs | 254 | ||||
-rw-r--r-- | src/sys/socket/consts.rs | 3 | ||||
-rw-r--r-- | src/sys/socket/ffi.rs | 14 | ||||
-rw-r--r-- | src/sys/socket/mod.rs | 32 | ||||
-rw-r--r-- | src/sys/socket/sockopt.rs | 62 | ||||
-rw-r--r-- | src/sys/wait.rs | 61 | ||||
-rw-r--r-- | src/ucontext.rs | 12 | ||||
-rw-r--r-- | src/unistd.rs | 181 | ||||
-rw-r--r-- | test/test_mount.rs | 6 | ||||
-rw-r--r-- | test/test_mq.rs | 8 | ||||
-rw-r--r-- | test/test_poll.rs | 10 | ||||
-rw-r--r-- | test/test_signalfd.rs | 3 | ||||
-rw-r--r-- | test/test_unistd.rs | 116 |
33 files changed, 1087 insertions, 582 deletions
diff --git a/.travis.yml b/.travis.yml index 700e7fdf..cf8a20cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,35 +1,42 @@ +# +# Operating Environment +# language: rust sudo: false dist: trusty services: - docker +addons: + apt: + packages: + - gcc-multilib + - libcurl4-openssl-dev + - libelf-dev + - libdw-dev + - binutils-dev rust: - - 1.1.0 # Oldest supported version - - 1.2.0 - - 1.3.0 - - 1.4.0 - - 1.5.0 - - 1.6.0 + - 1.2.0 # Oldest supported version + - 1.7.0 + - 1.8.0 - stable - beta - nightly -script: - - bash ci/run-travis.sh - +# +# Environment Variables and Build Matrix +# env: - - ARCH=x86_64 - - ARCH=i686 + global: + - PATH=$HOME/.local/bin:$PATH + - TRAVIS_CARGO_NIGHTLY_FEATURE="" + matrix: + - ARCH=x86_64 + - ARCH=i686 -os: +os: # OSX included in build matrix explicitly - linux -addons: - apt: - packages: - - gcc-multilib - # Failures on nightly shouldn't fail the overall build. matrix: fast_finish: true @@ -43,10 +50,10 @@ matrix: rust: stable - os: osx env: ARCH=x86_64 - rust: 1.1.0 + rust: 1.2.0 - os: osx env: ARCH=i686 - rust: 1.1.0 + rust: 1.2.0 # Docker builds for other targets - os: linux env: TARGET=aarch64-unknown-linux-gnu DOCKER_IMAGE=posborne/rust-cross:arm @@ -61,7 +68,7 @@ matrix: rust: 1.7.0 sudo: true - os: linux - env: TARGET=mipsel-unknwon-linux-gnu DOCKER_IMAGE=posborne/rust-cross:mips + env: TARGET=mipsel-unknown-linux-gnu DOCKER_IMAGE=posborne/rust-cross:mips rust: 1.7.0 sudo: true - os: linux @@ -71,25 +78,30 @@ matrix: allow_failures: - rust: nightly - env: TARGET=mips-unknown-linux-gnu DOCKER_IMAGE=posborne/rust-cross:mips - - env: TARGET=mipsel-unknwon-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 -# Deploy documentation to S3 for specific branches. At some -# point, it would be nice to also support building docs for -# a specific tag -deploy: - provider: s3 - access_key_id: AKIAIGFX36YKEFRZJAXA - secret_access_key: - secure: Q10KEdtBoYxaGXtt23L00J0obv9fpVWtao8YKFEroZMOmvu8Sq2+9aTNGEQRp2OojOxuu+DjZInJlUDFhq6trmV3kpZH2BF7cNRxiZQpQ2FEmlr6ZpYN38GhcIUKdxXqVwXiASJi6j+vz6QdpaOGCs5lQC3VhM5sn49MFXNUrFU= - bucket: rustdoc - endpoint: "rustdoc.s3-website-us-east-1.amazonaws.com" - skip_cleanup: true - local-dir: target/doc - upload-dir: nix/${TRAVIS_BRANCH}/${TRAVIS_OS_NAME} - acl: public_read - on: - condition: "\"$TRAVIS_RUST_VERSION/$ARCH\" == \"1.1.0/x86_64\"" - repo: carllerche/nix-rust - branch: - - master + +# +# Build/Test/Deploy Steps +# +before_script: + - pip install 'travis-cargo<0.2' --user + +script: + - bash ci/run-travis.sh + - | + if [ "$TRAVIS_OS_NAME" = "linux" ]; then + travis-cargo --only stable doc + fi + +after_success: + - | + if [ "$TRAVIS_OS_NAME" = "linux" ] && \ + [ "$TRAVIS_RUST_VERSION" = "stable" ] && \ + [ "$ARCH" = "x86_64" ]; then + # Upload docs for stable (on master) to gh-pages + travis-cargo --only stable doc-upload + # Measure code coverage using kcov and upload to coveralls.io + travis-cargo coveralls --no-sudo --verify + fi diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..4238ed74 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,132 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +## [Unreleased] + +## [0.7.0] 2016-09-09 + +### Added +- Added `lseek` and `lseek64` in `::nix::unistd` + ([#377](https://github.com/nix-rust/nix/pull/377)) +- Added `mkdir` and `getcwd` in `::nix::unistd` + ([#416](https://github.com/nix-rust/nix/pull/416)) +- Added accessors `sigmask_mut` and `sigmask` to `UContext` in + `::nix::ucontext`. + ([#370](https://github.com/nix-rust/nix/pull/370)) +- Added `WUNTRACED` to `WaitPidFlag` in `::nix::sys::wait` for non-_linux_ + targets. + ([#379](https://github.com/nix-rust/nix/pull/379)) +- Added new module `::nix::sys::reboot` with enumeration `RebootMode` and + functions `reboot` and `set_cad_enabled`. Currently for _linux_ only. + ([#386](https://github.com/nix-rust/nix/pull/386)) +- `FdSet` in `::nix::sys::select` now also implements `Clone`. + ([#405](https://github.com/nix-rust/nix/pull/405)) +- Added `F_FULLFSYNC` to `FcntlArg` in `::nix::fcntl` for _apple_ targets. + ([#407](https://github.com/nix-rust/nix/pull/407)) +- Added `CpuSet::unset` in `::nix::sched`. + ([#402](https://github.com/nix-rust/nix/pull/402)) +- Added constructor method `new()` to `PollFd` in `::nix::poll`, in order to + allow creation of objects, after removing public access to members. + ([#399](https://github.com/nix-rust/nix/pull/399)) +- Added method `revents()` to `PollFd` in `::nix::poll`, in order to provide + read access to formerly public member `revents`. + ([#399](https://github.com/nix-rust/nix/pull/399)) +- Added `MSG_CMSG_CLOEXEC` to `MsgFlags` in `::nix::sys::socket` for _linux_ only. + ([#422](https://github.com/nix-rust/nix/pull/422)) + +### Changed +- Replaced the reexported integer constants for signals by the enumeration + `Signal` in `::nix::sys::signal`. + ([#362](https://github.com/nix-rust/nix/pull/362)) +- Renamed `EventFdFlag` to `EfdFlags` in `::nix::sys::eventfd`. + ([#383](https://github.com/nix-rust/nix/pull/383)) +- Changed the result types of `CpuSet::is_set` and `CpuSet::set` in + `::nix::sched` to `Result<bool>` and `Result<()>`, respectively. They now + return `EINVAL`, if an invalid argument for the `field` parameter is passed. + ([#402](https://github.com/nix-rust/nix/pull/402)) +- `MqAttr` in `::nix::mqueue` is now an opaque proxy for `::libc::mq_attr`, + which has the same structure as the old `MqAttr`. The field `mq_flags` of + `::libc::mq_attr` is readable using the new method `flags()` of `MqAttr`. + `MqAttr` also no longer implements `Debug`. + ([#0](https://github.com/nix-rust/nix/pull/0)) +- The parameter `msq_prio` of `mq_receive` with type `u32` in `::nix::mqueue` + was replaced by a parameter named `msg_prio` with type `&mut u32`, so that + the message priority can be obtained by the caller. + ([#0](https://github.com/nix-rust/nix/pull/0)) +- The type alias `MQd` in `::nix::queue` was replaced by the type alias + `libc::mqd_t`, both of which are aliases for the same type. + ([#0](https://github.com/nix-rust/nix/pull/0)) + +### Removed +- Type alias `SigNum` from `::nix::sys::signal`. + ([#362](https://github.com/nix-rust/nix/pull/362)) +- Type alias `CpuMask` from `::nix::shed`. + ([#402](https://github.com/nix-rust/nix/pull/402)) +- Removed public fields from `PollFd` in `::nix::poll`. (See also added method + `revents()`. + ([#399](https://github.com/nix-rust/nix/pull/399)) + +### Fixed +- Fixed the build problem for NetBSD (Note, that we currently do not support + it, so it might already be broken again). + ([#389](https://github.com/nix-rust/nix/pull/389)) +- 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)) + +## [0.6.0] 2016-06-10 + +### Added +- Added `gettid` in `::nix::unistd` for _linux_ and _android_. + ([#293](https://github.com/nix-rust/nix/pull/293)) +- Some _mips_ support in `::nix::sched` and `::nix::sys::syscall`. + ([#301](https://github.com/nix-rust/nix/pull/301)) +- Added `SIGNALFD_SIGINFO_SIZE` in `::nix::sys::signalfd`. + ([#309](https://github.com/nix-rust/nix/pull/309)) +- Added new module `::nix::ucontext` with struct `UContext`. Currently for + _linux_ only. + ([#311](https://github.com/nix-rust/nix/pull/311)) +- Added `EPOLLEXCLUSIVE` to `EpollEventKind` in `::nix::sys::epoll`. + ([#330](https://github.com/nix-rust/nix/pull/330)) +- Added `pause` to `::nix::unistd`. + ([#336](https://github.com/nix-rust/nix/pull/336)) +- Added `sleep` to `::nix::unistd`. + ([#351](https://github.com/nix-rust/nix/pull/351)) +- Added `S_IFDIR`, `S_IFLNK`, `S_IFMT` to `SFlag` in `::nix::sys::stat`. + ([#359](https://github.com/nix-rust/nix/pull/359)) +- Added `clear` and `extend` functions to `SigSet`'s implementation in + `::nix::sys::signal`. + ([#347](https://github.com/nix-rust/nix/pull/347)) +- `sockaddr_storage_to_addr` in `::nix::sys::socket` now supports `sockaddr_nl` + on _linux_ and _android_. + ([#366](https://github.com/nix-rust/nix/pull/366)) +- Added support for `SO_ORIGINAL_DST` in `::nix::sys::socket` on _linux_. + ([#367](https://github.com/nix-rust/nix/pull/367)) +- Added `SIGINFO` in `::nix::sys::signal` for the _macos_ target as well as + `SIGPWR` and `SIGSTKFLT` in `::nix::sys::signal` for non-_macos_ targets. + ([#361](https://github.com/nix-rust/nix/pull/361)) + +### Changed +- Changed the structure `IoVec` in `::nix::sys::uio`. + ([#304](https://github.com/nix-rust/nix/pull/304)) +- Replaced `CREATE_NEW_FD` by `SIGNALFD_NEW` in `::nix::sys::signalfd`. + ([#309](https://github.com/nix-rust/nix/pull/309)) +- Renamed `SaFlag` to `SaFlags` and `SigFlag` to `SigFlags` in + `::nix::sys::signal`. + ([#314](https://github.com/nix-rust/nix/pull/314)) +- Renamed `Fork` to `ForkResult` and changed its fields in `::nix::unistd`. + ([#332](https://github.com/nix-rust/nix/pull/332)) +- Added the `signal` parameter to `clone`'s signature in `::nix::sched`. + ([#344](https://github.com/nix-rust/nix/pull/344)) +- `execv`, `execve`, and `execvp` now return `Result<Void>` instead of + `Result<()>` in `::nix::unistd`. + ([#357](https://github.com/nix-rust/nix/pull/357)) + +### Fixed +- Improved the conversion from `std::net::SocketAddr` to `InetAddr` in + `::nix::sys::socket::addr`. + ([#335](https://github.com/nix-rust/nix/pull/335)) + +## [0.5.0] 2016-03-01 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 35a85ec3..9158bf1b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -60,8 +60,13 @@ pull' model described there. Please make pull requests against the `master` branch. -[pr-docs]: https://help.github.com/articles/using-pull-requests/ +If you change the API by way of adding, removing or changing something or if +you fix a bug, please add an appropriate note to the [change log][cl]. We +follow the conventions of [Keep A CHANGELOG][kacl]. +[cl]: https://github.com/nix-rust/nix/blob/master/CHANGELOG.md +[kacl]: https://github.com/olivierlacan/keep-a-changelog/tree/18adb5f5be7a898d046f6a4acb93e39dcf40c4ad +[pr-docs]: https://help.github.com/articles/using-pull-requests/ ## Testing diff --git a/CONVENTIONS.md b/CONVENTIONS.md index b3a68d16..8abec905 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -9,6 +9,11 @@ conventions we try to follow have been documented here. If you find an instance of either, feel free to remedy the flaw by opening a pull request with appropriate changes or additions. +## Change Log + +We follow the conventions laid out in [Keep A CHANGELOG][kacl]. + +[kacl]: https://github.com/olivierlacan/keep-a-changelog/tree/18adb5f5be7a898d046f6a4acb93e39dcf40c4ad ## libc constants, functions and structs @@ -2,7 +2,7 @@ name = "nix" description = "Rust friendly bindings to *nix APIs" -version = "0.5.1-pre" +version = "0.7.1-pre" authors = ["Carl Lerche <me@carllerche.com>"] homepage = "https://github.com/nix-rust/nix" repository = "https://github.com/nix-rust/nix" @@ -23,7 +23,7 @@ preadv_pwritev = [] signalfd = [] [dependencies] -libc = "0.2.8" +libc = { git = "https://github.com/rust-lang/libc" } bitflags = "0.4" cfg-if = "0.1.0" void = "1.0.2" @@ -36,7 +36,7 @@ semver = "0.1.20" # Old version for compatibility with rustc_version. rand = "0.3.8" tempdir = "0.3" tempfile = "2" -nix-test = { path = "nix-test" } +nix-test = { path = "nix-test", version = "0.0.1" } [[test]] name = "test" @@ -1,18 +1,32 @@ # Rust bindings to *nix APIs -Rust friendly bindings to various *nix platform APIs (Linux, Darwin, +[![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) + +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 what can be while still providing platform specific APIs. -[![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) +For many system APIs, Nix provides a safe alternative to the unsafe APIs +exposed by the [libc crate](https://github.com/rust-lang/libc). This is done by +wrapping the libc functionality with types/abstractions that enforce legal/safe +usage. -## Documentation -API documentation generated by rustdoc: +As an example of what Nix provides, examine the differences between what is +exposed by libc and nix for the +[gethostname](http://man7.org/linux/man-pages/man2/gethostname.2.html) system +call: -- Linux: [master](http://rustdoc.s3-website-us-east-1.amazonaws.com/nix/master/linux/nix/) -- OS X: [master](http://rustdoc.s3-website-us-east-1.amazonaws.com/nix/master/osx/nix/) +```rust,ignore +// libc api (unsafe, requires handling return code/errno) +pub unsafe extern fn gethostname(name: *mut c_char, len: size_t) -> c_int; + +// nix api (returns a nix::Result) +pub fn gethostname(name: &mut [u8]) -> Result<()>; +``` ## Usage @@ -20,15 +34,19 @@ To use `nix`, first add this to your `Cargo.toml`: ```toml [dependencies] -nix = "0.5.0" +nix = "0.7.0" ``` Then, add this to your crate root: -```rust +```rust,ignore extern crate nix; ``` - ## Contributing -See [CONTRIBUTING](CONTRIBUTING.md). +Contributions are very welcome. Please See [CONTRIBUTING](CONTRIBUTING.md) for +additional details. + +## License + +Nix is licensed under the MIT license. See [LICENSE](LICENSE) for more details. diff --git a/RELEASE_PROCEDURE.md b/RELEASE_PROCEDURE.md new file mode 100644 index 00000000..14496f23 --- /dev/null +++ b/RELEASE_PROCEDURE.md @@ -0,0 +1,41 @@ +This document lists the steps that lead to a successful release of the Nix +library. + +# Before Release + +Based on changes since the last release, pick a new version number +following semver conventions. For nix, a change that drops support for +some Rust versions counts as a breaking change, and requires a major bump. + +The release is prepared as follows: + +- Ask for a new libc version if, necessary. It usually is. +- Make a commit with a message like "Release v0.8.3" with the following + changes: + - In `CHANGELOG.md`, rename the Unreleased section to the new version + followed by the date of the release. + - In `Cargo.toml`, update the version to the new version. + - In `Cargo.toml`, change the libc dependency to the latest version. + - In `README.md`, update the version in the Usage section to the new + version. +- Make a pull request. +- Once the PR is merged, tag the merge commit, e.g. `git tag v0.8.3 + $MERGE_COMMIT_SHA1`. +- Push the tag, e.g. `git push origin v0.8.3`. + +# Create Release + +- Checkout the tag. +- Publish to crates.io with `cargo publish`. + +# After Release + +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.tml`, revert the libc dependency to its git master branch. +- Commit with a message like "Bump to v0.8.4-dev" +- Make a pull request. diff --git a/ci/run-travis.sh b/ci/run-travis.sh index 3b4ee138..5be6372e 100644 --- a/ci/run-travis.sh +++ b/ci/run-travis.sh @@ -33,7 +33,6 @@ fi if [ "$DOCKER_IMAGE" = "" ]; then export RUST_TEST_THREADS=1 curl -sSL "https://raw.githubusercontent.com/carllerche/travis-rust-matrix/master/test" | bash - cargo doc --no-deps else export RUST_VERSION=${TRAVIS_RUST_VERSION} export RUST_TARGET=${TARGET} @@ -6,7 +6,7 @@ set -e # This should only be run in a docker container, so verify that -if [ ! -f /.dockerinit ]; then +if [ ! $(pidof $0) = "1" ]; then echo "run.sh should only be executed in a docker container" echo "and that does not appear to be the case. Maybe you meant" echo "to execute the tests via run-all.sh or run-docker.sh." diff --git a/src/fcntl.rs b/src/fcntl.rs index 75e12549..1d9ba499 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -46,6 +46,8 @@ pub enum FcntlArg<'a> { F_ADD_SEALS(SealFlag), #[cfg(target_os = "linux")] F_GET_SEALS, + #[cfg(any(target_os = "macos", target_os = "ios"))] + F_FULLFSYNC, // TODO: Rest of flags } @@ -69,6 +71,8 @@ pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> { F_ADD_SEALS(flag) => libc::fcntl(fd, ffi::F_ADD_SEALS, flag.bits()), #[cfg(target_os = "linux")] F_GET_SEALS => libc::fcntl(fd, ffi::F_GET_SEALS), + #[cfg(any(target_os = "macos", target_os = "ios"))] + F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC), #[cfg(any(target_os = "linux", target_os = "android"))] _ => unimplemented!() } @@ -8,7 +8,7 @@ // latest bitflags triggers a rustc bug with cross-crate macro expansions causing dead_code // warnings even though the macro expands into something with allow(dead_code) #![allow(dead_code)] -#![deny(warnings)] +#![cfg_attr(test, deny(warnings))] #[macro_use] extern crate bitflags; @@ -40,7 +40,7 @@ pub mod fcntl; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod mount; -#[cfg(any(target_os = "linux"))] +#[cfg(target_os = "linux")] pub mod mqueue; #[cfg(any(target_os = "linux", target_os = "macos"))] diff --git a/src/mqueue.rs b/src/mqueue.rs index b8a2250e..9bf6e77e 100644 --- a/src/mqueue.rs +++ b/src/mqueue.rs @@ -4,114 +4,117 @@ use {Errno, Result}; -use libc::{c_int, c_long, c_char, size_t, mode_t}; +use libc::{self, c_char, c_long, mode_t, mqd_t, size_t}; use std::ffi::CString; use sys::stat::Mode; -use std::ptr; - -pub use self::consts::*; - -pub type MQd = c_int; - -#[cfg(target_os = "linux")] -mod consts { - use libc::c_int; - - bitflags!( - flags MQ_OFlag: c_int { - const O_RDONLY = 0o00000000, - const O_WRONLY = 0o00000001, - const O_RDWR = 0o00000002, - const O_CREAT = 0o00000100, - const O_EXCL = 0o00000200, - const O_NONBLOCK = 0o00004000, - const O_CLOEXEC = 0o02000000, - } - ); - - bitflags!( - flags FdFlag: c_int { - const FD_CLOEXEC = 1 - } - ); +use std::mem; + +libc_bitflags!{ + flags MQ_OFlag: libc::c_int { + O_RDONLY, + O_WRONLY, + O_RDWR, + O_CREAT, + O_EXCL, + O_NONBLOCK, + O_CLOEXEC, + } } -mod ffi { - use libc::{c_char, size_t, ssize_t, c_uint, c_int}; - use super::MQd; - use super::MqAttr; - - #[allow(improper_ctypes)] - extern "C" { - pub fn mq_open(name: *const c_char, oflag: c_int, ...) -> MQd; - - pub fn mq_close (mqd: MQd) -> c_int; - - pub fn mq_unlink(name: *const c_char) -> c_int; - - pub fn mq_receive (mqd: MQd, msg_ptr: *const c_char, msg_len: size_t, msq_prio: *const c_uint) -> ssize_t; - - pub fn mq_send (mqd: MQd, msg_ptr: *const c_char, msg_len: size_t, msq_prio: c_uint) -> c_int; - - pub fn mq_getattr(mqd: MQd, attr: *mut MqAttr) -> c_int; - - pub fn mq_setattr(mqd: MQd, newattr: *const MqAttr, oldattr: *mut MqAttr) -> c_int; +libc_bitflags!{ + flags FdFlag: libc::c_int { + FD_CLOEXEC, } } #[repr(C)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy)] pub struct MqAttr { - pub mq_flags: c_long, - pub mq_maxmsg: c_long, - pub mq_msgsize: c_long, - pub mq_curmsgs: c_long, - pad: [c_long; 4] + mq_attr: libc::mq_attr, } -impl MqAttr { - pub fn new(mq_flags: c_long, mq_maxmsg: c_long, mq_msgsize: c_long, mq_curmsgs: c_long) -> MqAttr { - MqAttr { mq_flags: mq_flags, mq_maxmsg: mq_maxmsg, mq_msgsize: mq_msgsize, mq_curmsgs: mq_curmsgs, pad: [0; 4] } - } +impl PartialEq<MqAttr> for MqAttr { + fn eq(&self, other: &MqAttr) -> bool { + let self_attr = self.mq_attr; + let other_attr = other.mq_attr; + self_attr.mq_flags == other_attr.mq_flags && self_attr.mq_maxmsg == other_attr.mq_maxmsg && + self_attr.mq_msgsize == other_attr.mq_msgsize && + self_attr.mq_curmsgs == other_attr.mq_curmsgs + } } +impl MqAttr { + pub fn new(mq_flags: c_long, + mq_maxmsg: c_long, + mq_msgsize: c_long, + mq_curmsgs: c_long) + -> MqAttr { + let mut attr = unsafe { mem::uninitialized::<libc::mq_attr>() }; + attr.mq_flags = mq_flags; + attr.mq_maxmsg = mq_maxmsg; + attr.mq_msgsize = mq_msgsize; + attr.mq_curmsgs = mq_curmsgs; + MqAttr { mq_attr: attr } + } -pub fn mq_open(name: &CString, oflag: MQ_OFlag, mode: Mode, attr: Option<&MqAttr>) -> Result<MQd> { - let attr_p = attr.map(|attr| attr as *const MqAttr).unwrap_or(ptr::null()); - let res = unsafe { ffi::mq_open(name.as_ptr(), oflag.bits(), mode.bits() as mode_t, attr_p) }; + pub fn flags(&self) -> c_long { + self.mq_attr.mq_flags + } +} + +pub fn mq_open(name: &CString, + oflag: MQ_OFlag, + mode: Mode, + attr: Option<&MqAttr>) + -> Result<mqd_t> { + let res = match attr { + Some(mq_attr) => unsafe { + libc::mq_open(name.as_ptr(), + oflag.bits(), + mode.bits() as mode_t, + &mq_attr.mq_attr as *const libc::mq_attr) + }, + None => unsafe { libc::mq_open(name.as_ptr(), oflag.bits()) }, + }; Errno::result(res) } pub fn mq_unlink(name: &CString) -> Result<()> { - let res = unsafe { ffi::mq_unlink(name.as_ptr()) }; + let res = unsafe { libc::mq_unlink(name.as_ptr()) }; Errno::result(res).map(drop) } -pub fn mq_close(mqdes: MQd) -> Result<()> { - let res = unsafe { ffi::mq_close(mqdes) }; +pub fn mq_close(mqdes: mqd_t) -> Result<()> { + let res = unsafe { libc::mq_close(mqdes) }; Errno::result(res).map(drop) } - -pub fn mq_receive(mqdes: MQd, message: &mut [u8], msq_prio: u32) -> Result<usize> { +pub fn mq_receive(mqdes: mqd_t, message: &mut [u8], msg_prio: &mut u32) -> Result<usize> { let len = message.len() as size_t; - let res = unsafe { ffi::mq_receive(mqdes, message.as_mut_ptr() as *mut c_char, len, &msq_prio) }; - + let res = unsafe { + libc::mq_receive(mqdes, + message.as_mut_ptr() as *mut c_char, + len, + msg_prio as *mut u32) + }; Errno::result(res).map(|r| r as usize) } -pub fn mq_send(mqdes: MQd, message: &[u8], msq_prio: u32) -> Result<()> { - let res = unsafe { ffi::mq_send(mqdes, message.as_ptr() as *const c_char, message.len(), msq_prio) }; - +pub fn mq_send(mqdes: mqd_t, message: &[u8], msq_prio: u32) -> Result<()> { + let res = unsafe { + libc::mq_send(mqdes, + message.as_ptr() as *const c_char, + message.len(), + msq_prio) + }; Errno::result(res).map(drop) } -pub fn mq_getattr(mqd: MQd) -> Result<MqAttr> { - let mut attr = MqAttr::new(0, 0, 0, 0); - let res = unsafe { ffi::mq_getattr(mqd, &mut attr) }; - try!(Errno::result(res)); - Ok(attr) +pub fn mq_getattr(mqd: mqd_t) -> Result<MqAttr> { + let mut attr = unsafe { mem::uninitialized::<libc::mq_attr>() }; + let res = unsafe { libc::mq_getattr(mqd, &mut attr) }; + Errno::result(res).map(|_| MqAttr { mq_attr: attr }) } /// Set the attributes of the message queue. Only `O_NONBLOCK` can be set, everything else will be ignored @@ -119,27 +122,32 @@ pub fn mq_getattr(mqd: MQd) -> Result<MqAttr> { /// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()` convenience functions as they are easier to use /// /// [Further reading](http://man7.org/linux/man-pages/man3/mq_setattr.3.html) -pub fn mq_setattr(mqd: MQd, newattr: &MqAttr) -> Result<MqAttr> { - let mut attr = MqAttr::new(0, 0, 0, 0); - let res = unsafe { ffi::mq_setattr(mqd, newattr as *const MqAttr, &mut attr) }; - try!(Errno::result(res)); - Ok(attr) +pub fn mq_setattr(mqd: mqd_t, newattr: &MqAttr) -> Result<MqAttr> { + let mut attr = unsafe { mem::uninitialized::<libc::mq_attr>() }; + let res = unsafe { libc::mq_setattr(mqd, &newattr.mq_attr as *const libc::mq_attr, &mut attr) }; + Errno::result(res).map(|_| MqAttr { mq_attr: attr }) } /// Convenience function. /// Sets the `O_NONBLOCK` attribute for a given message queue descriptor /// Returns the old attributes -pub fn mq_set_nonblock(mqd: MQd) -> Result<(MqAttr)> { +pub fn mq_set_nonblock(mqd: mqd_t) -> Result<(MqAttr)> { let oldattr = try!(mq_getattr(mqd)); - let newattr = MqAttr::new(O_NONBLOCK.bits() as c_long, oldattr.mq_maxmsg, oldattr.mq_msgsize, oldattr.mq_curmsgs); + let newattr = MqAttr::new(O_NONBLOCK.bits() as c_long, + oldattr.mq_attr.mq_maxmsg, + oldattr.mq_attr.mq_msgsize, + oldattr.mq_attr.mq_curmsgs); mq_setattr(mqd, &newattr) } /// Convenience function. /// Removes `O_NONBLOCK` attribute for a given message queue descriptor /// Returns the old attributes -pub fn mq_remove_nonblock(mqd: MQd) -> Result<(MqAttr)> { +pub fn mq_remove_nonblock(mqd: mqd_t) -> Result<(MqAttr)> { let oldattr = try!(mq_getattr(mqd)); - let newattr = MqAttr::new(0, oldattr.mq_maxmsg, oldattr.mq_msgsize, oldattr.mq_curmsgs); + let newattr = MqAttr::new(0, + oldattr.mq_attr.mq_maxmsg, + oldattr.mq_attr.mq_msgsize, + oldattr.mq_attr.mq_curmsgs); mq_setattr(mqd, &newattr) } diff --git a/src/poll.rs b/src/poll.rs index 88ca9825..6ba9f5e4 100644 --- a/src/poll.rs +++ b/src/poll.rs @@ -1,74 +1,48 @@ -use libc::c_int; +use libc; use {Errno, Result}; -pub use self::ffi::PollFd; -pub use self::ffi::consts::*; - -mod ffi { - use libc::c_int; - pub use self::consts::*; - - #[derive(Clone, Copy, Debug)] - #[repr(C)] - pub struct PollFd { - pub fd: c_int, - pub events: EventFlags, - pub revents: EventFlags - } - - #[cfg(target_os = "linux")] - pub mod consts { - use libc::{c_short, c_ulong}; +#[repr(C)] +#[derive(Clone, Copy)] +pub struct PollFd { + pollfd: libc::pollfd, +} - bitflags! { - flags EventFlags: c_short { - const POLLIN = 0x001, - const POLLPRI = 0x002, - const POLLOUT = 0x004, - const POLLRDNORM = 0x040, - const POLLWRNORM = 0x100, - const POLLRDBAND = 0x080, - const POLLWRBAND = 0x200, - const POLLERR = 0x008, - const POLLHUP = 0x010, - const POLLNVAL = 0x020, - } +impl PollFd { + pub fn new(fd: libc::c_int, events: EventFlags, revents: EventFlags) -> PollFd { + PollFd { + pollfd: libc::pollfd { + fd: fd, + events: events.bits(), + revents: revents.bits(), + }, } - - pub type nfds_t = c_ulong; } - #[cfg(target_os = "macos")] - pub mod consts { - use libc::{c_short, c_uint}; - - bitflags! { - flags EventFlags: c_short { - const POLLIN = 0x0001, - const POLLPRI = 0x0002, - const POLLOUT = 0x0004, - const POLLRDNORM = 0x0040, - const POLLWRNORM = 0x0004, - const POLLRDBAND = 0x0080, - const POLLWRBAND = 0x0100, - const POLLERR = 0x0008, - const POLLHUP = 0x0010, - const POLLNVAL = 0x0020, - } - } - - pub type nfds_t = c_uint; + pub fn revents(&self) -> Option<EventFlags> { + EventFlags::from_bits(self.pollfd.revents) } +} - #[allow(improper_ctypes)] - extern { - pub fn poll(fds: *mut PollFd, nfds: nfds_t, timeout: c_int) -> c_int; +libc_bitflags! { + flags EventFlags: libc::c_short { + POLLIN, + POLLPRI, + POLLOUT, + POLLRDNORM, + POLLWRNORM, + POLLRDBAND, + POLLWRBAND, + POLLERR, + POLLHUP, + POLLNVAL, } } -pub fn poll(fds: &mut [PollFd], timeout: c_int) -> Result<c_int> { +pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result<libc::c_int> { let res = unsafe { - ffi::poll(fds.as_mut_ptr(), fds.len() as ffi::nfds_t, timeout) + libc::poll(fds.as_mut_ptr() as *mut libc::pollfd, + fds.len() as libc::nfds_t, + timeout) }; Errno::result(res) diff --git a/src/sched.rs b/src/sched.rs index 934ce13f..91a7c42a 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -1,204 +1,110 @@ use std::mem; use std::os::unix::io::RawFd; use std::option::Option; -use libc::{self, c_int, c_void, c_ulong, pid_t}; -use {Errno, Result}; +use libc::{self, c_int, c_void, pid_t}; +use {Errno, Error, Result}; // For some functions taking with a parameter of type CloneFlags, // only a subset of these flags have an effect. -bitflags!{ - flags CloneFlags: c_int { - const CLONE_VM = libc::CLONE_VM, - const CLONE_FS = libc::CLONE_FS, - const CLONE_FILES = libc::CLONE_FILES, - const CLONE_SIGHAND = libc::CLONE_SIGHAND, - const CLONE_PTRACE = libc::CLONE_PTRACE, - const CLONE_VFORK = libc::CLONE_VFORK, - const CLONE_PARENT = libc::CLONE_PARENT, - const CLONE_THREAD = libc::CLONE_THREAD, - const CLONE_NEWNS = libc::CLONE_NEWNS, - const CLONE_SYSVSEM = libc::CLONE_SYSVSEM, - const CLONE_SETTLS = libc::CLONE_SETTLS, - const CLONE_PARENT_SETTID = libc::CLONE_PARENT_SETTID, - const CLONE_CHILD_CLEARTID = libc::CLONE_CHILD_CLEARTID, - const CLONE_DETACHED = libc::CLONE_DETACHED, - const CLONE_UNTRACED = libc::CLONE_UNTRACED, - const CLONE_CHILD_SETTID = libc::CLONE_CHILD_SETTID, - // TODO: Once, we use a version containing - // https://github.com/rust-lang-nursery/libc/pull/147 - // get rid of the casts. - const CLONE_NEWUTS = libc::CLONE_NEWUTS as c_int, - const CLONE_NEWIPC = libc::CLONE_NEWIPC as c_int, - const CLONE_NEWUSER = libc::CLONE_NEWUSER as c_int, - const CLONE_NEWPID = libc::CLONE_NEWPID as c_int, - const CLONE_NEWNET = libc::CLONE_NEWNET as c_int, - const CLONE_IO = libc::CLONE_IO as c_int, - } -} - -// Support a maximum CPU set of 1024 nodes -#[cfg(all(target_arch = "x86_64", target_os = "linux"))] -mod cpuset_attribs { - use super::CpuMask; - pub const CPU_SETSIZE: usize = 1024; - pub const CPU_MASK_BITS: usize = 64; - - #[inline] - pub fn set_cpu_mask_flag(cur: CpuMask, bit: usize) -> CpuMask { - cur | (1u64 << bit) - } - - #[inline] - pub fn clear_cpu_mask_flag(cur: CpuMask, bit: usize) -> CpuMask { - cur & !(1u64 << bit) - } -} - -#[cfg(all(target_arch = "x86", target_os = "linux"))] -mod cpuset_attribs { - use super::CpuMask; - pub const CPU_SETSIZE: usize = 1024; - pub const CPU_MASK_BITS: usize = 32; - - #[inline] - pub fn set_cpu_mask_flag(cur: CpuMask, bit: usize) -> CpuMask { - cur | (1u32 << bit) - } - - #[inline] - pub fn clear_cpu_mask_flag(cur: CpuMask, bit: usize) -> CpuMask { - cur & !(1u32 << bit) - } -} - -#[cfg(all(target_arch = "aarch64", any(target_os = "linux", target_os = "android")))] -mod cpuset_attribs { - use super::CpuMask; - pub const CPU_SETSIZE: usize = 1024; - pub const CPU_MASK_BITS: usize = 64; - - #[inline] - pub fn set_cpu_mask_flag(cur: CpuMask, bit: usize) -> CpuMask { - cur | (1u64 << bit) - } - - #[inline] - pub fn clear_cpu_mask_flag(cur: CpuMask, bit: usize) -> CpuMask { - cur & !(1u64 << bit) - } -} - -#[cfg(all(any(target_arch = "arm", target_arch = "mips"), target_os = "android"))] -mod cpuset_attribs { - use super::CpuMask; - // bionic only supports up to 32 independent CPUs, instead of 1024. - pub const CPU_SETSIZE: usize = 32; - pub const CPU_MASK_BITS: usize = 32; - - #[inline] - pub fn set_cpu_mask_flag(cur: CpuMask, bit: usize) -> CpuMask { - cur | (1u32 << bit) - } - - #[inline] - pub fn clear_cpu_mask_flag(cur: CpuMask, bit: usize) -> CpuMask { - cur & !(1u32 << bit) - } -} - -#[cfg(all(any(target_arch = "arm", target_arch = "mips"), target_os = "linux"))] -mod cpuset_attribs { - use super::CpuMask; - pub const CPU_SETSIZE: usize = 1024; - pub const CPU_MASK_BITS: usize = 32; - - #[inline] - pub fn set_cpu_mask_flag(cur: CpuMask, bit: usize) -> CpuMask { - cur | (1u32 << bit) - } - - #[inline] - pub fn clear_cpu_mask_flag(cur: CpuMask, bit: usize) -> CpuMask { - cur & !(1u32 << bit) +libc_bitflags!{ + flags CloneFlags: libc::c_int { + CLONE_VM, + CLONE_FS, + CLONE_FILES, + CLONE_SIGHAND, + CLONE_PTRACE, + CLONE_VFORK, + CLONE_PARENT, + CLONE_THREAD, + CLONE_NEWNS, + CLONE_SYSVSEM, + CLONE_SETTLS, + CLONE_PARENT_SETTID, + CLONE_CHILD_CLEARTID, + CLONE_DETACHED, + CLONE_UNTRACED, + CLONE_CHILD_SETTID, + CLONE_NEWUTS, + CLONE_NEWIPC, + CLONE_NEWUSER, + CLONE_NEWPID, + CLONE_NEWNET, + CLONE_IO, } } pub type CloneCb<'a> = Box<FnMut() -> isize + 'a>; -// A single CPU mask word -pub type CpuMask = c_ulong; - -// Structure representing the CPU set to apply #[repr(C)] #[derive(Clone, Copy)] pub struct CpuSet { - cpu_mask: [CpuMask; cpuset_attribs::CPU_SETSIZE/cpuset_attribs::CPU_MASK_BITS] + cpu_set: libc::cpu_set_t, } impl CpuSet { pub fn new() -> CpuSet { - CpuSet { - cpu_mask: unsafe { mem::zeroed() } - } + CpuSet { cpu_set: unsafe { mem::zeroed() } } } - pub fn set(&mut self, field: usize) { - let word = field / cpuset_attribs::CPU_MASK_BITS; - let bit = field % cpuset_attribs::CPU_MASK_BITS; - - self.cpu_mask[word] = cpuset_attribs::set_cpu_mask_flag(self.cpu_mask[word], bit); + pub fn is_set(&self, field: usize) -> Result<bool> { + if field >= 8 * mem::size_of::<libc::cpu_set_t>() { + Err(Error::Sys(Errno::EINVAL)) + } else { + Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) }) + } } - pub fn unset(&mut self, field: usize) { - let word = field / cpuset_attribs::CPU_MASK_BITS; - let bit = field % cpuset_attribs::CPU_MASK_BITS; + pub fn set(&mut self, field: usize) -> Result<()> { + if field >= 8 * mem::size_of::<libc::cpu_set_t>() { + Err(Error::Sys(Errno::EINVAL)) + } else { + Ok(unsafe { libc::CPU_SET(field, &mut self.cpu_set) }) + } + } - self.cpu_mask[word] = cpuset_attribs::clear_cpu_mask_flag(self.cpu_mask[word], bit); + pub fn unset(&mut self, field: usize) -> Result<()> { + if field >= 8 * mem::size_of::<libc::cpu_set_t>() { + Err(Error::Sys(Errno::EINVAL)) + } else { + Ok(unsafe { libc::CPU_CLR(field, &mut self.cpu_set) }) + } } } mod ffi { - use libc::{c_void, c_int, pid_t, size_t}; - use super::CpuSet; + use libc::{c_void, c_int}; - pub type CloneCb = extern "C" fn (data: *const super::CloneCb) -> c_int; + pub type CloneCb = extern "C" fn(data: *const super::CloneCb) -> c_int; // We cannot give a proper #[repr(C)] to super::CloneCb #[allow(improper_ctypes)] - extern { + extern "C" { // create a child process // doc: http://man7.org/linux/man-pages/man2/clone.2.html - pub fn clone( - cb: *const CloneCb, - child_stack: *mut c_void, - flags: c_int, - arg: *mut super::CloneCb, - ...) -> c_int; - - // disassociate parts of the process execution context - // doc: http://man7.org/linux/man-pages/man2/unshare.2.html - pub fn unshare(flags: c_int) -> c_int; - - // reassociate thread with a namespace - // doc: http://man7.org/linux/man-pages/man2/setns.2.html - pub fn setns(fd: c_int, nstype: c_int) -> c_int; - - // Set the current CPU set that a task is allowed to run on - pub fn sched_setaffinity(__pid: pid_t, __cpusetsize: size_t, __cpuset: *const CpuSet) -> c_int; + pub fn clone(cb: *const CloneCb, + child_stack: *mut c_void, + flags: c_int, + arg: *mut super::CloneCb, + ...) + -> c_int; } } pub fn sched_setaffinity(pid: isize, cpuset: &CpuSet) -> Result<()> { - use libc::{pid_t, size_t}; - let res = unsafe { - ffi::sched_setaffinity(pid as pid_t, mem::size_of::<CpuSet>() as size_t, mem::transmute(cpuset)) + libc::sched_setaffinity(pid as libc::pid_t, + mem::size_of::<CpuSet>() as libc::size_t, + mem::transmute(cpuset)) }; Errno::result(res).map(drop) } -pub fn clone(mut cb: CloneCb, stack: &mut [u8], flags: CloneFlags, signal: Option<c_int>) -> Result<pid_t> { +pub fn clone(mut cb: CloneCb, + stack: &mut [u8], + flags: CloneFlags, + signal: Option<c_int>) + -> Result<pid_t> { extern "C" fn callback(data: *mut CloneCb) -> c_int { let cb: &mut CloneCb = unsafe { &mut *data }; (*cb)() as c_int @@ -217,13 +123,13 @@ pub fn clone(mut cb: CloneCb, stack: &mut [u8], flags: CloneFlags, signal: Optio } pub fn unshare(flags: CloneFlags) -> Result<()> { - let res = unsafe { ffi::unshare(flags.bits()) }; + let res = unsafe { libc::unshare(flags.bits()) }; Errno::result(res).map(drop) } pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> { - let res = unsafe { ffi::setns(fd, nstype.bits()) }; + let res = unsafe { libc::setns(fd, nstype.bits()) }; Errno::result(res).map(drop) } diff --git a/src/sys/event.rs b/src/sys/event.rs index 8b112689..0e94475e 100644 --- a/src/sys/event.rs +++ b/src/sys/event.rs @@ -355,7 +355,7 @@ pub fn ev_set(ev: &mut KEvent, filter: EventFilter, flags: EventFlag, fflags: FilterFlag, - udata: i64) { + udata: isize) { ev.ident = ident as uintptr_t; ev.filter = filter; diff --git a/src/sys/eventfd.rs b/src/sys/eventfd.rs index cd740341..e6e410ec 100644 --- a/src/sys/eventfd.rs +++ b/src/sys/eventfd.rs @@ -2,26 +2,16 @@ use libc; use std::os::unix::io::RawFd; use {Errno, Result}; -bitflags!( - flags EventFdFlag: libc::c_int { - const EFD_CLOEXEC = 0o2000000, // Since Linux 2.6.27 - const EFD_NONBLOCK = 0o0004000, // Since Linux 2.6.27 - const EFD_SEMAPHORE = 0o0000001, // Since Linux 2.6.30 - } -); - -mod ffi { - use libc; - - extern { - pub fn eventfd(initval: libc::c_uint, flags: libc::c_int) -> libc::c_int; +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 } } -pub fn eventfd(initval: usize, flags: EventFdFlag) -> Result<RawFd> { - unsafe { - let res = ffi::eventfd(initval as libc::c_uint, flags.bits()); +pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result<RawFd> { + let res = unsafe { libc::eventfd(initval, flags.bits()) }; - Errno::result(res).map(|r| r as RawFd) - } + Errno::result(res).map(|r| r as RawFd) } diff --git a/src/sys/mman.rs b/src/sys/mman.rs index 5bc1c82d..a1bf6134 100644 --- a/src/sys/mman.rs +++ b/src/sys/mman.rs @@ -131,7 +131,7 @@ mod consts { const MAP_RENAME = libc::MAP_RENAME, const MAP_NORESERVE = libc::MAP_NORESERVE, const MAP_HASSEMAPHORE = libc::MAP_HASSEMAPHORE, - #[cfg(not(target_os = "openbsd"))] + #[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))] const MAP_STACK = libc::MAP_STACK, #[cfg(target_os = "netbsd")] const MAP_WIRED = libc::MAP_WIRED, diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 82934164..793bc70e 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -31,6 +31,9 @@ pub mod stat; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod syscall; +#[cfg(any(target_os = "linux"))] +pub mod reboot; + #[cfg(not(target_os = "ios"))] pub mod termios; diff --git a/src/sys/reboot.rs b/src/sys/reboot.rs new file mode 100644 index 00000000..94f30f62 --- /dev/null +++ b/src/sys/reboot.rs @@ -0,0 +1,43 @@ +//! Reboot/shutdown or enable/disable Ctrl-Alt-Delete. + +use {Errno, Error, Result}; +use libc; +use void::Void; +use std::mem::drop; + +/// How exactly should the system be rebooted. +/// +/// See [`set_cad_enabled()`](fn.set_cad_enabled.html) for +/// enabling/disabling Ctrl-Alt-Delete. +#[repr(i32)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum RebootMode { + RB_HALT_SYSTEM = libc::RB_HALT_SYSTEM, + RB_KEXEC = libc::RB_KEXEC, + RB_POWER_OFF = libc::RB_POWER_OFF, + RB_AUTOBOOT = libc::RB_AUTOBOOT, + // we do not support Restart2, + RB_SW_SUSPEND = libc::RB_SW_SUSPEND, +} + +pub fn reboot(how: RebootMode) -> Result<Void> { + unsafe { + libc::reboot(how as libc::c_int) + }; + Err(Error::Sys(Errno::last())) +} + +/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete). +/// +/// Corresponds to calling `reboot(RB_ENABLE_CAD)` or `reboot(RB_DISABLE_CAD)` in C. +pub fn set_cad_enabled(enable: bool) -> Result<()> { + let cmd = if enable { + libc::RB_ENABLE_CAD + } else { + libc::RB_DISABLE_CAD + }; + let res = unsafe { + libc::reboot(cmd) + }; + Errno::result(res).map(drop) +} diff --git a/src/sys/select.rs b/src/sys/select.rs index 1b47d759..28b664aa 100644 --- a/src/sys/select.rs +++ b/src/sys/select.rs @@ -8,6 +8,7 @@ pub const FD_SETSIZE: RawFd = 1024; #[cfg(any(target_os = "macos", target_os = "ios"))] #[repr(C)] +#[derive(Clone)] pub struct FdSet { bits: [i32; FD_SETSIZE as usize / 32] } diff --git a/src/sys/signal.rs b/src/sys/signal.rs index 753c1562..18827332 100644 --- a/src/sys/signal.rs +++ b/src/sys/signal.rs @@ -2,48 +2,166 @@ // See http://rust-lang.org/COPYRIGHT. use libc; -use {Errno, Result}; +use {Errno, Error, Result}; use std::mem; use std::ptr; -pub use libc::{ +// Currently there is only one definition of c_int in libc, as well as only one +// type for signal constants. +// We would prefer to use the libc::c_int alias in the repr attribute. Unfortunately +// this is not (yet) possible. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(i32)] +pub enum Signal { + SIGHUP = libc::SIGHUP, + SIGINT = libc::SIGINT, + SIGQUIT = libc::SIGQUIT, + SIGILL = libc::SIGILL, + SIGTRAP = libc::SIGTRAP, + SIGABRT = libc::SIGABRT, + SIGBUS = libc::SIGBUS, + SIGFPE = libc::SIGFPE, + SIGKILL = libc::SIGKILL, + SIGUSR1 = libc::SIGUSR1, + SIGSEGV = libc::SIGSEGV, + SIGUSR2 = libc::SIGUSR2, + SIGPIPE = libc::SIGPIPE, + SIGALRM = libc::SIGALRM, + SIGTERM = libc::SIGTERM, + #[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))] + SIGSTKFLT = libc::SIGSTKFLT, + SIGCHLD = libc::SIGCHLD, + SIGCONT = libc::SIGCONT, + SIGSTOP = libc::SIGSTOP, + SIGTSTP = libc::SIGTSTP, + SIGTTIN = libc::SIGTTIN, + SIGTTOU = libc::SIGTTOU, + SIGURG = libc::SIGURG, + SIGXCPU = libc::SIGXCPU, + SIGXFSZ = libc::SIGXFSZ, + SIGVTALRM = libc::SIGVTALRM, + SIGPROF = libc::SIGPROF, + SIGWINCH = libc::SIGWINCH, + SIGIO = libc::SIGIO, + #[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))] + SIGPWR = libc::SIGPWR, + SIGSYS = libc::SIGSYS, + #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "emscripten")))] + SIGEMT = libc::SIGEMT, + #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "emscripten")))] + SIGINFO = libc::SIGINFO, +} + +pub use self::Signal::*; + +#[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))] +const SIGNALS: [Signal; 31] = [ SIGHUP, SIGINT, SIGQUIT, SIGILL, + SIGTRAP, SIGABRT, + SIGBUS, SIGFPE, SIGKILL, + SIGUSR1, SIGSEGV, + SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, - SIGTRAP, - SIGIOT, - SIGBUS, - SIGSYS, - SIGURG, + SIGSTKFLT, + SIGCHLD, + SIGCONT, SIGSTOP, SIGTSTP, - SIGCONT, - SIGCHLD, SIGTTIN, SIGTTOU, - SIGIO, + SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH, + SIGIO, + SIGPWR, + SIGSYS]; +#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "emscripten")))] +const SIGNALS: [Signal; 31] = [ + SIGHUP, + SIGINT, + SIGQUIT, + SIGILL, + SIGTRAP, + SIGABRT, + SIGBUS, + SIGFPE, + SIGKILL, SIGUSR1, + SIGSEGV, SIGUSR2, -}; - -// This doesn't always exist, but when it does, it's 7 -pub const SIGEMT: libc::c_int = 7; + SIGPIPE, + SIGALRM, + SIGTERM, + SIGCHLD, + SIGCONT, + SIGSTOP, + SIGTSTP, + SIGTTIN, + SIGTTOU, + SIGURG, + SIGXCPU, + SIGXFSZ, + SIGVTALRM, + SIGPROF, + SIGWINCH, + SIGIO, + SIGSYS, + SIGEMT, + SIGINFO]; pub const NSIG: libc::c_int = 32; +pub struct SignalIterator { + next: usize, +} + +impl Iterator for SignalIterator { + type Item = Signal; + + fn next(&mut self) -> Option<Signal> { + if self.next < SIGNALS.len() { + let next_signal = SIGNALS[self.next]; + self.next += 1; + Some(next_signal) + } else { + None + } + } +} + +impl Signal { + pub fn iterator() -> SignalIterator { + SignalIterator{next: 0} + } + + // We do not implement the From trait, because it is supposed to be infallible. + // With Rust RFC 1542 comes the appropriate trait TryFrom. Once it is + // implemented, we'll replace this function. + #[inline] + pub fn from_c_int(signum: libc::c_int) -> Result<Signal> { + match 0 < signum && signum < NSIG { + true => Ok(unsafe { mem::transmute(signum) }), + false => Err(Error::invalid_argument()), + } + } +} + +pub const SIGIOT : Signal = SIGABRT; +pub const SIGPOLL : Signal = SIGIO; +pub const SIGUNUSED : Signal = SIGSYS; + bitflags!{ flags SaFlags: libc::c_int { const SA_NOCLDSTOP = libc::SA_NOCLDSTOP, @@ -69,7 +187,6 @@ pub struct SigSet { sigset: libc::sigset_t } -pub type SigNum = libc::c_int; impl SigSet { pub fn all() -> SigSet { @@ -86,40 +203,33 @@ impl SigSet { SigSet { sigset: sigset } } - pub fn add(&mut self, signum: SigNum) -> Result<()> { - let res = unsafe { libc::sigaddset(&mut self.sigset as *mut libc::sigset_t, signum) }; - - Errno::result(res).map(drop) + pub fn add(&mut self, signal: Signal) { + unsafe { libc::sigaddset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) }; } - pub fn clear(&mut self) -> Result<()> { - let res = unsafe { libc::sigemptyset(&mut self.sigset as *mut libc::sigset_t) }; - - Errno::result(res).map(drop) + pub fn clear(&mut self) { + unsafe { libc::sigemptyset(&mut self.sigset as *mut libc::sigset_t) }; } - pub fn remove(&mut self, signum: SigNum) -> Result<()> { - let res = unsafe { libc::sigdelset(&mut self.sigset as *mut libc::sigset_t, signum) }; - - Errno::result(res).map(drop) + pub fn remove(&mut self, signal: Signal) { + unsafe { libc::sigdelset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) }; } - pub fn extend(&mut self, other: &SigSet) -> Result<()> { - for i in 1..NSIG { - if try!(other.contains(i)) { - try!(self.add(i)); - } + pub fn contains(&self, signal: Signal) -> bool { + let res = unsafe { libc::sigismember(&self.sigset as *const libc::sigset_t, signal as libc::c_int) }; + + match res { + 1 => true, + 0 => false, + _ => unreachable!("unexpected value from sigismember"), } - Ok(()) } - pub fn contains(&self, signum: SigNum) -> Result<bool> { - let res = unsafe { libc::sigismember(&self.sigset as *const libc::sigset_t, signum) }; - - match try!(Errno::result(res)) { - 1 => Ok(true), - 0 => Ok(false), - _ => unreachable!("unexpected value from sigismember"), + pub fn extend(&mut self, other: &SigSet) { + for signal in Signal::iterator() { + if other.contains(signal) { + self.add(signal); + } } } @@ -154,11 +264,11 @@ impl SigSet { /// Suspends execution of the calling thread until one of the signals in the /// signal mask becomes pending, and returns the accepted signal. - pub fn wait(&self) -> Result<SigNum> { - let mut signum: SigNum = unsafe { mem::uninitialized() }; + pub fn wait(&self) -> Result<Signal> { + let mut signum: libc::c_int = unsafe { mem::uninitialized() }; let res = unsafe { libc::sigwait(&self.sigset as *const libc::sigset_t, &mut signum) }; - Errno::result(res).map(|_| signum) + Errno::result(res).map(|_| Signal::from_c_int(signum).unwrap()) } } @@ -174,8 +284,8 @@ impl AsRef<libc::sigset_t> for SigSet { pub enum SigHandler { SigDfl, SigIgn, - Handler(extern fn(SigNum)), - SigAction(extern fn(SigNum, *mut libc::siginfo_t, *mut libc::c_void)) + Handler(extern fn(libc::c_int)), + SigAction(extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)) } pub struct SigAction { @@ -203,11 +313,11 @@ impl SigAction { } } -pub unsafe fn sigaction(signum: SigNum, sigaction: &SigAction) -> Result<SigAction> { +pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigAction> { let mut oldact = mem::uninitialized::<libc::sigaction>(); let res = - libc::sigaction(signum, &sigaction.sigaction as *const libc::sigaction, &mut oldact as *mut libc::sigaction); + libc::sigaction(signal as libc::c_int, &sigaction.sigaction as *const libc::sigaction, &mut oldact as *mut libc::sigaction); Errno::result(res).map(|_| SigAction { sigaction: oldact }) } @@ -246,14 +356,14 @@ pub fn pthread_sigmask(how: SigFlags, Errno::result(res).map(drop) } -pub fn kill(pid: libc::pid_t, signum: SigNum) -> Result<()> { - let res = unsafe { libc::kill(pid, signum) }; +pub fn kill(pid: libc::pid_t, signal: Signal) -> Result<()> { + let res = unsafe { libc::kill(pid, signal as libc::c_int) }; Errno::result(res).map(drop) } -pub fn raise(signum: SigNum) -> Result<()> { - let res = unsafe { libc::raise(signum) }; +pub fn raise(signal: Signal) -> Result<()> { + let res = unsafe { libc::raise(signal as libc::c_int) }; Errno::result(res).map(drop) } @@ -265,42 +375,42 @@ mod tests { #[test] fn test_contains() { let mut mask = SigSet::empty(); - mask.add(SIGUSR1).unwrap(); + mask.add(SIGUSR1); - assert_eq!(mask.contains(SIGUSR1), Ok(true)); - assert_eq!(mask.contains(SIGUSR2), Ok(false)); + assert!(mask.contains(SIGUSR1)); + assert!(!mask.contains(SIGUSR2)); let all = SigSet::all(); - assert_eq!(all.contains(SIGUSR1), Ok(true)); - assert_eq!(all.contains(SIGUSR2), Ok(true)); + assert!(all.contains(SIGUSR1)); + assert!(all.contains(SIGUSR2)); } #[test] fn test_clear() { let mut set = SigSet::all(); - set.clear().unwrap(); - for i in 1..NSIG { - assert_eq!(set.contains(i), Ok(false)); + set.clear(); + for signal in Signal::iterator() { + assert!(!set.contains(signal)); } } #[test] fn test_extend() { let mut one_signal = SigSet::empty(); - one_signal.add(SIGUSR1).unwrap(); + one_signal.add(SIGUSR1); let mut two_signals = SigSet::empty(); - two_signals.add(SIGUSR2).unwrap(); - two_signals.extend(&one_signal).unwrap(); + two_signals.add(SIGUSR2); + two_signals.extend(&one_signal); - assert_eq!(two_signals.contains(SIGUSR1), Ok(true)); - assert_eq!(two_signals.contains(SIGUSR2), Ok(true)); + assert!(two_signals.contains(SIGUSR1)); + assert!(two_signals.contains(SIGUSR2)); } #[test] fn test_thread_signal_block() { let mut mask = SigSet::empty(); - mask.add(SIGUSR1).unwrap(); + mask.add(SIGUSR1); assert!(mask.thread_block().is_ok()); } @@ -308,18 +418,18 @@ mod tests { #[test] fn test_thread_signal_swap() { let mut mask = SigSet::empty(); - mask.add(SIGUSR1).unwrap(); + mask.add(SIGUSR1); mask.thread_block().unwrap(); - assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1).unwrap()); + assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); let mask2 = SigSet::empty(); - mask.add(SIGUSR2).unwrap(); + mask.add(SIGUSR2); let oldmask = mask2.thread_swap_mask(SIG_SETMASK).unwrap(); - assert!(oldmask.contains(SIGUSR1).unwrap()); - assert!(!oldmask.contains(SIGUSR2).unwrap()); + assert!(oldmask.contains(SIGUSR1)); + assert!(!oldmask.contains(SIGUSR2)); } // TODO(#251): Re-enable after figuring out flakiness. @@ -327,8 +437,8 @@ mod tests { #[test] fn test_sigwait() { let mut mask = SigSet::empty(); - mask.add(SIGUSR1).unwrap(); - mask.add(SIGUSR2).unwrap(); + mask.add(SIGUSR1); + mask.add(SIGUSR2); mask.thread_block().unwrap(); raise(SIGUSR1).unwrap(); diff --git a/src/sys/socket/consts.rs b/src/sys/socket/consts.rs index ddd8f6a9..63eaf28a 100644 --- a/src/sys/socket/consts.rs +++ b/src/sys/socket/consts.rs @@ -59,6 +59,8 @@ mod os { pub const SO_TIMESTAMP: c_int = 29; pub const SO_TYPE: c_int = 3; pub const SO_BUSY_POLL: c_int = 46; + #[cfg(target_os = "linux")] + pub const SO_ORIGINAL_DST: c_int = 80; // Socket options for TCP sockets pub const TCP_NODELAY: c_int = 1; @@ -96,6 +98,7 @@ mod os { const MSG_DONTWAIT = 0x0040, const MSG_EOR = 0x0080, const MSG_ERRQUEUE = 0x2000, + const MSG_CMSG_CLOEXEC = 0x40000000, } } diff --git a/src/sys/socket/ffi.rs b/src/sys/socket/ffi.rs index 1cbf766c..55a47eb6 100644 --- a/src/sys/socket/ffi.rs +++ b/src/sys/socket/ffi.rs @@ -4,8 +4,11 @@ pub use libc::{socket, listen, bind, accept, connect, setsockopt, sendto, recvfrom, getsockname, getpeername, recv, send}; use libc::{c_int, c_void, socklen_t, size_t, ssize_t}; -use sys::uio::IoVec; +#[cfg(target_os = "macos")] +use libc::c_uint; + +use sys::uio::IoVec; #[cfg(target_os = "linux")] pub type type_of_cmsg_len = size_t; @@ -13,6 +16,13 @@ pub type type_of_cmsg_len = size_t; #[cfg(not(target_os = "linux"))] pub type type_of_cmsg_len = socklen_t; +// OSX always aligns struct cmsghdr as if it were a 32-bit OS +#[cfg(target_os = "macos")] +pub type type_of_cmsg_data = c_uint; + +#[cfg(not(target_os = "macos"))] +pub type type_of_cmsg_data = size_t; + // Private because we don't expose any external functions that operate // directly on this type; we just use it internally at FFI boundaries. // Note that in some cases we store pointers in *const fields that the @@ -37,7 +47,7 @@ pub struct cmsghdr { pub cmsg_len: type_of_cmsg_len, pub cmsg_level: c_int, pub cmsg_type: c_int, - pub cmsg_data: [type_of_cmsg_len; 0] + pub cmsg_data: [type_of_cmsg_data; 0] } extern { diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index c96a5c8d..69f26aa0 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -94,7 +94,7 @@ unsafe fn copy_bytes<'a, 'b, T: ?Sized>(src: &T, dst: &'a mut &'b mut [u8]) { } -use self::ffi::{cmsghdr, msghdr, type_of_cmsg_len}; +use self::ffi::{cmsghdr, msghdr, type_of_cmsg_len, type_of_cmsg_data}; /// A structure used to make room in a cmsghdr passed to recvmsg. The /// size and alignment match that of a cmsghdr followed by a T, but the @@ -169,8 +169,7 @@ impl<'a> Iterator for CmsgIterator<'a> { (SOL_SOCKET, SCM_RIGHTS) => unsafe { Some(ControlMessage::ScmRights( slice::from_raw_parts( - &cmsg.cmsg_data as *const _ as *const _, - len / mem::size_of::<RawFd>()))) + &cmsg.cmsg_data as *const _ as *const _, 1))) }, (_, _) => unsafe { Some(ControlMessage::Unknown(UnknownCmsg( @@ -201,12 +200,8 @@ pub enum ControlMessage<'a> { pub struct UnknownCmsg<'a>(&'a cmsghdr, &'a [u8]); fn cmsg_align(len: usize) -> usize { - let round_to = mem::size_of::<type_of_cmsg_len>(); - if len % round_to == 0 { - len - } else { - len + round_to - (len % round_to) - } + let align_bytes = mem::size_of::<type_of_cmsg_data>() - 1; + (len + align_bytes) & !align_bytes } impl<'a> ControlMessage<'a> { @@ -217,7 +212,7 @@ impl<'a> ControlMessage<'a> { /// The value of CMSG_LEN on this message. fn len(&self) -> usize { - mem::size_of::<cmsghdr>() + match *self { + cmsg_align(mem::size_of::<cmsghdr>()) + match *self { ControlMessage::ScmRights(fds) => { mem::size_of_val(fds) }, @@ -240,7 +235,11 @@ impl<'a> ControlMessage<'a> { cmsg_data: [], }; copy_bytes(&cmsg, buf); - copy_bytes(fds, buf); + + let padlen = cmsg_align(mem::size_of_val(&cmsg)) - + mem::size_of_val(&cmsg); + let buf2 = &mut &mut buf[padlen..]; + copy_bytes(fds, buf2); }, ControlMessage::Unknown(UnknownCmsg(orig_cmsg, bytes)) => { copy_bytes(orig_cmsg, buf); @@ -267,10 +266,10 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<' // multiple of size_t. Note also that the resulting vector claims // to have length == capacity, so it's presently uninitialized. let mut cmsg_buffer = unsafe { - let mut vec = Vec::<size_t>::with_capacity(capacity / mem::size_of::<size_t>()); + let mut vec = Vec::<u8>::with_capacity(len); let ptr = vec.as_mut_ptr(); mem::forget(vec); - Vec::<u8>::from_raw_parts(ptr as *mut _, capacity, capacity) + Vec::<u8>::from_raw_parts(ptr as *mut _, len, len) }; { let mut ptr = &mut cmsg_buffer[..]; @@ -290,7 +289,7 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<' msg_iov: iov.as_ptr(), msg_iovlen: iov.len() as size_t, msg_control: cmsg_buffer.as_ptr() as *const c_void, - msg_controllen: len as size_t, + msg_controllen: capacity as size_t, msg_flags: 0, }; let ret = unsafe { ffi::sendmsg(fd, &mhdr, flags.bits()) }; @@ -630,6 +629,11 @@ pub unsafe fn sockaddr_storage_to_addr( consts::AF_UNIX => { Ok(SockAddr::Unix(UnixAddr(*(addr as *const _ as *const sockaddr_un), len))) } + #[cfg(any(target_os = "linux", target_os = "android"))] + consts::AF_NETLINK => { + use libc::sockaddr_nl; + Ok(SockAddr::Netlink(NetlinkAddr(*(addr as *const _ as *const sockaddr_nl)))) + } af => panic!("unexpected address family {}", af), } } diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 17de2d27..bf17347c 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -2,6 +2,8 @@ use super::{ffi, consts, GetSockOpt, SetSockOpt}; use {Errno, Result}; use sys::time::TimeVal; use libc::{c_int, uint8_t, c_void, socklen_t}; +#[cfg(target_os = "linux")] +use libc::sockaddr_in; use std::mem; use std::os::unix::io::RawFd; @@ -47,10 +49,6 @@ macro_rules! getsockopt_impl { // Helper to generate the sockopt accessors macro_rules! sockopt_impl { - (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => { - sockopt_impl!(GetOnly, $name, $level, $flag, $ty, GetStruct<$ty>); - }; - (GetOnly, $name:ident, $level:path, $flag:path, bool) => { sockopt_impl!(GetOnly, $name, $level, $flag, bool, GetBool); }; @@ -63,17 +61,6 @@ macro_rules! sockopt_impl { sockopt_impl!(GetOnly, $name, $level, $flag, usize, GetUsize); }; - (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => { - #[derive(Copy, Clone, Debug)] - pub struct $name; - - getsockopt_impl!($name, $level, $flag, $ty, $getter); - }; - - (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => { - sockopt_impl!(SetOnly, $name, $level, $flag, $ty, SetStruct<$ty>); - }; - (SetOnly, $name:ident, $level:path, $flag:path, bool) => { sockopt_impl!(SetOnly, $name, $level, $flag, bool, SetBool); }; @@ -86,31 +73,50 @@ macro_rules! sockopt_impl { sockopt_impl!(SetOnly, $name, $level, $flag, usize, SetUsize); }; - (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => { - #[derive(Copy, Clone, Debug)] - pub struct $name; + (Both, $name:ident, $level:path, $flag:path, bool) => { + sockopt_impl!(Both, $name, $level, $flag, bool, GetBool, SetBool); + }; - setsockopt_impl!($name, $level, $flag, $ty, $setter); + (Both, $name:ident, $level:path, $flag:path, u8) => { + sockopt_impl!(Both, $name, $level, $flag, u8, GetU8, SetU8); }; - (Both, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty, $setter:ty) => { + (Both, $name:ident, $level:path, $flag:path, usize) => { + sockopt_impl!(Both, $name, $level, $flag, usize, GetUsize, SetUsize); + }; + + /* + * Matchers with generic getter types must be placed at the end, so + * they'll only match _after_ specialized matchers fail + */ + (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => { + sockopt_impl!(GetOnly, $name, $level, $flag, $ty, GetStruct<$ty>); + }; + + (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => { #[derive(Copy, Clone, Debug)] pub struct $name; - setsockopt_impl!($name, $level, $flag, $ty, $setter); getsockopt_impl!($name, $level, $flag, $ty, $getter); }; - (Both, $name:ident, $level:path, $flag:path, bool) => { - sockopt_impl!(Both, $name, $level, $flag, bool, GetBool, SetBool); + (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => { + sockopt_impl!(SetOnly, $name, $level, $flag, $ty, SetStruct<$ty>); }; - (Both, $name:ident, $level:path, $flag:path, u8) => { - sockopt_impl!(Both, $name, $level, $flag, u8, GetU8, SetU8); + (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => { + #[derive(Copy, Clone, Debug)] + pub struct $name; + + setsockopt_impl!($name, $level, $flag, $ty, $setter); }; - (Both, $name:ident, $level:path, $flag:path, usize) => { - sockopt_impl!(Both, $name, $level, $flag, usize, GetUsize, SetUsize); + (Both, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty, $setter:ty) => { + #[derive(Copy, Clone, Debug)] + pub struct $name; + + setsockopt_impl!($name, $level, $flag, $ty, $setter); + getsockopt_impl!($name, $level, $flag, $ty, $getter); }; (Both, $name:ident, $level:path, $flag:path, $ty:ty) => { @@ -168,6 +174,8 @@ sockopt_impl!(GetOnly, SockType, consts::SOL_SOCKET, consts::SO_TYPE, super::Soc target_os = "linux", target_os = "nacl"))] sockopt_impl!(GetOnly, AcceptConn, consts::SOL_SOCKET, consts::SO_ACCEPTCONN, bool); +#[cfg(target_os = "linux")] +sockopt_impl!(GetOnly, OriginalDst, consts::SOL_IP, consts::SO_ORIGINAL_DST, sockaddr_in); /* * diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 3d9b3a50..259efb70 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -1,7 +1,7 @@ -use libc::{pid_t, c_int}; +use libc::{self, pid_t, c_int}; use {Errno, Result}; -use sys::signal; +use sys::signal::Signal; mod ffi { use libc::{pid_t, c_int}; @@ -15,7 +15,8 @@ mod ffi { target_os = "android")))] bitflags!( flags WaitPidFlag: c_int { - const WNOHANG = 0x00000001, + const WNOHANG = libc::WNOHANG, + const WUNTRACED = libc::WUNTRACED, } ); @@ -23,14 +24,14 @@ bitflags!( target_os = "android"))] bitflags!( flags WaitPidFlag: c_int { - const WNOHANG = 0x00000001, - const WUNTRACED = 0x00000002, - const WEXITED = 0x00000004, - const WCONTINUED = 0x00000008, - const WNOWAIT = 0x01000000, // Don't reap, just poll status. - const __WNOTHREAD = 0x20000000, // Don't wait on children of other threads in this group - const __WALL = 0x40000000, // Wait on all children, regardless of type - // const __WCLONE = 0x80000000, + const WNOHANG = libc::WNOHANG, + const WUNTRACED = libc::WUNTRACED, + const WEXITED = libc::WEXITED, + const WCONTINUED = libc::WCONTINUED, + const WNOWAIT = libc::WNOWAIT, // Don't reap, just poll status. + const __WNOTHREAD = libc::__WNOTHREAD, // Don't wait on children of other threads in this group + const __WALL = libc::__WALL, // Wait on all children, regardless of type + const __WCLONE = libc::__WCLONE, } ); @@ -41,8 +42,8 @@ const WSTOPPED: WaitPidFlag = WUNTRACED; #[derive(Eq, PartialEq, Clone, Copy, Debug)] pub enum WaitStatus { Exited(pid_t, i8), - Signaled(pid_t, signal::SigNum, bool), - Stopped(pid_t, signal::SigNum), + Signaled(pid_t, Signal, bool), + Stopped(pid_t, Signal), Continued(pid_t), StillAlive } @@ -50,7 +51,7 @@ pub enum WaitStatus { #[cfg(any(target_os = "linux", target_os = "android"))] mod status { - use sys::signal; + use sys::signal::Signal; pub fn exited(status: i32) -> bool { (status & 0x7F) == 0 @@ -64,8 +65,8 @@ mod status { ((((status & 0x7f) + 1) as i8) >> 1) > 0 } - pub fn term_signal(status: i32) -> signal::SigNum { - (status & 0x7f) as signal::SigNum + pub fn term_signal(status: i32) -> Signal { + Signal::from_c_int(status & 0x7f).unwrap() } pub fn dumped_core(status: i32) -> bool { @@ -76,8 +77,8 @@ mod status { (status & 0xff) == 0x7f } - pub fn stop_signal(status: i32) -> signal::SigNum { - ((status & 0xFF00) >> 8) as signal::SigNum + pub fn stop_signal(status: i32) -> Signal { + Signal::from_c_int((status & 0xFF00) >> 8).unwrap() } pub fn continued(status: i32) -> bool { @@ -88,7 +89,7 @@ mod status { #[cfg(any(target_os = "macos", target_os = "ios"))] mod status { - use sys::signal; + use sys::signal::{Signal,SIGCONT}; const WCOREFLAG: i32 = 0x80; const WSTOPPED: i32 = 0x7f; @@ -101,16 +102,16 @@ mod status { ((status >> 8) & 0xFF) as i8 } - pub fn stop_signal(status: i32) -> signal::SigNum { - (status >> 8) as signal::SigNum + pub fn stop_signal(status: i32) -> Signal { + Signal::from_c_int(status >> 8).unwrap() } pub fn continued(status: i32) -> bool { - wstatus(status) == WSTOPPED && stop_signal(status) == 0x13 + wstatus(status) == WSTOPPED && stop_signal(status) == SIGCONT } pub fn stopped(status: i32) -> bool { - wstatus(status) == WSTOPPED && stop_signal(status) != 0x13 + wstatus(status) == WSTOPPED && stop_signal(status) != SIGCONT } pub fn exited(status: i32) -> bool { @@ -121,8 +122,8 @@ mod status { wstatus(status) != WSTOPPED && wstatus(status) != 0 } - pub fn term_signal(status: i32) -> signal::SigNum { - wstatus(status) as signal::SigNum + pub fn term_signal(status: i32) -> Signal { + Signal::from_c_int(wstatus(status)).unwrap() } pub fn dumped_core(status: i32) -> bool { @@ -135,7 +136,7 @@ mod status { target_os = "dragonfly", target_os = "netbsd"))] mod status { - use sys::signal; + use sys::signal::Signal; const WCOREFLAG: i32 = 0x80; const WSTOPPED: i32 = 0x7f; @@ -148,16 +149,16 @@ mod status { wstatus(status) == WSTOPPED } - pub fn stop_signal(status: i32) -> signal::SigNum { - (status >> 8) as signal::SigNum + pub fn stop_signal(status: i32) -> Signal { + Signal::from_c_int(status >> 8).unwrap() } pub fn signaled(status: i32) -> bool { wstatus(status) != WSTOPPED && wstatus(status) != 0 && status != 0x13 } - pub fn term_signal(status: i32) -> signal::SigNum { - wstatus(status) as signal::SigNum + pub fn term_signal(status: i32) -> Signal { + Signal::from_c_int(wstatus(status)).unwrap() } pub fn exited(status: i32) -> bool { diff --git a/src/ucontext.rs b/src/ucontext.rs index f77b4815..6886dd41 100644 --- a/src/ucontext.rs +++ b/src/ucontext.rs @@ -1,6 +1,8 @@ use libc; +#[cfg(not(target_env = "musl"))] use {Errno, Result}; use std::mem; +use sys::signal::SigSet; #[derive(Clone, Copy)] pub struct UContext { @@ -8,6 +10,7 @@ pub struct UContext { } impl UContext { + #[cfg(not(target_env = "musl"))] pub fn get() -> Result<UContext> { let mut context: libc::ucontext_t = unsafe { mem::uninitialized() }; let res = unsafe { @@ -16,10 +19,19 @@ impl UContext { Errno::result(res).map(|_| UContext { context: context }) } + #[cfg(not(target_env = "musl"))] pub fn set(&self) -> Result<()> { let res = unsafe { libc::setcontext(&self.context as *const libc::ucontext_t) }; Errno::result(res).map(drop) } + + pub fn sigmask_mut(&mut self) -> &mut SigSet { + unsafe { mem::transmute(&mut self.context.uc_sigmask) } + } + + pub fn sigmask(&self) -> &SigSet { + unsafe { mem::transmute(&self.context.uc_sigmask) } + } } diff --git a/src/unistd.rs b/src/unistd.rs index 8db44163..2eb218b3 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -3,13 +3,14 @@ use {Errno, Error, Result, NixPath}; use fcntl::{fcntl, OFlag, O_NONBLOCK, O_CLOEXEC, FD_CLOEXEC}; use fcntl::FcntlArg::{F_SETFD, F_SETFL}; -use libc::{self, c_char, c_void, c_int, c_uint, size_t, pid_t, off_t, uid_t, gid_t}; +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, OsStr}; -use std::os::unix::ffi::OsStrExt; +use std::ffi::{CString, CStr, OsString, OsStr}; +use std::os::unix::ffi::{OsStringExt, OsStrExt}; use std::os::unix::io::RawFd; -use std::path::{PathBuf, Path}; +use std::path::{PathBuf}; use void::Void; +use sys::stat::Mode; #[cfg(any(target_os = "linux", target_os = "android"))] pub use self::linux::*; @@ -113,11 +114,109 @@ pub fn chdir<P: ?Sized + NixPath>(path: &P) -> Result<()> { Errno::result(res).map(drop) } +/// Creates new directory `path` with access rights `mode`. +/// +/// # Errors +/// +/// There are several situations where mkdir might fail: +/// +/// - current user has insufficient rights in the parent directory +/// - the path already exists +/// - the path name is too long (longer than `PATH_MAX`, usually 4096 on linux, 1024 on OS X) +/// +/// For a full list consult +/// [man mkdir(2)](http://man7.org/linux/man-pages/man2/mkdir.2.html#ERRORS) +/// +/// # Example +/// +/// ```rust +/// extern crate tempdir; +/// extern crate nix; +/// +/// use nix::unistd; +/// use nix::sys::stat; +/// use tempdir::TempDir; +/// +/// fn main() { +/// let mut tmp_dir = TempDir::new("test_mkdir").unwrap().into_path(); +/// tmp_dir.push("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), +/// Err(err) => println!("Error creating directory: {}", err), +/// } +/// } +/// ``` +#[inline] +pub fn mkdir<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()> { + let res = try!(path.with_nix_path(|cstr| { + unsafe { libc::mkdir(cstr.as_ptr(), mode.bits() as mode_t) } + })); + + Errno::result(res).map(drop) +} + +/// Returns the current directory as a PathBuf +/// +/// Err is returned if the current user doesn't have the permission to read or search a component +/// of the current path. +/// +/// # Example +/// +/// ```rust +/// extern crate nix; +/// +/// use nix::unistd; +/// +/// fn main() { +/// // assume that we are allowed to get current directory +/// let dir = unistd::getcwd().unwrap(); +/// println!("The current directory is {:?}", dir); +/// } +/// ``` +#[inline] +pub fn getcwd() -> Result<PathBuf> { + let mut buf = Vec::with_capacity(512); + loop { + unsafe { + let ptr = buf.as_mut_ptr() as *mut libc::c_char; + + // The buffer must be large enough to store the absolute pathname plus + // a terminating null byte, or else null is returned. + // To safely handle this we start with a reasonable size (512 bytes) + // and double the buffer size upon every error + if !libc::getcwd(ptr, buf.capacity()).is_null() { + let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len(); + buf.set_len(len); + buf.shrink_to_fit(); + return Ok(PathBuf::from(OsString::from_vec(buf))); + } else { + let error = Errno::last(); + // ERANGE means buffer was too small to store directory name + if error != Errno::ERANGE { + return Err(Error::Sys(error)); + } + } + + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. + let cap = buf.capacity(); + buf.set_len(cap); + buf.reserve(1); + } + } +} + #[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| { - // We use `0 - 1` to get `-1 : {u,g}id_t` which is specified as the no-op value for chown(3). - unsafe { libc::chown(cstr.as_ptr(), owner.unwrap_or(0 - 1), group.unwrap_or(0 - 1)) } + // According to the POSIX specification, -1 is used to indicate that + // owner and group, respectively, are not to be changed. Since uid_t and + // gid_t are unsigned types, we use wrapping_sub to get '-1'. + unsafe { libc::chown(cstr.as_ptr(), + owner.unwrap_or((0 as uid_t).wrapping_sub(1)), + group.unwrap_or((0 as gid_t).wrapping_sub(1))) } })); Errno::result(res).map(drop) @@ -174,7 +273,10 @@ pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> { pub fn sethostname(name: &[u8]) -> Result<()> { // Handle some differences in type of the len arg across platforms. cfg_if! { - if #[cfg(any(target_os = "macos", target_os = "ios"))] { + if #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", ))] { type sethostname_len_t = c_int; } else { type sethostname_len_t = size_t; @@ -212,6 +314,40 @@ pub fn write(fd: RawFd, buf: &[u8]) -> Result<usize> { Errno::result(res).map(|r| r as usize) } +pub enum Whence { + SeekSet, + SeekCur, + SeekEnd, + SeekData, + SeekHole +} + +impl Whence { + fn to_libc_type(&self) -> c_int { + match self { + &Whence::SeekSet => libc::SEEK_SET, + &Whence::SeekCur => libc::SEEK_CUR, + &Whence::SeekEnd => libc::SEEK_END, + &Whence::SeekData => 3, + &Whence::SeekHole => 4 + } + } + +} + +pub fn lseek(fd: RawFd, offset: libc::off_t, whence: Whence) -> Result<libc::off_t> { + let res = unsafe { libc::lseek(fd, offset, whence.to_libc_type()) }; + + Errno::result(res).map(|r| r as libc::off_t) +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn lseek64(fd: RawFd, offset: libc::off64_t, whence: Whence) -> Result<libc::off64_t> { + let res = unsafe { libc::lseek64(fd, offset, whence.to_libc_type()) }; + + Errno::result(res).map(|r| r as libc::off64_t) +} + pub fn pipe() -> Result<(RawFd, RawFd)> { unsafe { let mut fds: [c_int; 2] = mem::uninitialized(); @@ -290,7 +426,6 @@ pub fn unlink<P: ?Sized + NixPath>(path: &P) -> Result<()> { libc::unlink(cstr.as_ptr()) } })); - Errno::result(res).map(drop) } @@ -376,19 +511,41 @@ pub fn sleep(seconds: libc::c_uint) -> c_uint { unsafe { libc::sleep(seconds) } } +/// Creates a regular file which persists even after process termination +/// +/// * `template`: a path whose 6 rightmost characters must be X, e.g. /tmp/tmpfile_XXXXXX +/// * returns: tuple of file descriptor and filename +/// +/// Err is returned either if no temporary filename could be created or the template doesn't +/// end with XXXXXX +/// +/// # Example +/// +/// ```rust +/// use nix::unistd; +/// +/// let fd = match unistd::mkstemp("/tmp/tempfile_XXXXXX") { +/// Ok((fd, path)) => { +/// unistd::unlink(path.as_path()).unwrap(); // flag file to be deleted at app termination +/// fd +/// } +/// Err(e) => panic!("mkstemp failed: {}", e) +/// }; +/// // do something with fd +/// ``` #[inline] pub fn mkstemp<P: ?Sized + NixPath>(template: &P) -> Result<(RawFd, PathBuf)> { let res = template.with_nix_path(|path| { - let owned_path = path.to_owned(); - let path_ptr = owned_path.into_raw(); + let mut path_copy = path.to_bytes_with_nul().to_owned(); + let p: *mut i8 = path_copy.as_mut_ptr() as *mut i8; unsafe { - (libc::mkstemp(path_ptr), CString::from_raw(path_ptr)) + (libc::mkstemp(p), OsStr::from_bytes(CStr::from_ptr(p).to_bytes())) } }); match res { Ok((fd, pathname)) => { try!(Errno::result(fd)); - Ok((fd, Path::new(OsStr::from_bytes(pathname.as_bytes())).to_owned())) + Ok((fd, PathBuf::from(pathname).to_owned())) } Err(e) => { Err(e) diff --git a/test/test_mount.rs b/test/test_mount.rs index 74d2938c..7ddbce9f 100644 --- a/test/test_mount.rs +++ b/test/test_mount.rs @@ -15,7 +15,7 @@ mod test_mount { use std::os::unix::fs::PermissionsExt; use std::process::{self, Command}; - use libc::{self, EACCES, EROFS}; + use libc::{EACCES, EROFS}; use nix::mount::{mount, umount, MsFlags, MS_BIND, MS_RDONLY, MS_NOEXEC}; use nix::sched::{unshare, CLONE_NEWNS, CLONE_NEWUSER}; @@ -179,8 +179,8 @@ exit 23"; let mut handle = stderr.lock(); writeln!(handle, "unshare failed: {}. Are unprivileged user namespaces available?", - e); - writeln!(handle, "mount is not being tested"); + e).unwrap(); + writeln!(handle, "mount is not being tested").unwrap(); // Exit with success because not all systems support unprivileged user namespaces, and // that's not what we're testing for. process::exit(0); diff --git a/test/test_mq.rs b/test/test_mq.rs index 94431a04..fd050d47 100644 --- a/test/test_mq.rs +++ b/test/test_mq.rs @@ -33,7 +33,9 @@ fn test_mq_send_and_receive() { let mq_name_in_child = &CString::new(b"/a_nix_test_queue".as_ref()).unwrap(); let mqd_in_child = mq_open(mq_name_in_child, O_CREAT | O_RDONLY, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, Some(&attr)).unwrap(); let mut buf = [0u8; 32]; - mq_receive(mqd_in_child, &mut buf, 1).unwrap(); + let mut prio = 0u32; + mq_receive(mqd_in_child, &mut buf, &mut prio).unwrap(); + assert!(prio == 1); write(writer, &buf).unwrap(); // pipe result to parent process. Otherwise cargo does not report test failures correctly mq_close(mqd_in_child).unwrap(); } @@ -99,10 +101,10 @@ fn test_mq_set_nonblocking() { let mqd = mq_open(mq_name, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, Some(&initial_attr)).unwrap(); mq_set_nonblock(mqd).unwrap(); let new_attr = mq_getattr(mqd); - assert!(new_attr.unwrap().mq_flags == O_NONBLOCK.bits() as c_long); + assert!(new_attr.unwrap().flags() == O_NONBLOCK.bits() as c_long); mq_remove_nonblock(mqd).unwrap(); let new_attr = mq_getattr(mqd); - assert!(new_attr.unwrap().mq_flags == 0); + assert!(new_attr.unwrap().flags() == 0); mq_close(mqd).unwrap(); } diff --git a/test/test_poll.rs b/test/test_poll.rs index 54fd4029..13a95d2c 100644 --- a/test/test_poll.rs +++ b/test/test_poll.rs @@ -4,19 +4,15 @@ use nix::unistd::{write, pipe}; #[test] fn test_poll() { let (r, w) = pipe().unwrap(); - let mut fds = [PollFd { - fd: r, - events: POLLIN, - revents: EventFlags::empty() - }]; + let mut fds = [PollFd::new(r, POLLIN, EventFlags::empty())]; let nfds = poll(&mut fds, 100).unwrap(); assert_eq!(nfds, 0); - assert!(!fds[0].revents.contains(POLLIN)); + assert!(!fds[0].revents().unwrap().contains(POLLIN)); write(w, b".").unwrap(); let nfds = poll(&mut fds, 100).unwrap(); assert_eq!(nfds, 1); - assert!(fds[0].revents.contains(POLLIN)); + assert!(fds[0].revents().unwrap().contains(POLLIN)); } diff --git a/test/test_signalfd.rs b/test/test_signalfd.rs index 8bb62bb2..28a6decb 100644 --- a/test/test_signalfd.rs +++ b/test/test_signalfd.rs @@ -1,9 +1,10 @@ extern crate nix; #[cfg(feature = "signalfd")] - use nix::sys::signalfd::SignalFd; +#[cfg(feature = "signalfd")] use nix::sys::signal; +#[cfg(feature = "signalfd")] use nix::unistd; #[cfg(feature = "signalfd")] diff --git a/test/test_unistd.rs b/test/test_unistd.rs index 9b4bff2c..8b9e6e49 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -1,31 +1,41 @@ +extern crate tempdir; + use nix::unistd::*; use nix::unistd::ForkResult::*; use nix::sys::wait::*; +use nix::sys::stat; +use std::iter; use std::ffi::CString; +use std::io::{Write, Read}; +use std::os::unix::prelude::*; +use std::env::current_dir; +use tempfile::tempfile; +use tempdir::TempDir; +use libc::off_t; #[test] fn test_fork_and_waitpid() { let pid = fork(); match pid { - Ok(Child) => {} // ignore child here - Ok(Parent { child }) => { - // assert that child was created and pid > 0 - assert!(child > 0); - let wait_status = waitpid(child, None); - match wait_status { - // assert that waitpid returned correct status and the pid is the one of the child - Ok(WaitStatus::Exited(pid_t, _)) => assert!(pid_t == child), - - // panic, must never happen - Ok(_) => panic!("Child still alive, should never happen"), - - // panic, waitpid should never fail - Err(_) => panic!("Error: waitpid Failed") - } - - }, - // panic, fork should never fail unless there is a serious problem with the OS - Err(_) => panic!("Error: Fork Failed") + Ok(Child) => {} // ignore child here + Ok(Parent { child }) => { + // assert that child was created and pid > 0 + assert!(child > 0); + let wait_status = waitpid(child, None); + match wait_status { + // assert that waitpid returned correct status and the pid is the one of the child + Ok(WaitStatus::Exited(pid_t, _)) => assert!(pid_t == child), + + // panic, must never happen + Ok(_) => panic!("Child still alive, should never happen"), + + // panic, waitpid should never fail + Err(_) => panic!("Error: waitpid Failed") + } + + }, + // panic, fork should never fail unless there is a serious problem with the OS + Err(_) => panic!("Error: Fork Failed") } } @@ -33,15 +43,15 @@ fn test_fork_and_waitpid() { fn test_wait() { let pid = fork(); match pid { - Ok(Child) => {} // ignore child here - Ok(Parent { child }) => { - let wait_status = wait(); - - // just assert that (any) one child returns with WaitStatus::Exited - assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0))); - }, - // panic, fork should never fail unless there is a serious problem with the OS - Err(_) => panic!("Error: Fork Failed") + Ok(Child) => {} // ignore child here + Ok(Parent { child }) => { + let wait_status = wait(); + + // just assert that (any) one child returns with WaitStatus::Exited + assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0))); + }, + // panic, fork should never fail unless there is a serious problem with the OS + Err(_) => panic!("Error: Fork Failed") } } @@ -123,6 +133,56 @@ macro_rules! execve_test_factory( ) ); +#[test] +fn test_getcwd() { + let mut tmp_dir = TempDir::new("test_getcwd").unwrap().into_path(); + assert!(chdir(tmp_dir.as_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) + 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()); + } + assert!(chdir(tmp_dir.as_path()).is_ok()); + assert_eq!(getcwd().unwrap(), current_dir().unwrap()); +} + +#[test] +fn test_lseek() { + const CONTENTS: &'static [u8] = b"abcdef123456"; + let mut tmp = tempfile().unwrap(); + tmp.write(CONTENTS).unwrap(); + + let offset: off_t = 5; + lseek(tmp.as_raw_fd(), offset, Whence::SeekSet).unwrap(); + + let mut buf = String::new(); + tmp.read_to_string(&mut buf).unwrap(); + assert_eq!(b"f123456", buf.as_bytes()); + + close(tmp.as_raw_fd()).unwrap(); +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +#[test] +fn test_lseek64() { + const CONTENTS: &'static [u8] = b"abcdef123456"; + let mut tmp = tempfile().unwrap(); + tmp.write(CONTENTS).unwrap(); + + lseek64(tmp.as_raw_fd(), 5, Whence::SeekSet).unwrap(); + + let mut buf = String::new(); + tmp.read_to_string(&mut buf).unwrap(); + assert_eq!(b"f123456", buf.as_bytes()); + + close(tmp.as_raw_fd()).unwrap(); +} + execve_test_factory!(test_execve, execve, b"/bin/sh", b"/system/bin/sh"); #[cfg(any(target_os = "linux", target_os = "android"))] |