Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

1
vendor/nix/.cargo-checksum.json vendored Normal file

File diff suppressed because one or more lines are too long

2140
vendor/nix/CHANGELOG.md vendored Normal file

File diff suppressed because it is too large Load Diff

442
vendor/nix/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,442 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "assert-impl"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3464313de0c867016e3e69d7e1e9ae3499bcc4c18e12283d381359ed38b5b9e"
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "caps"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190baaad529bcfbde9e1a19022c42781bdb6ff9de25721abdb8fd98c0807730b"
dependencies = [
"libc",
"thiserror",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "errno"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]]
name = "getrandom"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]]
name = "nix"
version = "0.30.1"
dependencies = [
"assert-impl",
"bitflags 2.5.0",
"caps",
"cfg-if",
"cfg_aliases",
"libc",
"memoffset",
"parking_lot",
"pin-utils",
"rand",
"semver",
"sysctl",
"tempfile",
]
[[package]]
name = "parking_lot"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "rand"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
]
[[package]]
name = "redox_syscall"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
dependencies = [
"bitflags 2.5.0",
]
[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.5.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "syn"
version = "2.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "sysctl"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225e483f02d0ad107168dc57381a8a40c3aeea6abe47f37506931f861643cfa8"
dependencies = [
"bitflags 1.3.2",
"byteorder",
"libc",
"thiserror",
"walkdir",
]
[[package]]
name = "tempfile"
version = "3.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
dependencies = [
"cfg-if",
"fastrand",
"rustix",
"windows-sys",
]
[[package]]
name = "thiserror"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "winapi-util"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.5.0",
]

158
vendor/nix/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,158 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.69"
name = "nix"
version = "0.30.1"
authors = ["The nix-rust Project Developers"]
build = "build.rs"
include = [
"build.rs",
"src/**/*",
"test/**/*",
"LICENSE",
"README.md",
"CHANGELOG.md",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Rust friendly bindings to *nix APIs"
readme = "README.md"
categories = ["os::unix-apis"]
license = "MIT"
repository = "https://github.com/nix-rust/nix"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"--cfg",
"docsrs",
]
targets = [
"x86_64-unknown-linux-gnu",
"aarch64-linux-android",
"x86_64-apple-darwin",
"aarch64-apple-ios",
"x86_64-unknown-freebsd",
"x86_64-unknown-openbsd",
"x86_64-unknown-netbsd",
"x86_64-unknown-dragonfly",
"x86_64-unknown-fuchsia",
"x86_64-unknown-redox",
"x86_64-unknown-illumos",
]
[features]
acct = []
aio = ["pin-utils"]
default = []
dir = ["fs"]
env = []
event = ["poll"]
fanotify = []
feature = []
fs = []
hostname = []
inotify = []
ioctl = []
kmod = []
mman = []
mount = ["uio"]
mqueue = ["fs"]
net = ["socket"]
personality = []
poll = []
process = []
pthread = []
ptrace = ["process"]
quota = []
reboot = []
resource = []
sched = ["process"]
signal = ["process"]
socket = ["memoffset"]
syslog = []
term = []
time = []
ucontext = ["signal"]
uio = []
user = ["feature"]
zerocopy = [
"fs",
"uio",
]
[lib]
name = "nix"
path = "src/lib.rs"
[[test]]
name = "test"
path = "test/test.rs"
[[test]]
name = "test-aio-drop"
path = "test/sys/test_aio_drop.rs"
[[test]]
name = "test-clearenv"
path = "test/test_clearenv.rs"
[[test]]
name = "test-prctl"
path = "test/sys/test_prctl.rs"
[dependencies.bitflags]
version = "2.3.3"
[dependencies.cfg-if]
version = "1.0"
[dependencies.libc]
version = "0.2.171"
features = ["extra_traits"]
[dependencies.memoffset]
version = "0.9"
optional = true
[dependencies.pin-utils]
version = "0.1.0"
optional = true
[dev-dependencies.assert-impl]
version = "0.1"
[dev-dependencies.parking_lot]
version = "0.12"
[dev-dependencies.rand]
version = "0.9"
[dev-dependencies.semver]
version = "1.0.7"
[dev-dependencies.tempfile]
version = "3.7.1"
[build-dependencies.cfg_aliases]
version = "0.2.1"
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dev-dependencies.caps]
version = "0.5.3"
[target.'cfg(target_os = "freebsd")'.dev-dependencies.sysctl]
version = "0.4"

21
vendor/nix/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Carl Lerche + nix-rust Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

126
vendor/nix/README.md vendored Normal file
View File

@@ -0,0 +1,126 @@
# 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://img.shields.io/crates/v/nix.svg)](https://crates.io/crates/nix)
[![docs.rs](https://img.shields.io/badge/docs.rs-nix-blue?style=flat-square&logo=docs.rs)](https://docs.rs/nix)
![maintenance-status](https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg)
[![msrv](https://img.shields.io/badge/msrv-1.69-blue?style=flat-square&logo=rust)](https://www.rust-lang.org)
Nix seeks to provide friendly bindings to various *nix platform APIs (Linux, Darwin,
...). The goal is to not provide a 100% unified interface, but to unify
what can be while still providing platform specific APIs.
For many system APIs, Nix provides a safe alternative to the unsafe APIs
exposed by the [libc crate](https://github.com/rust-lang/libc). This is done by
wrapping the libc functionality with types/abstractions that enforce legal/safe
usage.
As an example of what Nix provides, examine the differences between what is
exposed by libc and nix for the
[gethostname](https://man7.org/linux/man-pages/man2/gethostname.2.html) system
call:
```rust,ignore
// libc api (unsafe, requires handling return code/errno)
pub unsafe extern fn gethostname(name: *mut c_char, len: size_t) -> c_int;
// nix api (returns a nix::Result<OsString>)
pub fn gethostname() -> Result<OsString>;
```
## Supported Platforms
nix target support consists of three tiers. While nix attempts to support all
platforms supported by [libc](https://github.com/rust-lang/libc), only some
platforms are actively supported due to either technical or manpower
limitations. Support for platforms is split into three tiers:
* Tier 1 - Builds and tests for this target are run in CI. Failures of either
block the inclusion of new code.
* Tier 2 - Builds for this target are run in CI. Failures during the build
blocks the inclusion of new code. Tests may be run, but failures
in tests don't block the inclusion of new code.
* Tier 3 - Builds for this target are run in CI. Failures during the build
*do not* necessarily block the inclusion of new code. That is, at
our discretion a Tier 3 target may be dropped at any time, if it
would otherwise block development.
Platforms not listed are supported on a best-effort basis, relying on our users
to report any problems.
The following targets are supported by `nix`:
<table>
<tr>
<th>Tier 1</th>
<th>Tier 2</th>
<th>Tier 3</th>
</tr>
<tr>
<td>
<ul>
<li>aarch64-apple-darwin</li>
<li>aarch64-unknown-linux-gnu</li>
<li>arm-unknown-linux-gnueabi</li>
<li>armv7-unknown-linux-gnueabihf</li>
<li>i686-unknown-freebsd</li>
<li>i686-unknown-linux-gnu</li>
<li>i686-unknown-linux-musl</li>
<li>mips-unknown-linux-gnu</li>
<li>mips64-unknown-linux-gnuabi64</li>
<li>mips64el-unknown-linux-gnuabi64</li>
<li>mipsel-unknown-linux-gnu</li>
<li>powerpc64le-unknown-linux-gnu</li>
<li>x86_64-unknown-freebsd</li>
<li>x86_64-unknown-linux-gnu</li>
<li>x86_64-unknown-linux-musl</li>
</ul>
</td>
<td>
<ul>
<li>aarch64-apple-ios</li>
<li>aarch64-linux-android</li>
<li>aarch64-unknown-linux-ohos</li>
<li>arm-linux-androideabi</li>
<li>arm-unknown-linux-musleabi</li>
<li>armv7-linux-androideabi</li>
<li>armv7-unknown-linux-ohos</li>
<li>i686-linux-android</li>
<li>loongarch64-unknown-linux-gnu</li>
<li>s390x-unknown-linux-gnu</li>
<li>x86_64-linux-android</li>
<li>x86_64-unknown-illumos</li>
<li>x86_64-unknown-linux-ohos</li>
<li>x86_64-unknown-netbsd</li>
</td>
<td>
<li>armv7-unknown-linux-uclibceabihf</li>
<li>powerpc64-unknown-linux-gnu</li>
<li>x86_64-unknown-fuchsia</li>
<li>x86_64-unknown-dragonfly</li>
<li>x86_64-unknown-haiku</li>
<li>x86_64-unknown-linux-gnux32</li>
<li>x86_64-unknown-openbsd</li>
<li>x86_64-unknown-redox</li>
<li>i686-unknown-hurd-gnu</li>
</td>
</tr>
</table>
## Minimum Supported Rust Version (MSRV)
nix is supported on Rust 1.69 and higher. Its MSRV will not be
changed in the future without bumping the major or minor version.
## Contributing
Contributions are very welcome. Please See [CONTRIBUTING](CONTRIBUTING.md) for
additional details.
Feel free to join us in [the nix-rust/nix](https://discord.com/invite/rkBeJUsmyd) channel on Discord to
discuss `nix` development.
## License
Nix is licensed under the MIT license. See [LICENSE](LICENSE) for more details.

35
vendor/nix/build.rs vendored Normal file
View File

@@ -0,0 +1,35 @@
use cfg_aliases::cfg_aliases;
fn main() {
cfg_aliases! {
android: { target_os = "android" },
dragonfly: { target_os = "dragonfly" },
ios: { target_os = "ios" },
freebsd: { target_os = "freebsd" },
illumos: { target_os = "illumos" },
linux: { target_os = "linux" },
macos: { target_os = "macos" },
netbsd: { target_os = "netbsd" },
openbsd: { target_os = "openbsd" },
solaris: { target_os = "solaris" },
watchos: { target_os = "watchos" },
tvos: { target_os = "tvos" },
visionos: { target_os = "visionos" },
// cfg aliases we would like to use
apple_targets: { any(ios, macos, watchos, tvos, visionos) },
bsd: { any(freebsd, dragonfly, netbsd, openbsd, apple_targets) },
bsd_without_apple: { any(freebsd, dragonfly, netbsd, openbsd) },
linux_android: { any(android, linux) },
freebsdlike: { any(dragonfly, freebsd) },
netbsdlike: { any(netbsd, openbsd) },
solarish: { any(illumos, solaris) },
}
// Below are custom cfg values set during some CI steps.
println!("cargo:rustc-check-cfg=cfg(fbsd14)");
println!("cargo:rustc-check-cfg=cfg(qemu)");
// Cygwin target, added in 1.86
println!("cargo:rustc-check-cfg=cfg(target_os, values(\"cygwin\"))");
}

328
vendor/nix/src/dir.rs vendored Normal file
View File

@@ -0,0 +1,328 @@
//! List directory contents
use crate::errno::Errno;
use crate::fcntl::{self, OFlag};
use crate::sys;
use crate::{NixPath, Result};
use cfg_if::cfg_if;
use std::ffi;
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use std::ptr;
#[cfg(target_os = "linux")]
use libc::{dirent64 as dirent, readdir64_r as readdir_r};
#[cfg(not(target_os = "linux"))]
use libc::{dirent, readdir_r};
/// An open directory.
///
/// This is a lower-level interface than [`std::fs::ReadDir`]. Notable differences:
/// * can be opened from a file descriptor (as returned by [`openat`][openat],
/// perhaps before knowing if the path represents a file or directory).
/// * implements [`AsFd`][AsFd], so it can be passed to [`fstat`][fstat],
/// [`openat`][openat], etc. The file descriptor continues to be owned by the
/// `Dir`, so callers must not keep a `RawFd` after the `Dir` is dropped.
/// * can be iterated through multiple times without closing and reopening the file
/// descriptor. Each iteration rewinds when finished.
/// * returns entries for `.` (current directory) and `..` (parent directory).
/// * returns entries' names as a [`CStr`][cstr] (no allocation or conversion beyond whatever libc
/// does).
///
/// [AsFd]: std::os::fd::AsFd
/// [fstat]: crate::sys::stat::fstat
/// [openat]: crate::fcntl::openat
/// [cstr]: std::ffi::CStr
///
/// # Examples
///
/// Traverse the current directory, and print entries' names:
///
/// ```
/// use nix::dir::Dir;
/// use nix::fcntl::OFlag;
/// use nix::sys::stat::Mode;
///
/// let mut cwd = Dir::open(".", OFlag::O_RDONLY | OFlag::O_CLOEXEC, Mode::empty()).unwrap();
/// for res_entry in cwd.iter() {
/// let entry = res_entry.unwrap();
/// println!("File name: {}", entry.file_name().to_string_lossy());
/// }
/// ```
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Dir(ptr::NonNull<libc::DIR>);
impl Dir {
/// Opens the given path as with `fcntl::open`.
pub fn open<P: ?Sized + NixPath>(
path: &P,
oflag: OFlag,
mode: sys::stat::Mode,
) -> Result<Self> {
let fd = fcntl::open(path, oflag, mode)?;
Dir::from_fd(fd)
}
/// Opens the given path as with `fcntl::openat`.
pub fn openat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
oflag: OFlag,
mode: sys::stat::Mode,
) -> Result<Self> {
let fd = fcntl::openat(dirfd, path, oflag, mode)?;
Dir::from_fd(fd)
}
/// Converts from a descriptor-based object, closing the descriptor on success or failure.
///
/// # Safety
///
/// It is only safe if `fd` is an owned file descriptor.
#[inline]
#[deprecated(
since = "0.30.0",
note = "Deprecate this since it is not I/O-safe, use from_fd instead."
)]
pub unsafe fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
use std::os::fd::FromRawFd;
use std::os::fd::OwnedFd;
// SAFETY:
//
// This is indeed unsafe is `fd` it not an owned fd.
let owned_fd = unsafe { OwnedFd::from_raw_fd(fd.into_raw_fd()) };
Dir::from_fd(owned_fd)
}
/// Converts from a file descriptor, closing it on failure.
///
/// # Examples
///
/// `ENOTDIR` would be returned if `fd` does not refer to a directory:
///
/// ```should_panic
/// use std::os::fd::OwnedFd;
/// use nix::dir::Dir;
///
/// let temp_file = tempfile::tempfile().unwrap();
/// let temp_file_fd: OwnedFd = temp_file.into();
/// let never = Dir::from_fd(temp_file_fd).unwrap();
/// ```
#[doc(alias("fdopendir"))]
pub fn from_fd(fd: std::os::fd::OwnedFd) -> Result<Self> {
// take the ownership as the constructed `Dir` is now the owner
let raw_fd = fd.into_raw_fd();
let d = ptr::NonNull::new(unsafe { libc::fdopendir(raw_fd) })
.ok_or(Errno::last())?;
Ok(Dir(d))
}
/// Returns an iterator of `Result<Entry>` which rewinds when finished.
pub fn iter(&mut self) -> Iter {
Iter(self)
}
}
// `Dir` is not `Sync`. With the current implementation, it could be, but according to
// https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html,
// future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to
// call `readdir` simultaneously from multiple threads.
//
// `Dir` is safe to pass from one thread to another, as it's not reference-counted.
unsafe impl Send for Dir {}
impl std::os::fd::AsFd for Dir {
fn as_fd(&self) -> std::os::fd::BorrowedFd {
let raw_fd = self.as_raw_fd();
// SAFETY:
//
// `raw_fd` should be open and valid for the lifetime of the returned
// `BorrowedFd` as it is extracted from `&self`.
unsafe { std::os::fd::BorrowedFd::borrow_raw(raw_fd) }
}
}
impl AsRawFd for Dir {
fn as_raw_fd(&self) -> RawFd {
unsafe { libc::dirfd(self.0.as_ptr()) }
}
}
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(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
}
}
// The pass by mut is technically needless only because the inner NonNull is
// Copy. But philosophically we're mutating the Dir, so we pass by mut.
#[allow(clippy::needless_pass_by_ref_mut)]
fn next(dir: &mut Dir) -> Option<Result<Entry>> {
unsafe {
// Note: POSIX specifies that portable applications should dynamically allocate a
// buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
// for the NUL byte. It doesn't look like the std library does this; it just uses
// fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
// Probably fine here too then.
let mut ent = std::mem::MaybeUninit::<dirent>::uninit();
let mut result = ptr::null_mut();
if let Err(e) = Errno::result(readdir_r(
dir.0.as_ptr(),
ent.as_mut_ptr(),
&mut result,
)) {
return Some(Err(e));
}
if result.is_null() {
return None;
}
assert_eq!(result, ent.as_mut_ptr());
Some(Ok(Entry(ent.assume_init())))
}
}
/// Return type of [`Dir::iter`].
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Iter<'d>(&'d mut Dir);
impl Iterator for Iter<'_> {
type Item = Result<Entry>;
fn next(&mut self) -> Option<Self::Item> {
next(self.0)
}
}
impl Drop for Iter<'_> {
fn drop(&mut self) {
unsafe { libc::rewinddir((self.0).0.as_ptr()) }
}
}
/// The return type of [Dir::into_iter]
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct OwningIter(Dir);
impl Iterator for OwningIter {
type Item = Result<Entry>;
fn next(&mut self) -> Option<Self::Item> {
next(&mut self.0)
}
}
/// The file descriptor continues to be owned by the `OwningIter`,
/// so callers must not keep a `RawFd` after the `OwningIter` is dropped.
impl AsRawFd for OwningIter {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl IntoIterator for Dir {
type Item = Result<Entry>;
type IntoIter = OwningIter;
/// Creates a owning iterator, that is, one that takes ownership of the
/// `Dir`. The `Dir` cannot be used after calling this. This can be useful
/// when you have a function that both creates a `Dir` instance and returns
/// an `Iterator`.
///
/// Example:
///
/// ```
/// use nix::{dir::Dir, fcntl::OFlag, sys::stat::Mode};
/// use std::{iter::Iterator, string::String};
///
/// fn ls_upper(dirname: &str) -> impl Iterator<Item=String> {
/// let d = Dir::open(dirname, OFlag::O_DIRECTORY, Mode::S_IXUSR).unwrap();
/// d.into_iter().map(|x| x.unwrap().file_name().as_ref().to_string_lossy().to_ascii_uppercase())
/// }
/// ```
fn into_iter(self) -> Self::IntoIter {
OwningIter(self)
}
}
/// A directory entry, similar to `std::fs::DirEntry`.
///
/// Note that unlike the std version, this may represent the `.` or `..` entries.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct Entry(dirent);
/// Type of file referenced by a directory entry
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Type {
/// FIFO (Named pipe)
Fifo,
/// Character device
CharacterDevice,
/// Directory
Directory,
/// Block device
BlockDevice,
/// Regular file
File,
/// Symbolic link
Symlink,
/// Unix-domain socket
Socket,
}
impl Entry {
/// Returns the inode number (`d_ino`) of the underlying `dirent`.
#[allow(clippy::useless_conversion)] // Not useless on all OSes
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
pub fn ino(&self) -> u64 {
cfg_if! {
if #[cfg(any(target_os = "aix",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "cygwin",
solarish,
linux_android,
apple_targets))] {
self.0.d_ino as u64
} else {
u64::from(self.0.d_fileno)
}
}
}
/// Returns the bare file name of this directory entry without any other leading path component.
pub fn file_name(&self) -> &ffi::CStr {
unsafe { ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
}
/// Returns the type of this directory entry, if known.
///
/// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known;
/// notably, some Linux filesystems don't implement this. The caller should use `stat` or
/// `fstat` if this returns `None`.
pub fn file_type(&self) -> Option<Type> {
#[cfg(not(any(solarish, target_os = "aix", target_os = "haiku")))]
match self.0.d_type {
libc::DT_FIFO => Some(Type::Fifo),
libc::DT_CHR => Some(Type::CharacterDevice),
libc::DT_DIR => Some(Type::Directory),
libc::DT_BLK => Some(Type::BlockDevice),
libc::DT_REG => Some(Type::File),
libc::DT_LNK => Some(Type::Symlink),
libc::DT_SOCK => Some(Type::Socket),
/* libc::DT_UNKNOWN | */ _ => None,
}
// illumos, Solaris, and Haiku systems do not have the d_type member at all:
#[cfg(any(solarish, target_os = "aix", target_os = "haiku"))]
None
}
}

63
vendor/nix/src/env.rs vendored Normal file
View File

@@ -0,0 +1,63 @@
//! Environment variables
use cfg_if::cfg_if;
use std::fmt;
/// Indicates that [`clearenv`] failed for some unknown reason
#[derive(Clone, Copy, Debug)]
pub struct ClearEnvError;
impl fmt::Display for ClearEnvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "clearenv failed")
}
}
impl std::error::Error for ClearEnvError {}
/// Clear the environment of all name-value pairs.
///
/// On platforms where libc provides `clearenv()`, it will be used. libc's
/// `clearenv()` is documented to return an error code but not set errno; if the
/// return value indicates a failure, this function will return
/// [`ClearEnvError`].
///
/// On platforms where libc does not provide `clearenv()`, a fallback
/// implementation will be used that iterates over all environment variables and
/// removes them one-by-one.
///
/// # Safety
///
/// This function is not threadsafe and can cause undefined behavior in
/// combination with `std::env` or other program components that access the
/// environment. See, for example, the discussion on `std::env::remove_var`; this
/// function is a case of an "inherently unsafe non-threadsafe API" dealing with
/// the environment.
///
/// The caller must ensure no other threads access the process environment while
/// this function executes and that no raw pointers to an element of libc's
/// `environ` is currently held. The latter is not an issue if the only other
/// 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> {
cfg_if! {
if #[cfg(any(linux_android,
target_os = "fuchsia",
target_os = "wasi",
target_env = "uclibc",
target_os = "emscripten"))] {
let ret = unsafe { libc::clearenv() };
} else {
use std::env;
for (name, _) in env::vars_os() {
env::remove_var(name);
}
let ret = 0;
}
}
if ret == 0 {
Ok(())
} else {
Err(ClearEnvError)
}
}

3859
vendor/nix/src/errno.rs vendored Normal file

File diff suppressed because it is too large Load Diff

1619
vendor/nix/src/fcntl.rs vendored Normal file

File diff suppressed because it is too large Load Diff

127
vendor/nix/src/features.rs vendored Normal file
View File

@@ -0,0 +1,127 @@
//! Feature tests for OS functionality
pub use self::os::*;
#[cfg(any(linux_android, target_os = "emscripten"))]
mod os {
use crate::sys::utsname::uname;
use crate::Result;
use std::os::unix::ffi::OsStrExt;
use std::sync::atomic::{AtomicUsize, Ordering};
// Features:
// * atomic cloexec on socket: 2.6.27
// * pipe2: 2.6.27
// * accept4: 2.6.28
static VERS_UNKNOWN: usize = 1;
static VERS_2_6_18: usize = 2;
static VERS_2_6_27: usize = 3;
static VERS_2_6_28: usize = 4;
static VERS_3: usize = 5;
#[inline]
fn digit(dst: &mut usize, b: u8) {
*dst *= 10;
*dst += (b - b'0') as usize;
}
fn parse_kernel_version() -> Result<usize> {
let u = uname()?;
let mut curr: usize = 0;
let mut major: usize = 0;
let mut minor: usize = 0;
let mut patch: usize = 0;
for &b in u.release().as_bytes() {
if curr >= 3 {
break;
}
match b {
b'.' | b'-' => {
curr += 1;
}
b'0'..=b'9' => match curr {
0 => digit(&mut major, b),
1 => digit(&mut minor, b),
_ => digit(&mut patch, b),
},
_ => break,
}
}
Ok(if major >= 3 {
VERS_3
} else if major >= 2 {
if minor >= 7 {
VERS_UNKNOWN
} else if minor >= 6 {
if patch >= 28 {
VERS_2_6_28
} else if patch >= 27 {
VERS_2_6_27
} else {
VERS_2_6_18
}
} else {
VERS_UNKNOWN
}
} else {
VERS_UNKNOWN
})
}
fn kernel_version() -> Result<usize> {
static KERNEL_VERS: AtomicUsize = AtomicUsize::new(0);
let mut kernel_vers = KERNEL_VERS.load(Ordering::Relaxed);
if kernel_vers == 0 {
kernel_vers = parse_kernel_version()?;
KERNEL_VERS.store(kernel_vers, Ordering::Relaxed);
}
Ok(kernel_vers)
}
/// Check if the OS supports atomic close-on-exec for sockets
pub fn socket_atomic_cloexec() -> bool {
kernel_version()
.map(|version| version >= VERS_2_6_27)
.unwrap_or(false)
}
#[test]
fn test_parsing_kernel_version() {
assert!(kernel_version().unwrap() > 0);
}
}
#[cfg(any(
freebsdlike, // FreeBSD since 10.0 DragonFlyBSD since ???
netbsdlike, // NetBSD since 6.0 OpenBSD since 5.7
target_os = "hurd", // Since glibc 2.28
target_os = "illumos", // Since ???
target_os = "redox", // Since 1-july-2020
target_os = "cygwin",
))]
mod os {
/// Check if the OS supports atomic close-on-exec for sockets
pub const fn socket_atomic_cloexec() -> bool {
true
}
}
#[cfg(any(
target_os = "aix",
apple_targets,
target_os = "fuchsia",
target_os = "haiku",
target_os = "solaris"
))]
mod os {
/// Check if the OS supports atomic close-on-exec for sockets
pub const fn socket_atomic_cloexec() -> bool {
false
}
}

217
vendor/nix/src/ifaddrs.rs vendored Normal file
View File

@@ -0,0 +1,217 @@
//! Query network interface addresses
//!
//! Uses the Linux and/or BSD specific function `getifaddrs` to query the list
//! of interfaces and their associated addresses.
use cfg_if::cfg_if;
#[cfg(apple_targets)]
use std::convert::TryFrom;
use std::ffi;
use std::iter::Iterator;
use std::mem;
use std::option::Option;
use crate::net::if_::*;
use crate::sys::socket::{SockaddrLike, SockaddrStorage};
use crate::{Errno, Result};
/// Describes a single address for an interface as returned by `getifaddrs`.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct InterfaceAddress {
/// Name of the network interface
pub interface_name: String,
/// Flags as from `SIOCGIFFLAGS` ioctl
pub flags: InterfaceFlags,
/// Network address of this interface
pub address: Option<SockaddrStorage>,
/// Netmask of this interface
pub netmask: Option<SockaddrStorage>,
/// Broadcast address of this interface, if applicable
pub broadcast: Option<SockaddrStorage>,
/// Point-to-point destination address
pub destination: Option<SockaddrStorage>,
}
cfg_if! {
if #[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))] {
fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
info.ifa_ifu
}
} else {
fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
info.ifa_dstaddr
}
}
}
/// Workaround a bug in XNU where netmasks will always have the wrong size in
/// the sa_len field due to the kernel ignoring trailing zeroes in the structure
/// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470
///
/// To fix this, we stack-allocate a new sockaddr_storage, zero it out, and
/// memcpy sa_len of the netmask to that new storage. Finally, we reset the
/// ss_len field to sizeof(sockaddr_storage). This is supposedly valid as all
/// members of the sockaddr_storage are "ok" with being zeroed out (there are
/// no pointers).
#[cfg(apple_targets)]
unsafe fn workaround_xnu_bug(info: &libc::ifaddrs) -> Option<SockaddrStorage> {
let src_sock = info.ifa_netmask;
if src_sock.is_null() {
return None;
}
let mut dst_sock = mem::MaybeUninit::<libc::sockaddr_storage>::zeroed();
let dst_sock = unsafe {
// memcpy only sa_len bytes, assume the rest is zero
std::ptr::copy_nonoverlapping(
src_sock as *const u8,
dst_sock.as_mut_ptr().cast(),
(*src_sock).sa_len.into(),
);
// Initialize ss_len to sizeof(libc::sockaddr_storage).
(*dst_sock.as_mut_ptr()).ss_len =
u8::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap();
dst_sock.assume_init()
};
let dst_sock_ptr =
&dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr;
unsafe { SockaddrStorage::from_raw(dst_sock_ptr, None) }
}
impl InterfaceAddress {
/// Create an `InterfaceAddress` from the libc struct.
fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) };
#[cfg(apple_targets)]
let netmask = unsafe { workaround_xnu_bug(info) };
#[cfg(not(apple_targets))]
let netmask =
unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
let mut addr = InterfaceAddress {
interface_name: ifname.to_string_lossy().into_owned(),
flags: InterfaceFlags::from_bits_truncate(
info.ifa_flags as IflagsType,
),
address,
netmask,
broadcast: None,
destination: None,
};
let ifu = get_ifu_from_sockaddr(info);
if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
addr.destination = unsafe { SockaddrStorage::from_raw(ifu, None) };
} else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
addr.broadcast = unsafe { SockaddrStorage::from_raw(ifu, None) };
}
addr
}
}
/// Holds the results of `getifaddrs`.
///
/// Use the function `getifaddrs` to create this Iterator. Note that the
/// actual list of interfaces can be iterated once and will be freed as
/// soon as the Iterator goes out of scope.
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct InterfaceAddressIterator {
base: *mut libc::ifaddrs,
next: *mut libc::ifaddrs,
}
impl Drop for InterfaceAddressIterator {
fn drop(&mut self) {
unsafe { libc::freeifaddrs(self.base) };
}
}
impl Iterator for InterfaceAddressIterator {
type Item = InterfaceAddress;
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
match unsafe { self.next.as_ref() } {
Some(ifaddr) => {
self.next = ifaddr.ifa_next;
Some(InterfaceAddress::from_libc_ifaddrs(ifaddr))
}
None => None,
}
}
}
/// Get interface addresses using libc's `getifaddrs`
///
/// Note that the underlying implementation differs between OSes. Only the
/// most common address families are supported by the nix crate (due to
/// lack of time and complexity of testing). The address family is encoded
/// in the specific variant of `SockaddrStorage` returned for the fields
/// `address`, `netmask`, `broadcast`, and `destination`. For any entry not
/// supported, the returned list will contain a `None` entry.
///
/// # Example
/// ```
/// let addrs = nix::ifaddrs::getifaddrs().unwrap();
/// for ifaddr in addrs {
/// match ifaddr.address {
/// Some(address) => {
/// println!("interface {} address {}",
/// ifaddr.interface_name, address);
/// },
/// None => {
/// println!("interface {} with unsupported address family",
/// ifaddr.interface_name);
/// }
/// }
/// }
/// ```
pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit();
unsafe {
Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| {
InterfaceAddressIterator {
base: addrs.assume_init(),
next: addrs.assume_init(),
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
// Only checks if `getifaddrs` can be invoked without panicking.
#[test]
fn test_getifaddrs() {
let _ = getifaddrs();
}
// Ensures getting the netmask works, and in particular that
// `workaround_xnu_bug` works properly.
#[test]
fn test_getifaddrs_netmask_correct() {
let addrs = getifaddrs().unwrap();
for iface in addrs {
let sock = if let Some(sock) = iface.netmask {
sock
} else {
continue;
};
if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) {
let _ = sock.as_sockaddr_in().unwrap();
return;
} else if sock.family()
== Some(crate::sys::socket::AddressFamily::Inet6)
{
let _ = sock.as_sockaddr_in6().unwrap();
return;
}
}
panic!("No address?");
}
}

132
vendor/nix/src/kmod.rs vendored Normal file
View File

@@ -0,0 +1,132 @@
//! Load and unload kernel modules.
//!
//! For more details see
use std::ffi::CStr;
use std::os::unix::io::{AsFd, AsRawFd};
use crate::errno::Errno;
use crate::Result;
/// Loads a kernel module from a buffer.
///
/// It loads an ELF image into kernel space,
/// performs any necessary symbol relocations,
/// initializes module parameters to values provided by the caller,
/// and then runs the module's init function.
///
/// This function requires `CAP_SYS_MODULE` privilege.
///
/// The `module_image` argument points to a buffer containing the binary image
/// to be loaded. The buffer should contain a valid ELF image
/// built for the running kernel.
///
/// The `param_values` argument is a string containing space-delimited specifications
/// of the values for module parameters.
/// Each of the parameter specifications has the form:
///
/// `name[=value[,value...]]`
///
/// # Example
///
/// ```no_run
/// use std::fs::File;
/// use std::io::Read;
/// use std::ffi::CString;
/// use nix::kmod::init_module;
///
/// let mut f = File::open("mykernel.ko").unwrap();
/// let mut contents: Vec<u8> = Vec::new();
/// f.read_to_end(&mut contents).unwrap();
/// init_module(&mut contents, &CString::new("who=Rust when=Now,12").unwrap()).unwrap();
/// ```
///
/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
pub fn init_module(module_image: &[u8], param_values: &CStr) -> Result<()> {
let res = unsafe {
libc::syscall(
libc::SYS_init_module,
module_image.as_ptr(),
module_image.len(),
param_values.as_ptr(),
)
};
Errno::result(res).map(drop)
}
libc_bitflags!(
/// Flags used by the `finit_module` function.
pub struct ModuleInitFlags: libc::c_uint {
/// Ignore symbol version hashes.
MODULE_INIT_IGNORE_MODVERSIONS;
/// Ignore kernel version magic.
MODULE_INIT_IGNORE_VERMAGIC;
}
);
/// Loads a kernel module from a given file descriptor.
///
/// # Example
///
/// ```no_run
/// use std::fs::File;
/// use std::ffi::CString;
/// use nix::kmod::{finit_module, ModuleInitFlags};
///
/// let f = File::open("mymod.ko").unwrap();
/// finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()).unwrap();
/// ```
///
/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
pub fn finit_module<Fd: AsFd>(
fd: Fd,
param_values: &CStr,
flags: ModuleInitFlags,
) -> Result<()> {
let res = unsafe {
libc::syscall(
libc::SYS_finit_module,
fd.as_fd().as_raw_fd(),
param_values.as_ptr(),
flags.bits(),
)
};
Errno::result(res).map(drop)
}
libc_bitflags!(
/// Flags used by `delete_module`.
///
/// See [`man delete_module(2)`](https://man7.org/linux/man-pages/man2/delete_module.2.html)
/// for a detailed description how these flags work.
pub struct DeleteModuleFlags: libc::c_int {
/// `delete_module` will return immediately, with an error, if the module has a nonzero
/// reference count.
O_NONBLOCK;
/// `delete_module` will unload the module immediately, regardless of whether it has a
/// nonzero reference count.
O_TRUNC;
}
);
/// Unloads the kernel module with the given name.
///
/// # Example
///
/// ```no_run
/// use std::ffi::CString;
/// use nix::kmod::{delete_module, DeleteModuleFlags};
///
/// delete_module(&CString::new("mymod").unwrap(), DeleteModuleFlags::O_NONBLOCK).unwrap();
/// ```
///
/// See [`man delete_module(2)`](https://man7.org/linux/man-pages/man2/delete_module.2.html) for more information.
pub fn delete_module(name: &CStr, flags: DeleteModuleFlags) -> Result<()> {
let res = unsafe {
libc::syscall(libc::SYS_delete_module, name.as_ptr(), flags.bits())
};
Errno::result(res).map(drop)
}

413
vendor/nix/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,413 @@
//! Rust friendly bindings to the various *nix system functions.
//!
//! Modules are structured according to the C header file that they would be
//! defined in.
//!
//! # Features
//!
//! Nix uses the following Cargo features to enable optional functionality.
//! They may be enabled in any combination.
//! * `acct` - Process accounting
//! * `aio` - POSIX AIO
//! * `dir` - Stuff relating to directory iteration
//! * `env` - Manipulate environment variables
//! * `event` - Event-driven APIs, like `kqueue` and `epoll`
//! * `fanotify` - Linux's `fanotify` filesystem events monitoring API
//! * `feature` - Query characteristics of the OS at runtime
//! * `fs` - File system functionality
//! * `hostname` - Get and set the system's hostname
//! * `inotify` - Linux's `inotify` file system notification API
//! * `ioctl` - The `ioctl` syscall, and wrappers for many specific instances
//! * `kmod` - Load and unload kernel modules
//! * `mman` - Stuff relating to memory management
//! * `mount` - Mount and unmount file systems
//! * `mqueue` - POSIX message queues
//! * `net` - Networking-related functionality
//! * `personality` - Set the process execution domain
//! * `poll` - APIs like `poll` and `select`
//! * `process` - Stuff relating to running processes
//! * `pthread` - POSIX threads
//! * `ptrace` - Process tracing and debugging
//! * `quota` - File system quotas
//! * `reboot` - Reboot the system
//! * `resource` - Process resource limits
//! * `sched` - Manipulate process's scheduling
//! * `socket` - Sockets, whether for networking or local use
//! * `signal` - Send and receive signals to processes
//! * `syslog` - System logging
//! * `term` - Terminal control APIs
//! * `time` - Query the operating system's clocks
//! * `ucontext` - User thread context
//! * `uio` - Vectored I/O
//! * `user` - Stuff relating to users and groups
//! * `zerocopy` - APIs like `sendfile` and `copy_file_range`
#![crate_name = "nix"]
#![cfg(unix)]
#![allow(non_camel_case_types)]
// A clear document is a good document no matter if it has a summary in its
// first paragraph or not.
#![allow(clippy::too_long_first_doc_paragraph)]
#![recursion_limit = "500"]
#![deny(unused)]
#![deny(unexpected_cfgs)]
#![allow(unused_macros)]
#![cfg_attr(
not(all(
feature = "acct",
feature = "aio",
feature = "dir",
feature = "env",
feature = "event",
feature = "fanotify",
feature = "feature",
feature = "fs",
feature = "hostname",
feature = "inotify",
feature = "ioctl",
feature = "kmod",
feature = "mman",
feature = "mount",
feature = "mqueue",
feature = "net",
feature = "personality",
feature = "poll",
feature = "process",
feature = "pthread",
feature = "ptrace",
feature = "quota",
feature = "reboot",
feature = "resource",
feature = "sched",
feature = "socket",
feature = "signal",
feature = "syslog",
feature = "term",
feature = "time",
feature = "ucontext",
feature = "uio",
feature = "user",
feature = "zerocopy",
)),
allow(unused_imports)
)]
#![deny(unstable_features)]
#![deny(missing_copy_implementations)]
#![deny(missing_debug_implementations)]
#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(clippy::cast_ptr_alignment)]
#![deny(unsafe_op_in_unsafe_fn)]
// I found the change suggested by this rules could hurt code readability. I cannot
// remeber every type's default value, in such cases, it forces me to open
// the std doc to insepct the Default value, which is unnecessary with
// `.unwrap_or(value)`.
#![allow(clippy::unwrap_or_default)]
// Re-exported external crates
pub use libc;
// Private internal modules
#[macro_use]
mod macros;
// Public crates
#[cfg(not(target_os = "redox"))]
feature! {
#![feature = "dir"]
pub mod dir;
}
feature! {
#![feature = "env"]
pub mod env;
}
#[allow(missing_docs)]
pub mod errno;
feature! {
#![feature = "feature"]
#[deny(missing_docs)]
pub mod features;
}
pub mod fcntl;
feature! {
#![feature = "net"]
#[cfg(any(linux_android,
bsd,
solarish))]
#[deny(missing_docs)]
pub mod ifaddrs;
#[cfg(not(target_os = "redox"))]
#[deny(missing_docs)]
pub mod net;
}
#[cfg(linux_android)]
feature! {
#![feature = "kmod"]
pub mod kmod;
}
feature! {
#![feature = "mount"]
pub mod mount;
}
#[cfg(any(
freebsdlike,
all(target_os = "linux", not(target_env = "ohos")),
target_os = "netbsd"
))]
feature! {
#![feature = "mqueue"]
pub mod mqueue;
}
feature! {
#![feature = "poll"]
pub mod poll;
}
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
feature! {
#![feature = "term"]
#[deny(missing_docs)]
pub mod pty;
}
feature! {
#![feature = "sched"]
pub mod sched;
}
pub mod sys;
feature! {
#![feature = "time"]
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 = "aarch64",
target_arch = "s390x",
target_arch = "x86",
target_arch = "x86_64"
)
))]
feature! {
#![feature = "ucontext"]
#[allow(missing_docs)]
pub mod ucontext;
}
pub mod unistd;
#[cfg(any(feature = "poll", feature = "event"))]
mod poll_timeout;
#[cfg(any(
target_os = "freebsd",
target_os = "haiku",
target_os = "linux",
target_os = "netbsd",
apple_targets
))]
feature! {
#![feature = "process"]
pub mod spawn;
}
feature! {
#![feature = "syslog"]
pub mod syslog;
}
use std::ffi::{CStr, CString, OsStr};
use std::mem::MaybeUninit;
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
use std::{ptr, result, slice};
use errno::Errno;
/// Nix Result Type
pub type Result<T> = result::Result<T, Errno>;
/// Nix's main error type.
///
/// It's a wrapper around Errno. As such, it's very interoperable with
/// [`std::io::Error`], but it has the advantages of:
/// * `Clone`
/// * `Copy`
/// * `Eq`
/// * Small size
/// * Represents all of the system's errnos, instead of just the most common
/// 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;
}
impl NixPath for str {
fn is_empty(&self) -> bool {
NixPath::is_empty(OsStr::new(self))
}
fn len(&self) -> usize {
NixPath::len(OsStr::new(self))
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
OsStr::new(self).with_nix_path(f)
}
}
impl NixPath for OsStr {
fn is_empty(&self) -> bool {
self.as_bytes().is_empty()
}
fn len(&self) -> usize {
self.as_bytes().len()
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
self.as_bytes().with_nix_path(f)
}
}
impl NixPath for CStr {
fn is_empty(&self) -> bool {
self.to_bytes().is_empty()
}
fn len(&self) -> usize {
self.to_bytes().len()
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
Ok(f(self))
}
}
impl NixPath for [u8] {
fn is_empty(&self) -> bool {
self.is_empty()
}
fn len(&self) -> usize {
self.len()
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
// The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path
// longer than ~300 bytes. See the PR description to get stats for your own machine.
// https://github.com/nix-rust/nix/pull/1656
//
// By being smaller than a memory page, we also avoid the compiler inserting a probe frame:
// https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
const MAX_STACK_ALLOCATION: usize = 1024;
if self.len() >= MAX_STACK_ALLOCATION {
return with_nix_path_allocating(self, f);
}
let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
let buf_ptr = buf.as_mut_ptr().cast();
unsafe {
ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
buf_ptr.add(self.len()).write(0);
}
match CStr::from_bytes_with_nul(unsafe {
slice::from_raw_parts(buf_ptr, self.len() + 1)
}) {
Ok(s) => Ok(f(s)),
Err(_) => Err(Errno::EINVAL),
}
}
}
#[cold]
#[inline(never)]
fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
match CString::new(from) {
Ok(s) => Ok(f(&s)),
Err(_) => Err(Errno::EINVAL),
}
}
impl NixPath for Path {
fn is_empty(&self) -> bool {
NixPath::is_empty(self.as_os_str())
}
fn len(&self) -> usize {
NixPath::len(self.as_os_str())
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
self.as_os_str().with_nix_path(f)
}
}
impl NixPath for PathBuf {
fn is_empty(&self) -> bool {
NixPath::is_empty(self.as_os_str())
}
fn len(&self) -> usize {
NixPath::len(self.as_os_str())
}
fn with_nix_path<T, F>(&self, f: F) -> Result<T>
where
F: FnOnce(&CStr) -> T,
{
self.as_os_str().with_nix_path(f)
}
}
/// Like `NixPath::with_nix_path()`, but allow the `path` argument to be optional.
///
/// A NULL pointer will be provided if `path.is_none()`.
#[cfg(any(
all(apple_targets, feature = "mount"),
all(linux_android, any(feature = "mount", feature = "fanotify"))
))]
pub(crate) fn with_opt_nix_path<P, T, F>(path: Option<&P>, f: F) -> Result<T>
where
P: ?Sized + NixPath,
F: FnOnce(*const libc::c_char) -> T,
{
match path {
Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
None => Ok(f(ptr::null())),
}
}

331
vendor/nix/src/macros.rs vendored Normal file
View File

@@ -0,0 +1,331 @@
// Thanks to Tokio for this macro
macro_rules! feature {
(
#![$meta:meta]
$($item:item)*
) => {
$(
#[cfg($meta)]
#[cfg_attr(docsrs, doc(cfg($meta)))]
$item
)*
}
}
/// The `libc_bitflags!` macro helps with a common use case of defining a public bitflags type
/// with values from the libc crate. It is used the same way as the `bitflags!` macro, except
/// that only the name of the flag value has to be given.
///
/// The `libc` crate must be in scope with the name `libc`.
///
/// # Example
/// ```ignore
/// libc_bitflags!{
/// pub struct ProtFlags: libc::c_int {
/// PROT_NONE;
/// PROT_READ;
/// /// PROT_WRITE enables write protect
/// PROT_WRITE;
/// PROT_EXEC;
/// #[cfg(linux_android)]
/// PROT_GROWSDOWN;
/// #[cfg(linux_android)]
/// PROT_GROWSUP;
/// }
/// }
/// ```
///
/// Example with casting, due to a mistake in libc. In this example, the
/// various flags have different types, so we cast the broken ones to the right
/// type.
///
/// ```ignore
/// libc_bitflags!{
/// pub struct SaFlags: libc::c_ulong {
/// SA_NOCLDSTOP as libc::c_ulong;
/// SA_NOCLDWAIT;
/// SA_NODEFER as libc::c_ulong;
/// SA_ONSTACK;
/// SA_RESETHAND as libc::c_ulong;
/// SA_RESTART as libc::c_ulong;
/// SA_SIGINFO;
/// }
/// }
/// ```
macro_rules! libc_bitflags {
(
$(#[$outer:meta])*
pub struct $BitFlags:ident: $T:ty {
$(
$(#[$inner:ident $($args:tt)*])*
$Flag:ident $(as $cast:ty)*;
)+
}
) => {
::bitflags::bitflags! {
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
$(#[$outer])*
pub struct $BitFlags: $T {
$(
$(#[$inner $($args)*])*
const $Flag = libc::$Flag $(as $cast)*;
)+
}
}
};
}
/// The `libc_enum!` macro helps with a common use case of defining an enum exclusively using
/// values from the `libc` crate. This macro supports both `pub` and private `enum`s.
///
/// The `libc` crate must be in scope with the name `libc`.
///
/// # Example
/// ```ignore
/// libc_enum!{
/// pub enum ProtFlags {
/// PROT_NONE,
/// PROT_READ,
/// PROT_WRITE,
/// PROT_EXEC,
/// #[cfg(linux_android)]
/// PROT_GROWSDOWN,
/// #[cfg(linux_android)]
/// PROT_GROWSUP,
/// }
/// }
/// ```
// Some targets don't use all rules.
#[allow(unused_macro_rules)]
macro_rules! libc_enum {
// Exit rule.
(@make_enum
name: $BitFlags:ident,
{
$v:vis
attrs: [$($attrs:tt)*],
entries: [$($entries:tt)*],
}
) => {
$($attrs)*
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
$v enum $BitFlags {
$($entries)*
}
};
// 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)]
#[allow(deprecated)]
#[allow(unused_attributes)]
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
attrs: $attrs:tt,
from_type: $repr:path,
},
$entries:tt,
$try_froms:tt;
) => {
libc_enum! {
@make_enum
name: $BitFlags,
{
$v
attrs: $attrs,
entries: $entries,
from_type: $repr,
try_froms: $try_froms
}
}
};
// Munch an attr.
(@accumulate_entries
name: $BitFlags:ident,
$prefix:tt,
[$($entries:tt)*],
[$($try_froms:tt)*];
#[$attr:meta] $($tail:tt)*
) => {
libc_enum! {
@accumulate_entries
name: $BitFlags,
$prefix,
[
$($entries)*
#[$attr]
],
[
$($try_froms)*
#[$attr]
];
$($tail)*
}
};
// Munch last ident if not followed by a comma.
(@accumulate_entries
name: $BitFlags:ident,
$prefix: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)*],
[$($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)*
}
};
// Munch an ident and cast it to the given type; covers terminating comma.
(@accumulate_entries
name: $BitFlags:ident,
$prefix: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)*
}
};
// Entry rule.
(
$(#[$attr:meta])*
$v:vis enum $BitFlags:ident {
$($vals:tt)*
}
) => {
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
attrs: [$(#[$attr])*],
from_type: $repr,
},
[],
[];
$($vals)*
}
};
}

111
vendor/nix/src/mount/apple.rs vendored Normal file
View File

@@ -0,0 +1,111 @@
use crate::{Errno, NixPath, Result};
use libc::c_int;
libc_bitflags!(
/// Used with [`mount()`] and [`unmount()`].
pub struct MntFlags: c_int {
/// Do not interpret special files on the filesystem.
MNT_NODEV;
/// Enable data protection on the filesystem if the filesystem is configured for it.
MNT_CPROTECT;
/// file system is quarantined
MNT_QUARANTINE;
/// filesystem is stored locally
MNT_LOCAL;
/// quotas are enabled on filesystem
MNT_QUOTA;
/// identifies the root filesystem
MNT_ROOTFS;
/// file system is not appropriate path to user data
MNT_DONTBROWSE;
/// VFS will ignore ownership information on filesystem objects
MNT_IGNORE_OWNERSHIP;
/// filesystem was mounted by automounter
MNT_AUTOMOUNTED;
/// filesystem is journaled
MNT_JOURNALED;
/// Don't allow user extended attributes
MNT_NOUSERXATTR;
/// filesystem should defer writes
MNT_DEFWRITE;
/// don't block unmount if not responding
MNT_NOBLOCK;
/// file system is exported
MNT_EXPORTED;
/// file system written asynchronously
MNT_ASYNC;
/// Force a read-write mount even if the file system appears to be
/// unclean.
MNT_FORCE;
/// MAC support for objects.
MNT_MULTILABEL;
/// Do not update access times.
MNT_NOATIME;
/// Disallow program execution.
MNT_NOEXEC;
/// Do not honor setuid or setgid bits on files when executing them.
MNT_NOSUID;
/// Mount read-only.
MNT_RDONLY;
/// Causes the vfs subsystem to update its data structures pertaining to
/// the specified already mounted file system.
MNT_RELOAD;
/// Create a snapshot of the file system.
MNT_SNAPSHOT;
/// All I/O to the file system should be done synchronously.
MNT_SYNCHRONOUS;
/// Union with underlying fs.
MNT_UNION;
/// Indicates that the mount command is being applied to an already
/// mounted file system.
MNT_UPDATE;
}
);
/// Mount a file system.
///
/// # Arguments
/// - `source` - Specifies the file system. e.g. `/dev/sd0`.
/// - `target` - Specifies the destination. e.g. `/mnt`.
/// - `flags` - Optional flags controlling the mount.
/// - `data` - Optional file system specific data.
///
/// # see also
/// [`mount`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/mount.2.html)
pub fn mount<
P1: ?Sized + NixPath,
P2: ?Sized + NixPath,
P3: ?Sized + NixPath,
>(
source: &P1,
target: &P2,
flags: MntFlags,
data: Option<&P3>,
) -> Result<()> {
let res = source.with_nix_path(|s| {
target.with_nix_path(|t| {
crate::with_opt_nix_path(data, |d| unsafe {
libc::mount(
s.as_ptr(),
t.as_ptr(),
flags.bits(),
d.cast_mut().cast(),
)
})
})
})???;
Errno::result(res).map(drop)
}
/// Umount the file system mounted at `target`.
pub fn unmount<P>(target: &P, flags: MntFlags) -> Result<()>
where
P: ?Sized + NixPath,
{
let res = target.with_nix_path(|cstr| unsafe {
libc::unmount(cstr.as_ptr(), flags.bits())
})?;
Errno::result(res).map(drop)
}

View File

@@ -0,0 +1,429 @@
#[cfg(target_os = "freebsd")]
use crate::Error;
use crate::{Errno, NixPath, Result};
use libc::c_int;
#[cfg(target_os = "freebsd")]
use libc::{c_char, c_uint, c_void};
#[cfg(target_os = "freebsd")]
use std::{
borrow::Cow,
ffi::{CStr, CString},
fmt, io,
marker::PhantomData,
};
libc_bitflags!(
/// Used with [`Nmount::nmount`].
pub struct MntFlags: c_int {
/// ACL support enabled.
#[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
MNT_ACLS;
/// All I/O to the file system should be done asynchronously.
MNT_ASYNC;
/// dir should instead be a file system ID encoded as “FSID:val0:val1”.
#[cfg(target_os = "freebsd")]
MNT_BYFSID;
/// Force a read-write mount even if the file system appears to be
/// unclean.
MNT_FORCE;
/// GEOM journal support enabled.
#[cfg(target_os = "freebsd")]
MNT_GJOURNAL;
/// MAC support for objects.
#[cfg(target_os = "freebsd")]
MNT_MULTILABEL;
/// Disable read clustering.
#[cfg(freebsdlike)]
MNT_NOCLUSTERR;
/// Disable write clustering.
#[cfg(freebsdlike)]
MNT_NOCLUSTERW;
/// Enable NFS version 4 ACLs.
#[cfg(target_os = "freebsd")]
MNT_NFS4ACLS;
/// Do not update access times.
MNT_NOATIME;
/// Disallow program execution.
MNT_NOEXEC;
/// Do not honor setuid or setgid bits on files when executing them.
MNT_NOSUID;
/// Do not follow symlinks.
#[cfg(freebsdlike)]
MNT_NOSYMFOLLOW;
/// Mount read-only.
MNT_RDONLY;
/// Causes the vfs subsystem to update its data structures pertaining to
/// the specified already mounted file system.
MNT_RELOAD;
/// Create a snapshot of the file system.
///
/// See [mksnap_ffs(8)](https://www.freebsd.org/cgi/man.cgi?query=mksnap_ffs)
#[cfg(target_os = "freebsd")]
MNT_SNAPSHOT;
/// Using soft updates.
#[cfg(any(freebsdlike, netbsdlike))]
MNT_SOFTDEP;
/// Directories with the SUID bit set chown new files to their own
/// owner.
#[cfg(freebsdlike)]
MNT_SUIDDIR;
/// All I/O to the file system should be done synchronously.
MNT_SYNCHRONOUS;
/// Union with underlying fs.
#[cfg(any(
target_os = "freebsd",
target_os = "netbsd"
))]
MNT_UNION;
/// Indicates that the mount command is being applied to an already
/// mounted file system.
MNT_UPDATE;
/// Check vnode use counts.
#[cfg(target_os = "freebsd")]
MNT_NONBUSY;
}
);
/// The Error type of [`Nmount::nmount`].
///
/// It wraps an [`Errno`], but also may contain an additional message returned
/// by `nmount(2)`.
#[cfg(target_os = "freebsd")]
#[derive(Debug)]
pub struct NmountError {
errno: Error,
errmsg: Option<String>,
}
#[cfg(target_os = "freebsd")]
impl NmountError {
/// Returns the additional error string sometimes generated by `nmount(2)`.
pub fn errmsg(&self) -> Option<&str> {
self.errmsg.as_deref()
}
/// Returns the inner [`Error`]
pub const fn error(&self) -> Error {
self.errno
}
fn new(error: Error, errmsg: Option<&CStr>) -> Self {
Self {
errno: error,
errmsg: errmsg.map(CStr::to_string_lossy).map(Cow::into_owned),
}
}
}
#[cfg(target_os = "freebsd")]
impl std::error::Error for NmountError {}
#[cfg(target_os = "freebsd")]
impl fmt::Display for NmountError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(errmsg) = &self.errmsg {
write!(f, "{:?}: {}: {}", self.errno, errmsg, self.errno.desc())
} else {
write!(f, "{:?}: {}", self.errno, self.errno.desc())
}
}
}
#[cfg(target_os = "freebsd")]
impl From<NmountError> for io::Error {
fn from(err: NmountError) -> Self {
err.errno.into()
}
}
/// Result type of [`Nmount::nmount`].
#[cfg(target_os = "freebsd")]
pub type NmountResult = std::result::Result<(), NmountError>;
/// Mount a FreeBSD file system.
///
/// The `nmount(2)` system call works similarly to the `mount(8)` program; it
/// takes its options as a series of name-value pairs. Most of the values are
/// strings, as are all of the names. The `Nmount` structure builds up an
/// argument list and then executes the syscall.
///
/// # Examples
///
/// To mount `target` onto `mountpoint` with `nullfs`:
/// ```
/// # use nix::unistd::Uid;
/// # use ::sysctl::{CtlValue, Sysctl};
/// # let ctl = ::sysctl::Ctl::new("vfs.usermount").unwrap();
/// # if !Uid::current().is_root() && CtlValue::Int(0) == ctl.value().unwrap() {
/// # return;
/// # };
/// use nix::mount::{MntFlags, Nmount, unmount};
/// use std::ffi::CString;
/// use tempfile::tempdir;
///
/// let mountpoint = tempdir().unwrap();
/// let target = tempdir().unwrap();
///
/// let fstype = CString::new("fstype").unwrap();
/// let nullfs = CString::new("nullfs").unwrap();
/// Nmount::new()
/// .str_opt(&fstype, &nullfs)
/// .str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
/// .str_opt_owned("target", target.path().to_str().unwrap())
/// .nmount(MntFlags::empty()).unwrap();
///
/// unmount(mountpoint.path(), MntFlags::empty()).unwrap();
/// ```
///
/// # See Also
/// * [`nmount(2)`](https://www.freebsd.org/cgi/man.cgi?query=nmount)
/// * [`nullfs(5)`](https://www.freebsd.org/cgi/man.cgi?query=nullfs)
#[cfg(target_os = "freebsd")]
#[derive(Debug, Default)]
pub struct Nmount<'a> {
// n.b. notgull: In reality, this is a list that contains
// both mutable and immutable pointers.
// Be careful using this.
iov: Vec<libc::iovec>,
is_owned: Vec<bool>,
marker: PhantomData<&'a ()>,
}
#[cfg(target_os = "freebsd")]
impl<'a> Nmount<'a> {
/// Helper function to push a slice onto the `iov` array.
fn push_slice(&mut self, val: &'a [u8], is_owned: bool) {
self.iov.push(libc::iovec {
iov_base: val.as_ptr().cast_mut().cast(),
iov_len: val.len(),
});
self.is_owned.push(is_owned);
}
/// Helper function to push a pointer and its length onto the `iov` array.
fn push_pointer_and_length(
&mut self,
val: *const u8,
len: usize,
is_owned: bool,
) {
self.iov.push(libc::iovec {
iov_base: val as *mut _,
iov_len: len,
});
self.is_owned.push(is_owned);
}
/// Helper function to push a `nix` path as owned.
fn push_nix_path<P: ?Sized + NixPath>(&mut self, val: &P) {
val.with_nix_path(|s| {
let len = s.to_bytes_with_nul().len();
let ptr = s.to_owned().into_raw() as *const u8;
self.push_pointer_and_length(ptr, len, true);
})
.unwrap();
}
/// Add an opaque mount option.
///
/// Some file systems take binary-valued mount options. They can be set
/// with this method.
///
/// # Safety
///
/// Unsafe because it will cause `Nmount::nmount` to dereference a raw
/// pointer. The user is responsible for ensuring that `val` is valid and
/// its lifetime outlives `self`! An easy way to do that is to give the
/// value a larger scope than `name`
///
/// # Examples
/// ```
/// use libc::c_void;
/// use nix::mount::Nmount;
/// use std::ffi::CString;
/// use std::mem;
///
/// // Note that flags outlives name
/// let mut flags: u32 = 0xdeadbeef;
/// let name = CString::new("flags").unwrap();
/// let p = &mut flags as *mut u32 as *mut c_void;
/// let len = mem::size_of_val(&flags);
/// let mut nmount = Nmount::new();
/// unsafe { nmount.mut_ptr_opt(&name, p, len) };
/// ```
pub unsafe fn mut_ptr_opt(
&mut self,
name: &'a CStr,
val: *mut c_void,
len: usize,
) -> &mut Self {
self.push_slice(name.to_bytes_with_nul(), false);
self.push_pointer_and_length(val.cast(), len, false);
self
}
/// Add a mount option that does not take a value.
///
/// # Examples
/// ```
/// use nix::mount::Nmount;
/// use std::ffi::CString;
///
/// let read_only = CString::new("ro").unwrap();
/// Nmount::new()
/// .null_opt(&read_only);
/// ```
pub fn null_opt(&mut self, name: &'a CStr) -> &mut Self {
self.push_slice(name.to_bytes_with_nul(), false);
self.push_slice(&[], false);
self
}
/// Add a mount option that does not take a value, but whose name must be
/// owned.
///
///
/// This has higher runtime cost than [`Nmount::null_opt`], but is useful
/// when the name's lifetime doesn't outlive the `Nmount`, or it's a
/// different string type than `CStr`.
///
/// # Examples
/// ```
/// use nix::mount::Nmount;
///
/// let read_only = "ro";
/// let mut nmount: Nmount<'static> = Nmount::new();
/// nmount.null_opt_owned(read_only);
/// ```
pub fn null_opt_owned<P: ?Sized + NixPath>(
&mut self,
name: &P,
) -> &mut Self {
self.push_nix_path(name);
self.push_slice(&[], false);
self
}
/// Add a mount option as a [`CStr`].
///
/// # Examples
/// ```
/// use nix::mount::Nmount;
/// use std::ffi::CString;
///
/// let fstype = CString::new("fstype").unwrap();
/// let nullfs = CString::new("nullfs").unwrap();
/// Nmount::new()
/// .str_opt(&fstype, &nullfs);
/// ```
pub fn str_opt(&mut self, name: &'a CStr, val: &'a CStr) -> &mut Self {
self.push_slice(name.to_bytes_with_nul(), false);
self.push_slice(val.to_bytes_with_nul(), false);
self
}
/// Add a mount option as an owned string.
///
/// This has higher runtime cost than [`Nmount::str_opt`], but is useful
/// when the value's lifetime doesn't outlive the `Nmount`, or it's a
/// different string type than `CStr`.
///
/// # Examples
/// ```
/// use nix::mount::Nmount;
/// use std::path::Path;
///
/// let mountpoint = Path::new("/mnt");
/// Nmount::new()
/// .str_opt_owned("fspath", mountpoint.to_str().unwrap());
/// ```
pub fn str_opt_owned<P1, P2>(&mut self, name: &P1, val: &P2) -> &mut Self
where
P1: ?Sized + NixPath,
P2: ?Sized + NixPath,
{
self.push_nix_path(name);
self.push_nix_path(val);
self
}
/// Create a new `Nmount` struct with no options
pub fn new() -> Self {
Self::default()
}
/// Actually mount the file system.
pub fn nmount(&mut self, flags: MntFlags) -> NmountResult {
const ERRMSG_NAME: &[u8] = b"errmsg\0";
let mut errmsg = vec![0u8; 255];
// nmount can return extra error information via a "errmsg" return
// argument.
self.push_slice(ERRMSG_NAME, false);
// SAFETY: we are pushing a mutable iovec here, so we can't use
// the above method
self.iov.push(libc::iovec {
iov_base: errmsg.as_mut_ptr().cast(),
iov_len: errmsg.len(),
});
let niov = self.iov.len() as c_uint;
let iovp = self.iov.as_mut_ptr();
let res = unsafe { libc::nmount(iovp, niov, flags.bits()) };
match Errno::result(res) {
Ok(_) => Ok(()),
Err(error) => {
let errmsg = if errmsg[0] == 0 {
None
} else {
CStr::from_bytes_until_nul(&errmsg[..]).ok()
};
Err(NmountError::new(error, errmsg))
}
}
}
}
#[cfg(target_os = "freebsd")]
impl Drop for Nmount<'_> {
fn drop(&mut self) {
for (iov, is_owned) in self.iov.iter().zip(self.is_owned.iter()) {
if *is_owned {
// Free the owned string. Safe because we recorded ownership,
// and Nmount does not implement Clone.
unsafe {
drop(CString::from_raw(iov.iov_base as *mut c_char));
}
}
}
}
}
/// Unmount the file system mounted at `mountpoint`.
///
/// Useful flags include
/// * `MNT_FORCE` - Unmount even if still in use.
#[cfg_attr(
target_os = "freebsd",
doc = "
* `MNT_BYFSID` - `mountpoint` is not a path, but a file system ID
encoded as `FSID:val0:val1`, where `val0` and `val1`
are the contents of the `fsid_t val[]` array in decimal.
The file system that has the specified file system ID
will be unmounted. See
[`statfs`](crate::sys::statfs::statfs) to determine the
`fsid`.
"
)]
pub fn unmount<P>(mountpoint: &P, flags: MntFlags) -> Result<()>
where
P: ?Sized + NixPath,
{
let res = mountpoint.with_nix_path(|cstr| unsafe {
libc::unmount(cstr.as_ptr(), flags.bits())
})?;
Errno::result(res).map(drop)
}

152
vendor/nix/src/mount/linux.rs vendored Normal file
View File

@@ -0,0 +1,152 @@
use crate::errno::Errno;
use crate::{NixPath, Result};
use libc::{self, c_int, c_ulong};
libc_bitflags!(
/// Used with [`mount`].
pub struct MsFlags: c_ulong {
/// Mount read-only
MS_RDONLY;
/// Ignore suid and sgid bits
MS_NOSUID;
/// Disallow access to device special files
MS_NODEV;
/// Disallow program execution
MS_NOEXEC;
/// Writes are synced at once
MS_SYNCHRONOUS;
/// Alter flags of a mounted FS
MS_REMOUNT;
/// Allow mandatory locks on a FS
MS_MANDLOCK;
/// Directory modifications are synchronous
MS_DIRSYNC;
/// Do not update access times
MS_NOATIME;
/// Do not update directory access times
MS_NODIRATIME;
/// Linux 2.4.0 - Bind directory at different place
MS_BIND;
/// Move an existing mount to a new location
MS_MOVE;
/// Used to create a recursive bind mount.
MS_REC;
/// Suppress the display of certain kernel warning messages.
MS_SILENT;
/// VFS does not apply the umask
MS_POSIXACL;
/// The resulting mount cannot subsequently be bind mounted.
MS_UNBINDABLE;
/// Make this mount point private.
MS_PRIVATE;
/// If this is a shared mount point that is a member of a peer group
/// that contains other members, convert it to a slave mount.
MS_SLAVE;
/// Make this mount point shared.
MS_SHARED;
/// When a file on this filesystem is accessed, update the file's
/// last access time (atime) only if the current value of atime is
/// less than or equal to the file's last modification time (mtime) or
/// last status change time (ctime).
MS_RELATIME;
/// Mount request came from within the kernel
#[deprecated(since = "0.27.0", note = "Should only be used in-kernel")]
MS_KERNMOUNT;
/// Update inode I_version field
MS_I_VERSION;
/// Always update the last access time (atime) when files on this
/// filesystem are accessed.
MS_STRICTATIME;
/// Reduce on-disk updates of inode timestamps (atime, mtime, ctime) by
/// maintaining these changes only in memory.
MS_LAZYTIME;
#[deprecated(since = "0.27.0", note = "Should only be used in-kernel")]
#[allow(missing_docs)] // Not documented in Linux
MS_ACTIVE;
#[deprecated(since = "0.27.0", note = "Should only be used in-kernel")]
#[allow(missing_docs)] // Not documented in Linux
MS_NOUSER;
#[allow(missing_docs)] // Not documented in Linux; possibly kernel-only
MS_RMT_MASK;
#[allow(missing_docs)] // Not documented in Linux; possibly kernel-only
MS_MGC_VAL;
#[allow(missing_docs)] // Not documented in Linux; possibly kernel-only
MS_MGC_MSK;
}
);
libc_bitflags!(
/// Used with [`umount2].
pub struct MntFlags: c_int {
/// Attempt to unmount even if still in use, aborting pending requests.
MNT_FORCE;
/// Lazy unmount. Disconnect the file system immediately, but don't
/// actually unmount it until it ceases to be busy.
MNT_DETACH;
/// Mark the mount point as expired.
MNT_EXPIRE;
/// Don't dereference `target` if it is a symlink.
UMOUNT_NOFOLLOW;
}
);
/// Mount a file system.
///
/// # Arguments
/// - `source` - Specifies the file system. e.g. `/dev/sd0`.
/// - `target` - Specifies the destination. e.g. `/mnt`.
/// - `fstype` - The file system type, e.g. `ext4`.
/// - `flags` - Optional flags controlling the mount.
/// - `data` - Optional file system specific data.
///
/// # See Also
/// [`mount`](https://man7.org/linux/man-pages/man2/mount.2.html)
pub fn mount<
P1: ?Sized + NixPath,
P2: ?Sized + NixPath,
P3: ?Sized + NixPath,
P4: ?Sized + NixPath,
>(
source: Option<&P1>,
target: &P2,
fstype: Option<&P3>,
flags: MsFlags,
data: Option<&P4>,
) -> Result<()> {
let res = crate::with_opt_nix_path(source, |s| {
target.with_nix_path(|t| {
crate::with_opt_nix_path(fstype, |ty| {
crate::with_opt_nix_path(data, |d| unsafe {
libc::mount(
s,
t.as_ptr(),
ty,
flags.bits(),
d as *const libc::c_void,
)
})
})
})
})????;
Errno::result(res).map(drop)
}
/// Unmount the file system mounted at `target`.
pub fn umount<P: ?Sized + NixPath>(target: &P) -> Result<()> {
let res =
target.with_nix_path(|cstr| unsafe { libc::umount(cstr.as_ptr()) })?;
Errno::result(res).map(drop)
}
/// Unmount the file system mounted at `target`.
///
/// See also [`umount`](https://man7.org/linux/man-pages/man2/umount.2.html)
pub fn umount2<P: ?Sized + NixPath>(target: &P, flags: MntFlags) -> Result<()> {
let res = target.with_nix_path(|cstr| unsafe {
libc::umount2(cstr.as_ptr(), flags.bits())
})?;
Errno::result(res).map(drop)
}

18
vendor/nix/src/mount/mod.rs vendored Normal file
View File

@@ -0,0 +1,18 @@
//! Mount file systems
#[cfg(linux_android)]
mod linux;
#[cfg(linux_android)]
pub use self::linux::*;
#[cfg(bsd_without_apple)]
mod bsd_without_apple;
#[cfg(bsd_without_apple)]
pub use self::bsd_without_apple::*;
#[cfg(apple_targets)]
mod apple;
#[cfg(apple_targets)]
pub use self::apple::*;

353
vendor/nix/src/mqueue.rs vendored Normal file
View File

@@ -0,0 +1,353 @@
//! Posix Message Queue functions
//!
//! # Example
//!
// no_run because a kernel module may be required.
//! ```no_run
//! # use std::ffi::CString;
//! # use nix::mqueue::*;
//! use nix::sys::stat::Mode;
//!
//! const MSG_SIZE: mq_attr_member_t = 32;
//! let mq_name= "/a_nix_test_queue";
//!
//! let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
//! let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
//! let mqd0 = mq_open(mq_name, oflag0, mode, None).unwrap();
//! let msg_to_send = b"msg_1";
//! mq_send(&mqd0, msg_to_send, 1).unwrap();
//!
//! let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY;
//! let mqd1 = mq_open(mq_name, oflag1, mode, None).unwrap();
//! let mut buf = [0u8; 32];
//! let mut prio = 0u32;
//! let len = mq_receive(&mqd1, &mut buf, &mut prio).unwrap();
//! assert_eq!(prio, 1);
//! assert_eq!(msg_to_send, &buf[0..len]);
//!
//! mq_close(mqd1).unwrap();
//! mq_close(mqd0).unwrap();
//! ```
//! [Further reading and details on the C API](https://man7.org/linux/man-pages/man7/mq_overview.7.html)
use crate::errno::Errno;
use crate::NixPath;
use crate::Result;
use crate::sys::stat::Mode;
use libc::{self, mqd_t, size_t};
use std::mem;
#[cfg(any(
target_os = "linux",
target_os = "netbsd",
target_os = "dragonfly"
))]
use std::os::unix::io::{
AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd,
};
libc_bitflags! {
/// Used with [`mq_open`].
pub struct MQ_OFlag: libc::c_int {
/// Open the message queue for receiving messages.
O_RDONLY;
/// Open the queue for sending messages.
O_WRONLY;
/// Open the queue for both receiving and sending messages
O_RDWR;
/// Create a message queue.
O_CREAT;
/// If set along with `O_CREAT`, `mq_open` will fail if the message
/// queue name exists.
O_EXCL;
/// `mq_send` and `mq_receive` should fail with `EAGAIN` rather than
/// wait for resources that are not currently available.
O_NONBLOCK;
/// Set the close-on-exec flag for the message queue descriptor.
O_CLOEXEC;
}
}
/// A message-queue attribute, optionally used with [`mq_setattr`] and
/// [`mq_getattr`] and optionally [`mq_open`],
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct MqAttr {
mq_attr: libc::mq_attr,
}
/// Identifies an open POSIX Message Queue
// A safer wrapper around libc::mqd_t, which is a pointer on some platforms
// Deliberately is not Clone to prevent use-after-close scenarios
#[repr(transparent)]
#[derive(Debug)]
#[allow(missing_copy_implementations)]
pub struct MqdT(mqd_t);
// x32 compatibility
// See https://sourceware.org/bugzilla/show_bug.cgi?id=21279
/// Size of a message queue attribute member
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
pub type mq_attr_member_t = i64;
/// Size of a message queue attribute member
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
pub type mq_attr_member_t = libc::c_long;
impl MqAttr {
/// Create a new message queue attribute
///
/// # Arguments
///
/// - `mq_flags`: Either `0` or `O_NONBLOCK`.
/// - `mq_maxmsg`: Maximum number of messages on the queue.
/// - `mq_msgsize`: Maximum message size in bytes.
/// - `mq_curmsgs`: Number of messages currently in the queue.
pub fn new(
mq_flags: mq_attr_member_t,
mq_maxmsg: mq_attr_member_t,
mq_msgsize: mq_attr_member_t,
mq_curmsgs: mq_attr_member_t,
) -> MqAttr {
let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
unsafe {
let p = attr.as_mut_ptr();
(*p).mq_flags = mq_flags;
(*p).mq_maxmsg = mq_maxmsg;
(*p).mq_msgsize = mq_msgsize;
(*p).mq_curmsgs = mq_curmsgs;
MqAttr {
mq_attr: attr.assume_init(),
}
}
}
/// The current flags, either `0` or `O_NONBLOCK`.
pub const fn flags(&self) -> mq_attr_member_t {
self.mq_attr.mq_flags
}
/// The max number of messages that can be held by the queue
pub const fn maxmsg(&self) -> mq_attr_member_t {
self.mq_attr.mq_maxmsg
}
/// The maximum size of each message (in bytes)
pub const fn msgsize(&self) -> mq_attr_member_t {
self.mq_attr.mq_msgsize
}
/// The number of messages currently held in the queue
pub const fn curmsgs(&self) -> mq_attr_member_t {
self.mq_attr.mq_curmsgs
}
}
/// Open a message queue
///
/// See also [`mq_open(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html)
// The mode.bits() cast is only lossless on some OSes
#[allow(clippy::cast_lossless)]
pub fn mq_open<P>(
name: &P,
oflag: MQ_OFlag,
mode: Mode,
attr: Option<&MqAttr>,
) -> Result<MqdT>
where
P: ?Sized + NixPath,
{
let res = name.with_nix_path(|cstr| match attr {
Some(mq_attr) => unsafe {
libc::mq_open(
cstr.as_ptr(),
oflag.bits(),
mode.bits() as libc::c_int,
&mq_attr.mq_attr as *const libc::mq_attr,
)
},
None => unsafe { libc::mq_open(cstr.as_ptr(), oflag.bits()) },
})?;
Errno::result(res).map(MqdT)
}
/// Remove a message queue
///
/// See also [`mq_unlink(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html)
pub fn mq_unlink<P>(name: &P) -> Result<()>
where
P: ?Sized + NixPath,
{
let res =
name.with_nix_path(|cstr| unsafe { libc::mq_unlink(cstr.as_ptr()) })?;
Errno::result(res).map(drop)
}
/// Close a message queue
///
/// See also [`mq_close(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html)
pub fn mq_close(mqdes: MqdT) -> Result<()> {
let res = unsafe { libc::mq_close(mqdes.0) };
Errno::result(res).map(drop)
}
/// Receive a message from a message queue
///
/// See also [`mq_receive(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
pub fn mq_receive(
mqdes: &MqdT,
message: &mut [u8],
msg_prio: &mut u32,
) -> Result<usize> {
let len = message.len() as size_t;
let res = unsafe {
libc::mq_receive(
mqdes.0,
message.as_mut_ptr().cast(),
len,
msg_prio as *mut u32,
)
};
Errno::result(res).map(|r| r as usize)
}
feature! {
#![feature = "time"]
use crate::sys::time::TimeSpec;
/// Receive a message from a message queue with a timeout
///
/// See also ['mq_timedreceive(2)'](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
pub fn mq_timedreceive(
mqdes: &MqdT,
message: &mut [u8],
msg_prio: &mut u32,
abstime: &TimeSpec,
) -> Result<usize> {
let len = message.len() as size_t;
let res = unsafe {
libc::mq_timedreceive(
mqdes.0,
message.as_mut_ptr().cast(),
len,
msg_prio as *mut u32,
abstime.as_ref(),
)
};
Errno::result(res).map(|r| r as usize)
}
}
/// Send a message to a message queue
///
/// See also [`mq_send(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html)
pub fn mq_send(mqdes: &MqdT, message: &[u8], msq_prio: u32) -> Result<()> {
let res = unsafe {
libc::mq_send(mqdes.0, message.as_ptr().cast(), message.len(), msq_prio)
};
Errno::result(res).map(drop)
}
/// Get message queue attributes
///
/// See also [`mq_getattr(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html)
pub fn mq_getattr(mqd: &MqdT) -> Result<MqAttr> {
let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
let res = unsafe { libc::mq_getattr(mqd.0, attr.as_mut_ptr()) };
Errno::result(res).map(|_| unsafe {
MqAttr {
mq_attr: attr.assume_init(),
}
})
}
/// Set the attributes of the message queue. Only `O_NONBLOCK` can be set,
/// everything else will be ignored. Returns the old attributes.
///
/// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()`
/// convenience functions as they are easier to use.
///
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html)
pub fn mq_setattr(mqd: &MqdT, newattr: &MqAttr) -> Result<MqAttr> {
let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
let res = unsafe {
libc::mq_setattr(
mqd.0,
&newattr.mq_attr as *const libc::mq_attr,
attr.as_mut_ptr(),
)
};
Errno::result(res).map(|_| unsafe {
MqAttr {
mq_attr: attr.assume_init(),
}
})
}
/// 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: &MqdT) -> Result<MqAttr> {
let oldattr = mq_getattr(mqd)?;
let newattr = MqAttr::new(
mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()),
oldattr.mq_attr.mq_maxmsg,
oldattr.mq_attr.mq_msgsize,
oldattr.mq_attr.mq_curmsgs,
);
mq_setattr(mqd, &newattr)
}
/// Convenience function.
/// Removes `O_NONBLOCK` attribute for a given message queue descriptor
/// Returns the old attributes
pub fn mq_remove_nonblock(mqd: &MqdT) -> Result<MqAttr> {
let oldattr = mq_getattr(mqd)?;
let newattr = MqAttr::new(
0,
oldattr.mq_attr.mq_maxmsg,
oldattr.mq_attr.mq_msgsize,
oldattr.mq_attr.mq_curmsgs,
);
mq_setattr(mqd, &newattr)
}
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
impl AsFd for MqdT {
/// Borrow the underlying message queue descriptor.
fn as_fd(&self) -> BorrowedFd {
// SAFETY: [MqdT] will only contain a valid fd by construction.
unsafe { BorrowedFd::borrow_raw(self.0) }
}
}
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
impl AsRawFd for MqdT {
/// Return the underlying message queue descriptor.
///
/// Returned descriptor is a "shallow copy" of the descriptor, so it refers
/// to the same underlying kernel object as `self`.
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
impl FromRawFd for MqdT {
/// Construct an [MqdT] from [RawFd].
///
/// # Safety
/// The `fd` given must be a valid and open file descriptor for a message
/// queue.
unsafe fn from_raw_fd(fd: RawFd) -> MqdT {
MqdT(fd)
}
}
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
impl IntoRawFd for MqdT {
/// Consume this [MqdT] and return a [RawFd].
fn into_raw_fd(self) -> RawFd {
self.0
}
}

404
vendor/nix/src/net/if_.rs vendored Normal file
View File

@@ -0,0 +1,404 @@
//! Network interface name resolution.
//!
//! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
//! or "socan1" into device numbers.
use std::{ffi::{CStr, CString}, fmt};
use crate::{errno::Errno, Error, NixPath, Result};
use libc::{c_uint, IF_NAMESIZE};
#[cfg(not(solarish))]
/// type alias for InterfaceFlags
pub type IflagsType = libc::c_int;
#[cfg(solarish)]
/// type alias for InterfaceFlags
pub type IflagsType = libc::c_longlong;
/// Resolve an interface into an interface number.
pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
let if_index = name
.with_nix_path(|name| unsafe { libc::if_nametoindex(name.as_ptr()) })?;
if if_index == 0 {
Err(Error::last())
} else {
Ok(if_index)
}
}
/// Resolve an interface number into an interface.
pub fn if_indextoname(index: c_uint) -> Result<CString> {
// We need to allocate this anyway, so doing it directly is faster.
let mut buf = vec![0u8; IF_NAMESIZE];
let return_buf = unsafe {
libc::if_indextoname(index, buf.as_mut_ptr().cast())
};
Errno::result(return_buf.cast())?;
Ok(CStr::from_bytes_until_nul(buf.as_slice()).unwrap().to_owned())
}
libc_bitflags!(
/// Standard interface flags, used by `getifaddrs`
pub struct InterfaceFlags: IflagsType {
/// Interface is running. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_UP as IflagsType;
/// Valid broadcast address set. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_BROADCAST as IflagsType;
/// Internal debugging flag. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(not(any(target_os = "haiku", target_os = "cygwin")))]
IFF_DEBUG as IflagsType;
/// Interface is a loopback interface. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_LOOPBACK as IflagsType;
/// Interface is a point-to-point link. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_POINTOPOINT as IflagsType;
/// Avoid use of trailers. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(
linux_android,
solarish,
apple_targets,
target_os = "fuchsia",
target_os = "netbsd",
target_os = "cygwin"))]
IFF_NOTRAILERS as IflagsType;
/// Interface manages own routes.
#[cfg(any(target_os = "dragonfly"))]
IFF_SMART as IflagsType;
/// Resources allocated. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(
linux_android,
bsd,
solarish,
target_os = "fuchsia",
target_os = "cygwin"))]
IFF_RUNNING as IflagsType;
/// No arp protocol, L2 destination address not set. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_NOARP as IflagsType;
/// Interface is in promiscuous mode. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_PROMISC as IflagsType;
/// Receive all multicast packets. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(not(target_os = "cygwin"))]
IFF_ALLMULTI as IflagsType;
/// Master of a load balancing bundle. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_MASTER;
/// transmission in progress, tx hardware queue is full
#[cfg(any(target_os = "freebsd", apple_targets, netbsdlike))]
IFF_OACTIVE;
/// Protocol code on board.
#[cfg(solarish)]
IFF_INTELLIGENT as IflagsType;
/// Slave of a load balancing bundle. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_SLAVE;
/// Can't hear own transmissions.
#[cfg(bsd)]
IFF_SIMPLEX;
/// Supports multicast. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
IFF_MULTICAST as IflagsType;
/// Per link layer defined bit.
#[cfg(bsd)]
IFF_LINK0;
/// Multicast using broadcast.
#[cfg(solarish)]
IFF_MULTI_BCAST as IflagsType;
/// Is able to select media type via ifmap. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_PORTSEL;
/// Per link layer defined bit.
#[cfg(bsd)]
IFF_LINK1;
/// Non-unique address.
#[cfg(solarish)]
IFF_UNNUMBERED as IflagsType;
/// Auto media selection active. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_AUTOMEDIA;
/// Per link layer defined bit.
#[cfg(bsd)]
IFF_LINK2;
/// Use alternate physical connection.
#[cfg(any(freebsdlike, apple_targets))]
IFF_ALTPHYS;
/// DHCP controls interface.
#[cfg(solarish)]
IFF_DHCPRUNNING as IflagsType;
/// The addresses are lost when the interface goes down. (see
/// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_DYNAMIC;
/// Do not advertise.
#[cfg(solarish)]
IFF_PRIVATE as IflagsType;
/// Driver signals L1 up. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
IFF_LOWER_UP;
/// Interface is in polling mode.
#[cfg(any(target_os = "dragonfly"))]
IFF_POLLING_COMPAT;
/// Unconfigurable using ioctl(2).
#[cfg(any(target_os = "freebsd"))]
IFF_CANTCONFIG;
/// Do not transmit packets.
#[cfg(solarish)]
IFF_NOXMIT as IflagsType;
/// Driver signals dormant. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux", target_os = "cygwin"))]
IFF_DORMANT;
/// User-requested promisc mode.
#[cfg(freebsdlike)]
IFF_PPROMISC;
/// Just on-link subnet.
#[cfg(solarish)]
IFF_NOLOCAL as IflagsType;
/// Echo sent packets. Volatile.
#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
IFF_ECHO;
/// User-requested monitor mode.
#[cfg(freebsdlike)]
IFF_MONITOR;
/// Address is deprecated.
#[cfg(solarish)]
IFF_DEPRECATED as IflagsType;
/// Static ARP.
#[cfg(freebsdlike)]
IFF_STATICARP;
/// Address from stateless addrconf.
#[cfg(solarish)]
IFF_ADDRCONF as IflagsType;
/// Interface is in polling mode.
#[cfg(any(target_os = "dragonfly"))]
IFF_NPOLLING;
/// Router on interface.
#[cfg(solarish)]
IFF_ROUTER as IflagsType;
/// Interface is in polling mode.
#[cfg(any(target_os = "dragonfly"))]
IFF_IDIRECT;
/// Interface is winding down
#[cfg(any(target_os = "freebsd"))]
IFF_DYING;
/// No NUD on interface.
#[cfg(solarish)]
IFF_NONUD as IflagsType;
/// Interface is being renamed
#[cfg(any(target_os = "freebsd"))]
IFF_RENAMING;
/// Anycast address.
#[cfg(solarish)]
IFF_ANYCAST as IflagsType;
/// Don't exchange routing info.
#[cfg(solarish)]
IFF_NORTEXCH as IflagsType;
/// Do not provide packet information
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_NO_PI as IflagsType;
/// TUN device (no Ethernet headers)
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_TUN as IflagsType;
/// TAP device
#[cfg(any(linux_android, target_os = "fuchsia"))]
IFF_TAP as IflagsType;
/// IPv4 interface.
#[cfg(solarish)]
IFF_IPV4 as IflagsType;
/// IPv6 interface.
#[cfg(solarish)]
IFF_IPV6 as IflagsType;
/// in.mpathd test address
#[cfg(solarish)]
IFF_NOFAILOVER as IflagsType;
/// Interface has failed
#[cfg(solarish)]
IFF_FAILED as IflagsType;
/// Interface is a hot-spare
#[cfg(solarish)]
IFF_STANDBY as IflagsType;
/// Functioning but not used
#[cfg(solarish)]
IFF_INACTIVE as IflagsType;
/// Interface is offline
#[cfg(solarish)]
IFF_OFFLINE as IflagsType;
/// Has CoS marking supported
#[cfg(solarish)]
IFF_COS_ENABLED as IflagsType;
/// Prefer as source addr
#[cfg(solarish)]
IFF_PREFERRED as IflagsType;
/// RFC3041
#[cfg(solarish)]
IFF_TEMPORARY as IflagsType;
/// MTU set
#[cfg(solarish)]
IFF_FIXEDMTU as IflagsType;
/// Cannot send/receive packets
#[cfg(solarish)]
IFF_VIRTUAL as IflagsType;
/// Local address in use
#[cfg(solarish)]
IFF_DUPLICATE as IflagsType;
/// IPMP IP interface
#[cfg(solarish)]
IFF_IPMP as IflagsType;
}
);
impl fmt::Display for InterfaceFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
bitflags::parser::to_writer(self, f)
}
}
#[cfg(any(
bsd,
target_os = "fuchsia",
target_os = "linux",
solarish,
))]
mod if_nameindex {
use super::*;
use std::ffi::CStr;
use std::fmt;
use std::marker::PhantomData;
use std::ptr::NonNull;
/// A network interface. Has a name like "eth0" or "wlp4s0" or "wlan0", as well as an index
/// (1, 2, 3, etc) that identifies it in the OS's networking stack.
#[allow(missing_copy_implementations)]
#[repr(transparent)]
pub struct Interface(libc::if_nameindex);
impl Interface {
/// Obtain the index of this interface.
pub fn index(&self) -> c_uint {
self.0.if_index
}
/// Obtain the name of this interface.
pub fn name(&self) -> &CStr {
unsafe { CStr::from_ptr(self.0.if_name) }
}
}
impl fmt::Debug for Interface {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Interface")
.field("index", &self.index())
.field("name", &self.name())
.finish()
}
}
/// A list of the network interfaces available on this system. Obtained from [`if_nameindex()`].
#[repr(transparent)]
pub struct Interfaces {
ptr: NonNull<libc::if_nameindex>,
}
impl Interfaces {
/// Iterate over the interfaces in this list.
#[inline]
pub fn iter(&self) -> InterfacesIter<'_> {
self.into_iter()
}
/// Convert this to a slice of interfaces. Note that the underlying interfaces list is
/// null-terminated, so calling this calculates the length. If random access isn't needed,
/// [`Interfaces::iter()`] should be used instead.
pub fn to_slice(&self) -> &[Interface] {
let ifs = self.ptr.as_ptr().cast();
let len = self.iter().count();
unsafe { std::slice::from_raw_parts(ifs, len) }
}
}
impl Drop for Interfaces {
fn drop(&mut self) {
unsafe { libc::if_freenameindex(self.ptr.as_ptr()) };
}
}
impl fmt::Debug for Interfaces {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.to_slice().fmt(f)
}
}
impl<'a> IntoIterator for &'a Interfaces {
type IntoIter = InterfacesIter<'a>;
type Item = &'a Interface;
#[inline]
fn into_iter(self) -> Self::IntoIter {
InterfacesIter {
ptr: self.ptr.as_ptr(),
_marker: PhantomData,
}
}
}
/// An iterator over the interfaces in an [`Interfaces`].
#[derive(Debug)]
pub struct InterfacesIter<'a> {
ptr: *const libc::if_nameindex,
_marker: PhantomData<&'a Interfaces>,
}
impl<'a> Iterator for InterfacesIter<'a> {
type Item = &'a Interface;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
unsafe {
if (*self.ptr).if_index == 0 {
None
} else {
let ret = &*(self.ptr as *const Interface);
self.ptr = self.ptr.add(1);
Some(ret)
}
}
}
}
/// Retrieve a list of the network interfaces available on the local system.
///
/// ```
/// let interfaces = nix::net::if_::if_nameindex().unwrap();
/// for iface in &interfaces {
/// println!("Interface #{} is called {}", iface.index(), iface.name().to_string_lossy());
/// }
/// ```
pub fn if_nameindex() -> Result<Interfaces> {
unsafe {
let ifs = libc::if_nameindex();
let ptr = NonNull::new(ifs).ok_or_else(Error::last)?;
Ok(Interfaces { ptr })
}
}
}
#[cfg(any(
bsd,
target_os = "fuchsia",
target_os = "linux",
solarish,
))]
pub use if_nameindex::*;

4
vendor/nix/src/net/mod.rs vendored Normal file
View File

@@ -0,0 +1,4 @@
//! Functionality involving network interfaces
// To avoid clashing with the keyword "if", we use "if_" as the module name.
// The original header is called "net/if.h".
pub mod if_;

269
vendor/nix/src/poll.rs vendored Normal file
View File

@@ -0,0 +1,269 @@
//! Wait for events to trigger on specific file descriptors
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd};
use crate::errno::Errno;
pub use crate::poll_timeout::{PollTimeout, PollTimeoutTryFromError};
use crate::Result;
/// This is a wrapper around `libc::pollfd`.
///
/// It's meant to be used as an argument to the [`poll`](fn.poll.html) and
/// [`ppoll`](fn.ppoll.html) functions to specify the events of interest
/// for a specific file descriptor.
///
/// After a call to `poll` or `ppoll`, the events that occurred can be retrieved by calling
/// [`revents()`](#method.revents) on the `PollFd` object from the array passed to `poll`.
#[repr(transparent)]
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct PollFd<'fd> {
pollfd: libc::pollfd,
_fd: std::marker::PhantomData<BorrowedFd<'fd>>,
}
impl<'fd> PollFd<'fd> {
/// Creates a new `PollFd` specifying the events of interest
/// for a given file descriptor.
///
/// # Examples
/// ```no_run
/// # use std::os::unix::io::{AsFd, AsRawFd, FromRawFd};
/// # use nix::{
/// # poll::{PollTimeout, PollFd, PollFlags, poll},
/// # unistd::{pipe, read}
/// # };
/// let (r, w) = pipe().unwrap();
/// let pfd = PollFd::new(r.as_fd(), PollFlags::POLLIN);
/// ```
/// These are placed in an array and passed to [`poll`] or [`ppoll`](fn.ppoll.html).
// Unlike I/O functions, constructors like this must take `BorrowedFd`
// instead of AsFd or &AsFd. Otherwise, an `OwnedFd` argument would be
// dropped at the end of the method, leaving the structure referencing a
// closed file descriptor. For example:
//
// ```rust
// let (r, _) = pipe().unwrap();
// let pollfd = PollFd::new(r, flag); // Drops the OwnedFd
// // Do something with `pollfd`, which uses the CLOSED fd.
// ```
pub fn new(fd: BorrowedFd<'fd>, events: PollFlags) -> PollFd<'fd> {
PollFd {
pollfd: libc::pollfd {
fd: fd.as_raw_fd(),
events: events.bits(),
revents: PollFlags::empty().bits(),
},
_fd: std::marker::PhantomData,
}
}
/// Returns the events that occurred 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)
}
/// Returns if any of the events of interest 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.
///
/// Equivalent to `x.revents()? != PollFlags::empty()`.
///
/// This is marginally more efficient than [`PollFd::all`].
pub fn any(&self) -> Option<bool> {
Some(self.revents()? != PollFlags::empty())
}
/// Returns if all the events of interest 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.
///
/// Equivalent to `x.revents()? & x.events() == x.events()`.
///
/// This is marginally less efficient than [`PollFd::any`].
pub fn all(&self) -> Option<bool> {
Some(self.revents()? & self.events() == self.events())
}
/// 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 AsFd for PollFd<'_> {
fn as_fd(&self) -> BorrowedFd<'_> {
// Safety:
//
// BorrowedFd::borrow_raw(RawFd) requires that the raw fd being passed
// must remain open for the duration of the returned BorrowedFd, this is
// guaranteed as the returned BorrowedFd has the lifetime parameter same
// as `self`:
// "fn as_fd<'self>(&'self self) -> BorrowedFd<'self>"
// which means that `self` (PollFd) is guaranteed to outlive the returned
// BorrowedFd. (Lifetime: PollFd > BorrowedFd)
//
// And the lifetime parameter of PollFd::new(fd, ...) ensures that `fd`
// (an owned file descriptor) must outlive the returned PollFd:
// "pub fn new<Fd: AsFd>(fd: &'fd Fd, events: PollFlags) -> PollFd<'fd>"
// (Lifetime: Owned fd > PollFd)
//
// With two above relationships, we can conclude that the `Owned file
// descriptor` will outlive the returned BorrowedFd,
// (Lifetime: Owned fd > BorrowedFd)
// i.e., the raw fd being passed will remain valid for the lifetime of
// the returned BorrowedFd.
unsafe { BorrowedFd::borrow_raw(self.pollfd.fd) }
}
}
libc_bitflags! {
/// These flags define the different events that can be monitored by `poll` and `ppoll`
pub struct PollFlags: libc::c_short {
/// There is data to read.
POLLIN;
/// There is some exceptional condition on the file descriptor.
///
/// Possibilities include:
///
/// * There is out-of-band data on a TCP socket (see
/// [tcp(7)](https://man7.org/linux/man-pages/man7/tcp.7.html)).
/// * A pseudoterminal master in packet mode has seen a state
/// change on the slave (see
/// [ioctl_tty(2)](https://man7.org/linux/man-pages/man2/ioctl_tty.2.html)).
/// * A cgroup.events file has been modified (see
/// [cgroups(7)](https://man7.org/linux/man-pages/man7/cgroups.7.html)).
POLLPRI;
/// Writing is now possible, though a write larger that the
/// available space in a socket or pipe will still block (unless
/// `O_NONBLOCK` is set).
POLLOUT;
/// Equivalent to [`POLLIN`](constant.POLLIN.html)
#[cfg(not(target_os = "redox"))]
POLLRDNORM;
#[cfg(not(target_os = "redox"))]
/// Equivalent to [`POLLOUT`](constant.POLLOUT.html)
POLLWRNORM;
/// Priority band data can be read (generally unused on Linux).
#[cfg(not(target_os = "redox"))]
POLLRDBAND;
/// Priority data may be written.
#[cfg(not(target_os = "redox"))]
POLLWRBAND;
/// Error condition (only returned in
/// [`PollFd::revents`](struct.PollFd.html#method.revents);
/// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
/// This bit is also set for a file descriptor referring to the
/// write end of a pipe when the read end has been closed.
POLLERR;
/// Hang up (only returned in [`PollFd::revents`](struct.PollFd.html#method.revents);
/// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
/// Note that when reading from a channel such as a pipe or a stream
/// socket, this event merely indicates that the peer closed its
/// end of the channel. Subsequent reads from the channel will
/// return 0 (end of file) only after all outstanding data in the
/// channel has been consumed.
POLLHUP;
/// Invalid request: `fd` not open (only returned in
/// [`PollFd::revents`](struct.PollFd.html#method.revents);
/// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
POLLNVAL;
}
}
/// `poll` waits for one of a set of file descriptors to become ready to perform I/O.
/// ([`poll(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html))
///
/// `fds` contains all [`PollFd`](struct.PollFd.html) to poll.
/// The function will return as soon as any event occur for any of these `PollFd`s.
///
/// The `timeout` argument specifies the number of milliseconds that `poll()`
/// should block waiting for a file descriptor to become ready. The call
/// will block until either:
///
/// * a file descriptor becomes ready;
/// * the call is interrupted by a signal handler; or
/// * the timeout expires.
///
/// Note that the timeout interval will be rounded up to the system clock
/// granularity, and kernel scheduling delays mean that the blocking
/// interval may overrun by a small amount. Specifying a [`PollTimeout::NONE`]
/// in timeout means an infinite timeout. Specifying a timeout of
/// [`PollTimeout::ZERO`] causes `poll()` to return immediately, even if no file
/// descriptors are ready.
///
/// The return value contains the number of `fds` which have selected events ([`PollFd::revents`]).
///
/// # Examples
/// ```no_run
/// # use std::os::unix::io::{AsFd, AsRawFd, FromRawFd};
/// # use nix::{
/// # poll::{PollTimeout, PollFd, PollFlags, poll},
/// # unistd::{pipe, read}
/// # };
/// let (r0, w0) = pipe().unwrap();
/// let (r1, w1) = pipe().unwrap();
///
/// let mut pollfds = [
/// PollFd::new(r0.as_fd(), PollFlags::POLLIN),
/// PollFd::new(r1.as_fd(), PollFlags::POLLIN),
/// ];
///
/// let nready = poll(&mut pollfds, PollTimeout::NONE).unwrap();
/// assert!(nready >= 1); // Since there is no timeout
///
/// let mut buf = [0u8; 80];
/// if pollfds[0].any().unwrap_or_default() {
/// read(&r0, &mut buf[..]);
/// } else if pollfds[1].any().unwrap_or_default() {
/// read(&r1, &mut buf[..]);
/// }
/// ```
pub fn poll<T: Into<PollTimeout>>(
fds: &mut [PollFd],
timeout: T,
) -> Result<libc::c_int> {
let res = unsafe {
libc::poll(
fds.as_mut_ptr().cast(),
fds.len() as libc::nfds_t,
i32::from(timeout.into()),
)
};
Errno::result(res)
}
feature! {
#![feature = "signal"]
/// `ppoll()` allows an application to safely wait until either a file
/// descriptor becomes ready or until a signal is caught.
/// ([`poll(2)`](https://man7.org/linux/man-pages/man2/poll.2.html))
///
/// `ppoll` behaves like [`poll`], but let you specify what signals may interrupt it
/// with the `sigmask` argument. If you want `ppoll` to block indefinitely,
/// specify `None` as `timeout` (it is like `timeout = -1` for `poll`).
/// If `sigmask` is `None`, then no signal mask manipulation is performed,
/// so in that case `ppoll` differs from `poll` only in the precision of the
/// timeout argument.
///
#[cfg(any(linux_android, freebsdlike))]
pub fn ppoll(
fds: &mut [PollFd],
timeout: Option<crate::sys::time::TimeSpec>,
sigmask: Option<crate::sys::signal::SigSet>
) -> Result<libc::c_int>
{
let timeout = timeout.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
let sigmask = sigmask.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
let res = unsafe {
libc::ppoll(fds.as_mut_ptr().cast(),
fds.len() as libc::nfds_t,
timeout,
sigmask)
};
Errno::result(res)
}
}

224
vendor/nix/src/poll_timeout.rs vendored Normal file
View File

@@ -0,0 +1,224 @@
use std::time::Duration;
/// PollTimeout argument for polling.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct PollTimeout(i32);
impl PollTimeout {
/// Blocks indefinitely.
///
/// > Specifying a negative value in timeout means an infinite timeout.
pub const NONE: Self = Self(-1);
/// Returns immediately.
///
/// > Specifying a timeout of zero causes poll() to return immediately, even if no file
/// > descriptors are ready.
pub const ZERO: Self = Self(0);
/// Blocks for at most [`i32::MAX`] milliseconds.
pub const MAX: Self = Self(i32::MAX);
/// Returns if `self` equals [`PollTimeout::NONE`].
pub fn is_none(&self) -> bool {
// > Specifying a negative value in timeout means an infinite timeout.
*self <= Self::NONE
}
/// Returns if `self` does not equal [`PollTimeout::NONE`].
pub fn is_some(&self) -> bool {
!self.is_none()
}
/// Returns the timeout in milliseconds if there is some, otherwise returns `None`.
pub fn as_millis(&self) -> Option<u32> {
self.is_some().then_some(u32::try_from(self.0).unwrap())
}
/// Returns the timeout as a `Duration` if there is some, otherwise returns `None`.
pub fn duration(&self) -> Option<Duration> {
self.as_millis()
.map(|x| Duration::from_millis(u64::from(x)))
}
}
/// Error type for integer conversions into `PollTimeout`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PollTimeoutTryFromError {
/// Passing a value less than -1 is invalid on some systems, see
/// <https://man.freebsd.org/cgi/man.cgi?poll#end>.
TooNegative,
/// Passing a value greater than `i32::MAX` is invalid.
TooPositive,
}
impl std::fmt::Display for PollTimeoutTryFromError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::TooNegative => write!(f, "Passed a negative timeout less than -1."),
Self::TooPositive => write!(f, "Passed a positive timeout greater than `i32::MAX` milliseconds.")
}
}
}
impl std::error::Error for PollTimeoutTryFromError {}
impl<T: Into<PollTimeout>> From<Option<T>> for PollTimeout {
fn from(x: Option<T>) -> Self {
x.map_or(Self::NONE, |x| x.into())
}
}
impl TryFrom<Duration> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: Duration) -> std::result::Result<Self, Self::Error> {
Ok(Self(
i32::try_from(x.as_millis())
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
))
}
}
impl TryFrom<u128> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: u128) -> std::result::Result<Self, Self::Error> {
Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
))
}
}
impl TryFrom<u64> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: u64) -> std::result::Result<Self, Self::Error> {
Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
))
}
}
impl TryFrom<u32> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: u32) -> std::result::Result<Self, Self::Error> {
Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
))
}
}
impl From<u16> for PollTimeout {
fn from(x: u16) -> Self {
Self(i32::from(x))
}
}
impl From<u8> for PollTimeout {
fn from(x: u8) -> Self {
Self(i32::from(x))
}
}
impl TryFrom<i128> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i128) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
)),
}
}
}
impl TryFrom<i64> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i64) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(
i32::try_from(x)
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
)),
}
}
}
impl TryFrom<i32> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i32) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(x)),
}
}
}
impl TryFrom<i16> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i16) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(i32::from(x))),
}
}
}
impl TryFrom<i8> for PollTimeout {
type Error = PollTimeoutTryFromError;
fn try_from(x: i8) -> std::result::Result<Self, Self::Error> {
match x {
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
-1.. => Ok(Self(i32::from(x))),
}
}
}
impl TryFrom<PollTimeout> for Duration {
type Error = ();
fn try_from(x: PollTimeout) -> std::result::Result<Self, ()> {
x.duration().ok_or(())
}
}
impl TryFrom<PollTimeout> for u128 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for u64 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for u32 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for u16 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for u8 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl From<PollTimeout> for i128 {
fn from(x: PollTimeout) -> Self {
Self::from(x.0)
}
}
impl From<PollTimeout> for i64 {
fn from(x: PollTimeout) -> Self {
Self::from(x.0)
}
}
impl From<PollTimeout> for i32 {
fn from(x: PollTimeout) -> Self {
x.0
}
}
impl TryFrom<PollTimeout> for i16 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}
impl TryFrom<PollTimeout> for i8 {
type Error = <Self as TryFrom<i32>>::Error;
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
Self::try_from(x.0)
}
}

393
vendor/nix/src/pty.rs vendored Normal file
View File

@@ -0,0 +1,393 @@
//! Create master and slave virtual pseudo-terminals (PTYs)
pub use libc::pid_t as SessionId;
pub use libc::winsize as Winsize;
use std::ffi::CStr;
use std::io;
#[cfg(not(target_os = "aix"))]
use std::mem;
use std::os::unix::prelude::*;
use crate::errno::Errno;
#[cfg(not(target_os = "aix"))]
use crate::sys::termios::Termios;
#[cfg(all(feature = "process", not(target_os = "aix")))]
use crate::unistd::Pid;
use crate::{fcntl, unistd, Result};
/// Representation of a master/slave pty pair
///
/// This is returned by [`openpty`].
#[derive(Debug)]
pub struct OpenptyResult {
/// The master port in a virtual pty pair
pub master: OwnedFd,
/// The slave port in a virtual pty pair
pub slave: OwnedFd,
}
feature! {
#![feature = "process"]
/// A successful result of [`forkpty()`].
#[derive(Debug)]
pub enum ForkptyResult {
/// This is the parent process of the underlying fork.
Parent {
/// The PID of the fork's child process
child: Pid,
/// A file descriptor referring to master side of the pseudoterminal of
/// the child process.
master: OwnedFd,
},
/// This is the child process of the underlying fork.
Child,
}
}
/// Representation of the Master device in a master/slave pty pair
///
/// While this datatype is a thin wrapper around `OwnedFd`, it enforces that the available PTY
/// functions are given the correct file descriptor.
#[derive(Debug)]
pub struct PtyMaster(OwnedFd);
impl PtyMaster {
/// Constructs a `PytMaster` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `PtyMaster`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self(fd)
}
}
impl AsRawFd for PtyMaster {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl AsFd for PtyMaster {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl From<PtyMaster> for OwnedFd {
fn from(value: PtyMaster) -> Self {
value.0
}
}
impl IntoRawFd for PtyMaster {
fn into_raw_fd(self) -> RawFd {
let fd = self.0;
fd.into_raw_fd()
}
}
impl io::Read for PtyMaster {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unistd::read(&self.0, buf).map_err(io::Error::from)
}
}
impl io::Write for PtyMaster {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unistd::write(&self.0, buf).map_err(io::Error::from)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl io::Read for &PtyMaster {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unistd::read(&self.0, buf).map_err(io::Error::from)
}
}
impl io::Write for &PtyMaster {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unistd::write(&self.0, buf).map_err(io::Error::from)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
/// Grant access to a slave pseudoterminal (see
/// [`grantpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html))
///
/// `grantpt()` changes the mode and owner of the slave pseudoterminal device corresponding to the
/// master pseudoterminal referred to by `fd`. This is a necessary step towards opening the slave.
#[inline]
pub fn grantpt(fd: &PtyMaster) -> Result<()> {
if unsafe { libc::grantpt(fd.as_raw_fd()) } < 0 {
return Err(Errno::last());
}
Ok(())
}
/// Open a pseudoterminal device (see
/// [`posix_openpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html))
///
/// `posix_openpt()` returns a file descriptor to an existing unused pseudoterminal master device.
///
/// # Examples
///
/// A common use case with this function is to open both a master and slave PTY pair. This can be
/// done as follows:
///
/// ```
/// use std::path::Path;
/// use nix::fcntl::{OFlag, open};
/// use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt};
/// use nix::sys::stat::Mode;
///
/// # #[allow(dead_code)]
/// # fn run() -> nix::Result<()> {
/// // Open a new PTY master
/// let master_fd = posix_openpt(OFlag::O_RDWR)?;
///
/// // Allow a slave to be generated for it
/// grantpt(&master_fd)?;
/// unlockpt(&master_fd)?;
///
/// // Get the name of the slave
/// let slave_name = unsafe { ptsname(&master_fd) }?;
///
/// // Try to open the slave
/// let _slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, Mode::empty())?;
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
let fd = unsafe { libc::posix_openpt(flags.bits()) };
if fd < 0 {
return Err(Errno::last());
}
Ok(PtyMaster(unsafe { OwnedFd::from_raw_fd(fd) }))
}
/// Get the name of the slave pseudoterminal (see
/// [`ptsname(3)`](https://man7.org/linux/man-pages/man3/ptsname.3.html))
///
/// `ptsname()` returns the name of the slave pseudoterminal device corresponding to the master
/// referred to by `fd`.
///
/// This value is useful for opening the slave pty once the master has already been opened with
/// `posix_openpt()`.
///
/// # Safety
///
/// `ptsname()` mutates global variables and is *not* threadsafe.
/// Mutating global variables is always considered `unsafe` by Rust and this
/// function is marked as `unsafe` to reflect that.
///
/// For a threadsafe and non-`unsafe` alternative on Linux, see `ptsname_r()`.
#[inline]
pub unsafe fn ptsname(fd: &PtyMaster) -> Result<String> {
let name_ptr = unsafe { libc::ptsname(fd.as_raw_fd()) };
if name_ptr.is_null() {
return Err(Errno::last());
}
let name = unsafe { CStr::from_ptr(name_ptr) };
Ok(name.to_string_lossy().into_owned())
}
/// Get the name of the slave pseudoterminal (see
/// [`ptsname(3)`](https://man7.org/linux/man-pages/man3/ptsname.3.html))
///
/// `ptsname_r()` returns the name of the slave pseudoterminal device corresponding to the master
/// referred to by `fd`. This is the threadsafe version of `ptsname()`, but it is not part of the
/// POSIX standard and is instead a Linux-specific extension.
///
/// This value is useful for opening the slave ptty once the master has already been opened with
/// `posix_openpt()`.
#[cfg(linux_android)]
#[inline]
pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
let mut name_buf = Vec::<libc::c_char>::with_capacity(64);
let name_buf_ptr = name_buf.as_mut_ptr();
let cname = unsafe {
let cap = name_buf.capacity();
if libc::ptsname_r(fd.as_raw_fd(), name_buf_ptr, cap) != 0 {
return Err(crate::Error::last());
}
CStr::from_ptr(name_buf.as_ptr())
};
let name = cname.to_string_lossy().into_owned();
Ok(name)
}
/// Unlock a pseudoterminal master/slave pseudoterminal pair (see
/// [`unlockpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlockpt.html))
///
/// `unlockpt()` unlocks the slave pseudoterminal device corresponding to the master pseudoterminal
/// referred to by `fd`. This must be called before trying to open the slave side of a
/// pseudoterminal.
#[inline]
pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
if unsafe { libc::unlockpt(fd.as_raw_fd()) } < 0 {
return Err(Errno::last());
}
Ok(())
}
/// Create a new pseudoterminal, returning the slave and master file descriptors
/// in `OpenptyResult`
/// (see [`openpty`](https://man7.org/linux/man-pages/man3/openpty.3.html)).
///
/// If `winsize` is not `None`, the window size of the slave will be set to
/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
/// terminal settings of the slave will be set to the values in `termios`.
#[inline]
#[cfg(not(target_os = "aix"))]
pub fn openpty<
'a,
'b,
T: Into<Option<&'a Winsize>>,
U: Into<Option<&'b Termios>>,
>(
winsize: T,
termios: U,
) -> Result<OpenptyResult> {
use std::ptr;
let mut slave = mem::MaybeUninit::<libc::c_int>::uninit();
let mut master = mem::MaybeUninit::<libc::c_int>::uninit();
let ret = {
match (termios.into(), winsize.into()) {
(Some(termios), Some(winsize)) => {
let inner_termios = termios.get_libc_termios();
unsafe {
libc::openpty(
master.as_mut_ptr(),
slave.as_mut_ptr(),
ptr::null_mut(),
&*inner_termios as *const libc::termios as *mut _,
winsize as *const Winsize as *mut _,
)
}
}
(None, Some(winsize)) => unsafe {
libc::openpty(
master.as_mut_ptr(),
slave.as_mut_ptr(),
ptr::null_mut(),
ptr::null_mut(),
winsize as *const Winsize as *mut _,
)
},
(Some(termios), None) => {
let inner_termios = termios.get_libc_termios();
unsafe {
libc::openpty(
master.as_mut_ptr(),
slave.as_mut_ptr(),
ptr::null_mut(),
&*inner_termios as *const libc::termios as *mut _,
ptr::null_mut(),
)
}
}
(None, None) => unsafe {
libc::openpty(
master.as_mut_ptr(),
slave.as_mut_ptr(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
)
},
}
};
Errno::result(ret)?;
unsafe {
Ok(OpenptyResult {
master: OwnedFd::from_raw_fd(master.assume_init()),
slave: OwnedFd::from_raw_fd(slave.assume_init()),
})
}
}
feature! {
#![feature = "process"]
/// Create a new process operating in a pseudoterminal.
///
/// If `winsize` is not `None`, the window size of the slave will be set to
/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
/// terminal settings of the slave will be set to the values in `termios`.
///
/// # Safety
///
/// In a multithreaded program, only [async-signal-safe] functions like `pause`
/// and `_exit` may be called by the child (the parent isn't restricted) until
/// a call of `execve(2)`. Note that memory allocation may **not** be
/// async-signal-safe and thus must be prevented.
///
/// Those functions are only a small subset of your operating system's API, so
/// special care must be taken to only invoke code you can control and audit.
///
/// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html
///
/// # Reference
///
/// * [FreeBSD](https://man.freebsd.org/cgi/man.cgi?query=forkpty)
/// * [Linux](https://man7.org/linux/man-pages/man3/forkpty.3.html)
#[cfg(not(target_os = "aix"))]
pub unsafe fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(
winsize: T,
termios: U,
) -> Result<ForkptyResult> {
use std::ptr;
let mut master = mem::MaybeUninit::<libc::c_int>::uninit();
let term = match termios.into() {
Some(termios) => {
let inner_termios = termios.get_libc_termios();
&*inner_termios as *const libc::termios as *mut _
},
None => ptr::null_mut(),
};
let win = winsize
.into()
.map(|ws| ws as *const Winsize as *mut _)
.unwrap_or(ptr::null_mut());
let res = unsafe { libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win) };
let success_ret = Errno::result(res)?;
let forkpty_result = match success_ret {
// In the child process
0 => ForkptyResult::Child,
// In the parent process
child_pid => {
// SAFETY:
// 1. The master buffer is guaranteed to be initialized in the parent process
// 2. OwnedFd::from_raw_fd won't panic as the fd is a valid file descriptor
let master = unsafe { OwnedFd::from_raw_fd( master.assume_init() ) };
ForkptyResult::Parent {
master,
child: Pid::from_raw(child_pid),
}
}
};
Ok(forkpty_result)
}
}

326
vendor/nix/src/sched.rs vendored Normal file
View File

@@ -0,0 +1,326 @@
//! Execution scheduling
//!
//! See Also
//! [sched.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html)
use crate::{Errno, Result};
#[cfg(linux_android)]
pub use self::sched_linux_like::*;
#[cfg(linux_android)]
mod sched_linux_like {
use crate::errno::Errno;
use crate::unistd::Pid;
use crate::Result;
use libc::{self, c_int, c_void};
use std::mem;
use std::option::Option;
use std::os::unix::io::{AsFd, AsRawFd};
// 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;
// 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;
// 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>;
/// `clone` create a child process
/// ([`clone(2)`](https://man7.org/linux/man-pages/man2/clone.2.html))
///
/// `stack` is a reference to an array which will hold the stack of the new
/// process. Unlike when calling `clone(2)` from C, the provided stack
/// address need not be the highest address of the region. Nix will take
/// care of that requirement. The user only needs to provide a reference to
/// a normally allocated buffer.
///
/// # Safety
///
/// Because `clone` creates a child process with its stack located in
/// `stack` without specifying the size of the stack, special care must be
/// taken to ensure that the child process does not overflow the provided
/// stack space.
///
/// See [`fork`](crate::unistd::fork) for additional safety concerns related
/// to executing child processes.
pub unsafe fn clone(
mut cb: CloneCb,
stack: &mut [u8],
flags: CloneFlags,
signal: Option<c_int>,
) -> Result<Pid> {
extern "C" fn callback(data: *mut CloneCb) -> c_int {
let cb: &mut CloneCb = unsafe { &mut *data };
(*cb)() as c_int
}
let combined = flags.bits() | signal.unwrap_or(0);
let res = unsafe {
let ptr = stack.as_mut_ptr().add(stack.len());
let ptr_aligned = ptr.sub(ptr as usize % 16);
libc::clone(
mem::transmute::<
extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32,
extern "C" fn(*mut libc::c_void) -> i32,
>(
callback
as extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32,
),
ptr_aligned as *mut c_void,
combined,
&mut cb as *mut _ as *mut c_void,
)
};
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: AsFd>(fd: Fd, nstype: CloneFlags) -> Result<()> {
let res = unsafe { libc::setns(fd.as_fd().as_raw_fd(), nstype.bits()) };
Errno::result(res).map(drop)
}
}
#[cfg(any(linux_android, freebsdlike))]
pub use self::sched_affinity::*;
#[cfg(any(linux_android, freebsdlike))]
mod sched_affinity {
use crate::errno::Errno;
use crate::unistd::Pid;
use crate::Result;
use std::mem;
/// CpuSet represent a bit-mask of CPUs.
/// CpuSets are used by sched_setaffinity and
/// sched_getaffinity for example.
///
/// This is a wrapper around `libc::cpu_set_t`.
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct CpuSet {
#[cfg(not(target_os = "freebsd"))]
cpu_set: libc::cpu_set_t,
#[cfg(target_os = "freebsd")]
cpu_set: libc::cpuset_t,
}
impl CpuSet {
/// Create a new and empty CpuSet.
pub fn new() -> CpuSet {
CpuSet {
cpu_set: unsafe { mem::zeroed() },
}
}
/// Test to see if a CPU is in the CpuSet.
/// `field` is the CPU id to test
pub fn is_set(&self, field: usize) -> Result<bool> {
if field >= CpuSet::count() {
Err(Errno::EINVAL)
} else {
Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) })
}
}
/// Add a CPU to CpuSet.
/// `field` is the CPU id to add
pub fn set(&mut self, field: usize) -> Result<()> {
if field >= CpuSet::count() {
Err(Errno::EINVAL)
} else {
unsafe {
libc::CPU_SET(field, &mut self.cpu_set);
}
Ok(())
}
}
/// Remove a CPU from CpuSet.
/// `field` is the CPU id to remove
pub fn unset(&mut self, field: usize) -> Result<()> {
if field >= CpuSet::count() {
Err(Errno::EINVAL)
} else {
unsafe {
libc::CPU_CLR(field, &mut self.cpu_set);
}
Ok(())
}
}
/// Return the maximum number of CPU in CpuSet
pub const fn count() -> usize {
#[cfg(not(target_os = "freebsd"))]
let bytes = mem::size_of::<libc::cpu_set_t>();
#[cfg(target_os = "freebsd")]
let bytes = mem::size_of::<libc::cpuset_t>();
8 * bytes
}
}
impl Default for CpuSet {
fn default() -> Self {
Self::new()
}
}
/// `sched_setaffinity` set a thread's CPU affinity mask
/// ([`sched_setaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html))
///
/// `pid` is the thread ID to update.
/// If pid is zero, then the calling thread is updated.
///
/// The `cpuset` argument specifies the set of CPUs on which the thread
/// will be eligible to run.
///
/// # Example
///
/// Binding the current thread to CPU 0 can be done as follows:
///
/// ```rust,no_run
/// use nix::sched::{CpuSet, sched_setaffinity};
/// use nix::unistd::Pid;
///
/// let mut cpu_set = CpuSet::new();
/// cpu_set.set(0).unwrap();
/// sched_setaffinity(Pid::from_raw(0), &cpu_set).unwrap();
/// ```
pub fn sched_setaffinity(pid: Pid, cpuset: &CpuSet) -> Result<()> {
let res = unsafe {
libc::sched_setaffinity(
pid.into(),
mem::size_of::<CpuSet>() as libc::size_t,
&cpuset.cpu_set,
)
};
Errno::result(res).map(drop)
}
/// `sched_getaffinity` get a thread's CPU affinity mask
/// ([`sched_getaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_getaffinity.2.html))
///
/// `pid` is the thread ID to check.
/// If pid is zero, then the calling thread is checked.
///
/// Returned `cpuset` is the set of CPUs on which the thread
/// is eligible to run.
///
/// # Example
///
/// Checking if the current thread can run on CPU 0 can be done as follows:
///
/// ```rust,no_run
/// use nix::sched::sched_getaffinity;
/// use nix::unistd::Pid;
///
/// let cpu_set = sched_getaffinity(Pid::from_raw(0)).unwrap();
/// if cpu_set.is_set(0).unwrap() {
/// println!("Current thread can run on CPU 0");
/// }
/// ```
pub fn sched_getaffinity(pid: Pid) -> Result<CpuSet> {
let mut cpuset = CpuSet::new();
let res = unsafe {
libc::sched_getaffinity(
pid.into(),
mem::size_of::<CpuSet>() as libc::size_t,
&mut cpuset.cpu_set,
)
};
Errno::result(res).and(Ok(cpuset))
}
/// Determines the CPU on which the calling thread is running.
pub fn sched_getcpu() -> Result<usize> {
let res = unsafe { libc::sched_getcpu() };
Errno::result(res).map(|int| int as usize)
}
}
/// Explicitly yield the processor to other threads.
///
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html)
pub fn sched_yield() -> Result<()> {
let res = unsafe { libc::sched_yield() };
Errno::result(res).map(drop)
}

431
vendor/nix/src/spawn.rs vendored Normal file
View File

@@ -0,0 +1,431 @@
//! Safe wrappers around posix_spawn* functions found in the libc "spawn.h" header.
use std::{ffi::CStr, mem, os::fd::RawFd};
#[cfg(any(feature = "fs", feature = "term"))]
use crate::fcntl::OFlag;
#[cfg(feature = "signal")]
use crate::sys::signal::SigSet;
#[cfg(feature = "fs")]
use crate::sys::stat::Mode;
use crate::{errno::Errno, unistd::Pid, NixPath, Result};
/// A spawn attributes object. See [posix_spawnattr_t](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
#[repr(transparent)]
#[derive(Debug)]
pub struct PosixSpawnAttr {
attr: libc::posix_spawnattr_t,
}
impl PosixSpawnAttr {
/// Initialize the spawn attributes object. See
/// [posix_spawnattr_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
#[doc(alias("posix_spawnattr_init"))]
pub fn init() -> Result<PosixSpawnAttr> {
let mut attr = mem::MaybeUninit::uninit();
let res = unsafe { libc::posix_spawnattr_init(attr.as_mut_ptr()) };
Errno::result(res)?;
let attr = unsafe { attr.assume_init() };
Ok(PosixSpawnAttr { attr })
}
/// Reinitialize the spawn attributes object.
/// This is a wrapper around
/// [posix_spawnattr_destroy](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_destroy.html)
/// followed by
/// [posix_spawnattr_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_init.html).
#[doc(alias("posix_spawnattr_destroy"))]
pub fn reinit(mut self) -> Result<PosixSpawnAttr> {
let res = unsafe {
libc::posix_spawnattr_destroy(
&mut self.attr as *mut libc::posix_spawnattr_t,
)
};
Errno::result(res)?;
let res = unsafe {
libc::posix_spawnattr_init(
&mut self.attr as *mut libc::posix_spawnattr_t,
)
};
Errno::result(res)?;
Ok(self)
}
/// Set spawn flags. See
/// [posix_spawnattr_setflags](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setflags.html).
#[doc(alias("posix_spawnattr_setflags"))]
pub fn set_flags(&mut self, flags: PosixSpawnFlags) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setflags(
&mut self.attr as *mut libc::posix_spawnattr_t,
flags.bits() as libc::c_short,
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn flags. See
/// [posix_spawnattr_getflags](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getflags.html).
#[doc(alias("posix_spawnattr_getflags"))]
pub fn flags(&self) -> Result<PosixSpawnFlags> {
let mut flags: libc::c_short = 0;
let res = unsafe {
libc::posix_spawnattr_getflags(
&self.attr as *const libc::posix_spawnattr_t,
&mut flags,
)
};
Errno::result(res)?;
Ok(PosixSpawnFlags::from_bits_truncate(flags.into()))
}
/// Set spawn pgroup. See
/// [posix_spawnattr_setpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setpgroup.html).
#[doc(alias("posix_spawnattr_setpgroup"))]
pub fn set_pgroup(&mut self, pgroup: Pid) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setpgroup(
&mut self.attr as *mut libc::posix_spawnattr_t,
pgroup.as_raw(),
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn pgroup. See
/// [posix_spawnattr_getpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getpgroup.html).
#[doc(alias("posix_spawnattr_getpgroup"))]
pub fn pgroup(&self) -> Result<Pid> {
let mut pid: libc::pid_t = 0;
let res = unsafe {
libc::posix_spawnattr_getpgroup(
&self.attr as *const libc::posix_spawnattr_t,
&mut pid,
)
};
Errno::result(res)?;
Ok(Pid::from_raw(pid))
}
feature! {
#![feature = "signal"]
/// Set spawn sigdefault. See
/// [posix_spawnattr_setsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigdefault.html).
#[doc(alias("posix_spawnattr_setsigdefault"))]
pub fn set_sigdefault(&mut self, sigdefault: &SigSet) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setsigdefault(
&mut self.attr as *mut libc::posix_spawnattr_t,
sigdefault.as_ref(),
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn sigdefault. See
/// [posix_spawnattr_getsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigdefault.html).
#[doc(alias("posix_spawnattr_getsigdefault"))]
pub fn sigdefault(&self) -> Result<SigSet> {
let mut sigset = mem::MaybeUninit::uninit();
let res = unsafe {
libc::posix_spawnattr_getsigdefault(
&self.attr as *const libc::posix_spawnattr_t,
sigset.as_mut_ptr(),
)
};
Errno::result(res)?;
let sigdefault =
unsafe { SigSet::from_sigset_t_unchecked(sigset.assume_init()) };
Ok(sigdefault)
}
/// Set spawn sigmask. See
/// [posix_spawnattr_setsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigmask.html).
#[doc(alias("posix_spawnattr_setsigmask"))]
pub fn set_sigmask(&mut self, sigdefault: &SigSet) -> Result<()> {
let res = unsafe {
libc::posix_spawnattr_setsigmask(
&mut self.attr as *mut libc::posix_spawnattr_t,
sigdefault.as_ref(),
)
};
Errno::result(res)?;
Ok(())
}
/// Get spawn sigmask. See
/// [posix_spawnattr_getsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_getsigmask.html).
#[doc(alias("posix_spawnattr_getsigmask"))]
pub fn sigmask(&self) -> Result<SigSet> {
let mut sigset = mem::MaybeUninit::uninit();
let res = unsafe {
libc::posix_spawnattr_getsigmask(
&self.attr as *const libc::posix_spawnattr_t,
sigset.as_mut_ptr(),
)
};
Errno::result(res)?;
let sigdefault =
unsafe { SigSet::from_sigset_t_unchecked(sigset.assume_init()) };
Ok(sigdefault)
}
}
}
impl Drop for PosixSpawnAttr {
fn drop(&mut self) {
unsafe {
libc::posix_spawnattr_destroy(
&mut self.attr as *mut libc::posix_spawnattr_t,
);
}
}
}
libc_bitflags!(
/// Process attributes to be changed in the new process image when invoking [`posix_spawn`]
/// or [`posix_spawnp`]. See
/// [posix_spawn](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html).
pub struct PosixSpawnFlags: libc::c_int {
/// Reset effective user ID of the child process to parent's real user ID.
POSIX_SPAWN_RESETIDS;
/// Put the child in a process group specified by the spawn-pgroup attribute. See
/// [posix_spawnattr_setpgroup](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setpgroup.html).
POSIX_SPAWN_SETPGROUP;
/// Force set signals to default signal handling in child process. See
/// [posix_spawnattr_setsigdefault](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigdefault.html).
#[cfg(feature = "signal")]
POSIX_SPAWN_SETSIGDEF;
/// Set signal mask of child process. See
/// [posix_spawnattr_setsigmask](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnattr_setsigmask.html).
#[cfg(feature = "signal")]
POSIX_SPAWN_SETSIGMASK;
// TODO: Add support for the following two flags whenever support for
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html
// is added to nix.
// POSIX_SPAWN_SETSCHEDPARAM;
// POSIX_SPAWN_SETSCHEDULER;
}
);
/// A spawn file actions object. See [posix_spawn_file_actions_t](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addclose.html).
#[repr(transparent)]
#[derive(Debug)]
pub struct PosixSpawnFileActions {
fa: libc::posix_spawn_file_actions_t,
}
impl PosixSpawnFileActions {
/// Initialize the spawn file actions object. See
/// [posix_spawn_file_actions_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_init.html).
#[doc(alias("posix_spawn_file_actions_init"))]
pub fn init() -> Result<PosixSpawnFileActions> {
let mut actions = mem::MaybeUninit::uninit();
let res = unsafe {
libc::posix_spawn_file_actions_init(actions.as_mut_ptr())
};
Errno::result(res)?;
Ok(unsafe {
PosixSpawnFileActions {
fa: actions.assume_init(),
}
})
}
/// Reinitialize the spawn file actions object.
/// This is a wrapper around
/// [posix_spawn_file_actions_destroy](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_destroy.html).
/// followed by
/// [posix_spawn_file_actions_init](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_init.html).
#[doc(alias("posix_spawn_file_actions_destroy"))]
pub fn reinit(mut self) -> Result<PosixSpawnFileActions> {
let res = unsafe {
libc::posix_spawn_file_actions_destroy(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
)
};
Errno::result(res)?;
let res = unsafe {
libc::posix_spawn_file_actions_init(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
)
};
Errno::result(res)?;
Ok(self)
}
/// Add a [dup2](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup2.html) action. See
/// [posix_spawn_file_actions_adddup2](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_adddup2.html).
#[doc(alias("posix_spawn_file_actions_adddup2"))]
pub fn add_dup2(&mut self, fd: RawFd, newfd: RawFd) -> Result<()> {
let res = unsafe {
libc::posix_spawn_file_actions_adddup2(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
fd,
newfd,
)
};
Errno::result(res)?;
Ok(())
}
feature! {
#![all(feature = "fs", feature = "term")]
/// Add an open action. See
/// [posix_spawn_file_actions_addopen](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addopen.html).
#[doc(alias("posix_spawn_file_actions_addopen"))]
pub fn add_open<P: ?Sized + NixPath>(
&mut self,
fd: RawFd,
path: &P,
oflag: OFlag,
mode: Mode,
) -> Result<()> {
let res = path.with_nix_path(|cstr| unsafe {
libc::posix_spawn_file_actions_addopen(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
fd,
cstr.as_ptr(),
oflag.bits(),
mode.bits(),
)
})?;
Errno::result(res)?;
Ok(())
}
}
/// Add a close action. See
/// [posix_spawn_file_actions_addclose](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn_file_actions_addclose.html).
#[doc(alias("posix_spawn_file_actions_addclose"))]
pub fn add_close(&mut self, fd: RawFd) -> Result<()> {
let res = unsafe {
libc::posix_spawn_file_actions_addclose(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
fd,
)
};
Errno::result(res)?;
Ok(())
}
}
impl Drop for PosixSpawnFileActions {
fn drop(&mut self) {
unsafe {
libc::posix_spawn_file_actions_destroy(
&mut self.fa as *mut libc::posix_spawn_file_actions_t,
);
}
}
}
// The POSIX standard requires those `args` and `envp` to be of type `*const *mut [c_char]`,
// but implementations won't modify them, making the `mut` type redundant. Considering this,
// Nix does not expose this mutability, but we have to change the interface when calling the
// underlying libc interfaces , this helper function does the conversion job.
//
// SAFETY:
// It is safe to add the mutability in types as implementations won't mutable them.
unsafe fn to_exec_array<S: AsRef<CStr>>(args: &[S]) -> Vec<*mut libc::c_char> {
let mut v: Vec<*mut libc::c_char> = args
.iter()
.map(|s| s.as_ref().as_ptr().cast_mut())
.collect();
v.push(std::ptr::null_mut());
v
}
/// Create a new child process from the specified process image. See
/// [posix_spawn](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html).
pub fn posix_spawn<P, SA, SE>(
path: &P,
file_actions: &PosixSpawnFileActions,
attr: &PosixSpawnAttr,
args: &[SA],
envp: &[SE],
) -> Result<Pid>
where
P: NixPath + ?Sized,
SA: AsRef<CStr>,
SE: AsRef<CStr>,
{
let mut pid = 0;
let ret = unsafe {
let args_p = to_exec_array(args);
let env_p = to_exec_array(envp);
path.with_nix_path(|c_str| {
libc::posix_spawn(
&mut pid as *mut libc::pid_t,
c_str.as_ptr(),
&file_actions.fa as *const libc::posix_spawn_file_actions_t,
&attr.attr as *const libc::posix_spawnattr_t,
args_p.as_ptr(),
env_p.as_ptr(),
)
})?
};
if ret != 0 {
return Err(Errno::from_raw(ret));
}
Ok(Pid::from_raw(pid))
}
/// Create a new child process from the specified process image. See
/// [posix_spawnp](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawnp.html).
pub fn posix_spawnp<SA: AsRef<CStr>, SE: AsRef<CStr>>(
path: &CStr,
file_actions: &PosixSpawnFileActions,
attr: &PosixSpawnAttr,
args: &[SA],
envp: &[SE],
) -> Result<Pid> {
let mut pid = 0;
let ret = unsafe {
let args_p = to_exec_array(args);
let env_p = to_exec_array(envp);
libc::posix_spawnp(
&mut pid as *mut libc::pid_t,
path.as_ptr(),
&file_actions.fa as *const libc::posix_spawn_file_actions_t,
&attr.attr as *const libc::posix_spawnattr_t,
args_p.as_ptr(),
env_p.as_ptr(),
)
};
if ret != 0 {
return Err(Errno::from_raw(ret));
}
Ok(Pid::from_raw(pid))
}

1197
vendor/nix/src/sys/aio.rs vendored Normal file

File diff suppressed because it is too large Load Diff

255
vendor/nix/src/sys/epoll.rs vendored Normal file
View File

@@ -0,0 +1,255 @@
use crate::errno::Errno;
pub use crate::poll_timeout::PollTimeout as EpollTimeout;
pub use crate::poll_timeout::PollTimeoutTryFromError as EpollTimeoutTryFromError;
use crate::Result;
use libc::{self, c_int};
use std::mem;
use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd};
libc_bitflags!(
pub struct EpollFlags: c_int {
EPOLLIN;
EPOLLPRI;
EPOLLOUT;
EPOLLRDNORM;
EPOLLRDBAND;
EPOLLWRNORM;
EPOLLWRBAND;
EPOLLMSG;
EPOLLERR;
EPOLLHUP;
EPOLLRDHUP;
EPOLLEXCLUSIVE;
#[cfg(not(target_arch = "mips"))]
EPOLLWAKEUP;
EPOLLONESHOT;
EPOLLET;
}
);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(i32)]
#[non_exhaustive]
pub enum EpollOp {
EpollCtlAdd = libc::EPOLL_CTL_ADD,
EpollCtlDel = libc::EPOLL_CTL_DEL,
EpollCtlMod = libc::EPOLL_CTL_MOD,
}
libc_bitflags! {
pub struct EpollCreateFlags: c_int {
EPOLL_CLOEXEC;
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct EpollEvent {
event: libc::epoll_event,
}
impl EpollEvent {
pub fn new(events: EpollFlags, data: u64) -> Self {
EpollEvent {
event: libc::epoll_event {
events: events.bits() as u32,
u64: data,
},
}
}
pub fn empty() -> Self {
unsafe { mem::zeroed::<EpollEvent>() }
}
pub fn events(&self) -> EpollFlags {
EpollFlags::from_bits(self.event.events as c_int).unwrap()
}
pub fn data(&self) -> u64 {
self.event.u64
}
}
/// A safe wrapper around [`epoll`](https://man7.org/linux/man-pages/man7/epoll.7.html).
/// ```
/// # use nix::sys::{epoll::{EpollTimeout, Epoll, EpollEvent, EpollFlags, EpollCreateFlags}, eventfd::{EventFd, EfdFlags}};
/// # use nix::unistd::write;
/// # use std::os::unix::io::{OwnedFd, FromRawFd, AsFd};
/// # use std::time::{Instant, Duration};
/// # fn main() -> nix::Result<()> {
/// const DATA: u64 = 17;
/// const MILLIS: u8 = 100;
///
/// // Create epoll
/// let epoll = Epoll::new(EpollCreateFlags::empty())?;
///
/// // Create eventfd & Add event
/// let eventfd = EventFd::new()?;
/// epoll.add(&eventfd, EpollEvent::new(EpollFlags::EPOLLIN,DATA))?;
///
/// // Arm eventfd & Time wait
/// eventfd.write(1)?;
/// let now = Instant::now();
///
/// // Wait on event
/// let mut events = [EpollEvent::empty()];
/// epoll.wait(&mut events, MILLIS)?;
///
/// // Assert data correct & timeout didn't occur
/// assert_eq!(events[0].data(), DATA);
/// assert!(now.elapsed().as_millis() < MILLIS.into());
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
pub struct Epoll(pub OwnedFd);
impl Epoll {
/// Creates a new epoll instance and returns a file descriptor referring to that instance.
///
/// [`epoll_create1`](https://man7.org/linux/man-pages/man2/epoll_create1.2.html).
pub fn new(flags: EpollCreateFlags) -> Result<Self> {
let res = unsafe { libc::epoll_create1(flags.bits()) };
let fd = Errno::result(res)?;
let owned_fd = unsafe { OwnedFd::from_raw_fd(fd) };
Ok(Self(owned_fd))
}
/// Add an entry to the interest list of the epoll file descriptor for
/// specified in events.
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_ADD`.
pub fn add<Fd: AsFd>(&self, fd: Fd, mut event: EpollEvent) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlAdd, fd, &mut event)
}
/// Remove (deregister) the target file descriptor `fd` from the interest list.
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_DEL` .
pub fn delete<Fd: AsFd>(&self, fd: Fd) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlDel, fd, None)
}
/// Change the settings associated with `fd` in the interest list to the new settings specified
/// in `event`.
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_MOD`.
pub fn modify<Fd: AsFd>(
&self,
fd: Fd,
event: &mut EpollEvent,
) -> Result<()> {
self.epoll_ctl(EpollOp::EpollCtlMod, fd, event)
}
/// Waits for I/O events, blocking the calling thread if no events are currently available.
/// (This can be thought of as fetching items from the ready list of the epoll instance.)
///
/// [`epoll_wait`](https://man7.org/linux/man-pages/man2/epoll_wait.2.html)
pub fn wait<T: Into<EpollTimeout>>(
&self,
events: &mut [EpollEvent],
timeout: T,
) -> Result<usize> {
let res = unsafe {
libc::epoll_wait(
self.0.as_raw_fd(),
events.as_mut_ptr().cast(),
events.len() as c_int,
timeout.into().into(),
)
};
Errno::result(res).map(|r| r as usize)
}
/// This system call is used to add, modify, or remove entries in the interest list of the epoll
/// instance referred to by `self`. It requests that the operation `op` be performed for the
/// target file descriptor, `fd`.
///
/// When possible prefer [`Epoll::add`], [`Epoll::delete`] and [`Epoll::modify`].
///
/// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html)
fn epoll_ctl<'a, Fd: AsFd, T>(
&self,
op: EpollOp,
fd: Fd,
event: T,
) -> Result<()>
where
T: Into<Option<&'a mut EpollEvent>>,
{
let event: Option<&mut EpollEvent> = event.into();
let ptr = event
.map(|x| &mut x.event as *mut libc::epoll_event)
.unwrap_or(std::ptr::null_mut());
unsafe {
Errno::result(libc::epoll_ctl(
self.0.as_raw_fd(),
op as c_int,
fd.as_fd().as_raw_fd(),
ptr,
))
.map(drop)
}
}
}
#[deprecated(since = "0.27.0", note = "Use Epoll::new() instead")]
#[inline]
pub fn epoll_create() -> Result<RawFd> {
let res = unsafe { libc::epoll_create(1024) };
Errno::result(res)
}
#[deprecated(since = "0.27.0", note = "Use Epoll::new() instead")]
#[inline]
pub fn epoll_create1(flags: EpollCreateFlags) -> Result<RawFd> {
let res = unsafe { libc::epoll_create1(flags.bits()) };
Errno::result(res)
}
#[deprecated(
since = "0.27.0",
note = "Use corresponding Epoll methods instead"
)]
#[inline]
pub fn epoll_ctl<'a, T>(
epfd: RawFd,
op: EpollOp,
fd: RawFd,
event: T,
) -> Result<()>
where
T: Into<Option<&'a mut EpollEvent>>,
{
let mut event: Option<&mut EpollEvent> = event.into();
if event.is_none() && op != EpollOp::EpollCtlDel {
Err(Errno::EINVAL)
} else {
let res = unsafe {
if let Some(ref mut event) = event {
libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event)
} else {
libc::epoll_ctl(epfd, op as c_int, fd, std::ptr::null_mut())
}
};
Errno::result(res).map(drop)
}
}
#[deprecated(since = "0.27.0", note = "Use Epoll::wait() instead")]
#[inline]
pub fn epoll_wait(
epfd: RawFd,
events: &mut [EpollEvent],
timeout_ms: isize,
) -> Result<usize> {
let res = unsafe {
libc::epoll_wait(
epfd,
events.as_mut_ptr().cast(),
events.len() as c_int,
timeout_ms as c_int,
)
};
Errno::result(res).map(|r| r as usize)
}

460
vendor/nix/src/sys/event.rs vendored Normal file
View File

@@ -0,0 +1,460 @@
//! Kernel event notification mechanism
//!
//! # See Also
//! [kqueue(2)](https://www.freebsd.org/cgi/man.cgi?query=kqueue)
use crate::{Errno, Result};
#[cfg(not(target_os = "netbsd"))]
use libc::{c_int, c_long, intptr_t, time_t, timespec, uintptr_t};
#[cfg(target_os = "netbsd")]
use libc::{c_long, intptr_t, size_t, time_t, timespec, uintptr_t};
use std::convert::TryInto;
use std::mem;
use std::os::fd::{AsFd, BorrowedFd};
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd};
use std::ptr;
/// A kernel event queue. Used to notify a process of various asynchronous
/// events.
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct KEvent {
kevent: libc::kevent,
}
/// A kernel event queue.
///
/// Used by the kernel to notify the process of various types of asynchronous
/// events.
#[repr(transparent)]
#[derive(Debug)]
pub struct Kqueue(OwnedFd);
impl AsFd for Kqueue {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl From<Kqueue> for OwnedFd {
fn from(value: Kqueue) -> Self {
value.0
}
}
impl Kqueue {
/// Create a new kernel event queue.
pub fn new() -> Result<Self> {
let res = unsafe { libc::kqueue() };
Errno::result(res).map(|fd| unsafe { Self(OwnedFd::from_raw_fd(fd)) })
}
/// Register new events with the kqueue, and return any pending events to
/// the user.
///
/// This method will block until either the timeout expires, or a registered
/// event triggers a notification.
///
/// # Arguments
/// - `changelist` - Any new kevents to register for notifications.
/// - `eventlist` - Storage space for the kernel to return notifications.
/// - `timeout` - An optional timeout.
///
/// # Returns
/// Returns the number of events placed in the `eventlist`. If an error
/// occurs while processing an element of the `changelist` and there is
/// enough room in the `eventlist`, then the event will be placed in the
/// `eventlist` with `EV_ERROR` set in `flags` and the system error in
/// `data`.
pub fn kevent(
&self,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_opt: Option<timespec>,
) -> Result<usize> {
let res = unsafe {
libc::kevent(
self.0.as_raw_fd(),
changelist.as_ptr().cast(),
changelist.len() as type_of_nchanges,
eventlist.as_mut_ptr().cast(),
eventlist.len() as type_of_nchanges,
if let Some(ref timeout) = timeout_opt {
timeout as *const timespec
} else {
ptr::null()
},
)
};
Errno::result(res).map(|r| r as usize)
}
}
#[cfg(any(freebsdlike, apple_targets, target_os = "openbsd"))]
type type_of_udata = *mut libc::c_void;
#[cfg(target_os = "netbsd")]
type type_of_udata = intptr_t;
#[cfg(target_os = "netbsd")]
type type_of_event_filter = u32;
#[cfg(not(target_os = "netbsd"))]
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]
/// Kqueue filter types. These are all the different types of event that a
/// kqueue can notify for.
pub enum EventFilter {
/// Notifies on the completion of a POSIX AIO operation.
EVFILT_AIO,
#[cfg(target_os = "freebsd")]
/// Returns whenever there is no remaining data in the write buffer
EVFILT_EMPTY,
#[cfg(target_os = "dragonfly")]
/// Takes a descriptor as the identifier, and returns whenever one of
/// the specified exceptional conditions has occurred on the descriptor.
EVFILT_EXCEPT,
#[cfg(any(freebsdlike, apple_targets))]
/// Establishes a file system monitor.
EVFILT_FS,
#[cfg(target_os = "freebsd")]
/// Notify for completion of a list of POSIX AIO operations.
/// # See Also
/// [lio_listio(2)](https://www.freebsd.org/cgi/man.cgi?query=lio_listio)
EVFILT_LIO,
#[cfg(apple_targets)]
/// Mach portsets
EVFILT_MACHPORT,
/// Notifies when a process performs one or more of the requested
/// events.
EVFILT_PROC,
/// Returns events associated with the process referenced by a given
/// process descriptor, created by `pdfork()`. The events to monitor are:
///
/// - NOTE_EXIT: the process has exited. The exit status will be stored in data.
#[cfg(target_os = "freebsd")]
EVFILT_PROCDESC,
/// Takes a file descriptor as the identifier, and notifies whenever
/// there is data available to read.
EVFILT_READ,
#[cfg(target_os = "freebsd")]
#[doc(hidden)]
#[deprecated(since = "0.27.0", note = "Never fully implemented by the OS")]
EVFILT_SENDFILE,
/// Takes a signal number to monitor as the identifier and notifies when
/// the given signal is delivered to the process.
EVFILT_SIGNAL,
/// Establishes a timer and notifies when the timer expires.
EVFILT_TIMER,
#[cfg(any(freebsdlike, apple_targets))]
/// Notifies only when explicitly requested by the user.
EVFILT_USER,
#[cfg(apple_targets)]
/// Virtual memory events
EVFILT_VM,
/// Notifies when a requested event happens on a specified file.
EVFILT_VNODE,
/// Takes a file descriptor as the identifier, and notifies whenever
/// it is possible to write to the file without blocking.
EVFILT_WRITE,
}
impl TryFrom<type_of_event_filter>
}
#[cfg(any(freebsdlike, apple_targets, target_os = "openbsd"))]
#[doc(hidden)]
pub type type_of_event_flag = u16;
#[cfg(target_os = "netbsd")]
#[doc(hidden)]
pub type type_of_event_flag = u32;
libc_bitflags! {
/// Event flags. See the man page for details.
// There's no useful documentation we can write for the individual flags
// that wouldn't simply be repeating the man page.
pub struct EvFlags: type_of_event_flag {
#[allow(missing_docs)]
EV_ADD;
#[allow(missing_docs)]
EV_CLEAR;
#[allow(missing_docs)]
EV_DELETE;
#[allow(missing_docs)]
EV_DISABLE;
#[cfg(bsd)]
#[allow(missing_docs)]
EV_DISPATCH;
#[cfg(target_os = "freebsd")]
#[allow(missing_docs)]
EV_DROP;
#[allow(missing_docs)]
EV_ENABLE;
#[allow(missing_docs)]
EV_EOF;
#[allow(missing_docs)]
EV_ERROR;
#[cfg(apple_targets)]
#[allow(missing_docs)]
EV_FLAG0;
#[allow(missing_docs)]
EV_FLAG1;
#[cfg(target_os = "dragonfly")]
#[allow(missing_docs)]
EV_NODATA;
#[allow(missing_docs)]
EV_ONESHOT;
#[cfg(apple_targets)]
#[allow(missing_docs)]
EV_OOBAND;
#[cfg(apple_targets)]
#[allow(missing_docs)]
EV_POLL;
#[cfg(bsd)]
#[allow(missing_docs)]
EV_RECEIPT;
}
}
#[deprecated(since = "0.30.0", note = "Use `EvFlags instead`")]
/// The deprecated EventFlag type alias
pub type EventFlag = EvFlags;
libc_bitflags!(
/// Filter-specific flags. See the man page for details.
// There's no useful documentation we can write for the individual flags
// that wouldn't simply be repeating the man page.
#[allow(missing_docs)]
pub struct FilterFlag: u32 {
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_ABSOLUTE;
#[allow(missing_docs)]
NOTE_ATTRIB;
#[allow(missing_docs)]
NOTE_CHILD;
#[allow(missing_docs)]
NOTE_DELETE;
#[cfg(target_os = "openbsd")]
#[allow(missing_docs)]
NOTE_EOF;
#[allow(missing_docs)]
NOTE_EXEC;
#[allow(missing_docs)]
NOTE_EXIT;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_EXITSTATUS;
#[allow(missing_docs)]
NOTE_EXTEND;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFAND;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFCOPY;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFCTRLMASK;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFLAGSMASK;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFNOP;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_FFOR;
#[allow(missing_docs)]
NOTE_FORK;
#[allow(missing_docs)]
NOTE_LINK;
#[allow(missing_docs)]
NOTE_LOWAT;
#[cfg(target_os = "freebsd")]
#[allow(missing_docs)]
NOTE_MSECONDS;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_NONE;
#[cfg(any(
apple_targets,
target_os = "freebsd"))]
#[allow(missing_docs)]
NOTE_NSECONDS;
#[cfg(target_os = "dragonfly")]
#[allow(missing_docs)]
NOTE_OOB;
#[allow(missing_docs)]
NOTE_PCTRLMASK;
#[allow(missing_docs)]
NOTE_PDATAMASK;
#[allow(missing_docs)]
NOTE_RENAME;
#[allow(missing_docs)]
NOTE_REVOKE;
#[cfg(any(
apple_targets,
target_os = "freebsd"))]
#[allow(missing_docs)]
NOTE_SECONDS;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_SIGNAL;
#[allow(missing_docs)]
NOTE_TRACK;
#[allow(missing_docs)]
NOTE_TRACKERR;
#[cfg(any(apple_targets, freebsdlike))]
#[allow(missing_docs)]
NOTE_TRIGGER;
#[cfg(target_os = "openbsd")]
#[allow(missing_docs)]
NOTE_TRUNCATE;
#[cfg(any(
apple_targets,
target_os = "freebsd"))]
#[allow(missing_docs)]
NOTE_USECONDS;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_VM_ERROR;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_VM_PRESSURE;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
#[cfg(apple_targets)]
#[allow(missing_docs)]
NOTE_VM_PRESSURE_TERMINATE;
#[allow(missing_docs)]
NOTE_WRITE;
}
);
#[allow(missing_docs)]
#[deprecated(since = "0.27.0", note = "Use KEvent::new instead")]
pub fn kqueue() -> Result<Kqueue> {
Kqueue::new()
}
// KEvent can't derive Send because on some operating systems, udata is defined
// as a void*. However, KEvent's public API always treats udata as an intptr_t,
// which is safe to Send.
unsafe impl Send for KEvent {}
impl KEvent {
#[allow(clippy::needless_update)] // Not needless on all platforms.
/// Construct a new `KEvent` suitable for submission to the kernel via the
/// `changelist` argument of [`Kqueue::kevent`].
pub fn new(
ident: uintptr_t,
filter: EventFilter,
flags: EvFlags,
fflags: FilterFlag,
data: intptr_t,
udata: intptr_t,
) -> KEvent {
KEvent {
kevent: libc::kevent {
ident,
filter: filter as type_of_event_filter,
flags: flags.bits(),
fflags: fflags.bits(),
// data can be either i64 or intptr_t, depending on platform
data: data as _,
udata: udata as type_of_udata,
..unsafe { mem::zeroed() }
},
}
}
/// Value used to identify this event. The exact interpretation is
/// determined by the attached filter, but often is a raw file descriptor.
pub fn ident(&self) -> uintptr_t {
self.kevent.ident
}
/// Identifies the kernel filter used to process this event.
///
/// Will only return an error if the kernel reports an event via a filter
/// that is unknown to Nix.
pub fn filter(&self) -> Result<EventFilter> {
self.kevent.filter.try_into()
}
/// Flags control what the kernel will do when this event is added with
/// [`Kqueue::kevent`].
pub fn flags(&self) -> EvFlags {
EvFlags::from_bits(self.kevent.flags).unwrap()
}
/// Filter-specific flags.
pub fn fflags(&self) -> FilterFlag {
FilterFlag::from_bits(self.kevent.fflags).unwrap()
}
/// Filter-specific data value.
pub fn data(&self) -> intptr_t {
self.kevent.data as intptr_t
}
/// Opaque user-defined value passed through the kernel unchanged.
pub fn udata(&self) -> intptr_t {
self.kevent.udata as intptr_t
}
}
#[allow(missing_docs)]
#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
pub fn kevent(
kq: &Kqueue,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_ms: usize,
) -> Result<usize> {
// Convert ms to timespec
let timeout = timespec {
tv_sec: (timeout_ms / 1000) as time_t,
tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long,
};
kq.kevent(changelist, eventlist, Some(timeout))
}
#[cfg(any(apple_targets, freebsdlike, target_os = "openbsd"))]
type type_of_nchanges = c_int;
#[cfg(target_os = "netbsd")]
type type_of_nchanges = size_t;
#[allow(missing_docs)]
#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
pub fn kevent_ts(
kq: &Kqueue,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_opt: Option<timespec>,
) -> Result<usize> {
kq.kevent(changelist, eventlist, timeout_opt)
}
/// Modify an existing [`KEvent`].
// Probably should deprecate. Would anybody ever use it over `KEvent::new`?
#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
#[inline]
pub fn ev_set(
ev: &mut KEvent,
ident: usize,
filter: EventFilter,
flags: EvFlags,
fflags: FilterFlag,
udata: intptr_t,
) {
ev.kevent.ident = ident as uintptr_t;
ev.kevent.filter = filter as type_of_event_filter;
ev.kevent.flags = flags.bits();
ev.kevent.fflags = fflags.bits();
ev.kevent.data = 0;
ev.kevent.udata = udata as type_of_udata;
}

118
vendor/nix/src/sys/eventfd.rs vendored Normal file
View File

@@ -0,0 +1,118 @@
use crate::errno::Errno;
use crate::{unistd, Result};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
libc_bitflags! {
/// Eventfd flags.
pub struct EfdFlags: libc::c_int {
/// Set the close-on-exec (`FD_CLOEXEC`) flag on the new event file descriptor.
EFD_CLOEXEC; // Since Linux 2.6.27/FreeBSD 13.0
/// Set the `O_NONBLOCK` file status flag on the new event file description.
EFD_NONBLOCK; // Since Linux 2.6.27/FreeBSD 13.0
/// Provide semaphore-like semantics for reads from the new event file
/// descriptor.
EFD_SEMAPHORE; // Since Linux 2.6.30/FreeBSD 13.0
}
}
#[deprecated(
since = "0.28.0",
note = "Use EventFd::from_value_and_flags() instead"
)]
#[allow(missing_docs)]
pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result<OwnedFd> {
let res = unsafe { libc::eventfd(initval, flags.bits()) };
Errno::result(res).map(|r| unsafe { OwnedFd::from_raw_fd(r) })
}
/// An eventfd file descriptor.
#[derive(Debug)]
#[repr(transparent)]
pub struct EventFd(OwnedFd);
impl EventFd {
/// [`EventFd::from_value_and_flags`] with `init_val = 0` and `flags = EfdFlags::empty()`.
pub fn new() -> Result<Self> {
Self::from_value_and_flags(0, EfdFlags::empty())
}
/// Constructs [`EventFd`] with the given `init_val` and `flags`.
///
/// Wrapper around [`libc::eventfd`].
pub fn from_value_and_flags(
init_val: u32,
flags: EfdFlags,
) -> Result<Self> {
let res = unsafe { libc::eventfd(init_val, flags.bits()) };
Errno::result(res).map(|r| Self(unsafe { OwnedFd::from_raw_fd(r) }))
}
/// [`EventFd::from_value_and_flags`] with `init_val = 0` and given `flags`.
pub fn from_flags(flags: EfdFlags) -> Result<Self> {
Self::from_value_and_flags(0, flags)
}
/// [`EventFd::from_value_and_flags`] with given `init_val` and `flags = EfdFlags::empty()`.
pub fn from_value(init_val: u32) -> Result<Self> {
Self::from_value_and_flags(init_val, EfdFlags::empty())
}
/// Constructs an `EventFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid eventfd.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self(fd)
}
/// Enqueues `value` triggers, i.e., adds the integer value supplied in `value`
/// to the counter.
///
/// The next `value` calls to `poll`, `select` or `epoll` will return immediately.
///
/// [`EventFd::write`] with `value`.
pub fn write(&self, value: u64) -> Result<usize> {
unistd::write(&self.0, &value.to_ne_bytes())
}
/// Reads the value from the file descriptor.
///
/// * If [`EFD_SEMAPHORE`](EfdFlags::EFD_SEMAPHORE) was not specified and
/// the eventfd counter has a nonzero value, then this function returns
/// an `u64` containing that value, and the counter's value is reset to
/// zero.
///
/// * If [`EFD_SEMAPHORE`](EfdFlags::EFD_SEMAPHORE) was specified and the
/// eventfd counter has a nonzero value, then this function returns an
/// `u64` containing the value 1, and the counter's value is decremented
/// by 1.
///
/// * If the eventfd counter is zero at the time of this call, then the
/// call either blocks until the counter becomes nonzero (at which time,
/// this function proceeds as described above) or fails with the error
/// `EAGAIN` if the file descriptor has been made nonblocking with
/// [`EFD_NONBLOCK`](EfdFlags::EFD_NONBLOCK).
pub fn read(&self) -> Result<u64> {
let mut arr = [0; std::mem::size_of::<u64>()];
unistd::read(&self.0, &mut arr)?;
Ok(u64::from_ne_bytes(arr))
}
}
impl AsFd for EventFd {
fn as_fd(&self) -> BorrowedFd {
self.0.as_fd()
}
}
impl AsRawFd for EventFd {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl From<EventFd> for OwnedFd {
fn from(value: EventFd) -> Self {
value.0
}
}

446
vendor/nix/src/sys/fanotify.rs vendored Normal file
View File

@@ -0,0 +1,446 @@
//! Monitoring API for filesystem events.
//!
//! Fanotify is a Linux-only API to monitor filesystems events.
//!
//! Additional capabilities compared to the `inotify` API include the ability to
//! monitor all of the objects in a mounted filesystem, the ability to make
//! access permission decisions, and the possibility to read or modify files
//! before access by other applications.
//!
//! For more documentation, please read
//! [fanotify(7)](https://man7.org/linux/man-pages/man7/fanotify.7.html).
use crate::errno::Errno;
use crate::fcntl::OFlag;
use crate::unistd::{close, read, write};
use crate::{NixPath, Result};
use std::marker::PhantomData;
use std::mem::{size_of, MaybeUninit};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
use std::ptr;
libc_bitflags! {
/// Mask for defining which events shall be listened with [`Fanotify::mark()`]
/// and for querying notifications.
pub struct MaskFlags: u64 {
/// File was accessed.
FAN_ACCESS;
/// File was modified.
FAN_MODIFY;
/// Metadata has changed. Since Linux 5.1.
FAN_ATTRIB;
/// Writtable file was closed.
FAN_CLOSE_WRITE;
/// Unwrittable file was closed.
FAN_CLOSE_NOWRITE;
/// File was opened.
FAN_OPEN;
/// File was moved from X. Since Linux 5.1.
FAN_MOVED_FROM;
/// File was moved to Y. Since Linux 5.1.
FAN_MOVED_TO;
/// Subfile was created. Since Linux 5.1.
FAN_CREATE;
/// Subfile was deleted. Since Linux 5.1.
FAN_DELETE;
/// Self was deleted. Since Linux 5.1.
FAN_DELETE_SELF;
/// Self was moved. Since Linux 5.1.
FAN_MOVE_SELF;
/// File was opened for execution. Since Linux 5.0.
FAN_OPEN_EXEC;
/// Event queue overflowed.
FAN_Q_OVERFLOW;
/// Filesystem error. Since Linux 5.16.
FAN_FS_ERROR;
/// Permission to open file was requested.
FAN_OPEN_PERM;
/// Permission to access file was requested.
FAN_ACCESS_PERM;
/// Permission to open file for execution was requested. Since Linux
/// 5.0.
FAN_OPEN_EXEC_PERM;
/// Interested in child events.
FAN_EVENT_ON_CHILD;
/// File was renamed. Since Linux 5.17.
FAN_RENAME;
/// Event occurred against dir.
FAN_ONDIR;
/// Combination of `FAN_CLOSE_WRITE` and `FAN_CLOSE_NOWRITE`.
FAN_CLOSE;
/// Combination of `FAN_MOVED_FROM` and `FAN_MOVED_TO`.
FAN_MOVE;
}
}
libc_bitflags! {
/// Configuration options for [`Fanotify::init()`].
pub struct InitFlags: libc::c_uint {
/// Close-on-exec flag set on the file descriptor.
FAN_CLOEXEC;
/// Nonblocking flag set on the file descriptor.
FAN_NONBLOCK;
/// Receipt of events notifications.
FAN_CLASS_NOTIF;
/// Receipt of events for permission decisions, after they contain final
/// data.
FAN_CLASS_CONTENT;
/// Receipt of events for permission decisions, before they contain
/// final data.
FAN_CLASS_PRE_CONTENT;
/// Remove the limit on the number of events in the event queue.
///
/// Prior to Linux kernel 5.13, this limit was hardcoded to 16384. After
/// 5.13, one can change it via file `/proc/sys/fs/fanotify/max_queued_events`.
///
/// See `fanotify(7)` for details about this limit. Use of this flag
/// requires the `CAP_SYS_ADMIN` capability.
FAN_UNLIMITED_QUEUE;
/// Remove the limit on the number of fanotify marks per user.
///
/// Prior to Linux kernel 5.13, this limit was hardcoded to 8192 (per
/// group, not per user). After 5.13, one can change it via file
/// `/proc/sys/fs/fanotify/max_user_marks`.
///
/// See `fanotify(7)` for details about this limit. Use of this flag
/// requires the `CAP_SYS_ADMIN` capability.
FAN_UNLIMITED_MARKS;
/// Make `FanotifyEvent::pid` return pidfd. Since Linux 5.15.
FAN_REPORT_PIDFD;
/// Make `FanotifyEvent::pid` return thread id. Since Linux 4.20.
FAN_REPORT_TID;
}
}
libc_bitflags! {
/// File status flags for fanotify events file descriptors.
pub struct EventFFlags: libc::c_uint {
/// Read only access.
O_RDONLY as libc::c_uint;
/// Write only access.
O_WRONLY as libc::c_uint;
/// Read and write access.
O_RDWR as libc::c_uint;
/// Support for files exceeded 2 GB.
O_LARGEFILE as libc::c_uint;
/// Close-on-exec flag for the file descriptor. Since Linux 3.18.
O_CLOEXEC as libc::c_uint;
/// Append mode for the file descriptor.
O_APPEND as libc::c_uint;
/// Synchronized I/O data integrity completion.
O_DSYNC as libc::c_uint;
/// No file last access time update.
O_NOATIME as libc::c_uint;
/// Nonblocking mode for the file descriptor.
O_NONBLOCK as libc::c_uint;
/// Synchronized I/O file integrity completion.
O_SYNC as libc::c_uint;
}
}
impl TryFrom<OFlag> for EventFFlags {
type Error = Errno;
fn try_from(o_flag: OFlag) -> Result<Self> {
EventFFlags::from_bits(o_flag.bits() as u32).ok_or(Errno::EINVAL)
}
}
impl From<EventFFlags> for OFlag {
fn from(event_f_flags: EventFFlags) -> Self {
OFlag::from_bits_retain(event_f_flags.bits() as i32)
}
}
libc_bitflags! {
/// Configuration options for [`Fanotify::mark()`].
pub struct MarkFlags: libc::c_uint {
/// Add the events to the marks.
FAN_MARK_ADD;
/// Remove the events to the marks.
FAN_MARK_REMOVE;
/// Don't follow symlinks, mark them.
FAN_MARK_DONT_FOLLOW;
/// Raise an error if filesystem to be marked is not a directory.
FAN_MARK_ONLYDIR;
/// Events added to or removed from the marks.
FAN_MARK_IGNORED_MASK;
/// Ignore mask shall survive modify events.
FAN_MARK_IGNORED_SURV_MODIFY;
/// Remove all marks.
FAN_MARK_FLUSH;
/// Do not pin inode object in the inode cache. Since Linux 5.19.
FAN_MARK_EVICTABLE;
/// Events added to or removed from the marks. Since Linux 6.0.
FAN_MARK_IGNORE;
/// Default flag.
FAN_MARK_INODE;
/// Mark the mount specified by pathname.
FAN_MARK_MOUNT;
/// Mark the filesystem specified by pathname. Since Linux 4.20.
FAN_MARK_FILESYSTEM;
/// Combination of `FAN_MARK_IGNORE` and `FAN_MARK_IGNORED_SURV_MODIFY`.
FAN_MARK_IGNORE_SURV;
}
}
/// Compile version number of fanotify API.
pub const FANOTIFY_METADATA_VERSION: u8 = libc::FANOTIFY_METADATA_VERSION;
/// Abstract over [`libc::fanotify_event_metadata`], which represents an event
/// received via [`Fanotify::read_events`].
// Is not Clone due to fd field, to avoid use-after-close scenarios.
#[derive(Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
#[allow(missing_copy_implementations)]
pub struct FanotifyEvent(libc::fanotify_event_metadata);
impl FanotifyEvent {
/// Version number for the structure. It must be compared to
/// `FANOTIFY_METADATA_VERSION` to verify compile version and runtime
/// version does match. It can be done with the
/// `FanotifyEvent::check_version` method.
pub fn version(&self) -> u8 {
self.0.vers
}
/// Checks that compile fanotify API version is equal to the version of the
/// event.
pub fn check_version(&self) -> bool {
self.version() == FANOTIFY_METADATA_VERSION
}
/// Mask flags of the events.
pub fn mask(&self) -> MaskFlags {
MaskFlags::from_bits_truncate(self.0.mask)
}
/// The file descriptor of the event. If the value is `None` when reading
/// from the fanotify group, this event is to notify that a group queue
/// overflow occured.
pub fn fd(&self) -> Option<BorrowedFd> {
if self.0.fd == libc::FAN_NOFD {
None
} else {
// SAFETY: self.0.fd will be opened for the lifetime of `Self`,
// which is longer than the lifetime of the returned BorrowedFd, so
// it is safe.
Some(unsafe { BorrowedFd::borrow_raw(self.0.fd) })
}
}
/// PID of the process that caused the event. TID in case flag
/// `FAN_REPORT_TID` was set at group initialization.
pub fn pid(&self) -> i32 {
self.0.pid
}
}
impl Drop for FanotifyEvent {
fn drop(&mut self) {
if self.0.fd == libc::FAN_NOFD {
return;
}
let e = close(self.0.fd);
if !std::thread::panicking() && e == Err(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
}
}
/// Abstraction over the structure to be sent to allow or deny a given event.
#[derive(Debug)]
#[repr(transparent)]
pub struct FanotifyResponse<'a> {
inner: libc::fanotify_response,
_borrowed_fd: PhantomData<BorrowedFd<'a>>,
}
impl<'a> FanotifyResponse<'a> {
/// Create a new response.
pub fn new(fd: BorrowedFd<'a>, response: Response) -> Self {
Self {
inner: libc::fanotify_response {
fd: fd.as_raw_fd(),
response: response.bits(),
},
_borrowed_fd: PhantomData,
}
}
}
libc_bitflags! {
/// Response to be wrapped in [`FanotifyResponse`] and sent to the [`Fanotify`]
/// group to allow or deny an event.
pub struct Response: u32 {
/// Allow the event.
FAN_ALLOW;
/// Deny the event.
FAN_DENY;
}
}
/// A fanotify group. This is also a file descriptor that can feed to other
/// interfaces consuming file descriptors.
#[derive(Debug)]
pub struct Fanotify {
fd: OwnedFd,
}
impl Fanotify {
/// Initialize a new fanotify group.
///
/// Returns a Result containing a Fanotify instance.
///
/// For more information, see [fanotify_init(2)](https://man7.org/linux/man-pages/man7/fanotify_init.2.html).
pub fn init(
flags: InitFlags,
event_f_flags: EventFFlags,
) -> Result<Fanotify> {
let res = Errno::result(unsafe {
libc::fanotify_init(flags.bits(), event_f_flags.bits())
});
res.map(|fd| Fanotify {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
})
}
/// Add, remove, or modify an fanotify mark on a filesystem object.
///
/// Returns a Result containing either `()` on success or errno otherwise.
///
/// For more information, see [fanotify_mark(2)](https://man7.org/linux/man-pages/man7/fanotify_mark.2.html).
pub fn mark<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
&self,
flags: MarkFlags,
mask: MaskFlags,
dirfd: Fd,
path: Option<&P>,
) -> Result<()> {
let res = crate::with_opt_nix_path(path, |p| unsafe {
libc::fanotify_mark(
self.fd.as_raw_fd(),
flags.bits(),
mask.bits(),
dirfd.as_fd().as_raw_fd(),
p,
)
})?;
Errno::result(res).map(|_| ())
}
/// Read incoming events from the fanotify group.
///
/// Returns a Result containing either a `Vec` of events on success or errno
/// otherwise.
///
/// # Errors
///
/// Possible errors can be those that are explicitly listed in
/// [fanotify(2)](https://man7.org/linux/man-pages/man7/fanotify.2.html) in
/// addition to the possible errors caused by `read` call.
/// In particular, `EAGAIN` is returned when no event is available on a
/// group that has been initialized with the flag `InitFlags::FAN_NONBLOCK`,
/// thus making this method nonblocking.
pub fn read_events(&self) -> Result<Vec<FanotifyEvent>> {
let metadata_size = size_of::<libc::fanotify_event_metadata>();
const BUFSIZ: usize = 4096;
let mut buffer = [0u8; BUFSIZ];
let mut events = Vec::new();
let mut offset = 0;
let nread = read(&self.fd, &mut buffer)?;
while (nread - offset) >= metadata_size {
let metadata = unsafe {
let mut metadata =
MaybeUninit::<libc::fanotify_event_metadata>::uninit();
ptr::copy_nonoverlapping(
buffer.as_ptr().add(offset),
metadata.as_mut_ptr().cast(),
(BUFSIZ - offset).min(metadata_size),
);
metadata.assume_init()
};
events.push(FanotifyEvent(metadata));
offset += metadata.event_len as usize;
}
Ok(events)
}
/// Write an event response on the fanotify group.
///
/// Returns a Result containing either `()` on success or errno otherwise.
///
/// # Errors
///
/// Possible errors can be those that are explicitly listed in
/// [fanotify(2)](https://man7.org/linux/man-pages/man7/fanotify.2.html) in
/// addition to the possible errors caused by `write` call.
/// In particular, `EAGAIN` or `EWOULDBLOCK` is returned when no event is
/// available on a group that has been initialized with the flag
/// `InitFlags::FAN_NONBLOCK`, thus making this method nonblocking.
pub fn write_response(&self, response: FanotifyResponse) -> Result<()> {
write(self.fd.as_fd(), unsafe {
std::slice::from_raw_parts(
(&response.inner as *const libc::fanotify_response).cast(),
size_of::<libc::fanotify_response>(),
)
})?;
Ok(())
}
}
impl FromRawFd for Fanotify {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Fanotify {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
}
}
}
impl AsFd for Fanotify {
fn as_fd(&'_ self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
impl AsRawFd for Fanotify {
fn as_raw_fd(&self) -> RawFd
{
self.fd.as_raw_fd()
}
}
impl From<Fanotify> for OwnedFd {
fn from(value: Fanotify) -> Self {
value.fd
}
}
impl Fanotify {
/// Constructs a `Fanotify` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `Fanotify`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}

273
vendor/nix/src/sys/inotify.rs vendored Normal file
View File

@@ -0,0 +1,273 @@
//! Monitoring API for filesystem events.
//!
//! Inotify is a Linux-only API to monitor filesystems events.
//!
//! For more documentation, please read [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html).
//!
//! # Examples
//!
//! Monitor all events happening in directory "test":
//! ```no_run
//! # use nix::sys::inotify::{AddWatchFlags,InitFlags,Inotify};
//! #
//! // We create a new inotify instance.
//! let instance = Inotify::init(InitFlags::empty()).unwrap();
//!
//! // We add a new watch on directory "test" for all events.
//! let wd = instance.add_watch("test", AddWatchFlags::IN_ALL_EVENTS).unwrap();
//!
//! loop {
//! // We read from our inotify instance for events.
//! let events = instance.read_events().unwrap();
//! println!("Events: {:?}", events);
//! }
//! ```
use crate::errno::Errno;
use crate::unistd::read;
use crate::NixPath;
use crate::Result;
use cfg_if::cfg_if;
use libc::{c_char, c_int};
use std::ffi::{CStr, OsStr, OsString};
use std::mem::{size_of, MaybeUninit};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
use std::ptr;
libc_bitflags! {
/// Configuration options for [`inotify_add_watch`](fn.inotify_add_watch.html).
pub struct AddWatchFlags: u32 {
/// File was accessed.
IN_ACCESS;
/// File was modified.
IN_MODIFY;
/// Metadata changed.
IN_ATTRIB;
/// Writable file was closed.
IN_CLOSE_WRITE;
/// Nonwritable file was closed.
IN_CLOSE_NOWRITE;
/// File was opened.
IN_OPEN;
/// File was moved from X.
IN_MOVED_FROM;
/// File was moved to Y.
IN_MOVED_TO;
/// Subfile was created.
IN_CREATE;
/// Subfile was deleted.
IN_DELETE;
/// Self was deleted.
IN_DELETE_SELF;
/// Self was moved.
IN_MOVE_SELF;
/// Backing filesystem was unmounted.
IN_UNMOUNT;
/// Event queue overflowed.
IN_Q_OVERFLOW;
/// File was ignored.
IN_IGNORED;
/// Combination of `IN_CLOSE_WRITE` and `IN_CLOSE_NOWRITE`.
IN_CLOSE;
/// Combination of `IN_MOVED_FROM` and `IN_MOVED_TO`.
IN_MOVE;
/// Only watch the path if it is a directory.
IN_ONLYDIR;
/// Don't follow symlinks.
IN_DONT_FOLLOW;
/// Event occurred against directory.
IN_ISDIR;
/// Only send event once.
IN_ONESHOT;
/// All of the events.
IN_ALL_EVENTS;
}
}
libc_bitflags! {
/// Configuration options for [`inotify_init1`](fn.inotify_init1.html).
pub struct InitFlags: c_int {
/// Set the `FD_CLOEXEC` flag on the file descriptor.
IN_CLOEXEC;
/// Set the `O_NONBLOCK` flag on the open file description referred to by the new file descriptor.
IN_NONBLOCK;
}
}
/// An inotify instance. This is also a file descriptor, you can feed it to
/// other interfaces consuming file descriptors, epoll for example.
#[derive(Debug)]
pub struct Inotify {
fd: OwnedFd,
}
/// This object is returned when you create a new watch on an inotify instance.
/// It is then returned as part of an event once triggered. It allows you to
/// know which watch triggered which event.
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct WatchDescriptor {
wd: i32,
}
/// A single inotify event.
///
/// For more documentation see, [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html).
#[derive(Debug)]
pub struct InotifyEvent {
/// Watch descriptor. This field corresponds to the watch descriptor you
/// were issued when calling add_watch. It allows you to know which watch
/// this event comes from.
pub wd: WatchDescriptor,
/// Event mask. This field is a bitfield describing the exact event that
/// occured.
pub mask: AddWatchFlags,
/// This cookie is a number that allows you to connect related events. For
/// now only IN_MOVED_FROM and IN_MOVED_TO can be connected.
pub cookie: u32,
/// Filename. This field exists only if the event was triggered for a file
/// inside the watched directory.
pub name: Option<OsString>,
}
impl Inotify {
/// Initialize a new inotify instance.
///
/// Returns a Result containing an inotify instance.
///
/// For more information see, [inotify_init(2)](https://man7.org/linux/man-pages/man2/inotify_init.2.html).
pub fn init(flags: InitFlags) -> Result<Inotify> {
let res = Errno::result(unsafe { libc::inotify_init1(flags.bits()) });
res.map(|fd| Inotify {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
})
}
/// Adds a new watch on the target file or directory.
///
/// Returns a watch descriptor. This is not a File Descriptor!
///
/// For more information see, [inotify_add_watch(2)](https://man7.org/linux/man-pages/man2/inotify_add_watch.2.html).
pub fn add_watch<P: ?Sized + NixPath>(
&self,
path: &P,
mask: AddWatchFlags,
) -> Result<WatchDescriptor> {
let res = path.with_nix_path(|cstr| unsafe {
libc::inotify_add_watch(
self.fd.as_raw_fd(),
cstr.as_ptr(),
mask.bits(),
)
})?;
Errno::result(res).map(|wd| WatchDescriptor { wd })
}
/// Removes an existing watch using the watch descriptor returned by
/// inotify_add_watch.
///
/// Returns an EINVAL error if the watch descriptor is invalid.
///
/// For more information see, [inotify_rm_watch(2)](https://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html).
pub fn rm_watch(&self, wd: WatchDescriptor) -> Result<()> {
cfg_if! {
if #[cfg(target_os = "linux")] {
let arg = wd.wd;
} else if #[cfg(target_os = "android")] {
let arg = wd.wd as u32;
}
}
let res = unsafe { libc::inotify_rm_watch(self.fd.as_raw_fd(), arg) };
Errno::result(res).map(drop)
}
/// Reads a collection of events from the inotify file descriptor. This call
/// can either be blocking or non blocking depending on whether IN_NONBLOCK
/// was set at initialization.
///
/// Returns as many events as available. If the call was non blocking and no
/// events could be read then the EAGAIN error is returned.
pub fn read_events(&self) -> Result<Vec<InotifyEvent>> {
let header_size = size_of::<libc::inotify_event>();
const BUFSIZ: usize = 4096;
let mut buffer = [0u8; BUFSIZ];
let mut events = Vec::new();
let mut offset = 0;
let nread = read(&self.fd, &mut buffer)?;
while (nread - offset) >= header_size {
let event = unsafe {
let mut event = MaybeUninit::<libc::inotify_event>::uninit();
ptr::copy_nonoverlapping(
buffer.as_ptr().add(offset),
event.as_mut_ptr().cast(),
(BUFSIZ - offset).min(header_size),
);
event.assume_init()
};
let name = match event.len {
0 => None,
_ => {
let ptr = unsafe {
buffer.as_ptr().add(offset + header_size)
as *const c_char
};
let cstr = unsafe { CStr::from_ptr(ptr) };
Some(OsStr::from_bytes(cstr.to_bytes()).to_owned())
}
};
events.push(InotifyEvent {
wd: WatchDescriptor { wd: event.wd },
mask: AddWatchFlags::from_bits_truncate(event.mask),
cookie: event.cookie,
name,
});
offset += header_size + event.len as usize;
}
Ok(events)
}
/// Constructs an `Inotify` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `Inotify`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}
impl FromRawFd for Inotify {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Inotify {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
}
}
}
impl AsFd for Inotify {
fn as_fd(&'_ self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
impl From<Inotify> for OwnedFd {
fn from(value: Inotify) -> Self {
value.fd
}
}

129
vendor/nix/src/sys/ioctl/bsd.rs vendored Normal file
View File

@@ -0,0 +1,129 @@
/// The datatype used for the ioctl number
#[doc(hidden)]
#[cfg(not(solarish))]
pub type ioctl_num_type = ::libc::c_ulong;
#[doc(hidden)]
#[cfg(solarish)]
pub type ioctl_num_type = ::libc::c_int;
/// The datatype used for the 3rd argument
#[doc(hidden)]
pub type ioctl_param_type = ::libc::c_int;
mod consts {
use crate::sys::ioctl::ioctl_num_type;
#[doc(hidden)]
pub const VOID: ioctl_num_type = 0x2000_0000;
#[doc(hidden)]
pub const OUT: ioctl_num_type = 0x4000_0000;
#[doc(hidden)]
#[allow(overflowing_literals)]
pub const IN: ioctl_num_type = 0x8000_0000;
#[doc(hidden)]
pub const INOUT: ioctl_num_type = IN | OUT;
#[doc(hidden)]
pub const IOCPARM_MASK: ioctl_num_type = 0x1fff;
}
pub use self::consts::*;
#[macro_export]
#[doc(hidden)]
macro_rules! ioc {
($inout:expr, $group:expr, $num:expr, $len:expr) => {
$inout
| (($len as $crate::sys::ioctl::ioctl_num_type
& $crate::sys::ioctl::IOCPARM_MASK)
<< 16)
| (($group as $crate::sys::ioctl::ioctl_num_type) << 8)
| ($num as $crate::sys::ioctl::ioctl_num_type)
};
}
/// Generate an ioctl request code for a command that passes no data.
///
/// This is equivalent to the `_IO()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_none!()` directly.
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// const KVMIO: u8 = 0xAE;
/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03));
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! request_code_none {
($g:expr, $n:expr) => {
ioc!($crate::sys::ioctl::VOID, $g, $n, 0)
};
}
/// Generate an ioctl request code for a command that passes an integer
///
/// This is equivalent to the `_IOWINT()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_write_int!()` directly.
#[macro_export(local_inner_macros)]
macro_rules! request_code_write_int {
($g:expr, $n:expr) => {
ioc!(
$crate::sys::ioctl::VOID,
$g,
$n,
::std::mem::size_of::<$crate::libc::c_int>()
)
};
}
/// Generate an ioctl request code for a command that reads.
///
/// This is equivalent to the `_IOR()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_read!()` directly.
///
/// The read/write direction is relative to userland, so this
/// command would be userland is reading and the kernel is
/// writing.
#[macro_export(local_inner_macros)]
macro_rules! request_code_read {
($g:expr, $n:expr, $len:expr) => {
ioc!($crate::sys::ioctl::OUT, $g, $n, $len)
};
}
/// Generate an ioctl request code for a command that writes.
///
/// This is equivalent to the `_IOW()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_write!()` directly.
///
/// The read/write direction is relative to userland, so this
/// command would be userland is writing and the kernel is
/// reading.
#[macro_export(local_inner_macros)]
macro_rules! request_code_write {
($g:expr, $n:expr, $len:expr) => {
ioc!($crate::sys::ioctl::IN, $g, $n, $len)
};
}
/// Generate an ioctl request code for a command that reads and writes.
///
/// This is equivalent to the `_IOWR()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_readwrite!()` directly.
#[macro_export(local_inner_macros)]
macro_rules! request_code_readwrite {
($g:expr, $n:expr, $len:expr) => {
ioc!($crate::sys::ioctl::INOUT, $g, $n, $len)
};
}

180
vendor/nix/src/sys/ioctl/linux.rs vendored Normal file
View File

@@ -0,0 +1,180 @@
use cfg_if::cfg_if;
/// The datatype used for the ioctl number
#[cfg(any(
target_os = "android",
target_os = "fuchsia",
target_env = "musl",
target_env = "ohos"
))]
#[doc(hidden)]
pub type ioctl_num_type = ::libc::c_int;
#[cfg(not(any(
target_os = "android",
target_os = "fuchsia",
target_env = "musl",
target_env = "ohos"
)))]
#[doc(hidden)]
pub type ioctl_num_type = ::libc::c_ulong;
/// The datatype used for the 3rd argument
#[doc(hidden)]
pub type ioctl_param_type = ::libc::c_ulong;
#[doc(hidden)]
pub const NRBITS: ioctl_num_type = 8;
#[doc(hidden)]
pub const TYPEBITS: ioctl_num_type = 8;
cfg_if! {
if #[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "sparc64"
))] {
mod consts {
#[doc(hidden)]
pub const NONE: u8 = 1;
#[doc(hidden)]
pub const READ: u8 = 2;
#[doc(hidden)]
pub const WRITE: u8 = 4;
#[doc(hidden)]
pub const SIZEBITS: u8 = 13;
#[doc(hidden)]
pub const DIRBITS: u8 = 3;
}
} else {
// "Generic" ioctl protocol
mod consts {
#[doc(hidden)]
pub const NONE: u8 = 0;
#[doc(hidden)]
pub const READ: u8 = 2;
#[doc(hidden)]
pub const WRITE: u8 = 1;
#[doc(hidden)]
pub const SIZEBITS: u8 = 14;
#[doc(hidden)]
pub const DIRBITS: u8 = 2;
}
}
}
pub use self::consts::*;
#[doc(hidden)]
pub const NRSHIFT: ioctl_num_type = 0;
#[doc(hidden)]
pub const TYPESHIFT: ioctl_num_type = NRSHIFT + NRBITS as ioctl_num_type;
#[doc(hidden)]
pub const SIZESHIFT: ioctl_num_type = TYPESHIFT + TYPEBITS as ioctl_num_type;
#[doc(hidden)]
pub const DIRSHIFT: ioctl_num_type = SIZESHIFT + SIZEBITS as ioctl_num_type;
#[doc(hidden)]
pub const NRMASK: ioctl_num_type = (1 << NRBITS) - 1;
#[doc(hidden)]
pub const TYPEMASK: ioctl_num_type = (1 << TYPEBITS) - 1;
#[doc(hidden)]
pub const SIZEMASK: ioctl_num_type = (1 << SIZEBITS) - 1;
#[doc(hidden)]
pub const DIRMASK: ioctl_num_type = (1 << DIRBITS) - 1;
/// Encode an ioctl command.
#[macro_export]
#[doc(hidden)]
macro_rules! ioc {
($dir:expr, $ty:expr, $nr:expr, $sz:expr) => {
(($dir as $crate::sys::ioctl::ioctl_num_type
& $crate::sys::ioctl::DIRMASK)
<< $crate::sys::ioctl::DIRSHIFT)
| (($ty as $crate::sys::ioctl::ioctl_num_type
& $crate::sys::ioctl::TYPEMASK)
<< $crate::sys::ioctl::TYPESHIFT)
| (($nr as $crate::sys::ioctl::ioctl_num_type
& $crate::sys::ioctl::NRMASK)
<< $crate::sys::ioctl::NRSHIFT)
| (($sz as $crate::sys::ioctl::ioctl_num_type
& $crate::sys::ioctl::SIZEMASK)
<< $crate::sys::ioctl::SIZESHIFT)
};
}
/// Generate an ioctl request code for a command that passes no data.
///
/// This is equivalent to the `_IO()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_none!()` directly.
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// const KVMIO: u8 = 0xAE;
/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03));
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! request_code_none {
($ty:expr, $nr:expr) => {
ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0)
};
}
/// Generate an ioctl request code for a command that reads.
///
/// This is equivalent to the `_IOR()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_read!()` directly.
///
/// The read/write direction is relative to userland, so this
/// command would be userland is reading and the kernel is
/// writing.
#[macro_export(local_inner_macros)]
macro_rules! request_code_read {
($ty:expr, $nr:expr, $sz:expr) => {
ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz)
};
}
/// Generate an ioctl request code for a command that writes.
///
/// This is equivalent to the `_IOW()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_write!()` directly.
///
/// The read/write direction is relative to userland, so this
/// command would be userland is writing and the kernel is
/// reading.
#[macro_export(local_inner_macros)]
macro_rules! request_code_write {
($ty:expr, $nr:expr, $sz:expr) => {
ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz)
};
}
/// Generate an ioctl request code for a command that reads and writes.
///
/// This is equivalent to the `_IOWR()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_readwrite!()` directly.
#[macro_export(local_inner_macros)]
macro_rules! request_code_readwrite {
($ty:expr, $nr:expr, $sz:expr) => {
ioc!(
$crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE,
$ty,
$nr,
$sz
)
};
}

798
vendor/nix/src/sys/ioctl/mod.rs vendored Normal file
View File

@@ -0,0 +1,798 @@
//! Provide helpers for making ioctl system calls.
//!
//! This library is pretty low-level and messy. `ioctl` is not fun.
//!
//! What is an `ioctl`?
//! ===================
//!
//! The `ioctl` syscall is the grab-bag syscall on POSIX systems. Don't want to add a new
//! syscall? Make it an `ioctl`! `ioctl` refers to both the syscall, and the commands that can be
//! sent with it. `ioctl` stands for "IO control", and the commands are always sent to a file
//! descriptor.
//!
//! It is common to see `ioctl`s used for the following purposes:
//!
//! * Provide read/write access to out-of-band data related to a device such as configuration
//! (for instance, setting serial port options)
//! * Provide a mechanism for performing full-duplex data transfers (for instance, xfer on SPI
//! devices).
//! * Provide access to control functions on a device (for example, on Linux you can send
//! commands like pause, resume, and eject to the CDROM device.
//! * Do whatever else the device driver creator thought made most sense.
//!
//! `ioctl`s are synchronous system calls and are similar to read and write calls in that regard.
//! They operate on file descriptors and have an identifier that specifies what the ioctl is.
//! Additionally they may read or write data and therefore need to pass along a data pointer.
//! Besides the semantics of the ioctls being confusing, the generation of this identifer can also
//! be difficult.
//!
//! Historically `ioctl` numbers were arbitrary hard-coded values. In Linux (before 2.6) and some
//! unices this has changed to a more-ordered system where the ioctl numbers are partitioned into
//! subcomponents (For linux this is documented in
//! [`Documentation/ioctl/ioctl-number.rst`](https://elixir.bootlin.com/linux/latest/source/Documentation/userspace-api/ioctl/ioctl-number.rst)):
//!
//! * Number: The actual ioctl ID
//! * Type: A grouping of ioctls for a common purpose or driver
//! * Size: The size in bytes of the data that will be transferred
//! * Direction: Whether there is any data and if it's read, write, or both
//!
//! Newer drivers should not generate complete integer identifiers for their `ioctl`s instead
//! preferring to use the 4 components above to generate the final ioctl identifier. Because of
//! how old `ioctl`s are, however, there are many hard-coded `ioctl` identifiers. These are
//! commonly referred to as "bad" in `ioctl` documentation.
//!
//! Defining `ioctl`s
//! =================
//!
//! This library provides several `ioctl_*!` macros for binding `ioctl`s. These generate public
//! unsafe functions that can then be used for calling the ioctl. This macro has a few different
//! ways it can be used depending on the specific ioctl you're working with.
//!
//! A simple `ioctl` is `SPI_IOC_RD_MODE`. This ioctl works with the SPI interface on Linux. This
//! specific `ioctl` reads the mode of the SPI device as a `u8`. It's declared in
//! `/include/uapi/linux/spi/spidev.h` as `_IOR(SPI_IOC_MAGIC, 1, __u8)`. Since it uses the `_IOR`
//! macro, we know it's a `read` ioctl and can use the `ioctl_read!` macro as follows:
//!
//! ```
//! # #[macro_use] extern crate nix;
//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
//! const SPI_IOC_TYPE_MODE: u8 = 1;
//! ioctl_read!(spi_read_mode, SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, u8);
//! # fn main() {}
//! ```
//!
//! This generates the function:
//!
//! ```
//! # #[macro_use] extern crate nix;
//! # use std::mem;
//! # use nix::{libc, Result};
//! # use nix::errno::Errno;
//! # use nix::libc::c_int as c_int;
//! # const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
//! # const SPI_IOC_TYPE_MODE: u8 = 1;
//! pub unsafe fn spi_read_mode(fd: c_int, data: *mut u8) -> Result<c_int> {
//! let res = unsafe { libc::ioctl(fd, request_code_read!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, mem::size_of::<u8>()), data) };
//! Errno::result(res)
//! }
//! # fn main() {}
//! ```
//!
//! The return value for the wrapper functions generated by the `ioctl_*!` macros are `nix::Error`s.
//! These are generated by assuming the return value of the ioctl is `-1` on error and everything
//! else is a valid return value. If this is not the case, `Result::map` can be used to map some
//! of the range of "good" values (-Inf..-2, 0..Inf) into a smaller range in a helper function.
//!
//! Writing `ioctl`s generally use pointers as their data source and these should use the
//! `ioctl_write_ptr!`. But in some cases an `int` is passed directly. For these `ioctl`s use the
//! `ioctl_write_int!` macro. This variant does not take a type as the last argument:
//!
//! ```
//! # #[macro_use] extern crate nix;
//! const HCI_IOC_MAGIC: u8 = b'k';
//! const HCI_IOC_HCIDEVUP: u8 = 1;
//! ioctl_write_int!(hci_dev_up, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP);
//! # fn main() {}
//! ```
//!
//! Some `ioctl`s don't transfer any data, and those should use `ioctl_none!`. This macro
//! doesn't take a type and so it is declared similar to the `write_int` variant shown above.
//!
//! The mode for a given `ioctl` should be clear from the documentation if it has good
//! documentation. Otherwise it will be clear based on the macro used to generate the `ioctl`
//! number where `_IO`, `_IOR`, `_IOW`, and `_IOWR` map to "none", "read", "write_*", and "readwrite"
//! respectively. To determine the specific `write_` variant to use you'll need to find
//! what the argument type is supposed to be. If it's an `int`, then `write_int` should be used,
//! otherwise it should be a pointer and `write_ptr` should be used. On Linux the
//! [`ioctl_list` man page](https://man7.org/linux/man-pages/man2/ioctl_list.2.html) describes a
//! large number of `ioctl`s and describes their argument data type.
//!
//! Using "bad" `ioctl`s
//! --------------------
//!
//! As mentioned earlier, there are many old `ioctl`s that do not use the newer method of
//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the
//! `ioctl_*_bad!` macros. This naming comes from the Linux kernel which refers to these
//! `ioctl`s as "bad". These are a different variant as they bypass calling the macro that generates
//! the ioctl number and instead use the defined value directly.
//!
//! For example the `TCGETS` `ioctl` reads a `termios` data structure for a given file descriptor.
//! It's defined as `0x5401` in `ioctls.h` on Linux and can be implemented as:
//!
//! ```
//! # #[macro_use] extern crate nix;
//! # #[cfg(linux_android)]
//! # use nix::libc::TCGETS as TCGETS;
//! # #[cfg(linux_android)]
//! # use nix::libc::termios as termios;
//! # #[cfg(linux_android)]
//! ioctl_read_bad!(tcgets, TCGETS, termios);
//! # fn main() {}
//! ```
//!
//! The generated function has the same form as that generated by `ioctl_read!`:
//!
//! ```text
//! pub unsafe fn tcgets(fd: c_int, data: *mut termios) -> Result<c_int>;
//! ```
//!
//! Working with Arrays
//! -------------------
//!
//! Some `ioctl`s work with entire arrays of elements. These are supported by the `ioctl_*_buf`
//! family of macros: `ioctl_read_buf`, `ioctl_write_buf`, and `ioctl_readwrite_buf`. Note that
//! there are no "bad" versions for working with buffers. The generated functions include a `len`
//! argument to specify the number of elements (where the type of each element is specified in the
//! macro).
//!
//! Again looking to the SPI `ioctl`s on Linux for an example, there is a `SPI_IOC_MESSAGE` `ioctl`
//! that queues up multiple SPI messages by writing an entire array of `spi_ioc_transfer` structs.
//! `linux/spi/spidev.h` defines a macro to calculate the `ioctl` number like:
//!
//! ```C
//! #define SPI_IOC_MAGIC 'k'
//! #define SPI_MSGSIZE(N) ...
//! #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])
//! ```
//!
//! The `SPI_MSGSIZE(N)` calculation is already handled by the `ioctl_*!` macros, so all that's
//! needed to define this `ioctl` is:
//!
//! ```
//! # #[macro_use] extern crate nix;
//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
//! const SPI_IOC_TYPE_MESSAGE: u8 = 0;
//! # pub struct spi_ioc_transfer(u64);
//! ioctl_write_buf!(spi_transfer, SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, spi_ioc_transfer);
//! # fn main() {}
//! ```
//!
//! This generates a function like:
//!
//! ```
//! # #[macro_use] extern crate nix;
//! # use std::mem;
//! # use nix::{libc, Result};
//! # use nix::errno::Errno;
//! # use nix::libc::c_int as c_int;
//! # const SPI_IOC_MAGIC: u8 = b'k';
//! # const SPI_IOC_TYPE_MESSAGE: u8 = 0;
//! # pub struct spi_ioc_transfer(u64);
//! pub unsafe fn spi_message(fd: c_int, data: &mut [spi_ioc_transfer]) -> Result<c_int> {
//! let res = unsafe {
//! libc::ioctl(
//! fd,
//! request_code_write!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, data.len() * mem::size_of::<spi_ioc_transfer>()),
//! data
//! )
//! };
//! Errno::result(res)
//! }
//! # fn main() {}
//! ```
//!
//! Finding `ioctl` Documentation
//! -----------------------------
//!
//! For Linux, look at your system's headers. For example, `/usr/include/linux/input.h` has a lot
//! of lines defining macros which use `_IO`, `_IOR`, `_IOW`, `_IOC`, and `_IOWR`. Some `ioctl`s are
//! documented directly in the headers defining their constants, but others have more extensive
//! documentation in man pages (like termios' `ioctl`s which are in `tty_ioctl(4)`).
//!
//! Documenting the Generated Functions
//! ===================================
//!
//! In many cases, users will wish for the functions generated by the `ioctl`
//! macro to be public and documented. For this reason, the generated functions
//! are public by default. If you wish to hide the ioctl, you will need to put
//! them in a private module.
//!
//! For documentation, it is possible to use doc comments inside the `ioctl_*!` macros. Here is an
//! example :
//!
//! ```
//! # #[macro_use] extern crate nix;
//! # use nix::libc::c_int;
//! ioctl_read! {
//! /// Make the given terminal the controlling terminal of the calling process. The calling
//! /// process must be a session leader and not have a controlling terminal already. If the
//! /// terminal is already the controlling terminal of a different session group then the
//! /// ioctl will fail with **EPERM**, unless the caller is root (more precisely: has the
//! /// **CAP_SYS_ADMIN** capability) and arg equals 1, in which case the terminal is stolen
//! /// and all processes that had it as controlling terminal lose it.
//! tiocsctty, b't', 19, c_int
//! }
//!
//! # fn main() {}
//! ```
use cfg_if::cfg_if;
#[cfg(any(linux_android, target_os = "fuchsia", target_os = "redox"))]
#[macro_use]
mod linux;
#[cfg(any(linux_android, target_os = "fuchsia", target_os = "redox"))]
pub use self::linux::*;
#[cfg(any(bsd, solarish, target_os = "haiku",))]
#[macro_use]
mod bsd;
#[cfg(any(bsd, solarish, target_os = "haiku",))]
pub use self::bsd::*;
/// Convert raw ioctl return value to a Nix result
#[macro_export]
#[doc(hidden)]
macro_rules! convert_ioctl_res {
($w:expr) => {{
$crate::errno::Errno::result($w)
}};
}
/// Generates a wrapper function for an ioctl that passes no data to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// The `videodev2` driver on Linux defines the `log_status` `ioctl` as:
///
/// ```C
/// #define VIDIOC_LOG_STATUS _IO('V', 70)
/// ```
///
/// This can be implemented in Rust like:
///
/// ```no_run
/// # #[macro_use] extern crate nix;
/// ioctl_none!(log_status, b'V', 70);
/// fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_none {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_none!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type))
}
}
)
}
/// Generates a wrapper function for a "bad" ioctl that passes no data to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl request code
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```no_run
/// # #[macro_use] extern crate nix;
/// # use libc::TIOCNXCL;
/// # use std::fs::File;
/// # use std::os::unix::io::AsRawFd;
/// ioctl_none_bad!(tiocnxcl, TIOCNXCL);
/// fn main() {
/// let file = File::open("/dev/ttyUSB0").unwrap();
/// unsafe { tiocnxcl(file.as_raw_fd()) }.unwrap();
/// }
/// ```
// TODO: add an example using request_code_*!()
#[macro_export(local_inner_macros)]
macro_rules! ioctl_none_bad {
($(#[$attr:meta])* $name:ident, $nr:expr) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type))
}
}
)
}
/// Generates a wrapper function for an ioctl that reads data from the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
/// const SPI_IOC_TYPE_MODE: u8 = 1;
/// ioctl_read!(spi_read_mode, SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, u8);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_read {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *mut $ty)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
/// Generates a wrapper function for a "bad" ioctl that reads data from the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl request code
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// # #[cfg(linux_android)]
/// ioctl_read_bad!(tcgets, libc::TCGETS, libc::termios);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_read_bad {
($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *mut $ty)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
/// Generates a wrapper function for an ioctl that writes data through a pointer to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *const DATA_TYPE) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// # pub struct v4l2_audio {}
/// ioctl_write_ptr!(s_audio, b'V', 34, v4l2_audio);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_write_ptr {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *const $ty)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
/// Generates a wrapper function for a "bad" ioctl that writes data through a pointer to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl request code
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *const DATA_TYPE) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// # #[cfg(linux_android)]
/// ioctl_write_ptr_bad!(tcsets, libc::TCSETS, libc::termios);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_write_ptr_bad {
($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *const $ty)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
cfg_if! {
if #[cfg(freebsdlike)] {
/// Generates a wrapper function for a ioctl that writes an integer to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: nix::sys::ioctl::ioctl_param_type) -> Result<libc::c_int>
/// ```
///
/// `nix::sys::ioctl::ioctl_param_type` depends on the OS:
/// * BSD - `libc::c_int`
/// * Linux - `libc::c_ulong`
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// ioctl_write_int!(vt_activate, b'v', 4);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_write_int {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: $crate::sys::ioctl::ioctl_param_type)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write_int!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
} else {
/// Generates a wrapper function for a ioctl that writes an integer to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: nix::sys::ioctl::ioctl_param_type) -> Result<libc::c_int>
/// ```
///
/// `nix::sys::ioctl::ioctl_param_type` depends on the OS:
/// * BSD - `libc::c_int`
/// * Linux - `libc::c_ulong`
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// const HCI_IOC_MAGIC: u8 = b'k';
/// const HCI_IOC_HCIDEVUP: u8 = 1;
/// ioctl_write_int!(hci_dev_up, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_write_int {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: $crate::sys::ioctl::ioctl_param_type)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$crate::libc::c_int>()) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
}
}
/// Generates a wrapper function for a "bad" ioctl that writes an integer to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl request code
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: libc::c_int) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate nix;
/// # #[cfg(linux_android)]
/// ioctl_write_int_bad!(tcsbrk, libc::TCSBRK);
/// # fn main() {}
/// ```
///
/// ```rust
/// # #[macro_use] extern crate nix;
/// const KVMIO: u8 = 0xAE;
/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03));
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_write_int_bad {
($(#[$attr:meta])* $name:ident, $nr:expr) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: $crate::libc::c_int)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
/// Generates a wrapper function for an ioctl that reads and writes data to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// # pub struct v4l2_audio {}
/// ioctl_readwrite!(enum_audio, b'V', 65, v4l2_audio);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_readwrite {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *mut $ty)
-> $crate::Result<$crate::libc::c_int> {
let ioty = $ioty;
let nr = $nr;
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!(ioty, nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
/// Generates a wrapper function for a "bad" ioctl that reads and writes data to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl request code
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
// TODO: Find an example for ioctl_readwrite_bad
#[macro_export(local_inner_macros)]
macro_rules! ioctl_readwrite_bad {
($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: *mut $ty)
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
}
}
)
}
/// Generates a wrapper function for an ioctl that reads an array of elements from the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &mut [DATA_TYPE]) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
// TODO: Find an example for ioctl_read_buf
#[macro_export(local_inner_macros)]
macro_rules! ioctl_read_buf {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: &mut [$ty])
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data.as_mut_ptr()))
}
}
)
}
/// Generates a wrapper function for an ioctl that writes an array of elements to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &[DATA_TYPE]) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate nix;
/// const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
/// const SPI_IOC_TYPE_MESSAGE: u8 = 0;
/// # pub struct spi_ioc_transfer(u64);
/// ioctl_write_buf!(spi_transfer, SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, spi_ioc_transfer);
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! ioctl_write_buf {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: &[$ty])
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data.as_ptr()))
}
}
)
}
/// Generates a wrapper function for an ioctl that reads and writes an array of elements to the kernel.
///
/// The arguments to this macro are:
///
/// * The function name
/// * The ioctl identifier
/// * The ioctl sequence number
/// * The data type passed by this ioctl
///
/// The generated function has the following signature:
///
/// ```rust,ignore
/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &mut [DATA_TYPE]) -> Result<libc::c_int>
/// ```
///
/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
// TODO: Find an example for readwrite_buf
#[macro_export(local_inner_macros)]
macro_rules! ioctl_readwrite_buf {
($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
$(#[$attr])*
pub unsafe fn $name(fd: $crate::libc::c_int,
data: &mut [$ty])
-> $crate::Result<$crate::libc::c_int> {
unsafe {
convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data.as_mut_ptr()))
}
}
)
}

129
vendor/nix/src/sys/memfd.rs vendored Normal file
View File

@@ -0,0 +1,129 @@
//! Interfaces for managing memory-backed files.
use cfg_if::cfg_if;
use std::os::unix::io::{FromRawFd, OwnedFd, RawFd};
use crate::errno::Errno;
use crate::{NixPath, Result};
libc_bitflags!(
/// Options that change the behavior of [`memfd_create`].
pub struct MFdFlags: 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;
/// Anonymous file will be created using huge pages. It should be safe now to
/// combine with [`MFD_ALLOW_SEALING`] too.
/// However, despite its presence, on FreeBSD it is unimplemented for now (ENOSYS).
///
/// See also the hugetlb filesystem in [`memfd_create(2)`].
///
/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
#[cfg(linux_android)]
MFD_HUGETLB;
/// Shift to get the huge page size.
#[cfg(target_env = "ohos")]
MFD_HUGE_SHIFT;
/// Mask to get the huge page size.
#[cfg(target_env = "ohos")]
MFD_HUGE_MASK;
/// hugetlb size of 64KB.
#[cfg(target_env = "ohos")]
MFD_HUGE_64KB;
/// hugetlb size of 512KB.
#[cfg(target_env = "ohos")]
MFD_HUGE_512KB;
/// Following are to be used with [`MFD_HUGETLB`], indicating the desired hugetlb size.
///
/// See also the hugetlb filesystem in [`memfd_create(2)`].
///
/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
#[cfg(linux_android)]
MFD_HUGE_1MB;
/// hugetlb size of 2MB.
#[cfg(linux_android)]
MFD_HUGE_2MB;
/// hugetlb size of 8MB.
#[cfg(linux_android)]
MFD_HUGE_8MB;
/// hugetlb size of 16MB.
#[cfg(linux_android)]
MFD_HUGE_16MB;
/// hugetlb size of 32MB.
#[cfg(linux_android)]
MFD_HUGE_32MB;
/// hugetlb size of 256MB.
#[cfg(linux_android)]
MFD_HUGE_256MB;
/// hugetlb size of 512MB.
#[cfg(linux_android)]
MFD_HUGE_512MB;
/// hugetlb size of 1GB.
#[cfg(linux_android)]
MFD_HUGE_1GB;
/// hugetlb size of 2GB.
#[cfg(linux_android)]
MFD_HUGE_2GB;
/// hugetlb size of 16GB.
#[cfg(linux_android)]
MFD_HUGE_16GB;
}
);
#[deprecated(since = "0.30.0", note = "Use `MFdFlags instead`")]
/// The deprecated MemFdCreateFlag type alias
pub type MemFdCreateFlag = MFdFlags;
/// 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
#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
pub fn memfd_create<P: NixPath + ?Sized>(
name: &P,
flags: MFdFlags,
) -> Result<OwnedFd> {
let res = name.with_nix_path(|cstr| {
unsafe {
cfg_if! {
if #[cfg(all(
// Android does not have a memfd_create symbol
not(target_os = "android"),
any(
target_os = "freebsd",
// If the OS is Linux, gnu/musl/ohos expose a memfd_create symbol but not uclibc
target_env = "gnu",
target_env = "musl",
target_env = "ohos"
)))]
{
libc::memfd_create(cstr.as_ptr(), flags.bits())
} else {
libc::syscall(libc::SYS_memfd_create, cstr.as_ptr(), flags.bits())
}
}
}
})?;
Errno::result(res).map(|r| unsafe { OwnedFd::from_raw_fd(r as RawFd) })
}

667
vendor/nix/src/sys/mman.rs vendored Normal file
View File

@@ -0,0 +1,667 @@
//! Memory management declarations.
use crate::errno::Errno;
#[cfg(not(target_os = "android"))]
use crate::NixPath;
use crate::Result;
#[cfg(not(target_os = "android"))]
#[cfg(feature = "fs")]
use crate::{fcntl::OFlag, sys::stat::Mode};
use libc::{self, c_int, c_void, off_t, size_t};
use std::ptr::NonNull;
use std::{
num::NonZeroUsize,
os::unix::io::{AsFd, AsRawFd},
};
libc_bitflags! {
/// Desired memory protection of a memory mapping.
pub struct ProtFlags: c_int {
/// Pages cannot be accessed.
PROT_NONE;
/// Pages can be read.
PROT_READ;
/// Pages can be written.
PROT_WRITE;
/// Pages can be executed
PROT_EXEC;
/// Apply protection up to the end of a mapping that grows upwards.
#[cfg(linux_android)]
PROT_GROWSDOWN;
/// Apply protection down to the beginning of a mapping that grows downwards.
#[cfg(linux_android)]
PROT_GROWSUP;
}
}
libc_bitflags! {
/// Additional parameters for [`mmap`].
pub struct MapFlags: c_int {
/// Compatibility flag. Ignored.
#[cfg(not(any(target_os = "solaris", target_os = "redox")))]
MAP_FILE;
/// Share this mapping. Mutually exclusive with `MAP_PRIVATE`.
MAP_SHARED;
/// Force mmap to check and fail on unknown flags. This also enables `MAP_SYNC`.
#[cfg(target_os = "linux")]
MAP_SHARED_VALIDATE;
/// Create a private copy-on-write mapping. Mutually exclusive with `MAP_SHARED`.
MAP_PRIVATE;
/// Place the mapping at exactly the address specified in `addr`.
MAP_FIXED;
/// Place the mapping at exactly the address specified in `addr`, but never clobber an existing range.
#[cfg(target_os = "linux")]
MAP_FIXED_NOREPLACE;
/// 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.
MAP_ANONYMOUS;
/// Put the mapping into the first 2GB of the process address space.
#[cfg(any(all(linux_android,
any(target_arch = "x86", target_arch = "x86_64")),
all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "x86_64")),
all(target_os = "linux", target_env = "ohos", target_arch = "x86_64"),
all(target_os = "freebsd", target_pointer_width = "64")))]
MAP_32BIT;
/// Used for stacks; indicates to the kernel that the mapping should extend downward in memory.
#[cfg(linux_android)]
MAP_GROWSDOWN;
/// Compatibility flag. Ignored.
#[cfg(linux_android)]
MAP_DENYWRITE;
/// Compatibility flag. Ignored.
#[cfg(linux_android)]
MAP_EXECUTABLE;
/// Mark the mmaped region to be locked in the same way as `mlock(2)`.
#[cfg(linux_android)]
MAP_LOCKED;
/// Do not reserve swap space for this mapping.
///
/// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
#[cfg(not(any(freebsdlike, target_os = "aix", target_os = "hurd", target_os = "redox")))]
MAP_NORESERVE;
/// Populate page tables for a mapping.
#[cfg(linux_android)]
MAP_POPULATE;
/// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead.
#[cfg(linux_android)]
MAP_NONBLOCK;
/// Allocate the mapping using "huge pages."
#[cfg(linux_android)]
MAP_HUGETLB;
/// Make use of 64KB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_64KB;
/// Make use of 512KB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_512KB;
/// Make use of 1MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_1MB;
/// Make use of 2MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_2MB;
/// Make use of 8MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_8MB;
/// Make use of 16MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_16MB;
/// Make use of 32MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_32MB;
/// Make use of 256MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_256MB;
/// Make use of 512MB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_512MB;
/// Make use of 1GB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_1GB;
/// Make use of 2GB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_2GB;
/// Make use of 16GB huge page (must be supported by the system)
#[cfg(target_os = "linux")]
MAP_HUGE_16GB;
/// Lock the mapped region into memory as with `mlock(2)`.
#[cfg(target_os = "netbsd")]
MAP_WIRED;
/// Causes dirtied data in the specified range to be flushed to disk only when necessary.
#[cfg(freebsdlike)]
MAP_NOSYNC;
/// Rename private pages to a file.
///
/// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
#[cfg(netbsdlike)]
MAP_RENAME;
/// Region may contain semaphores.
#[cfg(any(freebsdlike, netbsdlike))]
MAP_HASSEMAPHORE;
/// Region grows down, like a stack.
#[cfg(any(linux_android, freebsdlike, netbsdlike))]
MAP_STACK;
/// Do not write through the page caches, write directly to the file. Used for Direct Access (DAX) enabled file systems.
// Available on Linux glibc and musl, MIPS* target excluded.
#[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64", target_arch = "mips32r6", target_arch = "mips64r6")), not(target_env = "uclibc")))]
MAP_SYNC;
/// Pages in this mapping are not retained in the kernel's memory cache.
#[cfg(apple_targets)]
MAP_NOCACHE;
/// Allows the W/X bit on the page, it's necessary on aarch64 architecture.
#[cfg(apple_targets)]
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;
/// Attempt to place the mapping at exactly the address specified in `addr`.
/// it's a default behavior on OpenBSD.
#[cfg(netbsdlike)]
MAP_TRYFIXED;
}
}
impl MapFlags {
/// Create `MAP_HUGETLB` with provided size of huge page.
///
/// Under the hood it computes `MAP_HUGETLB | (huge_page_size_log2 << libc::MAP_HUGE_SHIFT`).
/// `huge_page_size_log2` denotes logarithm of huge page size to use and should be
/// between 16 and 63 (inclusively).
///
/// ```
/// # use nix::sys::mman::MapFlags;
/// let f = MapFlags::map_hugetlb_with_size_log2(30).unwrap();
/// assert_eq!(f, MapFlags::MAP_HUGETLB | MapFlags::MAP_HUGE_1GB);
/// ```
#[cfg(any(linux_android, target_os = "fuchsia"))]
pub fn map_hugetlb_with_size_log2(
huge_page_size_log2: u32,
) -> Option<Self> {
if (16..=63).contains(&huge_page_size_log2) {
let flag = libc::MAP_HUGETLB
| (huge_page_size_log2 << libc::MAP_HUGE_SHIFT) as i32;
Some(Self(flag.into()))
} else {
None
}
}
}
#[cfg(any(target_os = "linux", target_os = "netbsd"))]
libc_bitflags! {
/// 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;
/// Works in conjunction with `MREMAP_MAYMOVE` but does not unmap `old_address`.
/// Note that, in this case, `old_size` and `new_size` must be the same.
#[cfg(target_os = "linux")]
MREMAP_DONTUNMAP;
/// Place the mapping at exactly the address specified in `new_address`.
#[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`].
#[repr(i32)]
#[non_exhaustive]
pub enum MmapAdvise {
/// No further special treatment. This is the default.
MADV_NORMAL,
/// Expect random page references.
MADV_RANDOM,
/// Expect sequential page references.
MADV_SEQUENTIAL,
/// Expect access in the near future.
MADV_WILLNEED,
/// Do not expect access in the near future.
MADV_DONTNEED,
/// Free up a given range of pages and its associated backing store.
#[cfg(linux_android)]
MADV_REMOVE,
/// Do not make pages in this range available to the child after a `fork(2)`.
#[cfg(linux_android)]
MADV_DONTFORK,
/// Undo the effect of `MADV_DONTFORK`.
#[cfg(linux_android)]
MADV_DOFORK,
/// Poison the given pages.
///
/// Subsequent references to those pages are treated like hardware memory corruption.
#[cfg(linux_android)]
MADV_HWPOISON,
/// Enable Kernel Samepage Merging (KSM) for the given pages.
#[cfg(linux_android)]
MADV_MERGEABLE,
/// Undo the effect of `MADV_MERGEABLE`
#[cfg(linux_android)]
MADV_UNMERGEABLE,
/// Preserve the memory of each page but offline the original page.
#[cfg(any(target_os = "android",
all(target_os = "linux", any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "x86",
target_arch = "x86_64",
target_arch = "sparc64"))))]
MADV_SOFT_OFFLINE,
/// Enable Transparent Huge Pages (THP) for pages in the given range.
#[cfg(linux_android)]
MADV_HUGEPAGE,
/// Undo the effect of `MADV_HUGEPAGE`.
#[cfg(linux_android)]
MADV_NOHUGEPAGE,
/// Exclude the given range from a core dump.
#[cfg(linux_android)]
MADV_DONTDUMP,
/// Undo the effect of an earlier `MADV_DONTDUMP`.
#[cfg(linux_android)]
MADV_DODUMP,
/// Specify that the application no longer needs the pages in the given range.
#[cfg(not(any(target_os = "aix", target_os = "hurd", target_os = "cygwin", target_os = "redox")))]
MADV_FREE,
/// Request that the system not flush the current range to disk unless it needs to.
#[cfg(freebsdlike)]
MADV_NOSYNC,
/// Undoes the effects of `MADV_NOSYNC` for any future pages dirtied within the given range.
#[cfg(freebsdlike)]
MADV_AUTOSYNC,
/// Region is not included in a core file.
#[cfg(freebsdlike)]
MADV_NOCORE,
/// Include region in a core file
#[cfg(freebsdlike)]
MADV_CORE,
/// This process should not be killed when swap space is exhausted.
#[cfg(any(target_os = "freebsd"))]
MADV_PROTECT,
/// Invalidate the hardware page table for the given region.
#[cfg(target_os = "dragonfly")]
MADV_INVAL,
/// Set the offset of the page directory page to `value` for the virtual page table.
#[cfg(target_os = "dragonfly")]
MADV_SETMAP,
/// Indicates that the application will not need the data in the given range.
#[cfg(apple_targets)]
MADV_ZERO_WIRED_PAGES,
/// Pages can be reused (by anyone).
#[cfg(apple_targets)]
MADV_FREE_REUSABLE,
/// Caller wants to reuse those pages.
#[cfg(apple_targets)]
MADV_FREE_REUSE,
// Darwin doesn't document this flag's behavior.
#[cfg(apple_targets)]
#[allow(missing_docs)]
MADV_CAN_REUSE,
/// Reclaim the address range when applicable.
#[cfg(linux_android)]
MADV_PAGEOUT,
/// Deactivate the address range when applicable.
#[cfg(linux_android)]
MADV_COLD,
/// After fork, the adress range is zero filled.
#[cfg(linux_android)]
MADV_WIPEONFORK,
/// Undo `MADV_WIPEONFORK` when it applied.
#[cfg(linux_android)]
MADV_KEEPONFORK,
/// Pre-load the address range for reading to reduce page-fault latency.
#[cfg(linux_android)]
MADV_POPULATE_READ,
/// Pre-fault the address range for writing to reduce page-fault
/// latency on subsequent writes.
#[cfg(linux_android)]
MADV_POPULATE_WRITE,
}
}
libc_bitflags! {
/// Configuration flags for [`msync`].
pub struct MsFlags: c_int {
/// Schedule an update but return immediately.
MS_ASYNC;
/// Invalidate all cached data.
MS_INVALIDATE;
/// Invalidate pages, but leave them mapped.
#[cfg(apple_targets)]
MS_KILLPAGES;
/// Deactivate pages, but leave them mapped.
#[cfg(apple_targets)]
MS_DEACTIVATE;
/// Perform an update and wait for it to complete.
MS_SYNC;
}
}
#[cfg(not(any(target_os = "haiku", target_os = "cygwin", target_os = "redox")))]
libc_bitflags! {
/// Flags for [`mlockall`].
pub struct MlockAllFlags: c_int {
/// Lock pages that are currently mapped into the address space of the process.
MCL_CURRENT;
/// Lock pages which will become mapped into the address space of the process in the future.
MCL_FUTURE;
}
}
/// Locks all memory pages that contain part of the address range with `length`
/// bytes starting at `addr`.
///
/// Locked pages never move to the swap area.
///
/// # Safety
///
/// `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: NonNull<c_void>, length: size_t) -> Result<()> {
unsafe { Errno::result(libc::mlock(addr.as_ptr(), length)).map(drop) }
}
/// Unlocks all memory pages that contain part of the address range with
/// `length` bytes starting at `addr`.
///
/// # Safety
///
/// `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: NonNull<c_void>, length: size_t) -> Result<()> {
unsafe { Errno::result(libc::munlock(addr.as_ptr(), length)).map(drop) }
}
/// Locks all memory pages mapped into this process' address space.
///
/// Locked pages never move to the swap area. For more information, see [`mlockall(2)`].
///
/// [`mlockall(2)`]: https://man7.org/linux/man-pages/man2/mlockall.2.html
#[cfg(not(any(target_os = "haiku", target_os = "cygwin", target_os = "redox")))]
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
#[cfg(not(any(target_os = "haiku", target_os = "cygwin", target_os = "redox")))]
pub fn munlockall() -> Result<()> {
unsafe { Errno::result(libc::munlockall()) }.map(drop)
}
/// Allocate memory, or map files or devices into memory
///
/// For anonymous mappings (`MAP_ANON`/`MAP_ANONYMOUS`), see [mmap_anonymous].
///
/// # Safety
///
/// 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<F: AsFd>(
addr: Option<NonZeroUsize>,
length: NonZeroUsize,
prot: ProtFlags,
flags: MapFlags,
f: F,
offset: off_t,
) -> Result<NonNull<c_void>> {
let ptr = addr.map_or(std::ptr::null_mut(), |a| a.get() as *mut c_void);
let fd = f.as_fd().as_raw_fd();
let ret = unsafe {
libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), fd, offset)
};
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
// SAFETY: `libc::mmap` returns a valid non-null pointer or `libc::MAP_FAILED`, thus `ret`
// will be non-null here.
Ok(unsafe { NonNull::new_unchecked(ret) })
}
}
/// Create an anonymous memory mapping.
///
/// This function is a wrapper around [`mmap`]:
/// `mmap(ptr, len, prot, MAP_ANONYMOUS | flags, -1, 0)`.
///
/// # Safety
///
/// 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_anonymous(
addr: Option<NonZeroUsize>,
length: NonZeroUsize,
prot: ProtFlags,
flags: MapFlags,
) -> Result<NonNull<c_void>> {
let ptr = addr.map_or(std::ptr::null_mut(), |a| a.get() as *mut c_void);
let flags = MapFlags::MAP_ANONYMOUS | flags;
let ret = unsafe {
libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), -1, 0)
};
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
// SAFETY: `libc::mmap` returns a valid non-null pointer or `libc::MAP_FAILED`, thus `ret`
// will be non-null here.
Ok(unsafe { NonNull::new_unchecked(ret) })
}
}
/// Expands (or shrinks) an existing memory mapping, potentially moving it at
/// the same time.
///
/// # Safety
///
/// See the `mremap(2)` [man page](https://man7.org/linux/man-pages/man2/mremap.2.html) for
/// detailed requirements.
#[cfg(any(target_os = "linux", target_os = "netbsd"))]
pub unsafe fn mremap(
addr: NonNull<c_void>,
old_size: size_t,
new_size: size_t,
flags: MRemapFlags,
new_address: Option<NonNull<c_void>>,
) -> Result<NonNull<c_void>> {
#[cfg(target_os = "linux")]
let ret = unsafe {
libc::mremap(
addr.as_ptr(),
old_size,
new_size,
flags.bits(),
new_address
.map(NonNull::as_ptr)
.unwrap_or(std::ptr::null_mut()),
)
};
#[cfg(target_os = "netbsd")]
let ret = unsafe {
libc::mremap(
addr.as_ptr(),
old_size,
new_address
.map(NonNull::as_ptr)
.unwrap_or(std::ptr::null_mut()),
new_size,
flags.bits(),
)
};
if std::ptr::eq(ret, libc::MAP_FAILED) {
Err(Errno::last())
} else {
// SAFETY: `libc::mremap` returns a valid non-null pointer or `libc::MAP_FAILED`, thus `ret`
// will be non-null here.
Ok(unsafe { NonNull::new_unchecked(ret) })
}
}
/// remove a mapping
///
/// # Safety
///
/// `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: NonNull<c_void>, len: size_t) -> Result<()> {
unsafe { Errno::result(libc::munmap(addr.as_ptr(), len)).map(drop) }
}
/// give advice about use of memory
///
/// # Safety
///
/// 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
#[allow(rustdoc::broken_intra_doc_links)] // For Hurd as `MADV_FREE` is not available on it
pub unsafe fn madvise(
addr: NonNull<c_void>,
length: size_t,
advise: MmapAdvise,
) -> Result<()> {
unsafe {
Errno::result(libc::madvise(addr.as_ptr(), length, advise as i32))
.map(drop)
}
}
/// Set protection of memory mapping.
///
/// See [`mprotect(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mprotect.html) for
/// details.
///
/// # Safety
///
/// Calls to `mprotect` are inherently unsafe, as changes to memory protections can lead to
/// SIGSEGVs.
///
/// ```
/// # use nix::libc::size_t;
/// # use nix::sys::mman::{mmap_anonymous, mprotect, MapFlags, ProtFlags};
/// # use std::ptr;
/// # use std::os::unix::io::BorrowedFd;
/// const ONE_K: size_t = 1024;
/// let one_k_non_zero = std::num::NonZeroUsize::new(ONE_K).unwrap();
/// let mut slice: &mut [u8] = unsafe {
/// let mem = mmap_anonymous(None, one_k_non_zero, ProtFlags::PROT_NONE, MapFlags::MAP_PRIVATE)
/// .unwrap();
/// mprotect(mem, ONE_K, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE).unwrap();
/// std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
/// };
/// assert_eq!(slice[0], 0x00);
/// slice[0] = 0xFF;
/// assert_eq!(slice[0], 0xFF);
/// ```
pub unsafe fn mprotect(
addr: NonNull<c_void>,
length: size_t,
prot: ProtFlags,
) -> Result<()> {
unsafe {
Errno::result(libc::mprotect(addr.as_ptr(), length, prot.bits()))
.map(drop)
}
}
/// synchronize a mapped region
///
/// # Safety
///
/// `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: NonNull<c_void>,
length: size_t,
flags: MsFlags,
) -> Result<()> {
unsafe {
Errno::result(libc::msync(addr.as_ptr(), length, flags.bits()))
.map(drop)
}
}
#[cfg(not(target_os = "android"))]
feature! {
#![feature = "fs"]
/// 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
pub fn shm_open<P>(
name: &P,
flag: OFlag,
mode: Mode
) -> Result<std::os::unix::io::OwnedFd>
where P: ?Sized + NixPath
{
use std::os::unix::io::{FromRawFd, OwnedFd};
let ret = name.with_nix_path(|cstr| {
#[cfg(apple_targets)]
unsafe {
libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::c_uint)
}
#[cfg(not(apple_targets))]
unsafe {
libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::mode_t)
}
})?;
match ret {
-1 => Err(Errno::last()),
fd => Ok(unsafe{ OwnedFd::from_raw_fd(fd) })
}
}
}
/// 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| unsafe { libc::shm_unlink(cstr.as_ptr()) })?;
Errno::result(ret).map(drop)
}

209
vendor/nix/src/sys/mod.rs vendored Normal file
View File

@@ -0,0 +1,209 @@
//! Mostly platform-specific functionality
#[cfg(any(
freebsdlike,
all(
target_os = "linux",
not(any(target_env = "uclibc", target_env = "ohos"))
),
apple_targets,
target_os = "netbsd"
))]
feature! {
#![feature = "aio"]
pub mod aio;
}
feature! {
#![feature = "event"]
#[cfg(linux_android)]
#[allow(missing_docs)]
pub mod epoll;
#[cfg(bsd)]
pub mod event;
/// Event file descriptor.
#[cfg(any(linux_android, target_os = "freebsd"))]
pub mod eventfd;
}
#[cfg(target_os = "linux")]
feature! {
#![feature = "fanotify"]
pub mod fanotify;
}
#[cfg(any(
bsd,
linux_android,
solarish,
target_os = "fuchsia",
target_os = "redox",
))]
#[cfg(feature = "ioctl")]
#[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
#[macro_use]
pub mod ioctl;
#[cfg(any(linux_android, target_os = "freebsd"))]
feature! {
#![feature = "fs"]
pub mod memfd;
}
feature! {
#![feature = "mman"]
pub mod mman;
}
#[cfg(target_os = "linux")]
feature! {
#![feature = "personality"]
pub mod personality;
}
#[cfg(target_os = "linux")]
feature! {
#![feature = "process"]
pub mod prctl;
}
feature! {
#![feature = "pthread"]
pub mod pthread;
}
#[cfg(any(linux_android, bsd))]
feature! {
#![feature = "ptrace"]
#[allow(missing_docs)]
pub mod ptrace;
}
#[cfg(target_os = "linux")]
feature! {
#![feature = "quota"]
pub mod quota;
}
#[cfg(any(target_os = "linux", netbsdlike))]
feature! {
#![feature = "reboot"]
pub mod reboot;
}
#[cfg(not(any(
target_os = "redox",
target_os = "fuchsia",
solarish,
target_os = "haiku"
)))]
feature! {
#![feature = "resource"]
pub mod resource;
}
feature! {
#![feature = "poll"]
pub mod select;
}
#[cfg(any(linux_android, freebsdlike, apple_targets, solarish))]
feature! {
#![feature = "zerocopy"]
pub mod sendfile;
}
pub mod signal;
#[cfg(linux_android)]
feature! {
#![feature = "signal"]
#[allow(missing_docs)]
pub mod signalfd;
}
feature! {
#![feature = "socket"]
#[allow(missing_docs)]
pub mod socket;
}
feature! {
#![feature = "fs"]
#[allow(missing_docs)]
pub mod stat;
}
#[cfg(any(
linux_android,
freebsdlike,
apple_targets,
target_os = "openbsd",
target_os = "cygwin"
))]
feature! {
#![feature = "fs"]
pub mod statfs;
}
feature! {
#![feature = "fs"]
pub mod statvfs;
}
#[cfg(linux_android)]
#[allow(missing_docs)]
pub mod sysinfo;
feature! {
#![feature = "term"]
#[allow(missing_docs)]
pub mod termios;
}
#[allow(missing_docs)]
pub mod time;
feature! {
#![feature = "uio"]
pub mod uio;
}
feature! {
#![feature = "feature"]
pub mod utsname;
}
feature! {
#![feature = "process"]
pub mod wait;
}
#[cfg(linux_android)]
feature! {
#![feature = "inotify"]
pub mod inotify;
}
#[cfg(linux_android)]
feature! {
#![feature = "time"]
pub mod timerfd;
}
#[cfg(all(
any(
target_os = "freebsd",
solarish,
target_os = "linux",
target_os = "netbsd"
),
feature = "time",
feature = "signal"
))]
feature! {
#![feature = "time"]
pub mod timer;
}

94
vendor/nix/src/sys/personality.rs vendored Normal file
View File

@@ -0,0 +1,94 @@
//! Process execution domains
use crate::errno::Errno;
use crate::Result;
use libc::{self, c_int, c_ulong};
libc_bitflags! {
/// Flags used and returned by [`get()`](fn.get.html) and
/// [`set()`](fn.set.html).
pub struct Persona: c_int {
/// Provide the legacy virtual address space layout.
ADDR_COMPAT_LAYOUT;
/// Disable address-space-layout randomization.
ADDR_NO_RANDOMIZE;
/// Limit the address space to 32 bits.
ADDR_LIMIT_32BIT;
/// Use `0xc0000000` as the offset at which to search a virtual memory
/// chunk on [`mmap(2)`], otherwise use `0xffffe000`.
///
/// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
ADDR_LIMIT_3GB;
/// User-space function pointers to signal handlers point to descriptors.
#[cfg(not(any(target_env = "musl", target_env = "uclibc", target_env = "ohos")))]
FDPIC_FUNCPTRS;
/// Map page 0 as read-only.
MMAP_PAGE_ZERO;
/// `PROT_READ` implies `PROT_EXEC` for [`mmap(2)`].
///
/// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
READ_IMPLIES_EXEC;
/// No effects.
SHORT_INODE;
/// [`select(2)`], [`pselect(2)`], and [`ppoll(2)`] do not modify the
/// returned timeout argument when interrupted by a signal handler.
///
/// [`select(2)`]: https://man7.org/linux/man-pages/man2/select.2.html
/// [`pselect(2)`]: https://man7.org/linux/man-pages/man2/pselect.2.html
/// [`ppoll(2)`]: https://man7.org/linux/man-pages/man2/ppoll.2.html
STICKY_TIMEOUTS;
/// Have [`uname(2)`] report a 2.6.40+ version number rather than a 3.x
/// version number.
///
/// [`uname(2)`]: https://man7.org/linux/man-pages/man2/uname.2.html
#[cfg(not(any(target_env = "musl", target_env = "uclibc", target_env = "ohos")))]
UNAME26;
/// No effects.
WHOLE_SECONDS;
}
}
/// Retrieve the current process personality.
///
/// Returns a Result containing a Persona instance.
///
/// Example:
///
/// ```
/// # use nix::sys::personality::{self, Persona};
/// let pers = personality::get().unwrap();
/// assert!(!pers.contains(Persona::WHOLE_SECONDS));
/// ```
pub fn get() -> Result<Persona> {
let res = unsafe { libc::personality(0xFFFFFFFF) };
Errno::result(res).map(Persona::from_bits_truncate)
}
/// Set the current process personality.
///
/// Returns a Result containing the *previous* personality for the
/// process, as a Persona.
///
/// For more information, see [personality(2)](https://man7.org/linux/man-pages/man2/personality.2.html)
///
/// **NOTE**: This call **replaces** the current personality entirely.
/// To **update** the personality, first call `get()` and then `set()`
/// with the modified persona.
///
/// Example:
///
// Disable test on aarch64 until we know why it fails.
// https://github.com/nix-rust/nix/issues/2060
#[cfg_attr(target_arch = "aarch64", doc = " ```no_run")]
#[cfg_attr(not(target_arch = "aarch64"), doc = " ```")]
/// # use nix::sys::personality::{self, Persona};
/// let mut pers = personality::get().unwrap();
/// assert!(!pers.contains(Persona::ADDR_NO_RANDOMIZE));
/// personality::set(pers | Persona::ADDR_NO_RANDOMIZE).unwrap();
/// ```
pub fn set(persona: Persona) -> Result<Persona> {
let res = unsafe { libc::personality(persona.bits() as c_ulong) };
Errno::result(res).map(Persona::from_bits_truncate)
}

228
vendor/nix/src/sys/prctl.rs vendored Normal file
View File

@@ -0,0 +1,228 @@
//! prctl is a Linux-only API for performing operations on a process or thread.
//!
//! Note that careless use of some prctl() operations can confuse the user-space run-time
//! environment, so these operations should be used with care.
//!
//! For more documentation, please read [prctl(2)](https://man7.org/linux/man-pages/man2/prctl.2.html).
use crate::errno::Errno;
use crate::sys::signal::Signal;
use crate::Result;
use libc::{c_int, c_ulong, c_void};
use std::convert::TryFrom;
use std::ffi::{CStr, CString};
use std::num::NonZeroUsize;
use std::ptr::NonNull;
libc_enum! {
/// The type of hardware memory corruption kill policy for the thread.
#[repr(i32)]
#[non_exhaustive]
#[allow(non_camel_case_types)]
pub enum PrctlMCEKillPolicy {
/// The thread will receive SIGBUS as soon as a memory corruption is detected.
PR_MCE_KILL_EARLY,
/// The process is killed only when it accesses a corrupted page.
PR_MCE_KILL_LATE,
/// Uses the system-wide default.
PR_MCE_KILL_DEFAULT,
}
impl TryFrom<i32>
}
fn prctl_set_bool(option: c_int, status: bool) -> Result<()> {
let res = unsafe { libc::prctl(option, status as c_ulong, 0, 0, 0) };
Errno::result(res).map(drop)
}
fn prctl_get_bool(option: c_int) -> Result<bool> {
let res = unsafe { libc::prctl(option, 0, 0, 0, 0) };
Errno::result(res).map(|res| res != 0)
}
/// Set the "child subreaper" attribute for this process
pub fn set_child_subreaper(attribute: bool) -> Result<()> {
prctl_set_bool(libc::PR_SET_CHILD_SUBREAPER, attribute)
}
/// Get the "child subreaper" attribute for this process
pub fn get_child_subreaper() -> Result<bool> {
// prctl writes into this var
let mut subreaper: c_int = 0;
let res = unsafe {
libc::prctl(libc::PR_GET_CHILD_SUBREAPER, &mut subreaper, 0, 0, 0)
};
Errno::result(res).map(|_| subreaper != 0)
}
/// Set the dumpable attribute which determines if core dumps are created for this process.
pub fn set_dumpable(attribute: bool) -> Result<()> {
prctl_set_bool(libc::PR_SET_DUMPABLE, attribute)
}
/// Get the dumpable attribute for this process.
pub fn get_dumpable() -> Result<bool> {
prctl_get_bool(libc::PR_GET_DUMPABLE)
}
/// Set the "keep capabilities" attribute for this process. This causes the thread to retain
/// capabilities even if it switches its UID to a nonzero value.
pub fn set_keepcaps(attribute: bool) -> Result<()> {
prctl_set_bool(libc::PR_SET_KEEPCAPS, attribute)
}
/// Get the "keep capabilities" attribute for this process
pub fn get_keepcaps() -> Result<bool> {
prctl_get_bool(libc::PR_GET_KEEPCAPS)
}
/// Clear the thread memory corruption kill policy and use the system-wide default
pub fn clear_mce_kill() -> Result<()> {
let res = unsafe {
libc::prctl(libc::PR_MCE_KILL, libc::PR_MCE_KILL_CLEAR, 0, 0, 0)
};
Errno::result(res).map(drop)
}
/// Set the thread memory corruption kill policy
pub fn set_mce_kill(policy: PrctlMCEKillPolicy) -> Result<()> {
let res = unsafe {
libc::prctl(
libc::PR_MCE_KILL,
libc::PR_MCE_KILL_SET,
policy as c_ulong,
0,
0,
)
};
Errno::result(res).map(drop)
}
/// Get the thread memory corruption kill policy
pub fn get_mce_kill() -> Result<PrctlMCEKillPolicy> {
let res = unsafe { libc::prctl(libc::PR_MCE_KILL_GET, 0, 0, 0, 0) };
match Errno::result(res) {
Ok(val) => Ok(PrctlMCEKillPolicy::try_from(val)?),
Err(e) => Err(e),
}
}
/// Set the parent-death signal of the calling process. This is the signal that the calling process
/// will get when its parent dies.
pub fn set_pdeathsig<T: Into<Option<Signal>>>(signal: T) -> Result<()> {
let sig = match signal.into() {
Some(s) => s as c_int,
None => 0,
};
let res = unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, sig, 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Returns the current parent-death signal
pub fn get_pdeathsig() -> Result<Option<Signal>> {
// prctl writes into this var
let mut sig: c_int = 0;
let res = unsafe { libc::prctl(libc::PR_GET_PDEATHSIG, &mut sig, 0, 0, 0) };
match Errno::result(res) {
Ok(_) => Ok(match sig {
0 => None,
_ => Some(Signal::try_from(sig)?),
}),
Err(e) => Err(e),
}
}
/// Set the name of the calling thread. Strings longer than 15 bytes will be truncated.
pub fn set_name(name: &CStr) -> Result<()> {
let res = unsafe { libc::prctl(libc::PR_SET_NAME, name.as_ptr(), 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Return the name of the calling thread
pub fn get_name() -> Result<CString> {
// Size of buffer determined by linux/sched.h TASK_COMM_LEN
let buf = [0u8; 16];
let res = unsafe { libc::prctl(libc::PR_GET_NAME, &buf, 0, 0, 0) };
Errno::result(res).and_then(|_| {
CStr::from_bytes_until_nul(&buf)
.map(CStr::to_owned)
.map_err(|_| Errno::EINVAL)
})
}
/// Sets the timer slack value for the calling thread. Timer slack is used by the kernel to group
/// timer expirations and make them the supplied amount of nanoseconds late.
pub fn set_timerslack(ns: c_ulong) -> Result<()> {
let res = unsafe { libc::prctl(libc::PR_SET_TIMERSLACK, ns, 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Get the timerslack for the calling thread.
pub fn get_timerslack() -> Result<i32> {
let res = unsafe { libc::prctl(libc::PR_GET_TIMERSLACK, 0, 0, 0, 0) };
Errno::result(res)
}
/// Disable all performance counters attached to the calling process.
pub fn task_perf_events_disable() -> Result<()> {
let res =
unsafe { libc::prctl(libc::PR_TASK_PERF_EVENTS_DISABLE, 0, 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Enable all performance counters attached to the calling process.
pub fn task_perf_events_enable() -> Result<()> {
let res =
unsafe { libc::prctl(libc::PR_TASK_PERF_EVENTS_ENABLE, 0, 0, 0, 0) };
Errno::result(res).map(drop)
}
/// Set the calling threads "no new privs" attribute. Once set this option can not be unset.
pub fn set_no_new_privs() -> Result<()> {
prctl_set_bool(libc::PR_SET_NO_NEW_PRIVS, true) // Cannot be unset
}
/// Get the "no new privs" attribute for the calling thread.
pub fn get_no_new_privs() -> Result<bool> {
prctl_get_bool(libc::PR_GET_NO_NEW_PRIVS)
}
/// Set the state of the "THP disable" flag for the calling thread. Setting this disables
/// transparent huge pages.
pub fn set_thp_disable(flag: bool) -> Result<()> {
prctl_set_bool(libc::PR_SET_THP_DISABLE, flag)
}
/// Get the "THP disable" flag for the calling thread.
pub fn get_thp_disable() -> Result<bool> {
prctl_get_bool(libc::PR_GET_THP_DISABLE)
}
/// Set an identifier (or reset it) to the address memory range.
pub fn set_vma_anon_name(addr: NonNull<c_void>, length: NonZeroUsize, name: Option<&CStr>) -> Result<()> {
let nameref = match name {
Some(n) => n.as_ptr(),
_ => std::ptr::null()
};
let res = unsafe { libc::prctl(libc::PR_SET_VMA, libc::PR_SET_VMA_ANON_NAME, addr.as_ptr(), length, nameref) };
Errno::result(res).map(drop)
}

43
vendor/nix/src/sys/pthread.rs vendored Normal file
View File

@@ -0,0 +1,43 @@
//! Low level threading primitives
#[cfg(not(target_os = "redox"))]
use crate::errno::Errno;
#[cfg(not(target_os = "redox"))]
use crate::Result;
use libc::{self, pthread_t};
/// Identifies an individual thread.
pub type Pthread = pthread_t;
/// Obtain ID of the calling thread (see
/// [`pthread_self(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_self.html)
///
/// The thread ID returned by `pthread_self()` is not the same thing as
/// the kernel thread ID returned by a call to `gettid(2)`.
#[inline]
pub fn pthread_self() -> Pthread {
unsafe { libc::pthread_self() }
}
feature! {
#![feature = "signal"]
/// 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
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[cfg(not(target_os = "redox"))]
pub fn pthread_kill<T>(thread: Pthread, signal: T) -> Result<()>
where T: Into<Option<crate::sys::signal::Signal>>
{
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)
}
}

187
vendor/nix/src/sys/ptrace/bsd.rs vendored Normal file
View File

@@ -0,0 +1,187 @@
use crate::errno::Errno;
use crate::sys::signal::Signal;
use crate::unistd::Pid;
use crate::Result;
use cfg_if::cfg_if;
use libc::{self, c_int};
use std::ptr;
pub type RequestType = c_int;
cfg_if! {
if #[cfg(any(freebsdlike, apple_targets, target_os = "openbsd"))] {
#[doc(hidden)]
pub type AddressType = *mut ::libc::c_char;
} else {
#[doc(hidden)]
pub type AddressType = *mut ::libc::c_void;
}
}
libc_enum! {
#[repr(i32)]
/// Ptrace Request enum defining the action to be taken.
#[non_exhaustive]
pub enum Request {
PT_TRACE_ME,
PT_READ_I,
PT_READ_D,
#[cfg(apple_targets)]
PT_READ_U,
PT_WRITE_I,
PT_WRITE_D,
#[cfg(apple_targets)]
PT_WRITE_U,
PT_CONTINUE,
PT_KILL,
#[cfg(any(any(freebsdlike, apple_targets),
all(target_os = "openbsd", target_arch = "x86_64"),
all(target_os = "netbsd", any(target_arch = "x86_64",
target_arch = "powerpc"))))]
PT_STEP,
PT_ATTACH,
PT_DETACH,
#[cfg(apple_targets)]
PT_SIGEXC,
#[cfg(apple_targets)]
PT_THUPDATE,
#[cfg(apple_targets)]
PT_ATTACHEXC
}
}
unsafe fn ptrace_other(
request: Request,
pid: Pid,
addr: AddressType,
data: c_int,
) -> Result<c_int> {
unsafe {
Errno::result(libc::ptrace(
request as RequestType,
libc::pid_t::from(pid),
addr,
data,
))
.map(|_| 0)
}
}
/// Sets the process as traceable, as with `ptrace(PT_TRACEME, ...)`
///
/// Indicates that this process is to be traced by its parent.
/// This is the only ptrace request to be issued by the tracee.
pub fn traceme() -> Result<()> {
unsafe {
ptrace_other(Request::PT_TRACE_ME, Pid::from_raw(0), ptr::null_mut(), 0)
.map(drop)
}
}
/// Attach to a running process, as with `ptrace(PT_ATTACH, ...)`
///
/// Attaches to the process specified by `pid`, making it a tracee of the calling process.
pub fn attach(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(Request::PT_ATTACH, pid, ptr::null_mut(), 0).map(drop)
}
}
/// Detaches the current running process, as with `ptrace(PT_DETACH, ...)`
///
/// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a
/// signal specified by `sig`.
pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as c_int,
None => 0,
};
unsafe {
ptrace_other(Request::PT_DETACH, pid, ptr::null_mut(), data).map(drop)
}
}
/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
///
/// Continues the execution of the process with PID `pid`, optionally
/// delivering a signal specified by `sig`.
pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as c_int,
None => 0,
};
unsafe {
// Ignore the useless return value
ptrace_other(Request::PT_CONTINUE, pid, 1 as AddressType, data)
.map(drop)
}
}
/// Issues a kill request as with `ptrace(PT_KILL, ...)`
///
/// This request is equivalent to `ptrace(PT_CONTINUE, ..., SIGKILL);`
pub fn kill(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(Request::PT_KILL, pid, 0 as AddressType, 0).map(drop)
}
}
/// Move the stopped tracee process forward by a single step as with
/// `ptrace(PT_STEP, ...)`
///
/// Advances the execution of the process with PID `pid` by a single step optionally delivering a
/// signal specified by `sig`.
///
/// # Example
/// ```rust
/// use nix::sys::ptrace::step;
/// use nix::unistd::Pid;
/// use nix::sys::signal::Signal;
/// use nix::sys::wait::*;
/// // 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);
/// }
/// _ => {},
/// }
/// ```
#[cfg(any(
any(freebsdlike, apple_targets),
all(target_os = "openbsd", target_arch = "x86_64"),
all(
target_os = "netbsd",
any(target_arch = "x86_64", target_arch = "powerpc")
)
))]
pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as c_int,
None => 0,
};
unsafe {
ptrace_other(Request::PT_STEP, pid, ptr::null_mut(), data).map(drop)
}
}
/// Reads a word from a processes memory at the given address
// Technically, ptrace doesn't dereference the pointer. It passes it directly
// to the kernel.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn read(pid: Pid, addr: AddressType) -> Result<c_int> {
unsafe {
// Traditionally there was a difference between reading data or
// instruction memory but not in modern systems.
ptrace_other(Request::PT_READ_D, pid, addr, 0)
}
}
/// Writes a word into the processes memory at the given address
// Technically, ptrace doesn't dereference the pointer. It passes it directly
// to the kernel.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn write(pid: Pid, addr: AddressType, data: c_int) -> Result<()> {
unsafe { ptrace_other(Request::PT_WRITE_D, pid, addr, data).map(drop) }
}

821
vendor/nix/src/sys/ptrace/linux.rs vendored Normal file
View File

@@ -0,0 +1,821 @@
//! For detailed description of the ptrace requests, consult `man ptrace`.
use crate::errno::Errno;
use crate::sys::signal::Signal;
use crate::unistd::Pid;
use crate::Result;
use cfg_if::cfg_if;
use libc::{self, c_long, c_void, siginfo_t};
use std::{mem, ptr};
pub type AddressType = *mut ::libc::c_void;
#[cfg(all(
target_os = "linux",
any(
all(
any(target_arch = "x86_64", target_arch = "aarch64"),
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "x86", target_env = "gnu"),
all(target_arch = "riscv64", target_env = "gnu"),
),
))]
use libc::user_regs_struct;
cfg_if! {
if #[cfg(any(all(target_os = "linux", target_arch = "s390x"),
all(target_os = "linux", target_env = "gnu"),
target_env = "uclibc"))] {
#[doc(hidden)]
pub type RequestType = ::libc::c_uint;
} else {
#[doc(hidden)]
pub type RequestType = ::libc::c_int;
}
}
libc_enum! {
#[cfg_attr(not(any(target_env = "musl", target_env = "uclibc", target_os = "android", target_env = "ohos")), repr(u32))]
#[cfg_attr(any(target_env = "musl", target_env = "uclibc", target_os = "android", target_env = "ohos"), repr(i32))]
/// Ptrace Request enum defining the action to be taken.
#[non_exhaustive]
pub enum Request {
PTRACE_TRACEME,
PTRACE_PEEKTEXT,
PTRACE_PEEKDATA,
PTRACE_PEEKUSER,
PTRACE_POKETEXT,
PTRACE_POKEDATA,
PTRACE_POKEUSER,
PTRACE_CONT,
PTRACE_KILL,
PTRACE_SINGLESTEP,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_GETREGS,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_SETREGS,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_GETFPREGS,
#[cfg(any(all(target_os = "android", target_pointer_width = "32"),
all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86_64",
target_pointer_width = "32"))))]
PTRACE_SETFPREGS,
PTRACE_ATTACH,
PTRACE_DETACH,
#[cfg(all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86",
target_arch = "x86_64")))]
PTRACE_GETFPXREGS,
#[cfg(all(target_os = "linux", any(target_env = "musl",
target_env = "ohos",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "x86",
target_arch = "x86_64")))]
PTRACE_SETFPXREGS,
PTRACE_SYSCALL,
PTRACE_SETOPTIONS,
PTRACE_GETEVENTMSG,
PTRACE_GETSIGINFO,
PTRACE_SETSIGINFO,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"))))]
PTRACE_GETREGSET,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"))))]
PTRACE_SETREGSET,
#[cfg(target_os = "linux")]
PTRACE_SEIZE,
#[cfg(target_os = "linux")]
PTRACE_INTERRUPT,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"))))]
PTRACE_LISTEN,
#[cfg(all(target_os = "linux", not(any(target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"))))]
PTRACE_PEEKSIGINFO,
#[cfg(all(target_os = "linux", target_env = "gnu",
any(target_arch = "x86", target_arch = "x86_64")))]
PTRACE_SYSEMU,
#[cfg(all(target_os = "linux", target_env = "gnu",
any(target_arch = "x86", target_arch = "x86_64")))]
PTRACE_SYSEMU_SINGLESTEP,
#[cfg(all(target_os = "linux", target_env = "gnu"))]
PTRACE_GET_SYSCALL_INFO,
}
}
libc_enum! {
#[repr(i32)]
/// 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,
/// Event that stops before a return from vfork or clone.
PTRACE_EVENT_VFORK,
/// Event that stops before a return from clone.
PTRACE_EVENT_CLONE,
/// Event that stops before a return from execve.
PTRACE_EVENT_EXEC,
/// Event for a return from vfork.
PTRACE_EVENT_VFORK_DONE,
/// 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.
PTRACE_EVENT_SECCOMP,
/// Stop triggered by the `INTERRUPT` syscall, or a group stop,
/// or when a new child is attached.
PTRACE_EVENT_STOP,
}
}
#[cfg(all(
target_os = "linux",
any(
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
),
all(
target_env = "musl",
target_arch = "aarch64",
)
),
))]
libc_enum! {
#[repr(i32)]
/// Defines a specific register set, as used in `PTRACE_GETREGSET` and `PTRACE_SETREGSET`.
#[non_exhaustive]
pub enum RegisterSetValue {
NT_PRSTATUS,
NT_PRFPREG,
NT_PRPSINFO,
NT_TASKSTRUCT,
NT_AUXV,
}
}
#[cfg(all(
target_os = "linux",
any(
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
),
all(
target_env = "musl",
target_arch = "aarch64",
)
),
))]
/// Represents register set areas, such as general-purpose registers or
/// floating-point registers.
///
/// # Safety
///
/// This trait is marked unsafe, since implementation of the trait must match
/// ptrace's request `VALUE` and return data type `Regs`.
pub unsafe trait RegisterSet {
/// Corresponding type of registers in the kernel.
const VALUE: RegisterSetValue;
/// Struct representing the register space.
type Regs;
}
#[cfg(all(
target_os = "linux",
any(
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
),
all(
target_env = "musl",
target_arch = "aarch64",
)
),
))]
/// Register sets used in [`getregset`] and [`setregset`]
pub mod regset {
use super::*;
#[derive(Debug, Clone, Copy)]
/// General-purpose registers.
pub enum NT_PRSTATUS {}
unsafe impl RegisterSet for NT_PRSTATUS {
const VALUE: RegisterSetValue = RegisterSetValue::NT_PRSTATUS;
type Regs = user_regs_struct;
}
#[derive(Debug, Clone, Copy)]
/// Floating-point registers.
pub enum NT_PRFPREG {}
unsafe impl RegisterSet for NT_PRFPREG {
const VALUE: RegisterSetValue = RegisterSetValue::NT_PRFPREG;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
type Regs = libc::user_fpregs_struct;
#[cfg(target_arch = "aarch64")]
type Regs = libc::user_fpsimd_struct;
#[cfg(target_arch = "riscv64")]
type Regs = libc::__riscv_mc_d_ext_state;
}
}
libc_bitflags! {
/// Ptrace options used in conjunction with the PTRACE_SETOPTIONS request.
/// See `man ptrace` for more details.
pub struct Options: libc::c_int {
/// When delivering system call traps set a bit to allow tracer to
/// distinguish between normal stops or syscall stops. May not work on
/// all systems.
PTRACE_O_TRACESYSGOOD;
/// Stop tracee at next fork and start tracing the forked process.
PTRACE_O_TRACEFORK;
/// Stop tracee at next vfork call and trace the vforked process.
PTRACE_O_TRACEVFORK;
/// Stop tracee at next clone call and trace the cloned process.
PTRACE_O_TRACECLONE;
/// Stop tracee at next execve call.
PTRACE_O_TRACEEXEC;
/// Stop tracee at vfork completion.
PTRACE_O_TRACEVFORKDONE;
/// Stop tracee at next exit call. Stops before exit commences allowing
/// tracer to see location of exit and register states.
PTRACE_O_TRACEEXIT;
/// Stop tracee when a SECCOMP_RET_TRACE rule is triggered. See `man seccomp` for more
/// details.
PTRACE_O_TRACESECCOMP;
/// Send a SIGKILL to the tracee if the tracer exits. This is useful
/// for ptrace jailers to prevent tracees from escaping their control.
PTRACE_O_EXITKILL;
}
}
fn ptrace_peek(
request: Request,
pid: Pid,
addr: AddressType,
data: *mut c_void,
) -> Result<c_long> {
let ret = unsafe {
Errno::clear();
libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)
};
match Errno::result(ret) {
Ok(..) | Err(Errno::UnknownErrno) => Ok(ret),
err @ Err(..) => err,
}
}
/// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)`
///
/// Note that since `PTRACE_GETREGS` are not available on all platforms (as in [ptrace(2)]),
/// `ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect
/// on aarch64 and riscv64.
///
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
any(
all(
target_arch = "x86_64",
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "x86", target_env = "gnu")
)
))]
pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
ptrace_get_data::<user_regs_struct>(Request::PTRACE_GETREGS, pid)
}
/// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)`
///
/// Note that since `PTRACE_GETREGS` are not available on all platforms (as in [ptrace(2)]),
/// `ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect
/// on aarch64 and riscv64.
///
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
any(
all(
target_arch = "aarch64",
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "riscv64", target_env = "gnu")
)
))]
pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
getregset::<regset::NT_PRSTATUS>(pid)
}
/// Get a particular set of user registers, as with `ptrace(PTRACE_GETREGSET, ...)`
#[cfg(all(
target_os = "linux",
any(
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)
))]
pub fn getregset<S: RegisterSet>(pid: Pid) -> Result<S::Regs> {
let request = Request::PTRACE_GETREGSET;
let mut data = mem::MaybeUninit::<S::Regs>::uninit();
let mut iov = libc::iovec {
iov_base: data.as_mut_ptr().cast(),
iov_len: mem::size_of::<S::Regs>(),
};
unsafe {
ptrace_other(
request,
pid,
S::VALUE as i32 as AddressType,
(&mut iov as *mut libc::iovec).cast(),
)?;
};
Ok(unsafe { data.assume_init() })
}
/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)`
///
/// Note that since `PTRACE_SETREGS` are not available on all platforms (as in [ptrace(2)]),
/// `ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect
/// on aarch64 and riscv64.
///
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
any(
all(
target_arch = "x86_64",
any(target_env = "gnu", target_env = "musl")
),
all(target_arch = "x86", target_env = "gnu")
)
))]
pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
let res = unsafe {
libc::ptrace(
Request::PTRACE_SETREGS as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<c_void>(),
&regs as *const user_regs_struct as *const c_void,
)
};
Errno::result(res).map(drop)
}
/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)`
///
/// Note that since `PTRACE_SETREGS` are not available on all platforms (as in [ptrace(2)]),
/// `ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ...)` is used instead to achieve the same effect
/// on aarch64 and riscv64.
///
/// [ptrace(2)]: https://www.man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(all(
target_os = "linux",
any(
all(
target_env = "gnu",
any(target_arch = "aarch64", target_arch = "riscv64")
),
all(target_env = "musl", target_arch = "aarch64")
)
))]
pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
setregset::<regset::NT_PRSTATUS>(pid, regs)
}
/// Set a particular set of user registers, as with `ptrace(PTRACE_SETREGSET, ...)`
#[cfg(all(
target_os = "linux",
any(
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)
))]
pub fn setregset<S: RegisterSet>(pid: Pid, mut regs: S::Regs) -> Result<()> {
let mut iov = libc::iovec {
iov_base: (&mut regs as *mut S::Regs).cast(),
iov_len: mem::size_of::<S::Regs>(),
};
unsafe {
ptrace_other(
Request::PTRACE_SETREGSET,
pid,
S::VALUE as i32 as AddressType,
(&mut iov as *mut libc::iovec).cast(),
)?;
}
Ok(())
}
/// Function for ptrace requests that return values from the data field.
/// Some ptrace get requests populate structs or larger elements than `c_long`
/// and therefore use the data field to return values. This function handles these
/// requests.
fn ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T> {
let mut data = mem::MaybeUninit::<T>::uninit();
let res = unsafe {
libc::ptrace(
request as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<T>(),
data.as_mut_ptr(),
)
};
Errno::result(res)?;
Ok(unsafe { data.assume_init() })
}
unsafe fn ptrace_other(
request: Request,
pid: Pid,
addr: AddressType,
data: *mut c_void,
) -> Result<c_long> {
unsafe {
Errno::result(libc::ptrace(
request as RequestType,
libc::pid_t::from(pid),
addr,
data,
))
.map(|_| 0)
}
}
/// Set options, as with `ptrace(PTRACE_SETOPTIONS, ...)`.
pub fn setoptions(pid: Pid, options: Options) -> Result<()> {
let res = unsafe {
libc::ptrace(
Request::PTRACE_SETOPTIONS as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<c_void>(),
options.bits() as *mut c_void,
)
};
Errno::result(res).map(drop)
}
/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG, ...)`
pub fn getevent(pid: Pid) -> Result<c_long> {
ptrace_get_data::<c_long>(Request::PTRACE_GETEVENTMSG, pid)
}
/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO, ...)`
pub fn getsiginfo(pid: Pid) -> Result<siginfo_t> {
ptrace_get_data::<siginfo_t>(Request::PTRACE_GETSIGINFO, pid)
}
/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO, ...)`
pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
let ret = unsafe {
Errno::clear();
libc::ptrace(
Request::PTRACE_SETSIGINFO as RequestType,
libc::pid_t::from(pid),
ptr::null_mut::<c_void>(),
sig as *const _ as *const c_void,
)
};
match Errno::result(ret) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}
/// Get the informations of the syscall that caused the stop, as with
/// `ptrace(PTRACE_GET_SYSCALL_INFO, ...`.
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub fn syscall_info(pid: Pid) -> Result<libc::ptrace_syscall_info> {
ptrace_get_data::<libc::ptrace_syscall_info>(Request::PTRACE_GET_SYSCALL_INFO, pid)
}
/// Sets the process as traceable, as with `ptrace(PTRACE_TRACEME, ...)`
///
/// Indicates that this process is to be traced by its parent.
/// This is the only ptrace request to be issued by the tracee.
pub fn traceme() -> Result<()> {
unsafe {
ptrace_other(
Request::PTRACE_TRACEME,
Pid::from_raw(0),
ptr::null_mut(),
ptr::null_mut(),
)
.map(drop) // ignore the useless return value
}
}
/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSCALL, ...)`
///
/// Arranges for the tracee to be stopped at the next entry to or exit from a system call,
/// optionally delivering a signal specified by `sig`.
pub fn syscall<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(Request::PTRACE_SYSCALL, pid, ptr::null_mut(), data)
.map(drop) // ignore the useless return value
}
}
/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSEMU, ...)`
///
/// In contrast to the `syscall` function, the syscall stopped at will not be executed.
/// Thus the the tracee will only be stopped once per syscall,
/// optionally delivering a signal specified by `sig`.
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(target_arch = "x86", target_arch = "x86_64")
))]
pub fn sysemu<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(Request::PTRACE_SYSEMU, pid, ptr::null_mut(), data)
.map(drop)
// ignore the useless return value
}
}
/// Attach to a running process, as with `ptrace(PTRACE_ATTACH, ...)`
///
/// Attaches to the process specified by `pid`, making it a tracee of the calling process.
pub fn attach(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(
Request::PTRACE_ATTACH,
pid,
ptr::null_mut(),
ptr::null_mut(),
)
.map(drop) // ignore the useless return value
}
}
/// 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(target_os = "linux")]
pub fn seize(pid: Pid, options: Options) -> Result<()> {
unsafe {
ptrace_other(
Request::PTRACE_SEIZE,
pid,
ptr::null_mut(),
options.bits() as *mut c_void,
)
.map(drop) // ignore the useless return value
}
}
/// Detaches the current running process, as with `ptrace(PTRACE_DETACH, ...)`
///
/// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a
/// signal specified by `sig`.
pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(Request::PTRACE_DETACH, pid, ptr::null_mut(), data)
.map(drop)
}
}
/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
///
/// Continues the execution of the process with PID `pid`, optionally
/// delivering a signal specified by `sig`.
pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop)
// ignore the useless return value
}
}
/// 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);`
pub fn kill(pid: Pid) -> Result<()> {
unsafe {
ptrace_other(
Request::PTRACE_KILL,
pid,
ptr::null_mut(),
ptr::null_mut(),
)
.map(drop)
}
}
/// 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
/// signal specified by `sig`.
///
/// # Example
/// ```rust
/// use nix::sys::ptrace::step;
/// use nix::unistd::Pid;
/// use nix::sys::signal::Signal;
/// use nix::sys::wait::*;
///
/// // 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<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data)
.map(drop)
}
}
/// Move the stopped tracee process forward by a single step or stop at the next syscall
/// as with `ptrace(PTRACE_SYSEMU_SINGLESTEP, ...)`
///
/// Advances the execution by a single step or until the next syscall.
/// In case the tracee is stopped at a syscall, the syscall will not be executed.
/// Optionally, the signal specified by `sig` is delivered to the tracee upon continuation.
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(target_arch = "x86", target_arch = "x86_64")
))]
pub fn sysemu_step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
let data = match sig.into() {
Some(s) => s as i32 as *mut c_void,
None => ptr::null_mut(),
};
unsafe {
ptrace_other(
Request::PTRACE_SYSEMU_SINGLESTEP,
pid,
ptr::null_mut(),
data,
)
.map(drop) // ignore the useless return value
}
}
/// Reads a word from a processes memory at the given address, as with
/// ptrace(PTRACE_PEEKDATA, ...)
pub fn read(pid: Pid, addr: AddressType) -> Result<c_long> {
ptrace_peek(Request::PTRACE_PEEKDATA, pid, addr, ptr::null_mut())
}
/// Writes a word into the processes memory at the given address, as with
/// ptrace(PTRACE_POKEDATA, ...)
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn write(pid: Pid, addr: AddressType, data: c_long) -> Result<()> {
unsafe {
// Safety(not_unsafe_ptr_arg_deref):
// `ptrace_other` is a common abstract
// but in `PTRACE_POKEDATA` situation, `data` is exactly what will be wtitten
ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data as *mut c_void)
.map(drop)
}
}
/// Reads a word from a user area at `offset`, as with ptrace(PTRACE_PEEKUSER, ...).
/// The user struct definition can be found in `/usr/include/sys/user.h`.
pub fn read_user(pid: Pid, offset: AddressType) -> Result<c_long> {
ptrace_peek(Request::PTRACE_PEEKUSER, pid, offset, ptr::null_mut())
}
/// Writes a word to a user area at `offset`, as with ptrace(PTRACE_POKEUSER, ...).
/// The user struct definition can be found in `/usr/include/sys/user.h`.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn write_user(pid: Pid, offset: AddressType, data: c_long) -> Result<()> {
unsafe {
// Safety(not_unsafe_ptr_arg_deref):
// `ptrace_other` is a common abstract
// but in `PTRACE_POKEDATA` situation, `data` is exactly what will be wtitten
ptrace_other(Request::PTRACE_POKEUSER, pid, offset, data as *mut c_void)
.map(drop)
}
}

13
vendor/nix/src/sys/ptrace/mod.rs vendored Normal file
View File

@@ -0,0 +1,13 @@
//! Provides helpers for making ptrace system calls
#[cfg(linux_android)]
mod linux;
#[cfg(linux_android)]
pub use self::linux::*;
#[cfg(bsd)]
mod bsd;
#[cfg(bsd)]
pub use self::bsd::*;

337
vendor/nix/src/sys/quota.rs vendored Normal file
View File

@@ -0,0 +1,337 @@
//! Set and configure disk quotas for users, groups, or projects.
//!
//! # Examples
//!
//! Enabling and setting a quota:
//!
//! ```rust,no_run
//! # use nix::sys::quota::{Dqblk, quotactl_on, quotactl_set, QuotaFmt, QuotaType, QuotaValidFlags};
//! quotactl_on(QuotaType::USRQUOTA, "/dev/sda1", QuotaFmt::QFMT_VFS_V1, "aquota.user").unwrap();
//! let mut dqblk: Dqblk = Default::default();
//! dqblk.set_blocks_hard_limit(10000);
//! dqblk.set_blocks_soft_limit(8000);
//! quotactl_set(QuotaType::USRQUOTA, "/dev/sda1", 50, &dqblk, QuotaValidFlags::QIF_BLIMITS).unwrap();
//! ```
use crate::errno::Errno;
use crate::{NixPath, Result};
use libc::{self, c_char, c_int};
use std::default::Default;
use std::{mem, ptr};
struct QuotaCmd(QuotaSubCmd, QuotaType);
impl QuotaCmd {
fn as_int(&self) -> c_int {
libc::QCMD(self.0 as i32, self.1 as i32)
}
}
// linux quota version >= 2
libc_enum! {
#[repr(i32)]
enum QuotaSubCmd {
Q_SYNC,
Q_QUOTAON,
Q_QUOTAOFF,
Q_GETQUOTA,
Q_SETQUOTA,
}
}
libc_enum! {
/// The scope of the quota.
#[repr(i32)]
#[non_exhaustive]
pub enum QuotaType {
/// Specify a user quota
USRQUOTA,
/// Specify a group quota
GRPQUOTA,
}
}
libc_enum! {
/// The type of quota format to use.
#[repr(i32)]
#[non_exhaustive]
pub enum QuotaFmt {
/// Use the original quota format.
QFMT_VFS_OLD,
/// Use the standard VFS v0 quota format.
///
/// Handles 32-bit UIDs/GIDs and quota limits up to 2<sup>32</sup> bytes/2<sup>32</sup> inodes.
QFMT_VFS_V0,
/// Use the VFS v1 quota format.
///
/// Handles 32-bit UIDs/GIDs and quota limits of 2<sup>64</sup> bytes/2<sup>64</sup> inodes.
QFMT_VFS_V1,
}
}
libc_bitflags!(
/// Indicates the quota fields that are valid to read from.
#[derive(Default)]
pub struct QuotaValidFlags: u32 {
/// The block hard & soft limit fields.
QIF_BLIMITS;
/// The current space field.
QIF_SPACE;
/// The inode hard & soft limit fields.
QIF_ILIMITS;
/// The current inodes field.
QIF_INODES;
/// The disk use time limit field.
QIF_BTIME;
/// The file quote time limit field.
QIF_ITIME;
/// All block & inode limits.
QIF_LIMITS;
/// The space & inodes usage fields.
QIF_USAGE;
/// The time limit fields.
QIF_TIMES;
/// All fields.
QIF_ALL;
}
);
/// Wrapper type for `if_dqblk`
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Dqblk(libc::dqblk);
impl Default for Dqblk {
fn default() -> Dqblk {
Dqblk(libc::dqblk {
dqb_bhardlimit: 0,
dqb_bsoftlimit: 0,
dqb_curspace: 0,
dqb_ihardlimit: 0,
dqb_isoftlimit: 0,
dqb_curinodes: 0,
dqb_btime: 0,
dqb_itime: 0,
dqb_valid: 0,
})
}
}
impl Dqblk {
/// The absolute limit on disk quota blocks allocated.
pub fn blocks_hard_limit(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
Some(self.0.dqb_bhardlimit)
} else {
None
}
}
/// Set the absolute limit on disk quota blocks allocated.
pub fn set_blocks_hard_limit(&mut self, limit: u64) {
self.0.dqb_bhardlimit = limit;
}
/// Preferred limit on disk quota blocks
pub fn blocks_soft_limit(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
Some(self.0.dqb_bsoftlimit)
} else {
None
}
}
/// Set the preferred limit on disk quota blocks allocated.
pub fn set_blocks_soft_limit(&mut self, limit: u64) {
self.0.dqb_bsoftlimit = limit;
}
/// Current occupied space (bytes).
pub fn occupied_space(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_SPACE) {
Some(self.0.dqb_curspace)
} else {
None
}
}
/// Maximum number of allocated inodes.
pub fn inodes_hard_limit(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
Some(self.0.dqb_ihardlimit)
} else {
None
}
}
/// Set the maximum number of allocated inodes.
pub fn set_inodes_hard_limit(&mut self, limit: u64) {
self.0.dqb_ihardlimit = limit;
}
/// Preferred inode limit
pub fn inodes_soft_limit(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
Some(self.0.dqb_isoftlimit)
} else {
None
}
}
/// Set the preferred limit of allocated inodes.
pub fn set_inodes_soft_limit(&mut self, limit: u64) {
self.0.dqb_isoftlimit = limit;
}
/// Current number of allocated inodes.
pub fn allocated_inodes(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_INODES) {
Some(self.0.dqb_curinodes)
} else {
None
}
}
/// Time limit for excessive disk use.
pub fn block_time_limit(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_BTIME) {
Some(self.0.dqb_btime)
} else {
None
}
}
/// Set the time limit for excessive disk use.
pub fn set_block_time_limit(&mut self, limit: u64) {
self.0.dqb_btime = limit;
}
/// Time limit for excessive files.
pub fn inode_time_limit(&self) -> Option<u64> {
let valid_fields =
QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
if valid_fields.contains(QuotaValidFlags::QIF_ITIME) {
Some(self.0.dqb_itime)
} else {
None
}
}
/// Set the time limit for excessive files.
pub fn set_inode_time_limit(&mut self, limit: u64) {
self.0.dqb_itime = limit;
}
}
fn quotactl<P: ?Sized + NixPath>(
cmd: QuotaCmd,
special: Option<&P>,
id: c_int,
addr: *mut c_char,
) -> Result<()> {
unsafe {
Errno::clear();
let res = match special {
Some(dev) => dev.with_nix_path(|path| {
libc::quotactl(cmd.as_int(), path.as_ptr(), id, addr)
}),
None => Ok(libc::quotactl(cmd.as_int(), ptr::null(), id, addr)),
}?;
Errno::result(res).map(drop)
}
}
/// Turn on disk quotas for a block device.
pub fn quotactl_on<P: ?Sized + NixPath>(
which: QuotaType,
special: &P,
format: QuotaFmt,
quota_file: &P,
) -> Result<()> {
quota_file.with_nix_path(|path| {
let mut path_copy = path.to_bytes_with_nul().to_owned();
let p: *mut c_char = path_copy.as_mut_ptr().cast();
quotactl(
QuotaCmd(QuotaSubCmd::Q_QUOTAON, which),
Some(special),
format as c_int,
p,
)
})?
}
/// Disable disk quotas for a block device.
pub fn quotactl_off<P: ?Sized + NixPath>(
which: QuotaType,
special: &P,
) -> Result<()> {
quotactl(
QuotaCmd(QuotaSubCmd::Q_QUOTAOFF, which),
Some(special),
0,
ptr::null_mut(),
)
}
/// Update the on-disk copy of quota usages for a filesystem.
///
/// If `special` is `None`, then all file systems with active quotas are sync'd.
pub fn quotactl_sync<P: ?Sized + NixPath>(
which: QuotaType,
special: Option<&P>,
) -> Result<()> {
quotactl(
QuotaCmd(QuotaSubCmd::Q_SYNC, which),
special,
0,
ptr::null_mut(),
)
}
/// Get disk quota limits and current usage for the given user/group id.
pub fn quotactl_get<P: ?Sized + NixPath>(
which: QuotaType,
special: &P,
id: c_int,
) -> Result<Dqblk> {
let mut dqblk = mem::MaybeUninit::<libc::dqblk>::uninit();
quotactl(
QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which),
Some(special),
id,
dqblk.as_mut_ptr().cast(),
)?;
Ok(unsafe { Dqblk(dqblk.assume_init()) })
}
/// Configure quota values for the specified fields for a given user/group id.
pub fn quotactl_set<P: ?Sized + NixPath>(
which: QuotaType,
special: &P,
id: c_int,
dqblk: &Dqblk,
fields: QuotaValidFlags,
) -> Result<()> {
let mut dqblk_copy = *dqblk;
dqblk_copy.0.dqb_valid = fields.bits();
quotactl(
QuotaCmd(QuotaSubCmd::Q_SETQUOTA, which),
Some(special),
id,
&mut dqblk_copy as *mut _ as *mut c_char,
)
}

141
vendor/nix/src/sys/reboot.rs vendored Normal file
View File

@@ -0,0 +1,141 @@
//! Reboot/shutdown
//!
//! On Linux, This can also be used to enable/disable Ctrl-Alt-Delete.
use crate::errno::Errno;
use crate::Result;
use cfg_if::cfg_if;
use std::convert::Infallible;
cfg_if! {
if #[cfg(target_os = "linux")] {
use std::mem::drop;
libc_enum! {
/// How exactly should the system be rebooted.
///
/// See [`set_cad_enabled()`](fn.set_cad_enabled.html) for
/// enabling/disabling Ctrl-Alt-Delete.
#[repr(i32)]
#[non_exhaustive]
pub enum RebootMode {
/// Halt the system.
RB_HALT_SYSTEM,
/// Execute a kernel that has been loaded earlier with
/// [`kexec_load(2)`](https://man7.org/linux/man-pages/man2/kexec_load.2.html).
RB_KEXEC,
/// Stop the system and switch off power, if possible.
RB_POWER_OFF,
/// Restart the system.
RB_AUTOBOOT,
// we do not support Restart2.
/// Suspend the system using software suspend.
RB_SW_SUSPEND,
}
}
/// Reboots or shuts down the system.
pub fn reboot(how: RebootMode) -> Result<Infallible> {
unsafe { libc::reboot(how as libc::c_int) };
Err(Errno::last())
}
/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete).
///
/// Corresponds to calling `reboot(RB_ENABLE_CAD)` or `reboot(RB_DISABLE_CAD)` in C.
pub fn set_cad_enabled(enable: bool) -> Result<()> {
let cmd = if enable {
libc::RB_ENABLE_CAD
} else {
libc::RB_DISABLE_CAD
};
let res = unsafe { libc::reboot(cmd) };
Errno::result(res).map(drop)
}
} else if #[cfg(netbsdlike)] {
use libc::c_int;
libc_bitflags! {
/// How exactly should the system be rebooted.
pub struct RebootMode: c_int {
/// The default, causing the system to reboot in its usual fashion.
RB_AUTOBOOT;
/// Interpreted by the bootstrap program itself, causing it to
/// prompt on the console as to what file should be booted.
/// Normally, the system is booted from the file “xx(0,0)bsd”,
/// where xx is the default disk name, without prompting for
/// the file name.
RB_ASKNAME;
/// Dump kernel memory before rebooting; see `savecore(8)` for
/// more information.
RB_DUMP;
/// The processor is simply halted; no reboot takes place.
RB_HALT;
/// Power off the system if the system hardware supports the
/// function, otherwise it has no effect.
///
/// Should be used in conjunction with `RB_HALT`.
RB_POWERDOWN;
/// By default, the system will halt if `reboot()` is called during
/// startup (before the system has finished autoconfiguration), even
/// if `RB_HALT` is not specified. This is because `panic(9)`s
/// during startup will probably just repeat on the next boot.
/// Use of this option implies that the user has requested the
/// action specified (for example, using the `ddb(4)` boot reboot
/// command), so the system will reboot if a halt is not explicitly
/// requested.
#[cfg(target_os = "openbsd")]
RB_USERREQ;
/// Load the symbol table and enable a built-in debugger in the
/// system. This option will have no useful function if the kernel
/// is not configured for debugging. Several other options have
/// different meaning if combined with this option, although their
/// use may not be possible via the `reboot()` call. See `ddb(4)` for
/// more information.
RB_KDB;
/// Normally, the disks are sync'd (see `sync(8)`) before the
/// processor is halted or rebooted. This option may be useful
/// if file system changes have been made manually or if the
/// processor is on fire.
RB_NOSYNC;
/// Normally, the reboot procedure involves an automatic disk
/// consistency check and then multi-user operations. `RB_SINGLE`
/// prevents this, booting the system with a single-user shell on
/// the console. `RB_SINGLE` is actually interpreted by the `init(8)`
/// program in the newly booted system.
///
/// When no options are given (i.e., `RB_AUTOBOOT` is used), the
/// system is rebooted from file /bsd in the root file system of
/// unit 0 of a disk chosen in a processor specific way. An automatic
/// consistency check of the disks is normally performed (see `fsck(8)`).
RB_SINGLE;
/// Initially invoke the `userconf(4)` facility when the system
/// starts up again, if it has been compiled into the kernel
/// that is loaded.
#[cfg(target_os = "netbsd")]
RB_USERCONF;
/// Don't update the hardware clock from the system clock, presumably
/// because the system clock is suspect.
#[cfg(target_os = "openbsd")]
RB_TIMEBAD;
}
}
/// Reboot system or halt processor
///
/// For more information, see the man pages:
///
/// * [NetBSD](https://man.netbsd.org/reboot.2)
/// * [OpenBSD](https://man.openbsd.org/reboot.2)
#[cfg(netbsdlike)]
pub fn reboot(how: RebootMode) -> Result<Infallible> {
#[cfg(target_os = "openbsd")]
unsafe { libc::reboot(how.bits()) };
#[cfg(target_os = "netbsd")]
unsafe { libc::reboot(how.bits(), std::ptr::null_mut()) };
Err(Errno::last())
}
}
}

402
vendor/nix/src/sys/resource.rs vendored Normal file
View File

@@ -0,0 +1,402 @@
//! Configure the process resource limits.
use cfg_if::cfg_if;
use libc::{c_int, c_long, rusage};
use crate::errno::Errno;
use crate::sys::time::TimeVal;
use crate::Result;
pub use libc::rlim_t;
pub use libc::RLIM_INFINITY;
use std::mem;
cfg_if! {
if #[cfg(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd"
))]{
use libc::{__rlimit_resource_t, rlimit};
} else if #[cfg(any(
bsd,
target_os = "android",
target_os = "aix",
all(target_os = "linux", not(target_env = "gnu")),
target_os = "cygwin"
))]{
use libc::rlimit;
}
}
libc_enum! {
/// Types of process resources.
///
/// The Resource enum is platform dependent. Check different platform
/// manuals for more details. Some platform links have been provided for
/// easier reference (non-exhaustive).
///
/// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html)
/// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit)
/// * [NetBSD](https://man.netbsd.org/setrlimit.2)
// 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(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd"
), repr(u32))]
#[cfg_attr(any(
bsd,
target_os = "android",
target_os = "aix",
all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc"))),
target_os = "cygwin"
), repr(i32))]
#[non_exhaustive]
pub enum Resource {
#[cfg(not(any(target_os = "freebsd", netbsdlike)))]
/// 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(linux_android)]
/// A limit on the combined number of flock locks and fcntl leases that
/// this process may establish.
RLIMIT_LOCKS,
#[cfg(any(linux_android, target_os = "freebsd", netbsdlike))]
/// The maximum size (in bytes) which a process may lock into memory
/// using the mlock(2) system call.
RLIMIT_MEMLOCK,
#[cfg(linux_android)]
/// 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(linux_android)]
/// A ceiling to which the process's nice value can be raised using
/// setpriority or nice.
RLIMIT_NICE,
#[cfg(any(
linux_android,
target_os = "freebsd",
netbsdlike,
target_os = "aix",
))]
/// 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(linux_android,
target_os = "freebsd",
netbsdlike,
target_os = "aix",
))]
/// When there is memory pressure and swap is available, prioritize
/// eviction of a process' resident pages beyond this amount (in bytes).
RLIMIT_RSS,
#[cfg(linux_android)]
/// 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(linux_android)]
/// A limit on the number of signals that may be queued for the real
/// user ID of the calling process.
RLIMIT_SIGPENDING,
#[cfg(freebsdlike)]
/// 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
///
/// The special value [`RLIM_INFINITY`] indicates that no limit will be
/// enforced.
///
/// # 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<(rlim_t, rlim_t)> {
let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();
cfg_if! {
if #[cfg(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd"
))] {
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() };
(rlim_cur, 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.
/// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to
/// the current hard limit for non-root users.
///
/// The special value [`RLIM_INFINITY`] indicates that no limit will be
/// enforced.
///
/// # Examples
///
/// ```
/// # use nix::sys::resource::{setrlimit, Resource};
///
/// let soft_limit = 512;
/// let hard_limit = 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: rlim_t,
hard_limit: rlim_t,
) -> Result<()> {
let new_rlim = rlimit {
rlim_cur: soft_limit,
rlim_max: hard_limit,
};
cfg_if! {
if #[cfg(any(
all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")),
target_os = "hurd",
))]{
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)
}
libc_enum! {
/// Whose resource usage should be returned by [`getrusage`].
#[repr(i32)]
#[non_exhaustive]
pub enum UsageWho {
/// Resource usage for the current process.
RUSAGE_SELF,
/// Resource usage for all the children that have terminated and been waited for.
RUSAGE_CHILDREN,
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
/// Resource usage for the calling thread.
RUSAGE_THREAD,
}
}
/// Output of `getrusage` with information about resource usage. Some of the fields
/// may be unused in some platforms, and will be always zeroed out. See their manuals
/// for details.
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Usage(rusage);
impl AsRef<rusage> for Usage {
fn as_ref(&self) -> &rusage {
&self.0
}
}
impl AsMut<rusage> for Usage {
fn as_mut(&mut self) -> &mut rusage {
&mut self.0
}
}
impl Usage {
/// Total amount of time spent executing in user mode.
pub fn user_time(&self) -> TimeVal {
TimeVal::from(self.0.ru_utime)
}
/// Total amount of time spent executing in kernel mode.
pub fn system_time(&self) -> TimeVal {
TimeVal::from(self.0.ru_stime)
}
/// The resident set size at its peak,
#[cfg_attr(apple_targets, doc = " in bytes.")]
#[cfg_attr(not(apple_targets), doc = " in kilobytes.")]
pub fn max_rss(&self) -> c_long {
self.0.ru_maxrss
}
/// Integral value expressed in kilobytes times ticks of execution indicating
/// the amount of text memory shared with other processes.
pub fn shared_integral(&self) -> c_long {
self.0.ru_ixrss
}
/// Integral value expressed in kilobytes times ticks of execution indicating
/// the amount of unshared memory used by data.
pub fn unshared_data_integral(&self) -> c_long {
self.0.ru_idrss
}
/// Integral value expressed in kilobytes times ticks of execution indicating
/// the amount of unshared memory used for stack space.
pub fn unshared_stack_integral(&self) -> c_long {
self.0.ru_isrss
}
/// Number of page faults that were served without resorting to I/O, with pages
/// that have been allocated previously by the kernel.
pub fn minor_page_faults(&self) -> c_long {
self.0.ru_minflt
}
/// Number of page faults that were served through I/O (i.e. swap).
pub fn major_page_faults(&self) -> c_long {
self.0.ru_majflt
}
/// Number of times all of the memory was fully swapped out.
pub fn full_swaps(&self) -> c_long {
self.0.ru_nswap
}
/// Number of times a read was done from a block device.
pub fn block_reads(&self) -> c_long {
self.0.ru_inblock
}
/// Number of times a write was done to a block device.
pub fn block_writes(&self) -> c_long {
self.0.ru_oublock
}
/// Number of IPC messages sent.
pub fn ipc_sends(&self) -> c_long {
self.0.ru_msgsnd
}
/// Number of IPC messages received.
pub fn ipc_receives(&self) -> c_long {
self.0.ru_msgrcv
}
/// Number of signals received.
pub fn signals(&self) -> c_long {
self.0.ru_nsignals
}
/// Number of times a context switch was voluntarily invoked.
pub fn voluntary_context_switches(&self) -> c_long {
self.0.ru_nvcsw
}
/// Number of times a context switch was imposed by the kernel (usually due to
/// time slice expiring or preemption by a higher priority process).
pub fn involuntary_context_switches(&self) -> c_long {
self.0.ru_nivcsw
}
}
/// Get usage information for a process, its children or the current thread
///
/// Real time information can be obtained for either the current process or (in some
/// systems) thread, but information about children processes is only provided for
/// those that have terminated and been waited for (see [`super::wait::wait`]).
///
/// Some information may be missing depending on the platform, and the way information
/// is provided for children may also vary. Check the manuals for details.
///
/// # References
///
/// * [getrusage(2)](https://pubs.opengroup.org/onlinepubs/009696699/functions/getrusage.html)
/// * [Linux](https://man7.org/linux/man-pages/man2/getrusage.2.html)
/// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=getrusage)
/// * [NetBSD](https://man.netbsd.org/getrusage.2)
/// * [MacOS](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrusage.2.html)
///
/// [`UsageWho`]: enum.UsageWho.html
///
/// Note: `getrusage` provides a safe wrapper to libc's [`libc::getrusage`].
pub fn getrusage(who: UsageWho) -> Result<Usage> {
unsafe {
let mut rusage = mem::MaybeUninit::<rusage>::uninit();
let res = libc::getrusage(who as c_int, rusage.as_mut_ptr());
Errno::result(res).map(|_| Usage(rusage.assume_init()))
}
}

319
vendor/nix/src/sys/select.rs vendored Normal file
View File

@@ -0,0 +1,319 @@
//! Portably monitor a group of file descriptors for readiness.
use crate::errno::Errno;
use crate::sys::time::{TimeSpec, TimeVal};
use crate::Result;
use libc::{self, c_int};
use std::convert::TryFrom;
use std::iter::FusedIterator;
use std::mem;
use std::ops::Range;
use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
use std::ptr::{null, null_mut};
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<'fd> {
set: libc::fd_set,
_fd: std::marker::PhantomData<BorrowedFd<'fd>>,
}
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<'fd> FdSet<'fd> {
/// Create an empty `FdSet`
pub fn new() -> FdSet<'fd> {
let mut fdset = mem::MaybeUninit::uninit();
unsafe {
libc::FD_ZERO(fdset.as_mut_ptr());
Self {
set: fdset.assume_init(),
_fd: std::marker::PhantomData,
}
}
}
/// Add a file descriptor to an `FdSet`
pub fn insert(&mut self, fd: BorrowedFd<'fd>) {
assert_fd_valid(fd.as_raw_fd());
unsafe { libc::FD_SET(fd.as_raw_fd(), &mut self.set) };
}
/// Remove a file descriptor from an `FdSet`
pub fn remove(&mut self, fd: BorrowedFd<'fd>) {
assert_fd_valid(fd.as_raw_fd());
unsafe { libc::FD_CLR(fd.as_raw_fd(), &mut self.set) };
}
/// Test an `FdSet` for the presence of a certain file descriptor.
pub fn contains(&self, fd: BorrowedFd<'fd>) -> bool {
assert_fd_valid(fd.as_raw_fd());
unsafe { libc::FD_ISSET(fd.as_raw_fd(), &self.set) }
}
/// Remove all file descriptors from this `FdSet`.
pub fn clear(&mut self) {
unsafe { libc::FD_ZERO(&mut self.set) };
}
/// Finds the highest file descriptor in the set.
///
/// Returns `None` if the set is empty.
///
/// This can be used to calculate the `nfds` parameter of the [`select`] function.
///
/// # Example
///
/// ```
/// # use std::os::unix::io::{AsRawFd, BorrowedFd};
/// # use nix::sys::select::FdSet;
/// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
/// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
/// let mut set = FdSet::new();
/// set.insert(fd_four);
/// set.insert(fd_nine);
/// assert_eq!(set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()), Some(9));
/// ```
///
/// [`select`]: fn.select.html
pub fn highest(&self) -> Option<BorrowedFd<'_>> {
self.fds(None).next_back()
}
/// Returns an iterator over the file descriptors in the set.
///
/// For performance, it takes an optional higher bound: the iterator will
/// not return any elements of the set greater than the given file
/// descriptor.
///
/// # Examples
///
/// ```
/// # use nix::sys::select::FdSet;
/// # use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
/// let mut set = FdSet::new();
/// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
/// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
/// set.insert(fd_four);
/// set.insert(fd_nine);
/// let fds: Vec<RawFd> = set.fds(None).map(|borrowed_fd|borrowed_fd.as_raw_fd()).collect();
/// assert_eq!(fds, vec![4, 9]);
/// ```
#[inline]
pub fn fds(&self, highest: Option<RawFd>) -> Fds {
Fds {
set: self,
range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE),
}
}
}
impl Default for FdSet<'_> {
fn default() -> Self {
Self::new()
}
}
/// Iterator over `FdSet`.
#[derive(Debug)]
pub struct Fds<'a, 'fd> {
set: &'a FdSet<'fd>,
range: Range<usize>,
}
impl<'fd> Iterator for Fds<'_, 'fd> {
type Item = BorrowedFd<'fd>;
fn next(&mut self) -> Option<Self::Item> {
for i in &mut self.range {
let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
if self.set.contains(borrowed_i) {
return Some(borrowed_i);
}
}
None
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (_, upper) = self.range.size_hint();
(0, upper)
}
}
impl<'fd> DoubleEndedIterator for Fds<'_, 'fd> {
#[inline]
fn next_back(&mut self) -> Option<BorrowedFd<'fd>> {
while let Some(i) = self.range.next_back() {
let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
if self.set.contains(borrowed_i) {
return Some(borrowed_i);
}
}
None
}
}
impl FusedIterator for Fds<'_, '_> {}
/// Monitors file descriptors for readiness
///
/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
/// file descriptors that are ready for the given operation are set.
///
/// When this function returns, `timeout` has an implementation-defined value.
///
/// # Parameters
///
/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
/// to the maximum of that.
/// * `readfds`: File descriptors to check for being ready to read.
/// * `writefds`: File descriptors to check for being ready to write.
/// * `errorfds`: File descriptors to check for pending error conditions.
/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
/// indefinitely).
///
/// # References
///
/// [select(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn select<'a, 'fd, N, R, W, E, T>(
nfds: N,
readfds: R,
writefds: W,
errorfds: E,
timeout: T,
) -> Result<c_int>
where
'fd: 'a,
N: Into<Option<c_int>>,
R: Into<Option<&'a mut FdSet<'fd>>>,
W: Into<Option<&'a mut FdSet<'fd>>>,
E: Into<Option<&'a mut FdSet<'fd>>>,
T: Into<Option<&'a mut TimeVal>>,
{
let mut readfds = readfds.into();
let mut writefds = writefds.into();
let mut errorfds = errorfds.into();
let timeout = timeout.into();
let nfds = nfds.into().unwrap_or_else(|| {
readfds
.iter_mut()
.chain(writefds.iter_mut())
.chain(errorfds.iter_mut())
.map(|set| {
set.highest()
.map(|borrowed_fd| borrowed_fd.as_raw_fd())
.unwrap_or(-1)
})
.max()
.unwrap_or(-1)
+ 1
});
let readfds = readfds
.map(|set| set as *mut _ as *mut libc::fd_set)
.unwrap_or(null_mut());
let writefds = writefds
.map(|set| set as *mut _ as *mut libc::fd_set)
.unwrap_or(null_mut());
let errorfds = errorfds
.map(|set| set as *mut _ as *mut libc::fd_set)
.unwrap_or(null_mut());
let timeout = timeout
.map(|tv| tv as *mut _ as *mut libc::timeval)
.unwrap_or(null_mut());
let res =
unsafe { libc::select(nfds, readfds, writefds, errorfds, timeout) };
Errno::result(res)
}
feature! {
#![feature = "signal"]
use crate::sys::signal::SigSet;
/// Monitors file descriptors for readiness with an altered signal mask.
///
/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
/// file descriptors that are ready for the given operation are set.
///
/// When this function returns, the original signal mask is restored.
///
/// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
///
/// # Parameters
///
/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
/// to the maximum of that.
/// * `readfds`: File descriptors to check for read readiness
/// * `writefds`: File descriptors to check for write readiness
/// * `errorfds`: File descriptors to check for pending error conditions.
/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
/// indefinitely).
/// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
/// ready (`None` to set no alternative signal mask).
///
/// # References
///
/// [pselect(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
///
/// [The new pselect() system call](https://lwn.net/Articles/176911/)
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn pselect<'a, 'fd, N, R, W, E, T, S>(nfds: N,
readfds: R,
writefds: W,
errorfds: E,
timeout: T,
sigmask: S) -> Result<c_int>
where
'fd: 'a,
N: Into<Option<c_int>>,
R: Into<Option<&'a mut FdSet<'fd>>>,
W: Into<Option<&'a mut FdSet<'fd>>>,
E: Into<Option<&'a mut FdSet<'fd>>>,
T: Into<Option<&'a TimeSpec>>,
S: Into<Option<&'a SigSet>>,
{
let mut readfds = readfds.into();
let mut writefds = writefds.into();
let mut errorfds = errorfds.into();
let sigmask = sigmask.into();
let timeout = timeout.into();
let nfds = nfds.into().unwrap_or_else(|| {
readfds.iter_mut()
.chain(writefds.iter_mut())
.chain(errorfds.iter_mut())
.map(|set| set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()).unwrap_or(-1))
.max()
.unwrap_or(-1) + 1
});
let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
let res = unsafe {
libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
};
Errno::result(res)
}
}

351
vendor/nix/src/sys/sendfile.rs vendored Normal file
View File

@@ -0,0 +1,351 @@
//! Send data from a file to a socket, bypassing userland.
use cfg_if::cfg_if;
use std::os::unix::io::{AsFd, AsRawFd};
use std::ptr;
use libc::{self, off_t};
use crate::errno::Errno;
use crate::Result;
/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
///
/// Returns a `Result` with the number of bytes written.
///
/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will
/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified
/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to
/// the byte after the last byte copied.
///
/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
///
/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html) for Linux,
/// see [the sendfile(2) man page.](https://docs.oracle.com/cd/E88353_01/html/E37843/sendfile-3c.html) for Solaris.
#[cfg(any(linux_android, solarish))]
pub fn sendfile<F1: AsFd, F2: AsFd>(
out_fd: F1,
in_fd: F2,
offset: Option<&mut off_t>,
count: usize,
) -> Result<usize> {
let offset = offset
.map(|offset| offset as *mut _)
.unwrap_or(ptr::null_mut());
let ret = unsafe {
libc::sendfile(
out_fd.as_fd().as_raw_fd(),
in_fd.as_fd().as_raw_fd(),
offset,
count,
)
};
Errno::result(ret).map(|r| r as usize)
}
/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
///
/// Returns a `Result` with the number of bytes written.
///
/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will
/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified
/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to
/// the byte after the last byte copied.
///
/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
///
/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
#[cfg(target_os = "linux")]
pub fn sendfile64<F1: AsFd, F2: AsFd>(
out_fd: F1,
in_fd: F2,
offset: Option<&mut libc::off64_t>,
count: usize,
) -> Result<usize> {
let offset = offset
.map(|offset| offset as *mut _)
.unwrap_or(ptr::null_mut());
let ret = unsafe {
libc::sendfile64(
out_fd.as_fd().as_raw_fd(),
in_fd.as_fd().as_raw_fd(),
offset,
count,
)
};
Errno::result(ret).map(|r| r as usize)
}
cfg_if! {
if #[cfg(any(freebsdlike, apple_targets))] {
use std::io::IoSlice;
#[derive(Clone, Debug)]
struct SendfileHeaderTrailer<'a> {
raw: libc::sf_hdtr,
_headers: Option<Vec<IoSlice<'a>>>,
_trailers: Option<Vec<IoSlice<'a>>>,
}
impl<'a> SendfileHeaderTrailer<'a> {
fn new(
headers: Option<&'a [&'a [u8]]>,
trailers: Option<&'a [&'a [u8]]>
) -> SendfileHeaderTrailer<'a> {
let mut header_iovecs: Option<Vec<IoSlice<'_>>> =
headers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
let mut trailer_iovecs: Option<Vec<IoSlice<'_>>> =
trailers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
SendfileHeaderTrailer {
raw: libc::sf_hdtr {
headers: {
header_iovecs
.as_mut()
.map_or(ptr::null_mut(), |v| v.as_mut_ptr())
.cast()
},
hdr_cnt: header_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32,
trailers: {
trailer_iovecs
.as_mut()
.map_or(ptr::null_mut(), |v| v.as_mut_ptr())
.cast()
},
trl_cnt: trailer_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32
},
_headers: header_iovecs,
_trailers: trailer_iovecs,
}
}
}
} else if #[cfg(solarish)] {
use std::os::unix::io::BorrowedFd;
use std::marker::PhantomData;
#[derive(Debug, Copy, Clone)]
/// Mapping of the raw C sendfilevec_t struct
pub struct SendfileVec<'fd> {
raw: libc::sendfilevec_t,
phantom: PhantomData<BorrowedFd<'fd>>
}
impl<'fd> SendfileVec<'fd> {
/// initialises SendfileVec to send data directly from the process's address space
/// same in C with sfv_fd set to SFV_FD_SELF.
pub fn newself(
off: off_t,
len: usize
) -> Self {
Self{raw: libc::sendfilevec_t{sfv_fd: libc::SFV_FD_SELF, sfv_flag: 0, sfv_off: off, sfv_len: len}, phantom: PhantomData}
}
/// initialises SendfileVec to send data from `fd`.
pub fn new(
fd: BorrowedFd<'fd>,
off: off_t,
len: usize
) -> SendfileVec<'fd> {
Self{raw: libc::sendfilevec_t{sfv_fd: fd.as_raw_fd(), sfv_flag: 0, sfv_off:off, sfv_len: len}, phantom: PhantomData}
}
}
impl From<SendfileVec<'_>> for libc::sendfilevec_t {
fn from<'fd>(vec: SendfileVec) -> libc::sendfilevec_t {
vec.raw
}
}
}
}
cfg_if! {
if #[cfg(target_os = "freebsd")] {
use libc::c_int;
libc_bitflags!{
/// Configuration options for [`sendfile`.](fn.sendfile.html)
pub struct SfFlags: c_int {
/// Causes `sendfile` to return EBUSY instead of blocking when attempting to read a
/// busy page.
SF_NODISKIO;
/// Causes `sendfile` to sleep until the network stack releases its reference to the
/// VM pages read. When `sendfile` returns, the data is not guaranteed to have been
/// sent, but it is safe to modify the file.
SF_SYNC;
/// Causes `sendfile` to cache exactly the number of pages specified in the
/// `readahead` parameter, disabling caching heuristics.
SF_USER_READAHEAD;
/// Causes `sendfile` not to cache the data read.
SF_NOCACHE;
}
}
/// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`.
///
/// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
/// an error occurs.
///
/// `in_fd` must describe a regular file or shared memory object. `out_sock` must describe a
/// stream socket.
///
/// If `offset` falls past the end of the file, the function returns success and zero bytes
/// written.
///
/// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
/// file (EOF).
///
/// `headers` and `trailers` specify optional slices of byte slices to be sent before and
/// after the data read from `in_fd`, respectively. The length of headers and trailers sent
/// is included in the returned count of bytes written. The values of `offset` and `count`
/// do not apply to headers or trailers.
///
/// `readahead` specifies the minimum number of pages to cache in memory ahead of the page
/// currently being sent.
///
/// For more information, see
/// [the sendfile(2) man page.](https://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2)
#[allow(clippy::too_many_arguments)]
pub fn sendfile<F1: AsFd, F2: AsFd>(
in_fd: F1,
out_sock: F2,
offset: off_t,
count: Option<usize>,
headers: Option<&[&[u8]]>,
trailers: Option<&[&[u8]]>,
flags: SfFlags,
readahead: u16
) -> (Result<()>, off_t) {
// Readahead goes in upper 16 bits
// Flags goes in lower 16 bits
// see `man 2 sendfile`
let ra32 = u32::from(readahead);
let flags: u32 = (ra32 << 16) | (flags.bits() as u32);
let mut bytes_sent: off_t = 0;
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr);
let return_code = unsafe {
libc::sendfile(in_fd.as_fd().as_raw_fd(),
out_sock.as_fd().as_raw_fd(),
offset,
count.unwrap_or(0),
hdtr_ptr as *mut libc::sf_hdtr,
&mut bytes_sent as *mut off_t,
flags as c_int)
};
(Errno::result(return_code).and(Ok(())), bytes_sent)
}
} else if #[cfg(target_os = "dragonfly")] {
/// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`.
///
/// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
/// an error occurs.
///
/// `in_fd` must describe a regular file. `out_sock` must describe a stream socket.
///
/// If `offset` falls past the end of the file, the function returns success and zero bytes
/// written.
///
/// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
/// file (EOF).
///
/// `headers` and `trailers` specify optional slices of byte slices to be sent before and
/// after the data read from `in_fd`, respectively. The length of headers and trailers sent
/// is included in the returned count of bytes written. The values of `offset` and `count`
/// do not apply to headers or trailers.
///
/// For more information, see
/// [the sendfile(2) man page.](https://leaf.dragonflybsd.org/cgi/web-man?command=sendfile&section=2)
pub fn sendfile<F1: AsFd, F2: AsFd>(
in_fd: F1,
out_sock: F2,
offset: off_t,
count: Option<usize>,
headers: Option<&[&[u8]]>,
trailers: Option<&[&[u8]]>,
) -> (Result<()>, off_t) {
let mut bytes_sent: off_t = 0;
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr);
let return_code = unsafe {
libc::sendfile(in_fd.as_fd().as_raw_fd(),
out_sock.as_fd().as_raw_fd(),
offset,
count.unwrap_or(0),
hdtr_ptr as *mut libc::sf_hdtr,
&mut bytes_sent as *mut off_t,
0)
};
(Errno::result(return_code).and(Ok(())), bytes_sent)
}
} else if #[cfg(apple_targets)] {
/// Read bytes from `in_fd` starting at `offset` and write up to `count` bytes to
/// `out_sock`.
///
/// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
/// an error occurs.
///
/// `in_fd` must describe a regular file. `out_sock` must describe a stream socket.
///
/// If `offset` falls past the end of the file, the function returns success and zero bytes
/// written.
///
/// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
/// file (EOF).
///
/// `hdtr` specifies an optional list of headers and trailers to be sent before and after
/// the data read from `in_fd`, respectively. The length of headers and trailers sent is
/// included in the returned count of bytes written. If any headers are specified and
/// `count` is non-zero, the length of the headers will be counted in the limit of total
/// bytes sent. Trailers do not count toward the limit of bytes sent and will always be sent
/// regardless. The value of `offset` does not affect headers or trailers.
///
/// For more information, see
/// [the sendfile(2) man page.](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/sendfile.2.html)
pub fn sendfile<F1: AsFd, F2: AsFd>(
in_fd: F1,
out_sock: F2,
offset: off_t,
count: Option<off_t>,
headers: Option<&[&[u8]]>,
trailers: Option<&[&[u8]]>
) -> (Result<()>, off_t) {
let mut len = count.unwrap_or(0);
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr);
let return_code = unsafe {
libc::sendfile(in_fd.as_fd().as_raw_fd(),
out_sock.as_fd().as_raw_fd(),
offset,
&mut len as *mut off_t,
hdtr_ptr as *mut libc::sf_hdtr,
0)
};
(Errno::result(return_code).and(Ok(())), len)
}
} else if #[cfg(solarish)] {
/// Write data from the vec arrays to `out_sock` and returns a `Result` and a
/// count of bytes written.
///
/// Each `SendfileVec` set needs to be instantiated either with `SendfileVec::new` or
/// `SendfileVec::newself`.
///
/// The former allows to send data from a file descriptor through `fd`,
/// from an offset `off` and for a given amount of data `len`.
///
/// The latter allows to send data from the process's address space, from an offset `off`
/// and for a given amount of data `len`.
///
/// For more information, see
/// [the sendfilev(3) man page.](https://illumos.org/man/3EXT/sendfilev)
pub fn sendfilev<F: AsFd>(
out_sock: F,
vec: &[SendfileVec]
) -> (Result<()>, usize) {
let mut len = 0usize;
let return_code = unsafe {
libc::sendfilev(out_sock.as_fd().as_raw_fd(), vec.as_ptr() as *const libc::sendfilevec_t, vec.len() as i32, &mut len)
};
(Errno::result(return_code).and(Ok(())), len)
}
}
}

1412
vendor/nix/src/sys/signal.rs vendored Normal file

File diff suppressed because it is too large Load Diff

174
vendor/nix/src/sys/signalfd.rs vendored Normal file
View File

@@ -0,0 +1,174 @@
//! Interface for the `signalfd` syscall.
//!
//! # Signal discarding
//! When a signal can't be delivered to a process (or thread), it will become a pending signal.
//! Failure to deliver could happen if the signal is blocked by every thread in the process or if
//! the signal handler is still handling a previous signal.
//!
//! If a signal is sent to a process (or thread) that already has a pending signal of the same
//! type, it will be discarded. This means that if signals of the same type are received faster than
//! they are processed, some of those signals will be dropped. Because of this limitation,
//! `signalfd` in itself cannot be used for reliable communication between processes or threads.
//!
//! Once the signal is unblocked, or the signal handler is finished, and a signal is still pending
//! (ie. not consumed from a signalfd) it will be delivered to the signal handler.
//!
//! Please note that signal discarding is not specific to `signalfd`, but also happens with regular
//! signal handlers.
use crate::errno::Errno;
pub use crate::sys::signal::{self, SigSet};
use crate::Result;
/// Information of a received signal, the return type of [`SignalFd::read_signal()`].
pub use libc::signalfd_siginfo as siginfo;
use std::mem;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
libc_bitflags! {
pub struct SfdFlags: libc::c_int {
SFD_NONBLOCK;
SFD_CLOEXEC;
}
}
#[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.
///
/// **Important:** please read the module level documentation about signal discarding before using
/// this function!
///
/// The `mask` parameter specifies the set of signals that can be accepted via this file descriptor.
///
/// A signal must be blocked on every thread in a process, otherwise it won't be visible from
/// signalfd (the default handler will be invoked instead).
///
/// See [the signalfd man page for more information](https://man7.org/linux/man-pages/man2/signalfd.2.html)
#[deprecated(since = "0.27.0", note = "Use SignalFd instead")]
pub fn signalfd<F: AsFd>(
fd: Option<F>,
mask: &SigSet,
flags: SfdFlags,
) -> Result<OwnedFd> {
_signalfd(fd, mask, flags)
}
fn _signalfd<F: AsFd>(
fd: Option<F>,
mask: &SigSet,
flags: SfdFlags,
) -> Result<OwnedFd> {
let raw_fd = fd.map_or(-1, |x| x.as_fd().as_raw_fd());
unsafe {
Errno::result(libc::signalfd(raw_fd, mask.as_ref(), flags.bits()))
.map(|raw_fd| FromRawFd::from_raw_fd(raw_fd))
}
}
/// A helper struct for creating, reading and closing a `signalfd` instance.
///
/// **Important:** please read the module level documentation about signal discarding before using
/// this struct!
///
/// # Examples
///
/// ```
/// # use nix::sys::signalfd::*;
/// // Set the thread to block the SIGUSR1 signal, otherwise the default handler will be used
/// let mut mask = SigSet::empty();
/// mask.add(signal::SIGUSR1);
/// mask.thread_block().unwrap();
///
/// // Signals are queued up on the file descriptor
/// let mut sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
///
/// match sfd.read_signal() {
/// // we caught a signal
/// Ok(Some(sig)) => (),
/// // there were no signals waiting (only happens when the SFD_NONBLOCK flag is set,
/// // otherwise the read_signal call blocks)
/// Ok(None) => (),
/// Err(err) => (), // some error happend
/// }
/// ```
#[derive(Debug)]
pub struct SignalFd(OwnedFd);
impl SignalFd {
pub fn new(mask: &SigSet) -> Result<SignalFd> {
Self::with_flags(mask, SfdFlags::empty())
}
pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result<SignalFd> {
let fd = _signalfd(None::<OwnedFd>, mask, flags)?;
Ok(SignalFd(fd))
}
pub fn set_mask(&self, mask: &SigSet) -> Result<()> {
self.update(mask, SfdFlags::empty())
}
pub fn read_signal(&self) -> Result<Option<siginfo>> {
let mut buffer = mem::MaybeUninit::<siginfo>::uninit();
let size = mem::size_of_val(&buffer);
let res = Errno::result(unsafe {
libc::read(self.0.as_raw_fd(), buffer.as_mut_ptr().cast(), size)
})
.map(|r| r as usize);
match res {
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),
}
}
/// Constructs a `SignalFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `SignalFd`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self(fd)
}
fn update(&self, mask: &SigSet, flags: SfdFlags) -> Result<()> {
let raw_fd = self.0.as_raw_fd();
unsafe {
Errno::result(libc::signalfd(raw_fd, mask.as_ref(), flags.bits()))
.map(drop)
}
}
}
impl AsFd for SignalFd {
fn as_fd(&self) -> BorrowedFd {
self.0.as_fd()
}
}
impl AsRawFd for SignalFd {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl From<SignalFd> for OwnedFd {
fn from(value: SignalFd) -> Self {
value.0
}
}
impl Iterator for SignalFd {
type Item = siginfo;
fn next(&mut self) -> Option<Self::Item> {
match self.read_signal() {
Ok(Some(sig)) => Some(sig),
Ok(None) | Err(_) => None,
}
}
}

2447
vendor/nix/src/sys/socket/addr.rs vendored Normal file

File diff suppressed because it is too large Load Diff

2567
vendor/nix/src/sys/socket/mod.rs vendored Normal file

File diff suppressed because it is too large Load Diff

2003
vendor/nix/src/sys/socket/sockopt.rs vendored Normal file

File diff suppressed because it is too large Load Diff

486
vendor/nix/src/sys/stat.rs vendored Normal file
View File

@@ -0,0 +1,486 @@
#[cfg(any(apple_targets, target_os = "openbsd"))]
pub use libc::c_uint;
#[cfg(any(target_os = "netbsd", freebsdlike))]
pub use libc::c_ulong;
pub use libc::stat as FileStat;
pub use libc::{dev_t, mode_t};
#[cfg(not(target_os = "redox"))]
use crate::fcntl::AtFlags;
use crate::sys::time::{TimeSpec, TimeVal};
use crate::{errno::Errno, NixPath, Result};
use std::mem;
libc_bitflags!(
/// "File type" flags for `mknod` and related functions.
pub struct SFlag: mode_t {
S_IFIFO;
S_IFCHR;
S_IFDIR;
S_IFBLK;
S_IFREG;
S_IFLNK;
S_IFSOCK;
S_IFMT;
}
);
libc_bitflags! {
/// "File mode / permissions" flags.
pub struct Mode: mode_t {
/// Read, write and execute for owner.
S_IRWXU;
/// Read for owner.
S_IRUSR;
/// Write for owner.
S_IWUSR;
/// Execute for owner.
S_IXUSR;
/// Read write and execute for group.
S_IRWXG;
/// Read for group.
S_IRGRP;
/// Write for group.
S_IWGRP;
/// Execute for group.
S_IXGRP;
/// Read, write and execute for other.
S_IRWXO;
/// Read for other.
S_IROTH;
/// Write for other.
S_IWOTH;
/// Execute for other.
S_IXOTH;
/// Set user id on execution.
S_ISUID as mode_t;
/// Set group id on execution.
S_ISGID as mode_t;
S_ISVTX as mode_t;
}
}
#[cfg(any(apple_targets, target_os = "openbsd"))]
pub type type_of_file_flag = c_uint;
#[cfg(any(freebsdlike, target_os = "netbsd"))]
pub type type_of_file_flag = c_ulong;
#[cfg(bsd)]
libc_bitflags! {
/// File flags.
pub struct FileFlag: type_of_file_flag {
/// The file may only be appended to.
SF_APPEND;
/// The file has been archived.
SF_ARCHIVED;
#[cfg(any(target_os = "dragonfly"))]
SF_CACHE;
/// The file may not be changed.
SF_IMMUTABLE;
/// Indicates a WAPBL journal file.
#[cfg(any(target_os = "netbsd"))]
SF_LOG;
/// Do not retain history for file
#[cfg(any(target_os = "dragonfly"))]
SF_NOHISTORY;
/// The file may not be renamed or deleted.
#[cfg(freebsdlike)]
SF_NOUNLINK;
/// Mask of superuser changeable flags
SF_SETTABLE;
/// Snapshot is invalid.
#[cfg(any(target_os = "netbsd"))]
SF_SNAPINVAL;
/// The file is a snapshot file.
#[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
SF_SNAPSHOT;
#[cfg(any(target_os = "dragonfly"))]
SF_XLINK;
/// The file may only be appended to.
UF_APPEND;
/// The file needs to be archived.
#[cfg(any(target_os = "freebsd"))]
UF_ARCHIVE;
#[cfg(any(target_os = "dragonfly"))]
UF_CACHE;
/// File is compressed at the file system level.
#[cfg(apple_targets)]
UF_COMPRESSED;
/// The file may be hidden from directory listings at the application's
/// discretion.
#[cfg(any(
target_os = "freebsd",
apple_targets,
))]
UF_HIDDEN;
/// The file may not be changed.
UF_IMMUTABLE;
/// Do not dump the file.
UF_NODUMP;
#[cfg(any(target_os = "dragonfly"))]
UF_NOHISTORY;
/// The file may not be renamed or deleted.
#[cfg(freebsdlike)]
UF_NOUNLINK;
/// The file is offline, or has the Windows and CIFS
/// `FILE_ATTRIBUTE_OFFLINE` attribute.
#[cfg(any(target_os = "freebsd"))]
UF_OFFLINE;
/// The directory is opaque when viewed through a union stack.
UF_OPAQUE;
/// The file is read only, and may not be written or appended.
#[cfg(any(target_os = "freebsd"))]
UF_READONLY;
/// The file contains a Windows reparse point.
#[cfg(any(target_os = "freebsd"))]
UF_REPARSE;
/// Mask of owner changeable flags.
UF_SETTABLE;
/// The file has the Windows `FILE_ATTRIBUTE_SPARSE_FILE` attribute.
#[cfg(any(target_os = "freebsd"))]
UF_SPARSE;
/// The file has the DOS, Windows and CIFS `FILE_ATTRIBUTE_SYSTEM`
/// attribute.
#[cfg(any(target_os = "freebsd"))]
UF_SYSTEM;
/// File renames and deletes are tracked.
#[cfg(apple_targets)]
UF_TRACKED;
#[cfg(any(target_os = "dragonfly"))]
UF_XLINK;
}
}
/// 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)
})?;
Errno::result(res).map(drop)
}
/// Create a special or ordinary file, relative to a given directory.
#[cfg(not(any(apple_targets, target_os = "redox", target_os = "haiku")))]
pub fn mknodat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
kind: SFlag,
perm: Mode,
dev: dev_t,
) -> Result<()> {
use std::os::fd::AsRawFd;
let res = path.with_nix_path(|cstr| unsafe {
libc::mknodat(
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
kind.bits() | perm.bits() as mode_t,
dev,
)
})?;
Errno::result(res).map(drop)
}
#[cfg(target_os = "linux")]
pub const fn major(dev: dev_t) -> u64 {
((dev >> 32) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)
}
#[cfg(target_os = "linux")]
pub const fn minor(dev: dev_t) -> u64 {
((dev >> 12) & 0xffff_ff00) | ((dev) & 0x0000_00ff)
}
#[cfg(target_os = "linux")]
pub const fn makedev(major: u64, minor: u64) -> dev_t {
((major & 0xffff_f000) << 32)
| ((major & 0x0000_0fff) << 8)
| ((minor & 0xffff_ff00) << 12)
| (minor & 0x0000_00ff)
}
pub fn umask(mode: Mode) -> Mode {
let prev = unsafe { libc::umask(mode.bits() as mode_t) };
Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
}
pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
let mut dst = mem::MaybeUninit::uninit();
let res = path.with_nix_path(|cstr| unsafe {
libc::stat(cstr.as_ptr(), dst.as_mut_ptr())
})?;
Errno::result(res)?;
Ok(unsafe { dst.assume_init() })
}
pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
let mut dst = mem::MaybeUninit::uninit();
let res = path.with_nix_path(|cstr| unsafe {
libc::lstat(cstr.as_ptr(), dst.as_mut_ptr())
})?;
Errno::result(res)?;
Ok(unsafe { dst.assume_init() })
}
pub fn fstat<Fd: std::os::fd::AsFd>(fd: Fd) -> Result<FileStat> {
use std::os::fd::AsRawFd;
let mut dst = mem::MaybeUninit::uninit();
let res = unsafe { libc::fstat(fd.as_fd().as_raw_fd(), dst.as_mut_ptr()) };
Errno::result(res)?;
Ok(unsafe { dst.assume_init() })
}
#[cfg(not(target_os = "redox"))]
pub fn fstatat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
pathname: &P,
f: AtFlags,
) -> Result<FileStat> {
use std::os::fd::AsRawFd;
let mut dst = mem::MaybeUninit::uninit();
let res = pathname.with_nix_path(|cstr| unsafe {
libc::fstatat(
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
dst.as_mut_ptr(),
f.bits() as libc::c_int,
)
})?;
Errno::result(res)?;
Ok(unsafe { dst.assume_init() })
}
/// Change the file permission bits of the file specified by a file descriptor.
///
/// # References
///
/// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
pub fn fchmod<Fd: std::os::fd::AsFd>(fd: Fd, mode: Mode) -> Result<()> {
use std::os::fd::AsRawFd;
let res =
unsafe { libc::fchmod(fd.as_fd().as_raw_fd(), mode.bits() as mode_t) };
Errno::result(res).map(drop)
}
/// Flags for `fchmodat` function.
#[derive(Clone, Copy, Debug)]
pub enum FchmodatFlags {
FollowSymlink,
NoFollowSymlink,
}
/// Change the file permission bits.
///
/// The file to be changed is determined relative to the directory associated
/// with the file descriptor `dirfd` or the current working directory
/// if `dirfd` is [`AT_FDCWD`](crate::fcntl::AT_FDCWD).
///
/// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link,
/// then the mode of the symbolic link is changed.
///
/// `fchmodat(AT_FDCWD, path, mode, FchmodatFlags::FollowSymlink)` is identical to
/// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented
/// in the `nix` crate.
///
/// # References
///
/// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
#[cfg(not(target_os = "redox"))]
pub fn fchmodat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
mode: Mode,
flag: FchmodatFlags,
) -> Result<()> {
use std::os::fd::AsRawFd;
let atflag = match flag {
FchmodatFlags::FollowSymlink => AtFlags::empty(),
FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
};
let res = path.with_nix_path(|cstr| unsafe {
libc::fchmodat(
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
mode.bits() as mode_t,
atflag.bits() as libc::c_int,
)
})?;
Errno::result(res).map(drop)
}
/// Change the access and modification times of a file.
///
/// `utimes(path, times)` is identical to
/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)`. The former
/// is a deprecated API so prefer using the latter if the platforms you care
/// about support it.
///
/// # References
///
/// [utimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html).
pub fn utimes<P: ?Sized + NixPath>(
path: &P,
atime: &TimeVal,
mtime: &TimeVal,
) -> Result<()> {
let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = path.with_nix_path(|cstr| unsafe {
libc::utimes(cstr.as_ptr(), &times[0])
})?;
Errno::result(res).map(drop)
}
/// Change the access and modification times of a file without following symlinks.
///
/// `lutimes(path, times)` is identical to
/// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former
/// is a deprecated API so prefer using the latter if the platforms you care
/// about support it.
///
/// # References
///
/// [lutimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html).
#[cfg(any(
target_os = "linux",
target_os = "haiku",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
))]
pub fn lutimes<P: ?Sized + NixPath>(
path: &P,
atime: &TimeVal,
mtime: &TimeVal,
) -> Result<()> {
let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = path.with_nix_path(|cstr| unsafe {
libc::lutimes(cstr.as_ptr(), &times[0])
})?;
Errno::result(res).map(drop)
}
/// Change the access and modification times of the file specified by a file descriptor.
///
/// If you want to set the timestamp to now, use `TimeSpec::UTIME_NOW`. Use
/// `TimeSpec::UTIME_OMIT` if you don't want to change it.
///
/// # References
///
/// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
#[inline]
pub fn futimens<Fd: std::os::fd::AsFd>(
fd: Fd,
atime: &TimeSpec,
mtime: &TimeSpec,
) -> Result<()> {
use std::os::fd::AsRawFd;
let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = unsafe { libc::futimens(fd.as_fd().as_raw_fd(), &times[0]) };
Errno::result(res).map(drop)
}
/// Flags for `utimensat` function.
// TODO: replace with fcntl::AtFlags
#[derive(Clone, Copy, Debug)]
pub enum UtimensatFlags {
FollowSymlink,
NoFollowSymlink,
}
/// Change the access and modification times of a file.
///
/// The file to be changed is determined relative to the directory associated
/// with the file descriptor `dirfd` or the current working directory
/// if `dirfd` is [`AT_FDCWD`](crate::fcntl::AT_FDCWD).
///
/// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link,
/// then the mode of the symbolic link is changed.
///
/// `utimensat(AT_FDCWD, path, times, UtimensatFlags::FollowSymlink)` is identical to
/// `utimes(path, times)`. The latter is a deprecated API so prefer using the
/// former if the platforms you care about support it.
///
/// If you want to set the timestamp to now, use `TimeSpec::UTIME_NOW`. Use
/// `TimeSpec::UTIME_OMIT` if you don't want to change it.
///
/// # References
///
/// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
#[cfg(not(target_os = "redox"))]
pub fn utimensat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
atime: &TimeSpec,
mtime: &TimeSpec,
flag: UtimensatFlags,
) -> Result<()> {
use std::os::fd::AsRawFd;
let atflag = match flag {
UtimensatFlags::FollowSymlink => AtFlags::empty(),
UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
};
let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
let res = path.with_nix_path(|cstr| unsafe {
libc::utimensat(
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
&times[0],
atflag.bits() as libc::c_int,
)
})?;
Errno::result(res).map(drop)
}
/// Create a directory at the path specified by `dirfd` and `path`.
///
/// If `path` is a relative path, then it is interpreted relative to the directory
/// referred to by the file descriptor `dirfd`. (One can use [`AT_FDCWD`][link] to
/// specify the current working directory in `dirfd`). If `path` is absolute,
/// then `dirfd` is ignored.
///
/// [link]: crate::fcntl::AT_FDCWD
#[cfg(not(target_os = "redox"))]
pub fn mkdirat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
dirfd: Fd,
path: &P,
mode: Mode,
) -> Result<()> {
use std::os::fd::AsRawFd;
let res = path.with_nix_path(|cstr| unsafe {
libc::mkdirat(
dirfd.as_fd().as_raw_fd(),
cstr.as_ptr(),
mode.bits() as mode_t,
)
})?;
Errno::result(res).map(drop)
}

695
vendor/nix/src/sys/statfs.rs vendored Normal file
View File

@@ -0,0 +1,695 @@
//! Get filesystem statistics, non-portably
//!
//! See [`statvfs`](crate::sys::statvfs) for a portable alternative.
#[cfg(not(any(linux_android, target_os = "cygwin")))]
use std::ffi::CStr;
use std::fmt::{self, Debug};
use std::mem;
use std::os::unix::io::{AsFd, AsRawFd};
use cfg_if::cfg_if;
#[cfg(all(feature = "mount", bsd))]
use crate::mount::MntFlags;
#[cfg(target_os = "linux")]
use crate::sys::statvfs::FsFlags;
use crate::{errno::Errno, NixPath, Result};
/// Identifies a mounted file system
#[cfg(target_os = "android")]
pub type fsid_t = libc::__fsid_t;
/// Identifies a mounted file system
#[cfg(not(any(target_os = "android", target_os = "cygwin")))]
pub type fsid_t = libc::fsid_t;
/// Identifies a mounted file system
#[cfg(target_os = "cygwin")]
pub type fsid_t = libc::c_long;
cfg_if! {
if #[cfg(any(linux_android, target_os = "fuchsia"))] {
type type_of_statfs = libc::statfs64;
const LIBC_FSTATFS: unsafe extern "C" fn
(fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
= libc::fstatfs64;
const LIBC_STATFS: unsafe extern "C" fn
(path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int
= libc::statfs64;
} else {
type type_of_statfs = libc::statfs;
const LIBC_FSTATFS: unsafe extern "C" fn
(fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
= libc::fstatfs;
const LIBC_STATFS: unsafe extern "C" fn
(path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int
= libc::statfs;
}
}
/// Describes a mounted file system
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Statfs(type_of_statfs);
#[cfg(target_os = "freebsd")]
type fs_type_t = u32;
#[cfg(target_os = "android")]
type fs_type_t = libc::c_ulong;
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
type fs_type_t = libc::c_uint;
#[cfg(all(target_os = "linux", any(target_env = "musl", target_env = "ohos")))]
type fs_type_t = libc::c_ulong;
#[cfg(all(target_os = "linux", target_env = "uclibc"))]
type fs_type_t = libc::c_int;
#[cfg(all(
target_os = "linux",
not(any(
target_arch = "s390x",
target_env = "musl",
target_env = "ohos",
target_env = "uclibc"
))
))]
type fs_type_t = libc::__fsword_t;
#[cfg(target_os = "cygwin")]
type fs_type_t = libc::c_long;
/// Describes the file system type as known by the operating system.
#[cfg(any(
target_os = "freebsd",
target_os = "android",
all(target_os = "linux", target_arch = "s390x"),
all(target_os = "linux", target_env = "musl"),
all(target_os = "linux", target_env = "ohos"),
all(
target_os = "linux",
not(any(target_arch = "s390x", target_env = "musl"))
),
target_os = "cygwin",
))]
#[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(linux_android)]
#[allow(missing_docs)]
pub const ADFS_SUPER_MAGIC: FsType =
FsType(libc::ADFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const AFFS_SUPER_MAGIC: FsType =
FsType(libc::AFFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const AFS_SUPER_MAGIC: FsType = FsType(libc::AFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const AUTOFS_SUPER_MAGIC: FsType =
FsType(libc::AUTOFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const BPF_FS_MAGIC: FsType = FsType(libc::BPF_FS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const BTRFS_SUPER_MAGIC: FsType =
FsType(libc::BTRFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const CGROUP2_SUPER_MAGIC: FsType =
FsType(libc::CGROUP2_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const CGROUP_SUPER_MAGIC: FsType =
FsType(libc::CGROUP_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const CODA_SUPER_MAGIC: FsType =
FsType(libc::CODA_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const CRAMFS_MAGIC: FsType = FsType(libc::CRAMFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const DEBUGFS_MAGIC: FsType = FsType(libc::DEBUGFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const DEVPTS_SUPER_MAGIC: FsType =
FsType(libc::DEVPTS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const ECRYPTFS_SUPER_MAGIC: FsType =
FsType(libc::ECRYPTFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const EFS_SUPER_MAGIC: FsType = FsType(libc::EFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const EXT2_SUPER_MAGIC: FsType =
FsType(libc::EXT2_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const EXT3_SUPER_MAGIC: FsType =
FsType(libc::EXT3_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const EXT4_SUPER_MAGIC: FsType =
FsType(libc::EXT4_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const F2FS_SUPER_MAGIC: FsType =
FsType(libc::F2FS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const FUSE_SUPER_MAGIC: FsType =
FsType(libc::FUSE_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const FUTEXFS_SUPER_MAGIC: FsType =
FsType(libc::FUTEXFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const HOSTFS_SUPER_MAGIC: FsType =
FsType(libc::HOSTFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const HPFS_SUPER_MAGIC: FsType =
FsType(libc::HPFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const HUGETLBFS_MAGIC: FsType = FsType(libc::HUGETLBFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const ISOFS_SUPER_MAGIC: FsType =
FsType(libc::ISOFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const JFFS2_SUPER_MAGIC: FsType =
FsType(libc::JFFS2_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX2_SUPER_MAGIC2: FsType =
FsType(libc::MINIX2_SUPER_MAGIC2 as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX2_SUPER_MAGIC: FsType =
FsType(libc::MINIX2_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX3_SUPER_MAGIC: FsType =
FsType(libc::MINIX3_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX_SUPER_MAGIC2: FsType =
FsType(libc::MINIX_SUPER_MAGIC2 as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MINIX_SUPER_MAGIC: FsType =
FsType(libc::MINIX_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const MSDOS_SUPER_MAGIC: FsType =
FsType(libc::MSDOS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const NCP_SUPER_MAGIC: FsType = FsType(libc::NCP_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const NFS_SUPER_MAGIC: FsType = FsType(libc::NFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const NILFS_SUPER_MAGIC: FsType =
FsType(libc::NILFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const OCFS2_SUPER_MAGIC: FsType =
FsType(libc::OCFS2_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const OPENPROM_SUPER_MAGIC: FsType =
FsType(libc::OPENPROM_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const OVERLAYFS_SUPER_MAGIC: FsType =
FsType(libc::OVERLAYFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const PROC_SUPER_MAGIC: FsType =
FsType(libc::PROC_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const QNX4_SUPER_MAGIC: FsType =
FsType(libc::QNX4_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const QNX6_SUPER_MAGIC: FsType =
FsType(libc::QNX6_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const RDTGROUP_SUPER_MAGIC: FsType =
FsType(libc::RDTGROUP_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const REISERFS_SUPER_MAGIC: FsType =
FsType(libc::REISERFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SECURITYFS_MAGIC: FsType =
FsType(libc::SECURITYFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SELINUX_MAGIC: FsType = FsType(libc::SELINUX_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SMACK_MAGIC: FsType = FsType(libc::SMACK_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SMB_SUPER_MAGIC: FsType = FsType(libc::SMB_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const SYSFS_MAGIC: FsType = FsType(libc::SYSFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const TMPFS_MAGIC: FsType = FsType(libc::TMPFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const TRACEFS_MAGIC: FsType = FsType(libc::TRACEFS_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const UDF_SUPER_MAGIC: FsType = FsType(libc::UDF_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const USBDEVICE_SUPER_MAGIC: FsType =
FsType(libc::USBDEVICE_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const XENFS_SUPER_MAGIC: FsType =
FsType(libc::XENFS_SUPER_MAGIC as fs_type_t);
#[cfg(linux_android)]
#[allow(missing_docs)]
pub const NSFS_MAGIC: FsType = FsType(libc::NSFS_MAGIC as fs_type_t);
#[cfg(all(linux_android, not(target_env = "musl"), not(target_env = "ohos")))]
#[allow(missing_docs)]
pub const XFS_SUPER_MAGIC: FsType = FsType(libc::XFS_SUPER_MAGIC as fs_type_t);
impl Statfs {
/// Magic code defining system type
#[cfg(not(any(
target_os = "openbsd",
target_os = "dragonfly",
apple_targets,
)))]
pub fn filesystem_type(&self) -> FsType {
FsType(self.0.f_type)
}
/// Magic code defining system type
#[cfg(not(any(linux_android, target_os = "cygwin")))]
pub fn filesystem_type_name(&self) -> &str {
let c_str = unsafe { CStr::from_ptr(self.0.f_fstypename.as_ptr()) };
c_str.to_str().unwrap()
}
/// Optimal transfer block size
#[cfg(apple_targets)]
pub fn optimal_transfer_size(&self) -> i32 {
self.0.f_iosize
}
/// Optimal transfer block size
#[cfg(target_os = "openbsd")]
pub fn optimal_transfer_size(&self) -> u32 {
self.0.f_iosize
}
/// Optimal transfer block size
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn optimal_transfer_size(&self) -> u32 {
self.0.f_bsize
}
/// Optimal transfer block size
#[cfg(any(
target_os = "android",
all(target_os = "linux", target_env = "musl"),
all(target_os = "linux", target_env = "ohos")
))]
pub fn optimal_transfer_size(&self) -> libc::c_ulong {
self.0.f_bsize
}
/// Optimal transfer block size
#[cfg(all(
target_os = "linux",
not(any(
target_arch = "s390x",
target_env = "musl",
target_env = "ohos",
target_env = "uclibc"
))
))]
pub fn optimal_transfer_size(&self) -> libc::__fsword_t {
self.0.f_bsize
}
/// Optimal transfer block size
#[cfg(all(target_os = "linux", target_env = "uclibc"))]
pub fn optimal_transfer_size(&self) -> libc::c_int {
self.0.f_bsize
}
/// Optimal transfer block size
#[cfg(target_os = "dragonfly")]
pub fn optimal_transfer_size(&self) -> libc::c_long {
self.0.f_iosize
}
/// Optimal transfer block size
#[cfg(target_os = "freebsd")]
pub fn optimal_transfer_size(&self) -> u64 {
self.0.f_iosize
}
/// Size of a block
#[cfg(any(apple_targets, target_os = "openbsd"))]
pub fn block_size(&self) -> u32 {
self.0.f_bsize
}
/// Size of a block
// f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn block_size(&self) -> u32 {
self.0.f_bsize
}
/// Size of a block
// f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
#[cfg(all(target_os = "linux", target_env = "musl"))]
pub fn block_size(&self) -> libc::c_ulong {
self.0.f_bsize
}
/// Size of a block
// f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
#[cfg(all(target_os = "linux", target_env = "ohos"))]
pub fn block_size(&self) -> libc::c_ulong {
self.0.f_bsize
}
/// Size of a block
// f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
#[cfg(all(target_os = "linux", target_env = "uclibc"))]
pub fn block_size(&self) -> libc::c_int {
self.0.f_bsize
}
/// Size of a block
// f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
#[cfg(all(
target_os = "linux",
not(any(
target_arch = "s390x",
target_env = "musl",
target_env = "ohos",
target_env = "uclibc"
))
))]
pub fn block_size(&self) -> libc::__fsword_t {
self.0.f_bsize
}
/// Size of a block
#[cfg(target_os = "freebsd")]
pub fn block_size(&self) -> u64 {
self.0.f_bsize
}
/// Size of a block
#[cfg(target_os = "android")]
pub fn block_size(&self) -> libc::c_ulong {
self.0.f_bsize
}
/// Size of a block
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn block_size(&self) -> libc::c_long {
self.0.f_bsize
}
/// Get the mount flags
#[cfg(all(feature = "mount", bsd))]
#[allow(clippy::unnecessary_cast)] // Not unnecessary on all arches
pub fn flags(&self) -> MntFlags {
MntFlags::from_bits_truncate(self.0.f_flags as i32)
}
/// Get the mount flags
// The f_flags field exists on Android and Fuchsia too, but without man
// pages I can't tell if it can be cast to FsFlags.
#[cfg(target_os = "linux")]
pub fn flags(&self) -> FsFlags {
FsFlags::from_bits_truncate(self.0.f_flags as libc::c_ulong)
}
/// Maximum length of filenames
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
pub fn maximum_name_length(&self) -> u32 {
self.0.f_namemax
}
/// Maximum length of filenames
#[cfg(all(
target_os = "linux",
target_arch = "s390x",
not(target_env = "musl")
))]
pub fn maximum_name_length(&self) -> u32 {
self.0.f_namelen
}
/// Maximum length of filenames
#[cfg(all(target_os = "linux", target_env = "musl"))]
pub fn maximum_name_length(&self) -> libc::c_ulong {
self.0.f_namelen
}
/// Maximum length of filenames
#[cfg(all(target_os = "linux", target_env = "uclibc"))]
pub fn maximum_name_length(&self) -> libc::c_int {
self.0.f_namelen
}
/// Maximum length of filenames
#[cfg(all(
target_os = "linux",
not(any(
target_arch = "s390x",
target_env = "musl",
target_env = "ohos",
target_env = "uclibc"
))
))]
pub fn maximum_name_length(&self) -> libc::__fsword_t {
self.0.f_namelen
}
/// Maximum length of filenames
#[cfg(target_os = "android")]
pub fn maximum_name_length(&self) -> libc::c_ulong {
self.0.f_namelen
}
/// Total data blocks in filesystem
#[cfg(any(
apple_targets,
linux_android,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "openbsd",
))]
pub fn blocks(&self) -> u64 {
self.0.f_blocks
}
/// Total data blocks in filesystem
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks(&self) -> libc::c_long {
self.0.f_blocks
}
/// Total data blocks in filesystem
#[cfg(target_os = "emscripten")]
pub fn blocks(&self) -> u32 {
self.0.f_blocks
}
/// Free blocks in filesystem
#[cfg(any(
apple_targets,
linux_android,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "openbsd",
))]
pub fn blocks_free(&self) -> u64 {
self.0.f_bfree
}
/// Free blocks in filesystem
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks_free(&self) -> libc::c_long {
self.0.f_bfree
}
/// Free blocks in filesystem
#[cfg(target_os = "emscripten")]
pub fn blocks_free(&self) -> u32 {
self.0.f_bfree
}
/// Free blocks available to unprivileged user
#[cfg(any(apple_targets, linux_android, target_os = "fuchsia"))]
pub fn blocks_available(&self) -> u64 {
self.0.f_bavail
}
/// Free blocks available to unprivileged user
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn blocks_available(&self) -> libc::c_long {
self.0.f_bavail
}
/// Free blocks available to unprivileged user
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
pub fn blocks_available(&self) -> i64 {
self.0.f_bavail
}
/// Free blocks available to unprivileged user
#[cfg(target_os = "emscripten")]
pub fn blocks_available(&self) -> u32 {
self.0.f_bavail
}
/// Total file nodes in filesystem
#[cfg(any(
apple_targets,
linux_android,
target_os = "freebsd",
target_os = "fuchsia",
target_os = "openbsd",
))]
pub fn files(&self) -> u64 {
self.0.f_files
}
/// Total file nodes in filesystem
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn files(&self) -> libc::c_long {
self.0.f_files
}
/// Total file nodes in filesystem
#[cfg(target_os = "emscripten")]
pub fn files(&self) -> u32 {
self.0.f_files
}
/// Free file nodes in filesystem
#[cfg(any(
apple_targets,
linux_android,
target_os = "fuchsia",
target_os = "openbsd",
))]
pub fn files_free(&self) -> u64 {
self.0.f_ffree
}
/// Free file nodes in filesystem
#[cfg(any(target_os = "dragonfly", target_os = "cygwin"))]
pub fn files_free(&self) -> libc::c_long {
self.0.f_ffree
}
/// Free file nodes in filesystem
#[cfg(target_os = "freebsd")]
pub fn files_free(&self) -> i64 {
self.0.f_ffree
}
/// Free file nodes in filesystem
#[cfg(target_os = "emscripten")]
pub fn files_free(&self) -> u32 {
self.0.f_ffree
}
/// Filesystem ID
pub fn filesystem_id(&self) -> fsid_t {
self.0.f_fsid
}
}
impl Debug for Statfs {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut ds = f.debug_struct("Statfs");
#[cfg(not(target_os = "cygwin"))]
ds.field("optimal_transfer_size", &self.optimal_transfer_size());
ds.field("block_size", &self.block_size());
ds.field("blocks", &self.blocks());
ds.field("blocks_free", &self.blocks_free());
ds.field("blocks_available", &self.blocks_available());
ds.field("files", &self.files());
ds.field("files_free", &self.files_free());
ds.field("filesystem_id", &self.filesystem_id());
#[cfg(all(feature = "mount", bsd))]
ds.field("flags", &self.flags());
ds.finish()
}
}
/// Describes a mounted file system.
///
/// The result is OS-dependent. For a portable 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::<type_of_statfs>::uninit();
let res = path.with_nix_path(|path| {
LIBC_STATFS(path.as_ptr(), stat.as_mut_ptr())
})?;
Errno::result(res).map(|_| Statfs(stat.assume_init()))
}
}
/// Describes a mounted file system.
///
/// The result is OS-dependent. For a portable alternative, see
/// [`fstatvfs`](crate::sys::statvfs::fstatvfs).
///
/// # Arguments
///
/// `fd` - File descriptor of any open file within the file system to describe
pub fn fstatfs<Fd: AsFd>(fd: Fd) -> Result<Statfs> {
unsafe {
let mut stat = mem::MaybeUninit::<type_of_statfs>::uninit();
Errno::result(LIBC_FSTATFS(fd.as_fd().as_raw_fd(), stat.as_mut_ptr()))
.map(|_| Statfs(stat.assume_init()))
}
}

150
vendor/nix/src/sys/statvfs.rs vendored Normal file
View File

@@ -0,0 +1,150 @@
//! Get filesystem statistics
//!
//! See [the man pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html)
//! for more details.
use std::mem;
use std::os::unix::io::{AsFd, AsRawFd};
use libc::{self, c_ulong};
use crate::{errno::Errno, NixPath, Result};
#[cfg(not(target_os = "redox"))]
libc_bitflags!(
/// File system mount Flags
#[derive(Default)]
pub struct FsFlags: c_ulong {
/// Read Only
#[cfg(not(target_os = "haiku"))]
ST_RDONLY;
/// Do not allow the set-uid bits to have an effect
#[cfg(not(target_os = "haiku"))]
ST_NOSUID;
/// Do not interpret character or block-special devices
#[cfg(linux_android)]
ST_NODEV;
/// Do not allow execution of binaries on the filesystem
#[cfg(linux_android)]
ST_NOEXEC;
/// All IO should be done synchronously
#[cfg(linux_android)]
ST_SYNCHRONOUS;
/// Allow mandatory locks on the filesystem
#[cfg(linux_android)]
ST_MANDLOCK;
/// Write on file/directory/symlink
#[cfg(target_os = "linux")]
ST_WRITE;
/// Append-only file
#[cfg(target_os = "linux")]
ST_APPEND;
/// Immutable file
#[cfg(target_os = "linux")]
ST_IMMUTABLE;
/// Do not update access times on files
#[cfg(linux_android)]
ST_NOATIME;
/// Do not update access times on files
#[cfg(linux_android)]
ST_NODIRATIME;
/// Update access time relative to modify/change time
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_env = "musl"), not(target_env = "ohos"))))]
ST_RELATIME;
}
);
/// Wrapper around the POSIX `statvfs` struct
///
/// For more information see the [`statvfs(3)` man pages](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_statvfs.h.html).
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Statvfs(libc::statvfs);
impl Statvfs {
/// get the file system block size
pub fn block_size(&self) -> c_ulong {
self.0.f_bsize
}
/// Get the fundamental file system block size
pub fn fragment_size(&self) -> c_ulong {
self.0.f_frsize
}
/// Get the number of blocks.
///
/// Units are in units of `fragment_size()`
pub fn blocks(&self) -> libc::fsblkcnt_t {
self.0.f_blocks
}
/// Get the number of free blocks in the file system
pub fn blocks_free(&self) -> libc::fsblkcnt_t {
self.0.f_bfree
}
/// Get the number of free blocks for unprivileged users
pub fn blocks_available(&self) -> libc::fsblkcnt_t {
self.0.f_bavail
}
/// Get the total number of file inodes
pub fn files(&self) -> libc::fsfilcnt_t {
self.0.f_files
}
/// Get the number of free file inodes
pub fn files_free(&self) -> libc::fsfilcnt_t {
self.0.f_ffree
}
/// Get the number of free file inodes for unprivileged users
pub fn files_available(&self) -> libc::fsfilcnt_t {
self.0.f_favail
}
/// Get the file system id
#[cfg(not(target_os = "hurd"))]
pub fn filesystem_id(&self) -> c_ulong {
self.0.f_fsid
}
/// Get the file system id
#[cfg(target_os = "hurd")]
pub fn filesystem_id(&self) -> u64 {
self.0.f_fsid
}
/// Get the mount flags
#[cfg(not(target_os = "redox"))]
pub fn flags(&self) -> FsFlags {
FsFlags::from_bits_truncate(self.0.f_flag)
}
/// Get the maximum filename length
pub fn name_max(&self) -> c_ulong {
self.0.f_namemax
}
}
/// Return a `Statvfs` object with information about the `path`
pub fn statvfs<P: ?Sized + NixPath>(path: &P) -> Result<Statvfs> {
unsafe {
Errno::clear();
let mut stat = mem::MaybeUninit::<libc::statvfs>::uninit();
let res = path.with_nix_path(|path| {
libc::statvfs(path.as_ptr(), stat.as_mut_ptr())
})?;
Errno::result(res).map(|_| Statvfs(stat.assume_init()))
}
}
/// Return a `Statvfs` object with information about `fd`
pub fn fstatvfs<Fd: AsFd>(fd: Fd) -> Result<Statvfs> {
unsafe {
Errno::clear();
let mut stat = mem::MaybeUninit::<libc::statvfs>::uninit();
Errno::result(libc::fstatvfs(fd.as_fd().as_raw_fd(), stat.as_mut_ptr()))
.map(|_| Statvfs(stat.assume_init()))
}
}

83
vendor/nix/src/sys/sysinfo.rs vendored Normal file
View File

@@ -0,0 +1,83 @@
use libc::SI_LOAD_SHIFT;
use std::time::Duration;
use std::{cmp, mem};
use crate::errno::Errno;
use crate::Result;
/// System info structure returned by `sysinfo`.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct SysInfo(libc::sysinfo);
// The fields are c_ulong on 32-bit linux, u64 on 64-bit linux; x32's ulong is u32
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
type mem_blocks_t = u64;
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
type mem_blocks_t = libc::c_ulong;
impl SysInfo {
/// Returns the load average tuple.
///
/// The returned values represent the load average over time intervals of
/// 1, 5, and 15 minutes, respectively.
pub fn load_average(&self) -> (f64, f64, f64) {
(
self.0.loads[0] as f64 / (1 << SI_LOAD_SHIFT) as f64,
self.0.loads[1] as f64 / (1 << SI_LOAD_SHIFT) as f64,
self.0.loads[2] as f64 / (1 << SI_LOAD_SHIFT) as f64,
)
}
/// Returns the time since system boot.
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
pub fn uptime(&self) -> Duration {
// Truncate negative values to 0
Duration::from_secs(cmp::max(self.0.uptime, 0) as u64)
}
/// Current number of processes.
pub fn process_count(&self) -> u16 {
self.0.procs
}
/// Returns the amount of swap memory in Bytes.
pub fn swap_total(&self) -> u64 {
self.scale_mem(self.0.totalswap)
}
/// Returns the amount of unused swap memory in Bytes.
pub fn swap_free(&self) -> u64 {
self.scale_mem(self.0.freeswap)
}
/// Returns the total amount of installed RAM in Bytes.
pub fn ram_total(&self) -> u64 {
self.scale_mem(self.0.totalram)
}
/// Returns the amount of completely unused RAM in Bytes.
///
/// "Unused" in this context means that the RAM in neither actively used by
/// programs, nor by the operating system as disk cache or buffer. It is
/// "wasted" RAM since it currently serves no purpose.
pub fn ram_unused(&self) -> u64 {
self.scale_mem(self.0.freeram)
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn scale_mem(&self, units: mem_blocks_t) -> u64 {
units as u64 * self.0.mem_unit as u64
}
}
/// Returns system information.
///
/// [See `sysinfo(2)`](https://man7.org/linux/man-pages/man2/sysinfo.2.html).
pub fn sysinfo() -> Result<SysInfo> {
let mut info = mem::MaybeUninit::uninit();
let res = unsafe { libc::sysinfo(info.as_mut_ptr()) };
Errno::result(res).map(|_| unsafe { SysInfo(info.assume_init()) })
}

925
vendor/nix/src/sys/termios.rs vendored Normal file
View File

@@ -0,0 +1,925 @@
//! An interface for controlling asynchronous communication ports
//!
//! This interface provides a safe wrapper around the termios subsystem defined by POSIX. The
//! underlying types are all implemented in libc for most platforms and either wrapped in safer
//! types here or exported directly.
//!
//! If you are unfamiliar with the `termios` API, you should first read the
//! [API documentation](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html) and
//! then come back to understand how `nix` safely wraps it.
//!
//! It should be noted that this API incurs some runtime overhead above the base `libc` definitions.
//! As this interface is not used with high-bandwidth information, this should be fine in most
//! cases. The primary cost when using this API is that the `Termios` datatype here duplicates the
//! standard fields of the underlying `termios` struct and uses safe type wrappers for those fields.
//! This means that when crossing the FFI interface to the underlying C library, data is first
//! copied into the underlying `termios` struct, then the operation is done, and the data is copied
//! back (with additional sanity checking) into the safe wrapper types. The `termios` struct is
//! relatively small across all platforms (on the order of 32-64 bytes).
//!
//! The following examples highlight some of the API use cases such that users coming from using C
//! or reading the standard documentation will understand how to use the safe API exposed here.
//!
//! Example disabling processing of the end-of-file control character:
//!
//! ```
//! # use self::nix::sys::termios::SpecialCharacterIndices::VEOF;
//! # use self::nix::sys::termios::{_POSIX_VDISABLE, Termios};
//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
//! termios.control_chars[VEOF as usize] = _POSIX_VDISABLE;
//! ```
//!
//! The flags within `Termios` are defined as bitfields using the `bitflags` crate. This provides
//! an interface for working with bitfields that is similar to working with the raw unsigned
//! integer types but offers type safety because of the internal checking that values will always
//! be a valid combination of the defined flags.
//!
//! An example showing some of the basic operations for interacting with the control flags:
//!
//! ```
//! # use self::nix::sys::termios::{ControlFlags, Termios};
//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
//! termios.control_flags & ControlFlags::CSIZE == ControlFlags::CS5;
//! termios.control_flags |= ControlFlags::CS5;
//! ```
//!
//! # Baud rates
//!
//! This API is not consistent across platforms when it comes to `BaudRate`: Android and Linux both
//! only support the rates specified by the `BaudRate` enum through their termios API while the BSDs
//! support arbitrary baud rates as the values of the `BaudRate` enum constants are the same integer
//! value of the constant (`B9600` == `9600`). Therefore the `nix::termios` API uses the following
//! conventions:
//!
//! * `cfgetispeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
//! * `cfgetospeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
//! * `cfsetispeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
//! * `cfsetospeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
//! * `cfsetspeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
//!
//! The most common use case of specifying a baud rate using the enum will work the same across
//! platforms:
//!
//! ```rust
//! # use nix::sys::termios::{BaudRate, cfsetispeed, cfsetospeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
//! cfsetispeed(&mut t, BaudRate::B9600).unwrap();
//! cfsetospeed(&mut t, BaudRate::B9600).unwrap();
//! cfsetspeed(&mut t, BaudRate::B9600).unwrap();
//! # }
//! ```
//!
//! Additionally round-tripping baud rates is consistent across platforms:
//!
//! ```rust
//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetispeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
//! # cfsetspeed(&mut t, BaudRate::B9600).unwrap();
//! let speed = cfgetispeed(&t);
//! assert_eq!(speed, cfgetospeed(&t));
//! cfsetispeed(&mut t, speed).unwrap();
//! # }
//! ```
//!
//! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`:
//!
#![cfg_attr(bsd, doc = " ```rust,ignore")]
#![cfg_attr(not(bsd), doc = " ```rust")]
//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
//! # cfsetspeed(&mut t, BaudRate::B9600);
//! assert_eq!(cfgetispeed(&t), BaudRate::B9600);
//! assert_eq!(cfgetospeed(&t), BaudRate::B9600);
//! # }
//! ```
//!
//! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s:
//!
#![cfg_attr(bsd, doc = " ```rust")]
#![cfg_attr(not(bsd), doc = " ```rust,ignore")]
//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
//! # cfsetspeed(&mut t, 9600u32);
//! assert_eq!(cfgetispeed(&t), 9600u32);
//! assert_eq!(cfgetospeed(&t), 9600u32);
//! # }
//! ```
//!
//! It's trivial to convert from a `BaudRate` to a `u32` on BSDs:
//!
#![cfg_attr(bsd, doc = " ```rust")]
#![cfg_attr(not(bsd), doc = " ```rust,ignore")]
//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
//! # cfsetspeed(&mut t, 9600u32);
//! assert_eq!(cfgetispeed(&t), BaudRate::B9600.into());
//! assert_eq!(u32::from(BaudRate::B9600), 9600u32);
//! # }
//! ```
//!
//! And on BSDs you can specify arbitrary baud rates (**note** this depends on hardware support)
//! by specifying baud rates directly using `u32`s:
//!
#![cfg_attr(bsd, doc = " ```rust")]
#![cfg_attr(not(bsd), doc = " ```rust,ignore")]
//! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios};
//! # fn main() {
//! # let mut t: Termios = unsafe { std::mem::zeroed() };
//! cfsetispeed(&mut t, 9600u32);
//! cfsetospeed(&mut t, 9600u32);
//! cfsetspeed(&mut t, 9600u32);
//! # }
//! ```
use crate::errno::Errno;
use crate::Result;
use cfg_if::cfg_if;
use libc::{self, c_int, tcflag_t};
use std::cell::{Ref, RefCell};
use std::convert::From;
use std::mem;
use std::os::unix::io::{AsFd, AsRawFd};
#[cfg(feature = "process")]
use crate::unistd::Pid;
/// Stores settings for the termios API
///
/// This is a wrapper around the `libc::termios` struct that provides a safe interface for the
/// standard fields. The only safe way to obtain an instance of this struct is to extract it from
/// an open port using `tcgetattr()`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Termios {
inner: RefCell<libc::termios>,
/// Input mode flags (see `termios.c_iflag` documentation)
pub input_flags: InputFlags,
/// Output mode flags (see `termios.c_oflag` documentation)
pub output_flags: OutputFlags,
/// Control mode flags (see `termios.c_cflag` documentation)
pub control_flags: ControlFlags,
/// Local mode flags (see `termios.c_lflag` documentation)
pub local_flags: LocalFlags,
/// Control characters (see `termios.c_cc` documentation)
pub control_chars: [libc::cc_t; NCCS],
/// Line discipline (see `termios.c_line` documentation)
#[cfg(linux_android)]
pub line_discipline: libc::cc_t,
/// Line discipline (see `termios.c_line` documentation)
#[cfg(target_os = "haiku")]
pub line_discipline: libc::c_char,
}
impl Termios {
/// Exposes an immutable reference to the underlying `libc::termios` data structure.
///
/// This is not part of `nix`'s public API because it requires additional work to maintain type
/// safety.
pub(crate) fn get_libc_termios(&self) -> Ref<libc::termios> {
{
let mut termios = self.inner.borrow_mut();
termios.c_iflag = self.input_flags.bits();
termios.c_oflag = self.output_flags.bits();
termios.c_cflag = self.control_flags.bits();
termios.c_lflag = self.local_flags.bits();
termios.c_cc = self.control_chars;
#[cfg(any(linux_android, target_os = "haiku"))]
{
termios.c_line = self.line_discipline;
}
}
self.inner.borrow()
}
/// Exposes the inner `libc::termios` datastore within `Termios`.
///
/// This is unsafe because if this is used to modify the inner `libc::termios` struct, it will
/// not automatically update the safe wrapper type around it. In this case it should also be
/// paired with a call to `update_wrapper()` so that the wrapper-type and internal
/// representation stay consistent.
pub(crate) unsafe fn get_libc_termios_mut(&mut self) -> *mut libc::termios {
{
let mut termios = self.inner.borrow_mut();
termios.c_iflag = self.input_flags.bits();
termios.c_oflag = self.output_flags.bits();
termios.c_cflag = self.control_flags.bits();
termios.c_lflag = self.local_flags.bits();
termios.c_cc = self.control_chars;
#[cfg(any(linux_android, target_os = "haiku"))]
{
termios.c_line = self.line_discipline;
}
}
self.inner.as_ptr()
}
/// Updates the wrapper values from the internal `libc::termios` data structure.
pub(crate) fn update_wrapper(&mut self) {
let termios = *self.inner.borrow_mut();
self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag);
self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag);
self.control_flags = ControlFlags::from_bits_retain(termios.c_cflag);
self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag);
self.control_chars = termios.c_cc;
#[cfg(any(linux_android, target_os = "haiku"))]
{
self.line_discipline = termios.c_line;
}
}
}
impl From<libc::termios> for Termios {
fn from(termios: libc::termios) -> Self {
Termios {
inner: RefCell::new(termios),
input_flags: InputFlags::from_bits_truncate(termios.c_iflag),
output_flags: OutputFlags::from_bits_truncate(termios.c_oflag),
control_flags: ControlFlags::from_bits_truncate(termios.c_cflag),
local_flags: LocalFlags::from_bits_truncate(termios.c_lflag),
control_chars: termios.c_cc,
#[cfg(any(linux_android, target_os = "haiku"))]
line_discipline: termios.c_line,
}
}
}
impl From<Termios> for libc::termios {
fn from(termios: Termios) -> Self {
termios.inner.into_inner()
}
}
libc_enum! {
/// Baud rates supported by the system.
///
/// For the BSDs, arbitrary baud rates can be specified by using `u32`s directly instead of this
/// enum.
///
/// B0 is special and will disable the port.
#[cfg_attr(target_os = "haiku", repr(u8))]
#[cfg_attr(target_os = "hurd", repr(i32))]
#[cfg_attr(all(apple_targets, target_pointer_width = "64"), repr(u64))]
#[cfg_attr(all(
not(all(apple_targets, target_pointer_width = "64")),
not(target_os = "haiku"),
not(target_os = "hurd")
), repr(u32))]
#[non_exhaustive]
pub enum BaudRate {
B0,
B50,
B75,
B110,
B134,
B150,
B200,
B300,
B600,
B1200,
B1800,
B2400,
B4800,
#[cfg(bsd)]
B7200,
B9600,
#[cfg(bsd)]
B14400,
B19200,
#[cfg(bsd)]
B28800,
B38400,
#[cfg(not(target_os = "aix"))]
B57600,
#[cfg(bsd)]
B76800,
#[cfg(not(target_os = "aix"))]
B115200,
#[cfg(solarish)]
B153600,
#[cfg(not(target_os = "aix"))]
B230400,
#[cfg(solarish)]
B307200,
#[cfg(any(linux_android,
solarish,
target_os = "freebsd",
target_os = "netbsd"))]
B460800,
#[cfg(linux_android)]
B500000,
#[cfg(linux_android)]
B576000,
#[cfg(any(linux_android,
solarish,
target_os = "freebsd",
target_os = "netbsd"))]
B921600,
#[cfg(linux_android)]
B1000000,
#[cfg(linux_android)]
B1152000,
#[cfg(linux_android)]
B1500000,
#[cfg(linux_android)]
B2000000,
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
B2500000,
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
B3000000,
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
B3500000,
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
B4000000,
}
impl TryFrom<libc::speed_t>
}
#[cfg(bsd)]
impl From<BaudRate> for u32 {
fn from(b: BaudRate) -> u32 {
b as u32
}
}
#[cfg(target_os = "haiku")]
impl From<BaudRate> for u8 {
fn from(b: BaudRate) -> u8 {
b as u8
}
}
// TODO: Add TCSASOFT, which will require treating this as a bitfield.
libc_enum! {
/// Specify when a port configuration change should occur.
///
/// Used as an argument to `tcsetattr()`
#[repr(i32)]
#[non_exhaustive]
pub enum SetArg {
/// The change will occur immediately
TCSANOW,
/// The change occurs after all output has been written
TCSADRAIN,
/// Same as `TCSADRAIN`, but will also flush the input buffer
TCSAFLUSH,
}
}
libc_enum! {
/// Specify a combination of the input and output buffers to flush
///
/// Used as an argument to `tcflush()`.
#[repr(i32)]
#[non_exhaustive]
pub enum FlushArg {
/// Flush data that was received but not read
TCIFLUSH,
/// Flush data written but not transmitted
TCOFLUSH,
/// Flush both received data not read and written data not transmitted
TCIOFLUSH,
}
}
libc_enum! {
/// Specify how transmission flow should be altered
///
/// Used as an argument to `tcflow()`.
#[repr(i32)]
#[non_exhaustive]
pub enum FlowArg {
/// Suspend transmission
TCOOFF,
/// Resume transmission
TCOON,
/// Transmit a STOP character, which should disable a connected terminal device
TCIOFF,
/// Transmit a START character, which should re-enable a connected terminal device
TCION,
}
}
// TODO: Make this usable directly as a slice index.
libc_enum! {
/// Indices into the `termios.c_cc` array for special characters.
#[repr(usize)]
#[non_exhaustive]
pub enum SpecialCharacterIndices {
#[cfg(not(any(target_os = "aix", target_os = "haiku")))]
VDISCARD,
#[cfg(any(bsd,
solarish,
target_os = "aix"))]
VDSUSP,
VEOF,
VEOL,
VEOL2,
VERASE,
#[cfg(any(freebsdlike, target_os = "illumos"))]
VERASE2,
VINTR,
VKILL,
#[cfg(not(target_os = "haiku"))]
VLNEXT,
#[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
solarish, target_os = "aix", target_os = "haiku")))]
VMIN,
VQUIT,
#[cfg(not(target_os = "haiku"))]
VREPRINT,
VSTART,
#[cfg(any(bsd, target_os = "illumos"))]
VSTATUS,
VSTOP,
VSUSP,
#[cfg(target_os = "linux")]
VSWTC,
#[cfg(any(solarish, target_os = "haiku"))]
VSWTCH,
#[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
solarish, target_os = "aix", target_os = "haiku")))]
VTIME,
#[cfg(not(any(target_os = "aix", target_os = "haiku")))]
VWERASE,
#[cfg(target_os = "dragonfly")]
VCHECKPT,
}
}
#[cfg(any(
all(target_os = "linux", target_arch = "sparc64"),
solarish,
target_os = "aix",
target_os = "haiku",
))]
impl SpecialCharacterIndices {
pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF;
pub const VTIME: SpecialCharacterIndices = SpecialCharacterIndices::VEOL;
}
pub use libc::NCCS;
#[cfg(any(bsd, linux_android, target_os = "aix", target_os = "solaris"))]
pub use libc::_POSIX_VDISABLE;
libc_bitflags! {
/// Flags for configuring the input mode of a terminal
pub struct InputFlags: tcflag_t {
IGNBRK;
BRKINT;
IGNPAR;
PARMRK;
INPCK;
ISTRIP;
INLCR;
IGNCR;
ICRNL;
IXON;
IXOFF;
#[cfg(not(target_os = "redox"))]
IXANY;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
IMAXBEL;
#[cfg(any(linux_android, apple_targets))]
IUTF8;
}
}
libc_bitflags! {
/// Flags for configuring the output mode of a terminal
pub struct OutputFlags: tcflag_t {
OPOST;
#[cfg(any(linux_android,
target_os = "haiku",
target_os = "openbsd"))]
OLCUC;
ONLCR;
OCRNL as tcflag_t;
ONOCR as tcflag_t;
ONLRET as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
OFDEL as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
NL0 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
NL1 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
CR0 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
CR1 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
CR2 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
CR3 as tcflag_t;
#[cfg(any(linux_android,
target_os = "freebsd",
target_os = "haiku",
apple_targets))]
TAB0 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
TAB1 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
TAB2 as tcflag_t;
#[cfg(any(linux_android,
target_os = "freebsd",
target_os = "haiku",
apple_targets))]
TAB3 as tcflag_t;
#[cfg(linux_android)]
XTABS;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
BS0 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
BS1 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
VT0 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
VT1 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
FF0 as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
FF1 as tcflag_t;
#[cfg(bsd)]
OXTABS;
#[cfg(bsd)]
ONOEOT as tcflag_t;
// Bitmasks for use with OutputFlags to select specific settings
// These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
// is resolved.
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
NLDLY as tcflag_t; // FIXME: Datatype needs to be corrected in libc for mac
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
CRDLY as tcflag_t;
#[cfg(any(linux_android,
target_os = "freebsd",
target_os = "haiku",
apple_targets))]
TABDLY as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
BSDLY as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
VTDLY as tcflag_t;
#[cfg(any(linux_android,
target_os = "haiku",
apple_targets))]
FFDLY as tcflag_t;
}
}
libc_bitflags! {
/// Flags for setting the control mode of a terminal
pub struct ControlFlags: tcflag_t {
#[cfg(bsd)]
CIGNORE;
CS5;
CS6;
CS7;
CS8;
CSTOPB;
CREAD;
PARENB;
PARODD;
HUPCL;
CLOCAL;
#[cfg(not(any(target_os = "redox", target_os = "aix")))]
CRTSCTS;
#[cfg(linux_android)]
CBAUD;
#[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))]
CMSPAR;
#[cfg(any(target_os = "android",
all(target_os = "linux",
not(any(target_arch = "powerpc", target_arch = "powerpc64")))))]
CIBAUD;
#[cfg(linux_android)]
CBAUDEX;
#[cfg(bsd)]
MDMBUF;
#[cfg(netbsdlike)]
CHWFLOW;
#[cfg(any(freebsdlike, netbsdlike))]
CCTS_OFLOW;
#[cfg(any(freebsdlike, netbsdlike))]
CRTS_IFLOW;
#[cfg(freebsdlike)]
CDTR_IFLOW;
#[cfg(freebsdlike)]
CDSR_OFLOW;
#[cfg(freebsdlike)]
CCAR_OFLOW;
// Bitmasks for use with ControlFlags to select specific settings
// These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
// is resolved.
CSIZE;
}
}
libc_bitflags! {
/// Flags for setting any local modes
pub struct LocalFlags: tcflag_t {
#[cfg(not(target_os = "redox"))]
ECHOKE;
ECHOE;
ECHOK;
ECHO;
ECHONL;
#[cfg(not(any(target_os = "redox", target_os = "cygwin")))]
ECHOPRT;
#[cfg(not(target_os = "redox"))]
ECHOCTL;
ISIG;
ICANON;
#[cfg(bsd)]
ALTWERASE;
IEXTEN;
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "aix", target_os = "cygwin")))]
EXTPROC;
TOSTOP;
#[cfg(not(target_os = "redox"))]
FLUSHO;
#[cfg(bsd)]
NOKERNINFO;
#[cfg(not(any(target_os = "redox", target_os = "cygwin")))]
PENDIN;
NOFLSH;
}
}
cfg_if! {
if #[cfg(bsd)] {
/// Get input baud rate (see
/// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
///
/// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
pub fn cfgetispeed(termios: &Termios) -> u32 {
let inner_termios = termios.get_libc_termios();
unsafe { libc::cfgetispeed(&*inner_termios) as u32 }
}
/// Get output baud rate (see
/// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
///
/// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
pub fn cfgetospeed(termios: &Termios) -> u32 {
let inner_termios = termios.get_libc_termios();
unsafe { libc::cfgetospeed(&*inner_termios) as u32 }
}
/// Set input baud rate (see
/// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
///
/// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
pub fn cfsetispeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
let res = unsafe { libc::cfsetispeed(inner_termios, baud.into() as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
/// Set output baud rate (see
/// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
///
/// `cfsetospeed()` sets the output baud rate in the given termios structure.
pub fn cfsetospeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
let res = unsafe { libc::cfsetospeed(inner_termios, baud.into() as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
/// Set both the input and output baud rates (see
/// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
///
/// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
/// this is part of the 4.4BSD standard and not part of POSIX.
pub fn cfsetspeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
let res = unsafe { libc::cfsetspeed(inner_termios, baud.into() as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
} else {
use std::convert::TryInto;
/// Get input baud rate (see
/// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
///
/// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
pub fn cfgetispeed(termios: &Termios) -> BaudRate {
let inner_termios = termios.get_libc_termios();
unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap()
}
/// Get output baud rate (see
/// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
///
/// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
pub fn cfgetospeed(termios: &Termios) -> BaudRate {
let inner_termios = termios.get_libc_termios();
unsafe { libc::cfgetospeed(&*inner_termios) }.try_into().unwrap()
}
/// Set input baud rate (see
/// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
///
/// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
/// Set output baud rate (see
/// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
///
/// `cfsetospeed()` sets the output baud rate in the given `Termios` structure.
pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
/// Set both the input and output baud rates (see
/// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
///
/// `cfsetspeed()` sets the input and output baud rate in the given `Termios` structure. Note that
/// this is part of the 4.4BSD standard and not part of POSIX.
#[cfg(not(target_os = "haiku"))]
pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
}
}
/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see
/// [termios(3)](https://man7.org/linux/man-pages/man3/termios.3.html)).
///
/// `cfmakeraw()` configures the termios structure such that input is available character-by-
/// character, echoing is disabled, and all special input and output processing is disabled. Note
/// that this is a non-standard function, but is available on Linux and BSDs.
pub fn cfmakeraw(termios: &mut Termios) {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
unsafe {
libc::cfmakeraw(inner_termios);
}
termios.update_wrapper();
}
/// Configures the port to "sane" mode (like the configuration of a newly created terminal) (see
/// [tcsetattr(3)](https://www.freebsd.org/cgi/man.cgi?query=tcsetattr)).
///
/// Note that this is a non-standard function, available on FreeBSD.
#[cfg(target_os = "freebsd")]
pub fn cfmakesane(termios: &mut Termios) {
let inner_termios = unsafe { termios.get_libc_termios_mut() };
unsafe {
libc::cfmakesane(inner_termios);
}
termios.update_wrapper();
}
/// Return the configuration of a port
/// [tcgetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)).
///
/// `tcgetattr()` returns a `Termios` structure with the current configuration for a port. Modifying
/// this structure *will not* reconfigure the port, instead the modifications should be done to
/// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`.
pub fn tcgetattr<Fd: AsFd>(fd: Fd) -> Result<Termios> {
let mut termios = mem::MaybeUninit::uninit();
let res = unsafe {
libc::tcgetattr(fd.as_fd().as_raw_fd(), termios.as_mut_ptr())
};
Errno::result(res)?;
unsafe { Ok(termios.assume_init().into()) }
}
/// Set the configuration for a terminal (see
/// [tcsetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)).
///
/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change
/// takes affect at a time specified by `actions`. Note that this function may return success if
/// *any* of the parameters were successfully set, not only if all were set successfully.
pub fn tcsetattr<Fd: AsFd>(
fd: Fd,
actions: SetArg,
termios: &Termios,
) -> Result<()> {
let inner_termios = termios.get_libc_termios();
Errno::result(unsafe {
libc::tcsetattr(
fd.as_fd().as_raw_fd(),
actions as c_int,
&*inner_termios,
)
})
.map(drop)
}
/// Block until all output data is written (see
/// [tcdrain(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
pub fn tcdrain<Fd: AsFd>(fd: Fd) -> Result<()> {
Errno::result(unsafe { libc::tcdrain(fd.as_fd().as_raw_fd()) }).map(drop)
}
/// Suspend or resume the transmission or reception of data (see
/// [tcflow(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)).
///
/// `tcflow()` suspends of resumes the transmission or reception of data for the given port
/// depending on the value of `action`.
pub fn tcflow<Fd: AsFd>(fd: Fd, action: FlowArg) -> Result<()> {
Errno::result(unsafe {
libc::tcflow(fd.as_fd().as_raw_fd(), action as c_int)
})
.map(drop)
}
/// Discard data in the output or input queue (see
/// [tcflush(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)).
///
/// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both
/// depending on the value of `action`.
pub fn tcflush<Fd: AsFd>(fd: Fd, action: FlushArg) -> Result<()> {
Errno::result(unsafe {
libc::tcflush(fd.as_fd().as_raw_fd(), action as c_int)
})
.map(drop)
}
/// Send a break for a specific duration (see
/// [tcsendbreak(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)).
///
/// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream
/// of zero-valued bits for an implementation-defined duration.
pub fn tcsendbreak<Fd: AsFd>(fd: Fd, duration: c_int) -> Result<()> {
Errno::result(unsafe {
libc::tcsendbreak(fd.as_fd().as_raw_fd(), duration)
})
.map(drop)
}
feature! {
#![feature = "process"]
/// Get the session controlled by the given terminal (see
/// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
pub fn tcgetsid<Fd: AsFd>(fd: Fd) -> Result<Pid> {
let res = unsafe { libc::tcgetsid(fd.as_fd().as_raw_fd()) };
Errno::result(res).map(Pid::from_raw)
}
}

752
vendor/nix/src/sys/time.rs vendored Normal file
View File

@@ -0,0 +1,752 @@
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
pub use libc::{suseconds_t, time_t};
use libc::{timespec, timeval};
use std::time::Duration;
use std::{cmp, fmt, ops};
const fn zero_init_timespec() -> timespec {
// `std::mem::MaybeUninit::zeroed()` is not yet a const fn
// (https://github.com/rust-lang/rust/issues/91850) so we will instead initialize an array of
// the appropriate size to zero and then transmute it to a timespec value.
unsafe { std::mem::transmute([0u8; std::mem::size_of::<timespec>()]) }
}
#[cfg(any(
all(feature = "time", any(target_os = "android", target_os = "linux")),
all(
any(
target_os = "freebsd",
solarish,
target_os = "linux",
target_os = "netbsd"
),
feature = "time",
feature = "signal"
)
))]
pub(crate) mod timer {
use crate::sys::time::{zero_init_timespec, TimeSpec};
use bitflags::bitflags;
#[derive(Debug, Clone, Copy)]
pub(crate) struct TimerSpec(libc::itimerspec);
impl TimerSpec {
pub const fn none() -> Self {
Self(libc::itimerspec {
it_interval: zero_init_timespec(),
it_value: zero_init_timespec(),
})
}
}
impl AsMut<libc::itimerspec> for TimerSpec {
fn as_mut(&mut self) -> &mut libc::itimerspec {
&mut self.0
}
}
impl AsRef<libc::itimerspec> for TimerSpec {
fn as_ref(&self) -> &libc::itimerspec {
&self.0
}
}
impl From<Expiration> for TimerSpec {
fn from(expiration: Expiration) -> TimerSpec {
match expiration {
Expiration::OneShot(t) => TimerSpec(libc::itimerspec {
it_interval: zero_init_timespec(),
it_value: *t.as_ref(),
}),
Expiration::IntervalDelayed(start, interval) => {
TimerSpec(libc::itimerspec {
it_interval: *interval.as_ref(),
it_value: *start.as_ref(),
})
}
Expiration::Interval(t) => TimerSpec(libc::itimerspec {
it_interval: *t.as_ref(),
it_value: *t.as_ref(),
}),
}
}
}
/// An enumeration allowing the definition of the expiration time of an alarm,
/// recurring or not.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Expiration {
/// Alarm will trigger once after the time given in `TimeSpec`
OneShot(TimeSpec),
/// Alarm will trigger after a specified delay and then every interval of
/// time.
IntervalDelayed(TimeSpec, TimeSpec),
/// Alarm will trigger every specified interval of time.
Interval(TimeSpec),
}
#[cfg(linux_android)]
bitflags! {
/// Flags that are used for arming the timer.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TimerSetTimeFlags: libc::c_int {
const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME;
const TFD_TIMER_CANCEL_ON_SET = libc::TFD_TIMER_CANCEL_ON_SET;
}
}
#[cfg(any(freebsdlike, target_os = "netbsd", solarish))]
bitflags! {
/// Flags that are used for arming the timer.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TimerSetTimeFlags: libc::c_int {
const TFD_TIMER_ABSTIME = libc::TIMER_ABSTIME;
}
}
impl From<TimerSpec> for Expiration {
fn from(timerspec: TimerSpec) -> Expiration {
match timerspec {
TimerSpec(libc::itimerspec {
it_interval:
libc::timespec {
tv_sec: 0,
tv_nsec: 0,
..
},
it_value: ts,
}) => Expiration::OneShot(ts.into()),
TimerSpec(libc::itimerspec {
it_interval: int_ts,
it_value: val_ts,
}) => {
if (int_ts.tv_sec == val_ts.tv_sec)
&& (int_ts.tv_nsec == val_ts.tv_nsec)
{
Expiration::Interval(int_ts.into())
} else {
Expiration::IntervalDelayed(
val_ts.into(),
int_ts.into(),
)
}
}
}
}
}
}
pub trait TimeValLike: Sized {
#[inline]
fn zero() -> Self {
Self::seconds(0)
}
#[inline]
fn hours(hours: i64) -> Self {
let secs = hours
.checked_mul(SECS_PER_HOUR)
.expect("TimeValLike::hours ouf of bounds");
Self::seconds(secs)
}
#[inline]
fn minutes(minutes: i64) -> Self {
let secs = minutes
.checked_mul(SECS_PER_MINUTE)
.expect("TimeValLike::minutes out of bounds");
Self::seconds(secs)
}
fn seconds(seconds: i64) -> Self;
fn milliseconds(milliseconds: i64) -> Self;
fn microseconds(microseconds: i64) -> Self;
fn nanoseconds(nanoseconds: i64) -> Self;
#[inline]
fn num_hours(&self) -> i64 {
self.num_seconds() / 3600
}
#[inline]
fn num_minutes(&self) -> i64 {
self.num_seconds() / 60
}
fn num_seconds(&self) -> i64;
fn num_milliseconds(&self) -> i64;
fn num_microseconds(&self) -> i64;
fn num_nanoseconds(&self) -> i64;
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct TimeSpec(timespec);
const NANOS_PER_SEC: i64 = 1_000_000_000;
const SECS_PER_MINUTE: i64 = 60;
const SECS_PER_HOUR: i64 = 3600;
#[cfg(target_pointer_width = "64")]
const TS_MAX_SECONDS: i64 = (i64::MAX / NANOS_PER_SEC) - 1;
#[cfg(target_pointer_width = "32")]
const TS_MAX_SECONDS: i64 = isize::MAX as i64;
const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS;
// x32 compatibility
// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
type timespec_tv_nsec_t = i64;
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
type timespec_tv_nsec_t = libc::c_long;
impl From<timespec> for TimeSpec {
fn from(ts: timespec) -> Self {
Self(ts)
}
}
impl From<Duration> for TimeSpec {
fn from(duration: Duration) -> Self {
Self::from_duration(duration)
}
}
impl From<TimeSpec> for Duration {
fn from(timespec: TimeSpec) -> Self {
Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32)
}
}
impl AsRef<timespec> for TimeSpec {
fn as_ref(&self) -> &timespec {
&self.0
}
}
impl AsMut<timespec> for TimeSpec {
fn as_mut(&mut self) -> &mut timespec {
&mut self.0
}
}
impl Ord for TimeSpec {
// The implementation of cmp is simplified by assuming that the struct is
// normalized. That is, tv_nsec must always be within [0, 1_000_000_000)
fn cmp(&self, other: &TimeSpec) -> cmp::Ordering {
if self.tv_sec() == other.tv_sec() {
self.tv_nsec().cmp(&other.tv_nsec())
} else {
self.tv_sec().cmp(&other.tv_sec())
}
}
}
impl PartialOrd for TimeSpec {
fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl TimeValLike for TimeSpec {
#[inline]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
fn seconds(seconds: i64) -> TimeSpec {
assert!(
(TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds),
"TimeSpec out of bounds; seconds={seconds}",
);
let mut ts = zero_init_timespec();
ts.tv_sec = seconds as time_t;
TimeSpec(ts)
}
#[inline]
fn milliseconds(milliseconds: i64) -> TimeSpec {
let nanoseconds = milliseconds
.checked_mul(1_000_000)
.expect("TimeSpec::milliseconds out of bounds");
TimeSpec::nanoseconds(nanoseconds)
}
/// Makes a new `TimeSpec` with given number of microseconds.
#[inline]
fn microseconds(microseconds: i64) -> TimeSpec {
let nanoseconds = microseconds
.checked_mul(1_000)
.expect("TimeSpec::milliseconds out of bounds");
TimeSpec::nanoseconds(nanoseconds)
}
/// Makes a new `TimeSpec` with given number of nanoseconds.
#[inline]
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
fn nanoseconds(nanoseconds: i64) -> TimeSpec {
let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
assert!(
(TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs),
"TimeSpec out of bounds"
);
let mut ts = zero_init_timespec();
ts.tv_sec = secs as time_t;
ts.tv_nsec = nanos as timespec_tv_nsec_t;
TimeSpec(ts)
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn num_seconds(&self) -> i64 {
if self.tv_sec() < 0 && self.tv_nsec() > 0 {
(self.tv_sec() + 1) as i64
} else {
self.tv_sec() as i64
}
}
fn num_milliseconds(&self) -> i64 {
self.num_nanoseconds() / 1_000_000
}
fn num_microseconds(&self) -> i64 {
self.num_nanoseconds() / 1_000
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn num_nanoseconds(&self) -> i64 {
let secs = self.num_seconds() * 1_000_000_000;
let nsec = self.nanos_mod_sec();
secs + nsec as i64
}
}
impl TimeSpec {
/// Leave the timestamp unchanged.
#[cfg(not(target_os = "redox"))]
// At the time of writing this PR, redox does not support this feature
pub const UTIME_OMIT: TimeSpec =
TimeSpec::new(0, libc::UTIME_OMIT as timespec_tv_nsec_t);
/// Update the timestamp to `Now`
// At the time of writing this PR, redox does not support this feature
#[cfg(not(target_os = "redox"))]
pub const UTIME_NOW: TimeSpec =
TimeSpec::new(0, libc::UTIME_NOW as timespec_tv_nsec_t);
/// Construct a new `TimeSpec` from its components
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self {
let mut ts = zero_init_timespec();
ts.tv_sec = seconds;
ts.tv_nsec = nanoseconds;
Self(ts)
}
fn nanos_mod_sec(&self) -> timespec_tv_nsec_t {
if self.tv_sec() < 0 && self.tv_nsec() > 0 {
self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t
} else {
self.tv_nsec()
}
}
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn tv_sec(&self) -> time_t {
self.0.tv_sec
}
pub const fn tv_nsec(&self) -> timespec_tv_nsec_t {
self.0.tv_nsec
}
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
pub const fn from_duration(duration: Duration) -> Self {
let mut ts = zero_init_timespec();
ts.tv_sec = duration.as_secs() as time_t;
ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t;
TimeSpec(ts)
}
pub const fn from_timespec(timespec: timespec) -> Self {
Self(timespec)
}
}
impl ops::Neg for TimeSpec {
type Output = TimeSpec;
fn neg(self) -> TimeSpec {
TimeSpec::nanoseconds(-self.num_nanoseconds())
}
}
impl ops::Add for TimeSpec {
type Output = TimeSpec;
fn add(self, rhs: TimeSpec) -> TimeSpec {
TimeSpec::nanoseconds(self.num_nanoseconds() + rhs.num_nanoseconds())
}
}
impl ops::Sub for TimeSpec {
type Output = TimeSpec;
fn sub(self, rhs: TimeSpec) -> TimeSpec {
TimeSpec::nanoseconds(self.num_nanoseconds() - rhs.num_nanoseconds())
}
}
impl ops::Mul<i32> for TimeSpec {
type Output = TimeSpec;
fn mul(self, rhs: i32) -> TimeSpec {
let usec = self
.num_nanoseconds()
.checked_mul(i64::from(rhs))
.expect("TimeSpec multiply out of bounds");
TimeSpec::nanoseconds(usec)
}
}
impl ops::Div<i32> for TimeSpec {
type Output = TimeSpec;
fn div(self, rhs: i32) -> TimeSpec {
let usec = self.num_nanoseconds() / i64::from(rhs);
TimeSpec::nanoseconds(usec)
}
}
impl fmt::Display for TimeSpec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (abs, sign) = if self.tv_sec() < 0 {
(-*self, "-")
} else {
(*self, "")
};
let sec = abs.tv_sec();
write!(f, "{sign}")?;
if abs.tv_nsec() == 0 {
if sec == 1 {
write!(f, "1 second")?;
} else {
write!(f, "{sec} seconds")?;
}
} else if abs.tv_nsec() % 1_000_000 == 0 {
write!(f, "{sec}.{:03} seconds", abs.tv_nsec() / 1_000_000)?;
} else if abs.tv_nsec() % 1_000 == 0 {
write!(f, "{sec}.{:06} seconds", abs.tv_nsec() / 1_000)?;
} else {
write!(f, "{sec}.{:09} seconds", abs.tv_nsec())?;
}
Ok(())
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct TimeVal(timeval);
const MICROS_PER_SEC: i64 = 1_000_000;
#[cfg(target_pointer_width = "64")]
const TV_MAX_SECONDS: i64 = (i64::MAX / MICROS_PER_SEC) - 1;
#[cfg(target_pointer_width = "32")]
const TV_MAX_SECONDS: i64 = isize::MAX as i64;
const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS;
impl AsRef<timeval> for TimeVal {
fn as_ref(&self) -> &timeval {
&self.0
}
}
impl AsMut<timeval> for TimeVal {
fn as_mut(&mut self) -> &mut timeval {
&mut self.0
}
}
impl Ord for TimeVal {
// The implementation of cmp is simplified by assuming that the struct is
// normalized. That is, tv_usec must always be within [0, 1_000_000)
fn cmp(&self, other: &TimeVal) -> cmp::Ordering {
if self.tv_sec() == other.tv_sec() {
self.tv_usec().cmp(&other.tv_usec())
} else {
self.tv_sec().cmp(&other.tv_sec())
}
}
}
impl PartialOrd for TimeVal {
fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl TimeValLike for TimeVal {
#[inline]
fn seconds(seconds: i64) -> TimeVal {
assert!(
(TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds),
"TimeVal out of bounds; seconds={seconds}"
);
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
TimeVal(timeval {
tv_sec: seconds as time_t,
tv_usec: 0,
})
}
#[inline]
fn milliseconds(milliseconds: i64) -> TimeVal {
let microseconds = milliseconds
.checked_mul(1_000)
.expect("TimeVal::milliseconds out of bounds");
TimeVal::microseconds(microseconds)
}
/// Makes a new `TimeVal` with given number of microseconds.
#[inline]
fn microseconds(microseconds: i64) -> TimeVal {
let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
assert!(
(TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
"TimeVal out of bounds"
);
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
TimeVal(timeval {
tv_sec: secs as time_t,
tv_usec: micros as suseconds_t,
})
}
/// Makes a new `TimeVal` with given number of nanoseconds. Some precision
/// will be lost
#[inline]
fn nanoseconds(nanoseconds: i64) -> TimeVal {
let microseconds = nanoseconds / 1000;
let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
assert!(
(TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
"TimeVal out of bounds"
);
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)]
// https://github.com/rust-lang/libc/issues/1848
TimeVal(timeval {
tv_sec: secs as time_t,
tv_usec: micros as suseconds_t,
})
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn num_seconds(&self) -> i64 {
if self.tv_sec() < 0 && self.tv_usec() > 0 {
(self.tv_sec() + 1) as i64
} else {
self.tv_sec() as i64
}
}
fn num_milliseconds(&self) -> i64 {
self.num_microseconds() / 1_000
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn num_microseconds(&self) -> i64 {
let secs = self.num_seconds() * 1_000_000;
let usec = self.micros_mod_sec();
secs + usec as i64
}
fn num_nanoseconds(&self) -> i64 {
self.num_microseconds() * 1_000
}
}
impl TimeVal {
/// Construct a new `TimeVal` from its components
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn new(seconds: time_t, microseconds: suseconds_t) -> Self {
Self(timeval {
tv_sec: seconds,
tv_usec: microseconds,
})
}
fn micros_mod_sec(&self) -> suseconds_t {
if self.tv_sec() < 0 && self.tv_usec() > 0 {
self.tv_usec() - MICROS_PER_SEC as suseconds_t
} else {
self.tv_usec()
}
}
#[cfg_attr(
any(target_env = "musl", target_env = "ohos"),
allow(deprecated)
)] // https://github.com/rust-lang/libc/issues/1848
pub const fn tv_sec(&self) -> time_t {
self.0.tv_sec
}
pub const fn tv_usec(&self) -> suseconds_t {
self.0.tv_usec
}
}
impl ops::Neg for TimeVal {
type Output = TimeVal;
fn neg(self) -> TimeVal {
TimeVal::microseconds(-self.num_microseconds())
}
}
impl ops::Add for TimeVal {
type Output = TimeVal;
fn add(self, rhs: TimeVal) -> TimeVal {
TimeVal::microseconds(self.num_microseconds() + rhs.num_microseconds())
}
}
impl ops::Sub for TimeVal {
type Output = TimeVal;
fn sub(self, rhs: TimeVal) -> TimeVal {
TimeVal::microseconds(self.num_microseconds() - rhs.num_microseconds())
}
}
impl ops::Mul<i32> for TimeVal {
type Output = TimeVal;
fn mul(self, rhs: i32) -> TimeVal {
let usec = self
.num_microseconds()
.checked_mul(i64::from(rhs))
.expect("TimeVal multiply out of bounds");
TimeVal::microseconds(usec)
}
}
impl ops::Div<i32> for TimeVal {
type Output = TimeVal;
fn div(self, rhs: i32) -> TimeVal {
let usec = self.num_microseconds() / i64::from(rhs);
TimeVal::microseconds(usec)
}
}
impl fmt::Display for TimeVal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (abs, sign) = if self.tv_sec() < 0 {
(-*self, "-")
} else {
(*self, "")
};
let sec = abs.tv_sec();
write!(f, "{sign}")?;
if abs.tv_usec() == 0 {
if sec == 1 {
write!(f, "1 second")?;
} else {
write!(f, "{sec} seconds")?;
}
} else if abs.tv_usec() % 1000 == 0 {
write!(f, "{sec}.{:03} seconds", abs.tv_usec() / 1000)?;
} else {
write!(f, "{sec}.{:06} seconds", abs.tv_usec())?;
}
Ok(())
}
}
impl From<timeval> for TimeVal {
fn from(tv: timeval) -> Self {
TimeVal(tv)
}
}
#[inline]
fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
(div_floor_64(this, other), mod_floor_64(this, other))
}
#[inline]
fn div_floor_64(this: i64, other: i64) -> i64 {
match div_rem_64(this, other) {
(d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1,
(d, _) => d,
}
}
#[inline]
fn mod_floor_64(this: i64, other: i64) -> i64 {
match this % other {
r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other,
r => r,
}
}
#[inline]
fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
(this / other, this % other)
}

187
vendor/nix/src/sys/timer.rs vendored Normal file
View File

@@ -0,0 +1,187 @@
//! Timer API via signals.
//!
//! Timer is a POSIX API to create timers and get expiration notifications
//! through queued Unix signals, for the current process. This is similar to
//! Linux's timerfd mechanism, except that API is specific to Linux and makes
//! use of file polling.
//!
//! For more documentation, please read [timer_create](https://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_create.html).
//!
//! # Examples
//!
//! Create an interval timer that signals SIGALARM every 250 milliseconds.
//!
//! ```no_run
//! use nix::sys::signal::{self, SigEvent, SigHandler, SigevNotify, Signal};
//! use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags};
//! use nix::time::ClockId;
//! use std::convert::TryFrom;
//! use std::sync::atomic::{AtomicU64, Ordering};
//! use std::thread::yield_now;
//! use std::time::Duration;
//!
//! const SIG: Signal = Signal::SIGALRM;
//! static ALARMS: AtomicU64 = AtomicU64::new(0);
//!
//! extern "C" fn handle_alarm(signal: libc::c_int) {
//! let signal = Signal::try_from(signal).unwrap();
//! if signal == SIG {
//! ALARMS.fetch_add(1, Ordering::Relaxed);
//! }
//! }
//!
//! fn main() {
//! let clockid = ClockId::CLOCK_MONOTONIC;
//! let sigevent = SigEvent::new(SigevNotify::SigevSignal {
//! signal: SIG,
//! si_value: 0,
//! });
//!
//! let mut timer = Timer::new(clockid, sigevent).unwrap();
//! let expiration = Expiration::Interval(Duration::from_millis(250).into());
//! let flags = TimerSetTimeFlags::empty();
//! timer.set(expiration, flags).expect("could not set timer");
//!
//! let handler = SigHandler::Handler(handle_alarm);
//! unsafe { signal::signal(SIG, handler) }.unwrap();
//!
//! loop {
//! let alarms = ALARMS.load(Ordering::Relaxed);
//! if alarms >= 10 {
//! println!("total alarms handled: {}", alarms);
//! break;
//! }
//! yield_now()
//! }
//! }
//! ```
use crate::sys::signal::SigEvent;
use crate::sys::time::timer::TimerSpec;
pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
use crate::time::ClockId;
use crate::{errno::Errno, Result};
use core::mem;
/// A Unix signal per-process timer.
#[derive(Debug)]
#[repr(transparent)]
pub struct Timer(libc::timer_t);
impl Timer {
/// Creates a new timer based on the clock defined by `clockid`. The details
/// of the signal and its handler are defined by the passed `sigevent`.
#[doc(alias("timer_create"))]
pub fn new(clockid: ClockId, mut sigevent: SigEvent) -> Result<Self> {
let mut timer_id: mem::MaybeUninit<libc::timer_t> =
mem::MaybeUninit::uninit();
Errno::result(unsafe {
libc::timer_create(
clockid.as_raw(),
sigevent.as_mut_ptr(),
timer_id.as_mut_ptr(),
)
})
.map(|_| {
// SAFETY: libc::timer_create is responsible for initializing
// timer_id.
unsafe { Self(timer_id.assume_init()) }
})
}
/// Set a new alarm on the timer.
///
/// # Types of alarm
///
/// There are 3 types of alarms you can set:
///
/// - one shot: the alarm will trigger once after the specified amount of
/// time.
/// Example: I want an alarm to go off in 60s and then disable itself.
///
/// - interval: the alarm will trigger every specified interval of time.
/// Example: I want an alarm to go off every 60s. The alarm will first
/// go off 60s after I set it and every 60s after that. The alarm will
/// not disable itself.
///
/// - interval delayed: the alarm will trigger after a certain amount of
/// time and then trigger at a specified interval.
/// Example: I want an alarm to go off every 60s but only start in 1h.
/// The alarm will first trigger 1h after I set it and then every 60s
/// after that. The alarm will not disable itself.
///
/// # Relative vs absolute alarm
///
/// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass
/// to the `Expiration` you want is relative. If however you want an alarm
/// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`.
/// Then the one shot TimeSpec and the delay TimeSpec of the delayed
/// interval are going to be interpreted as absolute.
///
/// # Disabling alarms
///
/// Note: Only one alarm can be set for any given timer. Setting a new alarm
/// actually removes the previous one.
///
/// Note: Setting a one shot alarm with a 0s TimeSpec disable the alarm
/// altogether.
#[doc(alias("timer_settime"))]
pub fn set(
&mut self,
expiration: Expiration,
flags: TimerSetTimeFlags,
) -> Result<()> {
let timerspec: TimerSpec = expiration.into();
Errno::result(unsafe {
libc::timer_settime(
self.0,
flags.bits(),
timerspec.as_ref(),
core::ptr::null_mut(),
)
})
.map(drop)
}
/// Get the parameters for the alarm currently set, if any.
#[doc(alias("timer_gettime"))]
pub fn get(&self) -> Result<Option<Expiration>> {
let mut timerspec = TimerSpec::none();
Errno::result(unsafe {
libc::timer_gettime(self.0, timerspec.as_mut())
})
.map(|_| {
if timerspec.as_ref().it_interval.tv_sec == 0
&& timerspec.as_ref().it_interval.tv_nsec == 0
&& timerspec.as_ref().it_value.tv_sec == 0
&& timerspec.as_ref().it_value.tv_nsec == 0
{
None
} else {
Some(timerspec.into())
}
})
}
/// Return the number of timers that have overrun
///
/// Each timer is able to queue one signal to the process at a time, meaning
/// if the signal is not handled before the next expiration the timer has
/// 'overrun'. This function returns how many times that has happened to
/// this timer, up to `libc::DELAYTIMER_MAX`. If more than the maximum
/// number of overruns have happened the return is capped to the maximum.
#[doc(alias("timer_getoverrun"))]
pub fn overruns(&self) -> i32 {
unsafe { libc::timer_getoverrun(self.0) }
}
}
impl Drop for Timer {
fn drop(&mut self) {
if !std::thread::panicking() {
let result = Errno::result(unsafe { libc::timer_delete(self.0) });
if let Err(Errno::EINVAL) = result {
panic!("close of Timer encountered EINVAL");
}
}
}
}

240
vendor/nix/src/sys/timerfd.rs vendored Normal file
View File

@@ -0,0 +1,240 @@
//! Timer API via file descriptors.
//!
//! Timer FD is a Linux-only API to create timers and get expiration
//! notifications through file descriptors.
//!
//! For more documentation, please read [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html).
//!
//! # Examples
//!
//! Create a new one-shot timer that expires after 1 second.
//! ```
//! # use std::os::unix::io::AsRawFd;
//! # use nix::sys::timerfd::{TimerFd, ClockId, TimerFlags, TimerSetTimeFlags,
//! # Expiration};
//! # use nix::sys::time::{TimeSpec, TimeValLike};
//! # use nix::unistd::read;
//! #
//! // We create a new monotonic timer.
//! let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty())
//! .unwrap();
//!
//! // We set a new one-shot timer in 1 seconds.
//! timer.set(
//! Expiration::OneShot(TimeSpec::seconds(1)),
//! TimerSetTimeFlags::empty()
//! ).unwrap();
//!
//! // We wait for the timer to expire.
//! timer.wait().unwrap();
//! ```
use crate::sys::time::timer::TimerSpec;
pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
use crate::unistd::read;
use crate::{errno::Errno, Result};
use libc::c_int;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
/// A timerfd instance. This is also a file descriptor, you can feed it to
/// other interfaces taking file descriptors as arguments, [`epoll`] for example.
///
/// [`epoll`]: crate::sys::epoll
#[derive(Debug)]
pub struct TimerFd {
fd: OwnedFd,
}
impl AsFd for TimerFd {
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
impl FromRawFd for TimerFd {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
TimerFd {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
}
}
}
impl From<TimerFd> for OwnedFd {
fn from(value: TimerFd) -> Self {
value.fd
}
}
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 {
/// A settable system-wide real-time clock.
CLOCK_REALTIME,
/// A non-settable monotonically increasing clock.
///
/// Does not change after system startup.
/// Does not measure time while the system is suspended.
CLOCK_MONOTONIC,
/// Like `CLOCK_MONOTONIC`, except that `CLOCK_BOOTTIME` includes the time
/// that the system was suspended.
CLOCK_BOOTTIME,
/// Like `CLOCK_REALTIME`, but will wake the system if it is suspended.
CLOCK_REALTIME_ALARM,
/// Like `CLOCK_BOOTTIME`, but will wake the system if it is suspended.
CLOCK_BOOTTIME_ALARM,
}
}
libc_bitflags! {
/// Additional flags to change the behaviour of the file descriptor at the
/// time of creation.
pub struct TimerFlags: c_int {
/// Set the `O_NONBLOCK` flag on the open file description referred to by the new file descriptor.
TFD_NONBLOCK;
/// Set the `FD_CLOEXEC` flag on the file descriptor.
TFD_CLOEXEC;
}
}
impl TimerFd {
/// Creates a new timer based on the clock defined by `clockid`. The
/// underlying fd can be assigned specific flags with `flags` (CLOEXEC,
/// NONBLOCK). The underlying fd will be closed on drop.
#[doc(alias("timerfd_create"))]
pub fn new(clockid: ClockId, flags: TimerFlags) -> Result<Self> {
Errno::result(unsafe {
libc::timerfd_create(clockid as i32, flags.bits())
})
.map(|fd| Self {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
})
}
/// Sets a new alarm on the timer.
///
/// # Types of alarm
///
/// There are 3 types of alarms you can set:
///
/// - one shot: the alarm will trigger once after the specified amount of
/// time.
/// Example: I want an alarm to go off in 60s and then disable itself.
///
/// - interval: the alarm will trigger every specified interval of time.
/// Example: I want an alarm to go off every 60s. The alarm will first
/// go off 60s after I set it and every 60s after that. The alarm will
/// not disable itself.
///
/// - interval delayed: the alarm will trigger after a certain amount of
/// time and then trigger at a specified interval.
/// Example: I want an alarm to go off every 60s but only start in 1h.
/// The alarm will first trigger 1h after I set it and then every 60s
/// after that. The alarm will not disable itself.
///
/// # Relative vs absolute alarm
///
/// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass
/// to the `Expiration` you want is relative. If however you want an alarm
/// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`.
/// Then the one shot TimeSpec and the delay TimeSpec of the delayed
/// interval are going to be interpreted as absolute.
///
/// # Cancel on a clock change
///
/// If you set a `TFD_TIMER_CANCEL_ON_SET` alongside `TFD_TIMER_ABSTIME`
/// and the clock for this timer is `CLOCK_REALTIME` or `CLOCK_REALTIME_ALARM`,
/// then this timer is marked as cancelable if the real-time clock undergoes
/// a discontinuous change.
///
/// # Disabling alarms
///
/// Note: Only one alarm can be set for any given timer. Setting a new alarm
/// actually removes the previous one.
///
/// Note: Setting a one shot alarm with a 0s TimeSpec disables the alarm
/// altogether.
#[doc(alias("timerfd_settime"))]
pub fn set(
&self,
expiration: Expiration,
flags: TimerSetTimeFlags,
) -> Result<()> {
let timerspec: TimerSpec = expiration.into();
Errno::result(unsafe {
libc::timerfd_settime(
self.fd.as_fd().as_raw_fd(),
flags.bits(),
timerspec.as_ref(),
std::ptr::null_mut(),
)
})
.map(drop)
}
/// Get the parameters for the alarm currently set, if any.
#[doc(alias("timerfd_gettime"))]
pub fn get(&self) -> Result<Option<Expiration>> {
let mut timerspec = TimerSpec::none();
Errno::result(unsafe {
libc::timerfd_gettime(
self.fd.as_fd().as_raw_fd(),
timerspec.as_mut(),
)
})
.map(|_| {
if timerspec.as_ref().it_interval.tv_sec == 0
&& timerspec.as_ref().it_interval.tv_nsec == 0
&& timerspec.as_ref().it_value.tv_sec == 0
&& timerspec.as_ref().it_value.tv_nsec == 0
{
None
} else {
Some(timerspec.into())
}
})
}
/// Remove the alarm if any is set.
#[doc(alias("timerfd_settime"))]
pub fn unset(&self) -> Result<()> {
Errno::result(unsafe {
libc::timerfd_settime(
self.fd.as_fd().as_raw_fd(),
TimerSetTimeFlags::empty().bits(),
TimerSpec::none().as_ref(),
std::ptr::null_mut(),
)
})
.map(drop)
}
/// Wait for the configured alarm to expire.
///
/// Note: If the alarm is unset, then you will wait forever.
pub fn wait(&self) -> Result<()> {
while let Err(e) = read(&self.fd, &mut [0u8; 8]) {
if e == Errno::ECANCELED {
break;
}
if e != Errno::EINTR {
return Err(e);
}
}
Ok(())
}
/// Constructs a `TimerFd` wrapping an existing `OwnedFd`.
///
/// # Safety
///
/// `OwnedFd` is a valid `TimerFd`.
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}

234
vendor/nix/src/sys/uio.rs vendored Normal file
View File

@@ -0,0 +1,234 @@
//! Vectored I/O
use crate::errno::Errno;
use crate::Result;
use libc::{self, c_int, off_t, size_t};
use std::io::{IoSlice, IoSliceMut};
use std::os::unix::io::{AsFd, AsRawFd};
/// 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: AsFd>(fd: Fd, iov: &[IoSlice<'_>]) -> Result<usize> {
// SAFETY: to quote the documentation for `IoSlice`:
//
// [IoSlice] is semantically a wrapper around a &[u8], but is
// guaranteed to be ABI compatible with the iovec type on Unix
// platforms.
//
// Because it is ABI compatible, a pointer cast here is valid
let res = unsafe {
libc::writev(
fd.as_fd().as_raw_fd(),
iov.as_ptr().cast(),
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)
// Clippy doesn't know that we need to pass iov mutably only because the
// mutation happens after converting iov to a pointer
#[allow(clippy::needless_pass_by_ref_mut)]
pub fn readv<Fd: AsFd>(fd: Fd, iov: &mut [IoSliceMut<'_>]) -> Result<usize> {
// SAFETY: same as in writev(), IoSliceMut is ABI-compatible with iovec
let res = unsafe {
libc::readv(
fd.as_fd().as_raw_fd(),
iov.as_ptr().cast(),
iov.len() as c_int,
)
};
Errno::result(res).map(|r| r as usize)
}
/// Write to `fd` at `offset` from buffers in `iov`.
///
/// Buffers in `iov` will be written in order until all buffers have been written
/// or an error occurs. The file offset is not changed.
///
/// See also: [`writev`](fn.writev.html) and [`pwrite`](fn.pwrite.html)
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "solaris", target_os = "cygwin")))]
pub fn pwritev<Fd: AsFd>(
fd: Fd,
iov: &[IoSlice<'_>],
offset: off_t,
) -> Result<usize> {
#[cfg(target_env = "uclibc")]
let offset = offset as libc::off64_t; // uclibc doesn't use off_t
// SAFETY: same as in writev()
let res = unsafe {
libc::pwritev(
fd.as_fd().as_raw_fd(),
iov.as_ptr().cast(),
iov.len() as c_int,
offset,
)
};
Errno::result(res).map(|r| r as usize)
}
/// Read from `fd` at `offset` filling buffers in `iov`.
///
/// Buffers in `iov` will be filled in order until all buffers have been filled,
/// no more bytes are available, or an error occurs. The file offset is not
/// changed.
///
/// See also: [`readv`](fn.readv.html) and [`pread`](fn.pread.html)
#[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "solaris", target_os = "cygwin")))]
// Clippy doesn't know that we need to pass iov mutably only because the
// mutation happens after converting iov to a pointer
#[allow(clippy::needless_pass_by_ref_mut)]
pub fn preadv<Fd: AsFd>(
fd: Fd,
iov: &mut [IoSliceMut<'_>],
offset: off_t,
) -> Result<usize> {
#[cfg(target_env = "uclibc")]
let offset = offset as libc::off64_t; // uclibc doesn't use off_t
// SAFETY: same as in readv()
let res = unsafe {
libc::preadv(
fd.as_fd().as_raw_fd(),
iov.as_ptr().cast(),
iov.len() as c_int,
offset,
)
};
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: AsFd>(fd: Fd, buf: &[u8], offset: off_t) -> Result<usize> {
let res = unsafe {
libc::pwrite(
fd.as_fd().as_raw_fd(),
buf.as_ptr().cast(),
buf.len() as size_t,
offset,
)
};
Errno::result(res).map(|r| r as usize)
}
/// Low-level read from 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: AsFd>(fd: Fd, buf: &mut [u8], offset: off_t) -> Result<usize> {
let res = unsafe {
libc::pread(
fd.as_fd().as_raw_fd(),
buf.as_mut_ptr().cast(),
buf.len() as size_t,
offset,
)
};
Errno::result(res).map(|r| r as usize)
}
/// A slice of memory in a remote process, starting at address `base`
/// and consisting of `len` bytes.
///
/// This is the same underlying C structure as `IoSlice`,
/// except that it refers to memory in some other process, and is
/// therefore not represented in Rust by an actual slice as `IoSlice` is. It
/// is used with [`process_vm_readv`](fn.process_vm_readv.html)
/// and [`process_vm_writev`](fn.process_vm_writev.html).
#[cfg(linux_android)]
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RemoteIoVec {
/// The starting address of this slice (`iov_base`).
pub base: usize,
/// The number of bytes in this slice (`iov_len`).
pub len: usize,
}
feature! {
#![feature = "process"]
/// Write data directly to another process's virtual memory
/// (see [`process_vm_writev`(2)]).
///
/// `local_iov` is a list of [`IoSlice`]s containing the data to be written,
/// and `remote_iov` is a list of [`RemoteIoVec`]s identifying where the
/// data should be written in the target process. On success, returns the
/// number of bytes written, which will always be a whole
/// number of `remote_iov` chunks.
///
/// This requires the same permissions as debugging the process using
/// [ptrace]: you must either be a privileged process (with
/// `CAP_SYS_PTRACE`), or you must be running as the same user as the
/// target process and the OS must have unprivileged debugging enabled.
///
/// This function is only available on Linux and Android(SDK23+).
///
/// [`process_vm_writev`(2)]: https://man7.org/linux/man-pages/man2/process_vm_writev.2.html
/// [ptrace]: ../ptrace/index.html
/// [`IoSlice`]: https://doc.rust-lang.org/std/io/struct.IoSlice.html
/// [`RemoteIoVec`]: struct.RemoteIoVec.html
#[cfg(all(linux_android, not(target_env = "uclibc")))]
pub fn process_vm_writev(
pid: crate::unistd::Pid,
local_iov: &[IoSlice<'_>],
remote_iov: &[RemoteIoVec]) -> Result<usize>
{
let res = unsafe {
libc::process_vm_writev(pid.into(),
local_iov.as_ptr().cast(), local_iov.len() as libc::c_ulong,
remote_iov.as_ptr().cast(), remote_iov.len() as libc::c_ulong, 0)
};
Errno::result(res).map(|r| r as usize)
}
/// Read data directly from another process's virtual memory
/// (see [`process_vm_readv`(2)]).
///
/// `local_iov` is a list of [`IoSliceMut`]s containing the buffer to copy
/// data into, and `remote_iov` is a list of [`RemoteIoVec`]s identifying
/// where the source data is in the target process. On success,
/// returns the number of bytes written, which will always be a whole
/// number of `remote_iov` chunks.
///
/// This requires the same permissions as debugging the process using
/// [`ptrace`]: you must either be a privileged process (with
/// `CAP_SYS_PTRACE`), or you must be running as the same user as the
/// target process and the OS must have unprivileged debugging enabled.
///
/// This function is only available on Linux and Android(SDK23+).
///
/// [`process_vm_readv`(2)]: https://man7.org/linux/man-pages/man2/process_vm_readv.2.html
/// [`ptrace`]: ../ptrace/index.html
/// [`IoSliceMut`]: https://doc.rust-lang.org/std/io/struct.IoSliceMut.html
/// [`RemoteIoVec`]: struct.RemoteIoVec.html
#[cfg(all(linux_android, not(target_env = "uclibc")))]
pub fn process_vm_readv(
pid: crate::unistd::Pid,
local_iov: &mut [IoSliceMut<'_>],
remote_iov: &[RemoteIoVec]) -> Result<usize>
{
let res = unsafe {
libc::process_vm_readv(pid.into(),
local_iov.as_ptr().cast(), local_iov.len() as libc::c_ulong,
remote_iov.as_ptr().cast(), remote_iov.len() as libc::c_ulong, 0)
};
Errno::result(res).map(|r| r as usize)
}
}

64
vendor/nix/src/sys/utsname.rs vendored Normal file
View File

@@ -0,0 +1,64 @@
//! Get system identification
use crate::{Errno, Result};
use libc::c_char;
use std::ffi::OsStr;
use std::mem;
use std::os::unix::ffi::OsStrExt;
/// 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) -> &OsStr {
cast_and_trim(&self.0.sysname)
}
/// Network name of this machine.
pub fn nodename(&self) -> &OsStr {
cast_and_trim(&self.0.nodename)
}
/// Release level of the operating system.
pub fn release(&self) -> &OsStr {
cast_and_trim(&self.0.release)
}
/// Version level of the operating system.
pub fn version(&self) -> &OsStr {
cast_and_trim(&self.0.version)
}
/// Machine hardware platform.
pub fn machine(&self) -> &OsStr {
cast_and_trim(&self.0.machine)
}
/// NIS or YP domain name of this machine.
#[cfg(linux_android)]
pub fn domainname(&self) -> &OsStr {
cast_and_trim(&self.0.domainname)
}
}
/// Get system identification
pub fn uname() -> Result<UtsName> {
unsafe {
let mut ret = mem::MaybeUninit::zeroed();
Errno::result(libc::uname(ret.as_mut_ptr()))?;
Ok(UtsName(ret.assume_init()))
}
}
fn cast_and_trim(slice: &[c_char]) -> &OsStr {
let length = slice
.iter()
.position(|&byte| byte == 0)
.unwrap_or(slice.len());
let bytes =
unsafe { std::slice::from_raw_parts(slice.as_ptr().cast(), length) };
OsStr::from_bytes(bytes)
}

381
vendor/nix/src/sys/wait.rs vendored Normal file
View File

@@ -0,0 +1,381 @@
//! Wait for a process to change status
use crate::errno::Errno;
use crate::sys::signal::Signal;
use crate::unistd::Pid;
use crate::Result;
use cfg_if::cfg_if;
use libc::{self, c_int};
use std::convert::TryFrom;
#[cfg(any(
target_os = "android",
all(target_os = "linux", not(target_env = "uclibc")),
))]
use std::os::unix::io::{AsRawFd, BorrowedFd};
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(linux_android,
apple_targets,
target_os = "freebsd",
target_os = "haiku",
target_os = "redox",
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(linux_android,
apple_targets,
target_os = "freebsd",
target_os = "haiku",
target_os = "redox",
target_os = "netbsd"))]
WSTOPPED;
/// Don't reap, just poll status.
#[cfg(any(linux_android,
apple_targets,
target_os = "freebsd",
target_os = "haiku",
target_os = "redox",
target_os = "netbsd"))]
WNOWAIT;
/// Don't wait on children of other threads in this group
#[cfg(any(linux_android, target_os = "redox"))]
__WNOTHREAD;
/// Wait on all children, regardless of type
#[cfg(any(linux_android, target_os = "redox"))]
__WALL;
/// Wait for "clone" children only.
#[cfg(any(linux_android, target_os = "redox"))]
__WCLONE;
}
);
/// Possible return values from `wait()` or `waitpid()`.
///
/// Each status (other than `StillAlive`) describes a state transition
/// in a child process `Pid`, such as the process exiting or stopping,
/// plus additional data about the transition if any.
///
/// Note that there are two Linux-specific enum variants, `PtraceEvent`
/// and `PtraceSyscall`. Portable code should avoid exhaustively
/// matching on `WaitStatus`.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum WaitStatus {
/// The process exited normally (as with `exit()` or returning from
/// `main`) with the given exit code. This case matches the C macro
/// `WIFEXITED(status)`; the second field is `WEXITSTATUS(status)`.
Exited(Pid, i32),
/// The process was killed by the given signal. The third field
/// indicates whether the signal generated a core dump. This case
/// matches the C macro `WIFSIGNALED(status)`; the last two fields
/// correspond to `WTERMSIG(status)` and `WCOREDUMP(status)`.
Signaled(Pid, Signal, bool),
/// The process is alive, but was stopped by the given signal. This
/// is only reported if `WaitPidFlag::WUNTRACED` was passed. This
/// case matches the C macro `WIFSTOPPED(status)`; the second field
/// is `WSTOPSIG(status)`.
Stopped(Pid, Signal),
/// The traced process was stopped by a `PTRACE_EVENT_*` event. See
/// [`nix::sys::ptrace`] and [`ptrace`(2)] for more information. All
/// currently-defined events use `SIGTRAP` as the signal; the third
/// field is the `PTRACE_EVENT_*` value of the event.
///
/// [`nix::sys::ptrace`]: ../ptrace/index.html
/// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(linux_android)]
PtraceEvent(Pid, Signal, c_int),
/// The traced process was stopped by execution of a system call,
/// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for
/// more information.
///
/// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
#[cfg(linux_android)]
PtraceSyscall(Pid),
/// The process was previously stopped but has resumed execution
/// after receiving a `SIGCONT` signal. This is only reported if
/// `WaitPidFlag::WCONTINUED` was passed. This case matches the C
/// macro `WIFCONTINUED(status)`.
Continued(Pid),
/// There are currently no state changes to report in any awaited
/// child process. This is only returned if `WaitPidFlag::WNOHANG`
/// was used (otherwise `wait()` or `waitpid()` would block until
/// there was something to report).
StillAlive,
}
impl WaitStatus {
/// Extracts the PID from the WaitStatus unless it equals StillAlive.
pub fn pid(&self) -> Option<Pid> {
use self::WaitStatus::*;
match *self {
Exited(p, _) | Signaled(p, _, _) | Stopped(p, _) | Continued(p) => {
Some(p)
}
StillAlive => None,
#[cfg(linux_android)]
PtraceEvent(p, _, _) | PtraceSyscall(p) => Some(p),
}
}
}
fn exited(status: i32) -> bool {
libc::WIFEXITED(status)
}
fn exit_status(status: i32) -> i32 {
libc::WEXITSTATUS(status)
}
fn signaled(status: i32) -> bool {
libc::WIFSIGNALED(status)
}
fn term_signal(status: i32) -> Result<Signal> {
Signal::try_from(libc::WTERMSIG(status))
}
fn dumped_core(status: i32) -> bool {
libc::WCOREDUMP(status)
}
fn stopped(status: i32) -> bool {
libc::WIFSTOPPED(status)
}
fn stop_signal(status: i32) -> Result<Signal> {
Signal::try_from(libc::WSTOPSIG(status))
}
#[cfg(linux_android)]
fn syscall_stop(status: i32) -> bool {
// From ptrace(2), setting PTRACE_O_TRACESYSGOOD has the effect
// of delivering SIGTRAP | 0x80 as the signal number for syscall
// stops. This allows easily distinguishing syscall stops from
// genuine SIGTRAP signals.
libc::WSTOPSIG(status) == libc::SIGTRAP | 0x80
}
#[cfg(linux_android)]
fn stop_additional(status: i32) -> c_int {
(status >> 16) as c_int
}
fn continued(status: i32) -> bool {
libc::WIFCONTINUED(status)
}
impl WaitStatus {
/// Convert a raw `wstatus` as returned by `waitpid`/`wait` into a `WaitStatus`
///
/// # Errors
///
/// Returns an `Error` corresponding to `EINVAL` for invalid status values.
///
/// # Examples
///
/// Convert a `wstatus` obtained from `libc::waitpid` into a `WaitStatus`:
///
/// ```
/// use nix::sys::wait::WaitStatus;
/// use nix::sys::signal::Signal;
/// let pid = nix::unistd::Pid::from_raw(1);
/// let status = WaitStatus::from_raw(pid, 0x0002);
/// assert_eq!(status, Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false)));
/// ```
pub fn from_raw(pid: Pid, status: i32) -> Result<WaitStatus> {
Ok(if exited(status) {
WaitStatus::Exited(pid, exit_status(status))
} else if signaled(status) {
WaitStatus::Signaled(pid, term_signal(status)?, dumped_core(status))
} else if stopped(status) {
cfg_if! {
if #[cfg(linux_android)] {
fn decode_stopped(pid: Pid, status: i32) -> Result<WaitStatus> {
let status_additional = stop_additional(status);
Ok(if syscall_stop(status) {
WaitStatus::PtraceSyscall(pid)
} else if status_additional == 0 {
WaitStatus::Stopped(pid, stop_signal(status)?)
} else {
WaitStatus::PtraceEvent(pid, stop_signal(status)?,
stop_additional(status))
})
}
} else {
fn decode_stopped(pid: Pid, status: i32) -> Result<WaitStatus> {
Ok(WaitStatus::Stopped(pid, stop_signal(status)?))
}
}
}
return decode_stopped(pid, status);
} else {
assert!(continued(status));
WaitStatus::Continued(pid)
})
}
/// Convert a `siginfo_t` as returned by `waitid` to a `WaitStatus`
///
/// # Errors
///
/// Returns an `Error` corresponding to `EINVAL` for invalid values.
///
/// # Safety
///
/// siginfo_t is actually a union, not all fields may be initialized.
/// The functions si_pid() and si_status() must be valid to call on
/// the passed siginfo_t.
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "haiku",
all(target_os = "linux", not(target_env = "uclibc")),
))]
unsafe fn from_siginfo(siginfo: &libc::siginfo_t) -> Result<WaitStatus> {
let si_pid = unsafe { siginfo.si_pid() };
if si_pid == 0 {
return Ok(WaitStatus::StillAlive);
}
assert_eq!(siginfo.si_signo, libc::SIGCHLD);
let pid = Pid::from_raw(si_pid);
let si_status = unsafe { siginfo.si_status() };
let status = match siginfo.si_code {
libc::CLD_EXITED => WaitStatus::Exited(pid, si_status),
libc::CLD_KILLED | libc::CLD_DUMPED => WaitStatus::Signaled(
pid,
Signal::try_from(si_status)?,
siginfo.si_code == libc::CLD_DUMPED,
),
libc::CLD_STOPPED => {
WaitStatus::Stopped(pid, Signal::try_from(si_status)?)
}
libc::CLD_CONTINUED => WaitStatus::Continued(pid),
#[cfg(linux_android)]
libc::CLD_TRAPPED => {
if si_status == libc::SIGTRAP | 0x80 {
WaitStatus::PtraceSyscall(pid)
} else {
WaitStatus::PtraceEvent(
pid,
Signal::try_from(si_status & 0xff)?,
(si_status >> 8) as c_int,
)
}
}
_ => return Err(Errno::EINVAL),
};
Ok(status)
}
}
/// 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::*;
let mut status: i32 = 0;
let option_bits = match options {
Some(bits) => bits.bits(),
None => 0,
};
let res = unsafe {
libc::waitpid(
pid.into().unwrap_or_else(|| Pid::from_raw(-1)).into(),
&mut status as *mut c_int,
option_bits,
)
};
match Errno::result(res)? {
0 => Ok(StillAlive),
res => WaitStatus::from_raw(Pid::from_raw(res), status),
}
}
/// 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)
}
/// The ID argument for `waitid`
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "haiku",
all(target_os = "linux", not(target_env = "uclibc")),
))]
#[derive(Debug)]
pub enum Id<'fd> {
/// Wait for any child
All,
/// Wait for the child whose process ID matches the given PID
Pid(Pid),
/// Wait for the child whose process group ID matches the given PID
///
/// If the PID is zero, the caller's process group is used since Linux 5.4.
PGid(Pid),
/// Wait for the child referred to by the given PID file descriptor
#[cfg(linux_android)]
PIDFd(BorrowedFd<'fd>),
/// A helper variant to resolve the unused parameter (`'fd`) problem on platforms
/// other than Linux and Android.
#[doc(hidden)]
_Unreachable(std::marker::PhantomData<&'fd std::convert::Infallible>),
}
/// Wait for a process to change status
///
/// See also [waitid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitid.html)
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "haiku",
all(target_os = "linux", not(target_env = "uclibc")),
))]
pub fn waitid(id: Id, flags: WaitPidFlag) -> Result<WaitStatus> {
let (idtype, idval) = match id {
Id::All => (libc::P_ALL, 0),
Id::Pid(pid) => (libc::P_PID, pid.as_raw() as libc::id_t),
Id::PGid(pid) => (libc::P_PGID, pid.as_raw() as libc::id_t),
#[cfg(linux_android)]
Id::PIDFd(fd) => (libc::P_PIDFD, fd.as_raw_fd() as libc::id_t),
Id::_Unreachable(_) => {
unreachable!("This variant could never be constructed")
}
};
let siginfo = unsafe {
// Memory is zeroed rather than uninitialized, as not all platforms
// initialize the memory in the StillAlive case
let mut siginfo: libc::siginfo_t = std::mem::zeroed();
Errno::result(libc::waitid(idtype, idval, &mut siginfo, flags.bits()))?;
siginfo
};
unsafe { WaitStatus::from_siginfo(&siginfo) }
}

293
vendor/nix/src/syslog.rs vendored Normal file
View File

@@ -0,0 +1,293 @@
//! Interfaces for controlling system log.
use crate::{NixPath, Result};
use std::ffi::OsStr;
use std::ptr;
/// Logging options of subsequent [`syslog`] calls can be set by calling [`openlog`].
///
/// The parameter `ident` is a string that will be prepended to every message. The `logopt`
/// argument specifies logging options. The `facility` parameter encodes a default facility to be
/// assigned to all messages that do not have an explicit facility encoded.
//
// On Linux, the `ident` argument needs to have static lifetime according to the
// man page:
//
// The argument ident in the call of openlog() is probably stored as-is. Thus,
// if the string it points to is changed, syslog() may start prepending the changed
// string, and if the string it points to ceases to exist, the results are
// undefined. Most portable is to use a string constant.
#[cfg(target_os = "linux")]
pub fn openlog(
ident: Option<&'static std::ffi::CStr>,
logopt: LogFlags,
facility: Facility,
) -> Result<()> {
let logopt = logopt.bits();
let facility = facility as libc::c_int;
match ident {
None => unsafe {
libc::openlog(ptr::null(), logopt, facility);
},
Some(ident) => ident.with_nix_path(|ident| unsafe {
libc::openlog(ident.as_ptr(), logopt, facility);
})?,
}
Ok(())
}
/// Logging options of subsequent [`syslog`] calls can be set by calling [`openlog`].
///
/// The parameter `ident` is a string that will be prepended to every message. The `logopt`
/// argument specifies logging options. The `facility` parameter encodes a default facility to be
/// assigned to all messages that do not have an explicit facility encoded.
#[cfg(not(target_os = "linux"))]
pub fn openlog<S: AsRef<OsStr> + ?Sized>(
ident: Option<&S>,
logopt: LogFlags,
facility: Facility,
) -> Result<()> {
let logopt = logopt.bits();
let facility = facility as libc::c_int;
match ident.map(OsStr::new) {
None => unsafe {
libc::openlog(ptr::null(), logopt, facility);
},
Some(ident) => ident.with_nix_path(|ident| unsafe {
libc::openlog(ident.as_ptr(), logopt, facility);
})?,
}
Ok(())
}
/// Writes message to the system message logger.
///
/// The message is then written to the system console, log files, logged-in users, or forwarded
/// to other machines as appropriate.
///
/// # Examples
///
/// ```rust
/// use nix::syslog::{openlog, syslog, Facility, LogFlags, Severity, Priority};
///
/// let priority = Priority::new(Severity::LOG_EMERG, Facility::LOG_USER);
/// syslog(priority, "Hello, nix!").unwrap();
///
/// // use `format!` to format the message
/// let name = "syslog";
/// syslog(priority, &format!("Hello, {name}!")).unwrap();
/// ```
pub fn syslog<P, S>(priority: P, message: &S) -> Result<()>
where
P: Into<Priority>,
S: AsRef<OsStr> + ?Sized,
{
let priority = priority.into();
let formatter = OsStr::new("%s");
let message = OsStr::new(message);
formatter.with_nix_path(|formatter| {
message.with_nix_path(|message| unsafe {
libc::syslog(priority.0, formatter.as_ptr(), message.as_ptr())
})
})??;
Ok(())
}
/// Set the process-wide priority mask to `mask` and return the previous mask
/// value.
///
/// Calls to `syslog()` with a priority level not set in `mask` are ignored. The
/// default is to log all priorities.
///
/// If the `mask` argument is `None`, the current logmask is not modified, this
/// can be used to query the current log mask.
pub fn setlogmask(mask: Option<LogMask>) -> LogMask {
let mask = match mask {
Some(mask) => mask.0,
None => 0,
};
let prev_mask = unsafe { libc::setlogmask(mask) };
LogMask(prev_mask)
}
/// Closes the log file.
pub fn closelog() {
unsafe { libc::closelog() }
}
/// System log priority mask.
#[derive(Debug, Clone, Copy)]
pub struct LogMask(libc::c_int);
impl LogMask {
/// Creates a mask of all priorities up to and including `priority`.
#[doc(alias("LOG_UPTO"))]
pub fn up_to(priority: Severity) -> Self {
let pri = priority as libc::c_int;
Self((1 << (pri + 1)) - 1)
}
/// Creates a mask for the specified priority.
#[doc(alias("LOG_MASK"))]
pub fn of_priority(priority: Severity) -> Self {
let pri = priority as libc::c_int;
Self(1 << pri)
}
/// Returns if the mask for the specified `priority` is set.
pub fn contains(&self, priority: Severity) -> bool {
let priority = Self::of_priority(priority);
let and_result = *self & priority;
and_result.0 != 0
}
}
impl std::ops::BitOr for LogMask {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl std::ops::BitAnd for LogMask {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & rhs.0)
}
}
impl std::ops::BitOrAssign for LogMask {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl std::ops::BitAndAssign for LogMask {
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl std::ops::Not for LogMask {
type Output = Self;
fn not(self) -> Self::Output {
Self(!self.0)
}
}
/// The priority for a log message.
#[derive(Debug, Clone, Copy)]
pub struct Priority(libc::c_int);
impl Priority {
/// Create a new priority from a facility and severity level.
pub fn new(severity: Severity, facility: Facility) -> Self {
let priority = (facility as libc::c_int) | (severity as libc::c_int);
Priority(priority)
}
}
impl From<Severity> for Priority {
fn from(severity: Severity) -> Self {
let priority = severity as libc::c_int;
Priority(priority)
}
}
libc_bitflags! {
/// Options for system logging.
pub struct LogFlags: libc::c_int {
/// Log the process id with each message: useful for identifying instantiations of
/// daemons.
LOG_PID;
/// If syslog() cannot pass the message to syslogd(8) it will attempt to write the
/// message to the console ("/dev/console").
LOG_CONS;
/// The converse of [`LOG_NDELAY`][LogFlags::LOG_NDELAY]; opening of the connection is
/// delayed until `syslog` is called.
///
/// This is the default, and need not be specified.
LOG_ODELAY;
/// Open the connection to syslogd(8) immediately. Normally the open is delayed until
/// the first message is logged. Useful for programs that need to manage the order in
/// which file descriptors are allocated.
LOG_NDELAY;
/// Write the message to standard error output as well to the system log.
#[cfg(not(any(solarish, target_os = "redox", target_os = "cygwin")))]
LOG_PERROR;
}
}
libc_enum! {
/// Severity levels for log messages.
#[repr(i32)]
#[non_exhaustive]
pub enum Severity {
/// A panic condition.
///
/// This is normally broadcast to all users.
LOG_EMERG,
/// A condition that should be corrected immediately, such as a corrupted system database.
LOG_ALERT,
/// Critical conditions, e.g., hard device errors.
LOG_CRIT,
/// Errors.
LOG_ERR,
/// Warning messages.
LOG_WARNING,
/// Conditions that are not error conditions, but should possibly be handled specially.
LOG_NOTICE,
/// Informational messages.
LOG_INFO,
/// Messages that contain information normally of use only when debugging a program.
LOG_DEBUG,
}
}
libc_enum! {
/// Facilities for log messages.
#[repr(i32)]
#[non_exhaustive]
pub enum Facility {
/// Messages generated by the kernel.
///
/// These cannot be generated by any user processes.
LOG_KERN,
/// Messages generated by random user processes.
///
/// This is the default facility identifier if none is specified.
LOG_USER,
/// The mail system.
LOG_MAIL,
/// System daemons, such as routed(8), that are not provided for explicitly by other facilities.
LOG_DAEMON,
/// The authorization system: login(1), su(1), getty(8), etc.
LOG_AUTH,
/// Messages generated internally by syslogd(8).
LOG_SYSLOG,
/// The line printer spooling system: cups-lpd(8), cupsd(8), etc.
LOG_LPR,
/// The network news system.
LOG_NEWS,
/// The uucp system.
LOG_UUCP,
/// Reserved for local use.
LOG_LOCAL0,
/// Reserved for local use.
LOG_LOCAL1,
/// Reserved for local use.
LOG_LOCAL2,
/// Reserved for local use.
LOG_LOCAL3,
/// Reserved for local use.
LOG_LOCAL4,
/// Reserved for local use.
LOG_LOCAL5,
/// Reserved for local use.
LOG_LOCAL6,
/// Reserved for local use.
LOG_LOCAL7,
}
}

288
vendor/nix/src/time.rs vendored Normal file
View File

@@ -0,0 +1,288 @@
//! Sleep, query system clocks, and set system clock
use crate::sys::time::TimeSpec;
#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
#[cfg(feature = "process")]
use crate::unistd::Pid;
use crate::{Errno, Result};
use libc::{self, clockid_t};
use std::mem::MaybeUninit;
/// Clock identifier
///
/// Newtype pattern around [`libc::clockid_t`].
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ClockId(clockid_t);
impl ClockId {
/// Creates `ClockId` from raw `clockid_t`
pub const fn from_raw(clk_id: clockid_t) -> Self {
ClockId(clk_id)
}
feature! {
#![feature = "process"]
/// Returns `ClockId` of a `pid` CPU-time clock
#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
pub fn pid_cpu_clock_id(pid: Pid) -> Result<Self> {
clock_getcpuclockid(pid)
}
}
/// Returns resolution of the clock id
#[cfg(not(target_os = "redox"))]
pub fn res(self) -> Result<TimeSpec> {
clock_getres(self)
}
/// Returns the current time on the clock id
pub fn now(self) -> Result<TimeSpec> {
clock_gettime(self)
}
/// Sets time to `timespec` on the clock id
#[cfg(not(any(
target_os = "ios",
target_os = "tvos",
target_os = "watchos",
target_os = "redox",
target_os = "hermit"
)))]
pub fn set_time(self, timespec: TimeSpec) -> Result<()> {
clock_settime(self, timespec)
}
/// Gets the raw `clockid_t` wrapped by `self`
pub const fn as_raw(self) -> clockid_t {
self.0
}
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
/// Starts at zero when the kernel boots and increments monotonically in SI seconds while the
/// machine is running.
pub const CLOCK_BOOTTIME: ClockId = ClockId(libc::CLOCK_BOOTTIME);
/// Like [`CLOCK_BOOTTIME`](ClockId::CLOCK_BOOTTIME), but will wake the system if it is
/// suspended..
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_BOOTTIME_ALARM: ClockId =
ClockId(libc::CLOCK_BOOTTIME_ALARM);
/// Increments in SI seconds.
pub const CLOCK_MONOTONIC: ClockId = ClockId(libc::CLOCK_MONOTONIC);
/// Like [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but optimized for execution time at the expense of accuracy.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_MONOTONIC_COARSE: ClockId =
ClockId(libc::CLOCK_MONOTONIC_COARSE);
#[cfg(freebsdlike)]
/// Like [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but optimized for execution time at the expense of accuracy.
pub const CLOCK_MONOTONIC_FAST: ClockId =
ClockId(libc::CLOCK_MONOTONIC_FAST);
#[cfg(freebsdlike)]
/// Like [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but optimized for accuracy at the expense of execution time.
pub const CLOCK_MONOTONIC_PRECISE: ClockId =
ClockId(libc::CLOCK_MONOTONIC_PRECISE);
/// Similar to [`CLOCK_MONOTONIC`](ClockId::CLOCK_MONOTONIC), but provides access to a raw
/// hardware-based time that is not subject to NTP adjustments.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_MONOTONIC_RAW: ClockId = ClockId(libc::CLOCK_MONOTONIC_RAW);
#[cfg(any(
linux_android,
apple_targets,
freebsdlike,
target_os = "emscripten",
target_os = "fuchsia",
target_os = "redox",
))]
/// Returns the execution time of the calling process.
pub const CLOCK_PROCESS_CPUTIME_ID: ClockId =
ClockId(libc::CLOCK_PROCESS_CPUTIME_ID);
#[cfg(freebsdlike)]
/// Increments when the CPU is running in user or kernel mode
pub const CLOCK_PROF: ClockId = ClockId(libc::CLOCK_PROF);
/// Increments as a wall clock should.
pub const CLOCK_REALTIME: ClockId = ClockId(libc::CLOCK_REALTIME);
/// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but not settable.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_REALTIME_ALARM: ClockId =
ClockId(libc::CLOCK_REALTIME_ALARM);
/// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but optimized for execution time at the expense of accuracy.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_REALTIME_COARSE: ClockId =
ClockId(libc::CLOCK_REALTIME_COARSE);
#[cfg(freebsdlike)]
/// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but optimized for execution time at the expense of accuracy.
pub const CLOCK_REALTIME_FAST: ClockId = ClockId(libc::CLOCK_REALTIME_FAST);
#[cfg(freebsdlike)]
/// Like [`CLOCK_REALTIME`](ClockId::CLOCK_REALTIME), but optimized for accuracy at the expense of execution time.
pub const CLOCK_REALTIME_PRECISE: ClockId =
ClockId(libc::CLOCK_REALTIME_PRECISE);
#[cfg(freebsdlike)]
/// Returns the current second without performing a full time counter query, using an in-kernel
/// cached value of the current second.
pub const CLOCK_SECOND: ClockId = ClockId(libc::CLOCK_SECOND);
#[allow(missing_docs)] // Undocumented on Linux!
#[cfg(any(
target_os = "emscripten",
target_os = "fuchsia",
all(
target_os = "linux",
any(target_env = "musl", target_env = "ohos")
)
))]
pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE);
/// International Atomic Time.
///
/// A nonsettable system-wide clock derived from wall-clock time but ignoring leap seconds.
#[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))]
pub const CLOCK_TAI: ClockId = ClockId(libc::CLOCK_TAI);
#[cfg(any(
linux_android,
apple_targets,
freebsdlike,
target_os = "emscripten",
target_os = "fuchsia",
))]
/// Returns the execution time of the calling thread.
pub const CLOCK_THREAD_CPUTIME_ID: ClockId =
ClockId(libc::CLOCK_THREAD_CPUTIME_ID);
#[cfg(freebsdlike)]
/// Starts at zero when the kernel boots and increments monotonically in SI seconds while the
/// machine is running.
pub const CLOCK_UPTIME: ClockId = ClockId(libc::CLOCK_UPTIME);
#[cfg(freebsdlike)]
/// Like [`CLOCK_UPTIME`](ClockId::CLOCK_UPTIME), but optimized for execution time at the expense of accuracy.
pub const CLOCK_UPTIME_FAST: ClockId = ClockId(libc::CLOCK_UPTIME_FAST);
#[cfg(freebsdlike)]
/// Like [`CLOCK_UPTIME`](ClockId::CLOCK_UPTIME), but optimized for accuracy at the expense of execution time.
pub const CLOCK_UPTIME_PRECISE: ClockId =
ClockId(libc::CLOCK_UPTIME_PRECISE);
#[cfg(freebsdlike)]
/// Increments only when the CPU is running in user mode on behalf of the calling process.
pub const CLOCK_VIRTUAL: ClockId = ClockId(libc::CLOCK_VIRTUAL);
}
impl From<ClockId> for clockid_t {
fn from(clock_id: ClockId) -> Self {
clock_id.as_raw()
}
}
impl From<clockid_t> for ClockId {
fn from(clk_id: clockid_t) -> Self {
ClockId::from_raw(clk_id)
}
}
impl std::fmt::Display for ClockId {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
/// Get the resolution of the specified clock, (see
/// [clock_getres(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_getres.html)).
#[cfg(not(target_os = "redox"))]
pub fn clock_getres(clock_id: ClockId) -> Result<TimeSpec> {
let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
let ret =
unsafe { libc::clock_getres(clock_id.as_raw(), c_time.as_mut_ptr()) };
Errno::result(ret)?;
let res = unsafe { c_time.assume_init() };
Ok(TimeSpec::from(res))
}
/// Get the time of the specified clock, (see
/// [clock_gettime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_gettime.html)).
pub fn clock_gettime(clock_id: ClockId) -> Result<TimeSpec> {
let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
let ret =
unsafe { libc::clock_gettime(clock_id.as_raw(), c_time.as_mut_ptr()) };
Errno::result(ret)?;
let res = unsafe { c_time.assume_init() };
Ok(TimeSpec::from(res))
}
/// Set the time of the specified clock, (see
/// [clock_settime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_settime.html)).
#[cfg(not(any(
target_os = "ios",
target_os = "tvos",
target_os = "watchos",
target_os = "redox",
target_os = "hermit"
)))]
pub fn clock_settime(clock_id: ClockId, timespec: TimeSpec) -> Result<()> {
let ret =
unsafe { libc::clock_settime(clock_id.as_raw(), timespec.as_ref()) };
Errno::result(ret).map(drop)
}
/// Get the clock id of the specified process id, (see
/// [clock_getcpuclockid(3)](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_getcpuclockid.html)).
#[cfg(any(freebsdlike, linux_android, target_os = "emscripten"))]
#[cfg(feature = "process")]
#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
pub fn clock_getcpuclockid(pid: Pid) -> Result<ClockId> {
let mut clk_id: MaybeUninit<libc::clockid_t> = MaybeUninit::uninit();
let ret =
unsafe { libc::clock_getcpuclockid(pid.into(), clk_id.as_mut_ptr()) };
if ret == 0 {
let res = unsafe { clk_id.assume_init() };
Ok(ClockId::from(res))
} else {
Err(Errno::from_raw(ret))
}
}
#[cfg(any(
linux_android,
solarish,
freebsdlike,
target_os = "netbsd",
target_os = "hurd",
target_os = "aix"
))]
libc_bitflags! {
/// Flags that are used for arming the timer.
pub struct ClockNanosleepFlags: libc::c_int {
/// Indicates that a requested time value should be treated as absolute instead of
/// relative.
TIMER_ABSTIME;
}
}
/// Suspend execution of this thread for the amount of time specified by `request`
/// and measured against the clock speficied by `clock_id`.
///
/// If `flags` is [`TIMER_ABSTIME`](ClockNanosleepFlags::TIMER_ABSTIME), this function will suspend
/// execution until the time value of clock_id reaches the absolute time specified by `request`. If
/// a signal is caught by a signal-catching function, or a signal causes the process to terminate,
/// this sleep is interrrupted.
///
/// see also [man 3 clock_nanosleep](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_nanosleep.html)
#[cfg(any(
linux_android,
solarish,
freebsdlike,
target_os = "netbsd",
target_os = "hurd",
target_os = "aix"
))]
pub fn clock_nanosleep(
clock_id: ClockId,
flags: ClockNanosleepFlags,
request: &TimeSpec,
) -> Result<TimeSpec> {
let mut remain = TimeSpec::new(0, 0);
let ret = unsafe {
libc::clock_nanosleep(
clock_id.as_raw(),
flags.bits(),
request.as_ref() as *const _,
remain.as_mut() as *mut _,
)
};
if ret == 0 {
Ok(remain)
} else {
Err(Errno::from_raw(ret))
}
}

47
vendor/nix/src/ucontext.rs vendored Normal file
View File

@@ -0,0 +1,47 @@
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
use crate::errno::Errno;
use crate::sys::signal::SigSet;
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
use crate::Result;
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
use std::mem;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct UContext {
context: libc::ucontext_t,
}
impl UContext {
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
pub fn get() -> Result<UContext> {
let mut context = mem::MaybeUninit::<libc::ucontext_t>::uninit();
let res = unsafe { libc::getcontext(context.as_mut_ptr()) };
Errno::result(res).map(|_| unsafe {
UContext {
context: context.assume_init(),
}
})
}
#[cfg(not(any(target_env = "musl", target_env = "ohos")))]
pub fn set(&self) -> Result<()> {
let res = unsafe {
libc::setcontext(&self.context as *const libc::ucontext_t)
};
Errno::result(res).map(drop)
}
pub fn sigmask_mut(&mut self) -> &mut SigSet {
unsafe {
&mut *(&mut self.context.uc_sigmask as *mut libc::sigset_t
as *mut SigSet)
}
}
pub fn sigmask(&self) -> &SigSet {
unsafe {
&*(&self.context.uc_sigmask as *const libc::sigset_t
as *const SigSet)
}
}
}

4010
vendor/nix/src/unistd.rs vendored Normal file

File diff suppressed because it is too large Load Diff

149
vendor/nix/test/common/mod.rs vendored Normal file
View File

@@ -0,0 +1,149 @@
use cfg_if::cfg_if;
#[macro_export]
macro_rules! skip {
($($reason: expr),+) => {{
use ::std::io::{self, Write};
let stderr = io::stderr();
let mut handle = stderr.lock();
writeln!(handle, $($reason),+).unwrap();
return;
}}
}
cfg_if! {
if #[cfg(linux_android)] {
#[macro_export] macro_rules! require_capability {
($name:expr, $capname:ident) => {
use ::caps::{Capability, CapSet, has_cap};
if !has_cap(None, CapSet::Effective, Capability::$capname)
.unwrap()
{
skip!("{} requires capability {}. Skipping test.", $name, Capability::$capname);
}
}
}
} else if #[cfg(not(target_os = "redox"))] {
#[macro_export] macro_rules! require_capability {
($name:expr, $capname:ident) => {}
}
}
}
/// Skip the test if we don't have the ability to mount file systems.
#[cfg(target_os = "freebsd")]
#[macro_export]
macro_rules! require_mount {
($name:expr) => {
use nix::unistd::Uid;
use sysctl::{CtlValue, Sysctl};
let ctl = ::sysctl::Ctl::new("vfs.usermount").unwrap();
if !Uid::current().is_root() && CtlValue::Int(0) == ctl.value().unwrap()
{
skip!(
"{} requires the ability to mount file systems. Skipping test.",
$name
);
}
};
}
#[cfg(linux_android)]
#[macro_export]
macro_rules! skip_if_cirrus {
($reason:expr) => {
if std::env::var_os("CIRRUS_CI").is_some() {
skip!("{}", $reason);
}
};
}
#[cfg(target_os = "freebsd")]
#[macro_export]
macro_rules! skip_if_jailed {
($name:expr) => {
use sysctl::{CtlValue, Sysctl};
let ctl = ::sysctl::Ctl::new("security.jail.jailed").unwrap();
if let CtlValue::Int(1) = ctl.value().unwrap() {
skip!("{} cannot run in a jail. Skipping test.", $name);
}
};
}
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
#[macro_export]
macro_rules! skip_if_not_root {
($name:expr) => {
use nix::unistd::Uid;
if !Uid::current().is_root() {
skip!("{} requires root privileges. Skipping test.", $name);
}
};
}
cfg_if! {
if #[cfg(linux_android)] {
#[macro_export] macro_rules! skip_if_seccomp {
($name:expr) => {
if let Ok(s) = std::fs::read_to_string("/proc/self/status") {
for l in s.lines() {
let mut fields = l.split_whitespace();
if fields.next() == Some("Seccomp:") &&
fields.next() != Some("0")
{
skip!("{} cannot be run in Seccomp mode. Skipping test.",
stringify!($name));
}
}
}
}
}
} else if #[cfg(not(target_os = "redox"))] {
#[macro_export] macro_rules! skip_if_seccomp {
($name:expr) => {}
}
}
}
cfg_if! {
if #[cfg(target_os = "linux")] {
#[macro_export] macro_rules! require_kernel_version {
($name:expr, $version_requirement:expr) => {
use semver::{Version, VersionReq};
let version_requirement = VersionReq::parse($version_requirement)
.expect("Bad match_version provided");
let uname = nix::sys::utsname::uname().unwrap();
println!("{}", uname.sysname().to_str().unwrap());
println!("{}", uname.nodename().to_str().unwrap());
println!("{}", uname.release().to_str().unwrap());
println!("{}", uname.version().to_str().unwrap());
println!("{}", uname.machine().to_str().unwrap());
// Fix stuff that the semver parser can't handle
let fixed_release = &uname.release().to_str().unwrap().to_string()
// Fedora 33 reports version as 4.18.el8_2.x86_64 or
// 5.18.200-fc33.x86_64. Remove the underscore.
.replace("_", "-")
// Cirrus-CI reports version as 4.19.112+ . Remove the +
.replace("+", "");
let mut version = Version::parse(fixed_release).unwrap();
//Keep only numeric parts
version.pre = semver::Prerelease::EMPTY;
version.build = semver::BuildMetadata::EMPTY;
if !version_requirement.matches(&version) {
skip!("Skip {} because kernel version `{}` doesn't match the requirement `{}`",
stringify!($name), version, version_requirement);
}
}
}
}
}

6
vendor/nix/test/mount/mod.rs vendored Normal file
View File

@@ -0,0 +1,6 @@
#[cfg(target_os = "linux")]
mod test_mount;
#[cfg(apple_targets)]
mod test_mount_apple;
#[cfg(target_os = "freebsd")]
mod test_nmount;

189
vendor/nix/test/mount/test_mount.rs vendored Normal file
View File

@@ -0,0 +1,189 @@
use std::fs::{self, File};
use std::io::{Read, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::fs::PermissionsExt;
use std::process::Command;
use libc::{EACCES, EROFS};
use nix::mount::{mount, umount, MsFlags};
use nix::sys::stat::{self, Mode};
use crate::*;
static SCRIPT_CONTENTS: &[u8] = b"#!/bin/sh
exit 23";
const EXPECTED_STATUS: i32 = 23;
const NONE: Option<&'static [u8]> = None;
#[test]
fn test_mount_tmpfs_without_flags_allows_rwx() {
require_capability!(
"test_mount_tmpfs_without_flags_allows_rwx",
CAP_SYS_ADMIN
);
let tempdir = tempfile::tempdir().unwrap();
mount(
NONE,
tempdir.path(),
Some(b"tmpfs".as_ref()),
MsFlags::empty(),
NONE,
)
.unwrap_or_else(|e| panic!("mount failed: {e}"));
let test_path = tempdir.path().join("test");
// Verify write.
fs::OpenOptions::new()
.create(true)
.write(true)
.mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
.open(&test_path)
.and_then(|mut f| f.write(SCRIPT_CONTENTS))
.unwrap_or_else(|e| panic!("write failed: {e}"));
// Verify read.
let mut buf = Vec::new();
File::open(&test_path)
.and_then(|mut f| f.read_to_end(&mut buf))
.unwrap_or_else(|e| panic!("read failed: {e}"));
assert_eq!(buf, SCRIPT_CONTENTS);
// while forking and unmounting prevent other child processes
let _m = FORK_MTX.lock();
// Verify execute.
assert_eq!(
EXPECTED_STATUS,
Command::new(&test_path)
.status()
.unwrap_or_else(|e| panic!("exec failed: {e}"))
.code()
.unwrap_or_else(|| panic!("child killed by signal"))
);
umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
}
#[test]
fn test_mount_rdonly_disallows_write() {
require_capability!("test_mount_rdonly_disallows_write", CAP_SYS_ADMIN);
let tempdir = tempfile::tempdir().unwrap();
mount(
NONE,
tempdir.path(),
Some(b"tmpfs".as_ref()),
MsFlags::MS_RDONLY,
NONE,
)
.unwrap_or_else(|e| panic!("mount failed: {e}"));
// EROFS: Read-only file system
assert_eq!(
EROFS,
File::create(tempdir.path().join("test"))
.unwrap_err()
.raw_os_error()
.unwrap()
);
umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
}
#[test]
fn test_mount_noexec_disallows_exec() {
require_capability!("test_mount_noexec_disallows_exec", CAP_SYS_ADMIN);
let tempdir = tempfile::tempdir().unwrap();
mount(
NONE,
tempdir.path(),
Some(b"tmpfs".as_ref()),
MsFlags::MS_NOEXEC,
NONE,
)
.unwrap_or_else(|e| panic!("mount failed: {e}"));
let test_path = tempdir.path().join("test");
fs::OpenOptions::new()
.create(true)
.write(true)
.mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
.open(&test_path)
.and_then(|mut f| f.write(SCRIPT_CONTENTS))
.unwrap_or_else(|e| panic!("write failed: {e}"));
// Verify that we cannot execute despite a+x permissions being set.
let mode = stat::Mode::from_bits_truncate(
fs::metadata(&test_path)
.map(|md| md.permissions().mode())
.unwrap_or_else(|e| panic!("metadata failed: {e}")),
);
assert!(
mode.contains(Mode::S_IXUSR | Mode::S_IXGRP | Mode::S_IXOTH),
"{:?} did not have execute permissions",
&test_path
);
// while forking and unmounting prevent other child processes
let _m = FORK_MTX.lock();
// EACCES: Permission denied
assert_eq!(
EACCES,
Command::new(&test_path)
.status()
.unwrap_err()
.raw_os_error()
.unwrap()
);
umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
}
#[test]
fn test_mount_bind() {
require_capability!("test_mount_bind", CAP_SYS_ADMIN);
let tempdir = tempfile::tempdir().unwrap();
let file_name = "test";
{
let mount_point = tempfile::tempdir().unwrap();
mount(
Some(tempdir.path()),
mount_point.path(),
NONE,
MsFlags::MS_BIND,
NONE,
)
.unwrap_or_else(|e| panic!("mount failed: {e}"));
fs::OpenOptions::new()
.create(true)
.write(true)
.mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
.open(mount_point.path().join(file_name))
.and_then(|mut f| f.write(SCRIPT_CONTENTS))
.unwrap_or_else(|e| panic!("write failed: {e}"));
// wait for child processes to prevent EBUSY
let _m = FORK_MTX.lock();
umount(mount_point.path())
.unwrap_or_else(|e| panic!("umount failed: {e}"));
}
// Verify the file written in the mount shows up in source directory, even
// after unmounting.
let mut buf = Vec::new();
File::open(tempdir.path().join(file_name))
.and_then(|mut f| f.read_to_end(&mut buf))
.unwrap_or_else(|e| panic!("read failed: {e}"));
assert_eq!(buf, SCRIPT_CONTENTS);
}

View File

@@ -0,0 +1,8 @@
use nix::errno::Errno;
use nix::mount::{mount, MntFlags};
#[test]
fn test_mount() {
let res = mount::<str, str, str>("", "", MntFlags::empty(), None);
assert_eq!(res, Err(Errno::ENOENT));
}

49
vendor/nix/test/mount/test_nmount.rs vendored Normal file
View File

@@ -0,0 +1,49 @@
use crate::*;
use nix::{
errno::Errno,
mount::{unmount, MntFlags, Nmount},
};
use std::{ffi::CString, fs::File, path::Path};
use tempfile::tempdir;
#[test]
fn ok() {
require_mount!("nullfs");
let mountpoint = tempdir().unwrap();
let target = tempdir().unwrap();
let _sentry = File::create(target.path().join("sentry")).unwrap();
let fstype = CString::new("fstype").unwrap();
let nullfs = CString::new("nullfs").unwrap();
Nmount::new()
.str_opt(&fstype, &nullfs)
.str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
.str_opt_owned("target", target.path().to_str().unwrap())
.nmount(MntFlags::empty())
.unwrap();
// Now check that the sentry is visible through the mountpoint
let exists = Path::exists(&mountpoint.path().join("sentry"));
// Cleanup the mountpoint before asserting
unmount(mountpoint.path(), MntFlags::empty()).unwrap();
assert!(exists);
}
#[test]
fn bad_fstype() {
let mountpoint = tempdir().unwrap();
let target = tempdir().unwrap();
let _sentry = File::create(target.path().join("sentry")).unwrap();
let e = Nmount::new()
.str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
.str_opt_owned("target", target.path().to_str().unwrap())
.nmount(MntFlags::empty())
.unwrap_err();
assert_eq!(e.error(), Errno::EINVAL);
assert_eq!(e.errmsg(), Some("Invalid fstype"));
}

98
vendor/nix/test/sys/mod.rs vendored Normal file
View File

@@ -0,0 +1,98 @@
mod test_signal;
// NOTE: DragonFly lacks a kernel-level implementation of Posix AIO as of
// this writing. There is an user-level implementation, but whether aio
// works or not heavily depends on which pthread implementation is chosen
// by the user at link time. For this reason we do not want to run aio test
// cases on DragonFly.
#[cfg(any(
target_os = "freebsd",
apple_targets,
all(
target_os = "linux",
not(any(target_env = "uclibc", target_env = "ohos"))
),
target_os = "netbsd"
))]
mod test_aio;
#[cfg(not(any(
target_os = "redox",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
target_os = "cygwin"
)))]
mod test_ioctl;
#[cfg(not(target_os = "redox"))]
mod test_mman;
#[cfg(not(target_os = "redox"))]
mod test_select;
#[cfg(target_os = "linux")]
mod test_signalfd;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
mod test_socket;
#[cfg(not(any(target_os = "redox")))]
mod test_sockopt;
mod test_stat;
#[cfg(linux_android)]
mod test_sysinfo;
#[cfg(not(any(
target_os = "redox",
target_os = "fuchsia",
target_os = "haiku"
)))]
mod test_termios;
mod test_uio;
mod test_wait;
#[cfg(linux_android)]
mod test_epoll;
#[cfg(target_os = "linux")]
mod test_fanotify;
#[cfg(target_os = "linux")]
mod test_inotify;
mod test_pthread;
#[cfg(any(linux_android, freebsdlike, netbsdlike, apple_targets))]
mod test_ptrace;
#[cfg(linux_android)]
mod test_timerfd;
#[cfg(all(
any(
target_os = "freebsd",
solarish,
target_os = "linux",
target_os = "netbsd"
),
feature = "time",
feature = "signal"
))]
mod test_timer;
#[cfg(bsd)]
mod test_event;
mod test_statvfs;
mod test_time;
mod test_utsname;
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "openbsd"))]
mod test_statfs;
#[cfg(not(any(
target_os = "redox",
target_os = "fuchsia",
solarish,
target_os = "haiku"
)))]
mod test_resource;
// This test module should be enabled for both linux_android and freebsd, but
// the `memfd_create(2)` symbol is not available under Linux QEMU,
//
// https://github.com/nix-rust/nix/actions/runs/9427112650/job/25970870477
//
// and I haven't found a way to stop the linker from linking that symbol, so
// only enable this for FreeBSD for now.
#[cfg(target_os = "freebsd")]
mod test_memfd;

685
vendor/nix/test/sys/test_aio.rs vendored Normal file
View File

@@ -0,0 +1,685 @@
use std::{
io::{Read, Seek, Write},
ops::Deref,
os::unix::io::{AsFd, AsRawFd, BorrowedFd},
pin::Pin,
sync::atomic::{AtomicBool, Ordering},
thread, time,
};
use libc::c_int;
use nix::{
errno::*,
sys::{
aio::*,
signal::{
sigaction, SaFlags, SigAction, SigHandler, SigSet, SigevNotify,
Signal,
},
time::{TimeSpec, TimeValLike},
},
};
use tempfile::tempfile;
pub static SIGNALED: AtomicBool = AtomicBool::new(false);
extern "C" fn sigfunc(_: c_int) {
SIGNALED.store(true, Ordering::Relaxed);
}
// Helper that polls an AioCb for completion or error
macro_rules! poll_aio {
($aiocb: expr) => {
loop {
let err = $aiocb.as_mut().error();
if err != Err(Errno::EINPROGRESS) {
break err;
};
thread::sleep(time::Duration::from_millis(10));
}
};
}
mod aio_fsync {
use super::*;
#[test]
fn test_accessors() {
let f = tempfile().unwrap();
let aiocb = AioFsync::new(
f.as_fd(),
AioFsyncMode::O_SYNC,
42,
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 99,
},
);
assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
assert_eq!(AioFsyncMode::O_SYNC, aiocb.mode());
assert_eq!(42, aiocb.priority());
let sev = aiocb.sigevent().sigevent();
assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
assert_eq!(99, sev.sigev_value.sival_ptr as i64);
}
/// `AioFsync::submit` should not modify the `AioCb` object if
/// `libc::aio_fsync` returns an error
// Skip on Linux, because Linux's AIO implementation can't detect errors
// synchronously
#[test]
#[cfg_attr(any(target_os = "android", target_os = "linux"), ignore)]
fn error() {
use std::mem;
const INITIAL: &[u8] = b"abcdef123456";
// Create an invalid AioFsyncMode
let mode = unsafe { mem::transmute::<i32, AioFsyncMode>(666) };
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aiof =
Box::pin(AioFsync::new(f.as_fd(), mode, 0, SigevNotify::SigevNone));
let err = aiof.as_mut().submit();
err.expect_err("assertion failed");
}
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn ok() {
const INITIAL: &[u8] = b"abcdef123456";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aiof = Box::pin(AioFsync::new(
f.as_fd(),
AioFsyncMode::O_SYNC,
0,
SigevNotify::SigevNone,
));
aiof.as_mut().submit().unwrap();
poll_aio!(&mut aiof).unwrap();
aiof.as_mut().aio_return().unwrap();
}
}
mod aio_read {
use super::*;
#[test]
fn test_accessors() {
let f = tempfile().unwrap();
let mut rbuf = vec![0; 4];
let aiocb = AioRead::new(
f.as_fd(),
2, //offset
&mut rbuf,
42, //priority
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 99,
},
);
assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
assert_eq!(4, aiocb.nbytes());
assert_eq!(2, aiocb.offset());
assert_eq!(42, aiocb.priority());
let sev = aiocb.sigevent().sigevent();
assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
assert_eq!(99, sev.sigev_value.sival_ptr as i64);
}
// Tests AioWrite.cancel. We aren't trying to test the OS's implementation,
// only our bindings. So it's sufficient to check that cancel
// returned any AioCancelStat value.
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn cancel() {
const INITIAL: &[u8] = b"abcdef123456";
let mut rbuf = vec![0; 4];
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let fd = f.as_fd();
let mut aior =
Box::pin(AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone));
aior.as_mut().submit().unwrap();
aior.as_mut().cancel().unwrap();
// Wait for aiow to complete, but don't care whether it succeeded
let _ = poll_aio!(&mut aior);
let _ = aior.as_mut().aio_return();
}
/// `AioRead::submit` should not modify the `AioCb` object if
/// `libc::aio_read` returns an error
// Skip on Linux, because Linux's AIO implementation can't detect errors
// synchronously
#[test]
#[cfg(any(target_os = "freebsd", apple_targets))]
fn error() {
const INITIAL: &[u8] = b"abcdef123456";
let mut rbuf = vec![0; 4];
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut aior = Box::pin(AioRead::new(
f.as_fd(),
-1, //an invalid offset
&mut rbuf,
0, //priority
SigevNotify::SigevNone,
));
aior.as_mut().submit().expect_err("assertion failed");
}
// Test a simple aio operation with no completion notification. We must
// poll for completion
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn ok() {
const INITIAL: &[u8] = b"abcdef123456";
let mut rbuf = vec![0; 4];
const EXPECT: &[u8] = b"cdef";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
{
let fd = f.as_fd();
let mut aior = Box::pin(AioRead::new(
fd,
2,
&mut rbuf,
0,
SigevNotify::SigevNone,
));
aior.as_mut().submit().unwrap();
let err = poll_aio!(&mut aior);
assert_eq!(err, Ok(()));
assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len());
}
assert_eq!(EXPECT, rbuf.deref());
}
// Like ok, but allocates the structure on the stack.
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn on_stack() {
const INITIAL: &[u8] = b"abcdef123456";
let mut rbuf = vec![0; 4];
const EXPECT: &[u8] = b"cdef";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
{
let fd = f.as_fd();
let mut aior =
AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone);
let mut aior = unsafe { Pin::new_unchecked(&mut aior) };
aior.as_mut().submit().unwrap();
let err = poll_aio!(&mut aior);
assert_eq!(err, Ok(()));
assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len());
}
assert_eq!(EXPECT, rbuf.deref());
}
}
#[cfg(target_os = "freebsd")]
#[cfg(fbsd14)]
mod aio_readv {
use std::io::IoSliceMut;
use super::*;
#[test]
fn test_accessors() {
let f = tempfile().unwrap();
let mut rbuf0 = vec![0; 4];
let mut rbuf1 = vec![0; 8];
let mut rbufs =
[IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
let aiocb = AioReadv::new(
f.as_fd(),
2, //offset
&mut rbufs,
42, //priority
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 99,
},
);
assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
assert_eq!(2, aiocb.iovlen());
assert_eq!(2, aiocb.offset());
assert_eq!(42, aiocb.priority());
let sev = aiocb.sigevent().sigevent();
assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
assert_eq!(99, sev.sigev_value.sival_ptr as i64);
}
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn ok() {
const INITIAL: &[u8] = b"abcdef123456";
let mut rbuf0 = vec![0; 4];
let mut rbuf1 = vec![0; 2];
let mut rbufs =
[IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
const EXPECT0: &[u8] = b"cdef";
const EXPECT1: &[u8] = b"12";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
{
let fd = f.as_fd();
let mut aior = Box::pin(AioReadv::new(
fd,
2,
&mut rbufs,
0,
SigevNotify::SigevNone,
));
aior.as_mut().submit().unwrap();
let err = poll_aio!(&mut aior);
assert_eq!(err, Ok(()));
assert_eq!(
aior.as_mut().aio_return().unwrap(),
EXPECT0.len() + EXPECT1.len()
);
}
assert_eq!(&EXPECT0, &rbuf0);
assert_eq!(&EXPECT1, &rbuf1);
}
}
mod aio_write {
use super::*;
#[test]
fn test_accessors() {
let f = tempfile().unwrap();
let wbuf = vec![0; 4];
let aiocb = AioWrite::new(
f.as_fd(),
2, //offset
&wbuf,
42, //priority
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 99,
},
);
assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
assert_eq!(4, aiocb.nbytes());
assert_eq!(2, aiocb.offset());
assert_eq!(42, aiocb.priority());
let sev = aiocb.sigevent().sigevent();
assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
assert_eq!(99, sev.sigev_value.sival_ptr as i64);
}
// Tests AioWrite.cancel. We aren't trying to test the OS's implementation,
// only our bindings. So it's sufficient to check that cancel
// returned any AioCancelStat value.
#[test]
#[cfg_attr(target_env = "musl", ignore)]
fn cancel() {
let wbuf: &[u8] = b"CDEF";
let f = tempfile().unwrap();
let mut aiow = Box::pin(AioWrite::new(
f.as_fd(),
0,
wbuf,
0,
SigevNotify::SigevNone,
));
aiow.as_mut().submit().unwrap();
let err = aiow.as_mut().error();
assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
aiow.as_mut().cancel().unwrap();
// Wait for aiow to complete, but don't care whether it succeeded
let _ = poll_aio!(&mut aiow);
let _ = aiow.as_mut().aio_return();
}
// Test a simple aio operation with no completion notification. We must
// poll for completion.
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn ok() {
const INITIAL: &[u8] = b"abcdef123456";
let wbuf = "CDEF".to_string().into_bytes();
let mut rbuf = Vec::new();
const EXPECT: &[u8] = b"abCDEF123456";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
{
let mut aiow = Box::pin(AioWrite::new(
f.as_fd(),
2,
&wbuf,
0,
SigevNotify::SigevNone,
));
aiow.as_mut().submit().unwrap();
let err = poll_aio!(&mut aiow);
assert_eq!(err, Ok(()));
assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
}
f.rewind().unwrap();
let len = f.read_to_end(&mut rbuf).unwrap();
assert_eq!(len, EXPECT.len());
assert_eq!(rbuf, EXPECT);
}
// Like ok, but allocates the structure on the stack.
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn on_stack() {
const INITIAL: &[u8] = b"abcdef123456";
let wbuf = "CDEF".to_string().into_bytes();
let mut rbuf = Vec::new();
const EXPECT: &[u8] = b"abCDEF123456";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
{
let mut aiow = AioWrite::new(
f.as_fd(),
2, //offset
&wbuf,
0, //priority
SigevNotify::SigevNone,
);
let mut aiow = unsafe { Pin::new_unchecked(&mut aiow) };
aiow.as_mut().submit().unwrap();
let err = poll_aio!(&mut aiow);
assert_eq!(err, Ok(()));
assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
}
f.rewind().unwrap();
let len = f.read_to_end(&mut rbuf).unwrap();
assert_eq!(len, EXPECT.len());
assert_eq!(rbuf, EXPECT);
}
/// `AioWrite::write` should not modify the `AioCb` object if
/// `libc::aio_write` returns an error.
// Skip on Linux, because Linux's AIO implementation can't detect errors
// synchronously
#[test]
#[cfg_attr(any(target_os = "android", target_os = "linux"), ignore)]
fn error() {
// Not I/O safe! Deliberately create an invalid fd.
let fd = unsafe { BorrowedFd::borrow_raw(666) };
let wbuf = "CDEF".to_string().into_bytes();
let mut aiow = Box::pin(AioWrite::new(
fd,
0, //offset
&wbuf,
0, //priority
SigevNotify::SigevNone,
));
aiow.as_mut().submit().expect_err("assertion failed");
// Dropping the AioWrite at this point should not panic
}
}
#[cfg(target_os = "freebsd")]
#[cfg(fbsd14)]
mod aio_writev {
use std::io::IoSlice;
use super::*;
#[test]
fn test_accessors() {
let f = tempfile().unwrap();
let wbuf0 = vec![0; 4];
let wbuf1 = vec![0; 8];
let wbufs = [IoSlice::new(&wbuf0), IoSlice::new(&wbuf1)];
let aiocb = AioWritev::new(
f.as_fd(),
2, //offset
&wbufs,
42, //priority
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 99,
},
);
assert_eq!(f.as_raw_fd(), aiocb.fd().as_raw_fd());
assert_eq!(2, aiocb.iovlen());
assert_eq!(2, aiocb.offset());
assert_eq!(42, aiocb.priority());
let sev = aiocb.sigevent().sigevent();
assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
assert_eq!(99, sev.sigev_value.sival_ptr as i64);
}
// Test a simple aio operation with no completion notification. We must
// poll for completion.
#[test]
#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
fn ok() {
const INITIAL: &[u8] = b"abcdef123456";
let wbuf0 = b"BC";
let wbuf1 = b"DEF";
let wbufs = [IoSlice::new(wbuf0), IoSlice::new(wbuf1)];
let wlen = wbuf0.len() + wbuf1.len();
let mut rbuf = Vec::new();
const EXPECT: &[u8] = b"aBCDEF123456";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
{
let mut aiow = Box::pin(AioWritev::new(
f.as_fd(),
1,
&wbufs,
0,
SigevNotify::SigevNone,
));
aiow.as_mut().submit().unwrap();
let err = poll_aio!(&mut aiow);
assert_eq!(err, Ok(()));
assert_eq!(aiow.as_mut().aio_return().unwrap(), wlen);
}
f.rewind().unwrap();
let len = f.read_to_end(&mut rbuf).unwrap();
assert_eq!(len, EXPECT.len());
assert_eq!(rbuf, EXPECT);
}
}
// Test an aio operation with completion delivered by a signal
#[test]
#[cfg_attr(
any(
all(target_env = "musl", target_arch = "x86_64"),
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
),
ignore
)]
fn sigev_signal() {
let _m = crate::SIGNAL_MTX.lock();
let sa = SigAction::new(
SigHandler::Handler(sigfunc),
SaFlags::SA_RESETHAND,
SigSet::empty(),
);
SIGNALED.store(false, Ordering::Relaxed);
unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
const INITIAL: &[u8] = b"abcdef123456";
const WBUF: &[u8] = b"CDEF";
let mut rbuf = Vec::new();
const EXPECT: &[u8] = b"abCDEF123456";
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
{
let mut aiow = Box::pin(AioWrite::new(
f.as_fd(),
2, //offset
WBUF,
0, //priority
SigevNotify::SigevSignal {
signal: Signal::SIGUSR2,
si_value: 0, //TODO: validate in sigfunc
},
));
aiow.as_mut().submit().unwrap();
while !SIGNALED.load(Ordering::Relaxed) {
thread::sleep(time::Duration::from_millis(10));
}
assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
}
f.rewind().unwrap();
let len = f.read_to_end(&mut rbuf).unwrap();
assert_eq!(len, EXPECT.len());
assert_eq!(rbuf, EXPECT);
}
// Tests using aio_cancel_all for all outstanding IOs.
#[test]
#[cfg_attr(target_env = "musl", ignore)]
fn test_aio_cancel_all() {
let wbuf: &[u8] = b"CDEF";
let f = tempfile().unwrap();
let mut aiocb = Box::pin(AioWrite::new(
f.as_fd(),
0, //offset
wbuf,
0, //priority
SigevNotify::SigevNone,
));
aiocb.as_mut().submit().unwrap();
let err = aiocb.as_mut().error();
assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
aio_cancel_all(f.as_fd()).unwrap();
// Wait for aiocb to complete, but don't care whether it succeeded
let _ = poll_aio!(&mut aiocb);
let _ = aiocb.as_mut().aio_return();
}
#[test]
fn test_aio_suspend() {
const INITIAL: &[u8] = b"abcdef123456";
const WBUF: &[u8] = b"CDEFG";
let timeout = TimeSpec::seconds(10);
let mut rbuf = vec![0; 4];
let rlen = rbuf.len();
let mut f = tempfile().unwrap();
f.write_all(INITIAL).unwrap();
let mut wcb = Box::pin(AioWrite::new(
f.as_fd(),
2, //offset
WBUF,
0, //priority
SigevNotify::SigevNone,
));
let mut rcb = Box::pin(AioRead::new(
f.as_fd(),
8, //offset
&mut rbuf,
0, //priority
SigevNotify::SigevNone,
));
wcb.as_mut().submit().unwrap();
rcb.as_mut().submit().unwrap();
loop {
{
let cbbuf = [
&*wcb as &dyn AsRef<libc::aiocb>,
&*rcb as &dyn AsRef<libc::aiocb>,
];
let r = aio_suspend(&cbbuf[..], Some(timeout));
match r {
Err(Errno::EINTR) => continue,
Err(e) => panic!("aio_suspend returned {e:?}"),
Ok(_) => (),
};
}
if rcb.as_mut().error() != Err(Errno::EINPROGRESS)
&& wcb.as_mut().error() != Err(Errno::EINPROGRESS)
{
break;
}
}
assert_eq!(wcb.as_mut().aio_return().unwrap(), WBUF.len());
assert_eq!(rcb.as_mut().aio_return().unwrap(), rlen);
}
/// aio_suspend relies on casting Rust Aio* struct pointers to libc::aiocb
/// pointers. This test ensures that such casts are valid.
#[test]
fn casting() {
let sev = SigevNotify::SigevNone;
// Only safe because we'll never await the futures
let fd = unsafe { BorrowedFd::borrow_raw(666) };
let aiof = AioFsync::new(fd, AioFsyncMode::O_SYNC, 0, sev);
assert_eq!(
aiof.as_ref() as *const libc::aiocb,
&aiof as *const AioFsync as *const libc::aiocb
);
let mut rbuf = [];
let aior = AioRead::new(fd, 0, &mut rbuf, 0, sev);
assert_eq!(
aior.as_ref() as *const libc::aiocb,
&aior as *const AioRead as *const libc::aiocb
);
let wbuf = [];
let aiow = AioWrite::new(fd, 0, &wbuf, 0, sev);
assert_eq!(
aiow.as_ref() as *const libc::aiocb,
&aiow as *const AioWrite as *const libc::aiocb
);
}
#[cfg(target_os = "freebsd")]
#[test]
fn casting_vectored() {
use std::io::{IoSlice, IoSliceMut};
let sev = SigevNotify::SigevNone;
let mut rbuf = [];
let mut rbufs = [IoSliceMut::new(&mut rbuf)];
// Only safe because we'll never await the futures
let fd = unsafe { BorrowedFd::borrow_raw(666) };
let aiorv = AioReadv::new(fd, 0, &mut rbufs[..], 0, sev);
assert_eq!(
aiorv.as_ref() as *const libc::aiocb,
&aiorv as *const AioReadv as *const libc::aiocb
);
let wbuf = [];
let wbufs = [IoSlice::new(&wbuf)];
let aiowv = AioWritev::new(fd, 0, &wbufs, 0, sev);
assert_eq!(
aiowv.as_ref() as *const libc::aiocb,
&aiowv as *const AioWritev as *const libc::aiocb
);
}

35
vendor/nix/test/sys/test_aio_drop.rs vendored Normal file
View File

@@ -0,0 +1,35 @@
// Test dropping an AioCb that hasn't yet finished.
// This must happen in its own process, because on OSX this test seems to hose
// the AIO subsystem and causes subsequent tests to fail
#[test]
#[should_panic(expected = "Dropped an in-progress AioCb")]
#[cfg(all(
not(target_env = "musl"),
not(target_env = "uclibc"),
not(target_env = "ohos"),
any(
target_os = "linux",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
)
))]
fn test_drop() {
use nix::sys::aio::*;
use nix::sys::signal::*;
use std::os::unix::io::AsFd;
use tempfile::tempfile;
const WBUF: &[u8] = b"CDEF";
let f = tempfile().unwrap();
f.set_len(6).unwrap();
let mut aiocb = Box::pin(AioWrite::new(
f.as_fd(),
2, //offset
WBUF,
0, //priority
SigevNotify::SigevNone,
));
aiocb.as_mut().submit().unwrap();
}

26
vendor/nix/test/sys/test_epoll.rs vendored Normal file
View File

@@ -0,0 +1,26 @@
#![allow(deprecated)]
use nix::errno::Errno;
use nix::sys::epoll::{epoll_create1, epoll_ctl};
use nix::sys::epoll::{EpollCreateFlags, EpollEvent, EpollFlags, EpollOp};
#[test]
pub fn test_epoll_errno() {
let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
let result = epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None);
result.expect_err("assertion failed");
assert_eq!(result.unwrap_err(), Errno::ENOENT);
let result = epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, None);
result.expect_err("assertion failed");
assert_eq!(result.unwrap_err(), Errno::EINVAL);
}
#[test]
pub fn test_epoll_ctl() {
let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
let mut event =
EpollEvent::new(EpollFlags::EPOLLIN | EpollFlags::EPOLLERR, 1);
epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, &mut event).unwrap();
epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None).unwrap();
}

41
vendor/nix/test/sys/test_event.rs vendored Normal file
View File

@@ -0,0 +1,41 @@
use libc::intptr_t;
use nix::sys::event::{EvFlags, EventFilter, FilterFlag, KEvent};
#[test]
fn test_struct_kevent() {
use std::mem;
let udata: intptr_t = 12345;
let data: intptr_t = 0x1337;
let actual = KEvent::new(
0xdead_beef,
EventFilter::EVFILT_READ,
EvFlags::EV_ONESHOT | EvFlags::EV_ADD,
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
data,
udata,
);
assert_eq!(0xdead_beef, actual.ident());
assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
assert_eq!(data, actual.data());
assert_eq!(udata, actual.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,
EvFlags::EV_ONESHOT | EvFlags::EV_ADD,
FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
0x1337,
udata,
);
assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
}

220
vendor/nix/test/sys/test_fanotify.rs vendored Normal file
View File

@@ -0,0 +1,220 @@
use crate::*;
use nix::errno::Errno;
use nix::fcntl::AT_FDCWD;
use nix::sys::fanotify::{
EventFFlags, Fanotify, FanotifyResponse, InitFlags, MarkFlags, MaskFlags,
Response,
};
use std::fs::{read_link, read_to_string, File, OpenOptions};
use std::io::ErrorKind;
use std::io::{Read, Write};
use std::os::fd::AsRawFd;
use std::thread;
#[test]
/// Run fanotify tests sequentially to avoid tmp files races
pub fn test_fanotify() {
require_capability!("test_fanotify", CAP_SYS_ADMIN);
test_fanotify_notifications();
test_fanotify_responses();
test_fanotify_overflow();
}
fn test_fanotify_notifications() {
let group =
Fanotify::init(InitFlags::FAN_CLASS_NOTIF, EventFFlags::O_RDONLY)
.unwrap();
let tempdir = tempfile::tempdir().unwrap();
let tempfile = tempdir.path().join("test");
OpenOptions::new()
.write(true)
.create_new(true)
.open(&tempfile)
.unwrap();
group
.mark(
MarkFlags::FAN_MARK_ADD,
MaskFlags::FAN_OPEN | MaskFlags::FAN_MODIFY | MaskFlags::FAN_CLOSE,
AT_FDCWD,
Some(&tempfile),
)
.unwrap();
// modify test file
{
let mut f = OpenOptions::new().write(true).open(&tempfile).unwrap();
f.write_all(b"hello").unwrap();
}
let mut events = group.read_events().unwrap();
assert_eq!(events.len(), 1, "should have read exactly one event");
let event = events.pop().unwrap();
assert!(event.check_version());
assert_eq!(
event.mask(),
MaskFlags::FAN_OPEN
| MaskFlags::FAN_MODIFY
| MaskFlags::FAN_CLOSE_WRITE
);
let fd_opt = event.fd();
let fd = fd_opt.as_ref().unwrap();
let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
assert_eq!(path, tempfile);
// read test file
{
let mut f = File::open(&tempfile).unwrap();
let mut s = String::new();
f.read_to_string(&mut s).unwrap();
}
let mut events = group.read_events().unwrap();
assert_eq!(events.len(), 1, "should have read exactly one event");
let event = events.pop().unwrap();
assert!(event.check_version());
assert_eq!(
event.mask(),
MaskFlags::FAN_OPEN | MaskFlags::FAN_CLOSE_NOWRITE
);
let fd_opt = event.fd();
let fd = fd_opt.as_ref().unwrap();
let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
assert_eq!(path, tempfile);
}
fn test_fanotify_responses() {
let group =
Fanotify::init(InitFlags::FAN_CLASS_CONTENT, EventFFlags::O_RDONLY)
.unwrap();
let tempdir = tempfile::tempdir().unwrap();
let tempfile = tempdir.path().join("test");
OpenOptions::new()
.write(true)
.create_new(true)
.open(&tempfile)
.unwrap();
group
.mark(
MarkFlags::FAN_MARK_ADD,
MaskFlags::FAN_OPEN_PERM,
AT_FDCWD,
Some(&tempfile),
)
.unwrap();
let file_thread = thread::spawn({
let tempfile = tempfile.clone();
move || {
// first open, should fail
let Err(e) = File::open(&tempfile) else {
panic!("The first open should fail");
};
assert_eq!(e.kind(), ErrorKind::PermissionDenied);
// second open, should succeed
File::open(&tempfile).unwrap();
}
});
// Deny the first open try
let mut events = group.read_events().unwrap();
assert_eq!(events.len(), 1, "should have read exactly one event");
let event = events.pop().unwrap();
assert!(event.check_version());
assert_eq!(event.mask(), MaskFlags::FAN_OPEN_PERM);
let fd_opt = event.fd();
let fd = fd_opt.as_ref().unwrap();
let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
assert_eq!(path, tempfile);
group
.write_response(FanotifyResponse::new(*fd, Response::FAN_DENY))
.unwrap();
// Allow the second open try
let mut events = group.read_events().unwrap();
assert_eq!(events.len(), 1, "should have read exactly one event");
let event = events.pop().unwrap();
assert!(event.check_version());
assert_eq!(event.mask(), MaskFlags::FAN_OPEN_PERM);
let fd_opt = event.fd();
let fd = fd_opt.as_ref().unwrap();
let path = read_link(format!("/proc/self/fd/{}", fd.as_raw_fd())).unwrap();
assert_eq!(path, tempfile);
group
.write_response(FanotifyResponse::new(*fd, Response::FAN_ALLOW))
.unwrap();
file_thread.join().unwrap();
}
fn test_fanotify_overflow() {
let max_events: usize =
read_to_string("/proc/sys/fs/fanotify/max_queued_events")
.unwrap()
.trim()
.parse()
.unwrap();
// make sure the kernel is configured with the default value,
// just so this test doesn't run forever
assert_eq!(max_events, 16384);
let group = Fanotify::init(
InitFlags::FAN_CLASS_NOTIF
| InitFlags::FAN_REPORT_TID
| InitFlags::FAN_NONBLOCK,
EventFFlags::O_RDONLY,
)
.unwrap();
let tempdir = tempfile::tempdir().unwrap();
let tempfile = tempdir.path().join("test");
OpenOptions::new()
.write(true)
.create_new(true)
.open(&tempfile)
.unwrap();
group
.mark(
MarkFlags::FAN_MARK_ADD,
MaskFlags::FAN_OPEN,
AT_FDCWD,
Some(&tempfile),
)
.unwrap();
thread::scope(|s| {
// perform 10 more events to demonstrate some will be dropped
for _ in 0..(max_events + 10) {
s.spawn(|| {
File::open(&tempfile).unwrap();
});
}
});
// flush the queue until it's empty
let mut n = 0;
let mut last_event = None;
loop {
match group.read_events() {
Ok(events) => {
n += events.len();
if let Some(event) = events.last() {
last_event = Some(event.mask());
}
}
Err(e) if e == Errno::EWOULDBLOCK => break,
Err(e) => panic!("{e:?}"),
}
}
// make sure we read all we expected.
// the +1 is for the overflow event.
assert_eq!(n, max_events + 1);
assert_eq!(last_event, Some(MaskFlags::FAN_Q_OVERFLOW));
}

65
vendor/nix/test/sys/test_inotify.rs vendored Normal file
View File

@@ -0,0 +1,65 @@
use nix::errno::Errno;
use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify};
use std::ffi::OsString;
use std::fs::{rename, File};
#[test]
pub fn test_inotify() {
let instance = Inotify::init(InitFlags::IN_NONBLOCK).unwrap();
let tempdir = tempfile::tempdir().unwrap();
instance
.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS)
.unwrap();
let events = instance.read_events();
assert_eq!(events.unwrap_err(), Errno::EAGAIN);
File::create(tempdir.path().join("test")).unwrap();
let events = instance.read_events().unwrap();
assert_eq!(events[0].name, Some(OsString::from("test")));
}
#[test]
pub fn test_inotify_multi_events() {
let instance = Inotify::init(InitFlags::IN_NONBLOCK).unwrap();
let tempdir = tempfile::tempdir().unwrap();
instance
.add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS)
.unwrap();
let events = instance.read_events();
assert_eq!(events.unwrap_err(), Errno::EAGAIN);
File::create(tempdir.path().join("test")).unwrap();
rename(tempdir.path().join("test"), tempdir.path().join("test2")).unwrap();
// Now there should be 5 events in queue:
// - IN_CREATE on test
// - IN_OPEN on test
// - IN_CLOSE_WRITE on test
// - IN_MOVED_FROM on test with a cookie
// - IN_MOVED_TO on test2 with the same cookie
let events = instance.read_events().unwrap();
assert_eq!(events.len(), 5);
assert_eq!(events[0].mask, AddWatchFlags::IN_CREATE);
assert_eq!(events[0].name, Some(OsString::from("test")));
assert_eq!(events[1].mask, AddWatchFlags::IN_OPEN);
assert_eq!(events[1].name, Some(OsString::from("test")));
assert_eq!(events[2].mask, AddWatchFlags::IN_CLOSE_WRITE);
assert_eq!(events[2].name, Some(OsString::from("test")));
assert_eq!(events[3].mask, AddWatchFlags::IN_MOVED_FROM);
assert_eq!(events[3].name, Some(OsString::from("test")));
assert_eq!(events[4].mask, AddWatchFlags::IN_MOVED_TO);
assert_eq!(events[4].name, Some(OsString::from("test2")));
assert_eq!(events[3].cookie, events[4].cookie);
}

383
vendor/nix/test/sys/test_ioctl.rs vendored Normal file
View File

@@ -0,0 +1,383 @@
#![allow(dead_code)]
// Simple tests to ensure macro generated fns compile
ioctl_none_bad!(do_bad, 0x1234);
ioctl_read_bad!(do_bad_read, 0x1234, u16);
ioctl_write_int_bad!(do_bad_write_int, 0x1234);
ioctl_write_ptr_bad!(do_bad_write_ptr, 0x1234, u8);
ioctl_readwrite_bad!(do_bad_readwrite, 0x1234, u32);
ioctl_none!(do_none, 0, 0);
ioctl_read!(read_test, 0, 0, u32);
ioctl_write_int!(write_ptr_int, 0, 0);
ioctl_write_ptr!(write_ptr_u8, 0, 0, u8);
ioctl_write_ptr!(write_ptr_u32, 0, 0, u32);
ioctl_write_ptr!(write_ptr_u64, 0, 0, u64);
ioctl_readwrite!(readwrite_test, 0, 0, u64);
ioctl_read_buf!(readbuf_test, 0, 0, u32);
const SPI_IOC_MAGIC: u8 = b'k';
const SPI_IOC_MESSAGE: u8 = 0;
ioctl_write_buf!(writebuf_test_consts, SPI_IOC_MAGIC, SPI_IOC_MESSAGE, u8);
ioctl_write_buf!(writebuf_test_u8, 0, 0, u8);
ioctl_write_buf!(writebuf_test_u32, 0, 0, u32);
ioctl_write_buf!(writebuf_test_u64, 0, 0, u64);
ioctl_readwrite_buf!(readwritebuf_test, 0, 0, u32);
// See C code for source of values for op calculations (does NOT work for mips/powerpc):
// https://gist.github.com/posborne/83ea6880770a1aef332e
//
// TODO: Need a way to compute these constants at test time. Using precomputed
// values is fragile and needs to be maintained.
#[cfg(linux_android)]
mod linux {
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
#[test]
fn test_op_none() {
if cfg!(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64"
)) {
assert_eq!(request_code_none!(b'q', 10) as u32, 0x2000_710A);
assert_eq!(request_code_none!(b'a', 255) as u32, 0x2000_61FF);
} else {
assert_eq!(request_code_none!(b'q', 10) as u32, 0x0000_710A);
assert_eq!(request_code_none!(b'a', 255) as u32, 0x0000_61FF);
}
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
#[test]
fn test_op_write() {
if cfg!(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64"
)) {
assert_eq!(request_code_write!(b'z', 10, 1) as u32, 0x8001_7A0A);
assert_eq!(request_code_write!(b'z', 10, 512) as u32, 0x8200_7A0A);
} else {
assert_eq!(request_code_write!(b'z', 10, 1) as u32, 0x4001_7A0A);
assert_eq!(request_code_write!(b'z', 10, 512) as u32, 0x4200_7A0A);
}
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_write_64() {
if cfg!(any(
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc64"
)) {
assert_eq!(
request_code_write!(b'z', 10, 1u64 << 32) as u32,
0x8000_7A0A
);
} else {
assert_eq!(
request_code_write!(b'z', 10, 1u64 << 32) as u32,
0x4000_7A0A
);
}
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
#[test]
fn test_op_read() {
if cfg!(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64"
)) {
assert_eq!(request_code_read!(b'z', 10, 1) as u32, 0x4001_7A0A);
assert_eq!(request_code_read!(b'z', 10, 512) as u32, 0x4200_7A0A);
} else {
assert_eq!(request_code_read!(b'z', 10, 1) as u32, 0x8001_7A0A);
assert_eq!(request_code_read!(b'z', 10, 512) as u32, 0x8200_7A0A);
}
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_read_64() {
if cfg!(any(
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc64"
)) {
assert_eq!(
request_code_read!(b'z', 10, 1u64 << 32) as u32,
0x4000_7A0A
);
} else {
assert_eq!(
request_code_read!(b'z', 10, 1u64 << 32) as u32,
0x8000_7A0A
);
}
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
#[test]
fn test_op_read_write() {
assert_eq!(request_code_readwrite!(b'z', 10, 1) as u32, 0xC001_7A0A);
assert_eq!(request_code_readwrite!(b'z', 10, 512) as u32, 0xC200_7A0A);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_read_write_64() {
assert_eq!(
request_code_readwrite!(b'z', 10, 1u64 << 32) as u32,
0xC000_7A0A
);
}
}
#[cfg(bsd)]
mod bsd {
#[test]
fn test_op_none() {
assert_eq!(request_code_none!(b'q', 10), 0x2000_710A);
assert_eq!(request_code_none!(b'a', 255), 0x2000_61FF);
}
#[cfg(freebsdlike)]
#[test]
fn test_op_write_int() {
assert_eq!(request_code_write_int!(b'v', 4), 0x2004_7604);
assert_eq!(request_code_write_int!(b'p', 2), 0x2004_7002);
}
#[test]
fn test_op_write() {
assert_eq!(request_code_write!(b'z', 10, 1), 0x8001_7A0A);
assert_eq!(request_code_write!(b'z', 10, 512), 0x8200_7A0A);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_write_64() {
assert_eq!(request_code_write!(b'z', 10, 1u64 << 32), 0x8000_7A0A);
}
#[test]
fn test_op_read() {
assert_eq!(request_code_read!(b'z', 10, 1), 0x4001_7A0A);
assert_eq!(request_code_read!(b'z', 10, 512), 0x4200_7A0A);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_read_64() {
assert_eq!(request_code_read!(b'z', 10, 1u64 << 32), 0x4000_7A0A);
}
#[test]
fn test_op_read_write() {
assert_eq!(request_code_readwrite!(b'z', 10, 1), 0xC001_7A0A);
assert_eq!(request_code_readwrite!(b'z', 10, 512), 0xC200_7A0A);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_op_read_write_64() {
assert_eq!(request_code_readwrite!(b'z', 10, 1u64 << 32), 0xC000_7A0A);
}
}
#[cfg(linux_android)]
mod linux_ioctls {
use std::mem;
use std::os::unix::io::AsRawFd;
use libc::{termios, TCGETS, TCSBRK, TCSETS, TIOCNXCL};
use tempfile::tempfile;
use nix::errno::Errno;
ioctl_none_bad!(tiocnxcl, TIOCNXCL);
#[test]
fn test_ioctl_none_bad() {
let file = tempfile().unwrap();
let res = unsafe { tiocnxcl(file.as_raw_fd()) };
assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_read_bad!(tcgets, TCGETS, termios);
#[test]
fn test_ioctl_read_bad() {
let file = tempfile().unwrap();
let mut termios = unsafe { mem::zeroed() };
let res = unsafe { tcgets(file.as_raw_fd(), &mut termios) };
assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_write_int_bad!(tcsbrk, TCSBRK);
#[test]
fn test_ioctl_write_int_bad() {
let file = tempfile().unwrap();
let res = unsafe { tcsbrk(file.as_raw_fd(), 0) };
assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_write_ptr_bad!(tcsets, TCSETS, termios);
#[test]
fn test_ioctl_write_ptr_bad() {
let file = tempfile().unwrap();
let termios: termios = unsafe { mem::zeroed() };
let res = unsafe { tcsets(file.as_raw_fd(), &termios) };
assert_eq!(res, Err(Errno::ENOTTY));
}
// FIXME: Find a suitable example for `ioctl_readwrite_bad`
// From linux/videodev2.h
ioctl_none!(log_status, b'V', 70);
#[test]
fn test_ioctl_none() {
let file = tempfile().unwrap();
let res = unsafe { log_status(file.as_raw_fd()) };
assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
#[repr(C)]
pub struct v4l2_audio {
index: u32,
name: [u8; 32],
capability: u32,
mode: u32,
reserved: [u32; 2],
}
// From linux/videodev2.h
ioctl_write_ptr!(s_audio, b'V', 34, v4l2_audio);
#[test]
fn test_ioctl_write_ptr() {
let file = tempfile().unwrap();
let data: v4l2_audio = unsafe { mem::zeroed() };
let res = unsafe { s_audio(file.as_raw_fd(), &data) };
assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// From linux/net/bluetooth/hci_sock.h
const HCI_IOC_MAGIC: u8 = b'H';
const HCI_IOC_HCIDEVUP: u8 = 201;
ioctl_write_int!(hcidevup, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP);
#[test]
fn test_ioctl_write_int() {
let file = tempfile().unwrap();
let res = unsafe { hcidevup(file.as_raw_fd(), 0) };
assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// From linux/videodev2.h
ioctl_read!(g_audio, b'V', 33, v4l2_audio);
#[test]
fn test_ioctl_read() {
let file = tempfile().unwrap();
let mut data: v4l2_audio = unsafe { mem::zeroed() };
let res = unsafe { g_audio(file.as_raw_fd(), &mut data) };
assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// From linux/videodev2.h
ioctl_readwrite!(enum_audio, b'V', 65, v4l2_audio);
#[test]
fn test_ioctl_readwrite() {
let file = tempfile().unwrap();
let mut data: v4l2_audio = unsafe { mem::zeroed() };
let res = unsafe { enum_audio(file.as_raw_fd(), &mut data) };
assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// FIXME: Find a suitable example for `ioctl_read_buf`.
#[repr(C)]
pub struct spi_ioc_transfer {
tx_buf: u64,
rx_buf: u64,
len: u32,
speed_hz: u32,
delay_usecs: u16,
bits_per_word: u8,
cs_change: u8,
tx_nbits: u8,
rx_nbits: u8,
pad: u16,
}
// From linux/spi/spidev.h
ioctl_write_buf!(
spi_ioc_message,
super::SPI_IOC_MAGIC,
super::SPI_IOC_MESSAGE,
spi_ioc_transfer
);
#[test]
fn test_ioctl_write_buf() {
let file = tempfile().unwrap();
let data: [spi_ioc_transfer; 4] = unsafe { mem::zeroed() };
let res = unsafe { spi_ioc_message(file.as_raw_fd(), &data[..]) };
assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
}
// FIXME: Find a suitable example for `ioctl_readwrite_buf`.
}
#[cfg(target_os = "freebsd")]
mod freebsd_ioctls {
use std::mem;
use std::os::unix::io::AsRawFd;
use libc::termios;
use tempfile::tempfile;
use nix::errno::Errno;
// From sys/sys/ttycom.h
const TTY_IOC_MAGIC: u8 = b't';
const TTY_IOC_TYPE_NXCL: u8 = 14;
const TTY_IOC_TYPE_GETA: u8 = 19;
const TTY_IOC_TYPE_SETA: u8 = 20;
ioctl_none!(tiocnxcl, TTY_IOC_MAGIC, TTY_IOC_TYPE_NXCL);
#[test]
fn test_ioctl_none() {
let file = tempfile().unwrap();
let res = unsafe { tiocnxcl(file.as_raw_fd()) };
assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_read!(tiocgeta, TTY_IOC_MAGIC, TTY_IOC_TYPE_GETA, termios);
#[test]
fn test_ioctl_read() {
let file = tempfile().unwrap();
let mut termios = unsafe { mem::zeroed() };
let res = unsafe { tiocgeta(file.as_raw_fd(), &mut termios) };
assert_eq!(res, Err(Errno::ENOTTY));
}
ioctl_write_ptr!(tiocseta, TTY_IOC_MAGIC, TTY_IOC_TYPE_SETA, termios);
#[test]
fn test_ioctl_write_ptr() {
let file = tempfile().unwrap();
let termios: termios = unsafe { mem::zeroed() };
let res = unsafe { tiocseta(file.as_raw_fd(), &termios) };
assert_eq!(res, Err(Errno::ENOTTY));
}
}

20
vendor/nix/test/sys/test_memfd.rs vendored Normal file
View File

@@ -0,0 +1,20 @@
#[test]
fn test_memfd_create() {
use nix::sys::memfd::memfd_create;
use nix::sys::memfd::MFdFlags;
use nix::unistd::lseek;
use nix::unistd::read;
use nix::unistd::{write, Whence};
let fd =
memfd_create("test_memfd_create_name", MFdFlags::MFD_CLOEXEC).unwrap();
let contents = b"hello";
assert_eq!(write(&fd, contents).unwrap(), 5);
lseek(&fd, 0, Whence::SeekSet).unwrap();
let mut buf = vec![0_u8; contents.len()];
assert_eq!(read(&fd, &mut buf).unwrap(), 5);
assert_eq!(contents, buf.as_slice());
}

200
vendor/nix/test/sys/test_mman.rs vendored Normal file
View File

@@ -0,0 +1,200 @@
#![allow(clippy::redundant_slicing)]
use nix::sys::mman::{mmap_anonymous, MapFlags, ProtFlags};
use std::num::NonZeroUsize;
#[test]
fn test_mmap_anonymous() {
unsafe {
let mut ptr = mmap_anonymous(
None,
NonZeroUsize::new(1).unwrap(),
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE,
)
.unwrap()
.cast::<u8>();
assert_eq!(*ptr.as_ref(), 0x00u8);
*ptr.as_mut() = 0xffu8;
assert_eq!(*ptr.as_ref(), 0xffu8);
}
}
#[test]
#[cfg(any(target_os = "linux", target_os = "netbsd"))]
fn test_mremap_grow() {
use nix::libc::size_t;
use nix::sys::mman::{mremap, MRemapFlags};
use std::ptr::NonNull;
const ONE_K: size_t = 1024;
let one_k_non_zero = NonZeroUsize::new(ONE_K).unwrap();
let slice: &mut [u8] = unsafe {
let mem = mmap_anonymous(
None,
one_k_non_zero,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE,
)
.unwrap();
std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
};
assert_eq!(slice[ONE_K - 1], 0x00);
slice[ONE_K - 1] = 0xFF;
assert_eq!(slice[ONE_K - 1], 0xFF);
let slice: &mut [u8] = unsafe {
#[cfg(target_os = "linux")]
let mem = mremap(
NonNull::from(&mut slice[..]).cast(),
ONE_K,
10 * ONE_K,
MRemapFlags::MREMAP_MAYMOVE,
None,
)
.unwrap();
#[cfg(target_os = "netbsd")]
let mem = mremap(
NonNull::from(&mut slice[..]).cast(),
ONE_K,
10 * ONE_K,
MRemapFlags::MAP_REMAPDUP,
None,
)
.unwrap();
std::slice::from_raw_parts_mut(mem.cast().as_ptr(), 10 * ONE_K)
};
// The first KB should still have the old data in it.
assert_eq!(slice[ONE_K - 1], 0xFF);
// The additional range should be zero-init'd and accessible.
assert_eq!(slice[10 * ONE_K - 1], 0x00);
slice[10 * ONE_K - 1] = 0xFF;
assert_eq!(slice[10 * ONE_K - 1], 0xFF);
}
#[test]
#[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::libc::size_t;
use nix::sys::mman::{mremap, MRemapFlags};
use std::num::NonZeroUsize;
use std::ptr::NonNull;
const ONE_K: size_t = 1024;
let ten_one_k = NonZeroUsize::new(10 * ONE_K).unwrap();
let slice: &mut [u8] = unsafe {
let mem = mmap_anonymous(
None,
ten_one_k,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE,
)
.unwrap();
std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
};
assert_eq!(slice[ONE_K - 1], 0x00);
slice[ONE_K - 1] = 0xFF;
assert_eq!(slice[ONE_K - 1], 0xFF);
let slice: &mut [u8] = unsafe {
let mem = mremap(
NonNull::from(&mut slice[..]).cast(),
ten_one_k.into(),
ONE_K,
MRemapFlags::empty(),
None,
)
.unwrap();
// Since we didn't supply MREMAP_MAYMOVE, the address should be the
// same.
assert_eq!(mem.as_ptr(), NonNull::from(&mut slice[..]).cast().as_ptr());
std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
};
// The first KB should still be accessible and have the old data in it.
assert_eq!(slice[ONE_K - 1], 0xFF);
}
#[test]
#[cfg(target_os = "linux")]
fn test_mremap_dontunmap() {
use nix::libc::size_t;
use nix::sys::mman::{mremap, MRemapFlags};
use std::num::NonZeroUsize;
use std::ptr::NonNull;
const ONE_K: size_t = 1024;
let one_k_non_zero = NonZeroUsize::new(ONE_K).unwrap();
let slice: &mut [u8] = unsafe {
let mem = mmap_anonymous(
None,
one_k_non_zero,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE,
)
.unwrap();
std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
};
// because we do not unmap `slice`, `old_size` and `new_size`
// need to be equal or `EINVAL` is set.
let _new_slice: &mut [u8] = unsafe {
let mem = mremap(
NonNull::from(&mut slice[..]).cast(),
ONE_K,
ONE_K,
MRemapFlags::MREMAP_MAYMOVE | MRemapFlags::MREMAP_DONTUNMAP,
None,
)
.unwrap();
std::slice::from_raw_parts_mut(mem.cast().as_ptr(), 10 * ONE_K)
};
}
#[test]
#[cfg(target_os = "linux")]
fn test_madv_wipeonfork() {
use nix::libc::size_t;
use nix::sys::mman::{madvise, MmapAdvise};
use nix::unistd::{fork, ForkResult};
use std::num::NonZeroUsize;
const ONE_K: size_t = 1024;
let ten_one_k = NonZeroUsize::new(10 * ONE_K).unwrap();
let slice: &mut [u8] = unsafe {
let mem = mmap_anonymous(
None,
ten_one_k,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE,
)
.unwrap();
madvise(mem, ONE_K, MmapAdvise::MADV_WIPEONFORK)
.expect("madvise failed");
std::slice::from_raw_parts_mut(mem.as_ptr().cast(), ONE_K)
};
slice[ONE_K - 1] = 0xFF;
let _m = crate::FORK_MTX.lock();
unsafe {
let res = fork().expect("fork failed");
match res {
ForkResult::Child => {
// that s the whole point of MADV_WIPEONFORK
assert_eq!(slice[ONE_K - 1], 0x00);
libc::_exit(0);
}
ForkResult::Parent { child } => {
nix::sys::signal::kill(child, nix::sys::signal::SIGTERM)
.unwrap();
let _ = nix::sys::wait::wait().unwrap();
}
}
}
}

172
vendor/nix/test/sys/test_prctl.rs vendored Normal file
View File

@@ -0,0 +1,172 @@
#[cfg(target_os = "linux")]
#[cfg(feature = "process")]
mod test_prctl {
use std::ffi::CStr;
use nix::sys::prctl;
#[cfg_attr(qemu, ignore)]
#[test]
fn test_get_set_subreaper() {
let original = prctl::get_child_subreaper().unwrap();
prctl::set_child_subreaper(true).unwrap();
let subreaper = prctl::get_child_subreaper().unwrap();
assert!(subreaper);
prctl::set_child_subreaper(original).unwrap();
}
#[test]
fn test_get_set_dumpable() {
let original = prctl::get_dumpable().unwrap();
prctl::set_dumpable(false).unwrap();
let dumpable = prctl::get_dumpable().unwrap();
assert!(!dumpable);
prctl::set_dumpable(original).unwrap();
}
#[test]
fn test_get_set_keepcaps() {
let original = prctl::get_keepcaps().unwrap();
prctl::set_keepcaps(true).unwrap();
let keepcaps = prctl::get_keepcaps().unwrap();
assert!(keepcaps);
prctl::set_keepcaps(original).unwrap();
}
#[test]
fn test_get_set_clear_mce_kill() {
use prctl::PrctlMCEKillPolicy::*;
prctl::set_mce_kill(PR_MCE_KILL_LATE).unwrap();
let mce = prctl::get_mce_kill().unwrap();
assert_eq!(mce, PR_MCE_KILL_LATE);
prctl::clear_mce_kill().unwrap();
let mce = prctl::get_mce_kill().unwrap();
assert_eq!(mce, PR_MCE_KILL_DEFAULT);
}
#[cfg_attr(qemu, ignore)]
#[test]
fn test_get_set_pdeathsig() {
use nix::sys::signal::Signal;
let original = prctl::get_pdeathsig().unwrap();
prctl::set_pdeathsig(Signal::SIGUSR1).unwrap();
let sig = prctl::get_pdeathsig().unwrap();
assert_eq!(sig, Some(Signal::SIGUSR1));
prctl::set_pdeathsig(original).unwrap();
}
#[test]
fn test_get_set_name() {
let original = prctl::get_name().unwrap();
let long_name =
CStr::from_bytes_with_nul(b"0123456789abcdefghijklmn\0").unwrap();
prctl::set_name(long_name).unwrap();
let res = prctl::get_name().unwrap();
// name truncated by kernel to TASK_COMM_LEN
assert_eq!(&long_name.to_str().unwrap()[..15], res.to_str().unwrap());
let short_name = CStr::from_bytes_with_nul(b"01234567\0").unwrap();
prctl::set_name(short_name).unwrap();
let res = prctl::get_name().unwrap();
assert_eq!(short_name.to_str().unwrap(), res.to_str().unwrap());
prctl::set_name(&original).unwrap();
}
#[cfg_attr(qemu, ignore)]
#[test]
fn test_get_set_timerslack() {
let original = prctl::get_timerslack().unwrap() as libc::c_ulong;
let slack = 60_000;
prctl::set_timerslack(slack).unwrap();
let res = prctl::get_timerslack().unwrap() as libc::c_ulong;
assert_eq!(slack, res);
prctl::set_timerslack(original).unwrap();
}
// Loongarch need to use a newer QEMU that disabled these PRCTL subcodes/methods.
// https://github.com/qemu/qemu/commit/220717a6f46a99031a5b1af964bbf4dec1310440
// So we should ignore them when testing in QEMU environments.
#[cfg_attr(all(qemu, target_arch = "loongarch64"), ignore)]
#[test]
fn test_disable_enable_perf_events() {
prctl::task_perf_events_disable().unwrap();
prctl::task_perf_events_enable().unwrap();
}
#[test]
fn test_get_set_no_new_privs() {
prctl::set_no_new_privs().unwrap();
let no_new_privs = prctl::get_no_new_privs().unwrap();
assert!(no_new_privs);
}
// Loongarch need to use a newer QEMU that disabled these PRCTL subcodes/methods
// https://github.com/qemu/qemu/commit/220717a6f46a99031a5b1af964bbf4dec1310440
// So we should ignore them when testing in QEMU environments.
#[cfg_attr(all(qemu, target_arch = "loongarch64"), ignore)]
#[test]
fn test_get_set_thp_disable() {
let original = prctl::get_thp_disable().unwrap();
prctl::set_thp_disable(true).unwrap();
let thp_disable = prctl::get_thp_disable().unwrap();
assert!(thp_disable);
prctl::set_thp_disable(original).unwrap();
}
// Ignore this test under QEMU, as it started failing after updating the Linux CI
// runner image, for reasons unknown.
//
// See: https://github.com/nix-rust/nix/issues/2418
#[test]
#[cfg_attr(qemu, ignore)]
fn test_set_vma_anon_name() {
use nix::errno::Errno;
use nix::sys::mman;
use std::num::NonZeroUsize;
const ONE_K: libc::size_t = 1024;
let sz = NonZeroUsize::new(ONE_K).unwrap();
let ptr = unsafe {
mman::mmap_anonymous(
None,
sz,
mman::ProtFlags::PROT_READ,
mman::MapFlags::MAP_SHARED,
)
.unwrap()
};
let err = prctl::set_vma_anon_name(
ptr,
sz,
Some(CStr::from_bytes_with_nul(b"[,$\0").unwrap()),
)
.unwrap_err();
assert_eq!(err, Errno::EINVAL);
// `CONFIG_ANON_VMA_NAME` kernel config might not be set
prctl::set_vma_anon_name(
ptr,
sz,
Some(CStr::from_bytes_with_nul(b"Nix\0").unwrap()),
)
.unwrap_or_default();
prctl::set_vma_anon_name(ptr, sz, None).unwrap_or_default();
}
}

32
vendor/nix/test/sys/test_pthread.rs vendored Normal file
View File

@@ -0,0 +1,32 @@
use nix::sys::pthread::*;
#[cfg(any(
target_env = "musl",
target_os = "redox",
target_env = "ohos",
target_os = "cygwin"
))]
#[test]
fn test_pthread_self() {
let tid = pthread_self();
assert!(!tid.is_null());
}
#[cfg(not(any(
target_env = "musl",
target_os = "redox",
target_env = "ohos",
target_os = "cygwin"
)))]
#[test]
fn test_pthread_self() {
let tid = pthread_self();
assert_ne!(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.");
}

412
vendor/nix/test/sys/test_ptrace.rs vendored Normal file
View File

@@ -0,0 +1,412 @@
#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(target_arch = "x86_64", target_arch = "x86")
))]
use memoffset::offset_of;
use nix::errno::Errno;
use nix::sys::ptrace;
#[cfg(linux_android)]
use nix::sys::ptrace::Options;
use nix::unistd::getpid;
#[cfg(linux_android)]
use std::mem;
use crate::*;
#[test]
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!("test_ptrace", CAP_SYS_PTRACE);
let err = ptrace::attach(getpid()).unwrap_err();
assert!(
err == Errno::EPERM || err == Errno::EINVAL || err == Errno::ENOSYS
);
}
// Just make sure ptrace_setoptions can be called at all, for now.
#[test]
#[cfg(linux_android)]
fn test_ptrace_setoptions() {
require_capability!("test_ptrace_setoptions", CAP_SYS_PTRACE);
let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD)
.unwrap_err();
assert_ne!(err, Errno::EOPNOTSUPP);
}
// Just make sure ptrace_getevent can be called at all, for now.
#[test]
#[cfg(linux_android)]
fn test_ptrace_getevent() {
require_capability!("test_ptrace_getevent", CAP_SYS_PTRACE);
let err = ptrace::getevent(getpid()).unwrap_err();
assert_ne!(err, Errno::EOPNOTSUPP);
}
// Just make sure ptrace_getsiginfo can be called at all, for now.
#[test]
#[cfg(linux_android)]
fn test_ptrace_getsiginfo() {
require_capability!("test_ptrace_getsiginfo", CAP_SYS_PTRACE);
if let Err(Errno::EOPNOTSUPP) = ptrace::getsiginfo(getpid()) {
panic!("ptrace_getsiginfo returns Errno::EOPNOTSUPP!");
}
}
// Just make sure ptrace_setsiginfo can be called at all, for now.
#[test]
#[cfg(linux_android)]
fn test_ptrace_setsiginfo() {
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!");
}
}
#[test]
fn test_ptrace_cont() {
use nix::sys::ptrace;
use nix::sys::signal::{raise, Signal};
use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
use nix::unistd::fork;
use nix::unistd::ForkResult::*;
require_capability!("test_ptrace_cont", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
// FIXME: qemu-user doesn't implement ptrace on all architectures
// and returns ENOSYS in this case.
// We (ab)use this behavior to detect the affected platforms
// and skip the test then.
// On valid platforms the ptrace call should return Errno::EPERM, this
// is already tested by `test_ptrace`.
let err = ptrace::attach(getpid()).unwrap_err();
if err == Errno::ENOSYS {
return;
}
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => {
ptrace::traceme().unwrap();
// As recommended by ptrace(2), raise SIGTRAP to pause the child
// until the parent is ready to continue
loop {
raise(Signal::SIGTRAP).unwrap();
}
}
Parent { child } => {
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
);
ptrace::cont(child, None).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
);
ptrace::cont(child, Some(Signal::SIGKILL)).unwrap();
match waitpid(child, None) {
Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
if pid == child =>
{
// FIXME It's been observed on some systems (apple) the
// tracee may not be killed but remain as a zombie process
// affecting other wait based tests. Add an extra kill just
// to make sure there are no zombies.
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"),
}
}
}
}
#[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(
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)
))]
#[test]
fn test_ptrace_syscall() {
use nix::sys::ptrace;
use nix::sys::signal::kill;
use nix::sys::signal::Signal;
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::fork;
use nix::unistd::getpid;
use nix::unistd::ForkResult::*;
require_capability!("test_ptrace_syscall", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => {
ptrace::traceme().unwrap();
// first sigstop until parent is ready to continue
let pid = getpid();
kill(pid, Signal::SIGSTOP).unwrap();
kill(pid, Signal::SIGTERM).unwrap();
unsafe {
::libc::_exit(0);
}
}
Parent { child } => {
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Stopped(child, Signal::SIGSTOP))
);
// set this option to recognize syscall-stops
ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACESYSGOOD)
.unwrap();
#[cfg(target_arch = "x86_64")]
let get_syscall_id =
|| ptrace::getregs(child).unwrap().orig_rax as libc::c_long;
#[cfg(target_arch = "x86")]
let get_syscall_id =
|| ptrace::getregs(child).unwrap().orig_eax as libc::c_long;
#[cfg(target_arch = "aarch64")]
let get_syscall_id =
|| ptrace::getregs(child).unwrap().regs[8] as libc::c_long;
#[cfg(target_arch = "riscv64")]
let get_syscall_id =
|| ptrace::getregs(child).unwrap().a7 as libc::c_long;
// this duplicates `get_syscall_id` for the purpose of testing `ptrace::read_user`.
#[cfg(target_arch = "x86_64")]
let rax_offset = offset_of!(libc::user_regs_struct, orig_rax);
#[cfg(target_arch = "x86")]
let rax_offset = offset_of!(libc::user_regs_struct, orig_eax);
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
let get_syscall_from_user_area = || {
// Find the offset of `user.regs.rax` (or `user.regs.eax` for x86)
let rax_offset = offset_of!(libc::user, regs) + rax_offset;
ptrace::read_user(child, rax_offset as _).unwrap()
as libc::c_long
};
// kill entry
ptrace::syscall(child, None).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::PtraceSyscall(child))
);
assert_eq!(get_syscall_id(), ::libc::SYS_kill);
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
// kill exit
ptrace::syscall(child, None).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::PtraceSyscall(child))
);
assert_eq!(get_syscall_id(), ::libc::SYS_kill);
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
// receive signal
ptrace::syscall(child, None).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Stopped(child, Signal::SIGTERM))
);
// inject signal
ptrace::syscall(child, Signal::SIGTERM).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Signaled(child, Signal::SIGTERM, false))
);
}
}
}
#[cfg(all(
target_os = "linux",
any(
all(
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64"
)
),
all(target_env = "musl", target_arch = "aarch64")
)
))]
#[test]
fn test_ptrace_regsets() {
use nix::sys::ptrace::{self, getregset, regset, setregset};
use nix::sys::signal::*;
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::fork;
use nix::unistd::ForkResult::*;
require_capability!("test_ptrace_regsets", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => {
ptrace::traceme().unwrap();
// As recommended by ptrace(2), raise SIGTRAP to pause the child
// until the parent is ready to continue
loop {
raise(Signal::SIGTRAP).unwrap();
}
}
Parent { child } => {
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
);
let mut regstruct =
getregset::<regset::NT_PRSTATUS>(child).unwrap();
let mut fpregstruct =
getregset::<regset::NT_PRFPREG>(child).unwrap();
#[cfg(target_arch = "x86_64")]
let (reg, fpreg) =
(&mut regstruct.r15, &mut fpregstruct.st_space[5]);
#[cfg(target_arch = "x86")]
let (reg, fpreg) =
(&mut regstruct.edx, &mut fpregstruct.st_space[5]);
#[cfg(target_arch = "aarch64")]
let (reg, fpreg) =
(&mut regstruct.regs[16], &mut fpregstruct.vregs[5]);
#[cfg(target_arch = "riscv64")]
let (reg, fpreg) = (&mut regstruct.t1, &mut fpregstruct.__f[5]);
*reg = 0xdeadbeefu32 as _;
*fpreg = 0xfeedfaceu32 as _;
let _ = setregset::<regset::NT_PRSTATUS>(child, regstruct);
regstruct = getregset::<regset::NT_PRSTATUS>(child).unwrap();
let _ = setregset::<regset::NT_PRFPREG>(child, fpregstruct);
fpregstruct = getregset::<regset::NT_PRFPREG>(child).unwrap();
#[cfg(target_arch = "x86_64")]
let (reg, fpreg) = (regstruct.r15, fpregstruct.st_space[5]);
#[cfg(target_arch = "x86")]
let (reg, fpreg) = (regstruct.edx, fpregstruct.st_space[5]);
#[cfg(target_arch = "aarch64")]
let (reg, fpreg) = (regstruct.regs[16], fpregstruct.vregs[5]);
#[cfg(target_arch = "riscv64")]
let (reg, fpreg) = (regstruct.t1, fpregstruct.__f[5]);
assert_eq!(reg, 0xdeadbeefu32 as _);
assert_eq!(fpreg, 0xfeedfaceu32 as _);
ptrace::cont(child, Some(Signal::SIGKILL)).unwrap();
match waitpid(child, None) {
Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
if pid == child => {}
_ => panic!("The process should have been killed"),
}
}
}
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
#[test]
fn test_ptrace_syscall_info() {
use nix::sys::ptrace;
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::fork;
use nix::unistd::ForkResult::*;
require_capability!("test_ptrace_syscall_info", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
match unsafe { fork() }.expect("Error: Fork Failed") {
Child => {
ptrace::traceme().unwrap();
std::thread::sleep(std::time::Duration::from_millis(1000));
unsafe {
::libc::_exit(0);
}
}
Parent { child } => loop {
if let Ok(WaitStatus::Exited(_, 0)) = waitpid(child, None) {
break;
}
let si = ptrace::syscall_info(child).unwrap();
assert!(si.op >= libc::PTRACE_SYSCALL_INFO_ENTRY);
},
}
}

44
vendor/nix/test/sys/test_resource.rs vendored Normal file
View File

@@ -0,0 +1,44 @@
use nix::sys::resource::{getrlimit, setrlimit, Resource};
use nix::sys::resource::{getrusage, UsageWho};
/// 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_attr(target_os = "cygwin", ignore)]
pub fn test_resource_limits_nofile() {
let (mut soft_limit, hard_limit) =
getrlimit(Resource::RLIMIT_NOFILE).unwrap();
soft_limit -= 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);
}
#[test]
pub fn test_self_cpu_time() {
// Make sure some CPU time is used.
let mut numbers: Vec<i32> = (1..1_000_000).collect();
numbers.iter_mut().for_each(|item| *item *= 2);
// FIXME: this is here to help ensure the compiler does not optimize the whole
// thing away. Replace the assert with test::black_box once stabilized.
assert_eq!(numbers[100..200].iter().sum::<i32>(), 30_100);
let usage = getrusage(UsageWho::RUSAGE_SELF)
.expect("Failed to call getrusage for SELF");
let rusage = usage.as_ref();
let user = usage.user_time();
assert!(user.tv_sec() > 0 || user.tv_usec() > 0);
assert_eq!(user.tv_sec(), rusage.ru_utime.tv_sec);
assert_eq!(user.tv_usec(), rusage.ru_utime.tv_usec);
}

293
vendor/nix/test/sys/test_select.rs vendored Normal file
View File

@@ -0,0 +1,293 @@
use nix::sys::select::*;
use nix::sys::signal::SigSet;
use nix::sys::time::{TimeSpec, TimeVal, TimeValLike};
use nix::unistd::{pipe, write};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
#[test]
pub fn test_pselect() {
let _mtx = crate::SIGNAL_MTX.lock();
let (r1, w1) = pipe().unwrap();
write(&w1, b"hi!").unwrap();
let (r2, _w2) = pipe().unwrap();
let mut fd_set = FdSet::new();
fd_set.insert(r1.as_fd());
fd_set.insert(r2.as_fd());
let timeout = TimeSpec::seconds(10);
let sigmask = SigSet::empty();
assert_eq!(
1,
pselect(None, &mut fd_set, None, None, &timeout, &sigmask).unwrap()
);
assert!(fd_set.contains(r1.as_fd()));
assert!(!fd_set.contains(r2.as_fd()));
}
#[test]
pub fn test_pselect_nfds2() {
let (r1, w1) = pipe().unwrap();
write(&w1, b"hi!").unwrap();
let (r2, _w2) = pipe().unwrap();
let mut fd_set = FdSet::new();
fd_set.insert(r1.as_fd());
fd_set.insert(r2.as_fd());
let timeout = TimeSpec::seconds(10);
assert_eq!(
1,
pselect(
std::cmp::max(r1.as_raw_fd(), r2.as_raw_fd()) + 1,
&mut fd_set,
None,
None,
&timeout,
None
)
.unwrap()
);
assert!(fd_set.contains(r1.as_fd()));
assert!(!fd_set.contains(r2.as_fd()));
}
macro_rules! generate_fdset_bad_fd_tests {
($fd:expr, $($method:ident),* $(,)?) => {
$(
#[test]
#[should_panic]
fn $method() {
let bad_fd = unsafe{BorrowedFd::borrow_raw($fd)};
FdSet::new().$method(bad_fd);
}
)*
}
}
mod test_fdset_too_large_fd {
use super::*;
generate_fdset_bad_fd_tests!(
FD_SETSIZE.try_into().unwrap(),
insert,
remove,
contains,
);
}
#[test]
fn fdset_insert() {
let mut fd_set = FdSet::new();
for i in 0..FD_SETSIZE {
let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
assert!(!fd_set.contains(borrowed_i));
}
let fd_seven = unsafe { BorrowedFd::borrow_raw(7) };
fd_set.insert(fd_seven);
assert!(fd_set.contains(fd_seven));
}
#[test]
fn fdset_remove() {
let mut fd_set = FdSet::new();
for i in 0..FD_SETSIZE {
let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
assert!(!fd_set.contains(borrowed_i));
}
let fd_seven = unsafe { BorrowedFd::borrow_raw(7) };
fd_set.insert(fd_seven);
fd_set.remove(fd_seven);
for i in 0..FD_SETSIZE {
let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
assert!(!fd_set.contains(borrowed_i));
}
}
#[test]
#[allow(non_snake_case)]
fn fdset_clear() {
let mut fd_set = FdSet::new();
let fd_one = unsafe { BorrowedFd::borrow_raw(1) };
let fd_FD_SETSIZE_divided_by_two =
unsafe { BorrowedFd::borrow_raw((FD_SETSIZE / 2) as RawFd) };
let fd_FD_SETSIZE_minus_one =
unsafe { BorrowedFd::borrow_raw((FD_SETSIZE - 1) as RawFd) };
fd_set.insert(fd_one);
fd_set.insert(fd_FD_SETSIZE_divided_by_two);
fd_set.insert(fd_FD_SETSIZE_minus_one);
fd_set.clear();
for i in 0..FD_SETSIZE {
let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
assert!(!fd_set.contains(borrowed_i));
}
}
#[test]
fn fdset_highest() {
let mut set = FdSet::new();
assert_eq!(
set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()),
None
);
let fd_zero = unsafe { BorrowedFd::borrow_raw(0) };
let fd_ninety = unsafe { BorrowedFd::borrow_raw(90) };
set.insert(fd_zero);
assert_eq!(
set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()),
Some(0)
);
set.insert(fd_ninety);
assert_eq!(
set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()),
Some(90)
);
set.remove(fd_zero);
assert_eq!(
set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()),
Some(90)
);
set.remove(fd_ninety);
assert_eq!(
set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()),
None
);
let fd_four = unsafe { BorrowedFd::borrow_raw(4) };
let fd_five = unsafe { BorrowedFd::borrow_raw(5) };
let fd_seven = unsafe { BorrowedFd::borrow_raw(7) };
set.insert(fd_four);
set.insert(fd_five);
set.insert(fd_seven);
assert_eq!(
set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()),
Some(7)
);
}
#[test]
fn fdset_fds() {
let mut set = FdSet::new();
let fd_zero = unsafe { BorrowedFd::borrow_raw(0) };
let fd_ninety = unsafe { BorrowedFd::borrow_raw(90) };
assert_eq!(
set.fds(None)
.map(|borrowed_fd| borrowed_fd.as_raw_fd())
.collect::<Vec<_>>(),
vec![]
);
set.insert(fd_zero);
assert_eq!(
set.fds(None)
.map(|borrowed_fd| borrowed_fd.as_raw_fd())
.collect::<Vec<_>>(),
vec![0]
);
set.insert(fd_ninety);
assert_eq!(
set.fds(None)
.map(|borrowed_fd| borrowed_fd.as_raw_fd())
.collect::<Vec<_>>(),
vec![0, 90]
);
// highest limit
assert_eq!(
set.fds(Some(89))
.map(|borrowed_fd| borrowed_fd.as_raw_fd())
.collect::<Vec<_>>(),
vec![0]
);
assert_eq!(
set.fds(Some(90))
.map(|borrowed_fd| borrowed_fd.as_raw_fd())
.collect::<Vec<_>>(),
vec![0, 90]
);
}
#[test]
fn test_select() {
let (r1, w1) = pipe().unwrap();
let (r2, _w2) = pipe().unwrap();
write(&w1, b"hi!").unwrap();
let mut fd_set = FdSet::new();
fd_set.insert(r1.as_fd());
fd_set.insert(r2.as_fd());
let mut timeout = TimeVal::seconds(10);
assert_eq!(
1,
select(None, &mut fd_set, None, None, &mut timeout).unwrap()
);
assert!(fd_set.contains(r1.as_fd()));
assert!(!fd_set.contains(r2.as_fd()));
}
#[test]
fn test_select_nfds() {
let (r1, w1) = pipe().unwrap();
let (r2, _w2) = pipe().unwrap();
write(&w1, b"hi!").unwrap();
let mut fd_set = FdSet::new();
fd_set.insert(r1.as_fd());
fd_set.insert(r2.as_fd());
let mut timeout = TimeVal::seconds(10);
{
assert_eq!(
1,
select(
Some(
fd_set
.highest()
.map(|borrowed_fd| borrowed_fd.as_raw_fd())
.unwrap()
+ 1
),
&mut fd_set,
None,
None,
&mut timeout
)
.unwrap()
);
}
assert!(fd_set.contains(r1.as_fd()));
assert!(!fd_set.contains(r2.as_fd()));
}
#[test]
fn test_select_nfds2() {
let (r1, w1) = pipe().unwrap();
write(&w1, b"hi!").unwrap();
let (r2, _w2) = pipe().unwrap();
let mut fd_set = FdSet::new();
fd_set.insert(r1.as_fd());
fd_set.insert(r2.as_fd());
let mut timeout = TimeVal::seconds(10);
assert_eq!(
1,
select(
std::cmp::max(r1.as_raw_fd(), r2.as_raw_fd()) + 1,
&mut fd_set,
None,
None,
&mut timeout
)
.unwrap()
);
assert!(fd_set.contains(r1.as_fd()));
assert!(!fd_set.contains(r2.as_fd()));
}

456
vendor/nix/test/sys/test_signal.rs vendored Normal file
View File

@@ -0,0 +1,456 @@
use nix::errno::Errno;
use nix::sys::signal::*;
use nix::unistd::*;
use std::hash::{Hash, Hasher};
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_os = "redox"))]
use std::thread;
#[test]
fn test_kill_none() {
kill(getpid(), None).expect("Should be able to send signal to myself.");
}
#[test]
#[cfg(not(target_os = "fuchsia"))]
fn test_killpg_none() {
killpg(getpgrp(), None)
.expect("Should be able to send signal to my process group.");
}
#[test]
fn test_old_sigaction_flags() {
let _m = crate::SIGNAL_MTX.lock();
extern "C" fn handler(_: ::libc::c_int) {}
let act = SigAction::new(
SigHandler::Handler(handler),
SaFlags::empty(),
SigSet::empty(),
);
let oact = unsafe { sigaction(SIGINT, &act) }.unwrap();
let _flags = oact.flags();
let oact = unsafe { sigaction(SIGINT, &act) }.unwrap();
let _flags = oact.flags();
}
#[test]
fn test_sigprocmask_noop() {
sigprocmask(SigmaskHow::SIG_BLOCK, None, None)
.expect("this should be an effective noop");
}
#[test]
fn test_sigprocmask() {
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;
let mut old_signal_set = SigSet::empty();
sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
.expect("expect to be able to retrieve old signals");
// Make sure the old set doesn't contain the signal, otherwise the following
// test don't make sense.
assert!(
!old_signal_set.contains(SIGNAL),
"the {SIGNAL:?} signal is already blocked, please change to a \
different one"
);
// Now block the signal.
let mut signal_set = SigSet::empty();
signal_set.add(SIGNAL);
sigprocmask(SigmaskHow::SIG_BLOCK, Some(&signal_set), None)
.expect("expect to be able to block signals");
// And test it again, to make sure the change was effective.
old_signal_set.clear();
sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
.expect("expect to be able to retrieve old signals");
assert!(
old_signal_set.contains(SIGNAL),
"expected the {SIGNAL:?} to be blocked"
);
// Reset the signal.
sigprocmask(SigmaskHow::SIG_UNBLOCK, Some(&signal_set), None)
.expect("expect to be able to block signals");
}
static SIGNALED: AtomicBool = AtomicBool::new(false);
extern "C" fn test_sigaction_handler(signal: libc::c_int) {
let signal = Signal::try_from(signal).unwrap();
SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
}
#[cfg(not(target_os = "redox"))]
extern "C" fn test_sigaction_action(
_: libc::c_int,
_: *mut libc::siginfo_t,
_: *mut libc::c_void,
) {
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_signal_sigaction() {
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
);
}
#[test]
fn test_signal() {
let _m = crate::SIGNAL_MTX.lock();
unsafe { signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap();
raise(Signal::SIGINT).unwrap();
assert_eq!(
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
SigHandler::SigIgn
);
let handler = SigHandler::Handler(test_sigaction_handler);
assert_eq!(
unsafe { signal(Signal::SIGINT, handler) }.unwrap(),
SigHandler::SigDfl
);
raise(Signal::SIGINT).unwrap();
assert!(SIGNALED.load(Ordering::Relaxed));
#[cfg(not(solarish))]
assert_eq!(
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
handler
);
// System V based OSes (e.g. illumos and Solaris) always resets the
// disposition to SIG_DFL prior to calling the signal handler
#[cfg(solarish)]
assert_eq!(
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
SigHandler::SigDfl
);
// Restore default signal handler
unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap();
}
#[test]
fn test_contains() {
let mut mask = SigSet::empty();
mask.add(SIGUSR1);
assert!(mask.contains(SIGUSR1));
assert!(!mask.contains(SIGUSR2));
let all = SigSet::all();
assert!(all.contains(SIGUSR1));
assert!(all.contains(SIGUSR2));
}
#[test]
fn test_clear() {
let mut set = SigSet::all();
set.clear();
for signal in Signal::iterator() {
assert!(!set.contains(signal));
}
}
#[test]
fn test_from_str_round_trips() {
for signal in Signal::iterator() {
assert_eq!(signal.as_ref().parse::<Signal>().unwrap(), signal);
assert_eq!(signal.to_string().parse::<Signal>().unwrap(), signal);
}
}
#[test]
fn test_from_str_invalid_value() {
let errval = Err(Errno::EINVAL);
assert_eq!("NOSIGNAL".parse::<Signal>(), errval);
assert_eq!("kill".parse::<Signal>(), errval);
assert_eq!("9".parse::<Signal>(), errval);
}
#[test]
fn test_extend() {
let mut one_signal = SigSet::empty();
one_signal.add(SIGUSR1);
let mut two_signals = SigSet::empty();
two_signals.add(SIGUSR2);
two_signals.extend(&one_signal);
assert!(two_signals.contains(SIGUSR1));
assert!(two_signals.contains(SIGUSR2));
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_thread_signal_set_mask() {
thread::spawn(|| {
let prev_mask = SigSet::thread_get_mask()
.expect("Failed to get existing signal mask!");
let mut test_mask = prev_mask;
test_mask.add(SIGUSR1);
test_mask.thread_set_mask().expect("assertion failed");
let new_mask =
SigSet::thread_get_mask().expect("Failed to get new mask!");
assert!(new_mask.contains(SIGUSR1));
assert!(!new_mask.contains(SIGUSR2));
prev_mask
.thread_set_mask()
.expect("Failed to revert signal mask!");
})
.join()
.unwrap();
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_thread_signal_block() {
thread::spawn(|| {
let mut mask = SigSet::empty();
mask.add(SIGUSR1);
mask.thread_block().expect("assertion failed");
assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
})
.join()
.unwrap();
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_thread_signal_unblock() {
thread::spawn(|| {
let mut mask = SigSet::empty();
mask.add(SIGUSR1);
mask.thread_unblock().expect("assertion failed");
assert!(!SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
})
.join()
.unwrap();
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_thread_signal_swap() {
thread::spawn(|| {
let mut mask = SigSet::empty();
mask.add(SIGUSR1);
mask.thread_block().unwrap();
assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
let mut mask2 = SigSet::empty();
mask2.add(SIGUSR2);
let oldmask = mask2.thread_swap_mask(SigmaskHow::SIG_SETMASK).unwrap();
assert!(oldmask.contains(SIGUSR1));
assert!(!oldmask.contains(SIGUSR2));
assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR2));
})
.join()
.unwrap();
}
#[test]
fn test_from_and_into_iterator() {
let sigset = SigSet::from_iter(vec![Signal::SIGUSR1, Signal::SIGUSR2]);
let signals = sigset.into_iter().collect::<Vec<Signal>>();
assert_eq!(signals, [Signal::SIGUSR1, Signal::SIGUSR2]);
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_sigaction() {
let _m = crate::SIGNAL_MTX.lock();
thread::spawn(|| {
extern "C" fn test_sigaction_handler(_: libc::c_int) {}
extern "C" fn test_sigaction_action(
_: libc::c_int,
_: *mut libc::siginfo_t,
_: *mut libc::c_void,
) {
}
let handler_sig = SigHandler::Handler(test_sigaction_handler);
let flags =
SaFlags::SA_ONSTACK | SaFlags::SA_RESTART | SaFlags::SA_SIGINFO;
let mut mask = SigSet::empty();
mask.add(SIGUSR1);
let action_sig = SigAction::new(handler_sig, flags, mask);
assert_eq!(
action_sig.flags(),
SaFlags::SA_ONSTACK | SaFlags::SA_RESTART
);
assert_eq!(action_sig.handler(), handler_sig);
mask = action_sig.mask();
assert!(mask.contains(SIGUSR1));
assert!(!mask.contains(SIGUSR2));
let handler_act = SigHandler::SigAction(test_sigaction_action);
let action_act = SigAction::new(handler_act, flags, mask);
assert_eq!(action_act.handler(), handler_act);
let action_dfl = SigAction::new(SigHandler::SigDfl, flags, mask);
assert_eq!(action_dfl.handler(), SigHandler::SigDfl);
let action_ign = SigAction::new(SigHandler::SigIgn, flags, mask);
assert_eq!(action_ign.handler(), SigHandler::SigIgn);
})
.join()
.unwrap();
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_sigwait() {
thread::spawn(|| {
let mut mask = SigSet::empty();
mask.add(SIGUSR1);
mask.add(SIGUSR2);
mask.thread_block().unwrap();
raise(SIGUSR1).unwrap();
assert_eq!(mask.wait().unwrap(), SIGUSR1);
})
.join()
.unwrap();
}
#[cfg(any(
bsd,
linux_android,
solarish,
target_os = "haiku",
target_os = "hurd",
target_os = "aix",
target_os = "fuchsia"
))]
#[test]
fn test_sigsuspend() {
// This test change signal handler
let _m = crate::SIGNAL_MTX.lock();
static SIGNAL_RECIEVED: AtomicBool = AtomicBool::new(false);
extern "C" fn test_sigsuspend_handler(_: libc::c_int) {
assert!(!SIGNAL_RECIEVED.swap(true, Ordering::SeqCst));
}
thread::spawn(|| {
const SIGNAL: Signal = Signal::SIGUSR1;
// Add signal mask to this thread
let mut signal_set = SigSet::empty();
signal_set.add(SIGNAL);
signal_set.thread_block().unwrap();
// Set signal handler and save old one.
let act = SigAction::new(
SigHandler::Handler(test_sigsuspend_handler),
SaFlags::empty(),
SigSet::empty(),
);
let old_act = unsafe { sigaction(SIGNAL, &act) }
.expect("expect to be able to set new action and get old action");
raise(SIGNAL).expect("expect be able to send signal");
// Now `SIGNAL` was sended but it is blocked.
let mut not_wait_set = SigSet::all();
not_wait_set.remove(SIGNAL);
// signal handler must run in SigSet::suspend()
assert!(!SIGNAL_RECIEVED.load(Ordering::SeqCst));
not_wait_set.suspend().unwrap();
assert!(SIGNAL_RECIEVED.load(Ordering::SeqCst));
// Restore the signal handler.
unsafe { sigaction(SIGNAL, &old_act) }
.expect("expect to be able to restore old action ");
})
.join()
.unwrap();
}
#[test]
fn test_from_sigset_t_unchecked() {
let src_set = SigSet::empty();
let set = unsafe { SigSet::from_sigset_t_unchecked(*src_set.as_ref()) };
for signal in Signal::iterator() {
assert!(!set.contains(signal));
}
let src_set = SigSet::all();
let set = unsafe { SigSet::from_sigset_t_unchecked(*src_set.as_ref()) };
for signal in Signal::iterator() {
assert!(set.contains(signal));
}
}
#[test]
fn test_eq_empty() {
let set0 = SigSet::empty();
let set1 = SigSet::empty();
assert_eq!(set0, set1);
}
#[test]
fn test_eq_all() {
let set0 = SigSet::all();
let set1 = SigSet::all();
assert_eq!(set0, set1);
}
#[test]
fn test_hash_empty() {
use std::collections::hash_map::DefaultHasher;
let set0 = SigSet::empty();
let mut h0 = DefaultHasher::new();
set0.hash(&mut h0);
let set1 = SigSet::empty();
let mut h1 = DefaultHasher::new();
set1.hash(&mut h1);
assert_eq!(h0.finish(), h1.finish());
}
#[test]
fn test_hash_all() {
use std::collections::hash_map::DefaultHasher;
let set0 = SigSet::all();
let mut h0 = DefaultHasher::new();
set0.hash(&mut h0);
let set1 = SigSet::all();
let mut h1 = DefaultHasher::new();
set1.hash(&mut h1);
assert_eq!(h0.finish(), h1.finish());
}

90
vendor/nix/test/sys/test_signalfd.rs vendored Normal file
View File

@@ -0,0 +1,90 @@
use std::convert::TryFrom;
#[test]
fn create_signalfd() {
use nix::sys::{signal::SigSet, signalfd::SignalFd};
let mask = SigSet::empty();
SignalFd::new(&mask).unwrap();
}
#[test]
fn create_signalfd_with_opts() {
use nix::sys::{
signal::SigSet,
signalfd::{SfdFlags, SignalFd},
};
let mask = SigSet::empty();
SignalFd::with_flags(&mask, SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK)
.unwrap();
}
#[test]
fn read_empty_signalfd() {
use nix::sys::{
signal::SigSet,
signalfd::{SfdFlags, SignalFd},
};
let mask = SigSet::empty();
let fd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
let res = fd.read_signal();
assert!(res.unwrap().is_none());
}
#[test]
fn test_signalfd() {
use nix::sys::signal::{self, raise, SigSet, Signal};
use nix::sys::signalfd::SignalFd;
// Grab the mutex for altering signals so we don't interfere with other tests.
let _m = crate::SIGNAL_MTX.lock();
// Block the SIGUSR1 signal from automatic processing for this thread
let mut mask = SigSet::empty();
mask.add(signal::SIGUSR1);
mask.thread_block().unwrap();
let fd = SignalFd::new(&mask).unwrap();
// Send a SIGUSR1 signal to the current process. Note that this uses `raise` instead of `kill`
// because `kill` with `getpid` isn't correct during multi-threaded execution like during a
// cargo test session. Instead use `raise` which does the correct thing by default.
raise(signal::SIGUSR1).expect("Error: raise(SIGUSR1) failed");
// And now catch that same signal.
let res = fd.read_signal().unwrap().unwrap();
let signo = Signal::try_from(res.ssi_signo as i32).unwrap();
assert_eq!(signo, signal::SIGUSR1);
}
/// Update the signal mask of an already existing signalfd.
#[test]
fn test_signalfd_setmask() {
use nix::sys::signal::{self, raise, SigSet, Signal};
use nix::sys::signalfd::SignalFd;
// Grab the mutex for altering signals so we don't interfere with other tests.
let _m = crate::SIGNAL_MTX.lock();
// Block the SIGUSR1 signal from automatic processing for this thread
let mut mask = SigSet::empty();
let fd = SignalFd::new(&mask).unwrap();
mask.add(signal::SIGUSR1);
mask.thread_block().unwrap();
fd.set_mask(&mask).unwrap();
// Send a SIGUSR1 signal to the current process. Note that this uses `raise` instead of `kill`
// because `kill` with `getpid` isn't correct during multi-threaded execution like during a
// cargo test session. Instead use `raise` which does the correct thing by default.
raise(signal::SIGUSR1).expect("Error: raise(SIGUSR1) failed");
// And now catch that same signal.
let res = fd.read_signal().unwrap().unwrap();
let signo = Signal::try_from(res.ssi_signo as i32).unwrap();
assert_eq!(signo, signal::SIGUSR1);
}

3172
vendor/nix/test/sys/test_socket.rs vendored Normal file

File diff suppressed because it is too large Load Diff

1272
vendor/nix/test/sys/test_sockopt.rs vendored Normal file

File diff suppressed because it is too large Load Diff

508
vendor/nix/test/sys/test_stat.rs vendored Normal file
View File

@@ -0,0 +1,508 @@
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::fs;
use std::fs::File;
#[cfg(not(target_os = "redox"))]
use std::os::unix::fs::symlink;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::os::unix::fs::PermissionsExt;
#[cfg(not(target_os = "redox"))]
use std::path::Path;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use std::time::{Duration, UNIX_EPOCH};
use libc::mode_t;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use libc::{S_IFLNK, S_IFMT};
#[cfg(not(target_os = "redox"))]
use nix::errno::Errno;
#[cfg(not(target_os = "redox"))]
use nix::fcntl;
#[cfg(any(
target_os = "linux",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
))]
use nix::sys::stat::lutimes;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::utimensat;
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::FchmodatFlags;
use nix::sys::stat::Mode;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::UtimensatFlags;
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::{self};
use nix::sys::stat::{fchmod, stat};
#[cfg(not(target_os = "redox"))]
use nix::sys::stat::{fchmodat, mkdirat};
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::stat::{futimens, utimes};
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use nix::sys::stat::FileStat;
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
use nix::sys::time::{TimeSpec, TimeVal, TimeValLike};
#[cfg(not(target_os = "redox"))]
use nix::unistd::chdir;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
use nix::Result;
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn assert_stat_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
assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
assert!(stats.st_mode > 0); // must be positive integer
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
assert_eq!(stats.st_size, 0); // size is 0 because we did not write anything to the file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
assert!(stats.st_blocks <= 16); // Up to 16 blocks can be allocated for a blank file
}
#[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
assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
assert!(stats.st_mode > 0); // must be positive integer
// st_mode is c_uint (u32 on Android) while S_IFMT is mode_t
// (u16 on Android), and that will be a compile error.
// On other platforms they are the same (either both are u16 or u32).
assert_eq!(
(stats.st_mode as usize) & (S_IFMT as usize),
S_IFLNK as usize
); // should be a link
assert_eq!(stats.st_nlink, 1); // there links created, must be 1
assert!(stats.st_size > 0); // size is > 0 because it points to another file
assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
// st_blocks depends on whether the machine's file system uses fast
// or slow symlinks, so just make sure it's not negative
assert!(stats.st_blocks >= 0);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_stat_and_fstat() {
use nix::sys::stat::fstat;
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
let file = File::create(&filename).unwrap();
let stat_result = stat(&filename);
assert_stat_results(stat_result);
let fstat_result = fstat(&file);
assert_stat_results(fstat_result);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_fstatat() {
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
File::create(&filename).unwrap();
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let result = stat::fstatat(&dirfd, &filename, fcntl::AtFlags::empty());
assert_stat_results(result);
}
#[test]
#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
fn test_stat_fstat_lstat() {
use nix::sys::stat::{fstat, lstat};
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("bar.txt");
let linkname = tempdir.path().join("barlink");
File::create(&filename).unwrap();
symlink("bar.txt", &linkname).unwrap();
let link = File::open(&linkname).unwrap();
// should be the same result as calling stat,
// since it's a regular file
let stat_result = stat(&filename);
assert_stat_results(stat_result);
let lstat_result = lstat(&linkname);
assert_lstat_results(lstat_result);
let fstat_result = fstat(&link);
assert_stat_results(fstat_result);
}
#[test]
fn test_fchmod() {
let tempdir = tempfile::tempdir().unwrap();
let filename = tempdir.path().join("foo.txt");
let file = File::create(&filename).unwrap();
let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmod(&file, mode1).unwrap();
let file_stat1 = stat(&filename).unwrap();
assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmod(&file, mode2).unwrap();
let file_stat2 = stat(&filename).unwrap();
assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_fchmodat() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
File::create(&fullpath).unwrap();
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let mut mode1 = Mode::empty();
mode1.insert(Mode::S_IRUSR);
mode1.insert(Mode::S_IWUSR);
fchmodat(&dirfd, filename, mode1, FchmodatFlags::FollowSymlink).unwrap();
let file_stat1 = stat(&fullpath).unwrap();
assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
chdir(tempdir.path()).unwrap();
let mut mode2 = Mode::empty();
mode2.insert(Mode::S_IROTH);
fchmodat(
fcntl::AT_FDCWD,
filename,
mode2,
FchmodatFlags::FollowSymlink,
)
.unwrap();
let file_stat2 = stat(&fullpath).unwrap();
assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
}
/// Asserts that the atime and mtime in a file's metadata match expected values.
///
/// The atime and mtime are expressed with a resolution of seconds because some file systems
/// (like macOS's HFS+) do not have higher granularity.
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn assert_times_eq(
exp_atime_sec: u64,
exp_mtime_sec: u64,
attr: &fs::Metadata,
) {
assert_eq!(
Duration::new(exp_atime_sec, 0),
attr.accessed().unwrap().duration_since(UNIX_EPOCH).unwrap()
);
assert_eq!(
Duration::new(exp_mtime_sec, 0),
attr.modified().unwrap().duration_since(UNIX_EPOCH).unwrap()
);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimes() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
utimes(&fullpath, &TimeVal::seconds(9990), &TimeVal::seconds(5550))
.unwrap();
assert_times_eq(9990, 5550, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(any(
target_os = "linux",
apple_targets,
target_os = "freebsd",
target_os = "netbsd"
))]
fn test_lutimes() {
let tempdir = tempfile::tempdir().unwrap();
let target = tempdir.path().join("target");
let fullpath = tempdir.path().join("symlink");
drop(File::create(&target).unwrap());
symlink(&target, &fullpath).unwrap();
let exp_target_metadata = fs::symlink_metadata(&target).unwrap();
lutimes(&fullpath, &TimeVal::seconds(4560), &TimeVal::seconds(1230))
.unwrap();
assert_times_eq(4560, 1230, &fs::symlink_metadata(&fullpath).unwrap());
let target_metadata = fs::symlink_metadata(&target).unwrap();
assert_eq!(
exp_target_metadata.accessed().unwrap(),
target_metadata.accessed().unwrap(),
"atime of symlink target was unexpectedly modified"
);
assert_eq!(
exp_target_metadata.modified().unwrap(),
target_metadata.modified().unwrap(),
"mtime of symlink target was unexpectedly modified"
);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_futimens() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
futimens(&fd, &TimeSpec::seconds(10), &TimeSpec::seconds(20)).unwrap();
assert_times_eq(10, 20, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimensat() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
drop(File::create(&fullpath).unwrap());
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
utimensat(
&dirfd,
filename,
&TimeSpec::seconds(12345),
&TimeSpec::seconds(678),
UtimensatFlags::FollowSymlink,
)
.unwrap();
assert_times_eq(12345, 678, &fs::metadata(&fullpath).unwrap());
chdir(tempdir.path()).unwrap();
utimensat(
fcntl::AT_FDCWD,
filename,
&TimeSpec::seconds(500),
&TimeSpec::seconds(800),
UtimensatFlags::FollowSymlink,
)
.unwrap();
assert_times_eq(500, 800, &fs::metadata(&fullpath).unwrap());
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_mkdirat_success_path() {
let tempdir = tempfile::tempdir().unwrap();
let filename = "example_subdir";
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
mkdirat(&dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed");
assert!(Path::exists(&tempdir.path().join(filename)));
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_mkdirat_success_mode() {
let expected_bits =
stat::SFlag::S_IFDIR.bits() | stat::Mode::S_IRWXU.bits();
let tempdir = tempfile::tempdir().unwrap();
let filename = "example_subdir";
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
mkdirat(&dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed");
let permissions = fs::metadata(tempdir.path().join(filename))
.unwrap()
.permissions();
let mode = permissions.mode();
assert_eq!(mode as mode_t, expected_bits)
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_mkdirat_fail() {
let tempdir = tempfile::tempdir().unwrap();
let not_dir_filename = "example_not_dir";
let filename = "example_subdir_dir";
let dirfd = fcntl::open(
&tempdir.path().join(not_dir_filename),
fcntl::OFlag::O_CREAT,
stat::Mode::empty(),
)
.unwrap();
let result = mkdirat(dirfd, filename, Mode::S_IRWXU).unwrap_err();
assert_eq!(result, Errno::ENOTDIR);
}
#[test]
#[cfg(not(any(
freebsdlike,
apple_targets,
target_os = "haiku",
target_os = "redox",
target_os = "solaris"
)))]
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_eq!(mode & libc::S_IFREG, libc::S_IFREG);
assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
}
#[test]
#[cfg(not(any(
solarish,
freebsdlike,
apple_targets,
target_os = "haiku",
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, file_name, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap();
let mode = fstatat(&target_dir, file_name, AtFlags::AT_SYMLINK_NOFOLLOW)
.unwrap()
.st_mode as mode_t;
assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_futimens_unchanged() {
let tempdir = tempfile::tempdir().unwrap();
let fullpath = tempdir.path().join("file");
drop(File::create(&fullpath).unwrap());
let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let old_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let old_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
futimens(&fd, &TimeSpec::UTIME_OMIT, &TimeSpec::UTIME_OMIT).unwrap();
let new_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let new_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
assert_eq!(old_atime, new_atime);
assert_eq!(old_mtime, new_mtime);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_utimensat_unchanged() {
let _dr = crate::DirRestore::new();
let tempdir = tempfile::tempdir().unwrap();
let filename = "foo.txt";
let fullpath = tempdir.path().join(filename);
drop(File::create(&fullpath).unwrap());
let dirfd =
fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
.unwrap();
let old_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let old_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
utimensat(
&dirfd,
filename,
&TimeSpec::UTIME_OMIT,
&TimeSpec::UTIME_OMIT,
UtimensatFlags::NoFollowSymlink,
)
.unwrap();
let new_atime = fs::metadata(fullpath.as_path())
.unwrap()
.accessed()
.unwrap();
let new_mtime = fs::metadata(fullpath.as_path())
.unwrap()
.modified()
.unwrap();
assert_eq!(old_atime, new_atime);
assert_eq!(old_mtime, new_mtime);
}
// The conversion is not useless on all platforms.
#[allow(clippy::useless_conversion)]
#[cfg(target_os = "freebsd")]
#[test]
fn test_chflags() {
use nix::{
sys::stat::{fstat, FileFlag},
unistd::chflags,
};
use tempfile::NamedTempFile;
let f = NamedTempFile::new().unwrap();
let initial =
FileFlag::from_bits_truncate(fstat(&f).unwrap().st_flags.into());
// UF_OFFLINE is preserved by all FreeBSD file systems, but not interpreted
// in any way, so it's handy for testing.
let commanded = initial ^ FileFlag::UF_OFFLINE;
chflags(f.path(), commanded).unwrap();
let changed =
FileFlag::from_bits_truncate(fstat(&f).unwrap().st_flags.into());
assert_eq!(commanded, changed);
}

98
vendor/nix/test/sys/test_statfs.rs vendored Normal file
View File

@@ -0,0 +1,98 @@
use nix::sys::statfs::*;
use nix::sys::statvfs::*;
use std::fs::File;
use std::path::Path;
fn check_fstatfs(path: &str) {
if !Path::new(path).exists() {
return;
}
let vfs = statvfs(path.as_bytes()).unwrap();
let file = File::open(path).unwrap();
let fs = fstatfs(&file).unwrap();
assert_fs_equals(fs, vfs);
}
fn check_statfs(path: &str) {
if !Path::new(path).exists() {
return;
}
let vfs = statvfs(path.as_bytes()).unwrap();
let fs = statfs(path.as_bytes()).unwrap();
assert_fs_equals(fs, vfs);
}
fn check_fstatfs_strict(path: &str) {
if !Path::new(path).exists() {
return;
}
let vfs = statvfs(path.as_bytes());
let file = File::open(path).unwrap();
let fs = fstatfs(&file);
assert_fs_equals_strict(fs.unwrap(), vfs.unwrap())
}
fn check_statfs_strict(path: &str) {
if !Path::new(path).exists() {
return;
}
let vfs = statvfs(path.as_bytes());
let fs = statfs(path.as_bytes());
assert_fs_equals_strict(fs.unwrap(), vfs.unwrap())
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn assert_fs_equals(fs: Statfs, vfs: Statvfs) {
assert_eq!(fs.blocks() as u64, vfs.blocks() as u64);
assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64);
}
#[test]
fn statfs_call() {
check_statfs("/tmp");
check_statfs("/dev");
check_statfs("/run");
check_statfs("/");
}
#[test]
fn fstatfs_call() {
check_fstatfs("/tmp");
check_fstatfs("/dev");
check_fstatfs("/run");
check_fstatfs("/");
}
// This test is ignored because files_free/blocks_free can change after statvfs call and before
// statfs call.
#[test]
#[ignore]
fn statfs_call_strict() {
check_statfs_strict("/tmp");
check_statfs_strict("/dev");
check_statfs_strict("/run");
check_statfs_strict("/");
}
// This test is ignored because files_free/blocks_free can change after statvfs call and before
// fstatfs call.
#[test]
#[ignore]
fn fstatfs_call_strict() {
check_fstatfs_strict("/tmp");
check_fstatfs_strict("/dev");
check_fstatfs_strict("/run");
check_fstatfs_strict("/");
}
// The cast is not unnecessary on all platforms.
#[allow(clippy::unnecessary_cast)]
fn assert_fs_equals_strict(fs: Statfs, vfs: Statvfs) {
assert_eq!(fs.files_free() as u64, vfs.files_free() as u64);
assert_eq!(fs.blocks_free() as u64, vfs.blocks_free() as u64);
assert_eq!(fs.blocks_available() as u64, vfs.blocks_available() as u64);
assert_eq!(fs.files() as u64, vfs.files() as u64);
assert_eq!(fs.blocks() as u64, vfs.blocks() as u64);
assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64);
}

13
vendor/nix/test/sys/test_statvfs.rs vendored Normal file
View File

@@ -0,0 +1,13 @@
use nix::sys::statvfs::*;
use std::fs::File;
#[test]
fn statvfs_call() {
statvfs(&b"/"[..]).unwrap();
}
#[test]
fn fstatvfs_call() {
let root = File::open("/").unwrap();
fstatvfs(&root).unwrap();
}

20
vendor/nix/test/sys/test_sysinfo.rs vendored Normal file
View File

@@ -0,0 +1,20 @@
use nix::sys::sysinfo::*;
#[test]
fn sysinfo_works() {
let info = sysinfo().unwrap();
let (l1, l5, l15) = info.load_average();
assert!(l1 >= 0.0);
assert!(l5 >= 0.0);
assert!(l15 >= 0.0);
info.uptime(); // just test Duration construction
assert!(
info.swap_free() <= info.swap_total(),
"more swap available than installed (free: {}, total: {})",
info.swap_free(),
info.swap_total()
);
}

117
vendor/nix/test/sys/test_termios.rs vendored Normal file
View File

@@ -0,0 +1,117 @@
use std::os::unix::io::AsFd;
use tempfile::tempfile;
use nix::errno::Errno;
use nix::fcntl;
use nix::pty::openpty;
use nix::sys::termios::{self, tcgetattr, BaudRate, LocalFlags, OutputFlags};
use nix::unistd::{read, write};
/// Helper function analogous to `std::io::Write::write_all`, but for `Fd`s
fn write_all<Fd: AsFd>(f: Fd, buf: &[u8]) {
let mut len = 0;
while len < buf.len() {
len += write(f.as_fd(), &buf[len..]).unwrap();
}
}
#[test]
fn test_baudrate_try_from() {
assert_eq!(Ok(BaudRate::B0), BaudRate::try_from(libc::B0));
#[cfg(not(target_os = "haiku"))]
BaudRate::try_from(999999999).expect_err("assertion failed");
#[cfg(target_os = "haiku")]
BaudRate::try_from(99).expect_err("assertion failed");
}
// Test tcgetattr on a terminal
#[test]
fn test_tcgetattr_pty() {
// openpty uses ptname(3) internally
let _m = crate::PTSNAME_MTX.lock();
let pty = openpty(None, None).expect("openpty failed");
termios::tcgetattr(&pty.slave).unwrap();
}
// Test tcgetattr on something that isn't a terminal
#[test]
fn test_tcgetattr_enotty() {
let file = tempfile().unwrap();
assert_eq!(termios::tcgetattr(&file).err(), Some(Errno::ENOTTY));
}
// Test modifying output flags
#[test]
fn test_output_flags() {
// openpty uses ptname(3) internally
let _m = crate::PTSNAME_MTX.lock();
// Open one pty to get attributes for the second one
let mut termios = {
let pty = openpty(None, None).expect("openpty failed");
tcgetattr(&pty.slave).expect("tcgetattr failed")
};
// Make sure postprocessing '\r' isn't specified by default or this test is useless.
assert!(!termios
.output_flags
.contains(OutputFlags::OPOST | OutputFlags::OCRNL));
// Specify that '\r' characters should be transformed to '\n'
// OPOST is specified to enable post-processing
termios
.output_flags
.insert(OutputFlags::OPOST | OutputFlags::OCRNL);
// Open a pty
let pty = openpty(None, &termios).unwrap();
// Write into the master
let string = "foofoofoo\r";
write_all(&pty.master, string.as_bytes());
// Read from the slave verifying that the output has been properly transformed
let mut buf = [0u8; 10];
crate::read_exact(&pty.slave, &mut buf);
let transformed_string = "foofoofoo\n";
assert_eq!(&buf, transformed_string.as_bytes());
}
// Test modifying local flags
#[test]
#[cfg(not(target_os = "solaris"))]
fn test_local_flags() {
// openpty uses ptname(3) internally
let _m = crate::PTSNAME_MTX.lock();
// Open one pty to get attributes for the second one
let mut termios = {
let pty = openpty(None, None).unwrap();
tcgetattr(&pty.slave).unwrap()
};
// Make sure echo is specified by default or this test is useless.
assert!(termios.local_flags.contains(LocalFlags::ECHO));
// Disable local echo
termios.local_flags.remove(LocalFlags::ECHO);
// Open a new pty with our modified termios settings
let pty = openpty(None, &termios).unwrap();
// Set the master is in nonblocking mode or reading will never return.
let flags = fcntl::fcntl(&pty.master, fcntl::F_GETFL).unwrap();
let new_flags =
fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK;
fcntl::fcntl(pty.master.as_fd(), fcntl::F_SETFL(new_flags)).unwrap();
// Write into the master
let string = "foofoofoo\r";
write_all(&pty.master, string.as_bytes());
// Try to read from the master, which should not have anything as echoing was disabled.
let mut buf = [0u8; 10];
let read = read(&pty.master, &mut buf).unwrap_err();
assert_eq!(read, Errno::EAGAIN);
}

Some files were not shown because too many files have changed in this diff Show More