aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid LeGare <legare@google.com>2022-03-21 17:53:10 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2022-03-21 17:53:10 +0000
commitd09577a8d86ae45d06a74e321736d168424ac8bf (patch)
treefb2abcd53c7bec252defd92f2b61bb3ddff8949d
parent566f028648f3e2fb9bcdc822199c3d49348ca734 (diff)
parentae4c8445f76414c46ce79a257c46307dfc7a5a20 (diff)
downloadnix-android13-qpr3-s4-release.tar.gz
Update nix to 0.23.1 am: d1a942bdc4 am: cc57d70224 am: ae4c8445f7t_frc_odp_330442040t_frc_odp_330442000t_frc_ase_330444010android-13.0.0_r83android-13.0.0_r82android-13.0.0_r81android-13.0.0_r80android-13.0.0_r79android-13.0.0_r78android-13.0.0_r77android-13.0.0_r76android-13.0.0_r75android-13.0.0_r74android-13.0.0_r73android-13.0.0_r72android-13.0.0_r71android-13.0.0_r70android-13.0.0_r69android-13.0.0_r68android-13.0.0_r67android-13.0.0_r66android-13.0.0_r65android-13.0.0_r64android-13.0.0_r63android-13.0.0_r62android-13.0.0_r61android-13.0.0_r60android-13.0.0_r59android-13.0.0_r58android-13.0.0_r57android-13.0.0_r56android-13.0.0_r54android-13.0.0_r53android-13.0.0_r52android-13.0.0_r51android-13.0.0_r50android-13.0.0_r49android-13.0.0_r48android-13.0.0_r47android-13.0.0_r46android-13.0.0_r45android-13.0.0_r44android-13.0.0_r43android-13.0.0_r42android-13.0.0_r41android-13.0.0_r40android-13.0.0_r39android-13.0.0_r38android-13.0.0_r37android-13.0.0_r36android-13.0.0_r35android-13.0.0_r34android-13.0.0_r33android-13.0.0_r32aml_go_odp_330912000aml_go_ads_330915100aml_go_ads_330915000aml_go_ads_330913000android13-qpr3-s9-releaseandroid13-qpr3-s8-releaseandroid13-qpr3-s7-releaseandroid13-qpr3-s6-releaseandroid13-qpr3-s5-releaseandroid13-qpr3-s4-releaseandroid13-qpr3-s3-releaseandroid13-qpr3-s2-releaseandroid13-qpr3-s14-releaseandroid13-qpr3-s13-releaseandroid13-qpr3-s12-releaseandroid13-qpr3-s11-releaseandroid13-qpr3-s10-releaseandroid13-qpr3-s1-releaseandroid13-qpr3-releaseandroid13-qpr3-c-s8-releaseandroid13-qpr3-c-s7-releaseandroid13-qpr3-c-s6-releaseandroid13-qpr3-c-s5-releaseandroid13-qpr3-c-s4-releaseandroid13-qpr3-c-s3-releaseandroid13-qpr3-c-s2-releaseandroid13-qpr3-c-s12-releaseandroid13-qpr3-c-s11-releaseandroid13-qpr3-c-s10-releaseandroid13-qpr3-c-s1-releaseandroid13-qpr2-s9-releaseandroid13-qpr2-s8-releaseandroid13-qpr2-s7-releaseandroid13-qpr2-s6-releaseandroid13-qpr2-s5-releaseandroid13-qpr2-s3-releaseandroid13-qpr2-s2-releaseandroid13-qpr2-s12-releaseandroid13-qpr2-s11-releaseandroid13-qpr2-s10-releaseandroid13-qpr2-s1-releaseandroid13-qpr2-releaseandroid13-qpr2-b-s1-releaseandroid13-mainline-go-adservices-releaseandroid13-frc-odp-releaseandroid13-devandroid13-d4-s2-releaseandroid13-d4-s1-releaseandroid13-d4-releaseandroid13-d3-s1-release
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/nix/+/2032805 Change-Id: I63433deeae5e050fe76db217c9e7a938c8dfdc3d
-rw-r--r--.cargo_vcs_info.json7
-rw-r--r--.gitattributes1
-rw-r--r--Android.bp2
-rw-r--r--CHANGELOG.md118
-rw-r--r--CONTRIBUTING.md114
-rw-r--r--CONVENTIONS.md86
-rw-r--r--Cargo.toml12
-rw-r--r--Cargo.toml.orig17
-rw-r--r--METADATA10
-rw-r--r--README.md23
-rw-r--r--patches/0001-Allow-android-compiled-binaries-to-use-timerfd.patch26
-rw-r--r--src/dir.rs3
-rw-r--r--src/env.rs6
-rw-r--r--src/errno.rs149
-rw-r--r--src/fcntl.rs18
-rw-r--r--src/features.rs20
-rw-r--r--src/kmod.rs1
-rw-r--r--src/lib.rs29
-rw-r--r--src/macros.rs128
-rw-r--r--src/mount/bsd.rs7
-rw-r--r--src/mount/linux.rs1
-rw-r--r--src/mount/mod.rs1
-rw-r--r--src/mqueue.rs3
-rw-r--r--src/poll.rs23
-rw-r--r--src/pty.rs12
-rw-r--r--src/sched.rs65
-rw-r--r--src/sys/aio.rs50
-rw-r--r--src/sys/epoll.rs5
-rw-r--r--src/sys/event.rs26
-rw-r--r--src/sys/eventfd.rs1
-rw-r--r--src/sys/memfd.rs29
-rw-r--r--src/sys/mman.rs112
-rw-r--r--src/sys/mod.rs21
-rw-r--r--src/sys/personality.rs4
-rw-r--r--src/sys/pthread.rs25
-rw-r--r--src/sys/ptrace/bsd.rs1
-rw-r--r--src/sys/ptrace/linux.rs47
-rw-r--r--src/sys/quota.rs2
-rw-r--r--src/sys/reboot.rs6
-rw-r--r--src/sys/resource.rs233
-rw-r--r--src/sys/select.rs30
-rw-r--r--src/sys/sendfile.rs2
-rw-r--r--src/sys/signal.rs238
-rw-r--r--src/sys/signalfd.rs25
-rw-r--r--src/sys/socket/addr.rs221
-rw-r--r--src/sys/socket/mod.rs122
-rw-r--r--src/sys/socket/sockopt.rs424
-rw-r--r--src/sys/stat.rs32
-rw-r--r--src/sys/statfs.rs54
-rw-r--r--src/sys/statvfs.rs2
-rw-r--r--src/sys/termios.rs124
-rw-r--r--src/sys/time.rs26
-rw-r--r--src/sys/timerfd.rs14
-rw-r--r--src/sys/uio.rs37
-rw-r--r--src/sys/utsname.rs8
-rw-r--r--src/sys/wait.rs20
-rw-r--r--src/time.rs8
-rw-r--r--src/ucontext.rs1
-rw-r--r--src/unistd.rs134
-rw-r--r--test/common/mod.rs6
-rw-r--r--test/sys/mod.rs4
-rw-r--r--test/sys/test_aio.rs18
-rw-r--r--test/sys/test_epoll.rs5
-rw-r--r--test/sys/test_inotify.rs6
-rw-r--r--test/sys/test_mman.rs40
-rw-r--r--test/sys/test_pthread.rs7
-rw-r--r--test/sys/test_ptrace.rs60
-rw-r--r--test/sys/test_select.rs32
-rw-r--r--test/sys/test_signal.rs8
-rw-r--r--test/sys/test_signalfd.rs2
-rw-r--r--test/sys/test_socket.rs357
-rw-r--r--test/sys/test_sockopt.rs105
-rw-r--r--test/sys/test_termios.rs6
-rw-r--r--test/sys/test_uio.rs12
-rw-r--r--test/sys/test_wait.rs10
-rw-r--r--test/test.rs6
-rw-r--r--test/test_dir.rs1
-rw-r--r--test/test_fcntl.rs32
-rw-r--r--test/test_kmod/mod.rs59
-rw-r--r--test/test_mount.rs5
-rw-r--r--test/test_mq.rs12
-rw-r--r--test/test_poll.rs16
-rw-r--r--test/test_pty.rs18
-rw-r--r--test/test_resource.rs23
-rw-r--r--test/test_stat.rs52
-rw-r--r--test/test_time.rs6
-rw-r--r--test/test_unistd.rs49
87 files changed, 2791 insertions, 1132 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index fb7ddb9..90eb8fa 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,6 @@
{
"git": {
- "sha1": "b29a85165b0bf494974642cc6ba58a924f143948"
- }
-}
+ "sha1": "afba7c5a33dd4fd62b047e64089487cf822ccec2"
+ },
+ "path_in_vcs": ""
+} \ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 3d432e0..0000000
--- a/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-/CHANGELOG.md merge=union
diff --git a/Android.bp b/Android.bp
index 2257d36..88940a1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -26,7 +26,7 @@ rust_library {
host_supported: true,
crate_name: "nix",
cargo_env_compat: true,
- cargo_pkg_version: "0.22.1",
+ cargo_pkg_version: "0.23.1",
srcs: ["src/lib.rs"],
edition: "2018",
rustlibs: [
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5367d7a..77d5b2a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,18 +3,132 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).
-## [0.22.1] - 13 August 2021
+## [0.23.1] - 2021-12-16
+
### Added
### Changed
+
+- Relaxed the bitflags requirement from 1.3.1 to 1.1. This partially reverts
+ #1492. From now on, the MSRV is not guaranteed to work with all versions of
+ all dependencies, just with some version of all dependencies.
+ (#[1607](https://github.com/nix-rust/nix/pull/1607))
+
### Fixed
-- Locked bitflags to < 1.3.0 to fix the build with rust < 1.46.0.
+- Fixed soundness issues in `FdSet::insert`, `FdSet::remove`, and
+ `FdSet::contains` involving file descriptors outside of the range
+ `0..FD_SETSIZE`.
+ (#[1575](https://github.com/nix-rust/nix/pull/1575))
+
+### Removed
+
+## [0.23.0] - 2021-09-28
+### Added
+
+- Added the `LocalPeerCred` sockopt.
+ (#[1482](https://github.com/nix-rust/nix/pull/1482))
+- Added `TimeSpec::from_duration` and `TimeSpec::from_timespec`
+ (#[1465](https://github.com/nix-rust/nix/pull/1465))
+- Added `IPV6_V6ONLY` sockopt.
+ (#[1470](https://github.com/nix-rust/nix/pull/1470))
+- Added `impl From<User> for libc::passwd` trait implementation to convert a `User`
+ into a `libc::passwd`. Consumes the `User` struct to give ownership over
+ the member pointers.
+ (#[1471](https://github.com/nix-rust/nix/pull/1471))
+- Added `pthread_kill`.
+ (#[1472](https://github.com/nix-rust/nix/pull/1472))
+- Added `mknodat`.
+ (#[1473](https://github.com/nix-rust/nix/pull/1473))
+- Added `setrlimit` and `getrlimit`.
+ (#[1302](https://github.com/nix-rust/nix/pull/1302))
+- Added `ptrace::interrupt` method for platforms that support `PTRACE_INTERRUPT`
+ (#[1422](https://github.com/nix-rust/nix/pull/1422))
+- Added `IP6T_SO_ORIGINAL_DST` sockopt.
+ (#[1490](https://github.com/nix-rust/nix/pull/1490))
+- Added the `PTRACE_EVENT_STOP` variant to the `sys::ptrace::Event` enum
+ (#[1335](https://github.com/nix-rust/nix/pull/1335))
+- Exposed `SockAddr::from_raw_sockaddr`
+ (#[1447](https://github.com/nix-rust/nix/pull/1447))
+- Added `TcpRepair`
+ (#[1503](https://github.com/nix-rust/nix/pull/1503))
+- Enabled `pwritev` and `preadv` for more operating systems.
+ (#[1511](https://github.com/nix-rust/nix/pull/1511))
+- Added support for `TCP_MAXSEG` TCP Maximum Segment Size socket options
+ (#[1292](https://github.com/nix-rust/nix/pull/1292))
+- Added `Ipv4RecvErr` and `Ipv6RecvErr` sockopts and associated control messages.
+ (#[1514](https://github.com/nix-rust/nix/pull/1514))
+- Added `AsRawFd` implementation on `PollFd`.
+ (#[1516](https://github.com/nix-rust/nix/pull/1516))
+- Added `Ipv4Ttl` and `Ipv6Ttl` sockopts.
+ (#[1515](https://github.com/nix-rust/nix/pull/1515))
+- Added `MAP_EXCL`, `MAP_ALIGNED_SUPER`, and `MAP_CONCEAL` mmap flags, and
+ exposed `MAP_ANONYMOUS` for all operating systems.
+ (#[1522](https://github.com/nix-rust/nix/pull/1522))
+ (#[1525](https://github.com/nix-rust/nix/pull/1525))
+ (#[1531](https://github.com/nix-rust/nix/pull/1531))
+ (#[1534](https://github.com/nix-rust/nix/pull/1534))
+- Added read/write accessors for 'events' on `PollFd`.
+ (#[1517](https://github.com/nix-rust/nix/pull/1517))
+
+### Changed
+
+- `FdSet::{contains, highest, fds}` no longer require a mutable reference.
+ (#[1464](https://github.com/nix-rust/nix/pull/1464))
+- `User::gecos` and corresponding `libc::passwd::pw_gecos` are supported on
+ 64-bit Android, change conditional compilation to include the field in
+ 64-bit Android builds
+ (#[1471](https://github.com/nix-rust/nix/pull/1471))
+- `eventfd`s are supported on Android, change conditional compilation to
+ include `sys::eventfd::eventfd` and `sys::eventfd::EfdFlags`for Android
+ builds.
+ (#[1481](https://github.com/nix-rust/nix/pull/1481))
+- Most enums that come from C, for example `Errno`, are now marked as
+ `#[non_exhaustive]`.
+ (#[1474](https://github.com/nix-rust/nix/pull/1474))
+- Many more functions, mostly contructors, are now `const`.
+ (#[1476](https://github.com/nix-rust/nix/pull/1476))
+ (#[1492](https://github.com/nix-rust/nix/pull/1492))
+- `sys::event::KEvent::filter` now returns a `Result` instead of being
+ infalliable. The only cases where it will now return an error are cases
+ where it previously would've had undefined behavior.
+ (#[1484](https://github.com/nix-rust/nix/pull/1484))
+- Minimum supported Rust version is now 1.46.0.
+ ([#1492](https://github.com/nix-rust/nix/pull/1492))
+- Rework `UnixAddr` to encapsulate internals better in order to fix soundness
+ issues. No longer allows creating a `UnixAddr` from a raw `sockaddr_un`.
+ ([#1496](https://github.com/nix-rust/nix/pull/1496))
+- Raised bitflags to 1.3.0 and the MSRV to 1.46.0.
+ ([#1492](https://github.com/nix-rust/nix/pull/1492))
+
+### Fixed
+
+- `posix_fadvise` now returns errors in the conventional way, rather than as a
+ non-zero value in `Ok()`.
+ (#[1538](https://github.com/nix-rust/nix/pull/1538))
+- Added more errno definitions for better backwards compatibility with
+ Nix 0.21.0.
+ (#[1467](https://github.com/nix-rust/nix/pull/1467))
+- Fixed potential undefined behavior in `Signal::try_from` on some platforms.
+ (#[1484](https://github.com/nix-rust/nix/pull/1484))
+- Fixed buffer overflow in `unistd::getgrouplist`.
+ (#[1545](https://github.com/nix-rust/nix/pull/1545))
+
### Removed
- Removed a couple of termios constants on redox that were never actually
supported.
(#[1483](https://github.com/nix-rust/nix/pull/1483))
+- Removed `nix::sys::signal::NSIG`. It was of dubious utility, and not correct
+ for all platforms.
+ (#[1484](https://github.com/nix-rust/nix/pull/1484))
+- Removed support for 32-bit Apple targets, since they've been dropped by both
+ Rustc and Xcode.
+ (#[1492](https://github.com/nix-rust/nix/pull/1492))
+- Deprecated `SockAddr/InetAddr::to_str` in favor of `ToString::to_string`
+ (#[1495](https://github.com/nix-rust/nix/pull/1495))
+- Removed `SigevNotify` on OpenBSD and Redox.
+ (#[1511](https://github.com/nix-rust/nix/pull/1511))
## [0.22.0] - 9 July 2021
### Added
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 55990c4..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,114 +0,0 @@
-# Contributing to nix
-
-We're really glad you're interested in contributing to nix! This
-document has a few pointers and guidelines to help get you started.
-
-To have a welcoming and inclusive project, nix uses the Rust project's
-[Code of Conduct][conduct]. All contributors are expected to follow it.
-
-[conduct]: https://www.rust-lang.org/conduct.html
-
-
-# Issues
-
-We use GitHub's [issue tracker][issues].
-
-[issues]: https://github.com/nix-rust/nix/issues
-
-
-## Bug reports
-
-Before submitting a new bug report, please [search existing
-issues][issue-search] to see if there's something related. If not, just
-[open a new issue][new-issue]!
-
-As a reminder, the more information you can give in your issue, the
-easier it is to figure out how to fix it. For nix, this will likely
-include the OS and version, and the architecture.
-
-[issue-search]: https://github.com/nix-rust/nix/search?utf8=%E2%9C%93&q=is%3Aissue&type=Issues
-[new-issue]: https://github.com/nix-rust/nix/issues/new
-
-
-## Feature / API requests
-
-If you'd like a new API or feature added, please [open a new
-issue][new-issue] requesting it. As with reporting a bug, the more
-information you can provide, the better.
-
-
-## Labels
-
-We use labels to help manage issues. The structure is modeled after
-[Rust's issue labeling scheme][rust-labels]:
-- **A-**prefixed labels state which area of the project the issue
- relates to
-- **E-**prefixed labels explain the level of experience necessary to fix the
- issue
-- **O-**prefixed labels specify the OS for issues that are OS-specific
-- **R-**prefixed labels specify the architecture for issues that are
- architecture-specific
-
-[rust-labels]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#issue-triage
-
-
-# Pull requests
-
-GitHub pull requests are the primary mechanism we use to change nix. GitHub itself has
-some [great documentation][pr-docs] on using the Pull Request feature. We use the 'fork and
-pull' model described there.
-
-Please make pull requests against the `master` branch.
-
-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
-
-nix has a test suite that you can run with `cargo test`. Ideally, we'd like pull
-requests to include tests where they make sense. For example, when fixing a bug,
-add a test that would have failed without the fix.
-
-After you've made your change, make sure the tests pass in your development
-environment. We also have [continuous integration set up on
-Cirrus-CI][cirrus-ci], which might find some issues on other platforms. The CI
-will run once you open a pull request.
-
-There is also infrastructure for running tests for other targets
-locally. More information is available in the [CI Readme][ci-readme].
-
-[cirrus-ci]: https://cirrus-ci.com/github/nix-rust/nix
-[ci-readme]: ci/README.md
-
-### Disabling a test in the CI environment
-
-Sometimes there are features that cannot be tested in the CI environment.
-To stop a test from running under CI, add `skip_if_cirrus!()` to it. Please
-describe the reason it shouldn't run under CI, and a link to an issue if
-possible!
-
-## bors, the bot who merges all the PRs
-
-All pull requests are merged via [bors], an integration bot. After the
-pull request has been reviewed, the reviewer will leave a comment like
-
-> bors r+
-
-to let bors know that it was approved. Then bors will check that it passes
-tests when merged with the latest changes in the `master` branch, and
-merge if the tests succeed.
-
-[bors]: https://bors-ng.github.io/
-
-
-## API conventions
-
-If you're adding a new API, we have a [document with
-conventions][conventions] to use throughout the nix project.
-
-[conventions]: https://github.com/nix-rust/nix/blob/master/CONVENTIONS.md
diff --git a/CONVENTIONS.md b/CONVENTIONS.md
deleted file mode 100644
index 2461085..0000000
--- a/CONVENTIONS.md
+++ /dev/null
@@ -1,86 +0,0 @@
-# Conventions
-
-In order to achieve our goal of wrapping [libc][libc] code in idiomatic rust
-constructs with minimal performance overhead, we follow the following
-conventions.
-
-Note that, thus far, not all the code follows these conventions and not all
-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
-
-We do not define integer constants ourselves, but use or reexport them from the
-[libc crate][libc].
-
-We use the functions exported from [libc][libc] instead of writing our own
-`extern` declarations.
-
-We use the `struct` definitions from [libc][libc] internally instead of writing
-our own. If we want to add methods to a libc type, we use the newtype pattern.
-For example,
-
-```rust
-pub struct SigSet(libc::sigset_t);
-
-impl SigSet {
- ...
-}
-```
-
-When creating newtypes, we use Rust's `CamelCase` type naming convention.
-
-## Bitflags
-
-Many C functions have flags parameters that are combined from constants using
-bitwise operations. We represent the types of these parameters by types defined
-using our `libc_bitflags!` macro, which is a convenience wrapper around the
-`bitflags!` macro from the [bitflags crate][bitflags] that brings in the
-constant value from `libc`.
-
-We name the type for a set of constants whose element's names start with `FOO_`
-`FooFlags`.
-
-For example,
-
-```rust
-libc_bitflags!{
- pub struct ProtFlags: libc::c_int {
- PROT_NONE;
- PROT_READ;
- PROT_WRITE;
- PROT_EXEC;
- #[cfg(any(target_os = "linux", target_os = "android"))]
- PROT_GROWSDOWN;
- #[cfg(any(target_os = "linux", target_os = "android"))]
- PROT_GROWSUP;
- }
-}
-```
-
-
-## Enumerations
-
-We represent sets of constants that are intended as mutually exclusive arguments
-to parameters of functions by [enumerations][enum].
-
-
-## Structures Initialized by libc Functions
-
-Whenever we need to use a [libc][libc] function to properly initialize a
-variable and said function allows us to use uninitialized memory, we use
-[`std::mem::MaybeUninit`][std_MaybeUninit] when defining the variable. This
-allows us to avoid the overhead incurred by zeroing or otherwise initializing
-the variable.
-
-[bitflags]: https://crates.io/crates/bitflags/
-[enum]: https://doc.rust-lang.org/reference.html#enumerations
-[libc]: https://crates.io/crates/libc/
-[std_MaybeUninit]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html
diff --git a/Cargo.toml b/Cargo.toml
index 09c5a73..122c1af 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,10 +11,11 @@
[package]
edition = "2018"
+rust-version = "1.46"
name = "nix"
-version = "0.22.1"
+version = "0.23.1"
authors = ["The nix-rust Project Developers"]
-exclude = ["/.gitignore", "/.cirrus.yml", "/ci/*", "/Cross.toml", "/RELEASE_PROCEDURE.md", "/bors.toml"]
+include = ["src/**/*", "test/**/*", "LICENSE", "README.md", "CHANGELOG.md"]
description = "Rust friendly bindings to *nix APIs"
categories = ["os::unix-apis"]
license = "MIT"
@@ -47,13 +48,13 @@ harness = false
name = "test-ptymaster-drop"
path = "test/test_ptymaster_drop.rs"
[dependencies.bitflags]
-version = ">= 1.1.0, < 1.3.0"
+version = "1.1"
[dependencies.cfg-if]
version = "1.0"
[dependencies.libc]
-version = "0.2.99"
+version = "0.2.102"
features = ["extra_traits"]
[dev-dependencies.assert-impl]
version = "0.1"
@@ -61,6 +62,9 @@ version = "0.1"
[dev-dependencies.lazy_static]
version = "1.2"
+[dev-dependencies.parking_lot]
+version = "0.11.2"
+
[dev-dependencies.rand]
version = "0.8"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 7a527d3..d2ca8ee 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -2,19 +2,13 @@
name = "nix"
description = "Rust friendly bindings to *nix APIs"
edition = "2018"
-version = "0.22.1"
+version = "0.23.1"
+rust-version = "1.46"
authors = ["The nix-rust Project Developers"]
repository = "https://github.com/nix-rust/nix"
license = "MIT"
categories = ["os::unix-apis"]
-exclude = [
- "/.gitignore",
- "/.cirrus.yml",
- "/ci/*",
- "/Cross.toml",
- "/RELEASE_PROCEDURE.md",
- "/bors.toml"
-]
+include = ["src/**/*", "test/**/*", "LICENSE", "README.md", "CHANGELOG.md"]
[package.metadata.docs.rs]
targets = [
@@ -32,8 +26,8 @@ targets = [
]
[dependencies]
-libc = { version = "0.2.99", features = [ "extra_traits" ] }
-bitflags = ">= 1.1.0, < 1.3.0"
+libc = { version = "0.2.102", features = [ "extra_traits" ] }
+bitflags = "1.1"
cfg-if = "1.0"
[target.'cfg(not(target_os = "redox"))'.dependencies]
@@ -45,6 +39,7 @@ cc = "1"
[dev-dependencies]
assert-impl = "0.1"
lazy_static = "1.2"
+parking_lot = "0.11.2"
rand = "0.8"
tempfile = "3.2.0"
semver = "1.0.0"
diff --git a/METADATA b/METADATA
index 163016d..e0fef6c 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/nix/nix-0.22.1.crate"
+ value: "https://static.crates.io/crates/nix/nix-0.23.1.crate"
}
- version: "0.22.1"
+ version: "0.23.1"
license_type: NOTICE
last_upgrade_date {
- year: 2021
- month: 9
- day: 22
+ year: 2022
+ month: 3
+ day: 16
}
}
diff --git a/README.md b/README.md
index 566043f..a8759f1 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# Rust bindings to *nix APIs
[![Cirrus Build Status](https://api.cirrus-ci.com/github/nix-rust/nix.svg)](https://cirrus-ci.com/github/nix-rust/nix)
-[![crates.io](https://meritbadge.herokuapp.com/nix)](https://crates.io/crates/nix)
+[![crates.io](https://img.shields.io/crates/v/nix.svg)](https://crates.io/crates/nix)
[Documentation (Releases)](https://docs.rs/nix/)
@@ -68,33 +68,26 @@ Tier 2:
* aarch64-linux-android
* arm-linux-androideabi
* arm-unknown-linux-musleabi
- * armv7-apple-ios
* armv7-linux-androideabi
- * armv7s-apple-ios
- * i386-apple-ios
- * i686-apple-darwin
* i686-linux-android
* powerpc-unknown-linux-gnu
* s390x-unknown-linux-gnu
* x86_64-apple-ios
* x86_64-linux-android
+ * x86_64-unknown-illumos
* x86_64-unknown-netbsd
Tier 3:
* x86_64-fuchsia
- * x86_64-unknown-redox
+ * x86_64-unknown-dragonfly
* x86_64-unknown-linux-gnux32
+ * x86_64-unknown-openbsd
+ * x86_64-unknown-redox
-## Usage
-
-`nix` requires Rust 1.41.0 or newer.
-
-To use `nix`, add this to your `Cargo.toml`:
+## Minimum Supported Rust Version (MSRV)
-```toml
-[dependencies]
-nix = "0.22.1"
-```
+nix is supported on Rust 1.46.0 and higher. It's MSRV will not be
+changed in the future without bumping the major or minor version.
## Contributing
diff --git a/patches/0001-Allow-android-compiled-binaries-to-use-timerfd.patch b/patches/0001-Allow-android-compiled-binaries-to-use-timerfd.patch
deleted file mode 100644
index e164a3c..0000000
--- a/patches/0001-Allow-android-compiled-binaries-to-use-timerfd.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From e09a4f4e8661e8036819deb97dc1d9fcf2a49d2b Mon Sep 17 00:00:00 2001
-From: Zach Johnson <zachoverflow@google.com>
-Date: Tue, 17 Nov 2020 11:05:57 -0800
-Subject: [PATCH] Allow android compiled binaries to use timerfd
-
-Bug: 171749953
-Test: compile
-Change-Id: Iaf16615cf834df02537e3b569ab08dd98a497a70
----
- src/sys/mod.rs | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/sys/mod.rs b/src/sys/mod.rs
-index bf7f541..b8b9e6f 100644
---- a/src/sys/mod.rs
-+++ b/src/sys/mod.rs
-@@ -103,5 +103,5 @@ pub mod wait;
- #[cfg(any(target_os = "android", target_os = "linux"))]
- pub mod inotify;
-
--#[cfg(target_os = "linux")]
-+#[cfg(any(target_os = "android", target_os = "linux"))]
- pub mod timerfd;
---
-2.29.2.299.gdc1121823c-goog
-
diff --git a/src/dir.rs b/src/dir.rs
index 65ed2e9..ed70a45 100644
--- a/src/dir.rs
+++ b/src/dir.rs
@@ -84,7 +84,7 @@ impl AsRawFd for Dir {
impl Drop for Dir {
fn drop(&mut self) {
let e = Errno::result(unsafe { libc::closedir(self.0.as_ptr()) });
- if !std::thread::panicking() && e == Err(Error::from(Errno::EBADF)) {
+ if !std::thread::panicking() && e == Err(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
}
@@ -211,6 +211,7 @@ impl Entry {
target_os = "linux",
target_os = "macos",
target_os = "solaris")))]
+ #[allow(clippy::useless_conversion)] // Not useless on all OSes
pub fn ino(&self) -> u64 {
u64::from(self.0.d_fileno)
}
diff --git a/src/env.rs b/src/env.rs
index 613b0cd..bcae287 100644
--- a/src/env.rs
+++ b/src/env.rs
@@ -1,3 +1,4 @@
+//! Environment variables
use cfg_if::cfg_if;
use std::fmt;
@@ -38,7 +39,6 @@ impl std::error::Error for ClearEnvError {}
/// environment access in the program is via `std::env`, but the requirement on
/// thread safety must still be upheld.
pub unsafe fn clearenv() -> std::result::Result<(), ClearEnvError> {
- let ret;
cfg_if! {
if #[cfg(any(target_os = "fuchsia",
target_os = "wasi",
@@ -47,13 +47,13 @@ pub unsafe fn clearenv() -> std::result::Result<(), ClearEnvError> {
target_os = "linux",
target_os = "android",
target_os = "emscripten"))] {
- ret = libc::clearenv();
+ let ret = libc::clearenv();
} else {
use std::env;
for (name, _) in env::vars_os() {
env::remove_var(name);
}
- ret = 0;
+ let ret = 0;
}
}
diff --git a/src/errno.rs b/src/errno.rs
index 00e2014..3da246e 100644
--- a/src/errno.rs
+++ b/src/errno.rs
@@ -63,7 +63,7 @@ impl Errno {
since = "0.22.0",
note = "It's a no-op now; just delete it."
)]
- pub fn as_errno(self) -> Option<Self> {
+ pub const fn as_errno(self) -> Option<Self> {
Some(self)
}
@@ -72,8 +72,9 @@ impl Errno {
since = "0.22.0",
note = "It's a no-op now; just delete it."
)]
+ #[allow(clippy::wrong_self_convention)] // False positive
pub fn from_errno(errno: Errno) -> Error {
- Error::from(errno)
+ errno
}
/// Create a new invalid argument error (`EINVAL`)
@@ -81,7 +82,7 @@ impl Errno {
since = "0.22.0",
note = "Use Errno::EINVAL instead"
)]
- pub fn invalid_argument() -> Error {
+ pub const fn invalid_argument() -> Error {
Errno::EINVAL
}
@@ -93,7 +94,7 @@ impl Errno {
desc(self)
}
- pub fn from_i32(err: i32) -> Errno {
+ pub const fn from_i32(err: i32) -> Errno {
from_i32(err)
}
@@ -103,6 +104,7 @@ impl Errno {
/// Returns `Ok(value)` if it does not contain the sentinel value. This
/// should not be used when `-1` is not the errno sentinel value.
+ #[inline]
pub fn result<S: ErrnoSentinel + PartialEq<S>>(value: S) -> Result<S> {
if value == S::sentinel() {
Err(Self::last())
@@ -122,7 +124,7 @@ impl Errno {
)]
#[allow(non_snake_case)]
#[inline]
- pub fn Sys(errno: Errno) -> Error {
+ pub const fn Sys(errno: Errno) -> Error {
errno
}
}
@@ -768,6 +770,7 @@ fn desc(errno: Errno) -> &'static str {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -905,13 +908,29 @@ mod consts {
EHWPOISON = libc::EHWPOISON,
}
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EWOULDBLOCK instead"
+ )]
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EDEADLOCK instead"
+ )]
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::ENOTSUP instead"
+ )]
+ pub const ENOTSUP: Errno = Errno::EOPNOTSUPP;
+
impl Errno {
pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
pub const EDEADLOCK: Errno = Errno::EDEADLK;
pub const ENOTSUP: Errno = Errno::EOPNOTSUPP;
}
- pub fn from_i32(e: i32) -> Errno {
+ pub const fn from_i32(e: i32) -> Errno {
use self::Errno::*;
match e {
@@ -1057,6 +1076,7 @@ mod consts {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -1167,13 +1187,29 @@ mod consts {
EQFULL = libc::EQFULL,
}
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::ELAST instead"
+ )]
+ pub const ELAST: Errno = Errno::EQFULL;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EWOULDBLOCK instead"
+ )]
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EDEADLOCK instead"
+ )]
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+
impl Errno {
pub const ELAST: Errno = Errno::EQFULL;
pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
pub const EDEADLOCK: Errno = Errno::EDEADLK;
}
- pub fn from_i32(e: i32) -> Errno {
+ pub const fn from_i32(e: i32) -> Errno {
use self::Errno::*;
match e {
@@ -1292,6 +1328,7 @@ mod consts {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -1392,6 +1429,27 @@ mod consts {
EOWNERDEAD = libc::EOWNERDEAD,
}
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::ELAST instead"
+ )]
+ pub const ELAST: Errno = Errno::EOWNERDEAD;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EWOULDBLOCK instead"
+ )]
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EDEADLOCK instead"
+ )]
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EOPNOTSUPP instead"
+ )]
+ pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
+
impl Errno {
pub const ELAST: Errno = Errno::EOWNERDEAD;
pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
@@ -1399,7 +1457,7 @@ mod consts {
pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
}
- pub fn from_i32(e: i32) -> Errno {
+ pub const fn from_i32(e: i32) -> Errno {
use self::Errno::*;
match e {
@@ -1509,6 +1567,7 @@ mod consts {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -1607,6 +1666,27 @@ mod consts {
EASYNC = libc::EASYNC,
}
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::ELAST instead"
+ )]
+ pub const ELAST: Errno = Errno::EASYNC;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EWOULDBLOCK instead"
+ )]
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EDEADLOCK instead"
+ )]
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EOPNOTSUPP instead"
+ )]
+ pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
+
impl Errno {
pub const ELAST: Errno = Errno::EASYNC;
pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
@@ -1614,7 +1694,7 @@ mod consts {
pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
}
- pub fn from_i32(e: i32) -> Errno {
+ pub const fn from_i32(e: i32) -> Errno {
use self::Errno::*;
match e {
@@ -1722,6 +1802,7 @@ mod consts {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -1821,12 +1902,23 @@ mod consts {
EPROTO = libc::EPROTO,
}
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::ELAST instead"
+ )]
+ pub const ELAST: Errno = Errno::ENOTSUP;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EWOULDBLOCK instead"
+ )]
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+
impl Errno {
pub const ELAST: Errno = Errno::ENOTSUP;
pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
}
- pub fn from_i32(e: i32) -> Errno {
+ pub const fn from_i32(e: i32) -> Errno {
use self::Errno::*;
match e {
@@ -1934,6 +2026,7 @@ mod consts {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -2034,12 +2127,23 @@ mod consts {
EPROTO = libc::EPROTO,
}
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::ELAST instead"
+ )]
+ pub const ELAST: Errno = Errno::ENOTSUP;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EWOULDBLOCK instead"
+ )]
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+
impl Errno {
pub const ELAST: Errno = Errno::ENOTSUP;
pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
}
- pub fn from_i32(e: i32) -> Errno {
+ pub const fn from_i32(e: i32) -> Errno {
use self::Errno::*;
match e {
@@ -2148,6 +2252,7 @@ mod consts {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -2237,11 +2342,17 @@ mod consts {
EPROTO = libc::EPROTO,
}
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EWOULDBLOCK instead"
+ )]
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+
impl Errno {
pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
}
- pub fn from_i32(e: i32) -> Errno {
+ pub const fn from_i32(e: i32) -> Errno {
use self::Errno::*;
match e {
@@ -2339,6 +2450,7 @@ mod consts {
mod consts {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
+ #[non_exhaustive]
pub enum Errno {
UnknownErrno = 0,
EPERM = libc::EPERM,
@@ -2464,12 +2576,23 @@ mod consts {
ESTALE = libc::ESTALE,
}
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::ELAST instead"
+ )]
+ pub const ELAST: Errno = Errno::ELAST;
+ #[deprecated(
+ since = "0.22.1",
+ note = "use nix::errno::Errno::EWOULDBLOCK instead"
+ )]
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+
impl Errno {
pub const ELAST: Errno = Errno::ESTALE;
pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
}
- pub fn from_i32(e: i32) -> Errno {
+ pub const fn from_i32(e: i32) -> Errno {
use self::Errno::*;
match e {
diff --git a/src/fcntl.rs b/src/fcntl.rs
index f8f1372..dd8e59a 100644
--- a/src/fcntl.rs
+++ b/src/fcntl.rs
@@ -325,7 +325,7 @@ fn inner_readlink<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P) -> Result
Some(next_size) => try_size = next_size,
// It's absurd that this would happen, but handle it sanely
// anyway.
- None => break Err(super::Error::from(Errno::ENAMETOOLONG)),
+ None => break Err(Errno::ENAMETOOLONG),
}
}
}
@@ -374,6 +374,7 @@ libc_bitflags!(
#[cfg(not(target_os = "redox"))]
#[derive(Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
pub enum FcntlArg<'a> {
F_DUPFD(RawFd),
F_DUPFD_CLOEXEC(RawFd),
@@ -405,6 +406,7 @@ pub enum FcntlArg<'a> {
#[cfg(target_os = "redox")]
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
+#[non_exhaustive]
pub enum FcntlArg {
F_DUPFD(RawFd),
F_DUPFD_CLOEXEC(RawFd),
@@ -454,6 +456,7 @@ pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
pub enum FlockArg {
LockShared,
LockExclusive,
@@ -643,12 +646,12 @@ pub fn fallocate(
))]
mod posix_fadvise {
use crate::errno::Errno;
- use libc;
use std::os::unix::io::RawFd;
use crate::Result;
libc_enum! {
#[repr(i32)]
+ #[non_exhaustive]
pub enum PosixFadviseAdvice {
POSIX_FADV_NORMAL,
POSIX_FADV_SEQUENTIAL,
@@ -664,9 +667,14 @@ mod posix_fadvise {
offset: libc::off_t,
len: libc::off_t,
advice: PosixFadviseAdvice,
- ) -> Result<libc::c_int> {
+ ) -> Result<()> {
let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
- Errno::result(res)
+
+ if res == 0 {
+ Ok(())
+ } else {
+ Err(Errno::from_i32(res))
+ }
}
}
@@ -683,6 +691,6 @@ pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Resu
match Errno::result(res) {
Err(err) => Err(err),
Ok(0) => Ok(()),
- Ok(errno) => Err(crate::Error::from(Errno::from_i32(errno))),
+ Ok(errno) => Err(Errno::from_i32(errno)),
}
}
diff --git a/src/features.rs b/src/features.rs
index bcda45d..ed80fd7 100644
--- a/src/features.rs
+++ b/src/features.rs
@@ -94,22 +94,28 @@ mod os {
}
}
-#[cfg(any(target_os = "illumos"))]
+#[cfg(any(
+ target_os = "dragonfly", // Since ???
+ target_os = "freebsd", // Since 10.0
+ target_os = "illumos", // Since ???
+ target_os = "netbsd", // Since 6.0
+ target_os = "openbsd", // Since 5.7
+ target_os = "redox", // Since 1-july-2020
+))]
mod os {
/// Check if the OS supports atomic close-on-exec for sockets
- pub fn socket_atomic_cloexec() -> bool {
+ pub const fn socket_atomic_cloexec() -> bool {
true
}
}
-#[cfg(any(target_os = "macos", target_os = "freebsd",
- target_os = "dragonfly", target_os = "ios",
- target_os = "openbsd", target_os = "netbsd",
- target_os = "redox", target_os = "fuchsia",
+#[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "fuchsia",
target_os = "solaris"))]
mod os {
/// Check if the OS supports atomic close-on-exec for sockets
- pub fn socket_atomic_cloexec() -> bool {
+ pub const fn socket_atomic_cloexec() -> bool {
false
}
}
diff --git a/src/kmod.rs b/src/kmod.rs
index 89e577a..c42068c 100644
--- a/src/kmod.rs
+++ b/src/kmod.rs
@@ -2,7 +2,6 @@
//!
//! For more details see
-use libc;
use std::ffi::CStr;
use std::os::unix::io::AsRawFd;
diff --git a/src/lib.rs b/src/lib.rs
index 3b534a5..3a2b63a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,15 +5,13 @@
#![crate_name = "nix"]
#![cfg(unix)]
#![allow(non_camel_case_types)]
-// 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)]
#![cfg_attr(test, deny(warnings))]
#![recursion_limit = "500"]
#![deny(unused)]
#![deny(unstable_features)]
#![deny(missing_copy_implementations)]
#![deny(missing_debug_implementations)]
+#![warn(missing_docs)]
// Re-exported external crates
pub use libc;
@@ -23,13 +21,14 @@ pub use libc;
// Public crates
#[cfg(not(target_os = "redox"))]
+#[allow(missing_docs)]
pub mod dir;
pub mod env;
+#[allow(missing_docs)]
pub mod errno;
-#[deny(missing_docs)]
pub mod features;
+#[allow(missing_docs)]
pub mod fcntl;
-#[deny(missing_docs)]
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
@@ -42,6 +41,7 @@ pub mod fcntl;
pub mod ifaddrs;
#[cfg(any(target_os = "android",
target_os = "linux"))]
+#[allow(missing_docs)]
pub mod kmod;
#[cfg(any(target_os = "android",
target_os = "freebsd",
@@ -52,23 +52,24 @@ pub mod mount;
target_os = "fushsia",
target_os = "linux",
target_os = "netbsd"))]
+#[allow(missing_docs)]
pub mod mqueue;
-#[deny(missing_docs)]
#[cfg(not(target_os = "redox"))]
pub mod net;
-#[deny(missing_docs)]
pub mod poll;
-#[deny(missing_docs)]
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
pub mod pty;
pub mod sched;
pub mod sys;
+#[allow(missing_docs)]
pub mod time;
// This can be implemented for other platforms as soon as libc
// provides bindings for them.
#[cfg(all(target_os = "linux",
any(target_arch = "x86", target_arch = "x86_64")))]
+#[allow(missing_docs)]
pub mod ucontext;
+#[allow(missing_docs)]
pub mod unistd;
/*
@@ -101,11 +102,17 @@ pub type Result<T> = result::Result<T, Errno>;
/// ones.
pub type Error = Errno;
+/// Common trait used to represent file system paths by many Nix functions.
pub trait NixPath {
+ /// Is the path empty?
fn is_empty(&self) -> bool;
+ /// Length of the path in bytes
fn len(&self) -> usize;
+ /// Execute a function with this path as a `CStr`.
+ ///
+ /// Mostly used internally by Nix.
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where F: FnOnce(&CStr) -> T;
}
@@ -153,7 +160,7 @@ impl NixPath for CStr {
where F: FnOnce(&CStr) -> T {
// Equivalence with the [u8] impl.
if self.len() >= PATH_MAX as usize {
- return Err(Error::from(Errno::ENAMETOOLONG))
+ return Err(Errno::ENAMETOOLONG)
}
Ok(f(self))
@@ -174,11 +181,11 @@ impl NixPath for [u8] {
let mut buf = [0u8; PATH_MAX as usize];
if self.len() >= PATH_MAX as usize {
- return Err(Error::from(Errno::ENAMETOOLONG))
+ return Err(Errno::ENAMETOOLONG)
}
match self.iter().position(|b| *b == 0) {
- Some(_) => Err(Error::from(Errno::EINVAL)),
+ Some(_) => Err(Errno::EINVAL),
None => {
unsafe {
// TODO: Replace with bytes::copy_memory. rust-lang/rust#24028
diff --git a/src/macros.rs b/src/macros.rs
index 7d6ac8d..3ccbfdd 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -83,9 +83,9 @@ macro_rules! libc_bitflags {
macro_rules! libc_enum {
// Exit rule.
(@make_enum
+ name: $BitFlags:ident,
{
$v:vis
- name: $BitFlags:ident,
attrs: [$($attrs:tt)*],
entries: [$($entries:tt)*],
}
@@ -97,38 +97,98 @@ macro_rules! libc_enum {
}
};
+ // Exit rule including TryFrom
+ (@make_enum
+ name: $BitFlags:ident,
+ {
+ $v:vis
+ attrs: [$($attrs:tt)*],
+ entries: [$($entries:tt)*],
+ from_type: $repr:path,
+ try_froms: [$($try_froms:tt)*]
+ }
+ ) => {
+ $($attrs)*
+ #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+ $v enum $BitFlags {
+ $($entries)*
+ }
+ impl ::std::convert::TryFrom<$repr> for $BitFlags {
+ type Error = $crate::Error;
+ #[allow(unused_doc_comments)]
+ fn try_from(x: $repr) -> $crate::Result<Self> {
+ match x {
+ $($try_froms)*
+ _ => Err($crate::Error::EINVAL)
+ }
+ }
+ }
+ };
+
// Done accumulating.
(@accumulate_entries
+ name: $BitFlags:ident,
+ {
+ $v:vis
+ attrs: $attrs:tt,
+ },
+ $entries:tt,
+ $try_froms:tt;
+ ) => {
+ libc_enum! {
+ @make_enum
+ name: $BitFlags,
+ {
+ $v
+ attrs: $attrs,
+ entries: $entries,
+ }
+ }
+ };
+
+ // Done accumulating and want TryFrom
+ (@accumulate_entries
+ name: $BitFlags:ident,
{
$v:vis
- name: $BitFlags:ident,
attrs: $attrs:tt,
+ from_type: $repr:path,
},
- $entries:tt;
+ $entries:tt,
+ $try_froms:tt;
) => {
libc_enum! {
@make_enum
+ name: $BitFlags,
{
$v
- name: $BitFlags,
attrs: $attrs,
entries: $entries,
+ from_type: $repr,
+ try_froms: $try_froms
}
}
};
// Munch an attr.
(@accumulate_entries
+ name: $BitFlags:ident,
$prefix:tt,
- [$($entries:tt)*];
+ [$($entries:tt)*],
+ [$($try_froms:tt)*];
#[$attr:meta] $($tail:tt)*
) => {
libc_enum! {
@accumulate_entries
+ name: $BitFlags,
$prefix,
[
$($entries)*
#[$attr]
+ ],
+ [
+ $($try_froms)*
+ #[$attr]
];
$($tail)*
}
@@ -136,32 +196,47 @@ macro_rules! libc_enum {
// Munch last ident if not followed by a comma.
(@accumulate_entries
+ name: $BitFlags:ident,
$prefix:tt,
- [$($entries:tt)*];
+ [$($entries:tt)*],
+ [$($try_froms:tt)*];
$entry:ident
) => {
libc_enum! {
@accumulate_entries
+ name: $BitFlags,
$prefix,
[
$($entries)*
$entry = libc::$entry,
+ ],
+ [
+ $($try_froms)*
+ libc::$entry => Ok($BitFlags::$entry),
];
}
};
// Munch an ident; covers terminating comma case.
(@accumulate_entries
+ name: $BitFlags:ident,
$prefix:tt,
- [$($entries:tt)*];
- $entry:ident, $($tail:tt)*
+ [$($entries:tt)*],
+ [$($try_froms:tt)*];
+ $entry:ident,
+ $($tail:tt)*
) => {
libc_enum! {
@accumulate_entries
+ name: $BitFlags,
$prefix,
[
$($entries)*
$entry = libc::$entry,
+ ],
+ [
+ $($try_froms)*
+ libc::$entry => Ok($BitFlags::$entry),
];
$($tail)*
}
@@ -169,16 +244,24 @@ macro_rules! libc_enum {
// Munch an ident and cast it to the given type; covers terminating comma.
(@accumulate_entries
+ name: $BitFlags:ident,
$prefix:tt,
- [$($entries:tt)*];
- $entry:ident as $ty:ty, $($tail:tt)*
+ [$($entries:tt)*],
+ [$($try_froms:tt)*];
+ $entry:ident as $ty:ty,
+ $($tail:tt)*
) => {
libc_enum! {
@accumulate_entries
+ name: $BitFlags,
$prefix,
[
$($entries)*
$entry = libc::$entry as $ty,
+ ],
+ [
+ $($try_froms)*
+ libc::$entry as $ty => Ok($BitFlags::$entry),
];
$($tail)*
}
@@ -193,11 +276,34 @@ macro_rules! libc_enum {
) => {
libc_enum! {
@accumulate_entries
+ name: $BitFlags,
+ {
+ $v
+ attrs: [$(#[$attr])*],
+ },
+ [],
+ [];
+ $($vals)*
+ }
+ };
+
+ // Entry rule including TryFrom
+ (
+ $(#[$attr:meta])*
+ $v:vis enum $BitFlags:ident {
+ $($vals:tt)*
+ }
+ impl TryFrom<$repr:path>
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ name: $BitFlags,
{
$v
- name: $BitFlags,
attrs: [$(#[$attr])*],
+ from_type: $repr,
},
+ [],
[];
$($vals)*
}
diff --git a/src/mount/bsd.rs b/src/mount/bsd.rs
index 0144908..627bfa5 100644
--- a/src/mount/bsd.rs
+++ b/src/mount/bsd.rs
@@ -111,7 +111,7 @@ impl NmountError {
}
/// Returns the inner [`Error`]
- pub fn error(&self) -> Error {
+ pub const fn error(&self) -> Error {
self.errno
}
@@ -347,6 +347,7 @@ impl<'a> Nmount<'a> {
self
}
+ /// Create a new `Nmount` struct with no options
pub fn new() -> Self {
Self::default()
}
@@ -382,7 +383,7 @@ impl<'a> Nmount<'a> {
Some(CStr::from_bytes_with_nul(sl).unwrap())
}
};
- Err(NmountError::new(error.into(), errmsg))
+ Err(NmountError::new(error, errmsg))
}
}
}
@@ -396,7 +397,7 @@ impl<'a> Drop for Nmount<'a> {
// Free the owned string. Safe because we recorded ownership,
// and Nmount does not implement Clone.
unsafe {
- CString::from_raw(iov.0.iov_base as *mut c_char);
+ drop(CString::from_raw(iov.0.iov_base as *mut c_char));
}
}
}
diff --git a/src/mount/linux.rs b/src/mount/linux.rs
index edb8afb..4cb2fa5 100644
--- a/src/mount/linux.rs
+++ b/src/mount/linux.rs
@@ -1,3 +1,4 @@
+#![allow(missing_docs)]
use libc::{self, c_ulong, c_int};
use crate::{Result, NixPath};
use crate::errno::Errno;
diff --git a/src/mount/mod.rs b/src/mount/mod.rs
index 8538bf3..14bf2a9 100644
--- a/src/mount/mod.rs
+++ b/src/mount/mod.rs
@@ -1,3 +1,4 @@
+//! Mount file systems
#[cfg(any(target_os = "android", target_os = "linux"))]
mod linux;
diff --git a/src/mqueue.rs b/src/mqueue.rs
index 3e49480..34fd802 100644
--- a/src/mqueue.rs
+++ b/src/mqueue.rs
@@ -59,7 +59,7 @@ impl MqAttr {
}
}
- pub fn flags(&self) -> mq_attr_member_t {
+ pub const fn flags(&self) -> mq_attr_member_t {
self.mq_attr.mq_flags
}
}
@@ -155,6 +155,7 @@ pub fn mq_setattr(mqd: mqd_t, newattr: &MqAttr) -> Result<MqAttr> {
/// Convenience function.
/// Sets the `O_NONBLOCK` attribute for a given message queue descriptor
/// Returns the old attributes
+#[allow(clippy::useless_conversion)] // Not useless on all OSes
pub fn mq_set_nonblock(mqd: mqd_t) -> Result<MqAttr> {
let oldattr = mq_getattr(mqd)?;
let newattr = MqAttr::new(mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()),
diff --git a/src/poll.rs b/src/poll.rs
index 0c3f208..8556c1b 100644
--- a/src/poll.rs
+++ b/src/poll.rs
@@ -3,7 +3,7 @@
use crate::sys::time::TimeSpec;
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
use crate::sys::signal::SigSet;
-use std::os::unix::io::RawFd;
+use std::os::unix::io::{AsRawFd, RawFd};
use crate::Result;
use crate::errno::Errno;
@@ -25,7 +25,7 @@ pub struct PollFd {
impl PollFd {
/// Creates a new `PollFd` specifying the events of interest
/// for a given file descriptor.
- pub fn new(fd: RawFd, events: PollFlags) -> PollFd {
+ pub const fn new(fd: RawFd, events: PollFlags) -> PollFd {
PollFd {
pollfd: libc::pollfd {
fd,
@@ -35,10 +35,27 @@ impl PollFd {
}
}
- /// Returns the events that occured in the last call to `poll` or `ppoll`.
+ /// Returns the events that occured in the last call to `poll` or `ppoll`. Will only return
+ /// `None` if the kernel provides status flags that Nix does not know about.
pub fn revents(self) -> Option<PollFlags> {
PollFlags::from_bits(self.pollfd.revents)
}
+
+ /// The events of interest for this `PollFd`.
+ pub fn events(self) -> PollFlags {
+ PollFlags::from_bits(self.pollfd.events).unwrap()
+ }
+
+ /// Modify the events of interest for this `PollFd`.
+ pub fn set_events(&mut self, events: PollFlags) {
+ self.pollfd.events = events.bits();
+ }
+}
+
+impl AsRawFd for PollFd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.pollfd.fd
+ }
}
libc_bitflags! {
diff --git a/src/pty.rs b/src/pty.rs
index a8eb938..facc9aa 100644
--- a/src/pty.rs
+++ b/src/pty.rs
@@ -10,7 +10,7 @@ use std::os::unix::prelude::*;
use crate::sys::termios::Termios;
use crate::unistd::{self, ForkResult, Pid};
-use crate::{Result, Error, fcntl};
+use crate::{Result, fcntl};
use crate::errno::Errno;
/// Representation of a master/slave pty pair
@@ -99,7 +99,7 @@ impl io::Write for PtyMaster {
#[inline]
pub fn grantpt(fd: &PtyMaster) -> Result<()> {
if unsafe { libc::grantpt(fd.as_raw_fd()) } < 0 {
- return Err(Error::from(Errno::last()));
+ return Err(Errno::last());
}
Ok(())
@@ -145,7 +145,7 @@ pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
};
if fd < 0 {
- return Err(Error::from(Errno::last()));
+ return Err(Errno::last());
}
Ok(PtyMaster(fd))
@@ -171,7 +171,7 @@ pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
pub unsafe fn ptsname(fd: &PtyMaster) -> Result<String> {
let name_ptr = libc::ptsname(fd.as_raw_fd());
if name_ptr.is_null() {
- return Err(Error::from(Errno::last()));
+ return Err(Errno::last());
}
let name = CStr::from_ptr(name_ptr);
@@ -195,7 +195,7 @@ pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
let cname = unsafe {
let cap = name_buf.capacity();
if libc::ptsname_r(fd.as_raw_fd(), name_buf_ptr, cap) != 0 {
- return Err(Error::last());
+ return Err(crate::Error::last());
}
CStr::from_ptr(name_buf.as_ptr())
};
@@ -213,7 +213,7 @@ pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
#[inline]
pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
if unsafe { libc::unlockpt(fd.as_raw_fd()) } < 0 {
- return Err(Error::from(Errno::last()));
+ return Err(Errno::last());
}
Ok(())
diff --git a/src/sched.rs b/src/sched.rs
index bf51bc1..c2dd7b8 100644
--- a/src/sched.rs
+++ b/src/sched.rs
@@ -1,3 +1,7 @@
+//! Execution scheduling
+//!
+//! See Also
+//! [sched.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html)
use crate::{Errno, Result};
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -11,38 +15,75 @@ mod sched_linux_like {
use std::option::Option;
use std::os::unix::io::RawFd;
use crate::unistd::Pid;
- use crate::{Error, Result};
+ use crate::Result;
// For some functions taking with a parameter of type CloneFlags,
// only a subset of these flags have an effect.
libc_bitflags! {
+ /// Options for use with [`clone`]
pub struct CloneFlags: c_int {
+ /// The calling process and the child process run in the same
+ /// memory space.
CLONE_VM;
+ /// The caller and the child process share the same filesystem
+ /// information.
CLONE_FS;
+ /// The calling process and the child process share the same file
+ /// descriptor table.
CLONE_FILES;
+ /// The calling process and the child process share the same table
+ /// of signal handlers.
CLONE_SIGHAND;
+ /// If the calling process is being traced, then trace the child
+ /// also.
CLONE_PTRACE;
+ /// The execution of the calling process is suspended until the
+ /// child releases its virtual memory resources via a call to
+ /// execve(2) or _exit(2) (as with vfork(2)).
CLONE_VFORK;
+ /// The parent of the new child (as returned by getppid(2))
+ /// will be the same as that of the calling process.
CLONE_PARENT;
+ /// The child is placed in the same thread group as the calling
+ /// process.
CLONE_THREAD;
+ /// The cloned child is started in a new mount namespace.
CLONE_NEWNS;
+ /// The child and the calling process share a single list of System
+ /// V semaphore adjustment values
CLONE_SYSVSEM;
- CLONE_SETTLS;
- CLONE_PARENT_SETTID;
- CLONE_CHILD_CLEARTID;
+ // Not supported by Nix due to lack of varargs support in Rust FFI
+ // CLONE_SETTLS;
+ // Not supported by Nix due to lack of varargs support in Rust FFI
+ // CLONE_PARENT_SETTID;
+ // Not supported by Nix due to lack of varargs support in Rust FFI
+ // CLONE_CHILD_CLEARTID;
+ /// Unused since Linux 2.6.2
+ #[deprecated(since = "0.23.0", note = "Deprecated by Linux 2.6.2")]
CLONE_DETACHED;
+ /// A tracing process cannot force `CLONE_PTRACE` on this child
+ /// process.
CLONE_UNTRACED;
- CLONE_CHILD_SETTID;
+ // Not supported by Nix due to lack of varargs support in Rust FFI
+ // CLONE_CHILD_SETTID;
+ /// Create the process in a new cgroup namespace.
CLONE_NEWCGROUP;
+ /// Create the process in a new UTS namespace.
CLONE_NEWUTS;
+ /// Create the process in a new IPC namespace.
CLONE_NEWIPC;
+ /// Create the process in a new user namespace.
CLONE_NEWUSER;
+ /// Create the process in a new PID namespace.
CLONE_NEWPID;
+ /// Create the process in a new network namespace.
CLONE_NEWNET;
+ /// The new process shares an I/O context with the calling process.
CLONE_IO;
}
}
+ /// Type for the function executed by [`clone`].
pub type CloneCb<'a> = Box<dyn FnMut() -> isize + 'a>;
/// CpuSet represent a bit-mask of CPUs.
@@ -68,7 +109,7 @@ mod sched_linux_like {
/// `field` is the CPU id to test
pub fn is_set(&self, field: usize) -> Result<bool> {
if field >= CpuSet::count() {
- Err(Error::from(Errno::EINVAL))
+ Err(Errno::EINVAL)
} else {
Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) })
}
@@ -78,7 +119,7 @@ mod sched_linux_like {
/// `field` is the CPU id to add
pub fn set(&mut self, field: usize) -> Result<()> {
if field >= CpuSet::count() {
- Err(Error::from(Errno::EINVAL))
+ Err(Errno::EINVAL)
} else {
unsafe { libc::CPU_SET(field, &mut self.cpu_set); }
Ok(())
@@ -89,7 +130,7 @@ mod sched_linux_like {
/// `field` is the CPU id to remove
pub fn unset(&mut self, field: usize) -> Result<()> {
if field >= CpuSet::count() {
- Err(Error::from(Errno::EINVAL))
+ Err(Errno::EINVAL)
} else {
unsafe { libc::CPU_CLR(field, &mut self.cpu_set);}
Ok(())
@@ -97,7 +138,7 @@ mod sched_linux_like {
}
/// Return the maximum number of CPU in CpuSet
- pub fn count() -> usize {
+ pub const fn count() -> usize {
8 * mem::size_of::<libc::cpu_set_t>()
}
}
@@ -212,12 +253,18 @@ mod sched_linux_like {
Errno::result(res).map(Pid::from_raw)
}
+ /// disassociate parts of the process execution context
+ ///
+ /// See also [unshare(2)](https://man7.org/linux/man-pages/man2/unshare.2.html)
pub fn unshare(flags: CloneFlags) -> Result<()> {
let res = unsafe { libc::unshare(flags.bits()) };
Errno::result(res).map(drop)
}
+ /// reassociate thread with a namespace
+ ///
+ /// See also [setns(2)](https://man7.org/linux/man-pages/man2/setns.2.html)
pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> {
let res = unsafe { libc::setns(fd, nstype.bits()) };
diff --git a/src/sys/aio.rs b/src/sys/aio.rs
index b63affb..e64a2a8 100644
--- a/src/sys/aio.rs
+++ b/src/sys/aio.rs
@@ -21,7 +21,7 @@
//! [`aio_cancel_all`](fn.aio_cancel_all.html), though the operating system may
//! not support this for all filesystems and devices.
-use crate::{Error, Result};
+use crate::Result;
use crate::errno::Errno;
use std::os::unix::io::RawFd;
use libc::{c_void, off_t, size_t};
@@ -39,6 +39,7 @@ libc_enum! {
/// Mode for `AioCb::fsync`. Controls whether only data or both data and
/// metadata are synced.
#[repr(i32)]
+ #[non_exhaustive]
pub enum AioFsyncMode {
/// do it like `fsync`
O_SYNC,
@@ -57,9 +58,13 @@ libc_enum! {
/// given `aiocb` should be used for a read operation, a write operation, or
/// ignored. Has no effect for any other aio functions.
#[repr(i32)]
+ #[non_exhaustive]
pub enum LioOpcode {
+ /// No operation
LIO_NOP,
+ /// Write data as if by a call to [`AioCb::write`]
LIO_WRITE,
+ /// Write data as if by a call to [`AioCb::read`]
LIO_READ,
}
}
@@ -143,15 +148,13 @@ impl<'a> AioCb<'a> {
/// # use std::{thread, time};
/// # use std::os::unix::io::AsRawFd;
/// # use tempfile::tempfile;
- /// # fn main() {
/// let f = tempfile().unwrap();
/// let mut aiocb = AioCb::from_fd( f.as_raw_fd(), 0, SigevNone);
/// aiocb.fsync(AioFsyncMode::O_SYNC).expect("aio_fsync failed early");
- /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) {
+ /// while (aiocb.error() == Err(Errno::EINPROGRESS)) {
/// thread::sleep(time::Duration::from_millis(10));
/// }
/// aiocb.aio_return().expect("aio_fsync failed late");
- /// # }
/// ```
pub fn from_fd(fd: RawFd, prio: libc::c_int,
sigev_notify: SigevNotify) -> Pin<Box<AioCb<'a>>> {
@@ -224,7 +227,6 @@ impl<'a> AioCb<'a> {
/// # use std::io::Write;
/// # use std::os::unix::io::AsRawFd;
/// # use tempfile::tempfile;
- /// # fn main() {
/// const INITIAL: &[u8] = b"abcdef123456";
/// const LEN: usize = 4;
/// let mut rbuf = vec![0; LEN];
@@ -238,13 +240,12 @@ impl<'a> AioCb<'a> {
/// SigevNotify::SigevNone,
/// LioOpcode::LIO_NOP);
/// aiocb.read().unwrap();
- /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) {
+ /// while (aiocb.error() == Err(Errno::EINPROGRESS)) {
/// thread::sleep(time::Duration::from_millis(10));
/// }
/// assert_eq!(aiocb.aio_return().unwrap() as usize, LEN);
/// }
/// assert_eq!(rbuf, b"cdef");
- /// # }
/// ```
pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8],
prio: libc::c_int, sigev_notify: SigevNotify,
@@ -399,7 +400,6 @@ impl<'a> AioCb<'a> {
/// # use std::{thread, time};
/// # use std::os::unix::io::AsRawFd;
/// # use tempfile::tempfile;
- /// # fn main() {
/// const WBUF: &[u8] = b"abcdef123456";
/// let mut f = tempfile().unwrap();
/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
@@ -409,11 +409,10 @@ impl<'a> AioCb<'a> {
/// SigevNotify::SigevNone,
/// LioOpcode::LIO_NOP);
/// aiocb.write().unwrap();
- /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) {
+ /// while (aiocb.error() == Err(Errno::EINPROGRESS)) {
/// thread::sleep(time::Duration::from_millis(10));
/// }
/// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
- /// # }
/// ```
// Note: another solution to the problem of writing const buffers would be
// to genericize AioCb for both &mut [u8] and &[u8] buffers. AioCb::read
@@ -475,7 +474,6 @@ impl<'a> AioCb<'a> {
/// # use std::io::Write;
/// # use std::os::unix::io::AsRawFd;
/// # use tempfile::tempfile;
- /// # fn main() {
/// let wbuf = b"CDEF";
/// let mut f = tempfile().unwrap();
/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
@@ -487,13 +485,12 @@ impl<'a> AioCb<'a> {
/// aiocb.write().unwrap();
/// let cs = aiocb.cancel().unwrap();
/// if cs == AioCancelStat::AioNotCanceled {
- /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) {
+ /// while (aiocb.error() == Err(Errno::EINPROGRESS)) {
/// thread::sleep(time::Duration::from_millis(10));
/// }
/// }
/// // Must call `aio_return`, but ignore the result
/// let _ = aiocb.aio_return();
- /// # }
/// ```
///
/// # References
@@ -508,7 +505,7 @@ impl<'a> AioCb<'a> {
libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
- -1 => Err(Error::from(Errno::last())),
+ -1 => Err(Errno::last()),
_ => panic!("unknown aio_cancel return value")
}
}
@@ -519,8 +516,8 @@ impl<'a> AioCb<'a> {
};
match r {
0 => Ok(()),
- num if num > 0 => Err(Error::from(Errno::from_i32(num))),
- -1 => Err(Error::from(Errno::last())),
+ num if num > 0 => Err(Errno::from_i32(num)),
+ -1 => Err(Errno::last()),
num => panic!("unknown aio_error return value {:?}", num)
}
}
@@ -543,7 +540,6 @@ impl<'a> AioCb<'a> {
/// # use std::{thread, time};
/// # use std::os::unix::io::AsRawFd;
/// # use tempfile::tempfile;
- /// # fn main() {
/// const WBUF: &[u8] = b"abcdef123456";
/// let mut f = tempfile().unwrap();
/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
@@ -553,11 +549,10 @@ impl<'a> AioCb<'a> {
/// SigevNotify::SigevNone,
/// LioOpcode::LIO_NOP);
/// aiocb.write().unwrap();
- /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) {
+ /// while (aiocb.error() == Err(Errno::EINPROGRESS)) {
/// thread::sleep(time::Duration::from_millis(10));
/// }
/// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
- /// # }
/// ```
///
/// # References
@@ -706,7 +701,6 @@ impl<'a> AioCb<'a> {
/// # use std::io::Write;
/// # use std::os::unix::io::AsRawFd;
/// # use tempfile::tempfile;
-/// # fn main() {
/// let wbuf = b"CDEF";
/// let mut f = tempfile().unwrap();
/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
@@ -718,13 +712,12 @@ impl<'a> AioCb<'a> {
/// aiocb.write().unwrap();
/// let cs = aio_cancel_all(f.as_raw_fd()).unwrap();
/// if cs == AioCancelStat::AioNotCanceled {
-/// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) {
+/// while (aiocb.error() == Err(Errno::EINPROGRESS)) {
/// thread::sleep(time::Duration::from_millis(10));
/// }
/// }
/// // Must call `aio_return`, but ignore the result
/// let _ = aiocb.aio_return();
-/// # }
/// ```
///
/// # References
@@ -735,7 +728,7 @@ pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
- -1 => Err(Error::from(Errno::last())),
+ -1 => Err(Errno::last()),
_ => panic!("unknown aio_cancel return value")
}
}
@@ -754,7 +747,6 @@ pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
/// # use nix::sys::signal::SigevNotify;
/// # use std::os::unix::io::AsRawFd;
/// # use tempfile::tempfile;
-/// # fn main() {
/// const WBUF: &[u8] = b"abcdef123456";
/// let mut f = tempfile().unwrap();
/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
@@ -766,7 +758,6 @@ pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
/// aiocb.write().unwrap();
/// aio_suspend(&[aiocb.as_ref()], None).expect("aio_suspend failed");
/// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
-/// # }
/// ```
/// # References
///
@@ -838,6 +829,7 @@ unsafe impl<'a> Sync for LioCb<'a> {}
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
impl<'a> LioCb<'a> {
+ /// Are no [`AioCb`]s contained?
pub fn is_empty(&self) -> bool {
self.aiocbs.is_empty()
}
@@ -870,7 +862,6 @@ impl<'a> LioCb<'a> {
/// # use nix::sys::signal::SigevNotify;
/// # use std::os::unix::io::AsRawFd;
/// # use tempfile::tempfile;
- /// # fn main() {
/// const WBUF: &[u8] = b"abcdef123456";
/// let mut f = tempfile().unwrap();
/// let mut liocb = LioCbBuilder::with_capacity(1)
@@ -885,7 +876,6 @@ impl<'a> LioCb<'a> {
/// liocb.listio(LioMode::LIO_WAIT,
/// SigevNotify::SigevNone).unwrap();
/// assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
- /// # }
/// ```
///
/// # References
@@ -930,7 +920,6 @@ impl<'a> LioCb<'a> {
/// # use std::os::unix::io::AsRawFd;
/// # use std::{thread, time};
/// # use tempfile::tempfile;
- /// # fn main() {
/// const WBUF: &[u8] = b"abcdef123456";
/// let mut f = tempfile().unwrap();
/// let mut liocb = LioCbBuilder::with_capacity(1)
@@ -943,13 +932,12 @@ impl<'a> LioCb<'a> {
/// LioOpcode::LIO_WRITE
/// ).finish();
/// let mut err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone);
- /// while err == Err(Error::from(Errno::EIO)) ||
- /// err == Err(Error::from(Errno::EAGAIN)) {
+ /// while err == Err(Errno::EIO) ||
+ /// err == Err(Errno::EAGAIN) {
/// thread::sleep(time::Duration::from_millis(10));
/// err = liocb.listio_resubmit(LioMode::LIO_WAIT, SigevNotify::SigevNone);
/// }
/// assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
- /// # }
/// ```
///
/// # References
diff --git a/src/sys/epoll.rs b/src/sys/epoll.rs
index b73af13..6bc2a25 100644
--- a/src/sys/epoll.rs
+++ b/src/sys/epoll.rs
@@ -1,4 +1,4 @@
-use crate::{Error, Result};
+use crate::Result;
use crate::errno::Errno;
use libc::{self, c_int};
use std::os::unix::io::RawFd;
@@ -29,6 +29,7 @@ libc_bitflags!(
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(i32)]
+#[non_exhaustive]
pub enum EpollOp {
EpollCtlAdd = libc::EPOLL_CTL_ADD,
EpollCtlDel = libc::EPOLL_CTL_DEL,
@@ -85,7 +86,7 @@ pub fn epoll_ctl<'a, T>(epfd: RawFd, op: EpollOp, fd: RawFd, event: T) -> Result
{
let mut event: Option<&mut EpollEvent> = event.into();
if event.is_none() && op != EpollOp::EpollCtlDel {
- Err(Error::from(Errno::EINVAL))
+ Err(Errno::EINVAL)
} else {
let res = unsafe {
if let Some(ref mut event) = event {
diff --git a/src/sys/event.rs b/src/sys/event.rs
index 8050af3..c648f5e 100644
--- a/src/sys/event.rs
+++ b/src/sys/event.rs
@@ -6,9 +6,9 @@ use crate::{Errno, Result};
use libc::{timespec, time_t, c_int, c_long, intptr_t, uintptr_t};
#[cfg(target_os = "netbsd")]
use libc::{timespec, time_t, c_long, intptr_t, uintptr_t, size_t};
+use std::convert::TryInto;
use std::os::unix::io::RawFd;
use std::ptr;
-use std::mem;
// Redefine kevent in terms of programmer-friendly enums and bitfields.
#[repr(C)]
@@ -36,6 +36,7 @@ type type_of_event_filter = i16;
libc_enum! {
#[cfg_attr(target_os = "netbsd", repr(u32))]
#[cfg_attr(not(target_os = "netbsd"), repr(i16))]
+ #[non_exhaustive]
pub enum EventFilter {
EVFILT_AIO,
/// Returns whenever there is no remaining data in the write buffer
@@ -75,6 +76,7 @@ libc_enum! {
EVFILT_VNODE,
EVFILT_WRITE,
}
+ impl TryFrom<type_of_event_filter>
}
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
@@ -232,8 +234,8 @@ impl KEvent {
self.kevent.ident
}
- pub fn filter(&self) -> EventFilter {
- unsafe { mem::transmute(self.kevent.filter as type_of_event_filter) }
+ pub fn filter(&self) -> Result<EventFilter> {
+ self.kevent.filter.try_into()
}
pub fn flags(&self) -> EventFlag {
@@ -312,6 +314,8 @@ pub fn ev_set(ev: &mut KEvent,
#[test]
fn test_struct_kevent() {
+ use std::mem;
+
let udata : intptr_t = 12345;
let actual = KEvent::new(0xdead_beef,
@@ -321,10 +325,24 @@ fn test_struct_kevent() {
0x1337,
udata);
assert_eq!(0xdead_beef, actual.ident());
- assert_eq!(libc::EVFILT_READ, actual.filter() as type_of_event_filter);
+ let filter = actual.kevent.filter;
+ assert_eq!(libc::EVFILT_READ, filter);
assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
assert_eq!(0x1337, actual.data() as type_of_data);
assert_eq!(udata as type_of_udata, actual.udata() as type_of_udata);
assert_eq!(mem::size_of::<libc::kevent>(), mem::size_of::<KEvent>());
}
+
+#[test]
+fn test_kevent_filter() {
+ let udata : intptr_t = 12345;
+
+ let actual = KEvent::new(0xdead_beef,
+ EventFilter::EVFILT_READ,
+ EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
+ FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
+ 0x1337,
+ udata);
+ assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
+}
diff --git a/src/sys/eventfd.rs b/src/sys/eventfd.rs
index baaaa89..c54f952 100644
--- a/src/sys/eventfd.rs
+++ b/src/sys/eventfd.rs
@@ -1,4 +1,3 @@
-use libc;
use std::os::unix::io::RawFd;
use crate::Result;
use crate::errno::Errno;
diff --git a/src/sys/memfd.rs b/src/sys/memfd.rs
index 51b7e6b..642676b 100644
--- a/src/sys/memfd.rs
+++ b/src/sys/memfd.rs
@@ -1,16 +1,43 @@
-use libc;
+//! Interfaces for managing memory-backed files.
+
use std::os::unix::io::RawFd;
use crate::Result;
use crate::errno::Errno;
use std::ffi::CStr;
libc_bitflags!(
+ /// Options that change the behavior of [`memfd_create`].
pub struct MemFdCreateFlag: libc::c_uint {
+ /// Set the close-on-exec ([`FD_CLOEXEC`]) flag on the new file descriptor.
+ ///
+ /// By default, the new file descriptor is set to remain open across an [`execve`]
+ /// (the `FD_CLOEXEC` flag is initially disabled). This flag can be used to change
+ /// this default. The file offset is set to the beginning of the file (see [`lseek`]).
+ ///
+ /// See also the description of the `O_CLOEXEC` flag in [`open(2)`].
+ ///
+ /// [`execve`]: crate::unistd::execve
+ /// [`lseek`]: crate::unistd::lseek
+ /// [`FD_CLOEXEC`]: crate::fcntl::FdFlag::FD_CLOEXEC
+ /// [`open(2)`]: https://man7.org/linux/man-pages/man2/open.2.html
MFD_CLOEXEC;
+ /// Allow sealing operations on this file.
+ ///
+ /// See also the file sealing notes given in [`memfd_create(2)`].
+ ///
+ /// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
MFD_ALLOW_SEALING;
}
);
+/// Creates an anonymous file that lives in memory, and return a file-descriptor to it.
+///
+/// The file behaves like a regular file, and so can be modified, truncated, memory-mapped, and so on.
+/// However, unlike a regular file, it lives in RAM and has a volatile backing storage.
+///
+/// For more information, see [`memfd_create(2)`].
+///
+/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result<RawFd> {
let res = unsafe {
libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags.bits())
diff --git a/src/sys/mman.rs b/src/sys/mman.rs
index 58edf08..0ef1ca8 100644
--- a/src/sys/mman.rs
+++ b/src/sys/mman.rs
@@ -1,4 +1,6 @@
-use crate::{Error, Result};
+//! Memory management declarations.
+
+use crate::Result;
#[cfg(not(target_os = "android"))]
use crate::NixPath;
use crate::errno::Errno;
@@ -30,7 +32,7 @@ libc_bitflags!{
}
libc_bitflags!{
- /// Additional parameters for `mmap()`.
+ /// Additional parameters for [`mmap`].
pub struct MapFlags: c_int {
/// Compatibility flag. Ignored.
MAP_FILE;
@@ -40,10 +42,13 @@ libc_bitflags!{
MAP_PRIVATE;
/// Place the mapping at exactly the address specified in `addr`.
MAP_FIXED;
+ /// To be used with `MAP_FIXED`, to forbid the system
+ /// to select a different address than the one specified.
+ #[cfg(target_os = "freebsd")]
+ MAP_EXCL;
/// Synonym for `MAP_ANONYMOUS`.
MAP_ANON;
/// The mapping is not backed by any file.
- #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
MAP_ANONYMOUS;
/// Put the mapping into the first 2GB of the process address space.
#[cfg(any(all(any(target_os = "android", target_os = "linux"),
@@ -65,8 +70,8 @@ libc_bitflags!{
MAP_LOCKED;
/// Do not reserve swap space for this mapping.
///
- /// This was removed in FreeBSD 11.
- #[cfg(not(target_os = "freebsd"))]
+ /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
+ #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd")))]
MAP_NORESERVE;
/// Populate page tables for a mapping.
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -122,39 +127,55 @@ libc_bitflags!{
MAP_NOSYNC;
/// Rename private pages to a file.
///
- /// This was removed in FreeBSD 11.
- #[cfg(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))]
+ /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
+ #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
MAP_RENAME;
/// Region may contain semaphores.
#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
MAP_HASSEMAPHORE;
/// Region grows down, like a stack.
- #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
MAP_STACK;
/// Pages in this mapping are not retained in the kernel's memory cache.
#[cfg(any(target_os = "ios", target_os = "macos"))]
MAP_NOCACHE;
+ /// Allows the W/X bit on the page, it's necessary on aarch64 architecture.
#[cfg(any(target_os = "ios", target_os = "macos"))]
MAP_JIT;
+ /// Allows to use large pages, underlying alignment based on size.
+ #[cfg(target_os = "freebsd")]
+ MAP_ALIGNED_SUPER;
+ /// Pages will be discarded in the core dumps.
+ #[cfg(target_os = "openbsd")]
+ MAP_CONCEAL;
}
}
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
libc_bitflags!{
- /// Options for `mremap()`.
+ /// Options for [`mremap`].
pub struct MRemapFlags: c_int {
/// Permit the kernel to relocate the mapping to a new virtual address, if necessary.
+ #[cfg(target_os = "linux")]
MREMAP_MAYMOVE;
/// Place the mapping at exactly the address specified in `new_address`.
+ #[cfg(target_os = "linux")]
MREMAP_FIXED;
+ /// Permits to use the old and new address as hints to relocate the mapping.
+ #[cfg(target_os = "netbsd")]
+ MAP_FIXED;
+ /// Allows to duplicate the mapping to be able to apply different flags on the copy.
+ #[cfg(target_os = "netbsd")]
+ MAP_REMAPDUP;
}
}
libc_enum!{
/// Usage information for a range of memory to allow for performance optimizations by the kernel.
///
- /// Used by [`madvise`](./fn.madvise.html).
+ /// Used by [`madvise`].
#[repr(i32)]
+ #[non_exhaustive]
pub enum MmapAdvise {
/// No further special treatment. This is the default.
MADV_NORMAL,
@@ -191,7 +212,8 @@ libc_enum!{
all(target_os = "linux", any(
target_arch = "aarch64",
target_arch = "arm",
- target_arch = "ppc",
+ target_arch = "powerpc",
+ target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "x86",
target_arch = "x86_64",
@@ -244,7 +266,7 @@ libc_enum!{
}
libc_bitflags!{
- /// Configuration flags for `msync`.
+ /// Configuration flags for [`msync`].
pub struct MsFlags: c_int {
/// Schedule an update but return immediately.
MS_ASYNC;
@@ -262,7 +284,7 @@ libc_bitflags!{
}
libc_bitflags!{
- /// Flags for `mlockall`.
+ /// Flags for [`mlockall`].
pub struct MlockAllFlags: c_int {
/// Lock pages that are currently mapped into the address space of the process.
MCL_CURRENT;
@@ -278,7 +300,9 @@ libc_bitflags!{
///
/// # Safety
///
-/// `addr` must meet all the requirements described in the `mlock(2)` man page.
+/// `addr` must meet all the requirements described in the [`mlock(2)`] man page.
+///
+/// [`mlock(2)`]: https://man7.org/linux/man-pages/man2/mlock.2.html
pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
Errno::result(libc::mlock(addr, length)).map(drop)
}
@@ -288,25 +312,28 @@ pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
///
/// # Safety
///
-/// `addr` must meet all the requirements described in the `munlock(2)` man
+/// `addr` must meet all the requirements described in the [`munlock(2)`] man
/// page.
+///
+/// [`munlock(2)`]: https://man7.org/linux/man-pages/man2/munlock.2.html
pub unsafe fn munlock(addr: *const c_void, length: size_t) -> Result<()> {
Errno::result(libc::munlock(addr, length)).map(drop)
}
/// Locks all memory pages mapped into this process' address space.
///
-/// Locked pages never move to the swap area.
-///
-/// # Safety
+/// Locked pages never move to the swap area. For more information, see [`mlockall(2)`].
///
-/// `addr` must meet all the requirements described in the `mlockall(2)` man
-/// page.
+/// [`mlockall(2)`]: https://man7.org/linux/man-pages/man2/mlockall.2.html
pub fn mlockall(flags: MlockAllFlags) -> Result<()> {
unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop)
}
/// Unlocks all memory pages mapped into this process' address space.
+///
+/// For more information, see [`munlockall(2)`].
+///
+/// [`munlockall(2)`]: https://man7.org/linux/man-pages/man2/munlockall.2.html
pub fn munlockall() -> Result<()> {
unsafe { Errno::result(libc::munlockall()) }.map(drop)
}
@@ -315,12 +342,14 @@ pub fn munlockall() -> Result<()> {
///
/// # Safety
///
-/// See the `mmap(2)` man page for detailed requirements.
+/// See the [`mmap(2)`] man page for detailed requirements.
+///
+/// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: MapFlags, fd: RawFd, offset: off_t) -> Result<*mut c_void> {
let ret = libc::mmap(addr, length, prot.bits(), flags.bits(), fd, offset);
if ret == libc::MAP_FAILED {
- Err(Error::from(Errno::last()))
+ Err(Errno::last())
} else {
Ok(ret)
}
@@ -333,7 +362,7 @@ pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: Ma
///
/// See the `mremap(2)` [man page](https://man7.org/linux/man-pages/man2/mremap.2.html) for
/// detailed requirements.
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
pub unsafe fn mremap(
addr: *mut c_void,
old_size: size_t,
@@ -341,10 +370,19 @@ pub unsafe fn mremap(
flags: MRemapFlags,
new_address: Option<* mut c_void>,
) -> Result<*mut c_void> {
+ #[cfg(target_os = "linux")]
let ret = libc::mremap(addr, old_size, new_size, flags.bits(), new_address.unwrap_or(std::ptr::null_mut()));
+ #[cfg(target_os = "netbsd")]
+ let ret = libc::mremap(
+ addr,
+ old_size,
+ new_address.unwrap_or(std::ptr::null_mut()),
+ new_size,
+ flags.bits(),
+ );
if ret == libc::MAP_FAILED {
- Err(Error::from(Errno::last()))
+ Err(Errno::last())
} else {
Ok(ret)
}
@@ -354,8 +392,10 @@ pub unsafe fn mremap(
///
/// # Safety
///
-/// `addr` must meet all the requirements described in the `munmap(2)` man
+/// `addr` must meet all the requirements described in the [`munmap(2)`] man
/// page.
+///
+/// [`munmap(2)`]: https://man7.org/linux/man-pages/man2/munmap.2.html
pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
Errno::result(libc::munmap(addr, len)).map(drop)
}
@@ -364,8 +404,10 @@ pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
///
/// # Safety
///
-/// See the `madvise(2)` man page. Take special care when using
-/// `MmapAdvise::MADV_FREE`.
+/// See the [`madvise(2)`] man page. Take special care when using
+/// [`MmapAdvise::MADV_FREE`].
+///
+/// [`madvise(2)`]: https://man7.org/linux/man-pages/man2/madvise.2.html
pub unsafe fn madvise(addr: *mut c_void, length: size_t, advise: MmapAdvise) -> Result<()> {
Errno::result(libc::madvise(addr, length, advise as i32)).map(drop)
}
@@ -403,12 +445,19 @@ pub unsafe fn mprotect(addr: *mut c_void, length: size_t, prot: ProtFlags) -> Re
///
/// # Safety
///
-/// `addr` must meet all the requirements described in the `msync(2)` man
+/// `addr` must meet all the requirements described in the [`msync(2)`] man
/// page.
+///
+/// [`msync(2)`]: https://man7.org/linux/man-pages/man2/msync.2.html
pub unsafe fn msync(addr: *mut c_void, length: size_t, flags: MsFlags) -> Result<()> {
Errno::result(libc::msync(addr, length, flags.bits())).map(drop)
}
+/// Creates and opens a new, or opens an existing, POSIX shared memory object.
+///
+/// For more information, see [`shm_open(3)`].
+///
+/// [`shm_open(3)`]: https://man7.org/linux/man-pages/man3/shm_open.3.html
#[cfg(not(target_os = "android"))]
pub fn shm_open<P: ?Sized + NixPath>(name: &P, flag: OFlag, mode: Mode) -> Result<RawFd> {
let ret = name.with_nix_path(|cstr| {
@@ -425,6 +474,11 @@ pub fn shm_open<P: ?Sized + NixPath>(name: &P, flag: OFlag, mode: Mode) -> Resul
Errno::result(ret)
}
+/// Performs the converse of [`shm_open`], removing an object previously created.
+///
+/// For more information, see [`shm_unlink(3)`].
+///
+/// [`shm_unlink(3)`]: https://man7.org/linux/man-pages/man3/shm_unlink.3.html
#[cfg(not(target_os = "android"))]
pub fn shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()> {
let ret = name.with_nix_path(|cstr| {
diff --git a/src/sys/mod.rs b/src/sys/mod.rs
index 51f0eed..156b0d9 100644
--- a/src/sys/mod.rs
+++ b/src/sys/mod.rs
@@ -1,3 +1,4 @@
+//! Mostly platform-specific functionality
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
@@ -7,6 +8,7 @@
pub mod aio;
#[cfg(any(target_os = "android", target_os = "linux"))]
+#[allow(missing_docs)]
pub mod epoll;
#[cfg(any(target_os = "dragonfly",
@@ -15,9 +17,11 @@ pub mod epoll;
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
+#[allow(missing_docs)]
pub mod event;
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[allow(missing_docs)]
pub mod eventfd;
#[cfg(any(target_os = "android",
@@ -37,9 +41,11 @@ pub mod ioctl;
pub mod memfd;
#[cfg(not(target_os = "redox"))]
+#[allow(missing_docs)]
pub mod mman;
#[cfg(target_os = "linux")]
+#[allow(missing_docs)]
pub mod personality;
pub mod pthread;
@@ -51,14 +57,19 @@ pub mod pthread;
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
+#[allow(missing_docs)]
pub mod ptrace;
#[cfg(target_os = "linux")]
pub mod quota;
#[cfg(any(target_os = "linux"))]
+#[allow(missing_docs)]
pub mod reboot;
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))]
+pub mod resource;
+
#[cfg(not(target_os = "redox"))]
pub mod select;
@@ -72,11 +83,14 @@ pub mod sendfile;
pub mod signal;
#[cfg(any(target_os = "android", target_os = "linux"))]
+#[allow(missing_docs)]
pub mod signalfd;
#[cfg(not(target_os = "redox"))]
+#[allow(missing_docs)]
pub mod socket;
+#[allow(missing_docs)]
pub mod stat;
#[cfg(any(target_os = "android",
@@ -92,10 +106,13 @@ pub mod statfs;
pub mod statvfs;
#[cfg(any(target_os = "android", target_os = "linux"))]
+#[allow(missing_docs)]
pub mod sysinfo;
+#[allow(missing_docs)]
pub mod termios;
+#[allow(missing_docs)]
pub mod time;
pub mod uio;
@@ -105,7 +122,9 @@ pub mod utsname;
pub mod wait;
#[cfg(any(target_os = "android", target_os = "linux"))]
+#[allow(missing_docs)]
pub mod inotify;
#[cfg(any(target_os = "android", target_os = "linux"))]
+#[allow(missing_docs)]
pub mod timerfd;
diff --git a/src/sys/personality.rs b/src/sys/personality.rs
index 6548b65..b15956c 100644
--- a/src/sys/personality.rs
+++ b/src/sys/personality.rs
@@ -39,7 +39,7 @@ pub fn get() -> Result<Persona> {
libc::personality(0xFFFFFFFF)
};
- Errno::result(res).map(|r| Persona::from_bits_truncate(r))
+ Errno::result(res).map(Persona::from_bits_truncate)
}
/// Set the current process personality.
@@ -66,5 +66,5 @@ pub fn set(persona: Persona) -> Result<Persona> {
libc::personality(persona.bits() as c_ulong)
};
- Errno::result(res).map(|r| Persona::from_bits_truncate(r))
+ Errno::result(res).map(Persona::from_bits_truncate)
}
diff --git a/src/sys/pthread.rs b/src/sys/pthread.rs
index f730408..d42e45d 100644
--- a/src/sys/pthread.rs
+++ b/src/sys/pthread.rs
@@ -1,5 +1,14 @@
+//! Low level threading primitives
+
+#[cfg(not(target_os = "redox"))]
+use crate::errno::Errno;
+#[cfg(not(target_os = "redox"))]
+use crate::Result;
+#[cfg(not(target_os = "redox"))]
+use crate::sys::signal::Signal;
use libc::{self, pthread_t};
+/// Identifies an individual thread.
pub type Pthread = pthread_t;
/// Obtain ID of the calling thread (see
@@ -11,3 +20,19 @@ pub type Pthread = pthread_t;
pub fn pthread_self() -> Pthread {
unsafe { libc::pthread_self() }
}
+
+/// Send a signal to a thread (see [`pthread_kill(3)`]).
+///
+/// If `signal` is `None`, `pthread_kill` will only preform error checking and
+/// won't send any signal.
+///
+/// [`pthread_kill(3)`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_kill.html
+#[cfg(not(target_os = "redox"))]
+pub fn pthread_kill<T: Into<Option<Signal>>>(thread: Pthread, signal: T) -> Result<()> {
+ let sig = match signal.into() {
+ Some(s) => s as libc::c_int,
+ None => 0,
+ };
+ let res = unsafe { libc::pthread_kill(thread, sig) };
+ Errno::result(res).map(drop)
+}
diff --git a/src/sys/ptrace/bsd.rs b/src/sys/ptrace/bsd.rs
index 141dfbc..a62881e 100644
--- a/src/sys/ptrace/bsd.rs
+++ b/src/sys/ptrace/bsd.rs
@@ -24,6 +24,7 @@ cfg_if! {
libc_enum! {
#[repr(i32)]
/// Ptrace Request enum defining the action to be taken.
+ #[non_exhaustive]
pub enum Request {
PT_TRACE_ME,
PT_READ_I,
diff --git a/src/sys/ptrace/linux.rs b/src/sys/ptrace/linux.rs
index 4ac4393..3723679 100644
--- a/src/sys/ptrace/linux.rs
+++ b/src/sys/ptrace/linux.rs
@@ -33,6 +33,7 @@ libc_enum!{
#[cfg_attr(not(any(target_env = "musl", target_os = "android")), repr(u32))]
#[cfg_attr(any(target_env = "musl", target_os = "android"), repr(i32))]
/// Ptrace Request enum defining the action to be taken.
+ #[non_exhaustive]
pub enum Request {
PTRACE_TRACEME,
PTRACE_PEEKTEXT,
@@ -97,11 +98,9 @@ libc_enum!{
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
PTRACE_SETREGSET,
- #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
- target_arch = "mips64"))))]
+ #[cfg(target_os = "linux")]
PTRACE_SEIZE,
- #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
- target_arch = "mips64"))))]
+ #[cfg(target_os = "linux")]
PTRACE_INTERRUPT,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips64"))))]
@@ -123,6 +122,7 @@ libc_enum!{
/// Using the ptrace options the tracer can configure the tracee to stop
/// at certain events. This enum is used to define those events as defined
/// in `man ptrace`.
+ #[non_exhaustive]
pub enum Event {
/// Event that stops before a return from fork or clone.
PTRACE_EVENT_FORK,
@@ -137,9 +137,11 @@ libc_enum!{
/// Event for a stop before an exit. Unlike the waitpid Exit status program.
/// registers can still be examined
PTRACE_EVENT_EXIT,
- /// STop triggered by a seccomp rule on a tracee.
+ /// Stop triggered by a seccomp rule on a tracee.
PTRACE_EVENT_SECCOMP,
- // PTRACE_EVENT_STOP not provided by libc because it's defined in glibc 2.26
+ /// Stop triggered by the `INTERRUPT` syscall, or a group stop,
+ /// or when a new child is attached.
+ PTRACE_EVENT_STOP,
}
}
@@ -337,7 +339,7 @@ pub fn attach(pid: Pid) -> Result<()> {
/// Attach to a running process, as with `ptrace(PTRACE_SEIZE, ...)`
///
/// Attaches to the process specified in pid, making it a tracee of the calling process.
-#[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64"))))]
+#[cfg(target_os = "linux")]
pub fn seize(pid: Pid, options: Options) -> Result<()> {
unsafe {
ptrace_other(
@@ -382,6 +384,16 @@ pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
}
}
+/// Stop a tracee, as with `ptrace(PTRACE_INTERRUPT, ...)`
+///
+/// This request is equivalent to `ptrace(PTRACE_INTERRUPT, ...)`
+#[cfg(target_os = "linux")]
+pub fn interrupt(pid: Pid) -> Result<()> {
+ unsafe {
+ ptrace_other(Request::PTRACE_INTERRUPT, pid, ptr::null_mut(), ptr::null_mut()).map(drop)
+ }
+}
+
/// Issues a kill request as with `ptrace(PTRACE_KILL, ...)`
///
/// This request is equivalent to `ptrace(PTRACE_CONT, ..., SIGKILL);`
@@ -391,7 +403,7 @@ pub fn kill(pid: Pid) -> Result<()> {
}
}
-/// Move the stopped tracee process forward by a single step as with
+/// Move the stopped tracee process forward by a single step as with
/// `ptrace(PTRACE_SINGLESTEP, ...)`
///
/// Advances the execution of the process with PID `pid` by a single step optionally delivering a
@@ -401,18 +413,17 @@ pub fn kill(pid: Pid) -> Result<()> {
/// ```rust
/// use nix::sys::ptrace::step;
/// use nix::unistd::Pid;
-/// use nix::sys::signal::Signal;
+/// use nix::sys::signal::Signal;
/// use nix::sys::wait::*;
-/// fn main() {
-/// // If a process changes state to the stopped state because of a SIGUSR1
-/// // signal, this will step the process forward and forward the user
-/// // signal to the stopped process
-/// match waitpid(Pid::from_raw(-1), None) {
-/// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => {
-/// let _ = step(pid, Signal::SIGUSR1);
-/// }
-/// _ => {},
+///
+/// // If a process changes state to the stopped state because of a SIGUSR1
+/// // signal, this will step the process forward and forward the user
+/// // signal to the stopped process
+/// match waitpid(Pid::from_raw(-1), None) {
+/// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => {
+/// let _ = step(pid, Signal::SIGUSR1);
/// }
+/// _ => {},
/// }
/// ```
pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
diff --git a/src/sys/quota.rs b/src/sys/quota.rs
index 1933013..6e34e38 100644
--- a/src/sys/quota.rs
+++ b/src/sys/quota.rs
@@ -42,6 +42,7 @@ libc_enum!{
libc_enum!{
/// The scope of the quota.
#[repr(i32)]
+ #[non_exhaustive]
pub enum QuotaType {
/// Specify a user quota
USRQUOTA,
@@ -53,6 +54,7 @@ libc_enum!{
libc_enum!{
/// The type of quota format to use.
#[repr(i32)]
+ #[non_exhaustive]
pub enum QuotaFmt {
/// Use the original quota format.
QFMT_VFS_OLD,
diff --git a/src/sys/reboot.rs b/src/sys/reboot.rs
index 5b37682..46ab68b 100644
--- a/src/sys/reboot.rs
+++ b/src/sys/reboot.rs
@@ -1,8 +1,7 @@
//! Reboot/shutdown or enable/disable Ctrl-Alt-Delete.
-use crate::{Error, Result};
+use crate::Result;
use crate::errno::Errno;
-use libc;
use std::convert::Infallible;
use std::mem::drop;
@@ -12,6 +11,7 @@ libc_enum! {
/// See [`set_cad_enabled()`](fn.set_cad_enabled.html) for
/// enabling/disabling Ctrl-Alt-Delete.
#[repr(i32)]
+ #[non_exhaustive]
pub enum RebootMode {
RB_HALT_SYSTEM,
RB_KEXEC,
@@ -26,7 +26,7 @@ pub fn reboot(how: RebootMode) -> Result<Infallible> {
unsafe {
libc::reboot(how as libc::c_int)
};
- Err(Error::from(Errno::last()))
+ Err(Errno::last())
}
/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete).
diff --git a/src/sys/resource.rs b/src/sys/resource.rs
new file mode 100644
index 0000000..f3bfb67
--- /dev/null
+++ b/src/sys/resource.rs
@@ -0,0 +1,233 @@
+//! Configure the process resource limits.
+use cfg_if::cfg_if;
+
+use crate::errno::Errno;
+use crate::Result;
+pub use libc::rlim_t;
+use std::mem;
+
+cfg_if! {
+ if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
+ use libc::{__rlimit_resource_t, rlimit, RLIM_INFINITY};
+ }else if #[cfg(any(
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "dragonfly",
+ all(target_os = "linux", not(target_env = "gnu"))
+ ))]{
+ use libc::{c_int, rlimit, RLIM_INFINITY};
+ }
+}
+
+libc_enum! {
+ /// The Resource enum is platform dependent. Check different platform
+ /// manuals for more details. Some platform links has been provided for
+ /// earier reference (non-exhaustive).
+ ///
+ /// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html)
+ /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit)
+
+ // linux-gnu uses u_int as resource enum, which is implemented in libc as
+ // well.
+ //
+ // https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html
+ // https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs
+ #[cfg_attr(all(target_os = "linux", target_env = "gnu"), repr(u32))]
+ #[cfg_attr(any(
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "dragonfly",
+ all(target_os = "linux", not(target_env = "gnu"))
+ ), repr(i32))]
+ #[non_exhaustive]
+ pub enum Resource {
+ #[cfg(not(any(
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )))]
+ /// The maximum amount (in bytes) of virtual memory the process is
+ /// allowed to map.
+ RLIMIT_AS,
+ /// The largest size (in bytes) core(5) file that may be created.
+ RLIMIT_CORE,
+ /// The maximum amount of cpu time (in seconds) to be used by each
+ /// process.
+ RLIMIT_CPU,
+ /// The maximum size (in bytes) of the data segment for a process
+ RLIMIT_DATA,
+ /// The largest size (in bytes) file that may be created.
+ RLIMIT_FSIZE,
+ /// The maximum number of open files for this process.
+ RLIMIT_NOFILE,
+ /// The maximum size (in bytes) of the stack segment for a process.
+ RLIMIT_STACK,
+
+ #[cfg(target_os = "freebsd")]
+ /// The maximum number of kqueues this user id is allowed to create.
+ RLIMIT_KQUEUES,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ /// A limit on the combined number of flock locks and fcntl leases that
+ /// this process may establish.
+ RLIMIT_LOCKS,
+
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
+ /// The maximum size (in bytes) which a process may lock into memory
+ /// using the mlock(2) system call.
+ RLIMIT_MEMLOCK,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ /// A limit on the number of bytes that can be allocated for POSIX
+ /// message queues for the real user ID of the calling process.
+ RLIMIT_MSGQUEUE,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ /// A ceiling to which the process's nice value can be raised using
+ /// setpriority or nice.
+ RLIMIT_NICE,
+
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
+ /// The maximum number of simultaneous processes for this user id.
+ RLIMIT_NPROC,
+
+ #[cfg(target_os = "freebsd")]
+ /// The maximum number of pseudo-terminals this user id is allowed to
+ /// create.
+ RLIMIT_NPTS,
+
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
+ /// When there is memory pressure and swap is available, prioritize
+ /// eviction of a process' resident pages beyond this amount (in bytes).
+ RLIMIT_RSS,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ /// A ceiling on the real-time priority that may be set for this process
+ /// using sched_setscheduler and sched_set‐ param.
+ RLIMIT_RTPRIO,
+
+ #[cfg(any(target_os = "linux"))]
+ /// A limit (in microseconds) on the amount of CPU time that a process
+ /// scheduled under a real-time scheduling policy may con‐ sume without
+ /// making a blocking system call.
+ RLIMIT_RTTIME,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ /// A limit on the number of signals that may be queued for the real
+ /// user ID of the calling process.
+ RLIMIT_SIGPENDING,
+
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ /// The maximum size (in bytes) of socket buffer usage for this user.
+ RLIMIT_SBSIZE,
+
+ #[cfg(target_os = "freebsd")]
+ /// The maximum size (in bytes) of the swap space that may be reserved
+ /// or used by all of this user id's processes.
+ RLIMIT_SWAP,
+
+ #[cfg(target_os = "freebsd")]
+ /// An alias for RLIMIT_AS.
+ RLIMIT_VMEM,
+ }
+}
+
+/// Get the current processes resource limits
+///
+/// A value of `None` indicates the value equals to `RLIM_INFINITY` which means
+/// there is no limit.
+///
+/// # Parameters
+///
+/// * `resource`: The [`Resource`] that we want to get the limits of.
+///
+/// # Examples
+///
+/// ```
+/// # use nix::sys::resource::{getrlimit, Resource};
+///
+/// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
+/// println!("current soft_limit: {:?}", soft_limit);
+/// println!("current hard_limit: {:?}", hard_limit);
+/// ```
+///
+/// # References
+///
+/// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
+///
+/// [`Resource`]: enum.Resource.html
+pub fn getrlimit(resource: Resource) -> Result<(Option<rlim_t>, Option<rlim_t>)> {
+ let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();
+
+ cfg_if! {
+ if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
+ let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
+ }else{
+ let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) };
+ }
+ }
+
+ Errno::result(res).map(|_| {
+ let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() };
+ (Some(rlim_cur), Some(rlim_max))
+ })
+}
+
+/// Set the current processes resource limits
+///
+/// # Parameters
+///
+/// * `resource`: The [`Resource`] that we want to set the limits of.
+/// * `soft_limit`: The value that the kernel enforces for the corresponding
+/// resource. Note: `None` input will be replaced by constant `RLIM_INFINITY`.
+/// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to
+/// the current hard limit for non-root users. Note: `None` input will be
+/// replaced by constant `RLIM_INFINITY`.
+///
+/// > Note: for some os (linux_gnu), setting hard_limit to `RLIM_INFINITY` can
+/// > results `EPERM` Error. So you will need to set the number explicitly.
+///
+/// # Examples
+///
+/// ```
+/// # use nix::sys::resource::{setrlimit, Resource};
+///
+/// let soft_limit = Some(512);
+/// let hard_limit = Some(1024);
+/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
+/// ```
+///
+/// # References
+///
+/// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
+///
+/// [`Resource`]: enum.Resource.html
+///
+/// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`.
+pub fn setrlimit(
+ resource: Resource,
+ soft_limit: Option<rlim_t>,
+ hard_limit: Option<rlim_t>,
+) -> Result<()> {
+ let new_rlim = rlimit {
+ rlim_cur: soft_limit.unwrap_or(RLIM_INFINITY),
+ rlim_max: hard_limit.unwrap_or(RLIM_INFINITY),
+ };
+ cfg_if! {
+ if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
+ let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) };
+ }else{
+ let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) };
+ }
+ }
+
+ Errno::result(res).map(drop)
+}
diff --git a/src/sys/select.rs b/src/sys/select.rs
index 5eb6423..4d7576a 100644
--- a/src/sys/select.rs
+++ b/src/sys/select.rs
@@ -1,3 +1,5 @@
+//! Portably monitor a group of file descriptors for readiness.
+use std::convert::TryFrom;
use std::iter::FusedIterator;
use std::mem;
use std::ops::Range;
@@ -11,11 +13,20 @@ use crate::sys::time::{TimeSpec, TimeVal};
pub use libc::FD_SETSIZE;
+/// Contains a set of file descriptors used by [`select`]
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct FdSet(libc::fd_set);
+fn assert_fd_valid(fd: RawFd) {
+ assert!(
+ usize::try_from(fd).map_or(false, |fd| fd < FD_SETSIZE),
+ "fd must be in the range 0..FD_SETSIZE",
+ );
+}
+
impl FdSet {
+ /// Create an empty `FdSet`
pub fn new() -> FdSet {
let mut fdset = mem::MaybeUninit::uninit();
unsafe {
@@ -24,18 +35,25 @@ impl FdSet {
}
}
+ /// Add a file descriptor to an `FdSet`
pub fn insert(&mut self, fd: RawFd) {
+ assert_fd_valid(fd);
unsafe { libc::FD_SET(fd, &mut self.0) };
}
+ /// Remove a file descriptor from an `FdSet`
pub fn remove(&mut self, fd: RawFd) {
+ assert_fd_valid(fd);
unsafe { libc::FD_CLR(fd, &mut self.0) };
}
- pub fn contains(&mut self, fd: RawFd) -> bool {
- unsafe { libc::FD_ISSET(fd, &mut self.0) }
+ /// Test an `FdSet` for the presence of a certain file descriptor.
+ pub fn contains(&self, fd: RawFd) -> bool {
+ assert_fd_valid(fd);
+ unsafe { libc::FD_ISSET(fd, &self.0) }
}
+ /// Remove all file descriptors from this `FdSet`.
pub fn clear(&mut self) {
unsafe { libc::FD_ZERO(&mut self.0) };
}
@@ -57,7 +75,7 @@ impl FdSet {
/// ```
///
/// [`select`]: fn.select.html
- pub fn highest(&mut self) -> Option<RawFd> {
+ pub fn highest(&self) -> Option<RawFd> {
self.fds(None).next_back()
}
@@ -79,7 +97,7 @@ impl FdSet {
/// assert_eq!(fds, vec![4, 9]);
/// ```
#[inline]
- pub fn fds(&mut self, highest: Option<RawFd>) -> Fds {
+ pub fn fds(&self, highest: Option<RawFd>) -> Fds {
Fds {
set: self,
range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE),
@@ -96,7 +114,7 @@ impl Default for FdSet {
/// Iterator over `FdSet`.
#[derive(Debug)]
pub struct Fds<'a> {
- set: &'a mut FdSet,
+ set: &'a FdSet,
range: Range<usize>,
}
@@ -104,7 +122,7 @@ impl<'a> Iterator for Fds<'a> {
type Item = RawFd;
fn next(&mut self) -> Option<RawFd> {
- while let Some(i) = self.range.next() {
+ for i in &mut self.range {
if self.set.contains(i as RawFd) {
return Some(i as RawFd);
}
diff --git a/src/sys/sendfile.rs b/src/sys/sendfile.rs
index a12c041..7a210c6 100644
--- a/src/sys/sendfile.rs
+++ b/src/sys/sendfile.rs
@@ -1,3 +1,5 @@
+//! Send data from a file to a socket, bypassing userland.
+
use cfg_if::cfg_if;
use std::os::unix::io::RawFd;
use std::ptr;
diff --git a/src/sys/signal.rs b/src/sys/signal.rs
index 273b352..61bdc74 100644
--- a/src/sys/signal.rs
+++ b/src/sys/signal.rs
@@ -1,12 +1,11 @@
// Portions of this file are Copyright 2014 The Rust Project Developers.
// See https://www.rust-lang.org/policies/licenses.
-///! Operating system signals.
+//! Operating system signals.
use crate::{Error, Result};
use crate::errno::Errno;
use crate::unistd::Pid;
-use std::convert::TryFrom;
use std::mem;
use std::fmt;
use std::str::FromStr;
@@ -18,58 +17,94 @@ use std::ptr;
pub use self::sigevent::*;
libc_enum!{
+ /// Types of operating system signals
// 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.
#[repr(i32)]
+ #[non_exhaustive]
pub enum Signal {
+ /// Hangup
SIGHUP,
+ /// Interrupt
SIGINT,
+ /// Quit
SIGQUIT,
+ /// Illegal instruction (not reset when caught)
SIGILL,
+ /// Trace trap (not reset when caught)
SIGTRAP,
+ /// Abort
SIGABRT,
+ /// Bus error
SIGBUS,
+ /// Floating point exception
SIGFPE,
+ /// Kill (cannot be caught or ignored)
SIGKILL,
+ /// User defined signal 1
SIGUSR1,
+ /// Segmentation violation
SIGSEGV,
+ /// User defined signal 2
SIGUSR2,
+ /// Write on a pipe with no one to read it
SIGPIPE,
+ /// Alarm clock
SIGALRM,
+ /// Software termination signal from kill
SIGTERM,
+ /// Stack fault (obsolete)
#[cfg(all(any(target_os = "android", target_os = "emscripten",
target_os = "fuchsia", target_os = "linux"),
not(any(target_arch = "mips", target_arch = "mips64",
target_arch = "sparc64"))))]
SIGSTKFLT,
+ /// To parent on child stop or exit
SIGCHLD,
+ /// Continue a stopped process
SIGCONT,
+ /// Sendable stop signal not from tty
SIGSTOP,
+ /// Stop signal from tty
SIGTSTP,
+ /// To readers pgrp upon background tty read
SIGTTIN,
+ /// Like TTIN if (tp->t_local&LTOSTOP)
SIGTTOU,
+ /// Urgent condition on IO channel
SIGURG,
+ /// Exceeded CPU time limit
SIGXCPU,
+ /// Exceeded file size limit
SIGXFSZ,
+ /// Virtual time alarm
SIGVTALRM,
+ /// Profiling time alarm
SIGPROF,
+ /// Window size changes
SIGWINCH,
+ /// Input/output possible signal
SIGIO,
#[cfg(any(target_os = "android", target_os = "emscripten",
target_os = "fuchsia", target_os = "linux"))]
+ /// Power failure imminent.
SIGPWR,
+ /// Bad system call
SIGSYS,
#[cfg(not(any(target_os = "android", target_os = "emscripten",
target_os = "fuchsia", target_os = "linux",
target_os = "redox")))]
+ /// Emulator trap
SIGEMT,
#[cfg(not(any(target_os = "android", target_os = "emscripten",
target_os = "fuchsia", target_os = "linux",
target_os = "redox")))]
+ /// Information request
SIGINFO,
}
+ impl TryFrom<i32>
}
impl FromStr for Signal {
@@ -121,7 +156,7 @@ impl FromStr for Signal {
target_os = "fuchsia", target_os = "linux",
target_os = "redox")))]
"SIGINFO" => Signal::SIGINFO,
- _ => return Err(Error::from(Errno::EINVAL)),
+ _ => return Err(Errno::EINVAL),
})
}
}
@@ -132,7 +167,7 @@ impl Signal {
/// This function is equivalent to `<Signal as AsRef<str>>::as_ref()`,
/// with difference that returned string is `'static`
/// and not bound to `self`'s lifetime.
- pub fn as_str(self) -> &'static str {
+ pub const fn as_str(self) -> &'static str {
match self {
Signal::SIGHUP => "SIGHUP",
Signal::SIGINT => "SIGINT",
@@ -334,9 +369,8 @@ const SIGNALS: [Signal; 31] = [
SIGEMT,
SIGINFO];
-pub const NSIG: libc::c_int = 32;
-
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+/// Iterate through all signals defined by this operating system
pub struct SignalIterator {
next: usize,
}
@@ -356,25 +390,17 @@ impl Iterator for SignalIterator {
}
impl Signal {
- pub fn iterator() -> SignalIterator {
+ /// Iterate through all signals defined by this OS
+ pub const fn iterator() -> SignalIterator {
SignalIterator{next: 0}
}
}
-impl TryFrom<libc::c_int> for Signal {
- type Error = Error;
-
- fn try_from(signum: libc::c_int) -> Result<Signal> {
- if 0 < signum && signum < NSIG {
- Ok(unsafe { mem::transmute(signum) })
- } else {
- Err(Error::from(Errno::EINVAL))
- }
- }
-}
-
+/// Alias for [`SIGABRT`]
pub const SIGIOT : Signal = SIGABRT;
+/// Alias for [`SIGIO`]
pub const SIGPOLL : Signal = SIGIO;
+/// Alias for [`SIGSYS`]
pub const SIGUNUSED : Signal = SIGSYS;
#[cfg(not(target_os = "redox"))]
@@ -383,26 +409,48 @@ type SaFlags_t = libc::c_int;
type SaFlags_t = libc::c_ulong;
libc_bitflags!{
+ /// Controls the behavior of a [`SigAction`]
pub struct SaFlags: SaFlags_t {
+ /// When catching a [`Signal::SIGCHLD`] signal, the signal will be
+ /// generated only when a child process exits, not when a child process
+ /// stops.
SA_NOCLDSTOP;
+ /// When catching a [`Signal::SIGCHLD`] signal, the system will not
+ /// create zombie processes when children of the calling process exit.
SA_NOCLDWAIT;
+ /// Further occurrences of the delivered signal are not masked during
+ /// the execution of the handler.
SA_NODEFER;
+ /// The system will deliver the signal to the process on a signal stack,
+ /// specified by each thread with sigaltstack(2).
SA_ONSTACK;
+ /// The handler is reset back to the default at the moment the signal is
+ /// delivered.
SA_RESETHAND;
+ /// Requests that certain system calls restart if interrupted by this
+ /// signal. See the man page for complete details.
SA_RESTART;
+ /// This flag is controlled internally by Nix.
SA_SIGINFO;
}
}
libc_enum! {
+ /// Specifies how certain functions should manipulate a signal mask
#[repr(i32)]
+ #[non_exhaustive]
pub enum SigmaskHow {
+ /// The new mask is the union of the current mask and the specified set.
SIG_BLOCK,
+ /// The new mask is the intersection of the current mask and the
+ /// complement of the specified set.
SIG_UNBLOCK,
+ /// The current mask is replaced by the specified set.
SIG_SETMASK,
}
}
+/// Specifies a set of [`Signal`]s that may be blocked, waited for, etc.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct SigSet {
sigset: libc::sigset_t
@@ -410,6 +458,7 @@ pub struct SigSet {
impl SigSet {
+ /// Initialize to include all signals.
pub fn all() -> SigSet {
let mut sigset = mem::MaybeUninit::uninit();
let _ = unsafe { libc::sigfillset(sigset.as_mut_ptr()) };
@@ -417,6 +466,7 @@ impl SigSet {
unsafe{ SigSet { sigset: sigset.assume_init() } }
}
+ /// Initialize to include nothing.
pub fn empty() -> SigSet {
let mut sigset = mem::MaybeUninit::uninit();
let _ = unsafe { libc::sigemptyset(sigset.as_mut_ptr()) };
@@ -424,18 +474,22 @@ impl SigSet {
unsafe{ SigSet { sigset: sigset.assume_init() } }
}
+ /// Add the specified signal to the set.
pub fn add(&mut self, signal: Signal) {
unsafe { libc::sigaddset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) };
}
+ /// Remove all signals from this set.
pub fn clear(&mut self) {
unsafe { libc::sigemptyset(&mut self.sigset as *mut libc::sigset_t) };
}
+ /// Remove the specified signal from this set.
pub fn remove(&mut self, signal: Signal) {
unsafe { libc::sigdelset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) };
}
+ /// Return whether this set includes the specified signal.
pub fn contains(&self, signal: Signal) -> bool {
let res = unsafe { libc::sigismember(&self.sigset as *const libc::sigset_t, signal as libc::c_int) };
@@ -446,6 +500,8 @@ impl SigSet {
}
}
+ /// Merge all of `other`'s signals into this set.
+ // TODO: use libc::sigorset on supported operating systems.
pub fn extend(&mut self, other: &SigSet) {
for signal in Signal::iterator() {
if other.contains(signal) {
@@ -487,6 +543,8 @@ impl SigSet {
/// signal mask becomes pending, and returns the accepted signal.
#[cfg(not(target_os = "redox"))] // RedoxFS does not yet support sigwait
pub fn wait(&self) -> Result<Signal> {
+ use std::convert::TryFrom;
+
let mut signum = mem::MaybeUninit::uninit();
let res = unsafe { libc::sigwait(&self.sigset as *const libc::sigset_t, signum.as_mut_ptr()) };
@@ -582,9 +640,31 @@ impl SigAction {
match self.sigaction.sa_sigaction {
libc::SIG_DFL => SigHandler::SigDfl,
libc::SIG_IGN => SigHandler::SigIgn,
- f if self.flags().contains(SaFlags::SA_SIGINFO) =>
- SigHandler::SigAction( unsafe { mem::transmute(f) } ),
- f => SigHandler::Handler( unsafe { mem::transmute(f) } ),
+ p if self.flags().contains(SaFlags::SA_SIGINFO) =>
+ SigHandler::SigAction(
+ // Safe for one of two reasons:
+ // * The SigHandler was created by SigHandler::new, in which
+ // case the pointer is correct, or
+ // * The SigHandler was created by signal or sigaction, which
+ // are unsafe functions, so the caller should've somehow
+ // ensured that it is correctly initialized.
+ unsafe{
+ *(&p as *const usize
+ as *const extern fn(_, _, _))
+ }
+ as extern fn(_, _, _)),
+ p => SigHandler::Handler(
+ // Safe for one of two reasons:
+ // * The SigHandler was created by SigHandler::new, in which
+ // case the pointer is correct, or
+ // * The SigHandler was created by signal or sigaction, which
+ // are unsafe functions, so the caller should've somehow
+ // ensured that it is correctly initialized.
+ unsafe{
+ *(&p as *const usize
+ as *const extern fn(libc::c_int))
+ }
+ as extern fn(libc::c_int)),
}
}
@@ -594,7 +674,18 @@ impl SigAction {
match self.sigaction.sa_handler {
libc::SIG_DFL => SigHandler::SigDfl,
libc::SIG_IGN => SigHandler::SigIgn,
- f => SigHandler::Handler( unsafe { mem::transmute(f) } ),
+ p => SigHandler::Handler(
+ // Safe for one of two reasons:
+ // * The SigHandler was created by SigHandler::new, in which
+ // case the pointer is correct, or
+ // * The SigHandler was created by signal or sigaction, which
+ // are unsafe functions, so the caller should've somehow
+ // ensured that it is correctly initialized.
+ unsafe{
+ *(&p as *const usize
+ as *const extern fn(libc::c_int))
+ }
+ as extern fn(libc::c_int)),
}
}
}
@@ -606,9 +697,16 @@ impl SigAction {
///
/// # Safety
///
-/// Signal handlers may be called at any point during execution, which limits what is safe to do in
-/// the body of the signal-catching function. Be certain to only make syscalls that are explicitly
-/// marked safe for signal handlers and only share global data using atomics.
+/// * Signal handlers may be called at any point during execution, which limits
+/// what is safe to do in the body of the signal-catching function. Be certain
+/// to only make syscalls that are explicitly marked safe for signal handlers
+/// and only share global data using atomics.
+///
+/// * There is also no guarantee that the old signal handler was installed
+/// correctly. If it was installed by this crate, it will be. But if it was
+/// installed by, for example, C code, then there is no guarantee its function
+/// pointer is valid. In that case, this function effectively dereferences a
+/// raw pointer of unknown provenance.
pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigAction> {
let mut oldact = mem::MaybeUninit::<libc::sigaction>::uninit();
@@ -681,13 +779,16 @@ pub unsafe fn signal(signal: Signal, handler: SigHandler) -> Result<SigHandler>
SigHandler::SigIgn => libc::signal(signal, libc::SIG_IGN),
SigHandler::Handler(handler) => libc::signal(signal, handler as libc::sighandler_t),
#[cfg(not(target_os = "redox"))]
- SigHandler::SigAction(_) => return Err(Error::from(Errno::ENOTSUP)),
+ SigHandler::SigAction(_) => return Err(Errno::ENOTSUP),
};
Errno::result(res).map(|oldhandler| {
match oldhandler {
libc::SIG_DFL => SigHandler::SigDfl,
libc::SIG_IGN => SigHandler::SigIgn,
- f => SigHandler::Handler(mem::transmute(f)),
+ p => SigHandler::Handler(
+ *(&p as *const usize
+ as *const extern fn(libc::c_int))
+ as extern fn(libc::c_int)),
}
})
}
@@ -754,6 +855,24 @@ pub fn sigprocmask(how: SigmaskHow, set: Option<&SigSet>, oldset: Option<&mut Si
Errno::result(res).map(drop)
}
+/// Send a signal to a process
+///
+/// # Arguments
+///
+/// * `pid` - Specifies which processes should receive the signal.
+/// - If positive, specifies an individual process
+/// - If zero, the signal will be sent to all processes whose group
+/// ID is equal to the process group ID of the sender. This is a
+/// variant of [`killpg`].
+/// - If `-1` and the process has super-user privileges, the signal
+/// is sent to all processes exclusing system processes.
+/// - If less than `-1`, the signal is sent to all processes whose
+/// process group ID is equal to the absolute value of `pid`.
+/// * `signal` - Signal to send. If `None`, error checking is performed
+/// but no signal is actually sent.
+///
+/// See Also
+/// [`kill(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html)
pub fn kill<T: Into<Option<Signal>>>(pid: Pid, signal: T) -> Result<()> {
let res = unsafe { libc::kill(pid.into(),
match signal.into() {
@@ -764,12 +883,16 @@ pub fn kill<T: Into<Option<Signal>>>(pid: Pid, signal: T) -> Result<()> {
Errno::result(res).map(drop)
}
-/// Send a signal to a process group [(see
-/// killpg(3))](https://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html).
+/// Send a signal to a process group
///
-/// If `pgrp` less then or equal 1, the behavior is platform-specific.
-/// If `signal` is `None`, `killpg` will only preform error checking and won't
-/// send any signal.
+/// # Arguments
+///
+/// * `pgrp` - Process group to signal. If less then or equal 1, the behavior
+/// is platform-specific.
+/// * `signal` - Signal to send. If `None`, `killpg` will only preform error
+/// checking and won't send any signal.
+///
+/// See Also [killpg(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html).
#[cfg(not(target_os = "fuchsia"))]
pub fn killpg<T: Into<Option<Signal>>>(pgrp: Pid, signal: T) -> Result<()> {
let res = unsafe { libc::killpg(pgrp.into(),
@@ -781,6 +904,9 @@ pub fn killpg<T: Into<Option<Signal>>>(pgrp: Pid, signal: T) -> Result<()> {
Errno::result(res).map(drop)
}
+/// Send a signal to the current thread
+///
+/// See Also [raise(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/raise.html)
pub fn raise(signal: Signal) -> Result<()> {
let res = unsafe { libc::raise(signal as libc::c_int) };
@@ -788,36 +914,51 @@ pub fn raise(signal: Signal) -> Result<()> {
}
+/// Identifies a thread for [`SigevNotify::SigevThreadId`]
#[cfg(target_os = "freebsd")]
pub type type_of_thread_id = libc::lwpid_t;
+/// Identifies a thread for [`SigevNotify::SigevThreadId`]
#[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.
+/// Specifies the notification method used by a [`SigEvent`]
// 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.
+#[cfg(not(any(target_os = "openbsd", target_os = "redox")))]
#[derive(Clone, Copy, Debug, Eq, Hash, 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 },
+ /// Notify by delivering a signal to the process.
+ SigevSignal {
+ /// Signal to deliver
+ signal: Signal,
+ /// Will be present in the `si_value` field of the [`libc::siginfo_t`]
+ /// structure of the queued 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`.
+ /// Notify by delivering an event to a kqueue.
#[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.
+ SigevKevent {
+ /// File descriptor of the kqueue to notify.
+ kq: RawFd,
+ /// Will be contained in the kevent's `udata` field.
+ udata: libc::intptr_t
+ },
+ /// Notify by delivering a signal to a thread.
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
- SigevThreadId { signal: Signal, thread_id: type_of_thread_id,
- si_value: libc::intptr_t },
+ SigevThreadId {
+ /// Signal to send
+ signal: Signal,
+ /// LWP ID of the thread to notify
+ thread_id: type_of_thread_id,
+ /// Will be present in the `si_value` field of the [`libc::siginfo_t`]
+ /// structure of the queued signal.
+ si_value: libc::intptr_t
+ },
}
#[cfg(not(any(target_os = "openbsd", target_os = "redox")))]
@@ -899,6 +1040,7 @@ mod sigevent {
fn set_tid(_sev: &mut libc::sigevent, _sigev_notify: &SigevNotify) {
}
+ /// Return a copy of the inner structure
pub fn sigevent(&self) -> libc::sigevent {
self.sigevent
}
@@ -949,7 +1091,7 @@ mod tests {
#[test]
fn test_from_str_invalid_value() {
- let errval = Err(Error::from(Errno::EINVAL));
+ let errval = Err(Errno::EINVAL);
assert_eq!("NOSIGNAL".parse::<Signal>(), errval);
assert_eq!("kill".parse::<Signal>(), errval);
assert_eq!("9".parse::<Signal>(), errval);
diff --git a/src/sys/signalfd.rs b/src/sys/signalfd.rs
index 49811a1..bc4a452 100644
--- a/src/sys/signalfd.rs
+++ b/src/sys/signalfd.rs
@@ -15,9 +15,8 @@
//!
//! Please note that signal discarding is not specific to `signalfd`, but also happens with regular
//! signal handlers.
-use libc;
use crate::unistd;
-use crate::{Error, Result};
+use crate::Result;
use crate::errno::Errno;
pub use crate::sys::signal::{self, SigSet};
pub use libc::signalfd_siginfo as siginfo;
@@ -34,7 +33,8 @@ libc_bitflags!{
}
pub const SIGNALFD_NEW: RawFd = -1;
-pub const SIGNALFD_SIGINFO_SIZE: usize = 128;
+#[deprecated(since = "0.23.0", note = "use mem::size_of::<siginfo>() instead")]
+pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::<siginfo>();
/// Creates a new file descriptor for reading signals.
///
@@ -98,15 +98,14 @@ impl SignalFd {
}
pub fn read_signal(&mut self) -> Result<Option<siginfo>> {
- let mut buffer = mem::MaybeUninit::<[u8; SIGNALFD_SIGINFO_SIZE]>::uninit();
+ let mut buffer = mem::MaybeUninit::<siginfo>::uninit();
+ let size = mem::size_of_val(&buffer);
let res = Errno::result(unsafe {
- libc::read(self.0,
- buffer.as_mut_ptr() as *mut libc::c_void,
- SIGNALFD_SIGINFO_SIZE as libc::size_t)
+ libc::read(self.0, buffer.as_mut_ptr() as *mut libc::c_void, size)
}).map(|r| r as usize);
match res {
- Ok(SIGNALFD_SIGINFO_SIZE) => Ok(Some(unsafe { mem::transmute(buffer.assume_init()) })),
+ Ok(x) if x == size => Ok(Some(unsafe { buffer.assume_init() })),
Ok(_) => unreachable!("partial read on signalfd"),
Err(Errno::EAGAIN) => Ok(None),
Err(error) => Err(error)
@@ -117,7 +116,7 @@ impl SignalFd {
impl Drop for SignalFd {
fn drop(&mut self) {
let e = unistd::close(self.0);
- if !std::thread::panicking() && e == Err(Error::from(Errno::EBADF)) {
+ if !std::thread::panicking() && e == Err(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
}
@@ -144,14 +143,6 @@ impl Iterator for SignalFd {
#[cfg(test)]
mod tests {
use super::*;
- use std::mem;
- use libc;
-
-
- #[test]
- fn check_siginfo_size() {
- assert_eq!(mem::size_of::<libc::signalfd_siginfo>(), SIGNALFD_SIGINFO_SIZE);
- }
#[test]
fn create_signalfd() {
diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs
index d486056..b119642 100644
--- a/src/sys/socket/addr.rs
+++ b/src/sys/socket/addr.rs
@@ -1,5 +1,5 @@
use super::sa_family_t;
-use crate::{Error, Result, NixPath};
+use crate::{Result, NixPath};
use crate::errno::Errno;
use memoffset::offset_of;
use std::{fmt, mem, net, ptr, slice};
@@ -32,6 +32,7 @@ pub use self::vsock::VsockAddr;
/// These constants specify the protocol family to be used
/// in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
#[repr(i32)]
+#[non_exhaustive]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum AddressFamily {
/// Local communication (see [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html))
@@ -236,7 +237,7 @@ impl AddressFamily {
///
/// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink, Link/Packet
/// and System. Returns None for unsupported or unknown address families.
- pub fn from_i32(family: i32) -> Option<AddressFamily> {
+ pub const fn from_i32(family: i32) -> Option<AddressFamily> {
match family {
libc::AF_UNIX => Some(AddressFamily::Unix),
libc::AF_INET => Some(AddressFamily::Inet),
@@ -269,6 +270,7 @@ pub enum InetAddr {
}
impl InetAddr {
+ #[allow(clippy::needless_update)] // It isn't needless on all OSes
pub fn from_std(std: &net::SocketAddr) -> InetAddr {
match *std {
net::SocketAddr::V4(ref addr) => {
@@ -292,6 +294,7 @@ impl InetAddr {
}
}
+ #[allow(clippy::needless_update)] // It isn't needless on all OSes
pub fn new(ip: IpAddr, port: u16) -> InetAddr {
match ip {
IpAddr::V4(ref ip) => {
@@ -313,7 +316,7 @@ impl InetAddr {
}
}
/// Gets the IP address associated with this socket address.
- pub fn ip(&self) -> IpAddr {
+ pub const fn ip(&self) -> IpAddr {
match *self {
InetAddr::V4(ref sa) => IpAddr::V4(Ipv4Addr(sa.sin_addr)),
InetAddr::V6(ref sa) => IpAddr::V6(Ipv6Addr(sa.sin6_addr)),
@@ -321,7 +324,7 @@ impl InetAddr {
}
/// Gets the port number associated with this socket address
- pub fn port(&self) -> u16 {
+ pub const fn port(&self) -> u16 {
match *self {
InetAddr::V6(ref sa) => u16::from_be(sa.sin6_port),
InetAddr::V4(ref sa) => u16::from_be(sa.sin_port),
@@ -343,6 +346,7 @@ impl InetAddr {
}
}
+ #[deprecated(since = "0.23.0", note = "use .to_string() instead")]
pub fn to_str(&self) -> String {
format!("{}", self)
}
@@ -372,7 +376,7 @@ impl IpAddr {
/// Create a new IpAddr that contains an IPv4 address.
///
/// The result will represent the IP address a.b.c.d
- pub fn new_v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr {
+ pub const fn new_v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr {
IpAddr::V4(Ipv4Addr::new(a, b, c, d))
}
@@ -381,7 +385,7 @@ impl IpAddr {
/// The result will represent the IP address a:b:c:d:e:f
#[allow(clippy::many_single_char_names)]
#[allow(clippy::too_many_arguments)]
- pub fn new_v6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> IpAddr {
+ pub const fn new_v6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> IpAddr {
IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h))
}
@@ -392,7 +396,7 @@ impl IpAddr {
}
}
- pub fn to_std(&self) -> net::IpAddr {
+ pub const fn to_std(&self) -> net::IpAddr {
match *self {
IpAddr::V4(ref ip) => net::IpAddr::V4(ip.to_std()),
IpAddr::V6(ref ip) => net::IpAddr::V6(ip.to_std()),
@@ -420,11 +424,11 @@ pub struct Ipv4Addr(pub libc::in_addr);
impl Ipv4Addr {
#[allow(clippy::identity_op)] // More readable this way
- pub fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
- let ip = ((u32::from(a) << 24) |
- (u32::from(b) << 16) |
- (u32::from(c) << 8) |
- (u32::from(d) << 0)).to_be();
+ pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
+ let ip = (((a as u32) << 24) |
+ ((b as u32) << 16) |
+ ((c as u32) << 8) |
+ ((d as u32) << 0)).to_be();
Ipv4Addr(libc::in_addr { s_addr: ip })
}
@@ -436,16 +440,16 @@ impl Ipv4Addr {
Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3])
}
- pub fn any() -> Ipv4Addr {
+ pub const fn any() -> Ipv4Addr {
Ipv4Addr(libc::in_addr { s_addr: libc::INADDR_ANY })
}
- pub fn octets(self) -> [u8; 4] {
+ pub const fn octets(self) -> [u8; 4] {
let bits = u32::from_be(self.0.s_addr);
[(bits >> 24) as u8, (bits >> 16) as u8, (bits >> 8) as u8, bits as u8]
}
- pub fn to_std(self) -> net::Ipv4Addr {
+ pub const fn to_std(self) -> net::Ipv4Addr {
let bits = self.octets();
net::Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3])
}
@@ -486,7 +490,7 @@ macro_rules! to_u16_array {
impl Ipv6Addr {
#[allow(clippy::many_single_char_names)]
#[allow(clippy::too_many_arguments)]
- pub fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr {
+ pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr {
Ipv6Addr(libc::in6_addr{s6_addr: to_u8_array!(a,b,c,d,e,f,g,h)})
}
@@ -496,11 +500,11 @@ impl Ipv6Addr {
}
/// Return the eight 16-bit segments that make up this address
- pub fn segments(&self) -> [u16; 8] {
+ pub const fn segments(&self) -> [u16; 8] {
to_u16_array!(self, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)
}
- pub fn to_std(&self) -> net::Ipv6Addr {
+ pub const fn to_std(&self) -> net::Ipv6Addr {
let s = self.segments();
net::Ipv6Addr::new(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7])
}
@@ -513,15 +517,42 @@ impl fmt::Display for Ipv6Addr {
}
/// A wrapper around `sockaddr_un`.
-///
-/// This also tracks the length of `sun_path` address (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(Clone, Copy, Debug)]
-pub struct UnixAddr(pub libc::sockaddr_un, pub usize);
+pub struct UnixAddr {
+ // INVARIANT: sun & path_len are valid as defined by docs for from_raw_parts
+ sun: libc::sockaddr_un,
+ path_len: usize,
+}
+
+// linux man page unix(7) says there are 3 kinds of unix socket:
+// pathname: addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1
+// unnamed: addrlen = sizeof(sa_family_t)
+// abstract: addren > sizeof(sa_family_t), name = sun_path[..(addrlen - sizeof(sa_family_t))]
+//
+// what we call path_len = addrlen - offsetof(struct sockaddr_un, sun_path)
+#[derive(PartialEq, Eq, Hash)]
+enum UnixAddrKind<'a> {
+ Pathname(&'a Path),
+ Unnamed,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Abstract(&'a [u8]),
+}
+impl<'a> UnixAddrKind<'a> {
+ /// Safety: sun & path_len must be valid
+ unsafe fn get(sun: &'a libc::sockaddr_un, path_len: usize) -> Self {
+ if path_len == 0 {
+ return Self::Unnamed;
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ if sun.sun_path[0] == 0 {
+ let name =
+ slice::from_raw_parts(sun.sun_path.as_ptr().add(1) as *const u8, path_len - 1);
+ return Self::Abstract(name);
+ }
+ let pathname = slice::from_raw_parts(sun.sun_path.as_ptr() as *const u8, path_len - 1);
+ Self::Pathname(Path::new(OsStr::from_bytes(pathname)))
+ }
+}
impl UnixAddr {
/// Create a new sockaddr_un representing a filesystem path.
@@ -535,15 +566,15 @@ impl UnixAddr {
let bytes = cstr.to_bytes();
- if bytes.len() > ret.sun_path.len() {
- return Err(Error::from(Errno::ENAMETOOLONG));
+ if bytes.len() >= ret.sun_path.len() {
+ return Err(Errno::ENAMETOOLONG);
}
ptr::copy_nonoverlapping(bytes.as_ptr(),
ret.sun_path.as_mut_ptr() as *mut u8,
bytes.len());
- Ok(UnixAddr(ret, bytes.len()))
+ Ok(UnixAddr::from_raw_parts(ret, bytes.len() + 1))
}
})?
}
@@ -562,8 +593,8 @@ impl UnixAddr {
.. mem::zeroed()
};
- if path.len() + 1 > ret.sun_path.len() {
- return Err(Error::from(Errno::ENAMETOOLONG));
+ if path.len() >= ret.sun_path.len() {
+ return Err(Errno::ENAMETOOLONG);
}
// Abstract addresses are represented by sun_path[0] ==
@@ -572,28 +603,39 @@ impl UnixAddr {
ret.sun_path.as_mut_ptr().offset(1) as *mut u8,
path.len());
- Ok(UnixAddr(ret, path.len() + 1))
+ Ok(UnixAddr::from_raw_parts(ret, path.len() + 1))
+ }
+ }
+
+ /// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `path_len` is the "addrlen"
+ /// of this address, but minus `offsetof(struct sockaddr_un, sun_path)`. Basically the length
+ /// of the data in `sun_path`.
+ ///
+ /// # Safety
+ /// This pair of sockaddr_un & path_len must be a valid unix addr, which means:
+ /// - path_len <= sockaddr_un.sun_path.len()
+ /// - if this is a unix addr with a pathname, sun.sun_path is a nul-terminated fs path and
+ /// sun.sun_path[path_len - 1] == 0 || sun.sun_path[path_len] == 0
+ pub(crate) unsafe fn from_raw_parts(sun: libc::sockaddr_un, mut path_len: usize) -> UnixAddr {
+ if let UnixAddrKind::Pathname(_) = UnixAddrKind::get(&sun, path_len) {
+ if sun.sun_path[path_len - 1] != 0 {
+ assert_eq!(sun.sun_path[path_len], 0);
+ path_len += 1
+ }
}
+ UnixAddr { sun, path_len }
}
- fn sun_path(&self) -> &[u8] {
- unsafe { slice::from_raw_parts(self.0.sun_path.as_ptr() as *const u8, self.1) }
+ fn kind(&self) -> UnixAddrKind<'_> {
+ // SAFETY: our sockaddr is always valid because of the invariant on the struct
+ unsafe { UnixAddrKind::get(&self.sun, self.path_len) }
}
/// If this address represents a filesystem path, return that path.
pub fn path(&self) -> Option<&Path> {
- if self.1 == 0 || self.0.sun_path[0] == 0 {
- // unnamed or abstract
- None
- } else {
- let p = self.sun_path();
- // 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])))
+ match self.kind() {
+ UnixAddrKind::Pathname(path) => Some(path),
+ _ => None,
}
}
@@ -603,31 +645,55 @@ impl UnixAddr {
/// leading null byte. `None` is returned for unnamed or path-backed sockets.
#[cfg(any(target_os = "android", target_os = "linux"))]
pub fn as_abstract(&self) -> Option<&[u8]> {
- if self.1 >= 1 && self.0.sun_path[0] == 0 {
- Some(&self.sun_path()[1..])
- } else {
- // unnamed or filesystem path
- None
+ match self.kind() {
+ UnixAddrKind::Abstract(name) => Some(name),
+ _ => None,
}
}
+
+ /// Returns the addrlen of this socket - `offsetof(struct sockaddr_un, sun_path)`
+ #[inline]
+ pub fn path_len(&self) -> usize {
+ self.path_len
+ }
+ /// Returns a pointer to the raw `sockaddr_un` struct
+ #[inline]
+ pub fn as_ptr(&self) -> *const libc::sockaddr_un {
+ &self.sun
+ }
+ /// Returns a mutable pointer to the raw `sockaddr_un` struct
+ #[inline]
+ pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_un {
+ &mut self.sun
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn fmt_abstract(abs: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
+ use fmt::Write;
+ f.write_str("@\"")?;
+ for &b in abs {
+ use fmt::Display;
+ char::from(b).escape_default().fmt(f)?;
+ }
+ f.write_char('"')?;
+ Ok(())
}
impl fmt::Display for UnixAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if self.1 == 0 {
- f.write_str("<unbound UNIX socket>")
- } else if let Some(path) = self.path() {
- path.display().fmt(f)
- } else {
- let display = String::from_utf8_lossy(&self.sun_path()[1..]);
- write!(f, "@{}", display)
+ match self.kind() {
+ UnixAddrKind::Pathname(path) => path.display().fmt(f),
+ UnixAddrKind::Unnamed => f.pad("<unbound UNIX socket>"),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ UnixAddrKind::Abstract(name) => fmt_abstract(name, f),
}
}
}
impl PartialEq for UnixAddr {
fn eq(&self, other: &UnixAddr) -> bool {
- self.sun_path() == other.sun_path()
+ self.kind() == other.kind()
}
}
@@ -635,12 +701,13 @@ impl Eq for UnixAddr {}
impl Hash for UnixAddr {
fn hash<H: Hasher>(&self, s: &mut H) {
- ( self.0.sun_family, self.sun_path() ).hash(s)
+ self.kind().hash(s)
}
}
/// Represents a socket address
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
pub enum SockAddr {
Inet(InetAddr),
Unix(UnixAddr),
@@ -686,7 +753,7 @@ impl SockAddr {
#[cfg(any(target_os = "ios", target_os = "macos"))]
pub fn new_sys_control(sockfd: RawFd, name: &str, unit: u32) -> Result<SockAddr> {
- SysControlAddr::from_name(sockfd, name, unit).map(|a| SockAddr::SysControl(a))
+ SysControlAddr::from_name(sockfd, name, unit).map(SockAddr::SysControl)
}
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -720,6 +787,7 @@ impl SockAddr {
}
}
+ #[deprecated(since = "0.23.0", note = "use .to_string() instead")]
pub fn to_str(&self) -> String {
format!("{}", self)
}
@@ -801,12 +869,12 @@ impl SockAddr {
},
mem::size_of_val(addr) as libc::socklen_t
),
- SockAddr::Unix(UnixAddr(ref addr, len)) => (
+ SockAddr::Unix(UnixAddr { ref sun, path_len }) => (
// This cast is always allowed in C
unsafe {
- &*(addr as *const libc::sockaddr_un as *const libc::sockaddr)
+ &*(sun as *const libc::sockaddr_un as *const libc::sockaddr)
},
- (len + offset_of!(libc::sockaddr_un, sun_path)) as libc::socklen_t
+ (path_len + offset_of!(libc::sockaddr_un, sun_path)) as libc::socklen_t
),
#[cfg(any(target_os = "android", target_os = "linux"))]
SockAddr::Netlink(NetlinkAddr(ref sa)) => (
@@ -913,11 +981,11 @@ pub mod netlink {
NetlinkAddr(addr)
}
- pub fn pid(&self) -> u32 {
+ pub const fn pid(&self) -> u32 {
self.0.nl_pid
}
- pub fn groups(&self) -> u32 {
+ pub const fn groups(&self) -> u32 {
self.0.nl_groups
}
}
@@ -998,7 +1066,7 @@ pub mod sys_control {
use libc::{self, c_uchar};
use std::{fmt, mem};
use std::os::unix::io::RawFd;
- use crate::{Errno, Error, Result};
+ use crate::{Errno, Result};
// FIXME: Move type into `libc`
#[repr(C)]
@@ -1009,7 +1077,7 @@ pub mod sys_control {
pub ctl_name: [c_uchar; MAX_KCTL_NAME],
}
- const CTL_IOC_MAGIC: u8 = 'N' as u8;
+ const CTL_IOC_MAGIC: u8 = b'N';
const CTL_IOC_INFO: u8 = 3;
const MAX_KCTL_NAME: usize = 96;
@@ -1020,7 +1088,7 @@ pub mod sys_control {
pub struct SysControlAddr(pub libc::sockaddr_ctl);
impl SysControlAddr {
- pub fn new(id: u32, unit: u32) -> SysControlAddr {
+ pub const fn new(id: u32, unit: u32) -> SysControlAddr {
let addr = libc::sockaddr_ctl {
sc_len: mem::size_of::<libc::sockaddr_ctl>() as c_uchar,
sc_family: AddressFamily::System as c_uchar,
@@ -1035,7 +1103,7 @@ pub mod sys_control {
pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result<SysControlAddr> {
if name.len() > MAX_KCTL_NAME {
- return Err(Error::from(Errno::ENAMETOOLONG));
+ return Err(Errno::ENAMETOOLONG);
}
let mut ctl_name = [0; MAX_KCTL_NAME];
@@ -1047,11 +1115,11 @@ pub mod sys_control {
Ok(SysControlAddr::new(info.ctl_id, unit))
}
- pub fn id(&self) -> u32 {
+ pub const fn id(&self) -> u32 {
self.0.sc_id
}
- pub fn unit(&self) -> u32 {
+ pub const fn unit(&self) -> u32 {
self.0.sc_unit
}
}
@@ -1372,11 +1440,8 @@ mod tests {
let name = String::from("nix\0abstract\0test");
let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();
- let sun_path1 = addr.sun_path();
- let sun_path2 = [0u8, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, 115, 116];
- assert_eq!(sun_path1.len(), sun_path2.len());
- for i in 0..sun_path1.len() {
- assert_eq!(sun_path1[i], sun_path2[i]);
- }
+ let sun_path1 = unsafe { &(*addr.as_ptr()).sun_path[..addr.path_len()] };
+ let sun_path2 = [0, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, 115, 116];
+ assert_eq!(sun_path1, sun_path2);
}
}
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs
index da5573c..97eea3d 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -2,7 +2,7 @@
//!
//! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html)
use cfg_if::cfg_if;
-use crate::{Error, Result, errno::Errno};
+use crate::{Result, errno::Errno};
use libc::{self, c_void, c_int, iovec, socklen_t, size_t,
CMSG_FIRSTHDR, CMSG_NXTHDR, CMSG_DATA, CMSG_LEN};
use memoffset::offset_of;
@@ -14,6 +14,7 @@ use crate::sys::time::TimeVal;
use crate::sys::uio::IoVec;
mod addr;
+#[deny(missing_docs)]
pub mod sockopt;
/*
@@ -70,6 +71,7 @@ pub use libc::{c_uint, CMSG_SPACE};
/// when creating a socket with [`socket()`](fn.socket.html)
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(i32)]
+#[non_exhaustive]
pub enum SockType {
/// Provides sequenced, reliable, two-way, connection-
/// based byte streams. An out-of-band data transmission
@@ -94,6 +96,7 @@ pub enum SockType {
/// to specify the protocol to use.
#[repr(i32)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
pub enum SockProtocol {
/// TCP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html))
Tcp = libc::IPPROTO_TCP,
@@ -308,9 +311,9 @@ cfg_if! {
}
}
- impl Into<libc::ucred> for UnixCredentials {
- fn into(self) -> libc::ucred {
- self.0
+ impl From<UnixCredentials> for libc::ucred {
+ fn from(uc: UnixCredentials) -> Self {
+ uc.0
}
}
} else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] {
@@ -356,6 +359,38 @@ cfg_if! {
}
}
+cfg_if!{
+ if #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+ ))] {
+ /// Return type of [`LocalPeerCred`](crate::sys::socket::sockopt::LocalPeerCred)
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ pub struct XuCred(libc::xucred);
+
+ impl XuCred {
+ /// Structure layout version
+ pub fn version(&self) -> u32 {
+ self.0.cr_version
+ }
+
+ /// Effective user ID
+ pub fn uid(&self) -> libc::uid_t {
+ self.0.cr_uid
+ }
+
+ /// Returns a list of group identifiers (the first one being the
+ /// effective GID)
+ pub fn groups(&self) -> &[libc::gid_t] {
+ &self.0.cr_groups
+ }
+ }
+ }
+}
+
/// Request for multicast socket operations
///
/// This is a wrapper type around `ip_mreq`.
@@ -384,7 +419,7 @@ pub struct Ipv6MembershipRequest(libc::ipv6_mreq);
impl Ipv6MembershipRequest {
/// Instantiate a new `Ipv6MembershipRequest`
- pub fn new(group: Ipv6Addr) -> Self {
+ pub const fn new(group: Ipv6Addr) -> Self {
Ipv6MembershipRequest(libc::ipv6_mreq {
ipv6mr_multiaddr: group.0,
ipv6mr_interface: 0,
@@ -490,16 +525,14 @@ impl<'a> Iterator for CmsgIterator<'a> {
//
// See https://github.com/nix-rust/nix/issues/999
#[derive(Clone, Debug, Eq, PartialEq)]
+#[non_exhaustive]
pub enum ControlMessageOwned {
- /// Received version of
- /// [`ControlMessage::ScmRights`][#enum.ControlMessage.html#variant.ScmRights]
+ /// Received version of [`ControlMessage::ScmRights`]
ScmRights(Vec<RawFd>),
- /// Received version of
- /// [`ControlMessage::ScmCredentials`][#enum.ControlMessage.html#variant.ScmCredentials]
+ /// Received version of [`ControlMessage::ScmCredentials`]
#[cfg(any(target_os = "android", target_os = "linux"))]
ScmCredentials(UnixCredentials),
- /// Received version of
- /// [`ControlMessage::ScmCreds`][#enum.ControlMessage.html#variant.ScmCreds]
+ /// Received version of [`ControlMessage::ScmCreds`]
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
ScmCreds(UnixCredentials),
/// A message of type `SCM_TIMESTAMP`, containing the time the
@@ -621,6 +654,13 @@ pub enum ControlMessageOwned {
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
RxqOvfl(u32),
+ /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Ipv4RecvErr(libc::sock_extended_err, Option<sockaddr_in>),
+ /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Ipv6RecvErr(libc::sock_extended_err, Option<sockaddr_in6>),
+
/// Catch-all variant for unimplemented cmsg types.
#[doc(hidden)]
Unknown(UnknownCmsg),
@@ -724,6 +764,16 @@ impl ControlMessageOwned {
let drop_counter = ptr::read_unaligned(p as *const u32);
ControlMessageOwned::RxqOvfl(drop_counter)
},
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::IPPROTO_IP, libc::IP_RECVERR) => {
+ let (err, addr) = Self::recv_err_helper::<sockaddr_in>(p, len);
+ ControlMessageOwned::Ipv4RecvErr(err, addr)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => {
+ let (err, addr) = Self::recv_err_helper::<sockaddr_in6>(p, len);
+ ControlMessageOwned::Ipv6RecvErr(err, addr)
+ },
(_, _) => {
let sl = slice::from_raw_parts(p, len);
let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
@@ -731,6 +781,24 @@ impl ControlMessageOwned {
}
}
}
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ unsafe fn recv_err_helper<T>(p: *mut libc::c_uchar, len: usize) -> (libc::sock_extended_err, Option<T>) {
+ let ee = p as *const libc::sock_extended_err;
+ let err = ptr::read_unaligned(ee);
+
+ // For errors originating on the network, SO_EE_OFFENDER(ee) points inside the p[..len]
+ // CMSG_DATA buffer. For local errors, there is no address included in the control
+ // message, and SO_EE_OFFENDER(ee) points beyond the end of the buffer. So, we need to
+ // validate that the address object is in-bounds before we attempt to copy it.
+ let addrp = libc::SO_EE_OFFENDER(ee) as *const T;
+
+ if addrp.offset(1) as usize - (p as usize) > len {
+ (err, None)
+ } else {
+ (err, Some(ptr::read_unaligned(addrp)))
+ }
+ }
}
/// A type-safe zero-copy wrapper around a single control message, as used wih
@@ -739,6 +807,7 @@ impl ControlMessageOwned {
///
/// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html)
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[non_exhaustive]
pub enum ControlMessage<'a> {
/// A message of type `SCM_RIGHTS`, containing an array of file
/// descriptors passed between processes.
@@ -772,7 +841,7 @@ pub enum ControlMessage<'a> {
///
/// Credentials are always overwritten by the kernel, so this variant does have
/// any data, unlike the receive-side
- /// [`ControlMessageOwned::ScmCreds`][#enum.ControlMessageOwned.html#variant.ScmCreds].
+ /// [`ControlMessageOwned::ScmCreds`].
///
/// For further information, please refer to the
/// [`unix(4)`](https://www.freebsd.org/cgi/man.cgi?query=unix) man page.
@@ -1231,6 +1300,7 @@ pub struct RecvMmsgData<'a, I>
target_os = "freebsd",
target_os = "netbsd",
))]
+#[allow(clippy::needless_collect)] // Complicated false positive
pub fn recvmmsg<'a, I>(
fd: RawFd,
data: impl std::iter::IntoIterator<Item=&'a mut RecvMmsgData<'a, I>,
@@ -1646,21 +1716,19 @@ pub fn send(fd: RawFd, buf: &[u8], flags: MsgFlags) -> Result<usize> {
*
*/
-/// Represents a socket option that can be accessed or set. Used as an argument
-/// to `getsockopt`
+/// Represents a socket option that can be retrieved.
pub trait GetSockOpt : Copy {
type Val;
- #[doc(hidden)]
+ /// Look up the value of this socket option on the given socket.
fn get(&self, fd: RawFd) -> Result<Self::Val>;
}
-/// Represents a socket option that can be accessed or set. Used as an argument
-/// to `setsockopt`
+/// Represents a socket option that can be set.
pub trait SetSockOpt : Clone {
type Val;
- #[doc(hidden)]
+ /// Set the value of this socket option on the given socket.
fn set(&self, fd: RawFd, val: &Self::Val) -> Result<()>;
}
@@ -1744,21 +1812,21 @@ pub fn sockaddr_storage_to_addr(
addr: &sockaddr_storage,
len: usize) -> Result<SockAddr> {
- assert!(len <= mem::size_of::<sockaddr_un>());
+ assert!(len <= mem::size_of::<sockaddr_storage>());
if len < mem::size_of_val(&addr.ss_family) {
- return Err(Error::from(Errno::ENOTCONN));
+ return Err(Errno::ENOTCONN);
}
match c_int::from(addr.ss_family) {
libc::AF_INET => {
- assert_eq!(len as usize, mem::size_of::<sockaddr_in>());
+ assert!(len as usize >= mem::size_of::<sockaddr_in>());
let sin = unsafe {
*(addr as *const sockaddr_storage as *const sockaddr_in)
};
Ok(SockAddr::Inet(InetAddr::V4(sin)))
}
libc::AF_INET6 => {
- assert_eq!(len as usize, mem::size_of::<sockaddr_in6>());
+ assert!(len as usize >= mem::size_of::<sockaddr_in6>());
let sin6 = unsafe {
*(addr as *const _ as *const sockaddr_in6)
};
@@ -1766,18 +1834,18 @@ pub fn sockaddr_storage_to_addr(
}
libc::AF_UNIX => {
let pathlen = len - offset_of!(sockaddr_un, sun_path);
- let sun = unsafe {
- *(addr as *const _ as *const sockaddr_un)
- };
- Ok(SockAddr::Unix(UnixAddr(sun, pathlen)))
+ unsafe {
+ let sun = *(addr as *const _ as *const sockaddr_un);
+ Ok(SockAddr::Unix(UnixAddr::from_raw_parts(sun, pathlen)))
+ }
}
#[cfg(any(target_os = "android", target_os = "linux"))]
libc::AF_PACKET => {
use libc::sockaddr_ll;
+ // Don't assert anything about the size.
// Apparently the Linux kernel can return smaller sizes when
// the value in the last element of sockaddr_ll (`sll_addr`) is
// smaller than the declared size of that field
- assert!(len as usize <= mem::size_of::<sockaddr_ll>());
let sll = unsafe {
*(addr as *const _ as *const sockaddr_ll)
};
diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs
index e2f2caf..fcb4be8 100644
--- a/src/sys/socket/sockopt.rs
+++ b/src/sys/socket/sockopt.rs
@@ -1,3 +1,4 @@
+//! Socket options as used by `setsockopt` and `getsockopt`.
use cfg_if::cfg_if;
use super::{GetSockOpt, SetSockOpt};
use crate::Result;
@@ -30,7 +31,7 @@ const TCP_CA_NAME_MAX: usize = 16;
/// # Arguments
///
/// * `$name:ident`: name of the type you want to implement `SetSockOpt` for.
-/// * `$level:path` : socket layer, or a `protocol level`: could be *raw sockets*
+/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `setsockopt` call.
@@ -41,7 +42,7 @@ const TCP_CA_NAME_MAX: usize = 16;
/// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for
/// `bool`, `SetUsize` for `usize`, etc.).
macro_rules! setsockopt_impl {
- ($name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => {
+ ($name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => {
impl SetSockOpt for $name {
type Val = $ty;
@@ -82,7 +83,7 @@ macro_rules! setsockopt_impl {
/// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for
/// `bool`, `GetUsize` for `usize`, etc.).
macro_rules! getsockopt_impl {
- ($name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => {
+ ($name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => {
impl GetSockOpt for $name {
type Val = $ty;
@@ -117,7 +118,7 @@ macro_rules! getsockopt_impl {
/// * `GetOnly`, `SetOnly` or `Both`: whether you want to implement only getter, only setter or
/// both of them.
/// * `$name:ident`: name of type `GetSockOpt`/`SetSockOpt` will be implemented for.
-/// * `$level:path` : socket layer, or a `protocol level`: could be *raw sockets*
+/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
/// (`lic::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
/// and more. Please refer to your system manual for more options. Will be passed as the second
/// argument (`level`) to the `getsockopt`/`setsockopt` call.
@@ -128,73 +129,99 @@ macro_rules! getsockopt_impl {
/// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`.
/// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`.
macro_rules! sockopt_impl {
- (GetOnly, $name:ident, $level:path, $flag:path, bool) => {
- sockopt_impl!(GetOnly, $name, $level, $flag, bool, GetBool);
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, bool) => {
+ sockopt_impl!($(#[$attr])*
+ $name, GetOnly, $level, $flag, bool, GetBool);
};
- (GetOnly, $name:ident, $level:path, $flag:path, u8) => {
- sockopt_impl!(GetOnly, $name, $level, $flag, u8, GetU8);
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, u8) => {
+ sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, GetU8);
};
- (GetOnly, $name:ident, $level:path, $flag:path, usize) => {
- sockopt_impl!(GetOnly, $name, $level, $flag, usize, GetUsize);
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, usize) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, GetOnly, $level, $flag, usize, GetUsize);
};
- (SetOnly, $name:ident, $level:path, $flag:path, bool) => {
- sockopt_impl!(SetOnly, $name, $level, $flag, bool, SetBool);
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, bool) => {
+ sockopt_impl!($(#[$attr])*
+ $name, SetOnly, $level, $flag, bool, SetBool);
};
- (SetOnly, $name:ident, $level:path, $flag:path, u8) => {
- sockopt_impl!(SetOnly, $name, $level, $flag, u8, SetU8);
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, u8) => {
+ sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, SetU8);
};
- (SetOnly, $name:ident, $level:path, $flag:path, usize) => {
- sockopt_impl!(SetOnly, $name, $level, $flag, usize, SetUsize);
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, usize) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, SetOnly, $level, $flag, usize, SetUsize);
};
- (Both, $name:ident, $level:path, $flag:path, bool) => {
- sockopt_impl!(Both, $name, $level, $flag, bool, GetBool, SetBool);
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, bool) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, bool, GetBool, SetBool);
};
- (Both, $name:ident, $level:path, $flag:path, u8) => {
- sockopt_impl!(Both, $name, $level, $flag, u8, GetU8, SetU8);
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, u8) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, u8, GetU8, SetU8);
};
- (Both, $name:ident, $level:path, $flag:path, usize) => {
- sockopt_impl!(Both, $name, $level, $flag, usize, GetUsize, SetUsize);
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, usize) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, usize, GetUsize, SetUsize);
};
- (Both, $name:ident, $level:path, $flag:path, OsString<$array:ty>) => {
- sockopt_impl!(Both, $name, $level, $flag, OsString, GetOsString<$array>, SetOsString);
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path,
+ OsString<$array:ty>) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, OsString, GetOsString<$array>,
+ SetOsString);
};
/*
* 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>);
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, GetOnly, $level, $flag, $ty, GetStruct<$ty>);
};
- (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => {
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty,
+ $getter:ty) =>
+ {
+ $(#[$attr])*
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
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>);
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, SetOnly, $level, $flag, $ty, SetStruct<$ty>);
};
- (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => {
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty,
+ $setter:ty) =>
+ {
+ $(#[$attr])*
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct $name;
setsockopt_impl!($name, $level, $flag, $ty, $setter);
};
- (Both, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty, $setter:ty) => {
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty,
+ $getter:ty, $setter:ty) =>
+ {
+ $(#[$attr])*
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct $name;
@@ -202,8 +229,10 @@ macro_rules! sockopt_impl {
getsockopt_impl!($name, $level, $flag, $ty, $getter);
};
- (Both, $name:ident, $level:path, $flag:path, $ty:ty) => {
- sockopt_impl!(Both, $name, $level, $flag, $ty, GetStruct<$ty>, SetStruct<$ty>);
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, $ty, GetStruct<$ty>,
+ SetStruct<$ty>);
};
}
@@ -213,17 +242,45 @@ macro_rules! sockopt_impl {
*
*/
-sockopt_impl!(Both, ReuseAddr, libc::SOL_SOCKET, libc::SO_REUSEADDR, bool);
+sockopt_impl!(
+ /// Enables local address reuse
+ ReuseAddr, Both, libc::SOL_SOCKET, libc::SO_REUSEADDR, bool
+);
#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
-sockopt_impl!(Both, ReusePort, libc::SOL_SOCKET, libc::SO_REUSEPORT, bool);
-sockopt_impl!(Both, TcpNoDelay, libc::IPPROTO_TCP, libc::TCP_NODELAY, bool);
-sockopt_impl!(Both, Linger, libc::SOL_SOCKET, libc::SO_LINGER, libc::linger);
-sockopt_impl!(SetOnly, IpAddMembership, libc::IPPROTO_IP, libc::IP_ADD_MEMBERSHIP, super::IpMembershipRequest);
-sockopt_impl!(SetOnly, IpDropMembership, libc::IPPROTO_IP, libc::IP_DROP_MEMBERSHIP, super::IpMembershipRequest);
+sockopt_impl!(
+ /// Permits multiple AF_INET or AF_INET6 sockets to be bound to an
+ /// identical socket address.
+ ReusePort, Both, libc::SOL_SOCKET, libc::SO_REUSEPORT, bool);
+sockopt_impl!(
+ /// Under most circumstances, TCP sends data when it is presented; when
+ /// outstanding data has not yet been acknowledged, it gathers small amounts
+ /// of output to be sent in a single packet once an acknowledgement is
+ /// received. For a small number of clients, such as window systems that
+ /// send a stream of mouse events which receive no replies, this
+ /// packetization may cause significant delays. The boolean option
+ /// TCP_NODELAY defeats this algorithm.
+ TcpNoDelay, Both, libc::IPPROTO_TCP, libc::TCP_NODELAY, bool);
+sockopt_impl!(
+ /// When enabled, a close(2) or shutdown(2) will not return until all
+ /// queued messages for the socket have been successfully sent or the
+ /// linger timeout has been reached.
+ Linger, Both, libc::SOL_SOCKET, libc::SO_LINGER, libc::linger);
+sockopt_impl!(
+ /// Join a multicast group
+ IpAddMembership, SetOnly, libc::IPPROTO_IP, libc::IP_ADD_MEMBERSHIP,
+ super::IpMembershipRequest);
+sockopt_impl!(
+ /// Leave a multicast group.
+ IpDropMembership, SetOnly, libc::IPPROTO_IP, libc::IP_DROP_MEMBERSHIP,
+ super::IpMembershipRequest);
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
- sockopt_impl!(SetOnly, Ipv6AddMembership, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest);
- sockopt_impl!(SetOnly, Ipv6DropMembership, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest);
+ sockopt_impl!(
+ /// Join an IPv6 multicast group.
+ Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest);
+ sockopt_impl!(
+ /// Leave an IPv6 multicast group.
+ Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest);
} else if #[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
@@ -232,64 +289,185 @@ cfg_if! {
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris"))] {
- sockopt_impl!(SetOnly, Ipv6AddMembership, libc::IPPROTO_IPV6, libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest);
- sockopt_impl!(SetOnly, Ipv6DropMembership, libc::IPPROTO_IPV6, libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest);
+ sockopt_impl!(
+ /// Join an IPv6 multicast group.
+ Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6,
+ libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest);
+ sockopt_impl!(
+ /// Leave an IPv6 multicast group.
+ Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6,
+ libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest);
}
}
-sockopt_impl!(Both, IpMulticastTtl, libc::IPPROTO_IP, libc::IP_MULTICAST_TTL, u8);
-sockopt_impl!(Both, IpMulticastLoop, libc::IPPROTO_IP, libc::IP_MULTICAST_LOOP, bool);
+sockopt_impl!(
+ /// Set or read the time-to-live value of outgoing multicast packets for
+ /// this socket.
+ IpMulticastTtl, Both, libc::IPPROTO_IP, libc::IP_MULTICAST_TTL, u8);
+sockopt_impl!(
+ /// Set or read a boolean integer argument that determines whether sent
+ /// multicast packets should be looped back to the local sockets.
+ IpMulticastLoop, Both, libc::IPPROTO_IP, libc::IP_MULTICAST_LOOP, bool);
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
-sockopt_impl!(Both, IpFreebind, libc::IPPROTO_IP, libc::IP_FREEBIND, bool);
-sockopt_impl!(Both, ReceiveTimeout, libc::SOL_SOCKET, libc::SO_RCVTIMEO, TimeVal);
-sockopt_impl!(Both, SendTimeout, libc::SOL_SOCKET, libc::SO_SNDTIMEO, TimeVal);
-sockopt_impl!(Both, Broadcast, libc::SOL_SOCKET, libc::SO_BROADCAST, bool);
-sockopt_impl!(Both, OobInline, libc::SOL_SOCKET, libc::SO_OOBINLINE, bool);
-sockopt_impl!(GetOnly, SocketError, libc::SOL_SOCKET, libc::SO_ERROR, i32);
-sockopt_impl!(Both, KeepAlive, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool);
+sockopt_impl!(
+ /// If enabled, this boolean option allows binding to an IP address that
+ /// is nonlocal or does not (yet) exist.
+ IpFreebind, Both, libc::IPPROTO_IP, libc::IP_FREEBIND, bool);
+sockopt_impl!(
+ /// Specify the receiving timeout until reporting an error.
+ ReceiveTimeout, Both, libc::SOL_SOCKET, libc::SO_RCVTIMEO, TimeVal);
+sockopt_impl!(
+ /// Specify the sending timeout until reporting an error.
+ SendTimeout, Both, libc::SOL_SOCKET, libc::SO_SNDTIMEO, TimeVal);
+sockopt_impl!(
+ /// Set or get the broadcast flag.
+ Broadcast, Both, libc::SOL_SOCKET, libc::SO_BROADCAST, bool);
+sockopt_impl!(
+ /// If this option is enabled, out-of-band data is directly placed into
+ /// the receive data stream.
+ OobInline, Both, libc::SOL_SOCKET, libc::SO_OOBINLINE, bool);
+sockopt_impl!(
+ /// Get and clear the pending socket error.
+ SocketError, GetOnly, libc::SOL_SOCKET, libc::SO_ERROR, i32);
+sockopt_impl!(
+ /// Enable sending of keep-alive messages on connection-oriented sockets.
+ KeepAlive, Both, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool);
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+))]
+sockopt_impl!(
+ /// Get the credentials of the peer process of a connected unix domain
+ /// socket.
+ LocalPeerCred, GetOnly, 0, libc::LOCAL_PEERCRED, super::XuCred);
#[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(GetOnly, PeerCredentials, libc::SOL_SOCKET, libc::SO_PEERCRED, super::UnixCredentials);
+sockopt_impl!(
+ /// Return the credentials of the foreign process connected to this socket.
+ PeerCredentials, GetOnly, libc::SOL_SOCKET, libc::SO_PEERCRED, super::UnixCredentials);
#[cfg(any(target_os = "ios",
target_os = "macos"))]
-sockopt_impl!(Both, TcpKeepAlive, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, u32);
+sockopt_impl!(
+ /// Specify the amount of time, in seconds, that the connection must be idle
+ /// before keepalive probes (if enabled) are sent.
+ TcpKeepAlive, Both, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, u32);
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "nacl"))]
-sockopt_impl!(Both, TcpKeepIdle, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, u32);
+sockopt_impl!(
+ /// The time (in seconds) the connection needs to remain idle before TCP
+ /// starts sending keepalive probes
+ TcpKeepIdle, Both, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, u32);
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ sockopt_impl!(
+ /// The maximum segment size for outgoing TCP packets.
+ TcpMaxSeg, Both, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
+ } else {
+ sockopt_impl!(
+ /// The maximum segment size for outgoing TCP packets.
+ TcpMaxSeg, GetOnly, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
+ }
+}
#[cfg(not(target_os = "openbsd"))]
-sockopt_impl!(Both, TcpKeepCount, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, u32);
+sockopt_impl!(
+ /// The maximum number of keepalive probes TCP should send before
+ /// dropping the connection.
+ TcpKeepCount, Both, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, u32);
+#[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux"))]
+sockopt_impl!(
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ TcpRepair, Both, libc::IPPROTO_TCP, libc::TCP_REPAIR, u32);
#[cfg(not(target_os = "openbsd"))]
-sockopt_impl!(Both, TcpKeepInterval, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, u32);
+sockopt_impl!(
+ /// The time (in seconds) between individual keepalive probes.
+ TcpKeepInterval, Both, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, u32);
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
-sockopt_impl!(Both, TcpUserTimeout, libc::IPPROTO_TCP, libc::TCP_USER_TIMEOUT, u32);
-sockopt_impl!(Both, RcvBuf, libc::SOL_SOCKET, libc::SO_RCVBUF, usize);
-sockopt_impl!(Both, SndBuf, libc::SOL_SOCKET, libc::SO_SNDBUF, usize);
+sockopt_impl!(
+ /// Specifies the maximum amount of time in milliseconds that transmitted
+ /// data may remain unacknowledged before TCP will forcibly close the
+ /// corresponding connection
+ TcpUserTimeout, Both, libc::IPPROTO_TCP, libc::TCP_USER_TIMEOUT, u32);
+sockopt_impl!(
+ /// Sets or gets the maximum socket receive buffer in bytes.
+ RcvBuf, Both, libc::SOL_SOCKET, libc::SO_RCVBUF, usize);
+sockopt_impl!(
+ /// Sets or gets the maximum socket send buffer in bytes.
+ SndBuf, Both, libc::SOL_SOCKET, libc::SO_SNDBUF, usize);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can
+ /// perform the same task as `SO_RCVBUF`, but the `rmem_max limit` can be
+ /// overridden.
+ RcvBufForce, SetOnly, libc::SOL_SOCKET, libc::SO_RCVBUFFORCE, usize);
#[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(SetOnly, RcvBufForce, libc::SOL_SOCKET, libc::SO_RCVBUFFORCE, usize);
+sockopt_impl!(
+ /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can
+ /// perform the same task as `SO_SNDBUF`, but the `wmem_max` limit can be
+ /// overridden.
+ SndBufForce, SetOnly, libc::SOL_SOCKET, libc::SO_SNDBUFFORCE, usize);
+sockopt_impl!(
+ /// Gets the socket type as an integer.
+ SockType, GetOnly, libc::SOL_SOCKET, libc::SO_TYPE, super::SockType);
+sockopt_impl!(
+ /// Returns a value indicating whether or not this socket has been marked to
+ /// accept connections with `listen(2)`.
+ AcceptConn, GetOnly, libc::SOL_SOCKET, libc::SO_ACCEPTCONN, bool);
#[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(SetOnly, SndBufForce, libc::SOL_SOCKET, libc::SO_SNDBUFFORCE, usize);
-sockopt_impl!(GetOnly, SockType, libc::SOL_SOCKET, libc::SO_TYPE, super::SockType);
-sockopt_impl!(GetOnly, AcceptConn, libc::SOL_SOCKET, libc::SO_ACCEPTCONN, bool);
+sockopt_impl!(
+ /// Bind this socket to a particular device like “eth0”.
+ BindToDevice, Both, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, OsString<[u8; libc::IFNAMSIZ]>);
#[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(Both, BindToDevice, libc::SOL_SOCKET, libc::SO_BINDTODEVICE, OsString<[u8; libc::IFNAMSIZ]>);
+sockopt_impl!(
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ OriginalDst, GetOnly, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::sockaddr_in);
#[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(GetOnly, OriginalDst, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::sockaddr_in);
-sockopt_impl!(Both, ReceiveTimestamp, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);
+sockopt_impl!(
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ Ip6tOriginalDst, GetOnly, libc::SOL_IPV6, libc::IP6T_SO_ORIGINAL_DST, libc::sockaddr_in6);
+sockopt_impl!(
+ /// Enable or disable the receiving of the `SO_TIMESTAMP` control message.
+ ReceiveTimestamp, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool);
#[cfg(all(target_os = "linux"))]
-sockopt_impl!(Both, ReceiveTimestampns, libc::SOL_SOCKET, libc::SO_TIMESTAMPNS, bool);
+sockopt_impl!(
+ /// Enable or disable the receiving of the `SO_TIMESTAMPNS` control message.
+ ReceiveTimestampns, Both, libc::SOL_SOCKET, libc::SO_TIMESTAMPNS, bool);
#[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(Both, IpTransparent, libc::SOL_IP, libc::IP_TRANSPARENT, bool);
+sockopt_impl!(
+ /// Setting this boolean option enables transparent proxying on this socket.
+ IpTransparent, Both, libc::SOL_IP, libc::IP_TRANSPARENT, bool);
#[cfg(target_os = "openbsd")]
-sockopt_impl!(Both, BindAny, libc::SOL_SOCKET, libc::SO_BINDANY, bool);
+sockopt_impl!(
+ /// Allows the socket to be bound to addresses which are not local to the
+ /// machine, so it can be used to make a transparent proxy.
+ BindAny, Both, libc::SOL_SOCKET, libc::SO_BINDANY, bool);
#[cfg(target_os = "freebsd")]
-sockopt_impl!(Both, BindAny, libc::IPPROTO_IP, libc::IP_BINDANY, bool);
+sockopt_impl!(
+ /// Can `bind(2)` to any address, even one not bound to any available
+ /// network interface in the system.
+ BindAny, Both, libc::IPPROTO_IP, libc::IP_BINDANY, bool);
#[cfg(target_os = "linux")]
-sockopt_impl!(Both, Mark, libc::SOL_SOCKET, libc::SO_MARK, u32);
+sockopt_impl!(
+ /// Set the mark for each packet sent through this socket (similar to the
+ /// netfilter MARK target but socket-based).
+ Mark, Both, libc::SOL_SOCKET, libc::SO_MARK, u32);
#[cfg(any(target_os = "android", target_os = "linux"))]
-sockopt_impl!(Both, PassCred, libc::SOL_SOCKET, libc::SO_PASSCRED, bool);
+sockopt_impl!(
+ /// Enable or disable the receiving of the `SCM_CREDENTIALS` control
+ /// message.
+ PassCred, Both, libc::SOL_SOCKET, libc::SO_PASSCRED, bool);
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
-sockopt_impl!(Both, TcpCongestion, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsString<[u8; TCP_CA_NAME_MAX]>);
+sockopt_impl!(
+ /// This option allows the caller to set the TCP congestion control
+ /// algorithm to be used, on a per-socket basis.
+ TcpCongestion, Both, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsString<[u8; TCP_CA_NAME_MAX]>);
#[cfg(any(
target_os = "android",
target_os = "ios",
@@ -297,7 +475,10 @@ sockopt_impl!(Both, TcpCongestion, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsSt
target_os = "macos",
target_os = "netbsd",
))]
-sockopt_impl!(Both, Ipv4PacketInfo, libc::IPPROTO_IP, libc::IP_PKTINFO, bool);
+sockopt_impl!(
+ /// Pass an `IP_PKTINFO` ancillary message that contains a pktinfo
+ /// structure that supplies some information about the incoming packet.
+ Ipv4PacketInfo, Both, libc::IPPROTO_IP, libc::IP_PKTINFO, bool);
#[cfg(any(
target_os = "android",
target_os = "freebsd",
@@ -307,7 +488,10 @@ sockopt_impl!(Both, Ipv4PacketInfo, libc::IPPROTO_IP, libc::IP_PKTINFO, bool);
target_os = "netbsd",
target_os = "openbsd",
))]
-sockopt_impl!(Both, Ipv6RecvPacketInfo, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, bool);
+sockopt_impl!(
+ /// Set delivery of the `IPV6_PKTINFO` control message on incoming
+ /// datagrams.
+ Ipv6RecvPacketInfo, Both, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, bool);
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
@@ -315,7 +499,10 @@ sockopt_impl!(Both, Ipv6RecvPacketInfo, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTIN
target_os = "netbsd",
target_os = "openbsd",
))]
-sockopt_impl!(Both, Ipv4RecvIf, libc::IPPROTO_IP, libc::IP_RECVIF, bool);
+sockopt_impl!(
+ /// The `recvmsg(2)` call returns a `struct sockaddr_dl` corresponding to
+ /// the interface on which the packet was received.
+ Ipv4RecvIf, Both, libc::IPPROTO_IP, libc::IP_RECVIF, bool);
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
@@ -323,14 +510,49 @@ sockopt_impl!(Both, Ipv4RecvIf, libc::IPPROTO_IP, libc::IP_RECVIF, bool);
target_os = "netbsd",
target_os = "openbsd",
))]
-sockopt_impl!(Both, Ipv4RecvDstAddr, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, bool);
+sockopt_impl!(
+ /// The `recvmsg(2)` call will return the destination IP address for a UDP
+ /// datagram.
+ Ipv4RecvDstAddr, Both, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, bool);
#[cfg(target_os = "linux")]
-sockopt_impl!(Both, UdpGsoSegment, libc::SOL_UDP, libc::UDP_SEGMENT, libc::c_int);
+sockopt_impl!(
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ UdpGsoSegment, Both, libc::SOL_UDP, libc::UDP_SEGMENT, libc::c_int);
#[cfg(target_os = "linux")]
-sockopt_impl!(Both, UdpGroSegment, libc::IPPROTO_UDP, libc::UDP_GRO, bool);
+sockopt_impl!(
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ UdpGroSegment, Both, libc::IPPROTO_UDP, libc::UDP_GRO, bool);
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
-sockopt_impl!(Both, RxqOvfl, libc::SOL_SOCKET, libc::SO_RXQ_OVFL, libc::c_int);
-
+sockopt_impl!(
+ /// Indicates that an unsigned 32-bit value ancillary message (cmsg) should
+ /// be attached to received skbs indicating the number of packets dropped by
+ /// the socket since its creation.
+ RxqOvfl, Both, libc::SOL_SOCKET, libc::SO_RXQ_OVFL, libc::c_int);
+sockopt_impl!(
+ /// The socket is restricted to sending and receiving IPv6 packets only.
+ Ipv6V6Only, Both, libc::IPPROTO_IPV6, libc::IPV6_V6ONLY, bool);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Enable extended reliable error message passing.
+ Ipv4RecvErr, Both, libc::IPPROTO_IP, libc::IP_RECVERR, bool);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Control receiving of asynchronous error options.
+ Ipv6RecvErr, Both, libc::IPPROTO_IPV6, libc::IPV6_RECVERR, bool);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+sockopt_impl!(
+ /// Set or retrieve the current time-to-live field that is used in every
+ /// packet sent from this socket.
+ Ipv4Ttl, Both, libc::IPPROTO_IP, libc::IP_TTL, libc::c_int);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+sockopt_impl!(
+ /// Set the unicast hop limit for the socket.
+ Ipv6Ttl, Both, libc::IPPROTO_IPV6, libc::IPV6_UNICAST_HOPS, libc::c_int);
+
+#[allow(missing_docs)]
+// Not documented by Linux!
#[cfg(any(target_os = "android", target_os = "linux"))]
#[derive(Copy, Clone, Debug)]
pub struct AlgSetAeadAuthSize;
@@ -353,6 +575,8 @@ impl SetSockOpt for AlgSetAeadAuthSize {
}
}
+#[allow(missing_docs)]
+// Not documented by Linux!
#[cfg(any(target_os = "android", target_os = "linux"))]
#[derive(Clone, Debug)]
pub struct AlgSetKey<T>(::std::marker::PhantomData<T>);
@@ -387,9 +611,9 @@ impl<T> SetSockOpt for AlgSetKey<T> where T: AsRef<[u8]> + Clone {
*/
/// Helper trait that describes what is expected from a `GetSockOpt` getter.
-unsafe trait Get<T> {
+trait Get<T> {
/// Returns an uninitialized value.
- unsafe fn uninit() -> Self;
+ fn uninit() -> Self;
/// Returns a pointer to the stored value. This pointer will be passed to the system's
/// `getsockopt` call (`man 3p getsockopt`, argument `option_value`).
fn ffi_ptr(&mut self) -> *mut c_void;
@@ -401,7 +625,7 @@ unsafe trait Get<T> {
}
/// Helper trait that describes what is expected from a `SetSockOpt` setter.
-unsafe trait Set<'a, T> {
+trait Set<'a, T> {
/// Initialize the setter with a given value.
fn new(val: &'a T) -> Self;
/// Returns a pointer to the stored value. This pointer will be passed to the system's
@@ -418,8 +642,8 @@ struct GetStruct<T> {
val: MaybeUninit<T>,
}
-unsafe impl<T> Get<T> for GetStruct<T> {
- unsafe fn uninit() -> Self {
+impl<T> Get<T> for GetStruct<T> {
+ fn uninit() -> Self {
GetStruct {
len: mem::size_of::<T>() as socklen_t,
val: MaybeUninit::uninit(),
@@ -445,7 +669,7 @@ struct SetStruct<'a, T: 'static> {
ptr: &'a T,
}
-unsafe impl<'a, T> Set<'a, T> for SetStruct<'a, T> {
+impl<'a, T> Set<'a, T> for SetStruct<'a, T> {
fn new(ptr: &'a T) -> SetStruct<'a, T> {
SetStruct { ptr }
}
@@ -465,8 +689,8 @@ struct GetBool {
val: MaybeUninit<c_int>,
}
-unsafe impl Get<bool> for GetBool {
- unsafe fn uninit() -> Self {
+impl Get<bool> for GetBool {
+ fn uninit() -> Self {
GetBool {
len: mem::size_of::<c_int>() as socklen_t,
val: MaybeUninit::uninit(),
@@ -492,7 +716,7 @@ struct SetBool {
val: c_int,
}
-unsafe impl<'a> Set<'a, bool> for SetBool {
+impl<'a> Set<'a, bool> for SetBool {
fn new(val: &'a bool) -> SetBool {
SetBool { val: if *val { 1 } else { 0 } }
}
@@ -512,8 +736,8 @@ struct GetU8 {
val: MaybeUninit<u8>,
}
-unsafe impl Get<u8> for GetU8 {
- unsafe fn uninit() -> Self {
+impl Get<u8> for GetU8 {
+ fn uninit() -> Self {
GetU8 {
len: mem::size_of::<u8>() as socklen_t,
val: MaybeUninit::uninit(),
@@ -539,7 +763,7 @@ struct SetU8 {
val: u8,
}
-unsafe impl<'a> Set<'a, u8> for SetU8 {
+impl<'a> Set<'a, u8> for SetU8 {
fn new(val: &'a u8) -> SetU8 {
SetU8 { val: *val as u8 }
}
@@ -559,8 +783,8 @@ struct GetUsize {
val: MaybeUninit<c_int>,
}
-unsafe impl Get<usize> for GetUsize {
- unsafe fn uninit() -> Self {
+impl Get<usize> for GetUsize {
+ fn uninit() -> Self {
GetUsize {
len: mem::size_of::<c_int>() as socklen_t,
val: MaybeUninit::uninit(),
@@ -586,7 +810,7 @@ struct SetUsize {
val: c_int,
}
-unsafe impl<'a> Set<'a, usize> for SetUsize {
+impl<'a> Set<'a, usize> for SetUsize {
fn new(val: &'a usize) -> SetUsize {
SetUsize { val: *val as c_int }
}
@@ -606,8 +830,8 @@ struct GetOsString<T: AsMut<[u8]>> {
val: MaybeUninit<T>,
}
-unsafe impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
- unsafe fn uninit() -> Self {
+impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
+ fn uninit() -> Self {
GetOsString {
len: mem::size_of::<T>() as socklen_t,
val: MaybeUninit::uninit(),
@@ -634,7 +858,7 @@ struct SetOsString<'a> {
val: &'a OsStr,
}
-unsafe impl<'a> Set<'a, OsString> for SetOsString<'a> {
+impl<'a> Set<'a, OsString> for SetOsString<'a> {
fn new(val: &'a OsString) -> SetOsString {
SetOsString { val: val.as_os_str() }
}
diff --git a/src/sys/stat.rs b/src/sys/stat.rs
index 15451e7..c8f1041 100644
--- a/src/sys/stat.rs
+++ b/src/sys/stat.rs
@@ -9,6 +9,7 @@ use std::os::unix::io::RawFd;
use crate::sys::time::{TimeSpec, TimeVal};
libc_bitflags!(
+ /// "File type" flags for `mknod` and related functions.
pub struct SFlag: mode_t {
S_IFIFO;
S_IFCHR;
@@ -22,6 +23,7 @@ libc_bitflags!(
);
libc_bitflags! {
+ /// "File mode / permissions" flags.
pub struct Mode: mode_t {
S_IRWXU;
S_IRUSR;
@@ -41,30 +43,45 @@ libc_bitflags! {
}
}
+/// Create a special or ordinary file, by pathname.
pub fn mknod<P: ?Sized + NixPath>(path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> {
- let res = path.with_nix_path(|cstr| {
- unsafe {
- libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
- }
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Create a special or ordinary file, relative to a given directory.
+#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
+pub fn mknodat<P: ?Sized + NixPath>(
+ dirfd: RawFd,
+ path: &P,
+ kind: SFlag,
+ perm: Mode,
+ dev: dev_t,
+) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::mknodat(dirfd, cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
})?;
Errno::result(res).map(drop)
}
#[cfg(target_os = "linux")]
-pub fn major(dev: dev_t) -> u64 {
+pub const fn major(dev: dev_t) -> u64 {
((dev >> 32) & 0xffff_f000) |
((dev >> 8) & 0x0000_0fff)
}
#[cfg(target_os = "linux")]
-pub fn minor(dev: dev_t) -> u64 {
+pub const fn minor(dev: dev_t) -> u64 {
((dev >> 12) & 0xffff_ff00) |
((dev ) & 0x0000_00ff)
}
#[cfg(target_os = "linux")]
-pub fn makedev(major: u64, minor: u64) -> dev_t {
+pub const fn makedev(major: u64, minor: u64) -> dev_t {
((major & 0xffff_f000) << 32) |
((major & 0x0000_0fff) << 8) |
((minor & 0xffff_ff00) << 12) |
@@ -239,6 +256,7 @@ pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
}
/// Flags for `utimensat` function.
+// TODO: replace with fcntl::AtFlags
#[derive(Clone, Copy, Debug)]
pub enum UtimensatFlags {
FollowSymlink,
diff --git a/src/sys/statfs.rs b/src/sys/statfs.rs
index 27b7259..829be57 100644
--- a/src/sys/statfs.rs
+++ b/src/sys/statfs.rs
@@ -1,3 +1,6 @@
+//! Get filesystem statistics, non-portably
+//!
+//! See [`statvfs`](crate::sys::statvfs) for a portable alternative.
use std::fmt::{self, Debug};
use std::mem;
use std::os::unix::io::AsRawFd;
@@ -6,11 +9,14 @@ use std::ffi::CStr;
use crate::{NixPath, Result, errno::Errno};
+/// Identifies a mounted file system
#[cfg(target_os = "android")]
pub type fsid_t = libc::__fsid_t;
+/// Identifies a mounted file system
#[cfg(not(target_os = "android"))]
pub type fsid_t = libc::fsid_t;
+/// Describes a mounted file system
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Statfs(libc::statfs);
@@ -26,6 +32,7 @@ type fs_type_t = libc::c_ulong;
#[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))]
type fs_type_t = libc::__fsword_t;
+/// Describes the file system type as known by the operating system.
#[cfg(any(
target_os = "freebsd",
target_os = "android",
@@ -36,63 +43,94 @@ type fs_type_t = libc::__fsword_t;
#[derive(Eq, Copy, Clone, PartialEq, Debug)]
pub struct FsType(pub fs_type_t);
+// These constants are defined without documentation in the Linux headers, so we
+// can't very well document them here.
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const ADFS_SUPER_MAGIC: FsType = FsType(libc::ADFS_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const AFFS_SUPER_MAGIC: FsType = FsType(libc::AFFS_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const CODA_SUPER_MAGIC: FsType = FsType(libc::CODA_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const CRAMFS_MAGIC: FsType = FsType(libc::CRAMFS_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const EFS_SUPER_MAGIC: FsType = FsType(libc::EFS_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const EXT2_SUPER_MAGIC: FsType = FsType(libc::EXT2_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const EXT3_SUPER_MAGIC: FsType = FsType(libc::EXT3_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const EXT4_SUPER_MAGIC: FsType = FsType(libc::EXT4_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const HPFS_SUPER_MAGIC: FsType = FsType(libc::HPFS_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const HUGETLBFS_MAGIC: FsType = FsType(libc::HUGETLBFS_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const ISOFS_SUPER_MAGIC: FsType = FsType(libc::ISOFS_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const JFFS2_SUPER_MAGIC: FsType = FsType(libc::JFFS2_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const MINIX_SUPER_MAGIC: FsType = FsType(libc::MINIX_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const MINIX_SUPER_MAGIC2: FsType = FsType(libc::MINIX_SUPER_MAGIC2 as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const MINIX2_SUPER_MAGIC: FsType = FsType(libc::MINIX2_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const MINIX2_SUPER_MAGIC2: FsType = FsType(libc::MINIX2_SUPER_MAGIC2 as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const MSDOS_SUPER_MAGIC: FsType = FsType(libc::MSDOS_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const NCP_SUPER_MAGIC: FsType = FsType(libc::NCP_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const NFS_SUPER_MAGIC: FsType = FsType(libc::NFS_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const OPENPROM_SUPER_MAGIC: FsType = FsType(libc::OPENPROM_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const OVERLAYFS_SUPER_MAGIC: FsType = FsType(libc::OVERLAYFS_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const PROC_SUPER_MAGIC: FsType = FsType(libc::PROC_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const QNX4_SUPER_MAGIC: FsType = FsType(libc::QNX4_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const REISERFS_SUPER_MAGIC: FsType = FsType(libc::REISERFS_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const SMB_SUPER_MAGIC: FsType = FsType(libc::SMB_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const TMPFS_MAGIC: FsType = FsType(libc::TMPFS_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const USBDEVICE_SUPER_MAGIC: FsType = FsType(libc::USBDEVICE_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const CGROUP_SUPER_MAGIC: FsType = FsType(libc::CGROUP_SUPER_MAGIC as fs_type_t);
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+#[allow(missing_docs)]
pub const CGROUP2_SUPER_MAGIC: FsType = FsType(libc::CGROUP2_SUPER_MAGIC as fs_type_t);
@@ -451,6 +489,14 @@ impl Debug for Statfs {
}
}
+/// Describes a mounted file system.
+///
+/// The result is OS-dependent. For a portabable alternative, see
+/// [`statvfs`](crate::sys::statvfs::statvfs).
+///
+/// # Arguments
+///
+/// `path` - Path to any file within the file system to describe
pub fn statfs<P: ?Sized + NixPath>(path: &P) -> Result<Statfs> {
unsafe {
let mut stat = mem::MaybeUninit::<libc::statfs>::uninit();
@@ -459,6 +505,14 @@ pub fn statfs<P: ?Sized + NixPath>(path: &P) -> Result<Statfs> {
}
}
+/// Describes a mounted file system.
+///
+/// The result is OS-dependent. For a portabable alternative, see
+/// [`fstatvfs`](crate::sys::statvfs::fstatvfs).
+///
+/// # Arguments
+///
+/// `fd` - File descriptor of any open file within the file system to describe
pub fn fstatfs<T: AsRawFd>(fd: &T) -> Result<Statfs> {
unsafe {
let mut stat = mem::MaybeUninit::<libc::statfs>::uninit();
diff --git a/src/sys/statvfs.rs b/src/sys/statvfs.rs
index 508fa8d..15e7a7d 100644
--- a/src/sys/statvfs.rs
+++ b/src/sys/statvfs.rs
@@ -150,7 +150,7 @@ mod test {
#[test]
fn statvfs_call() {
- statvfs("/".as_bytes()).unwrap();
+ statvfs(&b"/"[..]).unwrap();
}
#[test]
diff --git a/src/sys/termios.rs b/src/sys/termios.rs
index 36a3601..01d4608 100644
--- a/src/sys/termios.rs
+++ b/src/sys/termios.rs
@@ -152,11 +152,11 @@
//! # }
//! ```
use cfg_if::cfg_if;
-use crate::{Error, Result};
+use crate::Result;
use crate::errno::Errno;
use libc::{self, c_int, tcflag_t};
use std::cell::{Ref, RefCell};
-use std::convert::{From, TryFrom};
+use std::convert::From;
use std::mem;
use std::os::unix::io::RawFd;
@@ -256,6 +256,7 @@ libc_enum!{
/// B0 is special and will disable the port.
#[cfg_attr(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))]
#[cfg_attr(not(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64")), repr(u32))]
+ #[non_exhaustive]
pub enum BaudRate {
B0,
B50,
@@ -339,119 +340,7 @@ libc_enum!{
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
B4000000,
}
-}
-
-impl TryFrom<libc::speed_t> for BaudRate {
- type Error = Error;
-
- fn try_from(s: libc::speed_t) -> Result<BaudRate> {
- use libc::{B0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400, B4800,
- B9600, B19200, B38400, B57600, B115200, B230400};
- #[cfg(any(target_os = "android", target_os = "linux"))]
- use libc::{B500000, B576000, B1000000, B1152000, B1500000, B2000000};
- #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
- use libc::{B2500000, B3000000, B3500000, B4000000};
- #[cfg(any(target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "macos",
- target_os = "netbsd",
- target_os = "openbsd"))]
- use libc::{B7200, B14400, B28800, B76800};
- #[cfg(any(target_os = "android",
- target_os = "freebsd",
- target_os = "linux",
- target_os = "netbsd"))]
- use libc::{B460800, B921600};
- #[cfg(any(target_os = "illumos", target_os = "solaris"))]
- use libc::{B153600, B307200, B460800, B921600};
-
- match s {
- B0 => Ok(BaudRate::B0),
- B50 => Ok(BaudRate::B50),
- B75 => Ok(BaudRate::B75),
- B110 => Ok(BaudRate::B110),
- B134 => Ok(BaudRate::B134),
- B150 => Ok(BaudRate::B150),
- B200 => Ok(BaudRate::B200),
- B300 => Ok(BaudRate::B300),
- B600 => Ok(BaudRate::B600),
- B1200 => Ok(BaudRate::B1200),
- B1800 => Ok(BaudRate::B1800),
- B2400 => Ok(BaudRate::B2400),
- B4800 => Ok(BaudRate::B4800),
- #[cfg(any(target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "macos",
- target_os = "netbsd",
- target_os = "openbsd"))]
- B7200 => Ok(BaudRate::B7200),
- B9600 => Ok(BaudRate::B9600),
- #[cfg(any(target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "macos",
- target_os = "netbsd",
- target_os = "openbsd"))]
- B14400 => Ok(BaudRate::B14400),
- B19200 => Ok(BaudRate::B19200),
- #[cfg(any(target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "macos",
- target_os = "netbsd",
- target_os = "openbsd"))]
- B28800 => Ok(BaudRate::B28800),
- B38400 => Ok(BaudRate::B38400),
- B57600 => Ok(BaudRate::B57600),
- #[cfg(any(target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "macos",
- target_os = "netbsd",
- target_os = "openbsd"))]
- B76800 => Ok(BaudRate::B76800),
- B115200 => Ok(BaudRate::B115200),
- #[cfg(any(target_os = "illumos",
- target_os = "solaris"))]
- B153600 => Ok(BaudRate::B153600),
- B230400 => Ok(BaudRate::B230400),
- #[cfg(any(target_os = "illumos",
- target_os = "solaris"))]
- B307200 => Ok(BaudRate::B307200),
- #[cfg(any(target_os = "android",
- target_os = "freebsd",
- target_os = "illumos",
- target_os = "linux",
- target_os = "netbsd",
- target_os = "solaris"))]
- B460800 => Ok(BaudRate::B460800),
- #[cfg(any(target_os = "android", target_os = "linux"))]
- B500000 => Ok(BaudRate::B500000),
- #[cfg(any(target_os = "android", target_os = "linux"))]
- B576000 => Ok(BaudRate::B576000),
- #[cfg(any(target_os = "android",
- target_os = "freebsd",
- target_os = "illumos",
- target_os = "linux",
- target_os = "netbsd",
- target_os = "solaris"))]
- B921600 => Ok(BaudRate::B921600),
- #[cfg(any(target_os = "android", target_os = "linux"))]
- B1000000 => Ok(BaudRate::B1000000),
- #[cfg(any(target_os = "android", target_os = "linux"))]
- B1152000 => Ok(BaudRate::B1152000),
- #[cfg(any(target_os = "android", target_os = "linux"))]
- B1500000 => Ok(BaudRate::B1500000),
- #[cfg(any(target_os = "android", target_os = "linux"))]
- B2000000 => Ok(BaudRate::B2000000),
- #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
- B2500000 => Ok(BaudRate::B2500000),
- #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
- B3000000 => Ok(BaudRate::B3000000),
- #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
- B3500000 => Ok(BaudRate::B3500000),
- #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
- B4000000 => Ok(BaudRate::B4000000),
- _ => Err(Error::from(Errno::EINVAL))
- }
- }
+ impl TryFrom<libc::speed_t>
}
#[cfg(any(target_os = "freebsd",
@@ -472,6 +361,7 @@ libc_enum! {
///
/// Used as an argument to `tcsetattr()`
#[repr(i32)]
+ #[non_exhaustive]
pub enum SetArg {
/// The change will occur immediately
TCSANOW,
@@ -487,6 +377,7 @@ libc_enum! {
///
/// Used as an argument to `tcflush()`.
#[repr(i32)]
+ #[non_exhaustive]
pub enum FlushArg {
/// Flush data that was received but not read
TCIFLUSH,
@@ -502,6 +393,7 @@ libc_enum! {
///
/// Used as an argument to `tcflow()`.
#[repr(i32)]
+ #[non_exhaustive]
pub enum FlowArg {
/// Suspend transmission
TCOOFF,
@@ -518,6 +410,7 @@ libc_enum! {
libc_enum! {
/// Indices into the `termios.c_cc` array for special characters.
#[repr(usize)]
+ #[non_exhaustive]
pub enum SpecialCharacterIndices {
VDISCARD,
#[cfg(any(target_os = "dragonfly",
@@ -1113,6 +1006,7 @@ pub fn tcgetsid(fd: RawFd) -> Result<Pid> {
#[cfg(test)]
mod test {
use super::*;
+ use std::convert::TryFrom;
#[test]
fn try_from() {
diff --git a/src/sys/time.rs b/src/sys/time.rs
index 7546d1b..ac42471 100644
--- a/src/sys/time.rs
+++ b/src/sys/time.rs
@@ -77,11 +77,7 @@ impl From<timespec> for TimeSpec {
impl From<Duration> for TimeSpec {
fn from(duration: Duration) -> Self {
- #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
- TimeSpec(timespec {
- tv_sec: duration.as_secs() as time_t,
- tv_nsec: duration.subsec_nanos() as timespec_tv_nsec_t
- })
+ Self::from_duration(duration)
}
}
@@ -191,13 +187,25 @@ impl TimeSpec {
}
#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
- pub fn tv_sec(&self) -> time_t {
+ pub const fn tv_sec(&self) -> time_t {
self.0.tv_sec
}
- pub fn tv_nsec(&self) -> timespec_tv_nsec_t {
+ pub const fn tv_nsec(&self) -> timespec_tv_nsec_t {
self.0.tv_nsec
}
+
+ pub const fn from_duration(duration: Duration) -> Self {
+ #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
+ TimeSpec(timespec {
+ tv_sec: duration.as_secs() as time_t,
+ tv_nsec: duration.subsec_nanos() as timespec_tv_nsec_t
+ })
+ }
+
+ pub const fn from_timespec(timespec: timespec) -> Self {
+ Self(timespec)
+ }
}
impl ops::Neg for TimeSpec {
@@ -396,11 +404,11 @@ impl TimeVal {
}
#[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
- pub fn tv_sec(&self) -> time_t {
+ pub const fn tv_sec(&self) -> time_t {
self.0.tv_sec
}
- pub fn tv_usec(&self) -> suseconds_t {
+ pub const fn tv_usec(&self) -> suseconds_t {
self.0.tv_usec
}
}
diff --git a/src/sys/timerfd.rs b/src/sys/timerfd.rs
index 44915be..705a3c4 100644
--- a/src/sys/timerfd.rs
+++ b/src/sys/timerfd.rs
@@ -58,6 +58,7 @@ libc_enum! {
/// The type of the clock used to mark the progress of the timer. For more
/// details on each kind of clock, please refer to [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html).
#[repr(i32)]
+ #[non_exhaustive]
pub enum ClockId {
CLOCK_REALTIME,
CLOCK_MONOTONIC,
@@ -87,7 +88,7 @@ bitflags! {
struct TimerSpec(libc::itimerspec);
impl TimerSpec {
- pub fn none() -> Self {
+ pub const fn none() -> Self {
Self(libc::itimerspec {
it_interval: libc::timespec {
tv_sec: 0,
@@ -256,14 +257,9 @@ impl TimerFd {
///
/// Note: If the alarm is unset, then you will wait forever.
pub fn wait(&self) -> Result<()> {
- loop {
- if let Err(e) = read(self.fd, &mut [0u8; 8]) {
- match e {
- Errno::EINTR => continue,
- _ => return Err(e),
- }
- } else {
- break;
+ while let Err(e) = read(self.fd, &mut [0u8; 8]) {
+ if e != Errno::EINTR {
+ return Err(e)
}
}
diff --git a/src/sys/uio.rs b/src/sys/uio.rs
index 48a0efd..3abcde2 100644
--- a/src/sys/uio.rs
+++ b/src/sys/uio.rs
@@ -1,5 +1,4 @@
-// Silence invalid warnings due to rust-lang/rust#16719
-#![allow(improper_ctypes)]
+//! Vectored I/O
use crate::Result;
use crate::errno::Errno;
@@ -7,12 +6,18 @@ use libc::{self, c_int, c_void, size_t, off_t};
use std::marker::PhantomData;
use std::os::unix::io::RawFd;
+/// Low-level vectored write to a raw file descriptor
+///
+/// See also [writev(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/writev.html)
pub fn writev(fd: RawFd, iov: &[IoVec<&[u8]>]) -> Result<usize> {
let res = unsafe { libc::writev(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int) };
Errno::result(res).map(|r| r as usize)
}
+/// Low-level vectored read from a raw file descriptor
+///
+/// See also [readv(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readv.html)
pub fn readv(fd: RawFd, iov: &mut [IoVec<&mut [u8]>]) -> Result<usize> {
let res = unsafe { libc::readv(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int) };
@@ -25,11 +30,7 @@ pub fn readv(fd: RawFd, iov: &mut [IoVec<&mut [u8]>]) -> Result<usize> {
/// or an error occurs. The file offset is not changed.
///
/// See also: [`writev`](fn.writev.html) and [`pwrite`](fn.pwrite.html)
-#[cfg(any(target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "linux",
- target_os = "netbsd",
- target_os = "openbsd"))]
+#[cfg(not(target_os = "redox"))]
pub fn pwritev(fd: RawFd, iov: &[IoVec<&[u8]>],
offset: off_t) -> Result<usize> {
let res = unsafe {
@@ -46,11 +47,7 @@ pub fn pwritev(fd: RawFd, iov: &[IoVec<&[u8]>],
/// changed.
///
/// See also: [`readv`](fn.readv.html) and [`pread`](fn.pread.html)
-#[cfg(any(target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "linux",
- target_os = "netbsd",
- target_os = "openbsd"))]
+#[cfg(not(target_os = "redox"))]
pub fn preadv(fd: RawFd, iov: &[IoVec<&mut [u8]>],
offset: off_t) -> Result<usize> {
let res = unsafe {
@@ -60,6 +57,10 @@ pub fn preadv(fd: RawFd, iov: &[IoVec<&mut [u8]>],
Errno::result(res).map(|r| r as usize)
}
+/// Low-level write to a file, with specified offset.
+///
+/// See also [pwrite(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html)
+// TODO: move to unistd
pub fn pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result<usize> {
let res = unsafe {
libc::pwrite(fd, buf.as_ptr() as *const c_void, buf.len() as size_t,
@@ -69,6 +70,10 @@ pub fn pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result<usize> {
Errno::result(res).map(|r| r as usize)
}
+/// Low-level write to a file, with specified offset.
+///
+/// See also [pread(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html)
+// TODO: move to unistd
pub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize>{
let res = unsafe {
libc::pread(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t,
@@ -166,11 +171,17 @@ pub fn process_vm_readv(
Errno::result(res).map(|r| r as usize)
}
+/// A vector of buffers.
+///
+/// Vectored I/O methods like [`writev`] and [`readv`] use this structure for
+/// both reading and writing. Each `IoVec` specifies the base address and
+/// length of an area in memory.
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct IoVec<T>(pub(crate) libc::iovec, PhantomData<T>);
impl<T> IoVec<T> {
+ /// View the `IoVec` as a Rust slice.
#[inline]
pub fn as_slice(&self) -> &[u8] {
use std::slice;
@@ -192,6 +203,7 @@ impl<'a> IoVec<&'a [u8]> {
}, PhantomData)
}
+ /// Create an `IoVec` from a Rust slice.
pub fn from_slice(buf: &'a [u8]) -> IoVec<&'a [u8]> {
IoVec(libc::iovec {
iov_base: buf.as_ptr() as *mut c_void,
@@ -201,6 +213,7 @@ impl<'a> IoVec<&'a [u8]> {
}
impl<'a> IoVec<&'a mut [u8]> {
+ /// Create an `IoVec` from a mutable Rust slice.
pub fn from_mut_slice(buf: &'a mut [u8]) -> IoVec<&'a mut [u8]> {
IoVec(libc::iovec {
iov_base: buf.as_ptr() as *mut c_void,
diff --git a/src/sys/utsname.rs b/src/sys/utsname.rs
index bf1a814..98edee0 100644
--- a/src/sys/utsname.rs
+++ b/src/sys/utsname.rs
@@ -1,34 +1,42 @@
+//! Get system identification
use std::mem;
use libc::{self, c_char};
use std::ffi::CStr;
use std::str::from_utf8_unchecked;
+/// Describes the running system. Return type of [`uname`].
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct UtsName(libc::utsname);
impl UtsName {
+ /// Name of the operating system implementation
pub fn sysname(&self) -> &str {
to_str(&(&self.0.sysname as *const c_char ) as *const *const c_char)
}
+ /// Network name of this machine.
pub fn nodename(&self) -> &str {
to_str(&(&self.0.nodename as *const c_char ) as *const *const c_char)
}
+ /// Release level of the operating system.
pub fn release(&self) -> &str {
to_str(&(&self.0.release as *const c_char ) as *const *const c_char)
}
+ /// Version level of the operating system.
pub fn version(&self) -> &str {
to_str(&(&self.0.version as *const c_char ) as *const *const c_char)
}
+ /// Machine hardware platform.
pub fn machine(&self) -> &str {
to_str(&(&self.0.machine as *const c_char ) as *const *const c_char)
}
}
+/// Get system identification
pub fn uname() -> UtsName {
unsafe {
let mut ret = mem::MaybeUninit::uninit();
diff --git a/src/sys/wait.rs b/src/sys/wait.rs
index 6c5c0f0..ee49e37 100644
--- a/src/sys/wait.rs
+++ b/src/sys/wait.rs
@@ -1,3 +1,4 @@
+//! Wait for a process to change status
use crate::errno::Errno;
use crate::sys::signal::Signal;
use crate::unistd::Pid;
@@ -7,9 +8,17 @@ use libc::{self, c_int};
use std::convert::TryFrom;
libc_bitflags!(
+ /// Controls the behavior of [`waitpid`].
pub struct WaitPidFlag: c_int {
+ /// Do not block when there are no processes wishing to report status.
WNOHANG;
+ /// Report the status of selected processes which are stopped due to a
+ /// [`SIGTTIN`](crate::sys::signal::Signal::SIGTTIN),
+ /// [`SIGTTOU`](crate::sys::signal::Signal::SIGTTOU),
+ /// [`SIGTSTP`](crate::sys::signal::Signal::SIGTSTP), or
+ /// [`SIGSTOP`](crate::sys::signal::Signal::SIGSTOP) signal.
WUNTRACED;
+ /// Report the status of selected processes which have terminated.
#[cfg(any(target_os = "android",
target_os = "freebsd",
target_os = "haiku",
@@ -19,7 +28,11 @@ libc_bitflags!(
target_os = "macos",
target_os = "netbsd"))]
WEXITED;
+ /// Report the status of selected processes that have continued from a
+ /// job control stop by receiving a
+ /// [`SIGCONT`](crate::sys::signal::Signal::SIGCONT) signal.
WCONTINUED;
+ /// An alias for WUNTRACED.
#[cfg(any(target_os = "android",
target_os = "freebsd",
target_os = "haiku",
@@ -45,6 +58,7 @@ libc_bitflags!(
/// Wait on all children, regardless of type
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
__WALL;
+ /// Wait for "clone" children only.
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
__WCLONE;
}
@@ -213,6 +227,9 @@ impl WaitStatus {
}
}
+/// Wait for a process to change status
+///
+/// See also [waitpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitpid.html)
pub fn waitpid<P: Into<Option<Pid>>>(pid: P, options: Option<WaitPidFlag>) -> Result<WaitStatus> {
use self::WaitStatus::*;
@@ -237,6 +254,9 @@ pub fn waitpid<P: Into<Option<Pid>>>(pid: P, options: Option<WaitPidFlag>) -> Re
}
}
+/// Wait for any child process to change status or a signal is received.
+///
+/// See also [wait(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html)
pub fn wait() -> Result<WaitStatus> {
waitpid(None, None)
}
diff --git a/src/time.rs b/src/time.rs
index 45dd26e..6275b59 100644
--- a/src/time.rs
+++ b/src/time.rs
@@ -6,7 +6,7 @@ use crate::sys::time::TimeSpec;
target_os = "android",
target_os = "emscripten",
))]
-use crate::{unistd::Pid, Error};
+use crate::unistd::Pid;
use crate::{Errno, Result};
use libc::{self, clockid_t};
use std::mem::MaybeUninit;
@@ -20,7 +20,7 @@ pub struct ClockId(clockid_t);
impl ClockId {
/// Creates `ClockId` from raw `clockid_t`
- pub fn from_raw(clk_id: clockid_t) -> Self {
+ pub const fn from_raw(clk_id: clockid_t) -> Self {
ClockId(clk_id)
}
@@ -61,7 +61,7 @@ impl ClockId {
}
/// Gets the raw `clockid_t` wrapped by `self`
- pub fn as_raw(self) -> clockid_t {
+ pub const fn as_raw(self) -> clockid_t {
self.0
}
@@ -255,6 +255,6 @@ pub fn clock_getcpuclockid(pid: Pid) -> Result<ClockId> {
let res = unsafe { clk_id.assume_init() };
Ok(ClockId::from(res))
} else {
- Err(Error::from(Errno::from_i32(ret)))
+ Err(Errno::from_i32(ret))
}
}
diff --git a/src/ucontext.rs b/src/ucontext.rs
index a5b8cc7..f2338bd 100644
--- a/src/ucontext.rs
+++ b/src/ucontext.rs
@@ -1,4 +1,3 @@
-use libc;
#[cfg(not(target_env = "musl"))]
use crate::Result;
#[cfg(not(target_env = "musl"))]
diff --git a/src/unistd.rs b/src/unistd.rs
index de3b049..2c89d77 100644
--- a/src/unistd.rs
+++ b/src/unistd.rs
@@ -180,10 +180,7 @@ impl ForkResult {
/// Return `true` if this is the child process of the `fork()`
#[inline]
pub fn is_child(self) -> bool {
- match self {
- ForkResult::Child => true,
- _ => false
- }
+ matches!(self, ForkResult::Child)
}
/// Returns `true` if this is the parent process of the `fork()`
@@ -200,14 +197,19 @@ impl ForkResult {
/// 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};
+/// ```
+/// use nix::{sys::wait::waitpid,unistd::{fork, ForkResult, write}};
///
/// match unsafe{fork()} {
/// Ok(ForkResult::Parent { child, .. }) => {
/// println!("Continuing execution in parent process, new child has pid: {}", child);
+/// waitpid(child, None).unwrap();
+/// }
+/// Ok(ForkResult::Child) => {
+/// // Unsafe to use `println!` (or `unwrap`) here. See Safety.
+/// write(libc::STDOUT_FILENO, "I'm a new child process\n".as_bytes()).ok();
+/// unsafe { libc::_exit(0) };
/// }
-/// Ok(ForkResult::Child) => println!("I'm a new child process"),
/// Err(_) => println!("Fork failed"),
/// }
/// ```
@@ -393,7 +395,7 @@ pub fn dup3(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
#[inline]
fn dup3_polyfill(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
if oldfd == newfd {
- return Err(Error::from(Errno::EINVAL));
+ return Err(Errno::EINVAL);
}
let fd = dup2(oldfd, newfd)?;
@@ -567,7 +569,7 @@ fn reserve_double_buffer_size<T>(buf: &mut Vec<T>, limit: usize) -> Result<()> {
use std::cmp::min;
if buf.capacity() >= limit {
- return Err(Error::from(Errno::ERANGE))
+ return Err(Errno::ERANGE)
}
let capacity = min(buf.capacity() * 2, limit);
@@ -610,9 +612,9 @@ pub fn getcwd() -> Result<PathBuf> {
let error = Errno::last();
// ERANGE means buffer was too small to store directory name
if error != Errno::ERANGE {
- return Err(Error::from(error));
+ return Err(error);
}
- }
+ }
// Trigger the internal buffer resizing logic.
reserve_double_buffer_size(&mut buf, PATH_MAX as usize)?;
@@ -734,7 +736,7 @@ pub fn execv<S: AsRef<CStr>>(path: &CStr, argv: &[S]) -> Result<Infallible> {
libc::execv(path.as_ptr(), args_p.as_ptr())
};
- Err(Error::from(Errno::last()))
+ Err(Errno::last())
}
@@ -759,7 +761,7 @@ pub fn execve<SA: AsRef<CStr>, SE: AsRef<CStr>>(path: &CStr, args: &[SA], env: &
libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
};
- Err(Error::from(Errno::last()))
+ Err(Errno::last())
}
/// Replace the current process image with a new one and replicate shell `PATH`
@@ -779,7 +781,7 @@ pub fn execvp<S: AsRef<CStr>>(filename: &CStr, args: &[S]) -> Result<Infallible>
libc::execvp(filename.as_ptr(), args_p.as_ptr())
};
- Err(Error::from(Errno::last()))
+ Err(Errno::last())
}
/// Replace the current process image with a new one and replicate shell `PATH`
@@ -800,7 +802,7 @@ pub fn execvpe<SA: AsRef<CStr>, SE: AsRef<CStr>>(filename: &CStr, args: &[SA], e
libc::execvpe(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
};
- Err(Error::from(Errno::last()))
+ Err(Errno::last())
}
/// Replace the current process image with a new one (see
@@ -828,7 +830,7 @@ pub fn fexecve<SA: AsRef<CStr> ,SE: AsRef<CStr>>(fd: RawFd, args: &[SA], env: &[
libc::fexecve(fd, args_p.as_ptr(), env_p.as_ptr())
};
- Err(Error::from(Errno::last()))
+ Err(Errno::last())
}
/// Execute program relative to a directory file descriptor (see
@@ -853,7 +855,7 @@ pub fn execveat<SA: AsRef<CStr>,SE: AsRef<CStr>>(dirfd: RawFd, pathname: &CStr,
args_p.as_ptr(), env_p.as_ptr(), flags);
};
- Err(Error::from(Errno::last()))
+ Err(Errno::last())
}
/// Daemonize this process by detaching from the controlling terminal (see
@@ -1073,10 +1075,10 @@ pub fn pipe() -> std::result::Result<(RawFd, RawFd), Error> {
/// The following flags are supported, and will be set atomically as the pipe is
/// created:
///
-/// `O_CLOEXEC`: Set the close-on-exec flag for the new file descriptors.
-#[cfg_attr(target_os = "linux", doc = "`O_DIRECT`: Create a pipe that performs I/O in \"packet\" mode. ")]
-#[cfg_attr(target_os = "netbsd", doc = "`O_NOSIGPIPE`: Return `EPIPE` instead of raising `SIGPIPE`. ")]
-/// `O_NONBLOCK`: Set the non-blocking flag for the ends of the pipe.
+/// - `O_CLOEXEC`: Set the close-on-exec flag for the new file descriptors.
+#[cfg_attr(target_os = "linux", doc = "- `O_DIRECT`: Create a pipe that performs I/O in \"packet\" mode.")]
+#[cfg_attr(target_os = "netbsd", doc = "- `O_NOSIGPIPE`: Return `EPIPE` instead of raising `SIGPIPE`.")]
+/// - `O_NONBLOCK`: Set the non-blocking flag for the ends of the pipe.
///
/// See also [pipe(2)](https://man7.org/linux/man-pages/man2/pipe.2.html)
#[cfg(any(target_os = "android",
@@ -1133,9 +1135,9 @@ pub fn isatty(fd: RawFd) -> Result<bool> {
} else {
match Errno::last() {
Errno::ENOTTY => Ok(false),
- err => Err(Error::from(err)),
+ err => Err(err),
}
- }
+ }
}
}
@@ -1415,6 +1417,14 @@ pub fn getgroups() -> Result<Vec<Gid>> {
// Next, get the number of groups so we can size our Vec
let ngroups = unsafe { libc::getgroups(0, ptr::null_mut()) };
+ // If there are no supplementary groups, return early.
+ // This prevents a potential buffer over-read if the number of groups
+ // increases from zero before the next call. It would return the total
+ // number of groups beyond the capacity of the buffer.
+ if ngroups == 0 {
+ return Ok(Vec::new());
+ }
+
// Now actually get the groups. We try multiple times in case the number of
// groups has changed since the first call to getgroups() and the buffer is
// now too small.
@@ -1436,7 +1446,7 @@ pub fn getgroups() -> Result<Vec<Gid>> {
// EINVAL indicates that the buffer size was too
// small, resize it up to ngroups_max as limit.
reserve_double_buffer_size(&mut groups, ngroups_max)
- .or(Err(Error::from(Errno::EINVAL)))?;
+ .or(Err(Errno::EINVAL))?;
},
Err(e) => return Err(e)
}
@@ -1530,8 +1540,7 @@ pub fn getgrouplist(user: &CStr, group: Gid) -> Result<Vec<Gid>> {
Ok(None) | Err(_) => <c_int>::max_value(),
};
use std::cmp::min;
- let mut ngroups = min(ngroups_max, 8);
- let mut groups = Vec::<Gid>::with_capacity(ngroups as usize);
+ let mut groups = Vec::<Gid>::with_capacity(min(ngroups_max, 8) as usize);
cfg_if! {
if #[cfg(any(target_os = "ios", target_os = "macos"))] {
type getgrouplist_group_t = c_int;
@@ -1541,6 +1550,7 @@ pub fn getgrouplist(user: &CStr, group: Gid) -> Result<Vec<Gid>> {
}
let gid: gid_t = group.into();
loop {
+ let mut ngroups = groups.capacity() as i32;
let ret = unsafe {
libc::getgrouplist(user.as_ptr(),
gid as getgrouplist_group_t,
@@ -1558,7 +1568,7 @@ pub fn getgrouplist(user: &CStr, group: Gid) -> Result<Vec<Gid>> {
// groups as possible, but Linux manpages do not mention this
// behavior.
reserve_double_buffer_size(&mut groups, ngroups_max as usize)
- .map_err(|_| Error::from(Errno::EINVAL))?;
+ .map_err(|_| Errno::EINVAL)?;
}
}
}
@@ -1801,6 +1811,7 @@ pub fn mkstemp<P: ?Sized + NixPath>(template: &P) -> Result<(RawFd, PathBuf)> {
/// - [unistd.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html)
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(i32)]
+#[non_exhaustive]
pub enum PathconfVar {
#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "linux",
target_os = "netbsd", target_os = "openbsd", target_os = "redox"))]
@@ -1916,7 +1927,7 @@ pub fn fpathconf(fd: RawFd, var: PathconfVar) -> Result<Option<c_long>> {
if errno::errno() == 0 {
Ok(None)
} else {
- Err(Error::from(Errno::last()))
+ Err(Errno::last())
}
} else {
Ok(Some(raw))
@@ -1955,7 +1966,7 @@ pub fn pathconf<P: ?Sized + NixPath>(path: &P, var: PathconfVar) -> Result<Optio
if errno::errno() == 0 {
Ok(None)
} else {
- Err(Error::from(Errno::last()))
+ Err(Errno::last())
}
} else {
Ok(Some(raw))
@@ -1980,6 +1991,7 @@ pub fn pathconf<P: ?Sized + NixPath>(path: &P, var: PathconfVar) -> Result<Optio
/// - [limits.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(i32)]
+#[non_exhaustive]
pub enum SysconfVar {
/// Maximum number of I/O operations in a single list I/O call supported by
/// the implementation.
@@ -2454,7 +2466,7 @@ pub fn sysconf(var: SysconfVar) -> Result<Option<c_long>> {
if errno::errno() == 0 {
Ok(None)
} else {
- Err(Error::from(Errno::last()))
+ Err(Errno::last())
}
} else {
Ok(Some(raw))
@@ -2624,7 +2636,7 @@ pub struct User {
/// Group ID
pub gid: Gid,
/// User information
- #[cfg(not(target_os = "android"))]
+ #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
pub gecos: CString,
/// Home directory
pub dir: PathBuf,
@@ -2660,7 +2672,7 @@ impl From<&libc::passwd> for User {
User {
name: CStr::from_ptr((*pw).pw_name).to_string_lossy().into_owned(),
passwd: CString::new(CStr::from_ptr((*pw).pw_passwd).to_bytes()).unwrap(),
- #[cfg(not(target_os = "android"))]
+ #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
gecos: CString::new(CStr::from_ptr((*pw).pw_gecos).to_bytes()).unwrap(),
dir: PathBuf::from(OsStr::from_bytes(CStr::from_ptr((*pw).pw_dir).to_bytes())),
shell: PathBuf::from(OsStr::from_bytes(CStr::from_ptr((*pw).pw_shell).to_bytes())),
@@ -2690,6 +2702,58 @@ impl From<&libc::passwd> for User {
}
#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl From<User> for libc::passwd {
+ fn from(u: User) -> Self {
+ let name = match CString::new(u.name) {
+ Ok(n) => n.into_raw(),
+ Err(_) => CString::new("").unwrap().into_raw(),
+ };
+ let dir = match u.dir.into_os_string().into_string() {
+ Ok(s) => CString::new(s.as_str()).unwrap().into_raw(),
+ Err(_) => CString::new("").unwrap().into_raw(),
+ };
+ let shell = match u.shell.into_os_string().into_string() {
+ Ok(s) => CString::new(s.as_str()).unwrap().into_raw(),
+ Err(_) => CString::new("").unwrap().into_raw(),
+ };
+ Self {
+ pw_name: name,
+ pw_passwd: u.passwd.into_raw(),
+ #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
+ pw_gecos: u.gecos.into_raw(),
+ pw_dir: dir,
+ pw_shell: shell,
+ pw_uid: u.uid.0,
+ pw_gid: u.gid.0,
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ pw_class: u.class.into_raw(),
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ pw_change: u.change,
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ pw_expire: u.expire,
+ #[cfg(target_os = "illumos")]
+ pw_age: CString::new("").unwrap().into_raw(),
+ #[cfg(target_os = "illumos")]
+ pw_comment: CString::new("").unwrap().into_raw(),
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ pw_fields: 0,
+ }
+ }
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
impl User {
fn from_anything<F>(f: F) -> Result<Option<Self>>
where
@@ -2721,7 +2785,7 @@ impl User {
// Trigger the internal buffer resizing logic.
reserve_double_buffer_size(&mut cbuf, buflimit)?;
} else {
- return Err(Error::from(Errno::last()));
+ return Err(Errno::last());
}
}
}
@@ -2842,7 +2906,7 @@ impl Group {
// Trigger the internal buffer resizing logic.
reserve_double_buffer_size(&mut cbuf, buflimit)?;
} else {
- return Err(Error::from(Errno::last()));
+ return Err(Errno::last());
}
}
}
@@ -2901,7 +2965,7 @@ pub fn ttyname(fd: RawFd) -> Result<PathBuf> {
let ret = unsafe { libc::ttyname_r(fd, c_buf, buf.len()) };
if ret != 0 {
- return Err(Error::from(Errno::from_i32(ret)));
+ return Err(Errno::from_i32(ret));
}
let nul = buf.iter().position(|c| *c == b'\0').unwrap();
diff --git a/test/common/mod.rs b/test/common/mod.rs
index cdc3258..84a0b4f 100644
--- a/test/common/mod.rs
+++ b/test/common/mod.rs
@@ -14,19 +14,19 @@ use cfg_if::cfg_if;
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
#[macro_export] macro_rules! require_capability {
- ($capname:ident) => {
+ ($name:expr, $capname:ident) => {
use ::caps::{Capability, CapSet, has_cap};
if !has_cap(None, CapSet::Effective, Capability::$capname)
.unwrap()
{
- skip!("Insufficient capabilities. Skipping test.");
+ skip!("{} requires capability {}. Skipping test.", $name, Capability::$capname);
}
}
}
} else if #[cfg(not(target_os = "redox"))] {
#[macro_export] macro_rules! require_capability {
- ($capname:ident) => {}
+ ($name:expr, $capname:ident) => {}
}
}
}
diff --git a/test/sys/mod.rs b/test/sys/mod.rs
index 4f5316f..e73d9b1 100644
--- a/test/sys/mod.rs
+++ b/test/sys/mod.rs
@@ -11,6 +11,8 @@ mod test_signal;
target_os = "macos",
target_os = "netbsd"))]
mod test_aio;
+#[cfg(not(target_os = "redox"))]
+mod test_mman;
#[cfg(target_os = "linux")]
mod test_signalfd;
#[cfg(not(target_os = "redox"))]
@@ -41,5 +43,5 @@ mod test_pthread;
target_os = "netbsd",
target_os = "openbsd"))]
mod test_ptrace;
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "android", target_os = "linux"))]
mod test_timerfd;
diff --git a/test/sys/test_aio.rs b/test/sys/test_aio.rs
index 3208410..80cd053 100644
--- a/test/sys/test_aio.rs
+++ b/test/sys/test_aio.rs
@@ -1,5 +1,5 @@
use libc::{c_int, c_void};
-use nix::{Error, Result};
+use nix::Result;
use nix::errno::*;
use nix::sys::aio::*;
use nix::sys::signal::{SaFlags, SigAction, sigaction, SigevNotify, SigHandler, Signal, SigSet};
@@ -16,7 +16,7 @@ use tempfile::tempfile;
fn poll_aio(aiocb: &mut Pin<Box<AioCb>>) -> Result<()> {
loop {
let err = aiocb.error();
- if err != Err(Error::from(Errno::EINPROGRESS)) { return err; };
+ if err != Err(Errno::EINPROGRESS) { return err; };
thread::sleep(time::Duration::from_millis(10));
}
}
@@ -26,7 +26,7 @@ fn poll_aio(aiocb: &mut Pin<Box<AioCb>>) -> Result<()> {
fn poll_lio(liocb: &mut LioCb, i: usize) -> Result<()> {
loop {
let err = liocb.error(i);
- if err != Err(Error::from(Errno::EINPROGRESS)) { return err; };
+ if err != Err(Errno::EINPROGRESS) { return err; };
thread::sleep(time::Duration::from_millis(10));
}
}
@@ -70,7 +70,7 @@ fn test_cancel() {
LioOpcode::LIO_NOP);
aiocb.write().unwrap();
let err = aiocb.error();
- assert!(err == Ok(()) || err == Err(Error::from(Errno::EINPROGRESS)));
+ assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
let cancelstat = aiocb.cancel();
assert!(cancelstat.is_ok());
@@ -95,7 +95,7 @@ fn test_aio_cancel_all() {
LioOpcode::LIO_NOP);
aiocb.write().unwrap();
let err = aiocb.error();
- assert!(err == Ok(()) || err == Err(Error::from(Errno::EINPROGRESS)));
+ assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
let cancelstat = aio_cancel_all(f.as_raw_fd());
assert!(cancelstat.is_ok());
@@ -182,8 +182,8 @@ fn test_aio_suspend() {
Ok(_) => ()
};
}
- if rcb.error() != Err(Error::from(Errno::EINPROGRESS)) &&
- wcb.error() != Err(Error::from(Errno::EINPROGRESS)) {
+ if rcb.error() != Err(Errno::EINPROGRESS) &&
+ wcb.error() != Err(Errno::EINPROGRESS) {
break
}
}
@@ -406,7 +406,7 @@ extern fn sigfunc(_: c_int) {
#[test]
#[cfg_attr(any(all(target_env = "musl", target_arch = "x86_64"), target_arch = "mips", target_arch = "mips64"), ignore)]
fn test_write_sigev_signal() {
- let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::SIGNAL_MTX.lock();
let sa = SigAction::new(SigHandler::Handler(sigfunc),
SaFlags::SA_RESETHAND,
SigSet::empty());
@@ -544,7 +544,7 @@ fn test_liocb_listio_nowait() {
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
#[cfg_attr(any(target_arch = "mips", target_arch = "mips64", target_env = "musl"), ignore)]
fn test_liocb_listio_signal() {
- let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::SIGNAL_MTX.lock();
const INITIAL: &[u8] = b"abcdef123456";
const WBUF: &[u8] = b"CDEF";
let mut rbuf = vec![0; 4];
diff --git a/test/sys/test_epoll.rs b/test/sys/test_epoll.rs
index 57bc484..8d44cd0 100644
--- a/test/sys/test_epoll.rs
+++ b/test/sys/test_epoll.rs
@@ -1,6 +1,5 @@
use nix::sys::epoll::{EpollCreateFlags, EpollFlags, EpollOp, EpollEvent};
use nix::sys::epoll::{epoll_create1, epoll_ctl};
-use nix::Error;
use nix::errno::Errno;
#[test]
@@ -8,11 +7,11 @@ 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::from(Errno::ENOENT));
+ assert_eq!(result.unwrap_err(), Errno::ENOENT);
let result = epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, None);
assert!(result.is_err());
- assert_eq!(result.unwrap_err(), Error::from(Errno::EINVAL));
+ assert_eq!(result.unwrap_err(), Errno::EINVAL);
}
#[test]
diff --git a/test/sys/test_inotify.rs b/test/sys/test_inotify.rs
index 121b726..137816a 100644
--- a/test/sys/test_inotify.rs
+++ b/test/sys/test_inotify.rs
@@ -1,7 +1,5 @@
use nix::sys::inotify::{AddWatchFlags,InitFlags,Inotify};
-use nix::Error;
use nix::errno::Errno;
-use tempfile;
use std::ffi::OsString;
use std::fs::{rename, File};
@@ -14,7 +12,7 @@ pub fn test_inotify() {
instance.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS).unwrap();
let events = instance.read_events();
- assert_eq!(events.unwrap_err(), Error::from(Errno::EAGAIN));
+ assert_eq!(events.unwrap_err(), Errno::EAGAIN);
File::create(tempdir.path().join("test")).unwrap();
@@ -31,7 +29,7 @@ pub fn test_inotify_multi_events() {
instance.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS).unwrap();
let events = instance.read_events();
- assert_eq!(events.unwrap_err(), Error::from(Errno::EAGAIN));
+ assert_eq!(events.unwrap_err(), Errno::EAGAIN);
File::create(tempdir.path().join("test")).unwrap();
rename(tempdir.path().join("test"), tempdir.path().join("test2")).unwrap();
diff --git a/test/sys/test_mman.rs b/test/sys/test_mman.rs
index 152fff6..a7ceedc 100644
--- a/test/sys/test_mman.rs
+++ b/test/sys/test_mman.rs
@@ -1,27 +1,24 @@
-use nix::Error;
-use nix::libc::{c_void, size_t};
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
-#[cfg(target_os = "linux")]
-use nix::sys::mman::{mremap, MRemapFlags};
-
#[test]
fn test_mmap_anonymous() {
- let ref mut byte = unsafe {
+ unsafe {
let ptr = mmap(std::ptr::null_mut(), 1,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS, -1, 0)
- .unwrap();
- *(ptr as * mut u8)
- };
- assert_eq !(*byte, 0x00u8);
- *byte = 0xffu8;
- assert_eq !(*byte, 0xffu8);
+ .unwrap() as *mut u8;
+ assert_eq !(*ptr, 0x00u8);
+ *ptr = 0xffu8;
+ assert_eq !(*ptr, 0xffu8);
+ }
}
#[test]
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
fn test_mremap_grow() {
+ use nix::sys::mman::{mremap, MRemapFlags};
+ use nix::libc::{c_void, size_t};
+
const ONE_K : size_t = 1024;
let slice : &mut[u8] = unsafe {
let mem = mmap(std::ptr::null_mut(), ONE_K,
@@ -35,9 +32,14 @@ fn test_mremap_grow() {
assert_eq !(slice[ONE_K - 1], 0xFF);
let slice : &mut[u8] = unsafe {
+ #[cfg(target_os = "linux")]
let mem = mremap(slice.as_mut_ptr() as * mut c_void, ONE_K, 10 * ONE_K,
MRemapFlags::MREMAP_MAYMOVE, None)
.unwrap();
+ #[cfg(target_os = "netbsd")]
+ let mem = mremap(slice.as_mut_ptr() as * mut c_void, ONE_K, 10 * ONE_K,
+ MRemapFlags::MAP_REMAPDUP, None)
+ .unwrap();
std::slice::from_raw_parts_mut(mem as * mut u8, 10 * ONE_K)
};
@@ -51,8 +53,13 @@ fn test_mremap_grow() {
}
#[test]
-#[cfg(target_os = "linux")]
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
+// Segfaults for unknown reasons under QEMU for 32-bit targets
+#[cfg_attr(all(target_pointer_width = "32", qemu), ignore)]
fn test_mremap_shrink() {
+ use nix::sys::mman::{mremap, MRemapFlags};
+ use nix::libc::{c_void, size_t};
+
const ONE_K : size_t = 1024;
let slice : &mut[u8] = unsafe {
let mem = mmap(std::ptr::null_mut(), 10 * ONE_K,
@@ -66,11 +73,16 @@ fn test_mremap_shrink() {
assert_eq !(slice[ONE_K - 1], 0xFF);
let slice : &mut[u8] = unsafe {
+ #[cfg(target_os = "linux")]
let mem = mremap(slice.as_mut_ptr() as * mut c_void, 10 * ONE_K, ONE_K,
MRemapFlags::empty(), None)
.unwrap();
// Since we didn't supply MREMAP_MAYMOVE, the address should be the
// same.
+ #[cfg(target_os = "netbsd")]
+ let mem = mremap(slice.as_mut_ptr() as * mut c_void, 10 * ONE_K, ONE_K,
+ MRemapFlags::MAP_FIXED, None)
+ .unwrap();
assert_eq !(mem, slice.as_mut_ptr() as * mut c_void);
std::slice::from_raw_parts_mut(mem as * mut u8, ONE_K)
};
diff --git a/test/sys/test_pthread.rs b/test/sys/test_pthread.rs
index 1fc3dd9..fa9b510 100644
--- a/test/sys/test_pthread.rs
+++ b/test/sys/test_pthread.rs
@@ -13,3 +13,10 @@ fn test_pthread_self() {
let tid = pthread_self();
assert!(tid != 0);
}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_pthread_kill_none() {
+ pthread_kill(pthread_self(), None)
+ .expect("Should be able to send signal to my thread.");
+}
diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs
index 985945d..83fff9a 100644
--- a/test/sys/test_ptrace.rs
+++ b/test/sys/test_ptrace.rs
@@ -13,7 +13,7 @@ use crate::*;
fn test_ptrace() {
// Just make sure ptrace can be called at all, for now.
// FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS
- require_capability!(CAP_SYS_PTRACE);
+ require_capability!("test_ptrace", CAP_SYS_PTRACE);
let err = ptrace::attach(getpid()).unwrap_err();
assert!(err == Errno::EPERM || err == Errno::EINVAL ||
err == Errno::ENOSYS);
@@ -23,7 +23,7 @@ fn test_ptrace() {
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptrace_setoptions() {
- require_capability!(CAP_SYS_PTRACE);
+ require_capability!("test_ptrace_setoptions", CAP_SYS_PTRACE);
let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD).unwrap_err();
assert!(err != Errno::EOPNOTSUPP);
}
@@ -32,7 +32,7 @@ fn test_ptrace_setoptions() {
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptrace_getevent() {
- require_capability!(CAP_SYS_PTRACE);
+ require_capability!("test_ptrace_getevent", CAP_SYS_PTRACE);
let err = ptrace::getevent(getpid()).unwrap_err();
assert!(err != Errno::EOPNOTSUPP);
}
@@ -41,7 +41,7 @@ fn test_ptrace_getevent() {
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptrace_getsiginfo() {
- require_capability!(CAP_SYS_PTRACE);
+ require_capability!("test_ptrace_getsiginfo", CAP_SYS_PTRACE);
if let Err(Errno::EOPNOTSUPP) = ptrace::getsiginfo(getpid()) {
panic!("ptrace_getsiginfo returns Errno::EOPNOTSUPP!");
}
@@ -51,7 +51,7 @@ fn test_ptrace_getsiginfo() {
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptrace_setsiginfo() {
- require_capability!(CAP_SYS_PTRACE);
+ require_capability!("test_ptrace_setsiginfo", CAP_SYS_PTRACE);
let siginfo = unsafe { mem::zeroed() };
if let Err(Errno::EOPNOTSUPP) = ptrace::setsiginfo(getpid(), &siginfo) {
panic!("ptrace_setsiginfo returns Errno::EOPNOTSUPP!");
@@ -67,9 +67,9 @@ fn test_ptrace_cont() {
use nix::unistd::fork;
use nix::unistd::ForkResult::*;
- require_capability!(CAP_SYS_PTRACE);
+ require_capability!("test_ptrace_cont", CAP_SYS_PTRACE);
- let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::FORK_MTX.lock();
// FIXME: qemu-user doesn't implement ptrace on all architectures
// and retunrs ENOSYS in this case.
@@ -114,6 +114,48 @@ fn test_ptrace_cont() {
}
}
+#[cfg(target_os = "linux")]
+#[test]
+fn test_ptrace_interrupt() {
+ use nix::sys::ptrace;
+ use nix::sys::signal::Signal;
+ use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
+ use nix::unistd::fork;
+ use nix::unistd::ForkResult::*;
+ use std::thread::sleep;
+ use std::time::Duration;
+
+ require_capability!("test_ptrace_interrupt", CAP_SYS_PTRACE);
+
+ let _m = crate::FORK_MTX.lock();
+
+ match unsafe{fork()}.expect("Error: Fork Failed") {
+ Child => {
+ loop {
+ sleep(Duration::from_millis(1000));
+ }
+
+ },
+ Parent { child } => {
+ ptrace::seize(child, ptrace::Options::PTRACE_O_TRACESYSGOOD).unwrap();
+ ptrace::interrupt(child).unwrap();
+ assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, Signal::SIGTRAP, 128)));
+ ptrace::syscall(child, None).unwrap();
+ assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
+ ptrace::detach(child, Some(Signal::SIGKILL)).unwrap();
+ match waitpid(child, None) {
+ Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => {
+ let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
+ while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() {
+ let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
+ }
+ }
+ _ => panic!("The process should have been killed"),
+ }
+ },
+ }
+}
+
// ptrace::{setoptions, getregs} are only available in these platforms
#[cfg(all(target_os = "linux",
any(target_arch = "x86_64",
@@ -129,9 +171,9 @@ fn test_ptrace_syscall() {
use nix::unistd::getpid;
use nix::unistd::ForkResult::*;
- require_capability!(CAP_SYS_PTRACE);
+ require_capability!("test_ptrace_syscall", CAP_SYS_PTRACE);
- let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::FORK_MTX.lock();
match unsafe{fork()}.expect("Error: Fork Failed") {
Child => {
diff --git a/test/sys/test_select.rs b/test/sys/test_select.rs
index 3795108..2f7396b 100644
--- a/test/sys/test_select.rs
+++ b/test/sys/test_select.rs
@@ -5,9 +5,7 @@ use nix::sys::time::{TimeSpec, TimeValLike};
#[test]
pub fn test_pselect() {
- let _mtx = crate::SIGNAL_MTX
- .lock()
- .expect("Mutex got poisoned by another test");
+ let _mtx = crate::SIGNAL_MTX.lock();
let (r1, w1) = pipe().unwrap();
write(w1, b"hi!").unwrap();
@@ -52,3 +50,31 @@ pub fn test_pselect_nfds2() {
assert!(fd_set.contains(r1));
assert!(!fd_set.contains(r2));
}
+
+macro_rules! generate_fdset_bad_fd_tests {
+ ($fd:expr, $($method:ident),* $(,)?) => {
+ $(
+ #[test]
+ #[should_panic]
+ fn $method() {
+ FdSet::new().$method($fd);
+ }
+ )*
+ }
+}
+
+mod test_fdset_negative_fd {
+ use super::*;
+ generate_fdset_bad_fd_tests!(-1, insert, remove, contains);
+}
+
+mod test_fdset_too_large_fd {
+ use super::*;
+ use std::convert::TryInto;
+ generate_fdset_bad_fd_tests!(
+ FD_SETSIZE.try_into().unwrap(),
+ insert,
+ remove,
+ contains,
+ );
+}
diff --git a/test/sys/test_signal.rs b/test/sys/test_signal.rs
index 1b89af5..fdd2568 100644
--- a/test/sys/test_signal.rs
+++ b/test/sys/test_signal.rs
@@ -19,7 +19,7 @@ fn test_killpg_none() {
#[test]
fn test_old_sigaction_flags() {
- let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::SIGNAL_MTX.lock();
extern "C" fn handler(_: ::libc::c_int) {}
let act = SigAction::new(
@@ -41,7 +41,7 @@ fn test_sigprocmask_noop() {
#[test]
fn test_sigprocmask() {
- let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::SIGNAL_MTX.lock();
// This needs to be a signal that rust doesn't use in the test harness.
const SIGNAL: Signal = Signal::SIGCHLD;
@@ -89,7 +89,7 @@ extern fn test_sigaction_action(_: libc::c_int, _: *mut libc::siginfo_t, _: *mut
#[test]
#[cfg(not(target_os = "redox"))]
fn test_signal_sigaction() {
- let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::SIGNAL_MTX.lock();
let action_handler = SigHandler::SigAction(test_sigaction_action);
assert_eq!(unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(), Errno::ENOTSUP);
@@ -97,7 +97,7 @@ fn test_signal_sigaction() {
#[test]
fn test_signal() {
- let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::SIGNAL_MTX.lock();
unsafe { signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap();
raise(Signal::SIGINT).unwrap();
diff --git a/test/sys/test_signalfd.rs b/test/sys/test_signalfd.rs
index af04c22..b6f748b 100644
--- a/test/sys/test_signalfd.rs
+++ b/test/sys/test_signalfd.rs
@@ -6,7 +6,7 @@ fn test_signalfd() {
use nix::sys::signal::{self, raise, Signal, SigSet};
// Grab the mutex for altering signals so we don't interfere with other tests.
- let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::SIGNAL_MTX.lock();
// Block the SIGUSR1 signal from automatic processing for this thread
let mut mask = SigSet::empty();
diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs
index 5471afe..0f6fac6 100644
--- a/test/sys/test_socket.rs
+++ b/test/sys/test_socket.rs
@@ -1,12 +1,13 @@
-use nix::sys::socket::{AddressFamily, InetAddr, UnixAddr, getsockname};
+use nix::sys::socket::{AddressFamily, InetAddr, SockAddr, UnixAddr, getsockname, sockaddr, sockaddr_in6, sockaddr_storage_to_addr};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
+use std::mem::{self, MaybeUninit};
use std::net::{self, Ipv6Addr, SocketAddr, SocketAddrV6};
use std::os::unix::io::RawFd;
use std::path::Path;
use std::slice;
use std::str::FromStr;
-use libc::c_char;
+use libc::{c_char, sockaddr_storage};
#[cfg(any(target_os = "linux", target_os= "android"))]
use crate::*;
@@ -27,13 +28,36 @@ pub fn test_inetv4_addr_to_sock_addr() {
_ => panic!("nope"),
}
- assert_eq!(addr.to_str(), "127.0.0.1:3000");
+ assert_eq!(addr.to_string(), "127.0.0.1:3000");
let inet = addr.to_std();
assert_eq!(actual, inet);
}
#[test]
+pub fn test_inetv4_addr_roundtrip_sockaddr_storage_to_addr() {
+ let actual: net::SocketAddr = FromStr::from_str("127.0.0.1:3000").unwrap();
+ let addr = InetAddr::from_std(&actual);
+ let sockaddr = SockAddr::new_inet(addr);
+
+ let (storage, ffi_size) = {
+ let mut storage = MaybeUninit::<sockaddr_storage>::zeroed();
+ let storage_ptr = storage.as_mut_ptr().cast::<sockaddr>();
+ let (ffi_ptr, ffi_size) = sockaddr.as_ffi_pair();
+ assert_eq!(mem::size_of::<sockaddr>(), ffi_size as usize);
+ unsafe {
+ storage_ptr.copy_from_nonoverlapping(ffi_ptr as *const sockaddr, 1);
+ (storage.assume_init(), ffi_size)
+ }
+ };
+
+ let from_storage = sockaddr_storage_to_addr(&storage, ffi_size as usize).unwrap();
+ assert_eq!(from_storage, sockaddr);
+ let from_storage = sockaddr_storage_to_addr(&storage, mem::size_of::<sockaddr_storage>()).unwrap();
+ assert_eq!(from_storage, sockaddr);
+}
+
+#[test]
pub fn test_inetv6_addr_to_sock_addr() {
let port: u16 = 3000;
let flowinfo: u32 = 1;
@@ -54,6 +78,33 @@ pub fn test_inetv6_addr_to_sock_addr() {
assert_eq!(actual, addr.to_std());
}
+#[test]
+pub fn test_inetv6_addr_roundtrip_sockaddr_storage_to_addr() {
+ let port: u16 = 3000;
+ let flowinfo: u32 = 1;
+ let scope_id: u32 = 2;
+ let ip: Ipv6Addr = "fe80::1".parse().unwrap();
+
+ let actual = SocketAddr::V6(SocketAddrV6::new(ip, port, flowinfo, scope_id));
+ let addr = InetAddr::from_std(&actual);
+ let sockaddr = SockAddr::new_inet(addr);
+
+ let (storage, ffi_size) = {
+ let mut storage = MaybeUninit::<sockaddr_storage>::zeroed();
+ let storage_ptr = storage.as_mut_ptr().cast::<sockaddr_in6>();
+ let (ffi_ptr, ffi_size) = sockaddr.as_ffi_pair();
+ assert_eq!(mem::size_of::<sockaddr_in6>(), ffi_size as usize);
+ unsafe {
+ storage_ptr.copy_from_nonoverlapping((ffi_ptr as *const sockaddr).cast::<sockaddr_in6>(), 1);
+ (storage.assume_init(), ffi_size)
+ }
+ };
+
+ let from_storage = sockaddr_storage_to_addr(&storage, ffi_size as usize).unwrap();
+ assert_eq!(from_storage, sockaddr);
+ let from_storage = sockaddr_storage_to_addr(&storage, mem::size_of::<sockaddr_storage>()).unwrap();
+ assert_eq!(from_storage, sockaddr);
+}
#[test]
pub fn test_path_to_sock_addr() {
@@ -62,9 +113,9 @@ pub fn test_path_to_sock_addr() {
let addr = UnixAddr::new(actual).unwrap();
let expect: &[c_char] = unsafe {
- slice::from_raw_parts(path.as_bytes().as_ptr() as *const c_char, path.len())
+ slice::from_raw_parts(path.as_ptr() as *const c_char, path.len())
};
- assert_eq!(&addr.0.sun_path[..8], expect);
+ assert_eq!(unsafe { &(*addr.as_ptr()).sun_path[..8] }, expect);
assert_eq!(addr.path(), Some(actual));
}
@@ -82,7 +133,7 @@ pub fn test_addr_equality_path() {
let addr1 = UnixAddr::new(actual).unwrap();
let mut addr2 = addr1;
- addr2.0.sun_path[10] = 127;
+ unsafe { (*addr2.as_mut_ptr()).sun_path[10] = 127 };
assert_eq!(addr1, addr2);
assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
@@ -101,12 +152,12 @@ pub fn test_abstract_sun_path_too_long() {
pub fn test_addr_equality_abstract() {
let name = String::from("nix\0abstract\0test");
let addr1 = UnixAddr::new_abstract(name.as_bytes()).unwrap();
- let mut addr2 = addr1.clone();
+ let mut addr2 = addr1;
assert_eq!(addr1, addr2);
assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
- addr2.0.sun_path[17] = 127;
+ unsafe { (*addr2.as_mut_ptr()).sun_path[17] = 127 };
assert_ne!(addr1, addr2);
assert_ne!(calculate_hash(&addr1), calculate_hash(&addr2));
}
@@ -129,7 +180,7 @@ pub fn test_abstract_uds_addr() {
assert_eq!(addr.path(), None);
// Internally, name is null-prefixed (abstract namespace)
- assert_eq!(addr.0.sun_path[0], 0);
+ assert_eq!(unsafe { (*addr.as_ptr()).sun_path[0] }, 0);
}
#[test]
@@ -143,8 +194,7 @@ pub fn test_getsockname() {
.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());
+ assert_eq!(sockaddr, getsockname(sock).expect("getsockname failed"));
}
#[test]
@@ -237,9 +287,9 @@ mod recvfrom {
use nix::sys::socket::sockopt::{UdpGroSegment, UdpGsoSegment};
#[test]
- // Disable the test on emulated platforms because it fails in Cirrus-CI. Lack of QEMU
- // support is suspected.
- #[cfg_attr(not(any(target_arch = "x86_64", target_arch="i686")), ignore)]
+ // Disable the test under emulation because it fails in Cirrus-CI. Lack
+ // of QEMU support is suspected.
+ #[cfg_attr(qemu, ignore)]
pub fn gso() {
require_kernel_version!(udp_offload::gso, ">= 4.18");
@@ -291,9 +341,9 @@ mod recvfrom {
}
#[test]
- // Disable the test on emulated platforms because it fails in Cirrus-CI. Lack of QEMU
- // support is suspected.
- #[cfg_attr(not(any(target_arch = "x86_64", target_arch="i686")), ignore)]
+ // Disable the test on emulated platforms because it fails in Cirrus-CI.
+ // Lack of QEMU support is suspected.
+ #[cfg_attr(qemu, ignore)]
pub fn gro() {
require_kernel_version!(udp_offload::gro, ">= 5.3");
@@ -317,7 +367,6 @@ mod recvfrom {
target_os = "freebsd",
target_os = "netbsd",
))]
- #[allow(clippy::vec_init_then_push)]
#[test]
pub fn udp_sendmmsg() {
use nix::sys::uio::IoVec;
@@ -344,14 +393,14 @@ mod recvfrom {
let from = sendrecv(rsock, ssock, move |s, m, flags| {
let iov = [IoVec::from_slice(m)];
- let mut msgs = Vec::new();
- msgs.push(
+ let mut msgs = vec![
SendMmsgData {
iov: &iov,
cmsgs: &[],
addr: Some(sock_addr),
_lt: Default::default(),
- });
+ }
+ ];
let batch_size = 15;
@@ -532,7 +581,7 @@ pub fn test_recvmsg_ebadf() {
// Disable the test on emulated platforms due to a bug in QEMU versions <
// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808
-#[cfg_attr(not(any(target_arch = "x86_64", target_arch="i686")), ignore)]
+#[cfg_attr(qemu, ignore)]
#[test]
pub fn test_scm_rights() {
use nix::sys::uio::IoVec;
@@ -586,11 +635,10 @@ pub fn test_scm_rights() {
}
// Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross
-#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "i686")), ignore)]
#[cfg(any(target_os = "linux", target_os= "android"))]
+#[cfg_attr(qemu, ignore)]
#[test]
pub fn test_af_alg_cipher() {
- use libc;
use nix::sys::uio::IoVec;
use nix::unistd::read;
use nix::sys::socket::{socket, sendmsg, bind, accept, setsockopt,
@@ -654,12 +702,14 @@ pub fn test_af_alg_cipher() {
assert_eq!(decrypted, payload);
}
-// Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross
-#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "i686")), ignore)]
+// Disable the test on emulated platforms due to not enabled support of AF_ALG
+// in QEMU from rust cross
#[cfg(any(target_os = "linux", target_os= "android"))]
+#[cfg_attr(qemu, ignore)]
#[test]
pub fn test_af_alg_aead() {
use libc::{ALG_OP_DECRYPT, ALG_OP_ENCRYPT};
+ use nix::fcntl::{fcntl, FcntlArg, OFlag};
use nix::sys::uio::IoVec;
use nix::unistd::{read, close};
use nix::sys::socket::{socket, sendmsg, bind, accept, setsockopt,
@@ -738,6 +788,11 @@ pub fn test_af_alg_aead() {
// allocate buffer for decrypted data
let mut decrypted = vec![0u8; payload_len + (assoc_size as usize) + auth_size];
+ // Starting with kernel 4.9, the interface changed slightly such that the
+ // authentication tag memory is only needed in the output buffer for encryption
+ // and in the input buffer for decryption.
+ // Do not block on read, as we may have fewer bytes than buffer size
+ fcntl(session_socket,FcntlArg::F_SETFL(OFlag::O_NONBLOCK)).expect("fcntl non_blocking");
let num_bytes = read(session_socket, &mut decrypted).expect("read decrypt");
assert!(num_bytes >= payload_len + (assoc_size as usize));
@@ -780,7 +835,7 @@ pub fn test_sendmsg_ipv4packetinfo() {
if let InetAddr::V4(sin) = inet_addr {
cfg_if! {
if #[cfg(target_os = "netbsd")] {
- drop(sin);
+ let _dontcare = sin;
let pi = libc::in_pktinfo {
ipi_ifindex: 0, /* Unspecified interface */
ipi_addr: libc::in_addr { s_addr: 0 },
@@ -859,7 +914,7 @@ pub fn test_sendmsg_ipv6packetinfo() {
/// Tests that passing multiple fds using a single `ControlMessage` works.
// Disable the test on emulated platforms due to a bug in QEMU versions <
// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808
-#[cfg_attr(not(any(target_arch = "x86_64", target_arch="i686")), ignore)]
+#[cfg_attr(qemu, ignore)]
#[test]
fn test_scm_rights_single_cmsg_multiple_fds() {
use std::os::unix::net::UnixDatagram;
@@ -868,7 +923,6 @@ fn test_scm_rights_single_cmsg_multiple_fds() {
use nix::sys::socket::{ControlMessage, ControlMessageOwned, MsgFlags,
sendmsg, recvmsg};
use nix::sys::uio::IoVec;
- use libc;
let (send, receive) = UnixDatagram::pair().unwrap();
let thread = thread::spawn(move || {
@@ -1006,13 +1060,11 @@ fn test_scm_credentials() {
/// Ensure that we can send `SCM_CREDENTIALS` and `SCM_RIGHTS` with a single
/// `sendmsg` call.
#[cfg(any(target_os = "android", target_os = "linux"))]
-// qemu's handling of multiple cmsgs is bugged, ignore tests on non-x86
+// qemu's handling of multiple cmsgs is bugged, ignore tests under emulation
// see https://bugs.launchpad.net/qemu/+bug/1781280
-#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)]
+#[cfg_attr(qemu, ignore)]
#[test]
fn test_scm_credentials_and_rights() {
- use libc;
-
let space = cmsg_space!(libc::ucred, RawFd);
test_impl_scm_credentials_and_rights(space);
}
@@ -1020,9 +1072,9 @@ fn test_scm_credentials_and_rights() {
/// Ensure that passing a an oversized control message buffer to recvmsg
/// still works.
#[cfg(any(target_os = "android", target_os = "linux"))]
-// qemu's handling of multiple cmsgs is bugged, ignore tests on non-x86
+// qemu's handling of multiple cmsgs is bugged, ignore tests under emulation
// see https://bugs.launchpad.net/qemu/+bug/1781280
-#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)]
+#[cfg_attr(qemu, ignore)]
#[test]
fn test_too_large_cmsgspace() {
let space = vec![0u8; 1024];
@@ -1169,7 +1221,6 @@ fn loopback_address(family: AddressFamily) -> Option<nix::ifaddrs::InterfaceAddr
use std::io;
use std::io::Write;
use nix::ifaddrs::getifaddrs;
- use nix::sys::socket::SockAddr;
use nix::net::if_::*;
let addrs = match getifaddrs() {
@@ -1212,14 +1263,16 @@ fn loopback_address(family: AddressFamily) -> Option<nix::ifaddrs::InterfaceAddr
target_os = "netbsd",
))]
// qemu doesn't seem to be emulating this correctly in these architectures
-#[cfg_attr(any(
- target_arch = "mips",
- target_arch = "mips64",
- target_arch = "powerpc64",
+#[cfg_attr(all(
+ qemu,
+ any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc64",
+ )
), ignore)]
#[test]
pub fn test_recv_ipv4pktinfo() {
- use libc;
use nix::sys::socket::sockopt::Ipv4PacketInfo;
use nix::sys::socket::{bind, SockFlag, SockType};
use nix::sys::socket::{getsockname, setsockopt, socket};
@@ -1272,18 +1325,15 @@ pub fn test_recv_ipv4pktinfo() {
);
let mut cmsgs = msg.cmsgs();
- match cmsgs.next() {
- Some(ControlMessageOwned::Ipv4PacketInfo(pktinfo)) => {
- let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
- assert_eq!(
- pktinfo.ipi_ifindex as libc::c_uint,
- i,
- "unexpected ifindex (expected {}, got {})",
- i,
- pktinfo.ipi_ifindex
- );
- }
- _ => (),
+ if let Some(ControlMessageOwned::Ipv4PacketInfo(pktinfo)) = cmsgs.next() {
+ let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
+ assert_eq!(
+ pktinfo.ipi_ifindex as libc::c_uint,
+ i,
+ "unexpected ifindex (expected {}, got {})",
+ i,
+ pktinfo.ipi_ifindex
+ );
}
assert!(cmsgs.next().is_none(), "unexpected additional control msg");
assert_eq!(msg.bytes, 8);
@@ -1302,14 +1352,16 @@ pub fn test_recv_ipv4pktinfo() {
target_os = "openbsd",
))]
// qemu doesn't seem to be emulating this correctly in these architectures
-#[cfg_attr(any(
- target_arch = "mips",
- target_arch = "mips64",
- target_arch = "powerpc64",
+#[cfg_attr(all(
+ qemu,
+ any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc64",
+ )
), ignore)]
#[test]
pub fn test_recvif() {
- use libc;
use nix::net::if_::*;
use nix::sys::socket::sockopt::{Ipv4RecvIf, Ipv4RecvDstAddr};
use nix::sys::socket::{bind, SockFlag, SockType};
@@ -1413,14 +1465,16 @@ pub fn test_recvif() {
target_os = "openbsd",
))]
// qemu doesn't seem to be emulating this correctly in these architectures
-#[cfg_attr(any(
- target_arch = "mips",
- target_arch = "mips64",
- target_arch = "powerpc64",
+#[cfg_attr(all(
+ qemu,
+ any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc64",
+ )
), ignore)]
#[test]
pub fn test_recv_ipv6pktinfo() {
- use libc;
use nix::net::if_::*;
use nix::sys::socket::sockopt::Ipv6RecvPacketInfo;
use nix::sys::socket::{bind, SockFlag, SockType};
@@ -1494,9 +1548,9 @@ pub fn test_recv_ipv6pktinfo() {
}
#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(graviton, ignore = "Not supported by the CI environment")]
#[test]
pub fn test_vsock() {
- use libc;
use nix::errno::Errno;
use nix::sys::socket::{AddressFamily, socket, bind, connect, listen,
SockAddr, SockType, SockFlag};
@@ -1538,9 +1592,9 @@ pub fn test_vsock() {
thr.join().unwrap();
}
-// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack of QEMU
-// support is suspected.
-#[cfg_attr(not(any(target_arch = "x86_64")), ignore)]
+// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
#[cfg(all(target_os = "linux"))]
#[test]
fn test_recvmsg_timestampns() {
@@ -1589,9 +1643,9 @@ fn test_recvmsg_timestampns() {
nix::unistd::close(in_socket).unwrap();
}
-// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack of QEMU
-// support is suspected.
-#[cfg_attr(not(any(target_arch = "x86_64")), ignore)]
+// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
#[cfg(all(target_os = "linux"))]
#[test]
fn test_recvmmsg_timestampns() {
@@ -1646,9 +1700,9 @@ fn test_recvmmsg_timestampns() {
nix::unistd::close(in_socket).unwrap();
}
-// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack of QEMU
-// support is suspected.
-#[cfg_attr(not(any(target_arch = "x86_64")), ignore)]
+// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[test]
fn test_recvmsg_rxq_ovfl() {
@@ -1728,3 +1782,160 @@ fn test_recvmsg_rxq_ovfl() {
nix::unistd::close(in_socket).unwrap();
nix::unistd::close(out_socket).unwrap();
}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+))]
+mod linux_errqueue {
+ use nix::sys::socket::*;
+ use super::{FromStr, SocketAddr};
+
+ // Send a UDP datagram to a bogus destination address and observe an ICMP error (v4).
+ //
+ // Disable the test on QEMU because QEMU emulation of IP_RECVERR is broken (as documented on PR
+ // #1514).
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_recverr_v4() {
+ #[repr(u8)]
+ enum IcmpTypes {
+ DestUnreach = 3, // ICMP_DEST_UNREACH
+ }
+ #[repr(u8)]
+ enum IcmpUnreachCodes {
+ PortUnreach = 3, // ICMP_PORT_UNREACH
+ }
+
+ test_recverr_impl::<sockaddr_in, _, _>(
+ "127.0.0.1:6800",
+ AddressFamily::Inet,
+ sockopt::Ipv4RecvErr,
+ libc::SO_EE_ORIGIN_ICMP,
+ IcmpTypes::DestUnreach as u8,
+ IcmpUnreachCodes::PortUnreach as u8,
+ // Closure handles protocol-specific testing and returns generic sock_extended_err for
+ // protocol-independent test impl.
+ |cmsg| {
+ if let ControlMessageOwned::Ipv4RecvErr(ext_err, err_addr) = cmsg {
+ if let Some(origin) = err_addr {
+ // Validate that our network error originated from 127.0.0.1:0.
+ assert_eq!(origin.sin_family, AddressFamily::Inet as _);
+ assert_eq!(Ipv4Addr(origin.sin_addr), Ipv4Addr::new(127, 0, 0, 1));
+ assert_eq!(origin.sin_port, 0);
+ } else {
+ panic!("Expected some error origin");
+ }
+ *ext_err
+ } else {
+ panic!("Unexpected control message {:?}", cmsg);
+ }
+ },
+ )
+ }
+
+ // Essentially the same test as v4.
+ //
+ // Disable the test on QEMU because QEMU emulation of IPV6_RECVERR is broken (as documented on
+ // PR #1514).
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_recverr_v6() {
+ #[repr(u8)]
+ enum IcmpV6Types {
+ DestUnreach = 1, // ICMPV6_DEST_UNREACH
+ }
+ #[repr(u8)]
+ enum IcmpV6UnreachCodes {
+ PortUnreach = 4, // ICMPV6_PORT_UNREACH
+ }
+
+ test_recverr_impl::<sockaddr_in6, _, _>(
+ "[::1]:6801",
+ AddressFamily::Inet6,
+ sockopt::Ipv6RecvErr,
+ libc::SO_EE_ORIGIN_ICMP6,
+ IcmpV6Types::DestUnreach as u8,
+ IcmpV6UnreachCodes::PortUnreach as u8,
+ // Closure handles protocol-specific testing and returns generic sock_extended_err for
+ // protocol-independent test impl.
+ |cmsg| {
+ if let ControlMessageOwned::Ipv6RecvErr(ext_err, err_addr) = cmsg {
+ if let Some(origin) = err_addr {
+ // Validate that our network error originated from localhost:0.
+ assert_eq!(origin.sin6_family, AddressFamily::Inet6 as _);
+ assert_eq!(
+ Ipv6Addr(origin.sin6_addr),
+ Ipv6Addr::from_std(&"::1".parse().unwrap()),
+ );
+ assert_eq!(origin.sin6_port, 0);
+ } else {
+ panic!("Expected some error origin");
+ }
+ *ext_err
+ } else {
+ panic!("Unexpected control message {:?}", cmsg);
+ }
+ },
+ )
+ }
+
+ fn test_recverr_impl<SA, OPT, TESTF>(sa: &str,
+ af: AddressFamily,
+ opt: OPT,
+ ee_origin: u8,
+ ee_type: u8,
+ ee_code: u8,
+ testf: TESTF)
+ where
+ OPT: SetSockOpt<Val = bool>,
+ TESTF: FnOnce(&ControlMessageOwned) -> libc::sock_extended_err,
+ {
+ use nix::errno::Errno;
+ use nix::sys::uio::IoVec;
+
+ const MESSAGE_CONTENTS: &str = "ABCDEF";
+
+ let sock_addr = {
+ let std_sa = SocketAddr::from_str(sa).unwrap();
+ let inet_addr = InetAddr::from_std(&std_sa);
+ SockAddr::new_inet(inet_addr)
+ };
+ let sock = socket(af, SockType::Datagram, SockFlag::SOCK_CLOEXEC, None).unwrap();
+ setsockopt(sock, opt, &true).unwrap();
+ if let Err(e) = sendto(sock, MESSAGE_CONTENTS.as_bytes(), &sock_addr, MsgFlags::empty()) {
+ assert_eq!(e, Errno::EADDRNOTAVAIL);
+ println!("{:?} not available, skipping test.", af);
+ return;
+ }
+
+ let mut buf = [0u8; 8];
+ let iovec = [IoVec::from_mut_slice(&mut buf)];
+ let mut cspace = cmsg_space!(libc::sock_extended_err, SA);
+
+ let msg = recvmsg(sock, &iovec, Some(&mut cspace), MsgFlags::MSG_ERRQUEUE).unwrap();
+ // The sent message / destination associated with the error is returned:
+ assert_eq!(msg.bytes, MESSAGE_CONTENTS.as_bytes().len());
+ assert_eq!(&buf[..msg.bytes], MESSAGE_CONTENTS.as_bytes());
+ // recvmsg(2): "The original destination address of the datagram that caused the error is
+ // supplied via msg_name;" however, this is not literally true. E.g., an earlier version
+ // of this test used 0.0.0.0 (::0) as the destination address, which was mutated into
+ // 127.0.0.1 (::1).
+ assert_eq!(msg.address, Some(sock_addr));
+
+ // Check for expected control message.
+ let ext_err = match msg.cmsgs().next() {
+ Some(cmsg) => testf(&cmsg),
+ None => panic!("No control message"),
+ };
+
+ assert_eq!(ext_err.ee_errno, libc::ECONNREFUSED as u32);
+ assert_eq!(ext_err.ee_origin, ee_origin);
+ // ip(7): ee_type and ee_code are set from the type and code fields of the ICMP (ICMPv6)
+ // header.
+ assert_eq!(ext_err.ee_type, ee_type);
+ assert_eq!(ext_err.ee_code, ee_code);
+ // ip(7): ee_info contains the discovered MTU for EMSGSIZE errors.
+ assert_eq!(ext_err.ee_info, 0);
+ }
+}
diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs
index e0ed0f7..01920fd 100644
--- a/test/sys/test_sockopt.rs
+++ b/test/sys/test_sockopt.rs
@@ -3,12 +3,53 @@ use nix::sys::socket::{socket, sockopt, getsockopt, setsockopt, AddressFamily, S
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::*;
+// NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not.
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+))]
+#[test]
+pub fn test_local_peercred_seqpacket() {
+ use nix::{
+ unistd::{Gid, Uid},
+ sys::socket::socketpair
+ };
+
+ let (fd1, _fd2) = socketpair(AddressFamily::Unix, SockType::SeqPacket, None,
+ SockFlag::empty()).unwrap();
+ let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap();
+ assert_eq!(xucred.version(), 0);
+ assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
+ assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
+}
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+))]
+#[test]
+pub fn test_local_peercred_stream() {
+ use nix::{
+ unistd::{Gid, Uid},
+ sys::socket::socketpair
+ };
+
+ let (fd1, _fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None,
+ SockFlag::empty()).unwrap();
+ let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap();
+ assert_eq!(xucred.version(), 0);
+ assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
+ assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
+}
+
#[cfg(target_os = "linux")]
#[test]
fn is_so_mark_functional() {
use nix::sys::socket::sockopt;
- require_capability!(CAP_NET_ADMIN);
+ require_capability!("is_so_mark_functional", CAP_NET_ADMIN);
let s = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap();
setsockopt(s, sockopt::Mark, &1337).unwrap();
@@ -29,6 +70,57 @@ fn test_so_buf() {
assert!(actual >= bufsize);
}
+#[test]
+fn test_so_tcp_maxseg() {
+ use std::net::SocketAddr;
+ use std::str::FromStr;
+ use nix::sys::socket::{accept, bind, connect, listen, InetAddr, SockAddr};
+ use nix::unistd::{close, write};
+
+ let std_sa = SocketAddr::from_str("127.0.0.1:4001").unwrap();
+ let inet_addr = InetAddr::from_std(&std_sa);
+ let sock_addr = SockAddr::new_inet(inet_addr);
+
+ let rsock = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp)
+ .unwrap();
+ bind(rsock, &sock_addr).unwrap();
+ listen(rsock, 10).unwrap();
+ let initial = getsockopt(rsock, sockopt::TcpMaxSeg).unwrap();
+ // Initial MSS is expected to be 536 (https://tools.ietf.org/html/rfc879#section-1) but some
+ // platforms keep it even lower. This might fail if you've tuned your initial MSS to be larger
+ // than 700
+ cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ let segsize: u32 = 873;
+ assert!(initial < segsize);
+ setsockopt(rsock, sockopt::TcpMaxSeg, &segsize).unwrap();
+ } else {
+ assert!(initial < 700);
+ }
+ }
+
+ // Connect and check the MSS that was advertised
+ let ssock = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp)
+ .unwrap();
+ connect(ssock, &sock_addr).unwrap();
+ let rsess = accept(rsock).unwrap();
+ write(rsess, b"hello").unwrap();
+ let actual = getsockopt(ssock, sockopt::TcpMaxSeg).unwrap();
+ // Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max
+ // TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary.
+ cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ assert!((segsize - 100) <= actual);
+ assert!(actual <= segsize);
+ } else {
+ assert!(initial < actual);
+ assert!(536 < actual);
+ }
+ }
+ close(rsock).unwrap();
+ close(ssock).unwrap();
+}
+
// The CI doesn't supported getsockopt and setsockopt on emulated processors.
// It's beleived that a QEMU issue, the tests run ok on a fully emulated system.
// Current CI just run the binary with QEMU but the Kernel remains the same as the host.
@@ -94,3 +186,14 @@ fn test_so_tcp_keepalive() {
assert_eq!(getsockopt(fd, sockopt::TcpKeepInterval).unwrap(), x + 1);
}
}
+
+#[test]
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+fn test_ttl_opts() {
+ let fd4 = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None).unwrap();
+ setsockopt(fd4, sockopt::Ipv4Ttl, &1)
+ .expect("setting ipv4ttl on an inet socket should succeed");
+ let fd6 = socket(AddressFamily::Inet6, SockType::Datagram, SockFlag::empty(), None).unwrap();
+ setsockopt(fd6, sockopt::Ipv6Ttl, &1)
+ .expect("setting ipv6ttl on an inet6 socket should succeed");
+}
diff --git a/test/sys/test_termios.rs b/test/sys/test_termios.rs
index 63d6a51..4a86154 100644
--- a/test/sys/test_termios.rs
+++ b/test/sys/test_termios.rs
@@ -19,7 +19,7 @@ fn write_all(f: RawFd, buf: &[u8]) {
#[test]
fn test_tcgetattr_pty() {
// openpty uses ptname(3) internally
- let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::PTSNAME_MTX.lock();
let pty = openpty(None, None).expect("openpty failed");
assert!(termios::tcgetattr(pty.slave).is_ok());
@@ -46,7 +46,7 @@ fn test_tcgetattr_ebadf() {
#[test]
fn test_output_flags() {
// openpty uses ptname(3) internally
- let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::PTSNAME_MTX.lock();
// Open one pty to get attributes for the second one
let mut termios = {
@@ -88,7 +88,7 @@ fn test_output_flags() {
#[test]
fn test_local_flags() {
// openpty uses ptname(3) internally
- let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::PTSNAME_MTX.lock();
// Open one pty to get attributes for the second one
let mut termios = {
diff --git a/test/sys/test_uio.rs b/test/sys/test_uio.rs
index 9dd4f01..c63b581 100644
--- a/test/sys/test_uio.rs
+++ b/test/sys/test_uio.rs
@@ -141,7 +141,7 @@ fn test_pread() {
}
#[test]
-#[cfg(target_os = "linux")]
+#[cfg(not(target_os = "redox"))]
fn test_pwritev() {
use std::io::Read;
@@ -171,7 +171,7 @@ fn test_pwritev() {
}
#[test]
-#[cfg(target_os = "linux")]
+#[cfg(not(target_os = "redox"))]
fn test_preadv() {
use std::io::Write;
@@ -205,16 +205,16 @@ fn test_preadv() {
#[test]
#[cfg(target_os = "linux")]
-// FIXME: qemu-user doesn't implement process_vm_readv/writev on most arches
-#[cfg_attr(not(any(target_arch = "x86", target_arch = "x86_64")), ignore)]
+// qemu-user doesn't implement process_vm_readv/writev on most arches
+#[cfg_attr(qemu, ignore)]
fn test_process_vm_readv() {
use nix::unistd::ForkResult::*;
use nix::sys::signal::*;
use nix::sys::wait::*;
use crate::*;
- require_capability!(CAP_SYS_PTRACE);
- let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+ require_capability!("test_process_vm_readv", CAP_SYS_PTRACE);
+ let _m = crate::FORK_MTX.lock();
// Pre-allocate memory in the child, since allocation isn't safe
// post-fork (~= async-signal-safe)
diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs
index 2d26fb8..afe4f42 100644
--- a/test/sys/test_wait.rs
+++ b/test/sys/test_wait.rs
@@ -8,7 +8,7 @@ use libc::_exit;
#[test]
#[cfg(not(target_os = "redox"))]
fn test_wait_signal() {
- let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::FORK_MTX.lock();
// Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe.
match unsafe{fork()}.expect("Error: Fork Failed") {
@@ -25,7 +25,7 @@ fn test_wait_signal() {
#[test]
fn test_wait_exit() {
- let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::FORK_MTX.lock();
// Safe: Child only calls `_exit`, which is async-signal-safe.
match unsafe{fork()}.expect("Error: Fork Failed") {
@@ -46,7 +46,7 @@ fn test_waitstatus_from_raw() {
#[test]
fn test_waitstatus_pid() {
- let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::FORK_MTX.lock();
match unsafe{fork()}.unwrap() {
Child => unsafe { _exit(0) },
@@ -96,8 +96,8 @@ mod ptrace {
#[test]
fn test_wait_ptrace() {
- require_capability!(CAP_SYS_PTRACE);
- let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+ require_capability!("test_wait_ptrace", CAP_SYS_PTRACE);
+ let _m = crate::FORK_MTX.lock();
match unsafe{fork()}.expect("Error: Fork Failed") {
Child => ptrace_child(),
diff --git a/test/test.rs b/test/test.rs
index 94f8e22..aade937 100644
--- a/test/test.rs
+++ b/test/test.rs
@@ -24,6 +24,7 @@ mod test_mq;
#[cfg(not(target_os = "redox"))]
mod test_net;
mod test_nix_path;
+mod test_resource;
mod test_poll;
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
mod test_pty;
@@ -42,7 +43,7 @@ mod test_unistd;
use std::os::unix::io::RawFd;
use std::path::PathBuf;
-use std::sync::{Mutex, RwLock, RwLockWriteGuard};
+use parking_lot::{Mutex, RwLock, RwLockWriteGuard};
use nix::unistd::{chdir, getcwd, read};
@@ -83,8 +84,7 @@ struct DirRestore<'a> {
impl<'a> DirRestore<'a> {
fn new() -> Self {
- let guard = crate::CWD_LOCK.write()
- .expect("Lock got poisoned by another test");
+ let guard = crate::CWD_LOCK.write();
DirRestore{
_g: guard,
d: getcwd().unwrap(),
diff --git a/test/test_dir.rs b/test/test_dir.rs
index 0dc7308..2940b6e 100644
--- a/test/test_dir.rs
+++ b/test/test_dir.rs
@@ -17,6 +17,7 @@ fn flags() -> OFlag {
}
#[test]
+#[allow(clippy::unnecessary_sort_by)] // False positive
fn read() {
let tmp = tempdir().unwrap();
File::create(&tmp.path().join("foo")).unwrap();
diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs
index ae6756e..db2acfb 100644
--- a/test/test_fcntl.rs
+++ b/test/test_fcntl.rs
@@ -28,8 +28,6 @@ use std::io::prelude::*;
#[cfg(not(target_os = "redox"))]
use std::os::unix::fs;
-use crate::*;
-
#[test]
#[cfg(not(target_os = "redox"))]
fn test_openat() {
@@ -129,14 +127,14 @@ fn test_renameat2_exchange() {
let old_path = old_dir.path().join("old");
{
let mut old_f = File::create(&old_path).unwrap();
- old_f.write(b"old").unwrap();
+ old_f.write_all(b"old").unwrap();
}
let new_dir = tempfile::tempdir().unwrap();
let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
let new_path = new_dir.path().join("new");
{
let mut new_f = File::create(&new_path).unwrap();
- new_f.write(b"new").unwrap();
+ new_f.write_all(b"new").unwrap();
}
renameat2(
Some(old_dirfd),
@@ -238,14 +236,8 @@ mod linux_android {
/// The from_offset should be updated by the call to reflect
/// the 3 bytes read (6).
#[test]
- // QEMU does not support copy_file_range. Skip platforms that use QEMU in CI
- #[cfg_attr(all(target_os = "linux", any(
- target_arch = "aarch64",
- target_arch = "arm",
- target_arch = "mips",
- target_arch = "mips64",
- target_arch = "powerpc64"
- )), ignore)]
+ // QEMU does not support copy_file_range. Skip under qemu
+ #[cfg_attr(qemu, ignore)]
fn test_copy_file_range() {
const CONTENTS: &[u8] = b"foobarbaz";
@@ -327,9 +319,10 @@ mod linux_android {
let buf1 = b"abcdef";
let buf2 = b"defghi";
- let mut iovecs = Vec::with_capacity(2);
- iovecs.push(IoVec::from_slice(&buf1[0..3]));
- iovecs.push(IoVec::from_slice(&buf2[0..3]));
+ let iovecs = vec![
+ IoVec::from_slice(&buf1[0..3]),
+ IoVec::from_slice(&buf2[0..3])
+ ];
let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();
@@ -480,17 +473,16 @@ mod test_posix_fadvise {
fn test_success() {
let tmp = NamedTempFile::new().unwrap();
let fd = tmp.as_raw_fd();
- let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();
+ let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED);
- assert_eq!(res, 0);
+ assert!(res.is_ok());
}
#[test]
fn test_errno() {
let (rd, _wr) = pipe().unwrap();
- let errno = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
- .unwrap();
- assert_eq!(errno, Errno::ESPIPE as i32);
+ let res = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED);
+ assert_eq!(res, Err(Errno::ESPIPE));
}
}
diff --git a/test/test_kmod/mod.rs b/test/test_kmod/mod.rs
index 7626330..8eef538 100644
--- a/test/test_kmod/mod.rs
+++ b/test/test_kmod/mod.rs
@@ -5,9 +5,7 @@ use tempfile::{tempdir, TempDir};
use crate::*;
fn compile_kernel_module() -> (PathBuf, String, TempDir) {
- let _m = crate::FORK_MTX
- .lock()
- .expect("Mutex got poisoned by another test");
+ let _m = crate::FORK_MTX.lock();
let tmp_dir = tempdir().expect("unable to create temporary build directory");
@@ -34,16 +32,15 @@ fn compile_kernel_module() -> (PathBuf, String, TempDir) {
use nix::errno::Errno;
use nix::kmod::{delete_module, DeleteModuleFlags};
use nix::kmod::{finit_module, init_module, ModuleInitFlags};
-use nix::Error;
use std::ffi::CString;
use std::fs::File;
use std::io::Read;
#[test]
fn test_finit_and_delete_module() {
- require_capability!(CAP_SYS_MODULE);
- let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
- let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+ require_capability!("test_finit_and_delete_module", CAP_SYS_MODULE);
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
@@ -58,10 +55,10 @@ fn test_finit_and_delete_module() {
}
#[test]
-fn test_finit_and_delete_modul_with_params() {
- require_capability!(CAP_SYS_MODULE);
- let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
- let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+fn test_finit_and_delete_module_with_params() {
+ require_capability!("test_finit_and_delete_module_with_params", CAP_SYS_MODULE);
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
@@ -80,9 +77,9 @@ fn test_finit_and_delete_modul_with_params() {
#[test]
fn test_init_and_delete_module() {
- require_capability!(CAP_SYS_MODULE);
- let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
- let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+ require_capability!("test_init_and_delete_module", CAP_SYS_MODULE);
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
@@ -90,7 +87,7 @@ fn test_init_and_delete_module() {
let mut contents: Vec<u8> = Vec::new();
f.read_to_end(&mut contents)
.expect("unable to read kernel module content to buffer");
- init_module(&mut contents, &CString::new("").unwrap()).expect("unable to load kernel module");
+ init_module(&contents, &CString::new("").unwrap()).expect("unable to load kernel module");
delete_module(
&CString::new(kmod_name).unwrap(),
@@ -100,9 +97,9 @@ fn test_init_and_delete_module() {
#[test]
fn test_init_and_delete_module_with_params() {
- require_capability!(CAP_SYS_MODULE);
- let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
- let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+ require_capability!("test_init_and_delete_module_with_params", CAP_SYS_MODULE);
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
@@ -110,7 +107,7 @@ fn test_init_and_delete_module_with_params() {
let mut contents: Vec<u8> = Vec::new();
f.read_to_end(&mut contents)
.expect("unable to read kernel module content to buffer");
- init_module(&mut contents, &CString::new("who=Nix number=2015").unwrap())
+ init_module(&contents, &CString::new("who=Nix number=2015").unwrap())
.expect("unable to load kernel module");
delete_module(
@@ -121,23 +118,23 @@ fn test_init_and_delete_module_with_params() {
#[test]
fn test_finit_module_invalid() {
- require_capability!(CAP_SYS_MODULE);
- let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
- let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+ require_capability!("test_finit_module_invalid", CAP_SYS_MODULE);
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
let kmod_path = "/dev/zero";
let f = File::open(kmod_path).expect("unable to open kernel module");
let result = finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty());
- assert_eq!(result.unwrap_err(), Error::from(Errno::EINVAL));
+ assert_eq!(result.unwrap_err(), Errno::EINVAL);
}
#[test]
fn test_finit_module_twice_and_delete_module() {
- require_capability!(CAP_SYS_MODULE);
- let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
- let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+ require_capability!("test_finit_module_twice_and_delete_module", CAP_SYS_MODULE);
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
@@ -147,7 +144,7 @@ fn test_finit_module_twice_and_delete_module() {
let result = finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty());
- assert_eq!(result.unwrap_err(), Error::from(Errno::EEXIST));
+ assert_eq!(result.unwrap_err(), Errno::EEXIST);
delete_module(
&CString::new(kmod_name).unwrap(),
@@ -157,11 +154,11 @@ fn test_finit_module_twice_and_delete_module() {
#[test]
fn test_delete_module_not_loaded() {
- require_capability!(CAP_SYS_MODULE);
- let _m0 = crate::KMOD_MTX.lock().expect("Mutex got poisoned by another test");
- let _m1 = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+ require_capability!("test_delete_module_not_loaded", CAP_SYS_MODULE);
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
let result = delete_module(&CString::new("hello").unwrap(), DeleteModuleFlags::empty());
- assert_eq!(result.unwrap_err(), Error::from(Errno::ENOENT));
+ assert_eq!(result.unwrap_err(), Errno::ENOENT);
}
diff --git a/test/test_mount.rs b/test/test_mount.rs
index c1b6c8a..44287f9 100644
--- a/test/test_mount.rs
+++ b/test/test_mount.rs
@@ -21,14 +21,13 @@ mod test_mount {
use nix::sys::stat::{self, Mode};
use nix::unistd::getuid;
- use tempfile;
-
- static SCRIPT_CONTENTS: &'static [u8] = b"#!/bin/sh
+ static SCRIPT_CONTENTS: &[u8] = b"#!/bin/sh
exit 23";
const EXPECTED_STATUS: i32 = 23;
const NONE: Option<&'static [u8]> = None;
+ #[allow(clippy::bind_instead_of_map)] // False positive
pub fn test_mount_tmpfs_without_flags_allows_rwx() {
let tempdir = tempfile::tempdir().unwrap();
diff --git a/test/test_mq.rs b/test/test_mq.rs
index d082692..430df5d 100644
--- a/test/test_mq.rs
+++ b/test/test_mq.rs
@@ -60,7 +60,11 @@ fn test_mq_getattr() {
// FIXME: Fix failures for mips in QEMU
#[test]
#[cfg(not(any(target_os = "netbsd")))]
-#[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)]
+#[cfg_attr(all(
+ qemu,
+ any(target_arch = "mips", target_arch = "mips64")
+ ), ignore
+)]
fn test_mq_setattr() {
use nix::mqueue::{mq_getattr, mq_setattr};
const MSG_SIZE: mq_attr_member_t = 32;
@@ -97,7 +101,11 @@ fn test_mq_setattr() {
// FIXME: Fix failures for mips in QEMU
#[test]
#[cfg(not(any(target_os = "netbsd")))]
-#[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)]
+#[cfg_attr(all(
+ qemu,
+ any(target_arch = "mips", target_arch = "mips64")
+ ), ignore
+)]
fn test_mq_set_nonblocking() {
use nix::mqueue::{mq_getattr, mq_set_nonblock, mq_remove_nonblock};
const MSG_SIZE: mq_attr_member_t = 32;
diff --git a/test/test_poll.rs b/test/test_poll.rs
index 0395512..e4b369f 100644
--- a/test/test_poll.rs
+++ b/test/test_poll.rs
@@ -64,3 +64,19 @@ fn test_ppoll() {
assert_eq!(nfds, 1);
assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN));
}
+
+#[test]
+fn test_pollfd_fd() {
+ use std::os::unix::io::AsRawFd;
+
+ let pfd = PollFd::new(0x1234, PollFlags::empty());
+ assert_eq!(pfd.as_raw_fd(), 0x1234);
+}
+
+#[test]
+fn test_pollfd_events() {
+ let mut pfd = PollFd::new(-1, PollFlags::POLLIN);
+ assert_eq!(pfd.events(), PollFlags::POLLIN);
+ pfd.set_events(PollFlags::POLLOUT);
+ assert_eq!(pfd.events(), PollFlags::POLLOUT);
+}
diff --git a/test/test_pty.rs b/test/test_pty.rs
index 52b6334..71932f2 100644
--- a/test/test_pty.rs
+++ b/test/test_pty.rs
@@ -29,7 +29,7 @@ fn test_explicit_close() {
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptsname_equivalence() {
- let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::PTSNAME_MTX.lock();
// Open a new PTTY master
let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
@@ -46,7 +46,7 @@ fn test_ptsname_equivalence() {
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptsname_copy() {
- let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::PTSNAME_MTX.lock();
// Open a new PTTY master
let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
@@ -80,7 +80,7 @@ fn test_ptsname_r_copy() {
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
fn test_ptsname_unique() {
- let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::PTSNAME_MTX.lock();
// Open a new PTTY master
let master1_fd = posix_openpt(OFlag::O_RDWR).unwrap();
@@ -98,7 +98,7 @@ fn test_ptsname_unique() {
/// Common setup for testing PTTY pairs
fn open_ptty_pair() -> (PtyMaster, File) {
- let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::PTSNAME_MTX.lock();
// Open a new PTTY master
let master = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
@@ -114,6 +114,8 @@ fn open_ptty_pair() -> (PtyMaster, File) {
let slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty()).unwrap();
#[cfg(target_os = "illumos")]
+ // TODO: rewrite using ioctl!
+ #[allow(clippy::comparison_chain)]
{
use libc::{ioctl, I_FIND, I_PUSH};
@@ -185,7 +187,7 @@ fn test_write_ptty_pair() {
#[test]
fn test_openpty() {
// openpty uses ptname(3) internally
- let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::PTSNAME_MTX.lock();
let pty = openpty(None, None).unwrap();
assert!(pty.master > 0);
@@ -220,7 +222,7 @@ fn test_openpty() {
#[test]
fn test_openpty_with_termios() {
// openpty uses ptname(3) internally
- let _m = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::PTSNAME_MTX.lock();
// Open one pty to get attributes for the second one
let mut termios = {
@@ -271,9 +273,9 @@ fn test_forkpty() {
use nix::sys::signal::*;
use nix::sys::wait::wait;
// forkpty calls openpty which uses ptname(3) internally.
- let _m0 = crate::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m0 = crate::PTSNAME_MTX.lock();
// forkpty spawns a child process
- let _m1 = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m1 = crate::FORK_MTX.lock();
let string = "naninani\n";
let echoed_string = "naninani\r\n";
diff --git a/test/test_resource.rs b/test/test_resource.rs
new file mode 100644
index 0000000..5969750
--- /dev/null
+++ b/test/test_resource.rs
@@ -0,0 +1,23 @@
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))]
+use nix::sys::resource::{getrlimit, setrlimit, Resource};
+
+/// Tests the RLIMIT_NOFILE functionality of getrlimit(), where the resource RLIMIT_NOFILE refers
+/// to the maximum file descriptor number that can be opened by the process (aka the maximum number
+/// of file descriptors that the process can open, since Linux 4.5).
+///
+/// We first fetch the existing file descriptor maximum values using getrlimit(), then edit the
+/// soft limit to make sure it has a new and distinct value to the hard limit. We then setrlimit()
+/// to put the new soft limit in effect, and then getrlimit() once more to ensure the limits have
+/// been updated.
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "illumos")))]
+pub fn test_resource_limits_nofile() {
+ let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
+
+ let soft_limit = Some(soft_limit.map_or(1024, |v| v - 1));
+ assert_ne!(soft_limit, hard_limit);
+ setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
+
+ let (new_soft_limit, _) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
+ assert_eq!(new_soft_limit, soft_limit);
+}
diff --git a/test/test_stat.rs b/test/test_stat.rs
index 424371f..33cf748 100644
--- a/test/test_stat.rs
+++ b/test/test_stat.rs
@@ -11,7 +11,6 @@ use std::path::Path;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use libc::{S_IFMT, S_IFLNK};
-#[cfg(not(target_os = "redox"))]
use libc::mode_t;
#[cfg(not(target_os = "redox"))]
@@ -60,6 +59,7 @@ fn assert_stat_results(stat_result: Result<FileStat>) {
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
// (Android's st_blocks is ulonglong which is always non-negative.)
#[cfg_attr(target_os = "android", allow(unused_comparisons))]
+#[allow(clippy::absurd_extreme_comparisons)] // Not absurd on all OSes
fn assert_lstat_results(stat_result: Result<FileStat>) {
let stats = stat_result.expect("stat call failed");
assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
@@ -306,3 +306,53 @@ fn test_mkdirat_fail() {
let result = mkdirat(dirfd, filename, Mode::S_IRWXU).unwrap_err();
assert_eq!(result, Errno::ENOTDIR);
}
+
+#[test]
+#[cfg(not(any(target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "redox")))]
+fn test_mknod() {
+ use stat::{lstat, mknod, SFlag};
+
+ let file_name = "test_file";
+ let tempdir = tempfile::tempdir().unwrap();
+ let target = tempdir.path().join(file_name);
+ mknod(&target, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap();
+ let mode = lstat(&target).unwrap().st_mode as mode_t;
+ assert!(mode & libc::S_IFREG == libc::S_IFREG);
+ assert!(mode & libc::S_IRWXU == libc::S_IRWXU);
+}
+
+#[test]
+#[cfg(not(any(target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "redox")))]
+fn test_mknodat() {
+ use fcntl::{AtFlags, OFlag};
+ use nix::dir::Dir;
+ use stat::{fstatat, mknodat, SFlag};
+
+ let file_name = "test_file";
+ let tempdir = tempfile::tempdir().unwrap();
+ let target_dir = Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap();
+ mknodat(
+ target_dir.as_raw_fd(),
+ file_name,
+ SFlag::S_IFREG,
+ Mode::S_IRWXU,
+ 0,
+ )
+ .unwrap();
+ let mode = fstatat(
+ target_dir.as_raw_fd(),
+ file_name,
+ AtFlags::AT_SYMLINK_NOFOLLOW,
+ )
+ .unwrap()
+ .st_mode as mode_t;
+ assert!(mode & libc::S_IFREG == libc::S_IFREG);
+ assert!(mode & libc::S_IRWXU == libc::S_IRWXU);
+}
diff --git a/test/test_time.rs b/test/test_time.rs
index c321352..dc307e5 100644
--- a/test/test_time.rs
+++ b/test/test_time.rs
@@ -6,11 +6,12 @@
target_os = "emscripten",
))]
use nix::time::clock_getcpuclockid;
-use nix::time::{clock_getres, clock_gettime, ClockId};
+use nix::time::{clock_gettime, ClockId};
+#[cfg(not(target_os = "redox"))]
#[test]
pub fn test_clock_getres() {
- assert!(clock_getres(ClockId::CLOCK_REALTIME).is_ok());
+ assert!(nix::time::clock_getres(ClockId::CLOCK_REALTIME).is_ok());
}
#[test]
@@ -31,6 +32,7 @@ pub fn test_clock_getcpuclockid() {
assert!(clock_gettime(clock_id).is_ok());
}
+#[cfg(not(target_os = "redox"))]
#[test]
pub fn test_clock_id_res() {
assert!(ClockId::CLOCK_REALTIME.res().is_ok());
diff --git a/test/test_unistd.rs b/test/test_unistd.rs
index b95f154..61062ad 100644
--- a/test/test_unistd.rs
+++ b/test/test_unistd.rs
@@ -10,7 +10,7 @@ use nix::sys::stat::{self, Mode, SFlag};
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
use nix::pty::{posix_openpt, grantpt, unlockpt, ptsname};
use nix::errno::Errno;
-use std::{env, iter};
+use std::env;
#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
use std::ffi::CString;
#[cfg(not(target_os = "redox"))]
@@ -28,7 +28,7 @@ use crate::*;
#[test]
#[cfg(not(any(target_os = "netbsd")))]
fn test_fork_and_waitpid() {
- let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::FORK_MTX.lock();
// Safe: Child only calls `_exit`, which is signal-safe
match unsafe{fork()}.expect("Error: Fork Failed") {
@@ -56,7 +56,7 @@ fn test_fork_and_waitpid() {
#[test]
fn test_wait() {
// Grab FORK_MTX so wait doesn't reap a different test's child process
- let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::FORK_MTX.lock();
// Safe: Child only calls `_exit`, which is signal-safe
match unsafe{fork()}.expect("Error: Fork Failed") {
@@ -116,7 +116,7 @@ fn test_mkfifo_directory() {
target_os = "macos", target_os = "ios",
target_os = "android", target_os = "redox")))]
fn test_mkfifoat_none() {
- let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+ let _m = crate::CWD_LOCK.read();
let tempdir = tempdir().unwrap();
let mkfifoat_fifo = tempdir.path().join("mkfifoat_fifo");
@@ -151,10 +151,10 @@ fn test_mkfifoat() {
target_os = "macos", target_os = "ios",
target_os = "android", target_os = "redox")))]
fn test_mkfifoat_directory_none() {
- let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+ let _m = crate::CWD_LOCK.read();
// mkfifoat should fail if a directory is given
- assert!(!mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR).is_ok());
+ assert!(mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR).is_err());
}
#[test]
@@ -168,7 +168,7 @@ fn test_mkfifoat_directory() {
let mkfifoat_dir = "mkfifoat_dir";
stat::mkdirat(dirfd, mkfifoat_dir, Mode::S_IRUSR).unwrap();
- assert!(!mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR).is_ok());
+ assert!(mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR).is_err());
}
#[test]
@@ -206,7 +206,7 @@ fn test_setgroups() {
// Skip this test when not run as root as `setgroups()` requires root.
skip_if_not_root!("test_setgroups");
- let _m = crate::GROUPS_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::GROUPS_MTX.lock();
// Save the existing groups
let old_groups = getgroups().unwrap();
@@ -234,7 +234,7 @@ fn test_initgroups() {
// require root.
skip_if_not_root!("test_initgroups");
- let _m = crate::GROUPS_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::GROUPS_MTX.lock();
// Save the existing groups
let old_groups = getgroups().unwrap();
@@ -304,7 +304,7 @@ macro_rules! execve_test_factory(
skip_if_seccomp!($test_name);
}
- let m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+ let m = crate::FORK_MTX.lock();
// The `exec`d process will write to `writer`, and we'll read that
// data from `reader`.
let (reader, writer) = pipe().unwrap();
@@ -443,7 +443,7 @@ fn test_getcwd() {
// 4096 on linux, 1024 on macos)
let mut inner_tmp_dir = tmpdir_path;
for _ in 0..5 {
- let newdir = iter::repeat("a").take(100).collect::<String>();
+ let newdir = "a".repeat(100);
inner_tmp_dir.push(newdir);
assert!(mkdir(inner_tmp_dir.as_path(), Mode::S_IRWXU).is_ok());
}
@@ -549,7 +549,7 @@ cfg_if!{
if #[cfg(any(target_os = "android", target_os = "linux"))] {
macro_rules! require_acct{
() => {
- require_capability!(CAP_SYS_PACCT);
+ require_capability!("test_acct", CAP_SYS_PACCT);
}
}
} else if #[cfg(target_os = "freebsd")] {
@@ -575,7 +575,7 @@ fn test_acct() {
use std::process::Command;
use std::{thread, time};
- let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::FORK_MTX.lock();
require_acct!();
let file = NamedTempFile::new().unwrap();
@@ -735,7 +735,7 @@ fn test_alarm() {
};
// Maybe other tests that fork interfere with this one?
- let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::SIGNAL_MTX.lock();
let handler = SigHandler::Handler(alarm_signal_handler);
let signal_action = SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty());
@@ -773,7 +773,7 @@ fn test_alarm() {
#[test]
#[cfg(not(target_os = "redox"))]
fn test_canceling_alarm() {
- let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
+ let _m = crate::SIGNAL_MTX.lock();
assert_eq!(alarm::cancel(), None);
@@ -784,7 +784,7 @@ fn test_canceling_alarm() {
#[test]
#[cfg(not(target_os = "redox"))]
fn test_symlinkat() {
- let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+ let _m = crate::CWD_LOCK.read();
let tempdir = tempdir().unwrap();
@@ -883,7 +883,7 @@ fn test_linkat_newdirfd_none() {
#[test]
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
fn test_linkat_no_follow_symlink() {
- let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+ let _m = crate::CWD_LOCK.read();
let tempdir = tempdir().unwrap();
let oldfilename = "foo.txt";
@@ -920,7 +920,7 @@ fn test_linkat_no_follow_symlink() {
#[test]
#[cfg(not(target_os = "redox"))]
fn test_linkat_follow_symlink() {
- let _m = crate::CWD_LOCK.read().expect("Mutex got poisoned by another test");
+ let _m = crate::CWD_LOCK.read();
let tempdir = tempdir().unwrap();
let oldfilename = "foo.txt";
@@ -1025,13 +1025,22 @@ fn test_access_file_exists() {
assert!(access(&path, AccessFlags::R_OK | AccessFlags::W_OK).is_ok());
}
+#[cfg(not(target_os = "redox"))]
+#[test]
+fn test_user_into_passwd() {
+ // get the UID of the "nobody" user
+ let nobody = User::from_name("nobody").unwrap().unwrap();
+ let pwd: libc::passwd = nobody.into();
+ let _: User = (&pwd).into();
+}
+
/// Tests setting the filesystem UID with `setfsuid`.
#[cfg(any(target_os = "linux", target_os = "android"))]
#[test]
fn test_setfsuid() {
use std::os::unix::fs::PermissionsExt;
use std::{fs, io, thread};
- require_capability!(CAP_SETUID);
+ require_capability!("test_setfsuid", CAP_SETUID);
// get the UID of the "nobody" user
let nobody = User::from_name("nobody").unwrap().unwrap();
@@ -1042,7 +1051,7 @@ fn test_setfsuid() {
dbg!(&temp_path);
let temp_path_2 = (&temp_path).to_path_buf();
let mut permissions = fs::metadata(&temp_path).unwrap().permissions();
- permissions.set_mode(640);
+ permissions.set_mode(0o640);
// spawn a new thread where to test setfsuid
thread::spawn(move || {