diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/nix | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/nix')
92 files changed, 24529 insertions, 0 deletions
diff --git a/third_party/rust/nix/.cargo-checksum.json b/third_party/rust/nix/.cargo-checksum.json new file mode 100644 index 0000000000..360db4fb0f --- /dev/null +++ b/third_party/rust/nix/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cirrus.yml":"575b95843c9b44537d23c922a45367407e2df11850de860b2c6e10cac17bf6e5","CHANGELOG.md":"a7fdd923949c35aeeb74a6bcbdc7f4b1bf002ac5e8357fb7f26524b7dd89be4a","CONTRIBUTING.md":"a9101e3d1487170d691d5f062ff49a433c167582ac8984dd41a744be92652f74","CONVENTIONS.md":"e150ce43c1d188c392c1a3bf7f2e08e3cf84906705c7bef43f319037d29ea385","Cargo.toml":"c431d14813f84fabde8a2cf83732dd19e3421a15981c71c52dfcf096439f1e8d","LICENSE":"66e3ee1fa7f909ad3c612d556f2a0cdabcd809ad6e66f3b0605015ac64841b70","README.md":"bea57ef82d83f2f3253128d1fe19e621c0090cd1ca94b6b5eec39de0b1bd8213","build.rs":"14c9c678c33f5894509da47f77d6a326b14aecb4190ce87a24cce98687ca63b2","src/dir.rs":"4cab6057561b0cd410f8592b025897e97ee8ec9aeeef3551e5f4f49eb6d51595","src/errno.rs":"1333550c57702687d3ad17fafb6d753e24ee0e941d7e90ff197001a508483b73","src/errno_dragonfly.c":"a857e47b114acb85fddcb252a610ab5734d225c26b7bedd7c35d7789d46c8526","src/fcntl.rs":"5c0a541dfc2f1a8eef3c40a85bd0c088d80bd957b23ab4e8f0add1bdd21ff7c1","src/features.rs":"96dddb379462eecdacbbf555b57ae67091392aca8cc9c2a650fce6e58566a8d8","src/ifaddrs.rs":"8316679172dfc5068764fc56be66d5a0ea335759bb4f49dd5cb73f5ef637e64c","src/kmod.rs":"4d8a695d3d761f351a39d654303a1bd168e74295b7d142b918737e355b24f34d","src/lib.rs":"7d8f96632d33e84a3404a77047db5d5eb0be590bd9a34a8dd089008c30877627","src/macros.rs":"aec27fa0fd98900913fada926c9a4581cd28f2640e3a7b5480707f923c9200f8","src/mount.rs":"828a85e2ab79c79e659f9a23dc7b30159717359c8965dc14005e8b0e738f7a96","src/mqueue.rs":"8fb7cf968eae132e920eafdb0666f24488e90e79bf4353ccbd86ac7854c3f00d","src/net/if_.rs":"f7e02076fcf3cadf3fdf141884c9bd2c468a7047ba60bc490f0057df802b53ce","src/net/mod.rs":"577f70170e53d4a6de1abb70bf8f1031ec3e65c0e63ef5fcf05c907125e7ac17","src/poll.rs":"fbfe998d309cee2172ff9f36983e4ae3427d5d286c018e74d4a933a438eb4a8c","src/pty.rs":"cb3a152828dfbaa7744c3575c4573b6b78c96119bba9fc5be2fcb1c513778c1a","src/sched.rs":"55f447f52fad261809557ffe40a758be64127eef9c4fa040bce568ce90e72fd0","src/sys/aio.rs":"44eedf4c5e61f372f6a97a8c5050fce16327fcf666ef1ac1ea242a029b2735e6","src/sys/epoll.rs":"62e42d4860316ec0ebec19ad4b2648980162d3db725219c60bfde0034e61535e","src/sys/event.rs":"9ab3069f566c229fbe54f594b950744da602c2c57a08c531a678ae7368e32549","src/sys/eventfd.rs":"08008cf3dc64c2216847c02c0dd8d7189cf08edbaafe35ba2c57c053fde09ef4","src/sys/ioctl/bsd.rs":"84f370169dd341cf03b38dc0aeb995c9ff9db8a10a9bc429a3f7bf28b3879949","src/sys/ioctl/linux.rs":"51da98e73e28c04b118d481f636087b75ca81ead34d53918d09b87206d30747e","src/sys/ioctl/mod.rs":"24df6e1fd54fdcde553be56cf6b11e36e04c9182a8812b40380bb3a1c8b08a1e","src/sys/memfd.rs":"11cd93c867fdbdbc9588cecb94268691de42b2ef2a38fe33525be7c7f60c85d5","src/sys/mman.rs":"c6d042e61e860131fcccbaab6c8f5d5af34f356b92d322379102f929e61d7e8d","src/sys/mod.rs":"c94cd8fa162d9af8c6e884381e4c440a9d893d020dfb3e39e330a882780406a3","src/sys/pthread.rs":"cfa9ccd6f3b86c0c3fe012773c9c82a7813b298c2f20f8ab629781db627ce56b","src/sys/ptrace/bsd.rs":"8a7eacfc172b55763ae32109bf9b252669ba68b72cd5122f7504eb35c0c08345","src/sys/ptrace/linux.rs":"f09b45148004f4b28d8503c397a8d112d31046c98e68335bf4e89425d5b33f07","src/sys/ptrace/mod.rs":"671a6ccac955e75d5998f7e53ffc45ed4c7b6522a0f24a0937d60141f692dd39","src/sys/quota.rs":"4e65e0d25f42103ae4d4d0b96159bfb1de940f6a54fb90ec566c11ea24b7097d","src/sys/reboot.rs":"fde9da27c2928f7026231430fa14fec2058df4e49a0aeda2a237a60524f11241","src/sys/select.rs":"d8d15e0a36f76729e53f2cc036748c9e1279b2669e4c9e7a2ff0cbc76ba65a9f","src/sys/sendfile.rs":"1175a1c0507874e5f15af472ef0d346b957d5c791e3cd86ba7bf176219f96725","src/sys/signal.rs":"f124076bcb6b26163fa76ef4955309cf841df661d65ec6918f62d206b0a45408","src/sys/signalfd.rs":"bfcfce619bf199e50f9cc80a3eb778d48474a015cfdafc64a0c3517373a225a9","src/sys/socket/addr.rs":"870d8ecd2067a0c2175130607850e2649afadd9df3293b491ce3b8a702be2087","src/sys/socket/mod.rs":"fc60fc1e86b63c7a7347b731f3c79546fbf529815bcfe2d892178eb05c80864a","src/sys/socket/sockopt.rs":"50cd2a1d1be788bc87e71c0daff076b27a4290adaf86e1a91f20a8c7e8804b69","src/sys/stat.rs":"352c87619e4e2fc370c52cc084b9be6f268fd8b60ab0a1d30c81cb6bf6b416f5","src/sys/statfs.rs":"39f276bb13133679a79134b998b6761f95ce22ae963eae57dc4a070cd31d36ef","src/sys/statvfs.rs":"889213d0c512fde373c2a9dbee7f33b15d179f6f99677899b861780437a22493","src/sys/sysinfo.rs":"cecc8134bc8a1ed8068b37b856da9bd52625e48a10ef08b379fa6f75e6a6de4f","src/sys/termios.rs":"ea01fb2d33095176b39e8faae46b77e72bccd246f29aed5efed5010eb6ad3333","src/sys/time.rs":"5b70e4f829117e13ddf12fe5527819ed737ac87ea538e312e74b82c44d6d2deb","src/sys/uio.rs":"becb4c81b133a4837a27065b0599fd3ac67c0cf513973ef38b377e4bcc621ec6","src/sys/utsname.rs":"8fc57acc3947486f1809b21884eec047592297fb1dd739b48f6736f77c84bab9","src/sys/wait.rs":"9ce0ab7e4925bb5af67097c2020ed5ec5021e0b9701843387b31509a6bf5fa4c","src/ucontext.rs":"a03daf21919a091ee69dc0c51acc38bd2a96ac7fc828fae8df3b4d480d346e74","src/unistd.rs":"f26b7df61744afaa8a4ae67192401772482593d878d28b823bc5d822650b0670","test/sys/mod.rs":"3a7f212b7ff6a6fa3d2cd3ab635cdc1ba6d2fde02ceddb7b40e06059fc6d8f29","test/sys/test_aio.rs":"b2544bfb321ca7fbed276ee637c769fb438156d14666cdc1e1d547b3514a44e3","test/sys/test_aio_drop.rs":"30dd1d238269d00381fa50f6d3cb2b13794b7cceb9f6455f3878fcbffa9aa62d","test/sys/test_epoll.rs":"35093d0cb1096a934dfc4f6efc737eadc4bdc2e2134d2a879061374a51b10c97","test/sys/test_ioctl.rs":"eea690ed386da0a666df5eb23a417421fddb99dc8e39556f63b30969bb6cf779","test/sys/test_lio_listio_resubmit.rs":"203a583313542593148f375b087ae30620222a745680173fa98fc448d1e5ae7f","test/sys/test_pthread.rs":"3890e5ecbf2082e0d05d102cc9cec6e76ede3c15f250d104e3483b1c1c3400b1","test/sys/test_ptrace.rs":"4e8d5dff5fe6bc56e4ae53bdfd10f5e8ea567d8099576d1c690cf7a6b2bc955f","test/sys/test_select.rs":"bdb20211fc6ec1e3f186337eac51e08757acb6901d307d67c71bf9011f0d54bd","test/sys/test_signal.rs":"12d104ecee92dee9c7867f88cfa2105fe83ee31833091488ec38167510b2222f","test/sys/test_signalfd.rs":"71b5d6d782283f6db64ca90f7fb06617faec71091d59d2587e41bbc9d8c43d5c","test/sys/test_socket.rs":"7bcc626dd7576af7ed49ddb0f25f88960349557020b339ceba9fa48781aa41c8","test/sys/test_sockopt.rs":"9954047b09cc49d1c3168cb2e48ad753de1b9568925b7330f63d0357ee3bdb21","test/sys/test_sysinfo.rs":"1e1bea9130fe38ccb07cd0ad7334c7be1e45efc33f7656a5973f8cad7126f225","test/sys/test_termios.rs":"fa4be3ade859b527bf33408f85a6f57b127917cf5f2afb662d09f6019d07913a","test/sys/test_uio.rs":"55f674f8f603e51465befef110f6ccb4f945f4b9fbfd364b9c05c9ef7dbe1004","test/sys/test_wait.rs":"e6c5147e213daa93892cd828f53214995d2e019ff2372cc48d85ce9b93d26ec9","test/test.rs":"eb277cb169a7be77d1520326e4fff4e44fd15de2aaba6e2e28fc0557dee47843","test/test_dir.rs":"5d137a62f11d1a4993b4bb35dccc38a4c4416b7da374887f2335a9895b4fdee4","test/test_fcntl.rs":"fc46811761cf0d6d3236692cfee37f863acf65313014292c06bf76a638299026","test/test_kmod/hello_mod/Makefile":"0219f7bce0603f97d997fb377ca071966c90333ecc665e78a54dfeb97a9c811b","test/test_kmod/hello_mod/hello.c":"bcac6b19c5bd807e1f3878c15e426acc85785a8ade9840c3bb4d068635c9188c","test/test_kmod/mod.rs":"727618a0b13f5f27689b04b3132e9a211e2d255b8b5268057001a4fcc3b1f358","test/test_mount.rs":"78ddc657f5098360c764fffa3a7d844503e4b6b65b44bfd42d9aa9045b415cb6","test/test_mq.rs":"6fe92ae18ce203f8744e1487bb616f50dd1c5c898b1311d57ac7598560bae8eb","test/test_net.rs":"ec6d580b87292519d514b0236bdd5abdd576fcf4835cfe49ed1ddb47c5f1aea3","test/test_nix_path.rs":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","test/test_poll.rs":"a81dc01ae3c438aa3f921ca5d9b1f9a466737a58c2a7b4f785a3d2a8434ede2c","test/test_pty.rs":"dd91c509d9a6909347cb385b3d22552d3ac58c98386c1f6889aa520f76ee9d5b","test/test_ptymaster_drop.rs":"5cfbbb79551c205ab510c2d4ef497bf937ceac9151fbe2f2e543d6515e406990","test/test_sendfile.rs":"e0cbabbd34052ccaa03d6555d5631686aa076728f6378ee90f7ecec68f891144","test/test_stat.rs":"8224e990ae3de064caf6447d958477f75d127042d63dadb03a38f56b9e044d6d","test/test_unistd.rs":"79ab1b4526b0d2dd5395c8109a7aecaab7371b124f7751bd5cbe47d46f357f44"},"package":null}
\ No newline at end of file diff --git a/third_party/rust/nix/.cirrus.yml b/third_party/rust/nix/.cirrus.yml new file mode 100644 index 0000000000..e8fddcfd21 --- /dev/null +++ b/third_party/rust/nix/.cirrus.yml @@ -0,0 +1,21 @@ +freebsd_instance: + image: freebsd-11-2-release-amd64 + +# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the +# same VM. The binary will be built in 32-bit mode, but will execute on a +# 64-bit kernel and in a 64-bit environment. Our tests don't execute any of +# the system's binaries, so the environment shouldn't matter. +task: + name: FreeBSD 11.2 + # Install Rust + setup_script: + - pkg install -y curl + - curl https://sh.rustup.rs -sSf --output rustup.sh + - sh rustup.sh -y --default-toolchain 1.24.1 + - $HOME/.cargo/bin/rustup target add i686-unknown-freebsd + amd64_test_script: + - . $HOME/.cargo/env + - cargo test + i386_test_script: + - . $HOME/.cargo/env + - cargo test --target i686-unknown-freebsd diff --git a/third_party/rust/nix/CHANGELOG.md b/third_party/rust/nix/CHANGELOG.md new file mode 100644 index 0000000000..c1b8776814 --- /dev/null +++ b/third_party/rust/nix/CHANGELOG.md @@ -0,0 +1,624 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +## [0.13.1] - 2019-06-10 +### Added +### Changed +- Changed some public types from reexports of libc types like `uint32_t` to the + native equivalents like u32. + ([#1072](https://github.com/nix-rust/nix/pull/1072)) + +### Fixed +- Fixed the build on Android and Linux/mips with recent versions of libc. + ([#1072](https://github.com/nix-rust/nix/pull/1072)) +- Fixed build on Linux/arm and Linux/s390x with the latest Rust libc + ([52102cb](https://github.com/nix-rust/nix/commit/52102cb76398c4dfb9ea141b98c5b01a2e050973)) + +### Removed +- `Daemon`, `NOTE_REAP`, and `NOTE_EXIT_REPARENTED` are now deprecated on OSX + and iOS. + ([#1033](https://github.com/nix-rust/nix/pull/1033)) + +## [0.13.0] - 2019-01-15 +### Added +- Added PKTINFO(V4) & V6PKTINFO cmsg support - Android/FreeBSD/iOS/Linux/MacOS. + ([#990](https://github.com/nix-rust/nix/pull/990)) +- Added support of CString type in `setsockopt`. + ([#972](https://github.com/nix-rust/nix/pull/972)) +- Added option `TCP_CONGESTION` in `setsockopt`. + ([#972](https://github.com/nix-rust/nix/pull/972)) +- Added `symlinkat` wrapper. + ([#997](https://github.com/nix-rust/nix/pull/997)) +- Added `ptrace::{getregs, setregs}`. + ([#1010](https://github.com/nix-rust/nix/pull/1010)) +- Added `nix::sys::signal::signal`. + ([#817](https://github.com/nix-rust/nix/pull/817)) + +### Changed +### Fixed +- `lutimes` never worked on OpenBSD as it is not implemented on OpenBSD. It has + been removed. ([#1000](https://github.com/nix-rust/nix/pull/1000)) +- `fexecve` never worked on NetBSD or on OpenBSD as it is not implemented on + either OS. It has been removed. ([#1000](https://github.com/nix-rust/nix/pull/1000)) + +### Removed + +## [0.12.0] 2018-11-28 + +### Added +- Added `FromStr` and `Display` impls for `nix::sys::Signal` + ([#884](https://github.com/nix-rust/nix/pull/884)) +- Added a `sync` wrapper. + ([#961](https://github.com/nix-rust/nix/pull/961)) +- Added a `sysinfo` wrapper. + ([#922](https://github.com/nix-rust/nix/pull/922)) +- Support the `SO_PEERCRED` socket option and the `UnixCredentials` type on all Linux and Android targets. + ([#921](https://github.com/nix-rust/nix/pull/921)) +- Added support for `SCM_CREDENTIALS`, allowing to send process credentials over Unix sockets. + ([#923](https://github.com/nix-rust/nix/pull/923)) +- Added a `dir` module for reading directories (wraps `fdopendir`, `readdir`, and `rewinddir`). + ([#916](https://github.com/nix-rust/nix/pull/916)) +- Added `kmod` module that allows loading and unloading kernel modules on Linux. + ([#930](https://github.com/nix-rust/nix/pull/930)) +- Added `futimens` and `utimesat` wrappers ([#944](https://github.com/nix-rust/nix/pull/944)), + an `lutimes` wrapper ([#967](https://github.com/nix-rust/nix/pull/967)), + and a `utimes` wrapper ([#946](https://github.com/nix-rust/nix/pull/946)). +- Added `AF_UNSPEC` wrapper to `AddressFamily` ([#948](https://github.com/nix-rust/nix/pull/948)) +- Added the `mode_t` public alias within `sys::stat`. + ([#954](https://github.com/nix-rust/nix/pull/954)) +- Added a `truncate` wrapper. + ([#956](https://github.com/nix-rust/nix/pull/956)) +- Added a `fchownat` wrapper. + ([#955](https://github.com/nix-rust/nix/pull/955)) +- Added support for `ptrace` on BSD operating systems ([#949](https://github.com/nix-rust/nix/pull/949)) +- Added `ptrace` functions for reads and writes to tracee memory and ptrace kill + ([#949](https://github.com/nix-rust/nix/pull/949)) ([#958](https://github.com/nix-rust/nix/pull/958)) +- Added a `acct` wrapper module for enabling and disabling process accounting + ([#952](https://github.com/nix-rust/nix/pull/952)) +- Added the `time_t` and `suseconds_t` public aliases within `sys::time`. + ([#968](https://github.com/nix-rust/nix/pull/968)) +- Added `unistd::execvpe` for Haiku, Linux and OpenBSD + ([#975](https://github.com/nix-rust/nix/pull/975)) +- Added `Error::as_errno`. + ([#977](https://github.com/nix-rust/nix/pull/977)) + +### Changed +- Increased required Rust version to 1.24.1 + ([#900](https://github.com/nix-rust/nix/pull/900)) + ([#966](https://github.com/nix-rust/nix/pull/966)) + +### Fixed +- Made `preadv` take immutable slice of IoVec. + ([#914](https://github.com/nix-rust/nix/pull/914)) +- Fixed passing multiple file descriptors over Unix Sockets. + ([#918](https://github.com/nix-rust/nix/pull/918)) + +### Removed + +## [0.11.0] 2018-06-01 + +### Added +- Added `sendfile` on FreeBSD and Darwin. + ([#901](https://github.com/nix-rust/nix/pull/901)) +- Added `pselect` + ([#894](https://github.com/nix-rust/nix/pull/894)) +- Exposed `preadv` and `pwritev` on the BSDs. + ([#883](https://github.com/nix-rust/nix/pull/883)) +- Added `mlockall` and `munlockall` + ([#876](https://github.com/nix-rust/nix/pull/876)) +- Added `SO_MARK` on Linux. + ([#873](https://github.com/nix-rust/nix/pull/873)) +- Added safe support for nearly any buffer type in the `sys::aio` module. + ([#872](https://github.com/nix-rust/nix/pull/872)) +- Added `sys::aio::LioCb` as a wrapper for `libc::lio_listio`. + ([#872](https://github.com/nix-rust/nix/pull/872)) +- Added `unistd::getsid` + ([#850](https://github.com/nix-rust/nix/pull/850)) +- Added `alarm`. ([#830](https://github.com/nix-rust/nix/pull/830)) +- Added interface flags `IFF_NO_PI, IFF_TUN, IFF_TAP` on linux-like systems. + ([#853](https://github.com/nix-rust/nix/pull/853)) +- Added `statvfs` module to all MacOS and Linux architectures. + ([#832](https://github.com/nix-rust/nix/pull/832)) +- Added `EVFILT_EMPTY`, `EVFILT_PROCDESC`, and `EVFILT_SENDFILE` on FreeBSD. + ([#825](https://github.com/nix-rust/nix/pull/825)) +- Exposed `termios::cfmakesane` on FreeBSD. + ([#825](https://github.com/nix-rust/nix/pull/825)) +- Exposed `MSG_CMSG_CLOEXEC` on *BSD. + ([#825](https://github.com/nix-rust/nix/pull/825)) +- Added `fchmod`, `fchmodat`. + ([#857](https://github.com/nix-rust/nix/pull/857)) +- Added `request_code_write_int!` on FreeBSD/DragonFlyBSD + ([#833](https://github.com/nix-rust/nix/pull/833)) + +### Changed +- `Display` and `Debug` for `SysControlAddr` now includes all fields. + ([#837](https://github.com/nix-rust/nix/pull/837)) +- `ioctl!` has been replaced with a family of `ioctl_*!` macros. + ([#833](https://github.com/nix-rust/nix/pull/833)) +- `io!`, `ior!`, `iow!`, and `iorw!` has been renamed to `request_code_none!`, `request_code_read!`, + `request_code_write!`, and `request_code_readwrite!` respectively. These have also now been exposed + in the documentation. + ([#833](https://github.com/nix-rust/nix/pull/833)) +- Enabled more `ptrace::Request` definitions for uncommon Linux platforms + ([#892](https://github.com/nix-rust/nix/pull/892)) +- Emulation of `FD_CLOEXEC` and `O_NONBLOCK` was removed from `socket()`, `accept4()`, and + `socketpair()`. + ([#907](https://github.com/nix-rust/nix/pull/907)) + +### Fixed +- Fixed possible panics when using `SigAction::flags` on Linux + ([#869](https://github.com/nix-rust/nix/pull/869)) +- Properly exposed 460800 and 921600 baud rates on NetBSD + ([#837](https://github.com/nix-rust/nix/pull/837)) +- Fixed `ioctl_write_int!` on FreeBSD/DragonFlyBSD + ([#833](https://github.com/nix-rust/nix/pull/833)) +- `ioctl_write_int!` now properly supports passing a `c_ulong` as the parameter on Linux non-musl targets + ([#833](https://github.com/nix-rust/nix/pull/833)) + +### Removed +- Removed explicit support for the `bytes` crate from the `sys::aio` module. + See `sys::aio::AioCb::from_boxed_slice` examples for alternatives. + ([#872](https://github.com/nix-rust/nix/pull/872)) +- Removed `sys::aio::lio_listio`. Use `sys::aio::LioCb::listio` instead. + ([#872](https://github.com/nix-rust/nix/pull/872)) +- Removed emulated `accept4()` from macos, ios, and netbsd targets + ([#907](https://github.com/nix-rust/nix/pull/907)) +- Removed `IFF_NOTRAILERS` on OpenBSD, as it has been removed in OpenBSD 6.3 + ([#893](https://github.com/nix-rust/nix/pull/893)) + +## [0.10.0] 2018-01-26 + +### Added +- Added specialized wrapper: `sys::ptrace::step` + ([#852](https://github.com/nix-rust/nix/pull/852)) +- Added `AioCb::from_ptr` and `AioCb::from_mut_ptr` + ([#820](https://github.com/nix-rust/nix/pull/820)) +- Added specialized wrappers: `sys::ptrace::{traceme, syscall, cont, attach}`. Using the matching routines + with `sys::ptrace::ptrace` is now deprecated. +- Added `nix::poll` module for all platforms + ([#672](https://github.com/nix-rust/nix/pull/672)) +- Added `nix::ppoll` function for FreeBSD and DragonFly + ([#672](https://github.com/nix-rust/nix/pull/672)) +- Added protocol families in `AddressFamily` enum. + ([#647](https://github.com/nix-rust/nix/pull/647)) +- Added the `pid()` method to `WaitStatus` for extracting the PID. + ([#722](https://github.com/nix-rust/nix/pull/722)) +- Added `nix::unistd:fexecve`. + ([#727](https://github.com/nix-rust/nix/pull/727)) +- Expose `uname()` on all platforms. + ([#739](https://github.com/nix-rust/nix/pull/739)) +- Expose `signalfd` module on Android as well. + ([#739](https://github.com/nix-rust/nix/pull/739)) +- Added `nix::sys::ptrace::detach`. + ([#749](https://github.com/nix-rust/nix/pull/749)) +- Added timestamp socket control message variant: + `nix::sys::socket::ControlMessage::ScmTimestamp` + ([#663](https://github.com/nix-rust/nix/pull/663)) +- Added socket option variant that enables the timestamp socket + control message: `nix::sys::socket::sockopt::ReceiveTimestamp` + ([#663](https://github.com/nix-rust/nix/pull/663)) +- Added more accessor methods for `AioCb` + ([#773](https://github.com/nix-rust/nix/pull/773)) +- Add `nix::sys::fallocate` + ([#768](https:://github.com/nix-rust/nix/pull/768)) +- Added `nix::unistd::mkfifo`. + ([#602](https://github.com/nix-rust/nix/pull/774)) +- Added `ptrace::Options::PTRACE_O_EXITKILL` on Linux and Android. + ([#771](https://github.com/nix-rust/nix/pull/771)) +- Added `nix::sys::uio::{process_vm_readv, process_vm_writev}` on Linux + ([#568](https://github.com/nix-rust/nix/pull/568)) +- Added `nix::unistd::{getgroups, setgroups, getgrouplist, initgroups}`. ([#733](https://github.com/nix-rust/nix/pull/733)) +- Added `nix::sys::socket::UnixAddr::as_abstract` on Linux and Android. + ([#785](https://github.com/nix-rust/nix/pull/785)) +- Added `nix::unistd::execveat` on Linux and Android. + ([#800](https://github.com/nix-rust/nix/pull/800)) +- Added the `from_raw()` method to `WaitStatus` for converting raw status values + to `WaitStatus` independent of syscalls. + ([#741](https://github.com/nix-rust/nix/pull/741)) +- Added more standard trait implementations for various types. + ([#814](https://github.com/nix-rust/nix/pull/814)) +- Added `sigprocmask` to the signal module. + ([#826](https://github.com/nix-rust/nix/pull/826)) +- Added `nix::sys::socket::LinkAddr` on Linux and all bsdlike system. + ([#813](https://github.com/nix-rust/nix/pull/813)) +- Add socket options for `IP_TRANSPARENT` / `BIND_ANY`. + ([#835](https://github.com/nix-rust/nix/pull/835)) + +### Changed +- Exposed the `mqueue` module for all supported operating systems. + ([#834](https://github.com/nix-rust/nix/pull/834)) +- Use native `pipe2` on all BSD targets. Users should notice no difference. + ([#777](https://github.com/nix-rust/nix/pull/777)) +- Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692)) +- Marked `sys::ptrace::ptrace` as `unsafe`. +- Changed function signature of `socket()` and `socketpair()`. The `protocol` argument + has changed type from `c_int` to `SockProtocol`. + It accepts a `None` value for default protocol that was specified with zero using `c_int`. + ([#647](https://github.com/nix-rust/nix/pull/647)) +- Made `select` easier to use, adding the ability to automatically calculate the `nfds` parameter using the new + `FdSet::highest` ([#701](https://github.com/nix-rust/nix/pull/701)) +- Exposed `unistd::setresuid` and `unistd::setresgid` on FreeBSD and OpenBSD + ([#721](https://github.com/nix-rust/nix/pull/721)) +- Refactored the `statvfs` module removing extraneous API functions and the + `statvfs::vfs` module. Additionally `(f)statvfs()` now return the struct + directly. And the returned `Statvfs` struct now exposes its data through + accessor methods. ([#729](https://github.com/nix-rust/nix/pull/729)) +- The `addr` argument to `madvise` and `msync` is now `*mut` to better match the + libc API. ([#731](https://github.com/nix-rust/nix/pull/731)) +- `shm_open` and `shm_unlink` are no longer exposed on Android targets, where + they are not officially supported. ([#731](https://github.com/nix-rust/nix/pull/731)) +- `MapFlags`, `MmapAdvise`, and `MsFlags` expose some more variants and only + officially-supported variants are provided for each target. + ([#731](https://github.com/nix-rust/nix/pull/731)) +- Marked `pty::ptsname` function as `unsafe` + ([#744](https://github.com/nix-rust/nix/pull/744)) +- Moved constants ptrace request, event and options to enums and updated ptrace functions and argument types accordingly. + ([#749](https://github.com/nix-rust/nix/pull/749)) +- `AioCb::Drop` will now panic if the `AioCb` is still in-progress ([#715](https://github.com/nix-rust/nix/pull/715)) +- Restricted `nix::sys::socket::UnixAddr::new_abstract` to Linux and Android only. + ([#785](https://github.com/nix-rust/nix/pull/785)) +- The `ucred` struct has been removed in favor of a `UserCredentials` struct that + contains only getters for its fields. + ([#814](https://github.com/nix-rust/nix/pull/814)) +- Both `ip_mreq` and `ipv6_mreq` have been replaced with `IpMembershipRequest` and + `Ipv6MembershipRequest`. + ([#814](https://github.com/nix-rust/nix/pull/814)) +- Removed return type from `pause`. + ([#829](https://github.com/nix-rust/nix/pull/829)) +- Changed the termios APIs to allow for using a `u32` instead of the `BaudRate` + enum on BSD platforms to support arbitrary baud rates. See the module docs for + `nix::sys::termios` for more details. + ([#843](https://github.com/nix-rust/nix/pull/843)) + +### Fixed +- Fix compilation and tests for OpenBSD targets + ([#688](https://github.com/nix-rust/nix/pull/688)) +- Fixed error handling in `AioCb::fsync`, `AioCb::read`, and `AioCb::write`. + It is no longer an error to drop an `AioCb` that failed to enqueue in the OS. + ([#715](https://github.com/nix-rust/nix/pull/715)) +- Fix potential memory corruption on non-Linux platforms when using + `sendmsg`/`recvmsg`, caused by mismatched `msghdr` definition. + ([#648](https://github.com/nix-rust/nix/pull/648)) + +### Removed +- `AioCb::from_boxed_slice` has been removed. It was never actually safe. Use + `from_bytes` or `from_bytes_mut` instead. + ([#820](https://github.com/nix-rust/nix/pull/820)) +- The syscall module has been removed. This only exposed enough functionality for + `memfd_create()` and `pivot_root()`, which are still exposed as separate functions. + ([#747](https://github.com/nix-rust/nix/pull/747)) +- The `Errno` variants are no longer reexported from the `errno` module. `Errno` itself is no longer reexported from the + crate root and instead must be accessed using the `errno` module. ([#696](https://github.com/nix-rust/nix/pull/696)) +- Removed `MS_VERBOSE`, `MS_NOSEC`, and `MS_BORN` from `MsFlags`. These + are internal kernel flags and should never have been exposed. + ([#814](https://github.com/nix-rust/nix/pull/814)) + + +## [0.9.0] 2017-07-23 + +### Added +- Added `sysconf`, `pathconf`, and `fpathconf` + ([#630](https://github.com/nix-rust/nix/pull/630) +- Added `sys::signal::SigAction::{ flags, mask, handler}` + ([#611](https://github.com/nix-rust/nix/pull/609) +- Added `nix::sys::pthread::pthread_self` + ([#591](https://github.com/nix-rust/nix/pull/591) +- Added `AioCb::from_boxed_slice` + ([#582](https://github.com/nix-rust/nix/pull/582) +- Added `nix::unistd::{openat, fstatat, readlink, readlinkat}` + ([#551](https://github.com/nix-rust/nix/pull/551)) +- Added `nix::pty::{grantpt, posix_openpt, ptsname/ptsname_r, unlockpt}` + ([#556](https://github.com/nix-rust/nix/pull/556) +- Added `nix::ptr::openpty` + ([#456](https://github.com/nix-rust/nix/pull/456)) +- Added `nix::ptrace::{ptrace_get_data, ptrace_getsiginfo, ptrace_setsiginfo + and nix::Error::UnsupportedOperation}` + ([#614](https://github.com/nix-rust/nix/pull/614)) +- Added `cfmakeraw`, `cfsetspeed`, and `tcgetsid`. ([#527](https://github.com/nix-rust/nix/pull/527)) +- Added "bad none", "bad write_ptr", "bad write_int", and "bad readwrite" variants to the `ioctl!` + macro. ([#670](https://github.com/nix-rust/nix/pull/670)) +- On Linux and Android, added support for receiving `PTRACE_O_TRACESYSGOOD` + events from `wait` and `waitpid` using `WaitStatus::PtraceSyscall` + ([#566](https://github.com/nix-rust/nix/pull/566)). + +### Changed +- The `ioctl!` macro and its variants now allow the generated functions to have + doccomments. ([#661](https://github.com/nix-rust/nix/pull/661)) +- Changed `ioctl!(write ...)` into `ioctl!(write_ptr ...)` and `ioctl!(write_int ..)` variants + to more clearly separate those use cases. ([#670](https://github.com/nix-rust/nix/pull/670)) +- Marked `sys::mman::{ mmap, munmap, madvise, munlock, msync }` as unsafe. + ([#559](https://github.com/nix-rust/nix/pull/559)) +- Minimum supported Rust version is now 1.13. +- Removed `revents` argument from `PollFd::new()` as it's an output argument and + will be overwritten regardless of value. + ([#542](https://github.com/nix-rust/nix/pull/542)) +- Changed type signature of `sys::select::FdSet::contains` to make `self` + immutable ([#564](https://github.com/nix-rust/nix/pull/564)) +- Introduced wrapper types for `gid_t`, `pid_t`, and `uid_t` as `Gid`, `Pid`, and `Uid` + respectively. Various functions have been changed to use these new types as + arguments. ([#629](https://github.com/nix-rust/nix/pull/629)) +- Fixed compilation on all Android and iOS targets ([#527](https://github.com/nix-rust/nix/pull/527)) + and promoted them to Tier 2 support. +- `nix::sys::statfs::{statfs,fstatfs}` uses statfs definition from `libc::statfs` instead of own linux specific type `nix::sys::Statfs`. + Also file system type constants like `nix::sys::statfs::ADFS_SUPER_MAGIC` were removed in favor of the libc equivalent. + ([#561](https://github.com/nix-rust/nix/pull/561)) +- Revised the termios API including additional tests and documentation and exposed it on iOS. ([#527](https://github.com/nix-rust/nix/pull/527)) +- `eventfd`, `signalfd`, and `pwritev`/`preadv` functionality is now included by default for all + supported platforms. ([#681](https://github.com/nix-rust/nix/pull/561)) +- The `ioctl!` macro's plain variants has been replaced with "bad read" to be consistent with + other variants. The generated functions also have more strict types for their arguments. The + "*_buf" variants also now calculate total array size and take slice references for improved type + safety. The documentation has also been dramatically improved. + ([#670](https://github.com/nix-rust/nix/pull/670)) + +### Removed +- Removed `io::Error` from `nix::Error` and the conversion from `nix::Error` to `Errno` + ([#614](https://github.com/nix-rust/nix/pull/614)) +- All feature flags have been removed in favor of conditional compilation on supported platforms. + `execvpe` is no longer supported, but this was already broken and will be added back in the next + release. ([#681](https://github.com/nix-rust/nix/pull/561)) +- Removed `ioc_*` functions and many helper constants and macros within the `ioctl` module. These + should always have been private and only the `ioctl!` should be used in public code. + ([#670](https://github.com/nix-rust/nix/pull/670)) + +### Fixed +- Fixed multiple issues compiling under different archetectures and OSes. + Now compiles on Linux/MIPS ([#538](https://github.com/nix-rust/nix/pull/538)), + `Linux/PPC` ([#553](https://github.com/nix-rust/nix/pull/553)), + `MacOS/x86_64,i686` ([#553](https://github.com/nix-rust/nix/pull/553)), + `NetBSD/x64_64` ([#538](https://github.com/nix-rust/nix/pull/538)), + `FreeBSD/x86_64,i686` ([#536](https://github.com/nix-rust/nix/pull/536)), and + `Android` ([#631](https://github.com/nix-rust/nix/pull/631)). +- `bind` and `errno_location` now work correctly on `Android` + ([#631](https://github.com/nix-rust/nix/pull/631)) +- Added `nix::ptrace` on all Linux-kernel-based platforms + [#624](https://github.com/nix-rust/nix/pull/624). Previously it was + only available on x86, x86-64, and ARM, and also not on Android. +- Fixed `sys::socket::sendmsg` with zero entry `cmsgs` parameter. + ([#623](https://github.com/nix-rust/nix/pull/623)) +- Multiple constants related to the termios API have now been properly defined for + all supported platforms. ([#527](https://github.com/nix-rust/nix/pull/527)) +- `ioctl!` macro now supports working with non-int datatypes and properly supports all platforms. + ([#670](https://github.com/nix-rust/nix/pull/670)) + +## [0.8.1] 2017-04-16 + +### Fixed +- Fixed build on FreeBSD. (Cherry-picked + [a859ee3c](https://github.com/nix-rust/nix/commit/a859ee3c9396dfdb118fcc2c8ecc697e2d303467)) + +## [0.8.0] 2017-03-02 + +### Added +- Added `::nix::sys::termios::BaudRate` enum to provide portable baudrate + values. ([#518](https://github.com/nix-rust/nix/pull/518)) +- Added a new `WaitStatus::PtraceEvent` to support ptrace events on Linux + and Android ([#438](https://github.com/nix-rust/nix/pull/438)) +- Added support for POSIX AIO + ([#483](https://github.com/nix-rust/nix/pull/483)) + ([#506](https://github.com/nix-rust/nix/pull/506)) +- Added support for XNU system control sockets + ([#478](https://github.com/nix-rust/nix/pull/478)) +- Added support for `ioctl` calls on BSD platforms + ([#478](https://github.com/nix-rust/nix/pull/478)) +- Added struct `TimeSpec` + ([#475](https://github.com/nix-rust/nix/pull/475)) + ([#483](https://github.com/nix-rust/nix/pull/483)) +- Added complete definitions for all kqueue-related constants on all supported + OSes + ([#415](https://github.com/nix-rust/nix/pull/415)) +- Added function `epoll_create1` and bitflags `EpollCreateFlags` in + `::nix::sys::epoll` in order to support `::libc::epoll_create1`. + ([#410](https://github.com/nix-rust/nix/pull/410)) +- Added `setresuid` and `setresgid` for Linux in `::nix::unistd` + ([#448](https://github.com/nix-rust/nix/pull/448)) +- Added `getpgid` in `::nix::unistd` + ([#433](https://github.com/nix-rust/nix/pull/433)) +- Added `tcgetpgrp` and `tcsetpgrp` in `::nix::unistd` + ([#451](https://github.com/nix-rust/nix/pull/451)) +- Added `CLONE_NEWCGROUP` in `::nix::sched` + ([#457](https://github.com/nix-rust/nix/pull/457)) +- Added `getpgrp` in `::nix::unistd` + ([#491](https://github.com/nix-rust/nix/pull/491)) +- Added `fchdir` in `::nix::unistd` + ([#497](https://github.com/nix-rust/nix/pull/497)) +- Added `major` and `minor` in `::nix::sys::stat` for decomposing `dev_t` + ([#508](https://github.com/nix-rust/nix/pull/508)) +- Fixed the style of many bitflags and use `libc` in more places. + ([#503](https://github.com/nix-rust/nix/pull/503)) +- Added `ppoll` in `::nix::poll` + ([#520](https://github.com/nix-rust/nix/pull/520)) +- Added support for getting and setting pipe size with fcntl(2) on Linux + ([#540](https://github.com/nix-rust/nix/pull/540)) + +### Changed +- `::nix::sys::termios::{cfgetispeed, cfsetispeed, cfgetospeed, cfsetospeed}` + switched to use `BaudRate` enum from `speed_t`. + ([#518](https://github.com/nix-rust/nix/pull/518)) +- `epoll_ctl` now could accept None as argument `event` + when op is `EpollOp::EpollCtlDel`. + ([#480](https://github.com/nix-rust/nix/pull/480)) +- Removed the `bad` keyword from the `ioctl!` macro + ([#478](https://github.com/nix-rust/nix/pull/478)) +- Changed `TimeVal` into an opaque Newtype + ([#475](https://github.com/nix-rust/nix/pull/475)) +- `kill`'s signature, defined in `::nix::sys::signal`, changed, so that the + signal parameter has type `T: Into<Option<Signal>>`. `None` as an argument + for that parameter will result in a 0 passed to libc's `kill`, while a + `Some`-argument will result in the previous behavior for the contained + `Signal`. + ([#445](https://github.com/nix-rust/nix/pull/445)) +- The minimum supported version of rustc is now 1.7.0. + ([#444](https://github.com/nix-rust/nix/pull/444)) +- Changed `KEvent` to an opaque structure that may only be modified by its + constructor and the `ev_set` method. + ([#415](https://github.com/nix-rust/nix/pull/415)) + ([#442](https://github.com/nix-rust/nix/pull/442)) + ([#463](https://github.com/nix-rust/nix/pull/463)) +- `pipe2` now calls `libc::pipe2` where available. Previously it was emulated + using `pipe`, which meant that setting `O_CLOEXEC` was not atomic. + ([#427](https://github.com/nix-rust/nix/pull/427)) +- Renamed `EpollEventKind` to `EpollFlags` in `::nix::sys::epoll` in order for + it to conform with our conventions. + ([#410](https://github.com/nix-rust/nix/pull/410)) +- `EpollEvent` in `::nix::sys::epoll` is now an opaque proxy for + `::libc::epoll_event`. The formerly public field `events` is now be read-only + accessible with the new method `events()` of `EpollEvent`. Instances of + `EpollEvent` can be constructed using the new method `new()` of EpollEvent. + ([#410](https://github.com/nix-rust/nix/pull/410)) +- `SigFlags` in `::nix::sys::signal` has be renamed to `SigmaskHow` and its type + has changed from `bitflags` to `enum` in order to conform to our conventions. + ([#460](https://github.com/nix-rust/nix/pull/460)) +- `sethostname` now takes a `&str` instead of a `&[u8]` as this provides an API + that makes more sense in normal, correct usage of the API. +- `gethostname` previously did not expose the actual length of the hostname + written from the underlying system call at all. This has been updated to + return a `&CStr` within the provided buffer that is always properly + NUL-terminated (this is not guaranteed by the call with all platforms/libc + implementations). +- Exposed all fcntl(2) operations at the module level, so they can be + imported direclty instead of via `FcntlArg` enum. + ([#541](https://github.com/nix-rust/nix/pull/541)) + +### Fixed +- Fixed multiple issues with Unix domain sockets on non-Linux OSes + ([#474](https://github.com/nix-rust/nix/pull/415)) +- Fixed using kqueue with `EVFILT_USER` on FreeBSD + ([#415](https://github.com/nix-rust/nix/pull/415)) +- Fixed the build on FreeBSD, and fixed the getsockopt, sendmsg, and recvmsg + functions on that same OS. + ([#397](https://github.com/nix-rust/nix/pull/397)) +- Fixed an off-by-one bug in `UnixAddr::new_abstract` in `::nix::sys::socket`. + ([#429](https://github.com/nix-rust/nix/pull/429)) +- Fixed clone passing a potentially unaligned stack. + ([#490](https://github.com/nix-rust/nix/pull/490)) +- Fixed mkdev not creating a `dev_t` the same way as libc. + ([#508](https://github.com/nix-rust/nix/pull/508)) + +## [0.7.0] 2016-09-09 + +### Added +- Added `lseek` and `lseek64` in `::nix::unistd` + ([#377](https://github.com/nix-rust/nix/pull/377)) +- Added `mkdir` and `getcwd` in `::nix::unistd` + ([#416](https://github.com/nix-rust/nix/pull/416)) +- Added accessors `sigmask_mut` and `sigmask` to `UContext` in + `::nix::ucontext`. + ([#370](https://github.com/nix-rust/nix/pull/370)) +- Added `WUNTRACED` to `WaitPidFlag` in `::nix::sys::wait` for non-_linux_ + targets. + ([#379](https://github.com/nix-rust/nix/pull/379)) +- Added new module `::nix::sys::reboot` with enumeration `RebootMode` and + functions `reboot` and `set_cad_enabled`. Currently for _linux_ only. + ([#386](https://github.com/nix-rust/nix/pull/386)) +- `FdSet` in `::nix::sys::select` now also implements `Clone`. + ([#405](https://github.com/nix-rust/nix/pull/405)) +- Added `F_FULLFSYNC` to `FcntlArg` in `::nix::fcntl` for _apple_ targets. + ([#407](https://github.com/nix-rust/nix/pull/407)) +- Added `CpuSet::unset` in `::nix::sched`. + ([#402](https://github.com/nix-rust/nix/pull/402)) +- Added constructor method `new()` to `PollFd` in `::nix::poll`, in order to + allow creation of objects, after removing public access to members. + ([#399](https://github.com/nix-rust/nix/pull/399)) +- Added method `revents()` to `PollFd` in `::nix::poll`, in order to provide + read access to formerly public member `revents`. + ([#399](https://github.com/nix-rust/nix/pull/399)) +- Added `MSG_CMSG_CLOEXEC` to `MsgFlags` in `::nix::sys::socket` for _linux_ only. + ([#422](https://github.com/nix-rust/nix/pull/422)) + +### Changed +- Replaced the reexported integer constants for signals by the enumeration + `Signal` in `::nix::sys::signal`. + ([#362](https://github.com/nix-rust/nix/pull/362)) +- Renamed `EventFdFlag` to `EfdFlags` in `::nix::sys::eventfd`. + ([#383](https://github.com/nix-rust/nix/pull/383)) +- Changed the result types of `CpuSet::is_set` and `CpuSet::set` in + `::nix::sched` to `Result<bool>` and `Result<()>`, respectively. They now + return `EINVAL`, if an invalid argument for the `field` parameter is passed. + ([#402](https://github.com/nix-rust/nix/pull/402)) +- `MqAttr` in `::nix::mqueue` is now an opaque proxy for `::libc::mq_attr`, + which has the same structure as the old `MqAttr`. The field `mq_flags` of + `::libc::mq_attr` is readable using the new method `flags()` of `MqAttr`. + `MqAttr` also no longer implements `Debug`. + ([#392](https://github.com/nix-rust/nix/pull/392)) +- The parameter `msq_prio` of `mq_receive` with type `u32` in `::nix::mqueue` + was replaced by a parameter named `msg_prio` with type `&mut u32`, so that + the message priority can be obtained by the caller. + ([#392](https://github.com/nix-rust/nix/pull/392)) +- The type alias `MQd` in `::nix::queue` was replaced by the type alias + `libc::mqd_t`, both of which are aliases for the same type. + ([#392](https://github.com/nix-rust/nix/pull/392)) + +### Removed +- Type alias `SigNum` from `::nix::sys::signal`. + ([#362](https://github.com/nix-rust/nix/pull/362)) +- Type alias `CpuMask` from `::nix::shed`. + ([#402](https://github.com/nix-rust/nix/pull/402)) +- Removed public fields from `PollFd` in `::nix::poll`. (See also added method + `revents()`. + ([#399](https://github.com/nix-rust/nix/pull/399)) + +### Fixed +- Fixed the build problem for NetBSD (Note, that we currently do not support + it, so it might already be broken again). + ([#389](https://github.com/nix-rust/nix/pull/389)) +- Fixed the build on FreeBSD, and fixed the getsockopt, sendmsg, and recvmsg + functions on that same OS. + ([#397](https://github.com/nix-rust/nix/pull/397)) + +## [0.6.0] 2016-06-10 + +### Added +- Added `gettid` in `::nix::unistd` for _linux_ and _android_. + ([#293](https://github.com/nix-rust/nix/pull/293)) +- Some _mips_ support in `::nix::sched` and `::nix::sys::syscall`. + ([#301](https://github.com/nix-rust/nix/pull/301)) +- Added `SIGNALFD_SIGINFO_SIZE` in `::nix::sys::signalfd`. + ([#309](https://github.com/nix-rust/nix/pull/309)) +- Added new module `::nix::ucontext` with struct `UContext`. Currently for + _linux_ only. + ([#311](https://github.com/nix-rust/nix/pull/311)) +- Added `EPOLLEXCLUSIVE` to `EpollEventKind` in `::nix::sys::epoll`. + ([#330](https://github.com/nix-rust/nix/pull/330)) +- Added `pause` to `::nix::unistd`. + ([#336](https://github.com/nix-rust/nix/pull/336)) +- Added `sleep` to `::nix::unistd`. + ([#351](https://github.com/nix-rust/nix/pull/351)) +- Added `S_IFDIR`, `S_IFLNK`, `S_IFMT` to `SFlag` in `::nix::sys::stat`. + ([#359](https://github.com/nix-rust/nix/pull/359)) +- Added `clear` and `extend` functions to `SigSet`'s implementation in + `::nix::sys::signal`. + ([#347](https://github.com/nix-rust/nix/pull/347)) +- `sockaddr_storage_to_addr` in `::nix::sys::socket` now supports `sockaddr_nl` + on _linux_ and _android_. + ([#366](https://github.com/nix-rust/nix/pull/366)) +- Added support for `SO_ORIGINAL_DST` in `::nix::sys::socket` on _linux_. + ([#367](https://github.com/nix-rust/nix/pull/367)) +- Added `SIGINFO` in `::nix::sys::signal` for the _macos_ target as well as + `SIGPWR` and `SIGSTKFLT` in `::nix::sys::signal` for non-_macos_ targets. + ([#361](https://github.com/nix-rust/nix/pull/361)) + +### Changed +- Changed the structure `IoVec` in `::nix::sys::uio`. + ([#304](https://github.com/nix-rust/nix/pull/304)) +- Replaced `CREATE_NEW_FD` by `SIGNALFD_NEW` in `::nix::sys::signalfd`. + ([#309](https://github.com/nix-rust/nix/pull/309)) +- Renamed `SaFlag` to `SaFlags` and `SigFlag` to `SigFlags` in + `::nix::sys::signal`. + ([#314](https://github.com/nix-rust/nix/pull/314)) +- Renamed `Fork` to `ForkResult` and changed its fields in `::nix::unistd`. + ([#332](https://github.com/nix-rust/nix/pull/332)) +- Added the `signal` parameter to `clone`'s signature in `::nix::sched`. + ([#344](https://github.com/nix-rust/nix/pull/344)) +- `execv`, `execve`, and `execvp` now return `Result<Void>` instead of + `Result<()>` in `::nix::unistd`. + ([#357](https://github.com/nix-rust/nix/pull/357)) + +### Fixed +- Improved the conversion from `std::net::SocketAddr` to `InetAddr` in + `::nix::sys::socket::addr`. + ([#335](https://github.com/nix-rust/nix/pull/335)) + +## [0.5.0] 2016-03-01 diff --git a/third_party/rust/nix/CONTRIBUTING.md b/third_party/rust/nix/CONTRIBUTING.md new file mode 100644 index 0000000000..03a1f630db --- /dev/null +++ b/third_party/rust/nix/CONTRIBUTING.md @@ -0,0 +1,114 @@ +# Contributing to nix + +We're really glad you're interested in contributing to nix! This +document has a few pointers and guidelines to help get you started. + +To have a welcoming and inclusive project, nix uses the Rust project's +[Code of Conduct][conduct]. All contributors are expected to follow it. + +[conduct]: https://www.rust-lang.org/conduct.html + + +# Issues + +We use GitHub's [issue tracker][issues]. + +[issues]: https://github.com/nix-rust/nix/issues + + +## Bug reports + +Before submitting a new bug report, please [search existing +issues][issue-search] to see if there's something related. If not, just +[open a new issue][new-issue]! + +As a reminder, the more information you can give in your issue, the +easier it is to figure out how to fix it. For nix, this will likely +include the OS and version, and the architecture. + +[issue-search]: https://github.com/nix-rust/nix/search?utf8=%E2%9C%93&q=is%3Aissue&type=Issues +[new-issue]: https://github.com/nix-rust/nix/issues/new + + +## Feature / API requests + +If you'd like a new API or feature added, please [open a new +issue][new-issue] requesting it. As with reporting a bug, the more +information you can provide, the better. + + +## Labels + +We use labels to help manage issues. The structure is modeled after +[Rust's issue labeling scheme][rust-labels]: +- **A-**prefixed labels state which area of the project the issue + relates to +- **E-**prefixed labels explain the level of experience necessary to fix the + issue +- **O-**prefixed labels specify the OS for issues that are OS-specific +- **R-**prefixed labels specify the architecture for issues that are + architecture-specific + +[rust-labels]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#issue-triage + + +# Pull requests + +GitHub pull requests are the primary mechanism we use to change nix. GitHub itself has +some [great documentation][pr-docs] on using the Pull Request feature. We use the 'fork and +pull' model described there. + +Please make pull requests against the `master` branch. + +If you change the API by way of adding, removing or changing something or if +you fix a bug, please add an appropriate note to the [change log][cl]. We +follow the conventions of [Keep A CHANGELOG][kacl]. + +[cl]: https://github.com/nix-rust/nix/blob/master/CHANGELOG.md +[kacl]: https://github.com/olivierlacan/keep-a-changelog/tree/18adb5f5be7a898d046f6a4acb93e39dcf40c4ad +[pr-docs]: https://help.github.com/articles/using-pull-requests/ + +## Testing + +nix has a test suite that you can run with `cargo test`. Ideally, we'd like pull +requests to include tests where they make sense. For example, when fixing a bug, +add a test that would have failed without the fix. + +After you've made your change, make sure the tests pass in your development +environment. We also have [continuous integration set up on +Travis-CI][travis-ci], which might find some issues on other platforms. The CI +will run once you open a pull request. + +There is also infrastructure for running tests for other targets +locally. More information is available in the [CI Readme][ci-readme]. + +[travis-ci]: https://travis-ci.org/nix-rust/nix +[ci-readme]: ci/README.md + +### Disabling a test in the CI environment + +Sometimes there are features that cannot be tested in the CI environment. +To stop a test from running under CI, add `#[cfg_attr(travis, ignore)]` +to it. Please include a comment describing the reason it shouldn't run +under CI, and a link to an upstream issue if possible! + +## bors, the bot who merges all the PRs + +All pull requests are merged via [bors], an integration bot. After the +pull request has been reviewed, the reviewer will leave a comment like + +> bors r+ + +to let bors know that it was approved. Then bors will check that it passes +tests when merged with the latest changes in the `master` branch, and +merge if the tests succeed. + +[bors]: https://bors-ng.github.io/ + + +## API conventions + +If you're adding a new API, we have a [document with +conventions][conventions] to use throughout the nix project. + +[conventions]: https://github.com/nix-rust/nix/blob/master/CONVENTIONS.md diff --git a/third_party/rust/nix/CONVENTIONS.md b/third_party/rust/nix/CONVENTIONS.md new file mode 100644 index 0000000000..48daa93734 --- /dev/null +++ b/third_party/rust/nix/CONVENTIONS.md @@ -0,0 +1,87 @@ +# Conventions + +In order to achieve our goal of wrapping [libc][libc] code in idiomatic rust +constructs with minimal performance overhead, we follow the following +conventions. + +Note that, thus far, not all the code follows these conventions and not all +conventions we try to follow have been documented here. If you find an instance +of either, feel free to remedy the flaw by opening a pull request with +appropriate changes or additions. + +## Change Log + +We follow the conventions laid out in [Keep A CHANGELOG][kacl]. + +[kacl]: https://github.com/olivierlacan/keep-a-changelog/tree/18adb5f5be7a898d046f6a4acb93e39dcf40c4ad + +## libc constants, functions and structs + +We do not define integer constants ourselves, but use or reexport them from the +[libc crate][libc]. + +We use the functions exported from [libc][libc] instead of writing our own +`extern` declarations. + +We use the `struct` definitions from [libc][libc] internally instead of writing +our own. If we want to add methods to a libc type, we use the newtype pattern. +For example, + +```rust +pub struct SigSet(libc::sigset_t); + +impl SigSet { + ... +} +``` + +When creating newtypes, we use Rust's `CamelCase` type naming convention. + +## Bitflags + +Many C functions have flags parameters that are combined from constants using +bitwise operations. We represent the types of these parameters by types defined +using our `libc_bitflags!` macro, which is a convenience wrapper around the +`bitflags!` macro from the [bitflags crate][bitflags] that brings in the +constant value from `libc`. + +We name the type for a set of constants whose element's names start with `FOO_` +`FooFlags`. + +For example, + +```rust +libc_bitflags!{ + pub struct ProtFlags: libc::c_int { + PROT_NONE; + PROT_READ; + PROT_WRITE; + PROT_EXEC; + #[cfg(any(target_os = "linux", target_os = "android"))] + PROT_GROWSDOWN; + #[cfg(any(target_os = "linux", target_os = "android"))] + PROT_GROWSUP; + } +} +``` + + +## Enumerations + +We represent sets of constants that are intended as mutually exclusive arguments +to parameters of functions by [enumerations][enum]. + + +## Structures Initialized by libc Functions + +Whenever we need to use a [libc][libc] function to properly initialize a +variable and said function allows us to use uninitialized memory, we use +[`std::mem::uninitialized`][std_uninitialized] (or [`core::mem::uninitialized`][core_uninitialized]) +when defining the variable. This allows us to avoid the overhead incurred by +zeroing or otherwise initializing the variable. + +[bitflags]: https://crates.io/crates/bitflags/ +[core_uninitialized]: https://doc.rust-lang.org/core/mem/fn.uninitialized.html +[enum]: https://doc.rust-lang.org/reference.html#enumerations +[libc]: https://crates.io/crates/libc/ +[std_uninitialized]: https://doc.rust-lang.org/std/mem/fn.uninitialized.html diff --git a/third_party/rust/nix/Cargo.toml b/third_party/rust/nix/Cargo.toml new file mode 100644 index 0000000000..64854b3c06 --- /dev/null +++ b/third_party/rust/nix/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "nix" +description = "Rust friendly bindings to *nix APIs" +version = "0.13.1" +authors = ["The nix-rust Project Developers"] +repository = "https://github.com/nix-rust/nix" +license = "MIT" +categories = ["os::unix-apis"] +exclude = [ + "/.gitignore", + "/.travis.yml", + "/ci/*", + "/Cross.toml", + "/RELEASE_PROCEDURE.md", + "/bors.toml" +] + +[dependencies] +libc = "0.2.57" +bitflags = "1.0" +cfg-if = "0.1.0" +void = "1.0.2" + +[target.'cfg(target_os = "dragonfly")'.build-dependencies] +cc = "1" + +[dev-dependencies] +bytes = "0.4.8" +lazy_static = "1.2" +rand = "0.5" +tempfile = "3" + +[target.'cfg(target_os = "freebsd")'.dev-dependencies] +sysctl = "0.1" + +[[test]] +name = "test" +path = "test/test.rs" + +[[test]] +name = "test-aio-drop" +path = "test/sys/test_aio_drop.rs" + +[[test]] +name = "test-lio-listio-resubmit" +path = "test/sys/test_lio_listio_resubmit.rs" + +[[test]] +name = "test-mount" +path = "test/test_mount.rs" +harness = false + +[[test]] +name = "test-ptymaster-drop" +path = "test/test_ptymaster_drop.rs" diff --git a/third_party/rust/nix/LICENSE b/third_party/rust/nix/LICENSE new file mode 100644 index 0000000000..aff9096fdf --- /dev/null +++ b/third_party/rust/nix/LICENSE @@ -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. diff --git a/third_party/rust/nix/README.md b/third_party/rust/nix/README.md new file mode 100644 index 0000000000..5fa3a94f37 --- /dev/null +++ b/third_party/rust/nix/README.md @@ -0,0 +1,110 @@ +# Rust bindings to *nix APIs + +[![Build Status](https://travis-ci.org/nix-rust/nix.svg?branch=master)](https://travis-ci.org/nix-rust/nix) +[![crates.io](http://meritbadge.herokuapp.com/nix)](https://crates.io/crates/nix) + +[Documentation (Releases)](https://docs.rs/nix/) + +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](http://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<CStr>) +pub fn gethostname<'a>(buffer: &'a mut [u8]) -> Result<&'a CStr>; +``` + +## Supported Platforms + +nix target support consists of two 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* block the inclusion of new code. Testing may be run, but + failures in tests don't block the inclusion of new code. + +The following targets are all supported by nix on Rust 1.24.1 or newer (unless +otherwise noted): + +Tier 1: + * aarch64-unknown-linux-gnu + * arm-unknown-linux-gnueabi + * armv7-unknown-linux-gnueabihf + * i686-apple-darwin + * i686-unknown-freebsd + * i686-unknown-linux-gnu + * i686-unknown-linux-musl + * mips-unknown-linux-gnu + * mips64-unknown-linux-gnuabi64 + * mips64el-unknown-linux-gnuabi64 + * mipsel-unknown-linux-gnu + * powerpc64-unknown-linux-gnu + * powerpc64le-unknown-linux-gnu + * x86_64-apple-darwin + * x86_64-unknown-freebsd + * x86_64-unknown-linux-gnu + * x86_64-unknown-linux-musl + +Tier 2: + * aarch64-apple-ios + * aarch64-linux-android + * arm-linux-androideabi + * arm-unknown-linux-musleabi + * armv7-apple-ios + * armv7-linux-androideabi + * armv7s-apple-ios + * i386-apple-ios + * i686-linux-android + * powerpc-unknown-linux-gnu + * s390x-unknown-linux-gnu + * x86_64-apple-ios + * x86_64-linux-android + * x86_64-unknown-netbsd + +## Usage + +To use `nix`, first add this to your `Cargo.toml`: + +```toml +[dependencies] +nix = "0.13.1" +``` + +Then, add this to your crate root: + +```rust,ignore +extern crate nix; +``` + +## Contributing + +Contributions are very welcome. Please See [CONTRIBUTING](CONTRIBUTING.md) for +additional details. + +Feel free to join us in [the nix-rust/nix](https://gitter.im/nix-rust/nix) channel on Gitter to +discuss `nix` development. + +## License + +Nix is licensed under the MIT license. See [LICENSE](LICENSE) for more details. diff --git a/third_party/rust/nix/build.rs b/third_party/rust/nix/build.rs new file mode 100644 index 0000000000..92fd3667fa --- /dev/null +++ b/third_party/rust/nix/build.rs @@ -0,0 +1,12 @@ +#[cfg(target_os = "dragonfly")] +extern crate cc; + +#[cfg(target_os = "dragonfly")] +fn main() { + cc::Build::new() + .file("src/errno_dragonfly.c") + .compile("liberrno_dragonfly.a"); +} + +#[cfg(not(target_os = "dragonfly"))] +fn main() {} diff --git a/third_party/rust/nix/src/dir.rs b/third_party/rust/nix/src/dir.rs new file mode 100644 index 0000000000..814e9e0c0b --- /dev/null +++ b/third_party/rust/nix/src/dir.rs @@ -0,0 +1,210 @@ +use {Error, NixPath, Result}; +use errno::Errno; +use fcntl::{self, OFlag}; +use libc; +use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; +use std::{ffi, fmt, ptr}; +use sys; + +#[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`, perhaps before knowing +/// if the path represents a file or directory). +/// * implements `AsRawFd`, so it can be passed to `fstat`, `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` (no allocation or conversion beyond whatever libc +/// does). +pub struct Dir( + // This could be ptr::NonNull once nix requires Rust 1.25. + *mut 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<P: ?Sized + NixPath>(dirfd: RawFd, 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. + #[inline] + pub fn from<F: IntoRawFd>(fd: F) -> Result<Self> { + Dir::from_fd(fd.into_raw_fd()) + } + + /// Converts from a file descriptor, closing it on success or failure. + pub fn from_fd(fd: RawFd) -> Result<Self> { + let d = unsafe { libc::fdopendir(fd) }; + if d.is_null() { + let e = Error::last(); + unsafe { libc::close(fd) }; + return Err(e); + }; + 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 AsRawFd for Dir { + fn as_raw_fd(&self) -> RawFd { + unsafe { libc::dirfd(self.0) } + } +} + +impl fmt::Debug for Dir { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Dir") + .field("fd", &self.as_raw_fd()) + .finish() + } +} + +impl Drop for Dir { + fn drop(&mut self) { + unsafe { libc::closedir(self.0) }; + } +} + +#[derive(Debug)] +pub struct Iter<'d>(&'d mut Dir); + +impl<'d> Iterator for Iter<'d> { + type Item = Result<Entry>; + + fn next(&mut self) -> Option<Self::Item> { + 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: Entry = Entry(::std::mem::uninitialized()); + let mut result = ptr::null_mut(); + if let Err(e) = Errno::result(readdir_r((self.0).0, &mut ent.0, &mut result)) { + return Some(Err(e)); + } + if result == ptr::null_mut() { + return None; + } + assert_eq!(result, &mut ent.0 as *mut dirent); + return Some(Ok(ent)); + } + } +} + +impl<'d> Drop for Iter<'d> { + fn drop(&mut self) { + unsafe { libc::rewinddir((self.0).0) } + } +} + +/// A directory entry, similar to `std::fs::DirEntry`. +/// +/// Note that unlike the std version, this may represent the `.` or `..` entries. +#[derive(Copy, Clone)] +pub struct Entry(dirent); + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Type { + Fifo, + CharacterDevice, + Directory, + BlockDevice, + File, + Symlink, + Socket, +} + +impl Entry { + /// Returns the inode number (`d_ino`) of the underlying `dirent`. + #[cfg(any(target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "haiku", + target_os = "ios", + target_os = "l4re", + target_os = "linux", + target_os = "macos", + target_os = "solaris"))] + pub fn ino(&self) -> u64 { + self.0.d_ino as u64 + } + + /// Returns the inode number (`d_fileno`) of the underlying `dirent`. + #[cfg(not(any(target_os = "android", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "haiku", + target_os = "ios", + target_os = "l4re", + target_os = "linux", + target_os = "macos", + target_os = "solaris")))] + pub fn ino(&self) -> u64 { + self.0.d_fileno as u64 + } + + /// Returns the bare file name of this directory entry without any other leading path component. + pub fn file_name(&self) -> &ffi::CStr { + unsafe { ::std::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> { + 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, + } + } +} + +impl fmt::Debug for Entry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Entry") + .field("ino", &self.ino()) + .field("file_name", &self.file_name()) + .field("file_type", &self.file_type()) + .finish() + } +} diff --git a/third_party/rust/nix/src/errno.rs b/third_party/rust/nix/src/errno.rs new file mode 100644 index 0000000000..977ea0b64d --- /dev/null +++ b/third_party/rust/nix/src/errno.rs @@ -0,0 +1,1911 @@ +#[cfg(not(target_os = "dragonfly"))] +use libc; +use libc::{c_int, c_void}; +use std::{fmt, io, error}; +use {Error, Result}; + +pub use self::consts::*; + +cfg_if! { + if #[cfg(any(target_os = "freebsd", + target_os = "ios", + target_os = "macos"))] { + unsafe fn errno_location() -> *mut c_int { + libc::__error() + } + } else if #[cfg(target_os = "dragonfly")] { + // DragonFly uses a thread-local errno variable, but #[thread_local] is + // feature-gated and not available in stable Rust as of this writing + // (Rust 1.21.0). We have to use a C extension to access it + // (src/errno_dragonfly.c). + // + // Tracking issue for `thread_local` stabilization: + // + // https://github.com/rust-lang/rust/issues/29594 + // + // Once this becomes stable, we can remove build.rs, + // src/errno_dragonfly.c, and use: + // + // extern { #[thread_local] static errno: c_int; } + // + #[link(name="errno_dragonfly", kind="static")] + extern { + pub fn errno_location() -> *mut c_int; + } + } else if #[cfg(any(target_os = "android", + target_os = "netbsd", + target_os = "openbsd"))] { + unsafe fn errno_location() -> *mut c_int { + libc::__errno() + } + } else if #[cfg(target_os = "linux")] { + unsafe fn errno_location() -> *mut c_int { + libc::__errno_location() + } + } +} + +/// Sets the platform-specific errno to no-error +unsafe fn clear() -> () { + *errno_location() = 0; +} + +/// Returns the platform-specific value of errno +pub fn errno() -> i32 { + unsafe { + (*errno_location()) as i32 + } +} + +impl Errno { + pub fn last() -> Self { + last() + } + + pub fn desc(self) -> &'static str { + desc(self) + } + + pub fn from_i32(err: i32) -> Errno { + from_i32(err) + } + + pub unsafe fn clear() -> () { + clear() + } + + /// Returns `Ok(value)` if it does not contain the sentinel value. This + /// should not be used when `-1` is not the errno sentinel value. + pub fn result<S: ErrnoSentinel + PartialEq<S>>(value: S) -> Result<S> { + if value == S::sentinel() { + Err(Error::Sys(Self::last())) + } else { + Ok(value) + } + } +} + +/// The sentinel value indicates that a function failed and more detailed +/// information about the error can be found in `errno` +pub trait ErrnoSentinel: Sized { + fn sentinel() -> Self; +} + +impl ErrnoSentinel for isize { + fn sentinel() -> Self { -1 } +} + +impl ErrnoSentinel for i32 { + fn sentinel() -> Self { -1 } +} + +impl ErrnoSentinel for i64 { + fn sentinel() -> Self { -1 } +} + +impl ErrnoSentinel for *mut c_void { + fn sentinel() -> Self { (-1 as isize) as *mut c_void } +} + +impl ErrnoSentinel for libc::sighandler_t { + fn sentinel() -> Self { libc::SIG_ERR } +} + +impl error::Error for Errno { + fn description(&self) -> &str { + self.desc() + } +} + +impl fmt::Display for Errno { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}: {}", self, self.desc()) + } +} + +impl From<Errno> for io::Error { + fn from(err: Errno) -> Self { + io::Error::from_raw_os_error(err as i32) + } +} + +fn last() -> Errno { + Errno::from_i32(errno()) +} + +fn desc(errno: Errno) -> &'static str { + use self::Errno::*; + match errno { + UnknownErrno => "Unknown errno", + EPERM => "Operation not permitted", + ENOENT => "No such file or directory", + ESRCH => "No such process", + EINTR => "Interrupted system call", + EIO => "I/O error", + ENXIO => "No such device or address", + E2BIG => "Argument list too long", + ENOEXEC => "Exec format error", + EBADF => "Bad file number", + ECHILD => "No child processes", + EAGAIN => "Try again", + ENOMEM => "Out of memory", + EACCES => "Permission denied", + EFAULT => "Bad address", + ENOTBLK => "Block device required", + EBUSY => "Device or resource busy", + EEXIST => "File exists", + EXDEV => "Cross-device link", + ENODEV => "No such device", + ENOTDIR => "Not a directory", + EISDIR => "Is a directory", + EINVAL => "Invalid argument", + ENFILE => "File table overflow", + EMFILE => "Too many open files", + ENOTTY => "Not a typewriter", + ETXTBSY => "Text file busy", + EFBIG => "File too large", + ENOSPC => "No space left on device", + ESPIPE => "Illegal seek", + EROFS => "Read-only file system", + EMLINK => "Too many links", + EPIPE => "Broken pipe", + EDOM => "Math argument out of domain of func", + ERANGE => "Math result not representable", + EDEADLK => "Resource deadlock would occur", + ENAMETOOLONG => "File name too long", + ENOLCK => "No record locks available", + ENOSYS => "Function not implemented", + ENOTEMPTY => "Directory not empty", + ELOOP => "Too many symbolic links encountered", + ENOMSG => "No message of desired type", + EIDRM => "Identifier removed", + EINPROGRESS => "Operation now in progress", + EALREADY => "Operation already in progress", + ENOTSOCK => "Socket operation on non-socket", + EDESTADDRREQ => "Destination address required", + EMSGSIZE => "Message too long", + EPROTOTYPE => "Protocol wrong type for socket", + ENOPROTOOPT => "Protocol not available", + EPROTONOSUPPORT => "Protocol not supported", + ESOCKTNOSUPPORT => "Socket type not supported", + EPFNOSUPPORT => "Protocol family not supported", + EAFNOSUPPORT => "Address family not supported by protocol", + EADDRINUSE => "Address already in use", + EADDRNOTAVAIL => "Cannot assign requested address", + ENETDOWN => "Network is down", + ENETUNREACH => "Network is unreachable", + ENETRESET => "Network dropped connection because of reset", + ECONNABORTED => "Software caused connection abort", + ECONNRESET => "Connection reset by peer", + ENOBUFS => "No buffer space available", + EISCONN => "Transport endpoint is already connected", + ENOTCONN => "Transport endpoint is not connected", + ESHUTDOWN => "Cannot send after transport endpoint shutdown", + ETOOMANYREFS => "Too many references: cannot splice", + ETIMEDOUT => "Connection timed out", + ECONNREFUSED => "Connection refused", + EHOSTDOWN => "Host is down", + EHOSTUNREACH => "No route to host", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ECHRNG => "Channel number out of range", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EL2NSYNC => "Level 2 not synchronized", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EL3HLT => "Level 3 halted", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EL3RST => "Level 3 reset", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ELNRNG => "Link number out of range", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EUNATCH => "Protocol driver not attached", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ENOCSI => "No CSI structure available", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EL2HLT => "Level 2 halted", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EBADE => "Invalid exchange", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EBADR => "Invalid request descriptor", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EXFULL => "Exchange full", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ENOANO => "No anode", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EBADRQC => "Invalid request code", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EBADSLT => "Invalid slot", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EBFONT => "Bad font file format", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ENOSTR => "Device not a stream", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ENODATA => "No data available", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ETIME => "Timer expired", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ENOSR => "Out of streams resources", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ENONET => "Machine is not on the network", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ENOPKG => "Package not installed", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EREMOTE => "Object is remote", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ENOLINK => "Link has been severed", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EADV => "Advertise error", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ESRMNT => "Srmount error", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ECOMM => "Communication error on send", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EPROTO => "Protocol error", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EMULTIHOP => "Multihop attempted", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EDOTDOT => "RFS specific error", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EBADMSG => "Not a data message", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EOVERFLOW => "Value too large for defined data type", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ENOTUNIQ => "Name not unique on network", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EBADFD => "File descriptor in bad state", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EREMCHG => "Remote address changed", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ELIBACC => "Can not access a needed shared library", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ELIBBAD => "Accessing a corrupted shared library", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ELIBSCN => ".lib section in a.out corrupted", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ELIBMAX => "Attempting to link in too many shared libraries", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ELIBEXEC => "Cannot exec a shared library directly", + + #[cfg(any(target_os = "linux", target_os = "android", target_os = "openbsd"))] + EILSEQ => "Illegal byte sequence", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ERESTART => "Interrupted system call should be restarted", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ESTRPIPE => "Streams pipe error", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EUSERS => "Too many users", + + #[cfg(any(target_os = "linux", target_os = "android", target_os = "netbsd"))] + EOPNOTSUPP => "Operation not supported on transport endpoint", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ESTALE => "Stale file handle", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EUCLEAN => "Structure needs cleaning", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ENOTNAM => "Not a XENIX named type file", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ENAVAIL => "No XENIX semaphores available", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EISNAM => "Is a named type file", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EREMOTEIO => "Remote I/O error", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EDQUOT => "Quota exceeded", + + #[cfg(any(target_os = "linux", target_os = "android", target_os = "openbsd", target_os = "dragonfly"))] + ENOMEDIUM => "No medium found", + + #[cfg(any(target_os = "linux", target_os = "android", target_os = "openbsd"))] + EMEDIUMTYPE => "Wrong medium type", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ECANCELED => "Operation canceled", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ENOKEY => "Required key not available", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EKEYEXPIRED => "Key has expired", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EKEYREVOKED => "Key has been revoked", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EKEYREJECTED => "Key was rejected by service", + + #[cfg(any(target_os = "linux", target_os = "android"))] + EOWNERDEAD => "Owner died", + + #[cfg(any(target_os = "linux", target_os = "android"))] + ENOTRECOVERABLE => "State not recoverable", + + #[cfg(all(target_os = "linux", not(target_arch="mips")))] + ERFKILL => "Operation not possible due to RF-kill", + + #[cfg(all(target_os = "linux", not(target_arch="mips")))] + EHWPOISON => "Memory page has hardware error", + + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + EDOOFUS => "Programming error", + + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + EMULTIHOP => "Multihop attempted", + + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + ENOLINK => "Link has been severed", + + #[cfg(target_os = "freebsd")] + ENOTCAPABLE => "Capabilities insufficient", + + #[cfg(target_os = "freebsd")] + ECAPMODE => "Not permitted in capability mode", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + ENEEDAUTH => "Need authenticator", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + EOVERFLOW => "Value too large to be stored in data type", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "netbsd"))] + EILSEQ => "Illegal byte sequence", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + ENOATTR => "Attribute not found", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "netbsd"))] + EBADMSG => "Bad message", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "netbsd"))] + EPROTO => "Protocol error", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "ios"))] + ENOTRECOVERABLE => "State not recoverable", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "ios"))] + EOWNERDEAD => "Previous owner died", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + ENOTSUP => "Operation not supported", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + EPROCLIM => "Too many processes", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + EUSERS => "Too many users", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + EDQUOT => "Disc quota exceeded", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + ESTALE => "Stale NFS file handle", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + EREMOTE => "Too many levels of remote in path", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + EBADRPC => "RPC struct is bad", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + ERPCMISMATCH => "RPC version wrong", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + EPROGUNAVAIL => "RPC prog. not avail", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + EPROGMISMATCH => "Program version wrong", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + EPROCUNAVAIL => "Bad procedure for program", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + EFTYPE => "Inappropriate file type or format", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + EAUTH => "Authentication error", + + #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] + ECANCELED => "Operation canceled", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + EPWROFF => "Device power is off", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + EDEVERR => "Device error, e.g. paper out", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + EBADEXEC => "Bad executable", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + EBADARCH => "Bad CPU type in executable", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + ESHLIBVERS => "Shared library version mismatch", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + EBADMACHO => "Malformed Macho file", + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "netbsd"))] + EMULTIHOP => "Reserved", + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "netbsd"))] + ENODATA => "No message available on STREAM", + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "netbsd"))] + ENOLINK => "Reserved", + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "netbsd"))] + ENOSR => "No STREAM resources", + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "netbsd"))] + ENOSTR => "Not a STREAM", + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "netbsd"))] + ETIME => "STREAM ioctl timeout", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + EOPNOTSUPP => "Operation not supported on socket", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + ENOPOLICY => "No such policy registered", + + #[cfg(any(target_os = "macos", target_os = "ios"))] + EQFULL => "Interface output queue is full", + + #[cfg(target_os = "openbsd")] + EOPNOTSUPP => "Operation not supported", + + #[cfg(target_os = "openbsd")] + EIPSEC => "IPsec processing failure", + + #[cfg(target_os = "dragonfly")] + EASYNC => "Async", + } +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +mod consts { + use libc; + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EAGAIN = libc::EAGAIN, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EDEADLK = libc::EDEADLK, + ENAMETOOLONG = libc::ENAMETOOLONG, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + ENOTEMPTY = libc::ENOTEMPTY, + ELOOP = libc::ELOOP, + ENOMSG = libc::ENOMSG, + EIDRM = libc::EIDRM, + ECHRNG = libc::ECHRNG, + EL2NSYNC = libc::EL2NSYNC, + EL3HLT = libc::EL3HLT, + EL3RST = libc::EL3RST, + ELNRNG = libc::ELNRNG, + EUNATCH = libc::EUNATCH, + ENOCSI = libc::ENOCSI, + EL2HLT = libc::EL2HLT, + EBADE = libc::EBADE, + EBADR = libc::EBADR, + EXFULL = libc::EXFULL, + ENOANO = libc::ENOANO, + EBADRQC = libc::EBADRQC, + EBADSLT = libc::EBADSLT, + EBFONT = libc::EBFONT, + ENOSTR = libc::ENOSTR, + ENODATA = libc::ENODATA, + ETIME = libc::ETIME, + ENOSR = libc::ENOSR, + ENONET = libc::ENONET, + ENOPKG = libc::ENOPKG, + EREMOTE = libc::EREMOTE, + ENOLINK = libc::ENOLINK, + EADV = libc::EADV, + ESRMNT = libc::ESRMNT, + ECOMM = libc::ECOMM, + EPROTO = libc::EPROTO, + EMULTIHOP = libc::EMULTIHOP, + EDOTDOT = libc::EDOTDOT, + EBADMSG = libc::EBADMSG, + EOVERFLOW = libc::EOVERFLOW, + ENOTUNIQ = libc::ENOTUNIQ, + EBADFD = libc::EBADFD, + EREMCHG = libc::EREMCHG, + ELIBACC = libc::ELIBACC, + ELIBBAD = libc::ELIBBAD, + ELIBSCN = libc::ELIBSCN, + ELIBMAX = libc::ELIBMAX, + ELIBEXEC = libc::ELIBEXEC, + EILSEQ = libc::EILSEQ, + ERESTART = libc::ERESTART, + ESTRPIPE = libc::ESTRPIPE, + EUSERS = libc::EUSERS, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + EOPNOTSUPP = libc::EOPNOTSUPP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + EALREADY = libc::EALREADY, + EINPROGRESS = libc::EINPROGRESS, + ESTALE = libc::ESTALE, + EUCLEAN = libc::EUCLEAN, + ENOTNAM = libc::ENOTNAM, + ENAVAIL = libc::ENAVAIL, + EISNAM = libc::EISNAM, + EREMOTEIO = libc::EREMOTEIO, + EDQUOT = libc::EDQUOT, + ENOMEDIUM = libc::ENOMEDIUM, + EMEDIUMTYPE = libc::EMEDIUMTYPE, + ECANCELED = libc::ECANCELED, + ENOKEY = libc::ENOKEY, + EKEYEXPIRED = libc::EKEYEXPIRED, + EKEYREVOKED = libc::EKEYREVOKED, + EKEYREJECTED = libc::EKEYREJECTED, + EOWNERDEAD = libc::EOWNERDEAD, + ENOTRECOVERABLE = libc::ENOTRECOVERABLE, + #[cfg(not(any(target_os = "android", target_arch="mips")))] + ERFKILL = libc::ERFKILL, + #[cfg(not(any(target_os = "android", target_arch="mips")))] + EHWPOISON = libc::EHWPOISON, + } + + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + pub const EDEADLOCK: Errno = Errno::EDEADLK; + + pub fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EAGAIN => EAGAIN, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR => EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EDEADLK => EDEADLK, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::ENOTEMPTY => ENOTEMPTY, + libc::ELOOP => ELOOP, + libc::ENOMSG => ENOMSG, + libc::EIDRM => EIDRM, + libc::ECHRNG => ECHRNG, + libc::EL2NSYNC => EL2NSYNC, + libc::EL3HLT => EL3HLT, + libc::EL3RST => EL3RST, + libc::ELNRNG => ELNRNG, + libc::EUNATCH => EUNATCH, + libc::ENOCSI => ENOCSI, + libc::EL2HLT => EL2HLT, + libc::EBADE => EBADE, + libc::EBADR => EBADR, + libc::EXFULL => EXFULL, + libc::ENOANO => ENOANO, + libc::EBADRQC => EBADRQC, + libc::EBADSLT => EBADSLT, + libc::EBFONT => EBFONT, + libc::ENOSTR => ENOSTR, + libc::ENODATA => ENODATA, + libc::ETIME => ETIME, + libc::ENOSR => ENOSR, + libc::ENONET => ENONET, + libc::ENOPKG => ENOPKG, + libc::EREMOTE => EREMOTE, + libc::ENOLINK => ENOLINK, + libc::EADV => EADV, + libc::ESRMNT => ESRMNT, + libc::ECOMM => ECOMM, + libc::EPROTO => EPROTO, + libc::EMULTIHOP => EMULTIHOP, + libc::EDOTDOT => EDOTDOT, + libc::EBADMSG => EBADMSG, + libc::EOVERFLOW => EOVERFLOW, + libc::ENOTUNIQ => ENOTUNIQ, + libc::EBADFD => EBADFD, + libc::EREMCHG => EREMCHG, + libc::ELIBACC => ELIBACC, + libc::ELIBBAD => ELIBBAD, + libc::ELIBSCN => ELIBSCN, + libc::ELIBMAX => ELIBMAX, + libc::ELIBEXEC => ELIBEXEC, + libc::EILSEQ => EILSEQ, + libc::ERESTART => ERESTART, + libc::ESTRPIPE => ESTRPIPE, + libc::EUSERS => EUSERS, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::EOPNOTSUPP => EOPNOTSUPP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::EALREADY => EALREADY, + libc::EINPROGRESS => EINPROGRESS, + libc::ESTALE => ESTALE, + libc::EUCLEAN => EUCLEAN, + libc::ENOTNAM => ENOTNAM, + libc::ENAVAIL => ENAVAIL, + libc::EISNAM => EISNAM, + libc::EREMOTEIO => EREMOTEIO, + libc::EDQUOT => EDQUOT, + libc::ENOMEDIUM => ENOMEDIUM, + libc::EMEDIUMTYPE => EMEDIUMTYPE, + libc::ECANCELED => ECANCELED, + libc::ENOKEY => ENOKEY, + libc::EKEYEXPIRED => EKEYEXPIRED, + libc::EKEYREVOKED => EKEYREVOKED, + libc::EKEYREJECTED => EKEYREJECTED, + libc::EOWNERDEAD => EOWNERDEAD, + libc::ENOTRECOVERABLE => ENOTRECOVERABLE, + #[cfg(not(any(target_os = "android", target_arch="mips")))] + libc::ERFKILL => ERFKILL, + #[cfg(not(any(target_os = "android", target_arch="mips")))] + libc::EHWPOISON => EHWPOISON, + _ => UnknownErrno, + } + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod consts { + use libc; + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EDEADLK = libc::EDEADLK, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EAGAIN = libc::EAGAIN, + EINPROGRESS = libc::EINPROGRESS, + EALREADY = libc::EALREADY, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + ENOTSUP = libc::ENOTSUP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + ELOOP = libc::ELOOP, + ENAMETOOLONG = libc::ENAMETOOLONG, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + ENOTEMPTY = libc::ENOTEMPTY, + EPROCLIM = libc::EPROCLIM, + EUSERS = libc::EUSERS, + EDQUOT = libc::EDQUOT, + ESTALE = libc::ESTALE, + EREMOTE = libc::EREMOTE, + EBADRPC = libc::EBADRPC, + ERPCMISMATCH = libc::ERPCMISMATCH, + EPROGUNAVAIL = libc::EPROGUNAVAIL, + EPROGMISMATCH = libc::EPROGMISMATCH, + EPROCUNAVAIL = libc::EPROCUNAVAIL, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + EFTYPE = libc::EFTYPE, + EAUTH = libc::EAUTH, + ENEEDAUTH = libc::ENEEDAUTH, + EPWROFF = libc::EPWROFF, + EDEVERR = libc::EDEVERR, + EOVERFLOW = libc::EOVERFLOW, + EBADEXEC = libc::EBADEXEC, + EBADARCH = libc::EBADARCH, + ESHLIBVERS = libc::ESHLIBVERS, + EBADMACHO = libc::EBADMACHO, + ECANCELED = libc::ECANCELED, + EIDRM = libc::EIDRM, + ENOMSG = libc::ENOMSG, + EILSEQ = libc::EILSEQ, + ENOATTR = libc::ENOATTR, + EBADMSG = libc::EBADMSG, + EMULTIHOP = libc::EMULTIHOP, + ENODATA = libc::ENODATA, + ENOLINK = libc::ENOLINK, + ENOSR = libc::ENOSR, + ENOSTR = libc::ENOSTR, + EPROTO = libc::EPROTO, + ETIME = libc::ETIME, + EOPNOTSUPP = libc::EOPNOTSUPP, + ENOPOLICY = libc::ENOPOLICY, + ENOTRECOVERABLE = libc::ENOTRECOVERABLE, + EOWNERDEAD = libc::EOWNERDEAD, + EQFULL = libc::EQFULL, + } + + pub const ELAST: Errno = Errno::EQFULL; + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + pub const EDEADLOCK: Errno = Errno::EDEADLK; + + pub const EL2NSYNC: Errno = Errno::UnknownErrno; + + pub fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EDEADLK => EDEADLK, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR => EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EAGAIN => EAGAIN, + libc::EINPROGRESS => EINPROGRESS, + libc::EALREADY => EALREADY, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::ENOTSUP => ENOTSUP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::ELOOP => ELOOP, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::ENOTEMPTY => ENOTEMPTY, + libc::EPROCLIM => EPROCLIM, + libc::EUSERS => EUSERS, + libc::EDQUOT => EDQUOT, + libc::ESTALE => ESTALE, + libc::EREMOTE => EREMOTE, + libc::EBADRPC => EBADRPC, + libc::ERPCMISMATCH => ERPCMISMATCH, + libc::EPROGUNAVAIL => EPROGUNAVAIL, + libc::EPROGMISMATCH => EPROGMISMATCH, + libc::EPROCUNAVAIL => EPROCUNAVAIL, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::EFTYPE => EFTYPE, + libc::EAUTH => EAUTH, + libc::ENEEDAUTH => ENEEDAUTH, + libc::EPWROFF => EPWROFF, + libc::EDEVERR => EDEVERR, + libc::EOVERFLOW => EOVERFLOW, + libc::EBADEXEC => EBADEXEC, + libc::EBADARCH => EBADARCH, + libc::ESHLIBVERS => ESHLIBVERS, + libc::EBADMACHO => EBADMACHO, + libc::ECANCELED => ECANCELED, + libc::EIDRM => EIDRM, + libc::ENOMSG => ENOMSG, + libc::EILSEQ => EILSEQ, + libc::ENOATTR => ENOATTR, + libc::EBADMSG => EBADMSG, + libc::EMULTIHOP => EMULTIHOP, + libc::ENODATA => ENODATA, + libc::ENOLINK => ENOLINK, + libc::ENOSR => ENOSR, + libc::ENOSTR => ENOSTR, + libc::EPROTO => EPROTO, + libc::ETIME => ETIME, + libc::EOPNOTSUPP => EOPNOTSUPP, + libc::ENOPOLICY => ENOPOLICY, + libc::ENOTRECOVERABLE => ENOTRECOVERABLE, + libc::EOWNERDEAD => EOWNERDEAD, + libc::EQFULL => EQFULL, + _ => UnknownErrno, + } + } +} + +#[cfg(target_os = "freebsd")] +mod consts { + use libc; + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EDEADLK = libc::EDEADLK, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EAGAIN = libc::EAGAIN, + EINPROGRESS = libc::EINPROGRESS, + EALREADY = libc::EALREADY, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + ENOTSUP = libc::ENOTSUP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + ELOOP = libc::ELOOP, + ENAMETOOLONG = libc::ENAMETOOLONG, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + ENOTEMPTY = libc::ENOTEMPTY, + EPROCLIM = libc::EPROCLIM, + EUSERS = libc::EUSERS, + EDQUOT = libc::EDQUOT, + ESTALE = libc::ESTALE, + EREMOTE = libc::EREMOTE, + EBADRPC = libc::EBADRPC, + ERPCMISMATCH = libc::ERPCMISMATCH, + EPROGUNAVAIL = libc::EPROGUNAVAIL, + EPROGMISMATCH = libc::EPROGMISMATCH, + EPROCUNAVAIL = libc::EPROCUNAVAIL, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + EFTYPE = libc::EFTYPE, + EAUTH = libc::EAUTH, + ENEEDAUTH = libc::ENEEDAUTH, + EIDRM = libc::EIDRM, + ENOMSG = libc::ENOMSG, + EOVERFLOW = libc::EOVERFLOW, + ECANCELED = libc::ECANCELED, + EILSEQ = libc::EILSEQ, + ENOATTR = libc::ENOATTR, + EDOOFUS = libc::EDOOFUS, + EBADMSG = libc::EBADMSG, + EMULTIHOP = libc::EMULTIHOP, + ENOLINK = libc::ENOLINK, + EPROTO = libc::EPROTO, + ENOTCAPABLE = libc::ENOTCAPABLE, + ECAPMODE = libc::ECAPMODE, + ENOTRECOVERABLE = libc::ENOTRECOVERABLE, + EOWNERDEAD = libc::EOWNERDEAD, + } + + pub const ELAST: Errno = Errno::EOWNERDEAD; + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + pub const EDEADLOCK: Errno = Errno::EDEADLK; + + pub const EL2NSYNC: Errno = Errno::UnknownErrno; + + pub fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EDEADLK => EDEADLK, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR => EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EAGAIN => EAGAIN, + libc::EINPROGRESS => EINPROGRESS, + libc::EALREADY => EALREADY, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::ENOTSUP => ENOTSUP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::ELOOP => ELOOP, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::ENOTEMPTY => ENOTEMPTY, + libc::EPROCLIM => EPROCLIM, + libc::EUSERS => EUSERS, + libc::EDQUOT => EDQUOT, + libc::ESTALE => ESTALE, + libc::EREMOTE => EREMOTE, + libc::EBADRPC => EBADRPC, + libc::ERPCMISMATCH => ERPCMISMATCH, + libc::EPROGUNAVAIL => EPROGUNAVAIL, + libc::EPROGMISMATCH => EPROGMISMATCH, + libc::EPROCUNAVAIL => EPROCUNAVAIL, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::EFTYPE => EFTYPE, + libc::EAUTH => EAUTH, + libc::ENEEDAUTH => ENEEDAUTH, + libc::EIDRM => EIDRM, + libc::ENOMSG => ENOMSG, + libc::EOVERFLOW => EOVERFLOW, + libc::ECANCELED => ECANCELED, + libc::EILSEQ => EILSEQ, + libc::ENOATTR => ENOATTR, + libc::EDOOFUS => EDOOFUS, + libc::EBADMSG => EBADMSG, + libc::EMULTIHOP => EMULTIHOP, + libc::ENOLINK => ENOLINK, + libc::EPROTO => EPROTO, + libc::ENOTCAPABLE => ENOTCAPABLE, + libc::ECAPMODE => ECAPMODE, + libc::ENOTRECOVERABLE => ENOTRECOVERABLE, + libc::EOWNERDEAD => EOWNERDEAD, + _ => UnknownErrno, + } + } +} + + +#[cfg(target_os = "dragonfly")] +mod consts { + use libc; + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EDEADLK = libc::EDEADLK, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EAGAIN = libc::EAGAIN, + EINPROGRESS = libc::EINPROGRESS, + EALREADY = libc::EALREADY, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + ENOTSUP = libc::ENOTSUP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + ELOOP = libc::ELOOP, + ENAMETOOLONG = libc::ENAMETOOLONG, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + ENOTEMPTY = libc::ENOTEMPTY, + EPROCLIM = libc::EPROCLIM, + EUSERS = libc::EUSERS, + EDQUOT = libc::EDQUOT, + ESTALE = libc::ESTALE, + EREMOTE = libc::EREMOTE, + EBADRPC = libc::EBADRPC, + ERPCMISMATCH = libc::ERPCMISMATCH, + EPROGUNAVAIL = libc::EPROGUNAVAIL, + EPROGMISMATCH = libc::EPROGMISMATCH, + EPROCUNAVAIL = libc::EPROCUNAVAIL, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + EFTYPE = libc::EFTYPE, + EAUTH = libc::EAUTH, + ENEEDAUTH = libc::ENEEDAUTH, + EIDRM = libc::EIDRM, + ENOMSG = libc::ENOMSG, + EOVERFLOW = libc::EOVERFLOW, + ECANCELED = libc::ECANCELED, + EILSEQ = libc::EILSEQ, + ENOATTR = libc::ENOATTR, + EDOOFUS = libc::EDOOFUS, + EBADMSG = libc::EBADMSG, + EMULTIHOP = libc::EMULTIHOP, + ENOLINK = libc::ENOLINK, + EPROTO = libc::EPROTO, + ENOMEDIUM = libc::ENOMEDIUM, + EASYNC = libc::EASYNC, + } + + pub const ELAST: Errno = Errno::EASYNC; + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + pub const EDEADLOCK: Errno = Errno::EDEADLK; + pub const EOPNOTSUPP: Errno = Errno::ENOTSUP; + + pub const EL2NSYNC: Errno = Errno::UnknownErrno; + + pub fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EDEADLK => EDEADLK, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR=> EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EAGAIN => EAGAIN, + libc::EINPROGRESS => EINPROGRESS, + libc::EALREADY => EALREADY, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::ENOTSUP => ENOTSUP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::ELOOP => ELOOP, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::ENOTEMPTY => ENOTEMPTY, + libc::EPROCLIM => EPROCLIM, + libc::EUSERS => EUSERS, + libc::EDQUOT => EDQUOT, + libc::ESTALE => ESTALE, + libc::EREMOTE => EREMOTE, + libc::EBADRPC => EBADRPC, + libc::ERPCMISMATCH => ERPCMISMATCH, + libc::EPROGUNAVAIL => EPROGUNAVAIL, + libc::EPROGMISMATCH => EPROGMISMATCH, + libc::EPROCUNAVAIL => EPROCUNAVAIL, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::EFTYPE => EFTYPE, + libc::EAUTH => EAUTH, + libc::ENEEDAUTH => ENEEDAUTH, + libc::EIDRM => EIDRM, + libc::ENOMSG => ENOMSG, + libc::EOVERFLOW => EOVERFLOW, + libc::ECANCELED => ECANCELED, + libc::EILSEQ => EILSEQ, + libc::ENOATTR => ENOATTR, + libc::EDOOFUS => EDOOFUS, + libc::EBADMSG => EBADMSG, + libc::EMULTIHOP => EMULTIHOP, + libc::ENOLINK => ENOLINK, + libc::EPROTO => EPROTO, + libc::ENOMEDIUM => ENOMEDIUM, + libc::EASYNC => EASYNC, + _ => UnknownErrno, + } + } +} + + +#[cfg(target_os = "openbsd")] +mod consts { + use libc; + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EDEADLK = libc::EDEADLK, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EAGAIN = libc::EAGAIN, + EINPROGRESS = libc::EINPROGRESS, + EALREADY = libc::EALREADY, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + EOPNOTSUPP = libc::EOPNOTSUPP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + ELOOP = libc::ELOOP, + ENAMETOOLONG = libc::ENAMETOOLONG, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + ENOTEMPTY = libc::ENOTEMPTY, + EPROCLIM = libc::EPROCLIM, + EUSERS = libc::EUSERS, + EDQUOT = libc::EDQUOT, + ESTALE = libc::ESTALE, + EREMOTE = libc::EREMOTE, + EBADRPC = libc::EBADRPC, + ERPCMISMATCH = libc::ERPCMISMATCH, + EPROGUNAVAIL = libc::EPROGUNAVAIL, + EPROGMISMATCH = libc::EPROGMISMATCH, + EPROCUNAVAIL = libc::EPROCUNAVAIL, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + EFTYPE = libc::EFTYPE, + EAUTH = libc::EAUTH, + ENEEDAUTH = libc::ENEEDAUTH, + EIPSEC = libc::EIPSEC, + ENOATTR = libc::ENOATTR, + EILSEQ = libc::EILSEQ, + ENOMEDIUM = libc::ENOMEDIUM, + EMEDIUMTYPE = libc::EMEDIUMTYPE, + EOVERFLOW = libc::EOVERFLOW, + ECANCELED = libc::ECANCELED, + EIDRM = libc::EIDRM, + ENOMSG = libc::ENOMSG, + ENOTSUP = libc::ENOTSUP, + } + + pub const ELAST: Errno = Errno::ENOTSUP; + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + + pub const EL2NSYNC: Errno = Errno::UnknownErrno; + + pub fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EDEADLK => EDEADLK, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR => EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EAGAIN => EAGAIN, + libc::EINPROGRESS => EINPROGRESS, + libc::EALREADY => EALREADY, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::EOPNOTSUPP => EOPNOTSUPP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::ELOOP => ELOOP, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::ENOTEMPTY => ENOTEMPTY, + libc::EPROCLIM => EPROCLIM, + libc::EUSERS => EUSERS, + libc::EDQUOT => EDQUOT, + libc::ESTALE => ESTALE, + libc::EREMOTE => EREMOTE, + libc::EBADRPC => EBADRPC, + libc::ERPCMISMATCH => ERPCMISMATCH, + libc::EPROGUNAVAIL => EPROGUNAVAIL, + libc::EPROGMISMATCH => EPROGMISMATCH, + libc::EPROCUNAVAIL => EPROCUNAVAIL, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::EFTYPE => EFTYPE, + libc::EAUTH => EAUTH, + libc::ENEEDAUTH => ENEEDAUTH, + libc::EIPSEC => EIPSEC, + libc::ENOATTR => ENOATTR, + libc::EILSEQ => EILSEQ, + libc::ENOMEDIUM => ENOMEDIUM, + libc::EMEDIUMTYPE => EMEDIUMTYPE, + libc::EOVERFLOW => EOVERFLOW, + libc::ECANCELED => ECANCELED, + libc::EIDRM => EIDRM, + libc::ENOMSG => ENOMSG, + libc::ENOTSUP => ENOTSUP, + _ => UnknownErrno, + } + } +} + +#[cfg(target_os = "netbsd")] +mod consts { + use libc; + + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + #[repr(i32)] + pub enum Errno { + UnknownErrno = 0, + EPERM = libc::EPERM, + ENOENT = libc::ENOENT, + ESRCH = libc::ESRCH, + EINTR = libc::EINTR, + EIO = libc::EIO, + ENXIO = libc::ENXIO, + E2BIG = libc::E2BIG, + ENOEXEC = libc::ENOEXEC, + EBADF = libc::EBADF, + ECHILD = libc::ECHILD, + EDEADLK = libc::EDEADLK, + ENOMEM = libc::ENOMEM, + EACCES = libc::EACCES, + EFAULT = libc::EFAULT, + ENOTBLK = libc::ENOTBLK, + EBUSY = libc::EBUSY, + EEXIST = libc::EEXIST, + EXDEV = libc::EXDEV, + ENODEV = libc::ENODEV, + ENOTDIR = libc::ENOTDIR, + EISDIR = libc::EISDIR, + EINVAL = libc::EINVAL, + ENFILE = libc::ENFILE, + EMFILE = libc::EMFILE, + ENOTTY = libc::ENOTTY, + ETXTBSY = libc::ETXTBSY, + EFBIG = libc::EFBIG, + ENOSPC = libc::ENOSPC, + ESPIPE = libc::ESPIPE, + EROFS = libc::EROFS, + EMLINK = libc::EMLINK, + EPIPE = libc::EPIPE, + EDOM = libc::EDOM, + ERANGE = libc::ERANGE, + EAGAIN = libc::EAGAIN, + EINPROGRESS = libc::EINPROGRESS, + EALREADY = libc::EALREADY, + ENOTSOCK = libc::ENOTSOCK, + EDESTADDRREQ = libc::EDESTADDRREQ, + EMSGSIZE = libc::EMSGSIZE, + EPROTOTYPE = libc::EPROTOTYPE, + ENOPROTOOPT = libc::ENOPROTOOPT, + EPROTONOSUPPORT = libc::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT, + EOPNOTSUPP = libc::EOPNOTSUPP, + EPFNOSUPPORT = libc::EPFNOSUPPORT, + EAFNOSUPPORT = libc::EAFNOSUPPORT, + EADDRINUSE = libc::EADDRINUSE, + EADDRNOTAVAIL = libc::EADDRNOTAVAIL, + ENETDOWN = libc::ENETDOWN, + ENETUNREACH = libc::ENETUNREACH, + ENETRESET = libc::ENETRESET, + ECONNABORTED = libc::ECONNABORTED, + ECONNRESET = libc::ECONNRESET, + ENOBUFS = libc::ENOBUFS, + EISCONN = libc::EISCONN, + ENOTCONN = libc::ENOTCONN, + ESHUTDOWN = libc::ESHUTDOWN, + ETOOMANYREFS = libc::ETOOMANYREFS, + ETIMEDOUT = libc::ETIMEDOUT, + ECONNREFUSED = libc::ECONNREFUSED, + ELOOP = libc::ELOOP, + ENAMETOOLONG = libc::ENAMETOOLONG, + EHOSTDOWN = libc::EHOSTDOWN, + EHOSTUNREACH = libc::EHOSTUNREACH, + ENOTEMPTY = libc::ENOTEMPTY, + EPROCLIM = libc::EPROCLIM, + EUSERS = libc::EUSERS, + EDQUOT = libc::EDQUOT, + ESTALE = libc::ESTALE, + EREMOTE = libc::EREMOTE, + EBADRPC = libc::EBADRPC, + ERPCMISMATCH = libc::ERPCMISMATCH, + EPROGUNAVAIL = libc::EPROGUNAVAIL, + EPROGMISMATCH = libc::EPROGMISMATCH, + EPROCUNAVAIL = libc::EPROCUNAVAIL, + ENOLCK = libc::ENOLCK, + ENOSYS = libc::ENOSYS, + EFTYPE = libc::EFTYPE, + EAUTH = libc::EAUTH, + ENEEDAUTH = libc::ENEEDAUTH, + EIDRM = libc::EIDRM, + ENOMSG = libc::ENOMSG, + EOVERFLOW = libc::EOVERFLOW, + EILSEQ = libc::EILSEQ, + ENOTSUP = libc::ENOTSUP, + ECANCELED = libc::ECANCELED, + EBADMSG = libc::EBADMSG, + ENODATA = libc::ENODATA, + ENOSR = libc::ENOSR, + ENOSTR = libc::ENOSTR, + ETIME = libc::ETIME, + ENOATTR = libc::ENOATTR, + EMULTIHOP = libc::EMULTIHOP, + ENOLINK = libc::ENOLINK, + EPROTO = libc::EPROTO, + } + + pub const ELAST: Errno = Errno::ENOTSUP; + pub const EWOULDBLOCK: Errno = Errno::EAGAIN; + + pub const EL2NSYNC: Errno = Errno::UnknownErrno; + + pub fn from_i32(e: i32) -> Errno { + use self::Errno::*; + + match e { + libc::EPERM => EPERM, + libc::ENOENT => ENOENT, + libc::ESRCH => ESRCH, + libc::EINTR => EINTR, + libc::EIO => EIO, + libc::ENXIO => ENXIO, + libc::E2BIG => E2BIG, + libc::ENOEXEC => ENOEXEC, + libc::EBADF => EBADF, + libc::ECHILD => ECHILD, + libc::EDEADLK => EDEADLK, + libc::ENOMEM => ENOMEM, + libc::EACCES => EACCES, + libc::EFAULT => EFAULT, + libc::ENOTBLK => ENOTBLK, + libc::EBUSY => EBUSY, + libc::EEXIST => EEXIST, + libc::EXDEV => EXDEV, + libc::ENODEV => ENODEV, + libc::ENOTDIR => ENOTDIR, + libc::EISDIR => EISDIR, + libc::EINVAL => EINVAL, + libc::ENFILE => ENFILE, + libc::EMFILE => EMFILE, + libc::ENOTTY => ENOTTY, + libc::ETXTBSY => ETXTBSY, + libc::EFBIG => EFBIG, + libc::ENOSPC => ENOSPC, + libc::ESPIPE => ESPIPE, + libc::EROFS => EROFS, + libc::EMLINK => EMLINK, + libc::EPIPE => EPIPE, + libc::EDOM => EDOM, + libc::ERANGE => ERANGE, + libc::EAGAIN => EAGAIN, + libc::EINPROGRESS => EINPROGRESS, + libc::EALREADY => EALREADY, + libc::ENOTSOCK => ENOTSOCK, + libc::EDESTADDRREQ => EDESTADDRREQ, + libc::EMSGSIZE => EMSGSIZE, + libc::EPROTOTYPE => EPROTOTYPE, + libc::ENOPROTOOPT => ENOPROTOOPT, + libc::EPROTONOSUPPORT => EPROTONOSUPPORT, + libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT, + libc::EOPNOTSUPP => EOPNOTSUPP, + libc::EPFNOSUPPORT => EPFNOSUPPORT, + libc::EAFNOSUPPORT => EAFNOSUPPORT, + libc::EADDRINUSE => EADDRINUSE, + libc::EADDRNOTAVAIL => EADDRNOTAVAIL, + libc::ENETDOWN => ENETDOWN, + libc::ENETUNREACH => ENETUNREACH, + libc::ENETRESET => ENETRESET, + libc::ECONNABORTED => ECONNABORTED, + libc::ECONNRESET => ECONNRESET, + libc::ENOBUFS => ENOBUFS, + libc::EISCONN => EISCONN, + libc::ENOTCONN => ENOTCONN, + libc::ESHUTDOWN => ESHUTDOWN, + libc::ETOOMANYREFS => ETOOMANYREFS, + libc::ETIMEDOUT => ETIMEDOUT, + libc::ECONNREFUSED => ECONNREFUSED, + libc::ELOOP => ELOOP, + libc::ENAMETOOLONG => ENAMETOOLONG, + libc::EHOSTDOWN => EHOSTDOWN, + libc::EHOSTUNREACH => EHOSTUNREACH, + libc::ENOTEMPTY => ENOTEMPTY, + libc::EPROCLIM => EPROCLIM, + libc::EUSERS => EUSERS, + libc::EDQUOT => EDQUOT, + libc::ESTALE => ESTALE, + libc::EREMOTE => EREMOTE, + libc::EBADRPC => EBADRPC, + libc::ERPCMISMATCH => ERPCMISMATCH, + libc::EPROGUNAVAIL => EPROGUNAVAIL, + libc::EPROGMISMATCH => EPROGMISMATCH, + libc::EPROCUNAVAIL => EPROCUNAVAIL, + libc::ENOLCK => ENOLCK, + libc::ENOSYS => ENOSYS, + libc::EFTYPE => EFTYPE, + libc::EAUTH => EAUTH, + libc::ENEEDAUTH => ENEEDAUTH, + libc::EIDRM => EIDRM, + libc::ENOMSG => ENOMSG, + libc::EOVERFLOW => EOVERFLOW, + libc::EILSEQ => EILSEQ, + libc::ENOTSUP => ENOTSUP, + libc::ECANCELED => ECANCELED, + libc::EBADMSG => EBADMSG, + libc::ENODATA => ENODATA, + libc::ENOSR => ENOSR, + libc::ENOSTR => ENOSTR, + libc::ETIME => ETIME, + libc::ENOATTR => ENOATTR, + libc::EMULTIHOP => EMULTIHOP, + libc::ENOLINK => ENOLINK, + libc::EPROTO => EPROTO, + _ => UnknownErrno, + } + } +} diff --git a/third_party/rust/nix/src/errno_dragonfly.c b/third_party/rust/nix/src/errno_dragonfly.c new file mode 100644 index 0000000000..32fb4dab4d --- /dev/null +++ b/third_party/rust/nix/src/errno_dragonfly.c @@ -0,0 +1,3 @@ +#include <errno.h> + +int *errno_location() { return &errno; } diff --git a/third_party/rust/nix/src/fcntl.rs b/third_party/rust/nix/src/fcntl.rs new file mode 100644 index 0000000000..a763c10f9a --- /dev/null +++ b/third_party/rust/nix/src/fcntl.rs @@ -0,0 +1,394 @@ +use {Error, Result, NixPath}; +use errno::Errno; +use libc::{self, c_int, c_uint, c_char, size_t, ssize_t}; +use sys::stat::Mode; +use std::os::raw; +use std::os::unix::io::RawFd; +use std::ffi::OsStr; +use std::os::unix::ffi::OsStrExt; + +#[cfg(any(target_os = "android", target_os = "linux"))] +use sys::uio::IoVec; // For vmsplice + +libc_bitflags!{ + pub struct AtFlags: c_int { + AT_SYMLINK_NOFOLLOW; + #[cfg(any(target_os = "android", target_os = "linux"))] + AT_NO_AUTOMOUNT; + #[cfg(any(target_os = "android", target_os = "linux"))] + AT_EMPTY_PATH; + } +} + +libc_bitflags!( + /// Configuration options for opened files. + pub struct OFlag: c_int { + /// Mask for the access mode of the file. + O_ACCMODE; + /// Use alternate I/O semantics. + #[cfg(target_os = "netbsd")] + O_ALT_IO; + /// Open the file in append-only mode. + O_APPEND; + /// Generate a signal when input or output becomes possible. + O_ASYNC; + /// Closes the file descriptor once an `execve` call is made. + /// + /// Also sets the file offset to the beginning of the file. + O_CLOEXEC; + /// Create the file if it does not exist. + O_CREAT; + /// Try to minimize cache effects of the I/O for this file. + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd"))] + O_DIRECT; + /// If the specified path isn't a directory, fail. + O_DIRECTORY; + /// Implicitly follow each `write()` with an `fdatasync()`. + #[cfg(any(target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + O_DSYNC; + /// Error out if a file was not created. + O_EXCL; + /// Open for execute only. + #[cfg(target_os = "freebsd")] + O_EXEC; + /// Open with an exclusive file lock. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + O_EXLOCK; + /// Same as `O_SYNC`. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + all(target_os = "linux", not(target_env = "musl")), + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + O_FSYNC; + /// Allow files whose sizes can't be represented in an `off_t` to be opened. + #[cfg(any(target_os = "android", target_os = "linux"))] + O_LARGEFILE; + /// Do not update the file last access time during `read(2)`s. + #[cfg(any(target_os = "android", target_os = "linux"))] + O_NOATIME; + /// Don't attach the device as the process' controlling terminal. + O_NOCTTY; + /// Same as `O_NONBLOCK`. + O_NDELAY; + /// `open()` will fail if the given path is a symbolic link. + O_NOFOLLOW; + /// When possible, open the file in nonblocking mode. + O_NONBLOCK; + /// Don't deliver `SIGPIPE`. + #[cfg(target_os = "netbsd")] + O_NOSIGPIPE; + /// Obtain a file descriptor for low-level access. + /// + /// The file itself is not opened and other file operations will fail. + #[cfg(any(target_os = "android", target_os = "linux"))] + O_PATH; + /// Only allow reading. + /// + /// This should not be combined with `O_WRONLY` or `O_RDWR`. + O_RDONLY; + /// Allow both reading and writing. + /// + /// This should not be combined with `O_WRONLY` or `O_RDWR`. + O_RDWR; + /// Similar to `O_DSYNC` but applies to `read`s instead. + #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))] + O_RSYNC; + /// Skip search permission checks. + #[cfg(target_os = "netbsd")] + O_SEARCH; + /// Open with a shared file lock. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + O_SHLOCK; + /// Implicitly follow each `write()` with an `fsync()`. + O_SYNC; + /// Create an unnamed temporary file. + #[cfg(any(target_os = "android", target_os = "linux"))] + O_TMPFILE; + /// Truncate an existing regular file to 0 length if it allows writing. + O_TRUNC; + /// Restore default TTY attributes. + #[cfg(target_os = "freebsd")] + O_TTY_INIT; + /// Only allow writing. + /// + /// This should not be combined with `O_RDONLY` or `O_RDWR`. + O_WRONLY; + } +); + +pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> { + let fd = path.with_nix_path(|cstr| { + unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) } + })?; + + Errno::result(fd) +} + +pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> { + let fd = path.with_nix_path(|cstr| { + unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) } + })?; + Errno::result(fd) +} + +fn wrap_readlink_result(buffer: &mut[u8], res: ssize_t) -> Result<&OsStr> { + match Errno::result(res) { + Err(err) => Err(err), + Ok(len) => { + if (len as usize) >= buffer.len() { + Err(Error::Sys(Errno::ENAMETOOLONG)) + } else { + Ok(OsStr::from_bytes(&buffer[..(len as usize)])) + } + } + } +} + +pub fn readlink<'a, P: ?Sized + NixPath>(path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> { + let res = path.with_nix_path(|cstr| { + unsafe { libc::readlink(cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) } + })?; + + wrap_readlink_result(buffer, res) +} + + +pub fn readlinkat<'a, P: ?Sized + NixPath>(dirfd: RawFd, path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> { + let res = path.with_nix_path(|cstr| { + unsafe { libc::readlinkat(dirfd, cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) } + })?; + + wrap_readlink_result(buffer, res) +} + +/// Computes the raw fd consumed by a function of the form `*at`. +pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int { + match fd { + None => libc::AT_FDCWD, + Some(fd) => fd, + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +libc_bitflags!( + /// Additional flags for file sealing, which allows for limiting operations on a file. + pub struct SealFlag: c_int { + /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`. + F_SEAL_SEAL; + /// The file cannot be reduced in size. + F_SEAL_SHRINK; + /// The size of the file cannot be increased. + F_SEAL_GROW; + /// The file contents cannot be modified. + F_SEAL_WRITE; + } +); + +libc_bitflags!( + /// Additional configuration flags for `fcntl`'s `F_SETFD`. + pub struct FdFlag: c_int { + /// The file descriptor will automatically be closed during a successful `execve(2)`. + FD_CLOEXEC; + } +); + +#[allow(missing_debug_implementations)] +pub enum FcntlArg<'a> { + F_DUPFD(RawFd), + F_DUPFD_CLOEXEC(RawFd), + F_GETFD, + F_SETFD(FdFlag), // FD_FLAGS + F_GETFL, + F_SETFL(OFlag), // O_NONBLOCK + F_SETLK(&'a libc::flock), + F_SETLKW(&'a libc::flock), + F_GETLK(&'a mut libc::flock), + #[cfg(any(target_os = "linux", target_os = "android"))] + F_OFD_SETLK(&'a libc::flock), + #[cfg(any(target_os = "linux", target_os = "android"))] + F_OFD_SETLKW(&'a libc::flock), + #[cfg(any(target_os = "linux", target_os = "android"))] + F_OFD_GETLK(&'a mut libc::flock), + #[cfg(any(target_os = "android", target_os = "linux"))] + F_ADD_SEALS(SealFlag), + #[cfg(any(target_os = "android", target_os = "linux"))] + F_GET_SEALS, + #[cfg(any(target_os = "macos", target_os = "ios"))] + F_FULLFSYNC, + #[cfg(any(target_os = "linux", target_os = "android"))] + F_GETPIPE_SZ, + #[cfg(any(target_os = "linux", target_os = "android"))] + F_SETPIPE_SZ(c_int), + + // TODO: Rest of flags +} +pub use self::FcntlArg::*; + +// TODO: Figure out how to handle value fcntl returns +pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> { + let res = unsafe { + match arg { + F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd), + F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd), + F_GETFD => libc::fcntl(fd, libc::F_GETFD), + F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()), + F_GETFL => libc::fcntl(fd, libc::F_GETFL), + F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()), + F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock), + F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock), + F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock), + #[cfg(any(target_os = "android", target_os = "linux"))] + F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()), + #[cfg(any(target_os = "android", target_os = "linux"))] + F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS), + #[cfg(any(target_os = "macos", target_os = "ios"))] + F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC), + #[cfg(any(target_os = "linux", target_os = "android"))] + F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ), + #[cfg(any(target_os = "linux", target_os = "android"))] + F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size), + #[cfg(any(target_os = "linux", target_os = "android"))] + _ => unimplemented!() + } + }; + + Errno::result(res) +} + +#[derive(Clone, Copy)] +#[allow(missing_debug_implementations)] +pub enum FlockArg { + LockShared, + LockExclusive, + Unlock, + LockSharedNonblock, + LockExclusiveNonblock, + UnlockNonblock, +} + +pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> { + use self::FlockArg::*; + + let res = unsafe { + match arg { + LockShared => libc::flock(fd, libc::LOCK_SH), + LockExclusive => libc::flock(fd, libc::LOCK_EX), + Unlock => libc::flock(fd, libc::LOCK_UN), + LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB), + LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB), + UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB), + } + }; + + Errno::result(res).map(drop) +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +libc_bitflags! { + /// Additional flags to `splice` and friends. + pub struct SpliceFFlags: c_uint { + /// Request that pages be moved instead of copied. + /// + /// Not applicable to `vmsplice`. + SPLICE_F_MOVE; + /// Do not block on I/O. + SPLICE_F_NONBLOCK; + /// Hint that more data will be coming in a subsequent splice. + /// + /// Not applicable to `vmsplice`. + SPLICE_F_MORE; + /// Gift the user pages to the kernel. + /// + /// Not applicable to `splice`. + SPLICE_F_GIFT; + } +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn splice(fd_in: RawFd, off_in: Option<&mut libc::loff_t>, + fd_out: RawFd, off_out: Option<&mut libc::loff_t>, + len: usize, flags: SpliceFFlags) -> Result<usize> { + use std::ptr; + let off_in = off_in.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut()); + let off_out = off_out.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut()); + + let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) }; + Errno::result(ret).map(|r| r as usize) +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> { + let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) }; + Errno::result(ret).map(|r| r as usize) +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize> { + let ret = unsafe { + libc::vmsplice(fd, iov.as_ptr() as *const libc::iovec, iov.len(), flags.bits()) + }; + Errno::result(ret).map(|r| r as usize) +} + +#[cfg(any(target_os = "linux"))] +libc_bitflags!( + /// Mode argument flags for fallocate determining operation performed on a given range. + pub struct FallocateFlags: c_int { + /// File size is not changed. + /// + /// offset + len can be greater than file size. + FALLOC_FL_KEEP_SIZE; + /// Deallocates space by creating a hole. + /// + /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes. + FALLOC_FL_PUNCH_HOLE; + /// Removes byte range from a file without leaving a hole. + /// + /// Byte range to collapse starts at offset and continues for len bytes. + FALLOC_FL_COLLAPSE_RANGE; + /// Zeroes space in specified byte range. + /// + /// Byte range starts at offset and continues for len bytes. + FALLOC_FL_ZERO_RANGE; + /// Increases file space by inserting a hole within the file size. + /// + /// Does not overwrite existing data. Hole starts at offset and continues for len bytes. + FALLOC_FL_INSERT_RANGE; + /// Shared file data extants are made private to the file. + /// + /// Gaurantees that a subsequent write will not fail due to lack of space. + FALLOC_FL_UNSHARE_RANGE; + } +); + +/// Manipulates file space. +/// +/// Allows the caller to directly manipulate the allocated disk space for the +/// file referred to by fd. +#[cfg(any(target_os = "linux"))] +pub fn fallocate(fd: RawFd, mode: FallocateFlags, offset: libc::off_t, len: libc::off_t) -> Result<c_int> { + let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) }; + Errno::result(res) +} diff --git a/third_party/rust/nix/src/features.rs b/third_party/rust/nix/src/features.rs new file mode 100644 index 0000000000..7797aa4641 --- /dev/null +++ b/third_party/rust/nix/src/features.rs @@ -0,0 +1,103 @@ +//! Feature tests for OS functionality +pub use self::os::*; + +#[cfg(any(target_os = "linux", target_os = "android"))] +mod os { + use sys::utsname::uname; + + // 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() -> 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().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, + } + } + + 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() -> usize { + static mut KERNEL_VERS: usize = 0; + + unsafe { + if KERNEL_VERS == 0 { + KERNEL_VERS = parse_kernel_version(); + } + + KERNEL_VERS + } + } + + /// Check if the OS supports atomic close-on-exec for sockets + pub fn socket_atomic_cloexec() -> bool { + kernel_version() >= VERS_2_6_27 + } + + #[test] + pub fn test_parsing_kernel_version() { + assert!(kernel_version() > 0); + } +} + +#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "openbsd", target_os = "netbsd"))] +mod os { + /// Check if the OS supports atomic close-on-exec for sockets + pub fn socket_atomic_cloexec() -> bool { + false + } +} diff --git a/third_party/rust/nix/src/ifaddrs.rs b/third_party/rust/nix/src/ifaddrs.rs new file mode 100644 index 0000000000..61d36ff4cd --- /dev/null +++ b/third_party/rust/nix/src/ifaddrs.rs @@ -0,0 +1,146 @@ +//! Query network interface addresses +//! +//! Uses the Linux and/or BSD specific function `getifaddrs` to query the list +//! of interfaces and their associated addresses. + +use std::ffi; +use std::iter::Iterator; +use std::mem; +use std::option::Option; + +use libc; + +use {Result, Errno}; +use sys::socket::SockAddr; +use net::if_::*; + +/// Describes a single address for an interface as returned by `getifaddrs`. +#[derive(Clone, Eq, Hash, PartialEq, Debug)] +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<SockAddr>, + /// Netmask of this interface + pub netmask: Option<SockAddr>, + /// Broadcast address of this interface, if applicable + pub broadcast: Option<SockAddr>, + /// Point-to-point destination address + pub destination: Option<SockAddr>, +} + +cfg_if! { + if #[cfg(any(target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] { + 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 + } + } +} + +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 { SockAddr::from_libc_sockaddr(info.ifa_addr) }; + let netmask = unsafe { SockAddr::from_libc_sockaddr(info.ifa_netmask) }; + let mut addr = InterfaceAddress { + interface_name: ifname.to_string_lossy().to_string(), + flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32), + address: address, + netmask: netmask, + broadcast: None, + destination: None, + }; + + let ifu = get_ifu_from_sockaddr(info); + if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) { + addr.destination = unsafe { SockAddr::from_libc_sockaddr(ifu) }; + } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) { + addr.broadcast = unsafe { SockAddr::from_libc_sockaddr(ifu) }; + } + + 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 `SockAddr` 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: *mut libc::ifaddrs = unsafe { mem::uninitialized() }; + Errno::result(unsafe { libc::getifaddrs(&mut addrs) }).map(|_| { + InterfaceAddressIterator { + base: addrs, + next: addrs, + } + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + // Only checks if `getifaddrs` can be invoked without panicking. + #[test] + fn test_getifaddrs() { + let _ = getifaddrs(); + } +} diff --git a/third_party/rust/nix/src/kmod.rs b/third_party/rust/nix/src/kmod.rs new file mode 100644 index 0000000000..e853261b14 --- /dev/null +++ b/third_party/rust/nix/src/kmod.rs @@ -0,0 +1,123 @@ +//! Load and unload kernel modules. +//! +//! For more details see + +use libc; +use std::ffi::CStr; +use std::os::unix::io::AsRawFd; + +use errno::Errno; +use 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)`](http://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)`](http://man7.org/linux/man-pages/man2/init_module.2.html) for more information. +pub fn finit_module<T: AsRawFd>(fd: &T, param_values: &CStr, flags: ModuleInitFlags) -> Result<()> { + let res = unsafe { + libc::syscall( + libc::SYS_finit_module, + 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)`](http://man7.org/linux/man-pages/man2/delete_module.2.html) + /// for a detailed description how these flags work. + pub struct DeleteModuleFlags: libc::c_int { + O_NONBLOCK; + 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)`](http://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) +} diff --git a/third_party/rust/nix/src/lib.rs b/third_party/rust/nix/src/lib.rs new file mode 100644 index 0000000000..3293695a73 --- /dev/null +++ b/third_party/rust/nix/src/lib.rs @@ -0,0 +1,283 @@ +//! Rust friendly bindings to the various *nix system functions. +//! +//! Modules are structured according to the C header file that they would be +//! defined in. +#![crate_name = "nix"] +#![cfg(unix)] +#![allow(non_camel_case_types)] +// latest bitflags triggers a rustc bug with cross-crate macro expansions causing dead_code +// warnings even though the macro expands into something with allow(dead_code) +#![allow(dead_code)] +// #![cfg_attr(test, deny(warnings))] +#![recursion_limit = "500"] +#![deny(unused)] +#![deny(unstable_features)] +#![deny(missing_copy_implementations)] +#![deny(missing_debug_implementations)] + +// External crates +#[macro_use] +extern crate bitflags; +#[macro_use] +extern crate cfg_if; +extern crate void; + +// Re-exported external crates +pub extern crate libc; + +// Private internal modules +#[macro_use] mod macros; + +// Public crates +pub mod dir; +pub mod errno; +#[deny(missing_docs)] +pub mod features; +pub mod fcntl; +#[deny(missing_docs)] +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +pub mod ifaddrs; +#[cfg(any(target_os = "android", + target_os = "linux"))] +pub mod kmod; +#[cfg(any(target_os = "android", + target_os = "linux"))] +pub mod mount; +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "fushsia", + target_os = "linux", + target_os = "netbsd"))] +pub mod mqueue; +#[deny(missing_docs)] +pub mod net; +#[deny(missing_docs)] +pub mod poll; +#[deny(missing_docs)] +pub mod pty; +#[cfg(any(target_os = "android", + target_os = "linux"))] +pub mod sched; +pub mod sys; +// This can be implemented for other platforms as soon as libc +// provides bindings for them. +#[cfg(all(target_os = "linux", + any(target_arch = "x86", target_arch = "x86_64")))] +pub mod ucontext; +pub mod unistd; + +/* + * + * ===== Result / Error ===== + * + */ + +use libc::{c_char, PATH_MAX}; + +use std::{error, fmt, ptr, result}; +use std::ffi::{CStr, OsStr}; +use std::os::unix::ffi::OsStrExt; +use std::path::{Path, PathBuf}; + +use errno::Errno; + +/// Nix Result Type +pub type Result<T> = result::Result<T, Error>; + +/// Nix Error Type +/// +/// The nix error type provides a common way of dealing with +/// various system system/libc calls that might fail. Each +/// error has a corresponding errno (usually the one from the +/// underlying OS) to which it can be mapped in addition to +/// implementing other common traits. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Error { + Sys(Errno), + InvalidPath, + /// The operation involved a conversion to Rust's native String type, which failed because the + /// string did not contain all valid UTF-8. + InvalidUtf8, + /// The operation is not supported by Nix, in this instance either use the libc bindings or + /// consult the module documentation to see if there is a more appropriate interface available. + UnsupportedOperation, +} + +impl Error { + /// Convert this `Error` to an [`Errno`](enum.Errno.html). + /// + /// # Example + /// + /// ``` + /// # use nix::Error; + /// # use nix::errno::Errno; + /// let e = Error::from(Errno::EPERM); + /// assert_eq!(Some(Errno::EPERM), e.as_errno()); + /// ``` + pub fn as_errno(&self) -> Option<Errno> { + if let &Error::Sys(ref e) = self { + Some(*e) + } else { + None + } + } + + /// Create a nix Error from a given errno + pub fn from_errno(errno: Errno) -> Error { + Error::Sys(errno) + } + + /// Get the current errno and convert it to a nix Error + pub fn last() -> Error { + Error::Sys(Errno::last()) + } + + /// Create a new invalid argument error (`EINVAL`) + pub fn invalid_argument() -> Error { + Error::Sys(Errno::EINVAL) + } + +} + +impl From<Errno> for Error { + fn from(errno: Errno) -> Error { Error::from_errno(errno) } +} + +impl From<std::string::FromUtf8Error> for Error { + fn from(_: std::string::FromUtf8Error) -> Error { Error::InvalidUtf8 } +} + +impl error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::InvalidPath => "Invalid path", + Error::InvalidUtf8 => "Invalid UTF-8 string", + Error::UnsupportedOperation => "Unsupported Operation", + Error::Sys(ref errno) => errno.desc(), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::InvalidPath => write!(f, "Invalid path"), + Error::InvalidUtf8 => write!(f, "Invalid UTF-8 string"), + Error::UnsupportedOperation => write!(f, "Unsupported Operation"), + Error::Sys(errno) => write!(f, "{:?}: {}", errno, errno.desc()), + } + } +} + +pub trait NixPath { + fn len(&self) -> usize; + + fn with_nix_path<T, F>(&self, f: F) -> Result<T> + where F: FnOnce(&CStr) -> T; +} + +impl NixPath for str { + 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 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 len(&self) -> usize { + self.to_bytes().len() + } + + fn with_nix_path<T, F>(&self, f: F) -> Result<T> + where F: FnOnce(&CStr) -> T { + // Equivalence with the [u8] impl. + if self.len() >= PATH_MAX as usize { + return Err(Error::InvalidPath); + } + + Ok(f(self)) + } +} + +impl NixPath for [u8] { + fn len(&self) -> usize { + self.len() + } + + fn with_nix_path<T, F>(&self, f: F) -> Result<T> + where F: FnOnce(&CStr) -> T { + let mut buf = [0u8; PATH_MAX as usize]; + + if self.len() >= PATH_MAX as usize { + return Err(Error::InvalidPath); + } + + match self.iter().position(|b| *b == 0) { + Some(_) => Err(Error::InvalidPath), + None => { + unsafe { + // TODO: Replace with bytes::copy_memory. rust-lang/rust#24028 + ptr::copy_nonoverlapping(self.as_ptr(), buf.as_mut_ptr(), self.len()); + Ok(f(CStr::from_ptr(buf.as_ptr() as *const c_char))) + } + + } + } + } +} + +impl NixPath for Path { + 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 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) + } +} + +/// Treats `None` as an empty string. +impl<'a, NP: ?Sized + NixPath> NixPath for Option<&'a NP> { + fn len(&self) -> usize { + self.map_or(0, NixPath::len) + } + + fn with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T { + if let Some(nix_path) = *self { + nix_path.with_nix_path(f) + } else { + unsafe { CStr::from_ptr("\0".as_ptr() as *const _).with_nix_path(f) } + } + } +} diff --git a/third_party/rust/nix/src/macros.rs b/third_party/rust/nix/src/macros.rs new file mode 100644 index 0000000000..3d1b0e4b76 --- /dev/null +++ b/third_party/rust/nix/src/macros.rs @@ -0,0 +1,264 @@ +/// 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 +/// ``` +/// libc_bitflags!{ +/// pub struct ProtFlags: libc::c_int { +/// PROT_NONE; +/// PROT_READ; +/// /// PROT_WRITE enables write protect +/// PROT_WRITE; +/// PROT_EXEC; +/// #[cfg(any(target_os = "linux", target_os = "android"))] +/// PROT_GROWSDOWN; +/// #[cfg(any(target_os = "linux", target_os = "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. +/// +/// ``` +/// 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! { + $(#[$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 +/// ``` +/// libc_enum!{ +/// pub enum ProtFlags { +/// PROT_NONE, +/// PROT_READ, +/// PROT_WRITE, +/// PROT_EXEC, +/// #[cfg(any(target_os = "linux", target_os = "android"))] +/// PROT_GROWSDOWN, +/// #[cfg(any(target_os = "linux", target_os = "android"))] +/// PROT_GROWSUP, +/// } +/// } +/// ``` +macro_rules! libc_enum { + // (non-pub) Exit rule. + (@make_enum + { + name: $BitFlags:ident, + attrs: [$($attrs:tt)*], + entries: [$($entries:tt)*], + } + ) => { + $($attrs)* + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + enum $BitFlags { + $($entries)* + } + }; + + // (pub) Exit rule. + (@make_enum + { + pub, + name: $BitFlags:ident, + attrs: [$($attrs:tt)*], + entries: [$($entries:tt)*], + } + ) => { + $($attrs)* + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub enum $BitFlags { + $($entries)* + } + }; + + // (non-pub) Done accumulating. + (@accumulate_entries + { + name: $BitFlags:ident, + attrs: $attrs:tt, + }, + $entries:tt; + ) => { + libc_enum! { + @make_enum + { + name: $BitFlags, + attrs: $attrs, + entries: $entries, + } + } + }; + + // (pub) Done accumulating. + (@accumulate_entries + { + pub, + name: $BitFlags:ident, + attrs: $attrs:tt, + }, + $entries:tt; + ) => { + libc_enum! { + @make_enum + { + pub, + name: $BitFlags, + attrs: $attrs, + entries: $entries, + } + } + }; + + // Munch an attr. + (@accumulate_entries + $prefix:tt, + [$($entries:tt)*]; + #[$attr:meta] $($tail:tt)* + ) => { + libc_enum! { + @accumulate_entries + $prefix, + [ + $($entries)* + #[$attr] + ]; + $($tail)* + } + }; + + // Munch last ident if not followed by a comma. + (@accumulate_entries + $prefix:tt, + [$($entries:tt)*]; + $entry:ident + ) => { + libc_enum! { + @accumulate_entries + $prefix, + [ + $($entries)* + $entry = libc::$entry, + ]; + } + }; + + // Munch an ident; covers terminating comma case. + (@accumulate_entries + $prefix:tt, + [$($entries:tt)*]; + $entry:ident, $($tail:tt)* + ) => { + libc_enum! { + @accumulate_entries + $prefix, + [ + $($entries)* + $entry = libc::$entry, + ]; + $($tail)* + } + }; + + // Munch an ident and cast it to the given type; covers terminating comma. + (@accumulate_entries + $prefix:tt, + [$($entries:tt)*]; + $entry:ident as $ty:ty, $($tail:tt)* + ) => { + libc_enum! { + @accumulate_entries + $prefix, + [ + $($entries)* + $entry = libc::$entry as $ty, + ]; + $($tail)* + } + }; + + // (non-pub) Entry rule. + ( + $(#[$attr:meta])* + enum $BitFlags:ident { + $($vals:tt)* + } + ) => { + libc_enum! { + @accumulate_entries + { + name: $BitFlags, + attrs: [$(#[$attr])*], + }, + []; + $($vals)* + } + }; + + // (pub) Entry rule. + ( + $(#[$attr:meta])* + pub enum $BitFlags:ident { + $($vals:tt)* + } + ) => { + libc_enum! { + @accumulate_entries + { + pub, + name: $BitFlags, + attrs: [$(#[$attr])*], + }, + []; + $($vals)* + } + }; +} + +/// A Rust version of the familiar C `offset_of` macro. It returns the byte +/// offset of `field` within struct `ty` +macro_rules! offset_of { + ($ty:ty, $field:ident) => { + &(*(0 as *const $ty)).$field as *const _ as usize + } +} diff --git a/third_party/rust/nix/src/mount.rs b/third_party/rust/nix/src/mount.rs new file mode 100644 index 0000000000..51bc8d824a --- /dev/null +++ b/third_party/rust/nix/src/mount.rs @@ -0,0 +1,99 @@ +use libc::{c_ulong, c_int}; +use libc; +use {Result, NixPath}; +use errno::Errno; + +libc_bitflags!( + 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; + MS_MOVE; + MS_REC; + MS_SILENT; + MS_POSIXACL; + MS_UNBINDABLE; + MS_PRIVATE; + MS_SLAVE; + MS_SHARED; + MS_RELATIME; + MS_KERNMOUNT; + MS_I_VERSION; + MS_STRICTATIME; + MS_ACTIVE; + MS_NOUSER; + MS_RMT_MASK; + MS_MGC_VAL; + MS_MGC_MSK; + } +); + +libc_bitflags!( + pub struct MntFlags: c_int { + MNT_FORCE; + MNT_DETACH; + MNT_EXPIRE; + } +); + +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 = + source.with_nix_path(|source| { + target.with_nix_path(|target| { + fstype.with_nix_path(|fstype| { + data.with_nix_path(|data| { + unsafe { + libc::mount(source.as_ptr(), + target.as_ptr(), + fstype.as_ptr(), + flags.bits, + data.as_ptr() as *const libc::c_void) + } + }) + }) + }) + })????; + + Errno::result(res).map(drop) +} + +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) +} + +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) +} diff --git a/third_party/rust/nix/src/mqueue.rs b/third_party/rust/nix/src/mqueue.rs new file mode 100644 index 0000000000..87be65321b --- /dev/null +++ b/third_party/rust/nix/src/mqueue.rs @@ -0,0 +1,173 @@ +//! Posix Message Queue functions +//! +//! [Further reading and details on the C API](http://man7.org/linux/man-pages/man7/mq_overview.7.html) + +use Result; +use errno::Errno; + +use libc::{self, c_char, c_long, mqd_t, size_t}; +use std::ffi::CString; +use sys::stat::Mode; +use std::mem; + +libc_bitflags!{ + pub struct MQ_OFlag: libc::c_int { + O_RDONLY; + O_WRONLY; + O_RDWR; + O_CREAT; + O_EXCL; + O_NONBLOCK; + O_CLOEXEC; + } +} + +libc_bitflags!{ + pub struct FdFlag: libc::c_int { + FD_CLOEXEC; + } +} + +#[repr(C)] +#[derive(Clone, Copy)] +#[allow(missing_debug_implementations)] +pub struct MqAttr { + mq_attr: libc::mq_attr, +} + +impl PartialEq<MqAttr> for MqAttr { + fn eq(&self, other: &MqAttr) -> bool { + let self_attr = self.mq_attr; + let other_attr = other.mq_attr; + self_attr.mq_flags == other_attr.mq_flags && self_attr.mq_maxmsg == other_attr.mq_maxmsg && + self_attr.mq_msgsize == other_attr.mq_msgsize && + self_attr.mq_curmsgs == other_attr.mq_curmsgs + } +} + +impl MqAttr { + pub fn new(mq_flags: c_long, + mq_maxmsg: c_long, + mq_msgsize: c_long, + mq_curmsgs: c_long) + -> MqAttr { + let mut attr = unsafe { mem::uninitialized::<libc::mq_attr>() }; + attr.mq_flags = mq_flags; + attr.mq_maxmsg = mq_maxmsg; + attr.mq_msgsize = mq_msgsize; + attr.mq_curmsgs = mq_curmsgs; + MqAttr { mq_attr: attr } + } + + pub fn flags(&self) -> c_long { + self.mq_attr.mq_flags + } +} + + +/// Open a message queue +/// +/// See also [`mq_open(2)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html) +pub fn mq_open(name: &CString, + oflag: MQ_OFlag, + mode: Mode, + attr: Option<&MqAttr>) + -> Result<mqd_t> { + let res = match attr { + Some(mq_attr) => unsafe { + libc::mq_open(name.as_ptr(), + oflag.bits(), + mode.bits() as libc::c_int, + &mq_attr.mq_attr as *const libc::mq_attr) + }, + None => unsafe { libc::mq_open(name.as_ptr(), oflag.bits()) }, + }; + Errno::result(res) +} + +/// Remove a message queue +/// +/// See also [`mq_unlink(2)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html) +pub fn mq_unlink(name: &CString) -> Result<()> { + let res = unsafe { libc::mq_unlink(name.as_ptr()) }; + Errno::result(res).map(drop) +} + +/// Close a message queue +/// +/// See also [`mq_close(2)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html) +pub fn mq_close(mqdes: mqd_t) -> Result<()> { + let res = unsafe { libc::mq_close(mqdes) }; + Errno::result(res).map(drop) +} + +/// Receive a message from a message queue +/// +/// See also [`mq_receive(2)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html) +pub fn mq_receive(mqdes: mqd_t, message: &mut [u8], msg_prio: &mut u32) -> Result<usize> { + let len = message.len() as size_t; + let res = unsafe { + libc::mq_receive(mqdes, + message.as_mut_ptr() as *mut c_char, + len, + msg_prio as *mut u32) + }; + Errno::result(res).map(|r| r as usize) +} + +/// Send a message to a message queue +/// +/// See also [`mq_send(2)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html) +pub fn mq_send(mqdes: mqd_t, message: &[u8], msq_prio: u32) -> Result<()> { + let res = unsafe { + libc::mq_send(mqdes, + message.as_ptr() as *const c_char, + message.len(), + msq_prio) + }; + Errno::result(res).map(drop) +} + +/// Get message queue attributes +/// +/// See also [`mq_getattr(2)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html) +pub fn mq_getattr(mqd: mqd_t) -> Result<MqAttr> { + let mut attr = unsafe { mem::uninitialized::<libc::mq_attr>() }; + let res = unsafe { libc::mq_getattr(mqd, &mut attr) }; + Errno::result(res).map(|_| MqAttr { mq_attr: attr }) +} + +/// Set the attributes of the message queue. Only `O_NONBLOCK` can be set, everything else will be ignored +/// 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](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html) +pub fn mq_setattr(mqd: mqd_t, newattr: &MqAttr) -> Result<MqAttr> { + let mut attr = unsafe { mem::uninitialized::<libc::mq_attr>() }; + let res = unsafe { libc::mq_setattr(mqd, &newattr.mq_attr as *const libc::mq_attr, &mut attr) }; + Errno::result(res).map(|_| MqAttr { mq_attr: attr }) +} + +/// Convenience function. +/// Sets the `O_NONBLOCK` attribute for a given message queue descriptor +/// Returns the old attributes +pub fn mq_set_nonblock(mqd: mqd_t) -> Result<(MqAttr)> { + let oldattr = mq_getattr(mqd)?; + let newattr = MqAttr::new(MQ_OFlag::O_NONBLOCK.bits() as c_long, + oldattr.mq_attr.mq_maxmsg, + oldattr.mq_attr.mq_msgsize, + oldattr.mq_attr.mq_curmsgs); + mq_setattr(mqd, &newattr) +} + +/// Convenience function. +/// Removes `O_NONBLOCK` attribute for a given message queue descriptor +/// Returns the old attributes +pub fn mq_remove_nonblock(mqd: mqd_t) -> 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) +} diff --git a/third_party/rust/nix/src/net/if_.rs b/third_party/rust/nix/src/net/if_.rs new file mode 100644 index 0000000000..58d677ae34 --- /dev/null +++ b/third_party/rust/nix/src/net/if_.rs @@ -0,0 +1,268 @@ +//! Network interface name resolution. +//! +//! Uses Linux and/or POSIX functions to resolve interface names like "eth0" +//! or "socan1" into device numbers. + +use libc; +use libc::c_uint; +use {Result, Error, NixPath}; + +/// Resolve an interface into a 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) + } +} + +libc_bitflags!( + /// Standard interface flags, used by `getifaddrs` + pub struct InterfaceFlags: libc::c_int { + /// Interface is running. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_UP; + /// Valid broadcast address set. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_BROADCAST; + /// Internal debugging flag. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_DEBUG; + /// Interface is a loopback interface. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_LOOPBACK; + /// Interface is a point-to-point link. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_POINTOPOINT; + /// Avoid use of trailers. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", + target_os = "fuchsia", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "solaris"))] + IFF_NOTRAILERS; + /// Interface manages own routes. + #[cfg(any(target_os = "dragonfly"))] + IFF_SMART; + /// Resources allocated. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris"))] + IFF_RUNNING; + /// No arp protocol, L2 destination address not set. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_NOARP; + /// Interface is in promiscuous mode. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_PROMISC; + /// Receive all multicast packets. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_ALLMULTI; + /// Master of a load balancing bundle. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_MASTER; + /// transmission in progress, tx hardware queue is full + #[cfg(any(target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "ios"))] + IFF_OACTIVE; + /// Protocol code on board. + #[cfg(target_os = "solaris")] + IFF_INTELLIGENT; + /// Slave of a load balancing bundle. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_SLAVE; + /// Can't hear own transmissions. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "osx"))] + IFF_SIMPLEX; + /// Supports multicast. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + IFF_MULTICAST; + /// Per link layer defined bit. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "ios"))] + IFF_LINK0; + /// Multicast using broadcast. + #[cfg(any(target_os = "solaris"))] + IFF_MULTI_BCAST; + /// Is able to select media type via ifmap. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_PORTSEL; + /// Per link layer defined bit. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "ios"))] + IFF_LINK1; + /// Non-unique address. + #[cfg(any(target_os = "solaris"))] + IFF_UNNUMBERED; + /// Auto media selection active. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_AUTOMEDIA; + /// Per link layer defined bit. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "ios"))] + IFF_LINK2; + /// Use alternate physical connection. + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "ios"))] + IFF_ALTPHYS; + /// DHCP controlls interface. + #[cfg(any(target_os = "solaris"))] + IFF_DHCPRUNNING; + /// The addresses are lost when the interface goes down. (see + /// [`netdevice(7)`](http://man7.org/linux/man-pages/man7/netdevice.7.html)) + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_DYNAMIC; + /// Do not advertise. + #[cfg(any(target_os = "solaris"))] + IFF_PRIVATE; + /// Driver signals L1 up. Volatile. + #[cfg(any(target_os = "fuchsia", target_os = "linux"))] + 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(any(target_os = "solaris"))] + IFF_NOXMIT; + /// Driver signals dormant. Volatile. + #[cfg(any(target_os = "fuchsia", target_os = "linux"))] + IFF_DORMANT; + /// User-requested promisc mode. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + IFF_PPROMISC; + /// Just on-link subnet. + #[cfg(any(target_os = "solaris"))] + IFF_NOLOCAL; + /// Echo sent packets. Volatile. + #[cfg(any(target_os = "fuchsia", target_os = "linux"))] + IFF_ECHO; + /// User-requested monitor mode. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + IFF_MONITOR; + /// Address is deprecated. + #[cfg(any(target_os = "solaris"))] + IFF_DEPRECATED; + /// Static ARP. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + IFF_STATICARP; + /// Address from stateless addrconf. + #[cfg(any(target_os = "solaris"))] + IFF_ADDRCONF; + /// Interface is in polling mode. + #[cfg(any(target_os = "dragonfly"))] + IFF_NPOLLING; + /// Router on interface. + #[cfg(any(target_os = "solaris"))] + IFF_ROUTER; + /// 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(any(target_os = "solaris"))] + IFF_NONUD; + /// Interface is being renamed + #[cfg(any(target_os = "freebsd"))] + IFF_RENAMING; + /// Anycast address. + #[cfg(any(target_os = "solaris"))] + IFF_ANYCAST; + /// Don't exchange routing info. + #[cfg(any(target_os = "solaris"))] + IFF_NORTEXCH; + /// Do not provide packet information + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_NO_PI as libc::c_int; + /// TUN device (no Ethernet headers) + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_TUN as libc::c_int; + /// TAP device + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + IFF_TAP as libc::c_int; + /// IPv4 interface. + #[cfg(any(target_os = "solaris"))] + IFF_IPV4; + /// IPv6 interface. + #[cfg(any(target_os = "solaris"))] + IFF_IPV6; + /// in.mpathd test address + #[cfg(any(target_os = "solaris"))] + IFF_NOFAILOVER; + /// Interface has failed + #[cfg(any(target_os = "solaris"))] + IFF_FAILED; + /// Interface is a hot-spare + #[cfg(any(target_os = "solaris"))] + IFF_STANDBY; + /// Functioning but not used + #[cfg(any(target_os = "solaris"))] + IFF_INACTIVE; + /// Interface is offline + #[cfg(any(target_os = "solaris"))] + IFF_OFFLINE; + #[cfg(any(target_os = "solaris"))] + IFF_COS_ENABLED; + /// Prefer as source addr. + #[cfg(any(target_os = "solaris"))] + IFF_PREFERRED; + /// RFC3041 + #[cfg(any(target_os = "solaris"))] + IFF_TEMPORARY; + /// MTU set with SIOCSLIFMTU + #[cfg(any(target_os = "solaris"))] + IFF_FIXEDMTU; + /// Cannot send / receive packets + #[cfg(any(target_os = "solaris"))] + IFF_VIRTUAL; + /// Local address in use + #[cfg(any(target_os = "solaris"))] + IFF_DUPLICATE; + /// IPMP IP interface + #[cfg(any(target_os = "solaris"))] + IFF_IPMP; + } +); diff --git a/third_party/rust/nix/src/net/mod.rs b/third_party/rust/nix/src/net/mod.rs new file mode 100644 index 0000000000..079fcfde6f --- /dev/null +++ b/third_party/rust/nix/src/net/mod.rs @@ -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_; diff --git a/third_party/rust/nix/src/poll.rs b/third_party/rust/nix/src/poll.rs new file mode 100644 index 0000000000..eb6b241784 --- /dev/null +++ b/third_party/rust/nix/src/poll.rs @@ -0,0 +1,161 @@ +//! Wait for events to trigger on specific file descriptors +#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))] +use sys::time::TimeSpec; +#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))] +use sys::signal::SigSet; +use std::os::unix::io::RawFd; +use std::fmt; + +use libc; +use Result; +use errno::Errno; + +/// 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 occured can be +/// retrieved by calling [`revents()`](#method.revents) on the `PollFd`. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct PollFd { + pollfd: libc::pollfd, +} + +impl PollFd { + /// Creates a new `PollFd` specifying the events of interest + /// for a given file descriptor. + pub fn new(fd: RawFd, events: EventFlags) -> PollFd { + PollFd { + pollfd: libc::pollfd { + fd: fd, + events: events.bits(), + revents: EventFlags::empty().bits(), + }, + } + } + + /// Returns the events that occured in the last call to `poll` or `ppoll`. + pub fn revents(&self) -> Option<EventFlags> { + EventFlags::from_bits(self.pollfd.revents) + } +} + +impl fmt::Debug for PollFd { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let pfd = self.pollfd; + let mut ds = f.debug_struct("PollFd"); + ds.field("fd", &pfd.fd); + match EventFlags::from_bits(pfd.events) { + None => ds.field("events", &pfd.events), + Some(ef) => ds.field("events", &ef), + }; + match EventFlags::from_bits(pfd.revents) { + None => ds.field("revents", &pfd.revents), + Some(ef) => ds.field("revents", &ef), + }; + ds.finish() + } +} + +libc_bitflags! { + /// These flags define the different events that can be monitored by `poll` and `ppoll` + pub struct EventFlags: 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)](http://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)](http://man7.org/linux/man-pages/man2/ioctl_tty.2.html)). + /// * A cgroup.events file has been modified (see + /// [cgroups(7)](http://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) + POLLRDNORM; + /// Equivalent to [`POLLOUT`](constant.POLLOUT.html) + POLLWRNORM; + /// Priority band data can be read (generally unused on Linux). + POLLRDBAND; + /// Priority data may be written. + 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)`](http://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 negative value +/// in timeout means an infinite timeout. Specifying a timeout of zero +/// causes `poll()` to return immediately, even if no file descriptors are +/// ready. +pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result<libc::c_int> { + let res = unsafe { + libc::poll(fds.as_mut_ptr() as *mut libc::pollfd, + fds.len() as libc::nfds_t, + timeout) + }; + + Errno::result(res) +} + +/// `ppoll()` allows an application to safely wait until either a file +/// descriptor becomes ready or until a signal is caught. +/// ([`poll(2)`](http://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. +/// +#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))] +pub fn ppoll(fds: &mut [PollFd], timeout: TimeSpec, sigmask: SigSet) -> Result<libc::c_int> { + + + let res = unsafe { + libc::ppoll(fds.as_mut_ptr() as *mut libc::pollfd, + fds.len() as libc::nfds_t, + timeout.as_ref(), + sigmask.as_ref()) + }; + Errno::result(res) +} diff --git a/third_party/rust/nix/src/pty.rs b/third_party/rust/nix/src/pty.rs new file mode 100644 index 0000000000..b71718850e --- /dev/null +++ b/third_party/rust/nix/src/pty.rs @@ -0,0 +1,268 @@ +//! Create master and slave virtual pseudo-terminals (PTYs) + +use libc; + +pub use libc::pid_t as SessionId; +pub use libc::winsize as Winsize; + +use std::ffi::CStr; +use std::mem; +use std::os::unix::prelude::*; + +use sys::termios::Termios; +use {Result, Error, fcntl}; +use errno::Errno; + +/// Representation of a master/slave pty pair +/// +/// This is returned by `openpty`. Note that this type does *not* implement `Drop`, so the user +/// must manually close the file descriptors. +#[derive(Clone, Copy)] +#[allow(missing_debug_implementations)] +pub struct OpenptyResult { + /// The master port in a virtual pty pair + pub master: RawFd, + /// The slave port in a virtual pty pair + pub slave: RawFd, +} + + +/// Representation of the Master device in a master/slave pty pair +/// +/// While this datatype is a thin wrapper around `RawFd`, it enforces that the available PTY +/// functions are given the correct file descriptor. Additionally this type implements `Drop`, +/// so that when it's consumed or goes out of scope, it's automatically cleaned-up. +#[derive(Debug)] +pub struct PtyMaster(RawFd); + +impl AsRawFd for PtyMaster { + fn as_raw_fd(&self) -> RawFd { + self.0 + } +} + +impl IntoRawFd for PtyMaster { + fn into_raw_fd(self) -> RawFd { + let fd = self.0; + mem::forget(self); + fd + } +} + +impl Drop for PtyMaster { + fn drop(&mut self) { + // On drop, we ignore errors like EINTR and EIO because there's no clear + // way to handle them, we can't return anything, and (on FreeBSD at + // least) the file descriptor is deallocated in these cases. However, + // we must panic on EBADF, because it is always an error to close an + // invalid file descriptor. That frequently indicates a double-close + // condition, which can cause confusing errors for future I/O + // operations. + let e = ::unistd::close(self.0); + if e == Err(Error::Sys(Errno::EBADF)) { + panic!("Closing an invalid file descriptor!"); + }; + } +} + +/// Grant access to a slave pseudoterminal (see +/// [`grantpt(3)`](http://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(Error::last()); + } + + Ok(()) +} + +/// Open a pseudoterminal device (see +/// [`posix_openpt(3)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html)) +/// +/// `posix_openpt()` returns a file descriptor to an existing unused pseuterminal 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(Error::last()); + } + + Ok(PtyMaster(fd)) +} + +/// Get the name of the slave pseudoterminal (see +/// [`ptsname(3)`](http://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 = libc::ptsname(fd.as_raw_fd()); + if name_ptr.is_null() { + return Err(Error::last()); + } + + let name = CStr::from_ptr(name_ptr); + Ok(name.to_string_lossy().into_owned()) +} + +/// Get the name of the slave pseudoterminal (see +/// [`ptsname(3)`](http://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(any(target_os = "android", target_os = "linux"))] +#[inline] +pub fn ptsname_r(fd: &PtyMaster) -> Result<String> { + let mut name_buf = vec![0u8; 64]; + let name_buf_ptr = name_buf.as_mut_ptr() as *mut libc::c_char; + if unsafe { libc::ptsname_r(fd.as_raw_fd(), name_buf_ptr, name_buf.capacity()) } != 0 { + return Err(Error::last()); + } + + // Find the first null-character terminating this string. This is guaranteed to succeed if the + // return value of `libc::ptsname_r` is 0. + let null_index = name_buf.iter().position(|c| *c == b'\0').unwrap(); + name_buf.truncate(null_index); + + let name = String::from_utf8(name_buf)?; + Ok(name) +} + +/// Unlock a pseudoterminal master/slave pseudoterminal pair (see +/// [`unlockpt(3)`](http://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 +/// pseuoterminal. +#[inline] +pub fn unlockpt(fd: &PtyMaster) -> Result<()> { + if unsafe { libc::unlockpt(fd.as_raw_fd()) } < 0 { + return Err(Error::last()); + } + + Ok(()) +} + + +/// Create a new pseudoterminal, returning the slave and master file descriptors +/// in `OpenptyResult` +/// (see [`openpty`](http://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] +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: libc::c_int = unsafe { mem::uninitialized() }; + let mut master: libc::c_int = unsafe { mem::uninitialized() }; + let ret = { + match (termios.into(), winsize.into()) { + (Some(termios), Some(winsize)) => { + let inner_termios = termios.get_libc_termios(); + unsafe { + libc::openpty( + &mut master, + &mut slave, + ptr::null_mut(), + &*inner_termios as *const libc::termios as *mut _, + winsize as *const Winsize as *mut _, + ) + } + } + (None, Some(winsize)) => { + unsafe { + libc::openpty( + &mut master, + &mut slave, + 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( + &mut master, + &mut slave, + ptr::null_mut(), + &*inner_termios as *const libc::termios as *mut _, + ptr::null_mut(), + ) + } + } + (None, None) => { + unsafe { + libc::openpty( + &mut master, + &mut slave, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ) + } + } + } + }; + + Errno::result(ret)?; + + Ok(OpenptyResult { + master: master, + slave: slave, + }) +} diff --git a/third_party/rust/nix/src/sched.rs b/third_party/rust/nix/src/sched.rs new file mode 100644 index 0000000000..415826bc79 --- /dev/null +++ b/third_party/rust/nix/src/sched.rs @@ -0,0 +1,121 @@ +use std::mem; +// use std::os::unix::io::RawFd; +use std::option::Option; +use libc::{self, c_int, c_void}; +use {Error, Result}; +use errno::Errno; +use ::unistd::Pid; + +// For some functions taking with a parameter of type CloneFlags, +// only a subset of these flags have an effect. +libc_bitflags!{ + pub struct CloneFlags: c_int { + CLONE_VM; + CLONE_FS; + CLONE_FILES; + CLONE_SIGHAND; + CLONE_PTRACE; + CLONE_VFORK; + CLONE_PARENT; + CLONE_THREAD; + CLONE_NEWNS; + CLONE_SYSVSEM; + CLONE_SETTLS; + CLONE_PARENT_SETTID; + CLONE_CHILD_CLEARTID; + CLONE_DETACHED; + CLONE_UNTRACED; + CLONE_CHILD_SETTID; + CLONE_NEWCGROUP; + CLONE_NEWUTS; + CLONE_NEWIPC; + CLONE_NEWUSER; + CLONE_NEWPID; + CLONE_NEWNET; + CLONE_IO; + } +} + +pub type CloneCb<'a> = Box<FnMut() -> isize + 'a>; + +#[repr(C)] +#[derive(Clone, Copy)] +#[allow(missing_debug_implementations)] +pub struct CpuSet { + cpu_set: libc::cpu_set_t, +} + +impl CpuSet { + pub fn new() -> CpuSet { + CpuSet { cpu_set: unsafe { mem::zeroed() } } + } + + pub fn is_set(&self, field: usize) -> Result<bool> { + if field >= 8 * mem::size_of::<libc::cpu_set_t>() { + Err(Error::Sys(Errno::EINVAL)) + } else { + Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) }) + } + } + + pub fn set(&mut self, field: usize) -> Result<()> { + if field >= 8 * mem::size_of::<libc::cpu_set_t>() { + Err(Error::Sys(Errno::EINVAL)) + } else { + Ok(unsafe { libc::CPU_SET(field, &mut self.cpu_set) }) + } + } + + pub fn unset(&mut self, field: usize) -> Result<()> { + if field >= 8 * mem::size_of::<libc::cpu_set_t>() { + Err(Error::Sys(Errno::EINVAL)) + } else { + Ok(unsafe { libc::CPU_CLR(field, &mut self.cpu_set) }) + } + } +} + +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) +} + +pub 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 res = unsafe { + let combined = flags.bits() | signal.unwrap_or(0); + let ptr = stack.as_mut_ptr().offset(stack.len() as isize); + let ptr_aligned = ptr.offset((ptr as usize % 16) as isize * -1); + libc::clone(mem::transmute(callback as extern "C" fn(*mut Box<::std::ops::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) +} + +pub fn unshare(flags: CloneFlags) -> Result<()> { + let res = unsafe { libc::unshare(flags.bits()) }; + + Errno::result(res).map(drop) +} + +// pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> { +// let res = unsafe { libc::setns(fd, nstype.bits()) }; + +// Errno::result(res).map(drop) +// } diff --git a/third_party/rust/nix/src/sys/aio.rs b/third_party/rust/nix/src/sys/aio.rs new file mode 100644 index 0000000000..c54c2e316e --- /dev/null +++ b/third_party/rust/nix/src/sys/aio.rs @@ -0,0 +1,1286 @@ +// vim: tw=80 +//! POSIX Asynchronous I/O +//! +//! The POSIX AIO interface is used for asynchronous I/O on files and disk-like +//! devices. It supports [`read`](struct.AioCb.html#method.read), +//! [`write`](struct.AioCb.html#method.write), and +//! [`fsync`](struct.AioCb.html#method.fsync) operations. Completion +//! notifications can optionally be delivered via +//! [signals](../signal/enum.SigevNotify.html#variant.SigevSignal), via the +//! [`aio_suspend`](fn.aio_suspend.html) function, or via polling. Some +//! platforms support other completion +//! notifications, such as +//! [kevent](../signal/enum.SigevNotify.html#variant.SigevKevent). +//! +//! Multiple operations may be submitted in a batch with +//! [`lio_listio`](fn.lio_listio.html), though the standard does not guarantee +//! that they will be executed atomically. +//! +//! Outstanding operations may be cancelled with +//! [`cancel`](struct.AioCb.html#method.cancel) or +//! [`aio_cancel_all`](fn.aio_cancel_all.html), though the operating system may +//! not support this for all filesystems and devices. + +use {Error, Result}; +use errno::Errno; +use std::os::unix::io::RawFd; +use libc::{c_void, off_t, size_t}; +use libc; +use std::borrow::{Borrow, BorrowMut}; +use std::fmt; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::mem; +use std::ptr::{null, null_mut}; +use sys::signal::*; +use std::thread; +use sys::time::TimeSpec; + +libc_enum! { + /// Mode for `AioCb::fsync`. Controls whether only data or both data and + /// metadata are synced. + #[repr(i32)] + pub enum AioFsyncMode { + /// do it like `fsync` + O_SYNC, + /// on supported operating systems only, do it like `fdatasync` + #[cfg(any(target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + O_DSYNC + } +} + +libc_enum! { + /// When used with [`lio_listio`](fn.lio_listio.html), determines whether a + /// given `aiocb` should be used for a read operation, a write operation, or + /// ignored. Has no effect for any other aio functions. + #[repr(i32)] + pub enum LioOpcode { + LIO_NOP, + LIO_WRITE, + LIO_READ, + } +} + +libc_enum! { + /// Mode for [`lio_listio`](fn.lio_listio.html) + #[repr(i32)] + pub enum LioMode { + /// Requests that [`lio_listio`](fn.lio_listio.html) block until all + /// requested operations have been completed + LIO_WAIT, + /// Requests that [`lio_listio`](fn.lio_listio.html) return immediately + LIO_NOWAIT, + } +} + +/// Return values for [`AioCb::cancel`](struct.AioCb.html#method.cancel) and +/// [`aio_cancel_all`](fn.aio_cancel_all.html) +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum AioCancelStat { + /// All outstanding requests were canceled + AioCanceled = libc::AIO_CANCELED, + /// Some requests were not canceled. Their status should be checked with + /// `AioCb::error` + AioNotCanceled = libc::AIO_NOTCANCELED, + /// All of the requests have already finished + AioAllDone = libc::AIO_ALLDONE, +} + +/// Owns (uniquely or shared) a memory buffer to keep it from `Drop`ing while +/// the kernel has a pointer to it. +pub enum Buffer<'a> { + /// No buffer to own. + /// + /// Used for operations like `aio_fsync` that have no data, or for unsafe + /// operations that work with raw pointers. + None, + /// Keeps a reference to a slice + Phantom(PhantomData<&'a mut [u8]>), + /// Generic thing that keeps a buffer from dropping + BoxedSlice(Box<Borrow<[u8]>>), + /// Generic thing that keeps a mutable buffer from dropping + BoxedMutSlice(Box<BorrowMut<[u8]>>), +} + +impl<'a> Debug for Buffer<'a> { + // Note: someday it may be possible to Derive Debug for a trait object, but + // not today. + // https://github.com/rust-lang/rust/issues/1563 + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + Buffer::None => write!(fmt, "None"), + Buffer::Phantom(p) => p.fmt(fmt), + Buffer::BoxedSlice(ref bs) => { + let borrowed : &Borrow<[u8]> = bs.borrow(); + write!(fmt, "BoxedSlice({:?})", + borrowed as *const Borrow<[u8]>) + }, + Buffer::BoxedMutSlice(ref bms) => { + let borrowed : &BorrowMut<[u8]> = bms.borrow(); + write!(fmt, "BoxedMutSlice({:?})", + borrowed as *const BorrowMut<[u8]>) + } + } + } +} + +/// AIO Control Block. +/// +/// The basic structure used by all aio functions. Each `AioCb` represents one +/// I/O request. +pub struct AioCb<'a> { + aiocb: libc::aiocb, + /// Tracks whether the buffer pointed to by `libc::aiocb.aio_buf` is mutable + mutable: bool, + /// Could this `AioCb` potentially have any in-kernel state? + in_progress: bool, + /// Optionally keeps a reference to the data. + /// + /// Used to keep buffers from `Drop`'ing, and may be returned once the + /// `AioCb` is completed by [`buffer`](#method.buffer). + buffer: Buffer<'a> +} + +impl<'a> AioCb<'a> { + /// Remove the inner `Buffer` and return it + /// + /// It is an error to call this method while the `AioCb` is still in + /// progress. + pub fn buffer(&mut self) -> Buffer<'a> { + assert!(!self.in_progress); + let mut x = Buffer::None; + mem::swap(&mut self.buffer, &mut x); + x + } + + /// Remove the inner boxed slice, if any, and return it. + /// + /// The returned value will be the argument that was passed to + /// `from_boxed_slice` when this `AioCb` was created. + /// + /// It is an error to call this method while the `AioCb` is still in + /// progress. + pub fn boxed_slice(&mut self) -> Option<Box<Borrow<[u8]>>> { + assert!(!self.in_progress, "Can't remove the buffer from an AioCb that's still in-progress. Did you forget to call aio_return?"); + if let Buffer::BoxedSlice(_) = self.buffer { + let mut oldbuffer = Buffer::None; + mem::swap(&mut self.buffer, &mut oldbuffer); + if let Buffer::BoxedSlice(inner) = oldbuffer { + Some(inner) + } else { + unreachable!(); + } + } else { + None + } + } + + /// Remove the inner boxed mutable slice, if any, and return it. + /// + /// The returned value will be the argument that was passed to + /// `from_boxed_mut_slice` when this `AioCb` was created. + /// + /// It is an error to call this method while the `AioCb` is still in + /// progress. + pub fn boxed_mut_slice(&mut self) -> Option<Box<BorrowMut<[u8]>>> { + assert!(!self.in_progress, "Can't remove the buffer from an AioCb that's still in-progress. Did you forget to call aio_return?"); + if let Buffer::BoxedMutSlice(_) = self.buffer { + let mut oldbuffer = Buffer::None; + mem::swap(&mut self.buffer, &mut oldbuffer); + if let Buffer::BoxedMutSlice(inner) = oldbuffer { + Some(inner) + } else { + unreachable!(); + } + } else { + None + } + } + + /// Returns the underlying file descriptor associated with the `AioCb` + pub fn fd(&self) -> RawFd { + self.aiocb.aio_fildes + } + + /// Constructs a new `AioCb` with no associated buffer. + /// + /// The resulting `AioCb` structure is suitable for use with `AioCb::fsync`. + /// + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio`. + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// + /// # Examples + /// + /// Create an `AioCb` from a raw file descriptor and use it for an + /// [`fsync`](#method.fsync) operation. + /// + /// ``` + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify::SigevNone; + /// # use std::{thread, time}; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// let f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_fd( f.as_raw_fd(), 0, SigevNone); + /// aiocb.fsync(AioFsyncMode::O_SYNC).expect("aio_fsync failed early"); + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// aiocb.aio_return().expect("aio_fsync failed late"); + /// # } + /// ``` + pub fn from_fd(fd: RawFd, prio: libc::c_int, + sigev_notify: SigevNotify) -> AioCb<'a> { + let mut a = AioCb::common_init(fd, prio, sigev_notify); + a.aio_offset = 0; + a.aio_nbytes = 0; + a.aio_buf = null_mut(); + + AioCb { + aiocb: a, + mutable: false, + in_progress: false, + buffer: Buffer::None + } + } + + /// Constructs a new `AioCb` from a mutable slice. + /// + /// The resulting `AioCb` will be suitable for both read and write + /// operations, but only if the borrow checker can guarantee that the slice + /// will outlive the `AioCb`. That will usually be the case if the `AioCb` + /// is stack-allocated. If the borrow checker gives you trouble, try using + /// [`from_boxed_mut_slice`](#method.from_boxed_mut_slice) instead. + /// + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `offs`: File offset + /// * `buf`: A memory buffer + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio` + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// * `opcode`: This field is only used for `lio_listio`. It + /// determines which operation to use for this individual + /// aiocb + /// + /// # Examples + /// + /// Create an `AioCb` from a mutable slice and read into it. + /// + /// ``` + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::io::Write; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// const INITIAL: &[u8] = b"abcdef123456"; + /// const LEN: usize = 4; + /// let mut rbuf = vec![0; LEN]; + /// let mut f = tempfile().unwrap(); + /// f.write_all(INITIAL).unwrap(); + /// { + /// let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(), + /// 2, //offset + /// &mut rbuf, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.read().unwrap(); + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// assert_eq!(aiocb.aio_return().unwrap() as usize, LEN); + /// } + /// assert_eq!(rbuf, b"cdef"); + /// # } + /// ``` + pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8], + prio: libc::c_int, sigev_notify: SigevNotify, + opcode: LioOpcode) -> AioCb<'a> { + let mut a = AioCb::common_init(fd, prio, sigev_notify); + a.aio_offset = offs; + a.aio_nbytes = buf.len() as size_t; + a.aio_buf = buf.as_ptr() as *mut c_void; + a.aio_lio_opcode = opcode as libc::c_int; + + AioCb { + aiocb: a, + mutable: true, + in_progress: false, + buffer: Buffer::Phantom(PhantomData), + } + } + + /// The safest and most flexible way to create an `AioCb`. + /// + /// Unlike [`from_slice`], this method returns a structure suitable for + /// placement on the heap. It may be used for write operations, but not + /// read operations. Unlike `from_ptr`, this method will ensure that the + /// buffer doesn't `drop` while the kernel is still processing it. Any + /// object that can be borrowed as a boxed slice will work. + /// + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `offs`: File offset + /// * `buf`: A boxed slice-like object + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio` + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// * `opcode`: This field is only used for `lio_listio`. It + /// determines which operation to use for this individual + /// aiocb + /// + /// # Examples + /// + /// Create an `AioCb` from a Vector and use it for writing + /// + /// ``` + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::io::Write; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// let wbuf = Box::new(Vec::from("CDEF")); + /// let expected_len = wbuf.len(); + /// let mut f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(), + /// 2, //offset + /// wbuf, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.write().unwrap(); + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// assert_eq!(aiocb.aio_return().unwrap() as usize, expected_len); + /// # } + /// ``` + /// + /// Create an `AioCb` from a `Bytes` object + /// + /// ``` + /// # extern crate bytes; + /// # extern crate tempfile; + /// # extern crate nix; + /// # use bytes::Bytes; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// let wbuf = Box::new(Bytes::from(&b"CDEF"[..])); + /// let mut f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(), + /// 2, //offset + /// wbuf, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// # } + /// ``` + /// + /// If a library needs to work with buffers that aren't `Box`ed, it can + /// create a `Box`ed container for use with this method. Here's an example + /// using an un`Box`ed `Bytes` object. + /// + /// ``` + /// # extern crate bytes; + /// # extern crate tempfile; + /// # extern crate nix; + /// # use bytes::Bytes; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::borrow::Borrow; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// struct BytesContainer(Bytes); + /// impl Borrow<[u8]> for BytesContainer { + /// fn borrow(&self) -> &[u8] { + /// self.0.as_ref() + /// } + /// } + /// fn main() { + /// let wbuf = Bytes::from(&b"CDEF"[..]); + /// let boxed_wbuf = Box::new(BytesContainer(wbuf)); + /// let mut f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(), + /// 2, //offset + /// boxed_wbuf, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// } + /// ``` + /// + /// [`from_slice`]: #method.from_slice + pub fn from_boxed_slice(fd: RawFd, offs: off_t, buf: Box<Borrow<[u8]>>, + prio: libc::c_int, sigev_notify: SigevNotify, + opcode: LioOpcode) -> AioCb<'a> { + let mut a = AioCb::common_init(fd, prio, sigev_notify); + { + let borrowed : &Borrow<[u8]> = buf.borrow(); + let slice : &[u8] = borrowed.borrow(); + a.aio_nbytes = slice.len() as size_t; + a.aio_buf = slice.as_ptr() as *mut c_void; + } + a.aio_offset = offs; + a.aio_lio_opcode = opcode as libc::c_int; + + AioCb { + aiocb: a, + mutable: false, + in_progress: false, + buffer: Buffer::BoxedSlice(buf), + } + } + + /// The safest and most flexible way to create an `AioCb` for reading. + /// + /// Like [`from_boxed_slice`], but the slice is a mutable one. More + /// flexible than [`from_mut_slice`], because a wide range of objects can be + /// used. + /// + /// # Examples + /// + /// Create an `AioCb` from a Vector and use it for reading + /// + /// ``` + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::io::Write; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// const INITIAL: &[u8] = b"abcdef123456"; + /// const LEN: usize = 4; + /// let rbuf = Box::new(vec![0; LEN]); + /// let mut f = tempfile().unwrap(); + /// f.write_all(INITIAL).unwrap(); + /// let mut aiocb = AioCb::from_boxed_mut_slice( f.as_raw_fd(), + /// 2, //offset + /// rbuf, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.read().unwrap(); + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// assert_eq!(aiocb.aio_return().unwrap() as usize, LEN); + /// let mut buffer = aiocb.boxed_mut_slice().unwrap(); + /// const EXPECT: &[u8] = b"cdef"; + /// assert_eq!(buffer.borrow_mut(), EXPECT); + /// # } + /// ``` + /// + /// [`from_boxed_slice`]: #method.from_boxed_slice + /// [`from_mut_slice`]: #method.from_mut_slice + pub fn from_boxed_mut_slice(fd: RawFd, offs: off_t, + mut buf: Box<BorrowMut<[u8]>>, + prio: libc::c_int, sigev_notify: SigevNotify, + opcode: LioOpcode) -> AioCb<'a> { + let mut a = AioCb::common_init(fd, prio, sigev_notify); + { + let borrowed : &mut BorrowMut<[u8]> = buf.borrow_mut(); + let slice : &mut [u8] = borrowed.borrow_mut(); + a.aio_nbytes = slice.len() as size_t; + a.aio_buf = slice.as_mut_ptr() as *mut c_void; + } + a.aio_offset = offs; + a.aio_lio_opcode = opcode as libc::c_int; + + AioCb { + aiocb: a, + mutable: true, + in_progress: false, + buffer: Buffer::BoxedMutSlice(buf), + } + } + + /// Constructs a new `AioCb` from a mutable raw pointer + /// + /// Unlike `from_mut_slice`, this method returns a structure suitable for + /// placement on the heap. It may be used for both reads and writes. Due + /// to its unsafety, this method is not recommended. It is most useful when + /// heap allocation is required but for some reason the data cannot be + /// wrapped in a `struct` that implements `BorrowMut<[u8]>` + /// + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `offs`: File offset + /// * `buf`: Pointer to the memory buffer + /// * `len`: Length of the buffer pointed to by `buf` + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio` + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// * `opcode`: This field is only used for `lio_listio`. It + /// determines which operation to use for this individual + /// aiocb + /// + /// # Safety + /// + /// The caller must ensure that the storage pointed to by `buf` outlives the + /// `AioCb`. The lifetime checker can't help here. + pub unsafe fn from_mut_ptr(fd: RawFd, offs: off_t, + buf: *mut c_void, len: usize, + prio: libc::c_int, sigev_notify: SigevNotify, + opcode: LioOpcode) -> AioCb<'a> { + let mut a = AioCb::common_init(fd, prio, sigev_notify); + a.aio_offset = offs; + a.aio_nbytes = len; + a.aio_buf = buf; + a.aio_lio_opcode = opcode as libc::c_int; + + AioCb { + aiocb: a, + mutable: true, + in_progress: false, + buffer: Buffer::None + } + } + + /// Constructs a new `AioCb` from a raw pointer. + /// + /// Unlike `from_slice`, this method returns a structure suitable for + /// placement on the heap. Due to its unsafety, this method is not + /// recommended. It is most useful when heap allocation is required but for + /// some reason the data cannot be wrapped in a `struct` that implements + /// `Borrow<[u8]>` + /// + /// # Parameters + /// + /// * `fd`: File descriptor. Required for all aio functions. + /// * `offs`: File offset + /// * `buf`: Pointer to the memory buffer + /// * `len`: Length of the buffer pointed to by `buf` + /// * `prio`: If POSIX Prioritized IO is supported, then the + /// operation will be prioritized at the process's + /// priority level minus `prio` + /// * `sigev_notify`: Determines how you will be notified of event + /// completion. + /// * `opcode`: This field is only used for `lio_listio`. It + /// determines which operation to use for this individual + /// aiocb + /// + /// # Safety + /// + /// The caller must ensure that the storage pointed to by `buf` outlives the + /// `AioCb`. The lifetime checker can't help here. + pub unsafe fn from_ptr(fd: RawFd, offs: off_t, + buf: *const c_void, len: usize, + prio: libc::c_int, sigev_notify: SigevNotify, + opcode: LioOpcode) -> AioCb<'a> { + let mut a = AioCb::common_init(fd, prio, sigev_notify); + a.aio_offset = offs; + a.aio_nbytes = len; + // casting a const ptr to a mutable ptr here is ok, because we set the + // AioCb's mutable field to false + a.aio_buf = buf as *mut c_void; + a.aio_lio_opcode = opcode as libc::c_int; + + AioCb { + aiocb: a, + mutable: false, + in_progress: false, + buffer: Buffer::None + } + } + + /// Like `from_mut_slice`, but works on constant slices rather than + /// mutable slices. + /// + /// An `AioCb` created this way cannot be used with `read`, and its + /// `LioOpcode` cannot be set to `LIO_READ`. This method is useful when + /// writing a const buffer with `AioCb::write`, since `from_mut_slice` can't + /// work with const buffers. + /// + /// # Examples + /// + /// Construct an `AioCb` from a slice and use it for writing. + /// + /// ``` + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// const WBUF: &[u8] = b"abcdef123456"; + /// let mut f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + /// 2, //offset + /// WBUF, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.write().unwrap(); + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len()); + /// # } + /// ``` + // Note: another solution to the problem of writing const buffers would be + // to genericize AioCb for both &mut [u8] and &[u8] buffers. AioCb::read + // could take the former and AioCb::write could take the latter. However, + // then lio_listio wouldn't work, because that function needs a slice of + // AioCb, and they must all be of the same type. + pub fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8], + prio: libc::c_int, sigev_notify: SigevNotify, + opcode: LioOpcode) -> AioCb { + let mut a = AioCb::common_init(fd, prio, sigev_notify); + a.aio_offset = offs; + a.aio_nbytes = buf.len() as size_t; + // casting an immutable buffer to a mutable pointer looks unsafe, + // but technically its only unsafe to dereference it, not to create + // it. + a.aio_buf = buf.as_ptr() as *mut c_void; + assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer"); + a.aio_lio_opcode = opcode as libc::c_int; + + AioCb { + aiocb: a, + mutable: false, + in_progress: false, + buffer: Buffer::None, + } + } + + fn common_init(fd: RawFd, prio: libc::c_int, + sigev_notify: SigevNotify) -> libc::aiocb { + // Use mem::zeroed instead of explicitly zeroing each field, because the + // number and name of reserved fields is OS-dependent. On some OSes, + // some reserved fields are used the kernel for state, and must be + // explicitly zeroed when allocated. + let mut a = unsafe { mem::zeroed::<libc::aiocb>()}; + a.aio_fildes = fd; + a.aio_reqprio = prio; + a.aio_sigevent = SigEvent::new(sigev_notify).sigevent(); + a + } + + /// Update the notification settings for an existing `aiocb` + pub fn set_sigev_notify(&mut self, sigev_notify: SigevNotify) { + self.aiocb.aio_sigevent = SigEvent::new(sigev_notify).sigevent(); + } + + /// Cancels an outstanding AIO request. + /// + /// The operating system is not required to implement cancellation for all + /// file and device types. Even if it does, there is no guarantee that the + /// operation has not already completed. So the caller must check the + /// result and handle operations that were not canceled or that have already + /// completed. + /// + /// # Examples + /// + /// Cancel an outstanding aio operation. Note that we must still call + /// `aio_return` to free resources, even though we don't care about the + /// result. + /// + /// ``` + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::io::Write; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// let wbuf = b"CDEF"; + /// let mut f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + /// 2, //offset + /// &wbuf[..], + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.write().unwrap(); + /// let cs = aiocb.cancel().unwrap(); + /// if cs == AioCancelStat::AioNotCanceled { + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// } + /// // Must call `aio_return`, but ignore the result + /// let _ = aiocb.aio_return(); + /// # } + /// ``` + /// + /// # References + /// + /// [aio_cancel](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html) + pub fn cancel(&mut self) -> Result<AioCancelStat> { + match unsafe { libc::aio_cancel(self.aiocb.aio_fildes, &mut self.aiocb) } { + libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled), + libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled), + libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone), + -1 => Err(Error::last()), + _ => panic!("unknown aio_cancel return value") + } + } + + /// Retrieve error status of an asynchronous operation. + /// + /// If the request has not yet completed, returns `EINPROGRESS`. Otherwise, + /// returns `Ok` or any other error. + /// + /// # Examples + /// + /// Issue an aio operation and use `error` to poll for completion. Polling + /// is an alternative to `aio_suspend`, used by most of the other examples. + /// + /// ``` + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::errno::Errno; + /// # use nix::Error; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::{thread, time}; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// const WBUF: &[u8] = b"abcdef123456"; + /// let mut f = tempfile().unwrap(); + /// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + /// 2, //offset + /// WBUF, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_NOP); + /// aiocb.write().unwrap(); + /// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { + /// thread::sleep(time::Duration::from_millis(10)); + /// } + /// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len()); + /// # } + /// ``` + /// + /// # References + /// + /// [aio_error](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_error.html) + pub fn error(&mut self) -> Result<()> { + match unsafe { libc::aio_error(&mut self.aiocb as *mut libc::aiocb) } { + 0 => Ok(()), + num if num > 0 => Err(Error::from_errno(Errno::from_i32(num))), + -1 => Err(Error::last()), + num => panic!("unknown aio_error return value {:?}", num) + } + } + + /// An asynchronous version of `fsync(2)`. + /// + /// # References + /// + /// [aio_fsync](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_fsync.html) + pub fn fsync(&mut self, mode: AioFsyncMode) -> Result<()> { + let p: *mut libc::aiocb = &mut self.aiocb; + Errno::result(unsafe { + libc::aio_fsync(mode as libc::c_int, p) + }).map(|_| { + self.in_progress = true; + }) + } + + /// Returns the `aiocb`'s `LioOpcode` field + /// + /// If the value cannot be represented as an `LioOpcode`, returns `None` + /// instead. + pub fn lio_opcode(&self) -> Option<LioOpcode> { + match self.aiocb.aio_lio_opcode { + libc::LIO_READ => Some(LioOpcode::LIO_READ), + libc::LIO_WRITE => Some(LioOpcode::LIO_WRITE), + libc::LIO_NOP => Some(LioOpcode::LIO_NOP), + _ => None + } + } + + /// Returns the requested length of the aio operation in bytes + /// + /// This method returns the *requested* length of the operation. To get the + /// number of bytes actually read or written by a completed operation, use + /// `aio_return` instead. + pub fn nbytes(&self) -> usize { + self.aiocb.aio_nbytes + } + + /// Returns the file offset stored in the `AioCb` + pub fn offset(&self) -> off_t { + self.aiocb.aio_offset + } + + /// Returns the priority of the `AioCb` + pub fn priority(&self) -> libc::c_int { + self.aiocb.aio_reqprio + } + + /// Asynchronously reads from a file descriptor into a buffer + /// + /// # References + /// + /// [aio_read](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_read.html) + pub fn read(&mut self) -> Result<()> { + assert!(self.mutable, "Can't read into an immutable buffer"); + let p: *mut libc::aiocb = &mut self.aiocb; + Errno::result(unsafe { + libc::aio_read(p) + }).map(|_| { + self.in_progress = true; + }) + } + + /// Returns the `SigEvent` stored in the `AioCb` + pub fn sigevent(&self) -> SigEvent { + SigEvent::from(&self.aiocb.aio_sigevent) + } + + /// Retrieve return status of an asynchronous operation. + /// + /// Should only be called once for each `AioCb`, after `AioCb::error` + /// indicates that it has completed. The result is the same as for the + /// synchronous `read(2)`, `write(2)`, of `fsync(2)` functions. + /// + /// # References + /// + /// [aio_return](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_return.html) + // Note: this should be just `return`, but that's a reserved word + pub fn aio_return(&mut self) -> Result<isize> { + let p: *mut libc::aiocb = &mut self.aiocb; + self.in_progress = false; + Errno::result(unsafe { libc::aio_return(p) }) + } + + /// Asynchronously writes from a buffer to a file descriptor + /// + /// # References + /// + /// [aio_write](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_write.html) + pub fn write(&mut self) -> Result<()> { + let p: *mut libc::aiocb = &mut self.aiocb; + Errno::result(unsafe { + libc::aio_write(p) + }).map(|_| { + self.in_progress = true; + }) + } + +} + +/// Cancels outstanding AIO requests for a given file descriptor. +/// +/// # Examples +/// +/// Issue an aio operation, then cancel all outstanding operations on that file +/// descriptor. +/// +/// ``` +/// # extern crate tempfile; +/// # extern crate nix; +/// # use nix::errno::Errno; +/// # use nix::Error; +/// # use nix::sys::aio::*; +/// # use nix::sys::signal::SigevNotify; +/// # use std::{thread, time}; +/// # use std::io::Write; +/// # use std::os::unix::io::AsRawFd; +/// # use tempfile::tempfile; +/// # fn main() { +/// let wbuf = b"CDEF"; +/// let mut f = tempfile().unwrap(); +/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), +/// 2, //offset +/// &wbuf[..], +/// 0, //priority +/// SigevNotify::SigevNone, +/// LioOpcode::LIO_NOP); +/// aiocb.write().unwrap(); +/// let cs = aio_cancel_all(f.as_raw_fd()).unwrap(); +/// if cs == AioCancelStat::AioNotCanceled { +/// while (aiocb.error() == Err(Error::from(Errno::EINPROGRESS))) { +/// thread::sleep(time::Duration::from_millis(10)); +/// } +/// } +/// // Must call `aio_return`, but ignore the result +/// let _ = aiocb.aio_return(); +/// # } +/// ``` +/// +/// # References +/// +/// [`aio_cancel`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html) +pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> { + match unsafe { libc::aio_cancel(fd, null_mut()) } { + libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled), + libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled), + libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone), + -1 => Err(Error::last()), + _ => panic!("unknown aio_cancel return value") + } +} + +/// Suspends the calling process until at least one of the specified `AioCb`s +/// has completed, a signal is delivered, or the timeout has passed. +/// +/// If `timeout` is `None`, `aio_suspend` will block indefinitely. +/// +/// # Examples +/// +/// Use `aio_suspend` to block until an aio operation completes. +/// +// Disable doctest due to a known bug in FreeBSD's 32-bit emulation. The fix +// will be included in release 11.2. +// FIXME reenable the doc test when the CI machine gets upgraded to that release. +// https://svnweb.freebsd.org/base?view=revision&revision=325018 +/// ```no_run +/// # extern crate tempfile; +/// # extern crate nix; +/// # use nix::sys::aio::*; +/// # use nix::sys::signal::SigevNotify; +/// # use std::os::unix::io::AsRawFd; +/// # use tempfile::tempfile; +/// # fn main() { +/// const WBUF: &[u8] = b"abcdef123456"; +/// let mut f = tempfile().unwrap(); +/// let mut aiocb = AioCb::from_slice( f.as_raw_fd(), +/// 2, //offset +/// WBUF, +/// 0, //priority +/// SigevNotify::SigevNone, +/// LioOpcode::LIO_NOP); +/// aiocb.write().unwrap(); +/// aio_suspend(&[&aiocb], None).expect("aio_suspend failed"); +/// assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len()); +/// # } +/// ``` +/// # References +/// +/// [`aio_suspend`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_suspend.html) +pub fn aio_suspend(list: &[&AioCb], timeout: Option<TimeSpec>) -> Result<()> { + let plist = list as *const [&AioCb] as *const [*const libc::aiocb]; + let p = plist as *const *const libc::aiocb; + let timep = match timeout { + None => null::<libc::timespec>(), + Some(x) => x.as_ref() as *const libc::timespec + }; + Errno::result(unsafe { + libc::aio_suspend(p, list.len() as i32, timep) + }).map(drop) +} + +impl<'a> Debug for AioCb<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("AioCb") + .field("aio_fildes", &self.aiocb.aio_fildes) + .field("aio_offset", &self.aiocb.aio_offset) + .field("aio_buf", &self.aiocb.aio_buf) + .field("aio_nbytes", &self.aiocb.aio_nbytes) + .field("aio_lio_opcode", &self.aiocb.aio_lio_opcode) + .field("aio_reqprio", &self.aiocb.aio_reqprio) + .field("aio_sigevent", &SigEvent::from(&self.aiocb.aio_sigevent)) + .field("mutable", &self.mutable) + .field("in_progress", &self.in_progress) + .finish() + } +} + +impl<'a> Drop for AioCb<'a> { + /// If the `AioCb` has no remaining state in the kernel, just drop it. + /// Otherwise, dropping constitutes a resource leak, which is an error + fn drop(&mut self) { + assert!(thread::panicking() || !self.in_progress, + "Dropped an in-progress AioCb"); + } +} + +/// LIO Control Block. +/// +/// The basic structure used to issue multiple AIO operations simultaneously. +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +pub struct LioCb<'a> { + /// A collection of [`AioCb`]s. All of these will be issued simultaneously + /// by the [`listio`] method. + /// + /// [`AioCb`]: struct.AioCb.html + /// [`listio`]: #method.listio + pub aiocbs: Vec<AioCb<'a>>, + + /// The actual list passed to `libc::lio_listio`. + /// + /// It must live for as long as any of the operations are still being + /// processesed, because the aio subsystem uses its address as a unique + /// identifier. + list: Vec<*mut libc::aiocb>, + + /// A partial set of results. This field will get populated by + /// `listio_resubmit` when an `LioCb` is resubmitted after an error + results: Vec<Option<Result<isize>>> +} + +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +impl<'a> LioCb<'a> { + /// Initialize an empty `LioCb` + pub fn with_capacity(capacity: usize) -> LioCb<'a> { + LioCb { + aiocbs: Vec::with_capacity(capacity), + list: Vec::with_capacity(capacity), + results: Vec::with_capacity(capacity) + } + } + + /// Submits multiple asynchronous I/O requests with a single system call. + /// + /// They are not guaranteed to complete atomically, and the order in which + /// the requests are carried out is not specified. Reads, writes, and + /// fsyncs may be freely mixed. + /// + /// This function is useful for reducing the context-switch overhead of + /// submitting many AIO operations. It can also be used with + /// `LioMode::LIO_WAIT` to block on the result of several independent + /// operations. Used that way, it is often useful in programs that + /// otherwise make little use of AIO. + /// + /// # Examples + /// + /// Use `listio` to submit an aio operation and wait for its completion. In + /// this case, there is no need to use [`aio_suspend`] to wait or + /// [`AioCb::error`] to poll. + /// + /// ``` + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::os::unix::io::AsRawFd; + /// # use tempfile::tempfile; + /// # fn main() { + /// const WBUF: &[u8] = b"abcdef123456"; + /// let mut f = tempfile().unwrap(); + /// let mut liocb = LioCb::with_capacity(1); + /// liocb.aiocbs.push(AioCb::from_slice( f.as_raw_fd(), + /// 2, //offset + /// WBUF, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_WRITE)); + /// liocb.listio(LioMode::LIO_WAIT, + /// SigevNotify::SigevNone).unwrap(); + /// assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len()); + /// # } + /// ``` + /// + /// # References + /// + /// [`lio_listio`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html) + /// + /// [`aio_suspend`]: fn.aio_suspend.html + /// [`AioCb::error`]: struct.AioCb.html#method.error + pub fn listio(&mut self, mode: LioMode, + sigev_notify: SigevNotify) -> Result<()> { + let sigev = SigEvent::new(sigev_notify); + let sigevp = &mut sigev.sigevent() as *mut libc::sigevent; + self.list.clear(); + for a in &mut self.aiocbs { + a.in_progress = true; + self.list.push(a as *mut AioCb<'a> + as *mut libc::aiocb); + } + let p = self.list.as_ptr(); + Errno::result(unsafe { + libc::lio_listio(mode as i32, p, self.list.len() as i32, sigevp) + }).map(drop) + } + + /// Resubmits any incomplete operations with [`lio_listio`]. + /// + /// Sometimes, due to system resource limitations, an `lio_listio` call will + /// return `EIO`, or `EAGAIN`. Or, if a signal is received, it may return + /// `EINTR`. In any of these cases, only a subset of its constituent + /// operations will actually have been initiated. `listio_resubmit` will + /// resubmit any operations that are still uninitiated. + /// + /// After calling `listio_resubmit`, results should be collected by + /// [`LioCb::aio_return`]. + /// + /// # Examples + /// ```no_run + /// # extern crate tempfile; + /// # extern crate nix; + /// # use nix::Error; + /// # use nix::errno::Errno; + /// # use nix::sys::aio::*; + /// # use nix::sys::signal::SigevNotify; + /// # use std::os::unix::io::AsRawFd; + /// # use std::{thread, time}; + /// # use tempfile::tempfile; + /// # fn main() { + /// const WBUF: &[u8] = b"abcdef123456"; + /// let mut f = tempfile().unwrap(); + /// let mut liocb = LioCb::with_capacity(1); + /// liocb.aiocbs.push(AioCb::from_slice( f.as_raw_fd(), + /// 2, //offset + /// WBUF, + /// 0, //priority + /// SigevNotify::SigevNone, + /// LioOpcode::LIO_WRITE)); + /// let mut err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone); + /// while err == Err(Error::Sys(Errno::EIO)) || + /// err == Err(Error::Sys(Errno::EAGAIN)) { + /// thread::sleep(time::Duration::from_millis(10)); + /// err = liocb.listio_resubmit(LioMode::LIO_WAIT, SigevNotify::SigevNone); + /// } + /// assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len()); + /// # } + /// ``` + /// + /// # References + /// + /// [`lio_listio`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html) + /// + /// [`lio_listio`]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html + /// [`LioCb::aio_return`]: struct.LioCb.html#method.aio_return + // Note: the addresses of any EINPROGRESS or EOK aiocbs _must_ not be + // changed by this method, because the kernel relies on their addresses + // being stable. + // Note: aiocbs that are Ok(()) must be finalized by aio_return, or else the + // sigev_notify will immediately refire. + pub fn listio_resubmit(&mut self, mode:LioMode, + sigev_notify: SigevNotify) -> Result<()> { + let sigev = SigEvent::new(sigev_notify); + let sigevp = &mut sigev.sigevent() as *mut libc::sigevent; + self.list.clear(); + + while self.results.len() < self.aiocbs.len() { + self.results.push(None); + } + + for (i, a) in self.aiocbs.iter_mut().enumerate() { + if self.results[i].is_some() { + // Already collected final status for this operation + continue; + } + match a.error() { + Ok(()) => { + // aiocb is complete; collect its status and don't resubmit + self.results[i] = Some(a.aio_return()); + }, + Err(Error::Sys(Errno::EAGAIN)) => { + self.list.push(a as *mut AioCb<'a> as *mut libc::aiocb); + }, + Err(Error::Sys(Errno::EINPROGRESS)) => { + // aiocb is was successfully queued; no need to do anything + () + }, + Err(Error::Sys(Errno::EINVAL)) => panic!( + "AioCb was never submitted, or already finalized"), + _ => unreachable!() + } + } + let p = self.list.as_ptr(); + Errno::result(unsafe { + libc::lio_listio(mode as i32, p, self.list.len() as i32, sigevp) + }).map(drop) + } + + /// Collect final status for an individual `AioCb` submitted as part of an + /// `LioCb`. + /// + /// This is just like [`AioCb::aio_return`], except it takes into account + /// operations that were restarted by [`LioCb::listio_resubmit`] + /// + /// [`AioCb::aio_return`]: struct.AioCb.html#method.aio_return + /// [`LioCb::listio_resubmit`]: #method.listio_resubmit + pub fn aio_return(&mut self, i: usize) -> Result<isize> { + if i >= self.results.len() || self.results[i].is_none() { + self.aiocbs[i].aio_return() + } else { + self.results[i].unwrap() + } + } + + /// Retrieve error status of an individual `AioCb` submitted as part of an + /// `LioCb`. + /// + /// This is just like [`AioCb::error`], except it takes into account + /// operations that were restarted by [`LioCb::listio_resubmit`] + /// + /// [`AioCb::error`]: struct.AioCb.html#method.error + /// [`LioCb::listio_resubmit`]: #method.listio_resubmit + pub fn error(&mut self, i: usize) -> Result<()> { + if i >= self.results.len() || self.results[i].is_none() { + self.aiocbs[i].error() + } else { + Ok(()) + } + } +} + +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +impl<'a> Debug for LioCb<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("LioCb") + .field("aiocbs", &self.aiocbs) + .finish() + } +} + +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +impl<'a> From<Vec<AioCb<'a>>> for LioCb<'a> { + fn from(src: Vec<AioCb<'a>>) -> LioCb<'a> { + LioCb { + list: Vec::with_capacity(src.capacity()), + results: Vec::with_capacity(src.capacity()), + aiocbs: src, + } + } +} diff --git a/third_party/rust/nix/src/sys/epoll.rs b/third_party/rust/nix/src/sys/epoll.rs new file mode 100644 index 0000000000..3ed1dd7240 --- /dev/null +++ b/third_party/rust/nix/src/sys/epoll.rs @@ -0,0 +1,110 @@ +use Result; +use errno::Errno; +use libc::{self, c_int}; +use std::os::unix::io::RawFd; +use std::ptr; +use std::mem; +use ::Error; + +libc_bitflags!( + pub struct EpollFlags: c_int { + EPOLLIN; + EPOLLPRI; + EPOLLOUT; + EPOLLRDNORM; + EPOLLRDBAND; + EPOLLWRNORM; + EPOLLWRBAND; + EPOLLMSG; + EPOLLERR; + EPOLLHUP; + EPOLLRDHUP; + #[cfg(target_os = "linux")] // Added in 4.5; not in Android. + EPOLLEXCLUSIVE; + #[cfg(not(target_arch = "mips"))] + EPOLLWAKEUP; + EPOLLONESHOT; + EPOLLET; + } +); + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[repr(i32)] +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; + } +} + +#[allow(missing_debug_implementations)] +#[derive(Clone, Copy)] +#[repr(C)] +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 + } +} + +#[inline] +pub fn epoll_create() -> Result<RawFd> { + let res = unsafe { libc::epoll_create(1024) }; + + Errno::result(res) +} + +#[inline] +pub fn epoll_create1(flags: EpollCreateFlags) -> Result<RawFd> { + let res = unsafe { libc::epoll_create1(flags.bits()) }; + + Errno::result(res) +} + +#[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(Error::Sys(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, ptr::null_mut()) + } + }; + Errno::result(res).map(drop) + } +} + +#[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() as *mut libc::epoll_event, events.len() as c_int, timeout_ms as c_int) + }; + + Errno::result(res).map(|r| r as usize) +} diff --git a/third_party/rust/nix/src/sys/event.rs b/third_party/rust/nix/src/sys/event.rs new file mode 100644 index 0000000000..f1eaa0d155 --- /dev/null +++ b/third_party/rust/nix/src/sys/event.rs @@ -0,0 +1,352 @@ +/* TOOD: Implement for other kqueue based systems + */ + +use {Errno, Result}; +#[cfg(not(target_os = "netbsd"))] +use libc::{timespec, time_t, c_int, c_long, intptr_t, uintptr_t}; +#[cfg(target_os = "netbsd")] +use libc::{timespec, time_t, c_long, intptr_t, uintptr_t, size_t}; +use libc; +use std::os::unix::io::RawFd; +use std::ptr; +use std::mem; + +// Redefine kevent in terms of programmer-friendly enums and bitfields. +#[derive(Clone, Copy)] +#[repr(C)] +#[allow(missing_debug_implementations)] +pub struct KEvent { + kevent: libc::kevent, +} + +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "openbsd"))] +type type_of_udata = *mut libc::c_void; +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos"))] +type type_of_data = intptr_t; +#[cfg(any(target_os = "netbsd"))] +type type_of_udata = intptr_t; +#[cfg(any(target_os = "netbsd", target_os = "openbsd"))] +type type_of_data = libc::int64_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))] + pub enum EventFilter { + EVFILT_AIO, + /// Returns whenever there is no remaining data in the write buffer + #[cfg(target_os = "freebsd")] + EVFILT_EMPTY, + #[cfg(target_os = "dragonfly")] + EVFILT_EXCEPT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos"))] + EVFILT_FS, + #[cfg(target_os = "freebsd")] + EVFILT_LIO, + #[cfg(any(target_os = "ios", target_os = "macos"))] + EVFILT_MACHPORT, + 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, + EVFILT_READ, + /// Returns whenever an asynchronous `sendfile()` call completes. + #[cfg(target_os = "freebsd")] + EVFILT_SENDFILE, + EVFILT_SIGNAL, + EVFILT_TIMER, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos"))] + EVFILT_USER, + #[cfg(any(target_os = "ios", target_os = "macos"))] + EVFILT_VM, + EVFILT_VNODE, + EVFILT_WRITE, + } +} + +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "openbsd"))] +pub type type_of_event_flag = u16; +#[cfg(any(target_os = "netbsd"))] +pub type type_of_event_flag = u32; +libc_bitflags!{ + pub struct EventFlag: type_of_event_flag { + EV_ADD; + EV_CLEAR; + EV_DELETE; + EV_DISABLE; + // No released version of OpenBSD supports EV_DISPATCH or EV_RECEIPT. + // These have been commited to the -current branch though and are + // expected to be part of the OpenBSD 6.2 release in Nov 2017. + // See: https://marc.info/?l=openbsd-tech&m=149621427511219&w=2 + // https://github.com/rust-lang/libc/pull/613 + #[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "netbsd"))] + EV_DISPATCH; + #[cfg(target_os = "freebsd")] + EV_DROP; + EV_ENABLE; + EV_EOF; + EV_ERROR; + #[cfg(any(target_os = "macos", target_os = "ios"))] + EV_FLAG0; + EV_FLAG1; + #[cfg(target_os = "dragonfly")] + EV_NODATA; + EV_ONESHOT; + #[cfg(any(target_os = "macos", target_os = "ios"))] + EV_OOBAND; + #[cfg(any(target_os = "macos", target_os = "ios"))] + EV_POLL; + #[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos", + target_os = "netbsd"))] + EV_RECEIPT; + EV_SYSFLAGS; + } +} + +libc_bitflags!( + pub struct FilterFlag: u32 { + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_ABSOLUTE; + NOTE_ATTRIB; + NOTE_CHILD; + NOTE_DELETE; + #[cfg(target_os = "openbsd")] + NOTE_EOF; + NOTE_EXEC; + NOTE_EXIT; + #[cfg(any(target_os = "macos", target_os = "ios"))] + #[deprecated( since="0.14.0", note="Deprecated since OSX 10.9")] + #[allow(deprecated)] + NOTE_EXIT_REPARENTED; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_EXITSTATUS; + NOTE_EXTEND; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_FFAND; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_FFCOPY; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_FFCTRLMASK; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_FFLAGSMASK; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_FFNOP; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_FFOR; + NOTE_FORK; + NOTE_LINK; + NOTE_LOWAT; + #[cfg(target_os = "freebsd")] + NOTE_MSECONDS; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_NONE; + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))] + NOTE_NSECONDS; + #[cfg(target_os = "dragonfly")] + NOTE_OOB; + NOTE_PCTRLMASK; + NOTE_PDATAMASK; + #[cfg(any(target_os = "macos", target_os = "ios"))] + #[cfg(any(target_os = "macos", target_os = "ios"))] + #[deprecated( since="0.14.0", note="Deprecated since OSX 10.9")] + #[allow(deprecated)] + NOTE_REAP; + NOTE_RENAME; + NOTE_REVOKE; + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))] + NOTE_SECONDS; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_SIGNAL; + NOTE_TRACK; + NOTE_TRACKERR; + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly"))] + NOTE_TRIGGER; + #[cfg(target_os = "openbsd")] + NOTE_TRUNCATE; + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))] + NOTE_USECONDS; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_VM_ERROR; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_VM_PRESSURE; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_VM_PRESSURE_SUDDEN_TERMINATE; + #[cfg(any(target_os = "macos", target_os = "ios"))] + NOTE_VM_PRESSURE_TERMINATE; + NOTE_WRITE; + } +); + +pub fn kqueue() -> Result<RawFd> { + let res = unsafe { libc::kqueue() }; + + Errno::result(res) +} + + +// KEvent can't derive Send because on some operating systems, udata is defined +// as a void*. However, KEvent's public API always treats udata as an intptr_t, +// which is safe to Send. +unsafe impl Send for KEvent { +} + +impl KEvent { + pub fn new(ident: uintptr_t, filter: EventFilter, flags: EventFlag, + fflags:FilterFlag, data: intptr_t, udata: intptr_t) -> KEvent { + KEvent { kevent: libc::kevent { + ident: ident, + filter: filter as type_of_event_filter, + flags: flags.bits(), + fflags: fflags.bits(), + data: data as type_of_data, + udata: udata as type_of_udata + } } + } + + pub fn ident(&self) -> uintptr_t { + self.kevent.ident + } + + pub fn filter(&self) -> EventFilter { + unsafe { mem::transmute(self.kevent.filter as type_of_event_filter) } + } + + pub fn flags(&self) -> EventFlag { + EventFlag::from_bits(self.kevent.flags).unwrap() + } + + pub fn fflags(&self) -> FilterFlag { + FilterFlag::from_bits(self.kevent.fflags).unwrap() + } + + pub fn data(&self) -> intptr_t { + self.kevent.data as intptr_t + } + + pub fn udata(&self) -> intptr_t { + self.kevent.udata as intptr_t + } +} + +pub fn kevent(kq: RawFd, + 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 + }; + + kevent_ts(kq, changelist, eventlist, Some(timeout)) +} + +#[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd"))] +type type_of_nchanges = c_int; +#[cfg(target_os = "netbsd")] +type type_of_nchanges = size_t; + +pub fn kevent_ts(kq: RawFd, + changelist: &[KEvent], + eventlist: &mut [KEvent], + timeout_opt: Option<timespec>) -> Result<usize> { + + let res = unsafe { + libc::kevent( + kq, + changelist.as_ptr() as *const libc::kevent, + changelist.len() as type_of_nchanges, + eventlist.as_mut_ptr() as *mut libc::kevent, + eventlist.len() as type_of_nchanges, + if let Some(ref timeout) = timeout_opt {timeout as *const timespec} else {ptr::null()}) + }; + + Errno::result(res).map(|r| r as usize) +} + +#[inline] +pub fn ev_set(ev: &mut KEvent, + ident: usize, + filter: EventFilter, + flags: EventFlag, + 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; +} + +#[test] +fn test_struct_kevent() { + let udata : intptr_t = 12345; + + let expected = libc::kevent{ident: 0xdead_beef, + filter: libc::EVFILT_READ, + flags: libc::EV_ONESHOT | libc::EV_ADD, + fflags: libc::NOTE_CHILD | libc::NOTE_EXIT, + data: 0x1337, + udata: udata as type_of_udata}; + let actual = KEvent::new(0xdead_beef, + EventFilter::EVFILT_READ, + EventFlag::EV_ONESHOT | EventFlag::EV_ADD, + FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT, + 0x1337, + udata); + assert!(expected.ident == actual.ident()); + assert!(expected.filter == actual.filter() as type_of_event_filter); + assert!(expected.flags == actual.flags().bits()); + assert!(expected.fflags == actual.fflags().bits()); + assert!(expected.data == actual.data() as type_of_data); + assert!(expected.udata == actual.udata() as type_of_udata); + assert!(mem::size_of::<libc::kevent>() == mem::size_of::<KEvent>()); +} diff --git a/third_party/rust/nix/src/sys/eventfd.rs b/third_party/rust/nix/src/sys/eventfd.rs new file mode 100644 index 0000000000..c5a54e46a1 --- /dev/null +++ b/third_party/rust/nix/src/sys/eventfd.rs @@ -0,0 +1,18 @@ +use libc; +use std::os::unix::io::RawFd; +use Result; +use errno::Errno; + +libc_bitflags! { + pub struct EfdFlags: libc::c_int { + EFD_CLOEXEC; // Since Linux 2.6.27 + EFD_NONBLOCK; // Since Linux 2.6.27 + EFD_SEMAPHORE; // Since Linux 2.6.30 + } +} + +pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result<RawFd> { + let res = unsafe { libc::eventfd(initval, flags.bits()) }; + + Errno::result(res).map(|r| r as RawFd) +} diff --git a/third_party/rust/nix/src/sys/ioctl/bsd.rs b/third_party/rust/nix/src/sys/ioctl/bsd.rs new file mode 100644 index 0000000000..4c39fa6a05 --- /dev/null +++ b/third_party/rust/nix/src/sys/ioctl/bsd.rs @@ -0,0 +1,102 @@ +/// The datatype used for the ioctl number +#[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_int; + +mod consts { + use ::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)] + 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] +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] +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] +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] +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] +macro_rules! request_code_readwrite { + ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::INOUT, $g, $n, $len)) +} diff --git a/third_party/rust/nix/src/sys/ioctl/linux.rs b/third_party/rust/nix/src/sys/ioctl/linux.rs new file mode 100644 index 0000000000..750a1c9832 --- /dev/null +++ b/third_party/rust/nix/src/sys/ioctl/linux.rs @@ -0,0 +1,140 @@ +/// The datatype used for the ioctl number +#[cfg(any(target_os = "android", target_env = "musl"))] +#[doc(hidden)] +pub type ioctl_num_type = ::libc::c_int; +#[cfg(not(any(target_os = "android", target_env = "musl")))] +#[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(any(target_arch = "mips", target_arch = "mips64", 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; +} + +// "Generic" ioctl protocol +#[cfg(any(target_arch = "x86", + target_arch = "arm", + target_arch = "s390x", + target_arch = "x86_64", + target_arch = "aarch64"))] +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] +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] +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] +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] +macro_rules! request_code_readwrite { + ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz)) +} diff --git a/third_party/rust/nix/src/sys/ioctl/mod.rs b/third_party/rust/nix/src/sys/ioctl/mod.rs new file mode 100644 index 0000000000..35d508b131 --- /dev/null +++ b/third_party/rust/nix/src/sys/ioctl/mod.rs @@ -0,0 +1,778 @@ +//! 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.txt`](http://elixir.free-electrons.com/linux/latest/source/Documentation/ioctl/ioctl-number.txt)): +//! +//! * 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 = 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](http://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(any(target_os = "android", target_os = "linux"))] +//! # use nix::libc::TCGETS as TCGETS; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! # use nix::libc::termios as termios; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! 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 = 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() {} +//! ``` +#[cfg(any(target_os = "android", target_os = "linux"))] +#[macro_use] +mod linux; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use self::linux::*; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +#[macro_use] +mod bsd; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +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] +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> { + 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; +/// # extern crate libc; +/// # 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] +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> { + 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] +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> { + 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 +/// +/// ``` +/// # extern crate libc; +/// # #[macro_use] extern crate nix; +/// # #[cfg(any(target_os = "android", target_os = "linux"))] +/// ioctl_read_bad!(tcgets, libc::TCGETS, libc::termios); +/// # fn main() {} +/// ``` +#[macro_export] +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> { + 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] +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> { + 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 +/// +/// ``` +/// # extern crate libc; +/// # #[macro_use] extern crate nix; +/// # #[cfg(any(target_os = "android", target_os = "linux"))] +/// ioctl_write_ptr_bad!(tcsets, libc::TCSETS, libc::termios); +/// # fn main() {} +/// ``` +#[macro_export] +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> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} + +cfg_if!{ + if #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] { + /// 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] + 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> { + 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] + 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> { + 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 +/// +/// ``` +/// # extern crate libc; +/// # #[macro_use] extern crate nix; +/// # #[cfg(any(target_os = "android", target_os = "linux"))] +/// 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] +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> { + 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] +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> { + 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] +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> { + 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] +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> { + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} + +/// 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] +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> { + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} + +/// 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] +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> { + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ) +} diff --git a/third_party/rust/nix/src/sys/memfd.rs b/third_party/rust/nix/src/sys/memfd.rs new file mode 100644 index 0000000000..9672429b31 --- /dev/null +++ b/third_party/rust/nix/src/sys/memfd.rs @@ -0,0 +1,20 @@ +use libc; +use std::os::unix::io::RawFd; +use Result; +use errno::Errno; +use std::ffi::CStr; + +libc_bitflags!( + pub struct MemFdCreateFlag: libc::c_uint { + MFD_CLOEXEC; + MFD_ALLOW_SEALING; + } +); + +pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result<RawFd> { + let res = unsafe { + libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags.bits()) + }; + + Errno::result(res).map(|r| r as RawFd) +} diff --git a/third_party/rust/nix/src/sys/mman.rs b/third_party/rust/nix/src/sys/mman.rs new file mode 100644 index 0000000000..d6a28737c6 --- /dev/null +++ b/third_party/rust/nix/src/sys/mman.rs @@ -0,0 +1,296 @@ +use {Error, Result}; +#[cfg(not(target_os = "android"))] +use NixPath; +use errno::Errno; +#[cfg(not(target_os = "android"))] +use fcntl::OFlag; +use libc::{self, c_int, c_void, size_t, off_t}; +#[cfg(not(target_os = "android"))] +use sys::stat::Mode; +use std::os::unix::io::RawFd; + +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(any(target_os = "android", target_os = "linux"))] + PROT_GROWSDOWN; + /// Apply protection down to the beginning of a mapping that grows downwards. + #[cfg(any(target_os = "android", target_os = "linux"))] + PROT_GROWSUP; + } +} + +libc_bitflags!{ + /// Additional parameters for `mmap()`. + pub struct MapFlags: c_int { + /// Compatibility flag. Ignored. + MAP_FILE; + /// Share this mapping. Mutually exclusive with `MAP_PRIVATE`. + MAP_SHARED; + /// 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; + /// Synonym for `MAP_ANONYMOUS`. + MAP_ANON; + /// The mapping is not backed by any file. + #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] + MAP_ANONYMOUS; + /// Put the mapping into the first 2GB of the process address space. + #[cfg(any(all(any(target_os = "android", target_os = "linux"), + 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 = "freebsd", target_pointer_width = "64")))] + MAP_32BIT; + /// Used for stacks; indicates to the kernel that the mapping should extend downward in memory. + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_GROWSDOWN; + /// Compatibility flag. Ignored. + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_DENYWRITE; + /// Compatibility flag. Ignored. + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_EXECUTABLE; + /// Mark the mmaped region to be locked in the same way as `mlock(2)`. + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_LOCKED; + /// Do not reserve swap space for this mapping. + /// + /// This was removed in FreeBSD 11. + #[cfg(not(target_os = "freebsd"))] + MAP_NORESERVE; + /// Populate page tables for a mapping. + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_POPULATE; + /// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead. + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_NONBLOCK; + /// Allocate the mapping using "huge pages." + #[cfg(any(target_os = "android", target_os = "linux"))] + MAP_HUGETLB; + /// 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(any(target_os = "dragonfly", target_os = "freebsd"))] + MAP_NOSYNC; + /// Rename private pages to a file. + /// + /// This was removed in FreeBSD 11. + #[cfg(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))] + MAP_RENAME; + /// Region may contain semaphores. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] + MAP_HASSEMAPHORE; + /// Region grows down, like a stack. + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))] + MAP_STACK; + /// Pages in this mapping are not retained in the kernel's memory cache. + #[cfg(any(target_os = "ios", target_os = "macos"))] + MAP_NOCACHE; + #[cfg(any(target_os = "ios", target_os = "macos"))] + MAP_JIT; + } +} + +libc_enum!{ + /// Usage information for a range of memory to allow for performance optimizations by the kernel. + /// + /// Used by [`madvise`](./fn.madvise.html). + #[repr(i32)] + 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(any(target_os = "android", target_os = "linux"))] + MADV_REMOVE, + /// Do not make pages in this range available to the child after a `fork(2)`. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_DONTFORK, + /// Undo the effect of `MADV_DONTFORK`. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_DOFORK, + /// Poison the given pages. + /// + /// Subsequent references to those pages are treated like hardware memory corruption. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_HWPOISON, + /// Enable Kernel Samepage Merging (KSM) for the given pages. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_MERGEABLE, + /// Undo the effect of `MADV_MERGEABLE` + #[cfg(any(target_os = "android", target_os = "linux"))] + 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 = "ppc", + 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(any(target_os = "android", target_os = "linux"))] + MADV_HUGEPAGE, + /// Undo the effect of `MADV_HUGEPAGE`. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_NOHUGEPAGE, + /// Exclude the given range from a core dump. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_DONTDUMP, + /// Undo the effect of an earlier `MADV_DONTDUMP`. + #[cfg(any(target_os = "android", target_os = "linux"))] + MADV_DODUMP, + /// Specify that the application no longer needs the pages in the given range. + MADV_FREE, + /// Request that the system not flush the current range to disk unless it needs to. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + MADV_NOSYNC, + /// Undoes the effects of `MADV_NOSYNC` for any future pages dirtied within the given range. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + MADV_AUTOSYNC, + /// Region is not included in a core file. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + MADV_NOCORE, + /// Include region in a core file + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + MADV_CORE, + #[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(any(target_os = "ios", target_os = "macos"))] + MADV_ZERO_WIRED_PAGES, + #[cfg(any(target_os = "ios", target_os = "macos"))] + MADV_FREE_REUSABLE, + #[cfg(any(target_os = "ios", target_os = "macos"))] + MADV_FREE_REUSE, + #[cfg(any(target_os = "ios", target_os = "macos"))] + MADV_CAN_REUSE, + } +} + +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(any(target_os = "ios", target_os = "macos"))] + MS_KILLPAGES; + /// Deactivate pages, but leave them mapped. + #[cfg(any(target_os = "ios", target_os = "macos"))] + MS_DEACTIVATE; + /// Perform an update and wait for it to complete. + MS_SYNC; + } +} + +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. +pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> { + Errno::result(libc::mlock(addr, length)).map(drop) +} + +/// Unlocks all memory pages that contain part of the address range with `length` bytes starting at +/// `addr`. +pub unsafe fn munlock(addr: *const c_void, length: size_t) -> Result<()> { + Errno::result(libc::munlock(addr, length)).map(drop) +} + +/// Locks all memory pages mapped into this process' address space. Locked pages never move to the +/// swap area. +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. +pub fn munlockall() -> Result<()> { + unsafe { Errno::result(libc::munlockall()) }.map(drop) +} + +/// Calls to mmap are inherently unsafe, so they must be made in an unsafe block. Typically +/// a higher-level abstraction will hide the unsafe interactions with the mmap'd region. +pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: MapFlags, fd: RawFd, offset: off_t) -> Result<*mut c_void> { + let ret = libc::mmap(addr, length, prot.bits(), flags.bits(), fd, offset); + + if ret == libc::MAP_FAILED { + Err(Error::Sys(Errno::last())) + } else { + Ok(ret) + } +} + +pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> { + Errno::result(libc::munmap(addr, len)).map(drop) +} + +pub unsafe fn madvise(addr: *mut c_void, length: size_t, advise: MmapAdvise) -> Result<()> { + Errno::result(libc::madvise(addr, length, advise as i32)).map(drop) +} + +pub unsafe fn msync(addr: *mut c_void, length: size_t, flags: MsFlags) -> Result<()> { + Errno::result(libc::msync(addr, length, flags.bits())).map(drop) +} + +#[cfg(not(target_os = "android"))] +pub fn shm_open<P: ?Sized + NixPath>(name: &P, flag: OFlag, mode: Mode) -> Result<RawFd> { + let ret = name.with_nix_path(|cstr| { + #[cfg(any(target_os = "macos", target_os = "ios"))] + unsafe { + libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::c_uint) + } + #[cfg(not(any(target_os = "macos", target_os = "ios")))] + unsafe { + libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::mode_t) + } + })?; + + Errno::result(ret) +} + +#[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) +} diff --git a/third_party/rust/nix/src/sys/mod.rs b/third_party/rust/nix/src/sys/mod.rs new file mode 100644 index 0000000000..72d5964910 --- /dev/null +++ b/third_party/rust/nix/src/sys/mod.rs @@ -0,0 +1,90 @@ +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd"))] +pub mod aio; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub mod epoll; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +pub mod event; + +#[cfg(target_os = "linux")] +pub mod eventfd; + +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +#[macro_use] +pub mod ioctl; + +#[cfg(target_os = "linux")] +pub mod memfd; + +pub mod mman; + +pub mod pthread; + +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +pub mod ptrace; + +#[cfg(target_os = "linux")] +pub mod quota; + +#[cfg(any(target_os = "linux"))] +pub mod reboot; + +pub mod select; + +#[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] +pub mod sendfile; + +pub mod signal; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub mod signalfd; + +pub mod socket; + +pub mod stat; + +#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] +pub mod statfs; + +pub mod statvfs; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub mod sysinfo; + +pub mod termios; + +pub mod time; + +pub mod uio; + +pub mod utsname; + +pub mod wait; diff --git a/third_party/rust/nix/src/sys/pthread.rs b/third_party/rust/nix/src/sys/pthread.rs new file mode 100644 index 0000000000..a4d98250f2 --- /dev/null +++ b/third_party/rust/nix/src/sys/pthread.rs @@ -0,0 +1,13 @@ +use libc::{self, pthread_t}; + +pub type Pthread = pthread_t; + +/// Obtain ID of the calling thread (see +/// [`pthread_self(3)`](http://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() } +} diff --git a/third_party/rust/nix/src/sys/ptrace/bsd.rs b/third_party/rust/nix/src/sys/ptrace/bsd.rs new file mode 100644 index 0000000000..7797d10647 --- /dev/null +++ b/third_party/rust/nix/src/sys/ptrace/bsd.rs @@ -0,0 +1,170 @@ +use errno::Errno; +use libc::{self, c_int}; +use std::ptr; +use sys::signal::Signal; +use unistd::Pid; +use Result; + +pub type RequestType = c_int; + +cfg_if! { + if #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + 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. + pub enum Request { + PT_TRACE_ME, + PT_READ_I, + PT_READ_D, + #[cfg(target_os = "macos")] + PT_READ_U, + PT_WRITE_I, + PT_WRITE_D, + #[cfg(target_os = "macos")] + PT_WRITE_U, + PT_CONTINUE, + PT_KILL, + #[cfg(any(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos"), + 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(target_os = "macos")] + PT_SIGEXC, + #[cfg(target_os = "macos")] + PT_THUPDATE, + #[cfg(target_os = "macos")] + PT_ATTACHEXC + } +} + +unsafe fn ptrace_other( + request: Request, + pid: Pid, + addr: AddressType, + data: c_int, +) -> Result<c_int> { + 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 in 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 in pid allowing it to run freely +pub fn detach(pid: Pid) -> Result<()> { + unsafe { ptrace_other(Request::PT_DETACH, pid, ptr::null_mut(), 0).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 +/// extern crate nix; +/// use nix::sys::ptrace::step; +/// use nix::unistd::Pid; +/// use nix::sys::signal::Signal; +/// use nix::sys::wait::*; +/// fn main() { +/// // If a process changes state to the stopped state because of a SIGUSR1 +/// // signal, this will step the process forward and forward the user +/// // signal to the stopped process +/// match waitpid(Pid::from_raw(-1), None) { +/// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => { +/// let _ = step(pid, Signal::SIGUSR1); +/// } +/// _ => {}, +/// } +/// } +/// ``` +#[cfg( + any( + any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"), + 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 +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 +pub fn write(pid: Pid, addr: AddressType, data: c_int) -> Result<()> { + unsafe { ptrace_other(Request::PT_WRITE_D, pid, addr, data).map(drop) } +} diff --git a/third_party/rust/nix/src/sys/ptrace/linux.rs b/third_party/rust/nix/src/sys/ptrace/linux.rs new file mode 100644 index 0000000000..df15e66527 --- /dev/null +++ b/third_party/rust/nix/src/sys/ptrace/linux.rs @@ -0,0 +1,402 @@ +//! For detailed description of the ptrace requests, consult `man ptrace`. + +use std::{mem, ptr}; +use {Error, Result}; +use errno::Errno; +use libc::{self, c_void, c_long, siginfo_t}; +use ::unistd::Pid; +use sys::signal::Signal; + +pub type AddressType = *mut ::libc::c_void; + +#[cfg(all(target_os = "linux", + any(target_arch = "x86_64", + target_arch = "x86"), + 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")))] { + #[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_os = "android")), repr(u32))] + #[cfg_attr(any(target_env = "musl", target_os = "android"), repr(i32))] + /// Ptrace Request enum defining the action to be taken. + 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_arch = "mips", + target_arch = "mips64", + 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_arch = "mips", + target_arch = "mips64", + 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_arch = "mips", + target_arch = "mips64", + 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_arch = "mips", + target_arch = "mips64", + target_arch = "x86_64", + target_pointer_width = "32"))))] + PTRACE_SETFPREGS, + PTRACE_ATTACH, + PTRACE_DETACH, + #[cfg(all(target_os = "linux", any(target_env = "musl", + target_arch = "mips", + target_arch = "mips64", + target_arch = "x86", + target_arch = "x86_64")))] + PTRACE_GETFPXREGS, + #[cfg(all(target_os = "linux", any(target_env = "musl", + target_arch = "mips", + target_arch = "mips64", + 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 = "mips64"))))] + PTRACE_GETREGSET, + #[cfg(all(target_os = "linux", not(any(target_arch = "mips", + target_arch = "mips64"))))] + PTRACE_SETREGSET, + #[cfg(all(target_os = "linux", not(any(target_arch = "mips", + target_arch = "mips64"))))] + PTRACE_SEIZE, + #[cfg(all(target_os = "linux", not(any(target_arch = "mips", + target_arch = "mips64"))))] + PTRACE_INTERRUPT, + #[cfg(all(target_os = "linux", not(any(target_arch = "mips", + target_arch = "mips64"))))] + PTRACE_LISTEN, + #[cfg(all(target_os = "linux", not(any(target_arch = "mips", + target_arch = "mips64"))))] + PTRACE_PEEKSIGINFO, + } +} + +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`. + 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, + // PTRACE_EVENT_STOP not provided by libc because it's defined in glibc 2.26 + } +} + +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. + #[cfg(any(target_os = "android", target_os = "linux"))] + PTRACE_O_EXITKILL; + } +} + +/// Performs a ptrace request. If the request in question is provided by a specialised function +/// this function will return an unsupported operation error. +#[deprecated( + since="0.10.0", + note="usages of `ptrace()` should be replaced with the specialized helper functions instead" +)] +pub unsafe fn ptrace(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long> { + use self::Request::*; + match request { + PTRACE_PEEKTEXT | PTRACE_PEEKDATA | PTRACE_GETSIGINFO | + PTRACE_GETEVENTMSG | PTRACE_SETSIGINFO | PTRACE_SETOPTIONS | + PTRACE_POKETEXT | PTRACE_POKEDATA | PTRACE_KILL => Err(Error::UnsupportedOperation), + _ => ptrace_other(request, pid, addr, data) + } +} + +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(Error::Sys(Errno::UnknownErrno)) => Ok(ret), + err @ Err(..) => err, + } +} + +/// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)` +#[cfg(all(target_os = "linux", + any(target_arch = "x86_64", + 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) +} + +/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)` +#[cfg(all(target_os = "linux", + any(target_arch = "x86_64", + 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>(), + ®s as *const _ as *const c_void) + }; + Errno::result(res).map(drop) +} + +/// 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> { + // Creates an uninitialized pointer to store result in + let data: T = unsafe { mem::uninitialized() }; + let res = unsafe { + libc::ptrace(request as RequestType, + libc::pid_t::from(pid), + ptr::null_mut::<T>(), + &data as *const _ as *const c_void) + }; + Errno::result(res)?; + Ok(data) +} + +unsafe fn ptrace_other(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long> { + 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), + } +} + +/// 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 + } +} + +/// Ask for 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. +pub fn syscall(pid: Pid) -> Result<()> { + unsafe { + ptrace_other( + Request::PTRACE_SYSCALL, + pid, + ptr::null_mut(), + ptr::null_mut(), + ).map(drop) // ignore the useless return value + } +} + +/// Attach to a running process, as with `ptrace(PTRACE_ATTACH, ...)` +/// +/// Attaches to the process specified in 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 + } +} + +/// Detaches the current running process, as with `ptrace(PTRACE_DETACH, ...)` +/// +/// Detaches from the process specified in pid allowing it to run freely +pub fn detach(pid: Pid) -> Result<()> { + unsafe { + ptrace_other( + Request::PTRACE_DETACH, + pid, + ptr::null_mut(), + ptr::null_mut() + ).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 + } +} + +/// 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 +/// extern crate nix; +/// use nix::sys::ptrace::step; +/// use nix::unistd::Pid; +/// use nix::sys::signal::Signal; +/// use nix::sys::wait::*; +/// fn main() { +/// // If a process changes state to the stopped state because of a SIGUSR1 +/// // signal, this will step the process forward and forward the user +/// // signal to the stopped process +/// match waitpid(Pid::from_raw(-1), None) { +/// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => { +/// let _ = step(pid, Signal::SIGUSR1); +/// } +/// _ => {}, +/// } +/// } +/// ``` +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) + } +} + + +/// Reads a word from a processes memory at the given address +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 +pub fn write(pid: Pid, addr: AddressType, data: *mut c_void) -> Result<()> { + unsafe { + ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop) + } +} diff --git a/third_party/rust/nix/src/sys/ptrace/mod.rs b/third_party/rust/nix/src/sys/ptrace/mod.rs new file mode 100644 index 0000000000..782c30409b --- /dev/null +++ b/third_party/rust/nix/src/sys/ptrace/mod.rs @@ -0,0 +1,22 @@ +///! Provides helpers for making ptrace system calls + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod linux; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use self::linux::*; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +mod bsd; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] +pub use self::bsd::*; diff --git a/third_party/rust/nix/src/sys/quota.rs b/third_party/rust/nix/src/sys/quota.rs new file mode 100644 index 0000000000..14c0462929 --- /dev/null +++ b/third_party/rust/nix/src/sys/quota.rs @@ -0,0 +1,274 @@ +//! 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"); +//! 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); +//! ``` +use std::default::Default; +use std::{mem, ptr}; +use libc::{self, c_int, c_char}; +use {Result, NixPath}; +use errno::Errno; + +struct QuotaCmd(QuotaSubCmd, QuotaType); + +impl QuotaCmd { + fn as_int(&self) -> c_int { + unsafe { 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)] + pub enum QuotaType { + /// Specify a user quota + USRQUOTA, + /// Specify a group quota + GRPQUOTA, + } +} + +libc_enum!{ + /// The type of quota format to use. + #[repr(i32)] + 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` +// FIXME: Change to repr(transparent) +#[repr(C)] +#[derive(Clone, Copy)] +#[allow(missing_debug_implementations)] +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() as *mut c_char; + 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. +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 = unsafe { mem::uninitialized() }; + quotactl(QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which), Some(special), id, &mut dqblk as *mut _ as *mut c_char)?; + dqblk +} + +/// 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) +} diff --git a/third_party/rust/nix/src/sys/reboot.rs b/third_party/rust/nix/src/sys/reboot.rs new file mode 100644 index 0000000000..bafa8fc119 --- /dev/null +++ b/third_party/rust/nix/src/sys/reboot.rs @@ -0,0 +1,45 @@ +//! Reboot/shutdown or enable/disable Ctrl-Alt-Delete. + +use {Error, Result}; +use errno::Errno; +use libc; +use void::Void; +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)] + pub enum RebootMode { + RB_HALT_SYSTEM, + RB_KEXEC, + RB_POWER_OFF, + RB_AUTOBOOT, + // we do not support Restart2, + RB_SW_SUSPEND, + } +} + +pub fn reboot(how: RebootMode) -> Result<Void> { + unsafe { + libc::reboot(how as libc::c_int) + }; + Err(Error::Sys(Errno::last())) +} + +/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete). +/// +/// Corresponds to calling `reboot(RB_ENABLE_CAD)` or `reboot(RB_DISABLE_CAD)` in C. +pub fn set_cad_enabled(enable: bool) -> Result<()> { + let cmd = if enable { + libc::RB_ENABLE_CAD + } else { + libc::RB_DISABLE_CAD + }; + let res = unsafe { + libc::reboot(cmd) + }; + Errno::result(res).map(drop) +} diff --git a/third_party/rust/nix/src/sys/select.rs b/third_party/rust/nix/src/sys/select.rs new file mode 100644 index 0000000000..95b6b148f6 --- /dev/null +++ b/third_party/rust/nix/src/sys/select.rs @@ -0,0 +1,335 @@ +use std::mem; +use std::os::unix::io::RawFd; +use std::ptr::{null, null_mut}; +use libc::{self, c_int}; +use Result; +use errno::Errno; +use sys::signal::SigSet; +use sys::time::{TimeSpec, TimeVal}; + +pub use libc::FD_SETSIZE; + +// FIXME: Change to repr(transparent) once it's stable +#[repr(C)] +#[derive(Clone, Copy)] +#[allow(missing_debug_implementations)] +pub struct FdSet(libc::fd_set); + +impl FdSet { + pub fn new() -> FdSet { + let mut fdset = unsafe { mem::uninitialized() }; + unsafe { libc::FD_ZERO(&mut fdset) }; + FdSet(fdset) + } + + pub fn insert(&mut self, fd: RawFd) { + unsafe { libc::FD_SET(fd, &mut self.0) }; + } + + pub fn remove(&mut self, fd: RawFd) { + unsafe { libc::FD_CLR(fd, &mut self.0) }; + } + + pub fn contains(&mut self, fd: RawFd) -> bool { + unsafe { libc::FD_ISSET(fd, &mut self.0) } + } + + pub fn clear(&mut self) { + unsafe { libc::FD_ZERO(&mut self.0) }; + } + + /// 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 + /// + /// ``` + /// # extern crate nix; + /// # use nix::sys::select::FdSet; + /// # fn main() { + /// let mut set = FdSet::new(); + /// set.insert(4); + /// set.insert(9); + /// assert_eq!(set.highest(), Some(9)); + /// # } + /// ``` + /// + /// [`select`]: fn.select.html + pub fn highest(&mut self) -> Option<RawFd> { + for i in (0..FD_SETSIZE).rev() { + let i = i as RawFd; + if unsafe { libc::FD_ISSET(i, self as *mut _ as *mut libc::fd_set) } { + return Some(i) + } + } + + None + } +} + +/// 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)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html) +/// +/// [`FdSet::highest`]: struct.FdSet.html#method.highest +pub fn select<'a, N, R, W, E, T>(nfds: N, + readfds: R, + writefds: W, + errorfds: E, + timeout: T) -> Result<c_int> +where + N: Into<Option<c_int>>, + R: Into<Option<&'a mut FdSet>>, + W: Into<Option<&'a mut FdSet>>, + E: Into<Option<&'a mut FdSet>>, + 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().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) +} + +/// 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)](http://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, N, R, W, E, T, S>(nfds: N, + readfds: R, + writefds: W, + errorfds: E, + timeout: T, + sigmask: S) -> Result<c_int> +where + N: Into<Option<c_int>>, + R: Into<Option<&'a mut FdSet>>, + W: Into<Option<&'a mut FdSet>>, + E: Into<Option<&'a mut FdSet>>, + 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().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) +} + + +#[cfg(test)] +mod tests { + use super::*; + use std::os::unix::io::RawFd; + use sys::time::{TimeVal, TimeValLike}; + use unistd::{write, pipe}; + + #[test] + fn fdset_insert() { + let mut fd_set = FdSet::new(); + + for i in 0..FD_SETSIZE { + assert!(!fd_set.contains(i as RawFd)); + } + + fd_set.insert(7); + + assert!(fd_set.contains(7)); + } + + #[test] + fn fdset_remove() { + let mut fd_set = FdSet::new(); + + for i in 0..FD_SETSIZE { + assert!(!fd_set.contains(i as RawFd)); + } + + fd_set.insert(7); + fd_set.remove(7); + + for i in 0..FD_SETSIZE { + assert!(!fd_set.contains(i as RawFd)); + } + } + + #[test] + fn fdset_clear() { + let mut fd_set = FdSet::new(); + fd_set.insert(1); + fd_set.insert((FD_SETSIZE / 2) as RawFd); + fd_set.insert((FD_SETSIZE - 1) as RawFd); + + fd_set.clear(); + + for i in 0..FD_SETSIZE { + assert!(!fd_set.contains(i as RawFd)); + } + } + + #[test] + fn fdset_highest() { + let mut set = FdSet::new(); + assert_eq!(set.highest(), None); + set.insert(0); + assert_eq!(set.highest(), Some(0)); + set.insert(90); + assert_eq!(set.highest(), Some(90)); + set.remove(0); + assert_eq!(set.highest(), Some(90)); + set.remove(90); + assert_eq!(set.highest(), None); + + set.insert(4); + set.insert(5); + set.insert(7); + assert_eq!(set.highest(), Some(7)); + } + + #[test] + fn test_select() { + 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); + fd_set.insert(r2); + + let mut timeout = TimeVal::seconds(10); + assert_eq!(1, select(None, + &mut fd_set, + None, + None, + &mut timeout).unwrap()); + assert!(fd_set.contains(r1)); + assert!(!fd_set.contains(r2)); + } + + #[test] + fn test_select_nfds() { + 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); + fd_set.insert(r2); + + let mut timeout = TimeVal::seconds(10); + assert_eq!(1, select(Some(fd_set.highest().unwrap() + 1), + &mut fd_set, + None, + None, + &mut timeout).unwrap()); + assert!(fd_set.contains(r1)); + assert!(!fd_set.contains(r2)); + } + + #[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); + fd_set.insert(r2); + + let mut timeout = TimeVal::seconds(10); + assert_eq!(1, select(::std::cmp::max(r1, r2) + 1, + &mut fd_set, + None, + None, + &mut timeout).unwrap()); + assert!(fd_set.contains(r1)); + assert!(!fd_set.contains(r2)); + } +} diff --git a/third_party/rust/nix/src/sys/sendfile.rs b/third_party/rust/nix/src/sys/sendfile.rs new file mode 100644 index 0000000000..1190518d8b --- /dev/null +++ b/third_party/rust/nix/src/sys/sendfile.rs @@ -0,0 +1,200 @@ +use std::os::unix::io::RawFd; +use std::ptr; + +use libc::{self, off_t}; + +use Result; +use errno::Errno; + +/// 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.](http://man7.org/linux/man-pages/man2/sendfile.2.html) +#[cfg(any(target_os = "android", target_os = "linux"))] +pub fn sendfile( + out_fd: RawFd, + in_fd: RawFd, + 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, in_fd, offset, count) }; + Errno::result(ret).map(|r| r as usize) +} + +cfg_if! { + if #[cfg(any(target_os = "freebsd", + target_os = "ios", + target_os = "macos"))] { + use sys::uio::IoVec; + + #[allow(missing_debug_implementations)] + struct SendfileHeaderTrailer<'a>( + libc::sf_hdtr, + Option<Vec<IoVec<&'a [u8]>>>, + Option<Vec<IoVec<&'a [u8]>>>, + ); + + impl<'a> SendfileHeaderTrailer<'a> { + fn new( + headers: Option<&'a [&'a [u8]]>, + trailers: Option<&'a [&'a [u8]]> + ) -> SendfileHeaderTrailer<'a> { + let header_iovecs: Option<Vec<IoVec<&[u8]>>> = + headers.map(|s| s.iter().map(|b| IoVec::from_slice(b)).collect()); + let trailer_iovecs: Option<Vec<IoVec<&[u8]>>> = + trailers.map(|s| s.iter().map(|b| IoVec::from_slice(b)).collect()); + SendfileHeaderTrailer( + libc::sf_hdtr { + headers: { + header_iovecs + .as_ref() + .map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec + }, + hdr_cnt: header_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32, + trailers: { + trailer_iovecs + .as_ref() + .map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec + }, + trl_cnt: trailer_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32 + }, + header_iovecs, + trailer_iovecs, + ) + } + } + } +} + +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) + pub fn sendfile( + in_fd: RawFd, + out_sock: RawFd, + 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 flags: u32 = ((readahead as u32) << 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.0 as *const libc::sf_hdtr); + let return_code = unsafe { + libc::sendfile(in_fd, + out_sock, + 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(any(target_os = "ios", target_os = "macos"))] { + /// 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( + in_fd: RawFd, + out_sock: RawFd, + 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.0 as *const libc::sf_hdtr); + let return_code = unsafe { + libc::sendfile(in_fd, + out_sock, + offset, + &mut len as *mut off_t, + hdtr_ptr as *mut libc::sf_hdtr, + 0) + }; + (Errno::result(return_code).and(Ok(())), len) + } + } +} diff --git a/third_party/rust/nix/src/sys/signal.rs b/third_party/rust/nix/src/sys/signal.rs new file mode 100644 index 0000000000..cd86723e04 --- /dev/null +++ b/third_party/rust/nix/src/sys/signal.rs @@ -0,0 +1,984 @@ +// Portions of this file are Copyright 2014 The Rust Project Developers. +// See http://rust-lang.org/COPYRIGHT. + +///! Operating system signals. + +use libc; +use {Error, Result}; +use errno::Errno; +use std::mem; +use std::fmt; +use std::str::FromStr; +#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] +use std::os::unix::io::RawFd; +use std::ptr; + +#[cfg(not(target_os = "openbsd"))] +pub use self::sigevent::*; + +libc_enum!{ + // Currently there is only one definition of c_int in libc, as well as only one + // type for signal constants. + // We would prefer to use the libc::c_int alias in the repr attribute. Unfortunately + // this is not (yet) possible. + #[repr(i32)] + pub enum Signal { + SIGHUP, + SIGINT, + SIGQUIT, + SIGILL, + SIGTRAP, + SIGABRT, + SIGBUS, + SIGFPE, + SIGKILL, + SIGUSR1, + SIGSEGV, + SIGUSR2, + SIGPIPE, + SIGALRM, + SIGTERM, + #[cfg(all(any(target_os = "android", target_os = "emscripten", target_os = "linux"), + not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))] + SIGSTKFLT, + SIGCHLD, + SIGCONT, + SIGSTOP, + SIGTSTP, + SIGTTIN, + SIGTTOU, + SIGURG, + SIGXCPU, + SIGXFSZ, + SIGVTALRM, + SIGPROF, + SIGWINCH, + SIGIO, + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + SIGPWR, + SIGSYS, + #[cfg(not(any(target_os = "android", target_os = "emscripten", target_os = "linux")))] + SIGEMT, + #[cfg(not(any(target_os = "android", target_os = "emscripten", target_os = "linux")))] + SIGINFO, + } +} + +impl FromStr for Signal { + type Err = Error; + fn from_str(s: &str) -> Result<Signal> { + Ok(match s { + "SIGHUP" => Signal::SIGHUP, + "SIGINT" => Signal::SIGINT, + "SIGQUIT" => Signal::SIGQUIT, + "SIGILL" => Signal::SIGILL, + "SIGTRAP" => Signal::SIGTRAP, + "SIGABRT" => Signal::SIGABRT, + "SIGBUS" => Signal::SIGBUS, + "SIGFPE" => Signal::SIGFPE, + "SIGKILL" => Signal::SIGKILL, + "SIGUSR1" => Signal::SIGUSR1, + "SIGSEGV" => Signal::SIGSEGV, + "SIGUSR2" => Signal::SIGUSR2, + "SIGPIPE" => Signal::SIGPIPE, + "SIGALRM" => Signal::SIGALRM, + "SIGTERM" => Signal::SIGTERM, + #[cfg(all(any(target_os = "android", target_os = "emscripten", target_os = "linux"), + not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))] + "SIGSTKFLT" => Signal::SIGSTKFLT, + "SIGCHLD" => Signal::SIGCHLD, + "SIGCONT" => Signal::SIGCONT, + "SIGSTOP" => Signal::SIGSTOP, + "SIGTSTP" => Signal::SIGTSTP, + "SIGTTIN" => Signal::SIGTTIN, + "SIGTTOU" => Signal::SIGTTOU, + "SIGURG" => Signal::SIGURG, + "SIGXCPU" => Signal::SIGXCPU, + "SIGXFSZ" => Signal::SIGXFSZ, + "SIGVTALRM" => Signal::SIGVTALRM, + "SIGPROF" => Signal::SIGPROF, + "SIGWINCH" => Signal::SIGWINCH, + "SIGIO" => Signal::SIGIO, + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + "SIGPWR" => Signal::SIGPWR, + "SIGSYS" => Signal::SIGSYS, + #[cfg(not(any(target_os = "android", target_os = "emscripten", target_os = "linux")))] + "SIGEMT" => Signal::SIGEMT, + #[cfg(not(any(target_os = "android", target_os = "emscripten", target_os = "linux")))] + "SIGINFO" => Signal::SIGINFO, + _ => return Err(Error::invalid_argument()), + }) + } +} + +impl AsRef<str> for Signal { + fn as_ref(&self) -> &str { + match *self { + Signal::SIGHUP => "SIGHUP", + Signal::SIGINT => "SIGINT", + Signal::SIGQUIT => "SIGQUIT", + Signal::SIGILL => "SIGILL", + Signal::SIGTRAP => "SIGTRAP", + Signal::SIGABRT => "SIGABRT", + Signal::SIGBUS => "SIGBUS", + Signal::SIGFPE => "SIGFPE", + Signal::SIGKILL => "SIGKILL", + Signal::SIGUSR1 => "SIGUSR1", + Signal::SIGSEGV => "SIGSEGV", + Signal::SIGUSR2 => "SIGUSR2", + Signal::SIGPIPE => "SIGPIPE", + Signal::SIGALRM => "SIGALRM", + Signal::SIGTERM => "SIGTERM", + #[cfg(all(any(target_os = "android", target_os = "emscripten", target_os = "linux"), + not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))] + Signal::SIGSTKFLT => "SIGSTKFLT", + Signal::SIGCHLD => "SIGCHLD", + Signal::SIGCONT => "SIGCONT", + Signal::SIGSTOP => "SIGSTOP", + Signal::SIGTSTP => "SIGTSTP", + Signal::SIGTTIN => "SIGTTIN", + Signal::SIGTTOU => "SIGTTOU", + Signal::SIGURG => "SIGURG", + Signal::SIGXCPU => "SIGXCPU", + Signal::SIGXFSZ => "SIGXFSZ", + Signal::SIGVTALRM => "SIGVTALRM", + Signal::SIGPROF => "SIGPROF", + Signal::SIGWINCH => "SIGWINCH", + Signal::SIGIO => "SIGIO", + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + Signal::SIGPWR => "SIGPWR", + Signal::SIGSYS => "SIGSYS", + #[cfg(not(any(target_os = "android", target_os = "emscripten", target_os = "linux")))] + Signal::SIGEMT => "SIGEMT", + #[cfg(not(any(target_os = "android", target_os = "emscripten", target_os = "linux")))] + Signal::SIGINFO => "SIGINFO", + } + } +} + +impl fmt::Display for Signal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.as_ref()) + } +} + +pub use self::Signal::*; + +#[cfg(all(any(target_os = "linux", target_os = "android", target_os = "emscripten"), not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))] +const SIGNALS: [Signal; 31] = [ + SIGHUP, + SIGINT, + SIGQUIT, + SIGILL, + SIGTRAP, + SIGABRT, + SIGBUS, + SIGFPE, + SIGKILL, + SIGUSR1, + SIGSEGV, + SIGUSR2, + SIGPIPE, + SIGALRM, + SIGTERM, + SIGSTKFLT, + SIGCHLD, + SIGCONT, + SIGSTOP, + SIGTSTP, + SIGTTIN, + SIGTTOU, + SIGURG, + SIGXCPU, + SIGXFSZ, + SIGVTALRM, + SIGPROF, + SIGWINCH, + SIGIO, + SIGPWR, + SIGSYS]; +#[cfg(all(any(target_os = "linux", target_os = "android", target_os = "emscripten"), any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64")))] +const SIGNALS: [Signal; 30] = [ + SIGHUP, + SIGINT, + SIGQUIT, + SIGILL, + SIGTRAP, + SIGABRT, + SIGBUS, + SIGFPE, + SIGKILL, + SIGUSR1, + SIGSEGV, + SIGUSR2, + SIGPIPE, + SIGALRM, + SIGTERM, + SIGCHLD, + SIGCONT, + SIGSTOP, + SIGTSTP, + SIGTTIN, + SIGTTOU, + SIGURG, + SIGXCPU, + SIGXFSZ, + SIGVTALRM, + SIGPROF, + SIGWINCH, + SIGIO, + SIGPWR, + SIGSYS]; +#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "emscripten")))] +const SIGNALS: [Signal; 31] = [ + SIGHUP, + SIGINT, + SIGQUIT, + SIGILL, + SIGTRAP, + SIGABRT, + SIGBUS, + SIGFPE, + SIGKILL, + SIGUSR1, + SIGSEGV, + SIGUSR2, + SIGPIPE, + SIGALRM, + SIGTERM, + SIGCHLD, + SIGCONT, + SIGSTOP, + SIGTSTP, + SIGTTIN, + SIGTTOU, + SIGURG, + SIGXCPU, + SIGXFSZ, + SIGVTALRM, + SIGPROF, + SIGWINCH, + SIGIO, + SIGSYS, + SIGEMT, + SIGINFO]; + +pub const NSIG: libc::c_int = 32; + +#[derive(Clone, Copy)] +#[allow(missing_debug_implementations)] +pub struct SignalIterator { + next: usize, +} + +impl Iterator for SignalIterator { + type Item = Signal; + + fn next(&mut self) -> Option<Signal> { + if self.next < SIGNALS.len() { + let next_signal = SIGNALS[self.next]; + self.next += 1; + Some(next_signal) + } else { + None + } + } +} + +impl Signal { + pub fn iterator() -> SignalIterator { + SignalIterator{next: 0} + } + + // We do not implement the From trait, because it is supposed to be infallible. + // With Rust RFC 1542 comes the appropriate trait TryFrom. Once it is + // implemented, we'll replace this function. + #[inline] + pub fn from_c_int(signum: libc::c_int) -> Result<Signal> { + if 0 < signum && signum < NSIG { + Ok(unsafe { mem::transmute(signum) }) + } else { + Err(Error::invalid_argument()) + } + } +} + +pub const SIGIOT : Signal = SIGABRT; +pub const SIGPOLL : Signal = SIGIO; +pub const SIGUNUSED : Signal = SIGSYS; + +libc_bitflags!{ + pub struct SaFlags: libc::c_int { + SA_NOCLDSTOP; + SA_NOCLDWAIT; + SA_NODEFER; + SA_ONSTACK; + SA_RESETHAND; + SA_RESTART; + SA_SIGINFO; + } +} + +libc_enum! { + #[repr(i32)] + pub enum SigmaskHow { + SIG_BLOCK, + SIG_UNBLOCK, + SIG_SETMASK, + } +} + +#[derive(Clone, Copy)] +#[allow(missing_debug_implementations)] +pub struct SigSet { + sigset: libc::sigset_t +} + + +impl SigSet { + pub fn all() -> SigSet { + let mut sigset: libc::sigset_t = unsafe { mem::uninitialized() }; + let _ = unsafe { libc::sigfillset(&mut sigset as *mut libc::sigset_t) }; + + SigSet { sigset: sigset } + } + + pub fn empty() -> SigSet { + let mut sigset: libc::sigset_t = unsafe { mem::uninitialized() }; + let _ = unsafe { libc::sigemptyset(&mut sigset as *mut libc::sigset_t) }; + + SigSet { sigset: sigset } + } + + pub fn add(&mut self, signal: Signal) { + unsafe { libc::sigaddset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) }; + } + + pub fn clear(&mut self) { + unsafe { libc::sigemptyset(&mut self.sigset as *mut libc::sigset_t) }; + } + + pub fn remove(&mut self, signal: Signal) { + unsafe { libc::sigdelset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) }; + } + + pub fn contains(&self, signal: Signal) -> bool { + let res = unsafe { libc::sigismember(&self.sigset as *const libc::sigset_t, signal as libc::c_int) }; + + match res { + 1 => true, + 0 => false, + _ => unreachable!("unexpected value from sigismember"), + } + } + + pub fn extend(&mut self, other: &SigSet) { + for signal in Signal::iterator() { + if other.contains(signal) { + self.add(signal); + } + } + } + + /// Gets the currently blocked (masked) set of signals for the calling thread. + pub fn thread_get_mask() -> Result<SigSet> { + let mut oldmask: SigSet = unsafe { mem::uninitialized() }; + pthread_sigmask(SigmaskHow::SIG_SETMASK, None, Some(&mut oldmask))?; + Ok(oldmask) + } + + /// Sets the set of signals as the signal mask for the calling thread. + pub fn thread_set_mask(&self) -> Result<()> { + pthread_sigmask(SigmaskHow::SIG_SETMASK, Some(self), None) + } + + /// Adds the set of signals to the signal mask for the calling thread. + pub fn thread_block(&self) -> Result<()> { + pthread_sigmask(SigmaskHow::SIG_BLOCK, Some(self), None) + } + + /// Removes the set of signals from the signal mask for the calling thread. + pub fn thread_unblock(&self) -> Result<()> { + pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(self), None) + } + + /// Sets the set of signals as the signal mask, and returns the old mask. + pub fn thread_swap_mask(&self, how: SigmaskHow) -> Result<SigSet> { + let mut oldmask: SigSet = unsafe { mem::uninitialized() }; + pthread_sigmask(how, Some(self), Some(&mut oldmask))?; + Ok(oldmask) + } + + /// Suspends execution of the calling thread until one of the signals in the + /// signal mask becomes pending, and returns the accepted signal. + pub fn wait(&self) -> Result<Signal> { + let mut signum: libc::c_int = unsafe { mem::uninitialized() }; + let res = unsafe { libc::sigwait(&self.sigset as *const libc::sigset_t, &mut signum) }; + + Errno::result(res).map(|_| Signal::from_c_int(signum).unwrap()) + } +} + +impl AsRef<libc::sigset_t> for SigSet { + fn as_ref(&self) -> &libc::sigset_t { + &self.sigset + } +} + +/// A signal handler. +#[allow(unknown_lints)] +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum SigHandler { + /// Default signal handling. + SigDfl, + /// Request that the signal be ignored. + SigIgn, + /// Use the given signal-catching function, which takes in the signal. + Handler(extern fn(libc::c_int)), + /// Use the given signal-catching function, which takes in the signal, information about how + /// the signal was generated, and a pointer to the threads `ucontext_t`. + SigAction(extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)) +} + +/// Action to take on receipt of a signal. Corresponds to `sigaction`. +#[derive(Clone, Copy)] +#[allow(missing_debug_implementations)] +pub struct SigAction { + sigaction: libc::sigaction +} + +impl SigAction { + /// Creates a new action. + /// + /// The `SA_SIGINFO` bit in the `flags` argument is ignored (it will be set only if `handler` + /// is the `SigAction` variant). `mask` specifies other signals to block during execution of + /// the signal-catching function. + pub fn new(handler: SigHandler, flags: SaFlags, mask: SigSet) -> SigAction { + let mut s = unsafe { mem::uninitialized::<libc::sigaction>() }; + s.sa_sigaction = match handler { + SigHandler::SigDfl => libc::SIG_DFL, + SigHandler::SigIgn => libc::SIG_IGN, + SigHandler::Handler(f) => f as *const extern fn(libc::c_int) as usize, + SigHandler::SigAction(f) => f as *const extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void) as usize, + }; + s.sa_flags = match handler { + SigHandler::SigAction(_) => (flags | SaFlags::SA_SIGINFO).bits(), + _ => (flags - SaFlags::SA_SIGINFO).bits(), + }; + s.sa_mask = mask.sigset; + + SigAction { sigaction: s } + } + + /// Returns the flags set on the action. + pub fn flags(&self) -> SaFlags { + SaFlags::from_bits_truncate(self.sigaction.sa_flags) + } + + /// Returns the set of signals that are blocked during execution of the action's + /// signal-catching function. + pub fn mask(&self) -> SigSet { + SigSet { sigset: self.sigaction.sa_mask } + } + + /// Returns the action's handler. + pub fn handler(&self) -> SigHandler { + match self.sigaction.sa_sigaction { + libc::SIG_DFL => SigHandler::SigDfl, + libc::SIG_IGN => SigHandler::SigIgn, + f if self.flags().contains(SaFlags::SA_SIGINFO) => + SigHandler::SigAction( unsafe { mem::transmute(f) } ), + f => SigHandler::Handler( unsafe { mem::transmute(f) } ), + } + } +} + +/// Changes the action taken by a process on receipt of a specific signal. +/// +/// `signal` can be any signal except `SIGKILL` or `SIGSTOP`. On success, it returns the previous +/// action for the given signal. If `sigaction` fails, no new signal handler is installed. +/// +/// # Safety +/// +/// Signal handlers may be called at any point during execution, which limits what is safe to do in +/// the body of the signal-catching function. Be certain to only make syscalls that are explicitly +/// marked safe for signal handlers and only share global data using atomics. +pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigAction> { + let mut oldact = mem::uninitialized::<libc::sigaction>(); + + let res = + libc::sigaction(signal as libc::c_int, &sigaction.sigaction as *const libc::sigaction, &mut oldact as *mut libc::sigaction); + + Errno::result(res).map(|_| SigAction { sigaction: oldact }) +} + +/// Signal management (see [signal(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html)) +/// +/// Installs `handler` for the given `signal`, returning the previous signal +/// handler. `signal` should only be used following another call to `signal` or +/// if the current handler is the default. The return value of `signal` is +/// undefined after setting the handler with [`sigaction`][SigActionFn]. +/// +/// # Safety +/// +/// If the pointer to the previous signal handler is invalid, undefined +/// behavior could be invoked when casting it back to a [`SigAction`][SigActionStruct]. +/// +/// # Examples +/// +/// Ignore `SIGINT`: +/// +/// ```no_run +/// # use nix::sys::signal::{self, Signal, SigHandler}; +/// unsafe { signal::signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap(); +/// ``` +/// +/// Use a signal handler to set a flag variable: +/// +/// ```no_run +/// # #[macro_use] extern crate lazy_static; +/// # extern crate libc; +/// # extern crate nix; +/// # use std::sync::atomic::{AtomicBool, Ordering}; +/// # use nix::sys::signal::{self, Signal, SigHandler}; +/// lazy_static! { +/// static ref SIGNALED: AtomicBool = AtomicBool::new(false); +/// } +/// +/// extern fn handle_sigint(signal: libc::c_int) { +/// let signal = Signal::from_c_int(signal).unwrap(); +/// SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed); +/// } +/// +/// fn main() { +/// let handler = SigHandler::Handler(handle_sigint); +/// unsafe { signal::signal(Signal::SIGINT, handler) }.unwrap(); +/// } +/// ``` +/// +/// # Errors +/// +/// Returns [`Error::UnsupportedOperation`] if `handler` is +/// [`SigAction`][SigActionStruct]. Use [`sigaction`][SigActionFn] instead. +/// +/// `signal` also returns any error from `libc::signal`, such as when an attempt +/// is made to catch a signal that cannot be caught or to ignore a signal that +/// cannot be ignored. +/// +/// [`Error::UnsupportedOperation`]: ../../enum.Error.html#variant.UnsupportedOperation +/// [SigActionStruct]: struct.SigAction.html +/// [sigactionFn]: fn.sigaction.html +pub unsafe fn signal(signal: Signal, handler: SigHandler) -> Result<SigHandler> { + let signal = signal as libc::c_int; + let res = match handler { + SigHandler::SigDfl => libc::signal(signal, libc::SIG_DFL), + SigHandler::SigIgn => libc::signal(signal, libc::SIG_IGN), + SigHandler::Handler(handler) => libc::signal(signal, handler as libc::sighandler_t), + SigHandler::SigAction(_) => return Err(Error::UnsupportedOperation), + }; + Errno::result(res).map(|oldhandler| { + match oldhandler { + libc::SIG_DFL => SigHandler::SigDfl, + libc::SIG_IGN => SigHandler::SigIgn, + f => SigHandler::Handler(mem::transmute(f)), + } + }) +} + +/// Manages the signal mask (set of blocked signals) for the calling thread. +/// +/// If the `set` parameter is `Some(..)`, then the signal mask will be updated with the signal set. +/// The `how` flag decides the type of update. If `set` is `None`, `how` will be ignored, +/// and no modification will take place. +/// +/// If the 'oldset' parameter is `Some(..)` then the current signal mask will be written into it. +/// +/// If both `set` and `oldset` is `Some(..)`, the current signal mask will be written into oldset, +/// and then it will be updated with `set`. +/// +/// If both `set` and `oldset` is None, this function is a no-op. +/// +/// For more information, visit the [`pthread_sigmask`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html), +/// or [`sigprocmask`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html) man pages. +pub fn pthread_sigmask(how: SigmaskHow, + set: Option<&SigSet>, + oldset: Option<&mut SigSet>) -> Result<()> { + if set.is_none() && oldset.is_none() { + return Ok(()) + } + + let res = unsafe { + // if set or oldset is None, pass in null pointers instead + libc::pthread_sigmask(how as libc::c_int, + set.map_or_else(ptr::null::<libc::sigset_t>, + |s| &s.sigset as *const libc::sigset_t), + oldset.map_or_else(ptr::null_mut::<libc::sigset_t>, + |os| &mut os.sigset as *mut libc::sigset_t)) + }; + + Errno::result(res).map(drop) +} + +/// Examine and change blocked signals. +/// +/// For more informations see the [`sigprocmask` man +/// pages](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html). +pub fn sigprocmask(how: SigmaskHow, set: Option<&SigSet>, oldset: Option<&mut SigSet>) -> Result<()> { + if set.is_none() && oldset.is_none() { + return Ok(()) + } + + let res = unsafe { + // if set or oldset is None, pass in null pointers instead + libc::sigprocmask(how as libc::c_int, + set.map_or_else(ptr::null::<libc::sigset_t>, + |s| &s.sigset as *const libc::sigset_t), + oldset.map_or_else(ptr::null_mut::<libc::sigset_t>, + |os| &mut os.sigset as *mut libc::sigset_t)) + }; + + Errno::result(res).map(drop) +} + +pub fn kill<T: Into<Option<Signal>>>(pid: ::unistd::Pid, signal: T) -> Result<()> { + let res = unsafe { libc::kill(pid.into(), + match signal.into() { + Some(s) => s as libc::c_int, + None => 0, + }) }; + + Errno::result(res).map(drop) +} + +pub fn raise(signal: Signal) -> Result<()> { + let res = unsafe { libc::raise(signal as libc::c_int) }; + + Errno::result(res).map(drop) +} + + +#[cfg(target_os = "freebsd")] +pub type type_of_thread_id = libc::lwpid_t; +#[cfg(target_os = "linux")] +pub type type_of_thread_id = libc::pid_t; + +/// Used to request asynchronous notification of certain events, for example, +/// with POSIX AIO, POSIX message queues, and POSIX timers. +// sigval is actually a union of a int and a void*. But it's never really used +// as a pointer, because neither libc nor the kernel ever dereference it. nix +// therefore presents it as an intptr_t, which is how kevent uses it. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum SigevNotify { + /// No notification will be delivered + SigevNone, + /// The signal given by `signal` will be delivered to the process. The + /// value in `si_value` will be present in the `si_value` field of the + /// `siginfo_t` structure of the queued signal. + SigevSignal { signal: Signal, si_value: libc::intptr_t }, + // Note: SIGEV_THREAD is not implemented because libc::sigevent does not + // expose a way to set the union members needed by SIGEV_THREAD. + /// A new `kevent` is posted to the kqueue `kq`. The `kevent`'s `udata` + /// field will contain the value in `udata`. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + SigevKevent { kq: RawFd, udata: libc::intptr_t }, + /// The signal `signal` is queued to the thread whose LWP ID is given in + /// `thread_id`. The value stored in `si_value` will be present in the + /// `si_value` of the `siginfo_t` structure of the queued signal. + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + SigevThreadId { signal: Signal, thread_id: type_of_thread_id, + si_value: libc::intptr_t }, +} + +#[cfg(not(target_os = "openbsd"))] +mod sigevent { + use libc; + use std::mem; + use std::ptr; + use std::fmt::{self, Debug}; + use super::SigevNotify; + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + use super::type_of_thread_id; + + /// Used to request asynchronous notification of the completion of certain + /// events, such as POSIX AIO and timers. + #[repr(C)] + #[derive(Clone, Copy)] + pub struct SigEvent { + sigevent: libc::sigevent + } + + impl SigEvent { + /// **Note:** this constructor does not allow the user to set the + /// `sigev_notify_kevent_flags` field. That's considered ok because on FreeBSD + /// at least those flags don't do anything useful. That field is part of a + /// union that shares space with the more genuinely useful fields. + /// + /// **Note:** This constructor also doesn't allow the caller to set the + /// `sigev_notify_function` or `sigev_notify_attributes` fields, which are + /// required for `SIGEV_THREAD`. That's considered ok because on no operating + /// system is `SIGEV_THREAD` the most efficient way to deliver AIO + /// notification. FreeBSD and DragonFly BSD programs should prefer `SIGEV_KEVENT`. + /// Linux, Solaris, and portable programs should prefer `SIGEV_THREAD_ID` or + /// `SIGEV_SIGNAL`. That field is part of a union that shares space with the + /// more genuinely useful `sigev_notify_thread_id` + pub fn new(sigev_notify: SigevNotify) -> SigEvent { + let mut sev = unsafe { mem::zeroed::<libc::sigevent>()}; + sev.sigev_notify = match sigev_notify { + SigevNotify::SigevNone => libc::SIGEV_NONE, + SigevNotify::SigevSignal{..} => libc::SIGEV_SIGNAL, + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + SigevNotify::SigevKevent{..} => libc::SIGEV_KEVENT, + #[cfg(target_os = "freebsd")] + SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, + #[cfg(all(target_os = "linux", target_env = "gnu", not(target_arch = "mips")))] + SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID, + #[cfg(any(all(target_os = "linux", target_env = "musl"), target_arch = "mips"))] + SigevNotify::SigevThreadId{..} => 4 // No SIGEV_THREAD_ID defined + }; + sev.sigev_signo = match sigev_notify { + SigevNotify::SigevSignal{ signal, .. } => signal as libc::c_int, + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + SigevNotify::SigevKevent{ kq, ..} => kq, + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + SigevNotify::SigevThreadId{ signal, .. } => signal as libc::c_int, + _ => 0 + }; + sev.sigev_value.sival_ptr = match sigev_notify { + SigevNotify::SigevNone => ptr::null_mut::<libc::c_void>(), + SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut libc::c_void, + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + SigevNotify::SigevKevent{ udata, .. } => udata as *mut libc::c_void, + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut libc::c_void, + }; + SigEvent::set_tid(&mut sev, &sigev_notify); + SigEvent{sigevent: sev} + } + + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + fn set_tid(sev: &mut libc::sigevent, sigev_notify: &SigevNotify) { + sev.sigev_notify_thread_id = match *sigev_notify { + SigevNotify::SigevThreadId { thread_id, .. } => thread_id, + _ => 0 as type_of_thread_id + }; + } + + #[cfg(not(any(target_os = "freebsd", target_os = "linux")))] + fn set_tid(_sev: &mut libc::sigevent, _sigev_notify: &SigevNotify) { + } + + pub fn sigevent(&self) -> libc::sigevent { + self.sigevent + } + } + + impl Debug for SigEvent { + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("SigEvent") + .field("sigev_notify", &self.sigevent.sigev_notify) + .field("sigev_signo", &self.sigevent.sigev_signo) + .field("sigev_value", &self.sigevent.sigev_value.sival_ptr) + .field("sigev_notify_thread_id", + &self.sigevent.sigev_notify_thread_id) + .finish() + } + + #[cfg(not(any(target_os = "freebsd", target_os = "linux")))] + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("SigEvent") + .field("sigev_notify", &self.sigevent.sigev_notify) + .field("sigev_signo", &self.sigevent.sigev_signo) + .field("sigev_value", &self.sigevent.sigev_value.sival_ptr) + .finish() + } + } + + impl<'a> From<&'a libc::sigevent> for SigEvent { + fn from(sigevent: &libc::sigevent) -> Self { + SigEvent{ sigevent: *sigevent } + } + } +} + +#[cfg(test)] +mod tests { + use std::thread; + use super::*; + + #[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(Error::Sys(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)); + } + + // This test doesn't actually test get_mask functionality, see the set_mask test for that. + #[test] + fn test_thread_signal_get_mask() { + assert!(SigSet::thread_get_mask().is_ok()); + } + + #[test] + fn test_thread_signal_set_mask() { + 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); + + assert!(test_mask.thread_set_mask().is_ok()); + let new_mask = SigSet::thread_get_mask() + .expect("Failed to get new mask!"); + + assert!(new_mask.contains(SIGUSR1)); + assert!(!new_mask.contains(SIGUSR2)); + + prev_mask.thread_set_mask().expect("Failed to revert signal mask!"); + }).join().unwrap(); + } + + #[test] + fn test_thread_signal_block() { + thread::spawn(|| { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + + assert!(mask.thread_block().is_ok()); + + assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); + }).join().unwrap(); + } + + #[test] + fn test_thread_signal_unblock() { + thread::spawn(|| { + let mut mask = SigSet::empty(); + mask.add(SIGUSR1); + + assert!(mask.thread_unblock().is_ok()); + + assert!(!SigSet::thread_get_mask().unwrap().contains(SIGUSR1)); + }).join().unwrap(); + } + + #[test] + 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_sigaction() { + use libc; + thread::spawn(|| { + extern fn test_sigaction_handler(_: libc::c_int) {} + extern 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(); + } + + // TODO(#251): Re-enable after figuring out flakiness. + #[cfg(not(any(target_os = "macos", target_os = "ios")))] + #[test] + 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(); + } +} diff --git a/third_party/rust/nix/src/sys/signalfd.rs b/third_party/rust/nix/src/sys/signalfd.rs new file mode 100644 index 0000000000..5425a27be9 --- /dev/null +++ b/third_party/rust/nix/src/sys/signalfd.rs @@ -0,0 +1,170 @@ +//! 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 libc; +use unistd; +use {Error, Result}; +use errno::Errno; +pub use sys::signal::{self, SigSet}; +pub use libc::signalfd_siginfo as siginfo; + +use std::os::unix::io::{RawFd, AsRawFd}; +use std::mem; + + +libc_bitflags!{ + pub struct SfdFlags: libc::c_int { + SFD_NONBLOCK; + SFD_CLOEXEC; + } +} + +pub const SIGNALFD_NEW: RawFd = -1; +pub const SIGNALFD_SIGINFO_SIZE: usize = 128; + +/// 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](http://man7.org/linux/man-pages/man2/signalfd.2.html) +pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> { + unsafe { + Errno::result(libc::signalfd(fd as libc::c_int, mask.as_ref(), flags.bits())) + } +} + +/// 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(Clone, Debug, Eq, Hash, PartialEq)] +pub struct SignalFd(RawFd); + +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(SIGNALFD_NEW, mask, flags)?; + + Ok(SignalFd(fd)) + } + + pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> { + signalfd(self.0, mask, SfdFlags::empty()).map(drop) + } + + pub fn read_signal(&mut self) -> Result<Option<siginfo>> { + let mut buffer: [u8; SIGNALFD_SIGINFO_SIZE] = unsafe { mem::uninitialized() }; + + match unistd::read(self.0, &mut buffer) { + Ok(SIGNALFD_SIGINFO_SIZE) => Ok(Some(unsafe { mem::transmute(buffer) })), + Ok(_) => unreachable!("partial read on signalfd"), + Err(Error::Sys(Errno::EAGAIN)) => Ok(None), + Err(error) => Err(error) + } + } +} + +impl Drop for SignalFd { + fn drop(&mut self) { + let _ = unistd::close(self.0); + } +} + +impl AsRawFd for SignalFd { + fn as_raw_fd(&self) -> RawFd { + self.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, + } + } +} + + +#[cfg(test)] +mod tests { + use super::*; + use std::mem; + use libc; + + + #[test] + fn check_siginfo_size() { + assert_eq!(mem::size_of::<libc::signalfd_siginfo>(), SIGNALFD_SIGINFO_SIZE); + } + + #[test] + fn create_signalfd() { + let mask = SigSet::empty(); + let fd = SignalFd::new(&mask); + assert!(fd.is_ok()); + } + + #[test] + fn create_signalfd_with_opts() { + let mask = SigSet::empty(); + let fd = SignalFd::with_flags(&mask, SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK); + assert!(fd.is_ok()); + } + + #[test] + fn read_empty_signalfd() { + let mask = SigSet::empty(); + let mut fd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap(); + + let res = fd.read_signal(); + assert!(res.unwrap().is_none()); + } +} diff --git a/third_party/rust/nix/src/sys/socket/addr.rs b/third_party/rust/nix/src/sys/socket/addr.rs new file mode 100644 index 0000000000..9ec668985b --- /dev/null +++ b/third_party/rust/nix/src/sys/socket/addr.rs @@ -0,0 +1,1408 @@ +use super::sa_family_t; +use {Error, Result, NixPath}; +use errno::Errno; +use libc; +use std::{fmt, hash, mem, net, ptr, slice}; +use std::ffi::OsStr; +use std::path::Path; +use std::os::unix::ffi::OsStrExt; +#[cfg(any(target_os = "android", target_os = "linux"))] +use ::sys::socket::addr::netlink::NetlinkAddr; +#[cfg(any(target_os = "ios", target_os = "macos"))] +use std::os::unix::io::RawFd; +#[cfg(any(target_os = "ios", target_os = "macos"))] +use ::sys::socket::addr::sys_control::SysControlAddr; +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +pub use self::datalink::LinkAddr; + +/// These constants specify the protocol family to be used +/// in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html) +#[repr(i32)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +pub enum AddressFamily { + /// Local communication (see [`unix(7)`](http://man7.org/linux/man-pages/man7/unix.7.html)) + Unix = libc::AF_UNIX, + /// IPv4 Internet protocols (see [`ip(7)`](http://man7.org/linux/man-pages/man7/ip.7.html)) + Inet = libc::AF_INET, + /// IPv6 Internet protocols (see [`ipv6(7)`](http://man7.org/linux/man-pages/man7/ipv6.7.html)) + Inet6 = libc::AF_INET6, + /// Kernel user interface device (see [`netlink(7)`](http://man7.org/linux/man-pages/man7/netlink.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + Netlink = libc::AF_NETLINK, + /// Low level packet interface (see [`packet(7)`](http://man7.org/linux/man-pages/man7/packet.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + Packet = libc::AF_PACKET, + /// KEXT Controls and Notifications + #[cfg(any(target_os = "ios", target_os = "macos"))] + System = libc::AF_SYSTEM, + /// Amateur radio AX.25 protocol + #[cfg(any(target_os = "android", target_os = "linux"))] + Ax25 = libc::AF_AX25, + /// IPX - Novell protocols + Ipx = libc::AF_IPX, + /// AppleTalk + AppleTalk = libc::AF_APPLETALK, + #[cfg(any(target_os = "android", target_os = "linux"))] + NetRom = libc::AF_NETROM, + #[cfg(any(target_os = "android", target_os = "linux"))] + Bridge = libc::AF_BRIDGE, + /// Access to raw ATM PVCs + #[cfg(any(target_os = "android", target_os = "linux"))] + AtmPvc = libc::AF_ATMPVC, + /// ITU-T X.25 / ISO-8208 protocol (see [`x25(7)`](http://man7.org/linux/man-pages/man7/x25.7.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + X25 = libc::AF_X25, + #[cfg(any(target_os = "android", target_os = "linux"))] + Rose = libc::AF_ROSE, + Decnet = libc::AF_DECnet, + #[cfg(any(target_os = "android", target_os = "linux"))] + NetBeui = libc::AF_NETBEUI, + #[cfg(any(target_os = "android", target_os = "linux"))] + Security = libc::AF_SECURITY, + #[cfg(any(target_os = "android", target_os = "linux"))] + Key = libc::AF_KEY, + #[cfg(any(target_os = "android", target_os = "linux"))] + Ash = libc::AF_ASH, + #[cfg(any(target_os = "android", target_os = "linux"))] + Econet = libc::AF_ECONET, + #[cfg(any(target_os = "android", target_os = "linux"))] + AtmSvc = libc::AF_ATMSVC, + #[cfg(any(target_os = "android", target_os = "linux"))] + Rds = libc::AF_RDS, + Sna = libc::AF_SNA, + #[cfg(any(target_os = "android", target_os = "linux"))] + Irda = libc::AF_IRDA, + #[cfg(any(target_os = "android", target_os = "linux"))] + Pppox = libc::AF_PPPOX, + #[cfg(any(target_os = "android", target_os = "linux"))] + Wanpipe = libc::AF_WANPIPE, + #[cfg(any(target_os = "android", target_os = "linux"))] + Llc = libc::AF_LLC, + #[cfg(target_os = "linux")] + Ib = libc::AF_IB, + #[cfg(target_os = "linux")] + Mpls = libc::AF_MPLS, + #[cfg(any(target_os = "android", target_os = "linux"))] + Can = libc::AF_CAN, + #[cfg(any(target_os = "android", target_os = "linux"))] + Tipc = libc::AF_TIPC, + #[cfg(not(any(target_os = "ios", target_os = "macos")))] + Bluetooth = libc::AF_BLUETOOTH, + #[cfg(any(target_os = "android", target_os = "linux"))] + Iucv = libc::AF_IUCV, + #[cfg(any(target_os = "android", target_os = "linux"))] + RxRpc = libc::AF_RXRPC, + Isdn = libc::AF_ISDN, + #[cfg(any(target_os = "android", target_os = "linux"))] + Phonet = libc::AF_PHONET, + #[cfg(any(target_os = "android", target_os = "linux"))] + Ieee802154 = libc::AF_IEEE802154, + #[cfg(any(target_os = "android", target_os = "linux"))] + Caif = libc::AF_CAIF, + /// Interface to kernel crypto API + #[cfg(any(target_os = "android", target_os = "linux"))] + Alg = libc::AF_ALG, + #[cfg(target_os = "linux")] + Nfc = libc::AF_NFC, + #[cfg(target_os = "linux")] + Vsock = libc::AF_VSOCK, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + ImpLink = libc::AF_IMPLINK, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Pup = libc::AF_PUP, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Chaos = libc::AF_CHAOS, + #[cfg(any(target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Ns = libc::AF_NS, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Iso = libc::AF_ISO, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Datakit = libc::AF_DATAKIT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Ccitt = libc::AF_CCITT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Dli = libc::AF_DLI, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Lat = libc::AF_LAT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Hylink = libc::AF_HYLINK, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Link = libc::AF_LINK, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Coip = libc::AF_COIP, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Cnt = libc::AF_CNT, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Natm = libc::AF_NATM, + /// Unspecified address family, (see [`getaddrinfo(3)`](http://man7.org/linux/man-pages/man3/getaddrinfo.3.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + Unspec = libc::AF_UNSPEC, +} + +impl AddressFamily { + /// Create a new `AddressFamily` from an integer value retrieved from `libc`, usually from + /// the `sa_family` field of a `sockaddr`. + /// + /// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink, Link/Packet + /// and System. Returns None for unsupported or unknown address families. + pub fn from_i32(family: i32) -> Option<AddressFamily> { + match family { + libc::AF_UNIX => Some(AddressFamily::Unix), + libc::AF_INET => Some(AddressFamily::Inet), + libc::AF_INET6 => Some(AddressFamily::Inet6), + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_NETLINK => Some(AddressFamily::Netlink), + #[cfg(any(target_os = "macos", target_os = "macos"))] + libc::AF_SYSTEM => Some(AddressFamily::System), + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_PACKET => Some(AddressFamily::Packet), + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + libc::AF_LINK => Some(AddressFamily::Link), + _ => None + } + } +} + +#[derive(Copy)] +pub enum InetAddr { + V4(libc::sockaddr_in), + V6(libc::sockaddr_in6), +} + +impl InetAddr { + pub fn from_std(std: &net::SocketAddr) -> InetAddr { + match *std { + net::SocketAddr::V4(ref addr) => { + InetAddr::V4(libc::sockaddr_in { + sin_family: AddressFamily::Inet as sa_family_t, + sin_port: addr.port().to_be(), // network byte order + sin_addr: Ipv4Addr::from_std(addr.ip()).0, + .. unsafe { mem::zeroed() } + }) + } + net::SocketAddr::V6(ref addr) => { + InetAddr::V6(libc::sockaddr_in6 { + sin6_family: AddressFamily::Inet6 as sa_family_t, + sin6_port: addr.port().to_be(), // network byte order + sin6_addr: Ipv6Addr::from_std(addr.ip()).0, + sin6_flowinfo: addr.flowinfo(), // host byte order + sin6_scope_id: addr.scope_id(), // host byte order + .. unsafe { mem::zeroed() } + }) + } + } + } + + pub fn new(ip: IpAddr, port: u16) -> InetAddr { + match ip { + IpAddr::V4(ref ip) => { + InetAddr::V4(libc::sockaddr_in { + sin_family: AddressFamily::Inet as sa_family_t, + sin_port: port.to_be(), + sin_addr: ip.0, + .. unsafe { mem::zeroed() } + }) + } + IpAddr::V6(ref ip) => { + InetAddr::V6(libc::sockaddr_in6 { + sin6_family: AddressFamily::Inet6 as sa_family_t, + sin6_port: port.to_be(), + sin6_addr: ip.0, + .. unsafe { mem::zeroed() } + }) + } + } + } + /// Gets the IP address associated with this socket address. + pub fn ip(&self) -> IpAddr { + match *self { + InetAddr::V4(ref sa) => IpAddr::V4(Ipv4Addr(sa.sin_addr)), + InetAddr::V6(ref sa) => IpAddr::V6(Ipv6Addr(sa.sin6_addr)), + } + } + + /// Gets the port number associated with this socket address + pub fn port(&self) -> u16 { + match *self { + InetAddr::V6(ref sa) => u16::from_be(sa.sin6_port), + InetAddr::V4(ref sa) => u16::from_be(sa.sin_port), + } + } + + pub fn to_std(&self) -> net::SocketAddr { + match *self { + InetAddr::V4(ref sa) => net::SocketAddr::V4( + net::SocketAddrV4::new( + Ipv4Addr(sa.sin_addr).to_std(), + self.port())), + InetAddr::V6(ref sa) => net::SocketAddr::V6( + net::SocketAddrV6::new( + Ipv6Addr(sa.sin6_addr).to_std(), + self.port(), + sa.sin6_flowinfo, + sa.sin6_scope_id)), + } + } + + pub fn to_str(&self) -> String { + format!("{}", self) + } +} + +impl PartialEq for InetAddr { + fn eq(&self, other: &InetAddr) -> bool { + match (*self, *other) { + (InetAddr::V4(ref a), InetAddr::V4(ref b)) => { + a.sin_port == b.sin_port && + a.sin_addr.s_addr == b.sin_addr.s_addr + } + (InetAddr::V6(ref a), InetAddr::V6(ref b)) => { + a.sin6_port == b.sin6_port && + a.sin6_addr.s6_addr == b.sin6_addr.s6_addr && + a.sin6_flowinfo == b.sin6_flowinfo && + a.sin6_scope_id == b.sin6_scope_id + } + _ => false, + } + } +} + +impl Eq for InetAddr { +} + +impl hash::Hash for InetAddr { + fn hash<H: hash::Hasher>(&self, s: &mut H) { + match *self { + InetAddr::V4(ref a) => { + ( a.sin_family, + a.sin_port, + a.sin_addr.s_addr ).hash(s) + } + InetAddr::V6(ref a) => { + ( a.sin6_family, + a.sin6_port, + &a.sin6_addr.s6_addr, + a.sin6_flowinfo, + a.sin6_scope_id ).hash(s) + } + } + } +} + +impl Clone for InetAddr { + fn clone(&self) -> InetAddr { + *self + } +} + +impl fmt::Display for InetAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + InetAddr::V4(_) => write!(f, "{}:{}", self.ip(), self.port()), + InetAddr::V6(_) => write!(f, "[{}]:{}", self.ip(), self.port()), + } + } +} + +impl fmt::Debug for InetAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/* + * + * ===== IpAddr ===== + * + */ +#[derive(Clone, Copy)] +pub enum IpAddr { + V4(Ipv4Addr), + V6(Ipv6Addr), +} + +impl IpAddr { + /// Create a new IpAddr that contains an IPv4 address. + /// + /// The result will represent the IP address a.b.c.d + pub fn new_v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr { + IpAddr::V4(Ipv4Addr::new(a, b, c, d)) + } + + /// Create a new IpAddr that contains an IPv6 address. + /// + /// The result will represent the IP address a:b:c:d:e:f + pub fn new_v6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> IpAddr { + IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h)) + } + + /* + pub fn from_std(std: &net::IpAddr) -> IpAddr { + match *std { + net::IpAddr::V4(ref std) => IpAddr::V4(Ipv4Addr::from_std(std)), + net::IpAddr::V6(ref std) => IpAddr::V6(Ipv6Addr::from_std(std)), + } + } + pub fn to_std(&self) -> net::IpAddr { + match *self { + IpAddr::V4(ref ip) => net::IpAddr::V4(ip.to_std()), + IpAddr::V6(ref ip) => net::IpAddr::V6(ip.to_std()), + } + } + */ +} + +impl fmt::Display for IpAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + IpAddr::V4(ref v4) => v4.fmt(f), + IpAddr::V6(ref v6) => v6.fmt(f) + } + } +} + +impl fmt::Debug for IpAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/* + * + * ===== Ipv4Addr ===== + * + */ + +#[derive(Copy)] +pub struct Ipv4Addr(pub libc::in_addr); + +impl Ipv4Addr { + pub fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { + let ip = (((a as u32) << 24) | + ((b as u32) << 16) | + ((c as u32) << 8) | + ((d as u32) << 0)).to_be(); + + Ipv4Addr(libc::in_addr { s_addr: ip }) + } + + pub fn from_std(std: &net::Ipv4Addr) -> Ipv4Addr { + let bits = std.octets(); + Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3]) + } + + pub fn any() -> Ipv4Addr { + Ipv4Addr(libc::in_addr { s_addr: libc::INADDR_ANY }) + } + + pub fn octets(&self) -> [u8; 4] { + let bits = u32::from_be(self.0.s_addr); + [(bits >> 24) as u8, (bits >> 16) as u8, (bits >> 8) as u8, bits as u8] + } + + pub fn to_std(&self) -> net::Ipv4Addr { + let bits = self.octets(); + net::Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3]) + } +} + +impl PartialEq for Ipv4Addr { + fn eq(&self, other: &Ipv4Addr) -> bool { + self.0.s_addr == other.0.s_addr + } +} + +impl Eq for Ipv4Addr { +} + +impl hash::Hash for Ipv4Addr { + fn hash<H: hash::Hasher>(&self, s: &mut H) { + let saddr = self.0.s_addr; + saddr.hash(s) + } +} + +impl Clone for Ipv4Addr { + fn clone(&self) -> Ipv4Addr { + *self + } +} + +impl fmt::Display for Ipv4Addr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let octets = self.octets(); + write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) + } +} + +impl fmt::Debug for Ipv4Addr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/* + * + * ===== Ipv6Addr ===== + * + */ + +#[derive(Clone, Copy)] +pub struct Ipv6Addr(pub libc::in6_addr); + +// Note that IPv6 addresses are stored in big endian order on all architectures. +// See https://tools.ietf.org/html/rfc1700 or consult your favorite search +// engine. + +macro_rules! to_u8_array { + ($($num:ident),*) => { + [ $(($num>>8) as u8, ($num&0xff) as u8,)* ] + } +} + +macro_rules! to_u16_array { + ($slf:ident, $($first:expr, $second:expr),*) => { + [$( (($slf.0.s6_addr[$first] as u16) << 8) + $slf.0.s6_addr[$second] as u16,)*] + } +} + +impl Ipv6Addr { + pub fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { + let mut in6_addr_var: libc::in6_addr = unsafe{mem::uninitialized()}; + in6_addr_var.s6_addr = to_u8_array!(a,b,c,d,e,f,g,h); + Ipv6Addr(in6_addr_var) + } + + pub fn from_std(std: &net::Ipv6Addr) -> Ipv6Addr { + let s = std.segments(); + Ipv6Addr::new(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]) + } + + /// Return the eight 16-bit segments that make up this address + pub fn segments(&self) -> [u16; 8] { + to_u16_array!(self, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15) + } + + pub fn to_std(&self) -> net::Ipv6Addr { + let s = self.segments(); + net::Ipv6Addr::new(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]) + } +} + +impl fmt::Display for Ipv6Addr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.to_std().fmt(fmt) + } +} + +impl fmt::Debug for Ipv6Addr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/* + * + * ===== UnixAddr ===== + * + */ + +/// A wrapper around `sockaddr_un`. +/// +/// This also tracks the length of `sun_path` address (excluding +/// a terminating null), because it may not be null-terminated. For example, +/// unconnected and Linux abstract sockets are never null-terminated, and POSIX +/// does not require that `sun_len` include the terminating null even for normal +/// sockets. Note that the actual sockaddr length is greater by +/// `offset_of!(libc::sockaddr_un, sun_path)` +#[derive(Copy)] +pub struct UnixAddr(pub libc::sockaddr_un, pub usize); + +impl UnixAddr { + /// Create a new sockaddr_un representing a filesystem path. + pub fn new<P: ?Sized + NixPath>(path: &P) -> Result<UnixAddr> { + path.with_nix_path(|cstr| { + unsafe { + let mut ret = libc::sockaddr_un { + sun_family: AddressFamily::Unix as sa_family_t, + .. mem::zeroed() + }; + + let bytes = cstr.to_bytes(); + + if bytes.len() > ret.sun_path.len() { + return Err(Error::Sys(Errno::ENAMETOOLONG)); + } + + ptr::copy_nonoverlapping(bytes.as_ptr(), + ret.sun_path.as_mut_ptr() as *mut u8, + bytes.len()); + + Ok(UnixAddr(ret, bytes.len())) + } + })? + } + + /// Create a new `sockaddr_un` representing an address in the "abstract namespace". + /// + /// The leading null byte for the abstract namespace is automatically added; + /// thus the input `path` is expected to be the bare name, not null-prefixed. + /// This is a Linux-specific extension, primarily used to allow chrooted + /// processes to communicate with processes having a different filesystem view. + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn new_abstract(path: &[u8]) -> Result<UnixAddr> { + unsafe { + let mut ret = libc::sockaddr_un { + sun_family: AddressFamily::Unix as sa_family_t, + .. mem::zeroed() + }; + + if path.len() + 1 > ret.sun_path.len() { + return Err(Error::Sys(Errno::ENAMETOOLONG)); + } + + // Abstract addresses are represented by sun_path[0] == + // b'\0', so copy starting one byte in. + ptr::copy_nonoverlapping(path.as_ptr(), + ret.sun_path.as_mut_ptr().offset(1) as *mut u8, + path.len()); + + Ok(UnixAddr(ret, path.len() + 1)) + } + } + + fn sun_path(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.0.sun_path.as_ptr() as *const u8, self.1) } + } + + /// If this address represents a filesystem path, return that path. + pub fn path(&self) -> Option<&Path> { + if self.1 == 0 || self.0.sun_path[0] == 0 { + // unnamed or abstract + None + } else { + let p = self.sun_path(); + // POSIX only requires that `sun_len` be at least long enough to + // contain the pathname, and it need not be null-terminated. So we + // need to create a string that is the shorter of the + // null-terminated length or the full length. + let ptr = &self.0.sun_path as *const libc::c_char; + let reallen = unsafe { libc::strnlen(ptr, p.len()) }; + Some(Path::new(<OsStr as OsStrExt>::from_bytes(&p[..reallen]))) + } + } + + /// If this address represents an abstract socket, return its name. + /// + /// For abstract sockets only the bare name is returned, without the + /// leading null byte. `None` is returned for unnamed or path-backed sockets. + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn as_abstract(&self) -> Option<&[u8]> { + if self.1 >= 1 && self.0.sun_path[0] == 0 { + Some(&self.sun_path()[1..]) + } else { + // unnamed or filesystem path + None + } + } +} + +impl PartialEq for UnixAddr { + fn eq(&self, other: &UnixAddr) -> bool { + self.sun_path() == other.sun_path() + } +} + +impl Eq for UnixAddr { +} + +impl hash::Hash for UnixAddr { + fn hash<H: hash::Hasher>(&self, s: &mut H) { + ( self.0.sun_family, self.sun_path() ).hash(s) + } +} + +impl Clone for UnixAddr { + fn clone(&self) -> UnixAddr { + *self + } +} + +impl fmt::Display for UnixAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.1 == 0 { + f.write_str("<unbound UNIX socket>") + } else if let Some(path) = self.path() { + path.display().fmt(f) + } else { + let display = String::from_utf8_lossy(&self.sun_path()[1..]); + write!(f, "@{}", display) + } + } +} + +impl fmt::Debug for UnixAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/* + * + * ===== Sock addr ===== + * + */ + +/// Represents a socket address +#[derive(Copy, Debug)] +pub enum SockAddr { + Inet(InetAddr), + Unix(UnixAddr), + #[cfg(any(target_os = "android", target_os = "linux"))] + Netlink(NetlinkAddr), + #[cfg(any(target_os = "ios", target_os = "macos"))] + SysControl(SysControlAddr), + /// Datalink address (MAC) + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Link(LinkAddr) +} + +impl SockAddr { + pub fn new_inet(addr: InetAddr) -> SockAddr { + SockAddr::Inet(addr) + } + + pub fn new_unix<P: ?Sized + NixPath>(path: &P) -> Result<SockAddr> { + Ok(SockAddr::Unix(UnixAddr::new(path)?)) + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn new_netlink(pid: u32, groups: u32) -> SockAddr { + SockAddr::Netlink(NetlinkAddr::new(pid, groups)) + } + + #[cfg(any(target_os = "ios", target_os = "macos"))] + pub fn new_sys_control(sockfd: RawFd, name: &str, unit: u32) -> Result<SockAddr> { + SysControlAddr::from_name(sockfd, name, unit).map(|a| SockAddr::SysControl(a)) + } + + pub fn family(&self) -> AddressFamily { + match *self { + SockAddr::Inet(InetAddr::V4(..)) => AddressFamily::Inet, + SockAddr::Inet(InetAddr::V6(..)) => AddressFamily::Inet6, + SockAddr::Unix(..) => AddressFamily::Unix, + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Netlink(..) => AddressFamily::Netlink, + #[cfg(any(target_os = "ios", target_os = "macos"))] + SockAddr::SysControl(..) => AddressFamily::System, + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Link(..) => AddressFamily::Packet, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + SockAddr::Link(..) => AddressFamily::Link + } + } + + pub fn to_str(&self) -> String { + format!("{}", self) + } + + /// Creates a `SockAddr` struct from libc's sockaddr. + /// + /// Supports only the following address families: Unix, Inet (v4 & v6), Netlink and System. + /// Returns None for unsupported families. + pub unsafe fn from_libc_sockaddr(addr: *const libc::sockaddr) -> Option<SockAddr> { + if addr.is_null() { + None + } else { + match AddressFamily::from_i32((*addr).sa_family as i32) { + Some(AddressFamily::Unix) => None, + Some(AddressFamily::Inet) => Some(SockAddr::Inet( + InetAddr::V4(*(addr as *const libc::sockaddr_in)))), + Some(AddressFamily::Inet6) => Some(SockAddr::Inet( + InetAddr::V6(*(addr as *const libc::sockaddr_in6)))), + #[cfg(any(target_os = "android", target_os = "linux"))] + Some(AddressFamily::Netlink) => Some(SockAddr::Netlink( + NetlinkAddr(*(addr as *const libc::sockaddr_nl)))), + #[cfg(any(target_os = "ios", target_os = "macos"))] + Some(AddressFamily::System) => Some(SockAddr::SysControl( + SysControlAddr(*(addr as *const libc::sockaddr_ctl)))), + #[cfg(any(target_os = "android", target_os = "linux"))] + Some(AddressFamily::Packet) => Some(SockAddr::Link( + LinkAddr(*(addr as *const libc::sockaddr_ll)))), + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Some(AddressFamily::Link) => { + let ether_addr = LinkAddr(*(addr as *const libc::sockaddr_dl)); + if ether_addr.is_empty() { + None + } else { + Some(SockAddr::Link(ether_addr)) + } + }, + // Other address families are currently not supported and simply yield a None + // entry instead of a proper conversion to a `SockAddr`. + Some(_) | None => None, + } + } + } + + /// Conversion from nix's SockAddr type to the underlying libc sockaddr type. + /// + /// This is useful for interfacing with other libc functions that don't yet have nix wrappers. + /// Returns a reference to the underlying data type (as a sockaddr reference) along + /// with the size of the actual data type. sockaddr is commonly used as a proxy for + /// a superclass as C doesn't support inheritance, so many functions that take + /// a sockaddr * need to take the size of the underlying type as well and then internally cast it back. + pub unsafe fn as_ffi_pair(&self) -> (&libc::sockaddr, libc::socklen_t) { + match *self { + SockAddr::Inet(InetAddr::V4(ref addr)) => (mem::transmute(addr), mem::size_of::<libc::sockaddr_in>() as libc::socklen_t), + SockAddr::Inet(InetAddr::V6(ref addr)) => (mem::transmute(addr), mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t), + SockAddr::Unix(UnixAddr(ref addr, len)) => (mem::transmute(addr), (len + offset_of!(libc::sockaddr_un, sun_path)) as libc::socklen_t), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Netlink(NetlinkAddr(ref sa)) => (mem::transmute(sa), mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t), + #[cfg(any(target_os = "ios", target_os = "macos"))] + SockAddr::SysControl(SysControlAddr(ref sa)) => (mem::transmute(sa), mem::size_of::<libc::sockaddr_ctl>() as libc::socklen_t), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Link(LinkAddr(ref ether_addr)) => (mem::transmute(ether_addr), mem::size_of::<libc::sockaddr_ll>() as libc::socklen_t), + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + SockAddr::Link(LinkAddr(ref ether_addr)) => (mem::transmute(ether_addr), mem::size_of::<libc::sockaddr_dl>() as libc::socklen_t), + } + } +} + +impl PartialEq for SockAddr { + fn eq(&self, other: &SockAddr) -> bool { + match (*self, *other) { + (SockAddr::Inet(ref a), SockAddr::Inet(ref b)) => { + a == b + } + (SockAddr::Unix(ref a), SockAddr::Unix(ref b)) => { + a == b + } + #[cfg(any(target_os = "android", target_os = "linux"))] + (SockAddr::Netlink(ref a), SockAddr::Netlink(ref b)) => { + a == b + } + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + (SockAddr::Link(ref a), SockAddr::Link(ref b)) => { + a == b + } + _ => false, + } + } +} + +impl Eq for SockAddr { +} + +impl hash::Hash for SockAddr { + fn hash<H: hash::Hasher>(&self, s: &mut H) { + match *self { + SockAddr::Inet(ref a) => a.hash(s), + SockAddr::Unix(ref a) => a.hash(s), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Netlink(ref a) => a.hash(s), + #[cfg(any(target_os = "ios", target_os = "macos"))] + SockAddr::SysControl(ref a) => a.hash(s), + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + SockAddr::Link(ref ether_addr) => ether_addr.hash(s) + } + } +} + +impl Clone for SockAddr { + fn clone(&self) -> SockAddr { + *self + } +} + +impl fmt::Display for SockAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + SockAddr::Inet(ref inet) => inet.fmt(f), + SockAddr::Unix(ref unix) => unix.fmt(f), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Netlink(ref nl) => nl.fmt(f), + #[cfg(any(target_os = "ios", target_os = "macos"))] + SockAddr::SysControl(ref sc) => sc.fmt(f), + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + SockAddr::Link(ref ether_addr) => ether_addr.fmt(f) + } + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub mod netlink { + use ::sys::socket::addr::AddressFamily; + use libc::{sa_family_t, sockaddr_nl}; + use std::{fmt, mem}; + use std::hash::{Hash, Hasher}; + + #[derive(Copy, Clone)] + pub struct NetlinkAddr(pub sockaddr_nl); + + // , PartialEq, Eq, Debug, Hash + impl PartialEq for NetlinkAddr { + fn eq(&self, other: &Self) -> bool { + let (inner, other) = (self.0, other.0); + (inner.nl_family, inner.nl_pid, inner.nl_groups) == + (other.nl_family, other.nl_pid, other.nl_groups) + } + } + + impl Eq for NetlinkAddr {} + + impl Hash for NetlinkAddr { + fn hash<H: Hasher>(&self, s: &mut H) { + let inner = self.0; + (inner.nl_family, inner.nl_pid, inner.nl_groups).hash(s); + } + } + + + impl NetlinkAddr { + pub fn new(pid: u32, groups: u32) -> NetlinkAddr { + let mut addr: sockaddr_nl = unsafe { mem::zeroed() }; + addr.nl_family = AddressFamily::Netlink as sa_family_t; + addr.nl_pid = pid; + addr.nl_groups = groups; + + NetlinkAddr(addr) + } + + pub fn pid(&self) -> u32 { + self.0.nl_pid + } + + pub fn groups(&self) -> u32 { + self.0.nl_groups + } + } + + impl fmt::Display for NetlinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "pid: {} groups: {}", self.pid(), self.groups()) + } + } + + impl fmt::Debug for NetlinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } + } +} + +#[cfg(any(target_os = "ios", target_os = "macos"))] +pub mod sys_control { + use ::sys::socket::addr::AddressFamily; + use libc::{self, c_uchar}; + use std::{fmt, mem}; + use std::hash::{Hash, Hasher}; + use std::os::unix::io::RawFd; + use {Errno, Error, Result}; + + #[repr(C)] + pub struct ctl_ioc_info { + pub ctl_id: u32, + pub ctl_name: [c_uchar; MAX_KCTL_NAME], + } + + const CTL_IOC_MAGIC: u8 = 'N' as u8; + const CTL_IOC_INFO: u8 = 3; + const MAX_KCTL_NAME: usize = 96; + + ioctl_readwrite!(ctl_info, CTL_IOC_MAGIC, CTL_IOC_INFO, ctl_ioc_info); + + #[derive(Copy, Clone)] + #[repr(C)] + pub struct SysControlAddr(pub libc::sockaddr_ctl); + + impl PartialEq for SysControlAddr { + fn eq(&self, other: &Self) -> bool { + let (inner, other) = (self.0, other.0); + (inner.sc_id, inner.sc_unit) == + (other.sc_id, other.sc_unit) + } + } + + impl Eq for SysControlAddr {} + + impl Hash for SysControlAddr { + fn hash<H: Hasher>(&self, s: &mut H) { + let inner = self.0; + (inner.sc_id, inner.sc_unit).hash(s); + } + } + + + impl SysControlAddr { + pub fn new(id: u32, unit: u32) -> SysControlAddr { + let addr = libc::sockaddr_ctl { + sc_len: mem::size_of::<libc::sockaddr_ctl>() as c_uchar, + sc_family: AddressFamily::System as c_uchar, + ss_sysaddr: libc::AF_SYS_CONTROL as u16, + sc_id: id, + sc_unit: unit, + sc_reserved: [0; 5] + }; + + SysControlAddr(addr) + } + + pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result<SysControlAddr> { + if name.len() > MAX_KCTL_NAME { + return Err(Error::Sys(Errno::ENAMETOOLONG)); + } + + let mut ctl_name = [0; MAX_KCTL_NAME]; + ctl_name[..name.len()].clone_from_slice(name.as_bytes()); + let mut info = ctl_ioc_info { ctl_id: 0, ctl_name: ctl_name }; + + unsafe { ctl_info(sockfd, &mut info)?; } + + Ok(SysControlAddr::new(info.ctl_id, unit)) + } + + pub fn id(&self) -> u32 { + self.0.sc_id + } + + pub fn unit(&self) -> u32 { + self.0.sc_unit + } + } + + impl fmt::Display for SysControlAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self, f) + } + } + + impl fmt::Debug for SysControlAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("SysControlAddr") + .field("sc_len", &self.0.sc_len) + .field("sc_family", &self.0.sc_family) + .field("ss_sysaddr", &self.0.ss_sysaddr) + .field("sc_id", &self.0.sc_id) + .field("sc_unit", &self.0.sc_unit) + .finish() + } + } +} + + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod datalink { + use super::{libc, hash, fmt, AddressFamily}; + + /// Hardware Address + #[derive(Clone, Copy)] + pub struct LinkAddr(pub libc::sockaddr_ll); + + impl LinkAddr { + /// Always AF_PACKET + pub fn family(&self) -> AddressFamily { + assert_eq!(self.0.sll_family as i32, libc::AF_PACKET); + AddressFamily::Packet + } + + /// Physical-layer protocol + pub fn protocol(&self) -> u16 { + self.0.sll_protocol + } + + /// Interface number + pub fn ifindex(&self) -> usize { + self.0.sll_ifindex as usize + } + + /// ARP hardware type + pub fn hatype(&self) -> u16 { + self.0.sll_hatype + } + + /// Packet type + pub fn pkttype(&self) -> u8 { + self.0.sll_pkttype + } + + /// Length of MAC address + pub fn halen(&self) -> usize { + self.0.sll_halen as usize + } + + /// Physical-layer address (MAC) + pub fn addr(&self) -> [u8; 6] { + let a = self.0.sll_addr[0] as u8; + let b = self.0.sll_addr[1] as u8; + let c = self.0.sll_addr[2] as u8; + let d = self.0.sll_addr[3] as u8; + let e = self.0.sll_addr[4] as u8; + let f = self.0.sll_addr[5] as u8; + + [a, b, c, d, e, f] + } + } + + impl Eq for LinkAddr {} + + impl PartialEq for LinkAddr { + fn eq(&self, other: &Self) -> bool { + let (a, b) = (self.0, other.0); + (a.sll_family, a.sll_protocol, a.sll_ifindex, a.sll_hatype, + a.sll_pkttype, a.sll_halen, a.sll_addr) == + (b.sll_family, b.sll_protocol, b.sll_ifindex, b.sll_hatype, + b.sll_pkttype, b.sll_halen, b.sll_addr) + } + } + + impl hash::Hash for LinkAddr { + fn hash<H: hash::Hasher>(&self, s: &mut H) { + let a = self.0; + (a.sll_family, a.sll_protocol, a.sll_ifindex, a.sll_hatype, + a.sll_pkttype, a.sll_halen, a.sll_addr).hash(s); + } + } + + impl fmt::Display for LinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let addr = self.addr(); + write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + addr[0], + addr[1], + addr[2], + addr[3], + addr[4], + addr[5]) + } + } + + impl fmt::Debug for LinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } + } +} + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +mod datalink { + use super::{libc, hash, fmt, AddressFamily}; + + /// Hardware Address + #[derive(Clone, Copy)] + pub struct LinkAddr(pub libc::sockaddr_dl); + + impl LinkAddr { + /// Total length of sockaddr + pub fn len(&self) -> usize { + self.0.sdl_len as usize + } + + /// always == AF_LINK + pub fn family(&self) -> AddressFamily { + assert_eq!(self.0.sdl_family as i32, libc::AF_LINK); + AddressFamily::Link + } + + /// interface index, if != 0, system given index for interface + pub fn ifindex(&self) -> usize { + self.0.sdl_index as usize + } + + /// Datalink type + pub fn datalink_type(&self) -> u8 { + self.0.sdl_type + } + + // MAC address start position + pub fn nlen(&self) -> usize { + self.0.sdl_nlen as usize + } + + /// link level address length + pub fn alen(&self) -> usize { + self.0.sdl_alen as usize + } + + /// link layer selector length + pub fn slen(&self) -> usize { + self.0.sdl_slen as usize + } + + /// if link level address length == 0, + /// or `sdl_data` not be larger. + pub fn is_empty(&self) -> bool { + let nlen = self.nlen(); + let alen = self.alen(); + let data_len = self.0.sdl_data.len(); + + if alen > 0 && nlen + alen < data_len { + false + } else { + true + } + } + + /// Physical-layer address (MAC) + pub fn addr(&self) -> [u8; 6] { + let nlen = self.nlen(); + let data = self.0.sdl_data; + + assert!(!self.is_empty()); + + let a = data[nlen] as u8; + let b = data[nlen + 1] as u8; + let c = data[nlen + 2] as u8; + let d = data[nlen + 3] as u8; + let e = data[nlen + 4] as u8; + let f = data[nlen + 5] as u8; + + [a, b, c, d, e, f] + } + } + + impl Eq for LinkAddr {} + + impl PartialEq for LinkAddr { + #[cfg(any(target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + fn eq(&self, other: &Self) -> bool { + let (a, b) = (self.0, other.0); + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, + a.sdl_nlen, a.sdl_alen, a.sdl_slen, &a.sdl_data[..]) == + (b.sdl_len, b.sdl_family, b.sdl_index, b.sdl_type, + b.sdl_nlen, b.sdl_alen, b.sdl_slen, &b.sdl_data[..]) + } + + #[cfg(target_os = "dragonfly")] + fn eq(&self, other: &Self) -> bool { + let (a, b) = (self.0, other.0); + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, a.sdl_nlen, + a.sdl_alen, a.sdl_slen, a.sdl_data, a.sdl_rcf, a.sdl_route) == + (b.sdl_len, b.sdl_family, b.sdl_index, b.sdl_type, b.sdl_nlen, + b.sdl_alen, b.sdl_slen, b.sdl_data, b.sdl_rcf, b.sdl_route) + } + } + + impl hash::Hash for LinkAddr { + #[cfg(any(target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + fn hash<H: hash::Hasher>(&self, s: &mut H) { + let a = self.0; + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, + a.sdl_nlen, a.sdl_alen, a.sdl_slen, &a.sdl_data[..]).hash(s); + } + + #[cfg(target_os = "dragonfly")] + fn hash<H: hash::Hasher>(&self, s: &mut H) { + let a = self.0; + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, a.sdl_nlen, + a.sdl_alen, a.sdl_slen, a.sdl_data, a.sdl_rcf, a.sdl_route).hash(s); + } + } + + impl fmt::Display for LinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let addr = self.addr(); + write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + addr[0], + addr[1], + addr[2], + addr[3], + addr[4], + addr[5]) + } + } + + impl fmt::Debug for LinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } + } +} + +#[cfg(test)] +mod tests { + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + use super::*; + + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + #[test] + fn test_macos_loopback_datalink_addr() { + let bytes = [20i8, 18, 1, 0, 24, 3, 0, 0, 108, 111, 48, 0, 0, 0, 0, 0]; + let sa = bytes.as_ptr() as *const libc::sockaddr; + let _sock_addr = unsafe { SockAddr::from_libc_sockaddr(sa) }; + assert!(_sock_addr.is_none()); + } + + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + #[test] + fn test_macos_tap_datalink_addr() { + let bytes = [20i8, 18, 7, 0, 6, 3, 6, 0, 101, 110, 48, 24, 101, -112, -35, 76, -80]; + let ptr = bytes.as_ptr(); + let sa = ptr as *const libc::sockaddr; + let _sock_addr = unsafe { SockAddr::from_libc_sockaddr(sa) }; + + assert!(_sock_addr.is_some()); + + let sock_addr = _sock_addr.unwrap(); + + assert_eq!(sock_addr.family(), AddressFamily::Link); + + match sock_addr { + SockAddr::Link(ether_addr) => { + assert_eq!(ether_addr.addr(), [24u8, 101, 144, 221, 76, 176]); + }, + _ => { unreachable!() } + }; + } +} diff --git a/third_party/rust/nix/src/sys/socket/mod.rs b/third_party/rust/nix/src/sys/socket/mod.rs new file mode 100644 index 0000000000..62c0ea7412 --- /dev/null +++ b/third_party/rust/nix/src/sys/socket/mod.rs @@ -0,0 +1,1204 @@ +//! Socket interface functions +//! +//! [Further reading](http://man7.org/linux/man-pages/man7/socket.7.html) +use {Error, Result}; +use errno::Errno; +use libc::{self, c_void, c_int, socklen_t, size_t}; +use std::{fmt, mem, ptr, slice}; +use std::os::unix::io::RawFd; +use sys::time::TimeVal; +use sys::uio::IoVec; + +mod addr; +pub mod sockopt; + +/* + * + * ===== Re-exports ===== + * + */ + +pub use self::addr::{ + AddressFamily, + SockAddr, + InetAddr, + UnixAddr, + IpAddr, + Ipv4Addr, + Ipv6Addr, + LinkAddr, +}; +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use ::sys::socket::addr::netlink::NetlinkAddr; + +pub use libc::{ + cmsghdr, + msghdr, + sa_family_t, + sockaddr, + sockaddr_in, + sockaddr_in6, + sockaddr_storage, + sockaddr_un, +}; + +/// These constants are used to specify the communication semantics +/// when creating a socket with [`socket()`](fn.socket.html) +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[repr(i32)] +pub enum SockType { + /// Provides sequenced, reliable, two-way, connection- + /// based byte streams. An out-of-band data transmission + /// mechanism may be supported. + Stream = libc::SOCK_STREAM, + /// Supports datagrams (connectionless, unreliable + /// messages of a fixed maximum length). + Datagram = libc::SOCK_DGRAM, + /// Provides a sequenced, reliable, two-way connection- + /// based data transmission path for datagrams of fixed + /// maximum length; a consumer is required to read an + /// entire packet with each input system call. + SeqPacket = libc::SOCK_SEQPACKET, + /// Provides raw network protocol access. + Raw = libc::SOCK_RAW, + /// Provides a reliable datagram layer that does not + /// guarantee ordering. + Rdm = libc::SOCK_RDM, +} + +/// Constants used in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html) +/// to specify the protocol to use. +#[repr(i32)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SockProtocol { + /// TCP protocol ([ip(7)](http://man7.org/linux/man-pages/man7/ip.7.html)) + Tcp = libc::IPPROTO_TCP, + /// UDP protocol ([ip(7)](http://man7.org/linux/man-pages/man7/ip.7.html)) + Udp = libc::IPPROTO_UDP, + /// Allows applications and other KEXTs to be notified when certain kernel events occur + /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html)) + #[cfg(any(target_os = "ios", target_os = "macos"))] + KextEvent = libc::SYSPROTO_EVENT, + /// Allows applications to configure and control a KEXT + /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html)) + #[cfg(any(target_os = "ios", target_os = "macos"))] + KextControl = libc::SYSPROTO_CONTROL, +} + +libc_bitflags!{ + /// Additional socket options + pub struct SockFlag: c_int { + /// Set non-blocking mode on the new socket + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] + SOCK_NONBLOCK; + /// Set close-on-exec on the new descriptor + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] + SOCK_CLOEXEC; + /// Return `EPIPE` instead of raising `SIGPIPE` + #[cfg(target_os = "netbsd")] + SOCK_NOSIGPIPE; + /// For domains `AF_INET(6)`, only allow `connect(2)`, `sendto(2)`, or `sendmsg(2)` + /// to the DNS port (typically 53) + #[cfg(target_os = "openbsd")] + SOCK_DNS; + } +} + +libc_bitflags!{ + /// Flags for send/recv and their relatives + pub struct MsgFlags: c_int { + /// Sends or requests out-of-band data on sockets that support this notion + /// (e.g., of type [`Stream`](enum.SockType.html)); the underlying protocol must also + /// support out-of-band data. + MSG_OOB; + /// Peeks at an incoming message. The data is treated as unread and the next + /// [`recv()`](fn.recv.html) + /// or similar function shall still return this data. + MSG_PEEK; + /// Enables nonblocking operation; if the operation would block, + /// `EAGAIN` or `EWOULDBLOCK` is returned. This provides similar + /// behavior to setting the `O_NONBLOCK` flag + /// (via the [`fcntl`](../../fcntl/fn.fcntl.html) + /// `F_SETFL` operation), but differs in that `MSG_DONTWAIT` is a per- + /// call option, whereas `O_NONBLOCK` is a setting on the open file + /// description (see [open(2)](http://man7.org/linux/man-pages/man2/open.2.html)), + /// which will affect all threads in + /// the calling process and as well as other processes that hold + /// file descriptors referring to the same open file description. + MSG_DONTWAIT; + /// Receive flags: Control Data was discarded (buffer too small) + MSG_CTRUNC; + /// For raw ([`Packet`](addr/enum.AddressFamily.html)), Internet datagram + /// (since Linux 2.4.27/2.6.8), + /// netlink (since Linux 2.6.22) and UNIX datagram (since Linux 3.4) + /// sockets: return the real length of the packet or datagram, even + /// when it was longer than the passed buffer. Not implemented for UNIX + /// domain ([unix(7)](https://linux.die.net/man/7/unix)) sockets. + /// + /// For use with Internet stream sockets, see [tcp(7)](https://linux.die.net/man/7/tcp). + MSG_TRUNC; + /// Terminates a record (when this notion is supported, as for + /// sockets of type [`SeqPacket`](enum.SockType.html)). + MSG_EOR; + /// This flag specifies that queued errors should be received from + /// the socket error queue. (For more details, see + /// [recvfrom(2)](https://linux.die.net/man/2/recvfrom)) + #[cfg(any(target_os = "android", target_os = "linux"))] + MSG_ERRQUEUE; + /// Set the `close-on-exec` flag for the file descriptor received via a UNIX domain + /// file descriptor using the `SCM_RIGHTS` operation (described in + /// [unix(7)](https://linux.die.net/man/7/unix)). + /// This flag is useful for the same reasons as the `O_CLOEXEC` flag of + /// [open(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html). + /// + /// Only used in [`recvmsg`](fn.recvmsg.html) function. + #[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] + MSG_CMSG_CLOEXEC; + } +} + +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + /// Unix credentials of the sending process. + /// + /// This struct is used with the `SO_PEERCRED` ancillary message for UNIX sockets. + #[repr(C)] + #[derive(Clone, Copy)] + pub struct UnixCredentials(libc::ucred); + + impl UnixCredentials { + /// Returns the process identifier + pub fn pid(&self) -> libc::pid_t { + self.0.pid + } + + /// Returns the user identifier + pub fn uid(&self) -> libc::uid_t { + self.0.uid + } + + /// Returns the group identifier + pub fn gid(&self) -> libc::gid_t { + self.0.gid + } + } + + impl PartialEq for UnixCredentials { + fn eq(&self, other: &Self) -> bool { + self.0.pid == other.0.pid && self.0.uid == other.0.uid && self.0.gid == other.0.gid + } + } + impl Eq for UnixCredentials {} + + impl From<libc::ucred> for UnixCredentials { + fn from(cred: libc::ucred) -> Self { + UnixCredentials(cred) + } + } + + impl Into<libc::ucred> for UnixCredentials { + fn into(self) -> libc::ucred { + self.0 + } + } + + impl fmt::Debug for UnixCredentials { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("UnixCredentials") + .field("pid", &self.0.pid) + .field("uid", &self.0.uid) + .field("gid", &self.0.gid) + .finish() + } + } + } +} + +/// Request for multicast socket operations +/// +/// This is a wrapper type around `ip_mreq`. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IpMembershipRequest(libc::ip_mreq); + +impl IpMembershipRequest { + /// Instantiate a new `IpMembershipRequest` + /// + /// If `interface` is `None`, then `Ipv4Addr::any()` will be used for the interface. + pub fn new(group: Ipv4Addr, interface: Option<Ipv4Addr>) -> Self { + IpMembershipRequest(libc::ip_mreq { + imr_multiaddr: group.0, + imr_interface: interface.unwrap_or_else(Ipv4Addr::any).0, + }) + } +} + +impl PartialEq for IpMembershipRequest { + fn eq(&self, other: &Self) -> bool { + self.0.imr_multiaddr.s_addr == other.0.imr_multiaddr.s_addr + && self.0.imr_interface.s_addr == other.0.imr_interface.s_addr + } +} +impl Eq for IpMembershipRequest {} + +impl fmt::Debug for IpMembershipRequest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mref = &self.0.imr_multiaddr; + let maddr = mref.s_addr; + let iref = &self.0.imr_interface; + let ifaddr = iref.s_addr; + f.debug_struct("IpMembershipRequest") + .field("imr_multiaddr", &maddr) + .field("imr_interface", &ifaddr) + .finish() + } +} + +/// Request for ipv6 multicast socket operations +/// +/// This is a wrapper type around `ipv6_mreq`. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct Ipv6MembershipRequest(libc::ipv6_mreq); + +impl Ipv6MembershipRequest { + /// Instantiate a new `Ipv6MembershipRequest` + pub fn new(group: Ipv6Addr) -> Self { + Ipv6MembershipRequest(libc::ipv6_mreq { + ipv6mr_multiaddr: group.0, + ipv6mr_interface: 0, + }) + } +} + +impl PartialEq for Ipv6MembershipRequest { + fn eq(&self, other: &Self) -> bool { + self.0.ipv6mr_multiaddr.s6_addr == other.0.ipv6mr_multiaddr.s6_addr && + self.0.ipv6mr_interface == other.0.ipv6mr_interface + } +} +impl Eq for Ipv6MembershipRequest {} + +impl fmt::Debug for Ipv6MembershipRequest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Ipv6MembershipRequest") + .field("ipv6mr_multiaddr", &self.0.ipv6mr_multiaddr.s6_addr) + .field("ipv6mr_interface", &self.0.ipv6mr_interface) + .finish() + } +} + +/// Copy the in-memory representation of `src` into the byte slice `dst`. +/// +/// Returns the remainder of `dst`. +/// +/// Panics when `dst` is too small for `src` (more precisely, panics if +/// `mem::size_of_val(src) >= dst.len()`). +/// +/// Unsafe because it transmutes `src` to raw bytes, which is only safe for some +/// types `T`. Refer to the [Rustonomicon] for details. +/// +/// [Rustonomicon]: https://doc.rust-lang.org/nomicon/transmutes.html +unsafe fn copy_bytes<'a, T: ?Sized>(src: &T, dst: &'a mut [u8]) -> &'a mut [u8] { + let srclen = mem::size_of_val(src); + ptr::copy_nonoverlapping( + src as *const T as *const u8, + dst[..srclen].as_mut_ptr(), + srclen + ); + + &mut dst[srclen..] +} + +/// Fills `dst` with `len` zero bytes and returns the remainder of the slice. +/// +/// Panics when `len >= dst.len()`. +fn pad_bytes(len: usize, dst: &mut [u8]) -> &mut [u8] { + for pad in &mut dst[..len] { + *pad = 0; + } + + &mut dst[len..] +} + +cfg_if! { + // Darwin and DragonFly BSD always align struct cmsghdr to 32-bit only. + if #[cfg(any(target_os = "dragonfly", target_os = "ios", target_os = "macos"))] { + type align_of_cmsg_data = u32; + } else { + type align_of_cmsg_data = size_t; + } +} + +/// A structure used to make room in a cmsghdr passed to recvmsg. The +/// size and alignment match that of a cmsghdr followed by a T, but the +/// fields are not accessible, as the actual types will change on a call +/// to recvmsg. +/// +/// To make room for multiple messages, nest the type parameter with +/// tuples: +/// +/// ``` +/// use std::os::unix::io::RawFd; +/// use nix::sys::socket::CmsgSpace; +/// let cmsg: CmsgSpace<([RawFd; 3], CmsgSpace<[RawFd; 2]>)> = CmsgSpace::new(); +/// ``` +#[repr(C)] +#[allow(missing_debug_implementations)] +pub struct CmsgSpace<T> { + _hdr: cmsghdr, + _pad: [align_of_cmsg_data; 0], + _data: T, +} + +impl<T> CmsgSpace<T> { + /// Create a CmsgSpace<T>. The structure is used only for space, so + /// the fields are uninitialized. + pub fn new() -> Self { + // Safe because the fields themselves aren't accessible. + unsafe { mem::uninitialized() } + } +} + +#[derive(Debug)] +pub struct RecvMsg<'a> { + // The number of bytes received. + pub bytes: usize, + cmsg_buffer: &'a [u8], + pub address: Option<SockAddr>, + pub flags: MsgFlags, +} + +impl<'a> RecvMsg<'a> { + /// Iterate over the valid control messages pointed to by this + /// msghdr. + pub fn cmsgs(&self) -> CmsgIterator { + CmsgIterator { + buf: self.cmsg_buffer, + } + } +} + +#[derive(Debug)] +pub struct CmsgIterator<'a> { + /// Control message buffer to decode from. Must adhere to cmsg alignment. + buf: &'a [u8], +} + +impl<'a> Iterator for CmsgIterator<'a> { + type Item = ControlMessage<'a>; + + // The implementation loosely follows CMSG_FIRSTHDR / CMSG_NXTHDR, + // although we handle the invariants in slightly different places to + // get a better iterator interface. + fn next(&mut self) -> Option<ControlMessage<'a>> { + if self.buf.len() == 0 { + // The iterator assumes that `self.buf` always contains exactly the + // bytes we need, so we're at the end when the buffer is empty. + return None; + } + + // Safe if: `self.buf` is `cmsghdr`-aligned. + let cmsg: &'a cmsghdr = unsafe { + &*(self.buf[..mem::size_of::<cmsghdr>()].as_ptr() as *const cmsghdr) + }; + + let cmsg_len = cmsg.cmsg_len as usize; + + // Advance our internal pointer. + let cmsg_data = &self.buf[cmsg_align(mem::size_of::<cmsghdr>())..cmsg_len]; + self.buf = &self.buf[cmsg_align(cmsg_len)..]; + + // Safe if: `cmsg_data` contains the expected (amount of) content. This + // is verified by the kernel. + unsafe { + Some(ControlMessage::decode_from(cmsg, cmsg_data)) + } + } +} + +/// A type-safe wrapper around a single control message. More types may +/// be added to this enum; do not exhaustively pattern-match it. +/// [Further reading](http://man7.org/linux/man-pages/man3/cmsg.3.html) +#[allow(missing_debug_implementations)] +pub enum ControlMessage<'a> { + /// A message of type `SCM_RIGHTS`, containing an array of file + /// descriptors passed between processes. + /// + /// See the description in the "Ancillary messages" section of the + /// [unix(7) man page](http://man7.org/linux/man-pages/man7/unix.7.html). + /// + /// Using multiple `ScmRights` messages for a single `sendmsg` call isn't recommended since it + /// causes platform-dependent behaviour: It might swallow all but the first `ScmRights` message + /// or fail with `EINVAL`. Instead, you can put all fds to be passed into a single `ScmRights` + /// message. + ScmRights(&'a [RawFd]), + /// A message of type `SCM_CREDENTIALS`, containing the pid, uid and gid of + /// a process connected to the socket. + /// + /// This is similar to the socket option `SO_PEERCRED`, but requires a + /// process to explicitly send its credentials. A process running as root is + /// allowed to specify any credentials, while credentials sent by other + /// processes are verified by the kernel. + /// + /// For further information, please refer to the + /// [`unix(7)`](http://man7.org/linux/man-pages/man7/unix.7.html) man page. + // FIXME: When `#[repr(transparent)]` is stable, use it on `UnixCredentials` + // and put that in here instead of a raw ucred. + #[cfg(any(target_os = "android", target_os = "linux"))] + ScmCredentials(&'a libc::ucred), + /// A message of type `SCM_TIMESTAMP`, containing the time the + /// packet was received by the kernel. + /// + /// See the kernel's explanation in "SO_TIMESTAMP" of + /// [networking/timestamping](https://www.kernel.org/doc/Documentation/networking/timestamping.txt). + /// + /// # Examples + /// + // Disable this test on FreeBSD i386 + // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=222039 + #[cfg_attr(not(all(target_os = "freebsd", target_arch = "x86")), doc = " ```")] + #[cfg_attr(all(target_os = "freebsd", target_arch = "x86"), doc = " ```no_run")] + /// use nix::sys::socket::*; + /// use nix::sys::uio::IoVec; + /// use nix::sys::time::*; + /// use std::time::*; + /// + /// // Set up + /// let message = "Ohayō!".as_bytes(); + /// let in_socket = socket( + /// AddressFamily::Inet, + /// SockType::Datagram, + /// SockFlag::empty(), + /// None).unwrap(); + /// setsockopt(in_socket, sockopt::ReceiveTimestamp, &true).unwrap(); + /// let localhost = InetAddr::new(IpAddr::new_v4(127, 0, 0, 1), 0); + /// bind(in_socket, &SockAddr::new_inet(localhost)).unwrap(); + /// let address = getsockname(in_socket).unwrap(); + /// // Get initial time + /// let time0 = SystemTime::now(); + /// // Send the message + /// let iov = [IoVec::from_slice(message)]; + /// let flags = MsgFlags::empty(); + /// let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap(); + /// assert_eq!(message.len(), l); + /// // Receive the message + /// let mut buffer = vec![0u8; message.len()]; + /// let mut cmsgspace: CmsgSpace<TimeVal> = CmsgSpace::new(); + /// let iov = [IoVec::from_mut_slice(&mut buffer)]; + /// let r = recvmsg(in_socket, &iov, Some(&mut cmsgspace), flags).unwrap(); + /// let rtime = match r.cmsgs().next() { + /// Some(ControlMessage::ScmTimestamp(&rtime)) => rtime, + /// Some(_) => panic!("Unexpected control message"), + /// None => panic!("No control message") + /// }; + /// // Check the final time + /// let time1 = SystemTime::now(); + /// // the packet's received timestamp should lie in-between the two system + /// // times, unless the system clock was adjusted in the meantime. + /// let rduration = Duration::new(rtime.tv_sec() as u64, + /// rtime.tv_usec() as u32 * 1000); + /// assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration); + /// assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap()); + /// // Close socket + /// nix::unistd::close(in_socket).unwrap(); + /// ``` + ScmTimestamp(&'a TimeVal), + + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + Ipv4PacketInfo(&'a libc::in_pktinfo), + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + Ipv6PacketInfo(&'a libc::in6_pktinfo), + + /// Catch-all variant for unimplemented cmsg types. + #[doc(hidden)] + Unknown(UnknownCmsg<'a>), +} + +// An opaque structure used to prevent cmsghdr from being a public type +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct UnknownCmsg<'a>(&'a cmsghdr, &'a [u8]); + +// Round `len` up to meet the platform's required alignment for +// `cmsghdr`s and trailing `cmsghdr` data. This should match the +// behaviour of CMSG_ALIGN from the Linux headers and do the correct +// thing on other platforms that don't usually provide CMSG_ALIGN. +#[inline] +fn cmsg_align(len: usize) -> usize { + let align_bytes = mem::size_of::<align_of_cmsg_data>() - 1; + (len + align_bytes) & !align_bytes +} + +impl<'a> ControlMessage<'a> { + /// The value of CMSG_SPACE on this message. + fn space(&self) -> usize { + cmsg_align(self.len()) + } + + /// The value of CMSG_LEN on this message. + fn len(&self) -> usize { + cmsg_align(mem::size_of::<cmsghdr>()) + match *self { + ControlMessage::ScmRights(fds) => { + mem::size_of_val(fds) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::ScmCredentials(creds) => { + mem::size_of_val(creds) + } + ControlMessage::ScmTimestamp(t) => { + mem::size_of_val(t) + }, + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv4PacketInfo(pktinfo) => { + mem::size_of_val(pktinfo) + }, + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv6PacketInfo(pktinfo) => { + mem::size_of_val(pktinfo) + }, + ControlMessage::Unknown(UnknownCmsg(_, bytes)) => { + mem::size_of_val(bytes) + } + } + } + + /// Returns the value to put into the `cmsg_level` field of the header. + fn cmsg_level(&self) -> libc::c_int { + match *self { + ControlMessage::ScmRights(_) => libc::SOL_SOCKET, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET, + ControlMessage::ScmTimestamp(_) => libc::SOL_SOCKET, + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv4PacketInfo(_) => libc::IPPROTO_IP, + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6, + ControlMessage::Unknown(ref cmsg) => cmsg.0.cmsg_level, + } + } + + /// Returns the value to put into the `cmsg_type` field of the header. + fn cmsg_type(&self) -> libc::c_int { + match *self { + ControlMessage::ScmRights(_) => libc::SCM_RIGHTS, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS, + ControlMessage::ScmTimestamp(_) => libc::SCM_TIMESTAMP, + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv4PacketInfo(_) => libc::IP_PKTINFO, + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO, + ControlMessage::Unknown(ref cmsg) => cmsg.0.cmsg_type, + } + } + + // Unsafe: start and end of buffer must be cmsg_align'd. Updates + // the provided slice; panics if the buffer is too small. + unsafe fn encode_into(&self, buf: &mut [u8]) { + let final_buf = if let ControlMessage::Unknown(ref cmsg) = *self { + let &UnknownCmsg(orig_cmsg, bytes) = cmsg; + + let buf = copy_bytes(orig_cmsg, buf); + + let padlen = cmsg_align(mem::size_of_val(&orig_cmsg)) - + mem::size_of_val(&orig_cmsg); + let buf = pad_bytes(padlen, buf); + + copy_bytes(bytes, buf) + } else { + let cmsg = cmsghdr { + cmsg_len: self.len() as _, + cmsg_level: self.cmsg_level(), + cmsg_type: self.cmsg_type(), + ..mem::zeroed() // zero out platform-dependent padding fields + }; + let buf = copy_bytes(&cmsg, buf); + + let padlen = cmsg_align(mem::size_of_val(&cmsg)) - + mem::size_of_val(&cmsg); + let buf = pad_bytes(padlen, buf); + + match *self { + ControlMessage::ScmRights(fds) => { + copy_bytes(fds, buf) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + ControlMessage::ScmCredentials(creds) => { + copy_bytes(creds, buf) + }, + ControlMessage::ScmTimestamp(t) => { + copy_bytes(t, buf) + }, + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv4PacketInfo(pktinfo) => { + copy_bytes(pktinfo, buf) + }, + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + ControlMessage::Ipv6PacketInfo(pktinfo) => { + copy_bytes(pktinfo, buf) + } + ControlMessage::Unknown(_) => unreachable!(), + } + }; + + let padlen = self.space() - self.len(); + pad_bytes(padlen, final_buf); + } + + /// Decodes a `ControlMessage` from raw bytes. + /// + /// This is only safe to call if the data is correct for the message type + /// specified in the header. Normally, the kernel ensures that this is the + /// case. "Correct" in this case includes correct length, alignment and + /// actual content. + unsafe fn decode_from(header: &'a cmsghdr, data: &'a [u8]) -> ControlMessage<'a> { + match (header.cmsg_level, header.cmsg_type) { + (libc::SOL_SOCKET, libc::SCM_RIGHTS) => { + ControlMessage::ScmRights( + slice::from_raw_parts(data.as_ptr() as *const _, + data.len() / mem::size_of::<RawFd>())) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + (libc::SOL_SOCKET, libc::SCM_CREDENTIALS) => { + ControlMessage::ScmCredentials( + &*(data.as_ptr() as *const _) + ) + } + (libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => { + ControlMessage::ScmTimestamp( + &*(data.as_ptr() as *const _)) + }, + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => { + ControlMessage::Ipv6PacketInfo( + &*(data.as_ptr() as *const _)) + } + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" + ))] + (libc::IPPROTO_IP, libc::IP_PKTINFO) => { + ControlMessage::Ipv4PacketInfo( + &*(data.as_ptr() as *const _)) + } + + (_, _) => { + ControlMessage::Unknown(UnknownCmsg(header, data)) + } + } + } +} + + +/// Send data in scatter-gather vectors to a socket, possibly accompanied +/// by ancillary data. Optionally direct the message at the given address, +/// as with sendto. +/// +/// Allocates if cmsgs is nonempty. +pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<'a>], flags: MsgFlags, addr: Option<&'a SockAddr>) -> Result<usize> { + let mut capacity = 0; + for cmsg in cmsgs { + capacity += cmsg.space(); + } + // Note that the resulting vector claims to have length == capacity, + // so it's presently uninitialized. + let mut cmsg_buffer = unsafe { + let mut vec = Vec::<u8>::with_capacity(capacity); + vec.set_len(capacity); + vec + }; + { + let mut ofs = 0; + for cmsg in cmsgs { + let ptr = &mut cmsg_buffer[ofs..]; + unsafe { + cmsg.encode_into(ptr); + } + ofs += cmsg.space(); + } + } + + let (name, namelen) = match addr { + Some(addr) => { let (x, y) = unsafe { addr.as_ffi_pair() }; (x as *const _, y) } + None => (ptr::null(), 0), + }; + + let cmsg_ptr = if capacity > 0 { + cmsg_buffer.as_ptr() as *const c_void + } else { + ptr::null() + }; + + let mhdr = unsafe { + let mut mhdr: msghdr = mem::uninitialized(); + mhdr.msg_name = name as *mut _; + mhdr.msg_namelen = namelen; + mhdr.msg_iov = iov.as_ptr() as *mut _; + mhdr.msg_iovlen = iov.len() as _; + mhdr.msg_control = cmsg_ptr as *mut _; + mhdr.msg_controllen = capacity as _; + mhdr.msg_flags = 0; + mhdr + }; + let ret = unsafe { libc::sendmsg(fd, &mhdr, flags.bits()) }; + + Errno::result(ret).map(|r| r as usize) +} + +/// Receive message in scatter-gather vectors from a socket, and +/// optionally receive ancillary data into the provided buffer. +/// If no ancillary data is desired, use () as the type parameter. +pub fn recvmsg<'a, T>(fd: RawFd, iov: &[IoVec<&mut [u8]>], cmsg_buffer: Option<&'a mut CmsgSpace<T>>, flags: MsgFlags) -> Result<RecvMsg<'a>> { + let mut address: sockaddr_storage = unsafe { mem::uninitialized() }; + let (msg_control, msg_controllen) = match cmsg_buffer { + Some(cmsg_buffer) => (cmsg_buffer as *mut _, mem::size_of_val(cmsg_buffer)), + None => (ptr::null_mut(), 0), + }; + let mut mhdr = unsafe { + let mut mhdr: msghdr = mem::uninitialized(); + mhdr.msg_name = &mut address as *mut _ as *mut _; + mhdr.msg_namelen = mem::size_of::<sockaddr_storage>() as socklen_t; + mhdr.msg_iov = iov.as_ptr() as *mut _; + mhdr.msg_iovlen = iov.len() as _; + mhdr.msg_control = msg_control as *mut _; + mhdr.msg_controllen = msg_controllen as _; + mhdr.msg_flags = 0; + mhdr + }; + let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) }; + + let cmsg_buffer = if msg_controllen > 0 { + // got control message(s) + debug_assert!(!mhdr.msg_control.is_null()); + unsafe { + // Safe: The pointer is not null and the length is correct as part of `recvmsg`s + // contract. + slice::from_raw_parts(mhdr.msg_control as *const u8, + mhdr.msg_controllen as usize) + } + } else { + // No control message, create an empty buffer to avoid creating a slice from a null pointer + &[] + }; + + Ok(unsafe { RecvMsg { + bytes: Errno::result(ret)? as usize, + cmsg_buffer, + address: sockaddr_storage_to_addr(&address, + mhdr.msg_namelen as usize).ok(), + flags: MsgFlags::from_bits_truncate(mhdr.msg_flags), + } }) +} + + +/// Create an endpoint for communication +/// +/// The `protocol` specifies a particular protocol to be used with the +/// socket. Normally only a single protocol exists to support a +/// particular socket type within a given protocol family, in which case +/// protocol can be specified as `None`. However, it is possible that many +/// protocols may exist, in which case a particular protocol must be +/// specified in this manner. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html) +pub fn socket<T: Into<Option<SockProtocol>>>(domain: AddressFamily, ty: SockType, flags: SockFlag, protocol: T) -> Result<RawFd> { + let protocol = match protocol.into() { + None => 0, + Some(p) => p as c_int, + }; + + // SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a + // little easier to understand by separating it out. So we have to merge these bitfields + // here. + let mut ty = ty as c_int; + ty |= flags.bits(); + + let res = unsafe { libc::socket(domain as c_int, ty, protocol) }; + + Errno::result(res) +} + +/// Create a pair of connected sockets +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html) +pub fn socketpair<T: Into<Option<SockProtocol>>>(domain: AddressFamily, ty: SockType, protocol: T, + flags: SockFlag) -> Result<(RawFd, RawFd)> { + let protocol = match protocol.into() { + None => 0, + Some(p) => p as c_int, + }; + + // SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a + // little easier to understand by separating it out. So we have to merge these bitfields + // here. + let mut ty = ty as c_int; + ty |= flags.bits(); + + let mut fds = [-1, -1]; + + let res = unsafe { libc::socketpair(domain as c_int, ty, protocol, fds.as_mut_ptr()) }; + Errno::result(res)?; + + Ok((fds[0], fds[1])) +} + +/// Listen for connections on a socket +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html) +pub fn listen(sockfd: RawFd, backlog: usize) -> Result<()> { + let res = unsafe { libc::listen(sockfd, backlog as c_int) }; + + Errno::result(res).map(drop) +} + +/// Bind a name to a socket +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html) +pub fn bind(fd: RawFd, addr: &SockAddr) -> Result<()> { + let res = unsafe { + let (ptr, len) = addr.as_ffi_pair(); + libc::bind(fd, ptr, len) + }; + + Errno::result(res).map(drop) +} + +/// Accept a connection on a socket +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html) +pub fn accept(sockfd: RawFd) -> Result<RawFd> { + let res = unsafe { libc::accept(sockfd, ptr::null_mut(), ptr::null_mut()) }; + + Errno::result(res) +} + +/// Accept a connection on a socket +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/accept.2.html) +#[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "linux", + target_os = "openbsd"))] +pub fn accept4(sockfd: RawFd, flags: SockFlag) -> Result<RawFd> { + let res = unsafe { libc::accept4(sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits()) }; + + Errno::result(res) +} + +/// Initiate a connection on a socket +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html) +pub fn connect(fd: RawFd, addr: &SockAddr) -> Result<()> { + let res = unsafe { + let (ptr, len) = addr.as_ffi_pair(); + libc::connect(fd, ptr, len) + }; + + Errno::result(res).map(drop) +} + +/// Receive data from a connection-oriented socket. Returns the number of +/// bytes read +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html) +pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: MsgFlags) -> Result<usize> { + unsafe { + let ret = libc::recv( + sockfd, + buf.as_ptr() as *mut c_void, + buf.len() as size_t, + flags.bits()); + + Errno::result(ret).map(|r| r as usize) + } +} + +/// Receive data from a connectionless or connection-oriented socket. Returns +/// the number of bytes read and the socket address of the sender. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html) +pub fn recvfrom(sockfd: RawFd, buf: &mut [u8]) -> Result<(usize, SockAddr)> { + unsafe { + let addr: sockaddr_storage = mem::zeroed(); + let mut len = mem::size_of::<sockaddr_storage>() as socklen_t; + + let ret = Errno::result(libc::recvfrom( + sockfd, + buf.as_ptr() as *mut c_void, + buf.len() as size_t, + 0, + mem::transmute(&addr), + &mut len as *mut socklen_t))?; + + sockaddr_storage_to_addr(&addr, len as usize) + .map(|addr| (ret as usize, addr)) + } +} + +/// Send a message to a socket +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html) +pub fn sendto(fd: RawFd, buf: &[u8], addr: &SockAddr, flags: MsgFlags) -> Result<usize> { + let ret = unsafe { + let (ptr, len) = addr.as_ffi_pair(); + libc::sendto(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags.bits(), ptr, len) + }; + + Errno::result(ret).map(|r| r as usize) +} + +/// Send data to a connection-oriented socket. Returns the number of bytes read +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html) +pub fn send(fd: RawFd, buf: &[u8], flags: MsgFlags) -> Result<usize> { + let ret = unsafe { + libc::send(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags.bits()) + }; + + Errno::result(ret).map(|r| r as usize) +} + +/* + * + * ===== Socket Options ===== + * + */ + +/// The protocol level at which to get / set socket options. Used as an +/// argument to `getsockopt` and `setsockopt`. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html) +#[repr(i32)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SockLevel { + Socket = libc::SOL_SOCKET, + Tcp = libc::IPPROTO_TCP, + Ip = libc::IPPROTO_IP, + Ipv6 = libc::IPPROTO_IPV6, + Udp = libc::IPPROTO_UDP, + #[cfg(any(target_os = "android", target_os = "linux"))] + Netlink = libc::SOL_NETLINK, +} + +/// Represents a socket option that can be accessed or set. Used as an argument +/// to `getsockopt` +pub trait GetSockOpt : Copy { + type Val; + + #[doc(hidden)] + fn get(&self, fd: RawFd) -> Result<Self::Val>; +} + +/// Represents a socket option that can be accessed or set. Used as an argument +/// to `setsockopt` +pub trait SetSockOpt : Copy { + type Val; + + #[doc(hidden)] + fn set(&self, fd: RawFd, val: &Self::Val) -> Result<()>; +} + +/// Get the current value for the requested socket option +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html) +pub fn getsockopt<O: GetSockOpt>(fd: RawFd, opt: O) -> Result<O::Val> { + opt.get(fd) +} + +/// Sets the value for the requested socket option +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html) +/// +/// # Examples +/// +/// ``` +/// use nix::sys::socket::setsockopt; +/// use nix::sys::socket::sockopt::KeepAlive; +/// use std::net::TcpListener; +/// use std::os::unix::io::AsRawFd; +/// +/// let listener = TcpListener::bind("0.0.0.0:0").unwrap(); +/// let fd = listener.as_raw_fd(); +/// let res = setsockopt(fd, KeepAlive, &true); +/// assert!(res.is_ok()); +/// ``` +pub fn setsockopt<O: SetSockOpt>(fd: RawFd, opt: O, val: &O::Val) -> Result<()> { + opt.set(fd, val) +} + +/// Get the address of the peer connected to the socket `fd`. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html) +pub fn getpeername(fd: RawFd) -> Result<SockAddr> { + unsafe { + let addr: sockaddr_storage = mem::uninitialized(); + let mut len = mem::size_of::<sockaddr_storage>() as socklen_t; + + let ret = libc::getpeername(fd, mem::transmute(&addr), &mut len); + + Errno::result(ret)?; + + sockaddr_storage_to_addr(&addr, len as usize) + } +} + +/// Get the current address to which the socket `fd` is bound. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html) +pub fn getsockname(fd: RawFd) -> Result<SockAddr> { + unsafe { + let addr: sockaddr_storage = mem::uninitialized(); + let mut len = mem::size_of::<sockaddr_storage>() as socklen_t; + + let ret = libc::getsockname(fd, mem::transmute(&addr), &mut len); + + Errno::result(ret)?; + + sockaddr_storage_to_addr(&addr, len as usize) + } +} + +/// Return the appropriate `SockAddr` type from a `sockaddr_storage` of a certain +/// size. In C this would usually be done by casting. The `len` argument +/// should be the number of bytes in the `sockaddr_storage` that are actually +/// allocated and valid. It must be at least as large as all the useful parts +/// of the structure. Note that in the case of a `sockaddr_un`, `len` need not +/// include the terminating null. +pub unsafe fn sockaddr_storage_to_addr( + addr: &sockaddr_storage, + len: usize) -> Result<SockAddr> { + + if len < mem::size_of_val(&addr.ss_family) { + return Err(Error::Sys(Errno::ENOTCONN)); + } + + match addr.ss_family as c_int { + libc::AF_INET => { + assert!(len as usize == mem::size_of::<sockaddr_in>()); + let ret = *(addr as *const _ as *const sockaddr_in); + Ok(SockAddr::Inet(InetAddr::V4(ret))) + } + libc::AF_INET6 => { + assert!(len as usize == mem::size_of::<sockaddr_in6>()); + Ok(SockAddr::Inet(InetAddr::V6(*(addr as *const _ as *const sockaddr_in6)))) + } + libc::AF_UNIX => { + let sun = *(addr as *const _ as *const sockaddr_un); + let pathlen = len - offset_of!(sockaddr_un, sun_path); + Ok(SockAddr::Unix(UnixAddr(sun, pathlen))) + } + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_NETLINK => { + use libc::sockaddr_nl; + Ok(SockAddr::Netlink(NetlinkAddr(*(addr as *const _ as *const sockaddr_nl)))) + } + af => panic!("unexpected address family {}", af), + } +} + + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum Shutdown { + /// Further receptions will be disallowed. + Read, + /// Further transmissions will be disallowed. + Write, + /// Further receptions and transmissions will be disallowed. + Both, +} + +/// Shut down part of a full-duplex connection. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html) +pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> { + unsafe { + use libc::shutdown; + + let how = match how { + Shutdown::Read => libc::SHUT_RD, + Shutdown::Write => libc::SHUT_WR, + Shutdown::Both => libc::SHUT_RDWR, + }; + + Errno::result(shutdown(df, how)).map(drop) + } +} diff --git a/third_party/rust/nix/src/sys/socket/sockopt.rs b/third_party/rust/nix/src/sys/socket/sockopt.rs new file mode 100644 index 0000000000..6c5f450dff --- /dev/null +++ b/third_party/rust/nix/src/sys/socket/sockopt.rs @@ -0,0 +1,631 @@ +use super::{GetSockOpt, SetSockOpt}; +use Result; +use errno::Errno; +use sys::time::TimeVal; +use libc::{self, c_int, c_void, socklen_t}; +use std::mem; +use std::os::unix::io::RawFd; +use std::ffi::{OsStr, OsString}; +#[cfg(target_family = "unix")] +use std::os::unix::ffi::OsStrExt; + +// Constants +// TCP_CA_NAME_MAX isn't defined in user space include files +#[cfg(any(target_os = "freebsd", target_os = "linux"))] +const TCP_CA_NAME_MAX: usize = 16; + +/// Helper for implementing `SetSockOpt` for a given socket option. See +/// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html). +/// +/// This macro aims to help implementing `SetSockOpt` for different socket options that accept +/// different kinds of data to be used with `setsockopt`. +/// +/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option +/// you are implementing represents a simple type. +/// +/// # Arguments +/// +/// * `$name:ident`: name of the type you want to implement `SetSockOpt` for. +/// * `$level:path` : socket layer, or a `protocol level`: could be *raw sockets* +/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), +/// and more. Please refer to your system manual for more options. Will be passed as the second +/// argument (`level`) to the `setsockopt` call. +/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`, +/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`) +/// to the `setsockopt` call. +/// * Type of the value that you are going to set. +/// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for +/// `bool`, `SetUsize` for `usize`, etc.). +macro_rules! setsockopt_impl { + ($name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => { + impl SetSockOpt for $name { + type Val = $ty; + + fn set(&self, fd: RawFd, val: &$ty) -> Result<()> { + unsafe { + let setter: $setter = Set::new(val); + + let res = libc::setsockopt(fd, $level, $flag, + setter.ffi_ptr(), + setter.ffi_len()); + Errno::result(res).map(drop) + } + } + } + } +} + +/// Helper for implementing `GetSockOpt` for a given socket option. See +/// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html). +/// +/// This macro aims to help implementing `GetSockOpt` for different socket options that accept +/// different kinds of data to be use with `getsockopt`. +/// +/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option +/// you are implementing represents a simple type. +/// +/// # Arguments +/// +/// * Name of the type you want to implement `GetSockOpt` for. +/// * Socket layer, or a `protocol level`: could be *raw sockets* (`lic::SOL_SOCKET`), *ip +/// protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), and more. Please refer +/// to your system manual for more options. Will be passed as the second argument (`level`) to +/// the `getsockopt` call. +/// * A flag to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`, +/// `libc::SO_ORIGINAL_DST` and others. Will be passed as the third argument (`option_name`) to +/// the `getsockopt` call. +/// * Type of the value that you are going to get. +/// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for +/// `bool`, `GetUsize` for `usize`, etc.). +macro_rules! getsockopt_impl { + ($name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => { + impl GetSockOpt for $name { + type Val = $ty; + + fn get(&self, fd: RawFd) -> Result<$ty> { + unsafe { + let mut getter: $getter = Get::blank(); + + let res = libc::getsockopt(fd, $level, $flag, + getter.ffi_ptr(), + getter.ffi_len()); + Errno::result(res)?; + + Ok(getter.unwrap()) + } + } + } + } +} + +/// Helper to generate the sockopt accessors. See +/// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html) and +/// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html). +/// +/// This macro aims to help implementing `GetSockOpt` and `SetSockOpt` for different socket options +/// that accept different kinds of data to be use with `getsockopt` and `setsockopt` respectively. +/// +/// Basically this macro wraps up the [`getsockopt_impl!`](macro.getsockopt_impl.html) and +/// [`setsockopt_impl!`](macro.setsockopt_impl.html) macros. +/// +/// # Arguments +/// +/// * `GetOnly`, `SetOnly` or `Both`: whether you want to implement only getter, only setter or +/// both of them. +/// * `$name:ident`: name of type `GetSockOpt`/`SetSockOpt` will be implemented for. +/// * `$level:path` : socket layer, or a `protocol level`: could be *raw sockets* +/// (`lic::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), +/// and more. Please refer to your system manual for more options. Will be passed as the second +/// argument (`level`) to the `getsockopt`/`setsockopt` call. +/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`, +/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`) +/// to the `setsockopt`/`getsockopt` call. +/// * `$ty:ty`: type of the value that will be get/set. +/// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`. +/// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`. +macro_rules! sockopt_impl { + (GetOnly, $name:ident, $level:path, $flag:path, bool) => { + sockopt_impl!(GetOnly, $name, $level, $flag, bool, GetBool); + }; + + (GetOnly, $name:ident, $level:path, $flag:path, u8) => { + sockopt_impl!(GetOnly, $name, $level, $flag, u8, GetU8); + }; + + (GetOnly, $name:ident, $level:path, $flag:path, usize) => { + sockopt_impl!(GetOnly, $name, $level, $flag, usize, GetUsize); + }; + + (SetOnly, $name:ident, $level:path, $flag:path, bool) => { + sockopt_impl!(SetOnly, $name, $level, $flag, bool, SetBool); + }; + + (SetOnly, $name:ident, $level:path, $flag:path, u8) => { + sockopt_impl!(SetOnly, $name, $level, $flag, u8, SetU8); + }; + + (SetOnly, $name:ident, $level:path, $flag:path, usize) => { + sockopt_impl!(SetOnly, $name, $level, $flag, usize, SetUsize); + }; + + (Both, $name:ident, $level:path, $flag:path, bool) => { + sockopt_impl!(Both, $name, $level, $flag, bool, GetBool, SetBool); + }; + + (Both, $name:ident, $level:path, $flag:path, u8) => { + sockopt_impl!(Both, $name, $level, $flag, u8, GetU8, SetU8); + }; + + (Both, $name:ident, $level:path, $flag:path, usize) => { + sockopt_impl!(Both, $name, $level, $flag, usize, GetUsize, SetUsize); + }; + + (Both, $name:ident, $level:path, $flag:path, OsString<$array:ty>) => { + sockopt_impl!(Both, $name, $level, $flag, OsString, GetOsString<$array>, SetOsString); + }; + + /* + * Matchers with generic getter types must be placed at the end, so + * they'll only match _after_ specialized matchers fail + */ + (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => { + sockopt_impl!(GetOnly, $name, $level, $flag, $ty, GetStruct<$ty>); + }; + + (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => { + #[derive(Copy, Clone, Debug)] + pub struct $name; + + getsockopt_impl!($name, $level, $flag, $ty, $getter); + }; + + (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty) => { + sockopt_impl!(SetOnly, $name, $level, $flag, $ty, SetStruct<$ty>); + }; + + (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => { + #[derive(Copy, Clone, Debug)] + pub struct $name; + + setsockopt_impl!($name, $level, $flag, $ty, $setter); + }; + + (Both, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty, $setter:ty) => { + #[derive(Copy, Clone, Debug)] + pub struct $name; + + setsockopt_impl!($name, $level, $flag, $ty, $setter); + getsockopt_impl!($name, $level, $flag, $ty, $getter); + }; + + (Both, $name:ident, $level:path, $flag:path, $ty:ty) => { + sockopt_impl!(Both, $name, $level, $flag, $ty, GetStruct<$ty>, SetStruct<$ty>); + }; +} + +/* + * + * ===== Define sockopts ===== + * + */ + +sockopt_impl!(Both, ReuseAddr, libc::SOL_SOCKET, libc::SO_REUSEADDR, bool); +sockopt_impl!(Both, ReusePort, libc::SOL_SOCKET, libc::SO_REUSEPORT, bool); +sockopt_impl!(Both, TcpNoDelay, libc::IPPROTO_TCP, libc::TCP_NODELAY, bool); +sockopt_impl!(Both, Linger, libc::SOL_SOCKET, libc::SO_LINGER, libc::linger); +sockopt_impl!(SetOnly, IpAddMembership, libc::IPPROTO_IP, libc::IP_ADD_MEMBERSHIP, super::IpMembershipRequest); +sockopt_impl!(SetOnly, IpDropMembership, libc::IPPROTO_IP, libc::IP_DROP_MEMBERSHIP, super::IpMembershipRequest); +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + sockopt_impl!(SetOnly, Ipv6AddMembership, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest); + sockopt_impl!(SetOnly, Ipv6DropMembership, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest); + } else if #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] { + sockopt_impl!(SetOnly, Ipv6AddMembership, libc::IPPROTO_IPV6, libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest); + sockopt_impl!(SetOnly, Ipv6DropMembership, libc::IPPROTO_IPV6, libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest); + } +} +sockopt_impl!(Both, IpMulticastTtl, libc::IPPROTO_IP, libc::IP_MULTICAST_TTL, u8); +sockopt_impl!(Both, IpMulticastLoop, libc::IPPROTO_IP, libc::IP_MULTICAST_LOOP, bool); +sockopt_impl!(Both, ReceiveTimeout, libc::SOL_SOCKET, libc::SO_RCVTIMEO, TimeVal); +sockopt_impl!(Both, SendTimeout, libc::SOL_SOCKET, libc::SO_SNDTIMEO, TimeVal); +sockopt_impl!(Both, Broadcast, libc::SOL_SOCKET, libc::SO_BROADCAST, bool); +sockopt_impl!(Both, OobInline, libc::SOL_SOCKET, libc::SO_OOBINLINE, bool); +sockopt_impl!(GetOnly, SocketError, libc::SOL_SOCKET, libc::SO_ERROR, i32); +sockopt_impl!(Both, KeepAlive, libc::SOL_SOCKET, libc::SO_KEEPALIVE, bool); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(GetOnly, PeerCredentials, libc::SOL_SOCKET, libc::SO_PEERCRED, super::UnixCredentials); +#[cfg(any(target_os = "ios", + target_os = "macos"))] +sockopt_impl!(Both, TcpKeepAlive, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, u32); +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "nacl"))] +sockopt_impl!(Both, TcpKeepIdle, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, u32); +sockopt_impl!(Both, RcvBuf, libc::SOL_SOCKET, libc::SO_RCVBUF, usize); +sockopt_impl!(Both, SndBuf, libc::SOL_SOCKET, libc::SO_SNDBUF, usize); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(SetOnly, RcvBufForce, libc::SOL_SOCKET, libc::SO_RCVBUFFORCE, usize); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(SetOnly, SndBufForce, libc::SOL_SOCKET, libc::SO_SNDBUFFORCE, usize); +sockopt_impl!(GetOnly, SockType, libc::SOL_SOCKET, libc::SO_TYPE, super::SockType); +sockopt_impl!(GetOnly, AcceptConn, libc::SOL_SOCKET, libc::SO_ACCEPTCONN, bool); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(GetOnly, OriginalDst, libc::SOL_IP, libc::SO_ORIGINAL_DST, libc::sockaddr_in); +sockopt_impl!(Both, ReceiveTimestamp, libc::SOL_SOCKET, libc::SO_TIMESTAMP, bool); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(Both, IpTransparent, libc::SOL_IP, libc::IP_TRANSPARENT, bool); +#[cfg(target_os = "openbsd")] +sockopt_impl!(Both, BindAny, libc::SOL_SOCKET, libc::SO_BINDANY, bool); +#[cfg(target_os = "freebsd")] +sockopt_impl!(Both, BindAny, libc::IPPROTO_IP, libc::IP_BINDANY, bool); +#[cfg(target_os = "linux")] +sockopt_impl!(Both, Mark, libc::SOL_SOCKET, libc::SO_MARK, u32); +#[cfg(any(target_os = "android", target_os = "linux"))] +sockopt_impl!(Both, PassCred, libc::SOL_SOCKET, libc::SO_PASSCRED, bool); +#[cfg(any(target_os = "freebsd", target_os = "linux"))] +sockopt_impl!(Both, TcpCongestion, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsString<[u8; TCP_CA_NAME_MAX]>); +#[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" +))] +sockopt_impl!(Both, Ipv4PacketInfo, libc::IPPROTO_IP, libc::IP_PKTINFO, bool); +#[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" +))] +sockopt_impl!(Both, Ipv6RecvPacketInfo, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, bool); + + +/* + * + * ===== Accessor helpers ===== + * + */ + +/// Helper trait that describes what is expected from a `GetSockOpt` getter. +unsafe trait Get<T> { + /// Returns an empty value. + unsafe fn blank() -> Self; + /// Returns a pointer to the stored value. This pointer will be passed to the system's + /// `getsockopt` call (`man 3p getsockopt`, argument `option_value`). + fn ffi_ptr(&mut self) -> *mut c_void; + /// Returns length of the stored value. This pointer will be passed to the system's + /// `getsockopt` call (`man 3p getsockopt`, argument `option_len`). + fn ffi_len(&mut self) -> *mut socklen_t; + /// Returns the stored value. + unsafe fn unwrap(self) -> T; +} + +/// Helper trait that describes what is expected from a `SetSockOpt` setter. +unsafe trait Set<'a, T> { + /// Initialize the setter with a given value. + fn new(val: &'a T) -> Self; + /// Returns a pointer to the stored value. This pointer will be passed to the system's + /// `setsockopt` call (`man 3p setsockopt`, argument `option_value`). + fn ffi_ptr(&self) -> *const c_void; + /// Returns length of the stored value. This pointer will be passed to the system's + /// `setsockopt` call (`man 3p setsockopt`, argument `option_len`). + fn ffi_len(&self) -> socklen_t; +} + +/// Getter for an arbitrary `struct`. +struct GetStruct<T> { + len: socklen_t, + val: T, +} + +unsafe impl<T> Get<T> for GetStruct<T> { + unsafe fn blank() -> Self { + GetStruct { + len: mem::size_of::<T>() as socklen_t, + val: mem::zeroed(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + &mut self.val as *mut T as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn unwrap(self) -> T { + assert!(self.len as usize == mem::size_of::<T>(), "invalid getsockopt implementation"); + self.val + } +} + +/// Setter for an arbitrary `struct`. +struct SetStruct<'a, T: 'static> { + ptr: &'a T, +} + +unsafe impl<'a, T> Set<'a, T> for SetStruct<'a, T> { + fn new(ptr: &'a T) -> SetStruct<'a, T> { + SetStruct { ptr: ptr } + } + + fn ffi_ptr(&self) -> *const c_void { + self.ptr as *const T as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + mem::size_of::<T>() as socklen_t + } +} + +/// Getter for a boolean value. +struct GetBool { + len: socklen_t, + val: c_int, +} + +unsafe impl Get<bool> for GetBool { + unsafe fn blank() -> Self { + GetBool { + len: mem::size_of::<c_int>() as socklen_t, + val: mem::zeroed(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + &mut self.val as *mut c_int as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn unwrap(self) -> bool { + assert!(self.len as usize == mem::size_of::<c_int>(), "invalid getsockopt implementation"); + self.val != 0 + } +} + +/// Setter for a boolean value. +struct SetBool { + val: c_int, +} + +unsafe impl<'a> Set<'a, bool> for SetBool { + fn new(val: &'a bool) -> SetBool { + SetBool { val: if *val { 1 } else { 0 } } + } + + fn ffi_ptr(&self) -> *const c_void { + &self.val as *const c_int as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + mem::size_of::<c_int>() as socklen_t + } +} + +/// Getter for an `u8` value. +struct GetU8 { + len: socklen_t, + val: u8, +} + +unsafe impl Get<u8> for GetU8 { + unsafe fn blank() -> Self { + GetU8 { + len: mem::size_of::<u8>() as socklen_t, + val: mem::zeroed(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + &mut self.val as *mut u8 as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn unwrap(self) -> u8 { + assert!(self.len as usize == mem::size_of::<u8>(), "invalid getsockopt implementation"); + self.val as u8 + } +} + +/// Setter for an `u8` value. +struct SetU8 { + val: u8, +} + +unsafe impl<'a> Set<'a, u8> for SetU8 { + fn new(val: &'a u8) -> SetU8 { + SetU8 { val: *val as u8 } + } + + fn ffi_ptr(&self) -> *const c_void { + &self.val as *const u8 as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + mem::size_of::<c_int>() as socklen_t + } +} + +/// Getter for an `usize` value. +struct GetUsize { + len: socklen_t, + val: c_int, +} + +unsafe impl Get<usize> for GetUsize { + unsafe fn blank() -> Self { + GetUsize { + len: mem::size_of::<c_int>() as socklen_t, + val: mem::zeroed(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + &mut self.val as *mut c_int as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn unwrap(self) -> usize { + assert!(self.len as usize == mem::size_of::<c_int>(), "invalid getsockopt implementation"); + self.val as usize + } +} + +/// Setter for an `usize` value. +struct SetUsize { + val: c_int, +} + +unsafe impl<'a> Set<'a, usize> for SetUsize { + fn new(val: &'a usize) -> SetUsize { + SetUsize { val: *val as c_int } + } + + fn ffi_ptr(&self) -> *const c_void { + &self.val as *const c_int as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + mem::size_of::<c_int>() as socklen_t + } +} + +/// Getter for a `OsString` value. +struct GetOsString<T: AsMut<[u8]>> { + len: socklen_t, + val: T, +} + +unsafe impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> { + unsafe fn blank() -> Self { + GetOsString { + len: mem::size_of::<T>() as socklen_t, + val: mem::zeroed(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + &mut self.val as *mut T as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn unwrap(mut self) -> OsString { + OsStr::from_bytes(self.val.as_mut()).to_owned() + } +} + +/// Setter for a `OsString` value. +struct SetOsString<'a> { + val: &'a OsStr, +} + +unsafe impl<'a> Set<'a, OsString> for SetOsString<'a> { + fn new(val: &'a OsString) -> SetOsString { + SetOsString { val: val.as_os_str() } + } + + fn ffi_ptr(&self) -> *const c_void { + self.val.as_bytes().as_ptr() as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + self.val.len() as socklen_t + } +} + + +#[cfg(test)] +mod test { + #[cfg(any(target_os = "android", target_os = "linux"))] + #[test] + fn can_get_peercred_on_unix_socket() { + use super::super::*; + + let (a, b) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()).unwrap(); + let a_cred = getsockopt(a, super::PeerCredentials).unwrap(); + let b_cred = getsockopt(b, super::PeerCredentials).unwrap(); + assert_eq!(a_cred, b_cred); + assert!(a_cred.pid() != 0); + } + + #[test] + fn is_socket_type_unix() { + use super::super::*; + use ::unistd::close; + + let (a, b) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()).unwrap(); + let a_type = getsockopt(a, super::SockType).unwrap(); + assert!(a_type == SockType::Stream); + close(a).unwrap(); + close(b).unwrap(); + } + + #[test] + fn is_socket_type_dgram() { + use super::super::*; + use ::unistd::close; + + let s = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), None).unwrap(); + let s_type = getsockopt(s, super::SockType).unwrap(); + assert!(s_type == SockType::Datagram); + close(s).unwrap(); + } + + #[cfg(any(target_os = "freebsd", + target_os = "linux", + target_os = "nacl"))] + #[test] + fn can_get_listen_on_tcp_socket() { + use super::super::*; + use ::unistd::close; + + let s = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap(); + let s_listening = getsockopt(s, super::AcceptConn).unwrap(); + assert!(!s_listening); + listen(s, 10).unwrap(); + let s_listening2 = getsockopt(s, super::AcceptConn).unwrap(); + assert!(s_listening2); + close(s).unwrap(); + } + + #[cfg(target_os = "linux")] + #[test] + fn is_so_mark_functional() { + use super::super::*; + use ::unistd::Uid; + use ::std::io::{self, Write}; + + if !Uid::current().is_root() { + let stderr = io::stderr(); + let mut handle = stderr.lock(); + writeln!(handle, "SO_MARK requires root privileges. Skipping test.").unwrap(); + return; + } + + let s = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap(); + setsockopt(s, super::Mark, &1337).unwrap(); + let mark = getsockopt(s, super::Mark).unwrap(); + assert_eq!(mark, 1337); + } +} diff --git a/third_party/rust/nix/src/sys/stat.rs b/third_party/rust/nix/src/sys/stat.rs new file mode 100644 index 0000000000..1e0936ed86 --- /dev/null +++ b/third_party/rust/nix/src/sys/stat.rs @@ -0,0 +1,286 @@ +pub use libc::{dev_t, mode_t}; +pub use libc::stat as FileStat; + +use {Result, NixPath}; +use errno::Errno; +use fcntl::{AtFlags, at_rawfd}; +use libc; +use std::mem; +use std::os::unix::io::RawFd; +use sys::time::{TimeSpec, TimeVal}; + +libc_bitflags!( + pub struct SFlag: mode_t { + S_IFIFO; + S_IFCHR; + S_IFDIR; + S_IFBLK; + S_IFREG; + S_IFLNK; + S_IFSOCK; + S_IFMT; + } +); + +libc_bitflags! { + pub struct Mode: mode_t { + S_IRWXU; + S_IRUSR; + S_IWUSR; + S_IXUSR; + S_IRWXG; + S_IRGRP; + S_IWGRP; + S_IXGRP; + S_IRWXO; + S_IROTH; + S_IWOTH; + S_IXOTH; + S_ISUID as mode_t; + S_ISGID as mode_t; + S_ISVTX as mode_t; + } +} + +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) +} + +#[cfg(target_os = "linux")] +pub fn major(dev: dev_t) -> u64 { + ((dev >> 32) & 0xffff_f000) | + ((dev >> 8) & 0x0000_0fff) +} + +#[cfg(target_os = "linux")] +pub fn minor(dev: dev_t) -> u64 { + ((dev >> 12) & 0xffff_ff00) | + ((dev ) & 0x0000_00ff) +} + +#[cfg(target_os = "linux")] +pub 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 = unsafe { mem::uninitialized() }; + let res = path.with_nix_path(|cstr| { + unsafe { + libc::stat(cstr.as_ptr(), &mut dst as *mut FileStat) + } + })?; + + Errno::result(res)?; + + Ok(dst) +} + +pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> { + let mut dst = unsafe { mem::uninitialized() }; + let res = path.with_nix_path(|cstr| { + unsafe { + libc::lstat(cstr.as_ptr(), &mut dst as *mut FileStat) + } + })?; + + Errno::result(res)?; + + Ok(dst) +} + +pub fn fstat(fd: RawFd) -> Result<FileStat> { + let mut dst = unsafe { mem::uninitialized() }; + let res = unsafe { libc::fstat(fd, &mut dst as *mut FileStat) }; + + Errno::result(res)?; + + Ok(dst) +} + +pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> Result<FileStat> { + let mut dst = unsafe { mem::uninitialized() }; + let res = pathname.with_nix_path(|cstr| { + unsafe { libc::fstatat(dirfd, cstr.as_ptr(), &mut dst as *mut FileStat, f.bits() as libc::c_int) } + })?; + + Errno::result(res)?; + + Ok(dst) +} + +/// Change the file permission bits of the file specified by a file descriptor. +/// +/// # References +/// +/// [fchmod(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html). +pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> { + let res = unsafe { libc::fchmod(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 `None`. +/// +/// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link, +/// then the mode of the symbolic link is changed. +/// +/// `fchmod(None, 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)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html). +pub fn fchmodat<P: ?Sized + NixPath>( + dirfd: Option<RawFd>, + path: &P, + mode: Mode, + flag: FchmodatFlags, +) -> Result<()> { + let atflag = + match flag { + FchmodatFlags::FollowSymlink => AtFlags::empty(), + FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW, + }; + let res = path.with_nix_path(|cstr| unsafe { + libc::fchmodat( + at_rawfd(dirfd), + 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)](http://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(), ×[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)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html). +#[cfg(any(target_os = "linux", + target_os = "haiku", + target_os = "ios", + target_os = "macos", + 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(), ×[0]) + })?; + + Errno::result(res).map(drop) +} + +/// Change the access and modification times of the file specified by a file descriptor. +/// +/// # References +/// +/// [futimens(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html). +#[inline] +pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> { + let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()]; + let res = unsafe { libc::futimens(fd, ×[0]) }; + + Errno::result(res).map(drop) +} + +/// Flags for `utimensat` function. +#[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 `None`. +/// +/// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link, +/// then the mode of the symbolic link is changed. +/// +/// `utimensat(None, 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. +/// +/// # References +/// +/// [utimensat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html). +pub fn utimensat<P: ?Sized + NixPath>( + dirfd: Option<RawFd>, + path: &P, + atime: &TimeSpec, + mtime: &TimeSpec, + flag: UtimensatFlags +) -> Result<()> { + 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( + at_rawfd(dirfd), + cstr.as_ptr(), + ×[0], + atflag.bits() as libc::c_int, + ) + })?; + + Errno::result(res).map(drop) +} diff --git a/third_party/rust/nix/src/sys/statfs.rs b/third_party/rust/nix/src/sys/statfs.rs new file mode 100644 index 0000000000..e7ffae5e4e --- /dev/null +++ b/third_party/rust/nix/src/sys/statfs.rs @@ -0,0 +1,20 @@ +use {Result, NixPath}; +use errno::Errno; +use std::os::unix::io::AsRawFd; +use libc; + +pub fn statfs<P: ?Sized + NixPath>(path: &P, stat: &mut libc::statfs) -> Result<()> { + unsafe { + Errno::clear(); + let res = path.with_nix_path(|path| libc::statfs(path.as_ptr(), stat))?; + + Errno::result(res).map(drop) + } +} + +pub fn fstatfs<T: AsRawFd>(fd: &T, stat: &mut libc::statfs) -> Result<()> { + unsafe { + Errno::clear(); + Errno::result(libc::fstatfs(fd.as_raw_fd(), stat)).map(drop) + } +} diff --git a/third_party/rust/nix/src/sys/statvfs.rs b/third_party/rust/nix/src/sys/statvfs.rs new file mode 100644 index 0000000000..de69ae51f8 --- /dev/null +++ b/third_party/rust/nix/src/sys/statvfs.rs @@ -0,0 +1,161 @@ +//! Get filesystem statistics +//! +//! See [the man pages](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html) +//! for more details. +use std::mem; +use std::os::unix::io::AsRawFd; + +use libc::{self, c_ulong}; + +use {Result, NixPath}; +use errno::Errno; + +libc_bitflags!( + /// File system mount Flags + #[repr(C)] + #[derive(Default)] + pub struct FsFlags: c_ulong { + /// Read Only + ST_RDONLY; + /// Do not allow the set-uid bits to have an effect + ST_NOSUID; + /// Do not interpret character or block-special devices + #[cfg(any(target_os = "android", target_os = "linux"))] + ST_NODEV; + /// Do not allow execution of binaries on the filesystem + #[cfg(any(target_os = "android", target_os = "linux"))] + ST_NOEXEC; + /// All IO should be done synchronously + #[cfg(any(target_os = "android", target_os = "linux"))] + ST_SYNCHRONOUS; + /// Allow mandatory locks on the filesystem + #[cfg(any(target_os = "android", target_os = "linux"))] + 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(any(target_os = "android", target_os = "linux"))] + ST_NOATIME; + /// Do not update access times on files + #[cfg(any(target_os = "android", target_os = "linux"))] + ST_NODIRATIME; + /// Update access time relative to modify/change time + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_env = "musl"))))] + ST_RELATIME; + } +); + +/// Wrapper around the POSIX `statvfs` struct +/// +/// For more information see the [`statvfs(3)` man pages](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_statvfs.h.html). +// FIXME: Replace with repr(transparent) +#[repr(C)] +#[derive(Clone, Copy)] +#[allow(missing_debug_implementations)] +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 + pub fn filesystem_id(&self) -> c_ulong { + self.0.f_fsid + } + + /// Get the mount flags + 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: Statvfs = mem::uninitialized(); + let res = path.with_nix_path(|path| + libc::statvfs(path.as_ptr(), &mut stat.0) + )?; + + Errno::result(res).map(|_| stat) + } +} + +/// Return a `Statvfs` object with information about `fd` +pub fn fstatvfs<T: AsRawFd>(fd: &T) -> Result<Statvfs> { + unsafe { + Errno::clear(); + let mut stat: Statvfs = mem::uninitialized(); + Errno::result(libc::fstatvfs(fd.as_raw_fd(), &mut stat.0)).map(|_| stat) + } +} + +#[cfg(test)] +mod test { + use std::fs::File; + use sys::statvfs::*; + + #[test] + fn statvfs_call() { + statvfs("/".as_bytes()).unwrap(); + } + + #[test] + fn fstatvfs_call() { + let root = File::open("/").unwrap(); + fstatvfs(&root).unwrap(); + } +} diff --git a/third_party/rust/nix/src/sys/sysinfo.rs b/third_party/rust/nix/src/sys/sysinfo.rs new file mode 100644 index 0000000000..98ef7bd517 --- /dev/null +++ b/third_party/rust/nix/src/sys/sysinfo.rs @@ -0,0 +1,73 @@ +use libc::{self, SI_LOAD_SHIFT}; +use std::{cmp, mem}; +use std::time::Duration; + +use Result; +use errno::Errno; + +/// System info structure returned by `sysinfo`. +#[derive(Copy, Clone)] +#[allow(missing_debug_implementations)] // libc::sysinfo doesn't impl Debug +pub struct SysInfo(libc::sysinfo); + +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. + 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) + } + + fn scale_mem(&self, units: libc::c_ulong) -> u64 { + units as u64 * self.0.mem_unit as u64 + } +} + +/// Returns system information. +/// +/// [See `sysinfo(2)`](http://man7.org/linux/man-pages/man2/sysinfo.2.html). +pub fn sysinfo() -> Result<SysInfo> { + let mut info: libc::sysinfo = unsafe { mem::uninitialized() }; + let res = unsafe { libc::sysinfo(&mut info) }; + Errno::result(res).map(|_| SysInfo(info)) +} diff --git a/third_party/rust/nix/src/sys/termios.rs b/third_party/rust/nix/src/sys/termios.rs new file mode 100644 index 0000000000..d8815baa00 --- /dev/null +++ b/third_party/rust/nix/src/sys/termios.rs @@ -0,0 +1,1108 @@ +//! 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](http://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 = unsafe { Termios::default_uninit() }; +//! 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 = unsafe { Termios::default_uninit() }; +//! 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 +//! # #[macro_use] extern crate nix; +//! # use nix::sys::termios::{BaudRate, cfsetispeed, cfsetospeed, cfsetspeed, Termios}; +//! # fn main() { +//! # let mut t = unsafe { Termios::default_uninit() }; +//! cfsetispeed(&mut t, BaudRate::B9600); +//! cfsetospeed(&mut t, BaudRate::B9600); +//! cfsetspeed(&mut t, BaudRate::B9600); +//! # } +//! ``` +//! +//! Additionally round-tripping baud rates is consistent across platforms: +//! +//! ```rust +//! # extern crate nix; +//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetispeed, cfsetspeed, Termios}; +//! # fn main() { +//! # let mut t = unsafe { Termios::default_uninit() }; +//! # cfsetspeed(&mut t, BaudRate::B9600); +//! let speed = cfgetispeed(&t); +//! assert!(speed == cfgetospeed(&t)); +//! cfsetispeed(&mut t, speed); +//! # } +//! ``` +//! +//! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`: +//! +// FIXME: Replace `ignore` with `compile_fail` once 1.22 is the minimum support Rust version +#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd"), + doc = " ```rust,ignore")] +#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd")), + doc = " ```rust")] +//! # extern crate nix; +//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios}; +//! # fn main() { +//! # let mut t = unsafe { Termios::default_uninit() }; +//! # cfsetspeed(&mut t, BaudRate::B9600); +//! assert!(cfgetispeed(&t) == BaudRate::B9600); +//! assert!(cfgetospeed(&t) == BaudRate::B9600); +//! # } +//! ``` +//! +//! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s: +//! +// FIXME: Replace `ignore` with `compile_fail` once 1.22 is the minimum support Rust version +#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd"), + doc = " ```rust")] +#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd")), + doc = " ```rust,ignore")] +//! # extern crate nix; +//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios}; +//! # fn main() { +//! # let mut t = unsafe { Termios::default_uninit() }; +//! # cfsetspeed(&mut t, 9600u32); +//! assert!(cfgetispeed(&t) == 9600u32); +//! assert!(cfgetospeed(&t) == 9600u32); +//! # } +//! ``` +//! +//! It's trivial to convert from a `BaudRate` to a `u32` on BSDs: +//! +// FIXME: Replace `ignore` with `compile_fail` once 1.22 is the minimum support Rust version +#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd"), + doc = " ```rust")] +#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd")), + doc = " ```rust,ignore")] +//! # extern crate nix; +//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios}; +//! # fn main() { +//! # let mut t = unsafe { Termios::default_uninit() }; +//! # cfsetspeed(&mut t, 9600u32); +//! assert!(cfgetispeed(&t) == BaudRate::B9600.into()); +//! assert!(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: +//! +// FIXME: Replace `ignore` with `compile_fail` once 1.22 is the minimum support Rust version +#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd"), + doc = " ```rust")] +#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd")), + doc = " ```rust,ignore")] +//! # extern crate nix; +//! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios}; +//! # fn main() { +//! # let mut t = unsafe { Termios::default_uninit() }; +//! cfsetispeed(&mut t, 9600u32); +//! cfsetospeed(&mut t, 9600u32); +//! cfsetspeed(&mut t, 9600u32); +//! # } +//! ``` +use Result; +use errno::Errno; +use libc::{self, c_int, tcflag_t}; +use std::cell::{Ref, RefCell}; +use std::convert::From; +use std::mem; +use std::os::unix::io::RawFd; + +use ::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)] +#[allow(missing_debug_implementations)] +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], +} + +impl Termios { + /// Exposes an immutable reference to the underlying `libc::termios` data structure. + /// + /// This can be used for interfacing with other FFI functions like: + /// + /// ```rust + /// # extern crate libc; + /// # extern crate nix; + /// # fn main() { + /// # use nix::sys::termios::Termios; + /// # let mut termios = unsafe { Termios::default_uninit() }; + /// let inner_termios = termios.get_libc_termios(); + /// unsafe { libc::cfgetispeed(&*inner_termios) }; + /// # } + /// ``` + /// + /// There is no public API exposed for functions that modify the underlying `libc::termios` + /// data because it requires additional work to maintain type safety. + // FIXME: Switch this over to use pub(crate) + #[doc(hidden)] + pub 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; + } + 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. Therefore we disable docs to + /// effectively limit its use to nix internals. In this case it should also be paired with a + /// call to `update_wrapper()` so that the wrapper-type and internal representation stay + /// consistent. + 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; + } + self.inner.as_ptr() + } + + /// Allows for easily creating new `Termios` structs that will be overwritten with real data. + /// + /// This should only be used when the inner libc::termios struct will be overwritten before it's + /// read. + // FIXME: Switch this over to use pub(crate) + #[doc(hidden)] + pub unsafe fn default_uninit() -> Self { + Termios { + inner: RefCell::new(mem::uninitialized()), + input_flags: InputFlags::empty(), + output_flags: OutputFlags::empty(), + control_flags: ControlFlags::empty(), + local_flags: LocalFlags::empty(), + control_chars: [0 as libc::cc_t; NCCS], + } + } + + /// Updates the wrapper values from the internal `libc::termios` data structure. + #[doc(hidden)] + pub 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_truncate(termios.c_cflag); + self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag); + self.control_chars = termios.c_cc; + } +} + +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, + } + } +} + +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(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))] + #[cfg_attr(not(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64")), repr(u32))] + pub enum BaudRate { + B0, + B50, + B75, + B110, + B134, + B150, + B200, + B300, + B600, + B1200, + B1800, + B2400, + B4800, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + B7200, + B9600, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + B14400, + B19200, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + B28800, + B38400, + B57600, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + B76800, + B115200, + B230400, + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd"))] + B460800, + #[cfg(any(target_os = "android", target_os = "linux"))] + B500000, + #[cfg(any(target_os = "android", target_os = "linux"))] + B576000, + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd"))] + B921600, + #[cfg(any(target_os = "android", target_os = "linux"))] + B1000000, + #[cfg(any(target_os = "android", target_os = "linux"))] + B1152000, + #[cfg(any(target_os = "android", target_os = "linux"))] + B1500000, + #[cfg(any(target_os = "android", target_os = "linux"))] + 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 From<libc::speed_t> for BaudRate { + fn from(s: libc::speed_t) -> BaudRate { + + use libc::{B0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400, B4800, + B9600, B19200, B38400, B57600, B115200, B230400}; + #[cfg(any(target_os = "android", target_os = "linux"))] + use libc::{B500000, B576000, B1000000, B1152000, B1500000, B2000000}; + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] + use libc::{B2500000, B3000000, B3500000, B4000000}; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + use libc::{B7200, B14400, B28800, B76800}; + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd"))] + use libc::{B460800, B921600}; + + match s { + B0 => BaudRate::B0, + B50 => BaudRate::B50, + B75 => BaudRate::B75, + B110 => BaudRate::B110, + B134 => BaudRate::B134, + B150 => BaudRate::B150, + B200 => BaudRate::B200, + B300 => BaudRate::B300, + B600 => BaudRate::B600, + B1200 => BaudRate::B1200, + B1800 => BaudRate::B1800, + B2400 => BaudRate::B2400, + B4800 => BaudRate::B4800, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + B7200 => BaudRate::B7200, + B9600 => BaudRate::B9600, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + B14400 => BaudRate::B14400, + B19200 => BaudRate::B19200, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + B28800 => BaudRate::B28800, + B38400 => BaudRate::B38400, + B57600 => BaudRate::B57600, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + B76800 => BaudRate::B76800, + B115200 => BaudRate::B115200, + B230400 => BaudRate::B230400, + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd"))] + B460800 => BaudRate::B460800, + #[cfg(any(target_os = "android", target_os = "linux"))] + B500000 => BaudRate::B500000, + #[cfg(any(target_os = "android", target_os = "linux"))] + B576000 => BaudRate::B576000, + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd"))] + B921600 => BaudRate::B921600, + #[cfg(any(target_os = "android", target_os = "linux"))] + B1000000 => BaudRate::B1000000, + #[cfg(any(target_os = "android", target_os = "linux"))] + B1152000 => BaudRate::B1152000, + #[cfg(any(target_os = "android", target_os = "linux"))] + B1500000 => BaudRate::B1500000, + #[cfg(any(target_os = "android", target_os = "linux"))] + B2000000 => BaudRate::B2000000, + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] + B2500000 => BaudRate::B2500000, + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] + B3000000 => BaudRate::B3000000, + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] + B3500000 => BaudRate::B3500000, + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] + B4000000 => BaudRate::B4000000, + b => unreachable!("Invalid baud constant: {}", b), + } + } +} + +// TODO: Include `TryFrom<u32> for BaudRate` once that API stabilizes +#[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +impl From<BaudRate> for u32 { + fn from(b: BaudRate) -> u32 { + b as u32 + } +} + +// 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)] + 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)] + 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)] + 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)] + pub enum SpecialCharacterIndices { + VDISCARD, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + VDSUSP, + VEOF, + VEOL, + VEOL2, + VERASE, + #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] + VERASE2, + VINTR, + VKILL, + VLNEXT, + #[cfg(not(all(target_os = "linux", target_arch = "sparc64")))] + VMIN, + VQUIT, + VREPRINT, + VSTART, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + VSTATUS, + VSTOP, + VSUSP, + #[cfg(target_os = "linux")] + VSWTC, + #[cfg(target_os = "haiku")] + VSWTCH, + #[cfg(not(all(target_os = "linux", target_arch = "sparc64")))] + VTIME, + VWERASE, + #[cfg(target_os = "dragonfly")] + VCHECKPT, + } +} + +pub use libc::NCCS; +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +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; + IXANY; + IMAXBEL; + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] + IUTF8; + } +} + +libc_bitflags! { + /// Flags for configuring the output mode of a terminal + pub struct OutputFlags: tcflag_t { + OPOST; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "linux", + target_os = "openbsd"))] + OLCUC; + ONLCR; + OCRNL as tcflag_t; + ONOCR as tcflag_t; + ONLRET as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + OFILL as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + OFDEL as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + NL0 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + NL1 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + CR0 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + CR1 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + CR2 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + CR3 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + TAB0 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + TAB1 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + TAB2 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + TAB3 as tcflag_t; + #[cfg(any(target_os = "android", target_os = "linux"))] + XTABS; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + BS0 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + BS1 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + VT0 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + VT1 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + FF0 as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + FF1 as tcflag_t; + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + OXTABS; + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + 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(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + NLDLY as tcflag_t; // FIXME: Datatype needs to be corrected in libc for mac + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + CRDLY as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + TABDLY as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + BSDLY as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + VTDLY as tcflag_t; + #[cfg(any(target_os = "android", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] + FFDLY as tcflag_t; + } +} + +libc_bitflags! { + /// Flags for setting the control mode of a terminal + pub struct ControlFlags: tcflag_t { + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + CIGNORE; + CS5; + CS6; + CS7; + CS8; + CSTOPB; + CREAD; + PARENB; + PARODD; + HUPCL; + CLOCAL; + CRTSCTS; + #[cfg(any(target_os = "android", target_os = "linux"))] + 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(any(target_os = "android", target_os = "linux"))] + CBAUDEX; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + MDMBUF; + #[cfg(any(target_os = "netbsd", target_os = "openbsd"))] + CHWFLOW; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd"))] + CCTS_OFLOW; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd"))] + CRTS_IFLOW; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd"))] + CDTR_IFLOW; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd"))] + CDSR_OFLOW; + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd"))] + 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 { + ECHOKE; + ECHOE; + ECHOK; + ECHO; + ECHONL; + ECHOPRT; + ECHOCTL; + ISIG; + ICANON; + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + ALTWERASE; + IEXTEN; + EXTPROC; + TOSTOP; + FLUSHO; + #[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + NOKERNINFO; + PENDIN; + NOFLSH; + } +} + +cfg_if!{ + if #[cfg(any(target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] { + /// Get input baud rate (see + /// [cfgetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)). + /// + /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure. + 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)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)). + /// + /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure. + 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)](http://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)](http://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 { + /// Get input baud rate (see + /// [cfgetispeed(3p)](http://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) }.into() + } + + /// Get output baud rate (see + /// [cfgetospeed(3p)](http://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) }.into() + } + + /// Set input baud rate (see + /// [cfsetispeed(3p)](http://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)](http://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. + 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)](http://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)](http://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: RawFd) -> Result<Termios> { + let mut termios: libc::termios = unsafe { mem::uninitialized() }; + + let res = unsafe { libc::tcgetattr(fd, &mut termios) }; + + Errno::result(res)?; + + Ok(termios.into()) +} + +/// Set the configuration for a terminal (see +/// [tcsetattr(3p)](http://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: RawFd, actions: SetArg, termios: &Termios) -> Result<()> { + let inner_termios = termios.get_libc_termios(); + Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(drop) +} + +/// Block until all output data is written (see +/// [tcdrain(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)). +pub fn tcdrain(fd: RawFd) -> Result<()> { + Errno::result(unsafe { libc::tcdrain(fd) }).map(drop) +} + +/// Suspend or resume the transmission or reception of data (see +/// [tcflow(3p)](http://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: RawFd, action: FlowArg) -> Result<()> { + Errno::result(unsafe { libc::tcflow(fd, action as c_int) }).map(drop) +} + +/// Discard data in the output or input queue (see +/// [tcflush(3p)](http://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: RawFd, action: FlushArg) -> Result<()> { + Errno::result(unsafe { libc::tcflush(fd, action as c_int) }).map(drop) +} + +/// Send a break for a specific duration (see +/// [tcsendbreak(3p)](http://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: RawFd, duration: c_int) -> Result<()> { + Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(drop) +} + +/// Get the session controlled by the given terminal (see +/// [tcgetsid(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)). +pub fn tcgetsid(fd: RawFd) -> Result<Pid> { + let res = unsafe { libc::tcgetsid(fd) }; + + Errno::result(res).map(Pid::from_raw) +} diff --git a/third_party/rust/nix/src/sys/time.rs b/third_party/rust/nix/src/sys/time.rs new file mode 100644 index 0000000000..9671f5310a --- /dev/null +++ b/third_party/rust/nix/src/sys/time.rs @@ -0,0 +1,573 @@ +use std::{cmp, fmt, ops}; +use libc::{c_long, timespec, timeval}; +pub use libc::{time_t, suseconds_t}; + +pub trait TimeValLike: Sized { + #[inline] + fn zero() -> Self { + Self::seconds(0) + } + + #[inline] + fn hours(hours: i64) -> Self { + let secs = hours.checked_mul(SECS_PER_HOUR) + .expect("TimeValLike::hours ouf of bounds"); + Self::seconds(secs) + } + + #[inline] + fn minutes(minutes: i64) -> Self { + let secs = minutes.checked_mul(SECS_PER_MINUTE) + .expect("TimeValLike::minutes out of bounds"); + Self::seconds(secs) + } + + fn seconds(seconds: i64) -> Self; + fn milliseconds(milliseconds: i64) -> Self; + fn microseconds(microseconds: i64) -> Self; + fn nanoseconds(nanoseconds: i64) -> Self; + + #[inline] + fn num_hours(&self) -> i64 { + self.num_seconds() / 3600 + } + + #[inline] + fn num_minutes(&self) -> i64 { + self.num_seconds() / 60 + } + + fn num_seconds(&self) -> i64; + fn num_milliseconds(&self) -> i64; + fn num_microseconds(&self) -> i64; + fn num_nanoseconds(&self) -> i64; +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TimeSpec(timespec); + +const NANOS_PER_SEC: i64 = 1_000_000_000; +const SECS_PER_MINUTE: i64 = 60; +const SECS_PER_HOUR: i64 = 3600; + +#[cfg(target_pointer_width = "64")] +const TS_MAX_SECONDS: i64 = (::std::i64::MAX / NANOS_PER_SEC) - 1; + +#[cfg(target_pointer_width = "32")] +const TS_MAX_SECONDS: i64 = ::std::isize::MAX as i64; + +const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS; + + +impl AsRef<timespec> for TimeSpec { + fn as_ref(&self) -> ×pec { + &self.0 + } +} + +impl fmt::Debug for TimeSpec { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("TimeSpec") + .field("tv_sec", &self.tv_sec()) + .field("tv_nsec", &self.tv_nsec()) + .finish() + } +} + +impl PartialEq for TimeSpec { + // The implementation of cmp is simplified by assuming that the struct is + // normalized. That is, tv_nsec must always be within [0, 1_000_000_000) + fn eq(&self, other: &TimeSpec) -> bool { + self.tv_sec() == other.tv_sec() && self.tv_nsec() == other.tv_nsec() + } +} + +impl Eq for TimeSpec {} + +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] + fn seconds(seconds: i64) -> TimeSpec { + assert!(seconds >= TS_MIN_SECONDS && seconds <= TS_MAX_SECONDS, + "TimeSpec out of bounds; seconds={}", seconds); + TimeSpec(timespec {tv_sec: seconds as time_t, tv_nsec: 0 }) + } + + #[inline] + 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] + fn nanoseconds(nanoseconds: i64) -> TimeSpec { + let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC); + assert!(secs >= TS_MIN_SECONDS && secs <= TS_MAX_SECONDS, + "TimeSpec out of bounds"); + TimeSpec(timespec {tv_sec: secs as time_t, + tv_nsec: nanos as c_long }) + } + + fn num_seconds(&self) -> i64 { + if self.tv_sec() < 0 && self.tv_nsec() > 0 { + (self.tv_sec() + 1) as i64 + } else { + self.tv_sec() as i64 + } + } + + fn num_milliseconds(&self) -> i64 { + self.num_nanoseconds() / 1_000_000 + } + + fn num_microseconds(&self) -> i64 { + self.num_nanoseconds() / 1_000_000_000 + } + + fn num_nanoseconds(&self) -> i64 { + let secs = self.num_seconds() * 1_000_000_000; + let nsec = self.nanos_mod_sec(); + secs + nsec as i64 + } +} + +impl TimeSpec { + fn nanos_mod_sec(&self) -> c_long { + if self.tv_sec() < 0 && self.tv_nsec() > 0 { + self.tv_nsec() - NANOS_PER_SEC as c_long + } else { + self.tv_nsec() + } + } + + pub fn tv_sec(&self) -> time_t { + self.0.tv_sec + } + + pub fn tv_nsec(&self) -> c_long { + self.0.tv_nsec + } +} + +impl ops::Neg for TimeSpec { + type Output = TimeSpec; + + fn neg(self) -> TimeSpec { + TimeSpec::nanoseconds(-self.num_nanoseconds()) + } +} + +impl ops::Add for TimeSpec { + type Output = TimeSpec; + + fn add(self, rhs: TimeSpec) -> TimeSpec { + TimeSpec::nanoseconds( + self.num_nanoseconds() + rhs.num_nanoseconds()) + } +} + +impl ops::Sub for TimeSpec { + type Output = TimeSpec; + + fn sub(self, rhs: TimeSpec) -> TimeSpec { + TimeSpec::nanoseconds( + self.num_nanoseconds() - rhs.num_nanoseconds()) + } +} + +impl ops::Mul<i32> for TimeSpec { + type Output = TimeSpec; + + fn mul(self, rhs: i32) -> TimeSpec { + let usec = self.num_nanoseconds().checked_mul(rhs as i64) + .expect("TimeSpec multiply out of bounds"); + + TimeSpec::nanoseconds(usec) + } +} + +impl ops::Div<i32> for TimeSpec { + type Output = TimeSpec; + + fn div(self, rhs: i32) -> TimeSpec { + let usec = self.num_nanoseconds() / rhs as i64; + TimeSpec::nanoseconds(usec) + } +} + +impl fmt::Display for TimeSpec { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let (abs, sign) = if self.tv_sec() < 0 { + (-*self, "-") + } else { + (*self, "") + }; + + let sec = abs.tv_sec(); + + write!(f, "{}", sign)?; + + if abs.tv_nsec() == 0 { + if abs.tv_sec() == 1 { + write!(f, "{} second", sec)?; + } else { + write!(f, "{} seconds", sec)?; + } + } else if abs.tv_nsec() % 1_000_000 == 0 { + write!(f, "{}.{:03} seconds", sec, abs.tv_nsec() / 1_000_000)?; + } else if abs.tv_nsec() % 1_000 == 0 { + write!(f, "{}.{:06} seconds", sec, abs.tv_nsec() / 1_000)?; + } else { + write!(f, "{}.{:09} seconds", sec, abs.tv_nsec())?; + } + + Ok(()) + } +} + + + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TimeVal(timeval); + +const MICROS_PER_SEC: i64 = 1_000_000; + +#[cfg(target_pointer_width = "64")] +const TV_MAX_SECONDS: i64 = (::std::i64::MAX / MICROS_PER_SEC) - 1; + +#[cfg(target_pointer_width = "32")] +const TV_MAX_SECONDS: i64 = ::std::isize::MAX as i64; + +const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS; + +impl AsRef<timeval> for TimeVal { + fn as_ref(&self) -> &timeval { + &self.0 + } +} + +impl fmt::Debug for TimeVal { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("TimeVal") + .field("tv_sec", &self.tv_sec()) + .field("tv_usec", &self.tv_usec()) + .finish() + } +} + +impl PartialEq for TimeVal { + // The implementation of cmp is simplified by assuming that the struct is + // normalized. That is, tv_usec must always be within [0, 1_000_000) + fn eq(&self, other: &TimeVal) -> bool { + self.tv_sec() == other.tv_sec() && self.tv_usec() == other.tv_usec() + } +} + +impl Eq for TimeVal {} + +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!(seconds >= TV_MIN_SECONDS && seconds <= TV_MAX_SECONDS, + "TimeVal out of bounds; seconds={}", seconds); + TimeVal(timeval {tv_sec: seconds as time_t, tv_usec: 0 }) + } + + #[inline] + 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!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, + "TimeVal out of bounds"); + TimeVal(timeval {tv_sec: secs as time_t, + tv_usec: micros as suseconds_t }) + } + + /// Makes a new `TimeVal` with given number of nanoseconds. Some precision + /// will be lost + #[inline] + fn nanoseconds(nanoseconds: i64) -> TimeVal { + let microseconds = nanoseconds / 1000; + let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); + assert!(secs >= TV_MIN_SECONDS && secs <= TV_MAX_SECONDS, + "TimeVal out of bounds"); + TimeVal(timeval {tv_sec: secs as time_t, + tv_usec: micros as suseconds_t }) + } + + 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 + } + + fn num_microseconds(&self) -> i64 { + let secs = self.num_seconds() * 1_000_000; + let usec = self.micros_mod_sec(); + secs + usec as i64 + } + + fn num_nanoseconds(&self) -> i64 { + self.num_microseconds() * 1_000 + } +} + +impl TimeVal { + fn micros_mod_sec(&self) -> suseconds_t { + if self.tv_sec() < 0 && self.tv_usec() > 0 { + self.tv_usec() - MICROS_PER_SEC as suseconds_t + } else { + self.tv_usec() + } + } + + pub fn tv_sec(&self) -> time_t { + self.0.tv_sec + } + + pub fn tv_usec(&self) -> suseconds_t { + self.0.tv_usec + } +} + +impl ops::Neg for TimeVal { + 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(rhs as i64) + .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() / rhs as i64; + 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 abs.tv_sec() == 1 { + write!(f, "{} second", sec)?; + } else { + write!(f, "{} seconds", sec)?; + } + } else if abs.tv_usec() % 1000 == 0 { + write!(f, "{}.{:03} seconds", sec, abs.tv_usec() / 1000)?; + } else { + write!(f, "{}.{:06} seconds", sec, abs.tv_usec())?; + } + + Ok(()) + } +} + +#[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) +} + +#[cfg(test)] +mod test { + use super::{TimeSpec, TimeVal, TimeValLike}; + + #[test] + pub fn test_timespec() { + assert!(TimeSpec::seconds(1) != TimeSpec::zero()); + assert_eq!(TimeSpec::seconds(1) + TimeSpec::seconds(2), + TimeSpec::seconds(3)); + assert_eq!(TimeSpec::minutes(3) + TimeSpec::seconds(2), + TimeSpec::seconds(182)); + } + + #[test] + pub fn test_timespec_neg() { + let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123); + let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123); + + assert_eq!(a, -b); + } + + #[test] + pub fn test_timespec_ord() { + assert!(TimeSpec::seconds(1) == TimeSpec::nanoseconds(1_000_000_000)); + assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001)); + assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999)); + assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999)); + assert!(TimeSpec::seconds(-1) > TimeSpec::nanoseconds(-1_000_000_001)); + } + + #[test] + pub fn test_timespec_fmt() { + assert_eq!(TimeSpec::zero().to_string(), "0 seconds"); + assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds"); + assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds"); + assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds"); + assert_eq!(TimeSpec::nanoseconds(42).to_string(), "0.000000042 seconds"); + assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds"); + } + + #[test] + pub fn test_timeval() { + assert!(TimeVal::seconds(1) != TimeVal::zero()); + assert_eq!(TimeVal::seconds(1) + TimeVal::seconds(2), + TimeVal::seconds(3)); + assert_eq!(TimeVal::minutes(3) + TimeVal::seconds(2), + TimeVal::seconds(182)); + } + + #[test] + pub fn test_timeval_ord() { + assert!(TimeVal::seconds(1) == TimeVal::microseconds(1_000_000)); + assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001)); + assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999)); + assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999)); + assert!(TimeVal::seconds(-1) > TimeVal::microseconds(-1_000_001)); + } + + #[test] + pub fn test_timeval_neg() { + let a = TimeVal::seconds(1) + TimeVal::microseconds(123); + let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123); + + assert_eq!(a, -b); + } + + #[test] + pub fn test_timeval_fmt() { + assert_eq!(TimeVal::zero().to_string(), "0 seconds"); + assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds"); + assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds"); + assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds"); + assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds"); + assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds"); + } +} diff --git a/third_party/rust/nix/src/sys/uio.rs b/third_party/rust/nix/src/sys/uio.rs new file mode 100644 index 0000000000..b01fc7e6a9 --- /dev/null +++ b/third_party/rust/nix/src/sys/uio.rs @@ -0,0 +1,195 @@ +// Silence invalid warnings due to rust-lang/rust#16719 +#![allow(improper_ctypes)] + +use Result; +use errno::Errno; +use libc::{self, c_int, c_void, size_t, off_t}; +use std::marker::PhantomData; +use std::os::unix::io::RawFd; + +pub fn writev(fd: RawFd, iov: &[IoVec<&[u8]>]) -> Result<usize> { + let res = unsafe { libc::writev(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int) }; + + Errno::result(res).map(|r| r as usize) +} + +pub fn readv(fd: RawFd, iov: &mut [IoVec<&mut [u8]>]) -> Result<usize> { + let res = unsafe { libc::readv(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int) }; + + 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(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] +pub fn pwritev(fd: RawFd, iov: &[IoVec<&[u8]>], + offset: off_t) -> Result<usize> { + let res = unsafe { + libc::pwritev(fd, iov.as_ptr() as *const libc::iovec, 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(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] +pub fn preadv(fd: RawFd, iov: &[IoVec<&mut [u8]>], + offset: off_t) -> Result<usize> { + let res = unsafe { + libc::preadv(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int, offset) + }; + + Errno::result(res).map(|r| r as usize) +} + +pub fn pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result<usize> { + let res = unsafe { + libc::pwrite(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, + offset) + }; + + Errno::result(res).map(|r| r as usize) +} + +pub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize>{ + let res = unsafe { + libc::pread(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t, + 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 [`IoVec`](struct.IoVec.html), +/// except that it refers to memory in some other process, and is +/// therefore not represented in Rust by an actual slice as `IoVec` is. It +/// is used with [`process_vm_readv`](fn.process_vm_readv.html) +/// and [`process_vm_writev`](fn.process_vm_writev.html). +#[cfg(target_os = "linux")] +#[repr(C)] +#[derive(Clone, Copy)] +#[allow(missing_debug_implementations)] +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, +} + +/// Write data directly to another process's virtual memory +/// (see [`process_vm_writev`(2)]). +/// +/// `local_iov` is a list of [`IoVec`]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. +/// +/// [`process_vm_writev`(2)]: http://man7.org/linux/man-pages/man2/process_vm_writev.2.html +/// [ptrace]: ../ptrace/index.html +/// [`IoVec`]: struct.IoVec.html +/// [`RemoteIoVec`]: struct.RemoteIoVec.html +// #[cfg(target_os = "linux")] +// pub fn process_vm_writev(pid: ::unistd::Pid, local_iov: &[IoVec<&[u8]>], remote_iov: &[RemoteIoVec]) -> Result<usize> { +// let res = unsafe { +// libc::process_vm_writev(pid.into(), +// local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong, +// remote_iov.as_ptr() as *const libc::iovec, 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 [`IoVec`]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. +/// +/// [`process_vm_readv`(2)]: http://man7.org/linux/man-pages/man2/process_vm_readv.2.html +/// [`ptrace`]: ../ptrace/index.html +/// [`IoVec`]: struct.IoVec.html +/// [`RemoteIoVec`]: struct.RemoteIoVec.html +// #[cfg(any(target_os = "linux"))] +// pub fn process_vm_readv(pid: ::unistd::Pid, local_iov: &[IoVec<&mut [u8]>], remote_iov: &[RemoteIoVec]) -> Result<usize> { +// let res = unsafe { +// libc::process_vm_readv(pid.into(), +// local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong, +// remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0) +// }; + +// Errno::result(res).map(|r| r as usize) +// } + +#[repr(C)] +#[allow(missing_debug_implementations)] +pub struct IoVec<T>(libc::iovec, PhantomData<T>); + +impl<T> IoVec<T> { + #[inline] + pub fn as_slice(&self) -> &[u8] { + use std::slice; + + unsafe { + slice::from_raw_parts( + self.0.iov_base as *const u8, + self.0.iov_len as usize) + } + } +} + +impl<'a> IoVec<&'a [u8]> { + pub fn from_slice(buf: &'a [u8]) -> IoVec<&'a [u8]> { + IoVec(libc::iovec { + iov_base: buf.as_ptr() as *mut c_void, + iov_len: buf.len() as size_t, + }, PhantomData) + } +} + +impl<'a> IoVec<&'a mut [u8]> { + pub fn from_mut_slice(buf: &'a mut [u8]) -> IoVec<&'a mut [u8]> { + IoVec(libc::iovec { + iov_base: buf.as_ptr() as *mut c_void, + iov_len: buf.len() as size_t, + }, PhantomData) + } +} diff --git a/third_party/rust/nix/src/sys/utsname.rs b/third_party/rust/nix/src/sys/utsname.rs new file mode 100644 index 0000000000..e33d0739a0 --- /dev/null +++ b/third_party/rust/nix/src/sys/utsname.rs @@ -0,0 +1,68 @@ +use std::mem; +use libc::{self, c_char}; +use std::ffi::CStr; +use std::str::from_utf8_unchecked; + +#[repr(C)] +#[derive(Clone, Copy)] +#[allow(missing_debug_implementations)] +pub struct UtsName(libc::utsname); + +impl UtsName { + pub fn sysname(&self) -> &str { + to_str(&(&self.0.sysname as *const c_char ) as *const *const c_char) + } + + pub fn nodename(&self) -> &str { + to_str(&(&self.0.nodename as *const c_char ) as *const *const c_char) + } + + pub fn release(&self) -> &str { + to_str(&(&self.0.release as *const c_char ) as *const *const c_char) + } + + pub fn version(&self) -> &str { + to_str(&(&self.0.version as *const c_char ) as *const *const c_char) + } + + pub fn machine(&self) -> &str { + to_str(&(&self.0.machine as *const c_char ) as *const *const c_char) + } +} + +pub fn uname() -> UtsName { + unsafe { + let mut ret: UtsName = mem::uninitialized(); + libc::uname(&mut ret.0); + ret + } +} + +#[inline] +fn to_str<'a>(s: *const *const c_char) -> &'a str { + unsafe { + let res = CStr::from_ptr(*s).to_bytes(); + from_utf8_unchecked(res) + } +} + +#[cfg(test)] +mod test { + #[cfg(target_os = "linux")] + #[test] + pub fn test_uname_linux() { + assert_eq!(super::uname().sysname(), "Linux"); + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + #[test] + pub fn test_uname_darwin() { + assert_eq!(super::uname().sysname(), "Darwin"); + } + + #[cfg(target_os = "freebsd")] + #[test] + pub fn test_uname_freebsd() { + assert_eq!(super::uname().sysname(), "FreeBSD"); + } +} diff --git a/third_party/rust/nix/src/sys/wait.rs b/third_party/rust/nix/src/sys/wait.rs new file mode 100644 index 0000000000..3f99757daf --- /dev/null +++ b/third_party/rust/nix/src/sys/wait.rs @@ -0,0 +1,239 @@ +use libc::{self, c_int}; +use Result; +use errno::Errno; +use unistd::Pid; + +use sys::signal::Signal; + +libc_bitflags!( + pub struct WaitPidFlag: c_int { + WNOHANG; + WUNTRACED; + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd"))] + WEXITED; + WCONTINUED; + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd"))] + WSTOPPED; + /// Don't reap, just poll status. + #[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "haiku", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd"))] + WNOWAIT; + /// Don't wait on children of other threads in this group + #[cfg(any(target_os = "android", target_os = "linux"))] + __WNOTHREAD; + /// Wait on all children, regardless of type + #[cfg(any(target_os = "android", target_os = "linux"))] + __WALL; + #[cfg(any(target_os = "android", target_os = "linux"))] + __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(Eq, PartialEq, Clone, Copy, Debug)] +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)]: http://man7.org/linux/man-pages/man2/ptrace.2.html + #[cfg(any(target_os = "linux", target_os = "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)]: http://man7.org/linux/man-pages/man2/ptrace.2.html + #[cfg(any(target_os = "linux", target_os = "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(any(target_os = "android", target_os = "linux"))] + PtraceEvent(p, _, _) | PtraceSyscall(p) => Some(p), + } + } +} + +fn exited(status: i32) -> bool { + unsafe { libc::WIFEXITED(status) } +} + +fn exit_status(status: i32) -> i32 { + unsafe { libc::WEXITSTATUS(status) } +} + +fn signaled(status: i32) -> bool { + unsafe { libc::WIFSIGNALED(status) } +} + +fn term_signal(status: i32) -> Result<Signal> { + Signal::from_c_int(unsafe { libc::WTERMSIG(status) }) +} + +fn dumped_core(status: i32) -> bool { + unsafe { libc::WCOREDUMP(status) } +} + +fn stopped(status: i32) -> bool { + unsafe { libc::WIFSTOPPED(status) } +} + +fn stop_signal(status: i32) -> Result<Signal> { + Signal::from_c_int(unsafe { libc::WSTOPSIG(status) }) +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +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. + unsafe { libc::WSTOPSIG(status) == libc::SIGTRAP | 0x80 } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +fn stop_additional(status: i32) -> c_int { + (status >> 16) as c_int +} + +fn continued(status: i32) -> bool { + unsafe { 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(any(target_os = "android", target_os = "linux"))] { + 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) + }) + } +} + +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(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), + } +} + +pub fn wait() -> Result<WaitStatus> { + waitpid(None, None) +} diff --git a/third_party/rust/nix/src/ucontext.rs b/third_party/rust/nix/src/ucontext.rs new file mode 100644 index 0000000000..c94464d570 --- /dev/null +++ b/third_party/rust/nix/src/ucontext.rs @@ -0,0 +1,40 @@ +use libc; +#[cfg(not(target_env = "musl"))] +use Result; +#[cfg(not(target_env = "musl"))] +use errno::Errno; +use std::mem; +use sys::signal::SigSet; + +#[derive(Clone, Copy)] +#[allow(missing_debug_implementations)] +pub struct UContext { + context: libc::ucontext_t, +} + +impl UContext { + #[cfg(not(target_env = "musl"))] + pub fn get() -> Result<UContext> { + let mut context: libc::ucontext_t = unsafe { mem::uninitialized() }; + let res = unsafe { + libc::getcontext(&mut context as *mut libc::ucontext_t) + }; + Errno::result(res).map(|_| UContext { context: context }) + } + + #[cfg(not(target_env = "musl"))] + pub fn set(&self) -> Result<()> { + let res = unsafe { + libc::setcontext(&self.context as *const libc::ucontext_t) + }; + Errno::result(res).map(drop) + } + + pub fn sigmask_mut(&mut self) -> &mut SigSet { + unsafe { mem::transmute(&mut self.context.uc_sigmask) } + } + + pub fn sigmask(&self) -> &SigSet { + unsafe { mem::transmute(&self.context.uc_sigmask) } + } +} diff --git a/third_party/rust/nix/src/unistd.rs b/third_party/rust/nix/src/unistd.rs new file mode 100644 index 0000000000..7a45259adf --- /dev/null +++ b/third_party/rust/nix/src/unistd.rs @@ -0,0 +1,2313 @@ +//! Safe wrappers around functions found in libc "unistd.h" header + +use errno::{self, Errno}; +use {Error, Result, NixPath}; +use fcntl::{AtFlags, at_rawfd, fcntl, FdFlag, OFlag}; +use fcntl::FcntlArg::F_SETFD; +use libc::{self, c_char, c_void, c_int, c_long, c_uint, size_t, pid_t, off_t, + uid_t, gid_t, mode_t}; +use std::{fmt, mem, ptr}; +use std::ffi::{CString, CStr, OsString, OsStr}; +use std::os::unix::ffi::{OsStringExt, OsStrExt}; +use std::os::unix::io::RawFd; +use std::path::PathBuf; +use void::Void; +use sys::stat::Mode; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use self::pivot_root::*; + +#[cfg(any(target_os = "android", target_os = "freebsd", + target_os = "linux", target_os = "openbsd"))] +pub use self::setres::*; + +/// User identifier +/// +/// Newtype pattern around `uid_t` (which is just alias). It prevents bugs caused by accidentally +/// passing wrong value. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct Uid(uid_t); + +impl Uid { + /// Creates `Uid` from raw `uid_t`. + pub fn from_raw(uid: uid_t) -> Self { + Uid(uid) + } + + /// Returns Uid of calling process. This is practically a more Rusty alias for `getuid`. + pub fn current() -> Self { + getuid() + } + + /// Returns effective Uid of calling process. This is practically a more Rusty alias for `geteuid`. + pub fn effective() -> Self { + geteuid() + } + + /// Returns true if the `Uid` represents privileged user - root. (If it equals zero.) + pub fn is_root(&self) -> bool { + *self == ROOT + } + + /// Get the raw `uid_t` wrapped by `self`. + pub fn as_raw(&self) -> uid_t { + self.0 + } +} + +impl From<Uid> for uid_t { + fn from(uid: Uid) -> Self { + uid.0 + } +} + +impl fmt::Display for Uid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +/// Constant for UID = 0 +pub const ROOT: Uid = Uid(0); + +/// Group identifier +/// +/// Newtype pattern around `gid_t` (which is just alias). It prevents bugs caused by accidentally +/// passing wrong value. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct Gid(gid_t); + +impl Gid { + /// Creates `Gid` from raw `gid_t`. + pub fn from_raw(gid: gid_t) -> Self { + Gid(gid) + } + + /// Returns Gid of calling process. This is practically a more Rusty alias for `getgid`. + pub fn current() -> Self { + getgid() + } + + /// Returns effective Gid of calling process. This is practically a more Rusty alias for `getgid`. + pub fn effective() -> Self { + getegid() + } + + /// Get the raw `gid_t` wrapped by `self`. + pub fn as_raw(&self) -> gid_t { + self.0 + } +} + +impl From<Gid> for gid_t { + fn from(gid: Gid) -> Self { + gid.0 + } +} + +impl fmt::Display for Gid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +/// Process identifier +/// +/// Newtype pattern around `pid_t` (which is just alias). It prevents bugs caused by accidentally +/// passing wrong value. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct Pid(pid_t); + +impl Pid { + /// Creates `Pid` from raw `pid_t`. + pub fn from_raw(pid: pid_t) -> Self { + Pid(pid) + } + + /// Returns PID of calling process + pub fn this() -> Self { + getpid() + } + + /// Returns PID of parent of calling process + pub fn parent() -> Self { + getppid() + } + + /// Get the raw `pid_t` wrapped by `self`. + pub fn as_raw(&self) -> pid_t { + self.0 + } +} + +impl From<Pid> for pid_t { + fn from(pid: Pid) -> Self { + pid.0 + } +} + +impl fmt::Display for Pid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + + +/// Represents the successful result of calling `fork` +/// +/// When `fork` is called, the process continues execution in the parent process +/// and in the new child. This return type can be examined to determine whether +/// you are now executing in the parent process or in the child. +#[derive(Clone, Copy, Debug)] +pub enum ForkResult { + Parent { child: Pid }, + Child, +} + +impl ForkResult { + + /// Return `true` if this is the child process of the `fork()` + #[inline] + pub fn is_child(&self) -> bool { + match *self { + ForkResult::Child => true, + _ => false + } + } + + /// Returns `true` if this is the parent process of the `fork()` + #[inline] + pub fn is_parent(&self) -> bool { + !self.is_child() + } +} + +/// Create a new child process duplicating the parent process ([see +/// fork(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html)). +/// +/// After calling the fork system call (successfully) two processes will +/// be created that are identical with the exception of their pid and the +/// return value of this function. As an example: +/// +/// ```no_run +/// use nix::unistd::{fork, ForkResult}; +/// +/// match fork() { +/// Ok(ForkResult::Parent { child, .. }) => { +/// println!("Continuing execution in parent process, new child has pid: {}", child); +/// } +/// Ok(ForkResult::Child) => println!("I'm a new child process"), +/// Err(_) => println!("Fork failed"), +/// } +/// ``` +/// +/// This will print something like the following (order indeterministic). The +/// thing to note is that you end up with two processes continuing execution +/// immediately after the fork call but with different match arms. +/// +/// ```text +/// Continuing execution in parent process, new child has pid: 1234 +/// I'm a new child process +/// ``` +/// +/// # 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). 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]: http://man7.org/linux/man-pages/man7/signal-safety.7.html +#[inline] +pub fn fork() -> Result<ForkResult> { + use self::ForkResult::*; + let res = unsafe { libc::fork() }; + + Errno::result(res).map(|res| match res { + 0 => Child, + res => Parent { child: Pid(res) }, + }) +} + +/// Get the pid of this process (see +/// [getpid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpid.html)). +/// +/// Since you are running code, there is always a pid to return, so there +/// is no error case that needs to be handled. +#[inline] +pub fn getpid() -> Pid { + Pid(unsafe { libc::getpid() }) +} + +/// Get the pid of this processes' parent (see +/// [getpid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getppid.html)). +/// +/// There is always a parent pid to return, so there is no error case that needs +/// to be handled. +#[inline] +pub fn getppid() -> Pid { + Pid(unsafe { libc::getppid() }) // no error handling, according to man page: "These functions are always successful." +} + +/// Set a process group ID (see +/// [setpgid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgid.html)). +/// +/// Set the process group id (PGID) of a particular process. If a pid of zero +/// is specified, then the pid of the calling process is used. Process groups +/// may be used to group together a set of processes in order for the OS to +/// apply some operations across the group. +/// +/// `setsid()` may be used to create a new process group. +#[inline] +pub fn setpgid(pid: Pid, pgid: Pid) -> Result<()> { + let res = unsafe { libc::setpgid(pid.into(), pgid.into()) }; + Errno::result(res).map(drop) +} +#[inline] +pub fn getpgid(pid: Option<Pid>) -> Result<Pid> { + let res = unsafe { libc::getpgid(pid.unwrap_or(Pid(0)).into()) }; + Errno::result(res).map(Pid) +} + +/// Create new session and set process group id (see +/// [setsid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setsid.html)). +#[inline] +pub fn setsid() -> Result<Pid> { + Errno::result(unsafe { libc::setsid() }).map(Pid) +} + +/// Get the process group ID of a session leader +/// [getsid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getsid.html). +/// +/// Obtain the process group ID of the process that is the session leader of the process specified +/// by pid. If pid is zero, it specifies the calling process. +#[inline] +pub fn getsid(pid: Option<Pid>) -> Result<Pid> { + let res = unsafe { libc::getsid(pid.unwrap_or(Pid(0)).into()) }; + Errno::result(res).map(Pid) +} + + +/// Get the terminal foreground process group (see +/// [tcgetpgrp(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetpgrp.html)). +/// +/// Get the group process id (GPID) of the foreground process group on the +/// terminal associated to file descriptor (FD). +#[inline] +pub fn tcgetpgrp(fd: c_int) -> Result<Pid> { + let res = unsafe { libc::tcgetpgrp(fd) }; + Errno::result(res).map(Pid) +} +/// Set the terminal foreground process group (see +/// [tcgetpgrp(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetpgrp.html)). +/// +/// Get the group process id (PGID) to the foreground process group on the +/// terminal associated to file descriptor (FD). +#[inline] +pub fn tcsetpgrp(fd: c_int, pgrp: Pid) -> Result<()> { + let res = unsafe { libc::tcsetpgrp(fd, pgrp.into()) }; + Errno::result(res).map(drop) +} + + +/// Get the group id of the calling process (see +///[getpgrp(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgrp.html)). +/// +/// Get the process group id (PGID) of the calling process. +/// According to the man page it is always successful. +#[inline] +pub fn getpgrp() -> Pid { + Pid(unsafe { libc::getpgrp() }) +} + +/// Get the caller's thread ID (see +/// [gettid(2)](http://man7.org/linux/man-pages/man2/gettid.2.html). +/// +/// This function is only available on Linux based systems. In a single +/// threaded process, the main thread will have the same ID as the process. In +/// a multithreaded process, each thread will have a unique thread id but the +/// same process ID. +/// +/// No error handling is required as a thread id should always exist for any +/// process, even if threads are not being used. +#[cfg(any(target_os = "linux", target_os = "android"))] +#[inline] +pub fn gettid() -> Pid { + Pid(unsafe { libc::syscall(libc::SYS_gettid) as pid_t }) +} + +/// Create a copy of the specified file descriptor (see +/// [dup(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)). +/// +/// The new file descriptor will be have a new index but refer to the same +/// resource as the old file descriptor and the old and new file descriptors may +/// be used interchangeably. The new and old file descriptor share the same +/// underlying resource, offset, and file status flags. The actual index used +/// for the file descriptor will be the lowest fd index that is available. +/// +/// The two file descriptors do not share file descriptor flags (e.g. `OFlag::FD_CLOEXEC`). +#[inline] +pub fn dup(oldfd: RawFd) -> Result<RawFd> { + let res = unsafe { libc::dup(oldfd) }; + + Errno::result(res) +} + +/// Create a copy of the specified file descriptor using the specified fd (see +/// [dup(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)). +/// +/// This function behaves similar to `dup()` except that it will try to use the +/// specified fd instead of allocating a new one. See the man pages for more +/// detail on the exact behavior of this function. +#[inline] +pub fn dup2(oldfd: RawFd, newfd: RawFd) -> Result<RawFd> { + let res = unsafe { libc::dup2(oldfd, newfd) }; + + Errno::result(res) +} + +/// Create a new copy of the specified file descriptor using the specified fd +/// and flags (see [dup(2)](http://man7.org/linux/man-pages/man2/dup.2.html)). +/// +/// This function behaves similar to `dup2()` but allows for flags to be +/// specified. +pub fn dup3(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> { + dup3_polyfill(oldfd, newfd, flags) +} + +#[inline] +fn dup3_polyfill(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> { + if oldfd == newfd { + return Err(Error::Sys(Errno::EINVAL)); + } + + let fd = dup2(oldfd, newfd)?; + + if flags.contains(OFlag::O_CLOEXEC) { + if let Err(e) = fcntl(fd, F_SETFD(FdFlag::FD_CLOEXEC)) { + let _ = close(fd); + return Err(e); + } + } + + Ok(fd) +} + +/// Change the current working directory of the calling process (see +/// [chdir(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html)). +/// +/// This function may fail in a number of different scenarios. See the man +/// pages for additional details on possible failure cases. +#[inline] +pub fn chdir<P: ?Sized + NixPath>(path: &P) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { libc::chdir(cstr.as_ptr()) } + })?; + + Errno::result(res).map(drop) +} + +/// Change the current working directory of the process to the one +/// given as an open file descriptor (see +/// [fchdir(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchdir.html)). +/// +/// This function may fail in a number of different scenarios. See the man +/// pages for additional details on possible failure cases. +#[inline] +pub fn fchdir(dirfd: RawFd) -> Result<()> { + let res = unsafe { libc::fchdir(dirfd) }; + + Errno::result(res).map(drop) +} + +/// Creates new directory `path` with access rights `mode`. (see [mkdir(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html)) +/// +/// # Errors +/// +/// There are several situations where mkdir might fail: +/// +/// - current user has insufficient rights in the parent directory +/// - the path already exists +/// - the path name is too long (longer than `PATH_MAX`, usually 4096 on linux, 1024 on OS X) +/// +/// # Example +/// +/// ```rust +/// extern crate tempfile; +/// extern crate nix; +/// +/// use nix::unistd; +/// use nix::sys::stat; +/// use tempfile::tempdir; +/// +/// fn main() { +/// let tmp_dir1 = tempdir().unwrap(); +/// let tmp_dir2 = tmp_dir1.path().join("new_dir"); +/// +/// // create new directory and give read, write and execute rights to the owner +/// match unistd::mkdir(&tmp_dir2, stat::Mode::S_IRWXU) { +/// Ok(_) => println!("created {:?}", tmp_dir2), +/// Err(err) => println!("Error creating directory: {}", err), +/// } +/// } +/// ``` +#[inline] +pub fn mkdir<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { libc::mkdir(cstr.as_ptr(), mode.bits() as mode_t) } + })?; + + Errno::result(res).map(drop) +} + +/// Creates new fifo special file (named pipe) with path `path` and access rights `mode`. +/// +/// # Errors +/// +/// There are several situations where mkfifo might fail: +/// +/// - current user has insufficient rights in the parent directory +/// - the path already exists +/// - the path name is too long (longer than `PATH_MAX`, usually 4096 on linux, 1024 on OS X) +/// +/// For a full list consult +/// [posix specification](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifo.html) +/// +/// # Example +/// +/// ```rust +/// extern crate tempfile; +/// extern crate nix; +/// +/// use nix::unistd; +/// use nix::sys::stat; +/// use tempfile::tempdir; +/// +/// fn main() { +/// let tmp_dir = tempdir().unwrap(); +/// let fifo_path = tmp_dir.path().join("foo.pipe"); +/// +/// // create new fifo and give read, write and execute rights to the owner +/// match unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) { +/// Ok(_) => println!("created {:?}", fifo_path), +/// Err(err) => println!("Error creating fifo: {}", err), +/// } +/// } +/// ``` +#[inline] +pub fn mkfifo<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { libc::mkfifo(cstr.as_ptr(), mode.bits() as mode_t) } + })?; + + Errno::result(res).map(drop) +} + +/// Creates a symbolic link at `path2` which points to `path1`. +/// +/// If `dirfd` has a value, then `path2` is relative to directory associated +/// with the file descriptor. +/// +/// If `dirfd` is `None`, then `path2` is relative to the current working +/// directory. This is identical to `libc::symlink(path1, path2)`. +/// +/// See also [symlinkat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html). +pub fn symlinkat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>( + path1: &P1, + dirfd: Option<RawFd>, + path2: &P2) -> Result<()> { + let res = + path1.with_nix_path(|path1| { + path2.with_nix_path(|path2| { + unsafe { + libc::symlinkat( + path1.as_ptr(), + dirfd.unwrap_or(libc::AT_FDCWD), + path2.as_ptr() + ) + } + }) + })??; + Errno::result(res).map(drop) +} + +/// Returns the current directory as a `PathBuf` +/// +/// Err is returned if the current user doesn't have the permission to read or search a component +/// of the current path. +/// +/// # Example +/// +/// ```rust +/// extern crate nix; +/// +/// use nix::unistd; +/// +/// fn main() { +/// // assume that we are allowed to get current directory +/// let dir = unistd::getcwd().unwrap(); +/// println!("The current directory is {:?}", dir); +/// } +/// ``` +#[inline] +pub fn getcwd() -> Result<PathBuf> { + let mut buf = Vec::with_capacity(512); + loop { + unsafe { + let ptr = buf.as_mut_ptr() as *mut c_char; + + // The buffer must be large enough to store the absolute pathname plus + // a terminating null byte, or else null is returned. + // To safely handle this we start with a reasonable size (512 bytes) + // and double the buffer size upon every error + if !libc::getcwd(ptr, buf.capacity()).is_null() { + let len = CStr::from_ptr(buf.as_ptr() as *const c_char).to_bytes().len(); + buf.set_len(len); + buf.shrink_to_fit(); + return Ok(PathBuf::from(OsString::from_vec(buf))); + } else { + let error = Errno::last(); + // ERANGE means buffer was too small to store directory name + if error != Errno::ERANGE { + return Err(Error::Sys(error)); + } + } + + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. + let cap = buf.capacity(); + buf.set_len(cap); + buf.reserve(1); + } + } +} + +/// Computes the raw UID and GID values to pass to a `*chown` call. +fn chown_raw_ids(owner: Option<Uid>, group: Option<Gid>) -> (libc::uid_t, libc::gid_t) { + // According to the POSIX specification, -1 is used to indicate that owner and group + // are not to be changed. Since uid_t and gid_t are unsigned types, we have to wrap + // around to get -1. + let uid = owner.map(Into::into).unwrap_or((0 as uid_t).wrapping_sub(1)); + let gid = group.map(Into::into).unwrap_or((0 as gid_t).wrapping_sub(1)); + (uid, gid) +} + +/// Change the ownership of the file at `path` to be owned by the specified +/// `owner` (user) and `group` (see +/// [chown(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html)). +/// +/// The owner/group for the provided path name will not be modified if `None` is +/// provided for that argument. Ownership change will be attempted for the path +/// only if `Some` owner/group is provided. +#[inline] +pub fn chown<P: ?Sized + NixPath>(path: &P, owner: Option<Uid>, group: Option<Gid>) -> Result<()> { + let res = path.with_nix_path(|cstr| { + let (uid, gid) = chown_raw_ids(owner, group); + unsafe { libc::chown(cstr.as_ptr(), uid, gid) } + })?; + + Errno::result(res).map(drop) +} + +/// Flags for `fchownat` function. +#[derive(Clone, Copy, Debug)] +pub enum FchownatFlags { + FollowSymlink, + NoFollowSymlink, +} + +/// Change the ownership of the file at `path` to be owned by the specified +/// `owner` (user) and `group`. +/// +/// The owner/group for the provided path name will not be modified if `None` is +/// provided for that argument. Ownership change will be attempted for the path +/// only if `Some` owner/group is provided. +/// +/// 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 `None`. +/// +/// If `flag` is `FchownatFlags::NoFollowSymlink` and `path` names a symbolic link, +/// then the mode of the symbolic link is changed. +/// +/// `fchownat(None, path, mode, FchownatFlags::NoFollowSymlink)` is identical to +/// a call `libc::lchown(path, mode)`. That's why `lchmod` is unimplemented in +/// the `nix` crate. +/// +/// # References +/// +/// [fchownat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchownat.html). +pub fn fchownat<P: ?Sized + NixPath>( + dirfd: Option<RawFd>, + path: &P, + owner: Option<Uid>, + group: Option<Gid>, + flag: FchownatFlags, +) -> Result<()> { + let atflag = + match flag { + FchownatFlags::FollowSymlink => AtFlags::empty(), + FchownatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW, + }; + let res = path.with_nix_path(|cstr| unsafe { + let (uid, gid) = chown_raw_ids(owner, group); + libc::fchownat(at_rawfd(dirfd), cstr.as_ptr(), uid, gid, + atflag.bits() as libc::c_int) + })?; + + Errno::result(res).map(drop) +} + +fn to_exec_array(args: &[CString]) -> Vec<*const c_char> { + let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect(); + args_p.push(ptr::null()); + args_p +} + +/// Replace the current process image with a new one (see +/// [exec(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)). +/// +/// See the `::nix::unistd::execve` system call for additional details. `execv` +/// performs the same action but does not allow for customization of the +/// environment for the new process. +#[inline] +pub fn execv(path: &CString, argv: &[CString]) -> Result<Void> { + let args_p = to_exec_array(argv); + + unsafe { + libc::execv(path.as_ptr(), args_p.as_ptr()) + }; + + Err(Error::Sys(Errno::last())) +} + + +/// Replace the current process image with a new one (see +/// [execve(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)). +/// +/// The execve system call allows for another process to be "called" which will +/// replace the current process image. That is, this process becomes the new +/// command that is run. On success, this function will not return. Instead, +/// the new program will run until it exits. +/// +/// `::nix::unistd::execv` and `::nix::unistd::execve` take as arguments a slice +/// of `::std::ffi::CString`s for `args` and `env` (for `execve`). Each element +/// in the `args` list is an argument to the new process. Each element in the +/// `env` list should be a string in the form "key=value". +#[inline] +pub fn execve(path: &CString, args: &[CString], env: &[CString]) -> Result<Void> { + let args_p = to_exec_array(args); + let env_p = to_exec_array(env); + + unsafe { + libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr()) + }; + + Err(Error::Sys(Errno::last())) +} + +/// Replace the current process image with a new one and replicate shell `PATH` +/// searching behavior (see +/// [exec(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)). +/// +/// See `::nix::unistd::execve` for additional details. `execvp` behaves the +/// same as execv except that it will examine the `PATH` environment variables +/// for file names not specified with a leading slash. For example, `execv` +/// would not work if "bash" was specified for the path argument, but `execvp` +/// would assuming that a bash executable was on the system `PATH`. +#[inline] +pub fn execvp(filename: &CString, args: &[CString]) -> Result<Void> { + let args_p = to_exec_array(args); + + unsafe { + libc::execvp(filename.as_ptr(), args_p.as_ptr()) + }; + + Err(Error::Sys(Errno::last())) +} + +/// Replace the current process image with a new one and replicate shell `PATH` +/// searching behavior (see +/// [`execvpe(3)`](http://man7.org/linux/man-pages/man3/exec.3.html)). +/// +/// This functions like a combination of `execvp(2)` and `execve(2)` to pass an +/// environment and have a search path. See these two for additional +/// information. +#[cfg(any(target_os = "haiku", + target_os = "linux", + target_os = "openbsd"))] +pub fn execvpe(filename: &CString, args: &[CString], env: &[CString]) -> Result<Void> { + let args_p = to_exec_array(args); + let env_p = to_exec_array(env); + + unsafe { + libc::execvpe(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr()) + }; + + Err(Error::Sys(Errno::last())) +} + +/// Replace the current process image with a new one (see +/// [fexecve(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fexecve.html)). +/// +/// The `fexecve` function allows for another process to be "called" which will +/// replace the current process image. That is, this process becomes the new +/// command that is run. On success, this function will not return. Instead, +/// the new program will run until it exits. +/// +/// This function is similar to `execve`, except that the program to be executed +/// is referenced as a file descriptor instead of a path. +// Note for NetBSD and OpenBSD: although rust-lang/libc includes it (under +// unix/bsd/netbsdlike/) fexecve is not currently implemented on NetBSD nor on +// OpenBSD. +#[cfg(any(target_os = "android", + target_os = "linux", + target_os = "freebsd"))] +#[inline] +pub fn fexecve(fd: RawFd, args: &[CString], env: &[CString]) -> Result<Void> { + let args_p = to_exec_array(args); + let env_p = to_exec_array(env); + + unsafe { + libc::fexecve(fd, args_p.as_ptr(), env_p.as_ptr()) + }; + + Err(Error::Sys(Errno::last())) +} + +/// Execute program relative to a directory file descriptor (see +/// [execveat(2)](http://man7.org/linux/man-pages/man2/execveat.2.html)). +/// +/// The `execveat` function allows for another process to be "called" which will +/// replace the current process image. That is, this process becomes the new +/// command that is run. On success, this function will not return. Instead, +/// the new program will run until it exits. +/// +/// This function is similar to `execve`, except that the program to be executed +/// is referenced as a file descriptor to the base directory plus a path. +#[cfg(any(target_os = "android", target_os = "linux"))] +#[inline] +pub fn execveat(dirfd: RawFd, pathname: &CString, args: &[CString], + env: &[CString], flags: super::fcntl::AtFlags) -> Result<Void> { + let args_p = to_exec_array(args); + let env_p = to_exec_array(env); + + unsafe { + libc::syscall(libc::SYS_execveat, dirfd, pathname.as_ptr(), + args_p.as_ptr(), env_p.as_ptr(), flags); + }; + + Err(Error::Sys(Errno::last())) +} + +/// Daemonize this process by detaching from the controlling terminal (see +/// [daemon(3)](http://man7.org/linux/man-pages/man3/daemon.3.html)). +/// +/// When a process is launched it is typically associated with a parent and it, +/// in turn, by its controlling terminal/process. In order for a process to run +/// in the "background" it must daemonize itself by detaching itself. Under +/// posix, this is done by doing the following: +/// +/// 1. Parent process (this one) forks +/// 2. Parent process exits +/// 3. Child process continues to run. +/// +/// `nochdir`: +/// +/// * `nochdir = true`: The current working directory after daemonizing will +/// be the current working directory. +/// * `nochdir = false`: The current working directory after daemonizing will +/// be the root direcory, `/`. +/// +/// `noclose`: +/// +/// * `noclose = true`: The process' current stdin, stdout, and stderr file +/// descriptors will remain identical after daemonizing. +/// * `noclose = false`: The process' stdin, stdout, and stderr will point to +/// `/dev/null` after daemonizing. +#[cfg_attr(any(target_os = "macos", target_os = "ios"), deprecated( + since="0.14.0", + note="Deprecated in MacOSX 10.5" +))] +#[cfg_attr(any(target_os = "macos", target_os = "ios"), allow(deprecated))] +pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> { + let res = unsafe { libc::daemon(nochdir as c_int, noclose as c_int) }; + Errno::result(res).map(drop) +} + +/// Set the system host name (see +/// [sethostname(2)](http://man7.org/linux/man-pages/man2/gethostname.2.html)). +/// +/// Given a name, attempt to update the system host name to the given string. +/// On some systems, the host name is limited to as few as 64 bytes. An error +/// will be return if the name is not valid or the current process does not have +/// permissions to update the host name. +pub fn sethostname<S: AsRef<OsStr>>(name: S) -> Result<()> { + // Handle some differences in type of the len arg across platforms. + cfg_if! { + if #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", ))] { + type sethostname_len_t = c_int; + } else { + type sethostname_len_t = size_t; + } + } + let ptr = name.as_ref().as_bytes().as_ptr() as *const c_char; + let len = name.as_ref().len() as sethostname_len_t; + + let res = unsafe { libc::sethostname(ptr, len) }; + Errno::result(res).map(drop) +} + +/// Get the host name and store it in the provided buffer, returning a pointer +/// the `CStr` in that buffer on success (see +/// [gethostname(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html)). +/// +/// This function call attempts to get the host name for the running system and +/// store it in a provided buffer. The buffer will be populated with bytes up +/// to the length of the provided slice including a NUL terminating byte. If +/// the hostname is longer than the length provided, no error will be provided. +/// The posix specification does not specify whether implementations will +/// null-terminate in this case, but the nix implementation will ensure that the +/// buffer is null terminated in this case. +/// +/// ```no_run +/// use nix::unistd; +/// +/// let mut buf = [0u8; 64]; +/// let hostname_cstr = unistd::gethostname(&mut buf).expect("Failed getting hostname"); +/// let hostname = hostname_cstr.to_str().expect("Hostname wasn't valid UTF-8"); +/// println!("Hostname: {}", hostname); +/// ``` +pub fn gethostname(buffer: &mut [u8]) -> Result<&CStr> { + let ptr = buffer.as_mut_ptr() as *mut c_char; + let len = buffer.len() as size_t; + + let res = unsafe { libc::gethostname(ptr, len) }; + Errno::result(res).map(|_| { + buffer[len - 1] = 0; // ensure always null-terminated + unsafe { CStr::from_ptr(buffer.as_ptr() as *const c_char) } + }) +} + +/// Close a raw file descriptor +/// +/// Be aware that many Rust types implicitly close-on-drop, including +/// `std::fs::File`. Explicitly closing them with this method too can result in +/// a double-close condition, which can cause confusing `EBADF` errors in +/// seemingly unrelated code. Caveat programmer. See also +/// [close(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html). +/// +/// # Examples +/// +/// ```no_run +/// extern crate tempfile; +/// extern crate nix; +/// +/// use std::os::unix::io::AsRawFd; +/// use nix::unistd::close; +/// +/// fn main() { +/// let f = tempfile::tempfile().unwrap(); +/// close(f.as_raw_fd()).unwrap(); // Bad! f will also close on drop! +/// } +/// ``` +/// +/// ```rust +/// extern crate tempfile; +/// extern crate nix; +/// +/// use std::os::unix::io::IntoRawFd; +/// use nix::unistd::close; +/// +/// fn main() { +/// let f = tempfile::tempfile().unwrap(); +/// close(f.into_raw_fd()).unwrap(); // Good. into_raw_fd consumes f +/// } +/// ``` +pub fn close(fd: RawFd) -> Result<()> { + let res = unsafe { libc::close(fd) }; + Errno::result(res).map(drop) +} + +/// Read from a raw file descriptor. +/// +/// See also [read(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html) +pub fn read(fd: RawFd, buf: &mut [u8]) -> Result<usize> { + let res = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t) }; + + Errno::result(res).map(|r| r as usize) +} + +/// Write to a raw file descriptor. +/// +/// See also [write(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html) +pub fn write(fd: RawFd, buf: &[u8]) -> Result<usize> { + let res = unsafe { libc::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t) }; + + Errno::result(res).map(|r| r as usize) +} + +/// Directive that tells [`lseek`] and [`lseek64`] what the offset is relative to. +/// +/// [`lseek`]: ./fn.lseek.html +/// [`lseek64`]: ./fn.lseek64.html +#[repr(i32)] +#[derive(Clone, Copy, Debug)] +pub enum Whence { + /// Specify an offset relative to the start of the file. + SeekSet = libc::SEEK_SET, + /// Specify an offset relative to the current file location. + SeekCur = libc::SEEK_CUR, + /// Specify an offset relative to the end of the file. + SeekEnd = libc::SEEK_END, + /// Specify an offset relative to the next location in the file greater than or + /// equal to offset that contains some data. If offset points to + /// some data, then the file offset is set to offset. + #[cfg(any(target_os = "dragonfly", target_os = "freebsd", + all(target_os = "linux", not(any(target_env = "musl", + target_arch = "mips", + target_arch = "mips64")))))] + SeekData = libc::SEEK_DATA, + /// Specify an offset relative to the next hole in the file greater than + /// or equal to offset. If offset points into the middle of a hole, then + /// the file offset should be set to offset. If there is no hole past offset, + /// then the file offset should be adjusted to the end of the file (i.e., there + /// is an implicit hole at the end of any file). + #[cfg(any(target_os = "dragonfly", target_os = "freebsd", + all(target_os = "linux", not(any(target_env = "musl", + target_arch = "mips", + target_arch = "mips64")))))] + SeekHole = libc::SEEK_HOLE +} + +/// Move the read/write file offset. +/// +/// See also [lseek(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lseek.html) +pub fn lseek(fd: RawFd, offset: off_t, whence: Whence) -> Result<off_t> { + let res = unsafe { libc::lseek(fd, offset, whence as i32) }; + + Errno::result(res).map(|r| r as off_t) +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn lseek64(fd: RawFd, offset: libc::off64_t, whence: Whence) -> Result<libc::off64_t> { + let res = unsafe { libc::lseek64(fd, offset, whence as i32) }; + + Errno::result(res).map(|r| r as libc::off64_t) +} + +/// Create an interprocess channel. +/// +/// See also [pipe(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pipe.html) +pub fn pipe() -> Result<(RawFd, RawFd)> { + unsafe { + let mut fds: [c_int; 2] = mem::uninitialized(); + + let res = libc::pipe(fds.as_mut_ptr()); + + Errno::result(res)?; + + Ok((fds[0], fds[1])) + } +} + +/// Like `pipe`, but allows setting certain file descriptor flags. +/// +/// The following flags are supported, and will be set atomically as the pipe is +/// created: +/// +/// `O_CLOEXEC`: Set the close-on-exec flag for the new file descriptors. +/// `O_NONBLOCK`: Set the non-blocking flag for the ends of the pipe. +/// +/// See also [pipe(2)](http://man7.org/linux/man-pages/man2/pipe.2.html) +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd"))] +pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> { + let mut fds: [c_int; 2] = unsafe { mem::uninitialized() }; + + let res = unsafe { libc::pipe2(fds.as_mut_ptr(), flags.bits()) }; + + Errno::result(res)?; + + Ok((fds[0], fds[1])) +} + +/// Like `pipe`, but allows setting certain file descriptor flags. +/// +/// The following flags are supported, and will be set after the pipe is +/// created: +/// +/// `O_CLOEXEC`: Set the close-on-exec flag for the new file descriptors. +/// `O_NONBLOCK`: Set the non-blocking flag for the ends of the pipe. +#[cfg(any(target_os = "ios", target_os = "macos"))] +#[deprecated( + since="0.10.0", + note="pipe2(2) is not actually atomic on these platforms. Use pipe(2) and fcntl(2) instead" +)] +pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> { + let mut fds: [c_int; 2] = unsafe { mem::uninitialized() }; + + let res = unsafe { libc::pipe(fds.as_mut_ptr()) }; + + Errno::result(res)?; + + pipe2_setflags(fds[0], fds[1], flags)?; + + Ok((fds[0], fds[1])) +} + +#[cfg(any(target_os = "ios", target_os = "macos"))] +fn pipe2_setflags(fd1: RawFd, fd2: RawFd, flags: OFlag) -> Result<()> { + use fcntl::FcntlArg::F_SETFL; + + let mut res = Ok(0); + + if flags.contains(OFlag::O_CLOEXEC) { + res = res + .and_then(|_| fcntl(fd1, F_SETFD(FdFlag::FD_CLOEXEC))) + .and_then(|_| fcntl(fd2, F_SETFD(FdFlag::FD_CLOEXEC))); + } + + if flags.contains(OFlag::O_NONBLOCK) { + res = res + .and_then(|_| fcntl(fd1, F_SETFL(OFlag::O_NONBLOCK))) + .and_then(|_| fcntl(fd2, F_SETFL(OFlag::O_NONBLOCK))); + } + + match res { + Ok(_) => Ok(()), + Err(e) => { + let _ = close(fd1); + let _ = close(fd2); + Err(e) + } + } +} + +/// Truncate a file to a specified length +/// +/// See also +/// [truncate(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html) +pub fn truncate<P: ?Sized + NixPath>(path: &P, len: off_t) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { + libc::truncate(cstr.as_ptr(), len) + } + })?; + + Errno::result(res).map(drop) +} + +/// Truncate a file to a specified length +/// +/// See also +/// [ftruncate(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html) +pub fn ftruncate(fd: RawFd, len: off_t) -> Result<()> { + Errno::result(unsafe { libc::ftruncate(fd, len) }).map(drop) +} + +pub fn isatty(fd: RawFd) -> Result<bool> { + unsafe { + // ENOTTY means `fd` is a valid file descriptor, but not a TTY, so + // we return `Ok(false)` + if libc::isatty(fd) == 1 { + Ok(true) + } else { + match Errno::last() { + Errno::ENOTTY => Ok(false), + err => Err(Error::Sys(err)), + } + } + } +} + +/// Remove a directory entry +/// +/// See also [unlink(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html) +pub fn unlink<P: ?Sized + NixPath>(path: &P) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { + libc::unlink(cstr.as_ptr()) + } + })?; + Errno::result(res).map(drop) +} + +#[inline] +pub fn chroot<P: ?Sized + NixPath>(path: &P) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { libc::chroot(cstr.as_ptr()) } + })?; + + Errno::result(res).map(drop) +} + +/// Commit filesystem caches to disk +/// +/// See also [sync(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sync.html) +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd" +))] +pub fn sync() -> () { + unsafe { libc::sync() }; +} + +/// Synchronize changes to a file +/// +/// See also [fsync(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html) +#[inline] +pub fn fsync(fd: RawFd) -> Result<()> { + let res = unsafe { libc::fsync(fd) }; + + Errno::result(res).map(drop) +} + +/// Synchronize the data of a file +/// +/// See also +/// [fdatasync(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html) +// `fdatasync(2) is in POSIX, but in libc it is only defined in `libc::notbsd`. +// TODO: exclude only Apple systems after https://github.com/rust-lang/libc/pull/211 +#[cfg(any(target_os = "linux", + target_os = "android", + target_os = "emscripten"))] +#[inline] +pub fn fdatasync(fd: RawFd) -> Result<()> { + let res = unsafe { libc::fdatasync(fd) }; + + Errno::result(res).map(drop) +} + +/// Get a real user ID +/// +/// See also [getuid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getuid.html) +// POSIX requires that getuid is always successful, so no need to check return +// value or errno. +#[inline] +pub fn getuid() -> Uid { + Uid(unsafe { libc::getuid() }) +} + +/// Get the effective user ID +/// +/// See also [geteuid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/geteuid.html) +// POSIX requires that geteuid is always successful, so no need to check return +// value or errno. +#[inline] +pub fn geteuid() -> Uid { + Uid(unsafe { libc::geteuid() }) +} + +/// Get the real group ID +/// +/// See also [getgid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getgid.html) +// POSIX requires that getgid is always successful, so no need to check return +// value or errno. +#[inline] +pub fn getgid() -> Gid { + Gid(unsafe { libc::getgid() }) +} + +/// Get the effective group ID +/// +/// See also [getegid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getegid.html) +// POSIX requires that getegid is always successful, so no need to check return +// value or errno. +#[inline] +pub fn getegid() -> Gid { + Gid(unsafe { libc::getegid() }) +} + +/// Set the user ID +/// +/// See also [setuid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setuid.html) +#[inline] +pub fn setuid(uid: Uid) -> Result<()> { + let res = unsafe { libc::setuid(uid.into()) }; + + Errno::result(res).map(drop) +} + +/// Set the user ID +/// +/// See also [setgid(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setgid.html) +#[inline] +pub fn setgid(gid: Gid) -> Result<()> { + let res = unsafe { libc::setgid(gid.into()) }; + + Errno::result(res).map(drop) +} + +/// Get the list of supplementary group IDs of the calling process. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/009695399/functions/getgroups.html) +/// +/// **Note:** This function is not available for Apple platforms. On those +/// platforms, checking group membership should be achieved via communication +/// with the `opendirectoryd` service. +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +pub fn getgroups() -> Result<Vec<Gid>> { + // First get the number of groups so we can size our Vec + let ret = unsafe { libc::getgroups(0, ptr::null_mut()) }; + + // Now actually get the groups. We try multiple times in case the number of + // groups has changed since the first call to getgroups() and the buffer is + // now too small. + let mut groups = Vec::<Gid>::with_capacity(Errno::result(ret)? as usize); + loop { + // FIXME: On the platforms we currently support, the `Gid` struct has + // the same representation in memory as a bare `gid_t`. This is not + // necessarily the case on all Rust platforms, though. See RFC 1785. + let ret = unsafe { + libc::getgroups(groups.capacity() as c_int, groups.as_mut_ptr() as *mut gid_t) + }; + + match Errno::result(ret) { + Ok(s) => { + unsafe { groups.set_len(s as usize) }; + return Ok(groups); + }, + Err(Error::Sys(Errno::EINVAL)) => { + // EINVAL indicates that the buffer size was too small. Trigger + // the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. + let cap = groups.capacity(); + unsafe { groups.set_len(cap) }; + groups.reserve(1); + }, + Err(e) => return Err(e) + } + } +} + +/// Set the list of supplementary group IDs for the calling process. +/// +/// [Further reading](http://man7.org/linux/man-pages/man2/getgroups.2.html) +/// +/// **Note:** This function is not available for Apple platforms. On those +/// platforms, group membership management should be achieved via communication +/// with the `opendirectoryd` service. +/// +/// # Examples +/// +/// `setgroups` can be used when dropping privileges from the root user to a +/// specific user and group. For example, given the user `www-data` with UID +/// `33` and the group `backup` with the GID `34`, one could switch the user as +/// follows: +/// +/// ```rust,no_run +/// # use std::error::Error; +/// # use nix::unistd::*; +/// # +/// # fn try_main() -> Result<(), Box<Error>> { +/// let uid = Uid::from_raw(33); +/// let gid = Gid::from_raw(34); +/// setgroups(&[gid])?; +/// setgid(gid)?; +/// setuid(uid)?; +/// # +/// # Ok(()) +/// # } +/// # +/// # fn main() { +/// # try_main().unwrap(); +/// # } +/// ``` +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +pub fn setgroups(groups: &[Gid]) -> Result<()> { + cfg_if! { + if #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] { + type setgroups_ngroups_t = c_int; + } else { + type setgroups_ngroups_t = size_t; + } + } + // FIXME: On the platforms we currently support, the `Gid` struct has the + // same representation in memory as a bare `gid_t`. This is not necessarily + // the case on all Rust platforms, though. See RFC 1785. + let res = unsafe { + libc::setgroups(groups.len() as setgroups_ngroups_t, groups.as_ptr() as *const gid_t) + }; + + Errno::result(res).map(drop) +} + +/// Calculate the supplementary group access list. +/// +/// Gets the group IDs of all groups that `user` is a member of. The additional +/// group `group` is also added to the list. +/// +/// [Further reading](http://man7.org/linux/man-pages/man3/getgrouplist.3.html) +/// +/// **Note:** This function is not available for Apple platforms. On those +/// platforms, checking group membership should be achieved via communication +/// with the `opendirectoryd` service. +/// +/// # Errors +/// +/// Although the `getgrouplist()` call does not return any specific +/// errors on any known platforms, this implementation will return a system +/// error of `EINVAL` if the number of groups to be fetched exceeds the +/// `NGROUPS_MAX` sysconf value. This mimics the behaviour of `getgroups()` +/// and `setgroups()`. Additionally, while some implementations will return a +/// partial list of groups when `NGROUPS_MAX` is exceeded, this implementation +/// will only ever return the complete list or else an error. +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +pub fn getgrouplist(user: &CStr, group: Gid) -> Result<Vec<Gid>> { + let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) { + Ok(Some(n)) => n as c_int, + Ok(None) | Err(_) => <c_int>::max_value(), + }; + use std::cmp::min; + let mut ngroups = min(ngroups_max, 8); + let mut groups = Vec::<Gid>::with_capacity(ngroups as usize); + cfg_if! { + if #[cfg(any(target_os = "ios", target_os = "macos"))] { + type getgrouplist_group_t = c_int; + } else { + type getgrouplist_group_t = gid_t; + } + } + let gid: gid_t = group.into(); + loop { + let ret = unsafe { + libc::getgrouplist(user.as_ptr(), + gid as getgrouplist_group_t, + groups.as_mut_ptr() as *mut getgrouplist_group_t, + &mut ngroups) + }; + + // BSD systems only return 0 or -1, Linux returns ngroups on success. + if ret >= 0 { + unsafe { groups.set_len(ngroups as usize) }; + return Ok(groups); + } else if ret == -1 { + // Returns -1 if ngroups is too small, but does not set errno. + // BSD systems will still fill the groups buffer with as many + // groups as possible, but Linux manpages do not mention this + // behavior. + + let cap = groups.capacity(); + if cap >= ngroups_max as usize { + // We already have the largest capacity we can, give up + return Err(Error::invalid_argument()); + } + + // Reserve space for at least ngroups + groups.reserve(ngroups as usize); + + // Even if the buffer gets resized to bigger than ngroups_max, + // don't ever ask for more than ngroups_max groups + ngroups = min(ngroups_max, groups.capacity() as c_int); + } + } +} + +/// Initialize the supplementary group access list. +/// +/// Sets the supplementary group IDs for the calling process using all groups +/// that `user` is a member of. The additional group `group` is also added to +/// the list. +/// +/// [Further reading](http://man7.org/linux/man-pages/man3/initgroups.3.html) +/// +/// **Note:** This function is not available for Apple platforms. On those +/// platforms, group membership management should be achieved via communication +/// with the `opendirectoryd` service. +/// +/// # Examples +/// +/// `initgroups` can be used when dropping privileges from the root user to +/// another user. For example, given the user `www-data`, we could look up the +/// UID and GID for the user in the system's password database (usually found +/// in `/etc/passwd`). If the `www-data` user's UID and GID were `33` and `33`, +/// respectively, one could switch the user as follows: +/// +/// ```rust,no_run +/// # use std::error::Error; +/// # use std::ffi::CString; +/// # use nix::unistd::*; +/// # +/// # fn try_main() -> Result<(), Box<Error>> { +/// let user = CString::new("www-data").unwrap(); +/// let uid = Uid::from_raw(33); +/// let gid = Gid::from_raw(33); +/// initgroups(&user, gid)?; +/// setgid(gid)?; +/// setuid(uid)?; +/// # +/// # Ok(()) +/// # } +/// # +/// # fn main() { +/// # try_main().unwrap(); +/// # } +/// ``` +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +pub fn initgroups(user: &CStr, group: Gid) -> Result<()> { + cfg_if! { + if #[cfg(any(target_os = "ios", target_os = "macos"))] { + type initgroups_group_t = c_int; + } else { + type initgroups_group_t = gid_t; + } + } + let gid: gid_t = group.into(); + let res = unsafe { libc::initgroups(user.as_ptr(), gid as initgroups_group_t) }; + + Errno::result(res).map(drop) +} + +/// Suspend the thread until a signal is received. +/// +/// See also [pause(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pause.html). +#[inline] +pub fn pause() { + unsafe { libc::pause() }; +} + +pub mod alarm { + //! Alarm signal scheduling. + //! + //! Scheduling an alarm will trigger a `SIGALRM` signal when the time has + //! elapsed, which has to be caught, because the default action for the + //! signal is to terminate the program. This signal also can't be ignored + //! because the system calls like `pause` will not be interrupted, see the + //! second example below. + //! + //! # Examples + //! + //! Canceling an alarm: + //! + //! ``` + //! use nix::unistd::alarm; + //! + //! // Set an alarm for 60 seconds from now. + //! alarm::set(60); + //! + //! // Cancel the above set alarm, which returns the number of seconds left + //! // of the previously set alarm. + //! assert_eq!(alarm::cancel(), Some(60)); + //! ``` + //! + //! Scheduling an alarm and waiting for the signal: + //! + //! ``` + //! use std::time::{Duration, Instant}; + //! + //! use nix::unistd::{alarm, pause}; + //! use nix::sys::signal::*; + //! + //! // We need to setup an empty signal handler to catch the alarm signal, + //! // otherwise the program will be terminated once the signal is delivered. + //! extern fn signal_handler(_: nix::libc::c_int) { } + //! unsafe { sigaction(Signal::SIGALRM, &SigAction::new(SigHandler::Handler(signal_handler), SaFlags::empty(), SigSet::empty())); } + //! + //! // Set an alarm for 1 second from now. + //! alarm::set(1); + //! + //! let start = Instant::now(); + //! // Pause the process until the alarm signal is received. + //! pause(); + //! + //! assert!(start.elapsed() >= Duration::from_secs(1)); + //! ``` + //! + //! # References + //! + //! See also [alarm(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/alarm.html). + + use libc; + + /// Schedule an alarm signal. + /// + /// This will cause the system to generate a `SIGALRM` signal for the + /// process after the specified number of seconds have elapsed. + /// + /// Returns the leftover time of a previously set alarm if there was one. + pub fn set(secs: libc::c_uint) -> Option<libc::c_uint> { + assert!(secs != 0, "passing 0 to `alarm::set` is not allowed, to cancel an alarm use `alarm::cancel`"); + alarm(secs) + } + + /// Cancel an previously set alarm signal. + /// + /// Returns the leftover time of a previously set alarm if there was one. + pub fn cancel() -> Option<libc::c_uint> { + alarm(0) + } + + fn alarm(secs: libc::c_uint) -> Option<libc::c_uint> { + match unsafe { libc::alarm(secs) } { + 0 => None, + secs => Some(secs), + } + } +} + +/// Suspend execution for an interval of time +/// +/// See also [sleep(2)](http://pubs.opengroup.org/onlinepubs/009695399/functions/sleep.html#tag_03_705_05) +// Per POSIX, does not fail +#[inline] +pub fn sleep(seconds: c_uint) -> c_uint { + unsafe { libc::sleep(seconds) } +} + +pub mod acct { + use libc; + use {Result, NixPath}; + use errno::Errno; + use std::ptr; + + /// Enable process accounting + /// + /// See also [acct(2)](https://linux.die.net/man/2/acct) + pub fn enable<P: ?Sized + NixPath>(filename: &P) -> Result<()> { + let res = filename.with_nix_path(|cstr| { + unsafe { libc::acct(cstr.as_ptr()) } + })?; + + Errno::result(res).map(drop) + } + + /// Disable process accounting + pub fn disable() -> Result<()> { + let res = unsafe { libc::acct(ptr::null()) }; + + Errno::result(res).map(drop) + } +} + +/// Creates a regular file which persists even after process termination +/// +/// * `template`: a path whose 6 rightmost characters must be X, e.g. `/tmp/tmpfile_XXXXXX` +/// * returns: tuple of file descriptor and filename +/// +/// Err is returned either if no temporary filename could be created or the template doesn't +/// end with XXXXXX +/// +/// See also [mkstemp(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/mkstemp.html) +/// +/// # Example +/// +/// ```rust +/// use nix::unistd; +/// +/// let _ = match unistd::mkstemp("/tmp/tempfile_XXXXXX") { +/// Ok((fd, path)) => { +/// unistd::unlink(path.as_path()).unwrap(); // flag file to be deleted at app termination +/// fd +/// } +/// Err(e) => panic!("mkstemp failed: {}", e) +/// }; +/// // do something with fd +/// ``` +#[inline] +pub fn mkstemp<P: ?Sized + NixPath>(template: &P) -> Result<(RawFd, PathBuf)> { + let mut path = template.with_nix_path(|path| {path.to_bytes_with_nul().to_owned()})?; + let p = path.as_mut_ptr() as *mut _; + let fd = unsafe { libc::mkstemp(p) }; + let last = path.pop(); // drop the trailing nul + debug_assert!(last == Some(b'\0')); + let pathname = OsString::from_vec(path); + Errno::result(fd)?; + Ok((fd, PathBuf::from(pathname))) +} + +/// Variable names for `pathconf` +/// +/// Nix uses the same naming convention for these variables as the +/// [getconf(1)](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility. +/// That is, `PathconfVar` variables have the same name as the abstract +/// variables shown in the `pathconf(2)` man page. Usually, it's the same as +/// the C variable name without the leading `_PC_`. +/// +/// POSIX 1003.1-2008 standardizes all of these variables, but some OSes choose +/// not to implement variables that cannot change at runtime. +/// +/// # References +/// +/// - [pathconf(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html) +/// - [limits.h](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html) +/// - [unistd.h](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html) +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[repr(i32)] +pub enum PathconfVar { + #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "linux", + target_os = "netbsd", target_os = "openbsd"))] + /// Minimum number of bits needed to represent, as a signed integer value, + /// the maximum size of a regular file allowed in the specified directory. + FILESIZEBITS = libc::_PC_FILESIZEBITS, + /// Maximum number of links to a single file. + LINK_MAX = libc::_PC_LINK_MAX, + /// Maximum number of bytes in a terminal canonical input line. + MAX_CANON = libc::_PC_MAX_CANON, + /// Minimum number of bytes for which space is available in a terminal input + /// queue; therefore, the maximum number of bytes a conforming application + /// may require to be typed as input before reading them. + MAX_INPUT = libc::_PC_MAX_INPUT, + /// Maximum number of bytes in a filename (not including the terminating + /// null of a filename string). + NAME_MAX = libc::_PC_NAME_MAX, + /// Maximum number of bytes the implementation will store as a pathname in a + /// user-supplied buffer of unspecified size, including the terminating null + /// character. Minimum number the implementation will accept as the maximum + /// number of bytes in a pathname. + PATH_MAX = libc::_PC_PATH_MAX, + /// Maximum number of bytes that is guaranteed to be atomic when writing to + /// a pipe. + PIPE_BUF = libc::_PC_PIPE_BUF, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "linux", + target_os = "netbsd", target_os = "openbsd"))] + /// Symbolic links can be created. + POSIX2_SYMLINKS = libc::_PC_2_SYMLINKS, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "openbsd"))] + /// Minimum number of bytes of storage actually allocated for any portion of + /// a file. + POSIX_ALLOC_SIZE_MIN = libc::_PC_ALLOC_SIZE_MIN, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "openbsd"))] + /// Recommended increment for file transfer sizes between the + /// `POSIX_REC_MIN_XFER_SIZE` and `POSIX_REC_MAX_XFER_SIZE` values. + POSIX_REC_INCR_XFER_SIZE = libc::_PC_REC_INCR_XFER_SIZE, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "openbsd"))] + /// Maximum recommended file transfer size. + POSIX_REC_MAX_XFER_SIZE = libc::_PC_REC_MAX_XFER_SIZE, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "openbsd"))] + /// Minimum recommended file transfer size. + POSIX_REC_MIN_XFER_SIZE = libc::_PC_REC_MIN_XFER_SIZE, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "openbsd"))] + /// Recommended file transfer buffer alignment. + POSIX_REC_XFER_ALIGN = libc::_PC_REC_XFER_ALIGN, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "netbsd", target_os = "openbsd"))] + /// Maximum number of bytes in a symbolic link. + SYMLINK_MAX = libc::_PC_SYMLINK_MAX, + /// The use of `chown` and `fchown` is restricted to a process with + /// appropriate privileges, and to changing the group ID of a file only to + /// the effective group ID of the process or to one of its supplementary + /// group IDs. + _POSIX_CHOWN_RESTRICTED = libc::_PC_CHOWN_RESTRICTED, + /// Pathname components longer than {NAME_MAX} generate an error. + _POSIX_NO_TRUNC = libc::_PC_NO_TRUNC, + /// This symbol shall be defined to be the value of a character that shall + /// disable terminal special character handling. + _POSIX_VDISABLE = libc::_PC_VDISABLE, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "openbsd"))] + /// Asynchronous input or output operations may be performed for the + /// associated file. + _POSIX_ASYNC_IO = libc::_PC_ASYNC_IO, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "openbsd"))] + /// Prioritized input or output operations may be performed for the + /// associated file. + _POSIX_PRIO_IO = libc::_PC_PRIO_IO, + #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "linux", target_os = "netbsd", target_os = "openbsd"))] + /// Synchronized input or output operations may be performed for the + /// associated file. + _POSIX_SYNC_IO = libc::_PC_SYNC_IO, + #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))] + /// The resolution in nanoseconds for all file timestamps. + _POSIX_TIMESTAMP_RESOLUTION = libc::_PC_TIMESTAMP_RESOLUTION +} + +/// Like `pathconf`, but works with file descriptors instead of paths (see +/// [fpathconf(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html)) +/// +/// # Parameters +/// +/// - `fd`: The file descriptor whose variable should be interrogated +/// - `var`: The pathconf variable to lookup +/// +/// # Returns +/// +/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its +/// implementation level (for option variables). Implementation levels are +/// usually a decimal-coded date, such as 200112 for POSIX 2001.12 +/// - `Ok(None)`: the variable has no limit (for limit variables) or is +/// unsupported (for option variables) +/// - `Err(x)`: an error occurred +pub fn fpathconf(fd: RawFd, var: PathconfVar) -> Result<Option<c_long>> { + let raw = unsafe { + Errno::clear(); + libc::fpathconf(fd, var as c_int) + }; + if raw == -1 { + if errno::errno() == 0 { + Ok(None) + } else { + Err(Error::Sys(Errno::last())) + } + } else { + Ok(Some(raw)) + } +} + +/// Get path-dependent configurable system variables (see +/// [pathconf(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html)) +/// +/// Returns the value of a path-dependent configurable system variable. Most +/// supported variables also have associated compile-time constants, but POSIX +/// allows their values to change at runtime. There are generally two types of +/// `pathconf` variables: options and limits. See [pathconf(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html) for more details. +/// +/// # Parameters +/// +/// - `path`: Lookup the value of `var` for this file or directory +/// - `var`: The `pathconf` variable to lookup +/// +/// # Returns +/// +/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its +/// implementation level (for option variables). Implementation levels are +/// usually a decimal-coded date, such as 200112 for POSIX 2001.12 +/// - `Ok(None)`: the variable has no limit (for limit variables) or is +/// unsupported (for option variables) +/// - `Err(x)`: an error occurred +pub fn pathconf<P: ?Sized + NixPath>(path: &P, var: PathconfVar) -> Result<Option<c_long>> { + let raw = path.with_nix_path(|cstr| { + unsafe { + Errno::clear(); + libc::pathconf(cstr.as_ptr(), var as c_int) + } + })?; + if raw == -1 { + if errno::errno() == 0 { + Ok(None) + } else { + Err(Error::Sys(Errno::last())) + } + } else { + Ok(Some(raw)) + } +} + +/// Variable names for `sysconf` +/// +/// Nix uses the same naming convention for these variables as the +/// [getconf(1)](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility. +/// That is, `SysconfVar` variables have the same name as the abstract variables +/// shown in the `sysconf(3)` man page. Usually, it's the same as the C +/// variable name without the leading `_SC_`. +/// +/// All of these symbols are standardized by POSIX 1003.1-2008, but haven't been +/// implemented by all platforms. +/// +/// # References +/// +/// - [sysconf(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html) +/// - [unistd.h](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html) +/// - [limits.h](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html) +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[repr(i32)] +pub enum SysconfVar { + /// Maximum number of I/O operations in a single list I/O call supported by + /// the implementation. + AIO_LISTIO_MAX = libc::_SC_AIO_LISTIO_MAX, + /// Maximum number of outstanding asynchronous I/O operations supported by + /// the implementation. + AIO_MAX = libc::_SC_AIO_MAX, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// The maximum amount by which a process can decrease its asynchronous I/O + /// priority level from its own scheduling priority. + AIO_PRIO_DELTA_MAX = libc::_SC_AIO_PRIO_DELTA_MAX, + /// Maximum length of argument to the exec functions including environment data. + ARG_MAX = libc::_SC_ARG_MAX, + /// Maximum number of functions that may be registered with `atexit`. + ATEXIT_MAX = libc::_SC_ATEXIT_MAX, + /// Maximum obase values allowed by the bc utility. + BC_BASE_MAX = libc::_SC_BC_BASE_MAX, + /// Maximum number of elements permitted in an array by the bc utility. + BC_DIM_MAX = libc::_SC_BC_DIM_MAX, + /// Maximum scale value allowed by the bc utility. + BC_SCALE_MAX = libc::_SC_BC_SCALE_MAX, + /// Maximum length of a string constant accepted by the bc utility. + BC_STRING_MAX = libc::_SC_BC_STRING_MAX, + /// Maximum number of simultaneous processes per real user ID. + CHILD_MAX = libc::_SC_CHILD_MAX, + // _SC_CLK_TCK is obsolete + /// Maximum number of weights that can be assigned to an entry of the + /// LC_COLLATE order keyword in the locale definition file + COLL_WEIGHTS_MAX = libc::_SC_COLL_WEIGHTS_MAX, + /// Maximum number of timer expiration overruns. + DELAYTIMER_MAX = libc::_SC_DELAYTIMER_MAX, + /// Maximum number of expressions that can be nested within parentheses by + /// the expr utility. + EXPR_NEST_MAX = libc::_SC_EXPR_NEST_MAX, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// Maximum length of a host name (not including the terminating null) as + /// returned from the `gethostname` function + HOST_NAME_MAX = libc::_SC_HOST_NAME_MAX, + /// Maximum number of iovec structures that one process has available for + /// use with `readv` or `writev`. + IOV_MAX = libc::_SC_IOV_MAX, + /// Unless otherwise noted, the maximum length, in bytes, of a utility's + /// input line (either standard input or another file), when the utility is + /// described as processing text files. The length includes room for the + /// trailing <newline>. + LINE_MAX = libc::_SC_LINE_MAX, + /// Maximum length of a login name. + LOGIN_NAME_MAX = libc::_SC_LOGIN_NAME_MAX, + /// Maximum number of simultaneous supplementary group IDs per process. + NGROUPS_MAX = libc::_SC_NGROUPS_MAX, + /// Initial size of `getgrgid_r` and `getgrnam_r` data buffers + GETGR_R_SIZE_MAX = libc::_SC_GETGR_R_SIZE_MAX, + /// Initial size of `getpwuid_r` and `getpwnam_r` data buffers + GETPW_R_SIZE_MAX = libc::_SC_GETPW_R_SIZE_MAX, + /// The maximum number of open message queue descriptors a process may hold. + MQ_OPEN_MAX = libc::_SC_MQ_OPEN_MAX, + /// The maximum number of message priorities supported by the implementation. + MQ_PRIO_MAX = libc::_SC_MQ_PRIO_MAX, + /// A value one greater than the maximum value that the system may assign to + /// a newly-created file descriptor. + OPEN_MAX = libc::_SC_OPEN_MAX, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Advisory Information option. + _POSIX_ADVISORY_INFO = libc::_SC_ADVISORY_INFO, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports barriers. + _POSIX_BARRIERS = libc::_SC_BARRIERS, + /// The implementation supports asynchronous input and output. + _POSIX_ASYNCHRONOUS_IO = libc::_SC_ASYNCHRONOUS_IO, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports clock selection. + _POSIX_CLOCK_SELECTION = libc::_SC_CLOCK_SELECTION, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Process CPU-Time Clocks option. + _POSIX_CPUTIME = libc::_SC_CPUTIME, + /// The implementation supports the File Synchronization option. + _POSIX_FSYNC = libc::_SC_FSYNC, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the IPv6 option. + _POSIX_IPV6 = libc::_SC_IPV6, + /// The implementation supports job control. + _POSIX_JOB_CONTROL = libc::_SC_JOB_CONTROL, + /// The implementation supports memory mapped Files. + _POSIX_MAPPED_FILES = libc::_SC_MAPPED_FILES, + /// The implementation supports the Process Memory Locking option. + _POSIX_MEMLOCK = libc::_SC_MEMLOCK, + /// The implementation supports the Range Memory Locking option. + _POSIX_MEMLOCK_RANGE = libc::_SC_MEMLOCK_RANGE, + /// The implementation supports memory protection. + _POSIX_MEMORY_PROTECTION = libc::_SC_MEMORY_PROTECTION, + /// The implementation supports the Message Passing option. + _POSIX_MESSAGE_PASSING = libc::_SC_MESSAGE_PASSING, + /// The implementation supports the Monotonic Clock option. + _POSIX_MONOTONIC_CLOCK = libc::_SC_MONOTONIC_CLOCK, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// The implementation supports the Prioritized Input and Output option. + _POSIX_PRIORITIZED_IO = libc::_SC_PRIORITIZED_IO, + /// The implementation supports the Process Scheduling option. + _POSIX_PRIORITY_SCHEDULING = libc::_SC_PRIORITY_SCHEDULING, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Raw Sockets option. + _POSIX_RAW_SOCKETS = libc::_SC_RAW_SOCKETS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports read-write locks. + _POSIX_READER_WRITER_LOCKS = libc::_SC_READER_WRITER_LOCKS, + #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os = "openbsd"))] + /// The implementation supports realtime signals. + _POSIX_REALTIME_SIGNALS = libc::_SC_REALTIME_SIGNALS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Regular Expression Handling option. + _POSIX_REGEXP = libc::_SC_REGEXP, + /// Each process has a saved set-user-ID and a saved set-group-ID. + _POSIX_SAVED_IDS = libc::_SC_SAVED_IDS, + /// The implementation supports semaphores. + _POSIX_SEMAPHORES = libc::_SC_SEMAPHORES, + /// The implementation supports the Shared Memory Objects option. + _POSIX_SHARED_MEMORY_OBJECTS = libc::_SC_SHARED_MEMORY_OBJECTS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the POSIX shell. + _POSIX_SHELL = libc::_SC_SHELL, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Spawn option. + _POSIX_SPAWN = libc::_SC_SPAWN, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports spin locks. + _POSIX_SPIN_LOCKS = libc::_SC_SPIN_LOCKS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Process Sporadic Server option. + _POSIX_SPORADIC_SERVER = libc::_SC_SPORADIC_SERVER, + #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + _POSIX_SS_REPL_MAX = libc::_SC_SS_REPL_MAX, + /// The implementation supports the Synchronized Input and Output option. + _POSIX_SYNCHRONIZED_IO = libc::_SC_SYNCHRONIZED_IO, + /// The implementation supports the Thread Stack Address Attribute option. + _POSIX_THREAD_ATTR_STACKADDR = libc::_SC_THREAD_ATTR_STACKADDR, + /// The implementation supports the Thread Stack Size Attribute option. + _POSIX_THREAD_ATTR_STACKSIZE = libc::_SC_THREAD_ATTR_STACKSIZE, + #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", + target_os="netbsd", target_os="openbsd"))] + /// The implementation supports the Thread CPU-Time Clocks option. + _POSIX_THREAD_CPUTIME = libc::_SC_THREAD_CPUTIME, + /// The implementation supports the Non-Robust Mutex Priority Inheritance + /// option. + _POSIX_THREAD_PRIO_INHERIT = libc::_SC_THREAD_PRIO_INHERIT, + /// The implementation supports the Non-Robust Mutex Priority Protection option. + _POSIX_THREAD_PRIO_PROTECT = libc::_SC_THREAD_PRIO_PROTECT, + /// The implementation supports the Thread Execution Scheduling option. + _POSIX_THREAD_PRIORITY_SCHEDULING = libc::_SC_THREAD_PRIORITY_SCHEDULING, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Thread Process-Shared Synchronization + /// option. + _POSIX_THREAD_PROCESS_SHARED = libc::_SC_THREAD_PROCESS_SHARED, + #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))] + /// The implementation supports the Robust Mutex Priority Inheritance option. + _POSIX_THREAD_ROBUST_PRIO_INHERIT = libc::_SC_THREAD_ROBUST_PRIO_INHERIT, + #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))] + /// The implementation supports the Robust Mutex Priority Protection option. + _POSIX_THREAD_ROBUST_PRIO_PROTECT = libc::_SC_THREAD_ROBUST_PRIO_PROTECT, + /// The implementation supports thread-safe functions. + _POSIX_THREAD_SAFE_FUNCTIONS = libc::_SC_THREAD_SAFE_FUNCTIONS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Thread Sporadic Server option. + _POSIX_THREAD_SPORADIC_SERVER = libc::_SC_THREAD_SPORADIC_SERVER, + /// The implementation supports threads. + _POSIX_THREADS = libc::_SC_THREADS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports timeouts. + _POSIX_TIMEOUTS = libc::_SC_TIMEOUTS, + /// The implementation supports timers. + _POSIX_TIMERS = libc::_SC_TIMERS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Trace option. + _POSIX_TRACE = libc::_SC_TRACE, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Trace Event Filter option. + _POSIX_TRACE_EVENT_FILTER = libc::_SC_TRACE_EVENT_FILTER, + #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + _POSIX_TRACE_EVENT_NAME_MAX = libc::_SC_TRACE_EVENT_NAME_MAX, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Trace Inherit option. + _POSIX_TRACE_INHERIT = libc::_SC_TRACE_INHERIT, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Trace Log option. + _POSIX_TRACE_LOG = libc::_SC_TRACE_LOG, + #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + _POSIX_TRACE_NAME_MAX = libc::_SC_TRACE_NAME_MAX, + #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + _POSIX_TRACE_SYS_MAX = libc::_SC_TRACE_SYS_MAX, + #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + _POSIX_TRACE_USER_EVENT_MAX = libc::_SC_TRACE_USER_EVENT_MAX, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the Typed Memory Objects option. + _POSIX_TYPED_MEMORY_OBJECTS = libc::_SC_TYPED_MEMORY_OBJECTS, + /// Integer value indicating version of this standard (C-language binding) + /// to which the implementation conforms. For implementations conforming to + /// POSIX.1-2008, the value shall be 200809L. + _POSIX_VERSION = libc::_SC_VERSION, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation provides a C-language compilation environment with + /// 32-bit `int`, `long`, `pointer`, and `off_t` types. + _POSIX_V6_ILP32_OFF32 = libc::_SC_V6_ILP32_OFF32, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation provides a C-language compilation environment with + /// 32-bit `int`, `long`, and pointer types and an `off_t` type using at + /// least 64 bits. + _POSIX_V6_ILP32_OFFBIG = libc::_SC_V6_ILP32_OFFBIG, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation provides a C-language compilation environment with + /// 32-bit `int` and 64-bit `long`, `pointer`, and `off_t` types. + _POSIX_V6_LP64_OFF64 = libc::_SC_V6_LP64_OFF64, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation provides a C-language compilation environment with an + /// `int` type using at least 32 bits and `long`, pointer, and `off_t` types + /// using at least 64 bits. + _POSIX_V6_LPBIG_OFFBIG = libc::_SC_V6_LPBIG_OFFBIG, + /// The implementation supports the C-Language Binding option. + _POSIX2_C_BIND = libc::_SC_2_C_BIND, + /// The implementation supports the C-Language Development Utilities option. + _POSIX2_C_DEV = libc::_SC_2_C_DEV, + /// The implementation supports the Terminal Characteristics option. + _POSIX2_CHAR_TERM = libc::_SC_2_CHAR_TERM, + /// The implementation supports the FORTRAN Development Utilities option. + _POSIX2_FORT_DEV = libc::_SC_2_FORT_DEV, + /// The implementation supports the FORTRAN Runtime Utilities option. + _POSIX2_FORT_RUN = libc::_SC_2_FORT_RUN, + /// The implementation supports the creation of locales by the localedef + /// utility. + _POSIX2_LOCALEDEF = libc::_SC_2_LOCALEDEF, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Batch Environment Services and Utilities + /// option. + _POSIX2_PBS = libc::_SC_2_PBS, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Batch Accounting option. + _POSIX2_PBS_ACCOUNTING = libc::_SC_2_PBS_ACCOUNTING, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Batch Checkpoint/Restart option. + _POSIX2_PBS_CHECKPOINT = libc::_SC_2_PBS_CHECKPOINT, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Locate Batch Job Request option. + _POSIX2_PBS_LOCATE = libc::_SC_2_PBS_LOCATE, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Batch Job Message Request option. + _POSIX2_PBS_MESSAGE = libc::_SC_2_PBS_MESSAGE, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + /// The implementation supports the Track Batch Job Request option. + _POSIX2_PBS_TRACK = libc::_SC_2_PBS_TRACK, + /// The implementation supports the Software Development Utilities option. + _POSIX2_SW_DEV = libc::_SC_2_SW_DEV, + /// The implementation supports the User Portability Utilities option. + _POSIX2_UPE = libc::_SC_2_UPE, + /// Integer value indicating version of the Shell and Utilities volume of + /// POSIX.1 to which the implementation conforms. + _POSIX2_VERSION = libc::_SC_2_VERSION, + /// The size of a system page in bytes. + /// + /// POSIX also defines an alias named `PAGESIZE`, but Rust does not allow two + /// enum constants to have the same value, so nix omits `PAGESIZE`. + PAGE_SIZE = libc::_SC_PAGE_SIZE, + PTHREAD_DESTRUCTOR_ITERATIONS = libc::_SC_THREAD_DESTRUCTOR_ITERATIONS, + PTHREAD_KEYS_MAX = libc::_SC_THREAD_KEYS_MAX, + PTHREAD_STACK_MIN = libc::_SC_THREAD_STACK_MIN, + PTHREAD_THREADS_MAX = libc::_SC_THREAD_THREADS_MAX, + RE_DUP_MAX = libc::_SC_RE_DUP_MAX, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + RTSIG_MAX = libc::_SC_RTSIG_MAX, + SEM_NSEMS_MAX = libc::_SC_SEM_NSEMS_MAX, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + SEM_VALUE_MAX = libc::_SC_SEM_VALUE_MAX, + #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os = "openbsd"))] + SIGQUEUE_MAX = libc::_SC_SIGQUEUE_MAX, + STREAM_MAX = libc::_SC_STREAM_MAX, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="netbsd", + target_os="openbsd"))] + SYMLOOP_MAX = libc::_SC_SYMLOOP_MAX, + TIMER_MAX = libc::_SC_TIMER_MAX, + TTY_NAME_MAX = libc::_SC_TTY_NAME_MAX, + TZNAME_MAX = libc::_SC_TZNAME_MAX, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// The implementation supports the X/Open Encryption Option Group. + _XOPEN_CRYPT = libc::_SC_XOPEN_CRYPT, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// The implementation supports the Issue 4, Version 2 Enhanced + /// Internationalization Option Group. + _XOPEN_ENH_I18N = libc::_SC_XOPEN_ENH_I18N, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + _XOPEN_LEGACY = libc::_SC_XOPEN_LEGACY, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// The implementation supports the X/Open Realtime Option Group. + _XOPEN_REALTIME = libc::_SC_XOPEN_REALTIME, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// The implementation supports the X/Open Realtime Threads Option Group. + _XOPEN_REALTIME_THREADS = libc::_SC_XOPEN_REALTIME_THREADS, + /// The implementation supports the Issue 4, Version 2 Shared Memory Option + /// Group. + _XOPEN_SHM = libc::_SC_XOPEN_SHM, + #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", + target_os="linux", target_os = "macos", target_os="openbsd"))] + /// The implementation supports the XSI STREAMS Option Group. + _XOPEN_STREAMS = libc::_SC_XOPEN_STREAMS, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// The implementation supports the XSI option + _XOPEN_UNIX = libc::_SC_XOPEN_UNIX, + #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd", + target_os = "ios", target_os="linux", target_os = "macos", + target_os="openbsd"))] + /// Integer value indicating version of the X/Open Portability Guide to + /// which the implementation conforms. + _XOPEN_VERSION = libc::_SC_XOPEN_VERSION, +} + +/// Get configurable system variables (see +/// [sysconf(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html)) +/// +/// Returns the value of a configurable system variable. Most supported +/// variables also have associated compile-time constants, but POSIX +/// allows their values to change at runtime. There are generally two types of +/// sysconf variables: options and limits. See sysconf(3) for more details. +/// +/// # Returns +/// +/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its +/// implementation level (for option variables). Implementation levels are +/// usually a decimal-coded date, such as 200112 for POSIX 2001.12 +/// - `Ok(None)`: the variable has no limit (for limit variables) or is +/// unsupported (for option variables) +/// - `Err(x)`: an error occurred +pub fn sysconf(var: SysconfVar) -> Result<Option<c_long>> { + let raw = unsafe { + Errno::clear(); + libc::sysconf(var as c_int) + }; + if raw == -1 { + if errno::errno() == 0 { + Ok(None) + } else { + Err(Error::Sys(Errno::last())) + } + } else { + Ok(Some(raw)) + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod pivot_root { + use libc; + use {Result, NixPath}; + use errno::Errno; + + pub fn pivot_root<P1: ?Sized + NixPath, P2: ?Sized + NixPath>( + new_root: &P1, put_old: &P2) -> Result<()> { + let res = new_root.with_nix_path(|new_root| { + put_old.with_nix_path(|put_old| { + unsafe { + libc::syscall(libc::SYS_pivot_root, new_root.as_ptr(), put_old.as_ptr()) + } + }) + })??; + + Errno::result(res).map(drop) + } +} + +#[cfg(any(target_os = "android", target_os = "freebsd", + target_os = "linux", target_os = "openbsd"))] +mod setres { + use libc; + use Result; + use errno::Errno; + use super::{Uid, Gid}; + + /// Sets the real, effective, and saved uid. + /// ([see setresuid(2)](http://man7.org/linux/man-pages/man2/setresuid.2.html)) + /// + /// * `ruid`: real user id + /// * `euid`: effective user id + /// * `suid`: saved user id + /// * returns: Ok or libc error code. + /// + /// Err is returned if the user doesn't have permission to set this UID. + #[inline] + pub fn setresuid(ruid: Uid, euid: Uid, suid: Uid) -> Result<()> { + let res = unsafe { libc::setresuid(ruid.into(), euid.into(), suid.into()) }; + + Errno::result(res).map(drop) + } + + /// Sets the real, effective, and saved gid. + /// ([see setresuid(2)](http://man7.org/linux/man-pages/man2/setresuid.2.html)) + /// + /// * `rgid`: real group id + /// * `egid`: effective group id + /// * `sgid`: saved group id + /// * returns: Ok or libc error code. + /// + /// Err is returned if the user doesn't have permission to set this GID. + #[inline] + pub fn setresgid(rgid: Gid, egid: Gid, sgid: Gid) -> Result<()> { + let res = unsafe { libc::setresgid(rgid.into(), egid.into(), sgid.into()) }; + + Errno::result(res).map(drop) + } +} diff --git a/third_party/rust/nix/test/sys/mod.rs b/third_party/rust/nix/test/sys/mod.rs new file mode 100644 index 0000000000..46f2912394 --- /dev/null +++ b/third_party/rust/nix/test/sys/mod.rs @@ -0,0 +1,36 @@ +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", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd"))] +mod test_aio; +#[cfg(target_os = "linux")] +mod test_signalfd; +mod test_socket; +mod test_sockopt; +mod test_select; +#[cfg(any(target_os = "android", target_os = "linux"))] +mod test_sysinfo; +mod test_termios; +mod test_ioctl; +mod test_wait; +mod test_uio; + +#[cfg(target_os = "linux")] +mod test_epoll; +mod test_pthread; +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +mod test_ptrace; diff --git a/third_party/rust/nix/test/sys/test_aio.rs b/third_party/rust/nix/test/sys/test_aio.rs new file mode 100644 index 0000000000..d4b09b0b81 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_aio.rs @@ -0,0 +1,654 @@ +use bytes::{Bytes, BytesMut}; +use libc::{c_int, c_void}; +use nix::{Error, Result}; +use nix::errno::*; +use nix::sys::aio::*; +use nix::sys::signal::{SaFlags, SigAction, sigaction, SigevNotify, SigHandler, Signal, SigSet}; +use nix::sys::time::{TimeSpec, TimeValLike}; +use std::io::{Write, Read, Seek, SeekFrom}; +use std::ops::Deref; +use std::os::unix::io::AsRawFd; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::{thread, time}; +use tempfile::tempfile; + +// Helper that polls an AioCb for completion or error +fn poll_aio(aiocb: &mut AioCb) -> Result<()> { + loop { + let err = aiocb.error(); + if err != Err(Error::from(Errno::EINPROGRESS)) { return err; }; + thread::sleep(time::Duration::from_millis(10)); + } +} + +#[test] +fn test_accessors() { + let mut rbuf = vec![0; 4]; + let aiocb = AioCb::from_mut_slice( 1001, + 2, //offset + &mut rbuf, + 42, //priority + SigevNotify::SigevSignal { + signal: Signal::SIGUSR2, + si_value: 99 + }, + LioOpcode::LIO_NOP); + assert_eq!(1001, aiocb.fd()); + assert_eq!(Some(LioOpcode::LIO_NOP), aiocb.lio_opcode()); + 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 AioCb.cancel. We aren't trying to test the OS's implementation, only +// our bindings. So it's sufficient to check that AioCb.cancel returned any +// AioCancelStat value. +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_cancel() { + let wbuf: &[u8] = b"CDEF"; + + let f = tempfile().unwrap(); + let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + 0, //offset + wbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.write().unwrap(); + let err = aiocb.error(); + assert!(err == Ok(()) || err == Err(Error::from(Errno::EINPROGRESS))); + + let cancelstat = aiocb.cancel(); + assert!(cancelstat.is_ok()); + + // Wait for aiocb to complete, but don't care whether it succeeded + let _ = poll_aio(&mut aiocb); + let _ = aiocb.aio_return(); +} + +// Tests using aio_cancel_all for all outstanding IOs. +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_aio_cancel_all() { + let wbuf: &[u8] = b"CDEF"; + + let f = tempfile().unwrap(); + let mut aiocb = AioCb::from_slice(f.as_raw_fd(), + 0, //offset + wbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.write().unwrap(); + let err = aiocb.error(); + assert!(err == Ok(()) || err == Err(Error::from(Errno::EINPROGRESS))); + + let cancelstat = aio_cancel_all(f.as_raw_fd()); + assert!(cancelstat.is_ok()); + + // Wait for aiocb to complete, but don't care whether it succeeded + let _ = poll_aio(&mut aiocb); + let _ = aiocb.aio_return(); +} + +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_fsync() { + const INITIAL: &[u8] = b"abcdef123456"; + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + let mut aiocb = AioCb::from_fd( f.as_raw_fd(), + 0, //priority + SigevNotify::SigevNone); + let err = aiocb.fsync(AioFsyncMode::O_SYNC); + assert!(err.is_ok()); + poll_aio(&mut aiocb).unwrap(); + aiocb.aio_return().unwrap(); +} + +/// `AioCb::fsync` 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(any(target_os = "freebsd", target_os = "macos"))] +fn test_fsync_error() { + use std::mem; + + const INITIAL: &[u8] = b"abcdef123456"; + // Create an invalid AioFsyncMode + let mode = unsafe { mem::transmute(666) }; + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + let mut aiocb = AioCb::from_fd( f.as_raw_fd(), + 0, //priority + SigevNotify::SigevNone); + let err = aiocb.fsync(mode); + assert!(err.is_err()); +} + +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +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 = AioCb::from_slice( f.as_raw_fd(), + 2, //offset + WBUF, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_WRITE); + + let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(), + 8, //offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_READ); + wcb.write().unwrap(); + rcb.read().unwrap(); + loop { + { + let cbbuf = [&wcb, &rcb]; + assert!(aio_suspend(&cbbuf[..], Some(timeout)).is_ok()); + } + if rcb.error() != Err(Error::from(Errno::EINPROGRESS)) && + wcb.error() != Err(Error::from(Errno::EINPROGRESS)) { + break + } + } + + assert!(wcb.aio_return().unwrap() as usize == WBUF.len()); + assert!(rcb.aio_return().unwrap() as usize == rlen); +} + +// 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 test_read() { + 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 mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(), + 2, //offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.read().unwrap(); + + let err = poll_aio(&mut aiocb); + assert!(err == Ok(())); + assert!(aiocb.aio_return().unwrap() as usize == EXPECT.len()); + } + + assert!(EXPECT == rbuf.deref().deref()); +} + +/// `AioCb::read` 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", target_os = "macos"))] +fn test_read_error() { + const INITIAL: &[u8] = b"abcdef123456"; + let mut rbuf = vec![0; 4]; + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(), + -1, //an invalid offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + assert!(aiocb.read().is_err()); +} + +// Tests from_mut_slice +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_read_into_mut_slice() { + 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 mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(), + 2, //offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.read().unwrap(); + + let err = poll_aio(&mut aiocb); + assert!(err == Ok(())); + assert!(aiocb.aio_return().unwrap() as usize == EXPECT.len()); + } + + assert!(rbuf == EXPECT); +} + +// Tests from_ptr +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_read_into_pointer() { + 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(); + { + // Safety: ok because rbuf lives until after poll_aio + let mut aiocb = unsafe { + AioCb::from_mut_ptr( f.as_raw_fd(), + 2, //offset + rbuf.as_mut_ptr() as *mut c_void, + rbuf.len(), + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP) + }; + aiocb.read().unwrap(); + + let err = poll_aio(&mut aiocb); + assert!(err == Ok(())); + assert!(aiocb.aio_return().unwrap() as usize == EXPECT.len()); + } + + assert!(rbuf == EXPECT); +} + +// Test reading into an immutable buffer. It should fail +// FIXME: This test fails to panic on Linux/musl +#[test] +#[should_panic(expected = "Can't read into an immutable buffer")] +#[cfg_attr(target_env = "musl", ignore)] +fn test_read_immutable_buffer() { + let rbuf: &[u8] = b"CDEF"; + let f = tempfile().unwrap(); + let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + 2, //offset + rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.read().unwrap(); +} + + +// Test a simple aio operation with no completion notification. We must poll +// for completion. Unlike test_aio_read, this test uses AioCb::from_slice +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_write() { + 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 aiocb = AioCb::from_slice( f.as_raw_fd(), + 2, //offset + &wbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.write().unwrap(); + + let err = poll_aio(&mut aiocb); + assert!(err == Ok(())); + assert!(aiocb.aio_return().unwrap() as usize == wbuf.len()); + + f.seek(SeekFrom::Start(0)).unwrap(); + let len = f.read_to_end(&mut rbuf).unwrap(); + assert!(len == EXPECT.len()); + assert!(rbuf == EXPECT); +} + +// Tests `AioCb::from_boxed_slice` with `Bytes` +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_write_bytes() { + const INITIAL: &[u8] = b"abcdef123456"; + let wbuf = Box::new(Bytes::from(&b"CDEF"[..])); + let mut rbuf = Vec::new(); + const EXPECT: &[u8] = b"abCDEF123456"; + let expected_len = wbuf.len(); + + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + let mut aiocb = AioCb::from_boxed_slice( f.as_raw_fd(), + 2, //offset + wbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.write().unwrap(); + + let err = poll_aio(&mut aiocb); + assert!(err == Ok(())); + assert!(aiocb.aio_return().unwrap() as usize == expected_len); + + f.seek(SeekFrom::Start(0)).unwrap(); + let len = f.read_to_end(&mut rbuf).unwrap(); + assert!(len == EXPECT.len()); + assert!(rbuf == EXPECT); +} + +// Tests `AioCb::from_boxed_mut_slice` with `BytesMut` +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_read_bytes_mut_small() { + const INITIAL: &[u8] = b"abcdef"; + let rbuf = Box::new(BytesMut::from(vec![0; 4])); + const EXPECT: &[u8] = b"cdef"; + let mut f = tempfile().unwrap(); + f.write_all(INITIAL).unwrap(); + + let mut aiocb = AioCb::from_boxed_mut_slice( f.as_raw_fd(), + 2, //offset + rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.read().unwrap(); + + let err = poll_aio(&mut aiocb); + assert_eq!(err, Ok(())); + assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len()); + let buffer = aiocb.boxed_mut_slice().unwrap(); + assert_eq!(buffer.borrow(), EXPECT); +} + +// Tests `AioCb::from_ptr` +#[test] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_write_from_pointer() { + 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(); + // Safety: ok because aiocb outlives poll_aio + let mut aiocb = unsafe { + AioCb::from_ptr( f.as_raw_fd(), + 2, //offset + wbuf.as_ptr() as *const c_void, + wbuf.len(), + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP) + }; + aiocb.write().unwrap(); + + let err = poll_aio(&mut aiocb); + assert!(err == Ok(())); + assert!(aiocb.aio_return().unwrap() as usize == wbuf.len()); + + f.seek(SeekFrom::Start(0)).unwrap(); + let len = f.read_to_end(&mut rbuf).unwrap(); + assert!(len == EXPECT.len()); + assert!(rbuf == EXPECT); +} + +/// `AioCb::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(any(target_os = "freebsd", target_os = "macos"))] +fn test_write_error() { + let wbuf = "CDEF".to_string().into_bytes(); + let mut aiocb = AioCb::from_slice( 666, // An invalid file descriptor + 0, //offset + &wbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + assert!(aiocb.write().is_err()); +} + +lazy_static! { + pub static ref SIGNALED: AtomicBool = AtomicBool::new(false); +} + +extern fn sigfunc(_: c_int) { + SIGNALED.store(true, Ordering::Relaxed); +} + +// Test an aio operation with completion delivered by a signal +// FIXME: This test is ignored on mips because of failures in qemu in CI +#[test] +#[cfg_attr(any(all(target_env = "musl", target_arch = "x86_64"), target_arch = "mips", target_arch = "mips64"), ignore)] +fn test_write_sigev_signal() { + let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + 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 aiocb = AioCb::from_slice( f.as_raw_fd(), + 2, //offset + WBUF, + 0, //priority + SigevNotify::SigevSignal { + signal: Signal::SIGUSR2, + si_value: 0 //TODO: validate in sigfunc + }, + LioOpcode::LIO_NOP); + aiocb.write().unwrap(); + while !SIGNALED.load(Ordering::Relaxed) { + thread::sleep(time::Duration::from_millis(10)); + } + + assert!(aiocb.aio_return().unwrap() as usize == WBUF.len()); + f.seek(SeekFrom::Start(0)).unwrap(); + let len = f.read_to_end(&mut rbuf).unwrap(); + assert!(len == EXPECT.len()); + assert!(rbuf == EXPECT); +} + +// Test LioCb::listio with LIO_WAIT, so all AIO ops should be complete by the +// time listio returns. +#[test] +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_liocb_listio_wait() { + const INITIAL: &[u8] = b"abcdef123456"; + const WBUF: &[u8] = b"CDEF"; + let mut rbuf = vec![0; 4]; + let rlen = rbuf.len(); + let mut rbuf2 = Vec::new(); + const EXPECT: &[u8] = b"abCDEF123456"; + let mut f = tempfile().unwrap(); + + f.write_all(INITIAL).unwrap(); + + { + let wcb = AioCb::from_slice( f.as_raw_fd(), + 2, //offset + WBUF, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_WRITE); + + let rcb = AioCb::from_mut_slice( f.as_raw_fd(), + 8, //offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_READ); + let mut liocb = LioCb::with_capacity(2); + liocb.aiocbs.push(wcb); + liocb.aiocbs.push(rcb); + let err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone); + err.expect("lio_listio"); + + assert!(liocb.aio_return(0).unwrap() as usize == WBUF.len()); + assert!(liocb.aio_return(1).unwrap() as usize == rlen); + } + assert!(rbuf.deref().deref() == b"3456"); + + f.seek(SeekFrom::Start(0)).unwrap(); + let len = f.read_to_end(&mut rbuf2).unwrap(); + assert!(len == EXPECT.len()); + assert!(rbuf2 == EXPECT); +} + +// Test LioCb::listio with LIO_NOWAIT and no SigEvent, so we must use some other +// mechanism to check for the individual AioCb's completion. +#[test] +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +#[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)] +fn test_liocb_listio_nowait() { + const INITIAL: &[u8] = b"abcdef123456"; + const WBUF: &[u8] = b"CDEF"; + let mut rbuf = vec![0; 4]; + let rlen = rbuf.len(); + let mut rbuf2 = Vec::new(); + const EXPECT: &[u8] = b"abCDEF123456"; + let mut f = tempfile().unwrap(); + + f.write_all(INITIAL).unwrap(); + + { + let wcb = AioCb::from_slice( f.as_raw_fd(), + 2, //offset + WBUF, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_WRITE); + + let rcb = AioCb::from_mut_slice( f.as_raw_fd(), + 8, //offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_READ); + let mut liocb = LioCb::with_capacity(2); + liocb.aiocbs.push(wcb); + liocb.aiocbs.push(rcb); + let err = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone); + err.expect("lio_listio"); + + poll_aio(&mut liocb.aiocbs[0]).unwrap(); + poll_aio(&mut liocb.aiocbs[1]).unwrap(); + assert!(liocb.aiocbs[0].aio_return().unwrap() as usize == WBUF.len()); + assert!(liocb.aiocbs[1].aio_return().unwrap() as usize == rlen); + } + assert!(rbuf.deref().deref() == b"3456"); + + f.seek(SeekFrom::Start(0)).unwrap(); + let len = f.read_to_end(&mut rbuf2).unwrap(); + assert!(len == EXPECT.len()); + assert!(rbuf2 == EXPECT); +} + +// Test LioCb::listio with LIO_NOWAIT and a SigEvent to indicate when all +// AioCb's are complete. +// FIXME: This test is ignored on mips/mips64 because of failures in qemu in CI. +#[test] +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +#[cfg_attr(any(target_arch = "mips", target_arch = "mips64", target_env = "musl"), ignore)] +fn test_liocb_listio_signal() { + let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + const INITIAL: &[u8] = b"abcdef123456"; + const WBUF: &[u8] = b"CDEF"; + let mut rbuf = vec![0; 4]; + let rlen = rbuf.len(); + let mut rbuf2 = Vec::new(); + const EXPECT: &[u8] = b"abCDEF123456"; + let mut f = tempfile().unwrap(); + let sa = SigAction::new(SigHandler::Handler(sigfunc), + SaFlags::SA_RESETHAND, + SigSet::empty()); + let sigev_notify = SigevNotify::SigevSignal { signal: Signal::SIGUSR2, + si_value: 0 }; + + f.write_all(INITIAL).unwrap(); + + { + let wcb = AioCb::from_slice( f.as_raw_fd(), + 2, //offset + WBUF, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_WRITE); + + let rcb = AioCb::from_mut_slice( f.as_raw_fd(), + 8, //offset + &mut rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_READ); + let mut liocb = LioCb::with_capacity(2); + liocb.aiocbs.push(wcb); + liocb.aiocbs.push(rcb); + SIGNALED.store(false, Ordering::Relaxed); + unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap(); + let err = liocb.listio(LioMode::LIO_NOWAIT, sigev_notify); + err.expect("lio_listio"); + while !SIGNALED.load(Ordering::Relaxed) { + thread::sleep(time::Duration::from_millis(10)); + } + + assert!(liocb.aiocbs[0].aio_return().unwrap() as usize == WBUF.len()); + assert!(liocb.aiocbs[1].aio_return().unwrap() as usize == rlen); + } + assert!(rbuf.deref().deref() == b"3456"); + + f.seek(SeekFrom::Start(0)).unwrap(); + let len = f.read_to_end(&mut rbuf2).unwrap(); + assert!(len == EXPECT.len()); + assert!(rbuf2 == EXPECT); +} + +// Try to use LioCb::listio to read into an immutable buffer. It should fail +// FIXME: This test fails to panic on Linux/musl +#[test] +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +#[should_panic(expected = "Can't read into an immutable buffer")] +#[cfg_attr(target_env = "musl", ignore)] +fn test_liocb_listio_read_immutable() { + let rbuf: &[u8] = b"abcd"; + let f = tempfile().unwrap(); + + + let mut liocb = LioCb::from(vec![ + AioCb::from_slice( f.as_raw_fd(), + 2, //offset + rbuf, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_READ) + ]); + let _ = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone); +} diff --git a/third_party/rust/nix/test/sys/test_aio_drop.rs b/third_party/rust/nix/test/sys/test_aio_drop.rs new file mode 100644 index 0000000000..492da401ef --- /dev/null +++ b/third_party/rust/nix/test/sys/test_aio_drop.rs @@ -0,0 +1,32 @@ +extern crate nix; +extern crate tempfile; + +// 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"), + any(target_os = "linux", + target_os = "ios", + target_os = "macos", + target_os = "freebsd", + target_os = "netbsd")))] +fn test_drop() { + use nix::sys::aio::*; + use nix::sys::signal::*; + use std::os::unix::io::AsRawFd; + use tempfile::tempfile; + + const WBUF: &[u8] = b"CDEF"; + + let f = tempfile().unwrap(); + f.set_len(6).unwrap(); + let mut aiocb = AioCb::from_slice( f.as_raw_fd(), + 2, //offset + WBUF, + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_NOP); + aiocb.write().unwrap(); +} diff --git a/third_party/rust/nix/test/sys/test_epoll.rs b/third_party/rust/nix/test/sys/test_epoll.rs new file mode 100644 index 0000000000..e0dc5131de --- /dev/null +++ b/third_party/rust/nix/test/sys/test_epoll.rs @@ -0,0 +1,24 @@ +use nix::sys::epoll::{EpollCreateFlags, EpollFlags, EpollOp, EpollEvent}; +use nix::sys::epoll::{epoll_create1, epoll_ctl}; +use nix::Error; +use nix::errno::Errno; + +#[test] +pub fn test_epoll_errno() { + let efd = epoll_create1(EpollCreateFlags::empty()).unwrap(); + let result = epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::Sys(Errno::ENOENT)); + + let result = epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, None); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::Sys(Errno::EINVAL)); +} + +#[test] +pub fn test_epoll_ctl() { + let efd = epoll_create1(EpollCreateFlags::empty()).unwrap(); + let mut event = EpollEvent::new(EpollFlags::EPOLLIN | EpollFlags::EPOLLERR, 1); + epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, &mut event).unwrap(); + epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None).unwrap(); +} diff --git a/third_party/rust/nix/test/sys/test_ioctl.rs b/third_party/rust/nix/test/sys/test_ioctl.rs new file mode 100644 index 0000000000..0a439b3346 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_ioctl.rs @@ -0,0 +1,334 @@ +#![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(any(target_os = "linux", target_os = "android"))] +mod linux { + #[test] + fn test_op_none() { + if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){ + assert_eq!(request_code_none!(b'q', 10), 0x2000_710A); + assert_eq!(request_code_none!(b'a', 255), 0x2000_61FF); + } else { + assert_eq!(request_code_none!(b'q', 10), 0x0000_710A); + assert_eq!(request_code_none!(b'a', 255), 0x0000_61FF); + } + } + + #[test] + fn test_op_write() { + if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){ + assert_eq!(request_code_write!(b'z', 10, 1), 0x8001_7A0A); + assert_eq!(request_code_write!(b'z', 10, 512), 0x8200_7A0A); + } else { + assert_eq!(request_code_write!(b'z', 10, 1), 0x4001_7A0A); + assert_eq!(request_code_write!(b'z', 10, 512), 0x4200_7A0A); + } + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_op_write_64() { + if cfg!(any(target_arch = "mips64", target_arch="powerpc64")){ + assert_eq!(request_code_write!(b'z', 10, (1 as u64) << 32), 0x8000_7A0A); + } else { + assert_eq!(request_code_write!(b'z', 10, (1 as u64) << 32), 0x4000_7A0A); + } + + } + + #[test] + fn test_op_read() { + if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){ + assert_eq!(request_code_read!(b'z', 10, 1), 0x4001_7A0A); + assert_eq!(request_code_read!(b'z', 10, 512), 0x4200_7A0A); + } else { + assert_eq!(request_code_read!(b'z', 10, 1), 0x8001_7A0A); + assert_eq!(request_code_read!(b'z', 10, 512), 0x8200_7A0A); + } + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn test_op_read_64() { + if cfg!(any(target_arch = "mips64", target_arch="powerpc64")){ + assert_eq!(request_code_read!(b'z', 10, (1 as u64) << 32), 0x4000_7A0A); + } else { + assert_eq!(request_code_read!(b'z', 10, (1 as u64) << 32), 0x8000_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, (1 as u64) << 32), 0xC000_7A0A); + } +} + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +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(any(target_os = "dragonfly", target_os = "freebsd"))] + #[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, (1 as u64) << 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, (1 as u64) << 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, (1 as u64) << 32), 0xC000_7A0A); + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod linux_ioctls { + use std::mem; + use std::os::unix::io::AsRawFd; + + use tempfile::tempfile; + use libc::{TCGETS, TCSBRK, TCSETS, TIOCNXCL, termios}; + + use nix::Error::Sys; + use nix::errno::Errno::{ENOTTY, ENOSYS}; + + 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(Sys(ENOTTY))); + } + + ioctl_read_bad!(tcgets, TCGETS, termios); + #[test] + fn test_ioctl_read_bad() { + let file = tempfile().unwrap(); + let mut termios = unsafe { mem::uninitialized() }; + let res = unsafe { tcgets(file.as_raw_fd(), &mut termios) }; + assert_eq!(res, Err(Sys(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(Sys(ENOTTY))); + } + + ioctl_write_ptr_bad!(tcsets, TCSETS, termios); + #[test] + fn test_ioctl_write_ptr_bad() { + let file = tempfile().unwrap(); + let termios: termios = unsafe { mem::uninitialized() }; + let res = unsafe { tcsets(file.as_raw_fd(), &termios) }; + assert_eq!(res, Err(Sys(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(Sys(ENOTTY)) || res == Err(Sys(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(Sys(ENOTTY)) || res == Err(Sys(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(Sys(ENOTTY)) || res == Err(Sys(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::uninitialized() }; + let res = unsafe { g_audio(file.as_raw_fd(), &mut data) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(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::uninitialized() }; + let res = unsafe { enum_audio(file.as_raw_fd(), &mut data) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(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(Sys(ENOTTY)) || res == Err(Sys(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 tempfile::tempfile; + use libc::termios; + + use nix::Error::Sys; + use nix::errno::Errno::ENOTTY; + + // 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(Sys(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::uninitialized() }; + let res = unsafe { tiocgeta(file.as_raw_fd(), &mut termios) }; + assert_eq!(res, Err(Sys(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::uninitialized() }; + let res = unsafe { tiocseta(file.as_raw_fd(), &termios) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } +} diff --git a/third_party/rust/nix/test/sys/test_lio_listio_resubmit.rs b/third_party/rust/nix/test/sys/test_lio_listio_resubmit.rs new file mode 100644 index 0000000000..19ee3facf8 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_lio_listio_resubmit.rs @@ -0,0 +1,111 @@ +// vim: tw=80 + +// Annoyingly, Cargo is unable to conditionally build an entire test binary. So +// we must disable the test here rather than in Cargo.toml +#![cfg(target_os = "freebsd")] + +extern crate nix; +extern crate sysctl; +extern crate tempfile; + +use nix::Error; +use nix::errno::*; +use nix::libc::off_t; +use nix::sys::aio::*; +use nix::sys::signal::SigevNotify; +use nix::unistd::{SysconfVar, sysconf}; +use std::os::unix::io::AsRawFd; +use std::{thread, time}; +use sysctl::CtlValue; +use tempfile::tempfile; + +const BYTES_PER_OP: usize = 512; + +/// Attempt to collect final status for all of `liocb`'s operations, freeing +/// system resources +fn finish_liocb(liocb: &mut LioCb) { + for j in 0..liocb.aiocbs.len() { + loop { + let e = liocb.error(j); + match e { + Ok(()) => break, + Err(Error::Sys(Errno::EINPROGRESS)) => + thread::sleep(time::Duration::from_millis(10)), + Err(x) => panic!("aio_error({:?})", x) + } + } + assert_eq!(liocb.aio_return(j).unwrap(), BYTES_PER_OP as isize); + } +} + +// Deliberately exceed system resource limits, causing lio_listio to return EIO. +// This test must run in its own process since it deliberately uses all AIO +// resources. ATM it is only enabled on FreeBSD, because I don't know how to +// check system AIO limits on other operating systems. +#[test] +fn test_lio_listio_resubmit() { + let mut resubmit_count = 0; + + // Lookup system resource limits + let alm = sysconf(SysconfVar::AIO_LISTIO_MAX) + .expect("sysconf").unwrap() as usize; + let maqpp = if let CtlValue::Int(x) = sysctl::value( + "vfs.aio.max_aio_queue_per_proc").unwrap(){ + x as usize + } else { + panic!("unknown sysctl"); + }; + + // Find lio_listio sizes that satisfy the AIO_LISTIO_MAX constraint and also + // result in a final lio_listio call that can only partially be queued + let target_ops = maqpp + alm / 2; + let num_listios = (target_ops + alm - 3) / (alm - 2); + let ops_per_listio = (target_ops + num_listios - 1) / num_listios; + assert!((num_listios - 1) * ops_per_listio < maqpp, + "the last lio_listio won't make any progress; fix the algorithm"); + println!("Using {:?} LioCbs of {:?} operations apiece", num_listios, + ops_per_listio); + + let f = tempfile().unwrap(); + let buffer_set = (0..num_listios).map(|_| { + (0..ops_per_listio).map(|_| { + vec![0u8; BYTES_PER_OP] + }).collect::<Vec<_>>() + }).collect::<Vec<_>>(); + + let mut liocbs = (0..num_listios).map(|i| { + let mut liocb = LioCb::with_capacity(ops_per_listio); + for j in 0..ops_per_listio { + let offset = (BYTES_PER_OP * (i * ops_per_listio + j)) as off_t; + let wcb = AioCb::from_slice( f.as_raw_fd(), + offset, + &buffer_set[i][j][..], + 0, //priority + SigevNotify::SigevNone, + LioOpcode::LIO_WRITE); + liocb.aiocbs.push(wcb); + } + let mut err = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone); + while err == Err(Error::Sys(Errno::EIO)) || + err == Err(Error::Sys(Errno::EAGAIN)) || + err == Err(Error::Sys(Errno::EINTR)) { + // + thread::sleep(time::Duration::from_millis(10)); + resubmit_count += 1; + err = liocb.listio_resubmit(LioMode::LIO_NOWAIT, + SigevNotify::SigevNone); + } + liocb + }).collect::<Vec<_>>(); + + // Ensure that every AioCb completed + for liocb in liocbs.iter_mut() { + finish_liocb(liocb); + } + + if resubmit_count > 0 { + println!("Resubmitted {:?} times, test passed", resubmit_count); + } else { + println!("Never resubmitted. Test ambiguous"); + } +} diff --git a/third_party/rust/nix/test/sys/test_pthread.rs b/third_party/rust/nix/test/sys/test_pthread.rs new file mode 100644 index 0000000000..8928010087 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_pthread.rs @@ -0,0 +1,15 @@ +use nix::sys::pthread::*; + +#[cfg(target_env = "musl")] +#[test] +fn test_pthread_self() { + let tid = pthread_self(); + assert!(tid != ::std::ptr::null_mut()); +} + +#[cfg(not(target_env = "musl"))] +#[test] +fn test_pthread_self() { + let tid = pthread_self(); + assert!(tid != 0); +} diff --git a/third_party/rust/nix/test/sys/test_ptrace.rs b/third_party/rust/nix/test/sys/test_ptrace.rs new file mode 100644 index 0000000000..24d9b522ee --- /dev/null +++ b/third_party/rust/nix/test/sys/test_ptrace.rs @@ -0,0 +1,107 @@ +use nix::Error; +use nix::errno::Errno; +use nix::unistd::getpid; +use nix::sys::ptrace; +#[cfg(any(target_os = "android", target_os = "linux"))] +use nix::sys::ptrace::Options; + +#[cfg(any(target_os = "android", target_os = "linux"))] +use std::mem; + +#[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 + let err = ptrace::attach(getpid()).unwrap_err(); + assert!(err == Error::Sys(Errno::EPERM) || err == Error::Sys(Errno::EINVAL) || + err == Error::Sys(Errno::ENOSYS)); +} + +// Just make sure ptrace_setoptions can be called at all, for now. +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptrace_setoptions() { + let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD).unwrap_err(); + assert!(err != Error::UnsupportedOperation); +} + +// Just make sure ptrace_getevent can be called at all, for now. +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptrace_getevent() { + let err = ptrace::getevent(getpid()).unwrap_err(); + assert!(err != Error::UnsupportedOperation); +} + +// Just make sure ptrace_getsiginfo can be called at all, for now. +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptrace_getsiginfo() { + if let Err(Error::UnsupportedOperation) = ptrace::getsiginfo(getpid()) { + panic!("ptrace_getsiginfo returns Error::UnsupportedOperation!"); + } +} + +// Just make sure ptrace_setsiginfo can be called at all, for now. +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptrace_setsiginfo() { + let siginfo = unsafe { mem::uninitialized() }; + if let Err(Error::UnsupportedOperation) = ptrace::setsiginfo(getpid(), &siginfo) { + panic!("ptrace_setsiginfo returns Error::UnsupportedOperation!"); + } +} + + +#[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::*; + + let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // FIXME: qemu-user doesn't implement ptrace on all architectures + // and retunrs 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 == Error::Sys(Errno::ENOSYS) { + return; + } + + match 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"), + } + }, + } +} diff --git a/third_party/rust/nix/test/sys/test_select.rs b/third_party/rust/nix/test/sys/test_select.rs new file mode 100644 index 0000000000..cf68700c5e --- /dev/null +++ b/third_party/rust/nix/test/sys/test_select.rs @@ -0,0 +1,54 @@ +use nix::sys::select::*; +use nix::unistd::{pipe, write}; +use nix::sys::signal::SigSet; +use nix::sys::time::{TimeSpec, TimeValLike}; + +#[test] +pub fn test_pselect() { + let _mtx = ::SIGNAL_MTX + .lock() + .expect("Mutex got poisoned by another test"); + + 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); + fd_set.insert(r2); + + 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)); + assert!(!fd_set.contains(r2)); +} + +#[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); + fd_set.insert(r2); + + let timeout = TimeSpec::seconds(10); + assert_eq!( + 1, + pselect( + ::std::cmp::max(r1, r2) + 1, + &mut fd_set, + None, + None, + &timeout, + None + ).unwrap() + ); + assert!(fd_set.contains(r1)); + assert!(!fd_set.contains(r2)); +} diff --git a/third_party/rust/nix/test/sys/test_signal.rs b/third_party/rust/nix/test/sys/test_signal.rs new file mode 100644 index 0000000000..f5e4dfd9eb --- /dev/null +++ b/third_party/rust/nix/test/sys/test_signal.rs @@ -0,0 +1,98 @@ +use libc; +use nix::Error; +use nix::sys::signal::*; +use nix::unistd::*; +use std::sync::atomic::{AtomicBool, Ordering}; + +#[test] +fn test_kill_none() { + kill(getpid(), None).expect("Should be able to send signal to myself."); +} + +#[test] +fn test_old_sigaction_flags() { + 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 = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + + // 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_eq!(old_signal_set.contains(SIGNAL), false, + "the {:?} signal is already blocked, please change to a \ + different one", SIGNAL); + + // 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_eq!(old_signal_set.contains(SIGNAL), true, + "expected the {:?} to be blocked", SIGNAL); + + // Reset the signal. + sigprocmask(SigmaskHow::SIG_UNBLOCK, Some(&signal_set), None) + .expect("expect to be able to block signals"); +} + +lazy_static! { + static ref SIGNALED: AtomicBool = AtomicBool::new(false); +} + +extern fn test_sigaction_handler(signal: libc::c_int) { + let signal = Signal::from_c_int(signal).unwrap(); + SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed); +} + +extern fn test_sigaction_action(_: libc::c_int, _: *mut libc::siginfo_t, _: *mut libc::c_void) { +} + +#[test] +fn test_signal() { + let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + + 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)); + assert_eq!(unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(), handler); + + let action_handler = SigHandler::SigAction(test_sigaction_action); + assert_eq!(unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(), Error::UnsupportedOperation); + + // Restore default signal handler + unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(); +} diff --git a/third_party/rust/nix/test/sys/test_signalfd.rs b/third_party/rust/nix/test/sys/test_signalfd.rs new file mode 100644 index 0000000000..a3b6098841 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_signalfd.rs @@ -0,0 +1,25 @@ +#[test] +fn test_signalfd() { + use nix::sys::signalfd::SignalFd; + use nix::sys::signal::{self, raise, Signal, SigSet}; + + // Grab the mutex for altering signals so we don't interfere with other tests. + let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + + // Block the SIGUSR1 signal from automatic processing for this thread + let mut mask = SigSet::empty(); + mask.add(signal::SIGUSR1); + mask.thread_block().unwrap(); + + let mut 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::from_c_int(res.ssi_signo as i32).unwrap(); + assert_eq!(signo, signal::SIGUSR1); +} diff --git a/third_party/rust/nix/test/sys/test_socket.rs b/third_party/rust/nix/test/sys/test_socket.rs new file mode 100644 index 0000000000..1ed6c1f6ca --- /dev/null +++ b/third_party/rust/nix/test/sys/test_socket.rs @@ -0,0 +1,685 @@ +use nix::sys::socket::{InetAddr, UnixAddr, getsockname}; +use std::slice; +use std::net::{self, Ipv6Addr, SocketAddr, SocketAddrV6}; +use std::path::Path; +use std::str::FromStr; +use std::os::unix::io::RawFd; +use libc::c_char; +use tempfile; + +#[test] +pub fn test_inetv4_addr_to_sock_addr() { + let actual: net::SocketAddr = FromStr::from_str("127.0.0.1:3000").unwrap(); + let addr = InetAddr::from_std(&actual); + + match addr { + InetAddr::V4(addr) => { + let ip: u32 = 0x7f00_0001; + let port: u16 = 3000; + let saddr = addr.sin_addr.s_addr; + + assert_eq!(saddr, ip.to_be()); + assert_eq!(addr.sin_port, port.to_be()); + } + _ => panic!("nope"), + } + + assert_eq!(addr.to_str(), "127.0.0.1:3000"); + + let inet = addr.to_std(); + assert_eq!(actual, inet); +} + +#[test] +pub fn test_inetv6_addr_to_sock_addr() { + let port: u16 = 3000; + let flowinfo: u32 = 1; + let scope_id: u32 = 2; + let ip: Ipv6Addr = "fe80::1".parse().unwrap(); + + let actual = SocketAddr::V6(SocketAddrV6::new(ip, port, flowinfo, scope_id)); + let addr = InetAddr::from_std(&actual); + + match addr { + InetAddr::V6(addr) => { + assert_eq!(addr.sin6_port, port.to_be()); + assert_eq!(addr.sin6_flowinfo, flowinfo); + assert_eq!(addr.sin6_scope_id, scope_id); + } + _ => panic!("nope"), + } + + assert_eq!(actual, addr.to_std()); +} + +#[test] +pub fn test_path_to_sock_addr() { + let path = "/foo/bar"; + let actual = Path::new(path); + let addr = UnixAddr::new(actual).unwrap(); + + let expect: &[c_char] = unsafe { + slice::from_raw_parts(path.as_bytes().as_ptr() as *const c_char, path.len()) + }; + assert_eq!(&addr.0.sun_path[..8], expect); + + assert_eq!(addr.path(), Some(actual)); +} + +// Test getting/setting abstract addresses (without unix socket creation) +#[cfg(target_os = "linux")] +#[test] +pub fn test_abstract_uds_addr() { + let empty = String::new(); + let addr = UnixAddr::new_abstract(empty.as_bytes()).unwrap(); + assert_eq!(addr.as_abstract(), Some(empty.as_bytes())); + + let name = String::from("nix\0abstract\0test"); + let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap(); + assert_eq!(addr.as_abstract(), Some(name.as_bytes())); + assert_eq!(addr.path(), None); + + // Internally, name is null-prefixed (abstract namespace) + let internal: &[u8] = unsafe { + slice::from_raw_parts(addr.0.sun_path.as_ptr() as *const u8, addr.1) + }; + let mut abstract_name = name.clone(); + abstract_name.insert(0, '\0'); + assert_eq!(internal, abstract_name.as_bytes()); +} + +#[test] +pub fn test_getsockname() { + use nix::sys::socket::{socket, AddressFamily, SockType, SockFlag}; + use nix::sys::socket::{bind, SockAddr}; + + let tempdir = tempfile::tempdir().unwrap(); + let sockname = tempdir.path().join("sock"); + let sock = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None) + .expect("socket failed"); + let sockaddr = SockAddr::new_unix(&sockname).unwrap(); + bind(sock, &sockaddr).expect("bind failed"); + assert_eq!(sockaddr.to_str(), + getsockname(sock).expect("getsockname failed").to_str()); +} + +#[test] +pub fn test_socketpair() { + use nix::unistd::{read, write}; + use nix::sys::socket::{socketpair, AddressFamily, SockType, SockFlag}; + + let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) + .unwrap(); + write(fd1, b"hello").unwrap(); + let mut buf = [0;5]; + read(fd2, &mut buf).unwrap(); + + assert_eq!(&buf[..], b"hello"); +} + +#[test] +pub fn test_scm_rights() { + use nix::sys::uio::IoVec; + use nix::unistd::{pipe, read, write, close}; + use nix::sys::socket::{socketpair, sendmsg, recvmsg, + AddressFamily, SockType, SockFlag, + ControlMessage, CmsgSpace, MsgFlags}; + + let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) + .unwrap(); + let (r, w) = pipe().unwrap(); + let mut received_r: Option<RawFd> = None; + + { + let iov = [IoVec::from_slice(b"hello")]; + let fds = [r]; + let cmsg = ControlMessage::ScmRights(&fds); + assert_eq!(sendmsg(fd1, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(), 5); + close(r).unwrap(); + close(fd1).unwrap(); + } + + { + let mut buf = [0u8; 5]; + let iov = [IoVec::from_mut_slice(&mut buf[..])]; + let mut cmsgspace: CmsgSpace<[RawFd; 1]> = CmsgSpace::new(); + let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap(); + + for cmsg in msg.cmsgs() { + if let ControlMessage::ScmRights(fd) = cmsg { + assert_eq!(received_r, None); + assert_eq!(fd.len(), 1); + received_r = Some(fd[0]); + } else { + panic!("unexpected cmsg"); + } + } + assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); + close(fd2).unwrap(); + } + + let received_r = received_r.expect("Did not receive passed fd"); + // Ensure that the received file descriptor works + write(w, b"world").unwrap(); + let mut buf = [0u8; 5]; + read(received_r, &mut buf).unwrap(); + assert_eq!(&buf[..], b"world"); + close(received_r).unwrap(); + close(w).unwrap(); +} + +/// Tests that passing multiple fds using a single `ControlMessage` works. +#[test] +fn test_scm_rights_single_cmsg_multiple_fds() { + use std::os::unix::net::UnixDatagram; + use std::os::unix::io::{RawFd, AsRawFd}; + use std::thread; + use nix::sys::socket::{CmsgSpace, ControlMessage, MsgFlags, sendmsg, recvmsg}; + use nix::sys::uio::IoVec; + use libc; + + let (send, receive) = UnixDatagram::pair().unwrap(); + let thread = thread::spawn(move || { + let mut buf = [0u8; 8]; + let iovec = [IoVec::from_mut_slice(&mut buf)]; + let mut space = CmsgSpace::<[RawFd; 2]>::new(); + let msg = recvmsg( + receive.as_raw_fd(), + &iovec, + Some(&mut space), + MsgFlags::empty() + ).unwrap(); + assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); + + let mut cmsgs = msg.cmsgs(); + match cmsgs.next() { + Some(ControlMessage::ScmRights(fds)) => { + assert_eq!(fds.len(), 2, + "unexpected fd count (expected 2 fds, got {})", + fds.len()); + }, + _ => panic!(), + } + assert!(cmsgs.next().is_none(), "unexpected control msg"); + + assert_eq!(iovec[0].as_slice(), [1u8, 2, 3, 4, 5, 6, 7, 8]); + }); + + let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let iov = [IoVec::from_slice(&slice)]; + let fds = [libc::STDIN_FILENO, libc::STDOUT_FILENO]; // pass stdin and stdout + let cmsg = [ControlMessage::ScmRights(&fds)]; + sendmsg(send.as_raw_fd(), &iov, &cmsg, MsgFlags::empty(), None).unwrap(); + thread.join().unwrap(); +} + +// Verify `sendmsg` builds a valid `msghdr` when passing an empty +// `cmsgs` argument. This should result in a msghdr with a nullptr +// msg_control field and a msg_controllen of 0 when calling into the +// raw `sendmsg`. +#[test] +pub fn test_sendmsg_empty_cmsgs() { + use nix::sys::uio::IoVec; + use nix::unistd::close; + use nix::sys::socket::{socketpair, sendmsg, recvmsg, + AddressFamily, SockType, SockFlag, + CmsgSpace, MsgFlags}; + + let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) + .unwrap(); + + { + let iov = [IoVec::from_slice(b"hello")]; + assert_eq!(sendmsg(fd1, &iov, &[], MsgFlags::empty(), None).unwrap(), 5); + close(fd1).unwrap(); + } + + { + let mut buf = [0u8; 5]; + let iov = [IoVec::from_mut_slice(&mut buf[..])]; + let mut cmsgspace: CmsgSpace<[RawFd; 1]> = CmsgSpace::new(); + let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap(); + + for _ in msg.cmsgs() { + panic!("unexpected cmsg"); + } + assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); + close(fd2).unwrap(); + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_scm_credentials() { + use libc; + use nix::sys::uio::IoVec; + use nix::unistd::{close, getpid, getuid, getgid}; + use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt, + AddressFamily, SockType, SockFlag, + ControlMessage, CmsgSpace, MsgFlags}; + use nix::sys::socket::sockopt::PassCred; + + let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) + .unwrap(); + setsockopt(recv, PassCred, &true).unwrap(); + + { + let iov = [IoVec::from_slice(b"hello")]; + let cred = libc::ucred { + pid: getpid().as_raw(), + uid: getuid().as_raw(), + gid: getgid().as_raw(), + }; + let cmsg = ControlMessage::ScmCredentials(&cred); + assert_eq!(sendmsg(send, &iov, &[cmsg], MsgFlags::empty(), None).unwrap(), 5); + close(send).unwrap(); + } + + { + let mut buf = [0u8; 5]; + let iov = [IoVec::from_mut_slice(&mut buf[..])]; + let mut cmsgspace: CmsgSpace<libc::ucred> = CmsgSpace::new(); + let msg = recvmsg(recv, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap(); + let mut received_cred = None; + + for cmsg in msg.cmsgs() { + if let ControlMessage::ScmCredentials(cred) = cmsg { + assert!(received_cred.is_none()); + assert_eq!(cred.pid, getpid().as_raw()); + assert_eq!(cred.uid, getuid().as_raw()); + assert_eq!(cred.gid, getgid().as_raw()); + received_cred = Some(*cred); + } else { + panic!("unexpected cmsg"); + } + } + received_cred.expect("no creds received"); + assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); + close(recv).unwrap(); + } +} + +/// Ensure that we can send `SCM_CREDENTIALS` and `SCM_RIGHTS` with a single +/// `sendmsg` call. +#[cfg(any(target_os = "android", target_os = "linux"))] +// qemu's handling of multiple cmsgs is bugged, ignore tests on non-x86 +// see https://bugs.launchpad.net/qemu/+bug/1781280 +#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)] +#[test] +fn test_scm_credentials_and_rights() { + use nix::sys::socket::CmsgSpace; + use libc; + + test_impl_scm_credentials_and_rights(CmsgSpace::<(libc::ucred, CmsgSpace<RawFd>)>::new()); +} + +/// Ensure that passing a `CmsgSpace` with too much space for the received +/// messages still works. +#[cfg(any(target_os = "android", target_os = "linux"))] +// qemu's handling of multiple cmsgs is bugged, ignore tests on non-x86 +// see https://bugs.launchpad.net/qemu/+bug/1781280 +#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "x86")), ignore)] +#[test] +fn test_too_large_cmsgspace() { + use nix::sys::socket::CmsgSpace; + + test_impl_scm_credentials_and_rights(CmsgSpace::<[u8; 1024]>::new()); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_impl_scm_credentials_and_rights<T>(mut space: ::nix::sys::socket::CmsgSpace<T>) { + use libc; + use nix::sys::uio::IoVec; + use nix::unistd::{pipe, read, write, close, getpid, getuid, getgid}; + use nix::sys::socket::{socketpair, sendmsg, recvmsg, setsockopt, + AddressFamily, SockType, SockFlag, + ControlMessage, MsgFlags}; + use nix::sys::socket::sockopt::PassCred; + + let (send, recv) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()) + .unwrap(); + setsockopt(recv, PassCred, &true).unwrap(); + + let (r, w) = pipe().unwrap(); + let mut received_r: Option<RawFd> = None; + + { + let iov = [IoVec::from_slice(b"hello")]; + let cred = libc::ucred { + pid: getpid().as_raw(), + uid: getuid().as_raw(), + gid: getgid().as_raw(), + }; + let fds = [r]; + let cmsgs = [ + ControlMessage::ScmCredentials(&cred), + ControlMessage::ScmRights(&fds), + ]; + assert_eq!(sendmsg(send, &iov, &cmsgs, MsgFlags::empty(), None).unwrap(), 5); + close(r).unwrap(); + close(send).unwrap(); + } + + { + let mut buf = [0u8; 5]; + let iov = [IoVec::from_mut_slice(&mut buf[..])]; + let msg = recvmsg(recv, &iov, Some(&mut space), MsgFlags::empty()).unwrap(); + let mut received_cred = None; + + assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs"); + + for cmsg in msg.cmsgs() { + match cmsg { + ControlMessage::ScmRights(fds) => { + assert_eq!(received_r, None, "already received fd"); + assert_eq!(fds.len(), 1); + received_r = Some(fds[0]); + } + ControlMessage::ScmCredentials(cred) => { + assert!(received_cred.is_none()); + assert_eq!(cred.pid, getpid().as_raw()); + assert_eq!(cred.uid, getuid().as_raw()); + assert_eq!(cred.gid, getgid().as_raw()); + received_cred = Some(*cred); + } + _ => panic!("unexpected cmsg"), + } + } + received_cred.expect("no creds received"); + assert!(!msg.flags.intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); + close(recv).unwrap(); + } + + let received_r = received_r.expect("Did not receive passed fd"); + // Ensure that the received file descriptor works + write(w, b"world").unwrap(); + let mut buf = [0u8; 5]; + read(received_r, &mut buf).unwrap(); + assert_eq!(&buf[..], b"world"); + close(received_r).unwrap(); + close(w).unwrap(); +} + +// Test creating and using named unix domain sockets +#[test] +pub fn test_unixdomain() { + use nix::sys::socket::{AddressFamily, SockType, SockFlag}; + use nix::sys::socket::{bind, socket, connect, listen, accept, SockAddr}; + use nix::unistd::{read, write, close}; + use std::thread; + + let tempdir = tempfile::tempdir().unwrap(); + let sockname = tempdir.path().join("sock"); + let s1 = socket(AddressFamily::Unix, SockType::Stream, + SockFlag::empty(), None).expect("socket failed"); + let sockaddr = SockAddr::new_unix(&sockname).unwrap(); + bind(s1, &sockaddr).expect("bind failed"); + listen(s1, 10).expect("listen failed"); + + let thr = thread::spawn(move || { + let s2 = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None) + .expect("socket failed"); + connect(s2, &sockaddr).expect("connect failed"); + write(s2, b"hello").expect("write failed"); + close(s2).unwrap(); + }); + + let s3 = accept(s1).expect("accept failed"); + + let mut buf = [0;5]; + read(s3, &mut buf).unwrap(); + close(s3).unwrap(); + close(s1).unwrap(); + thr.join().unwrap(); + + assert_eq!(&buf[..], b"hello"); +} + +// Test creating and using named system control sockets +#[cfg(any(target_os = "macos", target_os = "ios"))] +#[test] +pub fn test_syscontrol() { + use nix::Error; + use nix::errno::Errno; + use nix::sys::socket::{AddressFamily, socket, SockAddr, SockType, SockFlag, SockProtocol}; + + let fd = socket(AddressFamily::System, SockType::Datagram, + SockFlag::empty(), SockProtocol::KextControl) + .expect("socket failed"); + let _sockaddr = SockAddr::new_sys_control(fd, "com.apple.net.utun_control", 0).expect("resolving sys_control name failed"); + assert_eq!(SockAddr::new_sys_control(fd, "foo.bar.lol", 0).err(), Some(Error::Sys(Errno::ENOENT))); + + // requires root privileges + // connect(fd, &sockaddr).expect("connect failed"); +} + +#[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos" +))] +// qemu doesn't seem to be emulating this correctly in these architectures +#[cfg_attr(any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "powerpc64", +), ignore)] +#[test] +pub fn test_recv_ipv4pktinfo() { + use libc; + use nix::ifaddrs::{getifaddrs, InterfaceAddress}; + use nix::net::if_::*; + use nix::sys::socket::sockopt::Ipv4PacketInfo; + use nix::sys::socket::{bind, AddressFamily, SockFlag, SockType}; + use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr}; + use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags}; + use nix::sys::uio::IoVec; + use std::io; + use std::io::Write; + use std::thread; + + fn loopback_v4addr() -> Option<InterfaceAddress> { + let addrs = match getifaddrs() { + Ok(iter) => iter, + Err(e) => { + let stdioerr = io::stderr(); + let mut handle = stdioerr.lock(); + writeln!(handle, "getifaddrs: {:?}", e).unwrap(); + return None; + }, + }; + for ifaddr in addrs { + if ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) { + match ifaddr.address { + Some(SockAddr::Inet(InetAddr::V4(..))) => { + return Some(ifaddr); + } + _ => continue, + } + } + } + None + } + + let lo_ifaddr = loopback_v4addr(); + let (lo_name, lo) = match lo_ifaddr { + Some(ifaddr) => (ifaddr.interface_name, + ifaddr.address.expect("Expect IPv4 address on interface")), + None => return, + }; + let receive = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("receive socket failed"); + bind(receive, &lo).expect("bind failed"); + let sa = getsockname(receive).expect("getsockname failed"); + setsockopt(receive, Ipv4PacketInfo, &true).expect("setsockopt failed"); + + let thread = thread::spawn(move || { + let mut buf = [0u8; 8]; + let iovec = [IoVec::from_mut_slice(&mut buf)]; + let mut space = CmsgSpace::<libc::in_pktinfo>::new(); + let msg = recvmsg( + receive, + &iovec, + Some(&mut space), + MsgFlags::empty(), + ).expect("recvmsg failed"); + assert!( + !msg.flags + .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC) + ); + + let mut cmsgs = msg.cmsgs(); + match cmsgs.next() { + Some(ControlMessage::Ipv4PacketInfo(pktinfo)) => { + let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); + assert_eq!( + pktinfo.ipi_ifindex as libc::c_uint, + i, + "unexpected ifindex (expected {}, got {})", + i, + pktinfo.ipi_ifindex + ); + } + _ => (), + } + assert!(cmsgs.next().is_none(), "unexpected additional control msg"); + assert_eq!( + iovec[0].as_slice(), + [1u8, 2, 3, 4, 5, 6, 7, 8] + ); + }); + + let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let iov = [IoVec::from_slice(&slice)]; + + let send = socket( + AddressFamily::Inet, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("send socket failed"); + sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed"); + + thread.join().unwrap(); +} + +#[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos" +))] +// qemu doesn't seem to be emulating this correctly in these architectures +#[cfg_attr(any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "powerpc64", +), ignore)] +#[test] +pub fn test_recv_ipv6pktinfo() { + use libc; + use nix::ifaddrs::{getifaddrs, InterfaceAddress}; + use nix::net::if_::*; + use nix::sys::socket::sockopt::Ipv6RecvPacketInfo; + use nix::sys::socket::{bind, AddressFamily, SockFlag, SockType}; + use nix::sys::socket::{getsockname, setsockopt, socket, SockAddr}; + use nix::sys::socket::{recvmsg, sendmsg, CmsgSpace, ControlMessage, MsgFlags}; + use nix::sys::uio::IoVec; + use std::io; + use std::io::Write; + use std::thread; + + fn loopback_v6addr() -> Option<InterfaceAddress> { + let addrs = match getifaddrs() { + Ok(iter) => iter, + Err(e) => { + let stdioerr = io::stderr(); + let mut handle = stdioerr.lock(); + writeln!(handle, "getifaddrs: {:?}", e).unwrap(); + return None; + }, + }; + for ifaddr in addrs { + if ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) { + match ifaddr.address { + Some(SockAddr::Inet(InetAddr::V6(..))) => { + return Some(ifaddr); + } + _ => continue, + } + } + } + None + } + + let lo_ifaddr = loopback_v6addr(); + let (lo_name, lo) = match lo_ifaddr { + Some(ifaddr) => (ifaddr.interface_name, + ifaddr.address.expect("Expect IPv4 address on interface")), + None => return, + }; + let receive = socket( + AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("receive socket failed"); + bind(receive, &lo).expect("bind failed"); + let sa = getsockname(receive).expect("getsockname failed"); + setsockopt(receive, Ipv6RecvPacketInfo, &true).expect("setsockopt failed"); + + let thread = thread::spawn(move || { + let mut buf = [0u8; 8]; + let iovec = [IoVec::from_mut_slice(&mut buf)]; + let mut space = CmsgSpace::<libc::in6_pktinfo>::new(); + let msg = recvmsg( + receive, + &iovec, + Some(&mut space), + MsgFlags::empty(), + ).expect("recvmsg failed"); + assert!( + !msg.flags + .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC) + ); + + let mut cmsgs = msg.cmsgs(); + match cmsgs.next() { + Some(ControlMessage::Ipv6PacketInfo(pktinfo)) => { + let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); + assert_eq!( + pktinfo.ipi6_ifindex, + i, + "unexpected ifindex (expected {}, got {})", + i, + pktinfo.ipi6_ifindex + ); + } + _ => (), + } + assert!(cmsgs.next().is_none(), "unexpected additional control msg"); + assert_eq!( + iovec[0].as_slice(), + [1u8, 2, 3, 4, 5, 6, 7, 8] + ); + }); + + let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let iov = [IoVec::from_slice(&slice)]; + + let send = socket( + AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + None, + ).expect("send socket failed"); + sendmsg(send, &iov, &[], MsgFlags::empty(), Some(&sa)).expect("sendmsg failed"); + + thread.join().unwrap(); +} diff --git a/third_party/rust/nix/test/sys/test_sockopt.rs b/third_party/rust/nix/test/sys/test_sockopt.rs new file mode 100644 index 0000000000..efe2c56b55 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_sockopt.rs @@ -0,0 +1,40 @@ +use rand::{thread_rng, Rng}; +use nix::sys::socket::{socket, sockopt, getsockopt, setsockopt, AddressFamily, SockType, SockFlag, SockProtocol}; + +#[test] +fn test_so_buf() { + let fd = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(), SockProtocol::Udp) + .unwrap(); + let bufsize: usize = thread_rng().gen_range(4096, 131_072); + setsockopt(fd, sockopt::SndBuf, &bufsize).unwrap(); + let actual = getsockopt(fd, sockopt::SndBuf).unwrap(); + assert!(actual >= bufsize); + setsockopt(fd, sockopt::RcvBuf, &bufsize).unwrap(); + let actual = getsockopt(fd, sockopt::RcvBuf).unwrap(); + assert!(actual >= bufsize); +} + +// The CI doesn't supported getsockopt and setsockopt on emulated processors. +// It's beleived that a QEMU issue, the tests run ok on a fully emulated system. +// Current CI just run the binary with QEMU but the Kernel remains the same as the host. +// So the syscall doesn't work properly unless the kernel is also emulated. +#[test] +#[cfg(all( + any(target_arch = "x86", target_arch = "x86_64"), + any(target_os = "freebsd", target_os = "linux") +))] +fn test_tcp_congestion() { + use std::ffi::OsString; + + let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap(); + + let val = getsockopt(fd, sockopt::TcpCongestion).unwrap(); + setsockopt(fd, sockopt::TcpCongestion, &val).unwrap(); + + setsockopt(fd, sockopt::TcpCongestion, &OsString::from("tcp_congestion_does_not_exist")).unwrap_err(); + + assert_eq!( + getsockopt(fd, sockopt::TcpCongestion).unwrap(), + val + ); +} diff --git a/third_party/rust/nix/test/sys/test_sysinfo.rs b/third_party/rust/nix/test/sys/test_sysinfo.rs new file mode 100644 index 0000000000..73e6586f62 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_sysinfo.rs @@ -0,0 +1,18 @@ +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()); +} diff --git a/third_party/rust/nix/test/sys/test_termios.rs b/third_party/rust/nix/test/sys/test_termios.rs new file mode 100644 index 0000000000..a14b8ce1a2 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_termios.rs @@ -0,0 +1,136 @@ +use std::os::unix::prelude::*; +use tempfile::tempfile; + +use nix::{Error, fcntl}; +use nix::errno::Errno; +use nix::pty::openpty; +use nix::sys::termios::{self, LocalFlags, OutputFlags, Termios, tcgetattr}; +use nix::unistd::{read, write, close}; + +/// Helper function analogous to `std::io::Write::write_all`, but for `RawFd`s +fn write_all(f: RawFd, buf: &[u8]) { + let mut len = 0; + while len < buf.len() { + len += write(f, &buf[len..]).unwrap(); + } +} + +// Test tcgetattr on a terminal +#[test] +fn test_tcgetattr_pty() { + // openpty uses ptname(3) internally + let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + let pty = openpty(None, None).expect("openpty failed"); + assert!(termios::tcgetattr(pty.master).is_ok()); + close(pty.master).expect("closing the master failed"); + close(pty.slave).expect("closing the slave failed"); +} + +// Test tcgetattr on something that isn't a terminal +#[test] +fn test_tcgetattr_enotty() { + let file = tempfile().unwrap(); + assert_eq!(termios::tcgetattr(file.as_raw_fd()).err(), + Some(Error::Sys(Errno::ENOTTY))); +} + +// Test tcgetattr on an invalid file descriptor +#[test] +fn test_tcgetattr_ebadf() { + assert_eq!(termios::tcgetattr(-1).err(), + Some(Error::Sys(Errno::EBADF))); +} + +// Test modifying output flags +#[test] +fn test_output_flags() { + // openpty uses ptname(3) internally + let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open one pty to get attributes for the second one + let mut termios = { + let pty = openpty(None, None).expect("openpty failed"); + assert!(pty.master > 0); + assert!(pty.slave > 0); + let termios = tcgetattr(pty.master).expect("tcgetattr failed"); + close(pty.master).unwrap(); + close(pty.slave).unwrap(); + termios + }; + + // 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(); + assert!(pty.master > 0); + assert!(pty.slave > 0); + + // 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]; + ::read_exact(pty.slave, &mut buf); + let transformed_string = "foofoofoo\n"; + close(pty.master).unwrap(); + close(pty.slave).unwrap(); + assert_eq!(&buf, transformed_string.as_bytes()); +} + +// Test modifying local flags +#[test] +fn test_local_flags() { + // openpty uses ptname(3) internally + let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open one pty to get attributes for the second one + let mut termios = { + let pty = openpty(None, None).unwrap(); + assert!(pty.master > 0); + assert!(pty.slave > 0); + let termios = tcgetattr(pty.master).unwrap(); + close(pty.master).unwrap(); + close(pty.slave).unwrap(); + termios + }; + + // 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(); + assert!(pty.master > 0); + assert!(pty.slave > 0); + + // 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, 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(); + close(pty.master).unwrap(); + close(pty.slave).unwrap(); + assert_eq!(read, Error::Sys(Errno::EAGAIN)); +} + +#[test] +fn test_cfmakeraw() { + let mut termios = unsafe { Termios::default_uninit() }; + termios::cfmakeraw(&mut termios); +} diff --git a/third_party/rust/nix/test/sys/test_uio.rs b/third_party/rust/nix/test/sys/test_uio.rs new file mode 100644 index 0000000000..f0cc8897e5 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_uio.rs @@ -0,0 +1,241 @@ +use nix::sys::uio::*; +use nix::unistd::*; +use rand::{thread_rng, Rng}; +use rand::distributions::Alphanumeric; +use std::{cmp, iter}; +use std::fs::{OpenOptions}; +use std::os::unix::io::AsRawFd; + +use tempfile::{tempfile, tempdir}; + +#[test] +fn test_writev() { + let mut to_write = Vec::with_capacity(16 * 128); + for _ in 0..16 { + let s: String = thread_rng().sample_iter(&Alphanumeric).take(128).collect(); + let b = s.as_bytes(); + to_write.extend(b.iter().cloned()); + } + // Allocate and fill iovecs + let mut iovecs = Vec::new(); + let mut consumed = 0; + while consumed < to_write.len() { + let left = to_write.len() - consumed; + let slice_len = if left <= 64 { left } else { thread_rng().gen_range(64, cmp::min(256, left)) }; + let b = &to_write[consumed..consumed+slice_len]; + iovecs.push(IoVec::from_slice(b)); + consumed += slice_len; + } + let pipe_res = pipe(); + assert!(pipe_res.is_ok()); + let (reader, writer) = pipe_res.ok().unwrap(); + // FileDesc will close its filedesc (reader). + let mut read_buf: Vec<u8> = iter::repeat(0u8).take(128 * 16).collect(); + // Blocking io, should write all data. + let write_res = writev(writer, &iovecs); + // Successful write + assert!(write_res.is_ok()); + let written = write_res.ok().unwrap(); + // Check whether we written all data + assert_eq!(to_write.len(), written); + let read_res = read(reader, &mut read_buf[..]); + // Successful read + assert!(read_res.is_ok()); + let read = read_res.ok().unwrap() as usize; + // Check we have read as much as we written + assert_eq!(read, written); + // Check equality of written and read data + assert_eq!(&to_write, &read_buf); + let close_res = close(writer); + assert!(close_res.is_ok()); + let close_res = close(reader); + assert!(close_res.is_ok()); +} + +#[test] +fn test_readv() { + let s:String = thread_rng().sample_iter(&Alphanumeric).take(128).collect(); + let to_write = s.as_bytes().to_vec(); + let mut storage = Vec::new(); + let mut allocated = 0; + while allocated < to_write.len() { + let left = to_write.len() - allocated; + let vec_len = if left <= 64 { left } else { thread_rng().gen_range(64, cmp::min(256, left)) }; + let v: Vec<u8> = iter::repeat(0u8).take(vec_len).collect(); + storage.push(v); + allocated += vec_len; + } + let mut iovecs = Vec::with_capacity(storage.len()); + for v in &mut storage { + iovecs.push(IoVec::from_mut_slice(&mut v[..])); + } + let pipe_res = pipe(); + assert!(pipe_res.is_ok()); + let (reader, writer) = pipe_res.ok().unwrap(); + // Blocking io, should write all data. + let write_res = write(writer, &to_write); + // Successful write + assert!(write_res.is_ok()); + let read_res = readv(reader, &mut iovecs[..]); + assert!(read_res.is_ok()); + let read = read_res.ok().unwrap(); + // Check whether we've read all data + assert_eq!(to_write.len(), read); + // Cccumulate data from iovecs + let mut read_buf = Vec::with_capacity(to_write.len()); + for iovec in &iovecs { + read_buf.extend(iovec.as_slice().iter().cloned()); + } + // Check whether iovecs contain all written data + assert_eq!(read_buf.len(), to_write.len()); + // Check equality of written and read data + assert_eq!(&read_buf, &to_write); + let close_res = close(reader); + assert!(close_res.is_ok()); + let close_res = close(writer); + assert!(close_res.is_ok()); +} + +#[test] +fn test_pwrite() { + use std::io::Read; + + let mut file = tempfile().unwrap(); + let buf = [1u8;8]; + assert_eq!(Ok(8), pwrite(file.as_raw_fd(), &buf, 8)); + let mut file_content = Vec::new(); + file.read_to_end(&mut file_content).unwrap(); + let mut expected = vec![0u8;8]; + expected.extend(vec![1;8]); + assert_eq!(file_content, expected); +} + +#[test] +fn test_pread() { + use std::io::Write; + + let tempdir = tempdir().unwrap(); + + let path = tempdir.path().join("pread_test_file"); + let mut file = OpenOptions::new().write(true).read(true).create(true) + .truncate(true).open(path).unwrap(); + let file_content: Vec<u8> = (0..64).collect(); + file.write_all(&file_content).unwrap(); + + let mut buf = [0u8;16]; + assert_eq!(Ok(16), pread(file.as_raw_fd(), &mut buf, 16)); + let expected: Vec<_> = (16..32).collect(); + assert_eq!(&buf[..], &expected[..]); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_pwritev() { + use std::io::Read; + + let to_write: Vec<u8> = (0..128).collect(); + let expected: Vec<u8> = [vec![0;100], to_write.clone()].concat(); + + let iovecs = [ + IoVec::from_slice(&to_write[0..17]), + IoVec::from_slice(&to_write[17..64]), + IoVec::from_slice(&to_write[64..128]), + ]; + + let tempdir = tempdir().unwrap(); + + // pwritev them into a temporary file + let path = tempdir.path().join("pwritev_test_file"); + let mut file = OpenOptions::new().write(true).read(true).create(true) + .truncate(true).open(path).unwrap(); + + let written = pwritev(file.as_raw_fd(), &iovecs, 100).ok().unwrap(); + assert_eq!(written, to_write.len()); + + // Read the data back and make sure it matches + let mut contents = Vec::new(); + file.read_to_end(&mut contents).unwrap(); + assert_eq!(contents, expected); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_preadv() { + use std::io::Write; + + let to_write: Vec<u8> = (0..200).collect(); + let expected: Vec<u8> = (100..200).collect(); + + let tempdir = tempdir().unwrap(); + + let path = tempdir.path().join("preadv_test_file"); + + let mut file = OpenOptions::new().read(true).write(true).create(true) + .truncate(true).open(path).unwrap(); + file.write_all(&to_write).unwrap(); + + let mut buffers: Vec<Vec<u8>> = vec![ + vec![0; 24], + vec![0; 1], + vec![0; 75], + ]; + + { + // Borrow the buffers into IoVecs and preadv into them + let iovecs: Vec<_> = buffers.iter_mut().map( + |buf| IoVec::from_mut_slice(&mut buf[..])).collect(); + assert_eq!(Ok(100), preadv(file.as_raw_fd(), &iovecs, 100)); + } + + let all = buffers.concat(); + assert_eq!(all, expected); +} + +// #[test] +// #[cfg(target_os = "linux")] +// // FIXME: qemu-user doesn't implement process_vm_readv/writev on most arches +// #[cfg_attr(not(any(target_arch = "x86", target_arch = "x86_64")), ignore)] +// fn test_process_vm_readv() { +// use nix::unistd::ForkResult::*; +// use nix::sys::signal::*; +// use nix::sys::wait::*; + +// let _ = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + +// // Pre-allocate memory in the child, since allocation isn't safe +// // post-fork (~= async-signal-safe) +// let mut vector = vec![1u8, 2, 3, 4, 5]; + +// let (r, w) = pipe().unwrap(); +// match fork().expect("Error: Fork Failed") { +// Parent { child } => { +// close(w).unwrap(); +// // wait for child +// read(r, &mut [0u8]).unwrap(); +// close(r).unwrap(); + +// let ptr = vector.as_ptr() as usize; +// let remote_iov = RemoteIoVec { base: ptr, len: 5 }; +// let mut buf = vec![0u8; 5]; + +// let ret = process_vm_readv(child, +// &[IoVec::from_mut_slice(&mut buf)], +// &[remote_iov]); + +// kill(child, SIGTERM).unwrap(); +// waitpid(child, None).unwrap(); + +// assert_eq!(Ok(5), ret); +// assert_eq!(20u8, buf.iter().sum()); +// }, +// Child => { +// let _ = close(r); +// for i in &mut vector { +// *i += 1; +// } +// let _ = write(w, b"\0"); +// let _ = close(w); +// loop { let _ = pause(); } +// }, +// } +// } diff --git a/third_party/rust/nix/test/sys/test_wait.rs b/third_party/rust/nix/test/sys/test_wait.rs new file mode 100644 index 0000000000..d07d82f0d9 --- /dev/null +++ b/third_party/rust/nix/test/sys/test_wait.rs @@ -0,0 +1,104 @@ +use nix::Error; +use nix::unistd::*; +use nix::unistd::ForkResult::*; +use nix::sys::signal::*; +use nix::sys::wait::*; +use libc::_exit; + +#[test] +fn test_wait_signal() { + let _ = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe. + match fork().expect("Error: Fork Failed") { + Child => { + pause(); + unsafe { _exit(123) } + }, + Parent { child } => { + kill(child, Some(SIGKILL)).expect("Error: Kill Failed"); + assert_eq!(waitpid(child, None), Ok(WaitStatus::Signaled(child, SIGKILL, false))); + }, + } +} + +#[test] +fn test_wait_exit() { + let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // Safe: Child only calls `_exit`, which is async-signal-safe. + match fork().expect("Error: Fork Failed") { + Child => unsafe { _exit(12); }, + Parent { child } => { + assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 12))); + }, + } +} + +#[test] +fn test_waitstatus_from_raw() { + let pid = Pid::from_raw(1); + assert_eq!(WaitStatus::from_raw(pid, 0x0002), Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false))); + assert_eq!(WaitStatus::from_raw(pid, 0x0200), Ok(WaitStatus::Exited(pid, 2))); + assert_eq!(WaitStatus::from_raw(pid, 0x7f7f), Err(Error::invalid_argument())); +} + +#[test] +fn test_waitstatus_pid() { + let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + match fork().unwrap() { + Child => unsafe { _exit(0) }, + Parent { child } => { + let status = waitpid(child, None).unwrap(); + assert_eq!(status.pid(), Some(child)); + } + } +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +// FIXME: qemu-user doesn't implement ptrace on most arches +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +mod ptrace { + use nix::sys::ptrace::{self, Options, Event}; + use nix::sys::signal::*; + use nix::sys::wait::*; + use nix::unistd::*; + use nix::unistd::ForkResult::*; + use libc::_exit; + + fn ptrace_child() -> ! { + ptrace::traceme().unwrap(); + // As recommended by ptrace(2), raise SIGTRAP to pause the child + // until the parent is ready to continue + raise(SIGTRAP).unwrap(); + unsafe { _exit(0) } + } + + fn ptrace_parent(child: Pid) { + // Wait for the raised SIGTRAP + assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, SIGTRAP))); + // We want to test a syscall stop and a PTRACE_EVENT stop + assert!(ptrace::setoptions(child, Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT).is_ok()); + + // First, stop on the next system call, which will be exit() + assert!(ptrace::syscall(child).is_ok()); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + // Then get the ptrace event for the process exiting + assert!(ptrace::cont(child, None).is_ok()); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, SIGTRAP, Event::PTRACE_EVENT_EXIT as i32))); + // Finally get the normal wait() result, now that the process has exited + assert!(ptrace::cont(child, None).is_ok()); + assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0))); + } + + #[test] + fn test_wait_ptrace() { + let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + match fork().expect("Error: Fork Failed") { + Child => ptrace_child(), + Parent { child } => ptrace_parent(child), + } + } +} diff --git a/third_party/rust/nix/test/test.rs b/third_party/rust/nix/test/test.rs new file mode 100644 index 0000000000..a91b6348e0 --- /dev/null +++ b/third_party/rust/nix/test/test.rs @@ -0,0 +1,80 @@ +extern crate bytes; +#[macro_use] +extern crate cfg_if; +#[macro_use] +extern crate nix; +#[macro_use] +extern crate lazy_static; +extern crate libc; +extern crate rand; +extern crate tempfile; + +macro_rules! skip_if_not_root { + ($name:expr) => { + use nix::unistd::Uid; + use std; + use std::io::Write; + + if !Uid::current().is_root() { + let stderr = std::io::stderr(); + let mut handle = stderr.lock(); + writeln!(handle, "{} requires root privileges. Skipping test.", $name).unwrap(); + return; + } + }; +} + +mod sys; +mod test_dir; +mod test_fcntl; +#[cfg(any(target_os = "android", + target_os = "linux"))] +mod test_kmod; +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "fushsia", + target_os = "linux", + target_os = "netbsd"))] +mod test_mq; +mod test_net; +mod test_nix_path; +mod test_poll; +mod test_pty; +#[cfg(any(target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos"))] +mod test_sendfile; +mod test_stat; +mod test_unistd; + +use std::os::unix::io::RawFd; +use std::sync::Mutex; +use nix::unistd::read; + +/// Helper function analogous to `std::io::Read::read_exact`, but for `RawFD`s +fn read_exact(f: RawFd, buf: &mut [u8]) { + let mut len = 0; + while len < buf.len() { + // get_mut would be better than split_at_mut, but it requires nightly + let (_, remaining) = buf.split_at_mut(len); + len += read(f, remaining).unwrap(); + } +} + +lazy_static! { + /// Any test that changes the process's current working directory must grab + /// this mutex + pub static ref CWD_MTX: Mutex<()> = Mutex::new(()); + /// Any test that changes the process's supplementary groups must grab this + /// mutex + pub static ref GROUPS_MTX: Mutex<()> = Mutex::new(()); + /// Any test that creates child processes must grab this mutex, regardless + /// of what it does with those children. + pub static ref FORK_MTX: Mutex<()> = Mutex::new(()); + /// Any test that calls ptsname(3) must grab this mutex. + pub static ref PTSNAME_MTX: Mutex<()> = Mutex::new(()); + /// Any test that alters signal handling must grab this mutex. + pub static ref SIGNAL_MTX: Mutex<()> = Mutex::new(()); +} diff --git a/third_party/rust/nix/test/test_dir.rs b/third_party/rust/nix/test/test_dir.rs new file mode 100644 index 0000000000..c42fbcd18a --- /dev/null +++ b/third_party/rust/nix/test/test_dir.rs @@ -0,0 +1,46 @@ +extern crate nix; +extern crate tempfile; + +use nix::dir::{Dir, Type}; +use nix::fcntl::OFlag; +use nix::sys::stat::Mode; +use std::fs::File; +use self::tempfile::tempdir; + +#[test] +fn read() { + let tmp = tempdir().unwrap(); + File::create(&tmp.path().join("foo")).unwrap(); + ::std::os::unix::fs::symlink("foo", tmp.path().join("bar")).unwrap(); + let mut dir = Dir::open(tmp.path(), OFlag::O_DIRECTORY | OFlag::O_RDONLY | OFlag::O_CLOEXEC, + Mode::empty()).unwrap(); + let mut entries: Vec<_> = dir.iter().map(|e| e.unwrap()).collect(); + entries.sort_by(|a, b| a.file_name().cmp(b.file_name())); + let entry_names: Vec<_> = entries + .iter() + .map(|e| e.file_name().to_str().unwrap().to_owned()) + .collect(); + assert_eq!(&entry_names[..], &[".", "..", "bar", "foo"]); + + // Check file types. The system is allowed to return DT_UNKNOWN (aka None here) but if it does + // return a type, ensure it's correct. + assert!(&[Some(Type::Directory), None].contains(&entries[0].file_type())); // .: dir + assert!(&[Some(Type::Directory), None].contains(&entries[1].file_type())); // ..: dir + assert!(&[Some(Type::Symlink), None].contains(&entries[2].file_type())); // bar: symlink + assert!(&[Some(Type::File), None].contains(&entries[3].file_type())); // foo: regular file +} + +#[test] +fn rewind() { + let tmp = tempdir().unwrap(); + let mut dir = Dir::open(tmp.path(), OFlag::O_DIRECTORY | OFlag::O_RDONLY | OFlag::O_CLOEXEC, + Mode::empty()).unwrap(); + let entries1: Vec<_> = dir.iter().map(|e| e.unwrap().file_name().to_owned()).collect(); + let entries2: Vec<_> = dir.iter().map(|e| e.unwrap().file_name().to_owned()).collect(); + assert_eq!(entries1, entries2); +} + +#[test] +fn ebadf() { + assert_eq!(Dir::from_fd(-1).unwrap_err(), nix::Error::Sys(nix::errno::Errno::EBADF)); +} diff --git a/third_party/rust/nix/test/test_fcntl.rs b/third_party/rust/nix/test/test_fcntl.rs new file mode 100644 index 0000000000..bcc523bfcc --- /dev/null +++ b/third_party/rust/nix/test/test_fcntl.rs @@ -0,0 +1,143 @@ +use nix::fcntl::{openat, open, OFlag, readlink, readlinkat}; +use nix::sys::stat::Mode; +use nix::unistd::{close, read}; +use tempfile::{self, NamedTempFile}; +use std::io::prelude::*; +use std::os::unix::fs; + +#[test] +fn test_openat() { + const CONTENTS: &[u8] = b"abcd"; + let mut tmp = NamedTempFile::new().unwrap(); + tmp.write_all(CONTENTS).unwrap(); + + let dirfd = open(tmp.path().parent().unwrap(), + OFlag::empty(), + Mode::empty()).unwrap(); + let fd = openat(dirfd, + tmp.path().file_name().unwrap(), + OFlag::O_RDONLY, + Mode::empty()).unwrap(); + + let mut buf = [0u8; 1024]; + assert_eq!(4, read(fd, &mut buf).unwrap()); + assert_eq!(CONTENTS, &buf[0..4]); + + close(fd).unwrap(); + close(dirfd).unwrap(); +} + +#[test] +fn test_readlink() { + let tempdir = tempfile::tempdir().unwrap(); + let src = tempdir.path().join("a"); + let dst = tempdir.path().join("b"); + println!("a: {:?}, b: {:?}", &src, &dst); + fs::symlink(&src.as_path(), &dst.as_path()).unwrap(); + let dirfd = open(tempdir.path(), + OFlag::empty(), + Mode::empty()).unwrap(); + + let mut buf = vec![0; src.to_str().unwrap().len() + 1]; + assert_eq!(readlink(&dst, &mut buf).unwrap().to_str().unwrap(), + src.to_str().unwrap()); + assert_eq!(readlinkat(dirfd, "b", &mut buf).unwrap().to_str().unwrap(), + src.to_str().unwrap()); +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +mod linux_android { + use std::io::prelude::*; + use std::os::unix::prelude::*; + + use libc::loff_t; + + use nix::fcntl::{SpliceFFlags, FallocateFlags, fallocate, splice, tee, vmsplice}; + use nix::sys::uio::IoVec; + use nix::unistd::{close, pipe, read, write}; + + use tempfile::{tempfile, NamedTempFile}; + + #[test] + fn test_splice() { + const CONTENTS: &[u8] = b"abcdef123456"; + let mut tmp = tempfile().unwrap(); + tmp.write_all(CONTENTS).unwrap(); + + let (rd, wr) = pipe().unwrap(); + let mut offset: loff_t = 5; + let res = splice(tmp.as_raw_fd(), Some(&mut offset), + wr, None, 2, SpliceFFlags::empty()).unwrap(); + + assert_eq!(2, res); + + let mut buf = [0u8; 1024]; + assert_eq!(2, read(rd, &mut buf).unwrap()); + assert_eq!(b"f1", &buf[0..2]); + assert_eq!(7, offset); + + close(rd).unwrap(); + close(wr).unwrap(); + } + + #[test] + fn test_tee() { + let (rd1, wr1) = pipe().unwrap(); + let (rd2, wr2) = pipe().unwrap(); + + write(wr1, b"abc").unwrap(); + let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap(); + + assert_eq!(2, res); + + let mut buf = [0u8; 1024]; + + // Check the tee'd bytes are at rd2. + assert_eq!(2, read(rd2, &mut buf).unwrap()); + assert_eq!(b"ab", &buf[0..2]); + + // Check all the bytes are still at rd1. + assert_eq!(3, read(rd1, &mut buf).unwrap()); + assert_eq!(b"abc", &buf[0..3]); + + close(rd1).unwrap(); + close(wr1).unwrap(); + close(rd2).unwrap(); + close(wr2).unwrap(); + } + + #[test] + fn test_vmsplice() { + let (rd, wr) = pipe().unwrap(); + + let buf1 = b"abcdef"; + let buf2 = b"defghi"; + let mut iovecs = Vec::with_capacity(2); + iovecs.push(IoVec::from_slice(&buf1[0..3])); + iovecs.push(IoVec::from_slice(&buf2[0..3])); + + let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap(); + + assert_eq!(6, res); + + // Check the bytes can be read at rd. + let mut buf = [0u8; 32]; + assert_eq!(6, read(rd, &mut buf).unwrap()); + assert_eq!(b"abcdef", &buf[0..6]); + + close(rd).unwrap(); + close(wr).unwrap(); + } + + #[test] + fn test_fallocate() { + let tmp = NamedTempFile::new().unwrap(); + + let fd = tmp.as_raw_fd(); + fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap(); + + // Check if we read exactly 100 bytes + let mut buf = [0u8; 200]; + assert_eq!(100, read(fd, &mut buf).unwrap()); + } +} diff --git a/third_party/rust/nix/test/test_kmod/hello_mod/Makefile b/third_party/rust/nix/test/test_kmod/hello_mod/Makefile new file mode 100644 index 0000000000..74c99b77e9 --- /dev/null +++ b/third_party/rust/nix/test/test_kmod/hello_mod/Makefile @@ -0,0 +1,7 @@ +obj-m += hello.o + +all: + make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean diff --git a/third_party/rust/nix/test/test_kmod/hello_mod/hello.c b/third_party/rust/nix/test/test_kmod/hello_mod/hello.c new file mode 100644 index 0000000000..1c34987d2a --- /dev/null +++ b/third_party/rust/nix/test/test_kmod/hello_mod/hello.c @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: GPL-2.0+ or MIT + */ +#include <linux/module.h> +#include <linux/kernel.h> + +static int number= 1; +static char *who = "World"; + +module_param(number, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(myint, "Just some number"); +module_param(who, charp, 0000); +MODULE_PARM_DESC(who, "Whot to greet"); + +int init_module(void) +{ + printk(KERN_INFO "Hello %s (%d)!\n", who, number); + return 0; +} + +void cleanup_module(void) +{ + printk(KERN_INFO "Goodbye %s (%d)!\n", who, number); +} + +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/third_party/rust/nix/test/test_kmod/mod.rs b/third_party/rust/nix/test/test_kmod/mod.rs new file mode 100644 index 0000000000..e0cb0df5bd --- /dev/null +++ b/third_party/rust/nix/test/test_kmod/mod.rs @@ -0,0 +1,152 @@ +use std::fs::copy; +use std::path::PathBuf; +use std::process::Command; +use tempfile::{tempdir, TempDir}; + +fn compile_kernel_module() -> (PathBuf, String, TempDir) { + let _m = ::FORK_MTX + .lock() + .expect("Mutex got poisoned by another test"); + + let tmp_dir = tempdir().expect("unable to create temporary build directory"); + + copy( + "test/test_kmod/hello_mod/hello.c", + &tmp_dir.path().join("hello.c"), + ).expect("unable to copy hello.c to temporary build directory"); + copy( + "test/test_kmod/hello_mod/Makefile", + &tmp_dir.path().join("Makefile"), + ).expect("unable to copy Makefile to temporary build directory"); + + let status = Command::new("make") + .current_dir(tmp_dir.path()) + .status() + .expect("failed to run make"); + + assert!(status.success()); + + // Return the relative path of the build kernel module + (tmp_dir.path().join("hello.ko"), "hello".to_owned(), tmp_dir) +} + +use nix::errno::Errno; +use nix::kmod::{delete_module, DeleteModuleFlags}; +use nix::kmod::{finit_module, init_module, ModuleInitFlags}; +use nix::Error; +use std::ffi::CString; +use std::fs::File; +use std::io::Read; + +#[test] +fn test_finit_and_delete_module() { + skip_if_not_root!("test_finit_module"); + + let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module(); + + let f = File::open(kmod_path).expect("unable to open kernel module"); + finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()) + .expect("unable to load kernel module"); + + delete_module( + &CString::new(kmod_name).unwrap(), + DeleteModuleFlags::empty(), + ).expect("unable to unload kernel module"); +} + +#[test] +fn test_finit_and_delete_modul_with_params() { + skip_if_not_root!("test_finit_module_with_params"); + + let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module(); + + let f = File::open(kmod_path).expect("unable to open kernel module"); + finit_module( + &f, + &CString::new("who=Rust number=2018").unwrap(), + ModuleInitFlags::empty(), + ).expect("unable to load kernel module"); + + delete_module( + &CString::new(kmod_name).unwrap(), + DeleteModuleFlags::empty(), + ).expect("unable to unload kernel module"); +} + +#[test] +fn test_init_and_delete_module() { + skip_if_not_root!("test_init_module"); + + let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module(); + + let mut f = File::open(kmod_path).expect("unable to open kernel module"); + let mut contents: Vec<u8> = Vec::new(); + f.read_to_end(&mut contents) + .expect("unable to read kernel module content to buffer"); + init_module(&mut contents, &CString::new("").unwrap()).expect("unable to load kernel module"); + + delete_module( + &CString::new(kmod_name).unwrap(), + DeleteModuleFlags::empty(), + ).expect("unable to unload kernel module"); +} + +#[test] +fn test_init_and_delete_module_with_params() { + skip_if_not_root!("test_init_module"); + + let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module(); + + let mut f = File::open(kmod_path).expect("unable to open kernel module"); + let mut contents: Vec<u8> = Vec::new(); + f.read_to_end(&mut contents) + .expect("unable to read kernel module content to buffer"); + init_module(&mut contents, &CString::new("who=Nix number=2015").unwrap()) + .expect("unable to load kernel module"); + + delete_module( + &CString::new(kmod_name).unwrap(), + DeleteModuleFlags::empty(), + ).expect("unable to unload kernel module"); +} + +#[test] +fn test_finit_module_invalid() { + skip_if_not_root!("test_finit_module_invalid"); + + let kmod_path = "/dev/zero"; + + let f = File::open(kmod_path).expect("unable to open kernel module"); + let result = finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()); + + assert_eq!(result.unwrap_err(), Error::Sys(Errno::EINVAL)); +} + +#[test] +fn test_finit_module_twice_and_delete_module() { + skip_if_not_root!("test_finit_module_twice_and_delete_module"); + + let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module(); + + let f = File::open(kmod_path).expect("unable to open kernel module"); + finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()) + .expect("unable to load kernel module"); + + let result = finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()); + + assert_eq!(result.unwrap_err(), Error::Sys(Errno::EEXIST)); + + delete_module( + &CString::new(kmod_name).unwrap(), + DeleteModuleFlags::empty(), + ).expect("unable to unload kernel module"); +} + +#[test] +fn test_delete_module_not_loaded() { + skip_if_not_root!("test_delete_module_not_loaded"); + + let result = delete_module(&CString::new("hello").unwrap(), DeleteModuleFlags::empty()); + + assert_eq!(result.unwrap_err(), Error::Sys(Errno::ENOENT)); +} diff --git a/third_party/rust/nix/test/test_mount.rs b/third_party/rust/nix/test/test_mount.rs new file mode 100644 index 0000000000..d2e08bc428 --- /dev/null +++ b/third_party/rust/nix/test/test_mount.rs @@ -0,0 +1,238 @@ +// Impelmentation note: to allow unprivileged users to run it, this test makes +// use of user and mount namespaces. On systems that allow unprivileged user +// namespaces (Linux >= 3.8 compiled with CONFIG_USER_NS), the test should run +// without root. + +extern crate libc; +extern crate nix; +extern crate tempfile; + +#[cfg(target_os = "linux")] +mod test_mount { + use std::fs::{self, File}; + use std::io::{self, Read, Write}; + use std::os::unix::fs::OpenOptionsExt; + use std::os::unix::fs::PermissionsExt; + use std::process::{self, Command}; + + use libc::{EACCES, EROFS}; + + use nix::errno::Errno; + use nix::mount::{mount, umount, MsFlags}; + use nix::sched::{unshare, CloneFlags}; + use nix::sys::stat::{self, Mode}; + use nix::unistd::getuid; + + use tempfile; + + static SCRIPT_CONTENTS: &'static [u8] = b"#!/bin/sh +exit 23"; + + const EXPECTED_STATUS: i32 = 23; + + const NONE: Option<&'static [u8]> = None; + pub fn test_mount_tmpfs_without_flags_allows_rwx() { + 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) + .or_else(|e| + if Errno::from_i32(e.raw_os_error().unwrap()) == Errno::EOVERFLOW { + // Skip tests on certain Linux kernels which have a bug + // regarding tmpfs in namespaces. + // Ubuntu 14.04 and 16.04 are known to be affected; 16.10 is + // not. There is no legitimate reason for open(2) to return + // EOVERFLOW here. + // https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1659087 + let stderr = io::stderr(); + let mut handle = stderr.lock(); + writeln!(handle, "Buggy Linux kernel detected. Skipping test.") + .unwrap(); + process::exit(0); + } else { + panic!("open failed: {}", e); + } + ) + .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); + + // 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)); + } + + pub fn test_mount_rdonly_disallows_write() { + 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 as i32, + File::create(tempdir.path().join("test")).unwrap_err().raw_os_error().unwrap()); + + umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e)); + } + + pub fn test_mount_noexec_disallows_exec() { + 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); + + // EACCES: Permission denied + assert_eq!(EACCES as i32, + Command::new(&test_path).status().unwrap_err().raw_os_error().unwrap()); + + umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {}", e)); + } + + pub fn test_mount_bind() { + 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)); + + 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); + } + + pub fn setup_namespaces() { + // Hold on to the uid in the parent namespace. + let uid = getuid(); + + unshare(CloneFlags::CLONE_NEWNS | CloneFlags::CLONE_NEWUSER).unwrap_or_else(|e| { + let stderr = io::stderr(); + let mut handle = stderr.lock(); + writeln!(handle, + "unshare failed: {}. Are unprivileged user namespaces available?", + e).unwrap(); + writeln!(handle, "mount is not being tested").unwrap(); + // Exit with success because not all systems support unprivileged user namespaces, and + // that's not what we're testing for. + process::exit(0); + }); + + // Map user as uid 1000. + fs::OpenOptions::new() + .write(true) + .open("/proc/self/uid_map") + .and_then(|mut f| f.write(format!("1000 {} 1\n", uid).as_bytes())) + .unwrap_or_else(|e| panic!("could not write uid map: {}", e)); + } +} + + +// Test runner + +/// Mimic normal test output (hackishly). +#[cfg(target_os = "linux")] +macro_rules! run_tests { + ( $($test_fn:ident),* ) => {{ + println!(); + + $( + print!("test test_mount::{} ... ", stringify!($test_fn)); + $test_fn(); + println!("ok"); + )* + + println!(); + }} +} + +#[cfg(target_os = "linux")] +fn main() { + use test_mount::{setup_namespaces, test_mount_tmpfs_without_flags_allows_rwx, + test_mount_rdonly_disallows_write, test_mount_noexec_disallows_exec, + test_mount_bind}; + setup_namespaces(); + + run_tests!(test_mount_tmpfs_without_flags_allows_rwx, + test_mount_rdonly_disallows_write, + test_mount_noexec_disallows_exec, + test_mount_bind); +} + +#[cfg(not(target_os = "linux"))] +fn main() {} diff --git a/third_party/rust/nix/test/test_mq.rs b/third_party/rust/nix/test/test_mq.rs new file mode 100644 index 0000000000..41ab358b33 --- /dev/null +++ b/third_party/rust/nix/test/test_mq.rs @@ -0,0 +1,152 @@ +use libc::c_long; + +use std::ffi::CString; +use std::str; + +use nix::errno::Errno::*; +use nix::Error::Sys; +use nix::mqueue::{mq_open, mq_close, mq_send, mq_receive}; +use nix::mqueue::{MqAttr, MQ_OFlag}; +use nix::sys::stat::Mode; + +#[test] +fn test_mq_send_and_receive() { + const MSG_SIZE: c_long = 32; + let attr = MqAttr::new(0, 10, MSG_SIZE, 0); + let mq_name= &CString::new(b"/a_nix_test_queue".as_ref()).unwrap(); + + 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 r0 = mq_open(mq_name, oflag0, mode, Some(&attr)); + if let Err(Sys(ENOSYS)) = r0 { + println!("message queues not supported or module not loaded?"); + return; + }; + let mqd0 = r0.unwrap(); + let msg_to_send = "msg_1"; + mq_send(mqd0, msg_to_send.as_bytes(), 1).unwrap(); + + let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY; + let mqd1 = mq_open(mq_name, oflag1, mode, Some(&attr)).unwrap(); + let mut buf = [0u8; 32]; + let mut prio = 0u32; + let len = mq_receive(mqd1, &mut buf, &mut prio).unwrap(); + assert!(prio == 1); + + mq_close(mqd1).unwrap(); + mq_close(mqd0).unwrap(); + assert_eq!(msg_to_send, str::from_utf8(&buf[0..len]).unwrap()); +} + + +#[test] +#[cfg(not(any(target_os = "netbsd")))] +fn test_mq_getattr() { + use nix::mqueue::mq_getattr; + const MSG_SIZE: c_long = 32; + let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); + let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap(); + let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; + let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; + let r = mq_open(mq_name, oflag, mode, Some(&initial_attr)); + if let Err(Sys(ENOSYS)) = r { + println!("message queues not supported or module not loaded?"); + return; + }; + let mqd = r.unwrap(); + + let read_attr = mq_getattr(mqd); + assert!(read_attr.unwrap() == initial_attr); + mq_close(mqd).unwrap(); +} + +// FIXME: Fix failures for mips in QEMU +#[test] +#[cfg(not(any(target_os = "netbsd")))] +#[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)] +fn test_mq_setattr() { + use nix::mqueue::{mq_getattr, mq_setattr}; + const MSG_SIZE: c_long = 32; + let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); + let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap(); + let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; + let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; + let r = mq_open(mq_name, oflag, mode, Some(&initial_attr)); + if let Err(Sys(ENOSYS)) = r { + println!("message queues not supported or module not loaded?"); + return; + }; + let mqd = r.unwrap(); + + let new_attr = MqAttr::new(0, 20, MSG_SIZE * 2, 100); + let old_attr = mq_setattr(mqd, &new_attr); + assert!(old_attr.unwrap() == initial_attr); + + let new_attr_get = mq_getattr(mqd); + // The following tests make sense. No changes here because according to the Linux man page only + // O_NONBLOCK can be set (see tests below) + assert!(new_attr_get.unwrap() != new_attr); + + let new_attr_non_blocking = MqAttr::new(MQ_OFlag::O_NONBLOCK.bits() as c_long, 10, MSG_SIZE, 0); + mq_setattr(mqd, &new_attr_non_blocking).unwrap(); + let new_attr_get = mq_getattr(mqd); + + // now the O_NONBLOCK flag has been set + assert!(new_attr_get.unwrap() != initial_attr); + assert!(new_attr_get.unwrap() == new_attr_non_blocking); + mq_close(mqd).unwrap(); +} + +// FIXME: Fix failures for mips in QEMU +#[test] +#[cfg(not(any(target_os = "netbsd")))] +#[cfg_attr(any(target_arch = "mips", target_arch = "mips64"), ignore)] +fn test_mq_set_nonblocking() { + use nix::mqueue::{mq_getattr, mq_set_nonblock, mq_remove_nonblock}; + const MSG_SIZE: c_long = 32; + let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); + let mq_name = &CString::new(b"/attr_test_get_attr".as_ref()).unwrap(); + let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; + let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; + let r = mq_open(mq_name, oflag, mode, Some(&initial_attr)); + if let Err(Sys(ENOSYS)) = r { + println!("message queues not supported or module not loaded?"); + return; + }; + let mqd = r.unwrap(); + mq_set_nonblock(mqd).unwrap(); + let new_attr = mq_getattr(mqd); + assert!(new_attr.unwrap().flags() == MQ_OFlag::O_NONBLOCK.bits() as c_long); + mq_remove_nonblock(mqd).unwrap(); + let new_attr = mq_getattr(mqd); + assert!(new_attr.unwrap().flags() == 0); + mq_close(mqd).unwrap(); +} + +#[test] +#[cfg(not(any(target_os = "netbsd")))] +fn test_mq_unlink() { + use nix::mqueue::mq_unlink; + const MSG_SIZE: c_long = 32; + let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0); + let mq_name_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap(); + let mq_name_not_opened = &CString::new(b"/mq_unlink_test".as_ref()).unwrap(); + let oflag = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY; + let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH; + let r = mq_open(mq_name_opened, oflag, mode, Some(&initial_attr)); + if let Err(Sys(ENOSYS)) = r { + println!("message queues not supported or module not loaded?"); + return; + }; + let mqd = r.unwrap(); + + let res_unlink = mq_unlink(mq_name_opened); + assert!(res_unlink == Ok(()) ); + + let res_unlink_not_opened = mq_unlink(mq_name_not_opened); + assert!(res_unlink_not_opened == Err(Sys(ENOENT)) ); + + mq_close(mqd).unwrap(); + let res_unlink_after_close = mq_unlink(mq_name_opened); + assert!(res_unlink_after_close == Err(Sys(ENOENT)) ); +} diff --git a/third_party/rust/nix/test/test_net.rs b/third_party/rust/nix/test/test_net.rs new file mode 100644 index 0000000000..b8940e718b --- /dev/null +++ b/third_party/rust/nix/test/test_net.rs @@ -0,0 +1,12 @@ +use nix::net::if_::*; + +#[cfg(any(target_os = "android", target_os = "linux"))] +const LOOPBACK: &[u8] = b"lo"; + +#[cfg(not(any(target_os = "android", target_os = "linux")))] +const LOOPBACK: &[u8] = b"lo0"; + +#[test] +fn test_if_nametoindex() { + assert!(if_nametoindex(&LOOPBACK[..]).is_ok()); +} diff --git a/third_party/rust/nix/test/test_nix_path.rs b/third_party/rust/nix/test/test_nix_path.rs new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/third_party/rust/nix/test/test_nix_path.rs diff --git a/third_party/rust/nix/test/test_poll.rs b/third_party/rust/nix/test/test_poll.rs new file mode 100644 index 0000000000..23df151790 --- /dev/null +++ b/third_party/rust/nix/test/test_poll.rs @@ -0,0 +1,68 @@ +use nix::poll::{EventFlags, poll, PollFd}; +use nix::unistd::{write, pipe, close}; + +#[test] +fn test_poll() { + let (r, w) = pipe().unwrap(); + let mut fds = [PollFd::new(r, EventFlags::POLLIN)]; + + // Poll an idle pipe. Should timeout + let nfds = poll(&mut fds, 100).unwrap(); + assert_eq!(nfds, 0); + assert!(!fds[0].revents().unwrap().contains(EventFlags::POLLIN)); + + write(w, b".").unwrap(); + + // Poll a readable pipe. Should return an event. + let nfds = poll(&mut fds, 100).unwrap(); + assert_eq!(nfds, 1); + assert!(fds[0].revents().unwrap().contains(EventFlags::POLLIN)); +} + +#[test] +fn test_poll_debug() { + assert_eq!(format!("{:?}", PollFd::new(0, EventFlags::empty())), + "PollFd { fd: 0, events: (empty), revents: (empty) }"); + assert_eq!(format!("{:?}", PollFd::new(1, EventFlags::POLLIN)), + "PollFd { fd: 1, events: POLLIN, revents: (empty) }"); + + // Testing revents requires doing some I/O + let (r, w) = pipe().unwrap(); + let mut fds = [PollFd::new(r, EventFlags::POLLIN)]; + write(w, b" ").unwrap(); + close(w).unwrap(); + poll(&mut fds, -1).unwrap(); + assert_eq!(format!("{:?}", fds[0]), + format!("PollFd {{ fd: {}, events: POLLIN, revents: POLLIN | POLLHUP }}", r)); + close(r).unwrap(); +} + +// ppoll(2) is the same as poll except for how it handles timeouts and signals. +// Repeating the test for poll(2) should be sufficient to check that our +// bindings are correct. +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux"))] +#[test] +fn test_ppoll() { + use nix::poll::ppoll; + use nix::sys::signal::SigSet; + use nix::sys::time::{TimeSpec, TimeValLike}; + + let timeout = TimeSpec::milliseconds(1); + let (r, w) = pipe().unwrap(); + let mut fds = [PollFd::new(r, EventFlags::POLLIN)]; + + // Poll an idle pipe. Should timeout + let nfds = ppoll(&mut fds, timeout, SigSet::empty()).unwrap(); + assert_eq!(nfds, 0); + assert!(!fds[0].revents().unwrap().contains(EventFlags::POLLIN)); + + write(w, b".").unwrap(); + + // Poll a readable pipe. Should return an event. + let nfds = ppoll(&mut fds, timeout, SigSet::empty()).unwrap(); + assert_eq!(nfds, 1); + assert!(fds[0].revents().unwrap().contains(EventFlags::POLLIN)); +} diff --git a/third_party/rust/nix/test/test_pty.rs b/third_party/rust/nix/test/test_pty.rs new file mode 100644 index 0000000000..4f428bed9a --- /dev/null +++ b/third_party/rust/nix/test/test_pty.rs @@ -0,0 +1,203 @@ +use std::io::Write; +use std::path::Path; +use std::os::unix::prelude::*; +use tempfile::tempfile; + +use nix::fcntl::{OFlag, open}; +use nix::pty::*; +use nix::sys::stat; +use nix::sys::termios::*; +use nix::unistd::{write, close}; + +/// Regression test for Issue #659 +/// This is the correct way to explicitly close a `PtyMaster` +#[test] +fn test_explicit_close() { + let mut f = { + let m = posix_openpt(OFlag::O_RDWR).unwrap(); + close(m.into_raw_fd()).unwrap(); + tempfile().unwrap() + }; + // This should work. But if there's been a double close, then it will + // return EBADF + f.write_all(b"whatever").unwrap(); +} + +/// Test equivalence of `ptsname` and `ptsname_r` +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptsname_equivalence() { + let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open a new PTTY master + let master_fd = posix_openpt(OFlag::O_RDWR).unwrap(); + assert!(master_fd.as_raw_fd() > 0); + + // Get the name of the slave + let slave_name = unsafe { ptsname(&master_fd) }.unwrap() ; + let slave_name_r = ptsname_r(&master_fd).unwrap(); + assert_eq!(slave_name, slave_name_r); +} + +/// Test data copying of `ptsname` +// TODO need to run in a subprocess, since ptsname is non-reentrant +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptsname_copy() { + let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open a new PTTY master + let master_fd = posix_openpt(OFlag::O_RDWR).unwrap(); + assert!(master_fd.as_raw_fd() > 0); + + // Get the name of the slave + let slave_name1 = unsafe { ptsname(&master_fd) }.unwrap(); + let slave_name2 = unsafe { ptsname(&master_fd) }.unwrap(); + assert!(slave_name1 == slave_name2); + // Also make sure that the string was actually copied and they point to different parts of + // memory. + assert!(slave_name1.as_ptr() != slave_name2.as_ptr()); +} + +/// Test data copying of `ptsname_r` +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptsname_r_copy() { + // Open a new PTTY master + let master_fd = posix_openpt(OFlag::O_RDWR).unwrap(); + assert!(master_fd.as_raw_fd() > 0); + + // Get the name of the slave + let slave_name1 = ptsname_r(&master_fd).unwrap(); + let slave_name2 = ptsname_r(&master_fd).unwrap(); + assert!(slave_name1 == slave_name2); + assert!(slave_name1.as_ptr() != slave_name2.as_ptr()); +} + +/// Test that `ptsname` returns different names for different devices +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_ptsname_unique() { + let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open a new PTTY master + let master1_fd = posix_openpt(OFlag::O_RDWR).unwrap(); + assert!(master1_fd.as_raw_fd() > 0); + + // Open a second PTTY master + let master2_fd = posix_openpt(OFlag::O_RDWR).unwrap(); + assert!(master2_fd.as_raw_fd() > 0); + + // Get the name of the slave + let slave_name1 = unsafe { ptsname(&master1_fd) }.unwrap(); + let slave_name2 = unsafe { ptsname(&master2_fd) }.unwrap(); + assert!(slave_name1 != slave_name2); +} + +/// Test opening a master/slave PTTY pair +/// +/// This is a single larger test because much of these functions aren't useful by themselves. So for +/// this test we perform the basic act of getting a file handle for a connect master/slave PTTY +/// pair. +#[test] +fn test_open_ptty_pair() { + let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open a new PTTY master + let master_fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed"); + assert!(master_fd.as_raw_fd() > 0); + + // Allow a slave to be generated for it + grantpt(&master_fd).expect("grantpt failed"); + unlockpt(&master_fd).expect("unlockpt failed"); + + // Get the name of the slave + let slave_name = unsafe { ptsname(&master_fd) }.expect("ptsname failed"); + + // Open the slave device + let slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty()).unwrap(); + assert!(slave_fd > 0); +} + +#[test] +fn test_openpty() { + // openpty uses ptname(3) internally + let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + let pty = openpty(None, None).unwrap(); + assert!(pty.master > 0); + assert!(pty.slave > 0); + + // Writing to one should be readable on the other one + let string = "foofoofoo\n"; + let mut buf = [0u8; 10]; + write(pty.master, string.as_bytes()).unwrap(); + ::read_exact(pty.slave, &mut buf); + + assert_eq!(&buf, string.as_bytes()); + + // Read the echo as well + let echoed_string = "foofoofoo\r\n"; + let mut buf = [0u8; 11]; + ::read_exact(pty.master, &mut buf); + assert_eq!(&buf, echoed_string.as_bytes()); + + let string2 = "barbarbarbar\n"; + let echoed_string2 = "barbarbarbar\r\n"; + let mut buf = [0u8; 14]; + write(pty.slave, string2.as_bytes()).unwrap(); + ::read_exact(pty.master, &mut buf); + + assert_eq!(&buf, echoed_string2.as_bytes()); + + close(pty.master).unwrap(); + close(pty.slave).unwrap(); +} + +#[test] +fn test_openpty_with_termios() { + // openpty uses ptname(3) internally + let _m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test"); + + // Open one pty to get attributes for the second one + let mut termios = { + let pty = openpty(None, None).unwrap(); + assert!(pty.master > 0); + assert!(pty.slave > 0); + let termios = tcgetattr(pty.master).unwrap(); + close(pty.master).unwrap(); + close(pty.slave).unwrap(); + termios + }; + // Make sure newlines are not transformed so the data is preserved when sent. + termios.output_flags.remove(OutputFlags::ONLCR); + + let pty = openpty(None, &termios).unwrap(); + // Must be valid file descriptors + assert!(pty.master > 0); + assert!(pty.slave > 0); + + // Writing to one should be readable on the other one + let string = "foofoofoo\n"; + let mut buf = [0u8; 10]; + write(pty.master, string.as_bytes()).unwrap(); + ::read_exact(pty.slave, &mut buf); + + assert_eq!(&buf, string.as_bytes()); + + // read the echo as well + let echoed_string = "foofoofoo\n"; + ::read_exact(pty.master, &mut buf); + assert_eq!(&buf, echoed_string.as_bytes()); + + let string2 = "barbarbarbar\n"; + let echoed_string2 = "barbarbarbar\n"; + let mut buf = [0u8; 13]; + write(pty.slave, string2.as_bytes()).unwrap(); + ::read_exact(pty.master, &mut buf); + + assert_eq!(&buf, echoed_string2.as_bytes()); + + close(pty.master).unwrap(); + close(pty.slave).unwrap(); +} diff --git a/third_party/rust/nix/test/test_ptymaster_drop.rs b/third_party/rust/nix/test/test_ptymaster_drop.rs new file mode 100644 index 0000000000..9b59d66435 --- /dev/null +++ b/third_party/rust/nix/test/test_ptymaster_drop.rs @@ -0,0 +1,21 @@ +extern crate nix; + +use nix::fcntl::OFlag; +use nix::pty::*; +use nix::unistd::close; +use std::os::unix::io::AsRawFd; + +/// Regression test for Issue #659 +/// `PtyMaster` should panic rather than double close the file descriptor +/// This must run in its own test process because it deliberately creates a race +/// condition. +#[test] +#[should_panic(expected = "Closing an invalid file descriptor!")] +// In Travis on i686-unknown-linux-musl, this test gets SIGABRT. I don't know +// why. It doesn't happen on any other target, and it doesn't happen on my PC. +#[cfg_attr(all(target_env = "musl", target_arch = "x86"), ignore)] +fn test_double_close() { + let m = posix_openpt(OFlag::O_RDWR).unwrap(); + close(m.as_raw_fd()).unwrap(); + drop(m); // should panic here +} diff --git a/third_party/rust/nix/test/test_sendfile.rs b/third_party/rust/nix/test/test_sendfile.rs new file mode 100644 index 0000000000..3bc7932f4c --- /dev/null +++ b/third_party/rust/nix/test/test_sendfile.rs @@ -0,0 +1,129 @@ +use std::io::prelude::*; +use std::os::unix::prelude::*; + +use libc::off_t; +use nix::sys::sendfile::*; +use tempfile::tempfile; + +cfg_if! { + if #[cfg(any(target_os = "android", target_os = "linux"))] { + use nix::unistd::{close, pipe, read}; + } else if #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] { + use std::net::Shutdown; + use std::os::unix::net::UnixStream; + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_sendfile_linux() { + const CONTENTS: &[u8] = b"abcdef123456"; + let mut tmp = tempfile().unwrap(); + tmp.write_all(CONTENTS).unwrap(); + + let (rd, wr) = pipe().unwrap(); + let mut offset: off_t = 5; + let res = sendfile(wr, tmp.as_raw_fd(), Some(&mut offset), 2).unwrap(); + + assert_eq!(2, res); + + let mut buf = [0u8; 1024]; + assert_eq!(2, read(rd, &mut buf).unwrap()); + assert_eq!(b"f1", &buf[0..2]); + assert_eq!(7, offset); + + close(rd).unwrap(); + close(wr).unwrap(); +} + +#[cfg(target_os = "freebsd")] +#[test] +fn test_sendfile_freebsd() { + // Declare the content + let header_strings = vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"]; + let body = "Xabcdef123456"; + let body_offset = 1; + let trailer_strings = vec!["\n", "Served by Make Believe\n"]; + + // Write the body to a file + let mut tmp = tempfile().unwrap(); + tmp.write_all(body.as_bytes()).unwrap(); + + // Prepare headers and trailers for sendfile + let headers: Vec<&[u8]> = header_strings.iter().map(|s| s.as_bytes()).collect(); + let trailers: Vec<&[u8]> = trailer_strings.iter().map(|s| s.as_bytes()).collect(); + + // Prepare socket pair + let (mut rd, wr) = UnixStream::pair().unwrap(); + + // Call the test method + let (res, bytes_written) = sendfile( + tmp.as_raw_fd(), + wr.as_raw_fd(), + body_offset as off_t, + None, + Some(headers.as_slice()), + Some(trailers.as_slice()), + SfFlags::empty(), + 0, + ); + assert!(res.is_ok()); + wr.shutdown(Shutdown::Both).unwrap(); + + // Prepare the expected result + let expected_string = + header_strings.concat() + &body[body_offset..] + &trailer_strings.concat(); + + // Verify the message that was sent + assert_eq!(bytes_written as usize, expected_string.as_bytes().len()); + + let mut read_string = String::new(); + let bytes_read = rd.read_to_string(&mut read_string).unwrap(); + assert_eq!(bytes_written as usize, bytes_read); + assert_eq!(expected_string, read_string); +} + +#[cfg(any(target_os = "ios", target_os = "macos"))] +#[test] +fn test_sendfile_darwin() { + // Declare the content + let header_strings = vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"]; + let body = "Xabcdef123456"; + let body_offset = 1; + let trailer_strings = vec!["\n", "Served by Make Believe\n"]; + + // Write the body to a file + let mut tmp = tempfile().unwrap(); + tmp.write_all(body.as_bytes()).unwrap(); + + // Prepare headers and trailers for sendfile + let headers: Vec<&[u8]> = header_strings.iter().map(|s| s.as_bytes()).collect(); + let trailers: Vec<&[u8]> = trailer_strings.iter().map(|s| s.as_bytes()).collect(); + + // Prepare socket pair + let (mut rd, wr) = UnixStream::pair().unwrap(); + + // Call the test method + let (res, bytes_written) = sendfile( + tmp.as_raw_fd(), + wr.as_raw_fd(), + body_offset as off_t, + None, + Some(headers.as_slice()), + Some(trailers.as_slice()), + ); + assert!(res.is_ok()); + wr.shutdown(Shutdown::Both).unwrap(); + + // Prepare the expected result + let expected_string = + header_strings.concat() + &body[body_offset..] + &trailer_strings.concat(); + + // Verify the message that was sent + assert_eq!(bytes_written as usize, expected_string.as_bytes().len()); + + let mut read_string = String::new(); + let bytes_read = rd.read_to_string(&mut read_string).unwrap(); + assert_eq!(bytes_written as usize, bytes_read); + assert_eq!(expected_string, read_string); +} diff --git a/third_party/rust/nix/test/test_stat.rs b/third_party/rust/nix/test/test_stat.rs new file mode 100644 index 0000000000..fae8df821c --- /dev/null +++ b/third_party/rust/nix/test/test_stat.rs @@ -0,0 +1,260 @@ +use std::fs::{self, File}; +use std::os::unix::fs::symlink; +use std::os::unix::prelude::AsRawFd; +use std::time::{Duration, UNIX_EPOCH}; + +#[cfg(not(any(target_os = "netbsd")))] +use libc::{S_IFMT, S_IFLNK}; + +use nix::fcntl; +use nix::sys::stat::{self, fchmod, fchmodat, futimens, stat, utimes, utimensat}; +#[cfg(any(target_os = "linux", + target_os = "haiku", + target_os = "ios", + target_os = "macos", + target_os = "freebsd", + target_os = "netbsd"))] +use nix::sys::stat::lutimes; +use nix::sys::stat::{Mode, FchmodatFlags, UtimensatFlags}; + +#[cfg(not(any(target_os = "netbsd")))] +use nix::sys::stat::FileStat; + +use nix::sys::time::{TimeSpec, TimeVal, TimeValLike}; +use nix::unistd::chdir; + +#[cfg(not(any(target_os = "netbsd")))] +use nix::Result; +use tempfile; + +#[allow(unused_comparisons)] +// uid and gid are signed on Windows, but not on other platforms. This function +// allows warning free compiles on all platforms, and can be removed when +// expression-level #[allow] is available. +#[cfg(not(any(target_os = "netbsd")))] +fn valid_uid_gid(stat: FileStat) -> bool { + // uid could be 0 for the `root` user. This quite possible when + // the tests are being run on a rooted Android device. + stat.st_uid >= 0 && stat.st_gid >= 0 +} + +#[cfg(not(any(target_os = "netbsd")))] +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!(stats.st_nlink == 1); // there links created, must be 1 + assert!(valid_uid_gid(stats)); // must be positive integers + assert!(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")))] +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!((stats.st_mode as usize) & (S_IFMT as usize) == S_IFLNK as usize); // should be a link + assert!(stats.st_nlink == 1); // there links created, must be 1 + assert!(valid_uid_gid(stats)); // must be positive integers + 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 + // (Android's st_blocks is ulonglong which is always non-negative.) + assert!(stats.st_blocks >= 0); +} + +#[test] +#[cfg(not(any(target_os = "netbsd")))] +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.as_raw_fd()); + assert_stat_results(fstat_result); +} + +#[test] +#[cfg(not(any(target_os = "netbsd")))] +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()); + + let result = stat::fstatat(dirfd.unwrap(), + &filename, + fcntl::AtFlags::empty()); + assert_stat_results(result); +} + +#[test] +#[cfg(not(any(target_os = "netbsd")))] +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.as_raw_fd()); + 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.as_raw_fd(), mode1).unwrap(); + + let file_stat1 = stat(&filename).unwrap(); + assert_eq!(file_stat1.st_mode & 0o7777, mode1.bits()); + + let mut mode2 = Mode::empty(); + mode2.insert(Mode::S_IROTH); + fchmod(file.as_raw_fd(), mode2).unwrap(); + + let file_stat2 = stat(&filename).unwrap(); + assert_eq!(file_stat2.st_mode & 0o7777, mode2.bits()); +} + +#[test] +fn test_fchmodat() { + 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(Some(dirfd), filename, mode1, FchmodatFlags::FollowSymlink).unwrap(); + + let file_stat1 = stat(&fullpath).unwrap(); + assert_eq!(file_stat1.st_mode & 0o7777, mode1.bits()); + + chdir(tempdir.path()).unwrap(); + + let mut mode2 = Mode::empty(); + mode2.insert(Mode::S_IROTH); + fchmodat(None, filename, mode2, FchmodatFlags::FollowSymlink).unwrap(); + + let file_stat2 = stat(&fullpath).unwrap(); + assert_eq!(file_stat2.st_mode & 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. +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] +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", + target_os = "haiku", + target_os = "ios", + target_os = "macos", + 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] +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] +fn test_utimensat() { + 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(Some(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(None, filename, &TimeSpec::seconds(500), &TimeSpec::seconds(800), + UtimensatFlags::FollowSymlink).unwrap(); + assert_times_eq(500, 800, &fs::metadata(&fullpath).unwrap()); +} diff --git a/third_party/rust/nix/test/test_unistd.rs b/third_party/rust/nix/test/test_unistd.rs new file mode 100644 index 0000000000..3fb5053512 --- /dev/null +++ b/third_party/rust/nix/test/test_unistd.rs @@ -0,0 +1,576 @@ +use nix::fcntl::{fcntl, FcntlArg, FdFlag, open, OFlag, readlink}; +use nix::unistd::*; +use nix::unistd::ForkResult::*; +use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, Signal, sigaction}; +use nix::sys::wait::*; +use nix::sys::stat::{self, Mode, SFlag}; +use std::{env, iter}; +use std::ffi::CString; +use std::fs::{self, File}; +use std::io::Write; +use std::os::unix::prelude::*; +use tempfile::{self, tempfile}; +use libc::{self, _exit, off_t}; + +#[test] +#[cfg(not(any(target_os = "netbsd")))] +fn test_fork_and_waitpid() { + let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // Safe: Child only calls `_exit`, which is signal-safe + match fork().expect("Error: Fork Failed") { + Child => unsafe { _exit(0) }, + Parent { child } => { + // assert that child was created and pid > 0 + let child_raw: ::libc::pid_t = child.into(); + assert!(child_raw > 0); + let wait_status = waitpid(child, None); + match wait_status { + // assert that waitpid returned correct status and the pid is the one of the child + Ok(WaitStatus::Exited(pid_t, _)) => assert!(pid_t == child), + + // panic, must never happen + s @ Ok(_) => panic!("Child exited {:?}, should never happen", s), + + // panic, waitpid should never fail + Err(s) => panic!("Error: waitpid returned Err({:?}", s) + } + + }, + } +} + +#[test] +fn test_wait() { + // Grab FORK_MTX so wait doesn't reap a different test's child process + let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // Safe: Child only calls `_exit`, which is signal-safe + match fork().expect("Error: Fork Failed") { + Child => unsafe { _exit(0) }, + Parent { child } => { + let wait_status = wait(); + + // just assert that (any) one child returns with WaitStatus::Exited + assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0))); + }, + } +} + +#[test] +fn test_mkstemp() { + let mut path = env::temp_dir(); + path.push("nix_tempfile.XXXXXX"); + + let result = mkstemp(&path); + match result { + Ok((fd, path)) => { + close(fd).unwrap(); + unlink(path.as_path()).unwrap(); + }, + Err(e) => panic!("mkstemp failed: {}", e) + } +} + +#[test] +fn test_mkstemp_directory() { + // mkstemp should fail if a directory is given + assert!(mkstemp(&env::temp_dir()).is_err()); +} + +#[test] +fn test_mkfifo() { + let tempdir = tempfile::tempdir().unwrap(); + let mkfifo_fifo = tempdir.path().join("mkfifo_fifo"); + + mkfifo(&mkfifo_fifo, Mode::S_IRUSR).unwrap(); + + let stats = stat::stat(&mkfifo_fifo).unwrap(); + let typ = stat::SFlag::from_bits_truncate(stats.st_mode); + assert!(typ == SFlag::S_IFIFO); +} + +#[test] +fn test_mkfifo_directory() { + // mkfifo should fail if a directory is given + assert!(mkfifo(&env::temp_dir(), Mode::S_IRUSR).is_err()); +} + +#[test] +fn test_getpid() { + let pid: ::libc::pid_t = getpid().into(); + let ppid: ::libc::pid_t = getppid().into(); + assert!(pid > 0); + assert!(ppid > 0); +} + +#[test] +fn test_getsid() { + let none_sid: ::libc::pid_t = getsid(None).unwrap().into(); + let pid_sid: ::libc::pid_t = getsid(Some(getpid())).unwrap().into(); + assert!(none_sid > 0); + assert!(none_sid == pid_sid); +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +mod linux_android { + use nix::unistd::gettid; + + #[test] + fn test_gettid() { + let tid: ::libc::pid_t = gettid().into(); + assert!(tid > 0); + } +} + +#[test] +// `getgroups()` and `setgroups()` do not behave as expected on Apple platforms +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +fn test_setgroups() { + // Skip this test when not run as root as `setgroups()` requires root. + skip_if_not_root!("test_setgroups"); + + let _m = ::GROUPS_MTX.lock().expect("Mutex got poisoned by another test"); + + // Save the existing groups + let old_groups = getgroups().unwrap(); + + // Set some new made up groups + let groups = [Gid::from_raw(123), Gid::from_raw(456)]; + setgroups(&groups).unwrap(); + + let new_groups = getgroups().unwrap(); + assert_eq!(new_groups, groups); + + // Revert back to the old groups + setgroups(&old_groups).unwrap(); +} + +#[test] +// `getgroups()` and `setgroups()` do not behave as expected on Apple platforms +#[cfg(not(any(target_os = "ios", target_os = "macos")))] +fn test_initgroups() { + // Skip this test when not run as root as `initgroups()` and `setgroups()` + // require root. + skip_if_not_root!("test_initgroups"); + + let _m = ::GROUPS_MTX.lock().expect("Mutex got poisoned by another test"); + + // Save the existing groups + let old_groups = getgroups().unwrap(); + + // It doesn't matter if the root user is not called "root" or if a user + // called "root" doesn't exist. We are just checking that the extra, + // made-up group, `123`, is set. + // FIXME: Test the other half of initgroups' functionality: whether the + // groups that the user belongs to are also set. + let user = CString::new("root").unwrap(); + let group = Gid::from_raw(123); + let group_list = getgrouplist(&user, group).unwrap(); + assert!(group_list.contains(&group)); + + initgroups(&user, group).unwrap(); + + let new_groups = getgroups().unwrap(); + assert_eq!(new_groups, group_list); + + // Revert back to the old groups + setgroups(&old_groups).unwrap(); +} + +macro_rules! execve_test_factory( + ($test_name:ident, $syscall:ident, $exe: expr $(, $pathname:expr, $flags:expr)*) => ( + #[test] + fn $test_name() { + let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + // The `exec`d process will write to `writer`, and we'll read that + // data from `reader`. + let (reader, writer) = pipe().unwrap(); + + // Safe: Child calls `exit`, `dup`, `close` and the provided `exec*` family function. + // NOTE: Technically, this makes the macro unsafe to use because you could pass anything. + // The tests make sure not to do that, though. + match fork().unwrap() { + Child => { + // Close stdout. + close(1).unwrap(); + // Make `writer` be the stdout of the new process. + dup(writer).unwrap(); + // exec! + $syscall( + $exe, + $(&CString::new($pathname).unwrap(), )* + &[CString::new(b"".as_ref()).unwrap(), + CString::new(b"-c".as_ref()).unwrap(), + CString::new(b"echo nix!!! && echo foo=$foo && echo baz=$baz" + .as_ref()).unwrap()], + &[CString::new(b"foo=bar".as_ref()).unwrap(), + CString::new(b"baz=quux".as_ref()).unwrap()] + $(, $flags)*).unwrap(); + }, + Parent { child } => { + // Wait for the child to exit. + waitpid(child, None).unwrap(); + // Read 1024 bytes. + let mut buf = [0u8; 1024]; + read(reader, &mut buf).unwrap(); + // It should contain the things we printed using `/bin/sh`. + let string = String::from_utf8_lossy(&buf); + assert!(string.contains("nix!!!")); + assert!(string.contains("foo=bar")); + assert!(string.contains("baz=quux")); + } + } + } + ) +); + +cfg_if!{ + if #[cfg(target_os = "android")] { + execve_test_factory!(test_execve, execve, &CString::new("/system/bin/sh").unwrap()); + execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd()); + } else if #[cfg(any(target_os = "freebsd", + target_os = "linux"))] { + execve_test_factory!(test_execve, execve, &CString::new("/bin/sh").unwrap()); + execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd()); + } else if #[cfg(any(target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] { + execve_test_factory!(test_execve, execve, &CString::new("/bin/sh").unwrap()); + // No fexecve() on DragonFly, ios, macos, NetBSD, OpenBSD. + // + // Note for NetBSD and OpenBSD: although rust-lang/libc includes it + // (under unix/bsd/netbsdlike/) fexecve is not currently implemented on + // NetBSD nor on OpenBSD. + } +} + +#[cfg(any(target_os = "haiku", target_os = "linux", target_os = "openbsd"))] +execve_test_factory!(test_execvpe, execvpe, &CString::new("sh").unwrap()); + +cfg_if!{ + if #[cfg(target_os = "android")] { + use nix::fcntl::AtFlags; + execve_test_factory!(test_execveat_empty, execveat, File::open("/system/bin/sh").unwrap().into_raw_fd(), + "", AtFlags::AT_EMPTY_PATH); + execve_test_factory!(test_execveat_relative, execveat, File::open("/system/bin/").unwrap().into_raw_fd(), + "./sh", AtFlags::empty()); + execve_test_factory!(test_execveat_absolute, execveat, File::open("/").unwrap().into_raw_fd(), + "/system/bin/sh", AtFlags::empty()); + } else if #[cfg(all(target_os = "linux"), any(target_arch ="x86_64", target_arch ="x86"))] { + use nix::fcntl::AtFlags; + execve_test_factory!(test_execveat_empty, execveat, File::open("/bin/sh").unwrap().into_raw_fd(), + "", AtFlags::AT_EMPTY_PATH); + execve_test_factory!(test_execveat_relative, execveat, File::open("/bin/").unwrap().into_raw_fd(), + "./sh", AtFlags::empty()); + execve_test_factory!(test_execveat_absolute, execveat, File::open("/").unwrap().into_raw_fd(), + "/bin/sh", AtFlags::empty()); + } +} + +#[test] +fn test_fchdir() { + // fchdir changes the process's cwd + let _m = ::CWD_MTX.lock().expect("Mutex got poisoned by another test"); + + let tmpdir = tempfile::tempdir().unwrap(); + let tmpdir_path = tmpdir.path().canonicalize().unwrap(); + let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd(); + + assert!(fchdir(tmpdir_fd).is_ok()); + assert_eq!(getcwd().unwrap(), tmpdir_path); + + assert!(close(tmpdir_fd).is_ok()); +} + +#[test] +fn test_getcwd() { + // chdir changes the process's cwd + let _m = ::CWD_MTX.lock().expect("Mutex got poisoned by another test"); + + let tmpdir = tempfile::tempdir().unwrap(); + let tmpdir_path = tmpdir.path().canonicalize().unwrap(); + assert!(chdir(&tmpdir_path).is_ok()); + assert_eq!(getcwd().unwrap(), tmpdir_path); + + // make path 500 chars longer so that buffer doubling in getcwd + // kicks in. Note: One path cannot be longer than 255 bytes + // (NAME_MAX) whole path cannot be longer than PATH_MAX (usually + // 4096 on linux, 1024 on macos) + let mut inner_tmp_dir = tmpdir_path.to_path_buf(); + for _ in 0..5 { + let newdir = iter::repeat("a").take(100).collect::<String>(); + inner_tmp_dir.push(newdir); + assert!(mkdir(inner_tmp_dir.as_path(), Mode::S_IRWXU).is_ok()); + } + assert!(chdir(inner_tmp_dir.as_path()).is_ok()); + assert_eq!(getcwd().unwrap(), inner_tmp_dir.as_path()); +} + +#[test] +fn test_chown() { + // Testing for anything other than our own UID/GID is hard. + let uid = Some(getuid()); + let gid = Some(getgid()); + + let tempdir = tempfile::tempdir().unwrap(); + let path = tempdir.path().join("file"); + { + File::create(&path).unwrap(); + } + + chown(&path, uid, gid).unwrap(); + chown(&path, uid, None).unwrap(); + chown(&path, None, gid).unwrap(); + + fs::remove_file(&path).unwrap(); + chown(&path, uid, gid).unwrap_err(); +} + +#[test] +fn test_fchownat() { + // Testing for anything other than our own UID/GID is hard. + let uid = Some(getuid()); + let gid = Some(getgid()); + + let tempdir = tempfile::tempdir().unwrap(); + let path = tempdir.path().join("file"); + { + File::create(&path).unwrap(); + } + + let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); + + fchownat(Some(dirfd), "file", uid, gid, FchownatFlags::FollowSymlink).unwrap(); + + chdir(tempdir.path()).unwrap(); + fchownat(None, "file", uid, gid, FchownatFlags::FollowSymlink).unwrap(); + + fs::remove_file(&path).unwrap(); + fchownat(None, "file", uid, gid, FchownatFlags::FollowSymlink).unwrap_err(); +} + +#[test] +fn test_lseek() { + const CONTENTS: &[u8] = b"abcdef123456"; + let mut tmp = tempfile().unwrap(); + tmp.write_all(CONTENTS).unwrap(); + let tmpfd = tmp.into_raw_fd(); + + let offset: off_t = 5; + lseek(tmpfd, offset, Whence::SeekSet).unwrap(); + + let mut buf = [0u8; 7]; + ::read_exact(tmpfd, &mut buf); + assert_eq!(b"f123456", &buf); + + close(tmpfd).unwrap(); +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +#[test] +fn test_lseek64() { + const CONTENTS: &[u8] = b"abcdef123456"; + let mut tmp = tempfile().unwrap(); + tmp.write_all(CONTENTS).unwrap(); + let tmpfd = tmp.into_raw_fd(); + + lseek64(tmpfd, 5, Whence::SeekSet).unwrap(); + + let mut buf = [0u8; 7]; + ::read_exact(tmpfd, &mut buf); + assert_eq!(b"f123456", &buf); + + close(tmpfd).unwrap(); +} + +// Skip on FreeBSD because FreeBSD's CI environment is jailed, and jails +// aren't allowed to use acct(2) +#[cfg(not(target_os = "freebsd"))] +#[test] +fn test_acct() { + use tempfile::NamedTempFile; + use std::process::Command; + use std::{thread, time}; + + skip_if_not_root!("test_acct"); + let file = NamedTempFile::new().unwrap(); + let path = file.path().to_str().unwrap(); + + acct::enable(path).unwrap(); + Command::new("echo").arg("Hello world"); + acct::disable().unwrap(); + + loop { + let len = fs::metadata(path).unwrap().len(); + if len > 0 { break; } + thread::sleep(time::Duration::from_millis(10)); + } +} + +#[test] +fn test_fpathconf_limited() { + let f = tempfile().unwrap(); + // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test + let path_max = fpathconf(f.as_raw_fd(), PathconfVar::PATH_MAX); + assert!(path_max.expect("fpathconf failed").expect("PATH_MAX is unlimited") > 0); +} + +#[test] +fn test_pathconf_limited() { + // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test + let path_max = pathconf("/", PathconfVar::PATH_MAX); + assert!(path_max.expect("pathconf failed").expect("PATH_MAX is unlimited") > 0); +} + +#[test] +fn test_sysconf_limited() { + // AFAIK, OPEN_MAX is limited on all platforms, so it makes a good test + let open_max = sysconf(SysconfVar::OPEN_MAX); + assert!(open_max.expect("sysconf failed").expect("OPEN_MAX is unlimited") > 0); +} + +#[cfg(target_os = "freebsd")] +#[test] +fn test_sysconf_unsupported() { + // I know of no sysconf variables that are unsupported everywhere, but + // _XOPEN_CRYPT is unsupported on FreeBSD 11.0, which is one of the platforms + // we test. + let open_max = sysconf(SysconfVar::_XOPEN_CRYPT); + assert!(open_max.expect("sysconf failed").is_none()) +} + +// Test that we can create a pair of pipes. No need to verify that they pass +// data; that's the domain of the OS, not nix. +#[test] +fn test_pipe() { + let (fd0, fd1) = pipe().unwrap(); + let m0 = stat::SFlag::from_bits_truncate(stat::fstat(fd0).unwrap().st_mode); + // S_IFIFO means it's a pipe + assert_eq!(m0, SFlag::S_IFIFO); + let m1 = stat::SFlag::from_bits_truncate(stat::fstat(fd1).unwrap().st_mode); + assert_eq!(m1, SFlag::S_IFIFO); +} + +// pipe2(2) is the same as pipe(2), except it allows setting some flags. Check +// that we can set a flag. +#[test] +fn test_pipe2() { + let (fd0, fd1) = pipe2(OFlag::O_CLOEXEC).unwrap(); + let f0 = FdFlag::from_bits_truncate(fcntl(fd0, FcntlArg::F_GETFD).unwrap()); + assert!(f0.contains(FdFlag::FD_CLOEXEC)); + let f1 = FdFlag::from_bits_truncate(fcntl(fd1, FcntlArg::F_GETFD).unwrap()); + assert!(f1.contains(FdFlag::FD_CLOEXEC)); +} + +#[test] +fn test_truncate() { + let tempdir = tempfile::tempdir().unwrap(); + let path = tempdir.path().join("file"); + + { + let mut tmp = File::create(&path).unwrap(); + const CONTENTS: &[u8] = b"12345678"; + tmp.write_all(CONTENTS).unwrap(); + } + + truncate(&path, 4).unwrap(); + + let metadata = fs::metadata(&path).unwrap(); + assert_eq!(4, metadata.len()); +} + +#[test] +fn test_ftruncate() { + let tempdir = tempfile::tempdir().unwrap(); + let path = tempdir.path().join("file"); + + let tmpfd = { + let mut tmp = File::create(&path).unwrap(); + const CONTENTS: &[u8] = b"12345678"; + tmp.write_all(CONTENTS).unwrap(); + tmp.into_raw_fd() + }; + + ftruncate(tmpfd, 2).unwrap(); + close(tmpfd).unwrap(); + + let metadata = fs::metadata(&path).unwrap(); + assert_eq!(2, metadata.len()); +} + +// Used in `test_alarm`. +static mut ALARM_CALLED: bool = false; + +// Used in `test_alarm`. +pub extern fn alarm_signal_handler(raw_signal: libc::c_int) { + assert_eq!(raw_signal, libc::SIGALRM, "unexpected signal: {}", raw_signal); + unsafe { ALARM_CALLED = true }; +} + +#[test] +fn test_alarm() { + let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + + let handler = SigHandler::Handler(alarm_signal_handler); + let signal_action = SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty()); + let old_handler = unsafe { + sigaction(Signal::SIGALRM, &signal_action) + .expect("unable to set signal handler for alarm") + }; + + // Set an alarm. + assert_eq!(alarm::set(60), None); + + // Overwriting an alarm should return the old alarm. + assert_eq!(alarm::set(1), Some(60)); + + // We should be woken up after 1 second by the alarm, so we'll sleep for 2 + // seconds to be sure. + sleep(2); + assert_eq!(unsafe { ALARM_CALLED }, true, "expected our alarm signal handler to be called"); + + // Reset the signal. + unsafe { + sigaction(Signal::SIGALRM, &old_handler) + .expect("unable to set signal handler for alarm"); + } +} + +#[test] +fn test_canceling_alarm() { + let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test"); + + assert_eq!(alarm::cancel(), None); + + assert_eq!(alarm::set(60), None); + assert_eq!(alarm::cancel(), Some(60)); +} + +#[test] +fn test_symlinkat() { + let mut buf = [0; 1024]; + let tempdir = tempfile::tempdir().unwrap(); + + let target = tempdir.path().join("a"); + let linkpath = tempdir.path().join("b"); + symlinkat(&target, None, &linkpath).unwrap(); + assert_eq!( + readlink(&linkpath, &mut buf).unwrap().to_str().unwrap(), + target.to_str().unwrap() + ); + + let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap(); + let target = "c"; + let linkpath = "d"; + symlinkat(target, Some(dirfd), linkpath).unwrap(); + assert_eq!( + readlink(&tempdir.path().join(linkpath), &mut buf) + .unwrap() + .to_str() + .unwrap(), + target + ); +} |