summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/nix
parentInitial commit. (diff)
downloadfirefox-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')
-rw-r--r--third_party/rust/nix/.cargo-checksum.json1
-rw-r--r--third_party/rust/nix/.cirrus.yml21
-rw-r--r--third_party/rust/nix/CHANGELOG.md624
-rw-r--r--third_party/rust/nix/CONTRIBUTING.md114
-rw-r--r--third_party/rust/nix/CONVENTIONS.md87
-rw-r--r--third_party/rust/nix/Cargo.toml55
-rw-r--r--third_party/rust/nix/LICENSE21
-rw-r--r--third_party/rust/nix/README.md110
-rw-r--r--third_party/rust/nix/build.rs12
-rw-r--r--third_party/rust/nix/src/dir.rs210
-rw-r--r--third_party/rust/nix/src/errno.rs1911
-rw-r--r--third_party/rust/nix/src/errno_dragonfly.c3
-rw-r--r--third_party/rust/nix/src/fcntl.rs394
-rw-r--r--third_party/rust/nix/src/features.rs103
-rw-r--r--third_party/rust/nix/src/ifaddrs.rs146
-rw-r--r--third_party/rust/nix/src/kmod.rs123
-rw-r--r--third_party/rust/nix/src/lib.rs283
-rw-r--r--third_party/rust/nix/src/macros.rs264
-rw-r--r--third_party/rust/nix/src/mount.rs99
-rw-r--r--third_party/rust/nix/src/mqueue.rs173
-rw-r--r--third_party/rust/nix/src/net/if_.rs268
-rw-r--r--third_party/rust/nix/src/net/mod.rs4
-rw-r--r--third_party/rust/nix/src/poll.rs161
-rw-r--r--third_party/rust/nix/src/pty.rs268
-rw-r--r--third_party/rust/nix/src/sched.rs121
-rw-r--r--third_party/rust/nix/src/sys/aio.rs1286
-rw-r--r--third_party/rust/nix/src/sys/epoll.rs110
-rw-r--r--third_party/rust/nix/src/sys/event.rs352
-rw-r--r--third_party/rust/nix/src/sys/eventfd.rs18
-rw-r--r--third_party/rust/nix/src/sys/ioctl/bsd.rs102
-rw-r--r--third_party/rust/nix/src/sys/ioctl/linux.rs140
-rw-r--r--third_party/rust/nix/src/sys/ioctl/mod.rs778
-rw-r--r--third_party/rust/nix/src/sys/memfd.rs20
-rw-r--r--third_party/rust/nix/src/sys/mman.rs296
-rw-r--r--third_party/rust/nix/src/sys/mod.rs90
-rw-r--r--third_party/rust/nix/src/sys/pthread.rs13
-rw-r--r--third_party/rust/nix/src/sys/ptrace/bsd.rs170
-rw-r--r--third_party/rust/nix/src/sys/ptrace/linux.rs402
-rw-r--r--third_party/rust/nix/src/sys/ptrace/mod.rs22
-rw-r--r--third_party/rust/nix/src/sys/quota.rs274
-rw-r--r--third_party/rust/nix/src/sys/reboot.rs45
-rw-r--r--third_party/rust/nix/src/sys/select.rs335
-rw-r--r--third_party/rust/nix/src/sys/sendfile.rs200
-rw-r--r--third_party/rust/nix/src/sys/signal.rs984
-rw-r--r--third_party/rust/nix/src/sys/signalfd.rs170
-rw-r--r--third_party/rust/nix/src/sys/socket/addr.rs1408
-rw-r--r--third_party/rust/nix/src/sys/socket/mod.rs1204
-rw-r--r--third_party/rust/nix/src/sys/socket/sockopt.rs631
-rw-r--r--third_party/rust/nix/src/sys/stat.rs286
-rw-r--r--third_party/rust/nix/src/sys/statfs.rs20
-rw-r--r--third_party/rust/nix/src/sys/statvfs.rs161
-rw-r--r--third_party/rust/nix/src/sys/sysinfo.rs73
-rw-r--r--third_party/rust/nix/src/sys/termios.rs1108
-rw-r--r--third_party/rust/nix/src/sys/time.rs573
-rw-r--r--third_party/rust/nix/src/sys/uio.rs195
-rw-r--r--third_party/rust/nix/src/sys/utsname.rs68
-rw-r--r--third_party/rust/nix/src/sys/wait.rs239
-rw-r--r--third_party/rust/nix/src/ucontext.rs40
-rw-r--r--third_party/rust/nix/src/unistd.rs2313
-rw-r--r--third_party/rust/nix/test/sys/mod.rs36
-rw-r--r--third_party/rust/nix/test/sys/test_aio.rs654
-rw-r--r--third_party/rust/nix/test/sys/test_aio_drop.rs32
-rw-r--r--third_party/rust/nix/test/sys/test_epoll.rs24
-rw-r--r--third_party/rust/nix/test/sys/test_ioctl.rs334
-rw-r--r--third_party/rust/nix/test/sys/test_lio_listio_resubmit.rs111
-rw-r--r--third_party/rust/nix/test/sys/test_pthread.rs15
-rw-r--r--third_party/rust/nix/test/sys/test_ptrace.rs107
-rw-r--r--third_party/rust/nix/test/sys/test_select.rs54
-rw-r--r--third_party/rust/nix/test/sys/test_signal.rs98
-rw-r--r--third_party/rust/nix/test/sys/test_signalfd.rs25
-rw-r--r--third_party/rust/nix/test/sys/test_socket.rs685
-rw-r--r--third_party/rust/nix/test/sys/test_sockopt.rs40
-rw-r--r--third_party/rust/nix/test/sys/test_sysinfo.rs18
-rw-r--r--third_party/rust/nix/test/sys/test_termios.rs136
-rw-r--r--third_party/rust/nix/test/sys/test_uio.rs241
-rw-r--r--third_party/rust/nix/test/sys/test_wait.rs104
-rw-r--r--third_party/rust/nix/test/test.rs80
-rw-r--r--third_party/rust/nix/test/test_dir.rs46
-rw-r--r--third_party/rust/nix/test/test_fcntl.rs143
-rw-r--r--third_party/rust/nix/test/test_kmod/hello_mod/Makefile7
-rw-r--r--third_party/rust/nix/test/test_kmod/hello_mod/hello.c26
-rw-r--r--third_party/rust/nix/test/test_kmod/mod.rs152
-rw-r--r--third_party/rust/nix/test/test_mount.rs238
-rw-r--r--third_party/rust/nix/test/test_mq.rs152
-rw-r--r--third_party/rust/nix/test/test_net.rs12
-rw-r--r--third_party/rust/nix/test/test_nix_path.rs0
-rw-r--r--third_party/rust/nix/test/test_poll.rs68
-rw-r--r--third_party/rust/nix/test/test_pty.rs203
-rw-r--r--third_party/rust/nix/test/test_ptymaster_drop.rs21
-rw-r--r--third_party/rust/nix/test/test_sendfile.rs129
-rw-r--r--third_party/rust/nix/test/test_stat.rs260
-rw-r--r--third_party/rust/nix/test/test_unistd.rs576
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>(),
+ &regs 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(), &times[0])
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Change the access and modification times of a file without following symlinks.
+///
+/// `lutimes(path, times)` is identical to
+/// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former
+/// is a deprecated API so prefer using the latter if the platforms you care
+/// about support it.
+///
+/// # References
+///
+/// [lutimes(2)](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(), &times[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, &times[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(),
+ &times[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) -> &timespec {
+ &self.0
+ }
+}
+
+impl fmt::Debug for TimeSpec {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.debug_struct("TimeSpec")
+ .field("tv_sec", &self.tv_sec())
+ .field("tv_nsec", &self.tv_nsec())
+ .finish()
+ }
+}
+
+impl 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
+ );
+}