summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/nix
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
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/CHANGELOG.md1696
-rw-r--r--third_party/rust/nix/Cargo.toml147
-rw-r--r--third_party/rust/nix/LICENSE21
-rw-r--r--third_party/rust/nix/README.md120
-rw-r--r--third_party/rust/nix/src/dir.rs282
-rw-r--r--third_party/rust/nix/src/env.rs64
-rw-r--r--third_party/rust/nix/src/errno.rs3380
-rw-r--r--third_party/rust/nix/src/fcntl.rs993
-rw-r--r--third_party/rust/nix/src/features.rs128
-rw-r--r--third_party/rust/nix/src/ifaddrs.rs213
-rw-r--r--third_party/rust/nix/src/kmod.rs128
-rw-r--r--third_party/rust/nix/src/lib.rs374
-rw-r--r--third_party/rust/nix/src/macros.rs331
-rw-r--r--third_party/rust/nix/src/mount/bsd.rs453
-rw-r--r--third_party/rust/nix/src/mount/linux.rs163
-rw-r--r--third_party/rust/nix/src/mount/mod.rs26
-rw-r--r--third_party/rust/nix/src/mqueue.rs358
-rw-r--r--third_party/rust/nix/src/net/if_.rs471
-rw-r--r--third_party/rust/nix/src/net/mod.rs4
-rw-r--r--third_party/rust/nix/src/poll.rs233
-rw-r--r--third_party/rust/nix/src/pty.rs357
-rw-r--r--third_party/rust/nix/src/sched.rs332
-rw-r--r--third_party/rust/nix/src/sys/aio.rs1245
-rw-r--r--third_party/rust/nix/src/sys/epoll.rs250
-rw-r--r--third_party/rust/nix/src/sys/event.rs525
-rw-r--r--third_party/rust/nix/src/sys/eventfd.rs17
-rw-r--r--third_party/rust/nix/src/sys/inotify.rs248
-rw-r--r--third_party/rust/nix/src/sys/ioctl/bsd.rs129
-rw-r--r--third_party/rust/nix/src/sys/ioctl/linux.rs168
-rw-r--r--third_party/rust/nix/src/sys/ioctl/mod.rs786
-rw-r--r--third_party/rust/nix/src/sys/memfd.rs65
-rw-r--r--third_party/rust/nix/src/sys/mman.rs607
-rw-r--r--third_party/rust/nix/src/sys/mod.rs231
-rw-r--r--third_party/rust/nix/src/sys/personality.rs96
-rw-r--r--third_party/rust/nix/src/sys/prctl.rs208
-rw-r--r--third_party/rust/nix/src/sys/pthread.rs43
-rw-r--r--third_party/rust/nix/src/sys/ptrace/bsd.rs195
-rw-r--r--third_party/rust/nix/src/sys/ptrace/linux.rs560
-rw-r--r--third_party/rust/nix/src/sys/ptrace/mod.rs25
-rw-r--r--third_party/rust/nix/src/sys/quota.rs337
-rw-r--r--third_party/rust/nix/src/sys/reboot.rs48
-rw-r--r--third_party/rust/nix/src/sys/resource.rs447
-rw-r--r--third_party/rust/nix/src/sys/select.rs554
-rw-r--r--third_party/rust/nix/src/sys/sendfile.rs291
-rw-r--r--third_party/rust/nix/src/sys/signal.rs1557
-rw-r--r--third_party/rust/nix/src/sys/signalfd.rs175
-rw-r--r--third_party/rust/nix/src/sys/socket/addr.rs2685
-rw-r--r--third_party/rust/nix/src/sys/socket/mod.rs2465
-rw-r--r--third_party/rust/nix/src/sys/socket/sockopt.rs1470
-rw-r--r--third_party/rust/nix/src/sys/stat.rs480
-rw-r--r--third_party/rust/nix/src/sys/statfs.rs853
-rw-r--r--third_party/rust/nix/src/sys/statvfs.rs172
-rw-r--r--third_party/rust/nix/src/sys/sysinfo.rs83
-rw-r--r--third_party/rust/nix/src/sys/termios.rs1259
-rw-r--r--third_party/rust/nix/src/sys/time.rs812
-rw-r--r--third_party/rust/nix/src/sys/timer.rs187
-rw-r--r--third_party/rust/nix/src/sys/timerfd.rs222
-rw-r--r--third_party/rust/nix/src/sys/uio.rs225
-rw-r--r--third_party/rust/nix/src/sys/utsname.rs85
-rw-r--r--third_party/rust/nix/src/sys/wait.rs393
-rw-r--r--third_party/rust/nix/src/time.rs283
-rw-r--r--third_party/rust/nix/src/ucontext.rs47
-rw-r--r--third_party/rust/nix/src/unistd.rs3977
-rw-r--r--third_party/rust/nix/test/common/mod.rs149
-rw-r--r--third_party/rust/nix/test/sys/mod.rs60
-rw-r--r--third_party/rust/nix/test/sys/test_aio.rs624
-rw-r--r--third_party/rust/nix/test/sys/test_aio_drop.rs35
-rw-r--r--third_party/rust/nix/test/sys/test_epoll.rs26
-rw-r--r--third_party/rust/nix/test/sys/test_inotify.rs65
-rw-r--r--third_party/rust/nix/test/sys/test_ioctl.rs376
-rw-r--r--third_party/rust/nix/test/sys/test_mman.rs122
-rw-r--r--third_party/rust/nix/test/sys/test_prctl.rs125
-rw-r--r--third_party/rust/nix/test/sys/test_pthread.rs22
-rw-r--r--third_party/rust/nix/test/sys/test_ptrace.rs275
-rw-r--r--third_party/rust/nix/test/sys/test_select.rs82
-rw-r--r--third_party/rust/nix/test/sys/test_signal.rs143
-rw-r--r--third_party/rust/nix/test/sys/test_signalfd.rs27
-rw-r--r--third_party/rust/nix/test/sys/test_socket.rs2687
-rw-r--r--third_party/rust/nix/test/sys/test_sockopt.rs448
-rw-r--r--third_party/rust/nix/test/sys/test_stat.rs29
-rw-r--r--third_party/rust/nix/test/sys/test_sysinfo.rs20
-rw-r--r--third_party/rust/nix/test/sys/test_termios.rs107
-rw-r--r--third_party/rust/nix/test/sys/test_timerfd.rs69
-rw-r--r--third_party/rust/nix/test/sys/test_uio.rs277
-rw-r--r--third_party/rust/nix/test/sys/test_wait.rs257
-rw-r--r--third_party/rust/nix/test/test.rs120
-rw-r--r--third_party/rust/nix/test/test_clearenv.rs9
-rw-r--r--third_party/rust/nix/test/test_dir.rs65
-rw-r--r--third_party/rust/nix/test/test_fcntl.rs574
-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.rs188
-rw-r--r--third_party/rust/nix/test/test_mount.rs267
-rw-r--r--third_party/rust/nix/test/test_mq.rs223
-rw-r--r--third_party/rust/nix/test/test_net.rs19
-rw-r--r--third_party/rust/nix/test/test_nix_path.rs1
-rw-r--r--third_party/rust/nix/test/test_nmount.rs49
-rw-r--r--third_party/rust/nix/test/test_poll.rs82
-rw-r--r--third_party/rust/nix/test/test_pty.rs276
-rw-r--r--third_party/rust/nix/test/test_resource.rs34
-rw-r--r--third_party/rust/nix/test/test_sched.rs39
-rw-r--r--third_party/rust/nix/test/test_sendfile.rs217
-rw-r--r--third_party/rust/nix/test/test_stat.rs421
-rw-r--r--third_party/rust/nix/test/test_time.rs59
-rw-r--r--third_party/rust/nix/test/test_timer.rs102
-rw-r--r--third_party/rust/nix/test/test_unistd.rs1393
107 files changed, 44976 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..d48e123da2
--- /dev/null
+++ b/third_party/rust/nix/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"92468d08ccd20acf93bac25d983dbaedbd6dafbfdebf45d670a557e1dd993650","Cargo.toml":"8e68a73dcb2ac8fd7a01b714c3c08c0148d0cbeb1b8a2bbb30ff10cf0332b505","LICENSE":"66e3ee1fa7f909ad3c612d556f2a0cdabcd809ad6e66f3b0605015ac64841b70","README.md":"693748b472aa697105dbaf37ad95e41b9b3ed6480533e03a5a93b10ec70c987a","src/dir.rs":"d40e2bc1df553898e718de552ddb57a160b9957b7d6a0f7e27e3e7fe6af536ab","src/env.rs":"028bc5e20139ebba418a655a2978a53335dc7680bf1de43d2c8333dd72cfa5c4","src/errno.rs":"3c2935cc3238c13a545ab8ceb6a8aa9fd03a9cbf72be041e9ea032f6ee19c2f4","src/fcntl.rs":"8cda1abc82c562b2340b3d27176b674b65d0e1c4bf888e7700c6b50e63c3bf0d","src/features.rs":"5381d43a7759c0bf4a26fc25c602d507beafa85282764bdbae6eff59f98fd16d","src/ifaddrs.rs":"377865eb48040d28c392a1aec0221320108e3392ea285d23405ae2cfa5c54b20","src/kmod.rs":"d2ef26c35db790e589f6418c1631aa48b01d4ea5674e050c12d1cfa7a18681ef","src/lib.rs":"571f418caf6a9646939ae4976ec4bde5f9325e9b4320715c9586590b9d8d4963","src/macros.rs":"73ab3b56f4b7cd08d3c70f035cd74bcbdac17ff05946adc875e05fd93280eb7e","src/mount/bsd.rs":"9f12470d33d9b3d18fdba5a10952f6e76b254df3d8794f296437d266db9623c4","src/mount/linux.rs":"6b0ea0c4598f5537e2dc57bdad6d3acc86e29d258aef3a639e1a2308f38bdb0e","src/mount/mod.rs":"ba9f60eb831224ab73bdd87e00e15d13b9ce9efb70b18bf8f3fe60406d522b3e","src/mqueue.rs":"0c78ef1a52c2df05a48b390707764dfca27c056033e01d604a1aa91058c7e9b4","src/net/if_.rs":"fa7413a9676e552b3fa0576081b83ac91278bb0d7240b7cdd962ea911336bba0","src/net/mod.rs":"577f70170e53d4a6de1abb70bf8f1031ec3e65c0e63ef5fcf05c907125e7ac17","src/poll.rs":"8b26383af51ff0733e07e6a5a2e174462e8849b1118ca12bdf3540d40a298602","src/pty.rs":"6a1122c4e7ea236cc4e1e36e2eb587eddf1e323edacdbcdd3b3c1619a60eedbf","src/sched.rs":"217055e50a54398e5b75a247e1be9a81a764aadd0267bcf6367f633e9b277f84","src/sys/aio.rs":"3e2e93dff85180a98b2b09b1ea926f89ee17d235c60fd2a96adf0ff7db601e5c","src/sys/epoll.rs":"5c45733ea19802443cc3f9e21766013d4ff0125a52dbf5f5a13f10ff19914e1e","src/sys/event.rs":"9b3b02c42f471e3d9f974be1b60aab11ff4d1fc79692c3d40991f0c22ca9ea83","src/sys/eventfd.rs":"22c1205c948aeb29f7a8a0f298644eb393778585ecbd2091d40559e0ed2de06b","src/sys/inotify.rs":"68b80856937286a23917638ed8f5fc057e396fe7c4f4d1178865e720ba7dba95","src/sys/ioctl/bsd.rs":"bbd02e30b0a78c1cb22777d9b00cfcbba9c68505cffc06118ac68474cf6fea39","src/sys/ioctl/linux.rs":"2aec4b3b3beb8119b9bb0c8001b9bb66cba3f2ff778c432e62f2da4243787184","src/sys/ioctl/mod.rs":"80e683efac7d1e2bd88680f028370f7c778e6a5097cae7845566bc8f43f4ff4f","src/sys/memfd.rs":"e2653a3c476585e993d9e8551cd0f991ba141c6bb46d9ece7fa6fd5cdeb862ae","src/sys/mman.rs":"4604656656a061606f99ff2f117c8f7463cd87aedaa4b3de5de863056bec5499","src/sys/mod.rs":"f5a5f1b51bbd2c865bbda2652bf0d2fd38e0773bd1a37460869c5a7008d27f03","src/sys/personality.rs":"8fbd8b522b8be4591a4cf25cba023884d3ad39b26666708d43eb79b81bf1c203","src/sys/prctl.rs":"d5b695099d4ac1b44e552988f488d7b88d285e68968ac29b93c153da52acd2bf","src/sys/pthread.rs":"258cdf7ff0b61a4afa6d228109e4cb4fb88d859bb8dfe6c959d95130fb010906","src/sys/ptrace/bsd.rs":"4c590d8f023ff52f396f8b6f2150c08e5c9486d3088d9c173db33a70d616b800","src/sys/ptrace/linux.rs":"b14114c4cbefd58f9df48e93e47d8f82992167942d4e683018cd31ceed184bc6","src/sys/ptrace/mod.rs":"3b5e4cc9cf447e989f40c73cb1951a4705322852009023c5a3d7e39ec1e9c39b","src/sys/quota.rs":"421ff70a0749dd0cea37a22a971bf3e9474f627bfa0e37874d9893787271996b","src/sys/reboot.rs":"eacdf57694a6629fb05787e16450446102a62818274495f2ad4e445807d09221","src/sys/resource.rs":"84eba41f288c97b4ab9060a6046d0d34e66a1f8e12655032c60a703280cca832","src/sys/select.rs":"2520086330bc1978a7db183bee92d96ba2711a4505839179a5f082ad555045b2","src/sys/sendfile.rs":"2075cea1ea5967b5de18e5cecb94bc041ff2675f7eb9e223051aa8a52dd8f733","src/sys/signal.rs":"39492bd9a98a48616a2f66a717def527574c6429251b533d8406d2b25cc2cb3f","src/sys/signalfd.rs":"af9eee01d712e48609fddf6b0ec332cd5f80b9940081b4c2e16cc338e959b5f3","src/sys/socket/addr.rs":"1b6f4e1ca16004cd5fa511264076b2c24ccc9cf3d7727157a0fabc62c47fe94a","src/sys/socket/mod.rs":"e65112b3b54905166b7a412fd9811e1e123c5eb1b3b46286937082fa6e86973d","src/sys/socket/sockopt.rs":"386bc08c0faf49a9d0ef99c70538c66b7d426d3deeb7b0ebedee1ef917d7bafc","src/sys/stat.rs":"3928598e6428d7e44b42d36aeb59ac353eaf0270801a6ac72c511804c5fdf358","src/sys/statfs.rs":"852c7e68c094ea8b1f978ee811c1e0bf6a38661c7a07114527697e7aae584dff","src/sys/statvfs.rs":"f699280f3ee2645ce39631d404355e7b49818849c4afa0bdf4f5020931cb1bef","src/sys/sysinfo.rs":"b4519b1ca091c9dbe94d2a6fd6304944bf3df5626973d2c6884022559706f0d9","src/sys/termios.rs":"7c0d9f4bc0062a510cf0f799264f807530c3d1e9e23a0d405d595f395ad7e01c","src/sys/time.rs":"c991d69a892cd7201c53d1399533e45802d581a1afa109016014aa9872d8db53","src/sys/timer.rs":"8c10f0e7cfac857ad00460be30bc68b957909cc9296e70718d3b5d4a0babafde","src/sys/timerfd.rs":"8958120030ed7bb58028b75e7e1829d4d231fa0da2c65f9335df7b7f1e0f6074","src/sys/uio.rs":"0ed960748eb4a85ce8f8413ab478d451f4460e85130cc3b803c85d72c057a529","src/sys/utsname.rs":"0cdda0cc111caaa0e4ebe2d4588bdc825d878e5bcb7a9136073b15f87a20e11f","src/sys/wait.rs":"c4c19ce13ea96c47fd51e227a1982d2aaafdb6b75edd726159848dd617f70da8","src/time.rs":"d4e0872361a57810837f5bd790cbca3a2b9db1ac4694a3c52d1564ad3532d3be","src/ucontext.rs":"b8f2e04757a9c2bc38c3b1e259d3a013da8a730fe9bfbe5487637395681b43d3","src/unistd.rs":"e9d24b6490d0578fc3e31200023e131df9471c09b7553b452a9ad76399eea753","test/common/mod.rs":"1d7e28e3635754664cd056f3a1079232ff5c118df619e1d0551a9972eb0b3cd6","test/sys/mod.rs":"87b2891d83067ff21f72b8ff7fde3019dc45b6877282ac278b6da151de45c7a7","test/sys/test_aio.rs":"ea9c0af6632739439d7f8187d2c82aeca7ac177d817210815b1a3edcd7a84e9b","test/sys/test_aio_drop.rs":"614070155fa16a979b7341d001639c5ce24a1d6f632c3abce45a5a6d49c4039b","test/sys/test_epoll.rs":"c30b08d665a1fe7d7a04fe51d50ec78fc74c2ac707ae0f95f82104d5c76ceaf2","test/sys/test_inotify.rs":"a141b9a995892547b51ceeb6761a70a6b86d37e8f38d13ea2c497b81b4b0f49f","test/sys/test_ioctl.rs":"00ccc5afb665e533a0a4b6d6a6be438bcaea19fce335390feef4e91d17b3036c","test/sys/test_mman.rs":"fe9019927ed3ca51d2d59d3305a53cc31797a1460044d24e2631f120c9289552","test/sys/test_prctl.rs":"9c3d0fb16a41c3fd80541b313c2bb63de75634ad4711a71af106e58b0cec9ea8","test/sys/test_pthread.rs":"ace36a2f5587f1874854281b4fd84e4e4d892a1e3c5cc38ced57975739522ad6","test/sys/test_ptrace.rs":"0385eebc8b1b8c72f655b745769decd9143ad83018198375982da0896310456b","test/sys/test_select.rs":"2843bc2484d51ba335567cc50bbc4eb6848ac6c6702ced42b177ffe04b49f7f3","test/sys/test_signal.rs":"19c267ffe9a37452719c2b030c62ab8e123d20e3f6ba4da6902375283e3b9593","test/sys/test_signalfd.rs":"0e1060143e2612c490bc3d0168d0bbb042ef55e3f1d91d2578b9e42e4310a14d","test/sys/test_socket.rs":"de724f58d1d703d28be1d4e35fd72df41f835082cdcb4fb579330d0be31dc74f","test/sys/test_sockopt.rs":"4a7fbb08719ae99803280f8af94e134ad90d92bf3ca4fbee0ef0ab398aadecea","test/sys/test_stat.rs":"6630a28217fd708bb84cd4f7e7101836b74f2420f9888923fdab664ccc331c1d","test/sys/test_sysinfo.rs":"ffd49bc96375914a2c4a4a59730cae8072f85771e2c4a80d3403df38d967e272","test/sys/test_termios.rs":"f38dfe45ab4ac9760b1d8c49e18da900d544927080a4a0bbb02b0c854c130455","test/sys/test_timerfd.rs":"cfed3abf58118611d08f6985251a7739cff67108e11214222a1d2394a3a026ce","test/sys/test_uio.rs":"90d973858f3e303c9fb99bc49d8d9a2e184be17e4e771cf3682af80b2ebd1533","test/sys/test_wait.rs":"6fd59fffeeb09ff620c359baefd062ba777598982b6cb001ccc07b6bc7605493","test/test.rs":"58a302e9055555806942c35e0edd0aaa63b2ebb3205c9a7e29491b726a5e2abe","test/test_clearenv.rs":"45ca548035b3c20ec87314715feaba2be973709a635d85b8cde46fd1d9f1ecd4","test/test_dir.rs":"ae3c11c58cb06da6557aa2a839c6653c54cd7724283fffe9df5a5d3feabdd89a","test/test_fcntl.rs":"7a23635451ed4a7b061cdc3015723c803c901e665e2beffde726b7f20ddf61d2","test/test_kmod/hello_mod/Makefile":"0219f7bce0603f97d997fb377ca071966c90333ecc665e78a54dfeb97a9c811b","test/test_kmod/hello_mod/hello.c":"bcac6b19c5bd807e1f3878c15e426acc85785a8ade9840c3bb4d068635c9188c","test/test_kmod/mod.rs":"b4ae25841c2f06f32de9f1acd8230eeccd7095721302ebe78ad454e4e4f9c783","test/test_mount.rs":"7d04b7e0f0f56e8129a0e68a6a338d7d46fdedde863dae4732d3c899e7864c66","test/test_mq.rs":"84dffb2201e2a4bb19f476fa7c23bbc2615453d27e5b958d3841ff371b6faa81","test/test_net.rs":"f2912327ebb2a3d37e6cff02a5ac3106cf889cc5c74404db4ef0034059ba26f1","test/test_nix_path.rs":"01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b","test/test_nmount.rs":"d6c112547bb80968170b5497cda4b6cbf69dabec6f51d494bd52298995ceff18","test/test_poll.rs":"d6eef82848734e47cc9e1f1ce521e35587e63f69876baea660ef22eebc2b1ea6","test/test_pty.rs":"4184e446af7a365ead628596cd77ad168c835a5dea6abca3ee614d86a4412dda","test/test_resource.rs":"40aef790ab745cec31a4b333d2ca406b462aa9bdf4a6d3756371e498b8d51e9a","test/test_sched.rs":"c4579bd376fab8816e63b07fa9ace31dc08e63ebb7c855a2c450698090d1d1e8","test/test_sendfile.rs":"f7d52f6aa680b5667b966d5c046917ba43924e9b9fca6adbcf68479459e06366","test/test_stat.rs":"c407ca47a5258750076d041afad2f6add4c3563be36628bde1c5b314f5d0765d","test/test_time.rs":"f7a21b1e279e60e84909d5dadda97ded66d3326b131fe317badf9af0a1b50335","test/test_timer.rs":"3ae20d364f075d2811f3ff94eda9886682cc21d8807656007d2464fe36d1e361","test/test_unistd.rs":"3ef7b335639747b6d57a04c2264a38ad379b680cebc0b8d8b8e247610630e58a"},"package":"2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"} \ No newline at end of file
diff --git a/third_party/rust/nix/CHANGELOG.md b/third_party/rust/nix/CHANGELOG.md
new file mode 100644
index 0000000000..3a171afd68
--- /dev/null
+++ b/third_party/rust/nix/CHANGELOG.md
@@ -0,0 +1,1696 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+This project adheres to [Semantic Versioning](https://semver.org/).
+
+## [0.27.1] - 2023-08-28
+
+### Fixed
+
+- Fixed generating the documentation on docs.rs.
+ ([#2111](https://github.com/nix-rust/nix/pull/2111))
+
+## [0.27.0] - 2023-08-28
+### Added
+- Added `AT_EACCESS` to `AtFlags` on all platforms but android
+ ([#1995](https://github.com/nix-rust/nix/pull/1995))
+- Add `PF_ROUTE` to `SockType` on macOS, iOS, all of the BSDs, Fuchsia, Haiku, Illumos.
+ ([#1867](https://github.com/nix-rust/nix/pull/1867))
+- Added `nix::ucontext` module on `aarch64-unknown-linux-gnu`.
+ (#[1662](https://github.com/nix-rust/nix/pull/1662))
+- Added `CanRaw` to `SockProtocol` and `CanBcm` as a separate `SocProtocol` constant.
+ ([#1912](https://github.com/nix-rust/nix/pull/1912))
+- Added `Generic` and `NFLOG` to `SockProtocol`.
+ ([#2092](https://github.com/nix-rust/nix/pull/2092))
+- Added `mq_timedreceive` to `::nix::mqueue`.
+ ([#1966])(https://github.com/nix-rust/nix/pull/1966)
+- Added `LocalPeerPid` to `nix::sys::socket::sockopt` for macOS. ([#1967](https://github.com/nix-rust/nix/pull/1967))
+- Added `TFD_TIMER_CANCEL_ON_SET` to `::nix::sys::time::TimerSetTimeFlags` on Linux and Android.
+ ([#2040](https://github.com/nix-rust/nix/pull/2040))
+- Added `SOF_TIMESTAMPING_OPT_ID` and `SOF_TIMESTAMPING_OPT_TSONLY` to `nix::sys::socket::TimestampingFlag`.
+ ([#2048](https://github.com/nix-rust/nix/pull/2048))
+- Enabled socket timestamping options on Android. ([#2077](https://github.com/nix-rust/nix/pull/2077))
+- Added vsock support for macOS ([#2056](https://github.com/nix-rust/nix/pull/2056))
+- Added `SO_SETFIB` and `SO_USER_COOKIE` to `nix::sys::socket::sockopt` for FreeBSD.
+ ([#2085](https://github.com/nix-rust/nix/pull/2085))
+- Added `SO_RTABLE` for OpenBSD and `SO_ACCEPTFILTER` for FreeBSD/NetBSD to `nix::sys::socket::sockopt`.
+ ([#2085](https://github.com/nix-rust/nix/pull/2085))
+- Added `MSG_WAITFORONE` to `MsgFlags` on Android, Fuchsia, Linux, NetBSD,
+ FreeBSD, OpenBSD, and Solaris.
+ ([#2014](https://github.com/nix-rust/nix/pull/2014))
+- Added `SO_TS_CLOCK` for FreeBSD to `nix::sys::socket::sockopt`.
+ ([#2093](https://github.com/nix-rust/nix/pull/2093))
+- Added support for prctl in Linux.
+ (#[1550](https://github.com/nix-rust/nix/pull/1550))
+- `nix::socket` and `nix::select` are now available on Redox.
+ ([#2012](https://github.com/nix-rust/nix/pull/2012))
+- Implemented AsFd, AsRawFd, FromRawFd, and IntoRawFd for `mqueue::MqdT`.
+ ([#2097](https://github.com/nix-rust/nix/pull/2097))
+- Add the ability to set `kevent_flags` on `SigEvent`.
+ ([#1731](https://github.com/nix-rust/nix/pull/1731))
+
+### Changed
+
+- All Cargo features have been removed from the default set. Users will need to
+ specify which features they depend on in their Cargo.toml.
+ ([#2091](https://github.com/nix-rust/nix/pull/2091))
+- Implemented I/O safety for many, but not all, of Nix's APIs. Many public
+ functions argument and return types have changed:
+ | Original Type | New Type |
+ | ------------- | --------------------- |
+ | AsRawFd | AsFd |
+ | RawFd | BorrowedFd or OwnedFd |
+
+ (#[1906](https://github.com/nix-rust/nix/pull/1906))
+- Use I/O safety with `copy_file_range`, and expose it on FreeBSD.
+ (#[1906](https://github.com/nix-rust/nix/pull/1906))
+- The MSRV is now 1.65
+ ([#1862](https://github.com/nix-rust/nix/pull/1862))
+ ([#2104](https://github.com/nix-rust/nix/pull/2104))
+- The epoll interface now uses a type.
+ ([#1882](https://github.com/nix-rust/nix/pull/1882))
+- With I/O-safe type applied in `pty::OpenptyResult` and `pty::ForkptyResult`,
+ users no longer need to manually close the file descriptors in these types.
+ ([#1921](https://github.com/nix-rust/nix/pull/1921))
+- Refactored `name` parameter of `mq_open` and `mq_unlink` to be generic over
+ `NixPath`.
+ ([#2102](https://github.com/nix-rust/nix/pull/2102)).
+- Made `clone` unsafe, like `fork`.
+ ([#1993](https://github.com/nix-rust/nix/pull/1993))
+
+### Removed
+
+- `sys::event::{kevent, kevent_ts}` are deprecated in favor of
+ `sys::kevent::Kqueue::kevent`, and `sys::event::kqueue` is deprecated in
+ favor of `sys::kevent::Kqueue::new`.
+ ([#1943](https://github.com/nix-rust/nix/pull/1943))
+- Removed deprecated IoVec API.
+ ([#1855](https://github.com/nix-rust/nix/pull/1855))
+- Removed deprecated net APIs.
+ ([#1861](https://github.com/nix-rust/nix/pull/1861))
+- `nix::sys::signalfd::signalfd` is deprecated. Use
+ `nix::sys::signalfd::SignalFd` instead.
+ ([#1938](https://github.com/nix-rust/nix/pull/1938))
+- Removed `SigEvent` support on Fuchsia, where it was unsound.
+ ([#2079](https://github.com/nix-rust/nix/pull/2079))
+- Removed `flock` from `::nix::fcntl` on Solaris.
+ ([#2082](https://github.com/nix-rust/nix/pull/2082))
+
+## [0.26.3] - 2023-08-27
+
+### Fixed
+- Fix: send `ETH_P_ALL` in htons format
+ ([#1925](https://github.com/nix-rust/nix/pull/1925))
+- Fix: `recvmsg` now sets the length of the received `sockaddr_un` field
+ correctly on Linux platforms. ([#2041](https://github.com/nix-rust/nix/pull/2041))
+- Fix potentially invalid conversions in
+ `SockaddrIn::from<std::net::SocketAddrV4>`,
+ `SockaddrIn6::from<std::net::SockaddrV6>`, `IpMembershipRequest::new`, and
+ `Ipv6MembershipRequest::new` with future Rust versions.
+ ([#2061](https://github.com/nix-rust/nix/pull/2061))
+- Fixed an incorrect lifetime returned from `recvmsg`.
+ ([#2095](https://github.com/nix-rust/nix/pull/2095))
+
+## [0.26.2] - 2023-01-18
+
+### Fixed
+
+- Fix `SockaddrIn6` bug that was swapping `flowinfo` and `scope_id` byte
+ ordering.
+ ([#1964](https://github.com/nix-rust/nix/pull/1964))
+
+## [0.26.1] - 2022-11-29
+### Fixed
+- Fix UB with `sys::socket::sockopt::SockType` using `SOCK_PACKET`.
+ ([#1821](https://github.com/nix-rust/nix/pull/1821))
+
+## [0.26.0] - 2022-11-29
+### Added
+
+- Added `SockaddrStorage::{as_unix_addr, as_unix_addr_mut}`
+ ([#1871](https://github.com/nix-rust/nix/pull/1871))
+- Added `MntFlags` and `unmount` on all of the BSDs.
+- Added `any()` and `all()` to `poll::PollFd`.
+ ([#1877](https://github.com/nix-rust/nix/pull/1877))
+- Add `MntFlags` and `unmount` on all of the BSDs.
+ ([#1849](https://github.com/nix-rust/nix/pull/1849))
+- Added a `Statfs::flags` method.
+ ([#1849](https://github.com/nix-rust/nix/pull/1849))
+- Added `NSFS_MAGIC` FsType on Linux and Android.
+ ([#1829](https://github.com/nix-rust/nix/pull/1829))
+- Added `sched_getcpu` on platforms that support it.
+ ([#1825](https://github.com/nix-rust/nix/pull/1825))
+- Added `sched_getaffinity` and `sched_setaffinity` on FreeBSD.
+ ([#1804](https://github.com/nix-rust/nix/pull/1804))
+- Added `line_discipline` field to `Termios` on Linux, Android and Haiku
+ ([#1805](https://github.com/nix-rust/nix/pull/1805))
+- Expose the memfd module on FreeBSD (memfd was added in FreeBSD 13)
+ ([#1808](https://github.com/nix-rust/nix/pull/1808))
+- Added `domainname` field of `UtsName` on Android and Linux
+ ([#1817](https://github.com/nix-rust/nix/pull/1817))
+- Re-export `RLIM_INFINITY` from `libc`
+ ([#1831](https://github.com/nix-rust/nix/pull/1831))
+- Added `syncfs(2)` on Linux
+ ([#1833](https://github.com/nix-rust/nix/pull/1833))
+- Added `faccessat(2)` on illumos
+ ([#1841](https://github.com/nix-rust/nix/pull/1841))
+- Added `eaccess()` on FreeBSD, DragonFly and Linux (glibc and musl).
+ ([#1842](https://github.com/nix-rust/nix/pull/1842))
+- Added `IP_TOS` `SO_PRIORITY` and `IPV6_TCLASS` sockopts for Linux
+ ([#1853](https://github.com/nix-rust/nix/pull/1853))
+- Added `new_unnamed` and `is_unnamed` for `UnixAddr` on Linux and Android.
+ ([#1857](https://github.com/nix-rust/nix/pull/1857))
+- Added `SockProtocol::Raw` for raw sockets
+ ([#1848](https://github.com/nix-rust/nix/pull/1848))
+- added `IP_MTU` (`IpMtu`) `IPPROTO_IP` sockopt on Linux and Android.
+ ([#1865](https://github.com/nix-rust/nix/pull/1865))
+
+### Changed
+
+- The MSRV is now 1.56.1
+ ([#1792](https://github.com/nix-rust/nix/pull/1792))
+- The `addr` argument of `sys::mman::mmap` is now of type `Option<NonZeroUsize>`.
+ ([#1870](https://github.com/nix-rust/nix/pull/1870))
+- The `length` argument of `sys::mman::mmap` is now of type `NonZeroUsize`.
+ ([#1873](https://github.com/nix-rust/nix/pull/1873))
+
+### Fixed
+
+- Fixed using `SockaddrStorage` to store a Unix-domain socket address on Linux.
+ ([#1871](https://github.com/nix-rust/nix/pull/1871))
+- Fix microsecond calculation for `TimeSpec`.
+ ([#1801](https://github.com/nix-rust/nix/pull/1801))
+- Fix `User::from_name` and `Group::from_name` panicking
+ when given a name containing a nul.
+ ([#1815](https://github.com/nix-rust/nix/pull/1815))
+- Fix `User::from_uid` and `User::from_name` crash on Android platform.
+ ([#1824](https://github.com/nix-rust/nix/pull/1824))
+- Workaround XNU bug causing netmasks returned by `getifaddrs` to misbehave.
+ ([#1788](https://github.com/nix-rust/nix/pull/1788))
+
+### Removed
+
+- Removed deprecated error constants and conversions.
+ ([#1860](https://github.com/nix-rust/nix/pull/1860))
+
+## [0.25.0] - 2022-08-13
+### Added
+
+- Added `faccessat`
+ ([#1780](https://github.com/nix-rust/nix/pull/1780))
+- Added `memfd` on Android.
+ (#[1773](https://github.com/nix-rust/nix/pull/1773))
+- Added `ETH_P_ALL` to `SockProtocol` enum
+ (#[1768](https://github.com/nix-rust/nix/pull/1768))
+- Added four non-standard Linux `SysconfVar` variants
+ (#[1761](https://github.com/nix-rust/nix/pull/1761))
+- Added const constructors for `TimeSpec` and `TimeVal`
+ (#[1760](https://github.com/nix-rust/nix/pull/1760))
+- Added `chflags`.
+ (#[1758](https://github.com/nix-rust/nix/pull/1758))
+- Added `aio_writev` and `aio_readv`.
+ (#[1713](https://github.com/nix-rust/nix/pull/1713))
+- impl `From<uid_t>` for `Uid` and `From<gid_t>` for `Gid`
+ (#[1727](https://github.com/nix-rust/nix/pull/1727))
+- impl `From<SockaddrIn>` for `std::net::SocketAddrV4` and
+ impl `From<SockaddrIn6>` for `std::net::SocketAddrV6`.
+ (#[1711](https://github.com/nix-rust/nix/pull/1711))
+- Added support for the `x86_64-unknown-haiku` target.
+ (#[1703](https://github.com/nix-rust/nix/pull/1703))
+- Added `ptrace::read_user` and `ptrace::write_user` for Linux.
+ (#[1697](https://github.com/nix-rust/nix/pull/1697))
+- Added `getrusage` and helper types `UsageWho` and `Usage`
+ (#[1747](https://github.com/nix-rust/nix/pull/1747))
+- Added the `DontRoute` SockOpt
+ (#[1752](https://github.com/nix-rust/nix/pull/1752))
+- Added `signal::SigSet::from_sigset_t_unchecked()`.
+ (#[1741](https://github.com/nix-rust/nix/pull/1741))
+- Added the `Ipv4OrigDstAddr` sockopt and control message.
+ (#[1772](https://github.com/nix-rust/nix/pull/1772))
+- Added the `Ipv6OrigDstAddr` sockopt and control message.
+ (#[1772](https://github.com/nix-rust/nix/pull/1772))
+- Added the `Ipv4SendSrcAddr` control message.
+ (#[1776](https://github.com/nix-rust/nix/pull/1776))
+
+### Changed
+
+- Reimplemented sendmmsg/recvmmsg to avoid allocations and with better API
+ (#[1744](https://github.com/nix-rust/nix/pull/1744))
+
+- Rewrote the aio module. The new module:
+ * Does more type checking at compile time rather than runtime.
+ * Gives the caller control over whether and when to `Box` an aio operation.
+ * Changes the type of the `priority` arguments to `i32`.
+ * Changes the return type of `aio_return` to `usize`.
+ (#[1713](https://github.com/nix-rust/nix/pull/1713))
+- `nix::poll::ppoll`: `sigmask` parameter is now optional.
+ (#[1739](https://github.com/nix-rust/nix/pull/1739))
+- Changed `gethostname` to return an owned `OsString`.
+ (#[1745](https://github.com/nix-rust/nix/pull/1745))
+- `signal:SigSet` is now marked as `repr(transparent)`.
+ (#[1741](https://github.com/nix-rust/nix/pull/1741))
+
+### Removed
+
+- Removed support for resubmitting partially complete `lio_listio` operations.
+ It was too complicated, and didn't fit Nix's theme of zero-cost abstractions.
+ Instead, it can be reimplemented downstream.
+ (#[1713](https://github.com/nix-rust/nix/pull/1713))
+
+## [0.24.2] - 2022-07-17
+### Fixed
+
+- Fixed buffer overflow in `nix::sys::socket::recvfrom`.
+ (#[1763](https://github.com/nix-rust/nix/pull/1763))
+- Enabled `SockaddrStorage::{as_link_addr, as_link_addr_mut}` for Linux-like
+ operating systems.
+ (#[1729](https://github.com/nix-rust/nix/pull/1729))
+- Fixed `SockaddrLike::from_raw` implementations for `VsockAddr` and
+ `SysControlAddr`.
+ (#[1736](https://github.com/nix-rust/nix/pull/1736))
+
+## [0.24.1] - 2022-04-22
+### Fixed
+
+- Fixed `UnixAddr::size` on Linux-based OSes.
+ (#[1702](https://github.com/nix-rust/nix/pull/1702))
+
+## [0.24.0] - 2022-04-21
+### Added
+
+- Added fine-grained features flags. Most Nix functionality can now be
+ conditionally enabled. By default, all features are enabled.
+ (#[1611](https://github.com/nix-rust/nix/pull/1611))
+- Added statfs FS type magic constants for `target_os = "android"`
+ and synced constants with libc v0.2.121.
+ (#[1690](https://github.com/nix-rust/nix/pull/1690))
+- Added `fexecve` on DragonFly.
+ (#[1577](https://github.com/nix-rust/nix/pull/1577))
+- `sys::uio::IoVec` is now `Send` and `Sync`
+ (#[1582](https://github.com/nix-rust/nix/pull/1582))
+- Added `EPOLLEXCLUSIVE` on Android.
+ (#[1567](https://github.com/nix-rust/nix/pull/1567))
+- Added `fdatasync` for FreeBSD, Fuchsia, NetBSD, and OpenBSD.
+ (#[1581](https://github.com/nix-rust/nix/pull/1581))
+- Added `sched_setaffinity` and `sched_getaffinity` on DragonFly.
+ (#[1537](https://github.com/nix-rust/nix/pull/1537))
+- Added `posix_fallocate` on DragonFly.
+ (#[1621](https://github.com/nix-rust/nix/pull/1621))
+- Added `SO_TIMESTAMPING` support
+ (#[1547](https://github.com/nix-rust/nix/pull/1547))
+- Added getter methods to `MqAttr` struct
+ (#[1619](https://github.com/nix-rust/nix/pull/1619))
+- Added the `TxTime` sockopt and control message.
+ (#[1564](https://github.com/nix-rust/nix/pull/1564))
+- Added POSIX per-process timer support
+ (#[1622](https://github.com/nix-rust/nix/pull/1622))
+- Added `sendfile` on DragonFly.
+ (#[1615](https://github.com/nix-rust/nix/pull/1615))
+- Added `UMOUNT_NOFOLLOW`, `FUSE_SUPER_MAGIC` on Linux.
+ (#[1634](https://github.com/nix-rust/nix/pull/1634))
+- Added `getresuid`, `setresuid`, `getresgid`, and `setresgid` on DragonFly, FreeBSD, and OpenBSD.
+ (#[1628](https://github.com/nix-rust/nix/pull/1628))
+- Added `MAP_FIXED_NOREPLACE` on Linux.
+ (#[1636](https://github.com/nix-rust/nix/pull/1636))
+- Added `fspacectl` on FreeBSD
+ (#[1640](https://github.com/nix-rust/nix/pull/1640))
+- Added `accept4` on DragonFly, Emscripten, Fuchsia, Illumos, and NetBSD.
+ (#[1654](https://github.com/nix-rust/nix/pull/1654))
+- Added `AsRawFd` implementation on `OwningIter`.
+ (#[1563](https://github.com/nix-rust/nix/pull/1563))
+- Added `process_vm_readv` and `process_vm_writev` on Android.
+ (#[1557](https://github.com/nix-rust/nix/pull/1557))
+- Added `nix::ucontext` module on s390x.
+ (#[1662](https://github.com/nix-rust/nix/pull/1662))
+- Implemented `Extend`, `FromIterator`, and `IntoIterator` for `SigSet` and
+ added `SigSet::iter` and `SigSetIter`.
+ (#[1553](https://github.com/nix-rust/nix/pull/1553))
+- Added `ENOTRECOVERABLE` and `EOWNERDEAD` error codes on DragonFly.
+ (#[1665](https://github.com/nix-rust/nix/pull/1665))
+- Implemented `Read` and `Write` for `&PtyMaster`
+ (#[1664](https://github.com/nix-rust/nix/pull/1664))
+- Added `MSG_NOSIGNAL` for Android, Dragonfly, FreeBSD, Fuchsia, Haiku, Illumos, Linux, NetBSD, OpenBSD and Solaris.
+ (#[1670](https://github.com/nix-rust/nix/pull/1670))
+- Added `waitid`.
+ (#[1584](https://github.com/nix-rust/nix/pull/1584))
+- Added `Ipv6DontFrag` for android, iOS, linux and macOS.
+- Added `IpDontFrag` for iOS, macOS.
+ (#[1692](https://github.com/nix-rust/nix/pull/1692))
+
+### Changed
+
+- `mqueue` functions now operate on a distinct type, `nix::mqueue::MqdT`.
+ Accessors take this type by reference, not by value.
+ (#[1639](https://github.com/nix-rust/nix/pull/1639))
+- Removed `SigSet::extend` in favor of `<SigSet as Extend<Signal>>::extend`.
+ Because of this change, you now need `use std::iter::Extend` to call `extend`
+ on a `SigSet`.
+ (#[1553](https://github.com/nix-rust/nix/pull/1553))
+- Removed the the `PATH_MAX` restriction from APIs accepting paths. Paths
+ will now be allocated on the heap if they are too long. In addition, large
+ instruction count improvements (~30x) were made to path handling.
+ (#[1656](https://github.com/nix-rust/nix/pull/1656))
+- Changed `getrlimit` and `setrlimit` to use `rlim_t` directly
+ instead of `Option<rlim_t>`.
+ (#[1668](https://github.com/nix-rust/nix/pull/1668))
+- Deprecated `InetAddr` and `SockAddr` in favor of `SockaddrIn`, `SockaddrIn6`,
+ and `SockaddrStorage`.
+ (#[1684](https://github.com/nix-rust/nix/pull/1684))
+- Deprecated `IpAddr`, `Ipv4Addr`, and `Ipv6Addr` in favor of their equivalents
+ from the standard library.
+ (#[1685](https://github.com/nix-rust/nix/pull/1685))
+- `uname` now returns a `Result<UtsName>` instead of just a `UtsName` and
+ ignoring failures from libc. And getters on the `UtsName` struct now return
+ an `&OsStr` instead of `&str`.
+ (#[1672](https://github.com/nix-rust/nix/pull/1672))
+- Replaced `IoVec` with `IoSlice` and `IoSliceMut`, and replaced `IoVec::from_slice` with
+ `IoSlice::new`. (#[1643](https://github.com/nix-rust/nix/pull/1643))
+
+### Fixed
+
+- `InetAddr::from_std` now sets the `sin_len`/`sin6_len` fields on the BSDs.
+ (#[1642](https://github.com/nix-rust/nix/pull/1642))
+- Fixed a panic in `LinkAddr::addr`. That function now returns an `Option`.
+ (#[1675](https://github.com/nix-rust/nix/pull/1675))
+ (#[1677](https://github.com/nix-rust/nix/pull/1677))
+
+### Removed
+
+- Removed public access to the inner fields of `NetlinkAddr`, `AlgAddr`,
+ `SysControlAddr`, `LinkAddr`, and `VsockAddr`.
+ (#[1614](https://github.com/nix-rust/nix/pull/1614))
+- Removed `EventFlag::EV_SYSFLAG`.
+ (#[1635](https://github.com/nix-rust/nix/pull/1635))
+
+## [0.23.1] - 2021-12-16
+
+### Changed
+
+- Relaxed the bitflags requirement from 1.3.1 to 1.1. This partially reverts
+ #1492. From now on, the MSRV is not guaranteed to work with all versions of
+ all dependencies, just with some version of all dependencies.
+ (#[1607](https://github.com/nix-rust/nix/pull/1607))
+
+### Fixed
+
+- Fixed soundness issues in `FdSet::insert`, `FdSet::remove`, and
+ `FdSet::contains` involving file descriptors outside of the range
+ `0..FD_SETSIZE`.
+ (#[1575](https://github.com/nix-rust/nix/pull/1575))
+
+## [0.23.0] - 2021-09-28
+### Added
+
+- Added the `LocalPeerCred` sockopt.
+ (#[1482](https://github.com/nix-rust/nix/pull/1482))
+- Added `TimeSpec::from_duration` and `TimeSpec::from_timespec`
+ (#[1465](https://github.com/nix-rust/nix/pull/1465))
+- Added `IPV6_V6ONLY` sockopt.
+ (#[1470](https://github.com/nix-rust/nix/pull/1470))
+- Added `impl From<User> for libc::passwd` trait implementation to convert a `User`
+ into a `libc::passwd`. Consumes the `User` struct to give ownership over
+ the member pointers.
+ (#[1471](https://github.com/nix-rust/nix/pull/1471))
+- Added `pthread_kill`.
+ (#[1472](https://github.com/nix-rust/nix/pull/1472))
+- Added `mknodat`.
+ (#[1473](https://github.com/nix-rust/nix/pull/1473))
+- Added `setrlimit` and `getrlimit`.
+ (#[1302](https://github.com/nix-rust/nix/pull/1302))
+- Added `ptrace::interrupt` method for platforms that support `PTRACE_INTERRUPT`
+ (#[1422](https://github.com/nix-rust/nix/pull/1422))
+- Added `IP6T_SO_ORIGINAL_DST` sockopt.
+ (#[1490](https://github.com/nix-rust/nix/pull/1490))
+- Added the `PTRACE_EVENT_STOP` variant to the `sys::ptrace::Event` enum
+ (#[1335](https://github.com/nix-rust/nix/pull/1335))
+- Exposed `SockAddr::from_raw_sockaddr`
+ (#[1447](https://github.com/nix-rust/nix/pull/1447))
+- Added `TcpRepair`
+ (#[1503](https://github.com/nix-rust/nix/pull/1503))
+- Enabled `pwritev` and `preadv` for more operating systems.
+ (#[1511](https://github.com/nix-rust/nix/pull/1511))
+- Added support for `TCP_MAXSEG` TCP Maximum Segment Size socket options
+ (#[1292](https://github.com/nix-rust/nix/pull/1292))
+- Added `Ipv4RecvErr` and `Ipv6RecvErr` sockopts and associated control messages.
+ (#[1514](https://github.com/nix-rust/nix/pull/1514))
+- Added `AsRawFd` implementation on `PollFd`.
+ (#[1516](https://github.com/nix-rust/nix/pull/1516))
+- Added `Ipv4Ttl` and `Ipv6Ttl` sockopts.
+ (#[1515](https://github.com/nix-rust/nix/pull/1515))
+- Added `MAP_EXCL`, `MAP_ALIGNED_SUPER`, and `MAP_CONCEAL` mmap flags, and
+ exposed `MAP_ANONYMOUS` for all operating systems.
+ (#[1522](https://github.com/nix-rust/nix/pull/1522))
+ (#[1525](https://github.com/nix-rust/nix/pull/1525))
+ (#[1531](https://github.com/nix-rust/nix/pull/1531))
+ (#[1534](https://github.com/nix-rust/nix/pull/1534))
+- Added read/write accessors for 'events' on `PollFd`.
+ (#[1517](https://github.com/nix-rust/nix/pull/1517))
+
+### Changed
+
+- `FdSet::{contains, highest, fds}` no longer require a mutable reference.
+ (#[1464](https://github.com/nix-rust/nix/pull/1464))
+- `User::gecos` and corresponding `libc::passwd::pw_gecos` are supported on
+ 64-bit Android, change conditional compilation to include the field in
+ 64-bit Android builds
+ (#[1471](https://github.com/nix-rust/nix/pull/1471))
+- `eventfd`s are supported on Android, change conditional compilation to
+ include `sys::eventfd::eventfd` and `sys::eventfd::EfdFlags`for Android
+ builds.
+ (#[1481](https://github.com/nix-rust/nix/pull/1481))
+- Most enums that come from C, for example `Errno`, are now marked as
+ `#[non_exhaustive]`.
+ (#[1474](https://github.com/nix-rust/nix/pull/1474))
+- Many more functions, mostly contructors, are now `const`.
+ (#[1476](https://github.com/nix-rust/nix/pull/1476))
+ (#[1492](https://github.com/nix-rust/nix/pull/1492))
+- `sys::event::KEvent::filter` now returns a `Result` instead of being
+ infalliable. The only cases where it will now return an error are cases
+ where it previously would've had undefined behavior.
+ (#[1484](https://github.com/nix-rust/nix/pull/1484))
+- Minimum supported Rust version is now 1.46.0.
+ ([#1492](https://github.com/nix-rust/nix/pull/1492))
+- Rework `UnixAddr` to encapsulate internals better in order to fix soundness
+ issues. No longer allows creating a `UnixAddr` from a raw `sockaddr_un`.
+ ([#1496](https://github.com/nix-rust/nix/pull/1496))
+- Raised bitflags to 1.3.0 and the MSRV to 1.46.0.
+ ([#1492](https://github.com/nix-rust/nix/pull/1492))
+
+### Fixed
+
+- `posix_fadvise` now returns errors in the conventional way, rather than as a
+ non-zero value in `Ok()`.
+ (#[1538](https://github.com/nix-rust/nix/pull/1538))
+- Added more errno definitions for better backwards compatibility with
+ Nix 0.21.0.
+ (#[1467](https://github.com/nix-rust/nix/pull/1467))
+- Fixed potential undefined behavior in `Signal::try_from` on some platforms.
+ (#[1484](https://github.com/nix-rust/nix/pull/1484))
+- Fixed buffer overflow in `unistd::getgrouplist`.
+ (#[1545](https://github.com/nix-rust/nix/pull/1545))
+
+
+### Removed
+
+- Removed a couple of termios constants on redox that were never actually
+ supported.
+ (#[1483](https://github.com/nix-rust/nix/pull/1483))
+- Removed `nix::sys::signal::NSIG`. It was of dubious utility, and not correct
+ for all platforms.
+ (#[1484](https://github.com/nix-rust/nix/pull/1484))
+- Removed support for 32-bit Apple targets, since they've been dropped by both
+ Rustc and Xcode.
+ (#[1492](https://github.com/nix-rust/nix/pull/1492))
+- Deprecated `SockAddr/InetAddr::to_str` in favor of `ToString::to_string`
+ (#[1495](https://github.com/nix-rust/nix/pull/1495))
+- Removed `SigevNotify` on OpenBSD and Redox.
+ (#[1511](https://github.com/nix-rust/nix/pull/1511))
+
+## [0.22.3] - 22 January 2022
+### Changed
+- Relaxed the bitflags requirement from 1.3.1 to 1.1. This partially reverts
+ #1492. From now on, the MSRV is not guaranteed to work with all versions of
+ all dependencies, just with some version of all dependencies.
+ (#[1607](https://github.com/nix-rust/nix/pull/1607))
+
+## [0.22.2] - 28 September 2021
+### Fixed
+- Fixed buffer overflow in `unistd::getgrouplist`.
+ (#[1545](https://github.com/nix-rust/nix/pull/1545))
+- Added more errno definitions for better backwards compatibility with
+ Nix 0.21.0.
+ (#[1467](https://github.com/nix-rust/nix/pull/1467))
+
+## [0.22.1] - 13 August 2021
+### Fixed
+- Locked bitflags to < 1.3.0 to fix the build with rust < 1.46.0.
+
+### Removed
+- Removed a couple of termios constants on redox that were never actually
+ supported.
+ (#[1483](https://github.com/nix-rust/nix/pull/1483))
+
+## [0.22.0] - 9 July 2021
+### Added
+- Added `if_nameindex` (#[1445](https://github.com/nix-rust/nix/pull/1445))
+- Added `nmount` for FreeBSD.
+ (#[1453](https://github.com/nix-rust/nix/pull/1453))
+- Added `IpFreebind` socket option (sockopt) on Linux, Fuchsia and Android.
+ (#[1456](https://github.com/nix-rust/nix/pull/1456))
+- Added `TcpUserTimeout` socket option (sockopt) on Linux and Fuchsia.
+ (#[1457](https://github.com/nix-rust/nix/pull/1457))
+- Added `renameat2` for Linux
+ (#[1458](https://github.com/nix-rust/nix/pull/1458))
+- Added `RxqOvfl` support on Linux, Fuchsia and Android.
+ (#[1455](https://github.com/nix-rust/nix/pull/1455))
+
+### Changed
+- `ptsname_r` now returns a lossily-converted string in the event of bad UTF,
+ just like `ptsname`.
+ ([#1446](https://github.com/nix-rust/nix/pull/1446))
+- Nix's error type is now a simple wrapper around the platform's Errno. This
+ means it is now `Into<std::io::Error>`. It's also `Clone`, `Copy`, `Eq`, and
+ has a small fixed size. It also requires less typing. For example, the old
+ enum variant `nix::Error::Sys(nix::errno::Errno::EINVAL)` is now simply
+ `nix::Error::EINVAL`.
+ ([#1446](https://github.com/nix-rust/nix/pull/1446))
+
+## [0.21.2] - 29 September 2021
+### Fixed
+- Fixed buffer overflow in `unistd::getgrouplist`.
+ (#[1545](https://github.com/nix-rust/nix/pull/1545))
+
+## [0.21.1] - 13 August 2021
+### Fixed
+- Locked bitflags to < 1.3.0 to fix the build with rust < 1.46.0.
+
+### Removed
+- Removed a couple of termios constants on redox that were never actually
+ supported.
+ (#[1483](https://github.com/nix-rust/nix/pull/1483))
+
+## [0.21.0] - 31 May 2021
+### Added
+- Added `getresuid` and `getresgid`
+ (#[1430](https://github.com/nix-rust/nix/pull/1430))
+- Added TIMESTAMPNS support for linux
+ (#[1402](https://github.com/nix-rust/nix/pull/1402))
+- Added `sendfile64` (#[1439](https://github.com/nix-rust/nix/pull/1439))
+- Added `MS_LAZYTIME` to `MsFlags`
+ (#[1437](https://github.com/nix-rust/nix/pull/1437))
+
+### Changed
+- Made `forkpty` unsafe, like `fork`
+ (#[1390](https://github.com/nix-rust/nix/pull/1390))
+- Made `Uid`, `Gid` and `Pid` methods `from_raw` and `as_raw` a `const fn`
+ (#[1429](https://github.com/nix-rust/nix/pull/1429))
+- Made `Uid::is_root` a `const fn`
+ (#[1429](https://github.com/nix-rust/nix/pull/1429))
+- `AioCb` is now always pinned. Once a `libc::aiocb` gets sent to the kernel,
+ its address in memory must not change. Nix now enforces that by using
+ `std::pin`. Most users won't need to change anything, except when using
+ `aio_suspend`. See that method's documentation for the new usage.
+ (#[1440](https://github.com/nix-rust/nix/pull/1440))
+- `LioCb` is now constructed using a distinct `LioCbBuilder` struct. This
+ avoids a soundness issue with the old `LioCb`. Usage is similar but
+ construction now uses the builder pattern. See the documentation for
+ details.
+ (#[1440](https://github.com/nix-rust/nix/pull/1440))
+- Minimum supported Rust version is now 1.41.0.
+ ([#1440](https://github.com/nix-rust/nix/pull/1440))
+- Errno aliases are now associated consts on `Errno`, instead of consts in the
+ `errno` module.
+ (#[1452](https://github.com/nix-rust/nix/pull/1452))
+
+### Fixed
+- Allow `sockaddr_ll` size, as reported by the Linux kernel, to be smaller then it's definition
+ (#[1395](https://github.com/nix-rust/nix/pull/1395))
+- Fix spurious errors using `sendmmsg` with multiple cmsgs
+ (#[1414](https://github.com/nix-rust/nix/pull/1414))
+- Added `Errno::EOPNOTSUPP` to FreeBSD, where it was missing.
+ (#[1452](https://github.com/nix-rust/nix/pull/1452))
+
+### Removed
+
+- Removed `sys::socket::accept4` from Android arm because libc removed it in
+ version 0.2.87.
+ ([#1399](https://github.com/nix-rust/nix/pull/1399))
+- `AioCb::from_boxed_slice` and `AioCb::from_boxed_mut_slice` have been
+ removed. They were useful with earlier versions of Rust, but should no
+ longer be needed now that async/await are available. `AioCb`s now work
+ exclusively with borrowed buffers, not owned ones.
+ (#[1440](https://github.com/nix-rust/nix/pull/1440))
+- Removed some Errno values from platforms where they aren't actually defined.
+ (#[1452](https://github.com/nix-rust/nix/pull/1452))
+
+## [0.20.2] - 28 September 2021
+### Fixed
+- Fixed buffer overflow in `unistd::getgrouplist`.
+ (#[1545](https://github.com/nix-rust/nix/pull/1545))
+
+## [0.20.1] - 13 August 2021
+### Fixed
+- Locked bitflags to < 1.3.0 to fix the build with rust < 1.46.0.
+
+### Removed
+- Removed a couple of termios constants on redox that were never actually
+ supported.
+ (#[1483](https://github.com/nix-rust/nix/pull/1483))
+
+## [0.20.0] - 20 February 2021
+### Added
+
+- Added a `passwd` field to `Group` (#[1338](https://github.com/nix-rust/nix/pull/1338))
+- Added `mremap` (#[1306](https://github.com/nix-rust/nix/pull/1306))
+- Added `personality` (#[1331](https://github.com/nix-rust/nix/pull/1331))
+- Added limited Fuchsia support (#[1285](https://github.com/nix-rust/nix/pull/1285))
+- Added `getpeereid` (#[1342](https://github.com/nix-rust/nix/pull/1342))
+- Implemented `IntoIterator` for `Dir`
+ (#[1333](https://github.com/nix-rust/nix/pull/1333)).
+
+### Changed
+
+- Minimum supported Rust version is now 1.40.0.
+ ([#1356](https://github.com/nix-rust/nix/pull/1356))
+- i686-apple-darwin has been demoted to Tier 2 support, because it's deprecated
+ by Xcode.
+ (#[1350](https://github.com/nix-rust/nix/pull/1350))
+- Fixed calling `recvfrom` on an `AddrFamily::Packet` socket
+ (#[1344](https://github.com/nix-rust/nix/pull/1344))
+
+### Fixed
+- `TimerFd` now closes the underlying fd on drop.
+ ([#1381](https://github.com/nix-rust/nix/pull/1381))
+- Define `*_MAGIC` filesystem constants on Linux s390x
+ (#[1372](https://github.com/nix-rust/nix/pull/1372))
+- mqueue, sysinfo, timespec, statfs, test_ptrace_syscall() on x32
+ (#[1366](https://github.com/nix-rust/nix/pull/1366))
+
+### Removed
+
+- `Dir`, `SignalFd`, and `PtyMaster` are no longer `Clone`.
+ (#[1382](https://github.com/nix-rust/nix/pull/1382))
+- Removed `SockLevel`, which hasn't been used for a few years
+ (#[1362](https://github.com/nix-rust/nix/pull/1362))
+- Removed both `Copy` and `Clone` from `TimerFd`.
+ ([#1381](https://github.com/nix-rust/nix/pull/1381))
+
+## [0.19.1] - 28 November 2020
+### Fixed
+- Fixed bugs in `recvmmsg`.
+ (#[1341](https://github.com/nix-rust/nix/pull/1341))
+
+## [0.19.0] - 6 October 2020
+### Added
+- Added Netlink protocol families to the `SockProtocol` enum
+ (#[1289](https://github.com/nix-rust/nix/pull/1289))
+- Added `clock_gettime`, `clock_settime`, `clock_getres`,
+ `clock_getcpuclockid` functions and `ClockId` struct.
+ (#[1281](https://github.com/nix-rust/nix/pull/1281))
+- Added wrapper functions for `PTRACE_SYSEMU` and `PTRACE_SYSEMU_SINGLESTEP`.
+ (#[1300](https://github.com/nix-rust/nix/pull/1300))
+- Add support for Vsock on Android rather than just Linux.
+ (#[1301](https://github.com/nix-rust/nix/pull/1301))
+- Added `TCP_KEEPCNT` and `TCP_KEEPINTVL` TCP keepalive options.
+ (#[1283](https://github.com/nix-rust/nix/pull/1283))
+### Changed
+- Expose `SeekData` and `SeekHole` on all Linux targets
+ (#[1284](https://github.com/nix-rust/nix/pull/1284))
+- Changed unistd::{execv,execve,execvp,execvpe,fexecve,execveat} to take both `&[&CStr]` and `&[CString]` as its list argument(s).
+ (#[1278](https://github.com/nix-rust/nix/pull/1278))
+- Made `unistd::fork` an unsafe funtion, bringing it in line with [libstd's decision](https://github.com/rust-lang/rust/pull/58059).
+ (#[1293](https://github.com/nix-rust/nix/pull/1293))
+
+## [0.18.0] - 26 July 2020
+### Added
+- Added `fchown(2)` wrapper.
+ (#[1257](https://github.com/nix-rust/nix/pull/1257))
+- Added support on linux systems for `MAP_HUGE_`_`SIZE`_ family of flags.
+ (#[1211](https://github.com/nix-rust/nix/pull/1211))
+- Added support for `F_OFD_*` `fcntl` commands on Linux and Android.
+ (#[1195](https://github.com/nix-rust/nix/pull/1195))
+- Added `env::clearenv()`: calls `libc::clearenv` on platforms
+ where it's available, and clears the environment of all variables
+ via `std::env::vars` and `std::env::remove_var` on others.
+ (#[1185](https://github.com/nix-rust/nix/pull/1185))
+- `FsType` inner value made public.
+ (#[1187](https://github.com/nix-rust/nix/pull/1187))
+- Added `unistd::setfsuid` and `unistd::setfsgid` to set the user or group
+ identity for filesystem checks per-thread.
+ (#[1163](https://github.com/nix-rust/nix/pull/1163))
+- Derived `Ord`, `PartialOrd` for `unistd::Pid` (#[1189](https://github.com/nix-rust/nix/pull/1189))
+- Added `select::FdSet::fds` method to iterate over file descriptors in a set.
+ ([#1207](https://github.com/nix-rust/nix/pull/1207))
+- Added support for UDP generic segmentation offload (GSO) and generic
+ receive offload (GRO) ([#1209](https://github.com/nix-rust/nix/pull/1209))
+- Added support for `sendmmsg` and `recvmmsg` calls
+ (#[1208](https://github.com/nix-rust/nix/pull/1208))
+- Added support for `SCM_CREDS` messages (`UnixCredentials`) on FreeBSD/DragonFly
+ (#[1216](https://github.com/nix-rust/nix/pull/1216))
+- Added `BindToDevice` socket option (sockopt) on Linux
+ (#[1233](https://github.com/nix-rust/nix/pull/1233))
+- Added `EventFilter` bitflags for `EV_DISPATCH` and `EV_RECEIPT` on OpenBSD.
+ (#[1252](https://github.com/nix-rust/nix/pull/1252))
+- Added support for `Ipv4PacketInfo` and `Ipv6PacketInfo` to `ControlMessage`.
+ (#[1222](https://github.com/nix-rust/nix/pull/1222))
+- `CpuSet` and `UnixCredentials` now implement `Default`.
+ (#[1244](https://github.com/nix-rust/nix/pull/1244))
+- Added `unistd::ttyname`
+ (#[1259](https://github.com/nix-rust/nix/pull/1259))
+- Added support for `Ipv4PacketInfo` and `Ipv6PacketInfo` to `ControlMessage` for iOS and Android.
+ (#[1265](https://github.com/nix-rust/nix/pull/1265))
+- Added support for `TimerFd`.
+ (#[1261](https://github.com/nix-rust/nix/pull/1261))
+
+### Changed
+- Changed `fallocate` return type from `c_int` to `()` (#[1201](https://github.com/nix-rust/nix/pull/1201))
+- Enabled `sys::ptrace::setregs` and `sys::ptrace::getregs` on x86_64-unknown-linux-musl target
+ (#[1198](https://github.com/nix-rust/nix/pull/1198))
+- On Linux, `ptrace::write` is now an `unsafe` function. Caveat programmer.
+ (#[1245](https://github.com/nix-rust/nix/pull/1245))
+- `execv`, `execve`, `execvp` and `execveat` in `::nix::unistd` and `reboot` in
+ `::nix::sys::reboot` now return `Result<Infallible>` instead of `Result<Void>` (#[1239](https://github.com/nix-rust/nix/pull/1239))
+- `sys::socket::sockaddr_storage_to_addr` is no longer `unsafe`. So is
+ `offset_of!`.
+- `sys::socket::sockaddr_storage_to_addr`, `offset_of!`, and `Errno::clear` are
+ no longer `unsafe`.
+- `SockAddr::as_ffi_pair`,`sys::socket::sockaddr_storage_to_addr`, `offset_of!`,
+ and `Errno::clear` are no longer `unsafe`.
+ (#[1244](https://github.com/nix-rust/nix/pull/1244))
+- Several `Inotify` methods now take `self` by value instead of by reference
+ (#[1244](https://github.com/nix-rust/nix/pull/1244))
+- `nix::poll::ppoll`: `timeout` parameter is now optional, None is equivalent for infinite timeout.
+
+### Fixed
+
+- Fixed `getsockopt`. The old code produced UB which triggers a panic with
+ Rust 1.44.0.
+ (#[1214](https://github.com/nix-rust/nix/pull/1214))
+
+- Fixed a bug in nix::unistd that would result in an infinite loop
+ when a group or user lookup required a buffer larger than
+ 16KB. (#[1198](https://github.com/nix-rust/nix/pull/1198))
+- Fixed unaligned casting of `cmsg_data` to `af_alg_iv` (#[1206](https://github.com/nix-rust/nix/pull/1206))
+- Fixed `readlink`/`readlinkat` when reading symlinks longer than `PATH_MAX` (#[1231](https://github.com/nix-rust/nix/pull/1231))
+- `PollFd`, `EpollEvent`, `IpMembershipRequest`, `Ipv6MembershipRequest`,
+ `TimeVal`, and `IoVec` are now `repr(transparent)`. This is required for
+ correctness's sake across all architectures and compilers, though now bugs
+ have been reported so far.
+ (#[1243](https://github.com/nix-rust/nix/pull/1243))
+- Fixed unaligned pointer read in `Inotify::read_events`.
+ (#[1244](https://github.com/nix-rust/nix/pull/1244))
+
+### Removed
+
+- Removed `sys::socket::addr::from_libc_sockaddr` from the public API.
+ (#[1215](https://github.com/nix-rust/nix/pull/1215))
+- Removed `sys::termios::{get_libc_termios, get_libc_termios_mut, update_wrapper`
+ from the public API. These were previously hidden in the docs but still usable
+ by downstream.
+ (#[1235](https://github.com/nix-rust/nix/pull/1235))
+
+- Nix no longer implements `NixPath` for `Option<P> where P: NixPath`. Most
+ Nix functions that accept `NixPath` arguments can't do anything useful with
+ `None`. The exceptions (`mount` and `quotactl_sync`) already take explicitly
+ optional arguments.
+ (#[1242](https://github.com/nix-rust/nix/pull/1242))
+
+- Removed `unistd::daemon` and `unistd::pipe2` on OSX and ios
+ (#[1255](https://github.com/nix-rust/nix/pull/1255))
+
+- Removed `sys::event::FilterFlag::NOTE_EXIT_REPARENTED` and
+ `sys::event::FilterFlag::NOTE_REAP` on OSX and ios.
+ (#[1255](https://github.com/nix-rust/nix/pull/1255))
+
+- Removed `sys::ptrace::ptrace` on Android and Linux.
+ (#[1255](https://github.com/nix-rust/nix/pull/1255))
+
+- Dropped support for powerpc64-unknown-linux-gnu
+ (#[1266](https://github.com/nix-rust/nix/pull/1268))
+
+## [0.17.0] - 3 February 2020
+### Added
+- Add `CLK_TCK` to `SysconfVar`
+ (#[1177](https://github.com/nix-rust/nix/pull/1177))
+### Removed
+- Removed deprecated Error::description from error types
+ (#[1175](https://github.com/nix-rust/nix/pull/1175))
+
+## [0.16.1] - 23 December 2019
+### Fixed
+
+- Fixed the build for OpenBSD
+ (#[1168](https://github.com/nix-rust/nix/pull/1168))
+
+## [0.16.0] - 1 December 2019
+### Added
+- Added `ptrace::seize()`: similar to `attach()` on Linux
+ but with better-defined semantics.
+ (#[1154](https://github.com/nix-rust/nix/pull/1154))
+
+- Added `Signal::as_str()`: returns signal name as `&'static str`
+ (#[1138](https://github.com/nix-rust/nix/pull/1138))
+
+- Added `posix_fallocate`.
+ ([#1105](https://github.com/nix-rust/nix/pull/1105))
+
+- Implemented `Default` for `FdSet`
+ ([#1107](https://github.com/nix-rust/nix/pull/1107))
+
+- Added `NixPath::is_empty`.
+ ([#1107](https://github.com/nix-rust/nix/pull/1107))
+
+- Added `mkfifoat`
+ ([#1133](https://github.com/nix-rust/nix/pull/1133))
+
+- Added `User::from_uid`, `User::from_name`, `User::from_gid` and
+ `Group::from_name`,
+ ([#1139](https://github.com/nix-rust/nix/pull/1139))
+
+- Added `linkat`
+ ([#1101](https://github.com/nix-rust/nix/pull/1101))
+
+- Added `sched_getaffinity`.
+ ([#1148](https://github.com/nix-rust/nix/pull/1148))
+
+- Added optional `Signal` argument to `ptrace::{detach, syscall}` for signal
+ injection. ([#1083](https://github.com/nix-rust/nix/pull/1083))
+
+### Changed
+- `sys::termios::BaudRate` now implements `TryFrom<speed_t>` instead of
+ `From<speed_t>`. The old `From` implementation would panic on failure.
+ ([#1159](https://github.com/nix-rust/nix/pull/1159))
+
+- `sys::socket::ControlMessage::ScmCredentials` and
+ `sys::socket::ControlMessageOwned::ScmCredentials` now wrap `UnixCredentials`
+ rather than `libc::ucred`.
+ ([#1160](https://github.com/nix-rust/nix/pull/1160))
+
+- `sys::socket::recvmsg` now takes a plain `Vec` instead of a `CmsgBuffer`
+ implementor. If you were already using `cmsg_space!`, then you needn't worry.
+ ([#1156](https://github.com/nix-rust/nix/pull/1156))
+
+- `sys::socket::recvfrom` now returns
+ `Result<(usize, Option<SockAddr>)>` instead of `Result<(usize, SockAddr)>`.
+ ([#1145](https://github.com/nix-rust/nix/pull/1145))
+
+- `Signal::from_c_int` has been replaced by `Signal::try_from`
+ ([#1113](https://github.com/nix-rust/nix/pull/1113))
+
+- Changed `readlink` and `readlinkat` to return `OsString`
+ ([#1109](https://github.com/nix-rust/nix/pull/1109))
+
+ ```rust
+ # use nix::fcntl::{readlink, readlinkat};
+ // the buffer argument of `readlink` and `readlinkat` has been removed,
+ // and the return value is now an owned type (`OsString`).
+ // Existing code can be updated by removing the buffer argument
+ // and removing any clone or similar operation on the output
+
+ // old code `readlink(&path, &mut buf)` can be replaced with the following
+ let _: OsString = readlink(&path);
+
+ // old code `readlinkat(dirfd, &path, &mut buf)` can be replaced with the following
+ let _: OsString = readlinkat(dirfd, &path);
+ ```
+
+- Minimum supported Rust version is now 1.36.0.
+ ([#1108](https://github.com/nix-rust/nix/pull/1108))
+
+- `Ipv4Addr::octets`, `Ipv4Addr::to_std`, `Error::as_errno`,
+ `ForkResult::is_child`, `ForkResult::is_parent`, `Gid::as_raw`,
+ `Uid::is_root`, `Uid::as_raw`, `Pid::as_raw`, and `PollFd::revents` now take
+ `self` by value.
+ ([#1107](https://github.com/nix-rust/nix/pull/1107))
+
+- Type `&CString` for parameters of `exec(v|ve|vp|vpe|veat)` are changed to `&CStr`.
+ ([#1121](https://github.com/nix-rust/nix/pull/1121))
+
+### Fixed
+- Fix length of abstract socket addresses
+ ([#1120](https://github.com/nix-rust/nix/pull/1120))
+
+- Fix initialization of msghdr in recvmsg/sendmsg when built with musl
+ ([#1136](https://github.com/nix-rust/nix/pull/1136))
+
+### Removed
+- Remove the deprecated `CmsgSpace`.
+ ([#1156](https://github.com/nix-rust/nix/pull/1156))
+
+## [0.15.0] - 10 August 2019
+### Added
+- Added `MSG_WAITALL` to `MsgFlags` in `sys::socket`.
+ ([#1079](https://github.com/nix-rust/nix/pull/1079))
+- Implemented `Clone`, `Copy`, `Debug`, `Eq`, `Hash`, and `PartialEq` for most
+ types that support them. ([#1035](https://github.com/nix-rust/nix/pull/1035))
+- Added `copy_file_range` wrapper
+ ([#1069](https://github.com/nix-rust/nix/pull/1069))
+- Add `mkdirat`.
+ ([#1084](https://github.com/nix-rust/nix/pull/1084))
+- Add `posix_fadvise`.
+ ([#1089](https://github.com/nix-rust/nix/pull/1089))
+- Added `AF_VSOCK` to `AddressFamily`.
+ ([#1091](https://github.com/nix-rust/nix/pull/1091))
+- Add `unlinkat`
+ ([#1058](https://github.com/nix-rust/nix/pull/1058))
+- Add `renameat`.
+ ([#1097](https://github.com/nix-rust/nix/pull/1097))
+
+### Changed
+- Support for `ifaddrs` now present when building for Android.
+ ([#1077](https://github.com/nix-rust/nix/pull/1077))
+- Minimum supported Rust version is now 1.31.0
+ ([#1035](https://github.com/nix-rust/nix/pull/1035))
+ ([#1095](https://github.com/nix-rust/nix/pull/1095))
+- Now functions `statfs()` and `fstatfs()` return result with `Statfs` wrapper
+ ([#928](https://github.com/nix-rust/nix/pull/928))
+
+### Fixed
+- Enabled `sched_yield` for all nix hosts.
+ ([#1090](https://github.com/nix-rust/nix/pull/1090))
+
+## [0.14.1] - 2019-06-06
+### Added
+- Macros exported by `nix` may now be imported via `use` on the Rust 2018
+ edition without importing helper macros on Linux targets.
+ ([#1066](https://github.com/nix-rust/nix/pull/1066))
+
+ For example, in Rust 2018, the `ioctl_read_bad!` macro can now be imported
+ without importing the `convert_ioctl_res!` macro.
+
+ ```rust
+ use nix::ioctl_read_bad;
+
+ ioctl_read_bad!(tcgets, libc::TCGETS, libc::termios);
+ ```
+
+### 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/commits))
+
+### Fixed
+- Fix the build on Android and Linux/mips with recent versions of libc.
+ ([#1072](https://github.com/nix-rust/nix/pull/1072/commits))
+
+## [0.14.0] - 2019-05-21
+### Added
+- Add IP_RECVIF & IP_RECVDSTADDR. Enable IP_PKTINFO and IP6_PKTINFO on netbsd/openbsd.
+ ([#1002](https://github.com/nix-rust/nix/pull/1002))
+- Added `inotify_init1`, `inotify_add_watch` and `inotify_rm_watch` wrappers for
+ Android and Linux. ([#1016](https://github.com/nix-rust/nix/pull/1016))
+- Add `ALG_SET_IV`, `ALG_SET_OP` and `ALG_SET_AEAD_ASSOCLEN` control messages and `AF_ALG`
+ socket types on Linux and Android ([#1031](https://github.com/nix-rust/nix/pull/1031))
+- Add killpg
+ ([#1034](https://github.com/nix-rust/nix/pull/1034))
+- Added ENOTSUP errno support for Linux and Android.
+ ([#969](https://github.com/nix-rust/nix/pull/969))
+- Add several errno constants from OpenBSD 6.2
+ ([#1036](https://github.com/nix-rust/nix/pull/1036))
+- Added `from_std` and `to_std` methods for `sys::socket::IpAddr`
+ ([#1043](https://github.com/nix-rust/nix/pull/1043))
+- Added `nix::unistd:seteuid` and `nix::unistd::setegid` for those platforms that do
+ not support `setresuid` nor `setresgid` respectively.
+ ([#1044](https://github.com/nix-rust/nix/pull/1044))
+- Added a `access` wrapper
+ ([#1045](https://github.com/nix-rust/nix/pull/1045))
+- Add `forkpty`
+ ([#1042](https://github.com/nix-rust/nix/pull/1042))
+- Add `sched_yield`
+ ([#1050](https://github.com/nix-rust/nix/pull/1050))
+
+### Changed
+- `PollFd` event flags renamed to `PollFlags` ([#1024](https://github.com/nix-rust/nix/pull/1024/))
+- `recvmsg` now returns an Iterator over `ControlMessageOwned` objects rather
+ than `ControlMessage` objects. This is sadly not backwards-compatible. Fix
+ code like this:
+ ```rust
+ if let ControlMessage::ScmRights(&fds) = cmsg {
+ ```
+
+ By replacing it with code like this:
+ ```rust
+ if let ControlMessageOwned::ScmRights(fds) = cmsg {
+ ```
+ ([#1020](https://github.com/nix-rust/nix/pull/1020))
+- Replaced `CmsgSpace` with the `cmsg_space` macro.
+ ([#1020](https://github.com/nix-rust/nix/pull/1020))
+
+### Fixed
+- Fixed multiple bugs when using `sendmsg` and `recvmsg` with ancillary control messages
+ ([#1020](https://github.com/nix-rust/nix/pull/1020))
+- Macros exported by `nix` may now be imported via `use` on the Rust 2018
+ edition without importing helper macros for BSD targets.
+ ([#1041](https://github.com/nix-rust/nix/pull/1041))
+
+ For example, in Rust 2018, the `ioctl_read_bad!` macro can now be imported
+ without importing the `convert_ioctl_res!` macro.
+
+ ```rust
+ use nix::ioctl_read_bad;
+
+ ioctl_read_bad!(tcgets, libc::TCGETS, libc::termios);
+ ```
+
+### Removed
+- `Daemon`, `NOTE_REAP`, and `NOTE_EXIT_REPARENTED` are now deprecated on OSX
+ and iOS.
+ ([#1033](https://github.com/nix-rust/nix/pull/1033))
+- `PTRACE_GETREGS`, `PTRACE_SETREGS`, `PTRACE_GETFPREGS`, and
+ `PTRACE_SETFPREGS` have been removed from some platforms where they never
+ should've been defined in the first place.
+ ([#1055](https://github.com/nix-rust/nix/pull/1055))
+
+## [0.13.1] - 2019-06-10
+### 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/commits))
+
+### Fixed
+- Fix the build on Android and Linux/mips with recent versions of libc.
+ ([#1072](https://github.com/nix-rust/nix/pull/1072/commits))
+- 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))
+- Added an `mprotect` wrapper.
+ ([#991](https://github.com/nix-rust/nix/pull/991))
+
+### 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))
+
+## [0.12.1] 2019-06-08
+### 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/commits))
+
+### Fixed
+- Fix the build on Android and Linux/mips with recent versions of libc.
+ ([#1072](https://github.com/nix-rust/nix/pull/1072/commits))
+- Fixed build on Linux/arm and Linux/s390x with the latest Rust libc
+ ([52102cb](https://github.com/nix-rust/nix/commit/52102cb76398c4dfb9ea141b98c5b01a2e050973))
+
+### Removed
+- `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))
+- `Daemon`, `NOTE_REAP`, and `NOTE_EXIT_REPARENTED` are now deprecated on OSX
+ and iOS.
+ ([#1033](https://github.com/nix-rust/nix/pull/1033))
+
+## [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))
+
+## [0.11.1] 2019-06-06
+### 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/commits))
+
+### Fixed
+- Fix the build on Android and Linux/mips with recent versions of libc.
+ ([#1072](https://github.com/nix-rust/nix/pull/1072/commits))
+- Fixed build on Linux/arm and Linux/s390x with the latest Rust libc
+ ([52102cb](https://github.com/nix-rust/nix/commit/52102cb76398c4dfb9ea141b98c5b01a2e050973))
+
+### Removed
+- `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))
+- `Daemon`, `NOTE_REAP`, and `NOTE_EXIT_REPARENTED` are now deprecated on OSX
+ and iOS.
+ ([#1033](https://github.com/nix-rust/nix/pull/1033))
+
+## [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/Cargo.toml b/third_party/rust/nix/Cargo.toml
new file mode 100644
index 0000000000..bb04ab2702
--- /dev/null
+++ b/third_party/rust/nix/Cargo.toml
@@ -0,0 +1,147 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+rust-version = "1.65"
+name = "nix"
+version = "0.27.1"
+authors = ["The nix-rust Project Developers"]
+include = [
+ "src/**/*",
+ "test/**/*",
+ "LICENSE",
+ "README.md",
+ "CHANGELOG.md",
+]
+description = "Rust friendly bindings to *nix APIs"
+readme = "README.md"
+categories = ["os::unix-apis"]
+license = "MIT"
+repository = "https://github.com/nix-rust/nix"
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = [
+ "--cfg",
+ "docsrs",
+]
+targets = [
+ "x86_64-unknown-linux-gnu",
+ "aarch64-linux-android",
+ "x86_64-apple-darwin",
+ "aarch64-apple-ios",
+ "x86_64-unknown-freebsd",
+ "x86_64-unknown-openbsd",
+ "x86_64-unknown-netbsd",
+ "x86_64-unknown-dragonfly",
+ "x86_64-fuchsia",
+ "x86_64-unknown-redox",
+ "x86_64-unknown-illumos",
+]
+
+[[test]]
+name = "test"
+path = "test/test.rs"
+
+[[test]]
+name = "test-aio-drop"
+path = "test/sys/test_aio_drop.rs"
+
+[[test]]
+name = "test-clearenv"
+path = "test/test_clearenv.rs"
+
+[[test]]
+name = "test-mount"
+path = "test/test_mount.rs"
+harness = false
+
+[[test]]
+name = "test-prctl"
+path = "test/sys/test_prctl.rs"
+
+[dependencies.bitflags]
+version = "2.3.1"
+
+[dependencies.cfg-if]
+version = "1.0"
+
+[dependencies.libc]
+version = "0.2.147"
+features = ["extra_traits"]
+
+[dependencies.memoffset]
+version = "0.9"
+optional = true
+
+[dependencies.pin-utils]
+version = "0.1.0"
+optional = true
+
+[dev-dependencies.assert-impl]
+version = "0.1"
+
+[dev-dependencies.parking_lot]
+version = "0.12"
+
+[dev-dependencies.rand]
+version = "0.8"
+
+[dev-dependencies.semver]
+version = "1.0.7"
+
+[dev-dependencies.tempfile]
+version = "3.7.1"
+
+[features]
+acct = []
+aio = ["pin-utils"]
+default = []
+dir = ["fs"]
+env = []
+event = []
+feature = []
+fs = []
+hostname = []
+inotify = []
+ioctl = []
+kmod = []
+mman = []
+mount = ["uio"]
+mqueue = ["fs"]
+net = ["socket"]
+personality = []
+poll = []
+process = []
+pthread = []
+ptrace = ["process"]
+quota = []
+reboot = []
+resource = []
+sched = ["process"]
+signal = ["process"]
+socket = ["memoffset"]
+term = []
+time = []
+ucontext = ["signal"]
+uio = []
+user = ["feature"]
+zerocopy = [
+ "fs",
+ "uio",
+]
+
+[target."cfg(any(target_os = \"android\", target_os = \"linux\"))".dev-dependencies.caps]
+version = "0.5.3"
+
+[target."cfg(target_os = \"freebsd\")".dev-dependencies.sysctl]
+version = "0.4"
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..e172de2750
--- /dev/null
+++ b/third_party/rust/nix/README.md
@@ -0,0 +1,120 @@
+# Rust bindings to *nix APIs
+
+[![Cirrus Build Status](https://api.cirrus-ci.com/github/nix-rust/nix.svg)](https://cirrus-ci.com/github/nix-rust/nix)
+[![crates.io](https://img.shields.io/crates/v/nix.svg)](https://crates.io/crates/nix)
+
+[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](https://man7.org/linux/man-pages/man2/gethostname.2.html) system
+call:
+
+```rust,ignore
+// libc api (unsafe, requires handling return code/errno)
+pub unsafe extern fn gethostname(name: *mut c_char, len: size_t) -> c_int;
+
+// nix api (returns a nix::Result<OsString>)
+pub fn gethostname() -> Result<OsString>;
+```
+
+## Supported Platforms
+
+nix target support consists of three tiers. While nix attempts to support all
+platforms supported by [libc](https://github.com/rust-lang/libc), only some
+platforms are actively supported due to either technical or manpower
+limitations. Support for platforms is split into three tiers:
+
+ * Tier 1 - Builds and tests for this target are run in CI. Failures of either
+ block the inclusion of new code.
+ * Tier 2 - Builds for this target are run in CI. Failures during the build
+ blocks the inclusion of new code. Tests may be run, but failures
+ in tests don't block the inclusion of new code.
+ * Tier 3 - Builds for this target are run in CI. Failures during the build
+ *do not* necessarily block the inclusion of new code. That is, at
+ our discretion a Tier 3 target may be dropped at any time, if it
+ would otherwise block development.
+
+Platforms not listed are supported on a best-effort basis, relying on our users
+to report any problems.
+
+The following targets are supported by `nix`:
+
+<table>
+ <tr>
+ <th>Tier 1</th>
+ <th>Tier 2</th>
+ <th>Tier 3</th>
+ </tr>
+ <tr>
+ <td>
+ <ul>
+ <li>aarch64-apple-darwin</li>
+ <li>aarch64-unknown-linux-gnu</li>
+ <li>arm-unknown-linux-gnueabi</li>
+ <li>armv7-unknown-linux-gnueabihf</li>
+ <li>i686-unknown-freebsd</li>
+ <li>i686-unknown-linux-gnu</li>
+ <li>i686-unknown-linux-musl</li>
+ <li>mips-unknown-linux-gnu</li>
+ <li>mips64-unknown-linux-gnuabi64</li>
+ <li>mips64el-unknown-linux-gnuabi64</li>
+ <li>mipsel-unknown-linux-gnu</li>
+ <li>powerpc64le-unknown-linux-gnu</li>
+ <li>x86_64-unknown-freebsd</li>
+ <li>x86_64-unknown-linux-gnu</li>
+ <li>x86_64-unknown-linux-musl</li>
+ </ul>
+ </td>
+ <td>
+ <ul>
+ <li>aarch64-apple-ios</li>
+ <li>aarch64-linux-android</li>
+ <li>arm-linux-androideabi</li>
+ <li>arm-unknown-linux-musleabi</li>
+ <li>armv7-linux-androideabi</li>
+ <li>i686-linux-android</li>
+ <li>s390x-unknown-linux-gnu</li>
+ <li>x86_64-linux-android</li>
+ <li>x86_64-unknown-illumos</li>
+ <li>x86_64-unknown-netbsd</li>
+ </td>
+ <td>
+ <li>armv7-unknown-linux-uclibceabihf</li>
+ <li>powerpc64-unknown-linux-gnu</li>
+ <li>x86_64-fuchsia</li>
+ <li>x86_64-unknown-dragonfly</li>
+ <li>x86_64-unknown-haiku</li>
+ <li>x86_64-unknown-linux-gnux32</li>
+ <li>x86_64-unknown-openbsd</li>
+ <li>x86_64-unknown-redox</li>
+ </td>
+ </tr>
+</table>
+
+## Minimum Supported Rust Version (MSRV)
+
+nix is supported on Rust 1.65 and higher. Its MSRV will not be
+changed in the future without bumping the major or minor version.
+
+## Contributing
+
+Contributions are very welcome. Please See [CONTRIBUTING](CONTRIBUTING.md) for
+additional details.
+
+Feel free to join us in [the nix-rust/nix](https://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/src/dir.rs b/third_party/rust/nix/src/dir.rs
new file mode 100644
index 0000000000..96a5843bc6
--- /dev/null
+++ b/third_party/rust/nix/src/dir.rs
@@ -0,0 +1,282 @@
+//! List directory contents
+
+use crate::errno::Errno;
+use crate::fcntl::{self, OFlag};
+use crate::sys;
+use crate::{Error, NixPath, Result};
+use cfg_if::cfg_if;
+use std::ffi;
+use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
+use std::ptr;
+
+#[cfg(target_os = "linux")]
+use libc::{dirent64 as dirent, readdir64_r as readdir_r};
+
+#[cfg(not(target_os = "linux"))]
+use libc::{dirent, readdir_r};
+
+/// An open directory.
+///
+/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences:
+/// * can be opened from a file descriptor (as returned by `openat`, 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).
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub struct Dir(ptr::NonNull<libc::DIR>);
+
+impl Dir {
+ /// Opens the given path as with `fcntl::open`.
+ pub fn open<P: ?Sized + NixPath>(
+ path: &P,
+ oflag: OFlag,
+ mode: sys::stat::Mode,
+ ) -> Result<Self> {
+ let fd = fcntl::open(path, oflag, mode)?;
+ Dir::from_fd(fd)
+ }
+
+ /// Opens the given path as with `fcntl::openat`.
+ pub fn openat<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.
+ #[doc(alias("fdopendir"))]
+ pub fn from_fd(fd: RawFd) -> Result<Self> {
+ let d = ptr::NonNull::new(unsafe { libc::fdopendir(fd) }).ok_or_else(
+ || {
+ let e = Error::last();
+ unsafe { libc::close(fd) };
+ 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.as_ptr()) }
+ }
+}
+
+impl Drop for Dir {
+ fn drop(&mut self) {
+ let e = Errno::result(unsafe { libc::closedir(self.0.as_ptr()) });
+ if !std::thread::panicking() && e == Err(Errno::EBADF) {
+ panic!("Closing an invalid file descriptor!");
+ };
+ }
+}
+
+// The pass by mut is technically needless only because the inner NonNull is
+// Copy. But philosophically we're mutating the Dir, so we pass by mut.
+#[allow(clippy::needless_pass_by_ref_mut)]
+fn next(dir: &mut Dir) -> Option<Result<Entry>> {
+ unsafe {
+ // Note: POSIX specifies that portable applications should dynamically allocate a
+ // buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
+ // for the NUL byte. It doesn't look like the std library does this; it just uses
+ // fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
+ // Probably fine here too then.
+ let mut ent = std::mem::MaybeUninit::<dirent>::uninit();
+ let mut result = ptr::null_mut();
+ if let Err(e) = Errno::result(readdir_r(
+ dir.0.as_ptr(),
+ ent.as_mut_ptr(),
+ &mut result,
+ )) {
+ return Some(Err(e));
+ }
+ if result.is_null() {
+ return None;
+ }
+ assert_eq!(result, ent.as_mut_ptr());
+ Some(Ok(Entry(ent.assume_init())))
+ }
+}
+
+/// Return type of [`Dir::iter`].
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub struct Iter<'d>(&'d mut Dir);
+
+impl<'d> Iterator for Iter<'d> {
+ type Item = Result<Entry>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ next(self.0)
+ }
+}
+
+impl<'d> Drop for Iter<'d> {
+ fn drop(&mut self) {
+ unsafe { libc::rewinddir((self.0).0.as_ptr()) }
+ }
+}
+
+/// The return type of [Dir::into_iter]
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub struct OwningIter(Dir);
+
+impl Iterator for OwningIter {
+ type Item = Result<Entry>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ next(&mut self.0)
+ }
+}
+
+/// The file descriptor continues to be owned by the `OwningIter`,
+/// so callers must not keep a `RawFd` after the `OwningIter` is dropped.
+impl AsRawFd for OwningIter {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl IntoIterator for Dir {
+ type Item = Result<Entry>;
+ type IntoIter = OwningIter;
+
+ /// Creates a owning iterator, that is, one that takes ownership of the
+ /// `Dir`. The `Dir` cannot be used after calling this. This can be useful
+ /// when you have a function that both creates a `Dir` instance and returns
+ /// an `Iterator`.
+ ///
+ /// Example:
+ ///
+ /// ```
+ /// use nix::{dir::Dir, fcntl::OFlag, sys::stat::Mode};
+ /// use std::{iter::Iterator, string::String};
+ ///
+ /// fn ls_upper(dirname: &str) -> impl Iterator<Item=String> {
+ /// let d = Dir::open(dirname, OFlag::O_DIRECTORY, Mode::S_IXUSR).unwrap();
+ /// d.into_iter().map(|x| x.unwrap().file_name().as_ref().to_string_lossy().to_ascii_uppercase())
+ /// }
+ /// ```
+ fn into_iter(self) -> Self::IntoIter {
+ OwningIter(self)
+ }
+}
+
+/// A directory entry, similar to `std::fs::DirEntry`.
+///
+/// Note that unlike the std version, this may represent the `.` or `..` entries.
+#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
+#[repr(transparent)]
+pub struct Entry(dirent);
+
+/// Type of file referenced by a directory entry
+#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
+pub enum Type {
+ /// FIFO (Named pipe)
+ Fifo,
+ /// Character device
+ CharacterDevice,
+ /// Directory
+ Directory,
+ /// Block device
+ BlockDevice,
+ /// Regular file
+ File,
+ /// Symbolic link
+ Symlink,
+ /// Unix-domain socket
+ Socket,
+}
+
+impl Entry {
+ /// Returns the inode number (`d_ino`) of the underlying `dirent`.
+ #[allow(clippy::useless_conversion)] // Not useless on all OSes
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ pub fn ino(&self) -> u64 {
+ cfg_if! {
+ if #[cfg(any(target_os = "aix",
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "l4re",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "solaris"))] {
+ self.0.d_ino as u64
+ } else {
+ u64::from(self.0.d_fileno)
+ }
+ }
+ }
+
+ /// Returns the bare file name of this directory entry without any other leading path component.
+ pub fn file_name(&self) -> &ffi::CStr {
+ unsafe { ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
+ }
+
+ /// Returns the type of this directory entry, if known.
+ ///
+ /// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known;
+ /// notably, some Linux filesystems don't implement this. The caller should use `stat` or
+ /// `fstat` if this returns `None`.
+ pub fn file_type(&self) -> Option<Type> {
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku"
+ )))]
+ match self.0.d_type {
+ libc::DT_FIFO => Some(Type::Fifo),
+ libc::DT_CHR => Some(Type::CharacterDevice),
+ libc::DT_DIR => Some(Type::Directory),
+ libc::DT_BLK => Some(Type::BlockDevice),
+ libc::DT_REG => Some(Type::File),
+ libc::DT_LNK => Some(Type::Symlink),
+ libc::DT_SOCK => Some(Type::Socket),
+ /* libc::DT_UNKNOWN | */ _ => None,
+ }
+
+ // illumos, Solaris, and Haiku systems do not have the d_type member at all:
+ #[cfg(any(
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku"
+ ))]
+ None
+ }
+}
diff --git a/third_party/rust/nix/src/env.rs b/third_party/rust/nix/src/env.rs
new file mode 100644
index 0000000000..95177a1d2a
--- /dev/null
+++ b/third_party/rust/nix/src/env.rs
@@ -0,0 +1,64 @@
+//! Environment variables
+use cfg_if::cfg_if;
+use std::fmt;
+
+/// Indicates that [`clearenv`] failed for some unknown reason
+#[derive(Clone, Copy, Debug)]
+pub struct ClearEnvError;
+
+impl fmt::Display for ClearEnvError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "clearenv failed")
+ }
+}
+
+impl std::error::Error for ClearEnvError {}
+
+/// Clear the environment of all name-value pairs.
+///
+/// On platforms where libc provides `clearenv()`, it will be used. libc's
+/// `clearenv()` is documented to return an error code but not set errno; if the
+/// return value indicates a failure, this function will return
+/// [`ClearEnvError`].
+///
+/// On platforms where libc does not provide `clearenv()`, a fallback
+/// implementation will be used that iterates over all environment variables and
+/// removes them one-by-one.
+///
+/// # Safety
+///
+/// This function is not threadsafe and can cause undefined behavior in
+/// combination with `std::env` or other program components that access the
+/// environment. See, for example, the discussion on `std::env::remove_var`; this
+/// function is a case of an "inherently unsafe non-threadsafe API" dealing with
+/// the environment.
+///
+/// The caller must ensure no other threads access the process environment while
+/// this function executes and that no raw pointers to an element of libc's
+/// `environ` is currently held. The latter is not an issue if the only other
+/// environment access in the program is via `std::env`, but the requirement on
+/// thread safety must still be upheld.
+pub unsafe fn clearenv() -> std::result::Result<(), ClearEnvError> {
+ cfg_if! {
+ if #[cfg(any(target_os = "fuchsia",
+ target_os = "wasi",
+ target_env = "uclibc",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten"))] {
+ let ret = libc::clearenv();
+ } else {
+ use std::env;
+ for (name, _) in env::vars_os() {
+ env::remove_var(name);
+ }
+ let ret = 0;
+ }
+ }
+
+ if ret == 0 {
+ Ok(())
+ } else {
+ Err(ClearEnvError)
+ }
+}
diff --git a/third_party/rust/nix/src/errno.rs b/third_party/rust/nix/src/errno.rs
new file mode 100644
index 0000000000..50b35248f8
--- /dev/null
+++ b/third_party/rust/nix/src/errno.rs
@@ -0,0 +1,3380 @@
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{c_int, c_void};
+use std::convert::TryFrom;
+use std::{error, fmt, io};
+
+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(any(target_os = "android",
+ target_os = "netbsd",
+ target_os = "openbsd"))] {
+ unsafe fn errno_location() -> *mut c_int {
+ libc::__errno()
+ }
+ } else if #[cfg(any(target_os = "linux",
+ target_os = "redox",
+ target_os = "dragonfly",
+ target_os = "fuchsia"))] {
+ unsafe fn errno_location() -> *mut c_int {
+ libc::__errno_location()
+ }
+ } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
+ unsafe fn errno_location() -> *mut c_int {
+ libc::___errno()
+ }
+ } else if #[cfg(any(target_os = "haiku",))] {
+ unsafe fn errno_location() -> *mut c_int {
+ libc::_errnop()
+ }
+ } else if #[cfg(any(target_os = "aix"))] {
+ unsafe fn errno_location() -> *mut c_int {
+ libc::_Errno()
+ }
+ }
+}
+
+/// Sets the platform-specific errno to no-error
+fn clear() {
+ // Safe because errno is a thread-local variable
+ unsafe {
+ *errno_location() = 0;
+ }
+}
+
+/// Returns the platform-specific value of errno
+pub fn errno() -> i32 {
+ unsafe { *errno_location() }
+}
+
+impl Errno {
+ pub fn last() -> Self {
+ last()
+ }
+
+ pub fn desc(self) -> &'static str {
+ desc(self)
+ }
+
+ pub const fn from_i32(err: i32) -> Errno {
+ from_i32(err)
+ }
+
+ pub 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.
+ #[inline]
+ pub fn result<S: ErrnoSentinel + PartialEq<S>>(value: S) -> Result<S> {
+ if value == S::sentinel() {
+ Err(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 {
+ -1isize as *mut c_void
+ }
+}
+
+impl ErrnoSentinel for libc::sighandler_t {
+ fn sentinel() -> Self {
+ libc::SIG_ERR
+ }
+}
+
+impl error::Error for Errno {}
+
+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)
+ }
+}
+
+impl TryFrom<io::Error> for Errno {
+ type Error = io::Error;
+
+ fn try_from(ioerror: io::Error) -> std::result::Result<Self, io::Error> {
+ ioerror.raw_os_error().map(Errno::from_i32).ok_or(ioerror)
+ }
+}
+
+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",
+ #[cfg(not(target_os = "haiku"))]
+ 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",
+ #[cfg(not(target_os = "haiku"))]
+ ESOCKTNOSUPPORT => "Socket type not supported",
+ #[cfg(not(target_os = "haiku"))]
+ EPFNOSUPPORT => "Protocol family not supported",
+ #[cfg(not(target_os = "haiku"))]
+ 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",
+ #[cfg(not(target_os = "haiku"))]
+ 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",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ECHRNG => "Channel number out of range",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EL2NSYNC => "Level 2 not synchronized",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EL3HLT => "Level 3 halted",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EL3RST => "Level 3 reset",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ELNRNG => "Link number out of range",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EUNATCH => "Protocol driver not attached",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOCSI => "No CSI structure available",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EL2HLT => "Level 2 halted",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EBADE => "Invalid exchange",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EBADR => "Invalid request descriptor",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EXFULL => "Exchange full",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOANO => "No anode",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EBADRQC => "Invalid request code",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EBADSLT => "Invalid slot",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EBFONT => "Bad font file format",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOSTR => "Device not a stream",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENODATA => "No data available",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ETIME => "Timer expired",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOSR => "Out of streams resources",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENONET => "Machine is not on the network",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOPKG => "Package not installed",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EREMOTE => "Object is remote",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOLINK => "Link has been severed",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EADV => "Advertise error",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ESRMNT => "Srmount error",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ECOMM => "Communication error on send",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EPROTO => "Protocol error",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EMULTIHOP => "Multihop attempted",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EDOTDOT => "RFS specific error",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "fuchsia"
+ ))]
+ EBADMSG => "Not a data message",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ EBADMSG => "Trying to read unreadable message",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "fuchsia",
+ target_os = "haiku"
+ ))]
+ EOVERFLOW => "Value too large for defined data type",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOTUNIQ => "Name not unique on network",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EBADFD => "File descriptor in bad state",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EREMCHG => "Remote address changed",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ELIBACC => "Can not access a needed shared library",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ELIBBAD => "Accessing a corrupted shared library",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ELIBSCN => ".lib section in a.out corrupted",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ELIBMAX => "Attempting to link in too many shared libraries",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ELIBEXEC => "Cannot exec a shared library directly",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia",
+ target_os = "openbsd"
+ ))]
+ EILSEQ => "Illegal byte sequence",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ERESTART => "Interrupted system call should be restarted",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ESTRPIPE => "Streams pipe error",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EUSERS => "Too many users",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ EOPNOTSUPP => "Operation not supported on transport endpoint",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ ESTALE => "Stale file handle",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EUCLEAN => "Structure needs cleaning",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ ENOTNAM => "Not a XENIX named type file",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ ENAVAIL => "No XENIX semaphores available",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EISNAM => "Is a named type file",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EREMOTEIO => "Remote I/O error",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EDQUOT => "Quota exceeded",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "openbsd",
+ target_os = "dragonfly"
+ ))]
+ ENOMEDIUM => "No medium found",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "openbsd"
+ ))]
+ EMEDIUMTYPE => "Wrong medium type",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia",
+ target_os = "haiku"
+ ))]
+ ECANCELED => "Operation canceled",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ ENOKEY => "Required key not available",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EKEYEXPIRED => "Key has expired",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EKEYREVOKED => "Key has been revoked",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EKEYREJECTED => "Key was rejected by service",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "fuchsia"
+ ))]
+ EOWNERDEAD => "Owner died",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ EOWNERDEAD => "Process died with lock",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "aix",
+ target_os = "fuchsia"
+ ))]
+ ENOTRECOVERABLE => "State not recoverable",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ ENOTRECOVERABLE => "Lock is not recoverable",
+
+ #[cfg(any(
+ all(target_os = "linux", not(target_arch = "mips")),
+ target_os = "fuchsia"
+ ))]
+ ERFKILL => "Operation not possible due to RF-kill",
+
+ #[cfg(any(
+ all(target_os = "linux", not(target_arch = "mips")),
+ target_os = "fuchsia"
+ ))]
+ 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",
+ target_os = "redox"
+ ))]
+ EMULTIHOP => "Multihop attempted",
+
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "redox"
+ ))]
+ 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",
+ target_os = "redox",
+ target_os = "illumos",
+ target_os = "solaris"
+ ))]
+ 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",
+ target_os = "redox",
+ target_os = "haiku"
+ ))]
+ EILSEQ => "Illegal byte sequence",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "haiku"
+ ))]
+ ENOATTR => "Attribute not found",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "haiku"
+ ))]
+ EBADMSG => "Bad message",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "haiku"
+ ))]
+ EPROTO => "Protocol error",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd"
+ ))]
+ ENOTRECOVERABLE => "State not recoverable",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd"
+ ))]
+ EOWNERDEAD => "Previous owner died",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku"
+ ))]
+ ENOTSUP => "Operation not supported",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "aix",
+ 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 = "aix",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ EUSERS => "Too many users",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku"
+ ))]
+ EDQUOT => "Disc quota exceeded",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku"
+ ))]
+ ESTALE => "Stale NFS file handle",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "aix",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ 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 = "aix",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ 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",
+ target_os = "haiku"
+ ))]
+ EMULTIHOP => "Reserved",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "aix",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ ENODATA => "No message available on STREAM",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "netbsd",
+ target_os = "haiku"
+ ))]
+ ENOLINK => "Reserved",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "aix",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ ENOSR => "No STREAM resources",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "aix",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ ENOSTR => "Not a STREAM",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "aix",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ ETIME => "STREAM ioctl timeout",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris"
+ ))]
+ 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 = "illumos", target_os = "solaris"))]
+ EDEADLOCK => "Resource deadlock would occur",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ ELOCKUNMAPPED => "Locked lock was unmapped",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ ENOTACTIVE => "Facility is not active",
+ }
+}
+
+#[cfg(any(target_os = "linux", target_os = "android", target_os = "fuchsia"))]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ 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,
+ }
+
+ impl Errno {
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ pub const ENOTSUP: Errno = Errno::EOPNOTSUPP;
+ }
+
+ pub const 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 {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ 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,
+ }
+
+ impl Errno {
+ pub const ELAST: Errno = Errno::EQFULL;
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ }
+
+ pub const 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 {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ 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,
+ }
+
+ impl Errno {
+ pub const ELAST: Errno = Errno::EOWNERDEAD;
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
+ }
+
+ pub const 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 {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ 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,
+ ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
+ EOWNERDEAD = libc::EOWNERDEAD,
+ EASYNC = libc::EASYNC,
+ }
+
+ impl Errno {
+ 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 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 {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ 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,
+ EBADMSG = libc::EBADMSG,
+ ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
+ EOWNERDEAD = libc::EOWNERDEAD,
+ EPROTO = libc::EPROTO,
+ }
+
+ impl Errno {
+ pub const ELAST: Errno = Errno::ENOTSUP;
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ }
+
+ pub const 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,
+ libc::EBADMSG => EBADMSG,
+ libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
+ libc::EOWNERDEAD => EOWNERDEAD,
+ libc::EPROTO => EPROTO,
+ _ => UnknownErrno,
+ }
+ }
+}
+
+#[cfg(target_os = "netbsd")]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ 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,
+ }
+
+ impl Errno {
+ pub const ELAST: Errno = Errno::ENOTSUP;
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ }
+
+ pub const 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,
+ }
+ }
+}
+
+#[cfg(target_os = "redox")]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ 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,
+ EUSERS = libc::EUSERS,
+ EDQUOT = libc::EDQUOT,
+ ESTALE = libc::ESTALE,
+ EREMOTE = libc::EREMOTE,
+ ENOLCK = libc::ENOLCK,
+ ENOSYS = libc::ENOSYS,
+ EIDRM = libc::EIDRM,
+ ENOMSG = libc::ENOMSG,
+ EOVERFLOW = libc::EOVERFLOW,
+ EILSEQ = libc::EILSEQ,
+ ECANCELED = libc::ECANCELED,
+ EBADMSG = libc::EBADMSG,
+ ENODATA = libc::ENODATA,
+ ENOSR = libc::ENOSR,
+ ENOSTR = libc::ENOSTR,
+ ETIME = libc::ETIME,
+ EMULTIHOP = libc::EMULTIHOP,
+ ENOLINK = libc::ENOLINK,
+ EPROTO = libc::EPROTO,
+ }
+
+ impl Errno {
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ }
+
+ pub const 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::EUSERS => EUSERS,
+ libc::EDQUOT => EDQUOT,
+ libc::ESTALE => ESTALE,
+ libc::EREMOTE => EREMOTE,
+ libc::ENOLCK => ENOLCK,
+ libc::ENOSYS => ENOSYS,
+ libc::EIDRM => EIDRM,
+ libc::ENOMSG => ENOMSG,
+ libc::EOVERFLOW => EOVERFLOW,
+ libc::EILSEQ => EILSEQ,
+ libc::ECANCELED => ECANCELED,
+ libc::EBADMSG => EBADMSG,
+ libc::ENODATA => ENODATA,
+ libc::ENOSR => ENOSR,
+ libc::ENOSTR => ENOSTR,
+ libc::ETIME => ETIME,
+ libc::EMULTIHOP => EMULTIHOP,
+ libc::ENOLINK => ENOLINK,
+ libc::EPROTO => EPROTO,
+ _ => UnknownErrno,
+ }
+ }
+}
+
+#[cfg(any(target_os = "illumos", target_os = "solaris"))]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ 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,
+ 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,
+ EDEADLK = libc::EDEADLK,
+ ENOLCK = libc::ENOLCK,
+ ECANCELED = libc::ECANCELED,
+ ENOTSUP = libc::ENOTSUP,
+ EDQUOT = libc::EDQUOT,
+ EBADE = libc::EBADE,
+ EBADR = libc::EBADR,
+ EXFULL = libc::EXFULL,
+ ENOANO = libc::ENOANO,
+ EBADRQC = libc::EBADRQC,
+ EBADSLT = libc::EBADSLT,
+ EDEADLOCK = libc::EDEADLOCK,
+ EBFONT = libc::EBFONT,
+ EOWNERDEAD = libc::EOWNERDEAD,
+ ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
+ 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,
+ ELOCKUNMAPPED = libc::ELOCKUNMAPPED,
+ ENOTACTIVE = libc::ENOTACTIVE,
+ EMULTIHOP = libc::EMULTIHOP,
+ EBADMSG = libc::EBADMSG,
+ ENAMETOOLONG = libc::ENAMETOOLONG,
+ 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,
+ ENOSYS = libc::ENOSYS,
+ ELOOP = libc::ELOOP,
+ ERESTART = libc::ERESTART,
+ ESTRPIPE = libc::ESTRPIPE,
+ ENOTEMPTY = libc::ENOTEMPTY,
+ 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,
+ }
+
+ impl Errno {
+ pub const ELAST: Errno = Errno::ESTALE;
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ }
+
+ pub const 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::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::EDEADLK => EDEADLK,
+ libc::ENOLCK => ENOLCK,
+ libc::ECANCELED => ECANCELED,
+ libc::ENOTSUP => ENOTSUP,
+ libc::EDQUOT => EDQUOT,
+ libc::EBADE => EBADE,
+ libc::EBADR => EBADR,
+ libc::EXFULL => EXFULL,
+ libc::ENOANO => ENOANO,
+ libc::EBADRQC => EBADRQC,
+ libc::EBADSLT => EBADSLT,
+ libc::EDEADLOCK => EDEADLOCK,
+ libc::EBFONT => EBFONT,
+ libc::EOWNERDEAD => EOWNERDEAD,
+ libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
+ 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::ELOCKUNMAPPED => ELOCKUNMAPPED,
+ libc::ENOTACTIVE => ENOTACTIVE,
+ libc::EMULTIHOP => EMULTIHOP,
+ libc::EBADMSG => EBADMSG,
+ libc::ENAMETOOLONG => ENAMETOOLONG,
+ 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::ENOSYS => ENOSYS,
+ libc::ELOOP => ELOOP,
+ libc::ERESTART => ERESTART,
+ libc::ESTRPIPE => ESTRPIPE,
+ libc::ENOTEMPTY => ENOTEMPTY,
+ 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,
+ _ => UnknownErrno,
+ }
+ }
+}
+
+#[cfg(target_os = "haiku")]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ 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,
+ 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,
+ ENOTSUP = libc::ENOTSUP,
+ 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,
+ ETIMEDOUT = libc::ETIMEDOUT,
+ ECONNREFUSED = libc::ECONNREFUSED,
+ ELOOP = libc::ELOOP,
+ ENAMETOOLONG = libc::ENAMETOOLONG,
+ EHOSTDOWN = libc::EHOSTDOWN,
+ EHOSTUNREACH = libc::EHOSTUNREACH,
+ ENOTEMPTY = libc::ENOTEMPTY,
+ EDQUOT = libc::EDQUOT,
+ ESTALE = libc::ESTALE,
+ ENOLCK = libc::ENOLCK,
+ ENOSYS = libc::ENOSYS,
+ EIDRM = libc::EIDRM,
+ ENOMSG = libc::ENOMSG,
+ EOVERFLOW = libc::EOVERFLOW,
+ ECANCELED = libc::ECANCELED,
+ EILSEQ = libc::EILSEQ,
+ ENOATTR = libc::ENOATTR,
+ EBADMSG = libc::EBADMSG,
+ EMULTIHOP = libc::EMULTIHOP,
+ ENOLINK = libc::ENOLINK,
+ EPROTO = libc::EPROTO,
+ }
+
+ impl Errno {
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
+ }
+
+ pub const 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::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::ENOTSUP => ENOTSUP,
+ 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::ETIMEDOUT => ETIMEDOUT,
+ libc::ECONNREFUSED => ECONNREFUSED,
+ libc::ELOOP => ELOOP,
+ libc::ENAMETOOLONG => ENAMETOOLONG,
+ libc::EHOSTDOWN => EHOSTDOWN,
+ libc::EHOSTUNREACH => EHOSTUNREACH,
+ libc::ENOTEMPTY => ENOTEMPTY,
+ libc::EDQUOT => EDQUOT,
+ libc::ESTALE => ESTALE,
+ libc::ENOLCK => ENOLCK,
+ libc::ENOSYS => ENOSYS,
+ libc::EIDRM => EIDRM,
+ libc::ENOMSG => ENOMSG,
+ libc::EOVERFLOW => EOVERFLOW,
+ libc::ECANCELED => ECANCELED,
+ libc::EILSEQ => EILSEQ,
+ libc::ENOATTR => ENOATTR,
+ libc::EBADMSG => EBADMSG,
+ libc::EMULTIHOP => EMULTIHOP,
+ libc::ENOLINK => ENOLINK,
+ libc::EPROTO => EPROTO,
+ _ => UnknownErrno,
+ }
+ }
+}
+
+#[cfg(target_os = "aix")]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ 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,
+ 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,
+ 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,
+ ECHRNG = libc::ECHRNG,
+ EL2NSYNC = libc::EL2NSYNC,
+ EL3HLT = libc::EL3HLT,
+ EL3RST = libc::EL3RST,
+ ELNRNG = libc::ELNRNG,
+ EUNATCH = libc::EUNATCH,
+ ENOCSI = libc::ENOCSI,
+ EL2HLT = libc::EL2HLT,
+ ENOLINK = libc::ENOLINK,
+ EPROTO = libc::EPROTO,
+ EMULTIHOP = libc::EMULTIHOP,
+ EBADMSG = libc::EBADMSG,
+ EOVERFLOW = libc::EOVERFLOW,
+ EILSEQ = libc::EILSEQ,
+ ERESTART = libc::ERESTART,
+ EOWNERDEAD = libc::EOWNERDEAD,
+ ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
+ ENOTSUP = libc::ENOTSUP,
+ EPROCLIM = libc::EPROCLIM,
+ EUSERS = libc::EUSERS,
+ EDQUOT = libc::EDQUOT,
+ ESTALE = libc::ESTALE,
+ EREMOTE = libc::EREMOTE,
+ ECANCELED = libc::ECANCELED,
+ ENODATA = libc::ENODATA,
+ ENOSR = libc::ENOSR,
+ ENOSTR = libc::ENOSTR,
+ ETIME = libc::ETIME,
+ EOPNOTSUPP = libc::EOPNOTSUPP,
+ }
+
+ pub const 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::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::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::ECHRNG => ECHRNG,
+ libc::EL2NSYNC => EL2NSYNC,
+ libc::EL3HLT => EL3HLT,
+ libc::EL3RST => EL3RST,
+ libc::ELNRNG => ELNRNG,
+ libc::EUNATCH => EUNATCH,
+ libc::ENOCSI => ENOCSI,
+ libc::EL2HLT => EL2HLT,
+ libc::ENOLINK => ENOLINK,
+ libc::EPROTO => EPROTO,
+ libc::EMULTIHOP => EMULTIHOP,
+ libc::EBADMSG => EBADMSG,
+ libc::EOVERFLOW => EOVERFLOW,
+ libc::EILSEQ => EILSEQ,
+ libc::ERESTART => ERESTART,
+ libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
+ libc::EOWNERDEAD => EOWNERDEAD,
+ libc::ENOTSUP => ENOTSUP,
+ libc::EPROCLIM => EPROCLIM,
+ libc::EUSERS => EUSERS,
+ libc::EDQUOT => EDQUOT,
+ libc::ESTALE => ESTALE,
+ libc::EREMOTE => EREMOTE,
+ libc::ECANCELED => ECANCELED,
+ libc::ENODATA => ENODATA,
+ libc::ENOSR => ENOSR,
+ libc::ENOSTR => ENOSTR,
+ libc::ETIME => ETIME,
+ libc::EOPNOTSUPP => EOPNOTSUPP,
+ _ => UnknownErrno,
+ }
+ }
+}
diff --git a/third_party/rust/nix/src/fcntl.rs b/third_party/rust/nix/src/fcntl.rs
new file mode 100644
index 0000000000..9bfecda5ac
--- /dev/null
+++ b/third_party/rust/nix/src/fcntl.rs
@@ -0,0 +1,993 @@
+use crate::errno::Errno;
+use libc::{self, c_char, c_int, c_uint, size_t, ssize_t};
+use std::ffi::OsString;
+#[cfg(not(target_os = "redox"))]
+use std::os::raw;
+use std::os::unix::ffi::OsStringExt;
+use std::os::unix::io::RawFd;
+// For splice and copy_file_range
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux"
+))]
+use std::{
+ os::unix::io::{AsFd, AsRawFd},
+ ptr,
+};
+
+#[cfg(feature = "fs")]
+use crate::{sys::stat::Mode, NixPath, Result};
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "wasi",
+ target_env = "uclibc",
+ target_os = "freebsd"
+))]
+#[cfg(feature = "fs")]
+pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
+
+#[cfg(not(target_os = "redox"))]
+#[cfg(any(feature = "fs", feature = "process"))]
+libc_bitflags! {
+ #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "process"))))]
+ pub struct AtFlags: c_int {
+ AT_REMOVEDIR;
+ AT_SYMLINK_FOLLOW;
+ 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;
+ #[cfg(not(target_os = "android"))]
+ AT_EACCESS;
+ }
+}
+
+#[cfg(any(feature = "fs", feature = "term"))]
+libc_bitflags!(
+ /// Configuration options for opened files.
+ #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term"))))]
+ pub struct OFlag: c_int {
+ /// Mask for the access mode of the file.
+ O_ACCMODE;
+ /// Use alternate I/O semantics.
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_ALT_IO;
+ /// Open the file in append-only mode.
+ O_APPEND;
+ /// Generate a signal when input or output becomes possible.
+ #[cfg(not(any(target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_DIRECT;
+ /// If the specified path isn't a directory, fail.
+ #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_DSYNC;
+ /// Error out if a file was not created.
+ O_EXCL;
+ /// Open for execute only.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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",
+ target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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",
+ target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_LARGEFILE;
+ /// Do not update the file last access time during `read(2)`s.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_NOATIME;
+ /// Don't attach the device as the process' controlling terminal.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_NOCTTY;
+ /// Same as `O_NONBLOCK`.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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_RDONLY`.
+ O_RDWR;
+ /// Similar to `O_DSYNC` but applies to `read`s instead.
+ #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_RSYNC;
+ /// Skip search permission checks.
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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",
+ target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_SHLOCK;
+ /// Implicitly follow each `write()` with an `fsync()`.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_SYNC;
+ /// Create an unnamed temporary file.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_TMPFILE;
+ /// Truncate an existing regular file to 0 length if it allows writing.
+ O_TRUNC;
+ /// Restore default TTY attributes.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_TTY_INIT;
+ /// Only allow writing.
+ ///
+ /// This should not be combined with `O_RDONLY` or `O_RDWR`.
+ O_WRONLY;
+ }
+);
+
+feature! {
+#![feature = "fs"]
+
+// The conversion is not identical on all operating systems.
+#[allow(clippy::useless_conversion)]
+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)
+}
+
+// The conversion is not identical on all operating systems.
+#[allow(clippy::useless_conversion)]
+#[cfg(not(target_os = "redox"))]
+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)
+}
+
+#[cfg(not(target_os = "redox"))]
+pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
+ old_dirfd: Option<RawFd>,
+ old_path: &P1,
+ new_dirfd: Option<RawFd>,
+ new_path: &P2,
+) -> Result<()> {
+ let res = old_path.with_nix_path(|old_cstr| {
+ new_path.with_nix_path(|new_cstr| unsafe {
+ libc::renameat(
+ at_rawfd(old_dirfd),
+ old_cstr.as_ptr(),
+ at_rawfd(new_dirfd),
+ new_cstr.as_ptr(),
+ )
+ })
+ })??;
+ Errno::result(res).map(drop)
+}
+}
+
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
+#[cfg(feature = "fs")]
+libc_bitflags! {
+ #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+ pub struct RenameFlags: u32 {
+ RENAME_EXCHANGE;
+ RENAME_NOREPLACE;
+ RENAME_WHITEOUT;
+ }
+}
+
+feature! {
+#![feature = "fs"]
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
+pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
+ old_dirfd: Option<RawFd>,
+ old_path: &P1,
+ new_dirfd: Option<RawFd>,
+ new_path: &P2,
+ flags: RenameFlags,
+) -> Result<()> {
+ let res = old_path.with_nix_path(|old_cstr| {
+ new_path.with_nix_path(|new_cstr| unsafe {
+ libc::renameat2(
+ at_rawfd(old_dirfd),
+ old_cstr.as_ptr(),
+ at_rawfd(new_dirfd),
+ new_cstr.as_ptr(),
+ flags.bits(),
+ )
+ })
+ })??;
+ Errno::result(res).map(drop)
+}
+
+fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> {
+ unsafe { v.set_len(len as usize) }
+ v.shrink_to_fit();
+ Ok(OsString::from_vec(v.to_vec()))
+}
+
+fn readlink_maybe_at<P: ?Sized + NixPath>(
+ dirfd: Option<RawFd>,
+ path: &P,
+ v: &mut Vec<u8>,
+) -> Result<libc::ssize_t> {
+ path.with_nix_path(|cstr| unsafe {
+ match dirfd {
+ #[cfg(target_os = "redox")]
+ Some(_) => unreachable!(),
+ #[cfg(not(target_os = "redox"))]
+ Some(dirfd) => libc::readlinkat(
+ dirfd,
+ cstr.as_ptr(),
+ v.as_mut_ptr() as *mut c_char,
+ v.capacity() as size_t,
+ ),
+ None => libc::readlink(
+ cstr.as_ptr(),
+ v.as_mut_ptr() as *mut c_char,
+ v.capacity() as size_t,
+ ),
+ }
+ })
+}
+
+fn inner_readlink<P: ?Sized + NixPath>(
+ dirfd: Option<RawFd>,
+ path: &P,
+) -> Result<OsString> {
+ let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
+
+ {
+ // simple case: result is strictly less than `PATH_MAX`
+ let res = readlink_maybe_at(dirfd, path, &mut v)?;
+ let len = Errno::result(res)?;
+ debug_assert!(len >= 0);
+ if (len as usize) < v.capacity() {
+ return wrap_readlink_result(v, res);
+ }
+ }
+
+ // Uh oh, the result is too long...
+ // Let's try to ask lstat how many bytes to allocate.
+ let mut try_size = {
+ let reported_size = match dirfd {
+ #[cfg(target_os = "redox")]
+ Some(_) => unreachable!(),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Some(dirfd) => {
+ let flags = if path.is_empty() {
+ AtFlags::AT_EMPTY_PATH
+ } else {
+ AtFlags::empty()
+ };
+ super::sys::stat::fstatat(
+ dirfd,
+ path,
+ flags | AtFlags::AT_SYMLINK_NOFOLLOW,
+ )
+ }
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "redox"
+ )))]
+ Some(dirfd) => super::sys::stat::fstatat(
+ dirfd,
+ path,
+ AtFlags::AT_SYMLINK_NOFOLLOW,
+ ),
+ None => super::sys::stat::lstat(path),
+ }
+ .map(|x| x.st_size)
+ .unwrap_or(0);
+
+ if reported_size > 0 {
+ // Note: even if `lstat`'s apparently valid answer turns out to be
+ // wrong, we will still read the full symlink no matter what.
+ reported_size as usize + 1
+ } else {
+ // If lstat doesn't cooperate, or reports an error, be a little less
+ // precise.
+ (libc::PATH_MAX as usize).max(128) << 1
+ }
+ };
+
+ loop {
+ {
+ v.reserve_exact(try_size);
+ let res = readlink_maybe_at(dirfd, path, &mut v)?;
+ let len = Errno::result(res)?;
+ debug_assert!(len >= 0);
+ if (len as usize) < v.capacity() {
+ return wrap_readlink_result(v, res);
+ }
+ }
+
+ // Ugh! Still not big enough!
+ match try_size.checked_shl(1) {
+ Some(next_size) => try_size = next_size,
+ // It's absurd that this would happen, but handle it sanely
+ // anyway.
+ None => break Err(Errno::ENAMETOOLONG),
+ }
+ }
+}
+
+pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
+ inner_readlink(None, path)
+}
+
+#[cfg(not(target_os = "redox"))]
+pub fn readlinkat<P: ?Sized + NixPath>(
+ dirfd: RawFd,
+ path: &P,
+) -> Result<OsString> {
+ inner_readlink(Some(dirfd), path)
+}
+
+/// Computes the raw fd consumed by a function of the form `*at`.
+#[cfg(not(target_os = "redox"))]
+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", target_os = "freebsd"))]
+#[cfg(feature = "fs")]
+libc_bitflags!(
+ /// Additional flags for file sealing, which allows for limiting operations on a file.
+ #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+ 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;
+ }
+);
+
+#[cfg(feature = "fs")]
+libc_bitflags!(
+ /// Additional configuration flags for `fcntl`'s `F_SETFD`.
+ #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+ pub struct FdFlag: c_int {
+ /// The file descriptor will automatically be closed during a successful `execve(2)`.
+ FD_CLOEXEC;
+ }
+);
+
+feature! {
+#![feature = "fs"]
+
+#[cfg(not(target_os = "redox"))]
+#[derive(Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
+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",
+ target_os = "freebsd"
+ ))]
+ F_ADD_SEALS(SealFlag),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "freebsd"
+ ))]
+ 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
+}
+
+#[cfg(target_os = "redox")]
+#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
+#[non_exhaustive]
+pub enum FcntlArg {
+ F_DUPFD(RawFd),
+ F_DUPFD_CLOEXEC(RawFd),
+ F_GETFD,
+ F_SETFD(FdFlag), // FD_FLAGS
+ F_GETFL,
+ F_SETFL(OFlag), // O_NONBLOCK
+}
+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()),
+ #[cfg(not(target_os = "redox"))]
+ F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
+ #[cfg(not(target_os = "redox"))]
+ F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
+ #[cfg(not(target_os = "redox"))]
+ F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "freebsd"
+ ))]
+ F_ADD_SEALS(flag) => {
+ libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits())
+ }
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "freebsd"
+ ))]
+ 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),
+ }
+ };
+
+ Errno::result(res)
+}
+
+// TODO: convert to libc_enum
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
+pub enum FlockArg {
+ LockShared,
+ LockExclusive,
+ Unlock,
+ LockSharedNonblock,
+ LockExclusiveNonblock,
+ UnlockNonblock,
+}
+
+#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
+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"))]
+#[cfg(feature = "zerocopy")]
+libc_bitflags! {
+ /// Additional flags to `splice` and friends.
+ #[cfg_attr(docsrs, doc(cfg(feature = "zerocopy")))]
+ 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;
+ }
+}
+
+feature! {
+#![feature = "zerocopy"]
+
+/// Copy a range of data from one file to another
+///
+/// The `copy_file_range` system call performs an in-kernel copy between
+/// file descriptors `fd_in` and `fd_out` without the additional cost of
+/// transferring data from the kernel to user space and back again. There may be
+/// additional optimizations for specific file systems. It copies up to `len`
+/// bytes of data from file descriptor `fd_in` to file descriptor `fd_out`,
+/// overwriting any data that exists within the requested range of the target
+/// file.
+///
+/// If the `off_in` and/or `off_out` arguments are used, the values
+/// will be mutated to reflect the new position within the file after
+/// copying. If they are not used, the relevant file descriptors will be seeked
+/// to the new position.
+///
+/// On successful completion the number of bytes actually copied will be
+/// returned.
+// Note: FreeBSD defines the offset argument as "off_t". Linux and Android
+// define it as "loff_t". But on both OSes, on all supported platforms, those
+// are 64 bits. So Nix uses i64 to make the docs simple and consistent.
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+pub fn copy_file_range<Fd1: AsFd, Fd2: AsFd>(
+ fd_in: Fd1,
+ off_in: Option<&mut i64>,
+ fd_out: Fd2,
+ off_out: Option<&mut i64>,
+ len: usize,
+) -> Result<usize> {
+ let off_in = off_in
+ .map(|offset| offset as *mut i64)
+ .unwrap_or(ptr::null_mut());
+ let off_out = off_out
+ .map(|offset| offset as *mut i64)
+ .unwrap_or(ptr::null_mut());
+
+ cfg_if::cfg_if! {
+ if #[cfg(target_os = "freebsd")] {
+ let ret = unsafe {
+ libc::copy_file_range(
+ fd_in.as_fd().as_raw_fd(),
+ off_in,
+ fd_out.as_fd().as_raw_fd(),
+ off_out,
+ len,
+ 0,
+ )
+ };
+ } else {
+ // May Linux distros still don't include copy_file_range in their
+ // libc implementations, so we need to make a direct syscall.
+ let ret = unsafe {
+ libc::syscall(
+ libc::SYS_copy_file_range,
+ fd_in,
+ off_in,
+ fd_out.as_fd().as_raw_fd(),
+ off_out,
+ len,
+ 0,
+ )
+ };
+ }
+ }
+ Errno::result(ret).map(|r| r as usize)
+}
+
+#[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> {
+ let off_in = off_in
+ .map(|offset| offset as *mut libc::loff_t)
+ .unwrap_or(ptr::null_mut());
+ let off_out = off_out
+ .map(|offset| offset as *mut libc::loff_t)
+ .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: &[std::io::IoSlice<'_>],
+ 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(target_os = "linux")]
+#[cfg(feature = "fs")]
+libc_bitflags!(
+ /// Mode argument flags for fallocate determining operation performed on a given range.
+ #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+ 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;
+ }
+);
+
+feature! {
+#![feature = "fs"]
+
+/// Manipulates file space.
+///
+/// Allows the caller to directly manipulate the allocated disk space for the
+/// file referred to by fd.
+#[cfg(target_os = "linux")]
+#[cfg(feature = "fs")]
+pub fn fallocate(
+ fd: RawFd,
+ mode: FallocateFlags,
+ offset: libc::off_t,
+ len: libc::off_t,
+) -> Result<()> {
+ let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
+ Errno::result(res).map(drop)
+}
+
+/// Argument to [`fspacectl`] describing the range to zero. The first member is
+/// the file offset, and the second is the length of the region.
+#[cfg(any(target_os = "freebsd"))]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
+
+#[cfg(any(target_os = "freebsd"))]
+impl SpacectlRange {
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.1 == 0
+ }
+
+ #[inline]
+ pub fn len(&self) -> libc::off_t {
+ self.1
+ }
+
+ #[inline]
+ pub fn offset(&self) -> libc::off_t {
+ self.0
+ }
+}
+
+/// Punch holes in a file.
+///
+/// `fspacectl` instructs the file system to deallocate a portion of a file.
+/// After a successful operation, this region of the file will return all zeroes
+/// if read. If the file system supports deallocation, then it may free the
+/// underlying storage, too.
+///
+/// # Arguments
+///
+/// - `fd` - File to operate on
+/// - `range.0` - File offset at which to begin deallocation
+/// - `range.1` - Length of the region to deallocate
+///
+/// # Returns
+///
+/// The operation may deallocate less than the entire requested region. On
+/// success, it returns the region that still remains to be deallocated. The
+/// caller should loop until the returned region is empty.
+///
+/// # Example
+///
+#[cfg_attr(fbsd14, doc = " ```")]
+#[cfg_attr(not(fbsd14), doc = " ```no_run")]
+/// # use std::io::Write;
+/// # use std::os::unix::fs::FileExt;
+/// # use std::os::unix::io::AsRawFd;
+/// # use nix::fcntl::*;
+/// # use tempfile::tempfile;
+/// const INITIAL: &[u8] = b"0123456789abcdef";
+/// let mut f = tempfile().unwrap();
+/// f.write_all(INITIAL).unwrap();
+/// let mut range = SpacectlRange(3, 6);
+/// while (!range.is_empty()) {
+/// range = fspacectl(f.as_raw_fd(), range).unwrap();
+/// }
+/// let mut buf = vec![0; INITIAL.len()];
+/// f.read_exact_at(&mut buf, 0).unwrap();
+/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
+/// ```
+#[cfg(target_os = "freebsd")]
+pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> {
+ let mut rqsr = libc::spacectl_range {
+ r_offset: range.0,
+ r_len: range.1,
+ };
+ let res = unsafe {
+ libc::fspacectl(
+ fd,
+ libc::SPACECTL_DEALLOC, // Only one command is supported ATM
+ &rqsr,
+ 0, // No flags are currently supported
+ &mut rqsr,
+ )
+ };
+ Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len))
+}
+
+/// Like [`fspacectl`], but will never return incomplete.
+///
+/// # Arguments
+///
+/// - `fd` - File to operate on
+/// - `offset` - File offset at which to begin deallocation
+/// - `len` - Length of the region to deallocate
+///
+/// # Returns
+///
+/// Returns `()` on success. On failure, the region may or may not be partially
+/// deallocated.
+///
+/// # Example
+///
+#[cfg_attr(fbsd14, doc = " ```")]
+#[cfg_attr(not(fbsd14), doc = " ```no_run")]
+/// # use std::io::Write;
+/// # use std::os::unix::fs::FileExt;
+/// # use std::os::unix::io::AsRawFd;
+/// # use nix::fcntl::*;
+/// # use tempfile::tempfile;
+/// const INITIAL: &[u8] = b"0123456789abcdef";
+/// let mut f = tempfile().unwrap();
+/// f.write_all(INITIAL).unwrap();
+/// fspacectl_all(f.as_raw_fd(), 3, 6).unwrap();
+/// let mut buf = vec![0; INITIAL.len()];
+/// f.read_exact_at(&mut buf, 0).unwrap();
+/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
+/// ```
+#[cfg(target_os = "freebsd")]
+pub fn fspacectl_all(
+ fd: RawFd,
+ offset: libc::off_t,
+ len: libc::off_t,
+) -> Result<()> {
+ let mut rqsr = libc::spacectl_range {
+ r_offset: offset,
+ r_len: len,
+ };
+ while rqsr.r_len > 0 {
+ let res = unsafe {
+ libc::fspacectl(
+ fd,
+ libc::SPACECTL_DEALLOC, // Only one command is supported ATM
+ &rqsr,
+ 0, // No flags are currently supported
+ &mut rqsr,
+ )
+ };
+ Errno::result(res)?;
+ }
+ Ok(())
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "wasi",
+ target_env = "uclibc",
+ target_os = "freebsd"
+))]
+mod posix_fadvise {
+ use crate::errno::Errno;
+ use crate::Result;
+ use std::os::unix::io::RawFd;
+
+ #[cfg(feature = "fs")]
+ libc_enum! {
+ #[repr(i32)]
+ #[non_exhaustive]
+ #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+ pub enum PosixFadviseAdvice {
+ POSIX_FADV_NORMAL,
+ POSIX_FADV_SEQUENTIAL,
+ POSIX_FADV_RANDOM,
+ POSIX_FADV_NOREUSE,
+ POSIX_FADV_WILLNEED,
+ POSIX_FADV_DONTNEED,
+ }
+ }
+
+ feature! {
+ #![feature = "fs"]
+ pub fn posix_fadvise(
+ fd: RawFd,
+ offset: libc::off_t,
+ len: libc::off_t,
+ advice: PosixFadviseAdvice,
+ ) -> Result<()> {
+ let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
+
+ if res == 0 {
+ Ok(())
+ } else {
+ Err(Errno::from_i32(res))
+ }
+ }
+ }
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "wasi",
+ target_os = "freebsd"
+))]
+pub fn posix_fallocate(
+ fd: RawFd,
+ offset: libc::off_t,
+ len: libc::off_t,
+) -> Result<()> {
+ let res = unsafe { libc::posix_fallocate(fd, offset, len) };
+ match Errno::result(res) {
+ Err(err) => Err(err),
+ Ok(0) => Ok(()),
+ Ok(errno) => Err(Errno::from_i32(errno)),
+ }
+}
+}
diff --git a/third_party/rust/nix/src/features.rs b/third_party/rust/nix/src/features.rs
new file mode 100644
index 0000000000..9e292cbf5d
--- /dev/null
+++ b/third_party/rust/nix/src/features.rs
@@ -0,0 +1,128 @@
+//! Feature tests for OS functionality
+pub use self::os::*;
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+mod os {
+ use crate::sys::utsname::uname;
+ use crate::Result;
+ use std::os::unix::ffi::OsStrExt;
+ use std::sync::atomic::{AtomicUsize, Ordering};
+
+ // Features:
+ // * atomic cloexec on socket: 2.6.27
+ // * pipe2: 2.6.27
+ // * accept4: 2.6.28
+
+ static VERS_UNKNOWN: usize = 1;
+ static VERS_2_6_18: usize = 2;
+ static VERS_2_6_27: usize = 3;
+ static VERS_2_6_28: usize = 4;
+ static VERS_3: usize = 5;
+
+ #[inline]
+ fn digit(dst: &mut usize, b: u8) {
+ *dst *= 10;
+ *dst += (b - b'0') as usize;
+ }
+
+ fn parse_kernel_version() -> Result<usize> {
+ let u = uname()?;
+
+ let mut curr: usize = 0;
+ let mut major: usize = 0;
+ let mut minor: usize = 0;
+ let mut patch: usize = 0;
+
+ for &b in u.release().as_bytes() {
+ if curr >= 3 {
+ break;
+ }
+
+ match b {
+ b'.' | b'-' => {
+ curr += 1;
+ }
+ b'0'..=b'9' => match curr {
+ 0 => digit(&mut major, b),
+ 1 => digit(&mut minor, b),
+ _ => digit(&mut patch, b),
+ },
+ _ => break,
+ }
+ }
+
+ Ok(if major >= 3 {
+ VERS_3
+ } else if major >= 2 {
+ if minor >= 7 {
+ VERS_UNKNOWN
+ } else if minor >= 6 {
+ if patch >= 28 {
+ VERS_2_6_28
+ } else if patch >= 27 {
+ VERS_2_6_27
+ } else {
+ VERS_2_6_18
+ }
+ } else {
+ VERS_UNKNOWN
+ }
+ } else {
+ VERS_UNKNOWN
+ })
+ }
+
+ fn kernel_version() -> Result<usize> {
+ static KERNEL_VERS: AtomicUsize = AtomicUsize::new(0);
+ let mut kernel_vers = KERNEL_VERS.load(Ordering::Relaxed);
+
+ if kernel_vers == 0 {
+ kernel_vers = parse_kernel_version()?;
+ KERNEL_VERS.store(kernel_vers, Ordering::Relaxed);
+ }
+
+ Ok(kernel_vers)
+ }
+
+ /// Check if the OS supports atomic close-on-exec for sockets
+ pub fn socket_atomic_cloexec() -> bool {
+ kernel_version()
+ .map(|version| version >= VERS_2_6_27)
+ .unwrap_or(false)
+ }
+
+ #[test]
+ pub fn test_parsing_kernel_version() {
+ assert!(kernel_version().unwrap() > 0);
+ }
+}
+
+#[cfg(any(
+ target_os = "dragonfly", // Since ???
+ target_os = "freebsd", // Since 10.0
+ target_os = "illumos", // Since ???
+ target_os = "netbsd", // Since 6.0
+ target_os = "openbsd", // Since 5.7
+ target_os = "redox", // Since 1-july-2020
+))]
+mod os {
+ /// Check if the OS supports atomic close-on-exec for sockets
+ pub const fn socket_atomic_cloexec() -> bool {
+ true
+ }
+}
+
+#[cfg(any(
+ target_os = "aix",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "solaris"
+))]
+mod os {
+ /// Check if the OS supports atomic close-on-exec for sockets
+ pub const fn socket_atomic_cloexec() -> bool {
+ false
+ }
+}
diff --git a/third_party/rust/nix/src/ifaddrs.rs b/third_party/rust/nix/src/ifaddrs.rs
new file mode 100644
index 0000000000..70b50b01eb
--- /dev/null
+++ b/third_party/rust/nix/src/ifaddrs.rs
@@ -0,0 +1,213 @@
+//! Query network interface addresses
+//!
+//! Uses the Linux and/or BSD specific function `getifaddrs` to query the list
+//! of interfaces and their associated addresses.
+
+use cfg_if::cfg_if;
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+use std::convert::TryFrom;
+use std::ffi;
+use std::iter::Iterator;
+use std::mem;
+use std::option::Option;
+
+use crate::net::if_::*;
+use crate::sys::socket::{SockaddrLike, SockaddrStorage};
+use crate::{Errno, Result};
+
+/// Describes a single address for an interface as returned by `getifaddrs`.
+#[derive(Clone, Debug, Eq, Hash, PartialEq)]
+pub struct InterfaceAddress {
+ /// Name of the network interface
+ pub interface_name: String,
+ /// Flags as from `SIOCGIFFLAGS` ioctl
+ pub flags: InterfaceFlags,
+ /// Network address of this interface
+ pub address: Option<SockaddrStorage>,
+ /// Netmask of this interface
+ pub netmask: Option<SockaddrStorage>,
+ /// Broadcast address of this interface, if applicable
+ pub broadcast: Option<SockaddrStorage>,
+ /// Point-to-point destination address
+ pub destination: Option<SockaddrStorage>,
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "android", 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
+ }
+ }
+}
+
+/// Workaround a bug in XNU where netmasks will always have the wrong size in
+/// the sa_len field due to the kernel ignoring trailing zeroes in the structure
+/// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470
+///
+/// To fix this, we stack-allocate a new sockaddr_storage, zero it out, and
+/// memcpy sa_len of the netmask to that new storage. Finally, we reset the
+/// ss_len field to sizeof(sockaddr_storage). This is supposedly valid as all
+/// members of the sockaddr_storage are "ok" with being zeroed out (there are
+/// no pointers).
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+unsafe fn workaround_xnu_bug(info: &libc::ifaddrs) -> Option<SockaddrStorage> {
+ let src_sock = info.ifa_netmask;
+ if src_sock.is_null() {
+ return None;
+ }
+
+ let mut dst_sock = mem::MaybeUninit::<libc::sockaddr_storage>::zeroed();
+
+ // memcpy only sa_len bytes, assume the rest is zero
+ std::ptr::copy_nonoverlapping(
+ src_sock as *const u8,
+ dst_sock.as_mut_ptr() as *mut u8,
+ (*src_sock).sa_len.into(),
+ );
+
+ // Initialize ss_len to sizeof(libc::sockaddr_storage).
+ (*dst_sock.as_mut_ptr()).ss_len =
+ u8::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap();
+ let dst_sock = dst_sock.assume_init();
+
+ let dst_sock_ptr =
+ &dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr;
+
+ SockaddrStorage::from_raw(dst_sock_ptr, None)
+}
+
+impl InterfaceAddress {
+ /// Create an `InterfaceAddress` from the libc struct.
+ fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
+ let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
+ let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) };
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ let netmask = unsafe { workaround_xnu_bug(info) };
+ #[cfg(not(any(target_os = "ios", target_os = "macos")))]
+ let netmask =
+ unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
+ let mut addr = InterfaceAddress {
+ interface_name: ifname.to_string_lossy().to_string(),
+ flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32),
+ address,
+ netmask,
+ broadcast: None,
+ destination: None,
+ };
+
+ let ifu = get_ifu_from_sockaddr(info);
+ if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
+ addr.destination = unsafe { SockaddrStorage::from_raw(ifu, None) };
+ } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
+ addr.broadcast = unsafe { SockaddrStorage::from_raw(ifu, None) };
+ }
+
+ addr
+ }
+}
+
+/// Holds the results of `getifaddrs`.
+///
+/// Use the function `getifaddrs` to create this Iterator. Note that the
+/// actual list of interfaces can be iterated once and will be freed as
+/// soon as the Iterator goes out of scope.
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub struct InterfaceAddressIterator {
+ base: *mut libc::ifaddrs,
+ next: *mut libc::ifaddrs,
+}
+
+impl Drop for InterfaceAddressIterator {
+ fn drop(&mut self) {
+ unsafe { libc::freeifaddrs(self.base) };
+ }
+}
+
+impl Iterator for InterfaceAddressIterator {
+ type Item = InterfaceAddress;
+ fn next(&mut self) -> Option<<Self as Iterator>::Item> {
+ match unsafe { self.next.as_ref() } {
+ Some(ifaddr) => {
+ self.next = ifaddr.ifa_next;
+ Some(InterfaceAddress::from_libc_ifaddrs(ifaddr))
+ }
+ None => None,
+ }
+ }
+}
+
+/// Get interface addresses using libc's `getifaddrs`
+///
+/// Note that the underlying implementation differs between OSes. Only the
+/// most common address families are supported by the nix crate (due to
+/// lack of time and complexity of testing). The address family is encoded
+/// in the specific variant of `SockaddrStorage` returned for the fields
+/// `address`, `netmask`, `broadcast`, and `destination`. For any entry not
+/// supported, the returned list will contain a `None` entry.
+///
+/// # Example
+/// ```
+/// let addrs = nix::ifaddrs::getifaddrs().unwrap();
+/// for ifaddr in addrs {
+/// match ifaddr.address {
+/// Some(address) => {
+/// println!("interface {} address {}",
+/// ifaddr.interface_name, address);
+/// },
+/// None => {
+/// println!("interface {} with unsupported address family",
+/// ifaddr.interface_name);
+/// }
+/// }
+/// }
+/// ```
+pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
+ let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit();
+ unsafe {
+ Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| {
+ InterfaceAddressIterator {
+ base: addrs.assume_init(),
+ next: addrs.assume_init(),
+ }
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ // Only checks if `getifaddrs` can be invoked without panicking.
+ #[test]
+ fn test_getifaddrs() {
+ let _ = getifaddrs();
+ }
+
+ // Ensures getting the netmask works, and in particular that
+ // `workaround_xnu_bug` works properly.
+ #[test]
+ fn test_getifaddrs_netmask_correct() {
+ let addrs = getifaddrs().unwrap();
+ for iface in addrs {
+ let sock = if let Some(sock) = iface.netmask {
+ sock
+ } else {
+ continue;
+ };
+ if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) {
+ let _ = sock.as_sockaddr_in().unwrap();
+ return;
+ } else if sock.family()
+ == Some(crate::sys::socket::AddressFamily::Inet6)
+ {
+ let _ = sock.as_sockaddr_in6().unwrap();
+ return;
+ }
+ }
+ panic!("No address?");
+ }
+}
diff --git a/third_party/rust/nix/src/kmod.rs b/third_party/rust/nix/src/kmod.rs
new file mode 100644
index 0000000000..d3725c3f8a
--- /dev/null
+++ b/third_party/rust/nix/src/kmod.rs
@@ -0,0 +1,128 @@
+//! Load and unload kernel modules.
+//!
+//! For more details see
+
+use std::ffi::CStr;
+use std::os::unix::io::{AsFd, AsRawFd};
+
+use crate::errno::Errno;
+use crate::Result;
+
+/// Loads a kernel module from a buffer.
+///
+/// It loads an ELF image into kernel space,
+/// performs any necessary symbol relocations,
+/// initializes module parameters to values provided by the caller,
+/// and then runs the module's init function.
+///
+/// This function requires `CAP_SYS_MODULE` privilege.
+///
+/// The `module_image` argument points to a buffer containing the binary image
+/// to be loaded. The buffer should contain a valid ELF image
+/// built for the running kernel.
+///
+/// The `param_values` argument is a string containing space-delimited specifications
+/// of the values for module parameters.
+/// Each of the parameter specifications has the form:
+///
+/// `name[=value[,value...]]`
+///
+/// # Example
+///
+/// ```no_run
+/// use std::fs::File;
+/// use std::io::Read;
+/// use std::ffi::CString;
+/// use nix::kmod::init_module;
+///
+/// let mut f = File::open("mykernel.ko").unwrap();
+/// let mut contents: Vec<u8> = Vec::new();
+/// f.read_to_end(&mut contents).unwrap();
+/// init_module(&mut contents, &CString::new("who=Rust when=Now,12").unwrap()).unwrap();
+/// ```
+///
+/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
+pub fn init_module(module_image: &[u8], param_values: &CStr) -> Result<()> {
+ let res = unsafe {
+ libc::syscall(
+ libc::SYS_init_module,
+ module_image.as_ptr(),
+ module_image.len(),
+ param_values.as_ptr(),
+ )
+ };
+
+ Errno::result(res).map(drop)
+}
+
+libc_bitflags!(
+ /// Flags used by the `finit_module` function.
+ pub struct ModuleInitFlags: libc::c_uint {
+ /// Ignore symbol version hashes.
+ MODULE_INIT_IGNORE_MODVERSIONS;
+ /// Ignore kernel version magic.
+ MODULE_INIT_IGNORE_VERMAGIC;
+ }
+);
+
+/// Loads a kernel module from a given file descriptor.
+///
+/// # Example
+///
+/// ```no_run
+/// use std::fs::File;
+/// use std::ffi::CString;
+/// use nix::kmod::{finit_module, ModuleInitFlags};
+///
+/// let f = File::open("mymod.ko").unwrap();
+/// finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()).unwrap();
+/// ```
+///
+/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
+pub fn finit_module<Fd: AsFd>(
+ fd: Fd,
+ param_values: &CStr,
+ flags: ModuleInitFlags,
+) -> Result<()> {
+ let res = unsafe {
+ libc::syscall(
+ libc::SYS_finit_module,
+ fd.as_fd().as_raw_fd(),
+ param_values.as_ptr(),
+ flags.bits(),
+ )
+ };
+
+ Errno::result(res).map(drop)
+}
+
+libc_bitflags!(
+ /// Flags used by `delete_module`.
+ ///
+ /// See [`man delete_module(2)`](https://man7.org/linux/man-pages/man2/delete_module.2.html)
+ /// for a detailed description how these flags work.
+ pub struct DeleteModuleFlags: libc::c_int {
+ 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)`](https://man7.org/linux/man-pages/man2/delete_module.2.html) for more information.
+pub fn delete_module(name: &CStr, flags: DeleteModuleFlags) -> Result<()> {
+ let res = unsafe {
+ libc::syscall(libc::SYS_delete_module, name.as_ptr(), flags.bits())
+ };
+
+ Errno::result(res).map(drop)
+}
diff --git a/third_party/rust/nix/src/lib.rs b/third_party/rust/nix/src/lib.rs
new file mode 100644
index 0000000000..af0c67b0f3
--- /dev/null
+++ b/third_party/rust/nix/src/lib.rs
@@ -0,0 +1,374 @@
+//! Rust friendly bindings to the various *nix system functions.
+//!
+//! Modules are structured according to the C header file that they would be
+//! defined in.
+//!
+//! # Features
+//!
+//! Nix uses the following Cargo features to enable optional functionality.
+//! They may be enabled in any combination.
+//! * `acct` - Process accounting
+//! * `aio` - POSIX AIO
+//! * `dir` - Stuff relating to directory iteration
+//! * `env` - Manipulate environment variables
+//! * `event` - Event-driven APIs, like `kqueue` and `epoll`
+//! * `feature` - Query characteristics of the OS at runtime
+//! * `fs` - File system functionality
+//! * `hostname` - Get and set the system's hostname
+//! * `inotify` - Linux's `inotify` file system notification API
+//! * `ioctl` - The `ioctl` syscall, and wrappers for many specific instances
+//! * `kmod` - Load and unload kernel modules
+//! * `mman` - Stuff relating to memory management
+//! * `mount` - Mount and unmount file systems
+//! * `mqueue` - POSIX message queues
+//! * `net` - Networking-related functionality
+//! * `personality` - Set the process execution domain
+//! * `poll` - APIs like `poll` and `select`
+//! * `process` - Stuff relating to running processes
+//! * `pthread` - POSIX threads
+//! * `ptrace` - Process tracing and debugging
+//! * `quota` - File system quotas
+//! * `reboot` - Reboot the system
+//! * `resource` - Process resource limits
+//! * `sched` - Manipulate process's scheduling
+//! * `socket` - Sockets, whether for networking or local use
+//! * `signal` - Send and receive signals to processes
+//! * `term` - Terminal control APIs
+//! * `time` - Query the operating system's clocks
+//! * `ucontext` - User thread context
+//! * `uio` - Vectored I/O
+//! * `user` - Stuff relating to users and groups
+//! * `zerocopy` - APIs like `sendfile` and `copy_file_range`
+#![crate_name = "nix"]
+#![cfg(unix)]
+#![cfg_attr(docsrs, doc(cfg(all())))]
+#![allow(non_camel_case_types)]
+#![cfg_attr(test, deny(warnings))]
+#![recursion_limit = "500"]
+#![deny(unused)]
+#![allow(unused_macros)]
+#![cfg_attr(
+ not(all(
+ feature = "acct",
+ feature = "aio",
+ feature = "dir",
+ feature = "env",
+ feature = "event",
+ feature = "feature",
+ feature = "fs",
+ feature = "hostname",
+ feature = "inotify",
+ feature = "ioctl",
+ feature = "kmod",
+ feature = "mman",
+ feature = "mount",
+ feature = "mqueue",
+ feature = "net",
+ feature = "personality",
+ feature = "poll",
+ feature = "process",
+ feature = "pthread",
+ feature = "ptrace",
+ feature = "quota",
+ feature = "reboot",
+ feature = "resource",
+ feature = "sched",
+ feature = "socket",
+ feature = "signal",
+ feature = "term",
+ feature = "time",
+ feature = "ucontext",
+ feature = "uio",
+ feature = "user",
+ feature = "zerocopy",
+ )),
+ allow(unused_imports)
+)]
+#![deny(unstable_features)]
+#![deny(missing_copy_implementations)]
+#![deny(missing_debug_implementations)]
+#![warn(missing_docs)]
+#![cfg_attr(docsrs, feature(doc_cfg))]
+#![deny(clippy::cast_ptr_alignment)]
+
+// Re-exported external crates
+pub use libc;
+
+// Private internal modules
+#[macro_use]
+mod macros;
+
+// Public crates
+#[cfg(not(target_os = "redox"))]
+feature! {
+ #![feature = "dir"]
+ pub mod dir;
+}
+feature! {
+ #![feature = "env"]
+ pub mod env;
+}
+#[allow(missing_docs)]
+pub mod errno;
+feature! {
+ #![feature = "feature"]
+
+ #[deny(missing_docs)]
+ pub mod features;
+}
+#[allow(missing_docs)]
+pub mod fcntl;
+feature! {
+ #![feature = "net"]
+
+ #[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 = "illumos",
+ target_os = "openbsd"))]
+ #[deny(missing_docs)]
+ pub mod ifaddrs;
+ #[cfg(not(target_os = "redox"))]
+ #[deny(missing_docs)]
+ pub mod net;
+}
+#[cfg(any(target_os = "android", target_os = "linux"))]
+feature! {
+ #![feature = "kmod"]
+ #[allow(missing_docs)]
+ pub mod kmod;
+}
+feature! {
+ #![feature = "mount"]
+ pub mod mount;
+}
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "netbsd"
+))]
+feature! {
+ #![feature = "mqueue"]
+ pub mod mqueue;
+}
+feature! {
+ #![feature = "poll"]
+ pub mod poll;
+}
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+feature! {
+ #![feature = "term"]
+ #[deny(missing_docs)]
+ pub mod pty;
+}
+feature! {
+ #![feature = "sched"]
+ pub mod sched;
+}
+pub mod sys;
+feature! {
+ #![feature = "time"]
+ #[allow(missing_docs)]
+ pub mod time;
+}
+// This can be implemented for other platforms as soon as libc
+// provides bindings for them.
+#[cfg(all(
+ target_os = "linux",
+ any(
+ target_arch = "aarch64",
+ target_arch = "s390x",
+ target_arch = "x86",
+ target_arch = "x86_64"
+ )
+))]
+feature! {
+ #![feature = "ucontext"]
+ #[allow(missing_docs)]
+ pub mod ucontext;
+}
+#[allow(missing_docs)]
+pub mod unistd;
+
+use std::ffi::{CStr, CString, OsStr};
+use std::mem::MaybeUninit;
+use std::os::unix::ffi::OsStrExt;
+use std::path::{Path, PathBuf};
+use std::{ptr, result, slice};
+
+use errno::Errno;
+
+/// Nix Result Type
+pub type Result<T> = result::Result<T, Errno>;
+
+/// Nix's main error type.
+///
+/// It's a wrapper around Errno. As such, it's very interoperable with
+/// [`std::io::Error`], but it has the advantages of:
+/// * `Clone`
+/// * `Copy`
+/// * `Eq`
+/// * Small size
+/// * Represents all of the system's errnos, instead of just the most common
+/// ones.
+pub type Error = Errno;
+
+/// Common trait used to represent file system paths by many Nix functions.
+pub trait NixPath {
+ /// Is the path empty?
+ fn is_empty(&self) -> bool;
+
+ /// Length of the path in bytes
+ fn len(&self) -> usize;
+
+ /// Execute a function with this path as a `CStr`.
+ ///
+ /// Mostly used internally by Nix.
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T;
+}
+
+impl NixPath for str {
+ fn is_empty(&self) -> bool {
+ NixPath::is_empty(OsStr::new(self))
+ }
+
+ fn len(&self) -> usize {
+ NixPath::len(OsStr::new(self))
+ }
+
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T,
+ {
+ OsStr::new(self).with_nix_path(f)
+ }
+}
+
+impl NixPath for OsStr {
+ fn is_empty(&self) -> bool {
+ self.as_bytes().is_empty()
+ }
+
+ fn len(&self) -> usize {
+ self.as_bytes().len()
+ }
+
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T,
+ {
+ self.as_bytes().with_nix_path(f)
+ }
+}
+
+impl NixPath for CStr {
+ fn is_empty(&self) -> bool {
+ self.to_bytes().is_empty()
+ }
+
+ fn len(&self) -> usize {
+ self.to_bytes().len()
+ }
+
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T,
+ {
+ Ok(f(self))
+ }
+}
+
+impl NixPath for [u8] {
+ fn is_empty(&self) -> bool {
+ self.is_empty()
+ }
+
+ fn len(&self) -> usize {
+ self.len()
+ }
+
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T,
+ {
+ // The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path
+ // longer than ~300 bytes. See the the PR description to get stats for your own machine.
+ // https://github.com/nix-rust/nix/pull/1656
+ //
+ // By being smaller than a memory page, we also avoid the compiler inserting a probe frame:
+ // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
+ const MAX_STACK_ALLOCATION: usize = 1024;
+
+ if self.len() >= MAX_STACK_ALLOCATION {
+ return with_nix_path_allocating(self, f);
+ }
+
+ let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
+ let buf_ptr = buf.as_mut_ptr() as *mut u8;
+
+ unsafe {
+ ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
+ buf_ptr.add(self.len()).write(0);
+ }
+
+ match CStr::from_bytes_with_nul(unsafe {
+ slice::from_raw_parts(buf_ptr, self.len() + 1)
+ }) {
+ Ok(s) => Ok(f(s)),
+ Err(_) => Err(Errno::EINVAL),
+ }
+ }
+}
+
+#[cold]
+#[inline(never)]
+fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
+where
+ F: FnOnce(&CStr) -> T,
+{
+ match CString::new(from) {
+ Ok(s) => Ok(f(&s)),
+ Err(_) => Err(Errno::EINVAL),
+ }
+}
+
+impl NixPath for Path {
+ fn is_empty(&self) -> bool {
+ NixPath::is_empty(self.as_os_str())
+ }
+
+ fn len(&self) -> usize {
+ NixPath::len(self.as_os_str())
+ }
+
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T,
+ {
+ self.as_os_str().with_nix_path(f)
+ }
+}
+
+impl NixPath for PathBuf {
+ fn is_empty(&self) -> bool {
+ NixPath::is_empty(self.as_os_str())
+ }
+
+ fn len(&self) -> usize {
+ NixPath::len(self.as_os_str())
+ }
+
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T,
+ {
+ self.as_os_str().with_nix_path(f)
+ }
+}
diff --git a/third_party/rust/nix/src/macros.rs b/third_party/rust/nix/src/macros.rs
new file mode 100644
index 0000000000..adff2bc6be
--- /dev/null
+++ b/third_party/rust/nix/src/macros.rs
@@ -0,0 +1,331 @@
+// Thanks to Tokio for this macro
+macro_rules! feature {
+ (
+ #![$meta:meta]
+ $($item:item)*
+ ) => {
+ $(
+ #[cfg($meta)]
+ #[cfg_attr(docsrs, doc(cfg($meta)))]
+ $item
+ )*
+ }
+}
+
+/// The `libc_bitflags!` macro helps with a common use case of defining a public bitflags type
+/// with values from the libc crate. It is used the same way as the `bitflags!` macro, except
+/// that only the name of the flag value has to be given.
+///
+/// The `libc` crate must be in scope with the name `libc`.
+///
+/// # Example
+/// ```ignore
+/// libc_bitflags!{
+/// pub struct ProtFlags: libc::c_int {
+/// PROT_NONE;
+/// PROT_READ;
+/// /// PROT_WRITE enables write protect
+/// PROT_WRITE;
+/// PROT_EXEC;
+/// #[cfg(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.
+///
+/// ```ignore
+/// libc_bitflags!{
+/// pub struct SaFlags: libc::c_ulong {
+/// SA_NOCLDSTOP as libc::c_ulong;
+/// SA_NOCLDWAIT;
+/// SA_NODEFER as libc::c_ulong;
+/// SA_ONSTACK;
+/// SA_RESETHAND as libc::c_ulong;
+/// SA_RESTART as libc::c_ulong;
+/// SA_SIGINFO;
+/// }
+/// }
+/// ```
+macro_rules! libc_bitflags {
+ (
+ $(#[$outer:meta])*
+ pub struct $BitFlags:ident: $T:ty {
+ $(
+ $(#[$inner:ident $($args:tt)*])*
+ $Flag:ident $(as $cast:ty)*;
+ )+
+ }
+ ) => {
+ ::bitflags::bitflags! {
+ #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+ #[repr(transparent)]
+ $(#[$outer])*
+ pub struct $BitFlags: $T {
+ $(
+ $(#[$inner $($args)*])*
+ const $Flag = libc::$Flag $(as $cast)*;
+ )+
+ }
+ }
+ };
+}
+
+/// The `libc_enum!` macro helps with a common use case of defining an enum exclusively using
+/// values from the `libc` crate. This macro supports both `pub` and private `enum`s.
+///
+/// The `libc` crate must be in scope with the name `libc`.
+///
+/// # Example
+/// ```ignore
+/// libc_enum!{
+/// pub enum ProtFlags {
+/// PROT_NONE,
+/// PROT_READ,
+/// PROT_WRITE,
+/// PROT_EXEC,
+/// #[cfg(any(target_os = "linux", target_os = "android"))]
+/// PROT_GROWSDOWN,
+/// #[cfg(any(target_os = "linux", target_os = "android"))]
+/// PROT_GROWSUP,
+/// }
+/// }
+/// ```
+// Some targets don't use all rules.
+#[allow(unused_macro_rules)]
+macro_rules! libc_enum {
+ // Exit rule.
+ (@make_enum
+ name: $BitFlags:ident,
+ {
+ $v:vis
+ attrs: [$($attrs:tt)*],
+ entries: [$($entries:tt)*],
+ }
+ ) => {
+ $($attrs)*
+ #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+ $v enum $BitFlags {
+ $($entries)*
+ }
+ };
+
+ // Exit rule including TryFrom
+ (@make_enum
+ name: $BitFlags:ident,
+ {
+ $v:vis
+ attrs: [$($attrs:tt)*],
+ entries: [$($entries:tt)*],
+ from_type: $repr:path,
+ try_froms: [$($try_froms:tt)*]
+ }
+ ) => {
+ $($attrs)*
+ #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+ $v enum $BitFlags {
+ $($entries)*
+ }
+ impl ::std::convert::TryFrom<$repr> for $BitFlags {
+ type Error = $crate::Error;
+ #[allow(unused_doc_comments)]
+ #[allow(deprecated)]
+ #[allow(unused_attributes)]
+ fn try_from(x: $repr) -> $crate::Result<Self> {
+ match x {
+ $($try_froms)*
+ _ => Err($crate::Error::EINVAL)
+ }
+ }
+ }
+ };
+
+ // Done accumulating.
+ (@accumulate_entries
+ name: $BitFlags:ident,
+ {
+ $v:vis
+ attrs: $attrs:tt,
+ },
+ $entries:tt,
+ $try_froms:tt;
+ ) => {
+ libc_enum! {
+ @make_enum
+ name: $BitFlags,
+ {
+ $v
+ attrs: $attrs,
+ entries: $entries,
+ }
+ }
+ };
+
+ // Done accumulating and want TryFrom
+ (@accumulate_entries
+ name: $BitFlags:ident,
+ {
+ $v:vis
+ attrs: $attrs:tt,
+ from_type: $repr:path,
+ },
+ $entries:tt,
+ $try_froms:tt;
+ ) => {
+ libc_enum! {
+ @make_enum
+ name: $BitFlags,
+ {
+ $v
+ attrs: $attrs,
+ entries: $entries,
+ from_type: $repr,
+ try_froms: $try_froms
+ }
+ }
+ };
+
+ // Munch an attr.
+ (@accumulate_entries
+ name: $BitFlags:ident,
+ $prefix:tt,
+ [$($entries:tt)*],
+ [$($try_froms:tt)*];
+ #[$attr:meta] $($tail:tt)*
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ name: $BitFlags,
+ $prefix,
+ [
+ $($entries)*
+ #[$attr]
+ ],
+ [
+ $($try_froms)*
+ #[$attr]
+ ];
+ $($tail)*
+ }
+ };
+
+ // Munch last ident if not followed by a comma.
+ (@accumulate_entries
+ name: $BitFlags:ident,
+ $prefix:tt,
+ [$($entries:tt)*],
+ [$($try_froms:tt)*];
+ $entry:ident
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ name: $BitFlags,
+ $prefix,
+ [
+ $($entries)*
+ $entry = libc::$entry,
+ ],
+ [
+ $($try_froms)*
+ libc::$entry => Ok($BitFlags::$entry),
+ ];
+ }
+ };
+
+ // Munch an ident; covers terminating comma case.
+ (@accumulate_entries
+ name: $BitFlags:ident,
+ $prefix:tt,
+ [$($entries:tt)*],
+ [$($try_froms:tt)*];
+ $entry:ident,
+ $($tail:tt)*
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ name: $BitFlags,
+ $prefix,
+ [
+ $($entries)*
+ $entry = libc::$entry,
+ ],
+ [
+ $($try_froms)*
+ libc::$entry => Ok($BitFlags::$entry),
+ ];
+ $($tail)*
+ }
+ };
+
+ // Munch an ident and cast it to the given type; covers terminating comma.
+ (@accumulate_entries
+ name: $BitFlags:ident,
+ $prefix:tt,
+ [$($entries:tt)*],
+ [$($try_froms:tt)*];
+ $entry:ident as $ty:ty,
+ $($tail:tt)*
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ name: $BitFlags,
+ $prefix,
+ [
+ $($entries)*
+ $entry = libc::$entry as $ty,
+ ],
+ [
+ $($try_froms)*
+ libc::$entry as $ty => Ok($BitFlags::$entry),
+ ];
+ $($tail)*
+ }
+ };
+
+ // Entry rule.
+ (
+ $(#[$attr:meta])*
+ $v:vis enum $BitFlags:ident {
+ $($vals:tt)*
+ }
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ name: $BitFlags,
+ {
+ $v
+ attrs: [$(#[$attr])*],
+ },
+ [],
+ [];
+ $($vals)*
+ }
+ };
+
+ // Entry rule including TryFrom
+ (
+ $(#[$attr:meta])*
+ $v:vis enum $BitFlags:ident {
+ $($vals:tt)*
+ }
+ impl TryFrom<$repr:path>
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ name: $BitFlags,
+ {
+ $v
+ attrs: [$(#[$attr])*],
+ from_type: $repr,
+ },
+ [],
+ [];
+ $($vals)*
+ }
+ };
+}
diff --git a/third_party/rust/nix/src/mount/bsd.rs b/third_party/rust/nix/src/mount/bsd.rs
new file mode 100644
index 0000000000..6ed2dc7fbf
--- /dev/null
+++ b/third_party/rust/nix/src/mount/bsd.rs
@@ -0,0 +1,453 @@
+#[cfg(target_os = "freebsd")]
+use crate::Error;
+use crate::{Errno, NixPath, Result};
+use libc::c_int;
+#[cfg(target_os = "freebsd")]
+use libc::{c_char, c_uint, c_void};
+#[cfg(target_os = "freebsd")]
+use std::{
+ borrow::Cow,
+ ffi::{CStr, CString},
+ fmt, io,
+ marker::PhantomData,
+};
+
+libc_bitflags!(
+ /// Used with [`Nmount::nmount`].
+ pub struct MntFlags: c_int {
+ /// ACL support enabled.
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_ACLS;
+ /// All I/O to the file system should be done asynchronously.
+ MNT_ASYNC;
+ /// dir should instead be a file system ID encoded as “FSID:val0:val1”.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_BYFSID;
+ /// Force a read-write mount even if the file system appears to be
+ /// unclean.
+ MNT_FORCE;
+ /// GEOM journal support enabled.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_GJOURNAL;
+ /// MAC support for objects.
+ #[cfg(any(target_os = "macos", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_MULTILABEL;
+ /// Disable read clustering.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_NOCLUSTERR;
+ /// Disable write clustering.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_NOCLUSTERW;
+ /// Enable NFS version 4 ACLs.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_NFS4ACLS;
+ /// Do not update access times.
+ MNT_NOATIME;
+ /// Disallow program execution.
+ MNT_NOEXEC;
+ /// Do not honor setuid or setgid bits on files when executing them.
+ MNT_NOSUID;
+ /// Do not follow symlinks.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_NOSYMFOLLOW;
+ /// Mount read-only.
+ MNT_RDONLY;
+ /// Causes the vfs subsystem to update its data structures pertaining to
+ /// the specified already mounted file system.
+ MNT_RELOAD;
+ /// Create a snapshot of the file system.
+ ///
+ /// See [mksnap_ffs(8)](https://www.freebsd.org/cgi/man.cgi?query=mksnap_ffs)
+ #[cfg(any(target_os = "macos", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_SNAPSHOT;
+ /// Using soft updates.
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_SOFTDEP;
+ /// Directories with the SUID bit set chown new files to their own
+ /// owner.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_SUIDDIR;
+ /// All I/O to the file system should be done synchronously.
+ MNT_SYNCHRONOUS;
+ /// Union with underlying fs.
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "netbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_UNION;
+ /// Indicates that the mount command is being applied to an already
+ /// mounted file system.
+ MNT_UPDATE;
+ /// Check vnode use counts.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_NONBUSY;
+ }
+);
+
+/// The Error type of [`Nmount::nmount`].
+///
+/// It wraps an [`Errno`], but also may contain an additional message returned
+/// by `nmount(2)`.
+#[cfg(target_os = "freebsd")]
+#[derive(Debug)]
+pub struct NmountError {
+ errno: Error,
+ errmsg: Option<String>,
+}
+
+#[cfg(target_os = "freebsd")]
+impl NmountError {
+ /// Returns the additional error string sometimes generated by `nmount(2)`.
+ pub fn errmsg(&self) -> Option<&str> {
+ self.errmsg.as_deref()
+ }
+
+ /// Returns the inner [`Error`]
+ pub const fn error(&self) -> Error {
+ self.errno
+ }
+
+ fn new(error: Error, errmsg: Option<&CStr>) -> Self {
+ Self {
+ errno: error,
+ errmsg: errmsg.map(CStr::to_string_lossy).map(Cow::into_owned),
+ }
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl std::error::Error for NmountError {}
+
+#[cfg(target_os = "freebsd")]
+impl fmt::Display for NmountError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(errmsg) = &self.errmsg {
+ write!(f, "{:?}: {}: {}", self.errno, errmsg, self.errno.desc())
+ } else {
+ write!(f, "{:?}: {}", self.errno, self.errno.desc())
+ }
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl From<NmountError> for io::Error {
+ fn from(err: NmountError) -> Self {
+ err.errno.into()
+ }
+}
+
+/// Result type of [`Nmount::nmount`].
+#[cfg(target_os = "freebsd")]
+pub type NmountResult = std::result::Result<(), NmountError>;
+
+/// Mount a FreeBSD file system.
+///
+/// The `nmount(2)` system call works similarly to the `mount(8)` program; it
+/// takes its options as a series of name-value pairs. Most of the values are
+/// strings, as are all of the names. The `Nmount` structure builds up an
+/// argument list and then executes the syscall.
+///
+/// # Examples
+///
+/// To mount `target` onto `mountpoint` with `nullfs`:
+/// ```
+/// # use nix::unistd::Uid;
+/// # use ::sysctl::{CtlValue, Sysctl};
+/// # let ctl = ::sysctl::Ctl::new("vfs.usermount").unwrap();
+/// # if !Uid::current().is_root() && CtlValue::Int(0) == ctl.value().unwrap() {
+/// # return;
+/// # };
+/// use nix::mount::{MntFlags, Nmount, unmount};
+/// use std::ffi::CString;
+/// use tempfile::tempdir;
+///
+/// let mountpoint = tempdir().unwrap();
+/// let target = tempdir().unwrap();
+///
+/// let fstype = CString::new("fstype").unwrap();
+/// let nullfs = CString::new("nullfs").unwrap();
+/// Nmount::new()
+/// .str_opt(&fstype, &nullfs)
+/// .str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
+/// .str_opt_owned("target", target.path().to_str().unwrap())
+/// .nmount(MntFlags::empty()).unwrap();
+///
+/// unmount(mountpoint.path(), MntFlags::empty()).unwrap();
+/// ```
+///
+/// # See Also
+/// * [`nmount(2)`](https://www.freebsd.org/cgi/man.cgi?query=nmount)
+/// * [`nullfs(5)`](https://www.freebsd.org/cgi/man.cgi?query=nullfs)
+#[cfg(target_os = "freebsd")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+#[derive(Debug, Default)]
+pub struct Nmount<'a> {
+ // n.b. notgull: In reality, this is a list that contains
+ // both mutable and immutable pointers.
+ // Be careful using this.
+ iov: Vec<libc::iovec>,
+ is_owned: Vec<bool>,
+ marker: PhantomData<&'a ()>,
+}
+
+#[cfg(target_os = "freebsd")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+impl<'a> Nmount<'a> {
+ /// Helper function to push a slice onto the `iov` array.
+ fn push_slice(&mut self, val: &'a [u8], is_owned: bool) {
+ self.iov.push(libc::iovec {
+ iov_base: val.as_ptr() as *mut _,
+ iov_len: val.len(),
+ });
+ self.is_owned.push(is_owned);
+ }
+
+ /// Helper function to push a pointer and its length onto the `iov` array.
+ fn push_pointer_and_length(
+ &mut self,
+ val: *const u8,
+ len: usize,
+ is_owned: bool,
+ ) {
+ self.iov.push(libc::iovec {
+ iov_base: val as *mut _,
+ iov_len: len,
+ });
+ self.is_owned.push(is_owned);
+ }
+
+ /// Helper function to push a `nix` path as owned.
+ fn push_nix_path<P: ?Sized + NixPath>(&mut self, val: &P) {
+ val.with_nix_path(|s| {
+ let len = s.to_bytes_with_nul().len();
+ let ptr = s.to_owned().into_raw() as *const u8;
+
+ self.push_pointer_and_length(ptr, len, true);
+ })
+ .unwrap();
+ }
+
+ /// Add an opaque mount option.
+ ///
+ /// Some file systems take binary-valued mount options. They can be set
+ /// with this method.
+ ///
+ /// # Safety
+ ///
+ /// Unsafe because it will cause `Nmount::nmount` to dereference a raw
+ /// pointer. The user is responsible for ensuring that `val` is valid and
+ /// its lifetime outlives `self`! An easy way to do that is to give the
+ /// value a larger scope than `name`
+ ///
+ /// # Examples
+ /// ```
+ /// use libc::c_void;
+ /// use nix::mount::Nmount;
+ /// use std::ffi::CString;
+ /// use std::mem;
+ ///
+ /// // Note that flags outlives name
+ /// let mut flags: u32 = 0xdeadbeef;
+ /// let name = CString::new("flags").unwrap();
+ /// let p = &mut flags as *mut u32 as *mut c_void;
+ /// let len = mem::size_of_val(&flags);
+ /// let mut nmount = Nmount::new();
+ /// unsafe { nmount.mut_ptr_opt(&name, p, len) };
+ /// ```
+ pub unsafe fn mut_ptr_opt(
+ &mut self,
+ name: &'a CStr,
+ val: *mut c_void,
+ len: usize,
+ ) -> &mut Self {
+ self.push_slice(name.to_bytes_with_nul(), false);
+ self.push_pointer_and_length(val.cast(), len, false);
+ self
+ }
+
+ /// Add a mount option that does not take a value.
+ ///
+ /// # Examples
+ /// ```
+ /// use nix::mount::Nmount;
+ /// use std::ffi::CString;
+ ///
+ /// let read_only = CString::new("ro").unwrap();
+ /// Nmount::new()
+ /// .null_opt(&read_only);
+ /// ```
+ pub fn null_opt(&mut self, name: &'a CStr) -> &mut Self {
+ self.push_slice(name.to_bytes_with_nul(), false);
+ self.push_slice(&[], false);
+ self
+ }
+
+ /// Add a mount option that does not take a value, but whose name must be
+ /// owned.
+ ///
+ ///
+ /// This has higher runtime cost than [`Nmount::null_opt`], but is useful
+ /// when the name's lifetime doesn't outlive the `Nmount`, or it's a
+ /// different string type than `CStr`.
+ ///
+ /// # Examples
+ /// ```
+ /// use nix::mount::Nmount;
+ ///
+ /// let read_only = "ro";
+ /// let mut nmount: Nmount<'static> = Nmount::new();
+ /// nmount.null_opt_owned(read_only);
+ /// ```
+ pub fn null_opt_owned<P: ?Sized + NixPath>(
+ &mut self,
+ name: &P,
+ ) -> &mut Self {
+ self.push_nix_path(name);
+ self.push_slice(&[], false);
+ self
+ }
+
+ /// Add a mount option as a [`CStr`].
+ ///
+ /// # Examples
+ /// ```
+ /// use nix::mount::Nmount;
+ /// use std::ffi::CString;
+ ///
+ /// let fstype = CString::new("fstype").unwrap();
+ /// let nullfs = CString::new("nullfs").unwrap();
+ /// Nmount::new()
+ /// .str_opt(&fstype, &nullfs);
+ /// ```
+ pub fn str_opt(&mut self, name: &'a CStr, val: &'a CStr) -> &mut Self {
+ self.push_slice(name.to_bytes_with_nul(), false);
+ self.push_slice(val.to_bytes_with_nul(), false);
+ self
+ }
+
+ /// Add a mount option as an owned string.
+ ///
+ /// This has higher runtime cost than [`Nmount::str_opt`], but is useful
+ /// when the value's lifetime doesn't outlive the `Nmount`, or it's a
+ /// different string type than `CStr`.
+ ///
+ /// # Examples
+ /// ```
+ /// use nix::mount::Nmount;
+ /// use std::path::Path;
+ ///
+ /// let mountpoint = Path::new("/mnt");
+ /// Nmount::new()
+ /// .str_opt_owned("fspath", mountpoint.to_str().unwrap());
+ /// ```
+ pub fn str_opt_owned<P1, P2>(&mut self, name: &P1, val: &P2) -> &mut Self
+ where
+ P1: ?Sized + NixPath,
+ P2: ?Sized + NixPath,
+ {
+ self.push_nix_path(name);
+ self.push_nix_path(val);
+ self
+ }
+
+ /// Create a new `Nmount` struct with no options
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Actually mount the file system.
+ pub fn nmount(&mut self, flags: MntFlags) -> NmountResult {
+ const ERRMSG_NAME: &[u8] = b"errmsg\0";
+ let mut errmsg = vec![0u8; 255];
+
+ // nmount can return extra error information via a "errmsg" return
+ // argument.
+ self.push_slice(ERRMSG_NAME, false);
+
+ // SAFETY: we are pushing a mutable iovec here, so we can't use
+ // the above method
+ self.iov.push(libc::iovec {
+ iov_base: errmsg.as_mut_ptr() as *mut c_void,
+ iov_len: errmsg.len(),
+ });
+
+ let niov = self.iov.len() as c_uint;
+ let iovp = self.iov.as_mut_ptr();
+ let res = unsafe { libc::nmount(iovp, niov, flags.bits()) };
+ match Errno::result(res) {
+ Ok(_) => Ok(()),
+ Err(error) => {
+ let errmsg = match errmsg.iter().position(|&x| x == 0) {
+ None => None,
+ Some(0) => None,
+ Some(n) => {
+ let sl = &errmsg[0..n + 1];
+ Some(CStr::from_bytes_with_nul(sl).unwrap())
+ }
+ };
+ Err(NmountError::new(error, errmsg))
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> Drop for Nmount<'a> {
+ fn drop(&mut self) {
+ for (iov, is_owned) in self.iov.iter().zip(self.is_owned.iter()) {
+ if *is_owned {
+ // Free the owned string. Safe because we recorded ownership,
+ // and Nmount does not implement Clone.
+ unsafe {
+ drop(CString::from_raw(iov.iov_base as *mut c_char));
+ }
+ }
+ }
+ }
+}
+
+/// Unmount the file system mounted at `mountpoint`.
+///
+/// Useful flags include
+/// * `MNT_FORCE` - Unmount even if still in use.
+#[cfg_attr(
+ target_os = "freebsd",
+ doc = "
+* `MNT_BYFSID` - `mountpoint` is not a path, but a file system ID
+ encoded as `FSID:val0:val1`, where `val0` and `val1`
+ are the contents of the `fsid_t val[]` array in decimal.
+ The file system that has the specified file system ID
+ will be unmounted. See
+ [`statfs`](crate::sys::statfs::statfs) to determine the
+ `fsid`.
+"
+)]
+pub fn unmount<P>(mountpoint: &P, flags: MntFlags) -> Result<()>
+where
+ P: ?Sized + NixPath,
+{
+ let res = mountpoint.with_nix_path(|cstr| unsafe {
+ libc::unmount(cstr.as_ptr(), flags.bits())
+ })?;
+
+ Errno::result(res).map(drop)
+}
diff --git a/third_party/rust/nix/src/mount/linux.rs b/third_party/rust/nix/src/mount/linux.rs
new file mode 100644
index 0000000000..e987603786
--- /dev/null
+++ b/third_party/rust/nix/src/mount/linux.rs
@@ -0,0 +1,163 @@
+use crate::errno::Errno;
+use crate::{NixPath, Result};
+use libc::{self, c_int, c_ulong};
+
+libc_bitflags!(
+ /// Used with [`mount`].
+ pub struct MsFlags: c_ulong {
+ /// Mount read-only
+ MS_RDONLY;
+ /// Ignore suid and sgid bits
+ MS_NOSUID;
+ /// Disallow access to device special files
+ MS_NODEV;
+ /// Disallow program execution
+ MS_NOEXEC;
+ /// Writes are synced at once
+ MS_SYNCHRONOUS;
+ /// Alter flags of a mounted FS
+ MS_REMOUNT;
+ /// Allow mandatory locks on a FS
+ MS_MANDLOCK;
+ /// Directory modifications are synchronous
+ MS_DIRSYNC;
+ /// Do not update access times
+ MS_NOATIME;
+ /// Do not update directory access times
+ MS_NODIRATIME;
+ /// Linux 2.4.0 - Bind directory at different place
+ MS_BIND;
+ /// Move an existing mount to a new location
+ MS_MOVE;
+ /// Used to create a recursive bind mount.
+ MS_REC;
+ /// Suppress the display of certain kernel warning messages.
+ MS_SILENT;
+ /// VFS does not apply the umask
+ MS_POSIXACL;
+ /// The resulting mount cannot subsequently be bind mounted.
+ MS_UNBINDABLE;
+ /// Make this mount point private.
+ MS_PRIVATE;
+ /// If this is a shared mount point that is a member of a peer group
+ /// that contains other members, convert it to a slave mount.
+ MS_SLAVE;
+ /// Make this mount point shared.
+ MS_SHARED;
+ /// When a file on this filesystem is accessed, update the file's
+ /// last access time (atime) only if the current value of atime is
+ /// less than or equal to the file's last modification time (mtime) or
+ /// last status change time (ctime).
+ MS_RELATIME;
+ /// Mount request came from within the kernel
+ #[deprecated(since = "0.27.0", note = "Should only be used in-kernel")]
+ MS_KERNMOUNT;
+ /// Update inode I_version field
+ MS_I_VERSION;
+ /// Always update the last access time (atime) when files on this
+ /// filesystem are accessed.
+ MS_STRICTATIME;
+ /// Reduce on-disk updates of inode timestamps (atime, mtime, ctime) by
+ /// maintaining these changes only in memory.
+ MS_LAZYTIME;
+ #[deprecated(since = "0.27.0", note = "Should only be used in-kernel")]
+ #[allow(missing_docs)] // Not documented in Linux
+ MS_ACTIVE;
+ #[deprecated(since = "0.27.0", note = "Should only be used in-kernel")]
+ #[allow(missing_docs)] // Not documented in Linux
+ MS_NOUSER;
+ #[allow(missing_docs)] // Not documented in Linux; possibly kernel-only
+ MS_RMT_MASK;
+ #[allow(missing_docs)] // Not documented in Linux; possibly kernel-only
+ MS_MGC_VAL;
+ #[allow(missing_docs)] // Not documented in Linux; possibly kernel-only
+ MS_MGC_MSK;
+ }
+);
+
+libc_bitflags!(
+ /// Used with [`umount2].
+ pub struct MntFlags: c_int {
+ /// Attempt to unmount even if still in use, aborting pending requests.
+ MNT_FORCE;
+ /// Lazy unmount. Disconnect the file system immediately, but don't
+ /// actually unmount it until it ceases to be busy.
+ MNT_DETACH;
+ /// Mark the mount point as expired.
+ MNT_EXPIRE;
+ /// Don't dereference `target` if it is a symlink.
+ UMOUNT_NOFOLLOW;
+ }
+);
+
+/// Mount a file system.
+///
+/// # Arguments
+/// - `source` - Specifies the file system. e.g. `/dev/sd0`.
+/// - `target` - Specifies the destination. e.g. `/mnt`.
+/// - `fstype` - The file system type, e.g. `ext4`.
+/// - `flags` - Optional flags controlling the mount.
+/// - `data` - Optional file system specific data.
+///
+/// # See Also
+/// [`mount`](https://man7.org/linux/man-pages/man2/mount.2.html)
+pub fn mount<
+ P1: ?Sized + NixPath,
+ P2: ?Sized + NixPath,
+ P3: ?Sized + NixPath,
+ P4: ?Sized + NixPath,
+>(
+ source: Option<&P1>,
+ target: &P2,
+ fstype: Option<&P3>,
+ flags: MsFlags,
+ data: Option<&P4>,
+) -> Result<()> {
+ fn with_opt_nix_path<P, T, F>(p: Option<&P>, f: F) -> Result<T>
+ where
+ P: ?Sized + NixPath,
+ F: FnOnce(*const libc::c_char) -> T,
+ {
+ match p {
+ Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
+ None => Ok(f(std::ptr::null())),
+ }
+ }
+
+ let res = with_opt_nix_path(source, |s| {
+ target.with_nix_path(|t| {
+ with_opt_nix_path(fstype, |ty| {
+ with_opt_nix_path(data, |d| unsafe {
+ libc::mount(
+ s,
+ t.as_ptr(),
+ ty,
+ flags.bits(),
+ d as *const libc::c_void,
+ )
+ })
+ })
+ })
+ })????;
+
+ Errno::result(res).map(drop)
+}
+
+/// Unmount the file system mounted at `target`.
+pub fn umount<P: ?Sized + NixPath>(target: &P) -> Result<()> {
+ let res =
+ target.with_nix_path(|cstr| unsafe { libc::umount(cstr.as_ptr()) })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Unmount the file system mounted at `target`.
+///
+/// See also [`umount`](https://man7.org/linux/man-pages/man2/umount.2.html)
+pub fn umount2<P: ?Sized + NixPath>(target: &P, flags: MntFlags) -> Result<()> {
+ let res = target.with_nix_path(|cstr| unsafe {
+ libc::umount2(cstr.as_ptr(), flags.bits())
+ })?;
+
+ Errno::result(res).map(drop)
+}
diff --git a/third_party/rust/nix/src/mount/mod.rs b/third_party/rust/nix/src/mount/mod.rs
new file mode 100644
index 0000000000..e98b49c343
--- /dev/null
+++ b/third_party/rust/nix/src/mount/mod.rs
@@ -0,0 +1,26 @@
+//! Mount file systems
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+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"
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+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/mqueue.rs b/third_party/rust/nix/src/mqueue.rs
new file mode 100644
index 0000000000..fb07d2accb
--- /dev/null
+++ b/third_party/rust/nix/src/mqueue.rs
@@ -0,0 +1,358 @@
+//! Posix Message Queue functions
+//!
+//! # Example
+//!
+// no_run because a kernel module may be required.
+//! ```no_run
+//! # use std::ffi::CString;
+//! # use nix::mqueue::*;
+//! use nix::sys::stat::Mode;
+//!
+//! const MSG_SIZE: mq_attr_member_t = 32;
+//! let mq_name= "/a_nix_test_queue";
+//!
+//! let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
+//! let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
+//! let mqd0 = mq_open(mq_name, oflag0, mode, None).unwrap();
+//! let msg_to_send = b"msg_1";
+//! mq_send(&mqd0, msg_to_send, 1).unwrap();
+//!
+//! let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY;
+//! let mqd1 = mq_open(mq_name, oflag1, mode, None).unwrap();
+//! let mut buf = [0u8; 32];
+//! let mut prio = 0u32;
+//! let len = mq_receive(&mqd1, &mut buf, &mut prio).unwrap();
+//! assert_eq!(prio, 1);
+//! assert_eq!(msg_to_send, &buf[0..len]);
+//!
+//! mq_close(mqd1).unwrap();
+//! mq_close(mqd0).unwrap();
+//! ```
+//! [Further reading and details on the C API](https://man7.org/linux/man-pages/man7/mq_overview.7.html)
+
+use crate::errno::Errno;
+use crate::NixPath;
+use crate::Result;
+
+use crate::sys::stat::Mode;
+use libc::{self, c_char, mqd_t, size_t};
+use std::mem;
+#[cfg(any(
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "dragonfly"
+))]
+use std::os::unix::io::{
+ AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd,
+};
+
+libc_bitflags! {
+ /// Used with [`mq_open`].
+ pub struct MQ_OFlag: libc::c_int {
+ /// Open the message queue for receiving messages.
+ O_RDONLY;
+ /// Open the queue for sending messages.
+ O_WRONLY;
+ /// Open the queue for both receiving and sending messages
+ O_RDWR;
+ /// Create a message queue.
+ O_CREAT;
+ /// If set along with `O_CREAT`, `mq_open` will fail if the message
+ /// queue name exists.
+ O_EXCL;
+ /// `mq_send` and `mq_receive` should fail with `EAGAIN` rather than
+ /// wait for resources that are not currently available.
+ O_NONBLOCK;
+ /// Set the close-on-exec flag for the message queue descriptor.
+ O_CLOEXEC;
+ }
+}
+
+/// A message-queue attribute, optionally used with [`mq_setattr`] and
+/// [`mq_getattr`] and optionally [`mq_open`],
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct MqAttr {
+ mq_attr: libc::mq_attr,
+}
+
+/// Identifies an open POSIX Message Queue
+// A safer wrapper around libc::mqd_t, which is a pointer on some platforms
+// Deliberately is not Clone to prevent use-after-close scenarios
+#[repr(transparent)]
+#[derive(Debug)]
+#[allow(missing_copy_implementations)]
+pub struct MqdT(mqd_t);
+
+// x32 compatibility
+// See https://sourceware.org/bugzilla/show_bug.cgi?id=21279
+/// Size of a message queue attribute member
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub type mq_attr_member_t = i64;
+/// Size of a message queue attribute member
+#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub type mq_attr_member_t = libc::c_long;
+
+impl MqAttr {
+ /// Create a new message queue attribute
+ ///
+ /// # Arguments
+ ///
+ /// - `mq_flags`: Either `0` or `O_NONBLOCK`.
+ /// - `mq_maxmsg`: Maximum number of messages on the queue.
+ /// - `mq_msgsize`: Maximum message size in bytes.
+ /// - `mq_curmsgs`: Number of messages currently in the queue.
+ pub fn new(
+ mq_flags: mq_attr_member_t,
+ mq_maxmsg: mq_attr_member_t,
+ mq_msgsize: mq_attr_member_t,
+ mq_curmsgs: mq_attr_member_t,
+ ) -> MqAttr {
+ let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
+ unsafe {
+ let p = attr.as_mut_ptr();
+ (*p).mq_flags = mq_flags;
+ (*p).mq_maxmsg = mq_maxmsg;
+ (*p).mq_msgsize = mq_msgsize;
+ (*p).mq_curmsgs = mq_curmsgs;
+ MqAttr {
+ mq_attr: attr.assume_init(),
+ }
+ }
+ }
+
+ /// The current flags, either `0` or `O_NONBLOCK`.
+ pub const fn flags(&self) -> mq_attr_member_t {
+ self.mq_attr.mq_flags
+ }
+
+ /// The max number of messages that can be held by the queue
+ pub const fn maxmsg(&self) -> mq_attr_member_t {
+ self.mq_attr.mq_maxmsg
+ }
+
+ /// The maximum size of each message (in bytes)
+ pub const fn msgsize(&self) -> mq_attr_member_t {
+ self.mq_attr.mq_msgsize
+ }
+
+ /// The number of messages currently held in the queue
+ pub const fn curmsgs(&self) -> mq_attr_member_t {
+ self.mq_attr.mq_curmsgs
+ }
+}
+
+/// Open a message queue
+///
+/// See also [`mq_open(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html)
+// The mode.bits() cast is only lossless on some OSes
+#[allow(clippy::cast_lossless)]
+pub fn mq_open<P>(
+ name: &P,
+ oflag: MQ_OFlag,
+ mode: Mode,
+ attr: Option<&MqAttr>,
+) -> Result<MqdT>
+where
+ P: ?Sized + NixPath,
+{
+ let res = name.with_nix_path(|cstr| match attr {
+ Some(mq_attr) => unsafe {
+ libc::mq_open(
+ cstr.as_ptr(),
+ oflag.bits(),
+ mode.bits() as libc::c_int,
+ &mq_attr.mq_attr as *const libc::mq_attr,
+ )
+ },
+ None => unsafe { libc::mq_open(cstr.as_ptr(), oflag.bits()) },
+ })?;
+
+ Errno::result(res).map(MqdT)
+}
+
+/// Remove a message queue
+///
+/// See also [`mq_unlink(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html)
+pub fn mq_unlink<P>(name: &P) -> Result<()>
+where
+ P: ?Sized + NixPath,
+{
+ let res =
+ name.with_nix_path(|cstr| unsafe { libc::mq_unlink(cstr.as_ptr()) })?;
+ Errno::result(res).map(drop)
+}
+
+/// Close a message queue
+///
+/// See also [`mq_close(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html)
+pub fn mq_close(mqdes: MqdT) -> Result<()> {
+ let res = unsafe { libc::mq_close(mqdes.0) };
+ Errno::result(res).map(drop)
+}
+
+/// Receive a message from a message queue
+///
+/// See also [`mq_receive(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
+pub fn mq_receive(
+ mqdes: &MqdT,
+ message: &mut [u8],
+ msg_prio: &mut u32,
+) -> Result<usize> {
+ let len = message.len() as size_t;
+ let res = unsafe {
+ libc::mq_receive(
+ mqdes.0,
+ message.as_mut_ptr() as *mut c_char,
+ len,
+ msg_prio as *mut u32,
+ )
+ };
+ Errno::result(res).map(|r| r as usize)
+}
+
+feature! {
+ #![feature = "time"]
+ use crate::sys::time::TimeSpec;
+ /// Receive a message from a message queue with a timeout
+ ///
+ /// See also ['mq_timedreceive(2)'](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
+ pub fn mq_timedreceive(
+ mqdes: &MqdT,
+ message: &mut [u8],
+ msg_prio: &mut u32,
+ abstime: &TimeSpec,
+ ) -> Result<usize> {
+ let len = message.len() as size_t;
+ let res = unsafe {
+ libc::mq_timedreceive(
+ mqdes.0,
+ message.as_mut_ptr() as *mut c_char,
+ len,
+ msg_prio as *mut u32,
+ abstime.as_ref(),
+ )
+ };
+ Errno::result(res).map(|r| r as usize)
+ }
+}
+
+/// Send a message to a message queue
+///
+/// See also [`mq_send(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html)
+pub fn mq_send(mqdes: &MqdT, message: &[u8], msq_prio: u32) -> Result<()> {
+ let res = unsafe {
+ libc::mq_send(
+ mqdes.0,
+ message.as_ptr() as *const c_char,
+ message.len(),
+ msq_prio,
+ )
+ };
+ Errno::result(res).map(drop)
+}
+
+/// Get message queue attributes
+///
+/// See also [`mq_getattr(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html)
+pub fn mq_getattr(mqd: &MqdT) -> Result<MqAttr> {
+ let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
+ let res = unsafe { libc::mq_getattr(mqd.0, attr.as_mut_ptr()) };
+ Errno::result(res).map(|_| unsafe {
+ MqAttr {
+ mq_attr: attr.assume_init(),
+ }
+ })
+}
+
+/// Set the attributes of the message queue. Only `O_NONBLOCK` can be set, everything else will be ignored
+/// Returns the old attributes
+/// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()` convenience functions as they are easier to use
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html)
+pub fn mq_setattr(mqd: &MqdT, newattr: &MqAttr) -> Result<MqAttr> {
+ let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
+ let res = unsafe {
+ libc::mq_setattr(
+ mqd.0,
+ &newattr.mq_attr as *const libc::mq_attr,
+ attr.as_mut_ptr(),
+ )
+ };
+ Errno::result(res).map(|_| unsafe {
+ MqAttr {
+ mq_attr: attr.assume_init(),
+ }
+ })
+}
+
+/// Convenience function.
+/// Sets the `O_NONBLOCK` attribute for a given message queue descriptor
+/// Returns the old attributes
+#[allow(clippy::useless_conversion)] // Not useless on all OSes
+pub fn mq_set_nonblock(mqd: &MqdT) -> Result<MqAttr> {
+ let oldattr = mq_getattr(mqd)?;
+ let newattr = MqAttr::new(
+ mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()),
+ oldattr.mq_attr.mq_maxmsg,
+ oldattr.mq_attr.mq_msgsize,
+ oldattr.mq_attr.mq_curmsgs,
+ );
+ mq_setattr(mqd, &newattr)
+}
+
+/// Convenience function.
+/// Removes `O_NONBLOCK` attribute for a given message queue descriptor
+/// Returns the old attributes
+pub fn mq_remove_nonblock(mqd: &MqdT) -> Result<MqAttr> {
+ let oldattr = mq_getattr(mqd)?;
+ let newattr = MqAttr::new(
+ 0,
+ oldattr.mq_attr.mq_maxmsg,
+ oldattr.mq_attr.mq_msgsize,
+ oldattr.mq_attr.mq_curmsgs,
+ );
+ mq_setattr(mqd, &newattr)
+}
+
+#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
+impl AsFd for MqdT {
+ /// Borrow the underlying message queue descriptor.
+ fn as_fd(&self) -> BorrowedFd {
+ // SAFETY: [MqdT] will only contain a valid fd by construction.
+ unsafe { BorrowedFd::borrow_raw(self.0) }
+ }
+}
+
+#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
+impl AsRawFd for MqdT {
+ /// Return the underlying message queue descriptor.
+ ///
+ /// Returned descriptor is a "shallow copy" of the descriptor, so it refers
+ /// to the same underlying kernel object as `self`.
+ fn as_raw_fd(&self) -> RawFd {
+ self.0
+ }
+}
+
+#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
+impl FromRawFd for MqdT {
+ /// Construct an [MqdT] from [RawFd].
+ ///
+ /// # Safety
+ /// The `fd` given must be a valid and open file descriptor for a message
+ /// queue.
+ unsafe fn from_raw_fd(fd: RawFd) -> MqdT {
+ MqdT(fd)
+ }
+}
+
+#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "dragonfly"))]
+impl IntoRawFd for MqdT {
+ /// Consume this [MqdT] and return a [RawFd].
+ fn into_raw_fd(self) -> RawFd {
+ self.0
+ }
+}
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..ec46260714
--- /dev/null
+++ b/third_party/rust/nix/src/net/if_.rs
@@ -0,0 +1,471 @@
+//! Network interface name resolution.
+//!
+//! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
+//! or "socan1" into device numbers.
+
+use crate::{Error, NixPath, Result};
+use libc::c_uint;
+
+/// 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)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_UP;
+ /// Valid broadcast address set. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_BROADCAST;
+ /// Internal debugging flag. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(not(target_os = "haiku"))]
+ IFF_DEBUG;
+ /// Interface is a loopback interface. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_LOOPBACK;
+ /// Interface is a point-to-point link. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_POINTOPOINT;
+ /// Avoid use of trailers. (see
+ /// [`netdevice(7)`](https://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 = "illumos",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NOTRAILERS;
+ /// Interface manages own routes.
+ #[cfg(any(target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_SMART;
+ /// Resources allocated. (see
+ /// [`netdevice(7)`](https://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 = "illumos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_RUNNING;
+ /// No arp protocol, L2 destination address not set. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_NOARP;
+ /// Interface is in promiscuous mode. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_PROMISC;
+ /// Receive all multicast packets. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_ALLMULTI;
+ /// Master of a load balancing bundle. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_OACTIVE;
+ /// Protocol code on board.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_INTELLIGENT;
+ /// Slave of a load balancing bundle. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_SLAVE;
+ /// Can't hear own transmissions.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_SIMPLEX;
+ /// Supports multicast. (see
+ /// [`netdevice(7)`](https://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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_LINK0;
+ /// Multicast using broadcast.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_MULTI_BCAST;
+ /// Is able to select media type via ifmap. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_LINK1;
+ /// Non-unique address.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_UNNUMBERED;
+ /// Auto media selection active. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_LINK2;
+ /// Use alternate physical connection.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_ALTPHYS;
+ /// DHCP controls interface.
+ #[cfg(any(target_os = "solaris", target_os = "illumos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_DHCPRUNNING;
+ /// The addresses are lost when the interface goes down. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_DYNAMIC;
+ /// Do not advertise.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_PRIVATE;
+ /// Driver signals L1 up. Volatile.
+ #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_LOWER_UP;
+ /// Interface is in polling mode.
+ #[cfg(any(target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_POLLING_COMPAT;
+ /// Unconfigurable using ioctl(2).
+ #[cfg(any(target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_CANTCONFIG;
+ /// Do not transmit packets.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NOXMIT;
+ /// Driver signals dormant. Volatile.
+ #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_DORMANT;
+ /// User-requested promisc mode.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_PPROMISC;
+ /// Just on-link subnet.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NOLOCAL;
+ /// Echo sent packets. Volatile.
+ #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_ECHO;
+ /// User-requested monitor mode.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_MONITOR;
+ /// Address is deprecated.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_DEPRECATED;
+ /// Static ARP.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_STATICARP;
+ /// Address from stateless addrconf.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_ADDRCONF;
+ /// Interface is in polling mode.
+ #[cfg(any(target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NPOLLING;
+ /// Router on interface.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_ROUTER;
+ /// Interface is in polling mode.
+ #[cfg(any(target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_IDIRECT;
+ /// Interface is winding down
+ #[cfg(any(target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_DYING;
+ /// No NUD on interface.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NONUD;
+ /// Interface is being renamed
+ #[cfg(any(target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_RENAMING;
+ /// Anycast address.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_ANYCAST;
+ /// Don't exchange routing info.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NORTEXCH;
+ /// Do not provide packet information
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NO_PI as libc::c_int;
+ /// TUN device (no Ethernet headers)
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_TUN as libc::c_int;
+ /// TAP device
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_TAP as libc::c_int;
+ /// IPv4 interface.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_IPV4;
+ /// IPv6 interface.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_IPV6;
+ /// in.mpathd test address
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NOFAILOVER;
+ /// Interface has failed
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_FAILED;
+ /// Interface is a hot-spare
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_STANDBY;
+ /// Functioning but not used
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_INACTIVE;
+ /// Interface is offline
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_OFFLINE;
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_COS_ENABLED;
+ /// Prefer as source addr.
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_PREFERRED;
+ /// RFC3041
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_TEMPORARY;
+ /// MTU set with SIOCSLIFMTU
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_FIXEDMTU;
+ /// Cannot send / receive packets
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_VIRTUAL;
+ /// Local address in use
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_DUPLICATE;
+ /// IPMP IP interface
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_IPMP;
+ }
+);
+
+#[cfg(any(
+ 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 = "illumos",
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+mod if_nameindex {
+ use super::*;
+
+ use std::ffi::CStr;
+ use std::fmt;
+ use std::marker::PhantomData;
+ use std::ptr::NonNull;
+
+ /// A network interface. Has a name like "eth0" or "wlp4s0" or "wlan0", as well as an index
+ /// (1, 2, 3, etc) that identifies it in the OS's networking stack.
+ #[allow(missing_copy_implementations)]
+ #[repr(transparent)]
+ pub struct Interface(libc::if_nameindex);
+
+ impl Interface {
+ /// Obtain the index of this interface.
+ pub fn index(&self) -> c_uint {
+ self.0.if_index
+ }
+
+ /// Obtain the name of this interface.
+ pub fn name(&self) -> &CStr {
+ unsafe { CStr::from_ptr(self.0.if_name) }
+ }
+ }
+
+ impl fmt::Debug for Interface {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Interface")
+ .field("index", &self.index())
+ .field("name", &self.name())
+ .finish()
+ }
+ }
+
+ /// A list of the network interfaces available on this system. Obtained from [`if_nameindex()`].
+ pub struct Interfaces {
+ ptr: NonNull<libc::if_nameindex>,
+ }
+
+ impl Interfaces {
+ /// Iterate over the interfaces in this list.
+ #[inline]
+ pub fn iter(&self) -> InterfacesIter<'_> {
+ self.into_iter()
+ }
+
+ /// Convert this to a slice of interfaces. Note that the underlying interfaces list is
+ /// null-terminated, so calling this calculates the length. If random access isn't needed,
+ /// [`Interfaces::iter()`] should be used instead.
+ pub fn to_slice(&self) -> &[Interface] {
+ let ifs = self.ptr.as_ptr() as *const Interface;
+ let len = self.iter().count();
+ unsafe { std::slice::from_raw_parts(ifs, len) }
+ }
+ }
+
+ impl Drop for Interfaces {
+ fn drop(&mut self) {
+ unsafe { libc::if_freenameindex(self.ptr.as_ptr()) };
+ }
+ }
+
+ impl fmt::Debug for Interfaces {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.to_slice().fmt(f)
+ }
+ }
+
+ impl<'a> IntoIterator for &'a Interfaces {
+ type IntoIter = InterfacesIter<'a>;
+ type Item = &'a Interface;
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ InterfacesIter {
+ ptr: self.ptr.as_ptr(),
+ _marker: PhantomData,
+ }
+ }
+ }
+
+ /// An iterator over the interfaces in an [`Interfaces`].
+ #[derive(Debug)]
+ pub struct InterfacesIter<'a> {
+ ptr: *const libc::if_nameindex,
+ _marker: PhantomData<&'a Interfaces>,
+ }
+
+ impl<'a> Iterator for InterfacesIter<'a> {
+ type Item = &'a Interface;
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ unsafe {
+ if (*self.ptr).if_index == 0 {
+ None
+ } else {
+ let ret = &*(self.ptr as *const Interface);
+ self.ptr = self.ptr.add(1);
+ Some(ret)
+ }
+ }
+ }
+ }
+
+ /// Retrieve a list of the network interfaces available on the local system.
+ ///
+ /// ```
+ /// let interfaces = nix::net::if_::if_nameindex().unwrap();
+ /// for iface in &interfaces {
+ /// println!("Interface #{} is called {}", iface.index(), iface.name().to_string_lossy());
+ /// }
+ /// ```
+ pub fn if_nameindex() -> Result<Interfaces> {
+ unsafe {
+ let ifs = libc::if_nameindex();
+ let ptr = NonNull::new(ifs).ok_or_else(Error::last)?;
+ Ok(Interfaces { ptr })
+ }
+ }
+}
+#[cfg(any(
+ 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 = "illumos",
+))]
+pub use if_nameindex::*;
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..9181bf7f30
--- /dev/null
+++ b/third_party/rust/nix/src/poll.rs
@@ -0,0 +1,233 @@
+//! Wait for events to trigger on specific file descriptors
+use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd};
+
+use crate::errno::Errno;
+use crate::Result;
+
+/// This is a wrapper around `libc::pollfd`.
+///
+/// It's meant to be used as an argument to the [`poll`](fn.poll.html) and
+/// [`ppoll`](fn.ppoll.html) functions to specify the events of interest
+/// for a specific file descriptor.
+///
+/// After a call to `poll` or `ppoll`, the events that occurred can be
+/// retrieved by calling [`revents()`](#method.revents) on the `PollFd`.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct PollFd<'fd> {
+ pollfd: libc::pollfd,
+ _fd: std::marker::PhantomData<BorrowedFd<'fd>>,
+}
+
+impl<'fd> PollFd<'fd> {
+ /// Creates a new `PollFd` specifying the events of interest
+ /// for a given file descriptor.
+ //
+ // Different from other I/O-safe interfaces, here, we have to take `AsFd`
+ // by reference to prevent the case where the `fd` is closed but it is
+ // still in use. For example:
+ //
+ // ```rust
+ // let (reader, _) = pipe().unwrap();
+ //
+ // // If `PollFd::new()` takes `AsFd` by value, then `reader` will be consumed,
+ // // but the file descriptor of `reader` will still be in use.
+ // let pollfd = PollFd::new(reader, flag);
+ //
+ // // Do something with `pollfd`, which uses the CLOSED fd.
+ // ```
+ pub fn new<Fd: AsFd>(fd: &'fd Fd, events: PollFlags) -> PollFd<'fd> {
+ PollFd {
+ pollfd: libc::pollfd {
+ fd: fd.as_fd().as_raw_fd(),
+ events: events.bits(),
+ revents: PollFlags::empty().bits(),
+ },
+ _fd: std::marker::PhantomData,
+ }
+ }
+
+ /// Returns the events that occurred in the last call to `poll` or `ppoll`. Will only return
+ /// `None` if the kernel provides status flags that Nix does not know about.
+ pub fn revents(self) -> Option<PollFlags> {
+ PollFlags::from_bits(self.pollfd.revents)
+ }
+
+ /// Returns if any of the events of interest occured in the last call to `poll` or `ppoll`. Will
+ /// only return `None` if the kernel provides status flags that Nix does not know about.
+ ///
+ /// Equivalent to `x.revents()? != PollFlags::empty()`.
+ ///
+ /// This is marginally more efficient than [`PollFd::all`].
+ pub fn any(self) -> Option<bool> {
+ Some(self.revents()? != PollFlags::empty())
+ }
+
+ /// Returns if all the events of interest occured in the last call to `poll` or `ppoll`. Will
+ /// only return `None` if the kernel provides status flags that Nix does not know about.
+ ///
+ /// Equivalent to `x.revents()? & x.events() == x.events()`.
+ ///
+ /// This is marginally less efficient than [`PollFd::any`].
+ pub fn all(self) -> Option<bool> {
+ Some(self.revents()? & self.events() == self.events())
+ }
+
+ /// The events of interest for this `PollFd`.
+ pub fn events(self) -> PollFlags {
+ PollFlags::from_bits(self.pollfd.events).unwrap()
+ }
+
+ /// Modify the events of interest for this `PollFd`.
+ pub fn set_events(&mut self, events: PollFlags) {
+ self.pollfd.events = events.bits();
+ }
+}
+
+impl<'fd> AsFd for PollFd<'fd> {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ // Safety:
+ //
+ // BorrowedFd::borrow_raw(RawFd) requires that the raw fd being passed
+ // must remain open for the duration of the returned BorrowedFd, this is
+ // guaranteed as the returned BorrowedFd has the lifetime parameter same
+ // as `self`:
+ // "fn as_fd<'self>(&'self self) -> BorrowedFd<'self>"
+ // which means that `self` (PollFd) is guaranteed to outlive the returned
+ // BorrowedFd. (Lifetime: PollFd > BorrowedFd)
+ //
+ // And the lifetime parameter of PollFd::new(fd, ...) ensures that `fd`
+ // (an owned file descriptor) must outlive the returned PollFd:
+ // "pub fn new<Fd: AsFd>(fd: &'fd Fd, events: PollFlags) -> PollFd<'fd>"
+ // (Lifetime: Owned fd > PollFd)
+ //
+ // With two above relationships, we can conclude that the `Owned file
+ // descriptor` will outlive the returned BorrowedFd,
+ // (Lifetime: Owned fd > BorrowedFd)
+ // i.e., the raw fd being passed will remain valid for the lifetime of
+ // the returned BorrowedFd.
+ unsafe { BorrowedFd::borrow_raw(self.pollfd.fd) }
+ }
+}
+
+libc_bitflags! {
+ /// These flags define the different events that can be monitored by `poll` and `ppoll`
+ pub struct PollFlags: libc::c_short {
+ /// There is data to read.
+ POLLIN;
+ /// There is some exceptional condition on the file descriptor.
+ ///
+ /// Possibilities include:
+ ///
+ /// * There is out-of-band data on a TCP socket (see
+ /// [tcp(7)](https://man7.org/linux/man-pages/man7/tcp.7.html)).
+ /// * A pseudoterminal master in packet mode has seen a state
+ /// change on the slave (see
+ /// [ioctl_tty(2)](https://man7.org/linux/man-pages/man2/ioctl_tty.2.html)).
+ /// * A cgroup.events file has been modified (see
+ /// [cgroups(7)](https://man7.org/linux/man-pages/man7/cgroups.7.html)).
+ POLLPRI;
+ /// Writing is now possible, though a write larger that the
+ /// available space in a socket or pipe will still block (unless
+ /// `O_NONBLOCK` is set).
+ POLLOUT;
+ /// Equivalent to [`POLLIN`](constant.POLLIN.html)
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ POLLRDNORM;
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Equivalent to [`POLLOUT`](constant.POLLOUT.html)
+ POLLWRNORM;
+ /// Priority band data can be read (generally unused on Linux).
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ POLLRDBAND;
+ /// Priority data may be written.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ POLLWRBAND;
+ /// Error condition (only returned in
+ /// [`PollFd::revents`](struct.PollFd.html#method.revents);
+ /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
+ /// This bit is also set for a file descriptor referring to the
+ /// write end of a pipe when the read end has been closed.
+ POLLERR;
+ /// Hang up (only returned in [`PollFd::revents`](struct.PollFd.html#method.revents);
+ /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
+ /// Note that when reading from a channel such as a pipe or a stream
+ /// socket, this event merely indicates that the peer closed its
+ /// end of the channel. Subsequent reads from the channel will
+ /// return 0 (end of file) only after all outstanding data in the
+ /// channel has been consumed.
+ POLLHUP;
+ /// Invalid request: `fd` not open (only returned in
+ /// [`PollFd::revents`](struct.PollFd.html#method.revents);
+ /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
+ POLLNVAL;
+ }
+}
+
+/// `poll` waits for one of a set of file descriptors to become ready to perform I/O.
+/// ([`poll(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html))
+///
+/// `fds` contains all [`PollFd`](struct.PollFd.html) to poll.
+/// The function will return as soon as any event occur for any of these `PollFd`s.
+///
+/// The `timeout` argument specifies the number of milliseconds that `poll()`
+/// should block waiting for a file descriptor to become ready. The call
+/// will block until either:
+///
+/// * a file descriptor becomes ready;
+/// * the call is interrupted by a signal handler; or
+/// * the timeout expires.
+///
+/// Note that the timeout interval will be rounded up to the system clock
+/// granularity, and kernel scheduling delays mean that the blocking
+/// interval may overrun by a small amount. Specifying a 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)
+}
+
+feature! {
+#![feature = "signal"]
+/// `ppoll()` allows an application to safely wait until either a file
+/// descriptor becomes ready or until a signal is caught.
+/// ([`poll(2)`](https://man7.org/linux/man-pages/man2/poll.2.html))
+///
+/// `ppoll` behaves like `poll`, but let you specify what signals may interrupt it
+/// with the `sigmask` argument. If you want `ppoll` to block indefinitely,
+/// specify `None` as `timeout` (it is like `timeout = -1` for `poll`).
+/// If `sigmask` is `None`, then no signal mask manipulation is performed,
+/// so in that case `ppoll` differs from `poll` only in the precision of the
+/// timeout argument.
+///
+#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
+pub fn ppoll(
+ fds: &mut [PollFd],
+ timeout: Option<crate::sys::time::TimeSpec>,
+ sigmask: Option<crate::sys::signal::SigSet>
+ ) -> Result<libc::c_int>
+{
+ let timeout = timeout.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
+ let sigmask = sigmask.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
+ let res = unsafe {
+ libc::ppoll(fds.as_mut_ptr() as *mut libc::pollfd,
+ fds.len() as libc::nfds_t,
+ timeout,
+ sigmask)
+ };
+ 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..455828b703
--- /dev/null
+++ b/third_party/rust/nix/src/pty.rs
@@ -0,0 +1,357 @@
+//! Create master and slave virtual pseudo-terminals (PTYs)
+
+pub use libc::pid_t as SessionId;
+pub use libc::winsize as Winsize;
+
+use std::ffi::CStr;
+use std::io;
+#[cfg(not(target_os = "aix"))]
+use std::mem;
+use std::os::unix::prelude::*;
+
+use crate::errno::Errno;
+#[cfg(not(target_os = "aix"))]
+use crate::sys::termios::Termios;
+#[cfg(feature = "process")]
+use crate::unistd::ForkResult;
+#[cfg(all(feature = "process", not(target_os = "aix")))]
+use crate::unistd::Pid;
+use crate::{fcntl, unistd, Result};
+
+/// Representation of a master/slave pty pair
+///
+/// This is returned by [`openpty`].
+#[derive(Debug)]
+pub struct OpenptyResult {
+ /// The master port in a virtual pty pair
+ pub master: OwnedFd,
+ /// The slave port in a virtual pty pair
+ pub slave: OwnedFd,
+}
+
+feature! {
+#![feature = "process"]
+/// Representation of a master with a forked pty
+///
+/// This is returned by [`forkpty`].
+#[derive(Debug)]
+pub struct ForkptyResult {
+ /// The master port in a virtual pty pair
+ pub master: OwnedFd,
+ /// Metadata about forked process
+ pub fork_result: ForkResult,
+}
+}
+
+/// Representation of the Master device in a master/slave pty pair
+///
+/// While this datatype is a thin wrapper around `OwnedFd`, it enforces that the available PTY
+/// functions are given the correct file descriptor.
+#[derive(Debug)]
+pub struct PtyMaster(OwnedFd);
+
+impl AsRawFd for PtyMaster {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for PtyMaster {
+ fn into_raw_fd(self) -> RawFd {
+ let fd = self.0;
+ fd.into_raw_fd()
+ }
+}
+
+impl io::Read for PtyMaster {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ unistd::read(self.0.as_raw_fd(), buf).map_err(io::Error::from)
+ }
+}
+
+impl io::Write for PtyMaster {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ unistd::write(self.0.as_raw_fd(), buf).map_err(io::Error::from)
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl io::Read for &PtyMaster {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ unistd::read(self.0.as_raw_fd(), buf).map_err(io::Error::from)
+ }
+}
+
+impl io::Write for &PtyMaster {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ unistd::write(self.0.as_raw_fd(), buf).map_err(io::Error::from)
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+/// Grant access to a slave pseudoterminal (see
+/// [`grantpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html))
+///
+/// `grantpt()` changes the mode and owner of the slave pseudoterminal device corresponding to the
+/// master pseudoterminal referred to by `fd`. This is a necessary step towards opening the slave.
+#[inline]
+pub fn grantpt(fd: &PtyMaster) -> Result<()> {
+ if unsafe { libc::grantpt(fd.as_raw_fd()) } < 0 {
+ return Err(Errno::last());
+ }
+
+ Ok(())
+}
+
+/// Open a pseudoterminal device (see
+/// [`posix_openpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html))
+///
+/// `posix_openpt()` returns a file descriptor to an existing unused pseudoterminal master device.
+///
+/// # Examples
+///
+/// A common use case with this function is to open both a master and slave PTY pair. This can be
+/// done as follows:
+///
+/// ```
+/// use std::path::Path;
+/// use nix::fcntl::{OFlag, open};
+/// use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt};
+/// use nix::sys::stat::Mode;
+///
+/// # #[allow(dead_code)]
+/// # fn run() -> nix::Result<()> {
+/// // Open a new PTY master
+/// let master_fd = posix_openpt(OFlag::O_RDWR)?;
+///
+/// // Allow a slave to be generated for it
+/// grantpt(&master_fd)?;
+/// unlockpt(&master_fd)?;
+///
+/// // Get the name of the slave
+/// let slave_name = unsafe { ptsname(&master_fd) }?;
+///
+/// // Try to open the slave
+/// let _slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, Mode::empty())?;
+/// # Ok(())
+/// # }
+/// ```
+#[inline]
+pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
+ let fd = unsafe { libc::posix_openpt(flags.bits()) };
+
+ if fd < 0 {
+ return Err(Errno::last());
+ }
+
+ Ok(PtyMaster(unsafe { OwnedFd::from_raw_fd(fd) }))
+}
+
+/// Get the name of the slave pseudoterminal (see
+/// [`ptsname(3)`](https://man7.org/linux/man-pages/man3/ptsname.3.html))
+///
+/// `ptsname()` returns the name of the slave pseudoterminal device corresponding to the master
+/// referred to by `fd`.
+///
+/// This value is useful for opening the slave pty once the master has already been opened with
+/// `posix_openpt()`.
+///
+/// # Safety
+///
+/// `ptsname()` mutates global variables and is *not* threadsafe.
+/// Mutating global variables is always considered `unsafe` by Rust and this
+/// function is marked as `unsafe` to reflect that.
+///
+/// For a threadsafe and non-`unsafe` alternative on Linux, see `ptsname_r()`.
+#[inline]
+pub unsafe fn ptsname(fd: &PtyMaster) -> Result<String> {
+ let name_ptr = libc::ptsname(fd.as_raw_fd());
+ if name_ptr.is_null() {
+ return Err(Errno::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)`](https://man7.org/linux/man-pages/man3/ptsname.3.html))
+///
+/// `ptsname_r()` returns the name of the slave pseudoterminal device corresponding to the master
+/// referred to by `fd`. This is the threadsafe version of `ptsname()`, but it is not part of the
+/// POSIX standard and is instead a Linux-specific extension.
+///
+/// This value is useful for opening the slave ptty once the master has already been opened with
+/// `posix_openpt()`.
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+#[inline]
+pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
+ let mut name_buf = Vec::<libc::c_char>::with_capacity(64);
+ let name_buf_ptr = name_buf.as_mut_ptr();
+ let cname = unsafe {
+ let cap = name_buf.capacity();
+ if libc::ptsname_r(fd.as_raw_fd(), name_buf_ptr, cap) != 0 {
+ return Err(crate::Error::last());
+ }
+ CStr::from_ptr(name_buf.as_ptr())
+ };
+
+ let name = cname.to_string_lossy().into_owned();
+ Ok(name)
+}
+
+/// Unlock a pseudoterminal master/slave pseudoterminal pair (see
+/// [`unlockpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlockpt.html))
+///
+/// `unlockpt()` unlocks the slave pseudoterminal device corresponding to the master pseudoterminal
+/// referred to by `fd`. This must be called before trying to open the slave side of a
+/// pseudoterminal.
+#[inline]
+pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
+ if unsafe { libc::unlockpt(fd.as_raw_fd()) } < 0 {
+ return Err(Errno::last());
+ }
+
+ Ok(())
+}
+
+/// Create a new pseudoterminal, returning the slave and master file descriptors
+/// in `OpenptyResult`
+/// (see [`openpty`](https://man7.org/linux/man-pages/man3/openpty.3.html)).
+///
+/// If `winsize` is not `None`, the window size of the slave will be set to
+/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
+/// terminal settings of the slave will be set to the values in `termios`.
+#[inline]
+#[cfg(not(target_os = "aix"))]
+pub fn openpty<
+ 'a,
+ 'b,
+ T: Into<Option<&'a Winsize>>,
+ U: Into<Option<&'b Termios>>,
+>(
+ winsize: T,
+ termios: U,
+) -> Result<OpenptyResult> {
+ use std::ptr;
+
+ let mut slave = mem::MaybeUninit::<libc::c_int>::uninit();
+ let mut master = mem::MaybeUninit::<libc::c_int>::uninit();
+ let ret = {
+ match (termios.into(), winsize.into()) {
+ (Some(termios), Some(winsize)) => {
+ let inner_termios = termios.get_libc_termios();
+ unsafe {
+ libc::openpty(
+ master.as_mut_ptr(),
+ slave.as_mut_ptr(),
+ ptr::null_mut(),
+ &*inner_termios as *const libc::termios as *mut _,
+ winsize as *const Winsize as *mut _,
+ )
+ }
+ }
+ (None, Some(winsize)) => unsafe {
+ libc::openpty(
+ master.as_mut_ptr(),
+ slave.as_mut_ptr(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ winsize as *const Winsize as *mut _,
+ )
+ },
+ (Some(termios), None) => {
+ let inner_termios = termios.get_libc_termios();
+ unsafe {
+ libc::openpty(
+ master.as_mut_ptr(),
+ slave.as_mut_ptr(),
+ ptr::null_mut(),
+ &*inner_termios as *const libc::termios as *mut _,
+ ptr::null_mut(),
+ )
+ }
+ }
+ (None, None) => unsafe {
+ libc::openpty(
+ master.as_mut_ptr(),
+ slave.as_mut_ptr(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ )
+ },
+ }
+ };
+
+ Errno::result(ret)?;
+
+ unsafe {
+ Ok(OpenptyResult {
+ master: OwnedFd::from_raw_fd(master.assume_init()),
+ slave: OwnedFd::from_raw_fd(slave.assume_init()),
+ })
+ }
+}
+
+feature! {
+#![feature = "process"]
+/// Create a new pseudoterminal, returning the master file descriptor and forked pid.
+/// in `ForkptyResult`
+/// (see [`forkpty`](https://man7.org/linux/man-pages/man3/forkpty.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`.
+///
+/// # 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]: https://man7.org/linux/man-pages/man7/signal-safety.7.html
+#[cfg(not(target_os = "aix"))]
+pub unsafe fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(
+ winsize: T,
+ termios: U,
+) -> Result<ForkptyResult> {
+ use std::ptr;
+
+ let mut master = mem::MaybeUninit::<libc::c_int>::uninit();
+
+ let term = match termios.into() {
+ Some(termios) => {
+ let inner_termios = termios.get_libc_termios();
+ &*inner_termios as *const libc::termios as *mut _
+ },
+ None => ptr::null_mut(),
+ };
+
+ let win = winsize
+ .into()
+ .map(|ws| ws as *const Winsize as *mut _)
+ .unwrap_or(ptr::null_mut());
+
+ let res = libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win);
+
+ let fork_result = Errno::result(res).map(|res| match res {
+ 0 => ForkResult::Child,
+ res => ForkResult::Parent { child: Pid::from_raw(res) },
+ })?;
+
+ Ok(ForkptyResult {
+ master: OwnedFd::from_raw_fd(master.assume_init()),
+ fork_result,
+ })
+}
+}
diff --git a/third_party/rust/nix/src/sched.rs b/third_party/rust/nix/src/sched.rs
new file mode 100644
index 0000000000..c9d5d6d8a1
--- /dev/null
+++ b/third_party/rust/nix/src/sched.rs
@@ -0,0 +1,332 @@
+//! Execution scheduling
+//!
+//! See Also
+//! [sched.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html)
+use crate::{Errno, Result};
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use self::sched_linux_like::*;
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+mod sched_linux_like {
+ use crate::errno::Errno;
+ use crate::unistd::Pid;
+ use crate::Result;
+ use libc::{self, c_int, c_void};
+ use std::mem;
+ use std::option::Option;
+ use std::os::unix::io::{AsFd, AsRawFd};
+
+ // For some functions taking with a parameter of type CloneFlags,
+ // only a subset of these flags have an effect.
+ libc_bitflags! {
+ /// Options for use with [`clone`]
+ pub struct CloneFlags: c_int {
+ /// The calling process and the child process run in the same
+ /// memory space.
+ CLONE_VM;
+ /// The caller and the child process share the same filesystem
+ /// information.
+ CLONE_FS;
+ /// The calling process and the child process share the same file
+ /// descriptor table.
+ CLONE_FILES;
+ /// The calling process and the child process share the same table
+ /// of signal handlers.
+ CLONE_SIGHAND;
+ /// If the calling process is being traced, then trace the child
+ /// also.
+ CLONE_PTRACE;
+ /// The execution of the calling process is suspended until the
+ /// child releases its virtual memory resources via a call to
+ /// execve(2) or _exit(2) (as with vfork(2)).
+ CLONE_VFORK;
+ /// The parent of the new child (as returned by getppid(2))
+ /// will be the same as that of the calling process.
+ CLONE_PARENT;
+ /// The child is placed in the same thread group as the calling
+ /// process.
+ CLONE_THREAD;
+ /// The cloned child is started in a new mount namespace.
+ CLONE_NEWNS;
+ /// The child and the calling process share a single list of System
+ /// V semaphore adjustment values
+ CLONE_SYSVSEM;
+ // Not supported by Nix due to lack of varargs support in Rust FFI
+ // CLONE_SETTLS;
+ // Not supported by Nix due to lack of varargs support in Rust FFI
+ // CLONE_PARENT_SETTID;
+ // Not supported by Nix due to lack of varargs support in Rust FFI
+ // CLONE_CHILD_CLEARTID;
+ /// Unused since Linux 2.6.2
+ #[deprecated(since = "0.23.0", note = "Deprecated by Linux 2.6.2")]
+ CLONE_DETACHED;
+ /// A tracing process cannot force `CLONE_PTRACE` on this child
+ /// process.
+ CLONE_UNTRACED;
+ // Not supported by Nix due to lack of varargs support in Rust FFI
+ // CLONE_CHILD_SETTID;
+ /// Create the process in a new cgroup namespace.
+ CLONE_NEWCGROUP;
+ /// Create the process in a new UTS namespace.
+ CLONE_NEWUTS;
+ /// Create the process in a new IPC namespace.
+ CLONE_NEWIPC;
+ /// Create the process in a new user namespace.
+ CLONE_NEWUSER;
+ /// Create the process in a new PID namespace.
+ CLONE_NEWPID;
+ /// Create the process in a new network namespace.
+ CLONE_NEWNET;
+ /// The new process shares an I/O context with the calling process.
+ CLONE_IO;
+ }
+ }
+
+ /// Type for the function executed by [`clone`].
+ pub type CloneCb<'a> = Box<dyn FnMut() -> isize + 'a>;
+
+ /// `clone` create a child process
+ /// ([`clone(2)`](https://man7.org/linux/man-pages/man2/clone.2.html))
+ ///
+ /// `stack` is a reference to an array which will hold the stack of the new
+ /// process. Unlike when calling `clone(2)` from C, the provided stack
+ /// address need not be the highest address of the region. Nix will take
+ /// care of that requirement. The user only needs to provide a reference to
+ /// a normally allocated buffer.
+ ///
+ /// # Safety
+ ///
+ /// Because `clone` creates a child process with its stack located in
+ /// `stack` without specifying the size of the stack, special care must be
+ /// taken to ensure that the child process does not overflow the provided
+ /// stack space.
+ ///
+ /// See [`fork`](crate::unistd::fork) for additional safety concerns related
+ /// to executing child processes.
+ pub unsafe fn clone(
+ mut cb: CloneCb,
+ stack: &mut [u8],
+ flags: CloneFlags,
+ signal: Option<c_int>,
+ ) -> Result<Pid> {
+ extern "C" fn callback(data: *mut CloneCb) -> c_int {
+ let cb: &mut CloneCb = unsafe { &mut *data };
+ (*cb)() as c_int
+ }
+
+ let combined = flags.bits() | signal.unwrap_or(0);
+ let ptr = stack.as_mut_ptr().add(stack.len());
+ let ptr_aligned = ptr.sub(ptr as usize % 16);
+ let res = libc::clone(
+ mem::transmute(
+ callback
+ as extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32,
+ ),
+ ptr_aligned as *mut c_void,
+ combined,
+ &mut cb as *mut _ as *mut c_void,
+ );
+
+ Errno::result(res).map(Pid::from_raw)
+ }
+
+ /// disassociate parts of the process execution context
+ ///
+ /// See also [unshare(2)](https://man7.org/linux/man-pages/man2/unshare.2.html)
+ pub fn unshare(flags: CloneFlags) -> Result<()> {
+ let res = unsafe { libc::unshare(flags.bits()) };
+
+ Errno::result(res).map(drop)
+ }
+
+ /// reassociate thread with a namespace
+ ///
+ /// See also [setns(2)](https://man7.org/linux/man-pages/man2/setns.2.html)
+ pub fn setns<Fd: AsFd>(fd: Fd, nstype: CloneFlags) -> Result<()> {
+ let res = unsafe { libc::setns(fd.as_fd().as_raw_fd(), nstype.bits()) };
+
+ Errno::result(res).map(drop)
+ }
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux"
+))]
+pub use self::sched_affinity::*;
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux"
+))]
+mod sched_affinity {
+ use crate::errno::Errno;
+ use crate::unistd::Pid;
+ use crate::Result;
+ use std::mem;
+
+ /// CpuSet represent a bit-mask of CPUs.
+ /// CpuSets are used by sched_setaffinity and
+ /// sched_getaffinity for example.
+ ///
+ /// This is a wrapper around `libc::cpu_set_t`.
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ pub struct CpuSet {
+ #[cfg(not(target_os = "freebsd"))]
+ cpu_set: libc::cpu_set_t,
+ #[cfg(target_os = "freebsd")]
+ cpu_set: libc::cpuset_t,
+ }
+
+ impl CpuSet {
+ /// Create a new and empty CpuSet.
+ pub fn new() -> CpuSet {
+ CpuSet {
+ cpu_set: unsafe { mem::zeroed() },
+ }
+ }
+
+ /// Test to see if a CPU is in the CpuSet.
+ /// `field` is the CPU id to test
+ pub fn is_set(&self, field: usize) -> Result<bool> {
+ if field >= CpuSet::count() {
+ Err(Errno::EINVAL)
+ } else {
+ Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) })
+ }
+ }
+
+ /// Add a CPU to CpuSet.
+ /// `field` is the CPU id to add
+ pub fn set(&mut self, field: usize) -> Result<()> {
+ if field >= CpuSet::count() {
+ Err(Errno::EINVAL)
+ } else {
+ unsafe {
+ libc::CPU_SET(field, &mut self.cpu_set);
+ }
+ Ok(())
+ }
+ }
+
+ /// Remove a CPU from CpuSet.
+ /// `field` is the CPU id to remove
+ pub fn unset(&mut self, field: usize) -> Result<()> {
+ if field >= CpuSet::count() {
+ Err(Errno::EINVAL)
+ } else {
+ unsafe {
+ libc::CPU_CLR(field, &mut self.cpu_set);
+ }
+ Ok(())
+ }
+ }
+
+ /// Return the maximum number of CPU in CpuSet
+ pub const fn count() -> usize {
+ #[cfg(not(target_os = "freebsd"))]
+ let bytes = mem::size_of::<libc::cpu_set_t>();
+ #[cfg(target_os = "freebsd")]
+ let bytes = mem::size_of::<libc::cpuset_t>();
+
+ 8 * bytes
+ }
+ }
+
+ impl Default for CpuSet {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+
+ /// `sched_setaffinity` set a thread's CPU affinity mask
+ /// ([`sched_setaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html))
+ ///
+ /// `pid` is the thread ID to update.
+ /// If pid is zero, then the calling thread is updated.
+ ///
+ /// The `cpuset` argument specifies the set of CPUs on which the thread
+ /// will be eligible to run.
+ ///
+ /// # Example
+ ///
+ /// Binding the current thread to CPU 0 can be done as follows:
+ ///
+ /// ```rust,no_run
+ /// use nix::sched::{CpuSet, sched_setaffinity};
+ /// use nix::unistd::Pid;
+ ///
+ /// let mut cpu_set = CpuSet::new();
+ /// cpu_set.set(0).unwrap();
+ /// sched_setaffinity(Pid::from_raw(0), &cpu_set).unwrap();
+ /// ```
+ pub fn sched_setaffinity(pid: Pid, cpuset: &CpuSet) -> Result<()> {
+ let res = unsafe {
+ libc::sched_setaffinity(
+ pid.into(),
+ mem::size_of::<CpuSet>() as libc::size_t,
+ &cpuset.cpu_set,
+ )
+ };
+
+ Errno::result(res).map(drop)
+ }
+
+ /// `sched_getaffinity` get a thread's CPU affinity mask
+ /// ([`sched_getaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_getaffinity.2.html))
+ ///
+ /// `pid` is the thread ID to check.
+ /// If pid is zero, then the calling thread is checked.
+ ///
+ /// Returned `cpuset` is the set of CPUs on which the thread
+ /// is eligible to run.
+ ///
+ /// # Example
+ ///
+ /// Checking if the current thread can run on CPU 0 can be done as follows:
+ ///
+ /// ```rust,no_run
+ /// use nix::sched::sched_getaffinity;
+ /// use nix::unistd::Pid;
+ ///
+ /// let cpu_set = sched_getaffinity(Pid::from_raw(0)).unwrap();
+ /// if cpu_set.is_set(0).unwrap() {
+ /// println!("Current thread can run on CPU 0");
+ /// }
+ /// ```
+ pub fn sched_getaffinity(pid: Pid) -> Result<CpuSet> {
+ let mut cpuset = CpuSet::new();
+ let res = unsafe {
+ libc::sched_getaffinity(
+ pid.into(),
+ mem::size_of::<CpuSet>() as libc::size_t,
+ &mut cpuset.cpu_set,
+ )
+ };
+
+ Errno::result(res).and(Ok(cpuset))
+ }
+
+ /// Determines the CPU on which the calling thread is running.
+ pub fn sched_getcpu() -> Result<usize> {
+ let res = unsafe { libc::sched_getcpu() };
+
+ Errno::result(res).map(|int| int as usize)
+ }
+}
+
+/// Explicitly yield the processor to other threads.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html)
+pub fn sched_yield() -> Result<()> {
+ let res = unsafe { libc::sched_yield() };
+
+ Errno::result(res).map(drop)
+}
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..5471177e3e
--- /dev/null
+++ b/third_party/rust/nix/src/sys/aio.rs
@@ -0,0 +1,1245 @@
+// 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.AioRead.html#method.new),
+//! [`write`](struct.AioWrite.html#method.new),
+//! [`fsync`](struct.AioFsync.html#method.new),
+//! [`readv`](struct.AioReadv.html#method.new), and
+//! [`writev`](struct.AioWritev.html#method.new), operations, subject to
+//! platform support. 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`](trait.Aio.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.
+#[cfg(target_os = "freebsd")]
+use std::io::{IoSlice, IoSliceMut};
+use std::{
+ convert::TryFrom,
+ fmt::{self, Debug},
+ marker::{PhantomData, PhantomPinned},
+ mem,
+ os::unix::io::RawFd,
+ pin::Pin,
+ ptr, thread,
+};
+
+use libc::{c_void, off_t};
+use pin_utils::unsafe_pinned;
+
+use crate::{
+ errno::Errno,
+ sys::{signal::*, time::TimeSpec},
+ Result,
+};
+
+libc_enum! {
+ /// Mode for `AioCb::fsync`. Controls whether only data or both data and
+ /// metadata are synced.
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum AioFsyncMode {
+ /// do it like `fsync`
+ O_SYNC,
+ /// 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_DSYNC
+ }
+ impl TryFrom<i32>
+}
+
+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, Eq, Hash, 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,
+}
+
+/// Newtype that adds Send and Sync to libc::aiocb, which contains raw pointers
+#[repr(transparent)]
+struct LibcAiocb(libc::aiocb);
+
+unsafe impl Send for LibcAiocb {}
+unsafe impl Sync for LibcAiocb {}
+
+/// Base class for all AIO operations. Should only be used directly when
+/// checking for completion.
+// We could create some kind of AsPinnedMut trait, and implement it for all aio
+// ops, allowing the crate's users to get pinned references to `AioCb`. That
+// could save some code for things like polling methods. But IMHO it would
+// provide polymorphism at the wrong level. Instead, the best place for
+// polymorphism is at the level of `Futures`.
+#[repr(C)]
+struct AioCb {
+ aiocb: LibcAiocb,
+ /// Could this `AioCb` potentially have any in-kernel state?
+ // It would be really nice to perform the in-progress check entirely at
+ // compile time. But I can't figure out how, because:
+ // * Future::poll takes a `Pin<&mut self>` rather than `self`, and
+ // * Rust's lack of an equivalent of C++'s Guaranteed Copy Elision means
+ // that there's no way to write an AioCb constructor that neither boxes
+ // the object itself, nor moves it during return.
+ in_progress: bool,
+}
+
+impl AioCb {
+ pin_utils::unsafe_unpinned!(aiocb: LibcAiocb);
+
+ fn aio_return(mut self: Pin<&mut Self>) -> Result<usize> {
+ self.in_progress = false;
+ unsafe {
+ let p: *mut libc::aiocb = &mut self.aiocb.0;
+ Errno::result(libc::aio_return(p))
+ }
+ .map(|r| r as usize)
+ }
+
+ fn cancel(mut self: Pin<&mut Self>) -> Result<AioCancelStat> {
+ let r = unsafe {
+ libc::aio_cancel(self.aiocb.0.aio_fildes, &mut self.aiocb.0)
+ };
+ match r {
+ libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
+ libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
+ libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
+ -1 => Err(Errno::last()),
+ _ => panic!("unknown aio_cancel return value"),
+ }
+ }
+
+ fn common_init(fd: RawFd, prio: i32, sigev_notify: SigevNotify) -> Self {
+ // 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();
+ AioCb {
+ aiocb: LibcAiocb(a),
+ in_progress: false,
+ }
+ }
+
+ fn error(self: Pin<&mut Self>) -> Result<()> {
+ let r = unsafe { libc::aio_error(&self.aiocb().0) };
+ match r {
+ 0 => Ok(()),
+ num if num > 0 => Err(Errno::from_i32(num)),
+ -1 => Err(Errno::last()),
+ num => panic!("unknown aio_error return value {num:?}"),
+ }
+ }
+
+ fn in_progress(&self) -> bool {
+ self.in_progress
+ }
+
+ fn set_in_progress(mut self: Pin<&mut Self>) {
+ self.as_mut().in_progress = true;
+ }
+
+ /// Update the notification settings for an existing AIO operation that has
+ /// not yet been submitted.
+ // Takes a normal reference rather than a pinned one because this method is
+ // normally called before the object needs to be pinned, that is, before
+ // it's been submitted to the kernel.
+ fn set_sigev_notify(&mut self, sigev_notify: SigevNotify) {
+ assert!(
+ !self.in_progress,
+ "Can't change notification settings for an in-progress operation"
+ );
+ self.aiocb.0.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
+ }
+}
+
+impl Debug for AioCb {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.debug_struct("AioCb")
+ .field("aiocb", &self.aiocb.0)
+ .field("in_progress", &self.in_progress)
+ .finish()
+ }
+}
+
+impl Drop for AioCb {
+ /// 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"
+ );
+ }
+}
+
+/// Methods common to all AIO operations
+pub trait Aio {
+ /// The return type of [`Aio::aio_return`].
+ type Output;
+
+ /// Retrieve return status of an asynchronous operation.
+ ///
+ /// Should only be called once for each operation, after [`Aio::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](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_return.html)
+ fn aio_return(self: Pin<&mut Self>) -> Result<Self::Output>;
+
+ /// 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.
+ ///
+ /// ```
+ /// # 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;
+ /// let wbuf = b"CDEF";
+ /// let mut f = tempfile().unwrap();
+ /// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
+ /// 2, //offset
+ /// &wbuf[..],
+ /// 0, //priority
+ /// SigevNotify::SigevNone));
+ /// aiocb.as_mut().submit().unwrap();
+ /// let cs = aiocb.as_mut().cancel().unwrap();
+ /// if cs == AioCancelStat::AioNotCanceled {
+ /// while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) {
+ /// thread::sleep(time::Duration::from_millis(10));
+ /// }
+ /// }
+ /// // Must call `aio_return`, but ignore the result
+ /// let _ = aiocb.as_mut().aio_return();
+ /// ```
+ ///
+ /// # References
+ ///
+ /// [aio_cancel](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html)
+ fn cancel(self: Pin<&mut Self>) -> Result<AioCancelStat>;
+
+ /// 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.
+ ///
+ /// ```
+ /// # 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;
+ /// const WBUF: &[u8] = b"abcdef123456";
+ /// let mut f = tempfile().unwrap();
+ /// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
+ /// 2, //offset
+ /// WBUF,
+ /// 0, //priority
+ /// SigevNotify::SigevNone));
+ /// aiocb.as_mut().submit().unwrap();
+ /// while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) {
+ /// thread::sleep(time::Duration::from_millis(10));
+ /// }
+ /// assert_eq!(aiocb.as_mut().aio_return().unwrap(), WBUF.len());
+ /// ```
+ ///
+ /// # References
+ ///
+ /// [aio_error](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_error.html)
+ fn error(self: Pin<&mut Self>) -> Result<()>;
+
+ /// Returns the underlying file descriptor associated with the operation.
+ fn fd(&self) -> RawFd;
+
+ /// Does this operation currently have any in-kernel state?
+ ///
+ /// Dropping an operation that does have in-kernel state constitutes a
+ /// resource leak.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # 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;
+ /// let f = tempfile().unwrap();
+ /// let mut aiof = Box::pin(AioFsync::new(f.as_raw_fd(), AioFsyncMode::O_SYNC,
+ /// 0, SigevNone));
+ /// assert!(!aiof.as_mut().in_progress());
+ /// aiof.as_mut().submit().expect("aio_fsync failed early");
+ /// assert!(aiof.as_mut().in_progress());
+ /// while (aiof.as_mut().error() == Err(Errno::EINPROGRESS)) {
+ /// thread::sleep(time::Duration::from_millis(10));
+ /// }
+ /// aiof.as_mut().aio_return().expect("aio_fsync failed late");
+ /// assert!(!aiof.as_mut().in_progress());
+ /// ```
+ fn in_progress(&self) -> bool;
+
+ /// Returns the priority of the `AioCb`
+ fn priority(&self) -> i32;
+
+ /// Update the notification settings for an existing AIO operation that has
+ /// not yet been submitted.
+ fn set_sigev_notify(&mut self, sev: SigevNotify);
+
+ /// Returns the `SigEvent` that will be used for notification.
+ fn sigevent(&self) -> SigEvent;
+
+ /// Actually start the I/O operation.
+ ///
+ /// After calling this method and until [`Aio::aio_return`] returns `Ok`,
+ /// the structure may not be moved in memory.
+ fn submit(self: Pin<&mut Self>) -> Result<()>;
+}
+
+macro_rules! aio_methods {
+ () => {
+ fn cancel(self: Pin<&mut Self>) -> Result<AioCancelStat> {
+ self.aiocb().cancel()
+ }
+
+ fn error(self: Pin<&mut Self>) -> Result<()> {
+ self.aiocb().error()
+ }
+
+ fn fd(&self) -> RawFd {
+ self.aiocb.aiocb.0.aio_fildes
+ }
+
+ fn in_progress(&self) -> bool {
+ self.aiocb.in_progress()
+ }
+
+ fn priority(&self) -> i32 {
+ self.aiocb.aiocb.0.aio_reqprio
+ }
+
+ fn set_sigev_notify(&mut self, sev: SigevNotify) {
+ self.aiocb.set_sigev_notify(sev)
+ }
+
+ fn sigevent(&self) -> SigEvent {
+ SigEvent::from(&self.aiocb.aiocb.0.aio_sigevent)
+ }
+ };
+ ($func:ident) => {
+ aio_methods!();
+
+ fn aio_return(self: Pin<&mut Self>) -> Result<<Self as Aio>::Output> {
+ self.aiocb().aio_return()
+ }
+
+ fn submit(mut self: Pin<&mut Self>) -> Result<()> {
+ let p: *mut libc::aiocb = &mut self.as_mut().aiocb().aiocb.0;
+ Errno::result({ unsafe { libc::$func(p) } }).map(|_| {
+ self.aiocb().set_in_progress();
+ })
+ }
+ };
+}
+
+/// An asynchronous version of `fsync(2)`.
+///
+/// # References
+///
+/// [aio_fsync](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_fsync.html)
+/// # Examples
+///
+/// ```
+/// # 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;
+/// let f = tempfile().unwrap();
+/// let mut aiof = Box::pin(AioFsync::new(f.as_raw_fd(), AioFsyncMode::O_SYNC,
+/// 0, SigevNone));
+/// aiof.as_mut().submit().expect("aio_fsync failed early");
+/// while (aiof.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// aiof.as_mut().aio_return().expect("aio_fsync failed late");
+/// ```
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioFsync {
+ aiocb: AioCb,
+ _pin: PhantomPinned,
+}
+
+impl AioFsync {
+ unsafe_pinned!(aiocb: AioCb);
+
+ /// Returns the operation's fsync mode: data and metadata or data only?
+ pub fn mode(&self) -> AioFsyncMode {
+ AioFsyncMode::try_from(self.aiocb.aiocb.0.aio_lio_opcode).unwrap()
+ }
+
+ /// Create a new `AioFsync`.
+ ///
+ /// # Arguments
+ ///
+ /// * `fd`: File descriptor to sync.
+ /// * `mode`: Whether to sync file metadata too, or just data.
+ /// * `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.
+ pub fn new(
+ fd: RawFd,
+ mode: AioFsyncMode,
+ prio: i32,
+ sigev_notify: SigevNotify,
+ ) -> Self {
+ let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+ // To save some memory, store mode in an unused field of the AioCb.
+ // True it isn't very much memory, but downstream creates will likely
+ // create an enum containing this and other AioCb variants and pack
+ // those enums into data structures like Vec, so it adds up.
+ aiocb.aiocb.0.aio_lio_opcode = mode as libc::c_int;
+ AioFsync {
+ aiocb,
+ _pin: PhantomPinned,
+ }
+ }
+}
+
+impl Aio for AioFsync {
+ type Output = ();
+
+ aio_methods!();
+
+ fn aio_return(self: Pin<&mut Self>) -> Result<()> {
+ self.aiocb().aio_return().map(drop)
+ }
+
+ fn submit(mut self: Pin<&mut Self>) -> Result<()> {
+ let aiocb = &mut self.as_mut().aiocb().aiocb.0;
+ let mode = mem::replace(&mut aiocb.aio_lio_opcode, 0);
+ let p: *mut libc::aiocb = aiocb;
+ Errno::result(unsafe { libc::aio_fsync(mode, p) }).map(|_| {
+ self.aiocb().set_in_progress();
+ })
+ }
+}
+
+// AioFsync does not need AsMut, since it can't be used with lio_listio
+
+impl AsRef<libc::aiocb> for AioFsync {
+ fn as_ref(&self) -> &libc::aiocb {
+ &self.aiocb.aiocb.0
+ }
+}
+
+/// Asynchronously reads from a file descriptor into a buffer
+///
+/// # References
+///
+/// [aio_read](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_read.html)
+///
+/// # Examples
+///
+///
+/// ```
+/// # 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;
+/// 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 aior = Box::pin(
+/// AioRead::new(
+/// f.as_raw_fd(),
+/// 2, //offset
+/// &mut rbuf,
+/// 0, //priority
+/// SigevNotify::SigevNone
+/// )
+/// );
+/// aior.as_mut().submit().unwrap();
+/// while (aior.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// assert_eq!(aior.as_mut().aio_return().unwrap(), LEN);
+/// }
+/// assert_eq!(rbuf, b"cdef");
+/// ```
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioRead<'a> {
+ aiocb: AioCb,
+ _data: PhantomData<&'a [u8]>,
+ _pin: PhantomPinned,
+}
+
+impl<'a> AioRead<'a> {
+ unsafe_pinned!(aiocb: AioCb);
+
+ /// 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.aiocb.0.aio_nbytes
+ }
+
+ /// Create a new `AioRead`, placing the data in a mutable slice.
+ ///
+ /// # Arguments
+ ///
+ /// * `fd`: File descriptor to read from
+ /// * `offs`: File offset
+ /// * `buf`: A memory buffer. It must outlive the `AioRead`.
+ /// * `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.
+ pub fn new(
+ fd: RawFd,
+ offs: off_t,
+ buf: &'a mut [u8],
+ prio: i32,
+ sigev_notify: SigevNotify,
+ ) -> Self {
+ let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+ aiocb.aiocb.0.aio_nbytes = buf.len();
+ aiocb.aiocb.0.aio_buf = buf.as_mut_ptr() as *mut c_void;
+ aiocb.aiocb.0.aio_lio_opcode = libc::LIO_READ;
+ aiocb.aiocb.0.aio_offset = offs;
+ AioRead {
+ aiocb,
+ _data: PhantomData,
+ _pin: PhantomPinned,
+ }
+ }
+
+ /// Returns the file offset of the operation.
+ pub fn offset(&self) -> off_t {
+ self.aiocb.aiocb.0.aio_offset
+ }
+}
+
+impl<'a> Aio for AioRead<'a> {
+ type Output = usize;
+
+ aio_methods!(aio_read);
+}
+
+impl<'a> AsMut<libc::aiocb> for AioRead<'a> {
+ fn as_mut(&mut self) -> &mut libc::aiocb {
+ &mut self.aiocb.aiocb.0
+ }
+}
+
+impl<'a> AsRef<libc::aiocb> for AioRead<'a> {
+ fn as_ref(&self) -> &libc::aiocb {
+ &self.aiocb.aiocb.0
+ }
+}
+
+/// Asynchronously reads from a file descriptor into a scatter/gather list of buffers.
+///
+/// # References
+///
+/// [aio_readv](https://www.freebsd.org/cgi/man.cgi?query=aio_readv)
+///
+/// # Examples
+///
+///
+#[cfg_attr(fbsd14, doc = " ```")]
+#[cfg_attr(not(fbsd14), doc = " ```no_run")]
+/// # use nix::errno::Errno;
+/// # use nix::Error;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use std::{thread, time};
+/// # use std::io::{IoSliceMut, Write};
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// const INITIAL: &[u8] = b"abcdef123456";
+/// let mut rbuf0 = vec![0; 4];
+/// let mut rbuf1 = vec![0; 2];
+/// let expected_len = rbuf0.len() + rbuf1.len();
+/// let mut rbufs = [IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
+/// let mut f = tempfile().unwrap();
+/// f.write_all(INITIAL).unwrap();
+/// {
+/// let mut aior = Box::pin(
+/// AioReadv::new(
+/// f.as_raw_fd(),
+/// 2, //offset
+/// &mut rbufs,
+/// 0, //priority
+/// SigevNotify::SigevNone
+/// )
+/// );
+/// aior.as_mut().submit().unwrap();
+/// while (aior.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// assert_eq!(aior.as_mut().aio_return().unwrap(), expected_len);
+/// }
+/// assert_eq!(rbuf0, b"cdef");
+/// assert_eq!(rbuf1, b"12");
+/// ```
+#[cfg(target_os = "freebsd")]
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioReadv<'a> {
+ aiocb: AioCb,
+ _data: PhantomData<&'a [&'a [u8]]>,
+ _pin: PhantomPinned,
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AioReadv<'a> {
+ unsafe_pinned!(aiocb: AioCb);
+
+ /// Returns the number of buffers the operation will read into.
+ pub fn iovlen(&self) -> usize {
+ self.aiocb.aiocb.0.aio_nbytes
+ }
+
+ /// Create a new `AioReadv`, placing the data in a list of mutable slices.
+ ///
+ /// # Arguments
+ ///
+ /// * `fd`: File descriptor to read from
+ /// * `offs`: File offset
+ /// * `bufs`: A scatter/gather list of memory buffers. They must
+ /// outlive the `AioReadv`.
+ /// * `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.
+ pub fn new(
+ fd: RawFd,
+ offs: off_t,
+ bufs: &mut [IoSliceMut<'a>],
+ prio: i32,
+ sigev_notify: SigevNotify,
+ ) -> Self {
+ let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+ // In vectored mode, aio_nbytes stores the length of the iovec array,
+ // not the byte count.
+ aiocb.aiocb.0.aio_nbytes = bufs.len();
+ aiocb.aiocb.0.aio_buf = bufs.as_mut_ptr() as *mut c_void;
+ aiocb.aiocb.0.aio_lio_opcode = libc::LIO_READV;
+ aiocb.aiocb.0.aio_offset = offs;
+ AioReadv {
+ aiocb,
+ _data: PhantomData,
+ _pin: PhantomPinned,
+ }
+ }
+
+ /// Returns the file offset of the operation.
+ pub fn offset(&self) -> off_t {
+ self.aiocb.aiocb.0.aio_offset
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> Aio for AioReadv<'a> {
+ type Output = usize;
+
+ aio_methods!(aio_readv);
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AsMut<libc::aiocb> for AioReadv<'a> {
+ fn as_mut(&mut self) -> &mut libc::aiocb {
+ &mut self.aiocb.aiocb.0
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AsRef<libc::aiocb> for AioReadv<'a> {
+ fn as_ref(&self) -> &libc::aiocb {
+ &self.aiocb.aiocb.0
+ }
+}
+
+/// Asynchronously writes from a buffer to a file descriptor
+///
+/// # References
+///
+/// [aio_write](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_write.html)
+///
+/// # Examples
+///
+/// ```
+/// # 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;
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(
+/// AioWrite::new(
+/// f.as_raw_fd(),
+/// 2, //offset
+/// WBUF,
+/// 0, //priority
+/// SigevNotify::SigevNone
+/// )
+/// );
+/// aiow.as_mut().submit().unwrap();
+/// while (aiow.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+/// ```
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioWrite<'a> {
+ aiocb: AioCb,
+ _data: PhantomData<&'a [u8]>,
+ _pin: PhantomPinned,
+}
+
+impl<'a> AioWrite<'a> {
+ unsafe_pinned!(aiocb: AioCb);
+
+ /// 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.aiocb.0.aio_nbytes
+ }
+
+ /// Construct a new `AioWrite`.
+ ///
+ /// # Arguments
+ ///
+ /// * `fd`: File descriptor to write to
+ /// * `offs`: File offset
+ /// * `buf`: A memory buffer. It must outlive the `AioWrite`.
+ /// * `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.
+ pub fn new(
+ fd: RawFd,
+ offs: off_t,
+ buf: &'a [u8],
+ prio: i32,
+ sigev_notify: SigevNotify,
+ ) -> Self {
+ let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+ aiocb.aiocb.0.aio_nbytes = buf.len();
+ // casting an immutable buffer to a mutable pointer looks unsafe,
+ // but technically its only unsafe to dereference it, not to create
+ // it. Type Safety guarantees that we'll never pass aiocb to
+ // aio_read or aio_readv.
+ aiocb.aiocb.0.aio_buf = buf.as_ptr() as *mut c_void;
+ aiocb.aiocb.0.aio_lio_opcode = libc::LIO_WRITE;
+ aiocb.aiocb.0.aio_offset = offs;
+ AioWrite {
+ aiocb,
+ _data: PhantomData,
+ _pin: PhantomPinned,
+ }
+ }
+
+ /// Returns the file offset of the operation.
+ pub fn offset(&self) -> off_t {
+ self.aiocb.aiocb.0.aio_offset
+ }
+}
+
+impl<'a> Aio for AioWrite<'a> {
+ type Output = usize;
+
+ aio_methods!(aio_write);
+}
+
+impl<'a> AsMut<libc::aiocb> for AioWrite<'a> {
+ fn as_mut(&mut self) -> &mut libc::aiocb {
+ &mut self.aiocb.aiocb.0
+ }
+}
+
+impl<'a> AsRef<libc::aiocb> for AioWrite<'a> {
+ fn as_ref(&self) -> &libc::aiocb {
+ &self.aiocb.aiocb.0
+ }
+}
+
+/// Asynchronously writes from a scatter/gather list of buffers to a file descriptor.
+///
+/// # References
+///
+/// [aio_writev](https://www.freebsd.org/cgi/man.cgi?query=aio_writev)
+///
+/// # Examples
+///
+#[cfg_attr(fbsd14, doc = " ```")]
+#[cfg_attr(not(fbsd14), doc = " ```no_run")]
+/// # use nix::errno::Errno;
+/// # use nix::Error;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use std::{thread, time};
+/// # use std::io::IoSlice;
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// const wbuf0: &[u8] = b"abcdef";
+/// const wbuf1: &[u8] = b"123456";
+/// let len = wbuf0.len() + wbuf1.len();
+/// let wbufs = [IoSlice::new(wbuf0), IoSlice::new(wbuf1)];
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(
+/// AioWritev::new(
+/// f.as_raw_fd(),
+/// 2, //offset
+/// &wbufs,
+/// 0, //priority
+/// SigevNotify::SigevNone
+/// )
+/// );
+/// aiow.as_mut().submit().unwrap();
+/// while (aiow.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), len);
+/// ```
+#[cfg(target_os = "freebsd")]
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioWritev<'a> {
+ aiocb: AioCb,
+ _data: PhantomData<&'a [&'a [u8]]>,
+ _pin: PhantomPinned,
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AioWritev<'a> {
+ unsafe_pinned!(aiocb: AioCb);
+
+ /// Returns the number of buffers the operation will read into.
+ pub fn iovlen(&self) -> usize {
+ self.aiocb.aiocb.0.aio_nbytes
+ }
+
+ /// Construct a new `AioWritev`.
+ ///
+ /// # Arguments
+ ///
+ /// * `fd`: File descriptor to write to
+ /// * `offs`: File offset
+ /// * `bufs`: A scatter/gather list of memory buffers. They must
+ /// outlive the `AioWritev`.
+ /// * `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.
+ pub fn new(
+ fd: RawFd,
+ offs: off_t,
+ bufs: &[IoSlice<'a>],
+ prio: i32,
+ sigev_notify: SigevNotify,
+ ) -> Self {
+ let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+ // In vectored mode, aio_nbytes stores the length of the iovec array,
+ // not the byte count.
+ aiocb.aiocb.0.aio_nbytes = bufs.len();
+ // casting an immutable buffer to a mutable pointer looks unsafe,
+ // but technically its only unsafe to dereference it, not to create
+ // it. Type Safety guarantees that we'll never pass aiocb to
+ // aio_read or aio_readv.
+ aiocb.aiocb.0.aio_buf = bufs.as_ptr() as *mut c_void;
+ aiocb.aiocb.0.aio_lio_opcode = libc::LIO_WRITEV;
+ aiocb.aiocb.0.aio_offset = offs;
+ AioWritev {
+ aiocb,
+ _data: PhantomData,
+ _pin: PhantomPinned,
+ }
+ }
+
+ /// Returns the file offset of the operation.
+ pub fn offset(&self) -> off_t {
+ self.aiocb.aiocb.0.aio_offset
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> Aio for AioWritev<'a> {
+ type Output = usize;
+
+ aio_methods!(aio_writev);
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AsMut<libc::aiocb> for AioWritev<'a> {
+ fn as_mut(&mut self) -> &mut libc::aiocb {
+ &mut self.aiocb.aiocb.0
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AsRef<libc::aiocb> for AioWritev<'a> {
+ fn as_ref(&self) -> &libc::aiocb {
+ &self.aiocb.aiocb.0
+ }
+}
+
+/// Cancels outstanding AIO requests for a given file descriptor.
+///
+/// # Examples
+///
+/// Issue an aio operation, then cancel all outstanding operations on that file
+/// descriptor.
+///
+/// ```
+/// # 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;
+/// let wbuf = b"CDEF";
+/// let mut f = tempfile().unwrap();
+/// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
+/// 2, //offset
+/// &wbuf[..],
+/// 0, //priority
+/// SigevNotify::SigevNone));
+/// aiocb.as_mut().submit().unwrap();
+/// let cs = aio_cancel_all(f.as_raw_fd()).unwrap();
+/// if cs == AioCancelStat::AioNotCanceled {
+/// while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// }
+/// // Must call `aio_return`, but ignore the result
+/// let _ = aiocb.as_mut().aio_return();
+/// ```
+///
+/// # References
+///
+/// [`aio_cancel`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html)
+pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
+ match unsafe { libc::aio_cancel(fd, ptr::null_mut()) } {
+ libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
+ libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
+ libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
+ -1 => Err(Errno::last()),
+ _ => panic!("unknown aio_cancel return value"),
+ }
+}
+
+/// Suspends the calling process until at least one of the specified operations
+/// have 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.
+///
+/// ```
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
+/// 2, //offset
+/// WBUF,
+/// 0, //priority
+/// SigevNotify::SigevNone));
+/// aiocb.as_mut().submit().unwrap();
+/// aio_suspend(&[&*aiocb], None).expect("aio_suspend failed");
+/// assert_eq!(aiocb.as_mut().aio_return().unwrap() as usize, WBUF.len());
+/// ```
+/// # References
+///
+/// [`aio_suspend`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_suspend.html)
+pub fn aio_suspend(
+ list: &[&dyn AsRef<libc::aiocb>],
+ timeout: Option<TimeSpec>,
+) -> Result<()> {
+ // Note that this allocation could be eliminated by making the argument
+ // generic, and accepting arguments like &[AioWrite]. But that would
+ // prevent using aio_suspend to wait on a heterogeneous list of mixed
+ // operations.
+ let v = list.iter()
+ .map(|x| x.as_ref() as *const libc::aiocb)
+ .collect::<Vec<*const libc::aiocb>>();
+ let p = v.as_ptr();
+ let timep = match timeout {
+ None => ptr::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)
+}
+
+/// 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, and writes may be freely
+/// mixed.
+///
+/// # Examples
+///
+/// Use `lio_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 `error` to poll.
+/// This mode is useful for otherwise-synchronous programs that want to execute
+/// a handful of I/O operations in parallel.
+/// ```
+/// # use std::os::unix::io::AsRawFd;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use tempfile::tempfile;
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(AioWrite::new(
+/// f.as_raw_fd(),
+/// 2, // offset
+/// WBUF,
+/// 0, // priority
+/// SigevNotify::SigevNone
+/// ));
+/// lio_listio(LioMode::LIO_WAIT, &mut[aiow.as_mut()], SigevNotify::SigevNone)
+/// .unwrap();
+/// // At this point, we are guaranteed that aiow is complete.
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+/// ```
+///
+/// Use `lio_listio` to submit multiple asynchronous operations with a single
+/// syscall, but receive notification individually. This is an efficient
+/// technique for reducing overall context-switch overhead, especially when
+/// combined with kqueue.
+/// ```
+/// # use std::os::unix::io::AsRawFd;
+/// # use std::thread;
+/// # use std::time;
+/// # use nix::errno::Errno;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use tempfile::tempfile;
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(AioWrite::new(
+/// f.as_raw_fd(),
+/// 2, // offset
+/// WBUF,
+/// 0, // priority
+/// SigevNotify::SigevNone
+/// ));
+/// lio_listio(LioMode::LIO_NOWAIT, &mut[aiow.as_mut()], SigevNotify::SigevNone)
+/// .unwrap();
+/// // We must wait for the completion of each individual operation
+/// while (aiow.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+/// ```
+///
+/// Use `lio_listio` to submit multiple operations, and receive notification
+/// only when all of them are complete. This can be useful when there is some
+/// logical relationship between the operations. But beware! Errors or system
+/// resource limitations may cause `lio_listio` to return `EIO`, `EAGAIN`, or
+/// `EINTR`, in which case some but not all operations may have been submitted.
+/// In that case, you must check the status of each individual operation, and
+/// possibly resubmit some.
+/// ```
+/// # use libc::c_int;
+/// # use std::os::unix::io::AsRawFd;
+/// # use std::sync::atomic::{AtomicBool, Ordering};
+/// # use std::thread;
+/// # use std::time;
+/// # use nix::errno::Errno;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::*;
+/// # use tempfile::tempfile;
+/// pub static SIGNALED: AtomicBool = AtomicBool::new(false);
+///
+/// extern fn sigfunc(_: c_int) {
+/// SIGNALED.store(true, Ordering::Relaxed);
+/// }
+/// let sa = SigAction::new(SigHandler::Handler(sigfunc),
+/// SaFlags::SA_RESETHAND,
+/// SigSet::empty());
+/// SIGNALED.store(false, Ordering::Relaxed);
+/// unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
+///
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(AioWrite::new(
+/// f.as_raw_fd(),
+/// 2, // offset
+/// WBUF,
+/// 0, // priority
+/// SigevNotify::SigevNone
+/// ));
+/// let sev = SigevNotify::SigevSignal { signal: Signal::SIGUSR2, si_value: 0 };
+/// lio_listio(LioMode::LIO_NOWAIT, &mut[aiow.as_mut()], sev).unwrap();
+/// while !SIGNALED.load(Ordering::Relaxed) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// // At this point, since `lio_listio` returned success and delivered its
+/// // notification, we know that all operations are complete.
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+/// ```
+#[deprecated(since = "0.27.0", note = "https://github.com/nix-rust/nix/issues/2017")]
+pub fn lio_listio(
+ mode: LioMode,
+ list: &mut [Pin<&mut dyn AsMut<libc::aiocb>>],
+ sigev_notify: SigevNotify,
+) -> Result<()> {
+ let p = list as *mut [Pin<&mut dyn AsMut<libc::aiocb>>]
+ as *mut [*mut libc::aiocb] as *mut *mut libc::aiocb;
+ let sigev = SigEvent::new(sigev_notify);
+ let sigevp = &mut sigev.sigevent() as *mut libc::sigevent;
+ Errno::result(unsafe {
+ libc::lio_listio(mode as i32, p, list.len() as i32, sigevp)
+ })
+ .map(drop)
+}
+
+#[cfg(test)]
+mod t {
+ use super::*;
+
+ /// aio_suspend relies on casting Rust Aio* struct pointers to libc::aiocb
+ /// pointers. This test ensures that such casts are valid.
+ #[test]
+ fn casting() {
+ let sev = SigevNotify::SigevNone;
+ let aiof = AioFsync::new(666, AioFsyncMode::O_SYNC, 0, sev);
+ assert_eq!(
+ aiof.as_ref() as *const libc::aiocb,
+ &aiof as *const AioFsync as *const libc::aiocb
+ );
+
+ let mut rbuf = [];
+ let aior = AioRead::new(666, 0, &mut rbuf, 0, sev);
+ assert_eq!(
+ aior.as_ref() as *const libc::aiocb,
+ &aior as *const AioRead as *const libc::aiocb
+ );
+
+ let wbuf = [];
+ let aiow = AioWrite::new(666, 0, &wbuf, 0, sev);
+ assert_eq!(
+ aiow.as_ref() as *const libc::aiocb,
+ &aiow as *const AioWrite as *const libc::aiocb
+ );
+ }
+
+ #[cfg(target_os = "freebsd")]
+ #[test]
+ fn casting_vectored() {
+ let sev = SigevNotify::SigevNone;
+
+ let mut rbuf = [];
+ let mut rbufs = [IoSliceMut::new(&mut rbuf)];
+ let aiorv = AioReadv::new(666, 0, &mut rbufs[..], 0, sev);
+ assert_eq!(
+ aiorv.as_ref() as *const libc::aiocb,
+ &aiorv as *const AioReadv as *const libc::aiocb
+ );
+
+ let wbuf = [];
+ let wbufs = [IoSlice::new(&wbuf)];
+ let aiowv = AioWritev::new(666, 0, &wbufs, 0, sev);
+ assert_eq!(
+ aiowv.as_ref() as *const libc::aiocb,
+ &aiowv as *const AioWritev as *const libc::aiocb
+ );
+ }
+}
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..36f9c17d0e
--- /dev/null
+++ b/third_party/rust/nix/src/sys/epoll.rs
@@ -0,0 +1,250 @@
+use crate::errno::Errno;
+use crate::Result;
+use libc::{self, c_int};
+use std::mem;
+use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd};
+
+libc_bitflags!(
+ pub struct EpollFlags: c_int {
+ EPOLLIN;
+ EPOLLPRI;
+ EPOLLOUT;
+ EPOLLRDNORM;
+ EPOLLRDBAND;
+ EPOLLWRNORM;
+ EPOLLWRBAND;
+ EPOLLMSG;
+ EPOLLERR;
+ EPOLLHUP;
+ EPOLLRDHUP;
+ EPOLLEXCLUSIVE;
+ #[cfg(not(target_arch = "mips"))]
+ EPOLLWAKEUP;
+ EPOLLONESHOT;
+ EPOLLET;
+ }
+);
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(i32)]
+#[non_exhaustive]
+pub enum EpollOp {
+ EpollCtlAdd = libc::EPOLL_CTL_ADD,
+ EpollCtlDel = libc::EPOLL_CTL_DEL,
+ EpollCtlMod = libc::EPOLL_CTL_MOD,
+}
+
+libc_bitflags! {
+ pub struct EpollCreateFlags: c_int {
+ EPOLL_CLOEXEC;
+ }
+}
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(transparent)]
+pub struct EpollEvent {
+ event: libc::epoll_event,
+}
+
+impl EpollEvent {
+ pub fn new(events: EpollFlags, data: u64) -> Self {
+ EpollEvent {
+ event: libc::epoll_event {
+ events: events.bits() as u32,
+ u64: data,
+ },
+ }
+ }
+
+ pub fn empty() -> Self {
+ unsafe { mem::zeroed::<EpollEvent>() }
+ }
+
+ pub fn events(&self) -> EpollFlags {
+ EpollFlags::from_bits(self.event.events as c_int).unwrap()
+ }
+
+ pub fn data(&self) -> u64 {
+ self.event.u64
+ }
+}
+
+/// A safe wrapper around [`epoll`](https://man7.org/linux/man-pages/man7/epoll.7.html).
+/// ```
+/// # use nix::sys::{epoll::{Epoll, EpollEvent, EpollFlags, EpollCreateFlags}, eventfd::{eventfd, EfdFlags}};
+/// # use nix::unistd::write;
+/// # use std::os::unix::io::{OwnedFd, FromRawFd, AsRawFd, AsFd};
+/// # use std::time::{Instant, Duration};
+/// # fn main() -> nix::Result<()> {
+/// const DATA: u64 = 17;
+/// const MILLIS: u64 = 100;
+///
+/// // Create epoll
+/// let epoll = Epoll::new(EpollCreateFlags::empty())?;
+///
+/// // Create eventfd & Add event
+/// let eventfd = eventfd(0, EfdFlags::empty())?;
+/// epoll.add(&eventfd, EpollEvent::new(EpollFlags::EPOLLIN,DATA))?;
+///
+/// // Arm eventfd & Time wait
+/// write(eventfd.as_raw_fd(), &1u64.to_ne_bytes())?;
+/// let now = Instant::now();
+///
+/// // Wait on event
+/// let mut events = [EpollEvent::empty()];
+/// epoll.wait(&mut events, MILLIS as isize)?;
+///
+/// // Assert data correct & timeout didn't occur
+/// assert_eq!(events[0].data(), DATA);
+/// assert!(now.elapsed() < Duration::from_millis(MILLIS));
+/// # Ok(())
+/// # }
+/// ```
+#[derive(Debug)]
+pub struct Epoll(pub OwnedFd);
+impl Epoll {
+ /// Creates a new epoll instance and returns a file descriptor referring to that instance.
+ ///
+ /// [`epoll_create1`](https://man7.org/linux/man-pages/man2/epoll_create1.2.html).
+ pub fn new(flags: EpollCreateFlags) -> Result<Self> {
+ let res = unsafe { libc::epoll_create1(flags.bits()) };
+ let fd = Errno::result(res)?;
+ let owned_fd = unsafe { OwnedFd::from_raw_fd(fd) };
+ Ok(Self(owned_fd))
+ }
+ /// Add an entry to the interest list of the epoll file descriptor for
+ /// specified in events.
+ ///
+ /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_ADD`.
+ pub fn add<Fd: AsFd>(&self, fd: Fd, mut event: EpollEvent) -> Result<()> {
+ self.epoll_ctl(EpollOp::EpollCtlAdd, fd, &mut event)
+ }
+ /// Remove (deregister) the target file descriptor `fd` from the interest list.
+ ///
+ /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_DEL` .
+ pub fn delete<Fd: AsFd>(&self, fd: Fd) -> Result<()> {
+ self.epoll_ctl(EpollOp::EpollCtlDel, fd, None)
+ }
+ /// Change the settings associated with `fd` in the interest list to the new settings specified
+ /// in `event`.
+ ///
+ /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html) with `EPOLL_CTL_MOD`.
+ pub fn modify<Fd: AsFd>(
+ &self,
+ fd: Fd,
+ event: &mut EpollEvent,
+ ) -> Result<()> {
+ self.epoll_ctl(EpollOp::EpollCtlMod, fd, event)
+ }
+ /// Waits for I/O events, blocking the calling thread if no events are currently available.
+ /// (This can be thought of as fetching items from the ready list of the epoll instance.)
+ ///
+ /// [`epoll_wait`](https://man7.org/linux/man-pages/man2/epoll_wait.2.html)
+ pub fn wait(
+ &self,
+ events: &mut [EpollEvent],
+ timeout: isize,
+ ) -> Result<usize> {
+ let res = unsafe {
+ libc::epoll_wait(
+ self.0.as_raw_fd(),
+ events.as_mut_ptr() as *mut libc::epoll_event,
+ events.len() as c_int,
+ timeout as c_int,
+ )
+ };
+
+ Errno::result(res).map(|r| r as usize)
+ }
+ /// This system call is used to add, modify, or remove entries in the interest list of the epoll
+ /// instance referred to by `self`. It requests that the operation `op` be performed for the
+ /// target file descriptor, `fd`.
+ ///
+ /// When possible prefer [`Epoll::add`], [`Epoll::delete`] and [`Epoll::modify`].
+ ///
+ /// [`epoll_ctl`](https://man7.org/linux/man-pages/man2/epoll_ctl.2.html)
+ fn epoll_ctl<'a, Fd: AsFd, T>(
+ &self,
+ op: EpollOp,
+ fd: Fd,
+ event: T,
+ ) -> Result<()>
+ where
+ T: Into<Option<&'a mut EpollEvent>>,
+ {
+ let event: Option<&mut EpollEvent> = event.into();
+ let ptr = event
+ .map(|x| &mut x.event as *mut libc::epoll_event)
+ .unwrap_or(std::ptr::null_mut());
+ unsafe {
+ Errno::result(libc::epoll_ctl(
+ self.0.as_raw_fd(),
+ op as c_int,
+ fd.as_fd().as_raw_fd(),
+ ptr,
+ ))
+ .map(drop)
+ }
+ }
+}
+
+#[deprecated(since = "0.27.0", note = "Use Epoll::new() instead")]
+#[inline]
+pub fn epoll_create() -> Result<RawFd> {
+ let res = unsafe { libc::epoll_create(1024) };
+
+ Errno::result(res)
+}
+
+#[deprecated(since = "0.27.0", note = "Use Epoll::new() instead")]
+#[inline]
+pub fn epoll_create1(flags: EpollCreateFlags) -> Result<RawFd> {
+ let res = unsafe { libc::epoll_create1(flags.bits()) };
+
+ Errno::result(res)
+}
+
+#[deprecated(since = "0.27.0", note = "Use Epoll::epoll_ctl() instead")]
+#[inline]
+pub fn epoll_ctl<'a, T>(
+ epfd: RawFd,
+ op: EpollOp,
+ fd: RawFd,
+ event: T,
+) -> Result<()>
+where
+ T: Into<Option<&'a mut EpollEvent>>,
+{
+ let mut event: Option<&mut EpollEvent> = event.into();
+ if event.is_none() && op != EpollOp::EpollCtlDel {
+ Err(Errno::EINVAL)
+ } else {
+ let res = unsafe {
+ if let Some(ref mut event) = event {
+ libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event)
+ } else {
+ libc::epoll_ctl(epfd, op as c_int, fd, std::ptr::null_mut())
+ }
+ };
+ Errno::result(res).map(drop)
+ }
+}
+
+#[deprecated(since = "0.27.0", note = "Use Epoll::wait() instead")]
+#[inline]
+pub fn epoll_wait(
+ epfd: RawFd,
+ events: &mut [EpollEvent],
+ timeout_ms: isize,
+) -> Result<usize> {
+ let res = unsafe {
+ libc::epoll_wait(
+ epfd,
+ events.as_mut_ptr() 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..ec7f7e277a
--- /dev/null
+++ b/third_party/rust/nix/src/sys/event.rs
@@ -0,0 +1,525 @@
+//! Kernel event notification mechanism
+//!
+//! # See Also
+//! [kqueue(2)](https://www.freebsd.org/cgi/man.cgi?query=kqueue)
+
+use crate::{Errno, Result};
+#[cfg(not(target_os = "netbsd"))]
+use libc::{c_int, c_long, intptr_t, time_t, timespec, uintptr_t};
+#[cfg(target_os = "netbsd")]
+use libc::{c_long, intptr_t, size_t, time_t, timespec, uintptr_t};
+use std::convert::TryInto;
+use std::mem;
+use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd};
+use std::ptr;
+
+/// A kernel event queue. Used to notify a process of various asynchronous
+/// events.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct KEvent {
+ kevent: libc::kevent,
+}
+
+/// A kernel event queue.
+///
+/// Used by the kernel to notify the process of various types of asynchronous
+/// events.
+#[repr(transparent)]
+#[derive(Debug)]
+pub struct Kqueue(OwnedFd);
+
+impl Kqueue {
+ /// Create a new kernel event queue.
+ pub fn new() -> Result<Self> {
+ let res = unsafe { libc::kqueue() };
+
+ Errno::result(res).map(|fd| unsafe { Self(OwnedFd::from_raw_fd(fd)) })
+ }
+
+ /// Register new events with the kqueue, and return any pending events to
+ /// the user.
+ ///
+ /// This method will block until either the timeout expires, or a registered
+ /// event triggers a notification.
+ ///
+ /// # Arguments
+ /// - `changelist` - Any new kevents to register for notifications.
+ /// - `eventlist` - Storage space for the kernel to return notifications.
+ /// - `timeout` - An optional timeout.
+ ///
+ /// # Returns
+ /// Returns the number of events placed in the `eventlist`. If an error
+ /// occurs while processing an element of the `changelist` and there is
+ /// enough room in the `eventlist`, then the event will be placed in the
+ /// `eventlist` with `EV_ERROR` set in `flags` and the system error in
+ /// `data`.
+ pub fn kevent(
+ &self,
+ changelist: &[KEvent],
+ eventlist: &mut [KEvent],
+ timeout_opt: Option<timespec>,
+ ) -> Result<usize> {
+ let res = unsafe {
+ libc::kevent(
+ self.0.as_raw_fd(),
+ changelist.as_ptr() 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)
+ }
+}
+
+#[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(target_os = "netbsd")]
+type type_of_udata = intptr_t;
+
+#[cfg(target_os = "netbsd")]
+type type_of_event_filter = u32;
+#[cfg(not(target_os = "netbsd"))]
+type type_of_event_filter = i16;
+libc_enum! {
+ #[cfg_attr(target_os = "netbsd", repr(u32))]
+ #[cfg_attr(not(target_os = "netbsd"), repr(i16))]
+ #[non_exhaustive]
+ /// Kqueue filter types. These are all the different types of event that a
+ /// kqueue can notify for.
+ pub enum EventFilter {
+ /// Notifies on the completion of a POSIX AIO operation.
+ EVFILT_AIO,
+ #[cfg(target_os = "freebsd")]
+ /// Returns whenever there is no remaining data in the write buffer
+ EVFILT_EMPTY,
+ #[cfg(target_os = "dragonfly")]
+ /// Takes a descriptor as the identifier, and returns whenever one of
+ /// the specified exceptional conditions has occurred on the descriptor.
+ EVFILT_EXCEPT,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos"))]
+ /// Establishes a file system monitor.
+ EVFILT_FS,
+ #[cfg(target_os = "freebsd")]
+ /// Notify for completion of a list of POSIX AIO operations.
+ /// # See Also
+ /// [lio_listio(2)](https://www.freebsd.org/cgi/man.cgi?query=lio_listio)
+ EVFILT_LIO,
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ /// Mach portsets
+ EVFILT_MACHPORT,
+ /// Notifies when a process performs one or more of the requested
+ /// events.
+ EVFILT_PROC,
+ /// Returns events associated with the process referenced by a given
+ /// process descriptor, created by `pdfork()`. The events to monitor are:
+ ///
+ /// - NOTE_EXIT: the process has exited. The exit status will be stored in data.
+ #[cfg(target_os = "freebsd")]
+ EVFILT_PROCDESC,
+ /// Takes a file descriptor as the identifier, and notifies whenever
+ /// there is data available to read.
+ EVFILT_READ,
+ #[cfg(target_os = "freebsd")]
+ #[doc(hidden)]
+ #[deprecated(since = "0.27.0", note = "Never fully implemented by the OS")]
+ EVFILT_SENDFILE,
+ /// Takes a signal number to monitor as the identifier and notifies when
+ /// the given signal is delivered to the process.
+ EVFILT_SIGNAL,
+ /// Establishes a timer and notifies when the timer expires.
+ EVFILT_TIMER,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos"))]
+ /// Notifies only when explicitly requested by the user.
+ EVFILT_USER,
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ /// Virtual memory events
+ EVFILT_VM,
+ /// Notifies when a requested event happens on a specified file.
+ EVFILT_VNODE,
+ /// Takes a file descriptor as the identifier, and notifies whenever
+ /// it is possible to write to the file without blocking.
+ EVFILT_WRITE,
+ }
+ impl TryFrom<type_of_event_filter>
+}
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "openbsd"
+))]
+#[doc(hidden)]
+pub type type_of_event_flag = u16;
+#[cfg(target_os = "netbsd")]
+#[doc(hidden)]
+pub type type_of_event_flag = u32;
+libc_bitflags! {
+ /// Event flags. See the man page for details.
+ // There's no useful documentation we can write for the individual flags
+ // that wouldn't simply be repeating the man page.
+ pub struct EventFlag: type_of_event_flag {
+ #[allow(missing_docs)]
+ EV_ADD;
+ #[allow(missing_docs)]
+ EV_CLEAR;
+ #[allow(missing_docs)]
+ EV_DELETE;
+ #[allow(missing_docs)]
+ EV_DISABLE;
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
+ target_os = "ios", target_os = "macos",
+ target_os = "netbsd", target_os = "openbsd"))]
+ #[allow(missing_docs)]
+ EV_DISPATCH;
+ #[cfg(target_os = "freebsd")]
+ #[allow(missing_docs)]
+ EV_DROP;
+ #[allow(missing_docs)]
+ EV_ENABLE;
+ #[allow(missing_docs)]
+ EV_EOF;
+ #[allow(missing_docs)]
+ EV_ERROR;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
+ EV_FLAG0;
+ #[allow(missing_docs)]
+ EV_FLAG1;
+ #[cfg(target_os = "dragonfly")]
+ #[allow(missing_docs)]
+ EV_NODATA;
+ #[allow(missing_docs)]
+ EV_ONESHOT;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
+ EV_OOBAND;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
+ EV_POLL;
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
+ target_os = "ios", target_os = "macos",
+ target_os = "netbsd", target_os = "openbsd"))]
+ #[allow(missing_docs)]
+ EV_RECEIPT;
+ }
+}
+
+libc_bitflags!(
+ /// Filter-specific flags. See the man page for details.
+ // There's no useful documentation we can write for the individual flags
+ // that wouldn't simply be repeating the man page.
+ #[allow(missing_docs)]
+ pub struct FilterFlag: u32 {
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
+ NOTE_ABSOLUTE;
+ #[allow(missing_docs)]
+ NOTE_ATTRIB;
+ #[allow(missing_docs)]
+ NOTE_CHILD;
+ #[allow(missing_docs)]
+ NOTE_DELETE;
+ #[cfg(target_os = "openbsd")]
+ #[allow(missing_docs)]
+ NOTE_EOF;
+ #[allow(missing_docs)]
+ NOTE_EXEC;
+ #[allow(missing_docs)]
+ NOTE_EXIT;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
+ NOTE_EXITSTATUS;
+ #[allow(missing_docs)]
+ NOTE_EXTEND;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ #[allow(missing_docs)]
+ NOTE_FFAND;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ #[allow(missing_docs)]
+ NOTE_FFCOPY;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ #[allow(missing_docs)]
+ NOTE_FFCTRLMASK;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ #[allow(missing_docs)]
+ NOTE_FFLAGSMASK;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ #[allow(missing_docs)]
+ NOTE_FFNOP;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ #[allow(missing_docs)]
+ NOTE_FFOR;
+ #[allow(missing_docs)]
+ NOTE_FORK;
+ #[allow(missing_docs)]
+ NOTE_LINK;
+ #[allow(missing_docs)]
+ NOTE_LOWAT;
+ #[cfg(target_os = "freebsd")]
+ #[allow(missing_docs)]
+ NOTE_MSECONDS;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
+ NOTE_NONE;
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
+ #[allow(missing_docs)]
+ NOTE_NSECONDS;
+ #[cfg(target_os = "dragonfly")]
+ #[allow(missing_docs)]
+ NOTE_OOB;
+ #[allow(missing_docs)]
+ NOTE_PCTRLMASK;
+ #[allow(missing_docs)]
+ NOTE_PDATAMASK;
+ #[allow(missing_docs)]
+ NOTE_RENAME;
+ #[allow(missing_docs)]
+ NOTE_REVOKE;
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
+ #[allow(missing_docs)]
+ NOTE_SECONDS;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
+ NOTE_SIGNAL;
+ #[allow(missing_docs)]
+ NOTE_TRACK;
+ #[allow(missing_docs)]
+ NOTE_TRACKERR;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ #[allow(missing_docs)]
+ NOTE_TRIGGER;
+ #[cfg(target_os = "openbsd")]
+ #[allow(missing_docs)]
+ NOTE_TRUNCATE;
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
+ #[allow(missing_docs)]
+ NOTE_USECONDS;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
+ NOTE_VM_ERROR;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
+ NOTE_VM_PRESSURE;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
+ NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[allow(missing_docs)]
+ NOTE_VM_PRESSURE_TERMINATE;
+ #[allow(missing_docs)]
+ NOTE_WRITE;
+ }
+);
+
+#[allow(missing_docs)]
+#[deprecated(since = "0.27.0", note = "Use KEvent::new instead")]
+pub fn kqueue() -> Result<Kqueue> {
+ Kqueue::new()
+}
+
+// KEvent can't derive Send because on some operating systems, udata is defined
+// as a void*. However, KEvent's public API always treats udata as an intptr_t,
+// which is safe to Send.
+unsafe impl Send for KEvent {}
+
+impl KEvent {
+ #[allow(clippy::needless_update)] // Not needless on all platforms.
+ /// Construct a new `KEvent` suitable for submission to the kernel via the
+ /// `changelist` argument of [`Kqueue::kevent`].
+ pub fn new(
+ ident: uintptr_t,
+ filter: EventFilter,
+ flags: EventFlag,
+ fflags: FilterFlag,
+ data: intptr_t,
+ udata: intptr_t,
+ ) -> KEvent {
+ KEvent {
+ kevent: libc::kevent {
+ ident,
+ filter: filter as type_of_event_filter,
+ flags: flags.bits(),
+ fflags: fflags.bits(),
+ // data can be either i64 or intptr_t, depending on platform
+ data: data as _,
+ udata: udata as type_of_udata,
+ ..unsafe { mem::zeroed() }
+ },
+ }
+ }
+
+ /// Value used to identify this event. The exact interpretation is
+ /// determined by the attached filter, but often is a raw file descriptor.
+ pub fn ident(&self) -> uintptr_t {
+ self.kevent.ident
+ }
+
+ /// Identifies the kernel filter used to process this event.
+ ///
+ /// Will only return an error if the kernel reports an event via a filter
+ /// that is unknown to Nix.
+ pub fn filter(&self) -> Result<EventFilter> {
+ self.kevent.filter.try_into()
+ }
+
+ /// Flags control what the kernel will do when this event is added with
+ /// [`Kqueue::kevent`].
+ pub fn flags(&self) -> EventFlag {
+ EventFlag::from_bits(self.kevent.flags).unwrap()
+ }
+
+ /// Filter-specific flags.
+ pub fn fflags(&self) -> FilterFlag {
+ FilterFlag::from_bits(self.kevent.fflags).unwrap()
+ }
+
+ /// Filter-specific data value.
+ pub fn data(&self) -> intptr_t {
+ self.kevent.data as intptr_t
+ }
+
+ /// Opaque user-defined value passed through the kernel unchanged.
+ pub fn udata(&self) -> intptr_t {
+ self.kevent.udata as intptr_t
+ }
+}
+
+#[allow(missing_docs)]
+#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
+pub fn kevent(
+ kq: &Kqueue,
+ changelist: &[KEvent],
+ eventlist: &mut [KEvent],
+ timeout_ms: usize,
+) -> Result<usize> {
+ // Convert ms to timespec
+ let timeout = timespec {
+ tv_sec: (timeout_ms / 1000) as time_t,
+ tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long,
+ };
+
+ kq.kevent(changelist, eventlist, Some(timeout))
+}
+
+#[cfg(any(
+ 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;
+
+#[allow(missing_docs)]
+#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
+pub fn kevent_ts(
+ kq: &Kqueue,
+ changelist: &[KEvent],
+ eventlist: &mut [KEvent],
+ timeout_opt: Option<timespec>,
+) -> Result<usize> {
+ kq.kevent(changelist, eventlist, timeout_opt)
+}
+
+/// Modify an existing [`KEvent`].
+// Probably should deprecate. Would anybody ever use it over `KEvent::new`?
+#[deprecated(since = "0.27.0", note = "Use Kqueue::kevent instead")]
+#[inline]
+pub fn ev_set(
+ ev: &mut KEvent,
+ ident: usize,
+ filter: EventFilter,
+ flags: 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() {
+ use std::mem;
+
+ let udata: intptr_t = 12345;
+
+ let actual = KEvent::new(
+ 0xdead_beef,
+ EventFilter::EVFILT_READ,
+ EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
+ FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
+ 0x1337,
+ udata,
+ );
+ assert_eq!(0xdead_beef, actual.ident());
+ let filter = actual.kevent.filter;
+ assert_eq!(libc::EVFILT_READ, filter);
+ assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
+ assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
+ assert_eq!(0x1337, actual.data());
+ assert_eq!(udata as type_of_udata, actual.udata() as type_of_udata);
+ assert_eq!(mem::size_of::<libc::kevent>(), mem::size_of::<KEvent>());
+}
+
+#[test]
+fn test_kevent_filter() {
+ let udata: intptr_t = 12345;
+
+ let actual = KEvent::new(
+ 0xdead_beef,
+ EventFilter::EVFILT_READ,
+ EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
+ FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
+ 0x1337,
+ udata,
+ );
+ assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
+}
diff --git a/third_party/rust/nix/src/sys/eventfd.rs b/third_party/rust/nix/src/sys/eventfd.rs
new file mode 100644
index 0000000000..f1723519cf
--- /dev/null
+++ b/third_party/rust/nix/src/sys/eventfd.rs
@@ -0,0 +1,17 @@
+use crate::errno::Errno;
+use crate::Result;
+use std::os::unix::io::{FromRawFd, OwnedFd};
+
+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<OwnedFd> {
+ let res = unsafe { libc::eventfd(initval, flags.bits()) };
+
+ Errno::result(res).map(|r| unsafe { OwnedFd::from_raw_fd(r) })
+}
diff --git a/third_party/rust/nix/src/sys/inotify.rs b/third_party/rust/nix/src/sys/inotify.rs
new file mode 100644
index 0000000000..e5fe930f49
--- /dev/null
+++ b/third_party/rust/nix/src/sys/inotify.rs
@@ -0,0 +1,248 @@
+//! Monitoring API for filesystem events.
+//!
+//! Inotify is a Linux-only API to monitor filesystems events.
+//!
+//! For more documentation, please read [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html).
+//!
+//! # Examples
+//!
+//! Monitor all events happening in directory "test":
+//! ```no_run
+//! # use nix::sys::inotify::{AddWatchFlags,InitFlags,Inotify};
+//! #
+//! // We create a new inotify instance.
+//! let instance = Inotify::init(InitFlags::empty()).unwrap();
+//!
+//! // We add a new watch on directory "test" for all events.
+//! let wd = instance.add_watch("test", AddWatchFlags::IN_ALL_EVENTS).unwrap();
+//!
+//! loop {
+//! // We read from our inotify instance for events.
+//! let events = instance.read_events().unwrap();
+//! println!("Events: {:?}", events);
+//! }
+//! ```
+
+use crate::errno::Errno;
+use crate::unistd::read;
+use crate::NixPath;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{c_char, c_int};
+use std::ffi::{CStr, OsStr, OsString};
+use std::mem::{size_of, MaybeUninit};
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
+use std::ptr;
+
+libc_bitflags! {
+ /// Configuration options for [`inotify_add_watch`](fn.inotify_add_watch.html).
+ pub struct AddWatchFlags: u32 {
+ /// File was accessed.
+ IN_ACCESS;
+ /// File was modified.
+ IN_MODIFY;
+ /// Metadata changed.
+ IN_ATTRIB;
+ /// Writable file was closed.
+ IN_CLOSE_WRITE;
+ /// Nonwritable file was closed.
+ IN_CLOSE_NOWRITE;
+ /// File was opened.
+ IN_OPEN;
+ /// File was moved from X.
+ IN_MOVED_FROM;
+ /// File was moved to Y.
+ IN_MOVED_TO;
+ /// Subfile was created.
+ IN_CREATE;
+ /// Subfile was deleted.
+ IN_DELETE;
+ /// Self was deleted.
+ IN_DELETE_SELF;
+ /// Self was moved.
+ IN_MOVE_SELF;
+
+ /// Backing filesystem was unmounted.
+ IN_UNMOUNT;
+ /// Event queue overflowed.
+ IN_Q_OVERFLOW;
+ /// File was ignored.
+ IN_IGNORED;
+
+ /// Combination of `IN_CLOSE_WRITE` and `IN_CLOSE_NOWRITE`.
+ IN_CLOSE;
+ /// Combination of `IN_MOVED_FROM` and `IN_MOVED_TO`.
+ IN_MOVE;
+
+ /// Only watch the path if it is a directory.
+ IN_ONLYDIR;
+ /// Don't follow symlinks.
+ IN_DONT_FOLLOW;
+
+ /// Event occurred against directory.
+ IN_ISDIR;
+ /// Only send event once.
+ IN_ONESHOT;
+ /// All of the events.
+ IN_ALL_EVENTS;
+ }
+}
+
+libc_bitflags! {
+ /// Configuration options for [`inotify_init1`](fn.inotify_init1.html).
+ pub struct InitFlags: c_int {
+ /// Set the `FD_CLOEXEC` flag on the file descriptor.
+ IN_CLOEXEC;
+ /// Set the `O_NONBLOCK` flag on the open file description referred to by the new file descriptor.
+ IN_NONBLOCK;
+ }
+}
+
+/// An inotify instance. This is also a file descriptor, you can feed it to
+/// other interfaces consuming file descriptors, epoll for example.
+#[derive(Debug)]
+pub struct Inotify {
+ fd: OwnedFd,
+}
+
+/// This object is returned when you create a new watch on an inotify instance.
+/// It is then returned as part of an event once triggered. It allows you to
+/// know which watch triggered which event.
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
+pub struct WatchDescriptor {
+ wd: i32,
+}
+
+/// A single inotify event.
+///
+/// For more documentation see, [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html).
+#[derive(Debug)]
+pub struct InotifyEvent {
+ /// Watch descriptor. This field corresponds to the watch descriptor you
+ /// were issued when calling add_watch. It allows you to know which watch
+ /// this event comes from.
+ pub wd: WatchDescriptor,
+ /// Event mask. This field is a bitfield describing the exact event that
+ /// occured.
+ pub mask: AddWatchFlags,
+ /// This cookie is a number that allows you to connect related events. For
+ /// now only IN_MOVED_FROM and IN_MOVED_TO can be connected.
+ pub cookie: u32,
+ /// Filename. This field exists only if the event was triggered for a file
+ /// inside the watched directory.
+ pub name: Option<OsString>,
+}
+
+impl Inotify {
+ /// Initialize a new inotify instance.
+ ///
+ /// Returns a Result containing an inotify instance.
+ ///
+ /// For more information see, [inotify_init(2)](https://man7.org/linux/man-pages/man2/inotify_init.2.html).
+ pub fn init(flags: InitFlags) -> Result<Inotify> {
+ let res = Errno::result(unsafe { libc::inotify_init1(flags.bits()) });
+
+ res.map(|fd| Inotify { fd: unsafe { OwnedFd::from_raw_fd(fd) } })
+ }
+
+ /// Adds a new watch on the target file or directory.
+ ///
+ /// Returns a watch descriptor. This is not a File Descriptor!
+ ///
+ /// For more information see, [inotify_add_watch(2)](https://man7.org/linux/man-pages/man2/inotify_add_watch.2.html).
+ pub fn add_watch<P: ?Sized + NixPath>(
+ &self,
+ path: &P,
+ mask: AddWatchFlags,
+ ) -> Result<WatchDescriptor> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::inotify_add_watch(self.fd.as_raw_fd(), cstr.as_ptr(), mask.bits())
+ })?;
+
+ Errno::result(res).map(|wd| WatchDescriptor { wd })
+ }
+
+ /// Removes an existing watch using the watch descriptor returned by
+ /// inotify_add_watch.
+ ///
+ /// Returns an EINVAL error if the watch descriptor is invalid.
+ ///
+ /// For more information see, [inotify_rm_watch(2)](https://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html).
+ pub fn rm_watch(&self, wd: WatchDescriptor) -> Result<()> {
+ cfg_if! {
+ if #[cfg(target_os = "linux")] {
+ let arg = wd.wd;
+ } else if #[cfg(target_os = "android")] {
+ let arg = wd.wd as u32;
+ }
+ }
+ let res = unsafe { libc::inotify_rm_watch(self.fd.as_raw_fd(), arg) };
+
+ Errno::result(res).map(drop)
+ }
+
+ /// Reads a collection of events from the inotify file descriptor. This call
+ /// can either be blocking or non blocking depending on whether IN_NONBLOCK
+ /// was set at initialization.
+ ///
+ /// Returns as many events as available. If the call was non blocking and no
+ /// events could be read then the EAGAIN error is returned.
+ pub fn read_events(&self) -> Result<Vec<InotifyEvent>> {
+ let header_size = size_of::<libc::inotify_event>();
+ const BUFSIZ: usize = 4096;
+ let mut buffer = [0u8; BUFSIZ];
+ let mut events = Vec::new();
+ let mut offset = 0;
+
+ let nread = read(self.fd.as_raw_fd(), &mut buffer)?;
+
+ while (nread - offset) >= header_size {
+ let event = unsafe {
+ let mut event = MaybeUninit::<libc::inotify_event>::uninit();
+ ptr::copy_nonoverlapping(
+ buffer.as_ptr().add(offset),
+ event.as_mut_ptr() as *mut u8,
+ (BUFSIZ - offset).min(header_size),
+ );
+ event.assume_init()
+ };
+
+ let name = match event.len {
+ 0 => None,
+ _ => {
+ let ptr = unsafe {
+ buffer.as_ptr().add(offset + header_size)
+ as *const c_char
+ };
+ let cstr = unsafe { CStr::from_ptr(ptr) };
+
+ Some(OsStr::from_bytes(cstr.to_bytes()).to_owned())
+ }
+ };
+
+ events.push(InotifyEvent {
+ wd: WatchDescriptor { wd: event.wd },
+ mask: AddWatchFlags::from_bits_truncate(event.mask),
+ cookie: event.cookie,
+ name,
+ });
+
+ offset += header_size + event.len as usize;
+ }
+
+ Ok(events)
+ }
+}
+
+impl FromRawFd for Inotify {
+ unsafe fn from_raw_fd(fd: RawFd) -> Self {
+ Inotify { fd: OwnedFd::from_raw_fd(fd) }
+ }
+}
+
+impl AsFd for Inotify {
+ fn as_fd(&'_ self) -> BorrowedFd<'_> {
+ self.fd.as_fd()
+ }
+}
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..307994cb96
--- /dev/null
+++ b/third_party/rust/nix/src/sys/ioctl/bsd.rs
@@ -0,0 +1,129 @@
+/// The datatype used for the ioctl number
+#[doc(hidden)]
+#[cfg(not(target_os = "illumos"))]
+pub type ioctl_num_type = ::libc::c_ulong;
+
+#[doc(hidden)]
+#[cfg(target_os = "illumos")]
+pub type ioctl_num_type = ::libc::c_int;
+
+/// The datatype used for the 3rd argument
+#[doc(hidden)]
+pub type ioctl_param_type = ::libc::c_int;
+
+mod consts {
+ use crate::sys::ioctl::ioctl_num_type;
+ #[doc(hidden)]
+ pub const VOID: ioctl_num_type = 0x2000_0000;
+ #[doc(hidden)]
+ pub const OUT: ioctl_num_type = 0x4000_0000;
+ #[doc(hidden)]
+ #[allow(overflowing_literals)]
+ pub const IN: ioctl_num_type = 0x8000_0000;
+ #[doc(hidden)]
+ pub const INOUT: ioctl_num_type = IN | OUT;
+ #[doc(hidden)]
+ pub const IOCPARM_MASK: ioctl_num_type = 0x1fff;
+}
+
+pub use self::consts::*;
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! ioc {
+ ($inout:expr, $group:expr, $num:expr, $len:expr) => {
+ $inout
+ | (($len as $crate::sys::ioctl::ioctl_num_type
+ & $crate::sys::ioctl::IOCPARM_MASK)
+ << 16)
+ | (($group as $crate::sys::ioctl::ioctl_num_type) << 8)
+ | ($num as $crate::sys::ioctl::ioctl_num_type)
+ };
+}
+
+/// Generate an ioctl request code for a command that passes no data.
+///
+/// This is equivalent to the `_IO()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_none!()` directly.
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// const KVMIO: u8 = 0xAE;
+/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03));
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_none {
+ ($g:expr, $n:expr) => {
+ ioc!($crate::sys::ioctl::VOID, $g, $n, 0)
+ };
+}
+
+/// Generate an ioctl request code for a command that passes an integer
+///
+/// This is equivalent to the `_IOWINT()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_write_int!()` directly.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_write_int {
+ ($g:expr, $n:expr) => {
+ ioc!(
+ $crate::sys::ioctl::VOID,
+ $g,
+ $n,
+ ::std::mem::size_of::<$crate::libc::c_int>()
+ )
+ };
+}
+
+/// Generate an ioctl request code for a command that reads.
+///
+/// This is equivalent to the `_IOR()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_read!()` directly.
+///
+/// The read/write direction is relative to userland, so this
+/// command would be userland is reading and the kernel is
+/// writing.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_read {
+ ($g:expr, $n:expr, $len:expr) => {
+ ioc!($crate::sys::ioctl::OUT, $g, $n, $len)
+ };
+}
+
+/// Generate an ioctl request code for a command that writes.
+///
+/// This is equivalent to the `_IOW()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_write!()` directly.
+///
+/// The read/write direction is relative to userland, so this
+/// command would be userland is writing and the kernel is
+/// reading.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_write {
+ ($g:expr, $n:expr, $len:expr) => {
+ ioc!($crate::sys::ioctl::IN, $g, $n, $len)
+ };
+}
+
+/// Generate an ioctl request code for a command that reads and writes.
+///
+/// This is equivalent to the `_IOWR()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_readwrite!()` directly.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_readwrite {
+ ($g:expr, $n:expr, $len:expr) => {
+ ioc!($crate::sys::ioctl::INOUT, $g, $n, $len)
+ };
+}
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..610b8ddac0
--- /dev/null
+++ b/third_party/rust/nix/src/sys/ioctl/linux.rs
@@ -0,0 +1,168 @@
+use cfg_if::cfg_if;
+
+/// 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_if! {
+ if #[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;
+ }
+ } else {
+ // "Generic" ioctl protocol
+ mod consts {
+ #[doc(hidden)]
+ pub const NONE: u8 = 0;
+ #[doc(hidden)]
+ pub const READ: u8 = 2;
+ #[doc(hidden)]
+ pub const WRITE: u8 = 1;
+ #[doc(hidden)]
+ pub const SIZEBITS: u8 = 14;
+ #[doc(hidden)]
+ pub const DIRBITS: u8 = 2;
+ }
+ }
+}
+
+pub use self::consts::*;
+
+#[doc(hidden)]
+pub const NRSHIFT: ioctl_num_type = 0;
+#[doc(hidden)]
+pub const TYPESHIFT: ioctl_num_type = NRSHIFT + NRBITS as ioctl_num_type;
+#[doc(hidden)]
+pub const SIZESHIFT: ioctl_num_type = TYPESHIFT + TYPEBITS as ioctl_num_type;
+#[doc(hidden)]
+pub const DIRSHIFT: ioctl_num_type = SIZESHIFT + SIZEBITS as ioctl_num_type;
+
+#[doc(hidden)]
+pub const NRMASK: ioctl_num_type = (1 << NRBITS) - 1;
+#[doc(hidden)]
+pub const TYPEMASK: ioctl_num_type = (1 << TYPEBITS) - 1;
+#[doc(hidden)]
+pub const SIZEMASK: ioctl_num_type = (1 << SIZEBITS) - 1;
+#[doc(hidden)]
+pub const DIRMASK: ioctl_num_type = (1 << DIRBITS) - 1;
+
+/// Encode an ioctl command.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! ioc {
+ ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => {
+ (($dir as $crate::sys::ioctl::ioctl_num_type
+ & $crate::sys::ioctl::DIRMASK)
+ << $crate::sys::ioctl::DIRSHIFT)
+ | (($ty as $crate::sys::ioctl::ioctl_num_type
+ & $crate::sys::ioctl::TYPEMASK)
+ << $crate::sys::ioctl::TYPESHIFT)
+ | (($nr as $crate::sys::ioctl::ioctl_num_type
+ & $crate::sys::ioctl::NRMASK)
+ << $crate::sys::ioctl::NRSHIFT)
+ | (($sz as $crate::sys::ioctl::ioctl_num_type
+ & $crate::sys::ioctl::SIZEMASK)
+ << $crate::sys::ioctl::SIZESHIFT)
+ };
+}
+
+/// Generate an ioctl request code for a command that passes no data.
+///
+/// This is equivalent to the `_IO()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_none!()` directly.
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// const KVMIO: u8 = 0xAE;
+/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03));
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_none {
+ ($ty:expr, $nr:expr) => {
+ ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0)
+ };
+}
+
+/// Generate an ioctl request code for a command that reads.
+///
+/// This is equivalent to the `_IOR()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_read!()` directly.
+///
+/// The read/write direction is relative to userland, so this
+/// command would be userland is reading and the kernel is
+/// writing.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_read {
+ ($ty:expr, $nr:expr, $sz:expr) => {
+ ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz)
+ };
+}
+
+/// Generate an ioctl request code for a command that writes.
+///
+/// This is equivalent to the `_IOW()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_write!()` directly.
+///
+/// The read/write direction is relative to userland, so this
+/// command would be userland is writing and the kernel is
+/// reading.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_write {
+ ($ty:expr, $nr:expr, $sz:expr) => {
+ ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz)
+ };
+}
+
+/// Generate an ioctl request code for a command that reads and writes.
+///
+/// This is equivalent to the `_IOWR()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_readwrite!()` directly.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_readwrite {
+ ($ty:expr, $nr:expr, $sz:expr) => {
+ ioc!(
+ $crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE,
+ $ty,
+ $nr,
+ $sz
+ )
+ };
+}
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..0b3fe3e769
--- /dev/null
+++ b/third_party/rust/nix/src/sys/ioctl/mod.rs
@@ -0,0 +1,786 @@
+//! Provide helpers for making ioctl system calls.
+//!
+//! This library is pretty low-level and messy. `ioctl` is not fun.
+//!
+//! What is an `ioctl`?
+//! ===================
+//!
+//! The `ioctl` syscall is the grab-bag syscall on POSIX systems. Don't want to add a new
+//! syscall? Make it an `ioctl`! `ioctl` refers to both the syscall, and the commands that can be
+//! sent with it. `ioctl` stands for "IO control", and the commands are always sent to a file
+//! descriptor.
+//!
+//! It is common to see `ioctl`s used for the following purposes:
+//!
+//! * Provide read/write access to out-of-band data related to a device such as configuration
+//! (for instance, setting serial port options)
+//! * Provide a mechanism for performing full-duplex data transfers (for instance, xfer on SPI
+//! devices).
+//! * Provide access to control functions on a device (for example, on Linux you can send
+//! commands like pause, resume, and eject to the CDROM device.
+//! * Do whatever else the device driver creator thought made most sense.
+//!
+//! `ioctl`s are synchronous system calls and are similar to read and write calls in that regard.
+//! They operate on file descriptors and have an identifier that specifies what the ioctl is.
+//! Additionally they may read or write data and therefore need to pass along a data pointer.
+//! Besides the semantics of the ioctls being confusing, the generation of this identifer can also
+//! be difficult.
+//!
+//! Historically `ioctl` numbers were arbitrary hard-coded values. In Linux (before 2.6) and some
+//! unices this has changed to a more-ordered system where the ioctl numbers are partitioned into
+//! subcomponents (For linux this is documented in
+//! [`Documentation/ioctl/ioctl-number.rst`](https://elixir.bootlin.com/linux/latest/source/Documentation/userspace-api/ioctl/ioctl-number.rst)):
+//!
+//! * Number: The actual ioctl ID
+//! * Type: A grouping of ioctls for a common purpose or driver
+//! * Size: The size in bytes of the data that will be transferred
+//! * Direction: Whether there is any data and if it's read, write, or both
+//!
+//! Newer drivers should not generate complete integer identifiers for their `ioctl`s instead
+//! preferring to use the 4 components above to generate the final ioctl identifier. Because of
+//! how old `ioctl`s are, however, there are many hard-coded `ioctl` identifiers. These are
+//! commonly referred to as "bad" in `ioctl` documentation.
+//!
+//! Defining `ioctl`s
+//! =================
+//!
+//! This library provides several `ioctl_*!` macros for binding `ioctl`s. These generate public
+//! unsafe functions that can then be used for calling the ioctl. This macro has a few different
+//! ways it can be used depending on the specific ioctl you're working with.
+//!
+//! A simple `ioctl` is `SPI_IOC_RD_MODE`. This ioctl works with the SPI interface on Linux. This
+//! specific `ioctl` reads the mode of the SPI device as a `u8`. It's declared in
+//! `/include/uapi/linux/spi/spidev.h` as `_IOR(SPI_IOC_MAGIC, 1, __u8)`. Since it uses the `_IOR`
+//! macro, we know it's a `read` ioctl and can use the `ioctl_read!` macro as follows:
+//!
+//! ```
+//! # #[macro_use] extern crate nix;
+//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
+//! const SPI_IOC_TYPE_MODE: u8 = 1;
+//! ioctl_read!(spi_read_mode, SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, u8);
+//! # fn main() {}
+//! ```
+//!
+//! This generates the function:
+//!
+//! ```
+//! # #[macro_use] extern crate nix;
+//! # use std::mem;
+//! # use nix::{libc, Result};
+//! # use nix::errno::Errno;
+//! # use nix::libc::c_int as c_int;
+//! # const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
+//! # const SPI_IOC_TYPE_MODE: u8 = 1;
+//! pub unsafe fn spi_read_mode(fd: c_int, data: *mut u8) -> Result<c_int> {
+//! let res = libc::ioctl(fd, request_code_read!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, mem::size_of::<u8>()), data);
+//! Errno::result(res)
+//! }
+//! # fn main() {}
+//! ```
+//!
+//! The return value for the wrapper functions generated by the `ioctl_*!` macros are `nix::Error`s.
+//! These are generated by assuming the return value of the ioctl is `-1` on error and everything
+//! else is a valid return value. If this is not the case, `Result::map` can be used to map some
+//! of the range of "good" values (-Inf..-2, 0..Inf) into a smaller range in a helper function.
+//!
+//! Writing `ioctl`s generally use pointers as their data source and these should use the
+//! `ioctl_write_ptr!`. But in some cases an `int` is passed directly. For these `ioctl`s use the
+//! `ioctl_write_int!` macro. This variant does not take a type as the last argument:
+//!
+//! ```
+//! # #[macro_use] extern crate nix;
+//! const HCI_IOC_MAGIC: u8 = b'k';
+//! const HCI_IOC_HCIDEVUP: u8 = 1;
+//! ioctl_write_int!(hci_dev_up, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP);
+//! # fn main() {}
+//! ```
+//!
+//! Some `ioctl`s don't transfer any data, and those should use `ioctl_none!`. This macro
+//! doesn't take a type and so it is declared similar to the `write_int` variant shown above.
+//!
+//! The mode for a given `ioctl` should be clear from the documentation if it has good
+//! documentation. Otherwise it will be clear based on the macro used to generate the `ioctl`
+//! number where `_IO`, `_IOR`, `_IOW`, and `_IOWR` map to "none", "read", "write_*", and "readwrite"
+//! respectively. To determine the specific `write_` variant to use you'll need to find
+//! what the argument type is supposed to be. If it's an `int`, then `write_int` should be used,
+//! otherwise it should be a pointer and `write_ptr` should be used. On Linux the
+//! [`ioctl_list` man page](https://man7.org/linux/man-pages/man2/ioctl_list.2.html) describes a
+//! large number of `ioctl`s and describes their argument data type.
+//!
+//! Using "bad" `ioctl`s
+//! --------------------
+//!
+//! As mentioned earlier, there are many old `ioctl`s that do not use the newer method of
+//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the
+//! `ioctl_*_bad!` macros. This naming comes from the Linux kernel which refers to these
+//! `ioctl`s as "bad". These are a different variant as they bypass calling the macro that generates
+//! the ioctl number and instead use the defined value directly.
+//!
+//! For example the `TCGETS` `ioctl` reads a `termios` data structure for a given file descriptor.
+//! It's defined as `0x5401` in `ioctls.h` on Linux and can be implemented as:
+//!
+//! ```
+//! # #[macro_use] extern crate nix;
+//! # #[cfg(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() {}
+//! ```
+use cfg_if::cfg_if;
+
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+#[macro_use]
+mod linux;
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "redox"
+))]
+pub use self::linux::*;
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "haiku",
+ target_os = "openbsd"
+))]
+#[macro_use]
+mod bsd;
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "haiku",
+ 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(local_inner_macros)]
+macro_rules! ioctl_none {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_none!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type))
+ }
+ )
+}
+
+/// Generates a wrapper function for a "bad" ioctl that passes no data to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl request code
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Example
+///
+/// ```no_run
+/// # #[macro_use] extern crate nix;
+/// # use libc::TIOCNXCL;
+/// # use std::fs::File;
+/// # use std::os::unix::io::AsRawFd;
+/// ioctl_none_bad!(tiocnxcl, TIOCNXCL);
+/// fn main() {
+/// let file = File::open("/dev/ttyUSB0").unwrap();
+/// unsafe { tiocnxcl(file.as_raw_fd()) }.unwrap();
+/// }
+/// ```
+// TODO: add an example using request_code_*!()
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_none_bad {
+ ($(#[$attr:meta])* $name:ident, $nr:expr) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type))
+ }
+ )
+}
+
+/// Generates a wrapper function for an ioctl that reads data from the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl identifier
+/// * The ioctl sequence number
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
+/// const SPI_IOC_TYPE_MODE: u8 = 1;
+/// ioctl_read!(spi_read_mode, SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, u8);
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_read {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: *mut $ty)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for a "bad" ioctl that reads data from the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl request code
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// # #[cfg(any(target_os = "android", target_os = "linux"))]
+/// ioctl_read_bad!(tcgets, libc::TCGETS, libc::termios);
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_read_bad {
+ ($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: *mut $ty)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for an ioctl that writes data through a pointer to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl identifier
+/// * The ioctl sequence number
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *const DATA_TYPE) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// # pub struct v4l2_audio {}
+/// ioctl_write_ptr!(s_audio, b'V', 34, v4l2_audio);
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_write_ptr {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: *const $ty)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for a "bad" ioctl that writes data through a pointer to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl request code
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *const DATA_TYPE) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// # #[cfg(any(target_os = "android", target_os = "linux"))]
+/// ioctl_write_ptr_bad!(tcsets, libc::TCSETS, libc::termios);
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_write_ptr_bad {
+ ($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: *const $ty)
+ -> $crate::Result<$crate::libc::c_int> {
+ 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(local_inner_macros)]
+ macro_rules! ioctl_write_int {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: $crate::sys::ioctl::ioctl_param_type)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write_int!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+ }
+ } else {
+ /// Generates a wrapper function for a ioctl that writes an integer to the kernel.
+ ///
+ /// The arguments to this macro are:
+ ///
+ /// * The function name
+ /// * The ioctl identifier
+ /// * The ioctl sequence number
+ ///
+ /// The generated function has the following signature:
+ ///
+ /// ```rust,ignore
+ /// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: nix::sys::ioctl::ioctl_param_type) -> Result<libc::c_int>
+ /// ```
+ ///
+ /// `nix::sys::ioctl::ioctl_param_type` depends on the OS:
+ /// * BSD - `libc::c_int`
+ /// * Linux - `libc::c_ulong`
+ ///
+ /// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # #[macro_use] extern crate nix;
+ /// const HCI_IOC_MAGIC: u8 = b'k';
+ /// const HCI_IOC_HCIDEVUP: u8 = 1;
+ /// ioctl_write_int!(hci_dev_up, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP);
+ /// # fn main() {}
+ /// ```
+ #[macro_export(local_inner_macros)]
+ macro_rules! ioctl_write_int {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: $crate::sys::ioctl::ioctl_param_type)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$crate::libc::c_int>()) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+ }
+ }
+}
+
+/// Generates a wrapper function for a "bad" ioctl that writes an integer to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl request code
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: libc::c_int) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Examples
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// # #[cfg(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(local_inner_macros)]
+macro_rules! ioctl_write_int_bad {
+ ($(#[$attr:meta])* $name:ident, $nr:expr) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: $crate::libc::c_int)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for an ioctl that reads and writes data to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl identifier
+/// * The ioctl sequence number
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// # pub struct v4l2_audio {}
+/// ioctl_readwrite!(enum_audio, b'V', 65, v4l2_audio);
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_readwrite {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: *mut $ty)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for a "bad" ioctl that reads and writes data to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl request code
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+// TODO: Find an example for ioctl_readwrite_bad
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_readwrite_bad {
+ ($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: *mut $ty)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for an ioctl that reads an array of elements from the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl identifier
+/// * The ioctl sequence number
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &mut [DATA_TYPE]) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+// TODO: Find an example for ioctl_read_buf
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_read_buf {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: &mut [$ty])
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for an ioctl that writes an array of elements to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl identifier
+/// * The ioctl sequence number
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &[DATA_TYPE]) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Examples
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
+/// const SPI_IOC_TYPE_MESSAGE: u8 = 0;
+/// # pub struct spi_ioc_transfer(u64);
+/// ioctl_write_buf!(spi_transfer, SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, spi_ioc_transfer);
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_write_buf {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: &[$ty])
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for an ioctl that reads and writes an array of elements to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl identifier
+/// * The ioctl sequence number
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &mut [DATA_TYPE]) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+// TODO: Find an example for readwrite_buf
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_readwrite_buf {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: &mut [$ty])
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, ::std::mem::size_of_val(data)) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
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..516ffd3262
--- /dev/null
+++ b/third_party/rust/nix/src/sys/memfd.rs
@@ -0,0 +1,65 @@
+//! Interfaces for managing memory-backed files.
+
+use cfg_if::cfg_if;
+use std::os::unix::io::{FromRawFd, OwnedFd, RawFd};
+
+use crate::errno::Errno;
+use crate::Result;
+use std::ffi::CStr;
+
+libc_bitflags!(
+ /// Options that change the behavior of [`memfd_create`].
+ pub struct MemFdCreateFlag: libc::c_uint {
+ /// Set the close-on-exec ([`FD_CLOEXEC`]) flag on the new file descriptor.
+ ///
+ /// By default, the new file descriptor is set to remain open across an [`execve`]
+ /// (the `FD_CLOEXEC` flag is initially disabled). This flag can be used to change
+ /// this default. The file offset is set to the beginning of the file (see [`lseek`]).
+ ///
+ /// See also the description of the `O_CLOEXEC` flag in [`open(2)`].
+ ///
+ /// [`execve`]: crate::unistd::execve
+ /// [`lseek`]: crate::unistd::lseek
+ /// [`FD_CLOEXEC`]: crate::fcntl::FdFlag::FD_CLOEXEC
+ /// [`open(2)`]: https://man7.org/linux/man-pages/man2/open.2.html
+ MFD_CLOEXEC;
+ /// Allow sealing operations on this file.
+ ///
+ /// See also the file sealing notes given in [`memfd_create(2)`].
+ ///
+ /// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
+ MFD_ALLOW_SEALING;
+ }
+);
+
+/// Creates an anonymous file that lives in memory, and return a file-descriptor to it.
+///
+/// The file behaves like a regular file, and so can be modified, truncated, memory-mapped, and so on.
+/// However, unlike a regular file, it lives in RAM and has a volatile backing storage.
+///
+/// For more information, see [`memfd_create(2)`].
+///
+/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
+#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
+pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result<OwnedFd> {
+ let res = unsafe {
+ cfg_if! {
+ if #[cfg(all(
+ // Android does not have a memfd_create symbol
+ not(target_os = "android"),
+ any(
+ target_os = "freebsd",
+ // If the OS is Linux, gnu and musl expose a memfd_create symbol but not uclibc
+ target_env = "gnu",
+ target_env = "musl",
+ )))]
+ {
+ libc::memfd_create(name.as_ptr(), flags.bits())
+ } else {
+ libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags.bits())
+ }
+ }
+ };
+
+ Errno::result(res).map(|r| unsafe { OwnedFd::from_raw_fd(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..8cfd6d6d54
--- /dev/null
+++ b/third_party/rust/nix/src/sys/mman.rs
@@ -0,0 +1,607 @@
+//! Memory management declarations.
+
+use crate::errno::Errno;
+#[cfg(not(target_os = "android"))]
+use crate::NixPath;
+use crate::Result;
+#[cfg(not(target_os = "android"))]
+#[cfg(feature = "fs")]
+use crate::{fcntl::OFlag, sys::stat::Mode};
+use libc::{self, c_int, c_void, off_t, size_t};
+use std::{num::NonZeroUsize, os::unix::io::{AsRawFd, AsFd}};
+
+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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PROT_GROWSDOWN;
+ /// Apply protection down to the beginning of a mapping that grows downwards.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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;
+ /// Place the mapping at exactly the address specified in `addr`, but never clobber an existing range.
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_FIXED_NOREPLACE;
+ /// To be used with `MAP_FIXED`, to forbid the system
+ /// to select a different address than the one specified.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_EXCL;
+ /// Synonym for `MAP_ANONYMOUS`.
+ MAP_ANON;
+ /// The mapping is not backed by any file.
+ MAP_ANONYMOUS;
+ /// Put the mapping into the first 2GB of the process address space.
+ #[cfg(any(all(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")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_GROWSDOWN;
+ /// Compatibility flag. Ignored.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_DENYWRITE;
+ /// Compatibility flag. Ignored.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_EXECUTABLE;
+ /// Mark the mmaped region to be locked in the same way as `mlock(2)`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_LOCKED;
+ /// Do not reserve swap space for this mapping.
+ ///
+ /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
+ #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd", target_os = "aix")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_NORESERVE;
+ /// Populate page tables for a mapping.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_POPULATE;
+ /// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_NONBLOCK;
+ /// Allocate the mapping using "huge pages."
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGETLB;
+ /// Make use of 64KB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_64KB;
+ /// Make use of 512KB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_512KB;
+ /// Make use of 1MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_1MB;
+ /// Make use of 2MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_2MB;
+ /// Make use of 8MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_8MB;
+ /// Make use of 16MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_16MB;
+ /// Make use of 32MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_32MB;
+ /// Make use of 256MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_256MB;
+ /// Make use of 512MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_512MB;
+ /// Make use of 1GB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_1GB;
+ /// Make use of 2GB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_2GB;
+ /// Make use of 16GB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_16GB;
+
+ /// Lock the mapped region into memory as with `mlock(2)`.
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_NOSYNC;
+ /// Rename private pages to a file.
+ ///
+ /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
+ #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_RENAME;
+ /// Region may contain semaphores.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HASSEMAPHORE;
+ /// Region grows down, like a stack.
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_STACK;
+ /// Pages in this mapping are not retained in the kernel's memory cache.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_NOCACHE;
+ /// Allows the W/X bit on the page, it's necessary on aarch64 architecture.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_JIT;
+ /// Allows to use large pages, underlying alignment based on size.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_ALIGNED_SUPER;
+ /// Pages will be discarded in the core dumps.
+ #[cfg(target_os = "openbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_CONCEAL;
+ }
+}
+
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
+libc_bitflags! {
+ /// Options for [`mremap`].
+ pub struct MRemapFlags: c_int {
+ /// Permit the kernel to relocate the mapping to a new virtual address, if necessary.
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MREMAP_MAYMOVE;
+ /// Place the mapping at exactly the address specified in `new_address`.
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MREMAP_FIXED;
+ /// Place the mapping at exactly the address specified in `new_address`.
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_FIXED;
+ /// Allows to duplicate the mapping to be able to apply different flags on the copy.
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_REMAPDUP;
+ }
+}
+
+libc_enum! {
+ /// Usage information for a range of memory to allow for performance optimizations by the kernel.
+ ///
+ /// Used by [`madvise`].
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum MmapAdvise {
+ /// No further special treatment. This is the default.
+ MADV_NORMAL,
+ /// Expect random page references.
+ MADV_RANDOM,
+ /// Expect sequential page references.
+ MADV_SEQUENTIAL,
+ /// Expect access in the near future.
+ MADV_WILLNEED,
+ /// Do not expect access in the near future.
+ MADV_DONTNEED,
+ /// Free up a given range of pages and its associated backing store.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_DONTFORK,
+ /// Undo the effect of `MADV_DONTFORK`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_HWPOISON,
+ /// Enable Kernel Samepage Merging (KSM) for the given pages.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_MERGEABLE,
+ /// Undo the effect of `MADV_MERGEABLE`
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_UNMERGEABLE,
+ /// Preserve the memory of each page but offline the original page.
+ #[cfg(any(target_os = "android",
+ all(target_os = "linux", any(
+ target_arch = "aarch64",
+ target_arch = "arm",
+ target_arch = "powerpc",
+ target_arch = "powerpc64",
+ target_arch = "s390x",
+ target_arch = "x86",
+ target_arch = "x86_64",
+ target_arch = "sparc64"))))]
+ MADV_SOFT_OFFLINE,
+ /// Enable Transparent Huge Pages (THP) for pages in the given range.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_HUGEPAGE,
+ /// Undo the effect of `MADV_HUGEPAGE`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_NOHUGEPAGE,
+ /// Exclude the given range from a core dump.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_DONTDUMP,
+ /// Undo the effect of an earlier `MADV_DONTDUMP`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_DODUMP,
+ /// Specify that the application no longer needs the pages in the given range.
+ #[cfg(not(target_os = "aix"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_AUTOSYNC,
+ /// Region is not included in a core file.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_NOCORE,
+ /// Include region in a core file
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_CORE,
+ /// This process should not be killed when swap space is exhausted.
+ #[cfg(any(target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_PROTECT,
+ /// Invalidate the hardware page table for the given region.
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_INVAL,
+ /// Set the offset of the page directory page to `value` for the virtual page table.
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_SETMAP,
+ /// Indicates that the application will not need the data in the given range.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_ZERO_WIRED_PAGES,
+ /// Pages can be reused (by anyone).
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_FREE_REUSABLE,
+ /// Caller wants to reuse those pages.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_FREE_REUSE,
+ // Darwin doesn't document this flag's behavior.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[allow(missing_docs)]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MS_KILLPAGES;
+ /// Deactivate pages, but leave them mapped.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MS_DEACTIVATE;
+ /// Perform an update and wait for it to complete.
+ MS_SYNC;
+ }
+}
+
+#[cfg(not(target_os = "haiku"))]
+libc_bitflags! {
+ /// Flags for [`mlockall`].
+ pub struct MlockAllFlags: c_int {
+ /// Lock pages that are currently mapped into the address space of the process.
+ MCL_CURRENT;
+ /// Lock pages which will become mapped into the address space of the process in the future.
+ MCL_FUTURE;
+ }
+}
+
+/// Locks all memory pages that contain part of the address range with `length`
+/// bytes starting at `addr`.
+///
+/// Locked pages never move to the swap area.
+///
+/// # Safety
+///
+/// `addr` must meet all the requirements described in the [`mlock(2)`] man page.
+///
+/// [`mlock(2)`]: https://man7.org/linux/man-pages/man2/mlock.2.html
+pub unsafe fn mlock(addr: *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`.
+///
+/// # Safety
+///
+/// `addr` must meet all the requirements described in the [`munlock(2)`] man
+/// page.
+///
+/// [`munlock(2)`]: https://man7.org/linux/man-pages/man2/munlock.2.html
+pub unsafe fn munlock(addr: *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. For more information, see [`mlockall(2)`].
+///
+/// [`mlockall(2)`]: https://man7.org/linux/man-pages/man2/mlockall.2.html
+#[cfg(not(target_os = "haiku"))]
+pub fn mlockall(flags: MlockAllFlags) -> Result<()> {
+ unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop)
+}
+
+/// Unlocks all memory pages mapped into this process' address space.
+///
+/// For more information, see [`munlockall(2)`].
+///
+/// [`munlockall(2)`]: https://man7.org/linux/man-pages/man2/munlockall.2.html
+#[cfg(not(target_os = "haiku"))]
+pub fn munlockall() -> Result<()> {
+ unsafe { Errno::result(libc::munlockall()) }.map(drop)
+}
+
+/// allocate memory, or map files or devices into memory
+///
+/// # Safety
+///
+/// See the [`mmap(2)`] man page for detailed requirements.
+///
+/// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
+pub unsafe fn mmap<F: AsFd>(
+ addr: Option<NonZeroUsize>,
+ length: NonZeroUsize,
+ prot: ProtFlags,
+ flags: MapFlags,
+ f: Option<F>,
+ offset: off_t,
+) -> Result<*mut c_void> {
+ let ptr =
+ addr.map_or(std::ptr::null_mut(), |a| usize::from(a) as *mut c_void);
+
+ let fd = f.map(|f| f.as_fd().as_raw_fd()).unwrap_or(-1);
+ let ret =
+ libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), fd, offset);
+
+ if ret == libc::MAP_FAILED {
+ Err(Errno::last())
+ } else {
+ Ok(ret)
+ }
+}
+
+/// Expands (or shrinks) an existing memory mapping, potentially moving it at
+/// the same time.
+///
+/// # Safety
+///
+/// See the `mremap(2)` [man page](https://man7.org/linux/man-pages/man2/mremap.2.html) for
+/// detailed requirements.
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
+pub unsafe fn mremap(
+ addr: *mut c_void,
+ old_size: size_t,
+ new_size: size_t,
+ flags: MRemapFlags,
+ new_address: Option<*mut c_void>,
+) -> Result<*mut c_void> {
+ #[cfg(target_os = "linux")]
+ let ret = libc::mremap(
+ addr,
+ old_size,
+ new_size,
+ flags.bits(),
+ new_address.unwrap_or(std::ptr::null_mut()),
+ );
+ #[cfg(target_os = "netbsd")]
+ let ret = libc::mremap(
+ addr,
+ old_size,
+ new_address.unwrap_or(std::ptr::null_mut()),
+ new_size,
+ flags.bits(),
+ );
+
+ if ret == libc::MAP_FAILED {
+ Err(Errno::last())
+ } else {
+ Ok(ret)
+ }
+}
+
+/// remove a mapping
+///
+/// # Safety
+///
+/// `addr` must meet all the requirements described in the [`munmap(2)`] man
+/// page.
+///
+/// [`munmap(2)`]: https://man7.org/linux/man-pages/man2/munmap.2.html
+pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
+ Errno::result(libc::munmap(addr, len)).map(drop)
+}
+
+/// give advice about use of memory
+///
+/// # Safety
+///
+/// See the [`madvise(2)`] man page. Take special care when using
+/// [`MmapAdvise::MADV_FREE`].
+///
+/// [`madvise(2)`]: https://man7.org/linux/man-pages/man2/madvise.2.html
+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)
+}
+
+/// Set protection of memory mapping.
+///
+/// See [`mprotect(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mprotect.html) for
+/// details.
+///
+/// # Safety
+///
+/// Calls to `mprotect` are inherently unsafe, as changes to memory protections can lead to
+/// SIGSEGVs.
+///
+/// ```
+/// # use nix::libc::size_t;
+/// # use nix::sys::mman::{mmap, mprotect, MapFlags, ProtFlags};
+/// # use std::ptr;
+/// # use std::os::unix::io::BorrowedFd;
+/// const ONE_K: size_t = 1024;
+/// let one_k_non_zero = std::num::NonZeroUsize::new(ONE_K).unwrap();
+/// let mut slice: &mut [u8] = unsafe {
+/// let mem = mmap::<BorrowedFd>(None, one_k_non_zero, ProtFlags::PROT_NONE,
+/// MapFlags::MAP_ANON | MapFlags::MAP_PRIVATE, None, 0).unwrap();
+/// mprotect(mem, ONE_K, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE).unwrap();
+/// std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
+/// };
+/// assert_eq!(slice[0], 0x00);
+/// slice[0] = 0xFF;
+/// assert_eq!(slice[0], 0xFF);
+/// ```
+pub unsafe fn mprotect(
+ addr: *mut c_void,
+ length: size_t,
+ prot: ProtFlags,
+) -> Result<()> {
+ Errno::result(libc::mprotect(addr, length, prot.bits())).map(drop)
+}
+
+/// synchronize a mapped region
+///
+/// # Safety
+///
+/// `addr` must meet all the requirements described in the [`msync(2)`] man
+/// page.
+///
+/// [`msync(2)`]: https://man7.org/linux/man-pages/man2/msync.2.html
+pub unsafe fn msync(
+ addr: *mut c_void,
+ length: size_t,
+ flags: MsFlags,
+) -> Result<()> {
+ Errno::result(libc::msync(addr, length, flags.bits())).map(drop)
+}
+
+#[cfg(not(target_os = "android"))]
+feature! {
+#![feature = "fs"]
+/// Creates and opens a new, or opens an existing, POSIX shared memory object.
+///
+/// For more information, see [`shm_open(3)`].
+///
+/// [`shm_open(3)`]: https://man7.org/linux/man-pages/man3/shm_open.3.html
+pub fn shm_open<P>(
+ name: &P,
+ flag: OFlag,
+ mode: Mode
+ ) -> Result<std::os::unix::io::OwnedFd>
+ where P: ?Sized + NixPath
+{
+ use std::os::unix::io::{FromRawFd, OwnedFd};
+
+ let ret = name.with_nix_path(|cstr| {
+ #[cfg(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)
+ }
+ })?;
+
+ match ret {
+ -1 => Err(Errno::last()),
+ fd => Ok(unsafe{ OwnedFd::from_raw_fd(fd) })
+ }
+}
+}
+
+/// Performs the converse of [`shm_open`], removing an object previously created.
+///
+/// For more information, see [`shm_unlink(3)`].
+///
+/// [`shm_unlink(3)`]: https://man7.org/linux/man-pages/man3/shm_unlink.3.html
+#[cfg(not(target_os = "android"))]
+pub fn shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()> {
+ let ret =
+ name.with_nix_path(|cstr| unsafe { libc::shm_unlink(cstr.as_ptr()) })?;
+
+ Errno::result(ret).map(drop)
+}
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..bf047b3dda
--- /dev/null
+++ b/third_party/rust/nix/src/sys/mod.rs
@@ -0,0 +1,231 @@
+//! Mostly platform-specific functionality
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ all(target_os = "linux", not(target_env = "uclibc")),
+ target_os = "macos",
+ target_os = "netbsd"
+))]
+feature! {
+ #![feature = "aio"]
+ pub mod aio;
+}
+
+feature! {
+ #![feature = "event"]
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[allow(missing_docs)]
+ 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(any(target_os = "android", target_os = "linux"))]
+ #[allow(missing_docs)]
+ pub mod eventfd;
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "openbsd"
+))]
+#[cfg(feature = "ioctl")]
+#[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
+#[macro_use]
+pub mod ioctl;
+
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+feature! {
+ #![feature = "fs"]
+ pub mod memfd;
+}
+
+#[cfg(not(target_os = "redox"))]
+feature! {
+ #![feature = "mman"]
+ pub mod mman;
+}
+
+#[cfg(target_os = "linux")]
+feature! {
+ #![feature = "personality"]
+ pub mod personality;
+}
+
+#[cfg(target_os = "linux")]
+feature! {
+ #![feature = "process"]
+ pub mod prctl;
+}
+
+feature! {
+ #![feature = "pthread"]
+ pub mod pthread;
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+))]
+feature! {
+ #![feature = "ptrace"]
+ #[allow(missing_docs)]
+ pub mod ptrace;
+}
+
+#[cfg(target_os = "linux")]
+feature! {
+ #![feature = "quota"]
+ pub mod quota;
+}
+
+#[cfg(target_os = "linux")]
+feature! {
+ #![feature = "reboot"]
+ pub mod reboot;
+}
+
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "haiku"
+)))]
+feature! {
+ #![feature = "resource"]
+ pub mod resource;
+}
+
+feature! {
+ #![feature = "poll"]
+ pub mod select;
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"
+))]
+feature! {
+ #![feature = "zerocopy"]
+ pub mod sendfile;
+}
+
+pub mod signal;
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+feature! {
+ #![feature = "signal"]
+ #[allow(missing_docs)]
+ pub mod signalfd;
+}
+
+feature! {
+ #![feature = "socket"]
+ #[allow(missing_docs)]
+ pub mod socket;
+}
+
+feature! {
+ #![feature = "fs"]
+ #[allow(missing_docs)]
+ pub mod stat;
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "openbsd"
+))]
+feature! {
+ #![feature = "fs"]
+ pub mod statfs;
+}
+
+feature! {
+ #![feature = "fs"]
+ pub mod statvfs;
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+#[allow(missing_docs)]
+pub mod sysinfo;
+
+feature! {
+ #![feature = "term"]
+ #[allow(missing_docs)]
+ pub mod termios;
+}
+
+#[allow(missing_docs)]
+pub mod time;
+
+feature! {
+ #![feature = "uio"]
+ pub mod uio;
+}
+
+feature! {
+ #![feature = "feature"]
+ pub mod utsname;
+}
+
+feature! {
+ #![feature = "process"]
+ pub mod wait;
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+feature! {
+ #![feature = "inotify"]
+ pub mod inotify;
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+feature! {
+ #![feature = "time"]
+ pub mod timerfd;
+}
+
+#[cfg(all(
+ any(
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd"
+ ),
+ feature = "time",
+ feature = "signal"
+))]
+feature! {
+ #![feature = "time"]
+ pub mod timer;
+}
diff --git a/third_party/rust/nix/src/sys/personality.rs b/third_party/rust/nix/src/sys/personality.rs
new file mode 100644
index 0000000000..30231dd7b8
--- /dev/null
+++ b/third_party/rust/nix/src/sys/personality.rs
@@ -0,0 +1,96 @@
+//! Process execution domains
+use crate::errno::Errno;
+use crate::Result;
+
+use libc::{self, c_int, c_ulong};
+
+libc_bitflags! {
+ /// Flags used and returned by [`get()`](fn.get.html) and
+ /// [`set()`](fn.set.html).
+ pub struct Persona: c_int {
+ /// Provide the legacy virtual address space layout.
+ ADDR_COMPAT_LAYOUT;
+ /// Disable address-space-layout randomization.
+ ADDR_NO_RANDOMIZE;
+ /// Limit the address space to 32 bits.
+ ADDR_LIMIT_32BIT;
+ /// Use `0xc0000000` as the offset at which to search a virtual memory
+ /// chunk on [`mmap(2)`], otherwise use `0xffffe000`.
+ ///
+ /// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
+ ADDR_LIMIT_3GB;
+ /// User-space function pointers to signal handlers point to descriptors.
+ #[cfg(not(any(target_env = "musl", target_env = "uclibc")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ FDPIC_FUNCPTRS;
+ /// Map page 0 as read-only.
+ MMAP_PAGE_ZERO;
+ /// `PROT_READ` implies `PROT_EXEC` for [`mmap(2)`].
+ ///
+ /// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
+ READ_IMPLIES_EXEC;
+ /// No effects.
+ SHORT_INODE;
+ /// [`select(2)`], [`pselect(2)`], and [`ppoll(2)`] do not modify the
+ /// returned timeout argument when interrupted by a signal handler.
+ ///
+ /// [`select(2)`]: https://man7.org/linux/man-pages/man2/select.2.html
+ /// [`pselect(2)`]: https://man7.org/linux/man-pages/man2/pselect.2.html
+ /// [`ppoll(2)`]: https://man7.org/linux/man-pages/man2/ppoll.2.html
+ STICKY_TIMEOUTS;
+ /// Have [`uname(2)`] report a 2.6.40+ version number rather than a 3.x
+ /// version number.
+ ///
+ /// [`uname(2)`]: https://man7.org/linux/man-pages/man2/uname.2.html
+ #[cfg(not(any(target_env = "musl", target_env = "uclibc")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ UNAME26;
+ /// No effects.
+ WHOLE_SECONDS;
+ }
+}
+
+/// Retrieve the current process personality.
+///
+/// Returns a Result containing a Persona instance.
+///
+/// Example:
+///
+/// ```
+/// # use nix::sys::personality::{self, Persona};
+/// let pers = personality::get().unwrap();
+/// assert!(!pers.contains(Persona::WHOLE_SECONDS));
+/// ```
+pub fn get() -> Result<Persona> {
+ let res = unsafe { libc::personality(0xFFFFFFFF) };
+
+ Errno::result(res).map(Persona::from_bits_truncate)
+}
+
+/// Set the current process personality.
+///
+/// Returns a Result containing the *previous* personality for the
+/// process, as a Persona.
+///
+/// For more information, see [personality(2)](https://man7.org/linux/man-pages/man2/personality.2.html)
+///
+/// **NOTE**: This call **replaces** the current personality entirely.
+/// To **update** the personality, first call `get()` and then `set()`
+/// with the modified persona.
+///
+/// Example:
+///
+// Disable test on aarch64 until we know why it fails.
+// https://github.com/nix-rust/nix/issues/2060
+#[cfg_attr(target_arch = "aarch64", doc = " ```no_run")]
+#[cfg_attr(not(target_arch = "aarch64"), doc = " ```")]
+/// # use nix::sys::personality::{self, Persona};
+/// let mut pers = personality::get().unwrap();
+/// assert!(!pers.contains(Persona::ADDR_NO_RANDOMIZE));
+/// personality::set(pers | Persona::ADDR_NO_RANDOMIZE).unwrap();
+/// ```
+pub fn set(persona: Persona) -> Result<Persona> {
+ let res = unsafe { libc::personality(persona.bits() as c_ulong) };
+
+ Errno::result(res).map(Persona::from_bits_truncate)
+}
diff --git a/third_party/rust/nix/src/sys/prctl.rs b/third_party/rust/nix/src/sys/prctl.rs
new file mode 100644
index 0000000000..995382cb0c
--- /dev/null
+++ b/third_party/rust/nix/src/sys/prctl.rs
@@ -0,0 +1,208 @@
+//! prctl is a Linux-only API for performing operations on a process or thread.
+//!
+//! Note that careless use of some prctl() operations can confuse the user-space run-time
+//! environment, so these operations should be used with care.
+//!
+//! For more documentation, please read [prctl(2)](https://man7.org/linux/man-pages/man2/prctl.2.html).
+
+use crate::errno::Errno;
+use crate::sys::signal::Signal;
+use crate::Result;
+
+use libc::{c_int, c_ulong};
+use std::convert::TryFrom;
+use std::ffi::{CStr, CString};
+
+libc_enum! {
+ /// The type of hardware memory corruption kill policy for the thread.
+
+ #[repr(i32)]
+ #[non_exhaustive]
+ #[allow(non_camel_case_types)]
+ pub enum PrctlMCEKillPolicy {
+ /// The thread will receive SIGBUS as soon as a memory corruption is detected.
+ PR_MCE_KILL_EARLY,
+ /// The process is killed only when it accesses a corrupted page.
+ PR_MCE_KILL_LATE,
+ /// Uses the system-wide default.
+ PR_MCE_KILL_DEFAULT,
+ }
+ impl TryFrom<i32>
+}
+
+fn prctl_set_bool(option: c_int, status: bool) -> Result<()> {
+ let res = unsafe { libc::prctl(option, status as c_ulong, 0, 0, 0) };
+ Errno::result(res).map(drop)
+}
+
+fn prctl_get_bool(option: c_int) -> Result<bool> {
+ let res = unsafe { libc::prctl(option, 0, 0, 0, 0) };
+ Errno::result(res).map(|res| res != 0)
+}
+
+/// Set the "child subreaper" attribute for this process
+pub fn set_child_subreaper(attribute: bool) -> Result<()> {
+ prctl_set_bool(libc::PR_SET_CHILD_SUBREAPER, attribute)
+}
+
+/// Get the "child subreaper" attribute for this process
+pub fn get_child_subreaper() -> Result<bool> {
+ // prctl writes into this var
+ let mut subreaper: c_int = 0;
+
+ let res = unsafe { libc::prctl(libc::PR_GET_CHILD_SUBREAPER, &mut subreaper, 0, 0, 0) };
+
+ Errno::result(res).map(|_| subreaper != 0)
+}
+
+/// Set the dumpable attribute which determines if core dumps are created for this process.
+pub fn set_dumpable(attribute: bool) -> Result<()> {
+ prctl_set_bool(libc::PR_SET_DUMPABLE, attribute)
+}
+
+/// Get the dumpable attribute for this process.
+pub fn get_dumpable() -> Result<bool> {
+ prctl_get_bool(libc::PR_GET_DUMPABLE)
+}
+
+/// Set the "keep capabilities" attribute for this process. This causes the thread to retain
+/// capabilities even if it switches its UID to a nonzero value.
+pub fn set_keepcaps(attribute: bool) -> Result<()> {
+ prctl_set_bool(libc::PR_SET_KEEPCAPS, attribute)
+}
+
+/// Get the "keep capabilities" attribute for this process
+pub fn get_keepcaps() -> Result<bool> {
+ prctl_get_bool(libc::PR_GET_KEEPCAPS)
+}
+
+/// Clear the thread memory corruption kill policy and use the system-wide default
+pub fn clear_mce_kill() -> Result<()> {
+ let res = unsafe { libc::prctl(libc::PR_MCE_KILL, libc::PR_MCE_KILL_CLEAR, 0, 0, 0) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Set the thread memory corruption kill policy
+pub fn set_mce_kill(policy: PrctlMCEKillPolicy) -> Result<()> {
+ let res = unsafe {
+ libc::prctl(
+ libc::PR_MCE_KILL,
+ libc::PR_MCE_KILL_SET,
+ policy as c_ulong,
+ 0,
+ 0,
+ )
+ };
+
+ Errno::result(res).map(drop)
+}
+
+/// Get the thread memory corruption kill policy
+pub fn get_mce_kill() -> Result<PrctlMCEKillPolicy> {
+ let res = unsafe { libc::prctl(libc::PR_MCE_KILL_GET, 0, 0, 0, 0) };
+
+ match Errno::result(res) {
+ Ok(val) => Ok(PrctlMCEKillPolicy::try_from(val)?),
+ Err(e) => Err(e),
+ }
+}
+
+/// Set the parent-death signal of the calling process. This is the signal that the calling process
+/// will get when its parent dies.
+pub fn set_pdeathsig<T: Into<Option<Signal>>>(signal: T) -> Result<()> {
+ let sig = match signal.into() {
+ Some(s) => s as c_int,
+ None => 0,
+ };
+
+ let res = unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, sig, 0, 0, 0) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Returns the current parent-death signal
+pub fn get_pdeathsig() -> Result<Option<Signal>> {
+ // prctl writes into this var
+ let mut sig: c_int = 0;
+
+ let res = unsafe { libc::prctl(libc::PR_GET_PDEATHSIG, &mut sig, 0, 0, 0) };
+
+ match Errno::result(res) {
+ Ok(_) => Ok(match sig {
+ 0 => None,
+ _ => Some(Signal::try_from(sig)?),
+ }),
+ Err(e) => Err(e),
+ }
+}
+
+/// Set the name of the calling thread. Strings longer than 15 bytes will be truncated.
+pub fn set_name(name: &CStr) -> Result<()> {
+ let res = unsafe { libc::prctl(libc::PR_SET_NAME, name.as_ptr(), 0, 0, 0) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Return the name of the calling thread
+pub fn get_name() -> Result<CString> {
+ // Size of buffer determined by linux/sched.h TASK_COMM_LEN
+ let buf = [0u8; 16];
+
+ let res = unsafe { libc::prctl(libc::PR_GET_NAME, &buf, 0, 0, 0) };
+
+ let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
+ let name = CStr::from_bytes_with_nul(&buf[..=len]).map_err(|_| Errno::EINVAL)?;
+
+ Errno::result(res).map(|_| name.to_owned())
+}
+
+/// Sets the timer slack value for the calling thread. Timer slack is used by the kernel to group
+/// timer expirations and make them the supplied amount of nanoseconds late.
+pub fn set_timerslack(ns: u64) -> Result<()> {
+ let res = unsafe { libc::prctl(libc::PR_SET_TIMERSLACK, ns, 0, 0, 0) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Get the timerslack for the calling thread.
+pub fn get_timerslack() -> Result<i32> {
+ let res = unsafe { libc::prctl(libc::PR_GET_TIMERSLACK, 0, 0, 0, 0) };
+
+ Errno::result(res)
+}
+
+/// Disable all performance counters attached to the calling process.
+pub fn task_perf_events_disable() -> Result<()> {
+ let res = unsafe { libc::prctl(libc::PR_TASK_PERF_EVENTS_DISABLE, 0, 0, 0, 0) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Enable all performance counters attached to the calling process.
+pub fn task_perf_events_enable() -> Result<()> {
+ let res = unsafe { libc::prctl(libc::PR_TASK_PERF_EVENTS_ENABLE, 0, 0, 0, 0) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Set the calling threads "no new privs" attribute. Once set this option can not be unset.
+pub fn set_no_new_privs() -> Result<()> {
+ prctl_set_bool(libc::PR_SET_NO_NEW_PRIVS, true) // Cannot be unset
+}
+
+/// Get the "no new privs" attribute for the calling thread.
+pub fn get_no_new_privs() -> Result<bool> {
+ prctl_get_bool(libc::PR_GET_NO_NEW_PRIVS)
+}
+
+/// Set the state of the "THP disable" flag for the calling thread. Setting this disables
+/// transparent huge pages.
+pub fn set_thp_disable(flag: bool) -> Result<()> {
+ prctl_set_bool(libc::PR_SET_THP_DISABLE, flag)
+}
+
+/// Get the "THP disable" flag for the calling thread.
+pub fn get_thp_disable() -> Result<bool> {
+ prctl_get_bool(libc::PR_GET_THP_DISABLE)
+}
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..6bad03a4d4
--- /dev/null
+++ b/third_party/rust/nix/src/sys/pthread.rs
@@ -0,0 +1,43 @@
+//! Low level threading primitives
+
+#[cfg(not(target_os = "redox"))]
+use crate::errno::Errno;
+#[cfg(not(target_os = "redox"))]
+use crate::Result;
+use libc::{self, pthread_t};
+
+/// Identifies an individual thread.
+pub type Pthread = pthread_t;
+
+/// Obtain ID of the calling thread (see
+/// [`pthread_self(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_self.html)
+///
+/// The thread ID returned by `pthread_self()` is not the same thing as
+/// the kernel thread ID returned by a call to `gettid(2)`.
+#[inline]
+pub fn pthread_self() -> Pthread {
+ unsafe { libc::pthread_self() }
+}
+
+feature! {
+#![feature = "signal"]
+
+/// Send a signal to a thread (see [`pthread_kill(3)`]).
+///
+/// If `signal` is `None`, `pthread_kill` will only preform error checking and
+/// won't send any signal.
+///
+/// [`pthread_kill(3)`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_kill.html
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
+#[cfg(not(target_os = "redox"))]
+pub fn pthread_kill<T>(thread: Pthread, signal: T) -> Result<()>
+ where T: Into<Option<crate::sys::signal::Signal>>
+{
+ let sig = match signal.into() {
+ Some(s) => s as libc::c_int,
+ None => 0,
+ };
+ let res = unsafe { libc::pthread_kill(thread, sig) };
+ Errno::result(res).map(drop)
+}
+}
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..ba267c6577
--- /dev/null
+++ b/third_party/rust/nix/src/sys/ptrace/bsd.rs
@@ -0,0 +1,195 @@
+use crate::errno::Errno;
+use crate::sys::signal::Signal;
+use crate::unistd::Pid;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_int};
+use std::ptr;
+
+pub type RequestType = c_int;
+
+cfg_if! {
+ if #[cfg(any(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.
+ #[non_exhaustive]
+ pub enum Request {
+ PT_TRACE_ME,
+ PT_READ_I,
+ PT_READ_D,
+ #[cfg(target_os = "macos")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PT_READ_U,
+ PT_WRITE_I,
+ PT_WRITE_D,
+ #[cfg(target_os = "macos")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PT_SIGEXC,
+ #[cfg(target_os = "macos")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PT_THUPDATE,
+ #[cfg(target_os = "macos")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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 by `pid`, making it a tracee of the calling process.
+pub fn attach(pid: Pid) -> Result<()> {
+ unsafe {
+ ptrace_other(Request::PT_ATTACH, pid, ptr::null_mut(), 0).map(drop)
+ }
+}
+
+/// Detaches the current running process, as with `ptrace(PT_DETACH, ...)`
+///
+/// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a
+/// signal specified by `sig`.
+pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as c_int,
+ None => 0,
+ };
+ unsafe {
+ ptrace_other(Request::PT_DETACH, pid, ptr::null_mut(), data).map(drop)
+ }
+}
+
+/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
+///
+/// Continues the execution of the process with PID `pid`, optionally
+/// delivering a signal specified by `sig`.
+pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as c_int,
+ None => 0,
+ };
+ unsafe {
+ // Ignore the useless return value
+ ptrace_other(Request::PT_CONTINUE, pid, 1 as AddressType, data)
+ .map(drop)
+ }
+}
+
+/// Issues a kill request as with `ptrace(PT_KILL, ...)`
+///
+/// This request is equivalent to `ptrace(PT_CONTINUE, ..., SIGKILL);`
+pub fn kill(pid: Pid) -> Result<()> {
+ unsafe {
+ ptrace_other(Request::PT_KILL, pid, 0 as AddressType, 0).map(drop)
+ }
+}
+
+/// Move the stopped tracee process forward by a single step as with
+/// `ptrace(PT_STEP, ...)`
+///
+/// Advances the execution of the process with PID `pid` by a single step optionally delivering a
+/// signal specified by `sig`.
+///
+/// # Example
+/// ```rust
+/// use nix::sys::ptrace::step;
+/// use nix::unistd::Pid;
+/// use nix::sys::signal::Signal;
+/// use nix::sys::wait::*;
+/// // If a process changes state to the stopped state because of a SIGUSR1
+/// // signal, this will step the process forward and forward the user
+/// // signal to the stopped process
+/// match waitpid(Pid::from_raw(-1), None) {
+/// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => {
+/// let _ = step(pid, Signal::SIGUSR1);
+/// }
+/// _ => {},
+/// }
+/// ```
+#[cfg(any(
+ any(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
+// Technically, ptrace doesn't dereference the pointer. It passes it directly
+// to the kernel.
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
+pub fn read(pid: Pid, addr: AddressType) -> Result<c_int> {
+ unsafe {
+ // Traditionally there was a difference between reading data or
+ // instruction memory but not in modern systems.
+ ptrace_other(Request::PT_READ_D, pid, addr, 0)
+ }
+}
+
+/// Writes a word into the processes memory at the given address
+// Technically, ptrace doesn't dereference the pointer. It passes it directly
+// to the kernel.
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
+pub fn write(pid: Pid, addr: AddressType, data: c_int) -> Result<()> {
+ unsafe { ptrace_other(Request::PT_WRITE_D, pid, addr, data).map(drop) }
+}
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..8c134cf7ee
--- /dev/null
+++ b/third_party/rust/nix/src/sys/ptrace/linux.rs
@@ -0,0 +1,560 @@
+//! For detailed description of the ptrace requests, consult `man ptrace`.
+
+use crate::errno::Errno;
+use crate::sys::signal::Signal;
+use crate::unistd::Pid;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_long, c_void, siginfo_t};
+use std::{mem, ptr};
+
+pub type AddressType = *mut ::libc::c_void;
+
+#[cfg(all(
+ target_os = "linux",
+ any(
+ all(
+ target_arch = "x86_64",
+ any(target_env = "gnu", target_env = "musl")
+ ),
+ all(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"),
+ target_env = "uclibc"))] {
+ #[doc(hidden)]
+ pub type RequestType = ::libc::c_uint;
+ } else {
+ #[doc(hidden)]
+ pub type RequestType = ::libc::c_int;
+ }
+}
+
+libc_enum! {
+ #[cfg_attr(not(any(target_env = "musl", target_env = "uclibc", target_os = "android")), repr(u32))]
+ #[cfg_attr(any(target_env = "musl", target_env = "uclibc", target_os = "android"), repr(i32))]
+ /// Ptrace Request enum defining the action to be taken.
+ #[non_exhaustive]
+ pub enum Request {
+ PTRACE_TRACEME,
+ PTRACE_PEEKTEXT,
+ PTRACE_PEEKDATA,
+ PTRACE_PEEKUSER,
+ PTRACE_POKETEXT,
+ PTRACE_POKEDATA,
+ PTRACE_POKEUSER,
+ PTRACE_CONT,
+ PTRACE_KILL,
+ PTRACE_SINGLESTEP,
+ #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
+ all(target_os = "linux", any(target_env = "musl",
+ target_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(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTRACE_SEIZE,
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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,
+ #[cfg(all(target_os = "linux", target_env = "gnu",
+ any(target_arch = "x86", target_arch = "x86_64")))]
+ PTRACE_SYSEMU,
+ #[cfg(all(target_os = "linux", target_env = "gnu",
+ any(target_arch = "x86", target_arch = "x86_64")))]
+ PTRACE_SYSEMU_SINGLESTEP,
+ }
+}
+
+libc_enum! {
+ #[repr(i32)]
+ /// Using the ptrace options the tracer can configure the tracee to stop
+ /// at certain events. This enum is used to define those events as defined
+ /// in `man ptrace`.
+ #[non_exhaustive]
+ pub enum Event {
+ /// Event that stops before a return from fork or clone.
+ PTRACE_EVENT_FORK,
+ /// Event that stops before a return from vfork or clone.
+ PTRACE_EVENT_VFORK,
+ /// Event that stops before a return from clone.
+ PTRACE_EVENT_CLONE,
+ /// Event that stops before a return from execve.
+ PTRACE_EVENT_EXEC,
+ /// Event for a return from vfork.
+ PTRACE_EVENT_VFORK_DONE,
+ /// Event for a stop before an exit. Unlike the waitpid Exit status program.
+ /// registers can still be examined
+ PTRACE_EVENT_EXIT,
+ /// Stop triggered by a seccomp rule on a tracee.
+ PTRACE_EVENT_SECCOMP,
+ /// Stop triggered by the `INTERRUPT` syscall, or a group stop,
+ /// or when a new child is attached.
+ PTRACE_EVENT_STOP,
+ }
+}
+
+libc_bitflags! {
+ /// Ptrace options used in conjunction with the PTRACE_SETOPTIONS request.
+ /// See `man ptrace` for more details.
+ pub struct Options: libc::c_int {
+ /// When delivering system call traps set a bit to allow tracer to
+ /// distinguish between normal stops or syscall stops. May not work on
+ /// all systems.
+ PTRACE_O_TRACESYSGOOD;
+ /// Stop tracee at next fork and start tracing the forked process.
+ PTRACE_O_TRACEFORK;
+ /// Stop tracee at next vfork call and trace the vforked process.
+ PTRACE_O_TRACEVFORK;
+ /// Stop tracee at next clone call and trace the cloned process.
+ PTRACE_O_TRACECLONE;
+ /// Stop tracee at next execve call.
+ PTRACE_O_TRACEEXEC;
+ /// Stop tracee at vfork completion.
+ PTRACE_O_TRACEVFORKDONE;
+ /// Stop tracee at next exit call. Stops before exit commences allowing
+ /// tracer to see location of exit and register states.
+ PTRACE_O_TRACEEXIT;
+ /// Stop tracee when a SECCOMP_RET_TRACE rule is triggered. See `man seccomp` for more
+ /// details.
+ PTRACE_O_TRACESECCOMP;
+ /// Send a SIGKILL to the tracee if the tracer exits. This is useful
+ /// for ptrace jailers to prevent tracees from escaping their control.
+ PTRACE_O_EXITKILL;
+ }
+}
+
+fn ptrace_peek(
+ request: Request,
+ pid: Pid,
+ addr: AddressType,
+ data: *mut c_void,
+) -> Result<c_long> {
+ let ret = unsafe {
+ Errno::clear();
+ libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)
+ };
+ match Errno::result(ret) {
+ Ok(..) | Err(Errno::UnknownErrno) => Ok(ret),
+ err @ Err(..) => err,
+ }
+}
+
+/// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)`
+#[cfg(all(
+ target_os = "linux",
+ any(
+ all(
+ target_arch = "x86_64",
+ any(target_env = "gnu", target_env = "musl")
+ ),
+ all(target_arch = "x86", target_env = "gnu")
+ )
+))]
+pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
+ ptrace_get_data::<user_regs_struct>(Request::PTRACE_GETREGS, pid)
+}
+
+/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)`
+#[cfg(all(
+ target_os = "linux",
+ any(
+ all(
+ target_arch = "x86_64",
+ any(target_env = "gnu", target_env = "musl")
+ ),
+ all(target_arch = "x86", target_env = "gnu")
+ )
+))]
+pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
+ let res = unsafe {
+ libc::ptrace(
+ Request::PTRACE_SETREGS as RequestType,
+ libc::pid_t::from(pid),
+ ptr::null_mut::<c_void>(),
+ &regs as *const _ 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> {
+ let mut data = mem::MaybeUninit::uninit();
+ let res = unsafe {
+ libc::ptrace(
+ request as RequestType,
+ libc::pid_t::from(pid),
+ ptr::null_mut::<T>(),
+ data.as_mut_ptr() as *const _ as *const c_void,
+ )
+ };
+ Errno::result(res)?;
+ Ok(unsafe { data.assume_init() })
+}
+
+unsafe fn ptrace_other(
+ request: Request,
+ pid: Pid,
+ addr: AddressType,
+ data: *mut c_void,
+) -> Result<c_long> {
+ 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
+ }
+}
+
+/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSCALL, ...)`
+///
+/// Arranges for the tracee to be stopped at the next entry to or exit from a system call,
+/// optionally delivering a signal specified by `sig`.
+pub fn syscall<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(Request::PTRACE_SYSCALL, pid, ptr::null_mut(), data)
+ .map(drop) // ignore the useless return value
+ }
+}
+
+/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSEMU, ...)`
+///
+/// In contrast to the `syscall` function, the syscall stopped at will not be executed.
+/// Thus the the tracee will only be stopped once per syscall,
+/// optionally delivering a signal specified by `sig`.
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ any(target_arch = "x86", target_arch = "x86_64")
+))]
+pub fn sysemu<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(Request::PTRACE_SYSEMU, pid, ptr::null_mut(), data)
+ .map(drop)
+ // ignore the useless return value
+ }
+}
+
+/// Attach to a running process, as with `ptrace(PTRACE_ATTACH, ...)`
+///
+/// Attaches to the process specified by `pid`, making it a tracee of the calling process.
+pub fn attach(pid: Pid) -> Result<()> {
+ unsafe {
+ ptrace_other(
+ Request::PTRACE_ATTACH,
+ pid,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ )
+ .map(drop) // ignore the useless return value
+ }
+}
+
+/// Attach to a running process, as with `ptrace(PTRACE_SEIZE, ...)`
+///
+/// Attaches to the process specified in pid, making it a tracee of the calling process.
+#[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn seize(pid: Pid, options: Options) -> Result<()> {
+ unsafe {
+ ptrace_other(
+ Request::PTRACE_SEIZE,
+ pid,
+ ptr::null_mut(),
+ options.bits() as *mut c_void,
+ )
+ .map(drop) // ignore the useless return value
+ }
+}
+
+/// Detaches the current running process, as with `ptrace(PTRACE_DETACH, ...)`
+///
+/// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a
+/// signal specified by `sig`.
+pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(Request::PTRACE_DETACH, pid, ptr::null_mut(), data)
+ .map(drop)
+ }
+}
+
+/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
+///
+/// Continues the execution of the process with PID `pid`, optionally
+/// delivering a signal specified by `sig`.
+pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop)
+ // ignore the useless return value
+ }
+}
+
+/// Stop a tracee, as with `ptrace(PTRACE_INTERRUPT, ...)`
+///
+/// This request is equivalent to `ptrace(PTRACE_INTERRUPT, ...)`
+#[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn interrupt(pid: Pid) -> Result<()> {
+ unsafe {
+ ptrace_other(
+ Request::PTRACE_INTERRUPT,
+ pid,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ )
+ .map(drop)
+ }
+}
+
+/// Issues a kill request as with `ptrace(PTRACE_KILL, ...)`
+///
+/// This request is equivalent to `ptrace(PTRACE_CONT, ..., SIGKILL);`
+pub fn kill(pid: Pid) -> Result<()> {
+ unsafe {
+ ptrace_other(
+ Request::PTRACE_KILL,
+ pid,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ )
+ .map(drop)
+ }
+}
+
+/// Move the stopped tracee process forward by a single step as with
+/// `ptrace(PTRACE_SINGLESTEP, ...)`
+///
+/// Advances the execution of the process with PID `pid` by a single step optionally delivering a
+/// signal specified by `sig`.
+///
+/// # Example
+/// ```rust
+/// use nix::sys::ptrace::step;
+/// use nix::unistd::Pid;
+/// use nix::sys::signal::Signal;
+/// use nix::sys::wait::*;
+///
+/// // If a process changes state to the stopped state because of a SIGUSR1
+/// // signal, this will step the process forward and forward the user
+/// // signal to the stopped process
+/// match waitpid(Pid::from_raw(-1), None) {
+/// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => {
+/// let _ = step(pid, Signal::SIGUSR1);
+/// }
+/// _ => {},
+/// }
+/// ```
+pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data)
+ .map(drop)
+ }
+}
+
+/// Move the stopped tracee process forward by a single step or stop at the next syscall
+/// as with `ptrace(PTRACE_SYSEMU_SINGLESTEP, ...)`
+///
+/// Advances the execution by a single step or until the next syscall.
+/// In case the tracee is stopped at a syscall, the syscall will not be executed.
+/// Optionally, the signal specified by `sig` is delivered to the tracee upon continuation.
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ any(target_arch = "x86", target_arch = "x86_64")
+))]
+pub fn sysemu_step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(
+ Request::PTRACE_SYSEMU_SINGLESTEP,
+ pid,
+ ptr::null_mut(),
+ data,
+ )
+ .map(drop) // ignore the useless return value
+ }
+}
+
+/// Reads a word from a processes memory at the given address, as with
+/// ptrace(PTRACE_PEEKDATA, ...)
+pub fn read(pid: Pid, addr: AddressType) -> Result<c_long> {
+ ptrace_peek(Request::PTRACE_PEEKDATA, pid, addr, ptr::null_mut())
+}
+
+/// Writes a word into the processes memory at the given address, as with
+/// ptrace(PTRACE_POKEDATA, ...)
+///
+/// # Safety
+///
+/// The `data` argument is passed directly to `ptrace(2)`. Read that man page
+/// for guidance.
+pub unsafe fn write(
+ pid: Pid,
+ addr: AddressType,
+ data: *mut c_void,
+) -> Result<()> {
+ ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop)
+}
+
+/// Reads a word from a user area at `offset`, as with ptrace(PTRACE_PEEKUSER, ...).
+/// The user struct definition can be found in `/usr/include/sys/user.h`.
+pub fn read_user(pid: Pid, offset: AddressType) -> Result<c_long> {
+ ptrace_peek(Request::PTRACE_PEEKUSER, pid, offset, ptr::null_mut())
+}
+
+/// Writes a word to a user area at `offset`, as with ptrace(PTRACE_POKEUSER, ...).
+/// The user struct definition can be found in `/usr/include/sys/user.h`.
+///
+/// # Safety
+///
+/// The `data` argument is passed directly to `ptrace(2)`. Read that man page
+/// for guidance.
+pub unsafe fn write_user(
+ pid: Pid,
+ offset: AddressType,
+ data: *mut c_void,
+) -> Result<()> {
+ ptrace_other(Request::PTRACE_POKEUSER, pid, offset, 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..88648acabc
--- /dev/null
+++ b/third_party/rust/nix/src/sys/ptrace/mod.rs
@@ -0,0 +1,25 @@
+//! 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..a32d07aa1e
--- /dev/null
+++ b/third_party/rust/nix/src/sys/quota.rs
@@ -0,0 +1,337 @@
+//! Set and configure disk quotas for users, groups, or projects.
+//!
+//! # Examples
+//!
+//! Enabling and setting a quota:
+//!
+//! ```rust,no_run
+//! # use nix::sys::quota::{Dqblk, quotactl_on, quotactl_set, QuotaFmt, QuotaType, QuotaValidFlags};
+//! quotactl_on(QuotaType::USRQUOTA, "/dev/sda1", QuotaFmt::QFMT_VFS_V1, "aquota.user").unwrap();
+//! let mut dqblk: Dqblk = Default::default();
+//! dqblk.set_blocks_hard_limit(10000);
+//! dqblk.set_blocks_soft_limit(8000);
+//! quotactl_set(QuotaType::USRQUOTA, "/dev/sda1", 50, &dqblk, QuotaValidFlags::QIF_BLIMITS).unwrap();
+//! ```
+use crate::errno::Errno;
+use crate::{NixPath, Result};
+use libc::{self, c_char, c_int};
+use std::default::Default;
+use std::{mem, ptr};
+
+struct QuotaCmd(QuotaSubCmd, QuotaType);
+
+impl QuotaCmd {
+ fn as_int(&self) -> c_int {
+ libc::QCMD(self.0 as i32, self.1 as i32)
+ }
+}
+
+// linux quota version >= 2
+libc_enum! {
+ #[repr(i32)]
+ enum QuotaSubCmd {
+ Q_SYNC,
+ Q_QUOTAON,
+ Q_QUOTAOFF,
+ Q_GETQUOTA,
+ Q_SETQUOTA,
+ }
+}
+
+libc_enum! {
+ /// The scope of the quota.
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum QuotaType {
+ /// Specify a user quota
+ USRQUOTA,
+ /// Specify a group quota
+ GRPQUOTA,
+ }
+}
+
+libc_enum! {
+ /// The type of quota format to use.
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum QuotaFmt {
+ /// Use the original quota format.
+ QFMT_VFS_OLD,
+ /// Use the standard VFS v0 quota format.
+ ///
+ /// Handles 32-bit UIDs/GIDs and quota limits up to 2<sup>32</sup> bytes/2<sup>32</sup> inodes.
+ QFMT_VFS_V0,
+ /// Use the VFS v1 quota format.
+ ///
+ /// Handles 32-bit UIDs/GIDs and quota limits of 2<sup>64</sup> bytes/2<sup>64</sup> inodes.
+ QFMT_VFS_V1,
+ }
+}
+
+libc_bitflags!(
+ /// Indicates the quota fields that are valid to read from.
+ #[derive(Default)]
+ pub struct QuotaValidFlags: u32 {
+ /// The block hard & soft limit fields.
+ QIF_BLIMITS;
+ /// The current space field.
+ QIF_SPACE;
+ /// The inode hard & soft limit fields.
+ QIF_ILIMITS;
+ /// The current inodes field.
+ QIF_INODES;
+ /// The disk use time limit field.
+ QIF_BTIME;
+ /// The file quote time limit field.
+ QIF_ITIME;
+ /// All block & inode limits.
+ QIF_LIMITS;
+ /// The space & inodes usage fields.
+ QIF_USAGE;
+ /// The time limit fields.
+ QIF_TIMES;
+ /// All fields.
+ QIF_ALL;
+ }
+);
+
+/// Wrapper type for `if_dqblk`
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct Dqblk(libc::dqblk);
+
+impl Default for Dqblk {
+ fn default() -> Dqblk {
+ Dqblk(libc::dqblk {
+ dqb_bhardlimit: 0,
+ dqb_bsoftlimit: 0,
+ dqb_curspace: 0,
+ dqb_ihardlimit: 0,
+ dqb_isoftlimit: 0,
+ dqb_curinodes: 0,
+ dqb_btime: 0,
+ dqb_itime: 0,
+ dqb_valid: 0,
+ })
+ }
+}
+
+impl Dqblk {
+ /// The absolute limit on disk quota blocks allocated.
+ pub fn blocks_hard_limit(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
+ Some(self.0.dqb_bhardlimit)
+ } else {
+ None
+ }
+ }
+
+ /// Set the absolute limit on disk quota blocks allocated.
+ pub fn set_blocks_hard_limit(&mut self, limit: u64) {
+ self.0.dqb_bhardlimit = limit;
+ }
+
+ /// Preferred limit on disk quota blocks
+ pub fn blocks_soft_limit(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
+ Some(self.0.dqb_bsoftlimit)
+ } else {
+ None
+ }
+ }
+
+ /// Set the preferred limit on disk quota blocks allocated.
+ pub fn set_blocks_soft_limit(&mut self, limit: u64) {
+ self.0.dqb_bsoftlimit = limit;
+ }
+
+ /// Current occupied space (bytes).
+ pub fn occupied_space(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_SPACE) {
+ Some(self.0.dqb_curspace)
+ } else {
+ None
+ }
+ }
+
+ /// Maximum number of allocated inodes.
+ pub fn inodes_hard_limit(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
+ Some(self.0.dqb_ihardlimit)
+ } else {
+ None
+ }
+ }
+
+ /// Set the maximum number of allocated inodes.
+ pub fn set_inodes_hard_limit(&mut self, limit: u64) {
+ self.0.dqb_ihardlimit = limit;
+ }
+
+ /// Preferred inode limit
+ pub fn inodes_soft_limit(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
+ Some(self.0.dqb_isoftlimit)
+ } else {
+ None
+ }
+ }
+
+ /// Set the preferred limit of allocated inodes.
+ pub fn set_inodes_soft_limit(&mut self, limit: u64) {
+ self.0.dqb_isoftlimit = limit;
+ }
+
+ /// Current number of allocated inodes.
+ pub fn allocated_inodes(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_INODES) {
+ Some(self.0.dqb_curinodes)
+ } else {
+ None
+ }
+ }
+
+ /// Time limit for excessive disk use.
+ pub fn block_time_limit(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_BTIME) {
+ Some(self.0.dqb_btime)
+ } else {
+ None
+ }
+ }
+
+ /// Set the time limit for excessive disk use.
+ pub fn set_block_time_limit(&mut self, limit: u64) {
+ self.0.dqb_btime = limit;
+ }
+
+ /// Time limit for excessive files.
+ pub fn inode_time_limit(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_ITIME) {
+ Some(self.0.dqb_itime)
+ } else {
+ None
+ }
+ }
+
+ /// Set the time limit for excessive files.
+ pub fn set_inode_time_limit(&mut self, limit: u64) {
+ self.0.dqb_itime = limit;
+ }
+}
+
+fn quotactl<P: ?Sized + NixPath>(
+ cmd: QuotaCmd,
+ special: Option<&P>,
+ id: c_int,
+ addr: *mut c_char,
+) -> Result<()> {
+ unsafe {
+ Errno::clear();
+ let res = match special {
+ Some(dev) => dev.with_nix_path(|path| {
+ libc::quotactl(cmd.as_int(), path.as_ptr(), id, addr)
+ }),
+ None => Ok(libc::quotactl(cmd.as_int(), ptr::null(), id, addr)),
+ }?;
+
+ Errno::result(res).map(drop)
+ }
+}
+
+/// Turn on disk quotas for a block device.
+pub fn quotactl_on<P: ?Sized + NixPath>(
+ which: QuotaType,
+ special: &P,
+ format: QuotaFmt,
+ quota_file: &P,
+) -> Result<()> {
+ quota_file.with_nix_path(|path| {
+ let mut path_copy = path.to_bytes_with_nul().to_owned();
+ let p: *mut c_char = path_copy.as_mut_ptr() 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.
+///
+/// If `special` is `None`, then all file systems with active quotas are sync'd.
+pub fn quotactl_sync<P: ?Sized + NixPath>(
+ which: QuotaType,
+ special: Option<&P>,
+) -> Result<()> {
+ quotactl(
+ QuotaCmd(QuotaSubCmd::Q_SYNC, which),
+ special,
+ 0,
+ ptr::null_mut(),
+ )
+}
+
+/// Get disk quota limits and current usage for the given user/group id.
+pub fn quotactl_get<P: ?Sized + NixPath>(
+ which: QuotaType,
+ special: &P,
+ id: c_int,
+) -> Result<Dqblk> {
+ let mut dqblk = mem::MaybeUninit::uninit();
+ quotactl(
+ QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which),
+ Some(special),
+ id,
+ dqblk.as_mut_ptr() as *mut c_char,
+ )?;
+ Ok(unsafe { Dqblk(dqblk.assume_init()) })
+}
+
+/// Configure quota values for the specified fields for a given user/group id.
+pub fn quotactl_set<P: ?Sized + NixPath>(
+ which: QuotaType,
+ special: &P,
+ id: c_int,
+ dqblk: &Dqblk,
+ fields: QuotaValidFlags,
+) -> Result<()> {
+ let mut dqblk_copy = *dqblk;
+ dqblk_copy.0.dqb_valid = fields.bits();
+ quotactl(
+ QuotaCmd(QuotaSubCmd::Q_SETQUOTA, which),
+ Some(special),
+ id,
+ &mut dqblk_copy as *mut _ as *mut c_char,
+ )
+}
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..02d98162bd
--- /dev/null
+++ b/third_party/rust/nix/src/sys/reboot.rs
@@ -0,0 +1,48 @@
+//! Reboot/shutdown or enable/disable Ctrl-Alt-Delete.
+
+use crate::errno::Errno;
+use crate::Result;
+use std::convert::Infallible;
+use std::mem::drop;
+
+libc_enum! {
+ /// How exactly should the system be rebooted.
+ ///
+ /// See [`set_cad_enabled()`](fn.set_cad_enabled.html) for
+ /// enabling/disabling Ctrl-Alt-Delete.
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum RebootMode {
+ /// Halt the system.
+ RB_HALT_SYSTEM,
+ /// Execute a kernel that has been loaded earlier with
+ /// [`kexec_load(2)`](https://man7.org/linux/man-pages/man2/kexec_load.2.html).
+ RB_KEXEC,
+ /// Stop the system and switch off power, if possible.
+ RB_POWER_OFF,
+ /// Restart the system.
+ RB_AUTOBOOT,
+ // we do not support Restart2.
+ /// Suspend the system using software suspend.
+ RB_SW_SUSPEND,
+ }
+}
+
+/// Reboots or shuts down the system.
+pub fn reboot(how: RebootMode) -> Result<Infallible> {
+ unsafe { libc::reboot(how as libc::c_int) };
+ Err(Errno::last())
+}
+
+/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete).
+///
+/// Corresponds to calling `reboot(RB_ENABLE_CAD)` or `reboot(RB_DISABLE_CAD)` in C.
+pub fn set_cad_enabled(enable: bool) -> Result<()> {
+ let cmd = if enable {
+ libc::RB_ENABLE_CAD
+ } else {
+ libc::RB_DISABLE_CAD
+ };
+ let res = unsafe { libc::reboot(cmd) };
+ Errno::result(res).map(drop)
+}
diff --git a/third_party/rust/nix/src/sys/resource.rs b/third_party/rust/nix/src/sys/resource.rs
new file mode 100644
index 0000000000..f42d32e3ca
--- /dev/null
+++ b/third_party/rust/nix/src/sys/resource.rs
@@ -0,0 +1,447 @@
+//! Configure the process resource limits.
+use cfg_if::cfg_if;
+use libc::{c_int, c_long, rusage};
+
+use crate::errno::Errno;
+use crate::sys::time::TimeVal;
+use crate::Result;
+pub use libc::rlim_t;
+pub use libc::RLIM_INFINITY;
+use std::mem;
+
+cfg_if! {
+ if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
+ use libc::{__rlimit_resource_t, rlimit};
+ } else if #[cfg(any(
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "aix",
+ all(target_os = "linux", not(target_env = "gnu"))
+ ))]{
+ use libc::rlimit;
+ }
+}
+
+libc_enum! {
+ /// Types of process resources.
+ ///
+ /// The Resource enum is platform dependent. Check different platform
+ /// manuals for more details. Some platform links have been provided for
+ /// easier reference (non-exhaustive).
+ ///
+ /// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html)
+ /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit)
+ /// * [NetBSD](https://man.netbsd.org/setrlimit.2)
+
+ // linux-gnu uses u_int as resource enum, which is implemented in libc as
+ // well.
+ //
+ // https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html
+ // https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs
+ #[cfg_attr(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")), repr(u32))]
+ #[cfg_attr(any(
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "aix",
+ all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc")))
+ ), repr(i32))]
+ #[non_exhaustive]
+ pub enum Resource {
+ #[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum amount (in bytes) of virtual memory the process is
+ /// allowed to map.
+ RLIMIT_AS,
+ /// The largest size (in bytes) core(5) file that may be created.
+ RLIMIT_CORE,
+ /// The maximum amount of cpu time (in seconds) to be used by each
+ /// process.
+ RLIMIT_CPU,
+ /// The maximum size (in bytes) of the data segment for a process
+ RLIMIT_DATA,
+ /// The largest size (in bytes) file that may be created.
+ RLIMIT_FSIZE,
+ /// The maximum number of open files for this process.
+ RLIMIT_NOFILE,
+ /// The maximum size (in bytes) of the stack segment for a process.
+ RLIMIT_STACK,
+
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum number of kqueues this user id is allowed to create.
+ RLIMIT_KQUEUES,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// A limit on the combined number of flock locks and fcntl leases that
+ /// this process may establish.
+ RLIMIT_LOCKS,
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "linux",
+ target_os = "netbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum size (in bytes) which a process may lock into memory
+ /// using the mlock(2) system call.
+ RLIMIT_MEMLOCK,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// A limit on the number of bytes that can be allocated for POSIX
+ /// message queues for the real user ID of the calling process.
+ RLIMIT_MSGQUEUE,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// A ceiling to which the process's nice value can be raised using
+ /// setpriority or nice.
+ RLIMIT_NICE,
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "linux",
+ target_os = "aix",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum number of simultaneous processes for this user id.
+ RLIMIT_NPROC,
+
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum number of pseudo-terminals this user id is allowed to
+ /// create.
+ RLIMIT_NPTS,
+
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "linux",
+ target_os = "aix",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// When there is memory pressure and swap is available, prioritize
+ /// eviction of a process' resident pages beyond this amount (in bytes).
+ RLIMIT_RSS,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// A ceiling on the real-time priority that may be set for this process
+ /// using sched_setscheduler and sched_set‐ param.
+ RLIMIT_RTPRIO,
+
+ #[cfg(any(target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// A limit (in microseconds) on the amount of CPU time that a process
+ /// scheduled under a real-time scheduling policy may con‐ sume without
+ /// making a blocking system call.
+ RLIMIT_RTTIME,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// A limit on the number of signals that may be queued for the real
+ /// user ID of the calling process.
+ RLIMIT_SIGPENDING,
+
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum size (in bytes) of socket buffer usage for this user.
+ RLIMIT_SBSIZE,
+
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum size (in bytes) of the swap space that may be reserved
+ /// or used by all of this user id's processes.
+ RLIMIT_SWAP,
+
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// An alias for RLIMIT_AS.
+ RLIMIT_VMEM,
+ }
+}
+
+/// Get the current processes resource limits
+///
+/// The special value [`RLIM_INFINITY`] indicates that no limit will be
+/// enforced.
+///
+/// # Parameters
+///
+/// * `resource`: The [`Resource`] that we want to get the limits of.
+///
+/// # Examples
+///
+/// ```
+/// # use nix::sys::resource::{getrlimit, Resource};
+///
+/// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
+/// println!("current soft_limit: {}", soft_limit);
+/// println!("current hard_limit: {}", hard_limit);
+/// ```
+///
+/// # References
+///
+/// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
+///
+/// [`Resource`]: enum.Resource.html
+pub fn getrlimit(resource: Resource) -> Result<(rlim_t, rlim_t)> {
+ let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();
+
+ cfg_if! {
+ if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
+ let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
+ } else {
+ let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) };
+ }
+ }
+
+ Errno::result(res).map(|_| {
+ let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() };
+ (rlim_cur, rlim_max)
+ })
+}
+
+/// Set the current processes resource limits
+///
+/// # Parameters
+///
+/// * `resource`: The [`Resource`] that we want to set the limits of.
+/// * `soft_limit`: The value that the kernel enforces for the corresponding
+/// resource.
+/// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to
+/// the current hard limit for non-root users.
+///
+/// The special value [`RLIM_INFINITY`] indicates that no limit will be
+/// enforced.
+///
+/// # Examples
+///
+/// ```
+/// # use nix::sys::resource::{setrlimit, Resource};
+///
+/// let soft_limit = 512;
+/// let hard_limit = 1024;
+/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
+/// ```
+///
+/// # References
+///
+/// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
+///
+/// [`Resource`]: enum.Resource.html
+///
+/// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`.
+pub fn setrlimit(
+ resource: Resource,
+ soft_limit: rlim_t,
+ hard_limit: rlim_t,
+) -> Result<()> {
+ let new_rlim = rlimit {
+ rlim_cur: soft_limit,
+ rlim_max: hard_limit,
+ };
+ cfg_if! {
+ if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
+ let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) };
+ }else{
+ let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) };
+ }
+ }
+
+ Errno::result(res).map(drop)
+}
+
+libc_enum! {
+ /// Whose resource usage should be returned by [`getrusage`].
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum UsageWho {
+ /// Resource usage for the current process.
+ RUSAGE_SELF,
+
+ /// Resource usage for all the children that have terminated and been waited for.
+ RUSAGE_CHILDREN,
+
+ #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Resource usage for the calling thread.
+ RUSAGE_THREAD,
+ }
+}
+
+/// Output of `getrusage` with information about resource usage. Some of the fields
+/// may be unused in some platforms, and will be always zeroed out. See their manuals
+/// for details.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct Usage(rusage);
+
+impl AsRef<rusage> for Usage {
+ fn as_ref(&self) -> &rusage {
+ &self.0
+ }
+}
+
+impl AsMut<rusage> for Usage {
+ fn as_mut(&mut self) -> &mut rusage {
+ &mut self.0
+ }
+}
+
+impl Usage {
+ /// Total amount of time spent executing in user mode.
+ pub fn user_time(&self) -> TimeVal {
+ TimeVal::from(self.0.ru_utime)
+ }
+
+ /// Total amount of time spent executing in kernel mode.
+ pub fn system_time(&self) -> TimeVal {
+ TimeVal::from(self.0.ru_stime)
+ }
+
+ /// The resident set size at its peak, in kilobytes.
+ pub fn max_rss(&self) -> c_long {
+ self.0.ru_maxrss
+ }
+
+ /// Integral value expressed in kilobytes times ticks of execution indicating
+ /// the amount of text memory shared with other processes.
+ pub fn shared_integral(&self) -> c_long {
+ self.0.ru_ixrss
+ }
+
+ /// Integral value expressed in kilobytes times ticks of execution indicating
+ /// the amount of unshared memory used by data.
+ pub fn unshared_data_integral(&self) -> c_long {
+ self.0.ru_idrss
+ }
+
+ /// Integral value expressed in kilobytes times ticks of execution indicating
+ /// the amount of unshared memory used for stack space.
+ pub fn unshared_stack_integral(&self) -> c_long {
+ self.0.ru_isrss
+ }
+
+ /// Number of page faults that were served without resorting to I/O, with pages
+ /// that have been allocated previously by the kernel.
+ pub fn minor_page_faults(&self) -> c_long {
+ self.0.ru_minflt
+ }
+
+ /// Number of page faults that were served through I/O (i.e. swap).
+ pub fn major_page_faults(&self) -> c_long {
+ self.0.ru_majflt
+ }
+
+ /// Number of times all of the memory was fully swapped out.
+ pub fn full_swaps(&self) -> c_long {
+ self.0.ru_nswap
+ }
+
+ /// Number of times a read was done from a block device.
+ pub fn block_reads(&self) -> c_long {
+ self.0.ru_inblock
+ }
+
+ /// Number of times a write was done to a block device.
+ pub fn block_writes(&self) -> c_long {
+ self.0.ru_oublock
+ }
+
+ /// Number of IPC messages sent.
+ pub fn ipc_sends(&self) -> c_long {
+ self.0.ru_msgsnd
+ }
+
+ /// Number of IPC messages received.
+ pub fn ipc_receives(&self) -> c_long {
+ self.0.ru_msgrcv
+ }
+
+ /// Number of signals received.
+ pub fn signals(&self) -> c_long {
+ self.0.ru_nsignals
+ }
+
+ /// Number of times a context switch was voluntarily invoked.
+ pub fn voluntary_context_switches(&self) -> c_long {
+ self.0.ru_nvcsw
+ }
+
+ /// Number of times a context switch was imposed by the kernel (usually due to
+ /// time slice expiring or preemption by a higher priority process).
+ pub fn involuntary_context_switches(&self) -> c_long {
+ self.0.ru_nivcsw
+ }
+}
+
+/// Get usage information for a process, its children or the current thread
+///
+/// Real time information can be obtained for either the current process or (in some
+/// systems) thread, but information about children processes is only provided for
+/// those that have terminated and been waited for (see [`super::wait::wait`]).
+///
+/// Some information may be missing depending on the platform, and the way information
+/// is provided for children may also vary. Check the manuals for details.
+///
+/// # References
+///
+/// * [getrusage(2)](https://pubs.opengroup.org/onlinepubs/009696699/functions/getrusage.html)
+/// * [Linux](https://man7.org/linux/man-pages/man2/getrusage.2.html)
+/// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=getrusage)
+/// * [NetBSD](https://man.netbsd.org/getrusage.2)
+/// * [MacOS](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrusage.2.html)
+///
+/// [`UsageWho`]: enum.UsageWho.html
+///
+/// Note: `getrusage` provides a safe wrapper to libc's [`libc::getrusage`].
+pub fn getrusage(who: UsageWho) -> Result<Usage> {
+ unsafe {
+ let mut rusage = mem::MaybeUninit::<rusage>::uninit();
+ let res = libc::getrusage(who as c_int, rusage.as_mut_ptr());
+ Errno::result(res).map(|_| Usage(rusage.assume_init()))
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::{getrusage, UsageWho};
+
+ #[test]
+ pub fn test_self_cpu_time() {
+ // Make sure some CPU time is used.
+ let mut numbers: Vec<i32> = (1..1_000_000).collect();
+ numbers.iter_mut().for_each(|item| *item *= 2);
+
+ // FIXME: this is here to help ensure the compiler does not optimize the whole
+ // thing away. Replace the assert with test::black_box once stabilized.
+ assert_eq!(numbers[100..200].iter().sum::<i32>(), 30_100);
+
+ let usage = getrusage(UsageWho::RUSAGE_SELF)
+ .expect("Failed to call getrusage for SELF");
+ let rusage = usage.as_ref();
+
+ let user = usage.user_time();
+ assert!(user.tv_sec() > 0 || user.tv_usec() > 0);
+ assert_eq!(user.tv_sec(), rusage.ru_utime.tv_sec);
+ assert_eq!(user.tv_usec(), rusage.ru_utime.tv_usec);
+ }
+}
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..0e2193b130
--- /dev/null
+++ b/third_party/rust/nix/src/sys/select.rs
@@ -0,0 +1,554 @@
+//! Portably monitor a group of file descriptors for readiness.
+use crate::errno::Errno;
+use crate::sys::time::{TimeSpec, TimeVal};
+use crate::Result;
+use libc::{self, c_int};
+use std::convert::TryFrom;
+use std::iter::FusedIterator;
+use std::mem;
+use std::ops::Range;
+use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
+use std::ptr::{null, null_mut};
+
+pub use libc::FD_SETSIZE;
+
+/// Contains a set of file descriptors used by [`select`]
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct FdSet<'fd> {
+ set: libc::fd_set,
+ _fd: std::marker::PhantomData<BorrowedFd<'fd>>,
+}
+
+fn assert_fd_valid(fd: RawFd) {
+ assert!(
+ usize::try_from(fd).map_or(false, |fd| fd < FD_SETSIZE),
+ "fd must be in the range 0..FD_SETSIZE",
+ );
+}
+
+impl<'fd> FdSet<'fd> {
+ /// Create an empty `FdSet`
+ pub fn new() -> FdSet<'fd> {
+ let mut fdset = mem::MaybeUninit::uninit();
+ unsafe {
+ libc::FD_ZERO(fdset.as_mut_ptr());
+ Self {
+ set: fdset.assume_init(),
+ _fd: std::marker::PhantomData,
+ }
+ }
+ }
+
+ /// Add a file descriptor to an `FdSet`
+ pub fn insert<Fd: AsFd>(&mut self, fd: &'fd Fd) {
+ assert_fd_valid(fd.as_fd().as_raw_fd());
+ unsafe { libc::FD_SET(fd.as_fd().as_raw_fd(), &mut self.set) };
+ }
+
+ /// Remove a file descriptor from an `FdSet`
+ pub fn remove<Fd: AsFd>(&mut self, fd: &'fd Fd) {
+ assert_fd_valid(fd.as_fd().as_raw_fd());
+ unsafe { libc::FD_CLR(fd.as_fd().as_raw_fd(), &mut self.set) };
+ }
+
+ /// Test an `FdSet` for the presence of a certain file descriptor.
+ pub fn contains<Fd: AsFd>(&self, fd: &'fd Fd) -> bool {
+ assert_fd_valid(fd.as_fd().as_raw_fd());
+ unsafe { libc::FD_ISSET(fd.as_fd().as_raw_fd(), &self.set) }
+ }
+
+ /// Remove all file descriptors from this `FdSet`.
+ pub fn clear(&mut self) {
+ unsafe { libc::FD_ZERO(&mut self.set) };
+ }
+
+ /// Finds the highest file descriptor in the set.
+ ///
+ /// Returns `None` if the set is empty.
+ ///
+ /// This can be used to calculate the `nfds` parameter of the [`select`] function.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use std::os::unix::io::{AsRawFd, BorrowedFd};
+ /// # use nix::sys::select::FdSet;
+ /// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
+ /// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
+ /// let mut set = FdSet::new();
+ /// set.insert(&fd_four);
+ /// set.insert(&fd_nine);
+ /// assert_eq!(set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()), Some(9));
+ /// ```
+ ///
+ /// [`select`]: fn.select.html
+ pub fn highest(&self) -> Option<BorrowedFd<'_>> {
+ self.fds(None).next_back()
+ }
+
+ /// Returns an iterator over the file descriptors in the set.
+ ///
+ /// For performance, it takes an optional higher bound: the iterator will
+ /// not return any elements of the set greater than the given file
+ /// descriptor.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use nix::sys::select::FdSet;
+ /// # use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
+ /// let mut set = FdSet::new();
+ /// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
+ /// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
+ /// set.insert(&fd_four);
+ /// set.insert(&fd_nine);
+ /// let fds: Vec<RawFd> = set.fds(None).map(|borrowed_fd|borrowed_fd.as_raw_fd()).collect();
+ /// assert_eq!(fds, vec![4, 9]);
+ /// ```
+ #[inline]
+ pub fn fds(&self, highest: Option<RawFd>) -> Fds {
+ Fds {
+ set: self,
+ range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE),
+ }
+ }
+}
+
+impl<'fd> Default for FdSet<'fd> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+/// Iterator over `FdSet`.
+#[derive(Debug)]
+pub struct Fds<'a, 'fd> {
+ set: &'a FdSet<'fd>,
+ range: Range<usize>,
+}
+
+impl<'a, 'fd> Iterator for Fds<'a, 'fd> {
+ type Item = BorrowedFd<'fd>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ for i in &mut self.range {
+ let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
+ if self.set.contains(&borrowed_i) {
+ return Some(borrowed_i);
+ }
+ }
+ None
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let (_, upper) = self.range.size_hint();
+ (0, upper)
+ }
+}
+
+impl<'a, 'fd> DoubleEndedIterator for Fds<'a, 'fd> {
+ #[inline]
+ fn next_back(&mut self) -> Option<BorrowedFd<'fd>> {
+ while let Some(i) = self.range.next_back() {
+ let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
+ if self.set.contains(&borrowed_i) {
+ return Some(borrowed_i);
+ }
+ }
+ None
+ }
+}
+
+impl<'a, 'fd> FusedIterator for Fds<'a, 'fd> {}
+
+/// Monitors file descriptors for readiness
+///
+/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
+/// file descriptors that are ready for the given operation are set.
+///
+/// When this function returns, `timeout` has an implementation-defined value.
+///
+/// # Parameters
+///
+/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
+/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
+/// to the maximum of that.
+/// * `readfds`: File descriptors to check for being ready to read.
+/// * `writefds`: File descriptors to check for being ready to write.
+/// * `errorfds`: File descriptors to check for pending error conditions.
+/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
+/// indefinitely).
+///
+/// # References
+///
+/// [select(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
+///
+/// [`FdSet::highest`]: struct.FdSet.html#method.highest
+pub fn select<'a, 'fd, N, R, W, E, T>(
+ nfds: N,
+ readfds: R,
+ writefds: W,
+ errorfds: E,
+ timeout: T,
+) -> Result<c_int>
+where
+ 'fd: 'a,
+ N: Into<Option<c_int>>,
+ R: Into<Option<&'a mut FdSet<'fd>>>,
+ W: Into<Option<&'a mut FdSet<'fd>>>,
+ E: Into<Option<&'a mut FdSet<'fd>>>,
+ T: Into<Option<&'a mut TimeVal>>,
+{
+ let mut readfds = readfds.into();
+ let mut writefds = writefds.into();
+ let mut errorfds = errorfds.into();
+ let timeout = timeout.into();
+
+ let nfds = nfds.into().unwrap_or_else(|| {
+ readfds
+ .iter_mut()
+ .chain(writefds.iter_mut())
+ .chain(errorfds.iter_mut())
+ .map(|set| {
+ set.highest()
+ .map(|borrowed_fd| borrowed_fd.as_raw_fd())
+ .unwrap_or(-1)
+ })
+ .max()
+ .unwrap_or(-1)
+ + 1
+ });
+
+ let readfds = readfds
+ .map(|set| set as *mut _ as *mut libc::fd_set)
+ .unwrap_or(null_mut());
+ let writefds = writefds
+ .map(|set| set as *mut _ as *mut libc::fd_set)
+ .unwrap_or(null_mut());
+ let errorfds = errorfds
+ .map(|set| set as *mut _ as *mut libc::fd_set)
+ .unwrap_or(null_mut());
+ let timeout = timeout
+ .map(|tv| tv as *mut _ as *mut libc::timeval)
+ .unwrap_or(null_mut());
+
+ let res =
+ unsafe { libc::select(nfds, readfds, writefds, errorfds, timeout) };
+
+ Errno::result(res)
+}
+
+feature! {
+#![feature = "signal"]
+
+use crate::sys::signal::SigSet;
+
+/// Monitors file descriptors for readiness with an altered signal mask.
+///
+/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
+/// file descriptors that are ready for the given operation are set.
+///
+/// When this function returns, the original signal mask is restored.
+///
+/// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
+///
+/// # Parameters
+///
+/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
+/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
+/// to the maximum of that.
+/// * `readfds`: File descriptors to check for read readiness
+/// * `writefds`: File descriptors to check for write readiness
+/// * `errorfds`: File descriptors to check for pending error conditions.
+/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
+/// indefinitely).
+/// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
+/// ready (`None` to set no alternative signal mask).
+///
+/// # References
+///
+/// [pselect(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
+///
+/// [The new pselect() system call](https://lwn.net/Articles/176911/)
+///
+/// [`FdSet::highest`]: struct.FdSet.html#method.highest
+pub fn pselect<'a, 'fd, N, R, W, E, T, S>(nfds: N,
+ readfds: R,
+ writefds: W,
+ errorfds: E,
+ timeout: T,
+ sigmask: S) -> Result<c_int>
+where
+ 'fd: 'a,
+ N: Into<Option<c_int>>,
+ R: Into<Option<&'a mut FdSet<'fd>>>,
+ W: Into<Option<&'a mut FdSet<'fd>>>,
+ E: Into<Option<&'a mut FdSet<'fd>>>,
+ T: Into<Option<&'a TimeSpec>>,
+ S: Into<Option<&'a SigSet>>,
+{
+ let mut readfds = readfds.into();
+ let mut writefds = writefds.into();
+ let mut errorfds = errorfds.into();
+ let sigmask = sigmask.into();
+ let timeout = timeout.into();
+
+ let nfds = nfds.into().unwrap_or_else(|| {
+ readfds.iter_mut()
+ .chain(writefds.iter_mut())
+ .chain(errorfds.iter_mut())
+ .map(|set| set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()).unwrap_or(-1))
+ .max()
+ .unwrap_or(-1) + 1
+ });
+
+ let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
+ let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
+ let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
+ let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
+ let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
+
+ let res = unsafe {
+ libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
+ };
+
+ Errno::result(res)
+}
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::sys::time::{TimeVal, TimeValLike};
+ use crate::unistd::{close, pipe, write};
+ use std::os::unix::io::{FromRawFd, OwnedFd, RawFd};
+
+ #[test]
+ fn fdset_insert() {
+ let mut fd_set = FdSet::new();
+
+ for i in 0..FD_SETSIZE {
+ let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
+ assert!(!fd_set.contains(&borrowed_i));
+ }
+
+ let fd_seven = unsafe { BorrowedFd::borrow_raw(7) };
+ fd_set.insert(&fd_seven);
+
+ assert!(fd_set.contains(&fd_seven));
+ }
+
+ #[test]
+ fn fdset_remove() {
+ let mut fd_set = FdSet::new();
+
+ for i in 0..FD_SETSIZE {
+ let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
+ assert!(!fd_set.contains(&borrowed_i));
+ }
+
+ let fd_seven = unsafe { BorrowedFd::borrow_raw(7) };
+ fd_set.insert(&fd_seven);
+ fd_set.remove(&fd_seven);
+
+ for i in 0..FD_SETSIZE {
+ let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
+ assert!(!fd_set.contains(&borrowed_i));
+ }
+ }
+
+ #[test]
+ #[allow(non_snake_case)]
+ fn fdset_clear() {
+ let mut fd_set = FdSet::new();
+ let fd_one = unsafe { BorrowedFd::borrow_raw(1) };
+ let fd_FD_SETSIZE_devided_by_two =
+ unsafe { BorrowedFd::borrow_raw((FD_SETSIZE / 2) as RawFd) };
+ let fd_FD_SETSIZE_minus_one =
+ unsafe { BorrowedFd::borrow_raw((FD_SETSIZE - 1) as RawFd) };
+ fd_set.insert(&fd_one);
+ fd_set.insert(&fd_FD_SETSIZE_devided_by_two);
+ fd_set.insert(&fd_FD_SETSIZE_minus_one);
+
+ fd_set.clear();
+
+ for i in 0..FD_SETSIZE {
+ let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
+ assert!(!fd_set.contains(&borrowed_i));
+ }
+ }
+
+ #[test]
+ fn fdset_highest() {
+ let mut set = FdSet::new();
+ assert_eq!(
+ set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()),
+ None
+ );
+ let fd_zero = unsafe { BorrowedFd::borrow_raw(0) };
+ let fd_ninety = unsafe { BorrowedFd::borrow_raw(90) };
+ set.insert(&fd_zero);
+ assert_eq!(
+ set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()),
+ Some(0)
+ );
+ set.insert(&fd_ninety);
+ assert_eq!(
+ set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()),
+ Some(90)
+ );
+ set.remove(&fd_zero);
+ assert_eq!(
+ set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()),
+ Some(90)
+ );
+ set.remove(&fd_ninety);
+ assert_eq!(
+ set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()),
+ None
+ );
+
+ let fd_four = unsafe { BorrowedFd::borrow_raw(4) };
+ let fd_five = unsafe { BorrowedFd::borrow_raw(5) };
+ let fd_seven = unsafe { BorrowedFd::borrow_raw(7) };
+ set.insert(&fd_four);
+ set.insert(&fd_five);
+ set.insert(&fd_seven);
+ assert_eq!(
+ set.highest().map(|borrowed_fd| borrowed_fd.as_raw_fd()),
+ Some(7)
+ );
+ }
+
+ #[test]
+ fn fdset_fds() {
+ let mut set = FdSet::new();
+ let fd_zero = unsafe { BorrowedFd::borrow_raw(0) };
+ let fd_ninety = unsafe { BorrowedFd::borrow_raw(90) };
+ assert_eq!(
+ set.fds(None)
+ .map(|borrowed_fd| borrowed_fd.as_raw_fd())
+ .collect::<Vec<_>>(),
+ vec![]
+ );
+ set.insert(&fd_zero);
+ assert_eq!(
+ set.fds(None)
+ .map(|borrowed_fd| borrowed_fd.as_raw_fd())
+ .collect::<Vec<_>>(),
+ vec![0]
+ );
+ set.insert(&fd_ninety);
+ assert_eq!(
+ set.fds(None)
+ .map(|borrowed_fd| borrowed_fd.as_raw_fd())
+ .collect::<Vec<_>>(),
+ vec![0, 90]
+ );
+
+ // highest limit
+ assert_eq!(
+ set.fds(Some(89))
+ .map(|borrowed_fd| borrowed_fd.as_raw_fd())
+ .collect::<Vec<_>>(),
+ vec![0]
+ );
+ assert_eq!(
+ set.fds(Some(90))
+ .map(|borrowed_fd| borrowed_fd.as_raw_fd())
+ .collect::<Vec<_>>(),
+ vec![0, 90]
+ );
+ }
+
+ #[test]
+ fn test_select() {
+ let (r1, w1) = pipe().unwrap();
+ let r1 = unsafe { OwnedFd::from_raw_fd(r1) };
+ let w1 = unsafe { OwnedFd::from_raw_fd(w1) };
+ let (r2, _w2) = pipe().unwrap();
+ let r2 = unsafe { OwnedFd::from_raw_fd(r2) };
+
+ write(w1.as_raw_fd(), b"hi!").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));
+ close(_w2).unwrap();
+ }
+
+ #[test]
+ fn test_select_nfds() {
+ let (r1, w1) = pipe().unwrap();
+ let (r2, _w2) = pipe().unwrap();
+ let r1 = unsafe { OwnedFd::from_raw_fd(r1) };
+ let w1 = unsafe { OwnedFd::from_raw_fd(w1) };
+ let r2 = unsafe { OwnedFd::from_raw_fd(r2) };
+
+ write(w1.as_raw_fd(), b"hi!").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()
+ .map(|borrowed_fd| borrowed_fd.as_raw_fd())
+ .unwrap()
+ + 1
+ ),
+ &mut fd_set,
+ None,
+ None,
+ &mut timeout
+ )
+ .unwrap()
+ );
+ }
+ assert!(fd_set.contains(&r1));
+ assert!(!fd_set.contains(&r2));
+ close(_w2).unwrap();
+ }
+
+ #[test]
+ fn test_select_nfds2() {
+ let (r1, w1) = pipe().unwrap();
+ write(w1, b"hi!").unwrap();
+ let (r2, _w2) = pipe().unwrap();
+ let r1 = unsafe { OwnedFd::from_raw_fd(r1) };
+ let r2 = unsafe { OwnedFd::from_raw_fd(r2) };
+ 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.as_raw_fd(), r2.as_raw_fd()) + 1,
+ &mut fd_set,
+ None,
+ None,
+ &mut timeout
+ )
+ .unwrap()
+ );
+ assert!(fd_set.contains(&r1));
+ assert!(!fd_set.contains(&r2));
+ close(_w2).unwrap();
+ }
+}
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..9f3c333f97
--- /dev/null
+++ b/third_party/rust/nix/src/sys/sendfile.rs
@@ -0,0 +1,291 @@
+//! Send data from a file to a socket, bypassing userland.
+
+use cfg_if::cfg_if;
+use std::os::unix::io::{AsFd, AsRawFd};
+use std::ptr;
+
+use libc::{self, off_t};
+
+use crate::errno::Errno;
+use crate::Result;
+
+/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
+///
+/// Returns a `Result` with the number of bytes written.
+///
+/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will
+/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified
+/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to
+/// the byte after the last byte copied.
+///
+/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
+///
+/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn sendfile<F1: AsFd, F2: AsFd>(
+ out_fd: F1,
+ in_fd: F2,
+ offset: Option<&mut off_t>,
+ count: usize,
+) -> Result<usize> {
+ let offset = offset
+ .map(|offset| offset as *mut _)
+ .unwrap_or(ptr::null_mut());
+ let ret = unsafe {
+ libc::sendfile(
+ out_fd.as_fd().as_raw_fd(),
+ in_fd.as_fd().as_raw_fd(),
+ offset,
+ count,
+ )
+ };
+ Errno::result(ret).map(|r| r as usize)
+}
+
+/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
+///
+/// Returns a `Result` with the number of bytes written.
+///
+/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will
+/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified
+/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to
+/// the byte after the last byte copied.
+///
+/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
+///
+/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
+#[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn sendfile64<F1: AsFd, F2: AsFd>(
+ out_fd: F1,
+ in_fd: F2,
+ offset: Option<&mut libc::off64_t>,
+ count: usize,
+) -> Result<usize> {
+ let offset = offset
+ .map(|offset| offset as *mut _)
+ .unwrap_or(ptr::null_mut());
+ let ret = unsafe {
+ libc::sendfile64(
+ out_fd.as_fd().as_raw_fd(),
+ in_fd.as_fd().as_raw_fd(),
+ offset,
+ count,
+ )
+ };
+ Errno::result(ret).map(|r| r as usize)
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos"))] {
+ use std::io::IoSlice;
+
+ #[derive(Clone, Debug)]
+ struct SendfileHeaderTrailer<'a>(
+ libc::sf_hdtr,
+ Option<Vec<IoSlice<'a>>>,
+ Option<Vec<IoSlice<'a>>>,
+ );
+
+ impl<'a> SendfileHeaderTrailer<'a> {
+ fn new(
+ headers: Option<&'a [&'a [u8]]>,
+ trailers: Option<&'a [&'a [u8]]>
+ ) -> SendfileHeaderTrailer<'a> {
+ let header_iovecs: Option<Vec<IoSlice<'_>>> =
+ headers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
+ let trailer_iovecs: Option<Vec<IoSlice<'_>>> =
+ trailers.map(|s| s.iter().map(|b| IoSlice::new(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)
+ #[allow(clippy::too_many_arguments)]
+ pub fn sendfile<F1: AsFd, F2: AsFd>(
+ in_fd: F1,
+ out_sock: F2,
+ offset: off_t,
+ count: Option<usize>,
+ headers: Option<&[&[u8]]>,
+ trailers: Option<&[&[u8]]>,
+ flags: SfFlags,
+ readahead: u16
+ ) -> (Result<()>, off_t) {
+ // Readahead goes in upper 16 bits
+ // Flags goes in lower 16 bits
+ // see `man 2 sendfile`
+ let ra32 = u32::from(readahead);
+ let flags: u32 = (ra32 << 16) | (flags.bits() as u32);
+ let mut bytes_sent: off_t = 0;
+ let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
+ let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
+ let return_code = unsafe {
+ libc::sendfile(in_fd.as_fd().as_raw_fd(),
+ out_sock.as_fd().as_raw_fd(),
+ offset,
+ count.unwrap_or(0),
+ hdtr_ptr as *mut libc::sf_hdtr,
+ &mut bytes_sent as *mut off_t,
+ flags as c_int)
+ };
+ (Errno::result(return_code).and(Ok(())), bytes_sent)
+ }
+ } else if #[cfg(target_os = "dragonfly")] {
+ /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`.
+ ///
+ /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
+ /// an error occurs.
+ ///
+ /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket.
+ ///
+ /// If `offset` falls past the end of the file, the function returns success and zero bytes
+ /// written.
+ ///
+ /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
+ /// file (EOF).
+ ///
+ /// `headers` and `trailers` specify optional slices of byte slices to be sent before and
+ /// after the data read from `in_fd`, respectively. The length of headers and trailers sent
+ /// is included in the returned count of bytes written. The values of `offset` and `count`
+ /// do not apply to headers or trailers.
+ ///
+ /// For more information, see
+ /// [the sendfile(2) man page.](https://leaf.dragonflybsd.org/cgi/web-man?command=sendfile&section=2)
+ pub fn sendfile<F1: AsFd, F2: AsFd>(
+ in_fd: F1,
+ out_sock: F2,
+ offset: off_t,
+ count: Option<usize>,
+ headers: Option<&[&[u8]]>,
+ trailers: Option<&[&[u8]]>,
+ ) -> (Result<()>, off_t) {
+ let mut bytes_sent: off_t = 0;
+ let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
+ let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
+ let return_code = unsafe {
+ libc::sendfile(in_fd.as_fd().as_raw_fd(),
+ out_sock.as_fd().as_raw_fd(),
+ offset,
+ count.unwrap_or(0),
+ hdtr_ptr as *mut libc::sf_hdtr,
+ &mut bytes_sent as *mut off_t,
+ 0)
+ };
+ (Errno::result(return_code).and(Ok(())), bytes_sent)
+ }
+ } else if #[cfg(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<F1: AsFd, F2: AsFd>(
+ in_fd: F1,
+ out_sock: F2,
+ offset: off_t,
+ count: Option<off_t>,
+ headers: Option<&[&[u8]]>,
+ trailers: Option<&[&[u8]]>
+ ) -> (Result<()>, off_t) {
+ let mut len = count.unwrap_or(0);
+ let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
+ let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
+ let return_code = unsafe {
+ libc::sendfile(in_fd.as_fd().as_raw_fd(),
+ out_sock.as_fd().as_raw_fd(),
+ offset,
+ &mut len as *mut off_t,
+ hdtr_ptr as *mut libc::sf_hdtr,
+ 0)
+ };
+ (Errno::result(return_code).and(Ok(())), len)
+ }
+ }
+}
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..c946e4a0b1
--- /dev/null
+++ b/third_party/rust/nix/src/sys/signal.rs
@@ -0,0 +1,1557 @@
+// Portions of this file are Copyright 2014 The Rust Project Developers.
+// See https://www.rust-lang.org/policies/licenses.
+
+//! Operating system signals.
+
+use crate::errno::Errno;
+use crate::{Error, Result};
+use cfg_if::cfg_if;
+use std::fmt;
+use std::mem;
+#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+use std::os::unix::io::RawFd;
+use std::ptr;
+use std::str::FromStr;
+
+#[cfg(not(any(
+ target_os = "fuchsia",
+ target_os = "openbsd",
+ target_os = "redox"
+)))]
+#[cfg(any(feature = "aio", feature = "signal"))]
+pub use self::sigevent::*;
+
+#[cfg(any(feature = "aio", feature = "process", feature = "signal"))]
+libc_enum! {
+ /// Types of operating system signals
+ // Currently there is only one definition of c_int in libc, as well as only one
+ // type for signal constants.
+ // We would prefer to use the libc::c_int alias in the repr attribute. Unfortunately
+ // this is not (yet) possible.
+ #[repr(i32)]
+ #[non_exhaustive]
+ #[cfg_attr(docsrs, doc(cfg(any(feature = "aio", feature = "signal"))))]
+ pub enum Signal {
+ /// Hangup
+ SIGHUP,
+ /// Interrupt
+ SIGINT,
+ /// Quit
+ SIGQUIT,
+ /// Illegal instruction (not reset when caught)
+ SIGILL,
+ /// Trace trap (not reset when caught)
+ SIGTRAP,
+ /// Abort
+ SIGABRT,
+ /// Bus error
+ SIGBUS,
+ /// Floating point exception
+ SIGFPE,
+ /// Kill (cannot be caught or ignored)
+ SIGKILL,
+ /// User defined signal 1
+ SIGUSR1,
+ /// Segmentation violation
+ SIGSEGV,
+ /// User defined signal 2
+ SIGUSR2,
+ /// Write on a pipe with no one to read it
+ SIGPIPE,
+ /// Alarm clock
+ SIGALRM,
+ /// Software termination signal from kill
+ SIGTERM,
+ /// Stack fault (obsolete)
+ #[cfg(all(any(target_os = "android", target_os = "emscripten",
+ target_os = "fuchsia", target_os = "linux"),
+ not(any(target_arch = "mips", target_arch = "mips64",
+ target_arch = "sparc64"))))]
+ SIGSTKFLT,
+ /// To parent on child stop or exit
+ SIGCHLD,
+ /// Continue a stopped process
+ SIGCONT,
+ /// Sendable stop signal not from tty
+ SIGSTOP,
+ /// Stop signal from tty
+ SIGTSTP,
+ /// To readers pgrp upon background tty read
+ SIGTTIN,
+ /// Like TTIN if (tp->t_local&LTOSTOP)
+ SIGTTOU,
+ /// Urgent condition on IO channel
+ SIGURG,
+ /// Exceeded CPU time limit
+ SIGXCPU,
+ /// Exceeded file size limit
+ SIGXFSZ,
+ /// Virtual time alarm
+ SIGVTALRM,
+ /// Profiling time alarm
+ SIGPROF,
+ /// Window size changes
+ SIGWINCH,
+ /// Input/output possible signal
+ #[cfg(not(target_os = "haiku"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SIGIO,
+ #[cfg(any(target_os = "android", target_os = "emscripten",
+ target_os = "fuchsia", target_os = "linux",
+ target_os = "aix"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Power failure imminent.
+ SIGPWR,
+ /// Bad system call
+ SIGSYS,
+ #[cfg(not(any(target_os = "android", target_os = "emscripten",
+ target_os = "fuchsia", target_os = "linux",
+ target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Emulator trap
+ SIGEMT,
+ #[cfg(not(any(target_os = "android", target_os = "emscripten",
+ target_os = "fuchsia", target_os = "linux",
+ target_os = "redox", target_os = "haiku",
+ target_os = "aix")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Information request
+ SIGINFO,
+ }
+ impl TryFrom<i32>
+}
+
+#[cfg(feature = "signal")]
+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 = "fuchsia",
+ 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,
+ #[cfg(not(target_os = "haiku"))]
+ "SIGIO" => Signal::SIGIO,
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ "SIGPWR" => Signal::SIGPWR,
+ "SIGSYS" => Signal::SIGSYS,
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "haiku"
+ )))]
+ "SIGEMT" => Signal::SIGEMT,
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "aix",
+ target_os = "haiku"
+ )))]
+ "SIGINFO" => Signal::SIGINFO,
+ _ => return Err(Errno::EINVAL),
+ })
+ }
+}
+
+#[cfg(feature = "signal")]
+impl Signal {
+ /// Returns name of signal.
+ ///
+ /// This function is equivalent to `<Signal as AsRef<str>>::as_ref()`,
+ /// with difference that returned string is `'static`
+ /// and not bound to `self`'s lifetime.
+ pub const fn as_str(self) -> &'static 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 = "fuchsia",
+ 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",
+ #[cfg(not(target_os = "haiku"))]
+ Signal::SIGIO => "SIGIO",
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "aix",
+ target_os = "linux"
+ ))]
+ Signal::SIGPWR => "SIGPWR",
+ Signal::SIGSYS => "SIGSYS",
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "haiku"
+ )))]
+ Signal::SIGEMT => "SIGEMT",
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "aix",
+ target_os = "haiku"
+ )))]
+ Signal::SIGINFO => "SIGINFO",
+ }
+ }
+}
+
+#[cfg(feature = "signal")]
+impl AsRef<str> for Signal {
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+#[cfg(feature = "signal")]
+impl fmt::Display for Signal {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(self.as_ref())
+ }
+}
+
+#[cfg(feature = "signal")]
+pub use self::Signal::*;
+
+#[cfg(target_os = "redox")]
+#[cfg(feature = "signal")]
+const SIGNALS: [Signal; 29] = [
+ 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,
+];
+#[cfg(target_os = "haiku")]
+#[cfg(feature = "signal")]
+const SIGNALS: [Signal; 28] = [
+ 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, SIGSYS,
+];
+#[cfg(all(
+ any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia"
+ ),
+ not(any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "sparc64"
+ ))
+))]
+#[cfg(feature = "signal")]
+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",
+ target_os = "fuchsia"
+ ),
+ any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64")
+))]
+#[cfg(feature = "signal")]
+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(target_os = "aix")]
+#[cfg(feature = "signal")]
+const SIGNALS: [Signal; 30] = [
+ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGEMT, SIGFPE, SIGKILL, SIGSEGV,
+ SIGSYS, SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2, SIGPWR, SIGWINCH,
+ SIGURG, SIGPOLL, SIGIO, SIGSTOP, SIGTSTP, SIGCONT, SIGTTIN, SIGTTOU,
+ SIGVTALRM, SIGPROF, SIGXCPU, SIGXFSZ, SIGTRAP,
+];
+#[cfg(not(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "emscripten",
+ target_os = "aix",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+#[cfg(feature = "signal")]
+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,
+];
+
+feature! {
+#![feature = "signal"]
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+/// Iterate through all signals defined by this operating system
+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 {
+ /// Iterate through all signals defined by this OS
+ pub const fn iterator() -> SignalIterator {
+ SignalIterator{next: 0}
+ }
+}
+
+/// Alias for [`SIGABRT`]
+pub const SIGIOT : Signal = SIGABRT;
+/// Alias for [`SIGIO`]
+#[cfg(not(target_os = "haiku"))]
+pub const SIGPOLL : Signal = SIGIO;
+/// Alias for [`SIGSYS`]
+pub const SIGUNUSED : Signal = SIGSYS;
+
+cfg_if! {
+ if #[cfg(target_os = "redox")] {
+ type SaFlags_t = libc::c_ulong;
+ } else if #[cfg(target_env = "uclibc")] {
+ type SaFlags_t = libc::c_ulong;
+ } else {
+ type SaFlags_t = libc::c_int;
+ }
+}
+}
+
+#[cfg(feature = "signal")]
+libc_bitflags! {
+ /// Controls the behavior of a [`SigAction`]
+ #[cfg_attr(docsrs, doc(cfg(feature = "signal")))]
+ pub struct SaFlags: SaFlags_t {
+ /// When catching a [`Signal::SIGCHLD`] signal, the signal will be
+ /// generated only when a child process exits, not when a child process
+ /// stops.
+ SA_NOCLDSTOP;
+ /// When catching a [`Signal::SIGCHLD`] signal, the system will not
+ /// create zombie processes when children of the calling process exit.
+ SA_NOCLDWAIT;
+ /// Further occurrences of the delivered signal are not masked during
+ /// the execution of the handler.
+ SA_NODEFER;
+ /// The system will deliver the signal to the process on a signal stack,
+ /// specified by each thread with sigaltstack(2).
+ SA_ONSTACK;
+ /// The handler is reset back to the default at the moment the signal is
+ /// delivered.
+ SA_RESETHAND;
+ /// Requests that certain system calls restart if interrupted by this
+ /// signal. See the man page for complete details.
+ SA_RESTART;
+ /// This flag is controlled internally by Nix.
+ SA_SIGINFO;
+ }
+}
+
+#[cfg(feature = "signal")]
+libc_enum! {
+ /// Specifies how certain functions should manipulate a signal mask
+ #[repr(i32)]
+ #[non_exhaustive]
+ #[cfg_attr(docsrs, doc(cfg(feature = "signal")))]
+ pub enum SigmaskHow {
+ /// The new mask is the union of the current mask and the specified set.
+ SIG_BLOCK,
+ /// The new mask is the intersection of the current mask and the
+ /// complement of the specified set.
+ SIG_UNBLOCK,
+ /// The current mask is replaced by the specified set.
+ SIG_SETMASK,
+ }
+}
+
+feature! {
+#![feature = "signal"]
+
+use crate::unistd::Pid;
+use std::iter::Extend;
+use std::iter::FromIterator;
+use std::iter::IntoIterator;
+
+/// Specifies a set of [`Signal`]s that may be blocked, waited for, etc.
+// We are using `transparent` here to be super sure that `SigSet`
+// is represented exactly like the `sigset_t` struct from C.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct SigSet {
+ sigset: libc::sigset_t
+}
+
+impl SigSet {
+ /// Initialize to include all signals.
+ #[doc(alias("sigfillset"))]
+ pub fn all() -> SigSet {
+ let mut sigset = mem::MaybeUninit::uninit();
+ let _ = unsafe { libc::sigfillset(sigset.as_mut_ptr()) };
+
+ unsafe{ SigSet { sigset: sigset.assume_init() } }
+ }
+
+ /// Initialize to include nothing.
+ #[doc(alias("sigemptyset"))]
+ pub fn empty() -> SigSet {
+ let mut sigset = mem::MaybeUninit::uninit();
+ let _ = unsafe { libc::sigemptyset(sigset.as_mut_ptr()) };
+
+ unsafe{ SigSet { sigset: sigset.assume_init() } }
+ }
+
+ /// Add the specified signal to the set.
+ #[doc(alias("sigaddset"))]
+ pub fn add(&mut self, signal: Signal) {
+ unsafe { libc::sigaddset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) };
+ }
+
+ /// Remove all signals from this set.
+ #[doc(alias("sigemptyset"))]
+ pub fn clear(&mut self) {
+ unsafe { libc::sigemptyset(&mut self.sigset as *mut libc::sigset_t) };
+ }
+
+ /// Remove the specified signal from this set.
+ #[doc(alias("sigdelset"))]
+ pub fn remove(&mut self, signal: Signal) {
+ unsafe { libc::sigdelset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) };
+ }
+
+ /// Return whether this set includes the specified signal.
+ #[doc(alias("sigismember"))]
+ 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"),
+ }
+ }
+
+ /// Returns an iterator that yields the signals contained in this set.
+ pub fn iter(&self) -> SigSetIter<'_> {
+ self.into_iter()
+ }
+
+ /// Gets the currently blocked (masked) set of signals for the calling thread.
+ pub fn thread_get_mask() -> Result<SigSet> {
+ let mut oldmask = mem::MaybeUninit::uninit();
+ do_pthread_sigmask(SigmaskHow::SIG_SETMASK, None, Some(oldmask.as_mut_ptr()))?;
+ Ok(unsafe{ SigSet{sigset: oldmask.assume_init()}})
+ }
+
+ /// 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 = mem::MaybeUninit::uninit();
+ do_pthread_sigmask(how, Some(self), Some(oldmask.as_mut_ptr()))?;
+ Ok(unsafe{ SigSet{sigset: oldmask.assume_init()}})
+ }
+
+ /// Suspends execution of the calling thread until one of the signals in the
+ /// signal mask becomes pending, and returns the accepted signal.
+ #[cfg(not(target_os = "redox"))] // RedoxFS does not yet support sigwait
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn wait(&self) -> Result<Signal> {
+ use std::convert::TryFrom;
+
+ let mut signum = mem::MaybeUninit::uninit();
+ let res = unsafe { libc::sigwait(&self.sigset as *const libc::sigset_t, signum.as_mut_ptr()) };
+
+ Errno::result(res).map(|_| unsafe {
+ Signal::try_from(signum.assume_init()).unwrap()
+ })
+ }
+
+ /// Converts a `libc::sigset_t` object to a [`SigSet`] without checking whether the
+ /// `libc::sigset_t` is already initialized.
+ ///
+ /// # Safety
+ ///
+ /// The `sigset` passed in must be a valid an initialized `libc::sigset_t` by calling either
+ /// [`sigemptyset(3)`](https://man7.org/linux/man-pages/man3/sigemptyset.3p.html) or
+ /// [`sigfillset(3)`](https://man7.org/linux/man-pages/man3/sigfillset.3p.html).
+ /// Otherwise, the results are undefined.
+ pub unsafe fn from_sigset_t_unchecked(sigset: libc::sigset_t) -> SigSet {
+ SigSet { sigset }
+ }
+}
+
+impl AsRef<libc::sigset_t> for SigSet {
+ fn as_ref(&self) -> &libc::sigset_t {
+ &self.sigset
+ }
+}
+
+// TODO: Consider specialization for the case where T is &SigSet and libc::sigorset is available.
+impl Extend<Signal> for SigSet {
+ fn extend<T>(&mut self, iter: T)
+ where T: IntoIterator<Item = Signal> {
+ for signal in iter {
+ self.add(signal);
+ }
+ }
+}
+
+impl FromIterator<Signal> for SigSet {
+ fn from_iter<T>(iter: T) -> Self
+ where T: IntoIterator<Item = Signal> {
+ let mut sigset = SigSet::empty();
+ sigset.extend(iter);
+ sigset
+ }
+}
+
+/// Iterator for a [`SigSet`].
+///
+/// Call [`SigSet::iter`] to create an iterator.
+#[derive(Clone, Debug)]
+pub struct SigSetIter<'a> {
+ sigset: &'a SigSet,
+ inner: SignalIterator,
+}
+
+impl Iterator for SigSetIter<'_> {
+ type Item = Signal;
+ fn next(&mut self) -> Option<Signal> {
+ loop {
+ match self.inner.next() {
+ None => return None,
+ Some(signal) if self.sigset.contains(signal) => return Some(signal),
+ Some(_signal) => continue,
+ }
+ }
+ }
+}
+
+impl<'a> IntoIterator for &'a SigSet {
+ type Item = Signal;
+ type IntoIter = SigSetIter<'a>;
+ fn into_iter(self) -> Self::IntoIter {
+ SigSetIter { sigset: self, inner: Signal::iterator() }
+ }
+}
+
+/// A signal handler.
+#[derive(Clone, Copy, Debug, Eq, Hash, 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`.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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, Debug, Eq, Hash, PartialEq)]
+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 {
+ #[cfg(not(target_os = "aix"))]
+ unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) {
+ (*p).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,
+ #[cfg(not(target_os = "redox"))]
+ SigHandler::SigAction(f) => f as *const extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void) as usize,
+ };
+ }
+
+ #[cfg(target_os = "aix")]
+ unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) {
+ (*p).sa_union.__su_sigaction = match handler {
+ SigHandler::SigDfl => mem::transmute::<usize, extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)>(libc::SIG_DFL),
+ SigHandler::SigIgn => mem::transmute::<usize, extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)>(libc::SIG_IGN),
+ SigHandler::Handler(f) => mem::transmute::<extern "C" fn(i32), extern "C" fn(i32, *mut libc::siginfo_t, *mut libc::c_void)>(f),
+ SigHandler::SigAction(f) => f,
+ };
+ }
+
+ let mut s = mem::MaybeUninit::<libc::sigaction>::uninit();
+ unsafe {
+ let p = s.as_mut_ptr();
+ install_sig(p, handler);
+ (*p).sa_flags = match handler {
+ #[cfg(not(target_os = "redox"))]
+ SigHandler::SigAction(_) => (flags | SaFlags::SA_SIGINFO).bits(),
+ _ => (flags - SaFlags::SA_SIGINFO).bits(),
+ };
+ (*p).sa_mask = mask.sigset;
+
+ SigAction { sigaction: s.assume_init() }
+ }
+ }
+
+ /// 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.
+ #[cfg(not(target_os = "aix"))]
+ pub fn handler(&self) -> SigHandler {
+ match self.sigaction.sa_sigaction {
+ libc::SIG_DFL => SigHandler::SigDfl,
+ libc::SIG_IGN => SigHandler::SigIgn,
+ #[cfg(not(target_os = "redox"))]
+ p if self.flags().contains(SaFlags::SA_SIGINFO) =>
+ SigHandler::SigAction(
+ // Safe for one of two reasons:
+ // * The SigHandler was created by SigHandler::new, in which
+ // case the pointer is correct, or
+ // * The SigHandler was created by signal or sigaction, which
+ // are unsafe functions, so the caller should've somehow
+ // ensured that it is correctly initialized.
+ unsafe{
+ *(&p as *const usize
+ as *const extern fn(_, _, _))
+ }
+ as extern fn(_, _, _)),
+ p => SigHandler::Handler(
+ // Safe for one of two reasons:
+ // * The SigHandler was created by SigHandler::new, in which
+ // case the pointer is correct, or
+ // * The SigHandler was created by signal or sigaction, which
+ // are unsafe functions, so the caller should've somehow
+ // ensured that it is correctly initialized.
+ unsafe{
+ *(&p as *const usize
+ as *const extern fn(libc::c_int))
+ }
+ as extern fn(libc::c_int)),
+ }
+ }
+
+ /// Returns the action's handler.
+ #[cfg(target_os = "aix")]
+ pub fn handler(&self) -> SigHandler {
+ unsafe {
+ match self.sigaction.sa_union.__su_sigaction as usize {
+ libc::SIG_DFL => SigHandler::SigDfl,
+ libc::SIG_IGN => SigHandler::SigIgn,
+ p if self.flags().contains(SaFlags::SA_SIGINFO) =>
+ SigHandler::SigAction(
+ *(&p as *const usize
+ as *const extern fn(_, _, _))
+ as extern fn(_, _, _)),
+ p => SigHandler::Handler(
+ *(&p as *const usize
+ as *const extern fn(libc::c_int))
+ as extern fn(libc::c_int)),
+ }
+ }
+ }
+}
+
+/// 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.
+///
+/// * There is also no guarantee that the old signal handler was installed
+/// correctly. If it was installed by this crate, it will be. But if it was
+/// installed by, for example, C code, then there is no guarantee its function
+/// pointer is valid. In that case, this function effectively dereferences a
+/// raw pointer of unknown provenance.
+pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigAction> {
+ let mut oldact = mem::MaybeUninit::<libc::sigaction>::uninit();
+
+ let res = libc::sigaction(signal as libc::c_int,
+ &sigaction.sigaction as *const libc::sigaction,
+ oldact.as_mut_ptr());
+
+ Errno::result(res).map(|_| SigAction { sigaction: oldact.assume_init() })
+}
+
+/// Signal management (see [signal(3p)](https://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
+/// # use std::convert::TryFrom;
+/// # use std::sync::atomic::{AtomicBool, Ordering};
+/// # use nix::sys::signal::{self, Signal, SigHandler};
+/// static SIGNALED: AtomicBool = AtomicBool::new(false);
+///
+/// extern fn handle_sigint(signal: libc::c_int) {
+/// let signal = Signal::try_from(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(Errno::EOPNOTSUPP)`] 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),
+ #[cfg(not(target_os = "redox"))]
+ SigHandler::SigAction(_) => return Err(Errno::ENOTSUP),
+ };
+ Errno::result(res).map(|oldhandler| {
+ match oldhandler {
+ libc::SIG_DFL => SigHandler::SigDfl,
+ libc::SIG_IGN => SigHandler::SigIgn,
+ p => SigHandler::Handler(
+ *(&p as *const usize
+ as *const extern fn(libc::c_int))
+ as extern fn(libc::c_int)),
+ }
+ })
+}
+
+fn do_pthread_sigmask(how: SigmaskHow,
+ set: Option<&SigSet>,
+ oldset: Option<*mut libc::sigset_t>) -> 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.unwrap_or(ptr::null_mut())
+ )
+ };
+
+ Errno::result(res).map(drop)
+}
+
+/// 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`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html),
+/// or [`sigprocmask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html) man pages.
+pub fn pthread_sigmask(how: SigmaskHow,
+ set: Option<&SigSet>,
+ oldset: Option<&mut SigSet>) -> Result<()>
+{
+ do_pthread_sigmask(how, set, oldset.map(|os| &mut os.sigset as *mut _ ))
+}
+
+/// Examine and change blocked signals.
+///
+/// For more information see the [`sigprocmask` man
+/// pages](https://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)
+}
+
+/// Send a signal to a process
+///
+/// # Arguments
+///
+/// * `pid` - Specifies which processes should receive the signal.
+/// - If positive, specifies an individual process.
+/// - If zero, the signal will be sent to all processes whose group
+/// ID is equal to the process group ID of the sender. This is a
+#[cfg_attr(target_os = "fuchsia", doc = "variant of `killpg`.")]
+#[cfg_attr(not(target_os = "fuchsia"), doc = "variant of [`killpg`].")]
+/// - If `-1` and the process has super-user privileges, the signal
+/// is sent to all processes exclusing system processes.
+/// - If less than `-1`, the signal is sent to all processes whose
+/// process group ID is equal to the absolute value of `pid`.
+/// * `signal` - Signal to send. If `None`, error checking is performed
+/// but no signal is actually sent.
+///
+/// See Also
+/// [`kill(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html)
+pub fn kill<T: Into<Option<Signal>>>(pid: Pid, signal: T) -> Result<()> {
+ let res = unsafe { libc::kill(pid.into(),
+ match signal.into() {
+ Some(s) => s as libc::c_int,
+ None => 0,
+ }) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Send a signal to a process group
+///
+/// # Arguments
+///
+/// * `pgrp` - Process group to signal. If less then or equal 1, the behavior
+/// is platform-specific.
+/// * `signal` - Signal to send. If `None`, `killpg` will only preform error
+/// checking and won't send any signal.
+///
+/// See Also [killpg(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html).
+#[cfg(not(target_os = "fuchsia"))]
+pub fn killpg<T: Into<Option<Signal>>>(pgrp: Pid, signal: T) -> Result<()> {
+ let res = unsafe { libc::killpg(pgrp.into(),
+ match signal.into() {
+ Some(s) => s as libc::c_int,
+ None => 0,
+ }) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Send a signal to the current thread
+///
+/// See Also [raise(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/raise.html)
+pub fn raise(signal: Signal) -> Result<()> {
+ let res = unsafe { libc::raise(signal as libc::c_int) };
+
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![any(feature = "aio", feature = "signal")]
+
+/// Identifies a thread for [`SigevNotify::SigevThreadId`]
+#[cfg(target_os = "freebsd")]
+pub type type_of_thread_id = libc::lwpid_t;
+/// Identifies a thread for [`SigevNotify::SigevThreadId`]
+#[cfg(any(target_env = "gnu", target_env = "uclibc"))]
+pub type type_of_thread_id = libc::pid_t;
+
+/// Specifies the notification method used by a [`SigEvent`]
+// sigval is actually a union of a int and a void*. But it's never really used
+// as a pointer, because neither libc nor the kernel ever dereference it. nix
+// therefore presents it as an intptr_t, which is how kevent uses it.
+#[cfg(not(any(target_os = "fuchsia", target_os = "openbsd", target_os = "redox")))]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum SigevNotify {
+ /// No notification will be delivered
+ SigevNone,
+ /// Notify by delivering a signal to the process.
+ SigevSignal {
+ /// Signal to deliver
+ signal: Signal,
+ /// Will be present in the `si_value` field of the [`libc::siginfo_t`]
+ /// structure of the queued signal.
+ si_value: libc::intptr_t
+ },
+ // Note: SIGEV_THREAD is not implemented, but could be if desired.
+ /// Notify by delivering an event to a kqueue.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SigevKevent {
+ /// File descriptor of the kqueue to notify.
+ kq: RawFd,
+ /// Will be contained in the kevent's `udata` field.
+ udata: libc::intptr_t
+ },
+ /// Notify by delivering an event to a kqueue, with optional event flags set
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[cfg(feature = "event")]
+ SigevKeventFlags {
+ /// File descriptor of the kqueue to notify.
+ kq: RawFd,
+ /// Will be contained in the kevent's `udata` field.
+ udata: libc::intptr_t,
+ /// Flags that will be set on the delivered event. See `kevent(2)`.
+ flags: crate::sys::event::EventFlag
+ },
+ /// Notify by delivering a signal to a thread.
+ #[cfg(any(
+ target_os = "freebsd",
+ target_env = "gnu",
+ target_env = "uclibc",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SigevThreadId {
+ /// Signal to send
+ signal: Signal,
+ /// LWP ID of the thread to notify
+ thread_id: type_of_thread_id,
+ /// Will be present in the `si_value` field of the [`libc::siginfo_t`]
+ /// structure of the queued signal.
+ si_value: libc::intptr_t
+ },
+}
+}
+
+#[cfg(not(any(
+ target_os = "fuchsia",
+ target_os = "openbsd",
+ target_os = "redox"
+)))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+mod sigevent {
+ feature! {
+ #![any(feature = "aio", feature = "signal")]
+
+ use std::mem;
+ use super::SigevNotify;
+
+ #[cfg(target_os = "freebsd")]
+ pub(crate) use ffi::sigevent as libc_sigevent;
+ #[cfg(not(target_os = "freebsd"))]
+ pub(crate) use libc::sigevent as libc_sigevent;
+
+ // For FreeBSD only, we define the C structure here. Because the structure
+ // defined in libc isn't correct. The real sigevent contains union fields,
+ // but libc could not represent those when sigevent was originally added, so
+ // instead libc simply defined the most useful field. Now that Rust can
+ // represent unions, there's a PR to libc to fix it. However, it's stuck
+ // forever due to backwards compatibility concerns. Even though there's a
+ // workaround, libc refuses to merge it. I think it's just too complicated
+ // for them to want to think about right now, because that project is
+ // short-staffed. So we define it here instead, so we won't have to wait on
+ // libc.
+ // https://github.com/rust-lang/libc/pull/2813
+ #[cfg(target_os = "freebsd")]
+ mod ffi {
+ use std::{fmt, hash};
+
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ #[repr(C)]
+ pub struct __c_anonymous_sigev_thread {
+ pub _function: *mut libc::c_void, // Actually a function pointer
+ pub _attribute: *mut libc::pthread_attr_t,
+ }
+ #[derive(Clone, Copy)]
+ // This will never be used on its own, and its parent has a Debug impl,
+ // so it doesn't need one.
+ #[allow(missing_debug_implementations)]
+ #[repr(C)]
+ pub union __c_anonymous_sigev_un {
+ pub _threadid: libc::__lwpid_t,
+ pub _sigev_thread: __c_anonymous_sigev_thread,
+ pub _kevent_flags: libc::c_ushort,
+ __spare__: [libc::c_long; 8],
+ }
+
+ #[derive(Clone, Copy)]
+ #[repr(C)]
+ pub struct sigevent {
+ pub sigev_notify: libc::c_int,
+ pub sigev_signo: libc::c_int,
+ pub sigev_value: libc::sigval,
+ pub _sigev_un: __c_anonymous_sigev_un,
+ }
+
+ impl fmt::Debug for sigevent {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut ds = f.debug_struct("sigevent");
+ ds.field("sigev_notify", &self.sigev_notify)
+ .field("sigev_signo", &self.sigev_signo)
+ .field("sigev_value", &self.sigev_value);
+ // Safe because we check the sigev_notify discriminant
+ unsafe {
+ match self.sigev_notify {
+ libc::SIGEV_KEVENT => {
+ ds.field("sigev_notify_kevent_flags", &self._sigev_un._kevent_flags);
+ }
+ libc::SIGEV_THREAD_ID => {
+ ds.field("sigev_notify_thread_id", &self._sigev_un._threadid);
+ }
+ libc::SIGEV_THREAD => {
+ ds.field("sigev_notify_function", &self._sigev_un._sigev_thread._function);
+ ds.field("sigev_notify_attributes", &self._sigev_un._sigev_thread._attribute);
+ }
+ _ => ()
+ };
+ }
+ ds.finish()
+ }
+ }
+
+ impl PartialEq for sigevent {
+ fn eq(&self, other: &Self) -> bool {
+ let mut equals = self.sigev_notify == other.sigev_notify;
+ equals &= self.sigev_signo == other.sigev_signo;
+ equals &= self.sigev_value == other.sigev_value;
+ // Safe because we check the sigev_notify discriminant
+ unsafe {
+ match self.sigev_notify {
+ libc::SIGEV_KEVENT => {
+ equals &= self._sigev_un._kevent_flags == other._sigev_un._kevent_flags;
+ }
+ libc::SIGEV_THREAD_ID => {
+ equals &= self._sigev_un._threadid == other._sigev_un._threadid;
+ }
+ libc::SIGEV_THREAD => {
+ equals &= self._sigev_un._sigev_thread == other._sigev_un._sigev_thread;
+ }
+ _ => /* The union field is don't care */ ()
+ }
+ }
+ equals
+ }
+ }
+
+ impl Eq for sigevent {}
+
+ impl hash::Hash for sigevent {
+ fn hash<H: hash::Hasher>(&self, s: &mut H) {
+ self.sigev_notify.hash(s);
+ self.sigev_signo.hash(s);
+ self.sigev_value.hash(s);
+ // Safe because we check the sigev_notify discriminant
+ unsafe {
+ match self.sigev_notify {
+ libc::SIGEV_KEVENT => {
+ self._sigev_un._kevent_flags.hash(s);
+ }
+ libc::SIGEV_THREAD_ID => {
+ self._sigev_un._threadid.hash(s);
+ }
+ libc::SIGEV_THREAD => {
+ self._sigev_un._sigev_thread.hash(s);
+ }
+ _ => /* The union field is don't care */ ()
+ }
+ }
+ }
+ }
+ }
+
+ /// Used to request asynchronous notification of the completion of certain
+ /// events, such as POSIX AIO and timers.
+ #[repr(C)]
+ #[derive(Clone, Debug, Eq, Hash, PartialEq)]
+ // It can't be Copy on all platforms.
+ #[allow(missing_copy_implementations)]
+ 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: libc_sigevent = unsafe { mem::zeroed() };
+ match sigev_notify {
+ SigevNotify::SigevNone => {
+ sev.sigev_notify = libc::SIGEV_NONE;
+ },
+ SigevNotify::SigevSignal{signal, si_value} => {
+ sev.sigev_notify = libc::SIGEV_SIGNAL;
+ sev.sigev_signo = signal as libc::c_int;
+ sev.sigev_value.sival_ptr = si_value as *mut libc::c_void
+ },
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ SigevNotify::SigevKevent{kq, udata} => {
+ sev.sigev_notify = libc::SIGEV_KEVENT;
+ sev.sigev_signo = kq;
+ sev.sigev_value.sival_ptr = udata as *mut libc::c_void;
+ },
+ #[cfg(target_os = "freebsd")]
+ #[cfg(feature = "event")]
+ SigevNotify::SigevKeventFlags{kq, udata, flags} => {
+ sev.sigev_notify = libc::SIGEV_KEVENT;
+ sev.sigev_signo = kq;
+ sev.sigev_value.sival_ptr = udata as *mut libc::c_void;
+ sev._sigev_un._kevent_flags = flags.bits();
+ },
+ #[cfg(target_os = "freebsd")]
+ SigevNotify::SigevThreadId{signal, thread_id, si_value} => {
+ sev.sigev_notify = libc::SIGEV_THREAD_ID;
+ sev.sigev_signo = signal as libc::c_int;
+ sev.sigev_value.sival_ptr = si_value as *mut libc::c_void;
+ sev._sigev_un._threadid = thread_id;
+ }
+ #[cfg(any(target_env = "gnu", target_env = "uclibc"))]
+ SigevNotify::SigevThreadId{signal, thread_id, si_value} => {
+ sev.sigev_notify = libc::SIGEV_THREAD_ID;
+ sev.sigev_signo = signal as libc::c_int;
+ sev.sigev_value.sival_ptr = si_value as *mut libc::c_void;
+ sev.sigev_notify_thread_id = thread_id;
+ }
+ }
+ SigEvent{sigevent: sev}
+ }
+
+ /// Return a copy of the inner structure
+ #[cfg(target_os = "freebsd")]
+ pub fn sigevent(&self) -> libc::sigevent {
+ // Safe because they're really the same structure. See
+ // https://github.com/rust-lang/libc/pull/2813
+ unsafe {
+ mem::transmute::<libc_sigevent, libc::sigevent>(self.sigevent)
+ }
+ }
+
+ /// Return a copy of the inner structure
+ #[cfg(not(target_os = "freebsd"))]
+ pub fn sigevent(&self) -> libc::sigevent {
+ self.sigevent
+ }
+
+ /// Returns a mutable pointer to the `sigevent` wrapped by `self`
+ #[cfg(target_os = "freebsd")]
+ pub fn as_mut_ptr(&mut self) -> *mut libc::sigevent {
+ // Safe because they're really the same structure. See
+ // https://github.com/rust-lang/libc/pull/2813
+ &mut self.sigevent as *mut libc_sigevent as *mut libc::sigevent
+ }
+
+ /// Returns a mutable pointer to the `sigevent` wrapped by `self`
+ #[cfg(not(target_os = "freebsd"))]
+ pub fn as_mut_ptr(&mut self) -> *mut libc::sigevent {
+ &mut self.sigevent
+ }
+ }
+
+ impl<'a> From<&'a libc::sigevent> for SigEvent {
+ #[cfg(target_os = "freebsd")]
+ fn from(sigevent: &libc::sigevent) -> Self {
+ // Safe because they're really the same structure. See
+ // https://github.com/rust-lang/libc/pull/2813
+ let sigevent = unsafe {
+ mem::transmute::<libc::sigevent, libc_sigevent>(*sigevent)
+ };
+ SigEvent{ sigevent }
+ }
+ #[cfg(not(target_os = "freebsd"))]
+ fn from(sigevent: &libc::sigevent) -> Self {
+ SigEvent{ sigevent: *sigevent }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ #[cfg(not(target_os = "redox"))]
+ use std::thread;
+
+ #[test]
+ fn test_contains() {
+ let mut mask = SigSet::empty();
+ mask.add(SIGUSR1);
+
+ assert!(mask.contains(SIGUSR1));
+ assert!(!mask.contains(SIGUSR2));
+
+ let all = SigSet::all();
+ assert!(all.contains(SIGUSR1));
+ assert!(all.contains(SIGUSR2));
+ }
+
+ #[test]
+ fn test_clear() {
+ let mut set = SigSet::all();
+ set.clear();
+ for signal in Signal::iterator() {
+ assert!(!set.contains(signal));
+ }
+ }
+
+ #[test]
+ fn test_from_str_round_trips() {
+ for signal in Signal::iterator() {
+ assert_eq!(signal.as_ref().parse::<Signal>().unwrap(), signal);
+ assert_eq!(signal.to_string().parse::<Signal>().unwrap(), signal);
+ }
+ }
+
+ #[test]
+ fn test_from_str_invalid_value() {
+ let errval = Err(Errno::EINVAL);
+ assert_eq!("NOSIGNAL".parse::<Signal>(), errval);
+ assert_eq!("kill".parse::<Signal>(), errval);
+ assert_eq!("9".parse::<Signal>(), errval);
+ }
+
+ #[test]
+ fn test_extend() {
+ let mut one_signal = SigSet::empty();
+ one_signal.add(SIGUSR1);
+
+ let mut two_signals = SigSet::empty();
+ two_signals.add(SIGUSR2);
+ two_signals.extend(&one_signal);
+
+ assert!(two_signals.contains(SIGUSR1));
+ assert!(two_signals.contains(SIGUSR2));
+ }
+
+ #[test]
+ #[cfg(not(target_os = "redox"))]
+ fn test_thread_signal_set_mask() {
+ thread::spawn(|| {
+ let prev_mask = SigSet::thread_get_mask()
+ .expect("Failed to get existing signal mask!");
+
+ let mut test_mask = prev_mask;
+ test_mask.add(SIGUSR1);
+
+ test_mask.thread_set_mask().expect("assertion failed");
+ let new_mask =
+ SigSet::thread_get_mask().expect("Failed to get new mask!");
+
+ assert!(new_mask.contains(SIGUSR1));
+ assert!(!new_mask.contains(SIGUSR2));
+
+ prev_mask
+ .thread_set_mask()
+ .expect("Failed to revert signal mask!");
+ })
+ .join()
+ .unwrap();
+ }
+
+ #[test]
+ #[cfg(not(target_os = "redox"))]
+ fn test_thread_signal_block() {
+ thread::spawn(|| {
+ let mut mask = SigSet::empty();
+ mask.add(SIGUSR1);
+
+ mask.thread_block().expect("assertion failed");
+
+ assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
+ })
+ .join()
+ .unwrap();
+ }
+
+ #[test]
+ #[cfg(not(target_os = "redox"))]
+ fn test_thread_signal_unblock() {
+ thread::spawn(|| {
+ let mut mask = SigSet::empty();
+ mask.add(SIGUSR1);
+
+ mask.thread_unblock().expect("assertion failed");
+
+ assert!(!SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
+ })
+ .join()
+ .unwrap();
+ }
+
+ #[test]
+ #[cfg(not(target_os = "redox"))]
+ fn test_thread_signal_swap() {
+ thread::spawn(|| {
+ let mut mask = SigSet::empty();
+ mask.add(SIGUSR1);
+ mask.thread_block().unwrap();
+
+ assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
+
+ let mut mask2 = SigSet::empty();
+ mask2.add(SIGUSR2);
+
+ let oldmask =
+ mask2.thread_swap_mask(SigmaskHow::SIG_SETMASK).unwrap();
+
+ assert!(oldmask.contains(SIGUSR1));
+ assert!(!oldmask.contains(SIGUSR2));
+
+ assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR2));
+ })
+ .join()
+ .unwrap();
+ }
+
+ #[test]
+ fn test_from_and_into_iterator() {
+ let sigset = SigSet::from_iter(vec![Signal::SIGUSR1, Signal::SIGUSR2]);
+ let signals = sigset.into_iter().collect::<Vec<Signal>>();
+ assert_eq!(signals, [Signal::SIGUSR1, Signal::SIGUSR2]);
+ }
+
+ #[test]
+ #[cfg(not(target_os = "redox"))]
+ fn test_sigaction() {
+ thread::spawn(|| {
+ extern "C" fn test_sigaction_handler(_: libc::c_int) {}
+ extern "C" fn test_sigaction_action(
+ _: libc::c_int,
+ _: *mut libc::siginfo_t,
+ _: *mut libc::c_void,
+ ) {
+ }
+
+ let handler_sig = SigHandler::Handler(test_sigaction_handler);
+
+ let flags =
+ SaFlags::SA_ONSTACK | SaFlags::SA_RESTART | SaFlags::SA_SIGINFO;
+
+ let mut mask = SigSet::empty();
+ mask.add(SIGUSR1);
+
+ let action_sig = SigAction::new(handler_sig, flags, mask);
+
+ assert_eq!(
+ action_sig.flags(),
+ SaFlags::SA_ONSTACK | SaFlags::SA_RESTART
+ );
+ assert_eq!(action_sig.handler(), handler_sig);
+
+ mask = action_sig.mask();
+ assert!(mask.contains(SIGUSR1));
+ assert!(!mask.contains(SIGUSR2));
+
+ let handler_act = SigHandler::SigAction(test_sigaction_action);
+ let action_act = SigAction::new(handler_act, flags, mask);
+ assert_eq!(action_act.handler(), handler_act);
+
+ let action_dfl = SigAction::new(SigHandler::SigDfl, flags, mask);
+ assert_eq!(action_dfl.handler(), SigHandler::SigDfl);
+
+ let action_ign = SigAction::new(SigHandler::SigIgn, flags, mask);
+ assert_eq!(action_ign.handler(), SigHandler::SigIgn);
+ })
+ .join()
+ .unwrap();
+ }
+
+ #[test]
+ #[cfg(not(target_os = "redox"))]
+ fn test_sigwait() {
+ thread::spawn(|| {
+ let mut mask = SigSet::empty();
+ mask.add(SIGUSR1);
+ mask.add(SIGUSR2);
+ mask.thread_block().unwrap();
+
+ raise(SIGUSR1).unwrap();
+ assert_eq!(mask.wait().unwrap(), SIGUSR1);
+ })
+ .join()
+ .unwrap();
+ }
+
+ #[test]
+ fn test_from_sigset_t_unchecked() {
+ let src_set = SigSet::empty();
+ let set = unsafe { SigSet::from_sigset_t_unchecked(src_set.sigset) };
+
+ for signal in Signal::iterator() {
+ assert!(!set.contains(signal));
+ }
+
+ let src_set = SigSet::all();
+ let set = unsafe { SigSet::from_sigset_t_unchecked(src_set.sigset) };
+
+ for signal in Signal::iterator() {
+ assert!(set.contains(signal));
+ }
+ }
+}
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..2b80ea643f
--- /dev/null
+++ b/third_party/rust/nix/src/sys/signalfd.rs
@@ -0,0 +1,175 @@
+//! Interface for the `signalfd` syscall.
+//!
+//! # Signal discarding
+//! When a signal can't be delivered to a process (or thread), it will become a pending signal.
+//! Failure to deliver could happen if the signal is blocked by every thread in the process or if
+//! the signal handler is still handling a previous signal.
+//!
+//! If a signal is sent to a process (or thread) that already has a pending signal of the same
+//! type, it will be discarded. This means that if signals of the same type are received faster than
+//! they are processed, some of those signals will be dropped. Because of this limitation,
+//! `signalfd` in itself cannot be used for reliable communication between processes or threads.
+//!
+//! Once the signal is unblocked, or the signal handler is finished, and a signal is still pending
+//! (ie. not consumed from a signalfd) it will be delivered to the signal handler.
+//!
+//! Please note that signal discarding is not specific to `signalfd`, but also happens with regular
+//! signal handlers.
+use crate::errno::Errno;
+pub use crate::sys::signal::{self, SigSet};
+use crate::Result;
+pub use libc::signalfd_siginfo as siginfo;
+
+use std::mem;
+use std::os::unix::io::{AsRawFd, RawFd, FromRawFd, OwnedFd, AsFd, BorrowedFd};
+
+libc_bitflags! {
+ pub struct SfdFlags: libc::c_int {
+ SFD_NONBLOCK;
+ SFD_CLOEXEC;
+ }
+}
+
+#[deprecated(since = "0.23.0", note = "use mem::size_of::<siginfo>() instead")]
+pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::<siginfo>();
+
+/// Creates a new file descriptor for reading signals.
+///
+/// **Important:** please read the module level documentation about signal discarding before using
+/// this function!
+///
+/// The `mask` parameter specifies the set of signals that can be accepted via this file descriptor.
+///
+/// A signal must be blocked on every thread in a process, otherwise it won't be visible from
+/// signalfd (the default handler will be invoked instead).
+///
+/// See [the signalfd man page for more information](https://man7.org/linux/man-pages/man2/signalfd.2.html)
+#[deprecated(since = "0.27.0", note = "Use SignalFd instead")]
+pub fn signalfd<F: AsFd>(fd: Option<F>, mask: &SigSet, flags: SfdFlags) -> Result<OwnedFd> {
+ _signalfd(fd, mask, flags)
+}
+
+fn _signalfd<F: AsFd>(fd: Option<F>, mask: &SigSet, flags: SfdFlags) -> Result<OwnedFd> {
+ let raw_fd = fd.map_or(-1, |x|x.as_fd().as_raw_fd());
+ unsafe {
+ Errno::result(libc::signalfd(
+ raw_fd,
+ mask.as_ref(),
+ flags.bits(),
+ )).map(|raw_fd|FromRawFd::from_raw_fd(raw_fd))
+ }
+}
+
+/// A helper struct for creating, reading and closing a `signalfd` instance.
+///
+/// **Important:** please read the module level documentation about signal discarding before using
+/// this struct!
+///
+/// # Examples
+///
+/// ```
+/// # use nix::sys::signalfd::*;
+/// // Set the thread to block the SIGUSR1 signal, otherwise the default handler will be used
+/// let mut mask = SigSet::empty();
+/// mask.add(signal::SIGUSR1);
+/// mask.thread_block().unwrap();
+///
+/// // Signals are queued up on the file descriptor
+/// let mut sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
+///
+/// match sfd.read_signal() {
+/// // we caught a signal
+/// Ok(Some(sig)) => (),
+/// // there were no signals waiting (only happens when the SFD_NONBLOCK flag is set,
+/// // otherwise the read_signal call blocks)
+/// Ok(None) => (),
+/// Err(err) => (), // some error happend
+/// }
+/// ```
+#[derive(Debug)]
+pub struct SignalFd(OwnedFd);
+
+impl SignalFd {
+ pub fn new(mask: &SigSet) -> Result<SignalFd> {
+ Self::with_flags(mask, SfdFlags::empty())
+ }
+
+ pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result<SignalFd> {
+ let fd = _signalfd(None::<OwnedFd>, mask, flags)?;
+
+ Ok(SignalFd(fd))
+ }
+
+ pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> {
+ _signalfd(Some(self.0.as_fd()), mask, SfdFlags::empty()).map(drop)
+ }
+
+ pub fn read_signal(&mut self) -> Result<Option<siginfo>> {
+ let mut buffer = mem::MaybeUninit::<siginfo>::uninit();
+
+ let size = mem::size_of_val(&buffer);
+ let res = Errno::result(unsafe {
+ libc::read(self.0.as_raw_fd(), buffer.as_mut_ptr() as *mut libc::c_void, size)
+ })
+ .map(|r| r as usize);
+ match res {
+ Ok(x) if x == size => Ok(Some(unsafe { buffer.assume_init() })),
+ Ok(_) => unreachable!("partial read on signalfd"),
+ Err(Errno::EAGAIN) => Ok(None),
+ Err(error) => Err(error),
+ }
+ }
+}
+
+impl AsFd for SignalFd {
+ fn as_fd(&self) -> BorrowedFd {
+ self.0.as_fd()
+ }
+}
+impl AsRawFd for SignalFd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl 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::*;
+
+ #[test]
+ fn create_signalfd() {
+ let mask = SigSet::empty();
+ SignalFd::new(&mask).unwrap();
+ }
+
+ #[test]
+ fn create_signalfd_with_opts() {
+ let mask = SigSet::empty();
+ SignalFd::with_flags(
+ &mask,
+ SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK,
+ )
+ .unwrap();
+ }
+
+ #[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..1783531d49
--- /dev/null
+++ b/third_party/rust/nix/src/sys/socket/addr.rs
@@ -0,0 +1,2685 @@
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "haiku",
+ target_os = "fuchsia",
+ target_os = "aix",
+))]
+#[cfg(feature = "net")]
+pub use self::datalink::LinkAddr;
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+pub use self::vsock::VsockAddr;
+use super::sa_family_t;
+use crate::errno::Errno;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use crate::sys::socket::addr::alg::AlgAddr;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use crate::sys::socket::addr::netlink::NetlinkAddr;
+#[cfg(all(
+ feature = "ioctl",
+ any(target_os = "ios", target_os = "macos")
+))]
+use crate::sys::socket::addr::sys_control::SysControlAddr;
+use crate::{NixPath, Result};
+use cfg_if::cfg_if;
+use memoffset::offset_of;
+use std::convert::TryInto;
+use std::ffi::OsStr;
+use std::hash::{Hash, Hasher};
+use std::os::unix::ffi::OsStrExt;
+use std::path::Path;
+use std::{fmt, mem, net, ptr, slice};
+
+/// Convert a std::net::Ipv4Addr into the libc form.
+#[cfg(feature = "net")]
+pub(crate) const fn ipv4addr_to_libc(addr: net::Ipv4Addr) -> libc::in_addr {
+ libc::in_addr {
+ s_addr: u32::from_ne_bytes(addr.octets())
+ }
+}
+
+/// Convert a std::net::Ipv6Addr into the libc form.
+#[cfg(feature = "net")]
+pub(crate) const fn ipv6addr_to_libc(addr: &net::Ipv6Addr) -> libc::in6_addr {
+ libc::in6_addr {
+ s6_addr: addr.octets()
+ }
+}
+
+/// These constants specify the protocol family to be used
+/// in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
+///
+/// # References
+///
+/// [address_families(7)](https://man7.org/linux/man-pages/man7/address_families.7.html)
+// Should this be u8?
+#[repr(i32)]
+#[non_exhaustive]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
+pub enum AddressFamily {
+ /// Local communication (see [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html))
+ Unix = libc::AF_UNIX,
+ /// IPv4 Internet protocols (see [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html))
+ Inet = libc::AF_INET,
+ /// IPv6 Internet protocols (see [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html))
+ Inet6 = libc::AF_INET6,
+ /// Kernel user interface device (see [`netlink(7)`](https://man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Netlink = libc::AF_NETLINK,
+ /// Kernel interface for interacting with the routing table
+ #[cfg(not(any(
+ target_os = "redox",
+ target_os = "linux",
+ target_os = "android"
+ )))]
+ Route = libc::PF_ROUTE,
+ /// Low level packet interface (see [`packet(7)`](https://man7.org/linux/man-pages/man7/packet.7.html))
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "illumos",
+ target_os = "fuchsia",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Packet = libc::AF_PACKET,
+ /// KEXT Controls and Notifications
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ System = libc::AF_SYSTEM,
+ /// Amateur radio AX.25 protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ax25 = libc::AF_AX25,
+ /// IPX - Novell protocols
+ #[cfg(not(any(target_os = "aix", target_os = "redox")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ipx = libc::AF_IPX,
+ /// AppleTalk
+ #[cfg(not(target_os = "redox"))]
+ AppleTalk = libc::AF_APPLETALK,
+ /// AX.25 packet layer protocol.
+ /// (see [netrom(4)](https://www.unix.com/man-page/linux/4/netrom/))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetRom = libc::AF_NETROM,
+ /// Can't be used for creating sockets; mostly used for bridge
+ /// links in
+ /// [rtnetlink(7)](https://man7.org/linux/man-pages/man7/rtnetlink.7.html)
+ /// protocol commands.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Bridge = libc::AF_BRIDGE,
+ /// Access to raw ATM PVCs
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AtmPvc = libc::AF_ATMPVC,
+ /// ITU-T X.25 / ISO-8208 protocol (see [`x25(7)`](https://man7.org/linux/man-pages/man7/x25.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ X25 = libc::AF_X25,
+ /// RATS (Radio Amateur Telecommunications Society) Open
+ /// Systems environment (ROSE) AX.25 packet layer protocol.
+ /// (see [netrom(4)](https://www.unix.com/man-page/linux/4/netrom/))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Rose = libc::AF_ROSE,
+ /// DECet protocol sockets.
+ #[cfg(not(any(target_os = "haiku", target_os = "redox")))]
+ Decnet = libc::AF_DECnet,
+ /// Reserved for "802.2LLC project"; never used.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetBeui = libc::AF_NETBEUI,
+ /// This was a short-lived (between Linux 2.1.30 and
+ /// 2.1.99pre2) protocol family for firewall upcalls.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Security = libc::AF_SECURITY,
+ /// Key management protocol.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Key = libc::AF_KEY,
+ #[allow(missing_docs)] // Not documented anywhere that I can find
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ash = libc::AF_ASH,
+ /// Acorn Econet protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Econet = libc::AF_ECONET,
+ /// Access to ATM Switched Virtual Circuits
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AtmSvc = libc::AF_ATMSVC,
+ /// Reliable Datagram Sockets (RDS) protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Rds = libc::AF_RDS,
+ /// IBM SNA
+ #[cfg(not(any(target_os = "haiku", target_os = "redox")))]
+ Sna = libc::AF_SNA,
+ /// Socket interface over IrDA
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Irda = libc::AF_IRDA,
+ /// Generic PPP transport layer, for setting up L2 tunnels (L2TP and PPPoE)
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Pppox = libc::AF_PPPOX,
+ /// Legacy protocol for wide area network (WAN) connectivity that was used
+ /// by Sangoma WAN cards
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Wanpipe = libc::AF_WANPIPE,
+ /// Logical link control (IEEE 802.2 LLC) protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Llc = libc::AF_LLC,
+ /// InfiniBand native addressing
+ #[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ib = libc::AF_IB,
+ /// Multiprotocol Label Switching
+ #[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Mpls = libc::AF_MPLS,
+ /// Controller Area Network automotive bus protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Can = libc::AF_CAN,
+ /// TIPC, "cluster domain sockets" protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Tipc = libc::AF_TIPC,
+ /// Bluetooth low-level socket protocol
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "solaris",
+ target_os = "redox",
+ )))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Bluetooth = libc::AF_BLUETOOTH,
+ /// IUCV (inter-user communication vehicle) z/VM protocol for
+ /// hypervisor-guest interaction
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Iucv = libc::AF_IUCV,
+ /// Rx, Andrew File System remote procedure call protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ RxRpc = libc::AF_RXRPC,
+ /// New "modular ISDN" driver interface protocol
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "redox",
+ )))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Isdn = libc::AF_ISDN,
+ /// Nokia cellular modem IPC/RPC interface
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Phonet = libc::AF_PHONET,
+ /// IEEE 802.15.4 WPAN (wireless personal area network) raw packet protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ieee802154 = libc::AF_IEEE802154,
+ /// Ericsson's Communication CPU to Application CPU interface (CAIF)
+ /// protocol.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Caif = libc::AF_CAIF,
+ /// Interface to kernel crypto API
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Alg = libc::AF_ALG,
+ /// Near field communication
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Nfc = libc::AF_NFC,
+ /// VMWare VSockets protocol for hypervisor-guest interaction.
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Vsock = libc::AF_VSOCK,
+ /// ARPANet IMP addresses
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ImpLink = libc::AF_IMPLINK,
+ /// PUP protocols, e.g. BSP
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Pup = libc::AF_PUP,
+ /// MIT CHAOS protocols
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Chaos = libc::AF_CHAOS,
+ /// Novell and Xerox protocol
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ns = libc::AF_NS,
+ #[allow(missing_docs)] // Not documented anywhere that I can find
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Iso = libc::AF_ISO,
+ /// Bell Labs virtual circuit switch ?
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Datakit = libc::AF_DATAKIT,
+ /// CCITT protocols, X.25 etc
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ccitt = libc::AF_CCITT,
+ /// DEC Direct data link interface
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Dli = libc::AF_DLI,
+ #[allow(missing_docs)] // Not documented anywhere that I can find
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Lat = libc::AF_LAT,
+ /// NSC Hyperchannel
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Hylink = libc::AF_HYLINK,
+ /// Link layer interface
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Link = libc::AF_LINK,
+ /// connection-oriented IP, aka ST II
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Coip = libc::AF_COIP,
+ /// Computer Network Technology
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Cnt = libc::AF_CNT,
+ /// Native ATM access
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Natm = libc::AF_NATM,
+ /// Unspecified address family, (see [`getaddrinfo(3)`](https://man7.org/linux/man-pages/man3/getaddrinfo.3.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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 const 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(not(any(
+ target_os = "redox",
+ target_os = "linux",
+ target_os = "android"
+ )))]
+ libc::PF_ROUTE => Some(AddressFamily::Route),
+ #[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 = "illumos",
+ target_os = "openbsd"
+ ))]
+ libc::AF_LINK => Some(AddressFamily::Link),
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+ libc::AF_VSOCK => Some(AddressFamily::Vsock),
+ _ => None,
+ }
+ }
+}
+
+/// A wrapper around `sockaddr_un`.
+#[derive(Clone, Copy, Debug)]
+#[repr(C)]
+pub struct UnixAddr {
+ // INVARIANT: sun & sun_len are valid as defined by docs for from_raw_parts
+ sun: libc::sockaddr_un,
+ /// The length of the valid part of `sun`, including the sun_family field
+ /// but excluding any trailing nul.
+ // On the BSDs, this field is built into sun
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "redox",
+ ))]
+ sun_len: u8,
+}
+
+// linux man page unix(7) says there are 3 kinds of unix socket:
+// pathname: addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1
+// unnamed: addrlen = sizeof(sa_family_t)
+// abstract: addren > sizeof(sa_family_t), name = sun_path[..(addrlen - sizeof(sa_family_t))]
+//
+// what we call path_len = addrlen - offsetof(struct sockaddr_un, sun_path)
+#[derive(PartialEq, Eq, Hash)]
+enum UnixAddrKind<'a> {
+ Pathname(&'a Path),
+ Unnamed,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Abstract(&'a [u8]),
+}
+impl<'a> UnixAddrKind<'a> {
+ /// Safety: sun & sun_len must be valid
+ #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms
+ unsafe fn get(sun: &'a libc::sockaddr_un, sun_len: u8) -> Self {
+ assert!(sun_len as usize >= offset_of!(libc::sockaddr_un, sun_path));
+ let path_len =
+ sun_len as usize - offset_of!(libc::sockaddr_un, sun_path);
+ if path_len == 0 {
+ return Self::Unnamed;
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ if sun.sun_path[0] == 0 {
+ let name = slice::from_raw_parts(
+ sun.sun_path.as_ptr().add(1) as *const u8,
+ path_len - 1,
+ );
+ return Self::Abstract(name);
+ }
+ let pathname =
+ slice::from_raw_parts(sun.sun_path.as_ptr() as *const u8, path_len);
+ if pathname.last() == Some(&0) {
+ // A trailing NUL is not considered part of the path, and it does
+ // not need to be included in the addrlen passed to functions like
+ // bind(). However, Linux adds a trailing NUL, even if one was not
+ // originally present, when returning addrs from functions like
+ // getsockname() (the BSDs do not do that). So we need to filter
+ // out any trailing NUL here, so sockaddrs can round-trip through
+ // the kernel and still compare equal.
+ Self::Pathname(Path::new(OsStr::from_bytes(
+ &pathname[0..pathname.len() - 1],
+ )))
+ } else {
+ Self::Pathname(Path::new(OsStr::from_bytes(pathname)))
+ }
+ }
+}
+
+impl UnixAddr {
+ /// Create a new sockaddr_un representing a filesystem path.
+ #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms
+ 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(Errno::ENAMETOOLONG);
+ }
+
+ let sun_len = (bytes.len()
+ + offset_of!(libc::sockaddr_un, sun_path))
+ .try_into()
+ .unwrap();
+
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ {
+ ret.sun_len = sun_len;
+ }
+ ptr::copy_nonoverlapping(
+ bytes.as_ptr(),
+ ret.sun_path.as_mut_ptr() as *mut u8,
+ bytes.len(),
+ );
+
+ Ok(UnixAddr::from_raw_parts(ret, sun_len))
+ })?
+ }
+
+ /// Create a new `sockaddr_un` representing an address in the "abstract namespace".
+ ///
+ /// The leading nul byte for the abstract namespace is automatically added;
+ /// thus the input `path` is expected to be the bare name, not NUL-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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms
+ 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() >= ret.sun_path.len() {
+ return Err(Errno::ENAMETOOLONG);
+ }
+ let sun_len =
+ (path.len() + 1 + offset_of!(libc::sockaddr_un, sun_path))
+ .try_into()
+ .unwrap();
+
+ // 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::from_raw_parts(ret, sun_len))
+ }
+ }
+
+ /// Create a new `sockaddr_un` representing an "unnamed" unix socket address.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn new_unnamed() -> UnixAddr {
+ let ret = libc::sockaddr_un {
+ sun_family: AddressFamily::Unix as sa_family_t,
+ ..unsafe { mem::zeroed() }
+ };
+
+ let sun_len: u8 =
+ offset_of!(libc::sockaddr_un, sun_path).try_into().unwrap();
+
+ unsafe { UnixAddr::from_raw_parts(ret, sun_len) }
+ }
+
+ /// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `sun_len`
+ /// is the size of the valid portion of the struct, excluding any trailing
+ /// NUL.
+ ///
+ /// # Safety
+ /// This pair of sockaddr_un & sun_len must be a valid unix addr, which
+ /// means:
+ /// - sun_len >= offset_of(sockaddr_un, sun_path)
+ /// - sun_len <= sockaddr_un.sun_path.len() - offset_of(sockaddr_un, sun_path)
+ /// - if this is a unix addr with a pathname, sun.sun_path is a
+ /// fs path, not necessarily nul-terminated.
+ pub(crate) unsafe fn from_raw_parts(
+ sun: libc::sockaddr_un,
+ sun_len: u8,
+ ) -> UnixAddr {
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "redox",
+ ))]
+ {
+ UnixAddr { sun, sun_len }
+ } else {
+ assert_eq!(sun_len, sun.sun_len);
+ UnixAddr {sun}
+ }
+ }
+ }
+
+ fn kind(&self) -> UnixAddrKind<'_> {
+ // SAFETY: our sockaddr is always valid because of the invariant on the struct
+ unsafe { UnixAddrKind::get(&self.sun, self.sun_len()) }
+ }
+
+ /// If this address represents a filesystem path, return that path.
+ pub fn path(&self) -> Option<&Path> {
+ match self.kind() {
+ UnixAddrKind::Pathname(path) => Some(path),
+ _ => None,
+ }
+ }
+
+ /// If this address represents an abstract socket, return its name.
+ ///
+ /// For abstract sockets only the bare name is returned, without the
+ /// leading NUL byte. `None` is returned for unnamed or path-backed sockets.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn as_abstract(&self) -> Option<&[u8]> {
+ match self.kind() {
+ UnixAddrKind::Abstract(name) => Some(name),
+ _ => None,
+ }
+ }
+
+ /// Check if this address is an "unnamed" unix socket address.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[inline]
+ pub fn is_unnamed(&self) -> bool {
+ matches!(self.kind(), UnixAddrKind::Unnamed)
+ }
+
+ /// Returns the addrlen of this socket - `offsetof(struct sockaddr_un, sun_path)`
+ #[inline]
+ pub fn path_len(&self) -> usize {
+ self.sun_len() as usize - offset_of!(libc::sockaddr_un, sun_path)
+ }
+ /// Returns a pointer to the raw `sockaddr_un` struct
+ #[inline]
+ pub fn as_ptr(&self) -> *const libc::sockaddr_un {
+ &self.sun
+ }
+ /// Returns a mutable pointer to the raw `sockaddr_un` struct
+ #[inline]
+ pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_un {
+ &mut self.sun
+ }
+
+ fn sun_len(&self) -> u8 {
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "redox",
+ ))]
+ {
+ self.sun_len
+ } else {
+ self.sun.sun_len
+ }
+ }
+ }
+}
+
+impl private::SockaddrLikePriv for UnixAddr {}
+impl SockaddrLike for UnixAddr {
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ fn len(&self) -> libc::socklen_t {
+ self.sun_len.into()
+ }
+
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if (l as usize) < offset_of!(libc::sockaddr_un, sun_path)
+ || l > u8::MAX as libc::socklen_t
+ {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_UNIX {
+ return None;
+ }
+ let mut su: libc::sockaddr_un = mem::zeroed();
+ let sup = &mut su as *mut libc::sockaddr_un as *mut u8;
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "redox",
+ ))] {
+ let su_len = len.unwrap_or(
+ mem::size_of::<libc::sockaddr_un>() as libc::socklen_t
+ );
+ } else {
+ let su_len = len.unwrap_or((*addr).sa_len as libc::socklen_t);
+ }
+ };
+ ptr::copy(addr as *const u8, sup, su_len as usize);
+ Some(Self::from_raw_parts(su, su_len as u8))
+ }
+
+ fn size() -> libc::socklen_t
+ where
+ Self: Sized,
+ {
+ mem::size_of::<libc::sockaddr_un>() as libc::socklen_t
+ }
+
+ unsafe fn set_length(&mut self, new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic> {
+ // `new_length` is only used on some platforms, so it must be provided even when not used
+ #![allow(unused_variables)]
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "redox",
+ ))] {
+ self.sun_len = new_length as u8;
+ }
+ };
+ Ok(())
+ }
+}
+
+impl AsRef<libc::sockaddr_un> for UnixAddr {
+ fn as_ref(&self) -> &libc::sockaddr_un {
+ &self.sun
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn fmt_abstract(abs: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
+ use fmt::Write;
+ f.write_str("@\"")?;
+ for &b in abs {
+ use fmt::Display;
+ char::from(b).escape_default().fmt(f)?;
+ }
+ f.write_char('"')?;
+ Ok(())
+}
+
+impl fmt::Display for UnixAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.kind() {
+ UnixAddrKind::Pathname(path) => path.display().fmt(f),
+ UnixAddrKind::Unnamed => f.pad("<unbound UNIX socket>"),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ UnixAddrKind::Abstract(name) => fmt_abstract(name, f),
+ }
+ }
+}
+
+impl PartialEq for UnixAddr {
+ fn eq(&self, other: &UnixAddr) -> bool {
+ self.kind() == other.kind()
+ }
+}
+
+impl Eq for UnixAddr {}
+
+impl Hash for UnixAddr {
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ self.kind().hash(s)
+ }
+}
+
+/// Anything that, in C, can be cast back and forth to `sockaddr`.
+///
+/// Most implementors also implement `AsRef<libc::XXX>` to access their
+/// inner type read-only.
+#[allow(clippy::len_without_is_empty)]
+pub trait SockaddrLike: private::SockaddrLikePriv {
+ /// Returns a raw pointer to the inner structure. Useful for FFI.
+ fn as_ptr(&self) -> *const libc::sockaddr {
+ self as *const Self as *const libc::sockaddr
+ }
+
+ /// Unsafe constructor from a variable length source
+ ///
+ /// Some C APIs from provide `len`, and others do not. If it's provided it
+ /// will be validated. If not, it will be guessed based on the family.
+ ///
+ /// # Arguments
+ ///
+ /// - `addr`: raw pointer to something that can be cast to a
+ /// `libc::sockaddr`. For example, `libc::sockaddr_in`,
+ /// `libc::sockaddr_in6`, etc.
+ /// - `len`: For fixed-width types like `sockaddr_in`, it will be
+ /// validated if present and ignored if not. For variable-width
+ /// types it is required and must be the total length of valid
+ /// data. For example, if `addr` points to a
+ /// named `sockaddr_un`, then `len` must be the length of the
+ /// structure up to but not including the trailing NUL.
+ ///
+ /// # Safety
+ ///
+ /// `addr` must be valid for the specific type of sockaddr. `len`, if
+ /// present, must not exceed the length of valid data in `addr`.
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized;
+
+ /// Return the address family of this socket
+ ///
+ /// # Examples
+ /// One common use is to match on the family of a union type, like this:
+ /// ```
+ /// # use nix::sys::socket::*;
+ /// # use std::os::unix::io::AsRawFd;
+ /// let fd = socket(AddressFamily::Inet, SockType::Stream,
+ /// SockFlag::empty(), None).unwrap();
+ /// let ss: SockaddrStorage = getsockname(fd.as_raw_fd()).unwrap();
+ /// match ss.family().unwrap() {
+ /// AddressFamily::Inet => println!("{}", ss.as_sockaddr_in().unwrap()),
+ /// AddressFamily::Inet6 => println!("{}", ss.as_sockaddr_in6().unwrap()),
+ /// _ => println!("Unexpected address family")
+ /// }
+ /// ```
+ fn family(&self) -> Option<AddressFamily> {
+ // Safe since all implementors have a sa_family field at the same
+ // address, and they're all repr(C)
+ AddressFamily::from_i32(unsafe {
+ (*(self as *const Self as *const libc::sockaddr)).sa_family as i32
+ })
+ }
+
+ cfg_if! {
+ if #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))] {
+ /// Return the length of valid data in the sockaddr structure.
+ ///
+ /// For fixed-size sockaddrs, this should be the size of the
+ /// structure. But for variable-sized types like [`UnixAddr`] it
+ /// may be less.
+ fn len(&self) -> libc::socklen_t {
+ // Safe since all implementors have a sa_len field at the same
+ // address, and they're all repr(transparent).
+ // Robust for all implementors.
+ unsafe {
+ (*(self as *const Self as *const libc::sockaddr)).sa_len
+ }.into()
+ }
+ } else {
+ /// Return the length of valid data in the sockaddr structure.
+ ///
+ /// For fixed-size sockaddrs, this should be the size of the
+ /// structure. But for variable-sized types like [`UnixAddr`] it
+ /// may be less.
+ fn len(&self) -> libc::socklen_t {
+ // No robust default implementation is possible without an
+ // sa_len field. Implementors with a variable size must
+ // override this method.
+ mem::size_of_val(self) as libc::socklen_t
+ }
+ }
+ }
+
+ /// Return the available space in the structure
+ fn size() -> libc::socklen_t
+ where
+ Self: Sized,
+ {
+ mem::size_of::<Self>() as libc::socklen_t
+ }
+
+ /// Set the length of this socket address
+ ///
+ /// This method may only be called on socket addresses whose lengths are dynamic, and it
+ /// returns an error if called on a type whose length is static.
+ ///
+ /// # Safety
+ ///
+ /// `new_length` must be a valid length for this type of address. Specifically, reads of that
+ /// length from `self` must be valid.
+ #[doc(hidden)]
+ unsafe fn set_length(&mut self, _new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic> {
+ Err(SocketAddressLengthNotDynamic)
+ }
+}
+
+/// The error returned by [`SockaddrLike::set_length`] on an address whose length is statically
+/// fixed.
+#[derive(Copy, Clone, Debug)]
+pub struct SocketAddressLengthNotDynamic;
+impl fmt::Display for SocketAddressLengthNotDynamic {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("Attempted to set length on socket whose length is statically fixed")
+ }
+}
+impl std::error::Error for SocketAddressLengthNotDynamic {}
+
+impl private::SockaddrLikePriv for () {
+ fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
+ ptr::null_mut()
+ }
+}
+
+/// `()` can be used in place of a real Sockaddr when no address is expected,
+/// for example for a field of `Option<S> where S: SockaddrLike`.
+// If this RFC ever stabilizes, then ! will be a better choice.
+// https://github.com/rust-lang/rust/issues/35121
+impl SockaddrLike for () {
+ fn as_ptr(&self) -> *const libc::sockaddr {
+ ptr::null()
+ }
+
+ unsafe fn from_raw(
+ _: *const libc::sockaddr,
+ _: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ None
+ }
+
+ fn family(&self) -> Option<AddressFamily> {
+ None
+ }
+
+ fn len(&self) -> libc::socklen_t {
+ 0
+ }
+}
+
+/// An IPv4 socket address
+#[cfg(feature = "net")]
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct SockaddrIn(libc::sockaddr_in);
+
+#[cfg(feature = "net")]
+impl SockaddrIn {
+ /// Returns the IP address associated with this socket address, in native
+ /// endian.
+ pub const fn ip(&self) -> libc::in_addr_t {
+ u32::from_be(self.0.sin_addr.s_addr)
+ }
+
+ /// Creates a new socket address from IPv4 octets and a port number.
+ pub fn new(a: u8, b: u8, c: u8, d: u8, port: u16) -> Self {
+ Self(libc::sockaddr_in {
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "aix",
+ target_os = "haiku",
+ target_os = "openbsd"
+ ))]
+ sin_len: Self::size() as u8,
+ sin_family: AddressFamily::Inet as sa_family_t,
+ sin_port: u16::to_be(port),
+ sin_addr: libc::in_addr {
+ s_addr: u32::from_ne_bytes([a, b, c, d]),
+ },
+ sin_zero: unsafe { mem::zeroed() },
+ })
+ }
+
+ /// Returns the port number associated with this socket address, in native
+ /// endian.
+ pub const fn port(&self) -> u16 {
+ u16::from_be(self.0.sin_port)
+ }
+}
+
+#[cfg(feature = "net")]
+impl private::SockaddrLikePriv for SockaddrIn {}
+#[cfg(feature = "net")]
+impl SockaddrLike for SockaddrIn {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_in>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_INET {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+}
+
+#[cfg(feature = "net")]
+impl AsRef<libc::sockaddr_in> for SockaddrIn {
+ fn as_ref(&self) -> &libc::sockaddr_in {
+ &self.0
+ }
+}
+
+#[cfg(feature = "net")]
+impl fmt::Display for SockaddrIn {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let ne = u32::from_be(self.0.sin_addr.s_addr);
+ let port = u16::from_be(self.0.sin_port);
+ write!(
+ f,
+ "{}.{}.{}.{}:{}",
+ ne >> 24,
+ (ne >> 16) & 0xFF,
+ (ne >> 8) & 0xFF,
+ ne & 0xFF,
+ port
+ )
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV4> for SockaddrIn {
+ fn from(addr: net::SocketAddrV4) -> Self {
+ Self(libc::sockaddr_in {
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "hermit",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ sin_len: mem::size_of::<libc::sockaddr_in>() as u8,
+ sin_family: AddressFamily::Inet as sa_family_t,
+ sin_port: addr.port().to_be(), // network byte order
+ sin_addr: ipv4addr_to_libc(*addr.ip()),
+ ..unsafe { mem::zeroed() }
+ })
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<SockaddrIn> for net::SocketAddrV4 {
+ fn from(addr: SockaddrIn) -> Self {
+ net::SocketAddrV4::new(
+ net::Ipv4Addr::from(addr.0.sin_addr.s_addr.to_ne_bytes()),
+ u16::from_be(addr.0.sin_port),
+ )
+ }
+}
+
+#[cfg(feature = "net")]
+impl std::str::FromStr for SockaddrIn {
+ type Err = net::AddrParseError;
+
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ net::SocketAddrV4::from_str(s).map(SockaddrIn::from)
+ }
+}
+
+/// An IPv6 socket address
+#[cfg(feature = "net")]
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct SockaddrIn6(libc::sockaddr_in6);
+
+#[cfg(feature = "net")]
+impl SockaddrIn6 {
+ /// Returns the flow information associated with this address.
+ pub const fn flowinfo(&self) -> u32 {
+ self.0.sin6_flowinfo
+ }
+
+ /// Returns the IP address associated with this socket address.
+ pub fn ip(&self) -> net::Ipv6Addr {
+ net::Ipv6Addr::from(self.0.sin6_addr.s6_addr)
+ }
+
+ /// Returns the port number associated with this socket address, in native
+ /// endian.
+ pub const fn port(&self) -> u16 {
+ u16::from_be(self.0.sin6_port)
+ }
+
+ /// Returns the scope ID associated with this address.
+ pub const fn scope_id(&self) -> u32 {
+ self.0.sin6_scope_id
+ }
+}
+
+#[cfg(feature = "net")]
+impl private::SockaddrLikePriv for SockaddrIn6 {}
+#[cfg(feature = "net")]
+impl SockaddrLike for SockaddrIn6 {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_INET6 {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+}
+
+#[cfg(feature = "net")]
+impl AsRef<libc::sockaddr_in6> for SockaddrIn6 {
+ fn as_ref(&self) -> &libc::sockaddr_in6 {
+ &self.0
+ }
+}
+
+#[cfg(feature = "net")]
+impl fmt::Display for SockaddrIn6 {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ // These things are really hard to display properly. Easier to let std
+ // do it.
+ let std = net::SocketAddrV6::new(
+ self.ip(),
+ self.port(),
+ self.flowinfo(),
+ self.scope_id(),
+ );
+ std.fmt(f)
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV6> for SockaddrIn6 {
+ fn from(addr: net::SocketAddrV6) -> Self {
+ #[allow(clippy::needless_update)] // It isn't needless on Illumos
+ Self(libc::sockaddr_in6 {
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "hermit",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ sin6_len: mem::size_of::<libc::sockaddr_in6>() as u8,
+ sin6_family: AddressFamily::Inet6 as sa_family_t,
+ sin6_port: addr.port().to_be(), // network byte order
+ sin6_addr: ipv6addr_to_libc(addr.ip()),
+ sin6_flowinfo: addr.flowinfo(), // host byte order
+ sin6_scope_id: addr.scope_id(), // host byte order
+ ..unsafe { mem::zeroed() }
+ })
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<SockaddrIn6> for net::SocketAddrV6 {
+ fn from(addr: SockaddrIn6) -> Self {
+ net::SocketAddrV6::new(
+ net::Ipv6Addr::from(addr.0.sin6_addr.s6_addr),
+ u16::from_be(addr.0.sin6_port),
+ addr.0.sin6_flowinfo,
+ addr.0.sin6_scope_id,
+ )
+ }
+}
+
+#[cfg(feature = "net")]
+impl std::str::FromStr for SockaddrIn6 {
+ type Err = net::AddrParseError;
+
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ net::SocketAddrV6::from_str(s).map(SockaddrIn6::from)
+ }
+}
+
+/// A container for any sockaddr type
+///
+/// Just like C's `sockaddr_storage`, this type is large enough to hold any type
+/// of sockaddr. It can be used as an argument with functions like
+/// [`bind`](super::bind) and [`getsockname`](super::getsockname). Though it is
+/// a union, it can be safely accessed through the `as_*` methods.
+///
+/// # Example
+/// ```
+/// # use nix::sys::socket::*;
+/// # use std::str::FromStr;
+/// # use std::os::unix::io::AsRawFd;
+/// let localhost = SockaddrIn::from_str("127.0.0.1:8081").unwrap();
+/// let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(),
+/// None).unwrap();
+/// bind(fd.as_raw_fd(), &localhost).expect("bind");
+/// let ss: SockaddrStorage = getsockname(fd.as_raw_fd()).expect("getsockname");
+/// assert_eq!(&localhost, ss.as_sockaddr_in().unwrap());
+/// ```
+#[derive(Clone, Copy, Eq)]
+#[repr(C)]
+pub union SockaddrStorage {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ alg: AlgAddr,
+ #[cfg(all(feature = "net", not(target_os = "redox")))]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ dl: LinkAddr,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ nl: NetlinkAddr,
+ #[cfg(all(
+ feature = "ioctl",
+ any(target_os = "ios", target_os = "macos")
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
+ sctl: SysControlAddr,
+ #[cfg(feature = "net")]
+ sin: SockaddrIn,
+ #[cfg(feature = "net")]
+ sin6: SockaddrIn6,
+ ss: libc::sockaddr_storage,
+ su: UnixAddr,
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos" ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ vsock: VsockAddr,
+}
+impl private::SockaddrLikePriv for SockaddrStorage {}
+impl SockaddrLike for SockaddrStorage {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ l: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if addr.is_null() {
+ return None;
+ }
+ if let Some(len) = l {
+ let ulen = len as usize;
+ if ulen < offset_of!(libc::sockaddr, sa_data)
+ || ulen > mem::size_of::<libc::sockaddr_storage>()
+ {
+ None
+ } else {
+ let mut ss: libc::sockaddr_storage = mem::zeroed();
+ let ssp = &mut ss as *mut libc::sockaddr_storage as *mut u8;
+ ptr::copy(addr as *const u8, ssp, len as usize);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ if i32::from(ss.ss_family) == libc::AF_UNIX {
+ // Safe because we UnixAddr is strictly smaller than
+ // SockaddrStorage, and we just initialized the structure.
+ (*(&mut ss as *mut libc::sockaddr_storage
+ as *mut UnixAddr))
+ .sun_len = len as u8;
+ }
+ Some(Self { ss })
+ }
+ } else {
+ // If length is not available and addr is of a fixed-length type,
+ // copy it. If addr is of a variable length type and len is not
+ // available, then there's nothing we can do.
+ match (*addr).sa_family as i32 {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_ALG => {
+ AlgAddr::from_raw(addr, l).map(|alg| Self { alg })
+ }
+ #[cfg(feature = "net")]
+ libc::AF_INET => {
+ SockaddrIn::from_raw(addr, l).map(|sin| Self { sin })
+ }
+ #[cfg(feature = "net")]
+ libc::AF_INET6 => {
+ SockaddrIn6::from_raw(addr, l).map(|sin6| Self { sin6 })
+ }
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "haiku",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ libc::AF_LINK => {
+ LinkAddr::from_raw(addr, l).map(|dl| Self { dl })
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_NETLINK => {
+ NetlinkAddr::from_raw(addr, l).map(|nl| Self { nl })
+ }
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg(feature = "net")]
+ libc::AF_PACKET => {
+ LinkAddr::from_raw(addr, l).map(|dl| Self { dl })
+ }
+ #[cfg(all(
+ feature = "ioctl",
+ any(target_os = "ios", target_os = "macos")
+ ))]
+ libc::AF_SYSTEM => {
+ SysControlAddr::from_raw(addr, l).map(|sctl| Self { sctl })
+ }
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos" ))]
+ libc::AF_VSOCK => {
+ VsockAddr::from_raw(addr, l).map(|vsock| Self { vsock })
+ }
+ _ => None,
+ }
+ }
+ }
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ fn len(&self) -> libc::socklen_t {
+ match self.as_unix_addr() {
+ // The UnixAddr type knows its own length
+ Some(ua) => ua.len(),
+ // For all else, we're just a boring SockaddrStorage
+ None => mem::size_of_val(self) as libc::socklen_t,
+ }
+ }
+
+ unsafe fn set_length(&mut self, new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic> {
+ match self.as_unix_addr_mut() {
+ Some(addr) => {
+ addr.set_length(new_length)
+ },
+ None => Err(SocketAddressLengthNotDynamic),
+ }
+ }
+}
+
+macro_rules! accessors {
+ (
+ $fname:ident,
+ $fname_mut:ident,
+ $sockty:ty,
+ $family:expr,
+ $libc_ty:ty,
+ $field:ident) => {
+ /// Safely and falliably downcast to an immutable reference
+ pub fn $fname(&self) -> Option<&$sockty> {
+ if self.family() == Some($family)
+ && self.len() >= mem::size_of::<$libc_ty>() as libc::socklen_t
+ {
+ // Safe because family and len are validated
+ Some(unsafe { &self.$field })
+ } else {
+ None
+ }
+ }
+
+ /// Safely and falliably downcast to a mutable reference
+ pub fn $fname_mut(&mut self) -> Option<&mut $sockty> {
+ if self.family() == Some($family)
+ && self.len() >= mem::size_of::<$libc_ty>() as libc::socklen_t
+ {
+ // Safe because family and len are validated
+ Some(unsafe { &mut self.$field })
+ } else {
+ None
+ }
+ }
+ };
+}
+
+impl SockaddrStorage {
+ /// Downcast to an immutable `[UnixAddr]` reference.
+ pub fn as_unix_addr(&self) -> Option<&UnixAddr> {
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ {
+ let p = unsafe{ &self.ss as *const libc::sockaddr_storage };
+ // Safe because UnixAddr is strictly smaller than
+ // sockaddr_storage, and we're fully initialized
+ let len = unsafe {
+ (*(p as *const UnixAddr )).sun_len as usize
+ };
+ } else {
+ let len = self.len() as usize;
+ }
+ }
+ // Sanity checks
+ if self.family() != Some(AddressFamily::Unix)
+ || len < offset_of!(libc::sockaddr_un, sun_path)
+ || len > mem::size_of::<libc::sockaddr_un>()
+ {
+ None
+ } else {
+ Some(unsafe { &self.su })
+ }
+ }
+
+ /// Downcast to a mutable `[UnixAddr]` reference.
+ pub fn as_unix_addr_mut(&mut self) -> Option<&mut UnixAddr> {
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ {
+ let p = unsafe{ &self.ss as *const libc::sockaddr_storage };
+ // Safe because UnixAddr is strictly smaller than
+ // sockaddr_storage, and we're fully initialized
+ let len = unsafe {
+ (*(p as *const UnixAddr )).sun_len as usize
+ };
+ } else {
+ let len = self.len() as usize;
+ }
+ }
+ // Sanity checks
+ if self.family() != Some(AddressFamily::Unix)
+ || len < offset_of!(libc::sockaddr_un, sun_path)
+ || len > mem::size_of::<libc::sockaddr_un>()
+ {
+ None
+ } else {
+ Some(unsafe { &mut self.su })
+ }
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ accessors! {as_alg_addr, as_alg_addr_mut, AlgAddr,
+ AddressFamily::Alg, libc::sockaddr_alg, alg}
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg(feature = "net")]
+ accessors! {
+ as_link_addr, as_link_addr_mut, LinkAddr,
+ AddressFamily::Packet, libc::sockaddr_ll, dl}
+
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ accessors! {
+ as_link_addr, as_link_addr_mut, LinkAddr,
+ AddressFamily::Link, libc::sockaddr_dl, dl}
+
+ #[cfg(feature = "net")]
+ accessors! {
+ as_sockaddr_in, as_sockaddr_in_mut, SockaddrIn,
+ AddressFamily::Inet, libc::sockaddr_in, sin}
+
+ #[cfg(feature = "net")]
+ accessors! {
+ as_sockaddr_in6, as_sockaddr_in6_mut, SockaddrIn6,
+ AddressFamily::Inet6, libc::sockaddr_in6, sin6}
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ accessors! {as_netlink_addr, as_netlink_addr_mut, NetlinkAddr,
+ AddressFamily::Netlink, libc::sockaddr_nl, nl}
+
+ #[cfg(all(feature = "ioctl", any(target_os = "ios", target_os = "macos")))]
+ #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
+ accessors! {as_sys_control_addr, as_sys_control_addr_mut, SysControlAddr,
+ AddressFamily::System, libc::sockaddr_ctl, sctl}
+
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ accessors! {as_vsock_addr, as_vsock_addr_mut, VsockAddr,
+ AddressFamily::Vsock, libc::sockaddr_vm, vsock}
+}
+
+impl fmt::Debug for SockaddrStorage {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("SockaddrStorage")
+ // Safe because sockaddr_storage has the least specific
+ // field types
+ .field("ss", unsafe { &self.ss })
+ .finish()
+ }
+}
+
+impl fmt::Display for SockaddrStorage {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ unsafe {
+ match self.ss.ss_family as i32 {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_ALG => self.alg.fmt(f),
+ #[cfg(feature = "net")]
+ libc::AF_INET => self.sin.fmt(f),
+ #[cfg(feature = "net")]
+ libc::AF_INET6 => self.sin6.fmt(f),
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ libc::AF_LINK => self.dl.fmt(f),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_NETLINK => self.nl.fmt(f),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "fuchsia"
+ ))]
+ #[cfg(feature = "net")]
+ libc::AF_PACKET => self.dl.fmt(f),
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg(feature = "ioctl")]
+ libc::AF_SYSTEM => self.sctl.fmt(f),
+ libc::AF_UNIX => self.su.fmt(f),
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+ libc::AF_VSOCK => self.vsock.fmt(f),
+ _ => "<Address family unspecified>".fmt(f),
+ }
+ }
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV4> for SockaddrStorage {
+ fn from(s: net::SocketAddrV4) -> Self {
+ unsafe {
+ let mut ss: Self = mem::zeroed();
+ ss.sin = SockaddrIn::from(s);
+ ss
+ }
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV6> for SockaddrStorage {
+ fn from(s: net::SocketAddrV6) -> Self {
+ unsafe {
+ let mut ss: Self = mem::zeroed();
+ ss.sin6 = SockaddrIn6::from(s);
+ ss
+ }
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddr> for SockaddrStorage {
+ fn from(s: net::SocketAddr) -> Self {
+ match s {
+ net::SocketAddr::V4(sa4) => Self::from(sa4),
+ net::SocketAddr::V6(sa6) => Self::from(sa6),
+ }
+ }
+}
+
+impl Hash for SockaddrStorage {
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ unsafe {
+ match self.ss.ss_family as i32 {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_ALG => self.alg.hash(s),
+ #[cfg(feature = "net")]
+ libc::AF_INET => self.sin.hash(s),
+ #[cfg(feature = "net")]
+ libc::AF_INET6 => self.sin6.hash(s),
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ libc::AF_LINK => self.dl.hash(s),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_NETLINK => self.nl.hash(s),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "fuchsia"
+ ))]
+ #[cfg(feature = "net")]
+ libc::AF_PACKET => self.dl.hash(s),
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg(feature = "ioctl")]
+ libc::AF_SYSTEM => self.sctl.hash(s),
+ libc::AF_UNIX => self.su.hash(s),
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+ libc::AF_VSOCK => self.vsock.hash(s),
+ _ => self.ss.hash(s),
+ }
+ }
+ }
+}
+
+impl PartialEq for SockaddrStorage {
+ fn eq(&self, other: &Self) -> bool {
+ unsafe {
+ match (self.ss.ss_family as i32, other.ss.ss_family as i32) {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::AF_ALG, libc::AF_ALG) => self.alg == other.alg,
+ #[cfg(feature = "net")]
+ (libc::AF_INET, libc::AF_INET) => self.sin == other.sin,
+ #[cfg(feature = "net")]
+ (libc::AF_INET6, libc::AF_INET6) => self.sin6 == other.sin6,
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ (libc::AF_LINK, libc::AF_LINK) => self.dl == other.dl,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::AF_NETLINK, libc::AF_NETLINK) => self.nl == other.nl,
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg(feature = "net")]
+ (libc::AF_PACKET, libc::AF_PACKET) => self.dl == other.dl,
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg(feature = "ioctl")]
+ (libc::AF_SYSTEM, libc::AF_SYSTEM) => self.sctl == other.sctl,
+ (libc::AF_UNIX, libc::AF_UNIX) => self.su == other.su,
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+ (libc::AF_VSOCK, libc::AF_VSOCK) => self.vsock == other.vsock,
+ _ => false,
+ }
+ }
+ }
+}
+
+pub(super) mod private {
+ pub trait SockaddrLikePriv {
+ /// Returns a mutable raw pointer to the inner structure.
+ ///
+ /// # Safety
+ ///
+ /// This method is technically safe, but modifying the inner structure's
+ /// `family` or `len` fields may result in violating Nix's invariants.
+ /// It is best to use this method only with foreign functions that do
+ /// not change the sockaddr type.
+ fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
+ self as *mut Self as *mut libc::sockaddr
+ }
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub mod netlink {
+ use super::*;
+ use crate::sys::socket::addr::AddressFamily;
+ use libc::{sa_family_t, sockaddr_nl};
+ use std::{fmt, mem};
+
+ /// Address for the Linux kernel user interface device.
+ ///
+ /// # References
+ ///
+ /// [netlink(7)](https://man7.org/linux/man-pages/man7/netlink.7.html)
+ #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
+ #[repr(transparent)]
+ pub struct NetlinkAddr(pub(in super::super) sockaddr_nl);
+
+ impl NetlinkAddr {
+ /// Construct a new socket address from its port ID and multicast groups
+ /// mask.
+ 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)
+ }
+
+ /// Return the socket's port ID.
+ pub const fn pid(&self) -> u32 {
+ self.0.nl_pid
+ }
+
+ /// Return the socket's multicast groups mask
+ pub const fn groups(&self) -> u32 {
+ self.0.nl_groups
+ }
+ }
+
+ impl private::SockaddrLikePriv for NetlinkAddr {}
+ impl SockaddrLike for NetlinkAddr {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_NETLINK {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_nl> for NetlinkAddr {
+ fn as_ref(&self) -> &libc::sockaddr_nl {
+ &self.0
+ }
+ }
+
+ impl fmt::Display for NetlinkAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "pid: {} groups: {}", self.pid(), self.groups())
+ }
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub mod alg {
+ use super::*;
+ use libc::{c_char, sockaddr_alg, AF_ALG};
+ use std::ffi::CStr;
+ use std::hash::{Hash, Hasher};
+ use std::{fmt, mem, str};
+
+ /// Socket address for the Linux kernel crypto API
+ #[derive(Copy, Clone)]
+ #[repr(transparent)]
+ pub struct AlgAddr(pub(in super::super) sockaddr_alg);
+
+ impl private::SockaddrLikePriv for AlgAddr {}
+ impl SockaddrLike for AlgAddr {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ l: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = l {
+ if l != mem::size_of::<libc::sockaddr_alg>() as libc::socklen_t
+ {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_ALG {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_alg> for AlgAddr {
+ fn as_ref(&self) -> &libc::sockaddr_alg {
+ &self.0
+ }
+ }
+
+ // , PartialEq, Eq, Debug, Hash
+ impl PartialEq for AlgAddr {
+ fn eq(&self, other: &Self) -> bool {
+ let (inner, other) = (self.0, other.0);
+ (
+ inner.salg_family,
+ &inner.salg_type[..],
+ inner.salg_feat,
+ inner.salg_mask,
+ &inner.salg_name[..],
+ ) == (
+ other.salg_family,
+ &other.salg_type[..],
+ other.salg_feat,
+ other.salg_mask,
+ &other.salg_name[..],
+ )
+ }
+ }
+
+ impl Eq for AlgAddr {}
+
+ impl Hash for AlgAddr {
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ let inner = self.0;
+ (
+ inner.salg_family,
+ &inner.salg_type[..],
+ inner.salg_feat,
+ inner.salg_mask,
+ &inner.salg_name[..],
+ )
+ .hash(s);
+ }
+ }
+
+ impl AlgAddr {
+ /// Construct an `AF_ALG` socket from its cipher name and type.
+ pub fn new(alg_type: &str, alg_name: &str) -> AlgAddr {
+ let mut addr: sockaddr_alg = unsafe { mem::zeroed() };
+ addr.salg_family = AF_ALG as u16;
+ addr.salg_type[..alg_type.len()]
+ .copy_from_slice(alg_type.to_string().as_bytes());
+ addr.salg_name[..alg_name.len()]
+ .copy_from_slice(alg_name.to_string().as_bytes());
+
+ AlgAddr(addr)
+ }
+
+ /// Return the socket's cipher type, for example `hash` or `aead`.
+ pub fn alg_type(&self) -> &CStr {
+ unsafe {
+ CStr::from_ptr(self.0.salg_type.as_ptr() as *const c_char)
+ }
+ }
+
+ /// Return the socket's cipher name, for example `sha1`.
+ pub fn alg_name(&self) -> &CStr {
+ unsafe {
+ CStr::from_ptr(self.0.salg_name.as_ptr() as *const c_char)
+ }
+ }
+ }
+
+ impl fmt::Display for AlgAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "type: {} alg: {}",
+ self.alg_name().to_string_lossy(),
+ self.alg_type().to_string_lossy()
+ )
+ }
+ }
+
+ impl fmt::Debug for AlgAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(self, f)
+ }
+ }
+}
+
+feature! {
+#![feature = "ioctl"]
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+pub mod sys_control {
+ use crate::sys::socket::addr::AddressFamily;
+ use libc::{self, c_uchar};
+ use std::{fmt, mem, ptr};
+ use std::os::unix::io::RawFd;
+ use crate::{Errno, Result};
+ use super::{private, SockaddrLike};
+
+ // FIXME: Move type into `libc`
+ #[repr(C)]
+ #[derive(Clone, Copy)]
+ #[allow(missing_debug_implementations)]
+ pub struct ctl_ioc_info {
+ pub ctl_id: u32,
+ pub ctl_name: [c_uchar; MAX_KCTL_NAME],
+ }
+
+ const CTL_IOC_MAGIC: u8 = b'N';
+ 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);
+
+ /// Apple system control socket
+ ///
+ /// # References
+ ///
+ /// <https://developer.apple.com/documentation/kernel/sockaddr_ctl>
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ #[repr(transparent)]
+ pub struct SysControlAddr(pub(in super::super) libc::sockaddr_ctl);
+
+ impl private::SockaddrLikePriv for SysControlAddr {}
+ impl SockaddrLike for SysControlAddr {
+ unsafe fn from_raw(addr: *const libc::sockaddr, len: Option<libc::socklen_t>)
+ -> Option<Self> where Self: Sized
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_ctl>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_SYSTEM {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_ctl> for SysControlAddr {
+ fn as_ref(&self) -> &libc::sockaddr_ctl {
+ &self.0
+ }
+ }
+
+ impl SysControlAddr {
+ /// Construct a new `SysControlAddr` from its kernel unique identifier
+ /// and unit number.
+ pub const fn new(id: u32, unit: u32) -> SysControlAddr {
+ let addr = libc::sockaddr_ctl {
+ sc_len: mem::size_of::<libc::sockaddr_ctl>() as c_uchar,
+ sc_family: AddressFamily::System as c_uchar,
+ ss_sysaddr: libc::AF_SYS_CONTROL as u16,
+ sc_id: id,
+ sc_unit: unit,
+ sc_reserved: [0; 5]
+ };
+
+ SysControlAddr(addr)
+ }
+
+ /// Construct a new `SysControlAddr` from its human readable name and
+ /// unit number.
+ pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result<SysControlAddr> {
+ if name.len() > MAX_KCTL_NAME {
+ return Err(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 };
+
+ unsafe { ctl_info(sockfd, &mut info)?; }
+
+ Ok(SysControlAddr::new(info.ctl_id, unit))
+ }
+
+ /// Return the kernel unique identifier
+ pub const fn id(&self) -> u32 {
+ self.0.sc_id
+ }
+
+ /// Return the kernel controller private unit number.
+ pub const 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)
+ }
+ }
+}
+}
+
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "fuchsia"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+mod datalink {
+ feature! {
+ #![feature = "net"]
+ use super::{fmt, mem, private, ptr, SockaddrLike};
+
+ /// Hardware Address
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ #[repr(transparent)]
+ pub struct LinkAddr(pub(in super::super) libc::sockaddr_ll);
+
+ impl LinkAddr {
+ /// 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)
+ // Returns an Option just for cross-platform compatibility
+ pub fn addr(&self) -> Option<[u8; 6]> {
+ Some([
+ self.0.sll_addr[0],
+ self.0.sll_addr[1],
+ self.0.sll_addr[2],
+ self.0.sll_addr[3],
+ self.0.sll_addr[4],
+ self.0.sll_addr[5],
+ ])
+ }
+ }
+
+ impl fmt::Display for LinkAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(addr) = self.addr() {
+ write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
+ addr[0],
+ addr[1],
+ addr[2],
+ addr[3],
+ addr[4],
+ addr[5])
+ } else {
+ Ok(())
+ }
+ }
+ }
+ impl private::SockaddrLikePriv for LinkAddr {}
+ impl SockaddrLike for LinkAddr {
+ unsafe fn from_raw(addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>)
+ -> Option<Self> where Self: Sized
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_ll>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_PACKET {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_ll> for LinkAddr {
+ fn as_ref(&self) -> &libc::sockaddr_ll {
+ &self.0
+ }
+ }
+
+ }
+}
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "haiku",
+ target_os = "aix",
+ target_os = "openbsd"
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+mod datalink {
+ feature! {
+ #![feature = "net"]
+ use super::{fmt, mem, private, ptr, SockaddrLike};
+
+ /// Hardware Address
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ #[repr(transparent)]
+ pub struct LinkAddr(pub(in super::super) libc::sockaddr_dl);
+
+ impl LinkAddr {
+ /// interface index, if != 0, system given index for interface
+ #[cfg(not(target_os = "haiku"))]
+ pub fn ifindex(&self) -> usize {
+ self.0.sdl_index as usize
+ }
+
+ /// Datalink type
+ #[cfg(not(target_os = "haiku"))]
+ 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
+ #[cfg(not(target_os = "haiku"))]
+ 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();
+
+ alen == 0 || nlen + alen >= data_len
+ }
+
+ /// Physical-layer address (MAC)
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ pub fn addr(&self) -> Option<[u8; 6]> {
+ let nlen = self.nlen();
+ let data = self.0.sdl_data;
+
+ if self.is_empty() {
+ None
+ } else {
+ Some([
+ data[nlen] as u8,
+ data[nlen + 1] as u8,
+ data[nlen + 2] as u8,
+ data[nlen + 3] as u8,
+ data[nlen + 4] as u8,
+ data[nlen + 5] as u8,
+ ])
+ }
+ }
+ }
+
+ impl fmt::Display for LinkAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(addr) = self.addr() {
+ write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
+ addr[0],
+ addr[1],
+ addr[2],
+ addr[3],
+ addr[4],
+ addr[5])
+ } else {
+ Ok(())
+ }
+ }
+ }
+ impl private::SockaddrLikePriv for LinkAddr {}
+ impl SockaddrLike for LinkAddr {
+ unsafe fn from_raw(addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>)
+ -> Option<Self> where Self: Sized
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_dl>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_LINK {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_dl> for LinkAddr {
+ fn as_ref(&self) -> &libc::sockaddr_dl {
+ &self.0
+ }
+ }
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub mod vsock {
+ use super::*;
+ use crate::sys::socket::addr::AddressFamily;
+ use libc::{sa_family_t, sockaddr_vm};
+ use std::hash::{Hash, Hasher};
+ use std::{fmt, mem};
+
+ /// Socket address for VMWare VSockets protocol
+ ///
+ /// # References
+ ///
+ /// [vsock(7)](https://man7.org/linux/man-pages/man7/vsock.7.html)
+ #[derive(Copy, Clone)]
+ #[repr(transparent)]
+ pub struct VsockAddr(pub(in super::super) sockaddr_vm);
+
+ impl private::SockaddrLikePriv for VsockAddr {}
+ impl SockaddrLike for VsockAddr {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_vm>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_VSOCK {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_vm> for VsockAddr {
+ fn as_ref(&self) -> &libc::sockaddr_vm {
+ &self.0
+ }
+ }
+
+ impl PartialEq for VsockAddr {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ fn eq(&self, other: &Self) -> bool {
+ let (inner, other) = (self.0, other.0);
+ (inner.svm_family, inner.svm_cid, inner.svm_port)
+ == (other.svm_family, other.svm_cid, other.svm_port)
+ }
+ #[cfg(target_os = "macos")]
+ fn eq(&self, other: &Self) -> bool {
+ let (inner, other) = (self.0, other.0);
+ (inner.svm_family, inner.svm_cid, inner.svm_port, inner.svm_len)
+ == (other.svm_family, other.svm_cid, other.svm_port, inner.svm_len)
+ }
+ }
+
+ impl Eq for VsockAddr {}
+
+ impl Hash for VsockAddr {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ let inner = self.0;
+ (inner.svm_family, inner.svm_cid, inner.svm_port).hash(s);
+ }
+ #[cfg(target_os = "macos")]
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ let inner = self.0;
+ (inner.svm_family, inner.svm_cid, inner.svm_port, inner.svm_len).hash(s);
+ }
+ }
+
+ /// VSOCK Address
+ ///
+ /// The address for AF_VSOCK socket is defined as a combination of a
+ /// 32-bit Context Identifier (CID) and a 32-bit port number.
+ impl VsockAddr {
+ /// Construct a `VsockAddr` from its raw fields.
+ pub fn new(cid: u32, port: u32) -> VsockAddr {
+ let mut addr: sockaddr_vm = unsafe { mem::zeroed() };
+ addr.svm_family = AddressFamily::Vsock as sa_family_t;
+ addr.svm_cid = cid;
+ addr.svm_port = port;
+
+ #[cfg(target_os = "macos")]
+ {
+ addr.svm_len = std::mem::size_of::<sockaddr_vm>() as u8;
+ }
+ VsockAddr(addr)
+ }
+
+ /// Context Identifier (CID)
+ pub fn cid(&self) -> u32 {
+ self.0.svm_cid
+ }
+
+ /// Port number
+ pub fn port(&self) -> u32 {
+ self.0.svm_port
+ }
+ }
+
+ impl fmt::Display for VsockAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "cid: {} port: {}", self.cid(), self.port())
+ }
+ }
+
+ impl fmt::Debug for VsockAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(self, f)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ mod types {
+ use super::*;
+
+ #[test]
+ fn test_ipv4addr_to_libc() {
+ let s = std::net::Ipv4Addr::new(1, 2, 3, 4);
+ let l = ipv4addr_to_libc(s);
+ assert_eq!(l.s_addr, u32::to_be(0x01020304));
+ }
+
+ #[test]
+ fn test_ipv6addr_to_libc() {
+ let s = std::net::Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8);
+ let l = ipv6addr_to_libc(&s);
+ assert_eq!(
+ l.s6_addr,
+ [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8]
+ );
+ }
+ }
+
+ #[cfg(not(target_os = "redox"))]
+ mod link {
+ #![allow(clippy::cast_ptr_alignment)]
+
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos"
+ ))]
+ use super::super::super::socklen_t;
+ use super::*;
+
+ /// Don't panic when trying to display an empty datalink address
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[test]
+ fn test_datalink_display() {
+ use super::super::LinkAddr;
+ use std::mem;
+
+ let la = LinkAddr(libc::sockaddr_dl {
+ sdl_len: 56,
+ sdl_family: 18,
+ sdl_index: 5,
+ sdl_type: 24,
+ sdl_nlen: 3,
+ sdl_alen: 0,
+ sdl_slen: 0,
+ ..unsafe { mem::zeroed() }
+ });
+ format!("{la}");
+ }
+
+ #[cfg(all(
+ any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ),
+ target_endian = "little"
+ ))]
+ #[test]
+ fn linux_loopback() {
+ #[repr(align(2))]
+ struct Raw([u8; 20]);
+
+ let bytes = Raw([
+ 17u8, 0, 0, 0, 1, 0, 0, 0, 4, 3, 0, 6, 1, 2, 3, 4, 5, 6, 0, 0,
+ ]);
+ let sa = bytes.0.as_ptr() as *const libc::sockaddr;
+ let len = None;
+ let sock_addr =
+ unsafe { SockaddrStorage::from_raw(sa, len) }.unwrap();
+ assert_eq!(sock_addr.family(), Some(AddressFamily::Packet));
+ match sock_addr.as_link_addr() {
+ Some(dl) => assert_eq!(dl.addr(), Some([1, 2, 3, 4, 5, 6])),
+ None => panic!("Can't unwrap sockaddr storage"),
+ }
+ }
+
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[test]
+ fn macos_loopback() {
+ 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 len = Some(bytes.len() as socklen_t);
+ let sock_addr =
+ unsafe { SockaddrStorage::from_raw(sa, len) }.unwrap();
+ assert_eq!(sock_addr.family(), Some(AddressFamily::Link));
+ match sock_addr.as_link_addr() {
+ Some(dl) => {
+ assert!(dl.addr().is_none());
+ }
+ None => panic!("Can't unwrap sockaddr storage"),
+ }
+ }
+
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[test]
+ fn macos_tap() {
+ 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 len = Some(bytes.len() as socklen_t);
+
+ let sock_addr =
+ unsafe { SockaddrStorage::from_raw(sa, len).unwrap() };
+ assert_eq!(sock_addr.family(), Some(AddressFamily::Link));
+ match sock_addr.as_link_addr() {
+ Some(dl) => {
+ assert_eq!(dl.addr(), Some([24u8, 101, 144, 221, 76, 176]))
+ }
+ None => panic!("Can't unwrap sockaddr storage"),
+ }
+ }
+
+ #[cfg(target_os = "illumos")]
+ #[test]
+ fn illumos_tap() {
+ let bytes = [25u8, 0, 0, 0, 6, 0, 6, 0, 24, 101, 144, 221, 76, 176];
+ let ptr = bytes.as_ptr();
+ let sa = ptr as *const libc::sockaddr;
+ let len = Some(bytes.len() as socklen_t);
+ let _sock_addr = unsafe { SockaddrStorage::from_raw(sa, len) };
+
+ assert!(_sock_addr.is_some());
+
+ let sock_addr = _sock_addr.unwrap();
+
+ assert_eq!(sock_addr.family().unwrap(), AddressFamily::Link);
+
+ assert_eq!(
+ sock_addr.as_link_addr().unwrap().addr(),
+ Some([24u8, 101, 144, 221, 76, 176])
+ );
+ }
+
+ #[test]
+ fn size() {
+ #[cfg(any(
+ target_os = "aix",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "openbsd",
+ target_os = "haiku"
+ ))]
+ let l = mem::size_of::<libc::sockaddr_dl>();
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ let l = mem::size_of::<libc::sockaddr_ll>();
+ assert_eq!(LinkAddr::size() as usize, l);
+ }
+ }
+
+ mod sockaddr_in {
+ use super::*;
+ use std::str::FromStr;
+
+ #[test]
+ fn display() {
+ let s = "127.0.0.1:8080";
+ let addr = SockaddrIn::from_str(s).unwrap();
+ assert_eq!(s, format!("{addr}"));
+ }
+
+ #[test]
+ fn size() {
+ assert_eq!(
+ mem::size_of::<libc::sockaddr_in>(),
+ SockaddrIn::size() as usize
+ );
+ }
+ }
+
+ mod sockaddr_in6 {
+ use super::*;
+ use std::str::FromStr;
+
+ #[test]
+ fn display() {
+ let s = "[1234:5678:90ab:cdef::1111:2222]:8080";
+ let addr = SockaddrIn6::from_str(s).unwrap();
+ assert_eq!(s, format!("{addr}"));
+ }
+
+ #[test]
+ fn size() {
+ assert_eq!(
+ mem::size_of::<libc::sockaddr_in6>(),
+ SockaddrIn6::size() as usize
+ );
+ }
+
+ #[test]
+ // Ensure that we can convert to-and-from std::net variants without change.
+ fn to_and_from() {
+ let s = "[1234:5678:90ab:cdef::1111:2222]:8080";
+ let mut nix_sin6 = SockaddrIn6::from_str(s).unwrap();
+ nix_sin6.0.sin6_flowinfo = 0x12345678;
+ nix_sin6.0.sin6_scope_id = 0x9abcdef0;
+
+ let std_sin6: std::net::SocketAddrV6 = nix_sin6.into();
+ assert_eq!(nix_sin6, std_sin6.into());
+ }
+ }
+
+ mod sockaddr_storage {
+ use super::*;
+
+ #[test]
+ fn from_sockaddr_un_named() {
+ let ua = UnixAddr::new("/var/run/mysock").unwrap();
+ let ptr = ua.as_ptr() as *const libc::sockaddr;
+ let ss = unsafe { SockaddrStorage::from_raw(ptr, Some(ua.len())) }
+ .unwrap();
+ assert_eq!(ss.len(), ua.len());
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[test]
+ fn from_sockaddr_un_abstract_named() {
+ let name = String::from("nix\0abstract\0test");
+ let ua = UnixAddr::new_abstract(name.as_bytes()).unwrap();
+ let ptr = ua.as_ptr() as *const libc::sockaddr;
+ let ss = unsafe { SockaddrStorage::from_raw(ptr, Some(ua.len())) }
+ .unwrap();
+ assert_eq!(ss.len(), ua.len());
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[test]
+ fn from_sockaddr_un_abstract_unnamed() {
+ let ua = UnixAddr::new_unnamed();
+ let ptr = ua.as_ptr() as *const libc::sockaddr;
+ let ss = unsafe { SockaddrStorage::from_raw(ptr, Some(ua.len())) }
+ .unwrap();
+ assert_eq!(ss.len(), ua.len());
+ }
+ }
+
+ mod unixaddr {
+ use super::*;
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[test]
+ fn abstract_sun_path() {
+ let name = String::from("nix\0abstract\0test");
+ let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();
+
+ let sun_path1 =
+ unsafe { &(*addr.as_ptr()).sun_path[..addr.path_len()] };
+ let sun_path2 = [
+ 0, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0,
+ 116, 101, 115, 116,
+ ];
+ assert_eq!(sun_path1, sun_path2);
+ }
+
+ #[test]
+ fn size() {
+ assert_eq!(
+ mem::size_of::<libc::sockaddr_un>(),
+ UnixAddr::size() as usize
+ );
+ }
+ }
+}
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..78dd617c55
--- /dev/null
+++ b/third_party/rust/nix/src/sys/socket/mod.rs
@@ -0,0 +1,2465 @@
+//! Socket interface functions
+//!
+//! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html)
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "uio")]
+use crate::sys::time::TimeSpec;
+#[cfg(not(target_os = "redox"))]
+#[cfg(feature = "uio")]
+use crate::sys::time::TimeVal;
+use crate::{errno::Errno, Result};
+use cfg_if::cfg_if;
+use libc::{self, c_int, c_void, size_t, socklen_t};
+#[cfg(all(feature = "uio", not(target_os = "redox")))]
+use libc::{
+ iovec, CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, CMSG_SPACE,
+};
+#[cfg(not(target_os = "redox"))]
+use std::io::{IoSlice, IoSliceMut};
+#[cfg(feature = "net")]
+use std::net;
+use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, RawFd, OwnedFd};
+use std::{mem, ptr};
+
+#[deny(missing_docs)]
+mod addr;
+#[deny(missing_docs)]
+pub mod sockopt;
+
+/*
+ *
+ * ===== Re-exports =====
+ *
+ */
+
+pub use self::addr::{SockaddrLike, SockaddrStorage};
+
+#[cfg(any(target_os = "illumos", target_os = "solaris"))]
+pub use self::addr::{AddressFamily, UnixAddr};
+#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+pub use self::addr::{AddressFamily, UnixAddr};
+#[cfg(not(any(
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "redox",
+)))]
+#[cfg(feature = "net")]
+pub use self::addr::{LinkAddr, SockaddrIn, SockaddrIn6};
+#[cfg(any(
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "redox",
+))]
+#[cfg(feature = "net")]
+pub use self::addr::{SockaddrIn, SockaddrIn6};
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use crate::sys::socket::addr::alg::AlgAddr;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use crate::sys::socket::addr::netlink::NetlinkAddr;
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+#[cfg(feature = "ioctl")]
+pub use crate::sys::socket::addr::sys_control::SysControlAddr;
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+pub use crate::sys::socket::addr::vsock::VsockAddr;
+
+#[cfg(all(feature = "uio", not(target_os = "redox")))]
+pub use libc::{cmsghdr, msghdr};
+pub use libc::{sa_family_t, sockaddr, sockaddr_storage, sockaddr_un};
+#[cfg(feature = "net")]
+pub use libc::{sockaddr_in, sockaddr_in6};
+
+#[cfg(feature = "net")]
+use crate::sys::socket::addr::{ipv4addr_to_libc, ipv6addr_to_libc};
+
+/// 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)]
+#[non_exhaustive]
+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.
+ #[cfg(not(target_os = "redox"))]
+ Raw = libc::SOCK_RAW,
+ /// Provides a reliable datagram layer that does not
+ /// guarantee ordering.
+ #[cfg(not(any(target_os = "haiku", target_os = "redox")))]
+ Rdm = libc::SOCK_RDM,
+}
+// The TryFrom impl could've been derived using libc_enum!. But for
+// backwards-compatibility with Nix-0.25.0 we manually implement it, so as to
+// keep the old variant names.
+impl TryFrom<i32> for SockType {
+ type Error = crate::Error;
+
+ fn try_from(x: i32) -> Result<Self> {
+ match x {
+ libc::SOCK_STREAM => Ok(Self::Stream),
+ libc::SOCK_DGRAM => Ok(Self::Datagram),
+ libc::SOCK_SEQPACKET => Ok(Self::SeqPacket),
+ #[cfg(not(target_os = "redox"))]
+ libc::SOCK_RAW => Ok(Self::Raw),
+ #[cfg(not(any(target_os = "haiku", target_os = "redox")))]
+ libc::SOCK_RDM => Ok(Self::Rdm),
+ _ => Err(Errno::EINVAL),
+ }
+ }
+}
+
+/// 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)]
+#[non_exhaustive]
+pub enum SockProtocol {
+ /// TCP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html))
+ Tcp = libc::IPPROTO_TCP,
+ /// UDP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html))
+ Udp = libc::IPPROTO_UDP,
+ /// Raw sockets ([raw(7)](https://man7.org/linux/man-pages/man7/raw.7.html))
+ Raw = libc::IPPROTO_RAW,
+ /// 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ KextControl = libc::SYSPROTO_CONTROL,
+ /// Receives routing and link updates and may be used to modify the routing tables (both IPv4 and IPv6), IP addresses, link
+ // parameters, neighbor setups, queueing disciplines, traffic classes and packet classifiers
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkRoute = libc::NETLINK_ROUTE,
+ /// Reserved for user-mode socket protocols
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkUserSock = libc::NETLINK_USERSOCK,
+ /// Query information about sockets of various protocol families from the kernel
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkSockDiag = libc::NETLINK_SOCK_DIAG,
+ /// Netfilter/iptables ULOG.
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkNFLOG = libc::NETLINK_NFLOG,
+ /// SELinux event notifications.
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkSELinux = libc::NETLINK_SELINUX,
+ /// Open-iSCSI
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkISCSI = libc::NETLINK_ISCSI,
+ /// Auditing
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkAudit = libc::NETLINK_AUDIT,
+ /// Access to FIB lookup from user space
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkFIBLookup = libc::NETLINK_FIB_LOOKUP,
+ /// Netfilter subsystem
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkNetFilter = libc::NETLINK_NETFILTER,
+ /// SCSI Transports
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkSCSITransport = libc::NETLINK_SCSITRANSPORT,
+ /// Infiniband RDMA
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkRDMA = libc::NETLINK_RDMA,
+ /// Transport IPv6 packets from netfilter to user space. Used by ip6_queue kernel module.
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkIPv6Firewall = libc::NETLINK_IP6_FW,
+ /// DECnet routing messages
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkDECNetRoutingMessage = libc::NETLINK_DNRTMSG,
+ /// Kernel messages to user space
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkKObjectUEvent = libc::NETLINK_KOBJECT_UEVENT,
+ /// Generic netlink family for simplified netlink usage.
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkGeneric = libc::NETLINK_GENERIC,
+ /// Netlink interface to request information about ciphers registered with the kernel crypto API as well as allow
+ /// configuration of the kernel crypto API.
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkCrypto = libc::NETLINK_CRYPTO,
+ /// Non-DIX type protocol number defined for the Ethernet IEEE 802.3 interface that allows packets of all protocols
+ /// defined in the interface to be received.
+ /// ([ref](https://man7.org/linux/man-pages/man7/packet.7.html))
+ // The protocol number is fed into the socket syscall in network byte order.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ EthAll = (libc::ETH_P_ALL as u16).to_be() as i32,
+ /// The Controller Area Network raw socket protocol
+ /// ([ref](https://docs.kernel.org/networking/can.html#how-to-use-socketcan))
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CanRaw = libc::CAN_RAW,
+}
+
+impl SockProtocol {
+ /// The Controller Area Network broadcast manager protocol
+ /// ([ref](https://docs.kernel.org/networking/can.html#how-to-use-socketcan))
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[allow(non_upper_case_globals)]
+ pub const CanBcm: SockProtocol = SockProtocol::NetlinkUserSock; // Matches libc::CAN_BCM
+}
+#[cfg(any(target_os = "android", target_os = "linux"))]
+libc_bitflags! {
+ /// Configuration flags for `SO_TIMESTAMPING` interface
+ ///
+ /// For use with [`Timestamping`][sockopt::Timestamping].
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ pub struct TimestampingFlag: libc::c_uint {
+ /// Report any software timestamps when available.
+ SOF_TIMESTAMPING_SOFTWARE;
+ /// Report hardware timestamps as generated by SOF_TIMESTAMPING_TX_HARDWARE when available.
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+ /// Collect transmitting timestamps as reported by hardware
+ SOF_TIMESTAMPING_TX_HARDWARE;
+ /// Collect transmitting timestamps as reported by software
+ SOF_TIMESTAMPING_TX_SOFTWARE;
+ /// Collect receiving timestamps as reported by hardware
+ SOF_TIMESTAMPING_RX_HARDWARE;
+ /// Collect receiving timestamps as reported by software
+ SOF_TIMESTAMPING_RX_SOFTWARE;
+ /// Generate a unique identifier along with each transmitted packet
+ SOF_TIMESTAMPING_OPT_ID;
+ /// Return transmit timestamps alongside an empty packet instead of the original packet
+ SOF_TIMESTAMPING_OPT_TSONLY;
+ }
+}
+
+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 = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SOCK_NONBLOCK;
+ /// Set close-on-exec on the new descriptor
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SOCK_CLOEXEC;
+ /// Return `EPIPE` instead of raising `SIGPIPE`
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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;
+ /// Receive operation blocks until the full amount of data can be
+ /// returned. The function may return smaller amount of data if a signal
+ /// is caught, an error or disconnect occurs.
+ MSG_WAITALL;
+ /// 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)](https://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.
+ #[cfg(not(target_os = "aix"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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)](https://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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MSG_CMSG_CLOEXEC;
+ /// Requests not to send `SIGPIPE` errors when the other end breaks the connection.
+ /// (For more details, see [send(2)](https://linux.die.net/man/2/send)).
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MSG_NOSIGNAL;
+ /// Turns on [`MSG_DONTWAIT`] after the first message has been received (only for
+ /// `recvmmsg()`).
+ #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MSG_WAITFORONE;
+ }
+}
+
+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
+ /// and the `SCM_CREDENTIALS` control message for UNIX sockets.
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ pub struct UnixCredentials(libc::ucred);
+
+ impl UnixCredentials {
+ /// Creates a new instance with the credentials of the current process
+ pub fn new() -> Self {
+ // Safe because these FFI functions are inherently safe
+ unsafe {
+ UnixCredentials(libc::ucred {
+ pid: libc::getpid(),
+ uid: libc::getuid(),
+ gid: libc::getgid()
+ })
+ }
+ }
+
+ /// 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 Default for UnixCredentials {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+
+ impl From<libc::ucred> for UnixCredentials {
+ fn from(cred: libc::ucred) -> Self {
+ UnixCredentials(cred)
+ }
+ }
+
+ impl From<UnixCredentials> for libc::ucred {
+ fn from(uc: UnixCredentials) -> Self {
+ uc.0
+ }
+ }
+ } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] {
+ /// Unix credentials of the sending process.
+ ///
+ /// This struct is used with the `SCM_CREDS` ancillary message for UNIX sockets.
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ pub struct UnixCredentials(libc::cmsgcred);
+
+ impl UnixCredentials {
+ /// Returns the process identifier
+ pub fn pid(&self) -> libc::pid_t {
+ self.0.cmcred_pid
+ }
+
+ /// Returns the real user identifier
+ pub fn uid(&self) -> libc::uid_t {
+ self.0.cmcred_uid
+ }
+
+ /// Returns the effective user identifier
+ pub fn euid(&self) -> libc::uid_t {
+ self.0.cmcred_euid
+ }
+
+ /// Returns the real group identifier
+ pub fn gid(&self) -> libc::gid_t {
+ self.0.cmcred_gid
+ }
+
+ /// Returns a list group identifiers (the first one being the effective GID)
+ pub fn groups(&self) -> &[libc::gid_t] {
+ unsafe {
+ std::slice::from_raw_parts(
+ self.0.cmcred_groups.as_ptr() as *const libc::gid_t,
+ self.0.cmcred_ngroups as _
+ )
+ }
+ }
+ }
+
+ impl From<libc::cmsgcred> for UnixCredentials {
+ fn from(cred: libc::cmsgcred) -> Self {
+ UnixCredentials(cred)
+ }
+ }
+ }
+}
+
+cfg_if! {
+ if #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+ ))] {
+ /// Return type of [`LocalPeerCred`](crate::sys::socket::sockopt::LocalPeerCred)
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ pub struct XuCred(libc::xucred);
+
+ impl XuCred {
+ /// Structure layout version
+ pub fn version(&self) -> u32 {
+ self.0.cr_version
+ }
+
+ /// Effective user ID
+ pub fn uid(&self) -> libc::uid_t {
+ self.0.cr_uid
+ }
+
+ /// Returns a list of group identifiers (the first one being the
+ /// effective GID)
+ pub fn groups(&self) -> &[libc::gid_t] {
+ &self.0.cr_groups
+ }
+ }
+ }
+}
+
+feature! {
+#![feature = "net"]
+/// Request for multicast socket operations
+///
+/// This is a wrapper type around `ip_mreq`.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+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: net::Ipv4Addr, interface: Option<net::Ipv4Addr>)
+ -> Self
+ {
+ let imr_addr = match interface {
+ None => net::Ipv4Addr::UNSPECIFIED,
+ Some(addr) => addr
+ };
+ IpMembershipRequest(libc::ip_mreq {
+ imr_multiaddr: ipv4addr_to_libc(group),
+ imr_interface: ipv4addr_to_libc(imr_addr)
+ })
+ }
+}
+
+/// Request for ipv6 multicast socket operations
+///
+/// This is a wrapper type around `ipv6_mreq`.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct Ipv6MembershipRequest(libc::ipv6_mreq);
+
+impl Ipv6MembershipRequest {
+ /// Instantiate a new `Ipv6MembershipRequest`
+ pub const fn new(group: net::Ipv6Addr) -> Self {
+ Ipv6MembershipRequest(libc::ipv6_mreq {
+ ipv6mr_multiaddr: ipv6addr_to_libc(&group),
+ ipv6mr_interface: 0,
+ })
+ }
+}
+}
+
+#[cfg(not(target_os = "redox"))]
+feature! {
+#![feature = "uio"]
+
+/// Create a buffer large enough for storing some control messages as returned
+/// by [`recvmsg`](fn.recvmsg.html).
+///
+/// # Examples
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// # use nix::sys::time::TimeVal;
+/// # use std::os::unix::io::RawFd;
+/// # fn main() {
+/// // Create a buffer for a `ControlMessageOwned::ScmTimestamp` message
+/// let _ = cmsg_space!(TimeVal);
+/// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message
+/// // with two file descriptors
+/// let _ = cmsg_space!([RawFd; 2]);
+/// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message
+/// // and a `ControlMessageOwned::ScmTimestamp` message
+/// let _ = cmsg_space!(RawFd, TimeVal);
+/// # }
+/// ```
+// Unfortunately, CMSG_SPACE isn't a const_fn, or else we could return a
+// stack-allocated array.
+#[macro_export]
+macro_rules! cmsg_space {
+ ( $( $x:ty ),* ) => {
+ {
+ let space = 0 $(+ $crate::sys::socket::cmsg_space::<$x>())*;
+ Vec::<u8>::with_capacity(space)
+ }
+ }
+}
+
+#[inline]
+#[doc(hidden)]
+pub fn cmsg_space<T>() -> usize {
+ // SAFETY: CMSG_SPACE is always safe
+ unsafe { libc::CMSG_SPACE(mem::size_of::<T>() as libc::c_uint) as usize }
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+/// Contains outcome of sending or receiving a message
+///
+/// Use [`cmsgs`][RecvMsg::cmsgs] to access all the control messages present, and
+/// [`iovs`][RecvMsg::iovs`] to access underlying io slices.
+pub struct RecvMsg<'a, 's, S> {
+ pub bytes: usize,
+ cmsghdr: Option<&'a cmsghdr>,
+ pub address: Option<S>,
+ pub flags: MsgFlags,
+ iobufs: std::marker::PhantomData<& 's()>,
+ mhdr: msghdr,
+}
+
+impl<'a, S> RecvMsg<'a, '_, S> {
+ /// Iterate over the valid control messages pointed to by this
+ /// msghdr.
+ pub fn cmsgs(&self) -> CmsgIterator {
+ CmsgIterator {
+ cmsghdr: self.cmsghdr,
+ mhdr: &self.mhdr
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct CmsgIterator<'a> {
+ /// Control message buffer to decode from. Must adhere to cmsg alignment.
+ cmsghdr: Option<&'a cmsghdr>,
+ mhdr: &'a msghdr
+}
+
+impl<'a> Iterator for CmsgIterator<'a> {
+ type Item = ControlMessageOwned;
+
+ fn next(&mut self) -> Option<ControlMessageOwned> {
+ match self.cmsghdr {
+ None => None, // No more messages
+ Some(hdr) => {
+ // Get the data.
+ // Safe if cmsghdr points to valid data returned by recvmsg(2)
+ let cm = unsafe { Some(ControlMessageOwned::decode_from(hdr))};
+ // Advance the internal pointer. Safe if mhdr and cmsghdr point
+ // to valid data returned by recvmsg(2)
+ self.cmsghdr = unsafe {
+ let p = CMSG_NXTHDR(self.mhdr as *const _, hdr as *const _);
+ p.as_ref()
+ };
+ cm
+ }
+ }
+ }
+}
+
+/// A type-safe wrapper around a single control message, as used with
+/// [`recvmsg`](#fn.recvmsg).
+///
+/// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html)
+// Nix version 0.13.0 and earlier used ControlMessage for both recvmsg and
+// sendmsg. However, on some platforms the messages returned by recvmsg may be
+// unaligned. ControlMessageOwned takes those messages by copy, obviating any
+// alignment issues.
+//
+// See https://github.com/nix-rust/nix/issues/999
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[non_exhaustive]
+pub enum ControlMessageOwned {
+ /// Received version of [`ControlMessage::ScmRights`]
+ ScmRights(Vec<RawFd>),
+ /// Received version of [`ControlMessage::ScmCredentials`]
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmCredentials(UnixCredentials),
+ /// Received version of [`ControlMessage::ScmCreds`]
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmCreds(UnixCredentials),
+ /// 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
+ ///
+ /// ```
+ /// # #[macro_use] extern crate nix;
+ /// # use nix::sys::socket::*;
+ /// # use nix::sys::time::*;
+ /// # use std::io::{IoSlice, IoSliceMut};
+ /// # use std::time::*;
+ /// # use std::str::FromStr;
+ /// # use std::os::unix::io::AsRawFd;
+ /// # fn main() {
+ /// // 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 = SockaddrIn::from_str("127.0.0.1:0").unwrap();
+ /// bind(in_socket.as_raw_fd(), &localhost).unwrap();
+ /// let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).unwrap();
+ /// // Get initial time
+ /// let time0 = SystemTime::now();
+ /// // Send the message
+ /// let iov = [IoSlice::new(message)];
+ /// let flags = MsgFlags::empty();
+ /// let l = sendmsg(in_socket.as_raw_fd(), &iov, &[], flags, Some(&address)).unwrap();
+ /// assert_eq!(message.len(), l);
+ /// // Receive the message
+ /// let mut buffer = vec![0u8; message.len()];
+ /// let mut cmsgspace = cmsg_space!(TimeVal);
+ /// let mut iov = [IoSliceMut::new(&mut buffer)];
+ /// let r = recvmsg::<SockaddrIn>(in_socket.as_raw_fd(), &mut iov, Some(&mut cmsgspace), flags)
+ /// .unwrap();
+ /// let rtime = match r.cmsgs().next() {
+ /// Some(ControlMessageOwned::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
+ /// # }
+ /// ```
+ ScmTimestamp(TimeVal),
+ /// A set of nanosecond resolution timestamps
+ ///
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ScmTimestampsns(Timestamps),
+ /// Nanoseconds resolution timestamp
+ ///
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmTimestampns(TimeSpec),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4PacketInfo(libc::in_pktinfo),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6PacketInfo(libc::in6_pktinfo),
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4RecvIf(libc::sockaddr_dl),
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4RecvDstAddr(libc::in_addr),
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4OrigDstAddr(libc::sockaddr_in),
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6OrigDstAddr(libc::sockaddr_in6),
+
+ /// UDP Generic Receive Offload (GRO) allows receiving multiple UDP
+ /// packets from a single sender.
+ /// Fixed-size payloads are following one by one in a receive buffer.
+ /// This Control Message indicates the size of all smaller packets,
+ /// except, maybe, the last one.
+ ///
+ /// `UdpGroSegment` socket option should be enabled on a socket
+ /// to allow receiving GRO packets.
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ UdpGroSegments(u16),
+
+ /// SO_RXQ_OVFL indicates that an unsigned 32 bit value
+ /// ancilliary msg (cmsg) should be attached to recieved
+ /// skbs indicating the number of packets dropped by the
+ /// socket between the last recieved packet and this
+ /// received packet.
+ ///
+ /// `RxqOvfl` socket option should be enabled on a socket
+ /// to allow receiving the drop counter.
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ RxqOvfl(u32),
+
+ /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4RecvErr(libc::sock_extended_err, Option<sockaddr_in>),
+ /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6RecvErr(libc::sock_extended_err, Option<sockaddr_in6>),
+
+ /// Catch-all variant for unimplemented cmsg types.
+ #[doc(hidden)]
+ Unknown(UnknownCmsg),
+}
+
+/// For representing packet timestamps via `SO_TIMESTAMPING` interface
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct Timestamps {
+ /// software based timestamp, usually one containing data
+ pub system: TimeSpec,
+ /// legacy timestamp, usually empty
+ pub hw_trans: TimeSpec,
+ /// hardware based timestamp
+ pub hw_raw: TimeSpec,
+}
+
+impl ControlMessageOwned {
+ /// Decodes a `ControlMessageOwned` 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.
+ // Clippy complains about the pointer alignment of `p`, not understanding
+ // that it's being fed to a function that can handle that.
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe fn decode_from(header: &cmsghdr) -> ControlMessageOwned
+ {
+ let p = CMSG_DATA(header);
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ let len = header as *const _ as usize + header.cmsg_len as usize
+ - p as usize;
+ match (header.cmsg_level, header.cmsg_type) {
+ (libc::SOL_SOCKET, libc::SCM_RIGHTS) => {
+ let n = len / mem::size_of::<RawFd>();
+ let mut fds = Vec::with_capacity(n);
+ for i in 0..n {
+ let fdp = (p as *const RawFd).add(i);
+ fds.push(ptr::read_unaligned(fdp));
+ }
+ ControlMessageOwned::ScmRights(fds)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SCM_CREDENTIALS) => {
+ let cred: libc::ucred = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmCredentials(cred.into())
+ }
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ (libc::SOL_SOCKET, libc::SCM_CREDS) => {
+ let cred: libc::cmsgcred = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmCreds(cred.into())
+ }
+ #[cfg(not(any(target_os = "aix", target_os = "haiku")))]
+ (libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => {
+ let tv: libc::timeval = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => {
+ let ts: libc::timespec = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
+ let tp = p as *const libc::timespec;
+ let ts: libc::timespec = ptr::read_unaligned(tp);
+ let system = TimeSpec::from(ts);
+ let ts: libc::timespec = ptr::read_unaligned(tp.add(1));
+ let hw_trans = TimeSpec::from(ts);
+ let ts: libc::timespec = ptr::read_unaligned(tp.add(2));
+ let hw_raw = TimeSpec::from(ts);
+ let timestamping = Timestamps { system, hw_trans, hw_raw };
+ ControlMessageOwned::ScmTimestampsns(timestamping)
+ }
+ #[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
+ let info = ptr::read_unaligned(p as *const libc::in6_pktinfo);
+ ControlMessageOwned::Ipv6PacketInfo(info)
+ }
+ #[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_PKTINFO) => {
+ let info = ptr::read_unaligned(p as *const libc::in_pktinfo);
+ ControlMessageOwned::Ipv4PacketInfo(info)
+ }
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_RECVIF) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_dl);
+ ControlMessageOwned::Ipv4RecvIf(dl)
+ },
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::in_addr);
+ ControlMessageOwned::Ipv4RecvDstAddr(dl)
+ },
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_ORIGDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_in);
+ ControlMessageOwned::Ipv4OrigDstAddr(dl)
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ (libc::SOL_UDP, libc::UDP_GRO) => {
+ let gso_size: u16 = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::UdpGroSegments(gso_size)
+ },
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SO_RXQ_OVFL) => {
+ let drop_counter = ptr::read_unaligned(p as *const u32);
+ ControlMessageOwned::RxqOvfl(drop_counter)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_RECVERR) => {
+ let (err, addr) = Self::recv_err_helper::<sockaddr_in>(p, len);
+ ControlMessageOwned::Ipv4RecvErr(err, addr)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => {
+ let (err, addr) = Self::recv_err_helper::<sockaddr_in6>(p, len);
+ ControlMessageOwned::Ipv6RecvErr(err, addr)
+ },
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_in6);
+ ControlMessageOwned::Ipv6OrigDstAddr(dl)
+ },
+ (_, _) => {
+ let sl = std::slice::from_raw_parts(p, len);
+ let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
+ ControlMessageOwned::Unknown(ucmsg)
+ }
+ }
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[allow(clippy::cast_ptr_alignment)] // False positive
+ unsafe fn recv_err_helper<T>(p: *mut libc::c_uchar, len: usize) -> (libc::sock_extended_err, Option<T>) {
+ let ee = p as *const libc::sock_extended_err;
+ let err = ptr::read_unaligned(ee);
+
+ // For errors originating on the network, SO_EE_OFFENDER(ee) points inside the p[..len]
+ // CMSG_DATA buffer. For local errors, there is no address included in the control
+ // message, and SO_EE_OFFENDER(ee) points beyond the end of the buffer. So, we need to
+ // validate that the address object is in-bounds before we attempt to copy it.
+ let addrp = libc::SO_EE_OFFENDER(ee) as *const T;
+
+ if addrp.offset(1) as usize - (p as usize) > len {
+ (err, None)
+ } else {
+ (err, Some(ptr::read_unaligned(addrp)))
+ }
+ }
+}
+
+/// A type-safe zero-copy wrapper around a single control message, as used wih
+/// [`sendmsg`](#fn.sendmsg). More types may be added to this enum; do not
+/// exhaustively pattern-match it.
+///
+/// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html)
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[non_exhaustive]
+pub enum ControlMessage<'a> {
+ /// A message of type `SCM_RIGHTS`, containing an array of file
+ /// descriptors passed between processes.
+ ///
+ /// See the description in the "Ancillary messages" section of the
+ /// [unix(7) man page](https://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)`](https://man7.org/linux/man-pages/man7/unix.7.html) man page.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmCredentials(&'a UnixCredentials),
+ /// A message of type `SCM_CREDS`, containing the pid, uid, euid, gid and groups of
+ /// a process connected to the socket.
+ ///
+ /// This is similar to the socket options `LOCAL_CREDS` and `LOCAL_PEERCRED`, but
+ /// requires a process to explicitly send its credentials.
+ ///
+ /// Credentials are always overwritten by the kernel, so this variant does have
+ /// any data, unlike the receive-side
+ /// [`ControlMessageOwned::ScmCreds`].
+ ///
+ /// For further information, please refer to the
+ /// [`unix(4)`](https://www.freebsd.org/cgi/man.cgi?query=unix) man page.
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmCreds,
+
+ /// Set IV for `AF_ALG` crypto API.
+ ///
+ /// For further information, please refer to the
+ /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html)
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AlgSetIv(&'a [u8]),
+ /// Set crypto operation for `AF_ALG` crypto API. It may be one of
+ /// `ALG_OP_ENCRYPT` or `ALG_OP_DECRYPT`
+ ///
+ /// For further information, please refer to the
+ /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html)
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AlgSetOp(&'a libc::c_int),
+ /// Set the length of associated authentication data (AAD) (applicable only to AEAD algorithms)
+ /// for `AF_ALG` crypto API.
+ ///
+ /// For further information, please refer to the
+ /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html)
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AlgSetAeadAssoclen(&'a u32),
+
+ /// UDP GSO makes it possible for applications to generate network packets
+ /// for a virtual MTU much greater than the real one.
+ /// The length of the send data no longer matches the expected length on
+ /// the wire.
+ /// The size of the datagram payload as it should appear on the wire may be
+ /// passed through this control message.
+ /// Send buffer should consist of multiple fixed-size wire payloads
+ /// following one by one, and the last, possibly smaller one.
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ UdpGsoSegments(&'a u16),
+
+ /// Configure the sending addressing and interface for v4
+ ///
+ /// For further information, please refer to the
+ /// [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html) man page.
+ #[cfg(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4PacketInfo(&'a libc::in_pktinfo),
+
+ /// Configure the sending addressing and interface for v6
+ ///
+ /// For further information, please refer to the
+ /// [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html) man page.
+ #[cfg(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6PacketInfo(&'a libc::in6_pktinfo),
+
+ /// Configure the IPv4 source address with `IP_SENDSRCADDR`.
+ #[cfg(any(
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4SendSrcAddr(&'a libc::in_addr),
+
+ /// SO_RXQ_OVFL indicates that an unsigned 32 bit value
+ /// ancilliary msg (cmsg) should be attached to recieved
+ /// skbs indicating the number of packets dropped by the
+ /// socket between the last recieved packet and this
+ /// received packet.
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ RxqOvfl(&'a u32),
+
+ /// Configure the transmission time of packets.
+ ///
+ /// For further information, please refer to the
+ /// [`tc-etf(8)`](https://man7.org/linux/man-pages/man8/tc-etf.8.html) man
+ /// page.
+ #[cfg(target_os = "linux")]
+ TxTime(&'a u64),
+}
+
+// An opaque structure used to prevent cmsghdr from being a public type
+#[doc(hidden)]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct UnknownCmsg(cmsghdr, Vec<u8>);
+
+impl<'a> ControlMessage<'a> {
+ /// The value of CMSG_SPACE on this message.
+ /// Safe because CMSG_SPACE is always safe
+ fn space(&self) -> usize {
+ unsafe{CMSG_SPACE(self.len() as libc::c_uint) as usize}
+ }
+
+ /// The value of CMSG_LEN on this message.
+ /// Safe because CMSG_LEN is always safe
+ #[cfg(any(target_os = "android",
+ all(target_os = "linux", not(target_env = "musl"))))]
+ fn cmsg_len(&self) -> usize {
+ unsafe{CMSG_LEN(self.len() as libc::c_uint) as usize}
+ }
+
+ #[cfg(not(any(target_os = "android",
+ all(target_os = "linux", not(target_env = "musl")))))]
+ fn cmsg_len(&self) -> libc::c_uint {
+ unsafe{CMSG_LEN(self.len() as libc::c_uint)}
+ }
+
+ /// Return a reference to the payload data as a byte pointer
+ fn copy_to_cmsg_data(&self, cmsg_data: *mut u8) {
+ let data_ptr = match *self {
+ ControlMessage::ScmRights(fds) => {
+ fds as *const _ as *const u8
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::ScmCredentials(creds) => {
+ &creds.0 as *const libc::ucred as *const u8
+ }
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => {
+ // The kernel overwrites the data, we just zero it
+ // to make sure it's not uninitialized memory
+ unsafe { ptr::write_bytes(cmsg_data, 0, self.len()) };
+ return
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetIv(iv) => {
+ #[allow(deprecated)] // https://github.com/rust-lang/libc/issues/1501
+ let af_alg_iv = libc::af_alg_iv {
+ ivlen: iv.len() as u32,
+ iv: [0u8; 0],
+ };
+
+ let size = mem::size_of_val(&af_alg_iv);
+
+ unsafe {
+ ptr::copy_nonoverlapping(
+ &af_alg_iv as *const _ as *const u8,
+ cmsg_data,
+ size,
+ );
+ ptr::copy_nonoverlapping(
+ iv.as_ptr(),
+ cmsg_data.add(size),
+ iv.len()
+ );
+ };
+
+ return
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetOp(op) => {
+ op as *const _ as *const u8
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetAeadAssoclen(len) => {
+ len as *const _ as *const u8
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ ControlMessage::UdpGsoSegments(gso_size) => {
+ gso_size as *const _ as *const u8
+ },
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4PacketInfo(info) => info as *const _ as *const u8,
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "freebsd",
+ target_os = "android", target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv6PacketInfo(info) => info as *const _ as *const u8,
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+ target_os = "openbsd", target_os = "dragonfly"))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4SendSrcAddr(addr) => addr as *const _ as *const u8,
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(drop_count) => {
+ drop_count as *const _ as *const u8
+ },
+ #[cfg(target_os = "linux")]
+ ControlMessage::TxTime(tx_time) => {
+ tx_time as *const _ as *const u8
+ },
+ };
+ unsafe {
+ ptr::copy_nonoverlapping(
+ data_ptr,
+ cmsg_data,
+ self.len()
+ )
+ };
+ }
+
+ /// The size of the payload, excluding its cmsghdr
+ fn len(&self) -> usize {
+ 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)
+ }
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => {
+ mem::size_of::<libc::cmsgcred>()
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetIv(iv) => {
+ mem::size_of::<&[u8]>() + iv.len()
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetOp(op) => {
+ mem::size_of_val(op)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetAeadAssoclen(len) => {
+ mem::size_of_val(len)
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ ControlMessage::UdpGsoSegments(gso_size) => {
+ mem::size_of_val(gso_size)
+ },
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4PacketInfo(info) => mem::size_of_val(info),
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "freebsd",
+ target_os = "android", target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info),
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+ target_os = "openbsd", target_os = "dragonfly"))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4SendSrcAddr(addr) => mem::size_of_val(addr),
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(drop_count) => {
+ mem::size_of_val(drop_count)
+ },
+ #[cfg(target_os = "linux")]
+ ControlMessage::TxTime(tx_time) => {
+ mem::size_of_val(tx_time)
+ },
+ }
+ }
+
+ /// 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,
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => libc::SOL_SOCKET,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetIv(_) | ControlMessage::AlgSetOp(_) |
+ ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG,
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP,
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4PacketInfo(_) => libc::IPPROTO_IP,
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "freebsd",
+ target_os = "android", target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6,
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+ target_os = "openbsd", target_os = "dragonfly"))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4SendSrcAddr(_) => libc::IPPROTO_IP,
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(_) => libc::SOL_SOCKET,
+ #[cfg(target_os = "linux")]
+ ControlMessage::TxTime(_) => libc::SOL_SOCKET,
+ }
+ }
+
+ /// 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,
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => libc::SCM_CREDS,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetIv(_) => {
+ libc::ALG_SET_IV
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetOp(_) => {
+ libc::ALG_SET_OP
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetAeadAssoclen(_) => {
+ libc::ALG_SET_AEAD_ASSOCLEN
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ ControlMessage::UdpGsoSegments(_) => {
+ libc::UDP_SEGMENT
+ },
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4PacketInfo(_) => libc::IP_PKTINFO,
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "freebsd",
+ target_os = "android", target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO,
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+ target_os = "openbsd", target_os = "dragonfly"))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4SendSrcAddr(_) => libc::IP_SENDSRCADDR,
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(_) => {
+ libc::SO_RXQ_OVFL
+ },
+ #[cfg(target_os = "linux")]
+ ControlMessage::TxTime(_) => {
+ libc::SCM_TXTIME
+ },
+ }
+ }
+
+ // Unsafe: cmsg must point to a valid cmsghdr with enough space to
+ // encode self.
+ unsafe fn encode_into(&self, cmsg: *mut cmsghdr) {
+ (*cmsg).cmsg_level = self.cmsg_level();
+ (*cmsg).cmsg_type = self.cmsg_type();
+ (*cmsg).cmsg_len = self.cmsg_len();
+ self.copy_to_cmsg_data(CMSG_DATA(cmsg));
+ }
+}
+
+
+/// 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.
+///
+/// # Examples
+/// When not directing to any specific address, use `()` for the generic type
+/// ```
+/// # use nix::sys::socket::*;
+/// # use nix::unistd::pipe;
+/// # use std::io::IoSlice;
+/// # use std::os::unix::io::AsRawFd;
+/// let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None,
+/// SockFlag::empty())
+/// .unwrap();
+/// let (r, w) = pipe().unwrap();
+///
+/// let iov = [IoSlice::new(b"hello")];
+/// let fds = [r];
+/// let cmsg = ControlMessage::ScmRights(&fds);
+/// sendmsg::<()>(fd1.as_raw_fd(), &iov, &[cmsg], MsgFlags::empty(), None).unwrap();
+/// ```
+/// When directing to a specific address, the generic type will be inferred.
+/// ```
+/// # use nix::sys::socket::*;
+/// # use nix::unistd::pipe;
+/// # use std::io::IoSlice;
+/// # use std::str::FromStr;
+/// # use std::os::unix::io::AsRawFd;
+/// let localhost = SockaddrIn::from_str("1.2.3.4:8080").unwrap();
+/// let fd = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(),
+/// None).unwrap();
+/// let (r, w) = pipe().unwrap();
+///
+/// let iov = [IoSlice::new(b"hello")];
+/// let fds = [r];
+/// let cmsg = ControlMessage::ScmRights(&fds);
+/// sendmsg(fd.as_raw_fd(), &iov, &[cmsg], MsgFlags::empty(), Some(&localhost)).unwrap();
+/// ```
+pub fn sendmsg<S>(fd: RawFd, iov: &[IoSlice<'_>], cmsgs: &[ControlMessage],
+ flags: MsgFlags, addr: Option<&S>) -> Result<usize>
+ where S: SockaddrLike
+{
+ let capacity = cmsgs.iter().map(|c| c.space()).sum();
+
+ // First size the buffer needed to hold the cmsgs. It must be zeroed,
+ // because subsequent code will not clear the padding bytes.
+ let mut cmsg_buffer = vec![0u8; capacity];
+
+ let mhdr = pack_mhdr_to_send(&mut cmsg_buffer[..], iov, cmsgs, addr);
+
+ let ret = unsafe { libc::sendmsg(fd, &mhdr, flags.bits()) };
+
+ Errno::result(ret).map(|r| r as usize)
+}
+
+
+/// An extension of `sendmsg` that allows the caller to transmit multiple
+/// messages on a socket using a single system call. This has performance
+/// benefits for some applications.
+///
+/// Allocations are performed for cmsgs and to build `msghdr` buffer
+///
+/// # Arguments
+///
+/// * `fd`: Socket file descriptor
+/// * `data`: Struct that implements `IntoIterator` with `SendMmsgData` items
+/// * `flags`: Optional flags passed directly to the operating system.
+///
+/// # Returns
+/// `Vec` with numbers of sent bytes on each sent message.
+///
+/// # References
+/// [`sendmsg`](fn.sendmsg.html)
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+pub fn sendmmsg<'a, XS, AS, C, I, S>(
+ fd: RawFd,
+ data: &'a mut MultiHeaders<S>,
+ slices: XS,
+ // one address per group of slices
+ addrs: AS,
+ // shared across all the messages
+ cmsgs: C,
+ flags: MsgFlags
+) -> crate::Result<MultiResults<'a, S>>
+ where
+ XS: IntoIterator<Item = &'a I>,
+ AS: AsRef<[Option<S>]>,
+ I: AsRef<[IoSlice<'a>]> + 'a,
+ C: AsRef<[ControlMessage<'a>]> + 'a,
+ S: SockaddrLike + 'a
+{
+
+ let mut count = 0;
+
+
+ for (i, ((slice, addr), mmsghdr)) in slices.into_iter().zip(addrs.as_ref()).zip(data.items.iter_mut() ).enumerate() {
+ let p = &mut mmsghdr.msg_hdr;
+ p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec;
+ p.msg_iovlen = slice.as_ref().len() as _;
+
+ p.msg_namelen = addr.as_ref().map_or(0, S::len);
+ p.msg_name = addr.as_ref().map_or(ptr::null(), S::as_ptr) as _;
+
+ // Encode each cmsg. This must happen after initializing the header because
+ // CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields.
+ // CMSG_FIRSTHDR is always safe
+ let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(p) };
+ for cmsg in cmsgs.as_ref() {
+ assert_ne!(pmhdr, ptr::null_mut());
+ // Safe because we know that pmhdr is valid, and we initialized it with
+ // sufficient space
+ unsafe { cmsg.encode_into(pmhdr) };
+ // Safe because mhdr is valid
+ pmhdr = unsafe { CMSG_NXTHDR(p, pmhdr) };
+ }
+
+ count = i+1;
+ }
+
+ let sent = Errno::result(unsafe {
+ libc::sendmmsg(
+ fd,
+ data.items.as_mut_ptr(),
+ count as _,
+ flags.bits() as _
+ )
+ })? as usize;
+
+ Ok(MultiResults {
+ rmm: data,
+ current_index: 0,
+ received: sent
+ })
+
+}
+
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+#[derive(Debug)]
+/// Preallocated structures needed for [`recvmmsg`] and [`sendmmsg`] functions
+pub struct MultiHeaders<S> {
+ // preallocated boxed slice of mmsghdr
+ items: Box<[libc::mmsghdr]>,
+ addresses: Box<[mem::MaybeUninit<S>]>,
+ // while we are not using it directly - this is used to store control messages
+ // and we retain pointers to them inside items array
+ _cmsg_buffers: Option<Box<[u8]>>,
+ msg_controllen: usize,
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+impl<S> MultiHeaders<S> {
+ /// Preallocate structure used by [`recvmmsg`] and [`sendmmsg`] takes number of headers to preallocate
+ ///
+ /// `cmsg_buffer` should be created with [`cmsg_space!`] if needed
+ pub fn preallocate(num_slices: usize, cmsg_buffer: Option<Vec<u8>>) -> Self
+ where
+ S: Copy + SockaddrLike,
+ {
+ // we will be storing pointers to addresses inside mhdr - convert it into boxed
+ // slice so it can'be changed later by pushing anything into self.addresses
+ let mut addresses = vec![std::mem::MaybeUninit::<S>::uninit(); num_slices].into_boxed_slice();
+
+ let msg_controllen = cmsg_buffer.as_ref().map_or(0, |v| v.capacity());
+
+ // we'll need a cmsg_buffer for each slice, we preallocate a vector and split
+ // it into "slices" parts
+ let mut cmsg_buffers =
+ cmsg_buffer.map(|v| vec![0u8; v.capacity() * num_slices].into_boxed_slice());
+
+ let items = addresses
+ .iter_mut()
+ .enumerate()
+ .map(|(ix, address)| {
+ let (ptr, cap) = match &mut cmsg_buffers {
+ Some(v) => ((&mut v[ix * msg_controllen] as *mut u8), msg_controllen),
+ None => (std::ptr::null_mut(), 0),
+ };
+ let msg_hdr = unsafe { pack_mhdr_to_receive(std::ptr::null_mut(), 0, ptr, cap, address.as_mut_ptr()) };
+ libc::mmsghdr {
+ msg_hdr,
+ msg_len: 0,
+ }
+ })
+ .collect::<Vec<_>>();
+
+ Self {
+ items: items.into_boxed_slice(),
+ addresses,
+ _cmsg_buffers: cmsg_buffers,
+ msg_controllen,
+ }
+ }
+}
+
+/// An extension of recvmsg that allows the caller to receive multiple messages from a socket using a single system call.
+///
+/// This has performance benefits for some applications.
+///
+/// This method performs no allocations.
+///
+/// Returns an iterator producing [`RecvMsg`], one per received messages. Each `RecvMsg` can produce
+/// iterators over [`IoSlice`] with [`iovs`][RecvMsg::iovs`] and
+/// `ControlMessageOwned` with [`cmsgs`][RecvMsg::cmsgs].
+///
+/// # Bugs (in underlying implementation, at least in Linux)
+/// The timeout argument does not work as intended. The timeout is checked only after the receipt
+/// of each datagram, so that if up to `vlen`-1 datagrams are received before the timeout expires,
+/// but then no further datagrams are received, the call will block forever.
+///
+/// If an error occurs after at least one message has been received, the call succeeds, and returns
+/// the number of messages received. The error code is expected to be returned on a subsequent
+/// call to recvmmsg(). In the current implementation, however, the error code can be
+/// overwritten in the meantime by an unrelated network event on a socket, for example an
+/// incoming ICMP packet.
+
+// On aarch64 linux using recvmmsg and trying to get hardware/kernel timestamps might not
+// always produce the desired results - see https://github.com/nix-rust/nix/pull/1744 for more
+// details
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+pub fn recvmmsg<'a, XS, S, I>(
+ fd: RawFd,
+ data: &'a mut MultiHeaders<S>,
+ slices: XS,
+ flags: MsgFlags,
+ mut timeout: Option<crate::sys::time::TimeSpec>,
+) -> crate::Result<MultiResults<'a, S>>
+where
+ XS: IntoIterator<Item = &'a I>,
+ I: AsRef<[IoSliceMut<'a>]> + 'a,
+{
+ let mut count = 0;
+ for (i, (slice, mmsghdr)) in slices.into_iter().zip(data.items.iter_mut()).enumerate() {
+ let p = &mut mmsghdr.msg_hdr;
+ p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec;
+ p.msg_iovlen = slice.as_ref().len() as _;
+ count = i + 1;
+ }
+
+ let timeout_ptr = timeout
+ .as_mut()
+ .map_or_else(std::ptr::null_mut, |t| t as *mut _ as *mut libc::timespec);
+
+ let received = Errno::result(unsafe {
+ libc::recvmmsg(
+ fd,
+ data.items.as_mut_ptr(),
+ count as _,
+ flags.bits() as _,
+ timeout_ptr,
+ )
+ })? as usize;
+
+ Ok(MultiResults {
+ rmm: data,
+ current_index: 0,
+ received,
+ })
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+#[derive(Debug)]
+/// Iterator over results of [`recvmmsg`]/[`sendmmsg`]
+///
+///
+pub struct MultiResults<'a, S> {
+ // preallocated structures
+ rmm: &'a MultiHeaders<S>,
+ current_index: usize,
+ received: usize,
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+impl<'a, S> Iterator for MultiResults<'a, S>
+where
+ S: Copy + SockaddrLike,
+{
+ type Item = RecvMsg<'a, 'a, S>;
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.current_index >= self.received {
+ return None;
+ }
+ let mmsghdr = self.rmm.items[self.current_index];
+
+ // as long as we are not reading past the index writen by recvmmsg - address
+ // will be initialized
+ let address = unsafe { self.rmm.addresses[self.current_index].assume_init() };
+
+ self.current_index += 1;
+ Some(unsafe {
+ read_mhdr(
+ mmsghdr.msg_hdr,
+ mmsghdr.msg_len as isize,
+ self.rmm.msg_controllen,
+ address,
+ )
+ })
+ }
+}
+
+impl<'a, S> RecvMsg<'_, 'a, S> {
+ /// Iterate over the filled io slices pointed by this msghdr
+ pub fn iovs(&self) -> IoSliceIterator<'a> {
+ IoSliceIterator {
+ index: 0,
+ remaining: self.bytes,
+ slices: unsafe {
+ // safe for as long as mgdr is properly initialized and references are valid.
+ // for multi messages API we initialize it with an empty
+ // slice and replace with a concrete buffer
+ // for single message API we hold a lifetime reference to ioslices
+ std::slice::from_raw_parts(self.mhdr.msg_iov as *const _, self.mhdr.msg_iovlen as _)
+ },
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct IoSliceIterator<'a> {
+ index: usize,
+ remaining: usize,
+ slices: &'a [IoSlice<'a>],
+}
+
+impl<'a> Iterator for IoSliceIterator<'a> {
+ type Item = &'a [u8];
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.index >= self.slices.len() {
+ return None;
+ }
+ let slice = &self.slices[self.index][..self.remaining.min(self.slices[self.index].len())];
+ self.remaining -= slice.len();
+ self.index += 1;
+ if slice.is_empty() {
+ return None;
+ }
+
+ Some(slice)
+ }
+}
+
+// test contains both recvmmsg and timestaping which is linux only
+// there are existing tests for recvmmsg only in tests/
+#[cfg(target_os = "linux")]
+#[cfg(test)]
+mod test {
+ use crate::sys::socket::{AddressFamily, ControlMessageOwned};
+ use crate::*;
+ use std::str::FromStr;
+ use std::os::unix::io::AsRawFd;
+
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_recvmm2() -> crate::Result<()> {
+ use crate::sys::socket::{
+ sendmsg, setsockopt, socket, sockopt::Timestamping, MsgFlags, SockFlag, SockType,
+ SockaddrIn, TimestampingFlag,
+ };
+ use std::io::{IoSlice, IoSliceMut};
+
+ let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap();
+
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )?;
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::SOCK_NONBLOCK,
+ None,
+ )?;
+
+ crate::sys::socket::bind(rsock.as_raw_fd(), &sock_addr)?;
+
+ setsockopt(&rsock, Timestamping, &TimestampingFlag::all())?;
+
+ let sbuf = (0..400).map(|i| i as u8).collect::<Vec<_>>();
+
+ let mut recv_buf = vec![0; 1024];
+
+ let mut recv_iovs = Vec::new();
+ let mut pkt_iovs = Vec::new();
+
+ for (ix, chunk) in recv_buf.chunks_mut(256).enumerate() {
+ pkt_iovs.push(IoSliceMut::new(chunk));
+ if ix % 2 == 1 {
+ recv_iovs.push(pkt_iovs);
+ pkt_iovs = Vec::new();
+ }
+ }
+ drop(pkt_iovs);
+
+ let flags = MsgFlags::empty();
+ let iov1 = [IoSlice::new(&sbuf)];
+
+ let cmsg = cmsg_space!(crate::sys::socket::Timestamps);
+ sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap();
+
+ let mut data = super::MultiHeaders::<()>::preallocate(recv_iovs.len(), Some(cmsg));
+
+ let t = sys::time::TimeSpec::from_duration(std::time::Duration::from_secs(10));
+
+ let recv = super::recvmmsg(rsock.as_raw_fd(), &mut data, recv_iovs.iter(), flags, Some(t))?;
+
+ for rmsg in recv {
+ #[cfg(not(any(qemu, target_arch = "aarch64")))]
+ let mut saw_time = false;
+ let mut recvd = 0;
+ for cmsg in rmsg.cmsgs() {
+ if let ControlMessageOwned::ScmTimestampsns(timestamps) = cmsg {
+ let ts = timestamps.system;
+
+ let sys_time =
+ crate::time::clock_gettime(crate::time::ClockId::CLOCK_REALTIME)?;
+ let diff = if ts > sys_time {
+ ts - sys_time
+ } else {
+ sys_time - ts
+ };
+ assert!(std::time::Duration::from(diff).as_secs() < 60);
+ #[cfg(not(any(qemu, target_arch = "aarch64")))]
+ {
+ saw_time = true;
+ }
+ }
+ }
+
+ #[cfg(not(any(qemu, target_arch = "aarch64")))]
+ assert!(saw_time);
+
+ for iov in rmsg.iovs() {
+ recvd += iov.len();
+ }
+ assert_eq!(recvd, 400);
+ }
+
+ Ok(())
+ }
+}
+unsafe fn read_mhdr<'a, 'i, S>(
+ mhdr: msghdr,
+ r: isize,
+ msg_controllen: usize,
+ mut address: S,
+) -> RecvMsg<'a, 'i, S>
+ where S: SockaddrLike
+{
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ let cmsghdr = {
+ if mhdr.msg_controllen > 0 {
+ debug_assert!(!mhdr.msg_control.is_null());
+ debug_assert!(msg_controllen >= mhdr.msg_controllen as usize);
+ CMSG_FIRSTHDR(&mhdr as *const msghdr)
+ } else {
+ ptr::null()
+ }.as_ref()
+ };
+
+ // Ignore errors if this socket address has statically-known length
+ //
+ // This is to ensure that unix socket addresses have their length set appropriately.
+ let _ = address.set_length(mhdr.msg_namelen as usize);
+
+ RecvMsg {
+ bytes: r as usize,
+ cmsghdr,
+ address: Some(address),
+ flags: MsgFlags::from_bits_truncate(mhdr.msg_flags),
+ mhdr,
+ iobufs: std::marker::PhantomData,
+ }
+}
+
+/// Pack pointers to various structures into into msghdr
+///
+/// # Safety
+/// `iov_buffer` and `iov_buffer_len` must point to a slice
+/// of `IoSliceMut` and number of available elements or be a null pointer and 0
+///
+/// `cmsg_buffer` and `cmsg_capacity` must point to a byte buffer used
+/// to store control headers later or be a null pointer and 0 if control
+/// headers are not used
+///
+/// Buffers must remain valid for the whole lifetime of msghdr
+unsafe fn pack_mhdr_to_receive<S>(
+ iov_buffer: *mut IoSliceMut,
+ iov_buffer_len: usize,
+ cmsg_buffer: *mut u8,
+ cmsg_capacity: usize,
+ address: *mut S,
+) -> msghdr
+ where
+ S: SockaddrLike
+{
+ // Musl's msghdr has private fields, so this is the only way to
+ // initialize it.
+ let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
+ let p = mhdr.as_mut_ptr();
+ (*p).msg_name = address as *mut c_void;
+ (*p).msg_namelen = S::size();
+ (*p).msg_iov = iov_buffer as *mut iovec;
+ (*p).msg_iovlen = iov_buffer_len as _;
+ (*p).msg_control = cmsg_buffer as *mut c_void;
+ (*p).msg_controllen = cmsg_capacity as _;
+ (*p).msg_flags = 0;
+ mhdr.assume_init()
+}
+
+fn pack_mhdr_to_send<'a, I, C, S>(
+ cmsg_buffer: &mut [u8],
+ iov: I,
+ cmsgs: C,
+ addr: Option<&S>
+) -> msghdr
+ where
+ I: AsRef<[IoSlice<'a>]>,
+ C: AsRef<[ControlMessage<'a>]>,
+ S: SockaddrLike + 'a
+{
+ let capacity = cmsg_buffer.len();
+
+ // The message header must be initialized before the individual cmsgs.
+ let cmsg_ptr = if capacity > 0 {
+ cmsg_buffer.as_mut_ptr() as *mut c_void
+ } else {
+ ptr::null_mut()
+ };
+
+ let mhdr = unsafe {
+ // Musl's msghdr has private fields, so this is the only way to
+ // initialize it.
+ let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
+ let p = mhdr.as_mut_ptr();
+ (*p).msg_name = addr.map(S::as_ptr).unwrap_or(ptr::null()) as *mut _;
+ (*p).msg_namelen = addr.map(S::len).unwrap_or(0);
+ // transmute iov into a mutable pointer. sendmsg doesn't really mutate
+ // the buffer, but the standard says that it takes a mutable pointer
+ (*p).msg_iov = iov.as_ref().as_ptr() as *mut _;
+ (*p).msg_iovlen = iov.as_ref().len() as _;
+ (*p).msg_control = cmsg_ptr;
+ (*p).msg_controllen = capacity as _;
+ (*p).msg_flags = 0;
+ mhdr.assume_init()
+ };
+
+ // Encode each cmsg. This must happen after initializing the header because
+ // CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields.
+ // CMSG_FIRSTHDR is always safe
+ let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(&mhdr as *const msghdr) };
+ for cmsg in cmsgs.as_ref() {
+ assert_ne!(pmhdr, ptr::null_mut());
+ // Safe because we know that pmhdr is valid, and we initialized it with
+ // sufficient space
+ unsafe { cmsg.encode_into(pmhdr) };
+ // Safe because mhdr is valid
+ pmhdr = unsafe { CMSG_NXTHDR(&mhdr as *const msghdr, pmhdr) };
+ }
+
+ mhdr
+}
+
+/// 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.
+///
+/// # Arguments
+///
+/// * `fd`: Socket file descriptor
+/// * `iov`: Scatter-gather list of buffers to receive the message
+/// * `cmsg_buffer`: Space to receive ancillary data. Should be created by
+/// [`cmsg_space!`](../../macro.cmsg_space.html)
+/// * `flags`: Optional flags passed directly to the operating system.
+///
+/// # References
+/// [recvmsg(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html)
+pub fn recvmsg<'a, 'outer, 'inner, S>(fd: RawFd, iov: &'outer mut [IoSliceMut<'inner>],
+ mut cmsg_buffer: Option<&'a mut Vec<u8>>,
+ flags: MsgFlags) -> Result<RecvMsg<'a, 'outer, S>>
+ where S: SockaddrLike + 'a,
+ 'inner: 'outer
+{
+ let mut address = mem::MaybeUninit::uninit();
+
+ let (msg_control, msg_controllen) = cmsg_buffer.as_mut()
+ .map(|v| (v.as_mut_ptr(), v.capacity()))
+ .unwrap_or((ptr::null_mut(), 0));
+ let mut mhdr = unsafe {
+ pack_mhdr_to_receive(iov.as_mut().as_mut_ptr(), iov.len(), msg_control, msg_controllen, address.as_mut_ptr())
+ };
+
+ let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) };
+
+ let r = Errno::result(ret)?;
+
+ Ok(unsafe { read_mhdr(mhdr, r, msg_controllen, address.assume_init()) })
+}
+}
+
+/// 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](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html)
+pub fn socket<T: Into<Option<SockProtocol>>>(
+ domain: AddressFamily,
+ ty: SockType,
+ flags: SockFlag,
+ protocol: T,
+) -> Result<OwnedFd> {
+ 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) };
+
+ match res {
+ -1 => Err(Errno::last()),
+ fd => {
+ // Safe because libc::socket returned success
+ unsafe { Ok(OwnedFd::from_raw_fd(fd)) }
+ }
+ }
+}
+
+/// Create a pair of connected sockets
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html)
+pub fn socketpair<T: Into<Option<SockProtocol>>>(
+ domain: AddressFamily,
+ ty: SockType,
+ protocol: T,
+ flags: SockFlag,
+) -> Result<(OwnedFd, OwnedFd)> {
+ 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)?;
+
+ // Safe because socketpair returned success.
+ unsafe {
+ Ok((OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1])))
+ }
+}
+
+/// Listen for connections on a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html)
+pub fn listen<F: AsFd>(sock: &F, backlog: usize) -> Result<()> {
+ let fd = sock.as_fd().as_raw_fd();
+ let res = unsafe { libc::listen(fd, backlog as c_int) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Bind a name to a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html)
+pub fn bind(fd: RawFd, addr: &dyn SockaddrLike) -> Result<()> {
+ let res = unsafe { libc::bind(fd, addr.as_ptr(), addr.len()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Accept a connection on a socket
+///
+/// [Further reading](https://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](https://man7.org/linux/man-pages/man2/accept.2.html)
+#[cfg(any(
+ all(
+ target_os = "android",
+ any(
+ target_arch = "aarch64",
+ target_arch = "x86",
+ target_arch = "x86_64"
+ )
+ ),
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ 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](https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html)
+pub fn connect(fd: RawFd, addr: &dyn SockaddrLike) -> Result<()> {
+ let res = unsafe { libc::connect(fd, addr.as_ptr(), addr.len()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Receive data from a connection-oriented socket. Returns the number of
+/// bytes read
+///
+/// [Further reading](https://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_mut_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, for connectionless sockets, the socket
+/// address of the sender.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html)
+pub fn recvfrom<T: SockaddrLike>(
+ sockfd: RawFd,
+ buf: &mut [u8],
+) -> Result<(usize, Option<T>)> {
+ unsafe {
+ let mut addr = mem::MaybeUninit::<T>::uninit();
+ let mut len = mem::size_of_val(&addr) as socklen_t;
+
+ let ret = Errno::result(libc::recvfrom(
+ sockfd,
+ buf.as_mut_ptr() as *mut c_void,
+ buf.len() as size_t,
+ 0,
+ addr.as_mut_ptr() as *mut sockaddr,
+ &mut len as *mut socklen_t,
+ ))? as usize;
+
+ Ok((
+ ret,
+ T::from_raw(
+ addr.assume_init().as_ptr(),
+ Some(len),
+ ),
+ ))
+ }
+}
+
+/// Send a message to a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html)
+pub fn sendto(
+ fd: RawFd,
+ buf: &[u8],
+ addr: &dyn SockaddrLike,
+ flags: MsgFlags,
+) -> Result<usize> {
+ let ret = unsafe {
+ libc::sendto(
+ fd,
+ buf.as_ptr() as *const c_void,
+ buf.len() as size_t,
+ flags.bits(),
+ addr.as_ptr(),
+ addr.len(),
+ )
+ };
+
+ Errno::result(ret).map(|r| r as usize)
+}
+
+/// Send data to a connection-oriented socket. Returns the number of bytes read
+///
+/// [Further reading](https://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 =====
+ *
+ */
+
+/// Represents a socket option that can be retrieved.
+pub trait GetSockOpt: Copy {
+ type Val;
+
+ /// Look up the value of this socket option on the given socket.
+ fn get<F: AsFd>(&self, fd: &F) -> Result<Self::Val>;
+}
+
+/// Represents a socket option that can be set.
+pub trait SetSockOpt: Clone {
+ type Val;
+
+ /// Set the value of this socket option on the given socket.
+ fn set<F: AsFd>(&self, fd: &F, val: &Self::Val) -> Result<()>;
+}
+
+/// Get the current value for the requested socket option
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html)
+pub fn getsockopt<F: AsFd, O: GetSockOpt>(fd: &F, opt: O) -> Result<O::Val> {
+ opt.get(fd)
+}
+
+/// Sets the value for the requested socket option
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html)
+///
+/// # Examples
+///
+/// ```
+/// use nix::sys::socket::setsockopt;
+/// use nix::sys::socket::sockopt::KeepAlive;
+/// use std::net::TcpListener;
+///
+/// let listener = TcpListener::bind("0.0.0.0:0").unwrap();
+/// let fd = listener;
+/// let res = setsockopt(&fd, KeepAlive, &true);
+/// assert!(res.is_ok());
+/// ```
+pub fn setsockopt<F: AsFd, O: SetSockOpt>(
+ fd: &F,
+ opt: O,
+ val: &O::Val,
+) -> Result<()> {
+ opt.set(fd, val)
+}
+
+/// Get the address of the peer connected to the socket `fd`.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html)
+pub fn getpeername<T: SockaddrLike>(fd: RawFd) -> Result<T> {
+ unsafe {
+ let mut addr = mem::MaybeUninit::<T>::uninit();
+ let mut len = T::size();
+
+ let ret =
+ libc::getpeername(fd, addr.as_mut_ptr() as *mut sockaddr, &mut len);
+
+ Errno::result(ret)?;
+
+ T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(Errno::EINVAL)
+ }
+}
+
+/// Get the current address to which the socket `fd` is bound.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html)
+pub fn getsockname<T: SockaddrLike>(fd: RawFd) -> Result<T> {
+ unsafe {
+ let mut addr = mem::MaybeUninit::<T>::uninit();
+ let mut len = T::size();
+
+ let ret =
+ libc::getsockname(fd, addr.as_mut_ptr() as *mut sockaddr, &mut len);
+
+ Errno::result(ret)?;
+
+ T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(Errno::EINVAL)
+ }
+}
+
+#[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](https://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)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[cfg(not(target_os = "redox"))]
+ #[test]
+ fn can_use_cmsg_space() {
+ let _ = cmsg_space!(u8);
+ }
+
+ #[cfg(not(any(
+ target_os = "redox",
+ target_os = "linux",
+ target_os = "android"
+ )))]
+ #[test]
+ fn can_open_routing_socket() {
+ let _ = super::socket(
+ super::AddressFamily::Route,
+ super::SockType::Raw,
+ super::SockFlag::empty(),
+ None,
+ )
+ .expect("Failed to open routing socket");
+ }
+}
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..44f3ebbc1d
--- /dev/null
+++ b/third_party/rust/nix/src/sys/socket/sockopt.rs
@@ -0,0 +1,1470 @@
+//! Socket options as used by `setsockopt` and `getsockopt`.
+use super::{GetSockOpt, SetSockOpt};
+use crate::errno::Errno;
+use crate::sys::time::TimeVal;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_int, c_void, socklen_t};
+use std::ffi::{OsStr, OsString};
+use std::mem::{self, MaybeUninit};
+#[cfg(target_family = "unix")]
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::io::{AsFd, AsRawFd};
+
+// Constants
+// TCP_CA_NAME_MAX isn't defined in user space include files
+#[cfg(any(target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+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:expr` : socket layer, or a `protocol level`: could be *raw sockets*
+/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
+/// and more. Please refer to your system manual for more options. Will be passed as the second
+/// argument (`level`) to the `setsockopt` call.
+/// * `$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:expr, $flag:path, $ty:ty, $setter:ty) => {
+ impl SetSockOpt for $name {
+ type Val = $ty;
+
+ fn set<F: AsFd>(&self, fd: &F, val: &$ty) -> Result<()> {
+ unsafe {
+ let setter: $setter = Set::new(val);
+
+ let res = libc::setsockopt(
+ fd.as_fd().as_raw_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:expr, $flag:path, $ty:ty, $getter:ty) => {
+ impl GetSockOpt for $name {
+ type Val = $ty;
+
+ fn get<F: AsFd>(&self, fd: &F) -> Result<$ty> {
+ unsafe {
+ let mut getter: $getter = Get::uninit();
+
+ let res = libc::getsockopt(
+ fd.as_fd().as_raw_fd(),
+ $level,
+ $flag,
+ getter.ffi_ptr(),
+ getter.ffi_len(),
+ );
+ Errno::result(res)?;
+
+ match <$ty>::try_from(getter.assume_init()) {
+ Err(_) => Err(Errno::EINVAL),
+ Ok(r) => Ok(r),
+ }
+ }
+ }
+ }
+ };
+}
+
+/// 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:expr` : socket layer, or a `protocol level`: could be *raw sockets*
+/// (`lic::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
+/// and more. Please refer to your system manual for more options. Will be passed as the second
+/// argument (`level`) to the `getsockopt`/`setsockopt` call.
+/// * `$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`.
+// Some targets don't use all rules.
+#[allow(unused_macro_rules)]
+macro_rules! sockopt_impl {
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, bool) => {
+ sockopt_impl!($(#[$attr])*
+ $name, GetOnly, $level, $flag, bool, GetBool);
+ };
+
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, u8) => {
+ sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, GetU8);
+ };
+
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, usize) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, GetOnly, $level, $flag, usize, GetUsize);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, bool) => {
+ sockopt_impl!($(#[$attr])*
+ $name, SetOnly, $level, $flag, bool, SetBool);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, u8) => {
+ sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, SetU8);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, usize) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, SetOnly, $level, $flag, usize, SetUsize);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, bool) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, bool, GetBool, SetBool);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, u8) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, u8, GetU8, SetU8);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, usize) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, usize, GetUsize, SetUsize);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path,
+ OsString<$array:ty>) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, OsString, GetOsString<$array>,
+ SetOsString);
+ };
+
+ /*
+ * Matchers with generic getter types must be placed at the end, so
+ * they'll only match _after_ specialized matchers fail
+ */
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, GetOnly, $level, $flag, $ty, GetStruct<$ty>);
+ };
+
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty,
+ $getter:ty) =>
+ {
+ $(#[$attr])*
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ pub struct $name;
+
+ getsockopt_impl!($name, $level, $flag, $ty, $getter);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, SetOnly, $level, $flag, $ty, SetStruct<$ty>);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty,
+ $setter:ty) =>
+ {
+ $(#[$attr])*
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ pub struct $name;
+
+ setsockopt_impl!($name, $level, $flag, $ty, $setter);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty,
+ $getter:ty, $setter:ty) =>
+ {
+ $(#[$attr])*
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ pub struct $name;
+
+ setsockopt_impl!($name, $level, $flag, $ty, $setter);
+ getsockopt_impl!($name, $level, $flag, $ty, $getter);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, $ty, GetStruct<$ty>,
+ SetStruct<$ty>);
+ };
+}
+
+/*
+ *
+ * ===== Define sockopts =====
+ *
+ */
+
+sockopt_impl!(
+ /// Enables local address reuse
+ ReuseAddr,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_REUSEADDR,
+ bool
+);
+#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+sockopt_impl!(
+ /// Permits multiple AF_INET or AF_INET6 sockets to be bound to an
+ /// identical socket address.
+ ReusePort,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_REUSEPORT,
+ bool
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Under most circumstances, TCP sends data when it is presented; when
+ /// outstanding data has not yet been acknowledged, it gathers small amounts
+ /// of output to be sent in a single packet once an acknowledgement is
+ /// received. For a small number of clients, such as window systems that
+ /// send a stream of mouse events which receive no replies, this
+ /// packetization may cause significant delays. The boolean option
+ /// TCP_NODELAY defeats this algorithm.
+ TcpNoDelay,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_NODELAY,
+ bool
+);
+sockopt_impl!(
+ /// When enabled, a close(2) or shutdown(2) will not return until all
+ /// queued messages for the socket have been successfully sent or the
+ /// linger timeout has been reached.
+ Linger,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_LINGER,
+ libc::linger
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Join a multicast group
+ IpAddMembership,
+ SetOnly,
+ libc::IPPROTO_IP,
+ libc::IP_ADD_MEMBERSHIP,
+ super::IpMembershipRequest
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Leave a multicast group.
+ IpDropMembership,
+ SetOnly,
+ libc::IPPROTO_IP,
+ libc::IP_DROP_MEMBERSHIP,
+ super::IpMembershipRequest
+);
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ #[cfg(feature = "net")]
+ sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Join an IPv6 multicast group.
+ Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest);
+ #[cfg(feature = "net")]
+ sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Leave an IPv6 multicast group.
+ Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest);
+ } else if #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))] {
+ #[cfg(feature = "net")]
+ sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Join an IPv6 multicast group.
+ Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6,
+ libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest);
+ #[cfg(feature = "net")]
+ sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Leave an IPv6 multicast group.
+ Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6,
+ libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest);
+ }
+}
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set or read the time-to-live value of outgoing multicast packets for
+ /// this socket.
+ IpMulticastTtl,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_MULTICAST_TTL,
+ u8
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set or read a boolean integer argument that determines whether sent
+ /// multicast packets should be looped back to the local sockets.
+ IpMulticastLoop,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_MULTICAST_LOOP,
+ bool
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set the protocol-defined priority for all packets to be
+ /// sent on this socket
+ Priority,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_PRIORITY,
+ libc::c_int
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set or receive the Type-Of-Service (TOS) field that is
+ /// sent with every IP packet originating from this socket
+ IpTos,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_TOS,
+ libc::c_int
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Traffic class associated with outgoing packets
+ Ipv6TClass,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_TCLASS,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// If enabled, this boolean option allows binding to an IP address that
+ /// is nonlocal or does not (yet) exist.
+ IpFreebind,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_FREEBIND,
+ bool
+);
+sockopt_impl!(
+ /// Specify the receiving timeout until reporting an error.
+ ReceiveTimeout,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_RCVTIMEO,
+ TimeVal
+);
+sockopt_impl!(
+ /// Specify the sending timeout until reporting an error.
+ SendTimeout,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_SNDTIMEO,
+ TimeVal
+);
+sockopt_impl!(
+ /// Set or get the broadcast flag.
+ Broadcast,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_BROADCAST,
+ bool
+);
+sockopt_impl!(
+ /// If this option is enabled, out-of-band data is directly placed into
+ /// the receive data stream.
+ OobInline,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_OOBINLINE,
+ bool
+);
+sockopt_impl!(
+ /// Get and clear the pending socket error.
+ SocketError,
+ GetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_ERROR,
+ i32
+);
+sockopt_impl!(
+ /// Set or get the don't route flag.
+ DontRoute,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_DONTROUTE,
+ bool
+);
+sockopt_impl!(
+ /// Enable sending of keep-alive messages on connection-oriented sockets.
+ KeepAlive,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_KEEPALIVE,
+ bool
+);
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+))]
+sockopt_impl!(
+ /// Get the credentials of the peer process of a connected unix domain
+ /// socket.
+ LocalPeerCred,
+ GetOnly,
+ 0,
+ libc::LOCAL_PEERCRED,
+ super::XuCred
+);
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+sockopt_impl!(
+ /// Get the PID of the peer process of a connected unix domain socket.
+ LocalPeerPid,
+ GetOnly,
+ 0,
+ libc::LOCAL_PEERPID,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Return the credentials of the foreign process connected to this socket.
+ PeerCredentials,
+ GetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_PEERCRED,
+ super::UnixCredentials
+);
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Specify the amount of time, in seconds, that the connection must be idle
+ /// before keepalive probes (if enabled) are sent.
+ TcpKeepAlive,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_KEEPALIVE,
+ u32
+);
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux"
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The time (in seconds) the connection needs to remain idle before TCP
+ /// starts sending keepalive probes
+ TcpKeepIdle,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_KEEPIDLE,
+ u32
+);
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ sockopt_impl!(
+ /// The maximum segment size for outgoing TCP packets.
+ TcpMaxSeg, Both, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
+ } else if #[cfg(not(target_os = "redox"))] {
+ sockopt_impl!(
+ /// The maximum segment size for outgoing TCP packets.
+ TcpMaxSeg, GetOnly, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
+ }
+}
+#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "redox")))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The maximum number of keepalive probes TCP should send before
+ /// dropping the connection.
+ TcpKeepCount,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_KEEPCNT,
+ u32
+);
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+sockopt_impl!(
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ TcpRepair,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_REPAIR,
+ u32
+);
+#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "redox")))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The time (in seconds) between individual keepalive probes.
+ TcpKeepInterval,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_KEEPINTVL,
+ u32
+);
+#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Specifies the maximum amount of time in milliseconds that transmitted
+ /// data may remain unacknowledged before TCP will forcibly close the
+ /// corresponding connection
+ TcpUserTimeout,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_USER_TIMEOUT,
+ u32
+);
+sockopt_impl!(
+ /// Sets or gets the maximum socket receive buffer in bytes.
+ RcvBuf,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_RCVBUF,
+ usize
+);
+sockopt_impl!(
+ /// Sets or gets the maximum socket send buffer in bytes.
+ SndBuf,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_SNDBUF,
+ usize
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can
+ /// perform the same task as `SO_RCVBUF`, but the `rmem_max limit` can be
+ /// overridden.
+ RcvBufForce,
+ SetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_RCVBUFFORCE,
+ usize
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can
+ /// perform the same task as `SO_SNDBUF`, but the `wmem_max` limit can be
+ /// overridden.
+ SndBufForce,
+ SetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_SNDBUFFORCE,
+ usize
+);
+sockopt_impl!(
+ /// Gets the socket type as an integer.
+ SockType,
+ GetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_TYPE,
+ super::SockType,
+ GetStruct<i32>
+);
+sockopt_impl!(
+ /// Returns a value indicating whether or not this socket has been marked to
+ /// accept connections with `listen(2)`.
+ AcceptConn,
+ GetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_ACCEPTCONN,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Bind this socket to a particular device like “eth0”.
+ BindToDevice,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_BINDTODEVICE,
+ OsString<[u8; libc::IFNAMSIZ]>
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ OriginalDst,
+ GetOnly,
+ libc::SOL_IP,
+ libc::SO_ORIGINAL_DST,
+ libc::sockaddr_in
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ Ip6tOriginalDst,
+ GetOnly,
+ libc::SOL_IPV6,
+ libc::IP6T_SO_ORIGINAL_DST,
+ libc::sockaddr_in6
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Specifies exact type of timestamping information collected by the kernel
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ Timestamping,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TIMESTAMPING,
+ super::TimestampingFlag
+);
+#[cfg(not(any(target_os = "aix", target_os = "haiku", target_os = "redox")))]
+sockopt_impl!(
+ /// Enable or disable the receiving of the `SO_TIMESTAMP` control message.
+ ReceiveTimestamp,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TIMESTAMP,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Enable or disable the receiving of the `SO_TIMESTAMPNS` control message.
+ ReceiveTimestampns,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TIMESTAMPNS,
+ bool
+);
+#[cfg(target_os = "freebsd")]
+sockopt_impl!(
+ /// Sets a specific timestamp format instead of the classic `SCM_TIMESTAMP`,
+ /// to follow up after `SO_TIMESTAMP` is set.
+ TsClock,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TS_CLOCK,
+ i32
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Setting this boolean option enables transparent proxying on this socket.
+ IpTransparent,
+ Both,
+ libc::SOL_IP,
+ libc::IP_TRANSPARENT,
+ bool
+);
+#[cfg(target_os = "openbsd")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Allows the socket to be bound to addresses which are not local to the
+ /// machine, so it can be used to make a transparent proxy.
+ BindAny,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_BINDANY,
+ bool
+);
+#[cfg(target_os = "freebsd")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Can `bind(2)` to any address, even one not bound to any available
+ /// network interface in the system.
+ BindAny,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_BINDANY,
+ bool
+);
+#[cfg(target_os = "freebsd")]
+sockopt_impl!(
+ /// Set the route table (FIB) for this socket up to the `net.fibs` OID limit
+ /// (more specific than the setfib command line/call which are process based).
+ Fib,
+ SetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_SETFIB,
+ i32
+);
+#[cfg(target_os = "freebsd")]
+sockopt_impl!(
+ /// Set `so_user_cookie` for this socket allowing network traffic based
+ /// upon it, similar to Linux's netfilter MARK.
+ UserCookie,
+ SetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_USER_COOKIE,
+ u32
+);
+#[cfg(target_os = "openbsd")]
+sockopt_impl!(
+ /// Set the route table for this socket, needs a privileged user if
+ /// the process/socket had been set to the non default route.
+ Rtable,
+ SetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_RTABLE,
+ i32
+);
+#[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
+sockopt_impl!(
+ /// Get/set a filter on this socket before accepting connections similarly
+ /// to Linux's TCP_DEFER_ACCEPT but after the listen's call.
+ AcceptFilter,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_ACCEPTFILTER,
+ libc::accept_filter_arg
+);
+#[cfg(target_os = "linux")]
+sockopt_impl!(
+ /// Set the mark for each packet sent through this socket (similar to the
+ /// netfilter MARK target but socket-based).
+ Mark,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_MARK,
+ u32
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Enable or disable the receiving of the `SCM_CREDENTIALS` control
+ /// message.
+ PassCred,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_PASSCRED,
+ bool
+);
+#[cfg(any(target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// This option allows the caller to set the TCP congestion control
+ /// algorithm to be used, on a per-socket basis.
+ TcpCongestion,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_CONGESTION,
+ OsString<[u8; TCP_CA_NAME_MAX]>
+);
+#[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Pass an `IP_PKTINFO` ancillary message that contains a pktinfo
+ /// structure that supplies some information about the incoming packet.
+ Ipv4PacketInfo,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_PKTINFO,
+ bool
+);
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set delivery of the `IPV6_PKTINFO` control message on incoming
+ /// datagrams.
+ Ipv6RecvPacketInfo,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_RECVPKTINFO,
+ bool
+);
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call returns a `struct sockaddr_dl` corresponding to
+ /// the interface on which the packet was received.
+ Ipv4RecvIf,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_RECVIF,
+ bool
+);
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call will return the destination IP address for a UDP
+ /// datagram.
+ Ipv4RecvDstAddr,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_RECVDSTADDR,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call will return the destination IP address for a UDP
+ /// datagram.
+ Ipv4OrigDstAddr,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_ORIGDSTADDR,
+ bool
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ UdpGsoSegment,
+ Both,
+ libc::SOL_UDP,
+ libc::UDP_SEGMENT,
+ libc::c_int
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ UdpGroSegment,
+ Both,
+ libc::IPPROTO_UDP,
+ libc::UDP_GRO,
+ bool
+);
+#[cfg(target_os = "linux")]
+sockopt_impl!(
+ /// Configures the behavior of time-based transmission of packets, for use
+ /// with the `TxTime` control message.
+ TxTime,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TXTIME,
+ libc::sock_txtime
+);
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+sockopt_impl!(
+ /// Indicates that an unsigned 32-bit value ancillary message (cmsg) should
+ /// be attached to received skbs indicating the number of packets dropped by
+ /// the socket since its creation.
+ RxqOvfl,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_RXQ_OVFL,
+ libc::c_int
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The socket is restricted to sending and receiving IPv6 packets only.
+ Ipv6V6Only,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_V6ONLY,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Enable extended reliable error message passing.
+ Ipv4RecvErr,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_RECVERR,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Control receiving of asynchronous error options.
+ Ipv6RecvErr,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_RECVERR,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Fetch the current system-estimated Path MTU.
+ IpMtu,
+ GetOnly,
+ libc::IPPROTO_IP,
+ libc::IP_MTU,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+sockopt_impl!(
+ /// Set or retrieve the current time-to-live field that is used in every
+ /// packet sent from this socket.
+ Ipv4Ttl,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_TTL,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+sockopt_impl!(
+ /// Set the unicast hop limit for the socket.
+ Ipv6Ttl,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_UNICAST_HOPS,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call will return the destination IP address for a UDP
+ /// datagram.
+ Ipv6OrigDstAddr,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_ORIGDSTADDR,
+ bool
+);
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+sockopt_impl!(
+ /// Set "don't fragment packet" flag on the IP packet.
+ IpDontFrag,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_DONTFRAG,
+ bool
+);
+#[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+))]
+sockopt_impl!(
+ /// Set "don't fragment packet" flag on the IPv6 packet.
+ Ipv6DontFrag,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_DONTFRAG,
+ bool
+);
+
+#[allow(missing_docs)]
+// Not documented by Linux!
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[derive(Copy, Clone, Debug)]
+pub struct AlgSetAeadAuthSize;
+
+// ALG_SET_AEAD_AUTH_SIZE read the length from passed `option_len`
+// See https://elixir.bootlin.com/linux/v4.4/source/crypto/af_alg.c#L222
+#[cfg(any(target_os = "android", target_os = "linux"))]
+impl SetSockOpt for AlgSetAeadAuthSize {
+ type Val = usize;
+
+ fn set<F: AsFd>(&self, fd: &F, val: &usize) -> Result<()> {
+ unsafe {
+ let res = libc::setsockopt(
+ fd.as_fd().as_raw_fd(),
+ libc::SOL_ALG,
+ libc::ALG_SET_AEAD_AUTHSIZE,
+ ::std::ptr::null(),
+ *val as libc::socklen_t,
+ );
+ Errno::result(res).map(drop)
+ }
+ }
+}
+
+#[allow(missing_docs)]
+// Not documented by Linux!
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[derive(Clone, Debug)]
+pub struct AlgSetKey<T>(::std::marker::PhantomData<T>);
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+impl<T> Default for AlgSetKey<T> {
+ fn default() -> Self {
+ AlgSetKey(Default::default())
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+impl<T> SetSockOpt for AlgSetKey<T>
+where
+ T: AsRef<[u8]> + Clone,
+{
+ type Val = T;
+
+ fn set<F: AsFd>(&self, fd: &F, val: &T) -> Result<()> {
+ unsafe {
+ let res = libc::setsockopt(
+ fd.as_fd().as_raw_fd(),
+ libc::SOL_ALG,
+ libc::ALG_SET_KEY,
+ val.as_ref().as_ptr() as *const _,
+ val.as_ref().len() as libc::socklen_t,
+ );
+ Errno::result(res).map(drop)
+ }
+ }
+}
+
+/*
+ *
+ * ===== Accessor helpers =====
+ *
+ */
+
+/// Helper trait that describes what is expected from a `GetSockOpt` getter.
+trait Get<T> {
+ /// Returns an uninitialized value.
+ fn uninit() -> Self;
+ /// Returns a pointer to the stored value. This pointer will be passed to the system's
+ /// `getsockopt` call (`man 3p getsockopt`, argument `option_value`).
+ fn ffi_ptr(&mut self) -> *mut c_void;
+ /// 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 hopefully initialized inner value.
+ unsafe fn assume_init(self) -> T;
+}
+
+/// Helper trait that describes what is expected from a `SetSockOpt` setter.
+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: MaybeUninit<T>,
+}
+
+impl<T> Get<T> for GetStruct<T> {
+ fn uninit() -> Self {
+ GetStruct {
+ len: mem::size_of::<T>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> T {
+ assert_eq!(
+ self.len as usize,
+ mem::size_of::<T>(),
+ "invalid getsockopt implementation"
+ );
+ self.val.assume_init()
+ }
+}
+
+/// Setter for an arbitrary `struct`.
+struct SetStruct<'a, T: 'static> {
+ ptr: &'a T,
+}
+
+impl<'a, T> Set<'a, T> for SetStruct<'a, T> {
+ fn new(ptr: &'a T) -> SetStruct<'a, T> {
+ SetStruct { 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: MaybeUninit<c_int>,
+}
+
+impl Get<bool> for GetBool {
+ fn uninit() -> Self {
+ GetBool {
+ len: mem::size_of::<c_int>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> bool {
+ assert_eq!(
+ self.len as usize,
+ mem::size_of::<c_int>(),
+ "invalid getsockopt implementation"
+ );
+ self.val.assume_init() != 0
+ }
+}
+
+/// Setter for a boolean value.
+struct SetBool {
+ val: c_int,
+}
+
+impl<'a> Set<'a, bool> for SetBool {
+ fn new(val: &'a bool) -> SetBool {
+ SetBool {
+ val: i32::from(*val),
+ }
+ }
+
+ 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: MaybeUninit<u8>,
+}
+
+impl Get<u8> for GetU8 {
+ fn uninit() -> Self {
+ GetU8 {
+ len: mem::size_of::<u8>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> u8 {
+ assert_eq!(
+ self.len as usize,
+ mem::size_of::<u8>(),
+ "invalid getsockopt implementation"
+ );
+ self.val.assume_init()
+ }
+}
+
+/// Setter for an `u8` value.
+struct SetU8 {
+ val: u8,
+}
+
+impl<'a> Set<'a, u8> for SetU8 {
+ fn new(val: &'a u8) -> SetU8 {
+ SetU8 { val: *val }
+ }
+
+ 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: MaybeUninit<c_int>,
+}
+
+impl Get<usize> for GetUsize {
+ fn uninit() -> Self {
+ GetUsize {
+ len: mem::size_of::<c_int>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> usize {
+ assert_eq!(
+ self.len as usize,
+ mem::size_of::<c_int>(),
+ "invalid getsockopt implementation"
+ );
+ self.val.assume_init() as usize
+ }
+}
+
+/// Setter for an `usize` value.
+struct SetUsize {
+ val: c_int,
+}
+
+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: MaybeUninit<T>,
+}
+
+impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
+ fn uninit() -> Self {
+ GetOsString {
+ len: mem::size_of::<T>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> OsString {
+ let len = self.len as usize;
+ let mut v = self.val.assume_init();
+ OsStr::from_bytes(&v.as_mut()[0..len]).to_owned()
+ }
+}
+
+/// Setter for a `OsString` value.
+struct SetOsString<'a> {
+ val: &'a OsStr,
+}
+
+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_ne!(a_cred.pid(), 0);
+ }
+
+ #[test]
+ fn is_socket_type_unix() {
+ use super::super::*;
+
+ let (a, _b) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ let a_type = getsockopt(&a, super::SockType).unwrap();
+ assert_eq!(a_type, SockType::Stream);
+ }
+
+ #[test]
+ fn is_socket_type_dgram() {
+ use super::super::*;
+
+ let s = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ let s_type = getsockopt(&s, super::SockType).unwrap();
+ assert_eq!(s_type, SockType::Datagram);
+ }
+
+ #[cfg(any(target_os = "freebsd", target_os = "linux"))]
+ #[test]
+ fn can_get_listen_on_tcp_socket() {
+ use super::super::*;
+
+ 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);
+ }
+}
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..7e51c03a3f
--- /dev/null
+++ b/third_party/rust/nix/src/sys/stat.rs
@@ -0,0 +1,480 @@
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "openbsd"))]
+pub use libc::c_uint;
+#[cfg(any(
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly"
+))]
+pub use libc::c_ulong;
+pub use libc::stat as FileStat;
+pub use libc::{dev_t, mode_t};
+
+#[cfg(not(target_os = "redox"))]
+use crate::fcntl::{at_rawfd, AtFlags};
+use crate::sys::time::{TimeSpec, TimeVal};
+use crate::{errno::Errno, NixPath, Result};
+use std::mem;
+use std::os::unix::io::RawFd;
+
+libc_bitflags!(
+ /// "File type" flags for `mknod` and related functions.
+ pub struct SFlag: mode_t {
+ S_IFIFO;
+ S_IFCHR;
+ S_IFDIR;
+ S_IFBLK;
+ S_IFREG;
+ S_IFLNK;
+ S_IFSOCK;
+ S_IFMT;
+ }
+);
+
+libc_bitflags! {
+ /// "File mode / permissions" flags.
+ pub struct Mode: mode_t {
+ /// Read, write and execute for owner.
+ S_IRWXU;
+ /// Read for owner.
+ S_IRUSR;
+ /// Write for owner.
+ S_IWUSR;
+ /// Execute for owner.
+ S_IXUSR;
+ /// Read write and execute for group.
+ S_IRWXG;
+ /// Read fr group.
+ S_IRGRP;
+ /// Write for group.
+ S_IWGRP;
+ /// Execute for group.
+ S_IXGRP;
+ /// Read, write and execute for other.
+ S_IRWXO;
+ /// Read for other.
+ S_IROTH;
+ /// Write for other.
+ S_IWOTH;
+ /// Execute for other.
+ S_IXOTH;
+ /// Set user id on execution.
+ S_ISUID as mode_t;
+ /// Set group id on execution.
+ S_ISGID as mode_t;
+ S_ISVTX as mode_t;
+ }
+}
+
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "openbsd"))]
+pub type type_of_file_flag = c_uint;
+#[cfg(any(
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly"
+))]
+pub type type_of_file_flag = c_ulong;
+
+#[cfg(any(
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "macos",
+ target_os = "ios"
+))]
+libc_bitflags! {
+ /// File flags.
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub struct FileFlag: type_of_file_flag {
+ /// The file may only be appended to.
+ SF_APPEND;
+ /// The file has been archived.
+ SF_ARCHIVED;
+ #[cfg(any(target_os = "dragonfly"))]
+ SF_CACHE;
+ /// The file may not be changed.
+ SF_IMMUTABLE;
+ /// Indicates a WAPBL journal file.
+ #[cfg(any(target_os = "netbsd"))]
+ SF_LOG;
+ /// Do not retain history for file
+ #[cfg(any(target_os = "dragonfly"))]
+ SF_NOHISTORY;
+ /// The file may not be renamed or deleted.
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ SF_NOUNLINK;
+ /// Mask of superuser changeable flags
+ SF_SETTABLE;
+ /// Snapshot is invalid.
+ #[cfg(any(target_os = "netbsd"))]
+ SF_SNAPINVAL;
+ /// The file is a snapshot file.
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
+ SF_SNAPSHOT;
+ #[cfg(any(target_os = "dragonfly"))]
+ SF_XLINK;
+ /// The file may only be appended to.
+ UF_APPEND;
+ /// The file needs to be archived.
+ #[cfg(any(target_os = "freebsd"))]
+ UF_ARCHIVE;
+ #[cfg(any(target_os = "dragonfly"))]
+ UF_CACHE;
+ /// File is compressed at the file system level.
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ UF_COMPRESSED;
+ /// The file may be hidden from directory listings at the application's
+ /// discretion.
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios",
+ ))]
+ UF_HIDDEN;
+ /// The file may not be changed.
+ UF_IMMUTABLE;
+ /// Do not dump the file.
+ UF_NODUMP;
+ #[cfg(any(target_os = "dragonfly"))]
+ UF_NOHISTORY;
+ /// The file may not be renamed or deleted.
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ UF_NOUNLINK;
+ /// The file is offline, or has the Windows and CIFS
+ /// `FILE_ATTRIBUTE_OFFLINE` attribute.
+ #[cfg(any(target_os = "freebsd"))]
+ UF_OFFLINE;
+ /// The directory is opaque when viewed through a union stack.
+ UF_OPAQUE;
+ /// The file is read only, and may not be written or appended.
+ #[cfg(any(target_os = "freebsd"))]
+ UF_READONLY;
+ /// The file contains a Windows reparse point.
+ #[cfg(any(target_os = "freebsd"))]
+ UF_REPARSE;
+ /// Mask of owner changeable flags.
+ UF_SETTABLE;
+ /// The file has the Windows `FILE_ATTRIBUTE_SPARSE_FILE` attribute.
+ #[cfg(any(target_os = "freebsd"))]
+ UF_SPARSE;
+ /// The file has the DOS, Windows and CIFS `FILE_ATTRIBUTE_SYSTEM`
+ /// attribute.
+ #[cfg(any(target_os = "freebsd"))]
+ UF_SYSTEM;
+ /// File renames and deletes are tracked.
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ UF_TRACKED;
+ #[cfg(any(target_os = "dragonfly"))]
+ UF_XLINK;
+ }
+}
+
+/// Create a special or ordinary file, by pathname.
+pub fn mknod<P: ?Sized + NixPath>(
+ path: &P,
+ kind: SFlag,
+ perm: Mode,
+ dev: dev_t,
+) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::mknod(cstr.as_ptr(), kind.bits() | perm.bits() as mode_t, dev)
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Create a special or ordinary file, relative to a given directory.
+#[cfg(not(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn mknodat<P: ?Sized + NixPath>(
+ dirfd: RawFd,
+ path: &P,
+ kind: SFlag,
+ perm: Mode,
+ dev: dev_t,
+) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::mknodat(
+ dirfd,
+ cstr.as_ptr(),
+ kind.bits() | perm.bits() as mode_t,
+ dev,
+ )
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+#[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub const fn major(dev: dev_t) -> u64 {
+ ((dev >> 32) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)
+}
+
+#[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub const fn minor(dev: dev_t) -> u64 {
+ ((dev >> 12) & 0xffff_ff00) | ((dev) & 0x0000_00ff)
+}
+
+#[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub const fn makedev(major: u64, minor: u64) -> dev_t {
+ ((major & 0xffff_f000) << 32)
+ | ((major & 0x0000_0fff) << 8)
+ | ((minor & 0xffff_ff00) << 12)
+ | (minor & 0x0000_00ff)
+}
+
+pub fn umask(mode: Mode) -> Mode {
+ let prev = unsafe { libc::umask(mode.bits() as mode_t) };
+ Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
+}
+
+pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
+ let mut dst = mem::MaybeUninit::uninit();
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::stat(cstr.as_ptr(), dst.as_mut_ptr())
+ })?;
+
+ Errno::result(res)?;
+
+ Ok(unsafe { dst.assume_init() })
+}
+
+pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
+ let mut dst = mem::MaybeUninit::uninit();
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::lstat(cstr.as_ptr(), dst.as_mut_ptr())
+ })?;
+
+ Errno::result(res)?;
+
+ Ok(unsafe { dst.assume_init() })
+}
+
+pub fn fstat(fd: RawFd) -> Result<FileStat> {
+ let mut dst = mem::MaybeUninit::uninit();
+ let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) };
+
+ Errno::result(res)?;
+
+ Ok(unsafe { dst.assume_init() })
+}
+
+#[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn fstatat<P: ?Sized + NixPath>(
+ dirfd: RawFd,
+ pathname: &P,
+ f: AtFlags,
+) -> Result<FileStat> {
+ let mut dst = mem::MaybeUninit::uninit();
+ let res = pathname.with_nix_path(|cstr| unsafe {
+ libc::fstatat(
+ dirfd,
+ cstr.as_ptr(),
+ dst.as_mut_ptr(),
+ f.bits() as libc::c_int,
+ )
+ })?;
+
+ Errno::result(res)?;
+
+ Ok(unsafe { dst.assume_init() })
+}
+
+/// Change the file permission bits of the file specified by a file descriptor.
+///
+/// # References
+///
+/// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
+pub fn fchmod(fd: 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.
+///
+/// `fchmodat(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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
+#[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html).
+pub fn utimes<P: ?Sized + NixPath>(
+ path: &P,
+ atime: &TimeVal,
+ mtime: &TimeVal,
+) -> Result<()> {
+ let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::utimes(cstr.as_ptr(), &times[0])
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Change the access and modification times of a file without following symlinks.
+///
+/// `lutimes(path, times)` is identical to
+/// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former
+/// is a deprecated API so prefer using the latter if the platforms you care
+/// about support it.
+///
+/// # References
+///
+/// [lutimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html).
+#[cfg(any(
+ target_os = "linux",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "netbsd"
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+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)](https://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.
+// TODO: replace with fcntl::AtFlags
+#[derive(Clone, Copy, Debug)]
+pub enum UtimensatFlags {
+ FollowSymlink,
+ NoFollowSymlink,
+}
+
+/// Change the access and modification times of a file.
+///
+/// The file to be changed is determined relative to the directory associated
+/// with the file descriptor `dirfd` or the current working directory
+/// if `dirfd` is `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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
+#[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+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)
+}
+
+#[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn mkdirat<P: ?Sized + NixPath>(
+ fd: RawFd,
+ path: &P,
+ mode: Mode,
+) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t)
+ })?;
+
+ 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..5111df2e6e
--- /dev/null
+++ b/third_party/rust/nix/src/sys/statfs.rs
@@ -0,0 +1,853 @@
+//! Get filesystem statistics, non-portably
+//!
+//! See [`statvfs`](crate::sys::statvfs) for a portable alternative.
+#[cfg(not(any(target_os = "linux", target_os = "android")))]
+use std::ffi::CStr;
+use std::fmt::{self, Debug};
+use std::mem;
+use std::os::unix::io::{AsFd, AsRawFd};
+
+use cfg_if::cfg_if;
+
+#[cfg(all(
+ feature = "mount",
+ any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )
+))]
+use crate::mount::MntFlags;
+#[cfg(target_os = "linux")]
+use crate::sys::statvfs::FsFlags;
+use crate::{errno::Errno, NixPath, Result};
+
+/// Identifies a mounted file system
+#[cfg(target_os = "android")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub type fsid_t = libc::__fsid_t;
+/// Identifies a mounted file system
+#[cfg(not(target_os = "android"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub type fsid_t = libc::fsid_t;
+
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] {
+ type type_of_statfs = libc::statfs64;
+ const LIBC_FSTATFS: unsafe extern fn
+ (fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
+ = libc::fstatfs64;
+ const LIBC_STATFS: unsafe extern fn
+ (path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int
+ = libc::statfs64;
+ } else {
+ type type_of_statfs = libc::statfs;
+ const LIBC_FSTATFS: unsafe extern fn
+ (fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
+ = libc::fstatfs;
+ const LIBC_STATFS: unsafe extern fn
+ (path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int
+ = libc::statfs;
+ }
+}
+
+/// Describes a mounted file system
+#[derive(Clone, Copy)]
+#[repr(transparent)]
+pub struct Statfs(type_of_statfs);
+
+#[cfg(target_os = "freebsd")]
+type fs_type_t = u32;
+#[cfg(target_os = "android")]
+type fs_type_t = libc::c_ulong;
+#[cfg(all(target_os = "linux", target_arch = "s390x"))]
+type fs_type_t = libc::c_uint;
+#[cfg(all(target_os = "linux", target_env = "musl"))]
+type fs_type_t = libc::c_ulong;
+#[cfg(all(target_os = "linux", target_env = "uclibc"))]
+type fs_type_t = libc::c_int;
+#[cfg(all(
+ target_os = "linux",
+ not(any(
+ target_arch = "s390x",
+ target_env = "musl",
+ target_env = "uclibc"
+ ))
+))]
+type fs_type_t = libc::__fsword_t;
+
+/// Describes the file system type as known by the operating system.
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "android",
+ all(target_os = "linux", target_arch = "s390x"),
+ all(target_os = "linux", target_env = "musl"),
+ all(
+ target_os = "linux",
+ not(any(target_arch = "s390x", target_env = "musl"))
+ ),
+))]
+#[derive(Eq, Copy, Clone, PartialEq, Debug)]
+pub struct FsType(pub fs_type_t);
+
+// These constants are defined without documentation in the Linux headers, so we
+// can't very well document them here.
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const ADFS_SUPER_MAGIC: FsType =
+ FsType(libc::ADFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const AFFS_SUPER_MAGIC: FsType =
+ FsType(libc::AFFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const AFS_SUPER_MAGIC: FsType = FsType(libc::AFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const AUTOFS_SUPER_MAGIC: FsType =
+ FsType(libc::AUTOFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const BPF_FS_MAGIC: FsType = FsType(libc::BPF_FS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const BTRFS_SUPER_MAGIC: FsType =
+ FsType(libc::BTRFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const CGROUP2_SUPER_MAGIC: FsType =
+ FsType(libc::CGROUP2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const CGROUP_SUPER_MAGIC: FsType =
+ FsType(libc::CGROUP_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const CODA_SUPER_MAGIC: FsType =
+ FsType(libc::CODA_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const CRAMFS_MAGIC: FsType = FsType(libc::CRAMFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const DEBUGFS_MAGIC: FsType = FsType(libc::DEBUGFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const DEVPTS_SUPER_MAGIC: FsType =
+ FsType(libc::DEVPTS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const ECRYPTFS_SUPER_MAGIC: FsType =
+ FsType(libc::ECRYPTFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const EFS_SUPER_MAGIC: FsType = FsType(libc::EFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const EXT2_SUPER_MAGIC: FsType =
+ FsType(libc::EXT2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const EXT3_SUPER_MAGIC: FsType =
+ FsType(libc::EXT3_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const EXT4_SUPER_MAGIC: FsType =
+ FsType(libc::EXT4_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const F2FS_SUPER_MAGIC: FsType =
+ FsType(libc::F2FS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const FUSE_SUPER_MAGIC: FsType =
+ FsType(libc::FUSE_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const FUTEXFS_SUPER_MAGIC: FsType =
+ FsType(libc::FUTEXFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const HOSTFS_SUPER_MAGIC: FsType =
+ FsType(libc::HOSTFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const HPFS_SUPER_MAGIC: FsType =
+ FsType(libc::HPFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const HUGETLBFS_MAGIC: FsType = FsType(libc::HUGETLBFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const ISOFS_SUPER_MAGIC: FsType =
+ FsType(libc::ISOFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const JFFS2_SUPER_MAGIC: FsType =
+ FsType(libc::JFFS2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const MINIX2_SUPER_MAGIC2: FsType =
+ FsType(libc::MINIX2_SUPER_MAGIC2 as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const MINIX2_SUPER_MAGIC: FsType =
+ FsType(libc::MINIX2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const MINIX3_SUPER_MAGIC: FsType =
+ FsType(libc::MINIX3_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const MINIX_SUPER_MAGIC2: FsType =
+ FsType(libc::MINIX_SUPER_MAGIC2 as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const MINIX_SUPER_MAGIC: FsType =
+ FsType(libc::MINIX_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const MSDOS_SUPER_MAGIC: FsType =
+ FsType(libc::MSDOS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const NCP_SUPER_MAGIC: FsType = FsType(libc::NCP_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const NFS_SUPER_MAGIC: FsType = FsType(libc::NFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const NILFS_SUPER_MAGIC: FsType =
+ FsType(libc::NILFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const OCFS2_SUPER_MAGIC: FsType =
+ FsType(libc::OCFS2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const OPENPROM_SUPER_MAGIC: FsType =
+ FsType(libc::OPENPROM_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const OVERLAYFS_SUPER_MAGIC: FsType =
+ FsType(libc::OVERLAYFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const PROC_SUPER_MAGIC: FsType =
+ FsType(libc::PROC_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const QNX4_SUPER_MAGIC: FsType =
+ FsType(libc::QNX4_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const QNX6_SUPER_MAGIC: FsType =
+ FsType(libc::QNX6_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const RDTGROUP_SUPER_MAGIC: FsType =
+ FsType(libc::RDTGROUP_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const REISERFS_SUPER_MAGIC: FsType =
+ FsType(libc::REISERFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SECURITYFS_MAGIC: FsType =
+ FsType(libc::SECURITYFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SELINUX_MAGIC: FsType = FsType(libc::SELINUX_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SMACK_MAGIC: FsType = FsType(libc::SMACK_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SMB_SUPER_MAGIC: FsType = FsType(libc::SMB_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SYSFS_MAGIC: FsType = FsType(libc::SYSFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const TMPFS_MAGIC: FsType = FsType(libc::TMPFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const TRACEFS_MAGIC: FsType = FsType(libc::TRACEFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const UDF_SUPER_MAGIC: FsType = FsType(libc::UDF_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const USBDEVICE_SUPER_MAGIC: FsType =
+ FsType(libc::USBDEVICE_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const XENFS_SUPER_MAGIC: FsType =
+ FsType(libc::XENFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const NSFS_MAGIC: FsType = FsType(libc::NSFS_MAGIC as fs_type_t);
+#[cfg(all(
+ any(target_os = "linux", target_os = "android"),
+ not(target_env = "musl")
+))]
+#[allow(missing_docs)]
+pub const XFS_SUPER_MAGIC: FsType = FsType(libc::XFS_SUPER_MAGIC as fs_type_t);
+
+impl Statfs {
+ /// Magic code defining system type
+ #[cfg(not(any(
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos"
+ )))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn filesystem_type(&self) -> FsType {
+ FsType(self.0.f_type)
+ }
+
+ /// Magic code defining system type
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn filesystem_type_name(&self) -> &str {
+ let c_str = unsafe { CStr::from_ptr(self.0.f_fstypename.as_ptr()) };
+ c_str.to_str().unwrap()
+ }
+
+ /// Optimal transfer block size
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> i32 {
+ self.0.f_iosize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(target_os = "openbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> u32 {
+ self.0.f_iosize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(all(target_os = "linux", target_arch = "s390x"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> u32 {
+ self.0.f_bsize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(any(
+ target_os = "android",
+ all(target_os = "linux", target_env = "musl")
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> libc::c_ulong {
+ self.0.f_bsize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(all(
+ target_os = "linux",
+ not(any(
+ target_arch = "s390x",
+ target_env = "musl",
+ target_env = "uclibc"
+ ))
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> libc::__fsword_t {
+ self.0.f_bsize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(all(target_os = "linux", target_env = "uclibc"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> libc::c_int {
+ self.0.f_bsize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> libc::c_long {
+ self.0.f_iosize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> u64 {
+ self.0.f_iosize
+ }
+
+ /// Size of a block
+ #[cfg(any(target_os = "ios", target_os = "macos", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> u32 {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
+ #[cfg(all(target_os = "linux", target_arch = "s390x"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> u32 {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
+ #[cfg(all(target_os = "linux", target_env = "musl"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> libc::c_ulong {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
+ #[cfg(all(target_os = "linux", target_env = "uclibc"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> libc::c_int {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
+ #[cfg(all(
+ target_os = "linux",
+ not(any(
+ target_arch = "s390x",
+ target_env = "musl",
+ target_env = "uclibc"
+ ))
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> libc::__fsword_t {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> u64 {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ #[cfg(target_os = "android")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> libc::c_ulong {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> libc::c_long {
+ self.0.f_bsize
+ }
+
+ /// Get the mount flags
+ #[cfg(all(
+ feature = "mount",
+ any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[allow(clippy::unnecessary_cast)] // Not unnecessary on all arches
+ pub fn flags(&self) -> MntFlags {
+ MntFlags::from_bits_truncate(self.0.f_flags as i32)
+ }
+
+ /// Get the mount flags
+ // The f_flags field exists on Android and Fuchsia too, but without man
+ // pages I can't tell if it can be cast to FsFlags.
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn flags(&self) -> FsFlags {
+ FsFlags::from_bits_truncate(self.0.f_flags as libc::c_ulong)
+ }
+
+ /// Maximum length of filenames
+ #[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn maximum_name_length(&self) -> u32 {
+ self.0.f_namemax
+ }
+
+ /// Maximum length of filenames
+ #[cfg(all(target_os = "linux", target_arch = "s390x"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn maximum_name_length(&self) -> u32 {
+ self.0.f_namelen
+ }
+
+ /// Maximum length of filenames
+ #[cfg(all(target_os = "linux", target_env = "musl"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn maximum_name_length(&self) -> libc::c_ulong {
+ self.0.f_namelen
+ }
+
+ /// Maximum length of filenames
+ #[cfg(all(target_os = "linux", target_env = "uclibc"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn maximum_name_length(&self) -> libc::c_int {
+ self.0.f_namelen
+ }
+
+ /// Maximum length of filenames
+ #[cfg(all(
+ target_os = "linux",
+ not(any(
+ target_arch = "s390x",
+ target_env = "musl",
+ target_env = "uclibc"
+ ))
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn maximum_name_length(&self) -> libc::__fsword_t {
+ self.0.f_namelen
+ }
+
+ /// Maximum length of filenames
+ #[cfg(target_os = "android")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn maximum_name_length(&self) -> libc::c_ulong {
+ self.0.f_namelen
+ }
+
+ /// Total data blocks in filesystem
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "openbsd",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks(&self) -> u64 {
+ self.0.f_blocks
+ }
+
+ /// Total data blocks in filesystem
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks(&self) -> libc::c_long {
+ self.0.f_blocks
+ }
+
+ /// Total data blocks in filesystem
+ #[cfg(target_os = "emscripten")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks(&self) -> u32 {
+ self.0.f_blocks
+ }
+
+ /// Free blocks in filesystem
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "openbsd",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_free(&self) -> u64 {
+ self.0.f_bfree
+ }
+
+ /// Free blocks in filesystem
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_free(&self) -> libc::c_long {
+ self.0.f_bfree
+ }
+
+ /// Free blocks in filesystem
+ #[cfg(target_os = "emscripten")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_free(&self) -> u32 {
+ self.0.f_bfree
+ }
+
+ /// Free blocks available to unprivileged user
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_available(&self) -> u64 {
+ self.0.f_bavail
+ }
+
+ /// Free blocks available to unprivileged user
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_available(&self) -> libc::c_long {
+ self.0.f_bavail
+ }
+
+ /// Free blocks available to unprivileged user
+ #[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_available(&self) -> i64 {
+ self.0.f_bavail
+ }
+
+ /// Free blocks available to unprivileged user
+ #[cfg(target_os = "emscripten")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_available(&self) -> u32 {
+ self.0.f_bavail
+ }
+
+ /// Total file nodes in filesystem
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "openbsd",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files(&self) -> u64 {
+ self.0.f_files
+ }
+
+ /// Total file nodes in filesystem
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files(&self) -> libc::c_long {
+ self.0.f_files
+ }
+
+ /// Total file nodes in filesystem
+ #[cfg(target_os = "emscripten")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files(&self) -> u32 {
+ self.0.f_files
+ }
+
+ /// Free file nodes in filesystem
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "openbsd",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files_free(&self) -> u64 {
+ self.0.f_ffree
+ }
+
+ /// Free file nodes in filesystem
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files_free(&self) -> libc::c_long {
+ self.0.f_ffree
+ }
+
+ /// Free file nodes in filesystem
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files_free(&self) -> i64 {
+ self.0.f_ffree
+ }
+
+ /// Free file nodes in filesystem
+ #[cfg(target_os = "emscripten")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files_free(&self) -> u32 {
+ self.0.f_ffree
+ }
+
+ /// Filesystem ID
+ pub fn filesystem_id(&self) -> fsid_t {
+ self.0.f_fsid
+ }
+}
+
+impl Debug for Statfs {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut ds = f.debug_struct("Statfs");
+ ds.field("optimal_transfer_size", &self.optimal_transfer_size());
+ ds.field("block_size", &self.block_size());
+ ds.field("blocks", &self.blocks());
+ ds.field("blocks_free", &self.blocks_free());
+ ds.field("blocks_available", &self.blocks_available());
+ ds.field("files", &self.files());
+ ds.field("files_free", &self.files_free());
+ ds.field("filesystem_id", &self.filesystem_id());
+ #[cfg(all(
+ feature = "mount",
+ any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )
+ ))]
+ ds.field("flags", &self.flags());
+ ds.finish()
+ }
+}
+
+/// Describes a mounted file system.
+///
+/// The result is OS-dependent. For a portable alternative, see
+/// [`statvfs`](crate::sys::statvfs::statvfs).
+///
+/// # Arguments
+///
+/// `path` - Path to any file within the file system to describe
+pub fn statfs<P: ?Sized + NixPath>(path: &P) -> Result<Statfs> {
+ unsafe {
+ let mut stat = mem::MaybeUninit::<type_of_statfs>::uninit();
+ let res = path.with_nix_path(|path| {
+ LIBC_STATFS(path.as_ptr(), stat.as_mut_ptr())
+ })?;
+ Errno::result(res).map(|_| Statfs(stat.assume_init()))
+ }
+}
+
+/// Describes a mounted file system.
+///
+/// The result is OS-dependent. For a portable alternative, see
+/// [`fstatvfs`](crate::sys::statvfs::fstatvfs).
+///
+/// # Arguments
+///
+/// `fd` - File descriptor of any open file within the file system to describe
+pub fn fstatfs<Fd: AsFd>(fd: Fd) -> Result<Statfs> {
+ unsafe {
+ let mut stat = mem::MaybeUninit::<type_of_statfs>::uninit();
+ Errno::result(LIBC_FSTATFS(fd.as_fd().as_raw_fd(), stat.as_mut_ptr()))
+ .map(|_| Statfs(stat.assume_init()))
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use std::fs::File;
+
+ use crate::sys::statfs::*;
+ use crate::sys::statvfs::*;
+ use std::path::Path;
+
+ #[test]
+ fn statfs_call() {
+ check_statfs("/tmp");
+ check_statfs("/dev");
+ check_statfs("/run");
+ check_statfs("/");
+ }
+
+ #[test]
+ fn fstatfs_call() {
+ check_fstatfs("/tmp");
+ check_fstatfs("/dev");
+ check_fstatfs("/run");
+ check_fstatfs("/");
+ }
+
+ fn check_fstatfs(path: &str) {
+ if !Path::new(path).exists() {
+ return;
+ }
+ let vfs = statvfs(path.as_bytes()).unwrap();
+ let file = File::open(path).unwrap();
+ let fs = fstatfs(&file).unwrap();
+ assert_fs_equals(fs, vfs);
+ }
+
+ fn check_statfs(path: &str) {
+ if !Path::new(path).exists() {
+ return;
+ }
+ let vfs = statvfs(path.as_bytes()).unwrap();
+ let fs = statfs(path.as_bytes()).unwrap();
+ assert_fs_equals(fs, vfs);
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn assert_fs_equals(fs: Statfs, vfs: Statvfs) {
+ assert_eq!(fs.files() as u64, vfs.files() as u64);
+ assert_eq!(fs.blocks() as u64, vfs.blocks() as u64);
+ assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64);
+ }
+
+ // This test is ignored because files_free/blocks_free can change after statvfs call and before
+ // statfs call.
+ #[test]
+ #[ignore]
+ fn statfs_call_strict() {
+ check_statfs_strict("/tmp");
+ check_statfs_strict("/dev");
+ check_statfs_strict("/run");
+ check_statfs_strict("/");
+ }
+
+ // This test is ignored because files_free/blocks_free can change after statvfs call and before
+ // fstatfs call.
+ #[test]
+ #[ignore]
+ fn fstatfs_call_strict() {
+ check_fstatfs_strict("/tmp");
+ check_fstatfs_strict("/dev");
+ check_fstatfs_strict("/run");
+ check_fstatfs_strict("/");
+ }
+
+ fn check_fstatfs_strict(path: &str) {
+ if !Path::new(path).exists() {
+ return;
+ }
+ let vfs = statvfs(path.as_bytes());
+ let file = File::open(path).unwrap();
+ let fs = fstatfs(&file);
+ assert_fs_equals_strict(fs.unwrap(), vfs.unwrap())
+ }
+
+ fn check_statfs_strict(path: &str) {
+ if !Path::new(path).exists() {
+ return;
+ }
+ let vfs = statvfs(path.as_bytes());
+ let fs = statfs(path.as_bytes());
+ assert_fs_equals_strict(fs.unwrap(), vfs.unwrap())
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn assert_fs_equals_strict(fs: Statfs, vfs: Statvfs) {
+ assert_eq!(fs.files_free() as u64, vfs.files_free() as u64);
+ assert_eq!(fs.blocks_free() as u64, vfs.blocks_free() as u64);
+ assert_eq!(fs.blocks_available() as u64, vfs.blocks_available() as u64);
+ assert_eq!(fs.files() as u64, vfs.files() as u64);
+ assert_eq!(fs.blocks() as u64, vfs.blocks() as u64);
+ assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64);
+ }
+}
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..35424e5e27
--- /dev/null
+++ b/third_party/rust/nix/src/sys/statvfs.rs
@@ -0,0 +1,172 @@
+//! Get filesystem statistics
+//!
+//! See [the man pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html)
+//! for more details.
+use std::mem;
+use std::os::unix::io::{AsFd, AsRawFd};
+
+use libc::{self, c_ulong};
+
+use crate::{errno::Errno, NixPath, Result};
+
+#[cfg(not(target_os = "redox"))]
+libc_bitflags!(
+ /// File system mount Flags
+ #[derive(Default)]
+ pub struct FsFlags: c_ulong {
+ /// Read Only
+ #[cfg(not(target_os = "haiku"))]
+ ST_RDONLY;
+ /// Do not allow the set-uid bits to have an effect
+ #[cfg(not(target_os = "haiku"))]
+ ST_NOSUID;
+ /// Do not interpret character or block-special devices
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_NODEV;
+ /// Do not allow execution of binaries on the filesystem
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_NOEXEC;
+ /// All IO should be done synchronously
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_SYNCHRONOUS;
+ /// Allow mandatory locks on the filesystem
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_MANDLOCK;
+ /// Write on file/directory/symlink
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_WRITE;
+ /// Append-only file
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_APPEND;
+ /// Immutable file
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_IMMUTABLE;
+ /// Do not update access times on files
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_NOATIME;
+ /// Do not update access times on files
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_NODIRATIME;
+ /// Update access time relative to modify/change time
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_env = "musl"))))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_RELATIME;
+ }
+);
+
+/// Wrapper around the POSIX `statvfs` struct
+///
+/// For more information see the [`statvfs(3)` man pages](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_statvfs.h.html).
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct Statvfs(libc::statvfs);
+
+impl Statvfs {
+ /// get the file system block size
+ pub fn block_size(&self) -> c_ulong {
+ self.0.f_bsize
+ }
+
+ /// Get the fundamental file system block size
+ pub fn fragment_size(&self) -> c_ulong {
+ self.0.f_frsize
+ }
+
+ /// Get the number of blocks.
+ ///
+ /// Units are in units of `fragment_size()`
+ pub fn blocks(&self) -> libc::fsblkcnt_t {
+ self.0.f_blocks
+ }
+
+ /// Get the number of free blocks in the file system
+ pub fn blocks_free(&self) -> libc::fsblkcnt_t {
+ self.0.f_bfree
+ }
+
+ /// Get the number of free blocks for unprivileged users
+ pub fn blocks_available(&self) -> libc::fsblkcnt_t {
+ self.0.f_bavail
+ }
+
+ /// Get the total number of file inodes
+ pub fn files(&self) -> libc::fsfilcnt_t {
+ self.0.f_files
+ }
+
+ /// Get the number of free file inodes
+ pub fn files_free(&self) -> libc::fsfilcnt_t {
+ self.0.f_ffree
+ }
+
+ /// Get the number of free file inodes for unprivileged users
+ pub fn files_available(&self) -> libc::fsfilcnt_t {
+ self.0.f_favail
+ }
+
+ /// Get the file system id
+ pub fn filesystem_id(&self) -> c_ulong {
+ self.0.f_fsid
+ }
+
+ /// Get the mount flags
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn flags(&self) -> FsFlags {
+ FsFlags::from_bits_truncate(self.0.f_flag)
+ }
+
+ /// Get the maximum filename length
+ pub fn name_max(&self) -> c_ulong {
+ self.0.f_namemax
+ }
+}
+
+/// Return a `Statvfs` object with information about the `path`
+pub fn statvfs<P: ?Sized + NixPath>(path: &P) -> Result<Statvfs> {
+ unsafe {
+ Errno::clear();
+ let mut stat = mem::MaybeUninit::<libc::statvfs>::uninit();
+ let res = path.with_nix_path(|path| {
+ libc::statvfs(path.as_ptr(), stat.as_mut_ptr())
+ })?;
+
+ Errno::result(res).map(|_| Statvfs(stat.assume_init()))
+ }
+}
+
+/// Return a `Statvfs` object with information about `fd`
+pub fn fstatvfs<Fd: AsFd>(fd: Fd) -> Result<Statvfs> {
+ unsafe {
+ Errno::clear();
+ let mut stat = mem::MaybeUninit::<libc::statvfs>::uninit();
+ Errno::result(libc::fstatvfs(fd.as_fd().as_raw_fd(), stat.as_mut_ptr()))
+ .map(|_| Statvfs(stat.assume_init()))
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use crate::sys::statvfs::*;
+ use std::fs::File;
+
+ #[test]
+ fn statvfs_call() {
+ statvfs(&b"/"[..]).unwrap();
+ }
+
+ #[test]
+ fn fstatvfs_call() {
+ let root = File::open("/").unwrap();
+ fstatvfs(&root).unwrap();
+ }
+}
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..e8aa00b00d
--- /dev/null
+++ b/third_party/rust/nix/src/sys/sysinfo.rs
@@ -0,0 +1,83 @@
+use libc::{self, SI_LOAD_SHIFT};
+use std::time::Duration;
+use std::{cmp, mem};
+
+use crate::errno::Errno;
+use crate::Result;
+
+/// System info structure returned by `sysinfo`.
+#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
+#[repr(transparent)]
+pub struct SysInfo(libc::sysinfo);
+
+// The fields are c_ulong on 32-bit linux, u64 on 64-bit linux; x32's ulong is u32
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
+type mem_blocks_t = u64;
+#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
+type mem_blocks_t = libc::c_ulong;
+
+impl SysInfo {
+ /// Returns the load average tuple.
+ ///
+ /// The returned values represent the load average over time intervals of
+ /// 1, 5, and 15 minutes, respectively.
+ pub fn load_average(&self) -> (f64, f64, f64) {
+ (
+ self.0.loads[0] as f64 / (1 << SI_LOAD_SHIFT) as f64,
+ self.0.loads[1] as f64 / (1 << SI_LOAD_SHIFT) as f64,
+ self.0.loads[2] as f64 / (1 << SI_LOAD_SHIFT) as f64,
+ )
+ }
+
+ /// Returns the time since system boot.
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ pub fn uptime(&self) -> Duration {
+ // Truncate negative values to 0
+ Duration::from_secs(cmp::max(self.0.uptime, 0) as u64)
+ }
+
+ /// Current number of processes.
+ pub fn process_count(&self) -> u16 {
+ self.0.procs
+ }
+
+ /// Returns the amount of swap memory in Bytes.
+ pub fn swap_total(&self) -> u64 {
+ self.scale_mem(self.0.totalswap)
+ }
+
+ /// Returns the amount of unused swap memory in Bytes.
+ pub fn swap_free(&self) -> u64 {
+ self.scale_mem(self.0.freeswap)
+ }
+
+ /// Returns the total amount of installed RAM in Bytes.
+ pub fn ram_total(&self) -> u64 {
+ self.scale_mem(self.0.totalram)
+ }
+
+ /// Returns the amount of completely unused RAM in Bytes.
+ ///
+ /// "Unused" in this context means that the RAM in neither actively used by
+ /// programs, nor by the operating system as disk cache or buffer. It is
+ /// "wasted" RAM since it currently serves no purpose.
+ pub fn ram_unused(&self) -> u64 {
+ self.scale_mem(self.0.freeram)
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn scale_mem(&self, units: mem_blocks_t) -> u64 {
+ units as u64 * self.0.mem_unit as u64
+ }
+}
+
+/// Returns system information.
+///
+/// [See `sysinfo(2)`](https://man7.org/linux/man-pages/man2/sysinfo.2.html).
+pub fn sysinfo() -> Result<SysInfo> {
+ let mut info = mem::MaybeUninit::uninit();
+ let res = unsafe { libc::sysinfo(info.as_mut_ptr()) };
+ Errno::result(res).map(|_| unsafe { SysInfo(info.assume_init()) })
+}
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..ecaa3eaf8f
--- /dev/null
+++ b/third_party/rust/nix/src/sys/termios.rs
@@ -0,0 +1,1259 @@
+//! An interface for controlling asynchronous communication ports
+//!
+//! This interface provides a safe wrapper around the termios subsystem defined by POSIX. The
+//! underlying types are all implemented in libc for most platforms and either wrapped in safer
+//! types here or exported directly.
+//!
+//! If you are unfamiliar with the `termios` API, you should first read the
+//! [API documentation](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html) and
+//! then come back to understand how `nix` safely wraps it.
+//!
+//! It should be noted that this API incurs some runtime overhead above the base `libc` definitions.
+//! As this interface is not used with high-bandwidth information, this should be fine in most
+//! cases. The primary cost when using this API is that the `Termios` datatype here duplicates the
+//! standard fields of the underlying `termios` struct and uses safe type wrappers for those fields.
+//! This means that when crossing the FFI interface to the underlying C library, data is first
+//! copied into the underlying `termios` struct, then the operation is done, and the data is copied
+//! back (with additional sanity checking) into the safe wrapper types. The `termios` struct is
+//! relatively small across all platforms (on the order of 32-64 bytes).
+//!
+//! The following examples highlight some of the API use cases such that users coming from using C
+//! or reading the standard documentation will understand how to use the safe API exposed here.
+//!
+//! Example disabling processing of the end-of-file control character:
+//!
+//! ```
+//! # use self::nix::sys::termios::SpecialCharacterIndices::VEOF;
+//! # use self::nix::sys::termios::{_POSIX_VDISABLE, Termios};
+//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
+//! termios.control_chars[VEOF as usize] = _POSIX_VDISABLE;
+//! ```
+//!
+//! The flags within `Termios` are defined as bitfields using the `bitflags` crate. This provides
+//! an interface for working with bitfields that is similar to working with the raw unsigned
+//! integer types but offers type safety because of the internal checking that values will always
+//! be a valid combination of the defined flags.
+//!
+//! An example showing some of the basic operations for interacting with the control flags:
+//!
+//! ```
+//! # use self::nix::sys::termios::{ControlFlags, Termios};
+//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
+//! termios.control_flags & ControlFlags::CSIZE == ControlFlags::CS5;
+//! termios.control_flags |= ControlFlags::CS5;
+//! ```
+//!
+//! # Baud rates
+//!
+//! This API is not consistent across platforms when it comes to `BaudRate`: Android and Linux both
+//! only support the rates specified by the `BaudRate` enum through their termios API while the BSDs
+//! support arbitrary baud rates as the values of the `BaudRate` enum constants are the same integer
+//! value of the constant (`B9600` == `9600`). Therefore the `nix::termios` API uses the following
+//! conventions:
+//!
+//! * `cfgetispeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
+//! * `cfgetospeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
+//! * `cfsetispeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
+//! * `cfsetospeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
+//! * `cfsetspeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
+//!
+//! The most common use case of specifying a baud rate using the enum will work the same across
+//! platforms:
+//!
+//! ```rust
+//! # use nix::sys::termios::{BaudRate, cfsetispeed, cfsetospeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t: Termios = unsafe { std::mem::zeroed() };
+//! cfsetispeed(&mut t, BaudRate::B9600).unwrap();
+//! cfsetospeed(&mut t, BaudRate::B9600).unwrap();
+//! cfsetspeed(&mut t, BaudRate::B9600).unwrap();
+//! # }
+//! ```
+//!
+//! Additionally round-tripping baud rates is consistent across platforms:
+//!
+//! ```rust
+//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetispeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t: Termios = unsafe { std::mem::zeroed() };
+//! # cfsetspeed(&mut t, BaudRate::B9600).unwrap();
+//! let speed = cfgetispeed(&t);
+//! assert_eq!(speed, cfgetospeed(&t));
+//! cfsetispeed(&mut t, speed).unwrap();
+//! # }
+//! ```
+//!
+//! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`:
+//!
+#![cfg_attr(
+ 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"
+)]
+//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t: Termios = unsafe { std::mem::zeroed() };
+//! # cfsetspeed(&mut t, BaudRate::B9600);
+//! assert_eq!(cfgetispeed(&t), BaudRate::B9600);
+//! assert_eq!(cfgetospeed(&t), BaudRate::B9600);
+//! # }
+//! ```
+//!
+//! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s:
+//!
+#![cfg_attr(
+ 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"
+)]
+//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t: Termios = unsafe { std::mem::zeroed() };
+//! # cfsetspeed(&mut t, 9600u32);
+//! assert_eq!(cfgetispeed(&t), 9600u32);
+//! assert_eq!(cfgetospeed(&t), 9600u32);
+//! # }
+//! ```
+//!
+//! It's trivial to convert from a `BaudRate` to a `u32` on BSDs:
+//!
+#![cfg_attr(
+ 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"
+)]
+//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t: Termios = unsafe { std::mem::zeroed() };
+//! # cfsetspeed(&mut t, 9600u32);
+//! assert_eq!(cfgetispeed(&t), BaudRate::B9600.into());
+//! assert_eq!(u32::from(BaudRate::B9600), 9600u32);
+//! # }
+//! ```
+//!
+//! And on BSDs you can specify arbitrary baud rates (**note** this depends on hardware support)
+//! by specifying baud rates directly using `u32`s:
+//!
+#![cfg_attr(
+ 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"
+)]
+//! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t: Termios = unsafe { std::mem::zeroed() };
+//! cfsetispeed(&mut t, 9600u32);
+//! cfsetospeed(&mut t, 9600u32);
+//! cfsetspeed(&mut t, 9600u32);
+//! # }
+//! ```
+use crate::errno::Errno;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_int, tcflag_t};
+use std::cell::{Ref, RefCell};
+use std::convert::From;
+use std::mem;
+use std::os::unix::io::{AsFd, AsRawFd};
+
+#[cfg(feature = "process")]
+use crate::unistd::Pid;
+
+/// Stores settings for the termios API
+///
+/// This is a wrapper around the `libc::termios` struct that provides a safe interface for the
+/// standard fields. The only safe way to obtain an instance of this struct is to extract it from
+/// an open port using `tcgetattr()`.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Termios {
+ inner: RefCell<libc::termios>,
+ /// Input mode flags (see `termios.c_iflag` documentation)
+ pub input_flags: InputFlags,
+ /// Output mode flags (see `termios.c_oflag` documentation)
+ pub output_flags: OutputFlags,
+ /// Control mode flags (see `termios.c_cflag` documentation)
+ pub control_flags: ControlFlags,
+ /// Local mode flags (see `termios.c_lflag` documentation)
+ pub local_flags: LocalFlags,
+ /// Control characters (see `termios.c_cc` documentation)
+ pub control_chars: [libc::cc_t; NCCS],
+ /// Line discipline (see `termios.c_line` documentation)
+ #[cfg(any(target_os = "linux", target_os = "android",))]
+ pub line_discipline: libc::cc_t,
+ /// Line discipline (see `termios.c_line` documentation)
+ #[cfg(target_os = "haiku")]
+ pub line_discipline: libc::c_char,
+}
+
+impl Termios {
+ /// Exposes an immutable reference to the underlying `libc::termios` data structure.
+ ///
+ /// This is not part of `nix`'s public API because it requires additional work to maintain type
+ /// safety.
+ pub(crate) fn get_libc_termios(&self) -> Ref<libc::termios> {
+ {
+ let mut termios = self.inner.borrow_mut();
+ termios.c_iflag = self.input_flags.bits();
+ termios.c_oflag = self.output_flags.bits();
+ termios.c_cflag = self.control_flags.bits();
+ termios.c_lflag = self.local_flags.bits();
+ termios.c_cc = self.control_chars;
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "haiku",
+ ))]
+ {
+ termios.c_line = self.line_discipline;
+ }
+ }
+ self.inner.borrow()
+ }
+
+ /// Exposes the inner `libc::termios` datastore within `Termios`.
+ ///
+ /// This is unsafe because if this is used to modify the inner `libc::termios` struct, it will
+ /// not automatically update the safe wrapper type around it. In this case it should also be
+ /// paired with a call to `update_wrapper()` so that the wrapper-type and internal
+ /// representation stay consistent.
+ pub(crate) unsafe fn get_libc_termios_mut(&mut self) -> *mut libc::termios {
+ {
+ let mut termios = self.inner.borrow_mut();
+ termios.c_iflag = self.input_flags.bits();
+ termios.c_oflag = self.output_flags.bits();
+ termios.c_cflag = self.control_flags.bits();
+ termios.c_lflag = self.local_flags.bits();
+ termios.c_cc = self.control_chars;
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "haiku",
+ ))]
+ {
+ termios.c_line = self.line_discipline;
+ }
+ }
+ self.inner.as_ptr()
+ }
+
+ /// Updates the wrapper values from the internal `libc::termios` data structure.
+ pub(crate) fn update_wrapper(&mut self) {
+ let termios = *self.inner.borrow_mut();
+ self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag);
+ self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag);
+ self.control_flags = ControlFlags::from_bits_retain(termios.c_cflag);
+ self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag);
+ self.control_chars = termios.c_cc;
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "haiku",
+ ))]
+ {
+ self.line_discipline = termios.c_line;
+ }
+ }
+}
+
+impl From<libc::termios> for Termios {
+ fn from(termios: libc::termios) -> Self {
+ Termios {
+ inner: RefCell::new(termios),
+ input_flags: InputFlags::from_bits_truncate(termios.c_iflag),
+ output_flags: OutputFlags::from_bits_truncate(termios.c_oflag),
+ control_flags: ControlFlags::from_bits_truncate(termios.c_cflag),
+ local_flags: LocalFlags::from_bits_truncate(termios.c_lflag),
+ control_chars: termios.c_cc,
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "haiku",
+ ))]
+ line_discipline: termios.c_line,
+ }
+ }
+}
+
+impl From<Termios> for libc::termios {
+ fn from(termios: Termios) -> Self {
+ termios.inner.into_inner()
+ }
+}
+
+libc_enum! {
+ /// Baud rates supported by the system.
+ ///
+ /// For the BSDs, arbitrary baud rates can be specified by using `u32`s directly instead of this
+ /// enum.
+ ///
+ /// B0 is special and will disable the port.
+ #[cfg_attr(target_os = "haiku", repr(u8))]
+ #[cfg_attr(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))]
+ #[cfg_attr(all(not(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64")), not(target_os = "haiku")), repr(u32))]
+ #[non_exhaustive]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B7200,
+ B9600,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B14400,
+ B19200,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B28800,
+ B38400,
+ #[cfg(not(target_os = "aix"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B57600,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B76800,
+ #[cfg(not(target_os = "aix"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B115200,
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B153600,
+ #[cfg(not(target_os = "aix"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B230400,
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B307200,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B460800,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B576000,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B921600,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B1000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B1152000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B1500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B2000000,
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B2500000,
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B3000000,
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B3500000,
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B4000000,
+ }
+ impl TryFrom<libc::speed_t>
+}
+
+#[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
+ }
+}
+
+#[cfg(target_os = "haiku")]
+impl From<BaudRate> for u8 {
+ fn from(b: BaudRate) -> u8 {
+ b as u8
+ }
+}
+
+// TODO: Add TCSASOFT, which will require treating this as a bitfield.
+libc_enum! {
+ /// Specify when a port configuration change should occur.
+ ///
+ /// Used as an argument to `tcsetattr()`
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum SetArg {
+ /// The change will occur immediately
+ TCSANOW,
+ /// The change occurs after all output has been written
+ TCSADRAIN,
+ /// Same as `TCSADRAIN`, but will also flush the input buffer
+ TCSAFLUSH,
+ }
+}
+
+libc_enum! {
+ /// Specify a combination of the input and output buffers to flush
+ ///
+ /// Used as an argument to `tcflush()`.
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum FlushArg {
+ /// Flush data that was received but not read
+ TCIFLUSH,
+ /// Flush data written but not transmitted
+ TCOFLUSH,
+ /// Flush both received data not read and written data not transmitted
+ TCIOFLUSH,
+ }
+}
+
+libc_enum! {
+ /// Specify how transmission flow should be altered
+ ///
+ /// Used as an argument to `tcflow()`.
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum FlowArg {
+ /// Suspend transmission
+ TCOOFF,
+ /// Resume transmission
+ TCOON,
+ /// Transmit a STOP character, which should disable a connected terminal device
+ TCIOFF,
+ /// Transmit a START character, which should re-enable a connected terminal device
+ TCION,
+ }
+}
+
+// TODO: Make this usable directly as a slice index.
+#[cfg(not(target_os = "haiku"))]
+libc_enum! {
+ /// Indices into the `termios.c_cc` array for special characters.
+ #[repr(usize)]
+ #[non_exhaustive]
+ pub enum SpecialCharacterIndices {
+ #[cfg(not(target_os = "aix"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VDISCARD,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "aix",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VDSUSP,
+ VEOF,
+ VEOL,
+ VEOL2,
+ VERASE,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VERASE2,
+ VINTR,
+ VKILL,
+ VLNEXT,
+ #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
+ target_os = "illumos", target_os = "solaris", target_os = "aix")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VMIN,
+ VQUIT,
+ VREPRINT,
+ VSTART,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VSTATUS,
+ VSTOP,
+ VSUSP,
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VSWTC,
+ #[cfg(any(target_os = "haiku", target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VSWTCH,
+ #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
+ target_os = "illumos", target_os = "solaris", target_os = "aix")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VTIME,
+ #[cfg(not(target_os = "aix"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VWERASE,
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VCHECKPT,
+ }
+}
+
+#[cfg(any(
+ all(target_os = "linux", target_arch = "sparc64"),
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "aix",
+))]
+impl SpecialCharacterIndices {
+ pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF;
+ pub const VTIME: SpecialCharacterIndices = SpecialCharacterIndices::VEOL;
+}
+
+pub use libc::NCCS;
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "aix",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub use libc::_POSIX_VDISABLE;
+
+libc_bitflags! {
+ /// Flags for configuring the input mode of a terminal
+ pub struct InputFlags: tcflag_t {
+ IGNBRK;
+ BRKINT;
+ IGNPAR;
+ PARMRK;
+ INPCK;
+ ISTRIP;
+ INLCR;
+ IGNCR;
+ ICRNL;
+ IXON;
+ IXOFF;
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IXANY;
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IMAXBEL;
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ OFILL as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ OFDEL as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NL0 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NL1 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CR0 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CR1 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CR2 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CR3 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ TAB0 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ TAB1 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ TAB2 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ TAB3 as tcflag_t;
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ XTABS;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BS0 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BS1 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VT0 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VT1 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ FF0 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ FF1 as tcflag_t;
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ OXTABS;
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CRDLY as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ TABDLY as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BSDLY as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VTDLY as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CIGNORE;
+ CS5;
+ CS6;
+ CS7;
+ CS8;
+ CSTOPB;
+ CREAD;
+ PARENB;
+ PARODD;
+ HUPCL;
+ CLOCAL;
+ #[cfg(not(any(target_os = "redox", target_os = "aix")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CRTSCTS;
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CBAUD;
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CBAUDEX;
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MDMBUF;
+ #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CHWFLOW;
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CCTS_OFLOW;
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CRTS_IFLOW;
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CDTR_IFLOW;
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CDSR_OFLOW;
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CCAR_OFLOW;
+
+ // Bitmasks for use with ControlFlags to select specific settings
+ // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
+ // is resolved.
+
+ CSIZE;
+ }
+}
+
+libc_bitflags! {
+ /// Flags for setting any local modes
+ pub struct LocalFlags: tcflag_t {
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ECHOKE;
+ ECHOE;
+ ECHOK;
+ ECHO;
+ ECHONL;
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ECHOPRT;
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ECHOCTL;
+ ISIG;
+ ICANON;
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ALTWERASE;
+ IEXTEN;
+ #[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "aix")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ EXTPROC;
+ TOSTOP;
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ FLUSHO;
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NOKERNINFO;
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
+ ///
+ /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ pub fn cfgetispeed(termios: &Termios) -> u32 {
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetispeed(&*inner_termios) as u32 }
+ }
+
+ /// Get output baud rate (see
+ /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
+ ///
+ /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ pub fn cfgetospeed(termios: &Termios) -> u32 {
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetospeed(&*inner_termios) as u32 }
+ }
+
+ /// Set input baud rate (see
+ /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
+ ///
+ /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
+ pub fn cfsetispeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetispeed(inner_termios, baud.into() as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
+ }
+
+ /// Set output baud rate (see
+ /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
+ ///
+ /// `cfsetospeed()` sets the output baud rate in the given termios structure.
+ pub fn cfsetospeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetospeed(inner_termios, baud.into() as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
+ }
+
+ /// Set both the input and output baud rates (see
+ /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
+ ///
+ /// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
+ /// this is part of the 4.4BSD standard and not part of POSIX.
+ pub fn cfsetspeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetspeed(inner_termios, baud.into() as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
+ }
+ } else {
+ use std::convert::TryInto;
+
+ /// Get input baud rate (see
+ /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
+ ///
+ /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
+ pub fn cfgetispeed(termios: &Termios) -> BaudRate {
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap()
+ }
+
+ /// Get output baud rate (see
+ /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
+ ///
+ /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
+ pub fn cfgetospeed(termios: &Termios) -> BaudRate {
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetospeed(&*inner_termios) }.try_into().unwrap()
+ }
+
+ /// Set input baud rate (see
+ /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
+ ///
+ /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
+ pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
+ }
+
+ /// Set output baud rate (see
+ /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
+ ///
+ /// `cfsetospeed()` sets the output baud rate in the given `Termios` structure.
+ pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
+ }
+
+ /// Set both the input and output baud rates (see
+ /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
+ ///
+ /// `cfsetspeed()` sets the input and output baud rate in the given `Termios` structure. Note that
+ /// this is part of the 4.4BSD standard and not part of POSIX.
+ #[cfg(not(target_os = "haiku"))]
+ pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
+ }
+ }
+}
+
+/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see
+/// [termios(3)](https://man7.org/linux/man-pages/man3/termios.3.html)).
+///
+/// `cfmakeraw()` configures the termios structure such that input is available character-by-
+/// character, echoing is disabled, and all special input and output processing is disabled. Note
+/// that this is a non-standard function, but is available on Linux and BSDs.
+pub fn cfmakeraw(termios: &mut Termios) {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ unsafe {
+ libc::cfmakeraw(inner_termios);
+ }
+ termios.update_wrapper();
+}
+
+/// Configures the port to "sane" mode (like the configuration of a newly created terminal) (see
+/// [tcsetattr(3)](https://www.freebsd.org/cgi/man.cgi?query=tcsetattr)).
+///
+/// Note that this is a non-standard function, available on FreeBSD.
+#[cfg(target_os = "freebsd")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn cfmakesane(termios: &mut Termios) {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ unsafe {
+ libc::cfmakesane(inner_termios);
+ }
+ termios.update_wrapper();
+}
+
+/// Return the configuration of a port
+/// [tcgetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)).
+///
+/// `tcgetattr()` returns a `Termios` structure with the current configuration for a port. Modifying
+/// this structure *will not* reconfigure the port, instead the modifications should be done to
+/// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`.
+pub fn tcgetattr<Fd: AsFd>(fd: Fd) -> Result<Termios> {
+ let mut termios = mem::MaybeUninit::uninit();
+
+ let res = unsafe {
+ libc::tcgetattr(fd.as_fd().as_raw_fd(), termios.as_mut_ptr())
+ };
+
+ Errno::result(res)?;
+
+ unsafe { Ok(termios.assume_init().into()) }
+}
+
+/// Set the configuration for a terminal (see
+/// [tcsetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)).
+///
+/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change
+/// takes affect at a time specified by `actions`. Note that this function may return success if
+/// *any* of the parameters were successfully set, not only if all were set successfully.
+pub fn tcsetattr<Fd: AsFd>(
+ fd: Fd,
+ actions: SetArg,
+ termios: &Termios,
+) -> Result<()> {
+ let inner_termios = termios.get_libc_termios();
+ Errno::result(unsafe {
+ libc::tcsetattr(
+ fd.as_fd().as_raw_fd(),
+ actions as c_int,
+ &*inner_termios,
+ )
+ })
+ .map(drop)
+}
+
+/// Block until all output data is written (see
+/// [tcdrain(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
+pub fn tcdrain<Fd: AsFd>(fd: Fd) -> Result<()> {
+ Errno::result(unsafe { libc::tcdrain(fd.as_fd().as_raw_fd()) }).map(drop)
+}
+
+/// Suspend or resume the transmission or reception of data (see
+/// [tcflow(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)).
+///
+/// `tcflow()` suspends of resumes the transmission or reception of data for the given port
+/// depending on the value of `action`.
+pub fn tcflow<Fd: AsFd>(fd: Fd, action: FlowArg) -> Result<()> {
+ Errno::result(unsafe {
+ libc::tcflow(fd.as_fd().as_raw_fd(), action as c_int)
+ })
+ .map(drop)
+}
+
+/// Discard data in the output or input queue (see
+/// [tcflush(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)).
+///
+/// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both
+/// depending on the value of `action`.
+pub fn tcflush<Fd: AsFd>(fd: Fd, action: FlushArg) -> Result<()> {
+ Errno::result(unsafe {
+ libc::tcflush(fd.as_fd().as_raw_fd(), action as c_int)
+ })
+ .map(drop)
+}
+
+/// Send a break for a specific duration (see
+/// [tcsendbreak(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)).
+///
+/// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream
+/// of zero-valued bits for an implementation-defined duration.
+pub fn tcsendbreak<Fd: AsFd>(fd: Fd, duration: c_int) -> Result<()> {
+ Errno::result(unsafe {
+ libc::tcsendbreak(fd.as_fd().as_raw_fd(), duration)
+ })
+ .map(drop)
+}
+
+feature! {
+#![feature = "process"]
+/// Get the session controlled by the given terminal (see
+/// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
+pub fn tcgetsid<Fd: AsFd>(fd: Fd) -> Result<Pid> {
+ let res = unsafe { libc::tcgetsid(fd.as_fd().as_raw_fd()) };
+
+ Errno::result(res).map(Pid::from_raw)
+}
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::convert::TryFrom;
+
+ #[test]
+ fn try_from() {
+ assert_eq!(Ok(BaudRate::B0), BaudRate::try_from(libc::B0));
+ #[cfg(not(target_os = "haiku"))]
+ BaudRate::try_from(999999999).expect_err("assertion failed");
+ #[cfg(target_os = "haiku")]
+ BaudRate::try_from(99).expect_err("assertion failed");
+ }
+}
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..a0160e21ff
--- /dev/null
+++ b/third_party/rust/nix/src/sys/time.rs
@@ -0,0 +1,812 @@
+#[cfg_attr(target_env = "musl", allow(deprecated))]
+// https://github.com/rust-lang/libc/issues/1848
+pub use libc::{suseconds_t, time_t};
+use libc::{timespec, timeval};
+use std::convert::From;
+use std::time::Duration;
+use std::{cmp, fmt, ops};
+
+const fn zero_init_timespec() -> timespec {
+ // `std::mem::MaybeUninit::zeroed()` is not yet a const fn
+ // (https://github.com/rust-lang/rust/issues/91850) so we will instead initialize an array of
+ // the appropriate size to zero and then transmute it to a timespec value.
+ unsafe { std::mem::transmute([0u8; std::mem::size_of::<timespec>()]) }
+}
+
+#[cfg(any(
+ all(feature = "time", any(target_os = "android", target_os = "linux")),
+ all(
+ any(
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd"
+ ),
+ feature = "time",
+ feature = "signal"
+ )
+))]
+pub(crate) mod timer {
+ use crate::sys::time::{zero_init_timespec, TimeSpec};
+ use bitflags::bitflags;
+
+ #[derive(Debug, Clone, Copy)]
+ pub(crate) struct TimerSpec(libc::itimerspec);
+
+ impl TimerSpec {
+ pub const fn none() -> Self {
+ Self(libc::itimerspec {
+ it_interval: zero_init_timespec(),
+ it_value: zero_init_timespec(),
+ })
+ }
+ }
+
+ impl AsMut<libc::itimerspec> for TimerSpec {
+ fn as_mut(&mut self) -> &mut libc::itimerspec {
+ &mut self.0
+ }
+ }
+
+ impl AsRef<libc::itimerspec> for TimerSpec {
+ fn as_ref(&self) -> &libc::itimerspec {
+ &self.0
+ }
+ }
+
+ impl From<Expiration> for TimerSpec {
+ fn from(expiration: Expiration) -> TimerSpec {
+ match expiration {
+ Expiration::OneShot(t) => TimerSpec(libc::itimerspec {
+ it_interval: zero_init_timespec(),
+ it_value: *t.as_ref(),
+ }),
+ Expiration::IntervalDelayed(start, interval) => {
+ TimerSpec(libc::itimerspec {
+ it_interval: *interval.as_ref(),
+ it_value: *start.as_ref(),
+ })
+ }
+ Expiration::Interval(t) => TimerSpec(libc::itimerspec {
+ it_interval: *t.as_ref(),
+ it_value: *t.as_ref(),
+ }),
+ }
+ }
+ }
+
+ /// An enumeration allowing the definition of the expiration time of an alarm,
+ /// recurring or not.
+ #[derive(Debug, Clone, Copy, Eq, PartialEq)]
+ pub enum Expiration {
+ /// Alarm will trigger once after the time given in `TimeSpec`
+ OneShot(TimeSpec),
+ /// Alarm will trigger after a specified delay and then every interval of
+ /// time.
+ IntervalDelayed(TimeSpec, TimeSpec),
+ /// Alarm will trigger every specified interval of time.
+ Interval(TimeSpec),
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ bitflags! {
+ /// Flags that are used for arming the timer.
+ #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+ pub struct TimerSetTimeFlags: libc::c_int {
+ const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME;
+ const TFD_TIMER_CANCEL_ON_SET = libc::TFD_TIMER_CANCEL_ON_SET;
+ }
+ }
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "dragonfly",
+ target_os = "illumos"
+ ))]
+ bitflags! {
+ /// Flags that are used for arming the timer.
+ #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+ pub struct TimerSetTimeFlags: libc::c_int {
+ const TFD_TIMER_ABSTIME = libc::TIMER_ABSTIME;
+ }
+ }
+
+ impl From<TimerSpec> for Expiration {
+ fn from(timerspec: TimerSpec) -> Expiration {
+ match timerspec {
+ TimerSpec(libc::itimerspec {
+ it_interval:
+ libc::timespec {
+ tv_sec: 0,
+ tv_nsec: 0,
+ ..
+ },
+ it_value: ts,
+ }) => Expiration::OneShot(ts.into()),
+ TimerSpec(libc::itimerspec {
+ it_interval: int_ts,
+ it_value: val_ts,
+ }) => {
+ if (int_ts.tv_sec == val_ts.tv_sec)
+ && (int_ts.tv_nsec == val_ts.tv_nsec)
+ {
+ Expiration::Interval(int_ts.into())
+ } else {
+ Expiration::IntervalDelayed(
+ val_ts.into(),
+ int_ts.into(),
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+pub trait TimeValLike: Sized {
+ #[inline]
+ fn zero() -> Self {
+ Self::seconds(0)
+ }
+
+ #[inline]
+ fn hours(hours: i64) -> Self {
+ let secs = hours
+ .checked_mul(SECS_PER_HOUR)
+ .expect("TimeValLike::hours ouf of bounds");
+ Self::seconds(secs)
+ }
+
+ #[inline]
+ fn minutes(minutes: i64) -> Self {
+ let secs = minutes
+ .checked_mul(SECS_PER_MINUTE)
+ .expect("TimeValLike::minutes out of bounds");
+ Self::seconds(secs)
+ }
+
+ fn seconds(seconds: i64) -> Self;
+ fn milliseconds(milliseconds: i64) -> Self;
+ fn microseconds(microseconds: i64) -> Self;
+ fn nanoseconds(nanoseconds: i64) -> Self;
+
+ #[inline]
+ fn num_hours(&self) -> i64 {
+ self.num_seconds() / 3600
+ }
+
+ #[inline]
+ fn num_minutes(&self) -> i64 {
+ self.num_seconds() / 60
+ }
+
+ fn num_seconds(&self) -> i64;
+ fn num_milliseconds(&self) -> i64;
+ fn num_microseconds(&self) -> i64;
+ fn num_nanoseconds(&self) -> i64;
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct TimeSpec(timespec);
+
+const NANOS_PER_SEC: i64 = 1_000_000_000;
+const SECS_PER_MINUTE: i64 = 60;
+const SECS_PER_HOUR: i64 = 3600;
+
+#[cfg(target_pointer_width = "64")]
+const TS_MAX_SECONDS: i64 = (i64::MAX / NANOS_PER_SEC) - 1;
+
+#[cfg(target_pointer_width = "32")]
+const TS_MAX_SECONDS: i64 = isize::MAX as i64;
+
+const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS;
+
+// x32 compatibility
+// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
+type timespec_tv_nsec_t = i64;
+#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
+type timespec_tv_nsec_t = libc::c_long;
+
+impl From<timespec> for TimeSpec {
+ fn from(ts: timespec) -> Self {
+ Self(ts)
+ }
+}
+
+impl From<Duration> for TimeSpec {
+ fn from(duration: Duration) -> Self {
+ Self::from_duration(duration)
+ }
+}
+
+impl From<TimeSpec> for Duration {
+ fn from(timespec: TimeSpec) -> Self {
+ Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32)
+ }
+}
+
+impl AsRef<timespec> for TimeSpec {
+ fn as_ref(&self) -> &timespec {
+ &self.0
+ }
+}
+
+impl AsMut<timespec> for TimeSpec {
+ fn as_mut(&mut self) -> &mut timespec {
+ &mut self.0
+ }
+}
+
+impl Ord for TimeSpec {
+ // The implementation of cmp is simplified by assuming that the struct is
+ // normalized. That is, tv_nsec must always be within [0, 1_000_000_000)
+ fn cmp(&self, other: &TimeSpec) -> cmp::Ordering {
+ if self.tv_sec() == other.tv_sec() {
+ self.tv_nsec().cmp(&other.tv_nsec())
+ } else {
+ self.tv_sec().cmp(&other.tv_sec())
+ }
+ }
+}
+
+impl PartialOrd for TimeSpec {
+ fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl TimeValLike for TimeSpec {
+ #[inline]
+ #[cfg_attr(target_env = "musl", allow(deprecated))]
+ // https://github.com/rust-lang/libc/issues/1848
+ fn seconds(seconds: i64) -> TimeSpec {
+ assert!(
+ (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds),
+ "TimeSpec out of bounds; seconds={seconds}",
+ );
+ let mut ts = zero_init_timespec();
+ ts.tv_sec = seconds as time_t;
+ TimeSpec(ts)
+ }
+
+ #[inline]
+ fn milliseconds(milliseconds: i64) -> TimeSpec {
+ let nanoseconds = milliseconds
+ .checked_mul(1_000_000)
+ .expect("TimeSpec::milliseconds out of bounds");
+
+ TimeSpec::nanoseconds(nanoseconds)
+ }
+
+ /// Makes a new `TimeSpec` with given number of microseconds.
+ #[inline]
+ fn microseconds(microseconds: i64) -> TimeSpec {
+ let nanoseconds = microseconds
+ .checked_mul(1_000)
+ .expect("TimeSpec::milliseconds out of bounds");
+
+ TimeSpec::nanoseconds(nanoseconds)
+ }
+
+ /// Makes a new `TimeSpec` with given number of nanoseconds.
+ #[inline]
+ #[cfg_attr(target_env = "musl", allow(deprecated))]
+ // https://github.com/rust-lang/libc/issues/1848
+ fn nanoseconds(nanoseconds: i64) -> TimeSpec {
+ let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
+ assert!(
+ (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs),
+ "TimeSpec out of bounds"
+ );
+ let mut ts = zero_init_timespec();
+ ts.tv_sec = secs as time_t;
+ ts.tv_nsec = nanos as timespec_tv_nsec_t;
+ TimeSpec(ts)
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn num_seconds(&self) -> i64 {
+ if self.tv_sec() < 0 && self.tv_nsec() > 0 {
+ (self.tv_sec() + 1) as i64
+ } else {
+ self.tv_sec() as i64
+ }
+ }
+
+ fn num_milliseconds(&self) -> i64 {
+ self.num_nanoseconds() / 1_000_000
+ }
+
+ fn num_microseconds(&self) -> i64 {
+ self.num_nanoseconds() / 1_000
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn num_nanoseconds(&self) -> i64 {
+ let secs = self.num_seconds() * 1_000_000_000;
+ let nsec = self.nanos_mod_sec();
+ secs + nsec as i64
+ }
+}
+
+impl TimeSpec {
+ /// Construct a new `TimeSpec` from its components
+ #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
+ pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self {
+ let mut ts = zero_init_timespec();
+ ts.tv_sec = seconds;
+ ts.tv_nsec = nanoseconds;
+ Self(ts)
+ }
+
+ fn nanos_mod_sec(&self) -> timespec_tv_nsec_t {
+ if self.tv_sec() < 0 && self.tv_nsec() > 0 {
+ self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t
+ } else {
+ self.tv_nsec()
+ }
+ }
+
+ #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
+ pub const fn tv_sec(&self) -> time_t {
+ self.0.tv_sec
+ }
+
+ pub const fn tv_nsec(&self) -> timespec_tv_nsec_t {
+ self.0.tv_nsec
+ }
+
+ #[cfg_attr(target_env = "musl", allow(deprecated))]
+ // https://github.com/rust-lang/libc/issues/1848
+ pub const fn from_duration(duration: Duration) -> Self {
+ let mut ts = zero_init_timespec();
+ ts.tv_sec = duration.as_secs() as time_t;
+ ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t;
+ TimeSpec(ts)
+ }
+
+ pub const fn from_timespec(timespec: timespec) -> Self {
+ Self(timespec)
+ }
+}
+
+impl ops::Neg for TimeSpec {
+ type Output = TimeSpec;
+
+ fn neg(self) -> TimeSpec {
+ TimeSpec::nanoseconds(-self.num_nanoseconds())
+ }
+}
+
+impl ops::Add for TimeSpec {
+ type Output = TimeSpec;
+
+ fn add(self, rhs: TimeSpec) -> TimeSpec {
+ TimeSpec::nanoseconds(self.num_nanoseconds() + rhs.num_nanoseconds())
+ }
+}
+
+impl ops::Sub for TimeSpec {
+ type Output = TimeSpec;
+
+ fn sub(self, rhs: TimeSpec) -> TimeSpec {
+ TimeSpec::nanoseconds(self.num_nanoseconds() - rhs.num_nanoseconds())
+ }
+}
+
+impl ops::Mul<i32> for TimeSpec {
+ type Output = TimeSpec;
+
+ fn mul(self, rhs: i32) -> TimeSpec {
+ let usec = self
+ .num_nanoseconds()
+ .checked_mul(i64::from(rhs))
+ .expect("TimeSpec multiply out of bounds");
+
+ TimeSpec::nanoseconds(usec)
+ }
+}
+
+impl ops::Div<i32> for TimeSpec {
+ type Output = TimeSpec;
+
+ fn div(self, rhs: i32) -> TimeSpec {
+ let usec = self.num_nanoseconds() / i64::from(rhs);
+ TimeSpec::nanoseconds(usec)
+ }
+}
+
+impl fmt::Display for TimeSpec {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let (abs, sign) = if self.tv_sec() < 0 {
+ (-*self, "-")
+ } else {
+ (*self, "")
+ };
+
+ let sec = abs.tv_sec();
+
+ write!(f, "{sign}")?;
+
+ if abs.tv_nsec() == 0 {
+ if sec == 1 {
+ write!(f, "1 second")?;
+ } else {
+ write!(f, "{sec} seconds")?;
+ }
+ } else if abs.tv_nsec() % 1_000_000 == 0 {
+ write!(f, "{sec}.{:03} seconds", abs.tv_nsec() / 1_000_000)?;
+ } else if abs.tv_nsec() % 1_000 == 0 {
+ write!(f, "{sec}.{:06} seconds", abs.tv_nsec() / 1_000)?;
+ } else {
+ write!(f, "{sec}.{:09} seconds", abs.tv_nsec())?;
+ }
+
+ Ok(())
+ }
+}
+
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct TimeVal(timeval);
+
+const MICROS_PER_SEC: i64 = 1_000_000;
+
+#[cfg(target_pointer_width = "64")]
+const TV_MAX_SECONDS: i64 = (i64::MAX / MICROS_PER_SEC) - 1;
+
+#[cfg(target_pointer_width = "32")]
+const TV_MAX_SECONDS: i64 = isize::MAX as i64;
+
+const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS;
+
+impl AsRef<timeval> for TimeVal {
+ fn as_ref(&self) -> &timeval {
+ &self.0
+ }
+}
+
+impl AsMut<timeval> for TimeVal {
+ fn as_mut(&mut self) -> &mut timeval {
+ &mut self.0
+ }
+}
+
+impl Ord for TimeVal {
+ // The implementation of cmp is simplified by assuming that the struct is
+ // normalized. That is, tv_usec must always be within [0, 1_000_000)
+ fn cmp(&self, other: &TimeVal) -> cmp::Ordering {
+ if self.tv_sec() == other.tv_sec() {
+ self.tv_usec().cmp(&other.tv_usec())
+ } else {
+ self.tv_sec().cmp(&other.tv_sec())
+ }
+ }
+}
+
+impl PartialOrd for TimeVal {
+ fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl TimeValLike for TimeVal {
+ #[inline]
+ fn seconds(seconds: i64) -> TimeVal {
+ assert!(
+ (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds),
+ "TimeVal out of bounds; seconds={seconds}"
+ );
+ #[cfg_attr(target_env = "musl", allow(deprecated))]
+ // https://github.com/rust-lang/libc/issues/1848
+ TimeVal(timeval {
+ tv_sec: seconds as time_t,
+ tv_usec: 0,
+ })
+ }
+
+ #[inline]
+ fn milliseconds(milliseconds: i64) -> TimeVal {
+ let microseconds = milliseconds
+ .checked_mul(1_000)
+ .expect("TimeVal::milliseconds out of bounds");
+
+ TimeVal::microseconds(microseconds)
+ }
+
+ /// Makes a new `TimeVal` with given number of microseconds.
+ #[inline]
+ fn microseconds(microseconds: i64) -> TimeVal {
+ let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
+ assert!(
+ (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
+ "TimeVal out of bounds"
+ );
+ #[cfg_attr(target_env = "musl", allow(deprecated))]
+ // https://github.com/rust-lang/libc/issues/1848
+ TimeVal(timeval {
+ tv_sec: secs as time_t,
+ tv_usec: micros as suseconds_t,
+ })
+ }
+
+ /// Makes a new `TimeVal` with given number of nanoseconds. Some precision
+ /// will be lost
+ #[inline]
+ fn nanoseconds(nanoseconds: i64) -> TimeVal {
+ let microseconds = nanoseconds / 1000;
+ let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
+ assert!(
+ (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
+ "TimeVal out of bounds"
+ );
+ #[cfg_attr(target_env = "musl", allow(deprecated))]
+ // https://github.com/rust-lang/libc/issues/1848
+ TimeVal(timeval {
+ tv_sec: secs as time_t,
+ tv_usec: micros as suseconds_t,
+ })
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn num_seconds(&self) -> i64 {
+ if self.tv_sec() < 0 && self.tv_usec() > 0 {
+ (self.tv_sec() + 1) as i64
+ } else {
+ self.tv_sec() as i64
+ }
+ }
+
+ fn num_milliseconds(&self) -> i64 {
+ self.num_microseconds() / 1_000
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn num_microseconds(&self) -> i64 {
+ let secs = self.num_seconds() * 1_000_000;
+ let usec = self.micros_mod_sec();
+ secs + usec as i64
+ }
+
+ fn num_nanoseconds(&self) -> i64 {
+ self.num_microseconds() * 1_000
+ }
+}
+
+impl TimeVal {
+ /// Construct a new `TimeVal` from its components
+ #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
+ pub const fn new(seconds: time_t, microseconds: suseconds_t) -> Self {
+ Self(timeval {
+ tv_sec: seconds,
+ tv_usec: microseconds,
+ })
+ }
+
+ fn micros_mod_sec(&self) -> suseconds_t {
+ if self.tv_sec() < 0 && self.tv_usec() > 0 {
+ self.tv_usec() - MICROS_PER_SEC as suseconds_t
+ } else {
+ self.tv_usec()
+ }
+ }
+
+ #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
+ pub const fn tv_sec(&self) -> time_t {
+ self.0.tv_sec
+ }
+
+ pub const fn tv_usec(&self) -> suseconds_t {
+ self.0.tv_usec
+ }
+}
+
+impl ops::Neg for TimeVal {
+ type Output = TimeVal;
+
+ fn neg(self) -> TimeVal {
+ TimeVal::microseconds(-self.num_microseconds())
+ }
+}
+
+impl ops::Add for TimeVal {
+ type Output = TimeVal;
+
+ fn add(self, rhs: TimeVal) -> TimeVal {
+ TimeVal::microseconds(self.num_microseconds() + rhs.num_microseconds())
+ }
+}
+
+impl ops::Sub for TimeVal {
+ type Output = TimeVal;
+
+ fn sub(self, rhs: TimeVal) -> TimeVal {
+ TimeVal::microseconds(self.num_microseconds() - rhs.num_microseconds())
+ }
+}
+
+impl ops::Mul<i32> for TimeVal {
+ type Output = TimeVal;
+
+ fn mul(self, rhs: i32) -> TimeVal {
+ let usec = self
+ .num_microseconds()
+ .checked_mul(i64::from(rhs))
+ .expect("TimeVal multiply out of bounds");
+
+ TimeVal::microseconds(usec)
+ }
+}
+
+impl ops::Div<i32> for TimeVal {
+ type Output = TimeVal;
+
+ fn div(self, rhs: i32) -> TimeVal {
+ let usec = self.num_microseconds() / i64::from(rhs);
+ TimeVal::microseconds(usec)
+ }
+}
+
+impl fmt::Display for TimeVal {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let (abs, sign) = if self.tv_sec() < 0 {
+ (-*self, "-")
+ } else {
+ (*self, "")
+ };
+
+ let sec = abs.tv_sec();
+
+ write!(f, "{sign}")?;
+
+ if abs.tv_usec() == 0 {
+ if sec == 1 {
+ write!(f, "1 second")?;
+ } else {
+ write!(f, "{sec} seconds")?;
+ }
+ } else if abs.tv_usec() % 1000 == 0 {
+ write!(f, "{sec}.{:03} seconds", abs.tv_usec() / 1000)?;
+ } else {
+ write!(f, "{sec}.{:06} seconds", abs.tv_usec())?;
+ }
+
+ Ok(())
+ }
+}
+
+impl From<timeval> for TimeVal {
+ fn from(tv: timeval) -> Self {
+ TimeVal(tv)
+ }
+}
+
+#[inline]
+fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
+ (div_floor_64(this, other), mod_floor_64(this, other))
+}
+
+#[inline]
+fn div_floor_64(this: i64, other: i64) -> i64 {
+ match div_rem_64(this, other) {
+ (d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1,
+ (d, _) => d,
+ }
+}
+
+#[inline]
+fn mod_floor_64(this: i64, other: i64) -> i64 {
+ match this % other {
+ r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other,
+ r => r,
+ }
+}
+
+#[inline]
+fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
+ (this / other, this % other)
+}
+
+#[cfg(test)]
+mod test {
+ use super::{TimeSpec, TimeVal, TimeValLike};
+ use std::time::Duration;
+
+ #[test]
+ pub fn test_timespec() {
+ assert_ne!(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_from() {
+ let duration = Duration::new(123, 123_456_789);
+ let timespec = TimeSpec::nanoseconds(123_123_456_789);
+
+ assert_eq!(TimeSpec::from(duration), timespec);
+ assert_eq!(Duration::from(timespec), duration);
+ }
+
+ #[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_eq!(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_ne!(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_eq!(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/timer.rs b/third_party/rust/nix/src/sys/timer.rs
new file mode 100644
index 0000000000..e1a34051e8
--- /dev/null
+++ b/third_party/rust/nix/src/sys/timer.rs
@@ -0,0 +1,187 @@
+//! Timer API via signals.
+//!
+//! Timer is a POSIX API to create timers and get expiration notifications
+//! through queued Unix signals, for the current process. This is similar to
+//! Linux's timerfd mechanism, except that API is specific to Linux and makes
+//! use of file polling.
+//!
+//! For more documentation, please read [timer_create](https://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_create.html).
+//!
+//! # Examples
+//!
+//! Create an interval timer that signals SIGALARM every 250 milliseconds.
+//!
+//! ```no_run
+//! use nix::sys::signal::{self, SigEvent, SigHandler, SigevNotify, Signal};
+//! use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags};
+//! use nix::time::ClockId;
+//! use std::convert::TryFrom;
+//! use std::sync::atomic::{AtomicU64, Ordering};
+//! use std::thread::yield_now;
+//! use std::time::Duration;
+//!
+//! const SIG: Signal = Signal::SIGALRM;
+//! static ALARMS: AtomicU64 = AtomicU64::new(0);
+//!
+//! extern "C" fn handle_alarm(signal: libc::c_int) {
+//! let signal = Signal::try_from(signal).unwrap();
+//! if signal == SIG {
+//! ALARMS.fetch_add(1, Ordering::Relaxed);
+//! }
+//! }
+//!
+//! fn main() {
+//! let clockid = ClockId::CLOCK_MONOTONIC;
+//! let sigevent = SigEvent::new(SigevNotify::SigevSignal {
+//! signal: SIG,
+//! si_value: 0,
+//! });
+//!
+//! let mut timer = Timer::new(clockid, sigevent).unwrap();
+//! let expiration = Expiration::Interval(Duration::from_millis(250).into());
+//! let flags = TimerSetTimeFlags::empty();
+//! timer.set(expiration, flags).expect("could not set timer");
+//!
+//! let handler = SigHandler::Handler(handle_alarm);
+//! unsafe { signal::signal(SIG, handler) }.unwrap();
+//!
+//! loop {
+//! let alarms = ALARMS.load(Ordering::Relaxed);
+//! if alarms >= 10 {
+//! println!("total alarms handled: {}", alarms);
+//! break;
+//! }
+//! yield_now()
+//! }
+//! }
+//! ```
+use crate::sys::signal::SigEvent;
+use crate::sys::time::timer::TimerSpec;
+pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
+use crate::time::ClockId;
+use crate::{errno::Errno, Result};
+use core::mem;
+
+/// A Unix signal per-process timer.
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct Timer(libc::timer_t);
+
+impl Timer {
+ /// Creates a new timer based on the clock defined by `clockid`. The details
+ /// of the signal and its handler are defined by the passed `sigevent`.
+ #[doc(alias("timer_create"))]
+ pub fn new(clockid: ClockId, mut sigevent: SigEvent) -> Result<Self> {
+ let mut timer_id: mem::MaybeUninit<libc::timer_t> =
+ mem::MaybeUninit::uninit();
+ Errno::result(unsafe {
+ libc::timer_create(
+ clockid.as_raw(),
+ sigevent.as_mut_ptr(),
+ timer_id.as_mut_ptr(),
+ )
+ })
+ .map(|_| {
+ // SAFETY: libc::timer_create is responsible for initializing
+ // timer_id.
+ unsafe { Self(timer_id.assume_init()) }
+ })
+ }
+
+ /// Set a new alarm on the timer.
+ ///
+ /// # Types of alarm
+ ///
+ /// There are 3 types of alarms you can set:
+ ///
+ /// - one shot: the alarm will trigger once after the specified amount of
+ /// time.
+ /// Example: I want an alarm to go off in 60s and then disable itself.
+ ///
+ /// - interval: the alarm will trigger every specified interval of time.
+ /// Example: I want an alarm to go off every 60s. The alarm will first
+ /// go off 60s after I set it and every 60s after that. The alarm will
+ /// not disable itself.
+ ///
+ /// - interval delayed: the alarm will trigger after a certain amount of
+ /// time and then trigger at a specified interval.
+ /// Example: I want an alarm to go off every 60s but only start in 1h.
+ /// The alarm will first trigger 1h after I set it and then every 60s
+ /// after that. The alarm will not disable itself.
+ ///
+ /// # Relative vs absolute alarm
+ ///
+ /// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass
+ /// to the `Expiration` you want is relative. If however you want an alarm
+ /// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`.
+ /// Then the one shot TimeSpec and the delay TimeSpec of the delayed
+ /// interval are going to be interpreted as absolute.
+ ///
+ /// # Disabling alarms
+ ///
+ /// Note: Only one alarm can be set for any given timer. Setting a new alarm
+ /// actually removes the previous one.
+ ///
+ /// Note: Setting a one shot alarm with a 0s TimeSpec disable the alarm
+ /// altogether.
+ #[doc(alias("timer_settime"))]
+ pub fn set(
+ &mut self,
+ expiration: Expiration,
+ flags: TimerSetTimeFlags,
+ ) -> Result<()> {
+ let timerspec: TimerSpec = expiration.into();
+ Errno::result(unsafe {
+ libc::timer_settime(
+ self.0,
+ flags.bits(),
+ timerspec.as_ref(),
+ core::ptr::null_mut(),
+ )
+ })
+ .map(drop)
+ }
+
+ /// Get the parameters for the alarm currently set, if any.
+ #[doc(alias("timer_gettime"))]
+ pub fn get(&self) -> Result<Option<Expiration>> {
+ let mut timerspec = TimerSpec::none();
+ Errno::result(unsafe {
+ libc::timer_gettime(self.0, timerspec.as_mut())
+ })
+ .map(|_| {
+ if timerspec.as_ref().it_interval.tv_sec == 0
+ && timerspec.as_ref().it_interval.tv_nsec == 0
+ && timerspec.as_ref().it_value.tv_sec == 0
+ && timerspec.as_ref().it_value.tv_nsec == 0
+ {
+ None
+ } else {
+ Some(timerspec.into())
+ }
+ })
+ }
+
+ /// Return the number of timers that have overrun
+ ///
+ /// Each timer is able to queue one signal to the process at a time, meaning
+ /// if the signal is not handled before the next expiration the timer has
+ /// 'overrun'. This function returns how many times that has happened to
+ /// this timer, up to `libc::DELAYTIMER_MAX`. If more than the maximum
+ /// number of overruns have happened the return is capped to the maximum.
+ #[doc(alias("timer_getoverrun"))]
+ pub fn overruns(&self) -> i32 {
+ unsafe { libc::timer_getoverrun(self.0) }
+ }
+}
+
+impl Drop for Timer {
+ fn drop(&mut self) {
+ if !std::thread::panicking() {
+ let result = Errno::result(unsafe { libc::timer_delete(self.0) });
+ if let Err(Errno::EINVAL) = result {
+ panic!("close of Timer encountered EINVAL");
+ }
+ }
+ }
+}
diff --git a/third_party/rust/nix/src/sys/timerfd.rs b/third_party/rust/nix/src/sys/timerfd.rs
new file mode 100644
index 0000000000..c4337c9dfa
--- /dev/null
+++ b/third_party/rust/nix/src/sys/timerfd.rs
@@ -0,0 +1,222 @@
+//! Timer API via file descriptors.
+//!
+//! Timer FD is a Linux-only API to create timers and get expiration
+//! notifications through file descriptors.
+//!
+//! For more documentation, please read [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html).
+//!
+//! # Examples
+//!
+//! Create a new one-shot timer that expires after 1 second.
+//! ```
+//! # use std::os::unix::io::AsRawFd;
+//! # use nix::sys::timerfd::{TimerFd, ClockId, TimerFlags, TimerSetTimeFlags,
+//! # Expiration};
+//! # use nix::sys::time::{TimeSpec, TimeValLike};
+//! # use nix::unistd::read;
+//! #
+//! // We create a new monotonic timer.
+//! let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty())
+//! .unwrap();
+//!
+//! // We set a new one-shot timer in 1 seconds.
+//! timer.set(
+//! Expiration::OneShot(TimeSpec::seconds(1)),
+//! TimerSetTimeFlags::empty()
+//! ).unwrap();
+//!
+//! // We wait for the timer to expire.
+//! timer.wait().unwrap();
+//! ```
+use crate::sys::time::timer::TimerSpec;
+pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
+use crate::unistd::read;
+use crate::{errno::Errno, Result};
+use libc::c_int;
+use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
+
+/// A timerfd instance. This is also a file descriptor, you can feed it to
+/// other interfaces taking file descriptors as arguments, [`epoll`] for example.
+///
+/// [`epoll`]: crate::sys::epoll
+#[derive(Debug)]
+pub struct TimerFd {
+ fd: OwnedFd,
+}
+
+impl AsFd for TimerFd {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.fd.as_fd()
+ }
+}
+
+impl FromRawFd for TimerFd {
+ unsafe fn from_raw_fd(fd: RawFd) -> Self {
+ TimerFd {
+ fd: OwnedFd::from_raw_fd(fd),
+ }
+ }
+}
+
+libc_enum! {
+ /// The type of the clock used to mark the progress of the timer. For more
+ /// details on each kind of clock, please refer to [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html).
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum ClockId {
+ /// A settable system-wide real-time clock.
+ CLOCK_REALTIME,
+ /// A non-settable monotonically increasing clock.
+ ///
+ /// Does not change after system startup.
+ /// Does not measure time while the system is suspended.
+ CLOCK_MONOTONIC,
+ /// Like `CLOCK_MONOTONIC`, except that `CLOCK_BOOTTIME` includes the time
+ /// that the system was suspended.
+ CLOCK_BOOTTIME,
+ /// Like `CLOCK_REALTIME`, but will wake the system if it is suspended.
+ CLOCK_REALTIME_ALARM,
+ /// Like `CLOCK_BOOTTIME`, but will wake the system if it is suspended.
+ CLOCK_BOOTTIME_ALARM,
+ }
+}
+
+libc_bitflags! {
+ /// Additional flags to change the behaviour of the file descriptor at the
+ /// time of creation.
+ pub struct TimerFlags: c_int {
+ /// Set the `O_NONBLOCK` flag on the open file description referred to by the new file descriptor.
+ TFD_NONBLOCK;
+ /// Set the `FD_CLOEXEC` flag on the file descriptor.
+ TFD_CLOEXEC;
+ }
+}
+
+impl TimerFd {
+ /// Creates a new timer based on the clock defined by `clockid`. The
+ /// underlying fd can be assigned specific flags with `flags` (CLOEXEC,
+ /// NONBLOCK). The underlying fd will be closed on drop.
+ #[doc(alias("timerfd_create"))]
+ pub fn new(clockid: ClockId, flags: TimerFlags) -> Result<Self> {
+ Errno::result(unsafe {
+ libc::timerfd_create(clockid as i32, flags.bits())
+ })
+ .map(|fd| Self {
+ fd: unsafe { OwnedFd::from_raw_fd(fd) },
+ })
+ }
+
+ /// Sets a new alarm on the timer.
+ ///
+ /// # Types of alarm
+ ///
+ /// There are 3 types of alarms you can set:
+ ///
+ /// - one shot: the alarm will trigger once after the specified amount of
+ /// time.
+ /// Example: I want an alarm to go off in 60s and then disable itself.
+ ///
+ /// - interval: the alarm will trigger every specified interval of time.
+ /// Example: I want an alarm to go off every 60s. The alarm will first
+ /// go off 60s after I set it and every 60s after that. The alarm will
+ /// not disable itself.
+ ///
+ /// - interval delayed: the alarm will trigger after a certain amount of
+ /// time and then trigger at a specified interval.
+ /// Example: I want an alarm to go off every 60s but only start in 1h.
+ /// The alarm will first trigger 1h after I set it and then every 60s
+ /// after that. The alarm will not disable itself.
+ ///
+ /// # Relative vs absolute alarm
+ ///
+ /// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass
+ /// to the `Expiration` you want is relative. If however you want an alarm
+ /// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`.
+ /// Then the one shot TimeSpec and the delay TimeSpec of the delayed
+ /// interval are going to be interpreted as absolute.
+ ///
+ /// # Cancel on a clock change
+ ///
+ /// If you set a `TFD_TIMER_CANCEL_ON_SET` alongside `TFD_TIMER_ABSTIME`
+ /// and the clock for this timer is `CLOCK_REALTIME` or `CLOCK_REALTIME_ALARM`,
+ /// then this timer is marked as cancelable if the real-time clock undergoes
+ /// a discontinuous change.
+ ///
+ /// # Disabling alarms
+ ///
+ /// Note: Only one alarm can be set for any given timer. Setting a new alarm
+ /// actually removes the previous one.
+ ///
+ /// Note: Setting a one shot alarm with a 0s TimeSpec disables the alarm
+ /// altogether.
+ #[doc(alias("timerfd_settime"))]
+ pub fn set(
+ &self,
+ expiration: Expiration,
+ flags: TimerSetTimeFlags,
+ ) -> Result<()> {
+ let timerspec: TimerSpec = expiration.into();
+ Errno::result(unsafe {
+ libc::timerfd_settime(
+ self.fd.as_fd().as_raw_fd(),
+ flags.bits(),
+ timerspec.as_ref(),
+ std::ptr::null_mut(),
+ )
+ })
+ .map(drop)
+ }
+
+ /// Get the parameters for the alarm currently set, if any.
+ #[doc(alias("timerfd_gettime"))]
+ pub fn get(&self) -> Result<Option<Expiration>> {
+ let mut timerspec = TimerSpec::none();
+ Errno::result(unsafe {
+ libc::timerfd_gettime(
+ self.fd.as_fd().as_raw_fd(),
+ timerspec.as_mut(),
+ )
+ })
+ .map(|_| {
+ if timerspec.as_ref().it_interval.tv_sec == 0
+ && timerspec.as_ref().it_interval.tv_nsec == 0
+ && timerspec.as_ref().it_value.tv_sec == 0
+ && timerspec.as_ref().it_value.tv_nsec == 0
+ {
+ None
+ } else {
+ Some(timerspec.into())
+ }
+ })
+ }
+
+ /// Remove the alarm if any is set.
+ #[doc(alias("timerfd_settime"))]
+ pub fn unset(&self) -> Result<()> {
+ Errno::result(unsafe {
+ libc::timerfd_settime(
+ self.fd.as_fd().as_raw_fd(),
+ TimerSetTimeFlags::empty().bits(),
+ TimerSpec::none().as_ref(),
+ std::ptr::null_mut(),
+ )
+ })
+ .map(drop)
+ }
+
+ /// Wait for the configured alarm to expire.
+ ///
+ /// Note: If the alarm is unset, then you will wait forever.
+ pub fn wait(&self) -> Result<()> {
+ while let Err(e) = read(self.fd.as_fd().as_raw_fd(), &mut [0u8; 8]) {
+ if e == Errno::ECANCELED {
+ break;
+ }
+ if e != Errno::EINTR {
+ return Err(e);
+ }
+ }
+
+ Ok(())
+ }
+}
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..eaf61edfd4
--- /dev/null
+++ b/third_party/rust/nix/src/sys/uio.rs
@@ -0,0 +1,225 @@
+//! Vectored I/O
+
+use crate::errno::Errno;
+use crate::Result;
+use libc::{self, c_int, c_void, off_t, size_t};
+use std::io::{IoSlice, IoSliceMut};
+use std::os::unix::io::{AsFd, AsRawFd};
+
+/// Low-level vectored write to a raw file descriptor
+///
+/// See also [writev(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/writev.html)
+pub fn writev<Fd: AsFd>(fd: Fd, iov: &[IoSlice<'_>]) -> Result<usize> {
+ // SAFETY: to quote the documentation for `IoSlice`:
+ //
+ // [IoSlice] is semantically a wrapper around a &[u8], but is
+ // guaranteed to be ABI compatible with the iovec type on Unix
+ // platforms.
+ //
+ // Because it is ABI compatible, a pointer cast here is valid
+ let res = unsafe {
+ libc::writev(fd.as_fd().as_raw_fd(), iov.as_ptr() as *const libc::iovec, iov.len() as c_int)
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+/// Low-level vectored read from a raw file descriptor
+///
+/// See also [readv(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readv.html)
+// Clippy doesn't know that we need to pass iov mutably only because the
+// mutation happens after converting iov to a pointer
+#[allow(clippy::needless_pass_by_ref_mut)]
+pub fn readv<Fd: AsFd>(fd: Fd, iov: &mut [IoSliceMut<'_>]) -> Result<usize> {
+ // SAFETY: same as in writev(), IoSliceMut is ABI-compatible with iovec
+ let res = unsafe {
+ libc::readv(fd.as_fd().as_raw_fd(), iov.as_ptr() 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(not(any(target_os = "redox", target_os = "haiku")))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn pwritev<Fd: AsFd>(fd: Fd, iov: &[IoSlice<'_>], offset: off_t) -> Result<usize> {
+ #[cfg(target_env = "uclibc")]
+ let offset = offset as libc::off64_t; // uclibc doesn't use off_t
+
+ // SAFETY: same as in writev()
+ let res = unsafe {
+ libc::pwritev(
+ fd.as_fd().as_raw_fd(),
+ iov.as_ptr() 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(not(any(target_os = "redox", target_os = "haiku")))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+// Clippy doesn't know that we need to pass iov mutably only because the
+// mutation happens after converting iov to a pointer
+#[allow(clippy::needless_pass_by_ref_mut)]
+pub fn preadv<Fd: AsFd>(
+ fd: Fd,
+ iov: &mut [IoSliceMut<'_>],
+ offset: off_t,
+) -> Result<usize> {
+ #[cfg(target_env = "uclibc")]
+ let offset = offset as libc::off64_t; // uclibc doesn't use off_t
+
+ // SAFETY: same as in readv()
+ let res = unsafe {
+ libc::preadv(
+ fd.as_fd().as_raw_fd(),
+ iov.as_ptr() as *const libc::iovec,
+ iov.len() as c_int,
+ offset,
+ )
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+/// Low-level write to a file, with specified offset.
+///
+/// See also [pwrite(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html)
+// TODO: move to unistd
+pub fn pwrite<Fd: AsFd>(fd: Fd, buf: &[u8], offset: off_t) -> Result<usize> {
+ let res = unsafe {
+ libc::pwrite(
+ fd.as_fd().as_raw_fd(),
+ buf.as_ptr() as *const c_void,
+ buf.len() as size_t,
+ offset,
+ )
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+/// Low-level read from a file, with specified offset.
+///
+/// See also [pread(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html)
+// TODO: move to unistd
+pub fn pread<Fd: AsFd>(fd: Fd, buf: &mut [u8], offset: off_t) -> Result<usize> {
+ let res = unsafe {
+ libc::pread(
+ fd.as_fd().as_raw_fd(),
+ buf.as_mut_ptr() 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 `IoSlice`,
+/// except that it refers to memory in some other process, and is
+/// therefore not represented in Rust by an actual slice as `IoSlice` is. It
+/// is used with [`process_vm_readv`](fn.process_vm_readv.html)
+/// and [`process_vm_writev`](fn.process_vm_writev.html).
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct RemoteIoVec {
+ /// The starting address of this slice (`iov_base`).
+ pub base: usize,
+ /// The number of bytes in this slice (`iov_len`).
+ pub len: usize,
+}
+
+feature! {
+#![feature = "process"]
+
+/// Write data directly to another process's virtual memory
+/// (see [`process_vm_writev`(2)]).
+///
+/// `local_iov` is a list of [`IoSlice`]s containing the data to be written,
+/// and `remote_iov` is a list of [`RemoteIoVec`]s identifying where the
+/// data should be written in the target process. On success, returns the
+/// number of bytes written, which will always be a whole
+/// number of `remote_iov` chunks.
+///
+/// This requires the same permissions as debugging the process using
+/// [ptrace]: you must either be a privileged process (with
+/// `CAP_SYS_PTRACE`), or you must be running as the same user as the
+/// target process and the OS must have unprivileged debugging enabled.
+///
+/// This function is only available on Linux and Android(SDK23+).
+///
+/// [`process_vm_writev`(2)]: https://man7.org/linux/man-pages/man2/process_vm_writev.2.html
+/// [ptrace]: ../ptrace/index.html
+/// [`IoSlice`]: https://doc.rust-lang.org/std/io/struct.IoSlice.html
+/// [`RemoteIoVec`]: struct.RemoteIoVec.html
+#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))]
+pub fn process_vm_writev(
+ pid: crate::unistd::Pid,
+ local_iov: &[IoSlice<'_>],
+ remote_iov: &[RemoteIoVec]) -> Result<usize>
+{
+ let res = unsafe {
+ libc::process_vm_writev(pid.into(),
+ local_iov.as_ptr() 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 [`IoSliceMut`]s containing the buffer to copy
+/// data into, and `remote_iov` is a list of [`RemoteIoVec`]s identifying
+/// where the source data is in the target process. On success,
+/// returns the number of bytes written, which will always be a whole
+/// number of `remote_iov` chunks.
+///
+/// This requires the same permissions as debugging the process using
+/// [`ptrace`]: you must either be a privileged process (with
+/// `CAP_SYS_PTRACE`), or you must be running as the same user as the
+/// target process and the OS must have unprivileged debugging enabled.
+///
+/// This function is only available on Linux and Android(SDK23+).
+///
+/// [`process_vm_readv`(2)]: https://man7.org/linux/man-pages/man2/process_vm_readv.2.html
+/// [`ptrace`]: ../ptrace/index.html
+/// [`IoSliceMut`]: https://doc.rust-lang.org/std/io/struct.IoSliceMut.html
+/// [`RemoteIoVec`]: struct.RemoteIoVec.html
+#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))]
+pub fn process_vm_readv(
+ pid: crate::unistd::Pid,
+ local_iov: &mut [IoSliceMut<'_>],
+ remote_iov: &[RemoteIoVec]) -> Result<usize>
+{
+ let res = unsafe {
+ libc::process_vm_readv(pid.into(),
+ local_iov.as_ptr() 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)
+}
+}
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..b48ed9f45e
--- /dev/null
+++ b/third_party/rust/nix/src/sys/utsname.rs
@@ -0,0 +1,85 @@
+//! Get system identification
+use crate::{Errno, Result};
+use libc::c_char;
+use std::ffi::OsStr;
+use std::mem;
+use std::os::unix::ffi::OsStrExt;
+
+/// Describes the running system. Return type of [`uname`].
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(transparent)]
+pub struct UtsName(libc::utsname);
+
+impl UtsName {
+ /// Name of the operating system implementation.
+ pub fn sysname(&self) -> &OsStr {
+ cast_and_trim(&self.0.sysname)
+ }
+
+ /// Network name of this machine.
+ pub fn nodename(&self) -> &OsStr {
+ cast_and_trim(&self.0.nodename)
+ }
+
+ /// Release level of the operating system.
+ pub fn release(&self) -> &OsStr {
+ cast_and_trim(&self.0.release)
+ }
+
+ /// Version level of the operating system.
+ pub fn version(&self) -> &OsStr {
+ cast_and_trim(&self.0.version)
+ }
+
+ /// Machine hardware platform.
+ pub fn machine(&self) -> &OsStr {
+ cast_and_trim(&self.0.machine)
+ }
+
+ /// NIS or YP domain name of this machine.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ pub fn domainname(&self) -> &OsStr {
+ cast_and_trim(&self.0.domainname)
+ }
+}
+
+/// Get system identification
+pub fn uname() -> Result<UtsName> {
+ unsafe {
+ let mut ret = mem::MaybeUninit::zeroed();
+ Errno::result(libc::uname(ret.as_mut_ptr()))?;
+ Ok(UtsName(ret.assume_init()))
+ }
+}
+
+fn cast_and_trim(slice: &[c_char]) -> &OsStr {
+ let length = slice
+ .iter()
+ .position(|&byte| byte == 0)
+ .unwrap_or(slice.len());
+ let bytes =
+ unsafe { std::slice::from_raw_parts(slice.as_ptr().cast(), length) };
+
+ OsStr::from_bytes(bytes)
+}
+
+#[cfg(test)]
+mod test {
+ #[cfg(target_os = "linux")]
+ #[test]
+ pub fn test_uname_linux() {
+ assert_eq!(super::uname().unwrap().sysname(), "Linux");
+ }
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[test]
+ pub fn test_uname_darwin() {
+ assert_eq!(super::uname().unwrap().sysname(), "Darwin");
+ }
+
+ #[cfg(target_os = "freebsd")]
+ #[test]
+ pub fn test_uname_freebsd() {
+ assert_eq!(super::uname().unwrap().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..f7a63ffcd2
--- /dev/null
+++ b/third_party/rust/nix/src/sys/wait.rs
@@ -0,0 +1,393 @@
+//! Wait for a process to change status
+use crate::errno::Errno;
+use crate::sys::signal::Signal;
+use crate::unistd::Pid;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_int};
+use std::convert::TryFrom;
+#[cfg(any(
+ target_os = "android",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+use std::os::unix::io::{AsRawFd, BorrowedFd};
+
+libc_bitflags!(
+ /// Controls the behavior of [`waitpid`].
+ pub struct WaitPidFlag: c_int {
+ /// Do not block when there are no processes wishing to report status.
+ WNOHANG;
+ /// Report the status of selected processes which are stopped due to a
+ /// [`SIGTTIN`](crate::sys::signal::Signal::SIGTTIN),
+ /// [`SIGTTOU`](crate::sys::signal::Signal::SIGTTOU),
+ /// [`SIGTSTP`](crate::sys::signal::Signal::SIGTSTP), or
+ /// [`SIGSTOP`](crate::sys::signal::Signal::SIGSTOP) signal.
+ WUNTRACED;
+ /// Report the status of selected processes which have terminated.
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "macos",
+ target_os = "netbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ WEXITED;
+ /// Report the status of selected processes that have continued from a
+ /// job control stop by receiving a
+ /// [`SIGCONT`](crate::sys::signal::Signal::SIGCONT) signal.
+ WCONTINUED;
+ /// An alias for WUNTRACED.
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "macos",
+ target_os = "netbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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 = "redox",
+ target_os = "macos",
+ target_os = "netbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ WNOWAIT;
+ /// Don't wait on children of other threads in this group
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ __WNOTHREAD;
+ /// Wait on all children, regardless of type
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ __WALL;
+ /// Wait for "clone" children only.
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ __WCLONE;
+ }
+);
+
+/// Possible return values from `wait()` or `waitpid()`.
+///
+/// Each status (other than `StillAlive`) describes a state transition
+/// in a child process `Pid`, such as the process exiting or stopping,
+/// plus additional data about the transition if any.
+///
+/// Note that there are two Linux-specific enum variants, `PtraceEvent`
+/// and `PtraceSyscall`. Portable code should avoid exhaustively
+/// matching on `WaitStatus`.
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum WaitStatus {
+ /// The process exited normally (as with `exit()` or returning from
+ /// `main`) with the given exit code. This case matches the C macro
+ /// `WIFEXITED(status)`; the second field is `WEXITSTATUS(status)`.
+ Exited(Pid, i32),
+ /// The process was killed by the given signal. The third field
+ /// indicates whether the signal generated a core dump. This case
+ /// matches the C macro `WIFSIGNALED(status)`; the last two fields
+ /// correspond to `WTERMSIG(status)` and `WCOREDUMP(status)`.
+ Signaled(Pid, Signal, bool),
+ /// The process is alive, but was stopped by the given signal. This
+ /// is only reported if `WaitPidFlag::WUNTRACED` was passed. This
+ /// case matches the C macro `WIFSTOPPED(status)`; the second field
+ /// is `WSTOPSIG(status)`.
+ Stopped(Pid, Signal),
+ /// The traced process was stopped by a `PTRACE_EVENT_*` event. See
+ /// [`nix::sys::ptrace`] and [`ptrace`(2)] for more information. All
+ /// currently-defined events use `SIGTRAP` as the signal; the third
+ /// field is the `PTRACE_EVENT_*` value of the event.
+ ///
+ /// [`nix::sys::ptrace`]: ../ptrace/index.html
+ /// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PtraceEvent(Pid, Signal, c_int),
+ /// The traced process was stopped by execution of a system call,
+ /// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for
+ /// more information.
+ ///
+ /// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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 {
+ libc::WIFEXITED(status)
+}
+
+fn exit_status(status: i32) -> i32 {
+ libc::WEXITSTATUS(status)
+}
+
+fn signaled(status: i32) -> bool {
+ libc::WIFSIGNALED(status)
+}
+
+fn term_signal(status: i32) -> Result<Signal> {
+ Signal::try_from(libc::WTERMSIG(status))
+}
+
+fn dumped_core(status: i32) -> bool {
+ libc::WCOREDUMP(status)
+}
+
+fn stopped(status: i32) -> bool {
+ libc::WIFSTOPPED(status)
+}
+
+fn stop_signal(status: i32) -> Result<Signal> {
+ Signal::try_from(libc::WSTOPSIG(status))
+}
+
+#[cfg(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.
+ 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 {
+ 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)
+ })
+ }
+
+ /// Convert a `siginfo_t` as returned by `waitid` to a `WaitStatus`
+ ///
+ /// # Errors
+ ///
+ /// Returns an `Error` corresponding to `EINVAL` for invalid values.
+ ///
+ /// # Safety
+ ///
+ /// siginfo_t is actually a union, not all fields may be initialized.
+ /// The functions si_pid() and si_status() must be valid to call on
+ /// the passed siginfo_t.
+ #[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+ ))]
+ unsafe fn from_siginfo(siginfo: &libc::siginfo_t) -> Result<WaitStatus> {
+ let si_pid = siginfo.si_pid();
+ if si_pid == 0 {
+ return Ok(WaitStatus::StillAlive);
+ }
+
+ assert_eq!(siginfo.si_signo, libc::SIGCHLD);
+
+ let pid = Pid::from_raw(si_pid);
+ let si_status = siginfo.si_status();
+
+ let status = match siginfo.si_code {
+ libc::CLD_EXITED => WaitStatus::Exited(pid, si_status),
+ libc::CLD_KILLED | libc::CLD_DUMPED => WaitStatus::Signaled(
+ pid,
+ Signal::try_from(si_status)?,
+ siginfo.si_code == libc::CLD_DUMPED,
+ ),
+ libc::CLD_STOPPED => {
+ WaitStatus::Stopped(pid, Signal::try_from(si_status)?)
+ }
+ libc::CLD_CONTINUED => WaitStatus::Continued(pid),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::CLD_TRAPPED => {
+ if si_status == libc::SIGTRAP | 0x80 {
+ WaitStatus::PtraceSyscall(pid)
+ } else {
+ WaitStatus::PtraceEvent(
+ pid,
+ Signal::try_from(si_status & 0xff)?,
+ (si_status >> 8) as c_int,
+ )
+ }
+ }
+ _ => return Err(Errno::EINVAL),
+ };
+
+ Ok(status)
+ }
+}
+
+/// Wait for a process to change status
+///
+/// See also [waitpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitpid.html)
+pub fn waitpid<P: Into<Option<Pid>>>(
+ pid: P,
+ options: Option<WaitPidFlag>,
+) -> Result<WaitStatus> {
+ use self::WaitStatus::*;
+
+ let mut status: i32 = 0;
+
+ let option_bits = match options {
+ Some(bits) => bits.bits(),
+ None => 0,
+ };
+
+ let res = unsafe {
+ libc::waitpid(
+ pid.into().unwrap_or_else(|| Pid::from_raw(-1)).into(),
+ &mut status as *mut c_int,
+ option_bits,
+ )
+ };
+
+ match Errno::result(res)? {
+ 0 => Ok(StillAlive),
+ res => WaitStatus::from_raw(Pid::from_raw(res), status),
+ }
+}
+
+/// Wait for any child process to change status or a signal is received.
+///
+/// See also [wait(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html)
+pub fn wait() -> Result<WaitStatus> {
+ waitpid(None, None)
+}
+
+/// The ID argument for `waitid`
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+#[derive(Debug)]
+pub enum Id<'fd> {
+ /// Wait for any child
+ All,
+ /// Wait for the child whose process ID matches the given PID
+ Pid(Pid),
+ /// Wait for the child whose process group ID matches the given PID
+ ///
+ /// If the PID is zero, the caller's process group is used since Linux 5.4.
+ PGid(Pid),
+ /// Wait for the child referred to by the given PID file descriptor
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ PIDFd(BorrowedFd<'fd>),
+ /// A helper variant to resolve the unused parameter (`'fd`) problem on platforms
+ /// other than Linux and Android.
+ #[doc(hidden)]
+ _Unreachable(std::marker::PhantomData<&'fd std::convert::Infallible>),
+}
+
+/// Wait for a process to change status
+///
+/// See also [waitid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitid.html)
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+pub fn waitid(id: Id, flags: WaitPidFlag) -> Result<WaitStatus> {
+ let (idtype, idval) = match id {
+ Id::All => (libc::P_ALL, 0),
+ Id::Pid(pid) => (libc::P_PID, pid.as_raw() as libc::id_t),
+ Id::PGid(pid) => (libc::P_PGID, pid.as_raw() as libc::id_t),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Id::PIDFd(fd) => (libc::P_PIDFD, fd.as_raw_fd() as libc::id_t),
+ Id::_Unreachable(_) => unreachable!("This variant could never be constructed"),
+ };
+
+ let siginfo = unsafe {
+ // Memory is zeroed rather than uninitialized, as not all platforms
+ // initialize the memory in the StillAlive case
+ let mut siginfo: libc::siginfo_t = std::mem::zeroed();
+ Errno::result(libc::waitid(idtype, idval, &mut siginfo, flags.bits()))?;
+ siginfo
+ };
+
+ unsafe { WaitStatus::from_siginfo(&siginfo) }
+}
diff --git a/third_party/rust/nix/src/time.rs b/third_party/rust/nix/src/time.rs
new file mode 100644
index 0000000000..2e03c46cf4
--- /dev/null
+++ b/third_party/rust/nix/src/time.rs
@@ -0,0 +1,283 @@
+use crate::sys::time::TimeSpec;
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+))]
+#[cfg(feature = "process")]
+use crate::unistd::Pid;
+use crate::{Errno, Result};
+use libc::{self, clockid_t};
+use std::mem::MaybeUninit;
+
+/// Clock identifier
+///
+/// Newtype pattern around `clockid_t` (which is just alias). It prevents bugs caused by
+/// accidentally passing wrong value.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct ClockId(clockid_t);
+
+impl ClockId {
+ /// Creates `ClockId` from raw `clockid_t`
+ pub const fn from_raw(clk_id: clockid_t) -> Self {
+ ClockId(clk_id)
+ }
+
+ feature! {
+ #![feature = "process"]
+ /// Returns `ClockId` of a `pid` CPU-time clock
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn pid_cpu_clock_id(pid: Pid) -> Result<Self> {
+ clock_getcpuclockid(pid)
+ }
+ }
+
+ /// Returns resolution of the clock id
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn res(self) -> Result<TimeSpec> {
+ clock_getres(self)
+ }
+
+ /// Returns the current time on the clock id
+ pub fn now(self) -> Result<TimeSpec> {
+ clock_gettime(self)
+ }
+
+ /// Sets time to `timespec` on the clock id
+ #[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "redox",
+ target_os = "hermit",
+ )))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn set_time(self, timespec: TimeSpec) -> Result<()> {
+ clock_settime(self, timespec)
+ }
+
+ /// Gets the raw `clockid_t` wrapped by `self`
+ pub const fn as_raw(self) -> clockid_t {
+ self.0
+ }
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_BOOTTIME: ClockId = ClockId(libc::CLOCK_BOOTTIME);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_BOOTTIME_ALARM: ClockId =
+ ClockId(libc::CLOCK_BOOTTIME_ALARM);
+ pub const CLOCK_MONOTONIC: ClockId = ClockId(libc::CLOCK_MONOTONIC);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_MONOTONIC_COARSE: ClockId =
+ ClockId(libc::CLOCK_MONOTONIC_COARSE);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_MONOTONIC_FAST: ClockId =
+ ClockId(libc::CLOCK_MONOTONIC_FAST);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_MONOTONIC_PRECISE: ClockId =
+ ClockId(libc::CLOCK_MONOTONIC_PRECISE);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_MONOTONIC_RAW: ClockId = ClockId(libc::CLOCK_MONOTONIC_RAW);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "redox",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_PROCESS_CPUTIME_ID: ClockId =
+ ClockId(libc::CLOCK_PROCESS_CPUTIME_ID);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_PROF: ClockId = ClockId(libc::CLOCK_PROF);
+ pub const CLOCK_REALTIME: ClockId = ClockId(libc::CLOCK_REALTIME);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_REALTIME_ALARM: ClockId =
+ ClockId(libc::CLOCK_REALTIME_ALARM);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_REALTIME_COARSE: ClockId =
+ ClockId(libc::CLOCK_REALTIME_COARSE);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_REALTIME_FAST: ClockId = ClockId(libc::CLOCK_REALTIME_FAST);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_REALTIME_PRECISE: ClockId =
+ ClockId(libc::CLOCK_REALTIME_PRECISE);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_SECOND: ClockId = ClockId(libc::CLOCK_SECOND);
+ #[cfg(any(
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ all(target_os = "linux", target_env = "musl")
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_TAI: ClockId = ClockId(libc::CLOCK_TAI);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_THREAD_CPUTIME_ID: ClockId =
+ ClockId(libc::CLOCK_THREAD_CPUTIME_ID);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_UPTIME: ClockId = ClockId(libc::CLOCK_UPTIME);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_UPTIME_FAST: ClockId = ClockId(libc::CLOCK_UPTIME_FAST);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_UPTIME_PRECISE: ClockId =
+ ClockId(libc::CLOCK_UPTIME_PRECISE);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_VIRTUAL: ClockId = ClockId(libc::CLOCK_VIRTUAL);
+}
+
+impl From<ClockId> for clockid_t {
+ fn from(clock_id: ClockId) -> Self {
+ clock_id.as_raw()
+ }
+}
+
+impl From<clockid_t> for ClockId {
+ fn from(clk_id: clockid_t) -> Self {
+ ClockId::from_raw(clk_id)
+ }
+}
+
+impl std::fmt::Display for ClockId {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.0, f)
+ }
+}
+
+/// Get the resolution of the specified clock, (see
+/// [clock_getres(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_getres.html)).
+#[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn clock_getres(clock_id: ClockId) -> Result<TimeSpec> {
+ let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
+ let ret =
+ unsafe { libc::clock_getres(clock_id.as_raw(), c_time.as_mut_ptr()) };
+ Errno::result(ret)?;
+ let res = unsafe { c_time.assume_init() };
+ Ok(TimeSpec::from(res))
+}
+
+/// Get the time of the specified clock, (see
+/// [clock_gettime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_gettime.html)).
+pub fn clock_gettime(clock_id: ClockId) -> Result<TimeSpec> {
+ let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
+ let ret =
+ unsafe { libc::clock_gettime(clock_id.as_raw(), c_time.as_mut_ptr()) };
+ Errno::result(ret)?;
+ let res = unsafe { c_time.assume_init() };
+ Ok(TimeSpec::from(res))
+}
+
+/// Set the time of the specified clock, (see
+/// [clock_settime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_settime.html)).
+#[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "redox",
+ target_os = "hermit",
+)))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn clock_settime(clock_id: ClockId, timespec: TimeSpec) -> Result<()> {
+ let ret =
+ unsafe { libc::clock_settime(clock_id.as_raw(), timespec.as_ref()) };
+ Errno::result(ret).map(drop)
+}
+
+/// Get the clock id of the specified process id, (see
+/// [clock_getcpuclockid(3)](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_getcpuclockid.html)).
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+))]
+#[cfg(feature = "process")]
+#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
+pub fn clock_getcpuclockid(pid: Pid) -> Result<ClockId> {
+ let mut clk_id: MaybeUninit<libc::clockid_t> = MaybeUninit::uninit();
+ let ret =
+ unsafe { libc::clock_getcpuclockid(pid.into(), clk_id.as_mut_ptr()) };
+ if ret == 0 {
+ let res = unsafe { clk_id.assume_init() };
+ Ok(ClockId::from(res))
+ } else {
+ Err(Errno::from_i32(ret))
+ }
+}
diff --git a/third_party/rust/nix/src/ucontext.rs b/third_party/rust/nix/src/ucontext.rs
new file mode 100644
index 0000000000..b2a39f7699
--- /dev/null
+++ b/third_party/rust/nix/src/ucontext.rs
@@ -0,0 +1,47 @@
+#[cfg(not(target_env = "musl"))]
+use crate::errno::Errno;
+use crate::sys::signal::SigSet;
+#[cfg(not(target_env = "musl"))]
+use crate::Result;
+#[cfg(not(target_env = "musl"))]
+use std::mem;
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct UContext {
+ context: libc::ucontext_t,
+}
+
+impl UContext {
+ #[cfg(not(target_env = "musl"))]
+ pub fn get() -> Result<UContext> {
+ let mut context = mem::MaybeUninit::<libc::ucontext_t>::uninit();
+ let res = unsafe { libc::getcontext(context.as_mut_ptr()) };
+ Errno::result(res).map(|_| unsafe {
+ UContext {
+ context: context.assume_init(),
+ }
+ })
+ }
+
+ #[cfg(not(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 {
+ &mut *(&mut self.context.uc_sigmask as *mut libc::sigset_t
+ as *mut SigSet)
+ }
+ }
+
+ pub fn sigmask(&self) -> &SigSet {
+ unsafe {
+ &*(&self.context.uc_sigmask as *const libc::sigset_t
+ as *const SigSet)
+ }
+ }
+}
diff --git a/third_party/rust/nix/src/unistd.rs b/third_party/rust/nix/src/unistd.rs
new file mode 100644
index 0000000000..bb9f1c1f67
--- /dev/null
+++ b/third_party/rust/nix/src/unistd.rs
@@ -0,0 +1,3977 @@
+//! Safe wrappers around functions found in libc "unistd.h" header
+
+use crate::errno::{self, Errno};
+#[cfg(not(target_os = "redox"))]
+#[cfg(feature = "fs")]
+use crate::fcntl::{at_rawfd, AtFlags};
+#[cfg(feature = "fs")]
+use crate::fcntl::{fcntl, FcntlArg::F_SETFD, FdFlag, OFlag};
+#[cfg(all(
+ feature = "fs",
+ any(
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "macos",
+ target_os = "ios"
+ )
+))]
+use crate::sys::stat::FileFlag;
+#[cfg(feature = "fs")]
+use crate::sys::stat::Mode;
+use crate::{Error, NixPath, Result};
+#[cfg(not(target_os = "redox"))]
+use cfg_if::cfg_if;
+use libc::{
+ self, c_char, c_int, c_long, c_uint, c_void, gid_t, mode_t, off_t, pid_t,
+ size_t, uid_t, PATH_MAX,
+};
+use std::convert::Infallible;
+use std::ffi::{CStr, OsString};
+#[cfg(not(target_os = "redox"))]
+use std::ffi::{CString, OsStr};
+#[cfg(not(target_os = "redox"))]
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::ffi::OsStringExt;
+use std::os::unix::io::RawFd;
+use std::os::unix::io::{AsFd, AsRawFd};
+use std::path::PathBuf;
+use std::{fmt, mem, ptr};
+
+feature! {
+ #![feature = "fs"]
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ pub use self::pivot_root::*;
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+pub use self::setres::*;
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+pub use self::getres::*;
+
+feature! {
+#![feature = "user"]
+
+/// 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 const fn from_raw(uid: uid_t) -> Self {
+ Uid(uid)
+ }
+
+ /// Returns Uid of calling process. This is practically a more Rusty alias for `getuid`.
+ #[doc(alias("getuid"))]
+ pub fn current() -> Self {
+ getuid()
+ }
+
+ /// Returns effective Uid of calling process. This is practically a more Rusty alias for `geteuid`.
+ #[doc(alias("geteuid"))]
+ pub fn effective() -> Self {
+ geteuid()
+ }
+
+ /// Returns true if the `Uid` represents privileged user - root. (If it equals zero.)
+ pub const fn is_root(self) -> bool {
+ self.0 == ROOT.0
+ }
+
+ /// Get the raw `uid_t` wrapped by `self`.
+ pub const fn as_raw(self) -> uid_t {
+ self.0
+ }
+}
+
+impl From<Uid> for uid_t {
+ fn from(uid: Uid) -> Self {
+ uid.0
+ }
+}
+
+impl From<uid_t> for Uid {
+ fn from(uid: uid_t) -> Self {
+ Uid(uid)
+ }
+}
+
+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 const fn from_raw(gid: gid_t) -> Self {
+ Gid(gid)
+ }
+
+ /// Returns Gid of calling process. This is practically a more Rusty alias for `getgid`.
+ #[doc(alias("getgid"))]
+ pub fn current() -> Self {
+ getgid()
+ }
+
+ /// Returns effective Gid of calling process. This is practically a more Rusty alias for `getegid`.
+ #[doc(alias("getegid"))]
+ pub fn effective() -> Self {
+ getegid()
+ }
+
+ /// Get the raw `gid_t` wrapped by `self`.
+ pub const fn as_raw(self) -> gid_t {
+ self.0
+ }
+}
+
+impl From<Gid> for gid_t {
+ fn from(gid: Gid) -> Self {
+ gid.0
+ }
+}
+
+impl From<gid_t> for Gid {
+ fn from(gid: gid_t) -> Self {
+ Gid(gid)
+ }
+}
+
+impl fmt::Display for Gid {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+}
+
+feature! {
+#![feature = "process"]
+/// 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, Ord, PartialOrd, Hash)]
+pub struct Pid(pid_t);
+
+impl Pid {
+ /// Creates `Pid` from raw `pid_t`.
+ pub const fn from_raw(pid: pid_t) -> Self {
+ Pid(pid)
+ }
+
+ /// Returns PID of calling process
+ #[doc(alias("getpid"))]
+ pub fn this() -> Self {
+ getpid()
+ }
+
+ /// Returns PID of parent of calling process
+ #[doc(alias("getppid"))]
+ pub fn parent() -> Self {
+ getppid()
+ }
+
+ /// Get the raw `pid_t` wrapped by `self`.
+ pub const 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 {
+ matches!(self, ForkResult::Child)
+ }
+
+ /// 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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html)).
+///
+/// After successfully calling the fork system call, a second process will
+/// be created which is identical to the original except for the pid and the
+/// return value of this function. As an example:
+///
+/// ```
+/// use nix::{sys::wait::waitpid,unistd::{fork, ForkResult, write}};
+///
+/// match unsafe{fork()} {
+/// Ok(ForkResult::Parent { child, .. }) => {
+/// println!("Continuing execution in parent process, new child has pid: {}", child);
+/// waitpid(child, None).unwrap();
+/// }
+/// Ok(ForkResult::Child) => {
+/// // Unsafe to use `println!` (or `unwrap`) here. See Safety.
+/// write(libc::STDOUT_FILENO, "I'm a new child process\n".as_bytes()).ok();
+/// unsafe { libc::_exit(0) };
+/// }
+/// Err(_) => println!("Fork failed"),
+/// }
+/// ```
+///
+/// This will print something like the following (order nondeterministic). 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]: https://man7.org/linux/man-pages/man7/signal-safety.7.html
+#[inline]
+pub unsafe fn fork() -> Result<ForkResult> {
+ use self::ForkResult::*;
+ let res = 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)](https://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)](https://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)](https://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)](https://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)](https://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]
+#[cfg(not(target_os = "redox"))]
+pub fn getsid(pid: Option<Pid>) -> Result<Pid> {
+ let res = unsafe { libc::getsid(pid.unwrap_or(Pid(0)).into()) };
+ Errno::result(res).map(Pid)
+}
+}
+
+feature! {
+#![all(feature = "process", feature = "term")]
+/// Get the terminal foreground process group (see
+/// [tcgetpgrp(3)](https://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)](https://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)
+}
+}
+
+feature! {
+#![feature = "process"]
+/// Get the group id of the calling process (see
+///[getpgrp(3)](https://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)](https://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 })
+}
+}
+
+feature! {
+#![feature = "fs"]
+/// Create a copy of the specified file descriptor (see
+/// [dup(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)).
+///
+/// The new file descriptor will 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)](https://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)](https://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(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)](https://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)](https://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]
+#[cfg(not(target_os = "fuchsia"))]
+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)](https://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
+/// use nix::unistd;
+/// use nix::sys::stat;
+/// use tempfile::tempdir;
+///
+/// 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](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifo.html)
+///
+/// # Example
+///
+/// ```rust
+/// use nix::unistd;
+/// use nix::sys::stat;
+/// use tempfile::tempdir;
+///
+/// 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]
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support fifo yet
+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 new fifo special file (named pipe) with path `path` and access rights `mode`.
+///
+/// If `dirfd` has a value, then `path` is relative to directory associated with the file descriptor.
+///
+/// If `dirfd` is `None`, then `path` is relative to the current working directory.
+///
+/// # References
+///
+/// [mkfifoat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifoat.html).
+// mkfifoat is not implemented in OSX or android
+#[inline]
+#[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "haiku",
+ target_os = "android",
+ target_os = "redox"
+)))]
+pub fn mkfifoat<P: ?Sized + NixPath>(
+ dirfd: Option<RawFd>,
+ path: &P,
+ mode: Mode,
+) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::mkfifoat(at_rawfd(dirfd), 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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html).
+#[cfg(not(target_os = "redox"))]
+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)
+}
+}
+
+// Double the buffer capacity up to limit. In case it already has
+// reached the limit, return Errno::ERANGE.
+#[cfg(any(feature = "fs", feature = "user"))]
+fn reserve_double_buffer_size<T>(buf: &mut Vec<T>, limit: usize) -> Result<()> {
+ use std::cmp::min;
+
+ if buf.capacity() >= limit {
+ return Err(Errno::ERANGE);
+ }
+
+ let capacity = min(buf.capacity() * 2, limit);
+ buf.reserve(capacity);
+
+ Ok(())
+}
+
+feature! {
+#![feature = "fs"]
+
+/// 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
+/// use nix::unistd;
+///
+/// // 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);
+ }
+ }
+
+ // Trigger the internal buffer resizing logic.
+ reserve_double_buffer_size(&mut buf, PATH_MAX as usize)?;
+ }
+ }
+}
+}
+
+feature! {
+#![all(feature = "user", feature = "fs")]
+
+/// Computes the raw UID and GID values to pass to a `*chown` call.
+// The cast is not unnecessary on all platforms.
+#[allow(clippy::unnecessary_cast)]
+fn chown_raw_ids(owner: Option<Uid>, group: Option<Gid>) -> (uid_t, 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_else(|| (0 as uid_t).wrapping_sub(1));
+ let gid = group
+ .map(Into::into)
+ .unwrap_or_else(|| (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)](https://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)
+}
+
+/// Change the ownership of the file referred to by the open file descriptor `fd` to be owned by
+/// the specified `owner` (user) and `group` (see
+/// [fchown(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchown.html)).
+///
+/// The owner/group for the provided file 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 fchown(fd: RawFd, owner: Option<Uid>, group: Option<Gid>) -> Result<()> {
+ let (uid, gid) = chown_raw_ids(owner, group);
+ let res = unsafe { libc::fchown(fd, 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, owner, group, FchownatFlags::NoFollowSymlink)` is identical to
+/// a call `libc::lchown(path, owner, group)`. That's why `lchown` is unimplemented in
+/// the `nix` crate.
+///
+/// # References
+///
+/// [fchownat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchownat.html).
+#[cfg(not(target_os = "redox"))]
+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)
+}
+}
+
+feature! {
+#![feature = "process"]
+fn to_exec_array<S: AsRef<CStr>>(args: &[S]) -> Vec<*const c_char> {
+ use std::iter::once;
+ args.iter()
+ .map(|s| s.as_ref().as_ptr())
+ .chain(once(ptr::null()))
+ .collect()
+}
+
+/// Replace the current process image with a new one (see
+/// [exec(3)](https://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<S: AsRef<CStr>>(path: &CStr, argv: &[S]) -> Result<Infallible> {
+ let args_p = to_exec_array(argv);
+
+ unsafe { libc::execv(path.as_ptr(), args_p.as_ptr()) };
+
+ Err(Errno::last())
+}
+
+/// Replace the current process image with a new one (see
+/// [execve(2)](https://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<SA: AsRef<CStr>, SE: AsRef<CStr>>(
+ path: &CStr,
+ args: &[SA],
+ env: &[SE],
+) -> Result<Infallible> {
+ 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(Errno::last())
+}
+
+/// Replace the current process image with a new one and replicate shell `PATH`
+/// searching behavior (see
+/// [exec(3)](https://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<S: AsRef<CStr>>(
+ filename: &CStr,
+ args: &[S],
+) -> Result<Infallible> {
+ let args_p = to_exec_array(args);
+
+ unsafe { libc::execvp(filename.as_ptr(), args_p.as_ptr()) };
+
+ Err(Errno::last())
+}
+
+/// Replace the current process image with a new one and replicate shell `PATH`
+/// searching behavior (see
+/// [`execvpe(3)`](https://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<SA: AsRef<CStr>, SE: AsRef<CStr>>(
+ filename: &CStr,
+ args: &[SA],
+ env: &[SE],
+) -> Result<Infallible> {
+ 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(Errno::last())
+}
+
+/// Replace the current process image with a new one (see
+/// [fexecve(2)](https://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.
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd"
+))]
+#[inline]
+pub fn fexecve<SA: AsRef<CStr>, SE: AsRef<CStr>>(
+ fd: RawFd,
+ args: &[SA],
+ env: &[SE],
+) -> Result<Infallible> {
+ 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(Errno::last())
+}
+
+/// Execute program relative to a directory file descriptor (see
+/// [execveat(2)](https://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<SA: AsRef<CStr>, SE: AsRef<CStr>>(
+ dirfd: RawFd,
+ pathname: &CStr,
+ args: &[SA],
+ env: &[SE],
+ flags: super::fcntl::AtFlags,
+) -> Result<Infallible> {
+ 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(Errno::last())
+}
+
+/// Daemonize this process by detaching from the controlling terminal (see
+/// [daemon(3)](https://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(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"
+))]
+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)
+}
+}
+
+feature! {
+#![feature = "hostname"]
+
+/// Set the system host name (see
+/// [sethostname(2)](https://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 returned if the name is not valid or the current process does not
+/// have permissions to update the host name.
+#[cfg(not(target_os = "redox"))]
+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 = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "aix",
+ target_os = "solaris", ))] {
+ 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 an internally allocated buffer, returning an
+/// `OsString` on success (see
+/// [gethostname(2)](https://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 an internal buffer, returning it as an `OsString` if successful.
+///
+/// ```no_run
+/// use nix::unistd;
+///
+/// let hostname = unistd::gethostname().expect("Failed getting hostname");
+/// let hostname = hostname.into_string().expect("Hostname wasn't valid UTF-8");
+/// println!("Hostname: {}", hostname);
+/// ```
+pub fn gethostname() -> Result<OsString> {
+ // The capacity is the max length of a hostname plus the NUL terminator.
+ let mut buffer: Vec<u8> = Vec::with_capacity(256);
+ let ptr = buffer.as_mut_ptr() as *mut c_char;
+ let len = buffer.capacity() as size_t;
+
+ let res = unsafe { libc::gethostname(ptr, len) };
+ Errno::result(res).map(|_| {
+ unsafe {
+ buffer.as_mut_ptr().wrapping_add(len - 1).write(0); // ensure always null-terminated
+ let len = CStr::from_ptr(buffer.as_ptr() as *const c_char).len();
+ buffer.set_len(len);
+ }
+ OsString::from_vec(buffer)
+ })
+}
+}
+
+/// 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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html).
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::os::unix::io::AsRawFd;
+/// use nix::unistd::close;
+///
+/// let f = tempfile::tempfile().unwrap();
+/// close(f.as_raw_fd()).unwrap(); // Bad! f will also close on drop!
+/// ```
+///
+/// ```rust
+/// use std::os::unix::io::IntoRawFd;
+/// use nix::unistd::close;
+///
+/// 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)](https://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)](https://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)
+}
+
+feature! {
+#![feature = "fs"]
+
+/// 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",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"
+ ))]
+ 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",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"
+ ))]
+ SeekHole = libc::SEEK_HOLE,
+}
+
+/// Move the read/write file offset.
+///
+/// See also [lseek(2)](https://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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pipe.html)
+pub fn pipe() -> std::result::Result<(RawFd, RawFd), Error> {
+ let mut fds = mem::MaybeUninit::<[c_int; 2]>::uninit();
+
+ let res = unsafe { libc::pipe(fds.as_mut_ptr() as *mut c_int) };
+
+ Error::result(res)?;
+
+ unsafe { Ok((fds.assume_init()[0], fds.assume_init()[1])) }
+}
+
+feature! {
+#![feature = "fs"]
+/// 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.
+#[cfg_attr(
+ target_os = "linux",
+ doc = "- `O_DIRECT`: Create a pipe that performs I/O in \"packet\" mode."
+)]
+#[cfg_attr(
+ target_os = "netbsd",
+ doc = "- `O_NOSIGPIPE`: Return `EPIPE` instead of raising `SIGPIPE`."
+)]
+/// - `O_NONBLOCK`: Set the non-blocking flag for the ends of the pipe.
+///
+/// See also [pipe(2)](https://man7.org/linux/man-pages/man2/pipe.2.html)
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"
+))]
+pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> {
+ let mut fds = mem::MaybeUninit::<[c_int; 2]>::uninit();
+
+ let res =
+ unsafe { libc::pipe2(fds.as_mut_ptr() as *mut c_int, flags.bits()) };
+
+ Errno::result(res)?;
+
+ unsafe { Ok((fds.assume_init()[0], fds.assume_init()[1])) }
+}
+
+/// Truncate a file to a specified length
+///
+/// See also
+/// [truncate(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html)
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html)
+pub fn ftruncate<Fd: AsFd>(fd: Fd, len: off_t) -> Result<()> {
+ Errno::result(unsafe { libc::ftruncate(fd.as_fd().as_raw_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(err),
+ }
+ }
+ }
+}
+
+/// Flags for `linkat` function.
+#[derive(Clone, Copy, Debug)]
+pub enum LinkatFlags {
+ SymlinkFollow,
+ NoSymlinkFollow,
+}
+
+/// Link one file to another file
+///
+/// Creates a new link (directory entry) at `newpath` for the existing file at `oldpath`. In the
+/// case of a relative `oldpath`, the path is interpreted relative to the directory associated
+/// with file descriptor `olddirfd` instead of the current working directory and similiarly for
+/// `newpath` and file descriptor `newdirfd`. In case `flag` is LinkatFlags::SymlinkFollow and
+/// `oldpath` names a symoblic link, a new link for the target of the symbolic link is created.
+/// If either `olddirfd` or `newdirfd` is `None`, `AT_FDCWD` is used respectively where `oldpath`
+/// and/or `newpath` is then interpreted relative to the current working directory of the calling
+/// process. If either `oldpath` or `newpath` is absolute, then `dirfd` is ignored.
+///
+/// # References
+/// See also [linkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/linkat.html)
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support symlinks yet
+pub fn linkat<P: ?Sized + NixPath>(
+ olddirfd: Option<RawFd>,
+ oldpath: &P,
+ newdirfd: Option<RawFd>,
+ newpath: &P,
+ flag: LinkatFlags,
+) -> Result<()> {
+ let atflag = match flag {
+ LinkatFlags::SymlinkFollow => AtFlags::AT_SYMLINK_FOLLOW,
+ LinkatFlags::NoSymlinkFollow => AtFlags::empty(),
+ };
+
+ let res = oldpath.with_nix_path(|oldcstr| {
+ newpath.with_nix_path(|newcstr| unsafe {
+ libc::linkat(
+ at_rawfd(olddirfd),
+ oldcstr.as_ptr(),
+ at_rawfd(newdirfd),
+ newcstr.as_ptr(),
+ atflag.bits() as libc::c_int,
+ )
+ })
+ })??;
+ Errno::result(res).map(drop)
+}
+
+/// Remove a directory entry
+///
+/// See also [unlink(2)](https://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)
+}
+
+/// Flags for `unlinkat` function.
+#[derive(Clone, Copy, Debug)]
+pub enum UnlinkatFlags {
+ RemoveDir,
+ NoRemoveDir,
+}
+
+/// Remove a directory entry
+///
+/// In the case of a relative path, the directory entry to be removed is determined relative to
+/// the directory associated with the file descriptor `dirfd` or the current working directory
+/// if `dirfd` is `None`. In the case of an absolute `path` `dirfd` is ignored. If `flag` is
+/// `UnlinkatFlags::RemoveDir` then removal of the directory entry specified by `dirfd` and `path`
+/// is performed.
+///
+/// # References
+/// See also [unlinkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlinkat.html)
+#[cfg(not(target_os = "redox"))]
+pub fn unlinkat<P: ?Sized + NixPath>(
+ dirfd: Option<RawFd>,
+ path: &P,
+ flag: UnlinkatFlags,
+) -> Result<()> {
+ let atflag = match flag {
+ UnlinkatFlags::RemoveDir => AtFlags::AT_REMOVEDIR,
+ UnlinkatFlags::NoRemoveDir => AtFlags::empty(),
+ };
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::unlinkat(
+ at_rawfd(dirfd),
+ cstr.as_ptr(),
+ atflag.bits() as libc::c_int,
+ )
+ })?;
+ Errno::result(res).map(drop)
+}
+
+#[inline]
+#[cfg(not(target_os = "fuchsia"))]
+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)](https://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() };
+}
+
+/// Commit filesystem caches containing file referred to by the open file
+/// descriptor `fd` to disk
+///
+/// See also [syncfs(2)](https://man7.org/linux/man-pages/man2/sync.2.html)
+#[cfg(target_os = "linux")]
+pub fn syncfs(fd: RawFd) -> Result<()> {
+ let res = unsafe { libc::syncfs(fd) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Synchronize changes to a file
+///
+/// See also [fsync(2)](https://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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html)
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "illumos",
+ target_os = "solaris"
+))]
+#[inline]
+pub fn fdatasync(fd: RawFd) -> Result<()> {
+ let res = unsafe { libc::fdatasync(fd) };
+
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "user"]
+
+/// Get a real user ID
+///
+/// See also [getuid(2)](https://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)](https://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)](https://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)](https://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 effective user ID
+///
+/// See also [seteuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/seteuid.html)
+#[inline]
+pub fn seteuid(euid: Uid) -> Result<()> {
+ let res = unsafe { libc::seteuid(euid.into()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Set the effective group ID
+///
+/// See also [setegid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setegid.html)
+#[inline]
+pub fn setegid(egid: Gid) -> Result<()> {
+ let res = unsafe { libc::setegid(egid.into()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Set the user ID
+///
+/// See also [setuid(2)](https://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 group ID
+///
+/// See also [setgid(2)](https://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)
+}
+}
+
+feature! {
+#![all(feature = "fs", feature = "user")]
+/// Set the user identity used for filesystem checks per-thread.
+/// On both success and failure, this call returns the previous filesystem user
+/// ID of the caller.
+///
+/// See also [setfsuid(2)](https://man7.org/linux/man-pages/man2/setfsuid.2.html)
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn setfsuid(uid: Uid) -> Uid {
+ let prev_fsuid = unsafe { libc::setfsuid(uid.into()) };
+ Uid::from_raw(prev_fsuid as uid_t)
+}
+
+/// Set the group identity used for filesystem checks per-thread.
+/// On both success and failure, this call returns the previous filesystem group
+/// ID of the caller.
+///
+/// See also [setfsgid(2)](https://man7.org/linux/man-pages/man2/setfsgid.2.html)
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn setfsgid(gid: Gid) -> Gid {
+ let prev_fsgid = unsafe { libc::setfsgid(gid.into()) };
+ Gid::from_raw(prev_fsgid as gid_t)
+}
+}
+
+feature! {
+#![feature = "user"]
+
+/// Get the list of supplementary group IDs of the calling process.
+///
+/// [Further reading](https://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 maximum number of groups. The value returned
+ // shall always be greater than or equal to one and less than or
+ // equal to the value of {NGROUPS_MAX} + 1.
+ let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) {
+ Ok(Some(n)) => (n + 1) as usize,
+ Ok(None) | Err(_) => <usize>::max_value(),
+ };
+
+ // Next, get the number of groups so we can size our Vec
+ let ngroups = unsafe { libc::getgroups(0, ptr::null_mut()) };
+
+ // If there are no supplementary groups, return early.
+ // This prevents a potential buffer over-read if the number of groups
+ // increases from zero before the next call. It would return the total
+ // number of groups beyond the capacity of the buffer.
+ if ngroups == 0 {
+ return Ok(Vec::new());
+ }
+
+ // Now actually get the groups. We try multiple times in case the number of
+ // groups has changed since the first call to getgroups() and the buffer is
+ // now too small.
+ let mut groups =
+ Vec::<Gid>::with_capacity(Errno::result(ngroups)? 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 ngroups = unsafe {
+ libc::getgroups(
+ groups.capacity() as c_int,
+ groups.as_mut_ptr() as *mut gid_t,
+ )
+ };
+
+ match Errno::result(ngroups) {
+ Ok(s) => {
+ unsafe { groups.set_len(s as usize) };
+ return Ok(groups);
+ }
+ Err(Errno::EINVAL) => {
+ // EINVAL indicates that the buffer size was too
+ // small, resize it up to ngroups_max as limit.
+ reserve_double_buffer_size(&mut groups, ngroups_max)
+ .or(Err(Errno::EINVAL))?;
+ }
+ Err(e) => return Err(e),
+ }
+ }
+}
+
+/// Set the list of supplementary group IDs for the calling process.
+///
+/// [Further reading](https://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<dyn Error>> {
+/// let uid = Uid::from_raw(33);
+/// let gid = Gid::from_raw(34);
+/// setgroups(&[gid])?;
+/// setgid(gid)?;
+/// setuid(uid)?;
+/// #
+/// # Ok(())
+/// # }
+/// #
+/// # try_main().unwrap();
+/// ```
+#[cfg(not(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+pub fn setgroups(groups: &[Gid]) -> Result<()> {
+ cfg_if! {
+ if #[cfg(any(target_os = "aix",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))] {
+ 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](https://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 = "aix",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "redox"
+)))]
+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 groups = Vec::<Gid>::with_capacity(min(ngroups_max, 8) as usize);
+ cfg_if! {
+ if #[cfg(any(target_os = "ios", target_os = "macos"))] {
+ type getgrouplist_group_t = c_int;
+ } else {
+ type getgrouplist_group_t = gid_t;
+ }
+ }
+ let gid: gid_t = group.into();
+ loop {
+ let mut ngroups = groups.capacity() as i32;
+ let ret = unsafe {
+ libc::getgrouplist(
+ user.as_ptr(),
+ gid as getgrouplist_group_t,
+ 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.
+ reserve_double_buffer_size(&mut groups, ngroups_max as usize)
+ .map_err(|_| Errno::EINVAL)?;
+ }
+ }
+}
+
+/// 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](https://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<dyn 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(())
+/// # }
+/// #
+/// # try_main().unwrap();
+/// ```
+#[cfg(not(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+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)
+}
+}
+
+feature! {
+#![feature = "signal"]
+
+/// Suspend the thread until a signal is received.
+///
+/// See also [pause(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pause.html).
+#[inline]
+#[cfg(not(target_os = "redox"))]
+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:
+ //!
+ #![cfg_attr(target_os = "redox", doc = " ```rust,ignore")]
+ #![cfg_attr(not(target_os = "redox"), doc = " ```rust")]
+ //! 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) { }
+ //! let sa = SigAction::new(
+ //! SigHandler::Handler(signal_handler),
+ //! SaFlags::SA_RESTART,
+ //! SigSet::empty()
+ //! );
+ //! unsafe {
+ //! sigaction(Signal::SIGALRM, &sa);
+ //! }
+ //!
+ //! let start = Instant::now();
+ //!
+ //! // Set an alarm for 1 second from now.
+ //! alarm::set(1);
+ //!
+ //! // Pause the process until the alarm signal is received.
+ //! let mut sigset = SigSet::empty();
+ //! sigset.add(Signal::SIGALRM);
+ //! sigset.wait();
+ //!
+ //! assert!(start.elapsed() >= Duration::from_secs(1));
+ //! ```
+ //!
+ //! # References
+ //!
+ //! See also [alarm(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/alarm.html).
+
+ /// 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)](https://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) }
+}
+
+feature! {
+#![feature = "acct"]
+
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+pub mod acct {
+ use crate::errno::Errno;
+ use crate::{NixPath, Result};
+ 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)
+ }
+}
+}
+
+feature! {
+#![feature = "fs"]
+/// 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)](https://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)))
+}
+}
+
+feature! {
+#![all(feature = "fs", feature = "feature")]
+
+/// Variable names for `pathconf`
+///
+/// Nix uses the same naming convention for these variables as the
+/// [getconf(1)](https://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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html)
+/// - [limits.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
+/// - [unistd.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html)
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(i32)]
+#[non_exhaustive]
+pub enum PathconfVar {
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "redox"
+ ))]
+ /// Minimum number of bits needed to represent, as a signed integer value,
+ /// the maximum size of a regular file allowed in the specified directory.
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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 = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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",
+ target_os = "redox"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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",
+ target_os = "redox"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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",
+ target_os = "redox"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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",
+ target_os = "redox"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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 = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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 = "illumos",
+ target_os = "linux",
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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 = "illumos",
+ target_os = "linux",
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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 = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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)](https://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(Errno::last())
+ }
+ } else {
+ Ok(Some(raw))
+ }
+}
+
+/// Get path-dependent configurable system variables (see
+/// [pathconf(2)](https://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)](https://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(Errno::last())
+ }
+ } else {
+ Ok(Some(raw))
+ }
+}
+}
+
+feature! {
+#![feature = "feature"]
+
+/// Variable names for `sysconf`
+///
+/// Nix uses the same naming convention for these variables as the
+/// [getconf(1)](https://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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html)
+/// - [unistd.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html)
+/// - [limits.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(i32)]
+#[non_exhaustive]
+pub enum SysconfVar {
+ /// Maximum number of I/O operations in a single list I/O call supported by
+ /// the implementation.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AIO_LISTIO_MAX = libc::_SC_AIO_LISTIO_MAX,
+ /// Maximum number of outstanding asynchronous I/O operations supported by
+ /// the implementation.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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`.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ATEXIT_MAX = libc::_SC_ATEXIT_MAX,
+ /// Maximum obase values allowed by the bc utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BC_BASE_MAX = libc::_SC_BC_BASE_MAX,
+ /// Maximum number of elements permitted in an array by the bc utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BC_DIM_MAX = libc::_SC_BC_DIM_MAX,
+ /// Maximum scale value allowed by the bc utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BC_SCALE_MAX = libc::_SC_BC_SCALE_MAX,
+ /// Maximum length of a string constant accepted by the bc utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BC_STRING_MAX = libc::_SC_BC_STRING_MAX,
+ /// Maximum number of simultaneous processes per real user ID.
+ CHILD_MAX = libc::_SC_CHILD_MAX,
+ // The number of clock ticks per second.
+ CLK_TCK = libc::_SC_CLK_TCK,
+ /// Maximum number of weights that can be assigned to an entry of the
+ /// LC_COLLATE order keyword in the locale definition file
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ COLL_WEIGHTS_MAX = libc::_SC_COLL_WEIGHTS_MAX,
+ /// Maximum number of timer expiration overruns.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ DELAYTIMER_MAX = libc::_SC_DELAYTIMER_MAX,
+ /// Maximum number of expressions that can be nested within parentheses by
+ /// the expr utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ EXPR_NEST_MAX = libc::_SC_EXPR_NEST_MAX,
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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`.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ LINE_MAX = libc::_SC_LINE_MAX,
+ /// Maximum length of a login name.
+ #[cfg(not(target_os = "haiku"))]
+ 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
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ GETGR_R_SIZE_MAX = libc::_SC_GETGR_R_SIZE_MAX,
+ /// Initial size of `getpwuid_r` and `getpwnam_r` data buffers
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ GETPW_R_SIZE_MAX = libc::_SC_GETPW_R_SIZE_MAX,
+ /// The maximum number of open message queue descriptors a process may hold.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MQ_OPEN_MAX = libc::_SC_MQ_OPEN_MAX,
+ /// The maximum number of message priorities supported by the implementation.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Advisory Information option.
+ _POSIX_ADVISORY_INFO = libc::_SC_ADVISORY_INFO,
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports barriers.
+ _POSIX_BARRIERS = libc::_SC_BARRIERS,
+ /// The implementation supports asynchronous input and output.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_ASYNCHRONOUS_IO = libc::_SC_ASYNCHRONOUS_IO,
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports clock selection.
+ _POSIX_CLOCK_SELECTION = libc::_SC_CLOCK_SELECTION,
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Process CPU-Time Clocks option.
+ _POSIX_CPUTIME = libc::_SC_CPUTIME,
+ /// The implementation supports the File Synchronization option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_FSYNC = libc::_SC_FSYNC,
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "openbsd",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the IPv6 option.
+ _POSIX_IPV6 = libc::_SC_IPV6,
+ /// The implementation supports job control.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_JOB_CONTROL = libc::_SC_JOB_CONTROL,
+ /// The implementation supports memory mapped Files.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MAPPED_FILES = libc::_SC_MAPPED_FILES,
+ /// The implementation supports the Process Memory Locking option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MEMLOCK = libc::_SC_MEMLOCK,
+ /// The implementation supports the Range Memory Locking option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MEMLOCK_RANGE = libc::_SC_MEMLOCK_RANGE,
+ /// The implementation supports memory protection.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MEMORY_PROTECTION = libc::_SC_MEMORY_PROTECTION,
+ /// The implementation supports the Message Passing option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MESSAGE_PASSING = libc::_SC_MESSAGE_PASSING,
+ /// The implementation supports the Monotonic Clock option.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MONOTONIC_CLOCK = libc::_SC_MONOTONIC_CLOCK,
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "openbsd",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Prioritized Input and Output option.
+ _POSIX_PRIORITIZED_IO = libc::_SC_PRIORITIZED_IO,
+ /// The implementation supports the Process Scheduling option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_PRIORITY_SCHEDULING = libc::_SC_PRIORITY_SCHEDULING,
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "openbsd",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Raw Sockets option.
+ _POSIX_RAW_SOCKETS = libc::_SC_RAW_SOCKETS,
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports realtime signals.
+ _POSIX_REALTIME_SIGNALS = libc::_SC_REALTIME_SIGNALS,
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SAVED_IDS = libc::_SC_SAVED_IDS,
+ /// The implementation supports semaphores.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SEMAPHORES = libc::_SC_SEMAPHORES,
+ /// The implementation supports the Shared Memory Objects option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SS_REPL_MAX = libc::_SC_SS_REPL_MAX,
+ /// The implementation supports the Synchronized Input and Output option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SYNCHRONIZED_IO = libc::_SC_SYNCHRONIZED_IO,
+ /// The implementation supports the Thread Stack Address Attribute option.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_ATTR_STACKADDR = libc::_SC_THREAD_ATTR_STACKADDR,
+ /// The implementation supports the Thread Stack Size Attribute option.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_PRIO_INHERIT = libc::_SC_THREAD_PRIO_INHERIT,
+ /// The implementation supports the Non-Robust Mutex Priority Protection option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_PRIO_PROTECT = libc::_SC_THREAD_PRIO_PROTECT,
+ /// The implementation supports the Thread Execution Scheduling option.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Thread Sporadic Server option.
+ _POSIX_THREAD_SPORADIC_SERVER = libc::_SC_THREAD_SPORADIC_SERVER,
+ /// The implementation supports threads.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports timeouts.
+ _POSIX_TIMEOUTS = libc::_SC_TIMEOUTS,
+ /// The implementation supports timers.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_TRACE_NAME_MAX = libc::_SC_TRACE_NAME_MAX,
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_TRACE_SYS_MAX = libc::_SC_TRACE_SYS_MAX,
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_C_BIND = libc::_SC_2_C_BIND,
+ /// The implementation supports the C-Language Development Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_C_DEV = libc::_SC_2_C_DEV,
+ /// The implementation supports the Terminal Characteristics option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_CHAR_TERM = libc::_SC_2_CHAR_TERM,
+ /// The implementation supports the FORTRAN Development Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_FORT_DEV = libc::_SC_2_FORT_DEV,
+ /// The implementation supports the FORTRAN Runtime Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_FORT_RUN = libc::_SC_2_FORT_RUN,
+ /// The implementation supports the creation of locales by the localedef
+ /// utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_SW_DEV = libc::_SC_2_SW_DEV,
+ /// The implementation supports the User Portability Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_UPE = libc::_SC_2_UPE,
+ /// Integer value indicating version of the Shell and Utilities volume of
+ /// POSIX.1 to which the implementation conforms.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _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,
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTHREAD_DESTRUCTOR_ITERATIONS = libc::_SC_THREAD_DESTRUCTOR_ITERATIONS,
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTHREAD_KEYS_MAX = libc::_SC_THREAD_KEYS_MAX,
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTHREAD_STACK_MIN = libc::_SC_THREAD_STACK_MIN,
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTHREAD_THREADS_MAX = libc::_SC_THREAD_THREADS_MAX,
+ #[cfg(not(target_os = "haiku"))]
+ 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ RTSIG_MAX = libc::_SC_RTSIG_MAX,
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SYMLOOP_MAX = libc::_SC_SYMLOOP_MAX,
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Integer value indicating version of the X/Open Portability Guide to
+ /// which the implementation conforms.
+ _XOPEN_VERSION = libc::_SC_XOPEN_VERSION,
+ /// The number of pages of physical memory. Note that it is possible for
+ /// the product of this value to overflow.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ _PHYS_PAGES = libc::_SC_PHYS_PAGES,
+ /// The number of currently available pages of physical memory.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ _AVPHYS_PAGES = libc::_SC_AVPHYS_PAGES,
+ /// The number of processors configured.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ _NPROCESSORS_CONF = libc::_SC_NPROCESSORS_CONF,
+ /// The number of processors currently online (available).
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ _NPROCESSORS_ONLN = libc::_SC_NPROCESSORS_ONLN,
+}
+
+/// Get configurable system variables (see
+/// [sysconf(3)](https://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(Errno::last())
+ }
+ } else {
+ Ok(Some(raw))
+ }
+}
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "fs")]
+mod pivot_root {
+ use crate::errno::Errno;
+ use crate::{NixPath, Result};
+
+ 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 = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+mod setres {
+ feature! {
+ #![feature = "user"]
+
+ use super::{Gid, Uid};
+ use crate::errno::Errno;
+ use crate::Result;
+
+ /// Sets the real, effective, and saved uid.
+ /// ([see setresuid(2)](https://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)](https://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)
+ }
+ }
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+mod getres {
+ feature! {
+ #![feature = "user"]
+
+ use super::{Gid, Uid};
+ use crate::errno::Errno;
+ use crate::Result;
+
+ /// Real, effective and saved user IDs.
+ #[derive(Debug, Copy, Clone, Eq, PartialEq)]
+ pub struct ResUid {
+ pub real: Uid,
+ pub effective: Uid,
+ pub saved: Uid,
+ }
+
+ /// Real, effective and saved group IDs.
+ #[derive(Debug, Copy, Clone, Eq, PartialEq)]
+ pub struct ResGid {
+ pub real: Gid,
+ pub effective: Gid,
+ pub saved: Gid,
+ }
+
+ /// Gets the real, effective, and saved user IDs.
+ ///
+ /// ([see getresuid(2)](http://man7.org/linux/man-pages/man2/getresuid.2.html))
+ ///
+ /// #Returns
+ ///
+ /// - `Ok((Uid, Uid, Uid))`: tuple of real, effective and saved uids on success.
+ /// - `Err(x)`: libc error code on failure.
+ ///
+ #[inline]
+ pub fn getresuid() -> Result<ResUid> {
+ let mut ruid = libc::uid_t::max_value();
+ let mut euid = libc::uid_t::max_value();
+ let mut suid = libc::uid_t::max_value();
+ let res = unsafe { libc::getresuid(&mut ruid, &mut euid, &mut suid) };
+
+ Errno::result(res).map(|_| ResUid {
+ real: Uid(ruid),
+ effective: Uid(euid),
+ saved: Uid(suid),
+ })
+ }
+
+ /// Gets the real, effective, and saved group IDs.
+ ///
+ /// ([see getresgid(2)](http://man7.org/linux/man-pages/man2/getresgid.2.html))
+ ///
+ /// #Returns
+ ///
+ /// - `Ok((Gid, Gid, Gid))`: tuple of real, effective and saved gids on success.
+ /// - `Err(x)`: libc error code on failure.
+ ///
+ #[inline]
+ pub fn getresgid() -> Result<ResGid> {
+ let mut rgid = libc::gid_t::max_value();
+ let mut egid = libc::gid_t::max_value();
+ let mut sgid = libc::gid_t::max_value();
+ let res = unsafe { libc::getresgid(&mut rgid, &mut egid, &mut sgid) };
+
+ Errno::result(res).map(|_| ResGid {
+ real: Gid(rgid),
+ effective: Gid(egid),
+ saved: Gid(sgid),
+ })
+ }
+ }
+}
+
+#[cfg(feature = "fs")]
+libc_bitflags! {
+ /// Options for access()
+ #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+ pub struct AccessFlags : c_int {
+ /// Test for existence of file.
+ F_OK;
+ /// Test for read permission.
+ R_OK;
+ /// Test for write permission.
+ W_OK;
+ /// Test for execute (search) permission.
+ X_OK;
+ }
+}
+
+feature! {
+#![feature = "fs"]
+
+/// Checks the file named by `path` for accessibility according to the flags given by `amode`
+/// See [access(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html)
+pub fn access<P: ?Sized + NixPath>(path: &P, amode: AccessFlags) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::access(cstr.as_ptr(), amode.bits())
+ })?;
+ Errno::result(res).map(drop)
+}
+
+/// Checks the file named by `path` for accessibility according to the flags given by `mode`
+///
+/// If `dirfd` has a value, then `path` is relative to directory associated with the file descriptor.
+///
+/// If `dirfd` is `None`, then `path` is relative to the current working directory.
+///
+/// # References
+///
+/// [faccessat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/faccessat.html)
+// redox: does not appear to support the *at family of syscalls.
+#[cfg(not(target_os = "redox"))]
+pub fn faccessat<P: ?Sized + NixPath>(
+ dirfd: Option<RawFd>,
+ path: &P,
+ mode: AccessFlags,
+ flags: AtFlags,
+) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::faccessat(
+ at_rawfd(dirfd),
+ cstr.as_ptr(),
+ mode.bits(),
+ flags.bits(),
+ )
+ })?;
+ Errno::result(res).map(drop)
+}
+
+/// Checks the file named by `path` for accessibility according to the flags given
+/// by `mode` using effective UID, effective GID and supplementary group lists.
+///
+/// # References
+///
+/// * [FreeBSD man page](https://www.freebsd.org/cgi/man.cgi?query=eaccess&sektion=2&n=1)
+/// * [Linux man page](https://man7.org/linux/man-pages/man3/euidaccess.3.html)
+#[cfg(any(
+ all(target_os = "linux", not(target_env = "uclibc")),
+ target_os = "freebsd",
+ target_os = "dragonfly"
+))]
+pub fn eaccess<P: ?Sized + NixPath>(path: &P, mode: AccessFlags) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::eaccess(cstr.as_ptr(), mode.bits())
+ })?;
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "user"]
+
+/// Representation of a User, based on `libc::passwd`
+///
+/// The reason some fields in this struct are `String` and others are `CString` is because some
+/// fields are based on the user's locale, which could be non-UTF8, while other fields are
+/// guaranteed to conform to [`NAME_REGEX`](https://serverfault.com/a/73101/407341), which only
+/// contains ASCII.
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct User {
+ /// Username
+ pub name: String,
+ /// User password (probably hashed)
+ pub passwd: CString,
+ /// User ID
+ pub uid: Uid,
+ /// Group ID
+ pub gid: Gid,
+ /// User information
+ #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
+ pub gecos: CString,
+ /// Home directory
+ pub dir: PathBuf,
+ /// Path to shell
+ pub shell: PathBuf,
+ /// Login class
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"
+ )))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub class: CString,
+ /// Last password change
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"
+ )))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub change: libc::time_t,
+ /// Expiration time of account
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"
+ )))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub expire: libc::time_t,
+}
+
+#[cfg(not(target_os = "redox"))] //RedoxFS does not support passwd
+impl From<&libc::passwd> for User {
+ fn from(pw: &libc::passwd) -> User {
+ unsafe {
+ User {
+ name: if pw.pw_name.is_null() {
+ Default::default()
+ } else {
+ CStr::from_ptr(pw.pw_name).to_string_lossy().into_owned()
+ },
+ passwd: if pw.pw_passwd.is_null() {
+ Default::default()
+ } else {
+ CString::new(CStr::from_ptr(pw.pw_passwd).to_bytes())
+ .unwrap()
+ },
+ #[cfg(not(all(
+ target_os = "android",
+ target_pointer_width = "32"
+ )))]
+ gecos: if pw.pw_gecos.is_null() {
+ Default::default()
+ } else {
+ CString::new(CStr::from_ptr(pw.pw_gecos).to_bytes())
+ .unwrap()
+ },
+ dir: if pw.pw_dir.is_null() {
+ Default::default()
+ } else {
+ PathBuf::from(OsStr::from_bytes(
+ CStr::from_ptr(pw.pw_dir).to_bytes(),
+ ))
+ },
+ shell: if pw.pw_shell.is_null() {
+ Default::default()
+ } else {
+ PathBuf::from(OsStr::from_bytes(
+ CStr::from_ptr(pw.pw_shell).to_bytes(),
+ ))
+ },
+ uid: Uid::from_raw(pw.pw_uid),
+ gid: Gid::from_raw(pw.pw_gid),
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"
+ )))]
+ class: CString::new(CStr::from_ptr(pw.pw_class).to_bytes())
+ .unwrap(),
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"
+ )))]
+ change: pw.pw_change,
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"
+ )))]
+ expire: pw.pw_expire,
+ }
+ }
+ }
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl From<User> for libc::passwd {
+ fn from(u: User) -> Self {
+ let name = match CString::new(u.name) {
+ Ok(n) => n.into_raw(),
+ Err(_) => CString::new("").unwrap().into_raw(),
+ };
+ let dir = match u.dir.into_os_string().into_string() {
+ Ok(s) => CString::new(s.as_str()).unwrap().into_raw(),
+ Err(_) => CString::new("").unwrap().into_raw(),
+ };
+ let shell = match u.shell.into_os_string().into_string() {
+ Ok(s) => CString::new(s.as_str()).unwrap().into_raw(),
+ Err(_) => CString::new("").unwrap().into_raw(),
+ };
+ Self {
+ pw_name: name,
+ pw_passwd: u.passwd.into_raw(),
+ #[cfg(not(all(
+ target_os = "android",
+ target_pointer_width = "32"
+ )))]
+ pw_gecos: u.gecos.into_raw(),
+ pw_dir: dir,
+ pw_shell: shell,
+ pw_uid: u.uid.0,
+ pw_gid: u.gid.0,
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"
+ )))]
+ pw_class: u.class.into_raw(),
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"
+ )))]
+ pw_change: u.change,
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"
+ )))]
+ pw_expire: u.expire,
+ #[cfg(target_os = "illumos")]
+ pw_age: CString::new("").unwrap().into_raw(),
+ #[cfg(target_os = "illumos")]
+ pw_comment: CString::new("").unwrap().into_raw(),
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ pw_fields: 0,
+ }
+ }
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl User {
+ /// # Safety
+ ///
+ /// If `f` writes to its `*mut *mut libc::passwd` parameter, then it must
+ /// also initialize the value pointed to by its `*mut libc::group`
+ /// parameter.
+ unsafe fn from_anything<F>(f: F) -> Result<Option<Self>>
+ where
+ F: Fn(
+ *mut libc::passwd,
+ *mut c_char,
+ libc::size_t,
+ *mut *mut libc::passwd,
+ ) -> libc::c_int,
+ {
+ let buflimit = 1048576;
+ let bufsize = match sysconf(SysconfVar::GETPW_R_SIZE_MAX) {
+ Ok(Some(n)) => n as usize,
+ Ok(None) | Err(_) => 16384,
+ };
+
+ let mut cbuf = Vec::with_capacity(bufsize);
+ let mut pwd = mem::MaybeUninit::<libc::passwd>::uninit();
+ let mut res = ptr::null_mut();
+
+ loop {
+ let error = f(
+ pwd.as_mut_ptr(),
+ cbuf.as_mut_ptr(),
+ cbuf.capacity(),
+ &mut res,
+ );
+ if error == 0 {
+ if res.is_null() {
+ return Ok(None);
+ } else {
+ // SAFETY: `f` guarantees that `pwd` is initialized if `res`
+ // is not null.
+ let pwd = pwd.assume_init();
+ return Ok(Some(User::from(&pwd)));
+ }
+ } else if Errno::last() == Errno::ERANGE {
+ // Trigger the internal buffer resizing logic.
+ reserve_double_buffer_size(&mut cbuf, buflimit)?;
+ } else {
+ return Err(Errno::last());
+ }
+ }
+ }
+
+ /// Get a user by UID.
+ ///
+ /// Internally, this function calls
+ /// [getpwuid_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use nix::unistd::{Uid, User};
+ /// // Returns an Result<Option<User>>, thus the double unwrap.
+ /// let res = User::from_uid(Uid::from_raw(0)).unwrap().unwrap();
+ /// assert_eq!(res.name, "root");
+ /// ```
+ pub fn from_uid(uid: Uid) -> Result<Option<Self>> {
+ // SAFETY: `getpwuid_r` will write to `res` if it initializes the value
+ // at `pwd`.
+ unsafe {
+ User::from_anything(|pwd, cbuf, cap, res| {
+ libc::getpwuid_r(uid.0, pwd, cbuf, cap, res)
+ })
+ }
+ }
+
+ /// Get a user by name.
+ ///
+ /// Internally, this function calls
+ /// [getpwnam_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwnam_r.html)
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use nix::unistd::User;
+ /// // Returns an Result<Option<User>>, thus the double unwrap.
+ /// let res = User::from_name("root").unwrap().unwrap();
+ /// assert_eq!(res.name, "root");
+ /// ```
+ pub fn from_name(name: &str) -> Result<Option<Self>> {
+ let name = match CString::new(name) {
+ Ok(c_str) => c_str,
+ Err(_nul_error) => return Ok(None),
+ };
+ // SAFETY: `getpwnam_r` will write to `res` if it initializes the value
+ // at `pwd`.
+ unsafe {
+ User::from_anything(|pwd, cbuf, cap, res| {
+ libc::getpwnam_r(name.as_ptr(), pwd, cbuf, cap, res)
+ })
+ }
+ }
+}
+
+/// Representation of a Group, based on `libc::group`
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Group {
+ /// Group name
+ pub name: String,
+ /// Group password
+ pub passwd: CString,
+ /// Group ID
+ pub gid: Gid,
+ /// List of Group members
+ pub mem: Vec<String>,
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl From<&libc::group> for Group {
+ fn from(gr: &libc::group) -> Group {
+ unsafe {
+ Group {
+ name: if gr.gr_name.is_null() {
+ Default::default()
+ } else {
+ CStr::from_ptr(gr.gr_name).to_string_lossy().into_owned()
+ },
+ passwd: if gr.gr_passwd.is_null() {
+ Default::default()
+ } else {
+ CString::new(CStr::from_ptr(gr.gr_passwd).to_bytes())
+ .unwrap()
+ },
+ gid: Gid::from_raw(gr.gr_gid),
+ mem: if gr.gr_mem.is_null() {
+ Default::default()
+ } else {
+ Group::members(gr.gr_mem)
+ },
+ }
+ }
+ }
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl Group {
+ unsafe fn members(mem: *mut *mut c_char) -> Vec<String> {
+ let mut ret = Vec::new();
+
+ for i in 0.. {
+ let u = mem.offset(i);
+ if (*u).is_null() {
+ break;
+ } else {
+ let s = CStr::from_ptr(*u).to_string_lossy().into_owned();
+ ret.push(s);
+ }
+ }
+
+ ret
+ }
+
+ /// # Safety
+ ///
+ /// If `f` writes to its `*mut *mut libc::group` parameter, then it must
+ /// also initialize the value pointed to by its `*mut libc::group`
+ /// parameter.
+ unsafe fn from_anything<F>(f: F) -> Result<Option<Self>>
+ where
+ F: Fn(
+ *mut libc::group,
+ *mut c_char,
+ libc::size_t,
+ *mut *mut libc::group,
+ ) -> libc::c_int,
+ {
+ let buflimit = 1048576;
+ let bufsize = match sysconf(SysconfVar::GETGR_R_SIZE_MAX) {
+ Ok(Some(n)) => n as usize,
+ Ok(None) | Err(_) => 16384,
+ };
+
+ let mut cbuf = Vec::with_capacity(bufsize);
+ let mut grp = mem::MaybeUninit::<libc::group>::uninit();
+ let mut res = ptr::null_mut();
+
+ loop {
+ let error = f(
+ grp.as_mut_ptr(),
+ cbuf.as_mut_ptr(),
+ cbuf.capacity(),
+ &mut res,
+ );
+ if error == 0 {
+ if res.is_null() {
+ return Ok(None);
+ } else {
+ // SAFETY: `f` guarantees that `grp` is initialized if `res`
+ // is not null.
+ let grp = grp.assume_init();
+ return Ok(Some(Group::from(&grp)));
+ }
+ } else if Errno::last() == Errno::ERANGE {
+ // Trigger the internal buffer resizing logic.
+ reserve_double_buffer_size(&mut cbuf, buflimit)?;
+ } else {
+ return Err(Errno::last());
+ }
+ }
+ }
+
+ /// Get a group by GID.
+ ///
+ /// Internally, this function calls
+ /// [getgrgid_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ ///
+ /// # Examples
+ ///
+ // Disable this test on all OS except Linux as root group may not exist.
+ #[cfg_attr(not(target_os = "linux"), doc = " ```no_run")]
+ #[cfg_attr(target_os = "linux", doc = " ```")]
+ /// use nix::unistd::{Gid, Group};
+ /// // Returns an Result<Option<Group>>, thus the double unwrap.
+ /// let res = Group::from_gid(Gid::from_raw(0)).unwrap().unwrap();
+ /// assert!(res.name == "root");
+ /// ```
+ pub fn from_gid(gid: Gid) -> Result<Option<Self>> {
+ // SAFETY: `getgrgid_r` will write to `res` if it initializes the value
+ // at `grp`.
+ unsafe {
+ Group::from_anything(|grp, cbuf, cap, res| {
+ libc::getgrgid_r(gid.0, grp, cbuf, cap, res)
+ })
+ }
+ }
+
+ /// Get a group by name.
+ ///
+ /// Internally, this function calls
+ /// [getgrnam_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ ///
+ /// # Examples
+ ///
+ // Disable this test on all OS except Linux as root group may not exist.
+ #[cfg_attr(not(target_os = "linux"), doc = " ```no_run")]
+ #[cfg_attr(target_os = "linux", doc = " ```")]
+ /// use nix::unistd::Group;
+ /// // Returns an Result<Option<Group>>, thus the double unwrap.
+ /// let res = Group::from_name("root").unwrap().unwrap();
+ /// assert!(res.name == "root");
+ /// ```
+ pub fn from_name(name: &str) -> Result<Option<Self>> {
+ let name = match CString::new(name) {
+ Ok(c_str) => c_str,
+ Err(_nul_error) => return Ok(None),
+ };
+ // SAFETY: `getgrnam_r` will write to `res` if it initializes the value
+ // at `grp`.
+ unsafe {
+ Group::from_anything(|grp, cbuf, cap, res| {
+ libc::getgrnam_r(name.as_ptr(), grp, cbuf, cap, res)
+ })
+ }
+ }
+}
+}
+
+feature! {
+#![feature = "term"]
+
+/// Get the name of the terminal device that is open on file descriptor fd
+/// (see [`ttyname(3)`](https://man7.org/linux/man-pages/man3/ttyname.3.html)).
+#[cfg(not(target_os = "fuchsia"))]
+pub fn ttyname(fd: RawFd) -> Result<PathBuf> {
+ const PATH_MAX: usize = libc::PATH_MAX as usize;
+ let mut buf = vec![0_u8; PATH_MAX];
+ let c_buf = buf.as_mut_ptr() as *mut libc::c_char;
+
+ let ret = unsafe { libc::ttyname_r(fd, c_buf, buf.len()) };
+ if ret != 0 {
+ return Err(Errno::from_i32(ret));
+ }
+
+ let nul = buf.iter().position(|c| *c == b'\0').unwrap();
+ buf.truncate(nul);
+ Ok(OsString::from_vec(buf).into())
+}
+}
+
+feature! {
+#![all(feature = "socket", feature = "user")]
+
+/// Get the effective user ID and group ID associated with a Unix domain socket.
+///
+/// See also [getpeereid(3)](https://www.freebsd.org/cgi/man.cgi?query=getpeereid)
+#[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "dragonfly",
+))]
+pub fn getpeereid(fd: RawFd) -> Result<(Uid, Gid)> {
+ let mut uid = 1;
+ let mut gid = 1;
+
+ let ret = unsafe { libc::getpeereid(fd, &mut uid, &mut gid) };
+
+ Errno::result(ret).map(|_| (Uid(uid), Gid(gid)))
+}
+}
+
+feature! {
+#![all(feature = "fs")]
+
+/// Set the file flags.
+///
+/// See also [chflags(2)](https://www.freebsd.org/cgi/man.cgi?query=chflags&sektion=2)
+#[cfg(any(
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "macos",
+ target_os = "ios"
+))]
+pub fn chflags<P: ?Sized + NixPath>(path: &P, flags: FileFlag) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::chflags(cstr.as_ptr(), flags.bits())
+ })?;
+
+ Errno::result(res).map(drop)
+}
+}
diff --git a/third_party/rust/nix/test/common/mod.rs b/third_party/rust/nix/test/common/mod.rs
new file mode 100644
index 0000000000..bb056aab87
--- /dev/null
+++ b/third_party/rust/nix/test/common/mod.rs
@@ -0,0 +1,149 @@
+use cfg_if::cfg_if;
+
+#[macro_export]
+macro_rules! skip {
+ ($($reason: expr),+) => {
+ use ::std::io::{self, Write};
+
+ let stderr = io::stderr();
+ let mut handle = stderr.lock();
+ writeln!(handle, $($reason),+).unwrap();
+ return;
+ }
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ #[macro_export] macro_rules! require_capability {
+ ($name:expr, $capname:ident) => {
+ use ::caps::{Capability, CapSet, has_cap};
+
+ if !has_cap(None, CapSet::Effective, Capability::$capname)
+ .unwrap()
+ {
+ skip!("{} requires capability {}. Skipping test.", $name, Capability::$capname);
+ }
+ }
+ }
+ } else if #[cfg(not(target_os = "redox"))] {
+ #[macro_export] macro_rules! require_capability {
+ ($name:expr, $capname:ident) => {}
+ }
+ }
+}
+
+/// Skip the test if we don't have the ability to mount file systems.
+#[cfg(target_os = "freebsd")]
+#[macro_export]
+macro_rules! require_mount {
+ ($name:expr) => {
+ use ::sysctl::{CtlValue, Sysctl};
+ use nix::unistd::Uid;
+
+ let ctl = ::sysctl::Ctl::new("vfs.usermount").unwrap();
+ if !Uid::current().is_root() && CtlValue::Int(0) == ctl.value().unwrap()
+ {
+ skip!(
+ "{} requires the ability to mount file systems. Skipping test.",
+ $name
+ );
+ }
+ };
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[macro_export]
+macro_rules! skip_if_cirrus {
+ ($reason:expr) => {
+ if std::env::var_os("CIRRUS_CI").is_some() {
+ skip!("{}", $reason);
+ }
+ };
+}
+
+#[cfg(target_os = "freebsd")]
+#[macro_export]
+macro_rules! skip_if_jailed {
+ ($name:expr) => {
+ use ::sysctl::{CtlValue, Sysctl};
+
+ let ctl = ::sysctl::Ctl::new("security.jail.jailed").unwrap();
+ if let CtlValue::Int(1) = ctl.value().unwrap() {
+ skip!("{} cannot run in a jail. Skipping test.", $name);
+ }
+ };
+}
+
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+#[macro_export]
+macro_rules! skip_if_not_root {
+ ($name:expr) => {
+ use nix::unistd::Uid;
+
+ if !Uid::current().is_root() {
+ skip!("{} requires root privileges. Skipping test.", $name);
+ }
+ };
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ #[macro_export] macro_rules! skip_if_seccomp {
+ ($name:expr) => {
+ if let Ok(s) = std::fs::read_to_string("/proc/self/status") {
+ for l in s.lines() {
+ let mut fields = l.split_whitespace();
+ if fields.next() == Some("Seccomp:") &&
+ fields.next() != Some("0")
+ {
+ skip!("{} cannot be run in Seccomp mode. Skipping test.",
+ stringify!($name));
+ }
+ }
+ }
+ }
+ }
+ } else if #[cfg(not(target_os = "redox"))] {
+ #[macro_export] macro_rules! skip_if_seccomp {
+ ($name:expr) => {}
+ }
+ }
+}
+
+cfg_if! {
+ if #[cfg(target_os = "linux")] {
+ #[macro_export] macro_rules! require_kernel_version {
+ ($name:expr, $version_requirement:expr) => {
+ use semver::{Version, VersionReq};
+
+ let version_requirement = VersionReq::parse($version_requirement)
+ .expect("Bad match_version provided");
+
+ let uname = nix::sys::utsname::uname().unwrap();
+ println!("{}", uname.sysname().to_str().unwrap());
+ println!("{}", uname.nodename().to_str().unwrap());
+ println!("{}", uname.release().to_str().unwrap());
+ println!("{}", uname.version().to_str().unwrap());
+ println!("{}", uname.machine().to_str().unwrap());
+
+ // Fix stuff that the semver parser can't handle
+ let fixed_release = &uname.release().to_str().unwrap().to_string()
+ // Fedora 33 reports version as 4.18.el8_2.x86_64 or
+ // 5.18.200-fc33.x86_64. Remove the underscore.
+ .replace("_", "-")
+ // Cirrus-CI reports version as 4.19.112+ . Remove the +
+ .replace("+", "");
+ let mut version = Version::parse(fixed_release).unwrap();
+
+ //Keep only numeric parts
+ version.pre = semver::Prerelease::EMPTY;
+ version.build = semver::BuildMetadata::EMPTY;
+
+ if !version_requirement.matches(&version) {
+ skip!("Skip {} because kernel version `{}` doesn't match the requirement `{}`",
+ stringify!($name), version, version_requirement);
+ }
+ }
+ }
+ }
+}
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..20312120a6
--- /dev/null
+++ b/third_party/rust/nix/test/sys/mod.rs
@@ -0,0 +1,60 @@
+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",
+ all(target_os = "linux", not(target_env = "uclibc")),
+ target_os = "macos",
+ target_os = "netbsd"
+))]
+mod test_aio;
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+mod test_ioctl;
+#[cfg(not(target_os = "redox"))]
+mod test_mman;
+#[cfg(not(target_os = "redox"))]
+mod test_select;
+#[cfg(target_os = "linux")]
+mod test_signalfd;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+mod test_socket;
+#[cfg(not(any(target_os = "redox")))]
+mod test_sockopt;
+mod test_stat;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod test_sysinfo;
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+mod test_termios;
+mod test_uio;
+mod test_wait;
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod test_epoll;
+#[cfg(target_os = "linux")]
+mod test_inotify;
+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;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod test_timerfd;
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..5035b5a08f
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_aio.rs
@@ -0,0 +1,624 @@
+use std::{
+ io::{Read, Seek, Write},
+ ops::Deref,
+ os::unix::io::AsRawFd,
+ pin::Pin,
+ sync::atomic::{AtomicBool, Ordering},
+ thread, time,
+};
+
+use libc::c_int;
+use nix::{
+ errno::*,
+ sys::{
+ aio::*,
+ signal::{
+ sigaction, SaFlags, SigAction, SigHandler, SigSet, SigevNotify,
+ Signal,
+ },
+ time::{TimeSpec, TimeValLike},
+ },
+};
+use tempfile::tempfile;
+
+pub static SIGNALED: AtomicBool = AtomicBool::new(false);
+
+extern "C" fn sigfunc(_: c_int) {
+ SIGNALED.store(true, Ordering::Relaxed);
+}
+
+// Helper that polls an AioCb for completion or error
+macro_rules! poll_aio {
+ ($aiocb: expr) => {
+ loop {
+ let err = $aiocb.as_mut().error();
+ if err != Err(Errno::EINPROGRESS) {
+ break err;
+ };
+ thread::sleep(time::Duration::from_millis(10));
+ }
+ };
+}
+
+mod aio_fsync {
+ use super::*;
+
+ #[test]
+ fn test_accessors() {
+ let aiocb = AioFsync::new(
+ 1001,
+ AioFsyncMode::O_SYNC,
+ 42,
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 99,
+ },
+ );
+ assert_eq!(1001, aiocb.fd());
+ assert_eq!(AioFsyncMode::O_SYNC, aiocb.mode());
+ assert_eq!(42, aiocb.priority());
+ let sev = aiocb.sigevent().sigevent();
+ assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+ assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+ }
+
+ /// `AioFsync::submit` should not modify the `AioCb` object if
+ /// `libc::aio_fsync` returns an error
+ // Skip on Linux, because Linux's AIO implementation can't detect errors
+ // synchronously
+ #[test]
+ #[cfg(any(target_os = "freebsd", target_os = "macos"))]
+ fn 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 aiof = Box::pin(AioFsync::new(
+ f.as_raw_fd(),
+ mode,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ let err = aiof.as_mut().submit();
+ err.expect_err("assertion failed");
+ }
+
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn ok() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let fd = f.as_raw_fd();
+ let mut aiof = Box::pin(AioFsync::new(
+ fd,
+ AioFsyncMode::O_SYNC,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ aiof.as_mut().submit().unwrap();
+ poll_aio!(&mut aiof).unwrap();
+ aiof.as_mut().aio_return().unwrap();
+ }
+}
+
+mod aio_read {
+ use super::*;
+
+ #[test]
+ fn test_accessors() {
+ let mut rbuf = vec![0; 4];
+ let aiocb = AioRead::new(
+ 1001,
+ 2, //offset
+ &mut rbuf,
+ 42, //priority
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 99,
+ },
+ );
+ assert_eq!(1001, aiocb.fd());
+ assert_eq!(4, aiocb.nbytes());
+ assert_eq!(2, aiocb.offset());
+ assert_eq!(42, aiocb.priority());
+ let sev = aiocb.sigevent().sigevent();
+ assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+ assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+ }
+
+ // Tests AioWrite.cancel. We aren't trying to test the OS's implementation,
+ // only our bindings. So it's sufficient to check that cancel
+ // returned any AioCancelStat value.
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn cancel() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut rbuf = vec![0; 4];
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let fd = f.as_raw_fd();
+ let mut aior =
+ Box::pin(AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone));
+ aior.as_mut().submit().unwrap();
+
+ aior.as_mut().cancel().unwrap();
+
+ // Wait for aiow to complete, but don't care whether it succeeded
+ let _ = poll_aio!(&mut aior);
+ let _ = aior.as_mut().aio_return();
+ }
+
+ /// `AioRead::submit` should not modify the `AioCb` object if
+ /// `libc::aio_read` returns an error
+ // Skip on Linux, because Linux's AIO implementation can't detect errors
+ // synchronously
+ #[test]
+ #[cfg(any(target_os = "freebsd", target_os = "macos"))]
+ fn error() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut rbuf = vec![0; 4];
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aior = Box::pin(AioRead::new(
+ f.as_raw_fd(),
+ -1, //an invalid offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ ));
+ aior.as_mut().submit().expect_err("assertion failed");
+ }
+
+ // Test a simple aio operation with no completion notification. We must
+ // poll for completion
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn ok() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut rbuf = vec![0; 4];
+ const EXPECT: &[u8] = b"cdef";
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ {
+ let fd = f.as_raw_fd();
+ let mut aior = Box::pin(AioRead::new(
+ fd,
+ 2,
+ &mut rbuf,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ aior.as_mut().submit().unwrap();
+
+ let err = poll_aio!(&mut aior);
+ assert_eq!(err, Ok(()));
+ assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len());
+ }
+ assert_eq!(EXPECT, rbuf.deref());
+ }
+
+ // Like ok, but allocates the structure on the stack.
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn on_stack() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut rbuf = vec![0; 4];
+ const EXPECT: &[u8] = b"cdef";
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ {
+ let fd = f.as_raw_fd();
+ let mut aior =
+ AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone);
+ let mut aior = unsafe { Pin::new_unchecked(&mut aior) };
+ aior.as_mut().submit().unwrap();
+
+ let err = poll_aio!(&mut aior);
+ assert_eq!(err, Ok(()));
+ assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len());
+ }
+ assert_eq!(EXPECT, rbuf.deref());
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+#[cfg(fbsd14)]
+mod aio_readv {
+ use std::io::IoSliceMut;
+
+ use super::*;
+
+ #[test]
+ fn test_accessors() {
+ let mut rbuf0 = vec![0; 4];
+ let mut rbuf1 = vec![0; 8];
+ let mut rbufs =
+ [IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
+ let aiocb = AioReadv::new(
+ 1001,
+ 2, //offset
+ &mut rbufs,
+ 42, //priority
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 99,
+ },
+ );
+ assert_eq!(1001, aiocb.fd());
+ assert_eq!(2, aiocb.iovlen());
+ assert_eq!(2, aiocb.offset());
+ assert_eq!(42, aiocb.priority());
+ let sev = aiocb.sigevent().sigevent();
+ assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+ assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+ }
+
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn ok() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut rbuf0 = vec![0; 4];
+ let mut rbuf1 = vec![0; 2];
+ let mut rbufs =
+ [IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
+ const EXPECT0: &[u8] = b"cdef";
+ const EXPECT1: &[u8] = b"12";
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ {
+ let fd = f.as_raw_fd();
+ let mut aior = Box::pin(AioReadv::new(
+ fd,
+ 2,
+ &mut rbufs,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ aior.as_mut().submit().unwrap();
+
+ let err = poll_aio!(&mut aior);
+ assert_eq!(err, Ok(()));
+ assert_eq!(
+ aior.as_mut().aio_return().unwrap(),
+ EXPECT0.len() + EXPECT1.len()
+ );
+ }
+ assert_eq!(&EXPECT0, &rbuf0);
+ assert_eq!(&EXPECT1, &rbuf1);
+ }
+}
+
+mod aio_write {
+ use super::*;
+
+ #[test]
+ fn test_accessors() {
+ let wbuf = vec![0; 4];
+ let aiocb = AioWrite::new(
+ 1001,
+ 2, //offset
+ &wbuf,
+ 42, //priority
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 99,
+ },
+ );
+ assert_eq!(1001, aiocb.fd());
+ assert_eq!(4, aiocb.nbytes());
+ assert_eq!(2, aiocb.offset());
+ assert_eq!(42, aiocb.priority());
+ let sev = aiocb.sigevent().sigevent();
+ assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+ assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+ }
+
+ // Tests AioWrite.cancel. We aren't trying to test the OS's implementation,
+ // only our bindings. So it's sufficient to check that cancel
+ // returned any AioCancelStat value.
+ #[test]
+ #[cfg_attr(target_env = "musl", ignore)]
+ fn cancel() {
+ let wbuf: &[u8] = b"CDEF";
+
+ let f = tempfile().unwrap();
+ let mut aiow = Box::pin(AioWrite::new(
+ f.as_raw_fd(),
+ 0,
+ wbuf,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ aiow.as_mut().submit().unwrap();
+ let err = aiow.as_mut().error();
+ assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
+
+ aiow.as_mut().cancel().unwrap();
+
+ // Wait for aiow to complete, but don't care whether it succeeded
+ let _ = poll_aio!(&mut aiow);
+ let _ = aiow.as_mut().aio_return();
+ }
+
+ // Test a simple aio operation with no completion notification. We must
+ // poll for completion.
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn ok() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let wbuf = "CDEF".to_string().into_bytes();
+ let mut rbuf = Vec::new();
+ const EXPECT: &[u8] = b"abCDEF123456";
+
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aiow = Box::pin(AioWrite::new(
+ f.as_raw_fd(),
+ 2,
+ &wbuf,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ aiow.as_mut().submit().unwrap();
+
+ let err = poll_aio!(&mut aiow);
+ assert_eq!(err, Ok(()));
+ assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
+
+ f.rewind().unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert_eq!(len, EXPECT.len());
+ assert_eq!(rbuf, EXPECT);
+ }
+
+ // Like ok, but allocates the structure on the stack.
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn on_stack() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let wbuf = "CDEF".to_string().into_bytes();
+ let mut rbuf = Vec::new();
+ const EXPECT: &[u8] = b"abCDEF123456";
+
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aiow = AioWrite::new(
+ f.as_raw_fd(),
+ 2, //offset
+ &wbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ );
+ let mut aiow = unsafe { Pin::new_unchecked(&mut aiow) };
+ aiow.as_mut().submit().unwrap();
+
+ let err = poll_aio!(&mut aiow);
+ assert_eq!(err, Ok(()));
+ assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
+
+ f.rewind().unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert_eq!(len, EXPECT.len());
+ assert_eq!(rbuf, EXPECT);
+ }
+
+ /// `AioWrite::write` should not modify the `AioCb` object if
+ /// `libc::aio_write` returns an error.
+ // Skip on Linux, because Linux's AIO implementation can't detect errors
+ // synchronously
+ #[test]
+ #[cfg(any(target_os = "freebsd", target_os = "macos"))]
+ fn error() {
+ let wbuf = "CDEF".to_string().into_bytes();
+ let mut aiow = Box::pin(AioWrite::new(
+ 666, // An invalid file descriptor
+ 0, //offset
+ &wbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ ));
+ aiow.as_mut().submit().expect_err("assertion failed");
+ // Dropping the AioWrite at this point should not panic
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+#[cfg(fbsd14)]
+mod aio_writev {
+ use std::io::IoSlice;
+
+ use super::*;
+
+ #[test]
+ fn test_accessors() {
+ let wbuf0 = vec![0; 4];
+ let wbuf1 = vec![0; 8];
+ let wbufs = [IoSlice::new(&wbuf0), IoSlice::new(&wbuf1)];
+ let aiocb = AioWritev::new(
+ 1001,
+ 2, //offset
+ &wbufs,
+ 42, //priority
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 99,
+ },
+ );
+ assert_eq!(1001, aiocb.fd());
+ assert_eq!(2, aiocb.iovlen());
+ assert_eq!(2, aiocb.offset());
+ assert_eq!(42, aiocb.priority());
+ let sev = aiocb.sigevent().sigevent();
+ assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+ assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+ }
+
+ // Test a simple aio operation with no completion notification. We must
+ // poll for completion.
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn ok() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let wbuf0 = b"BC";
+ let wbuf1 = b"DEF";
+ let wbufs = [IoSlice::new(wbuf0), IoSlice::new(wbuf1)];
+ let wlen = wbuf0.len() + wbuf1.len();
+ let mut rbuf = Vec::new();
+ const EXPECT: &[u8] = b"aBCDEF123456";
+
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aiow = Box::pin(AioWritev::new(
+ f.as_raw_fd(),
+ 1,
+ &wbufs,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ aiow.as_mut().submit().unwrap();
+
+ let err = poll_aio!(&mut aiow);
+ assert_eq!(err, Ok(()));
+ assert_eq!(aiow.as_mut().aio_return().unwrap(), wlen);
+
+ f.rewind().unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert_eq!(len, EXPECT.len());
+ assert_eq!(rbuf, EXPECT);
+ }
+}
+
+// Test an aio operation with completion delivered by a signal
+#[test]
+#[cfg_attr(
+ any(
+ all(target_env = "musl", target_arch = "x86_64"),
+ target_arch = "mips",
+ target_arch = "mips64"
+ ),
+ ignore
+)]
+fn sigev_signal() {
+ let _m = crate::SIGNAL_MTX.lock();
+ let sa = SigAction::new(
+ SigHandler::Handler(sigfunc),
+ SaFlags::SA_RESETHAND,
+ SigSet::empty(),
+ );
+ SIGNALED.store(false, Ordering::Relaxed);
+ unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
+
+ const INITIAL: &[u8] = b"abcdef123456";
+ const WBUF: &[u8] = b"CDEF";
+ let mut rbuf = Vec::new();
+ const EXPECT: &[u8] = b"abCDEF123456";
+
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aiow = Box::pin(AioWrite::new(
+ f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 0, //TODO: validate in sigfunc
+ },
+ ));
+ aiow.as_mut().submit().unwrap();
+ while !SIGNALED.load(Ordering::Relaxed) {
+ thread::sleep(time::Duration::from_millis(10));
+ }
+
+ assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+ f.rewind().unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert_eq!(len, EXPECT.len());
+ assert_eq!(rbuf, EXPECT);
+}
+
+// Tests using aio_cancel_all for all outstanding IOs.
+#[test]
+#[cfg_attr(target_env = "musl", ignore)]
+fn test_aio_cancel_all() {
+ let wbuf: &[u8] = b"CDEF";
+
+ let f = tempfile().unwrap();
+ let mut aiocb = Box::pin(AioWrite::new(
+ f.as_raw_fd(),
+ 0, //offset
+ wbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ ));
+ aiocb.as_mut().submit().unwrap();
+ let err = aiocb.as_mut().error();
+ assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
+
+ aio_cancel_all(f.as_raw_fd()).unwrap();
+
+ // Wait for aiocb to complete, but don't care whether it succeeded
+ let _ = poll_aio!(&mut aiocb);
+ let _ = aiocb.as_mut().aio_return();
+}
+
+#[test]
+// On Cirrus on Linux, this test fails due to a glibc bug.
+// https://github.com/nix-rust/nix/issues/1099
+#[cfg_attr(target_os = "linux", ignore)]
+// On Cirrus, aio_suspend is failing with EINVAL
+// https://github.com/nix-rust/nix/issues/1361
+#[cfg_attr(target_os = "macos", 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 = Box::pin(AioWrite::new(
+ f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ ));
+
+ let mut rcb = Box::pin(AioRead::new(
+ f.as_raw_fd(),
+ 8, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ ));
+ wcb.as_mut().submit().unwrap();
+ rcb.as_mut().submit().unwrap();
+ loop {
+ {
+ let cbbuf = [
+ &*wcb as &dyn AsRef<libc::aiocb>,
+ &*rcb as &dyn AsRef<libc::aiocb>,
+ ];
+ let r = aio_suspend(&cbbuf[..], Some(timeout));
+ match r {
+ Err(Errno::EINTR) => continue,
+ Err(e) => panic!("aio_suspend returned {e:?}"),
+ Ok(_) => (),
+ };
+ }
+ if rcb.as_mut().error() != Err(Errno::EINPROGRESS)
+ && wcb.as_mut().error() != Err(Errno::EINPROGRESS)
+ {
+ break;
+ }
+ }
+
+ assert_eq!(wcb.as_mut().aio_return().unwrap(), WBUF.len());
+ assert_eq!(rcb.as_mut().aio_return().unwrap(), rlen);
+}
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..bbe6623fd7
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_aio_drop.rs
@@ -0,0 +1,35 @@
+// Test dropping an AioCb that hasn't yet finished.
+// This must happen in its own process, because on OSX this test seems to hose
+// the AIO subsystem and causes subsequent tests to fail
+#[test]
+#[should_panic(expected = "Dropped an in-progress AioCb")]
+#[cfg(all(
+ not(target_env = "musl"),
+ not(target_env = "uclibc"),
+ 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 = Box::pin(AioWrite::new(
+ f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ ));
+ aiocb.as_mut().submit().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..84b100c1e9
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_epoll.rs
@@ -0,0 +1,26 @@
+#![allow(deprecated)]
+
+use nix::errno::Errno;
+use nix::sys::epoll::{epoll_create1, epoll_ctl};
+use nix::sys::epoll::{EpollCreateFlags, EpollEvent, EpollFlags, EpollOp};
+
+#[test]
+pub fn test_epoll_errno() {
+ let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
+ let result = epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None);
+ result.expect_err("assertion failed");
+ assert_eq!(result.unwrap_err(), Errno::ENOENT);
+
+ let result = epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, None);
+ result.expect_err("assertion failed");
+ assert_eq!(result.unwrap_err(), Errno::EINVAL);
+}
+
+#[test]
+pub fn test_epoll_ctl() {
+ let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
+ let mut event =
+ EpollEvent::new(EpollFlags::EPOLLIN | EpollFlags::EPOLLERR, 1);
+ epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, &mut event).unwrap();
+ epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None).unwrap();
+}
diff --git a/third_party/rust/nix/test/sys/test_inotify.rs b/third_party/rust/nix/test/sys/test_inotify.rs
new file mode 100644
index 0000000000..bb5851a903
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_inotify.rs
@@ -0,0 +1,65 @@
+use nix::errno::Errno;
+use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify};
+use std::ffi::OsString;
+use std::fs::{rename, File};
+
+#[test]
+pub fn test_inotify() {
+ let instance = Inotify::init(InitFlags::IN_NONBLOCK).unwrap();
+ let tempdir = tempfile::tempdir().unwrap();
+
+ instance
+ .add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS)
+ .unwrap();
+
+ let events = instance.read_events();
+ assert_eq!(events.unwrap_err(), Errno::EAGAIN);
+
+ File::create(tempdir.path().join("test")).unwrap();
+
+ let events = instance.read_events().unwrap();
+ assert_eq!(events[0].name, Some(OsString::from("test")));
+}
+
+#[test]
+pub fn test_inotify_multi_events() {
+ let instance = Inotify::init(InitFlags::IN_NONBLOCK).unwrap();
+ let tempdir = tempfile::tempdir().unwrap();
+
+ instance
+ .add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS)
+ .unwrap();
+
+ let events = instance.read_events();
+ assert_eq!(events.unwrap_err(), Errno::EAGAIN);
+
+ File::create(tempdir.path().join("test")).unwrap();
+ rename(tempdir.path().join("test"), tempdir.path().join("test2")).unwrap();
+
+ // Now there should be 5 events in queue:
+ // - IN_CREATE on test
+ // - IN_OPEN on test
+ // - IN_CLOSE_WRITE on test
+ // - IN_MOVED_FROM on test with a cookie
+ // - IN_MOVED_TO on test2 with the same cookie
+
+ let events = instance.read_events().unwrap();
+ assert_eq!(events.len(), 5);
+
+ assert_eq!(events[0].mask, AddWatchFlags::IN_CREATE);
+ assert_eq!(events[0].name, Some(OsString::from("test")));
+
+ assert_eq!(events[1].mask, AddWatchFlags::IN_OPEN);
+ assert_eq!(events[1].name, Some(OsString::from("test")));
+
+ assert_eq!(events[2].mask, AddWatchFlags::IN_CLOSE_WRITE);
+ assert_eq!(events[2].name, Some(OsString::from("test")));
+
+ assert_eq!(events[3].mask, AddWatchFlags::IN_MOVED_FROM);
+ assert_eq!(events[3].name, Some(OsString::from("test")));
+
+ assert_eq!(events[4].mask, AddWatchFlags::IN_MOVED_TO);
+ assert_eq!(events[4].name, Some(OsString::from("test2")));
+
+ assert_eq!(events[3].cookie, events[4].cookie);
+}
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..40f60cfdbc
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_ioctl.rs
@@ -0,0 +1,376 @@
+#![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 {
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ #[test]
+ fn test_op_none() {
+ if cfg!(any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc",
+ target_arch = "powerpc64"
+ )) {
+ assert_eq!(request_code_none!(b'q', 10) as u32, 0x2000_710A);
+ assert_eq!(request_code_none!(b'a', 255) as u32, 0x2000_61FF);
+ } else {
+ assert_eq!(request_code_none!(b'q', 10) as u32, 0x0000_710A);
+ assert_eq!(request_code_none!(b'a', 255) as u32, 0x0000_61FF);
+ }
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ #[test]
+ fn test_op_write() {
+ if cfg!(any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc",
+ target_arch = "powerpc64"
+ )) {
+ assert_eq!(request_code_write!(b'z', 10, 1) as u32, 0x8001_7A0A);
+ assert_eq!(request_code_write!(b'z', 10, 512) as u32, 0x8200_7A0A);
+ } else {
+ assert_eq!(request_code_write!(b'z', 10, 1) as u32, 0x4001_7A0A);
+ assert_eq!(request_code_write!(b'z', 10, 512) as u32, 0x4200_7A0A);
+ }
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_write_64() {
+ if cfg!(any(target_arch = "mips64", target_arch = "powerpc64")) {
+ assert_eq!(
+ request_code_write!(b'z', 10, 1u64 << 32) as u32,
+ 0x8000_7A0A
+ );
+ } else {
+ assert_eq!(
+ request_code_write!(b'z', 10, 1u64 << 32) as u32,
+ 0x4000_7A0A
+ );
+ }
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ #[test]
+ fn test_op_read() {
+ if cfg!(any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc",
+ target_arch = "powerpc64"
+ )) {
+ assert_eq!(request_code_read!(b'z', 10, 1) as u32, 0x4001_7A0A);
+ assert_eq!(request_code_read!(b'z', 10, 512) as u32, 0x4200_7A0A);
+ } else {
+ assert_eq!(request_code_read!(b'z', 10, 1) as u32, 0x8001_7A0A);
+ assert_eq!(request_code_read!(b'z', 10, 512) as u32, 0x8200_7A0A);
+ }
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_read_64() {
+ if cfg!(any(target_arch = "mips64", target_arch = "powerpc64")) {
+ assert_eq!(
+ request_code_read!(b'z', 10, 1u64 << 32) as u32,
+ 0x4000_7A0A
+ );
+ } else {
+ assert_eq!(
+ request_code_read!(b'z', 10, 1u64 << 32) as u32,
+ 0x8000_7A0A
+ );
+ }
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ #[test]
+ fn test_op_read_write() {
+ assert_eq!(request_code_readwrite!(b'z', 10, 1) as u32, 0xC001_7A0A);
+ assert_eq!(request_code_readwrite!(b'z', 10, 512) as u32, 0xC200_7A0A);
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_read_write_64() {
+ assert_eq!(
+ request_code_readwrite!(b'z', 10, 1u64 << 32) as u32,
+ 0xC000_7A0A
+ );
+ }
+}
+
+#[cfg(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, 1u64 << 32), 0x8000_7A0A);
+ }
+
+ #[test]
+ fn test_op_read() {
+ assert_eq!(request_code_read!(b'z', 10, 1), 0x4001_7A0A);
+ assert_eq!(request_code_read!(b'z', 10, 512), 0x4200_7A0A);
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_read_64() {
+ assert_eq!(request_code_read!(b'z', 10, 1u64 << 32), 0x4000_7A0A);
+ }
+
+ #[test]
+ fn test_op_read_write() {
+ assert_eq!(request_code_readwrite!(b'z', 10, 1), 0xC001_7A0A);
+ assert_eq!(request_code_readwrite!(b'z', 10, 512), 0xC200_7A0A);
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_read_write_64() {
+ assert_eq!(request_code_readwrite!(b'z', 10, 1u64 << 32), 0xC000_7A0A);
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod linux_ioctls {
+ use std::mem;
+ use std::os::unix::io::AsRawFd;
+
+ use libc::{termios, TCGETS, TCSBRK, TCSETS, TIOCNXCL};
+ use tempfile::tempfile;
+
+ use nix::errno::Errno;
+
+ ioctl_none_bad!(tiocnxcl, TIOCNXCL);
+ #[test]
+ fn test_ioctl_none_bad() {
+ let file = tempfile().unwrap();
+ let res = unsafe { tiocnxcl(file.as_raw_fd()) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+
+ ioctl_read_bad!(tcgets, TCGETS, termios);
+ #[test]
+ fn test_ioctl_read_bad() {
+ let file = tempfile().unwrap();
+ let mut termios = unsafe { mem::zeroed() };
+ let res = unsafe { tcgets(file.as_raw_fd(), &mut termios) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+
+ ioctl_write_int_bad!(tcsbrk, TCSBRK);
+ #[test]
+ fn test_ioctl_write_int_bad() {
+ let file = tempfile().unwrap();
+ let res = unsafe { tcsbrk(file.as_raw_fd(), 0) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+
+ ioctl_write_ptr_bad!(tcsets, TCSETS, termios);
+ #[test]
+ fn test_ioctl_write_ptr_bad() {
+ let file = tempfile().unwrap();
+ let termios: termios = unsafe { mem::zeroed() };
+ let res = unsafe { tcsets(file.as_raw_fd(), &termios) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+
+ // FIXME: Find a suitable example for `ioctl_readwrite_bad`
+
+ // From linux/videodev2.h
+ ioctl_none!(log_status, b'V', 70);
+ #[test]
+ fn test_ioctl_none() {
+ let file = tempfile().unwrap();
+ let res = unsafe { log_status(file.as_raw_fd()) };
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
+ }
+
+ #[repr(C)]
+ pub struct v4l2_audio {
+ index: u32,
+ name: [u8; 32],
+ capability: u32,
+ mode: u32,
+ reserved: [u32; 2],
+ }
+
+ // From linux/videodev2.h
+ ioctl_write_ptr!(s_audio, b'V', 34, v4l2_audio);
+ #[test]
+ fn test_ioctl_write_ptr() {
+ let file = tempfile().unwrap();
+ let data: v4l2_audio = unsafe { mem::zeroed() };
+ let res = unsafe { s_audio(file.as_raw_fd(), &data) };
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
+ }
+
+ // From linux/net/bluetooth/hci_sock.h
+ const HCI_IOC_MAGIC: u8 = b'H';
+ const HCI_IOC_HCIDEVUP: u8 = 201;
+ ioctl_write_int!(hcidevup, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP);
+ #[test]
+ fn test_ioctl_write_int() {
+ let file = tempfile().unwrap();
+ let res = unsafe { hcidevup(file.as_raw_fd(), 0) };
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
+ }
+
+ // From linux/videodev2.h
+ ioctl_read!(g_audio, b'V', 33, v4l2_audio);
+ #[test]
+ fn test_ioctl_read() {
+ let file = tempfile().unwrap();
+ let mut data: v4l2_audio = unsafe { mem::zeroed() };
+ let res = unsafe { g_audio(file.as_raw_fd(), &mut data) };
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
+ }
+
+ // From linux/videodev2.h
+ ioctl_readwrite!(enum_audio, b'V', 65, v4l2_audio);
+ #[test]
+ fn test_ioctl_readwrite() {
+ let file = tempfile().unwrap();
+ let mut data: v4l2_audio = unsafe { mem::zeroed() };
+ let res = unsafe { enum_audio(file.as_raw_fd(), &mut data) };
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
+ }
+
+ // FIXME: Find a suitable example for `ioctl_read_buf`.
+
+ #[repr(C)]
+ pub struct spi_ioc_transfer {
+ tx_buf: u64,
+ rx_buf: u64,
+ len: u32,
+ speed_hz: u32,
+ delay_usecs: u16,
+ bits_per_word: u8,
+ cs_change: u8,
+ tx_nbits: u8,
+ rx_nbits: u8,
+ pad: u16,
+ }
+
+ // From linux/spi/spidev.h
+ ioctl_write_buf!(
+ spi_ioc_message,
+ super::SPI_IOC_MAGIC,
+ super::SPI_IOC_MESSAGE,
+ spi_ioc_transfer
+ );
+ #[test]
+ fn test_ioctl_write_buf() {
+ let file = tempfile().unwrap();
+ let data: [spi_ioc_transfer; 4] = unsafe { mem::zeroed() };
+ let res = unsafe { spi_ioc_message(file.as_raw_fd(), &data[..]) };
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
+ }
+
+ // FIXME: Find a suitable example for `ioctl_readwrite_buf`.
+}
+
+#[cfg(target_os = "freebsd")]
+mod freebsd_ioctls {
+ use std::mem;
+ use std::os::unix::io::AsRawFd;
+
+ use libc::termios;
+ use tempfile::tempfile;
+
+ use nix::errno::Errno;
+
+ // From sys/sys/ttycom.h
+ const TTY_IOC_MAGIC: u8 = b't';
+ const TTY_IOC_TYPE_NXCL: u8 = 14;
+ const TTY_IOC_TYPE_GETA: u8 = 19;
+ const TTY_IOC_TYPE_SETA: u8 = 20;
+
+ ioctl_none!(tiocnxcl, TTY_IOC_MAGIC, TTY_IOC_TYPE_NXCL);
+ #[test]
+ fn test_ioctl_none() {
+ let file = tempfile().unwrap();
+ let res = unsafe { tiocnxcl(file.as_raw_fd()) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+
+ ioctl_read!(tiocgeta, TTY_IOC_MAGIC, TTY_IOC_TYPE_GETA, termios);
+ #[test]
+ fn test_ioctl_read() {
+ let file = tempfile().unwrap();
+ let mut termios = unsafe { mem::zeroed() };
+ let res = unsafe { tiocgeta(file.as_raw_fd(), &mut termios) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+
+ ioctl_write_ptr!(tiocseta, TTY_IOC_MAGIC, TTY_IOC_TYPE_SETA, termios);
+ #[test]
+ fn test_ioctl_write_ptr() {
+ let file = tempfile().unwrap();
+ let termios: termios = unsafe { mem::zeroed() };
+ let res = unsafe { tiocseta(file.as_raw_fd(), &termios) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+}
diff --git a/third_party/rust/nix/test/sys/test_mman.rs b/third_party/rust/nix/test/sys/test_mman.rs
new file mode 100644
index 0000000000..b4674e53fa
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_mman.rs
@@ -0,0 +1,122 @@
+use nix::sys::mman::{mmap, MapFlags, ProtFlags};
+use std::{num::NonZeroUsize, os::unix::io::BorrowedFd};
+
+#[test]
+fn test_mmap_anonymous() {
+ unsafe {
+ let ptr = mmap::<BorrowedFd>(
+ None,
+ NonZeroUsize::new(1).unwrap(),
+ ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
+ MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
+ None,
+ 0,
+ )
+ .unwrap() as *mut u8;
+ assert_eq!(*ptr, 0x00u8);
+ *ptr = 0xffu8;
+ assert_eq!(*ptr, 0xffu8);
+ }
+}
+
+#[test]
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
+fn test_mremap_grow() {
+ use nix::libc::{c_void, size_t};
+ use nix::sys::mman::{mremap, MRemapFlags};
+
+ const ONE_K: size_t = 1024;
+ let one_k_non_zero = NonZeroUsize::new(ONE_K).unwrap();
+
+ let slice: &mut [u8] = unsafe {
+ let mem = mmap::<BorrowedFd>(
+ None,
+ one_k_non_zero,
+ ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
+ MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
+ None,
+ 0,
+ )
+ .unwrap();
+ std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
+ };
+ assert_eq!(slice[ONE_K - 1], 0x00);
+ slice[ONE_K - 1] = 0xFF;
+ assert_eq!(slice[ONE_K - 1], 0xFF);
+
+ let slice: &mut [u8] = unsafe {
+ #[cfg(target_os = "linux")]
+ let mem = mremap(
+ slice.as_mut_ptr() as *mut c_void,
+ ONE_K,
+ 10 * ONE_K,
+ MRemapFlags::MREMAP_MAYMOVE,
+ None,
+ )
+ .unwrap();
+ #[cfg(target_os = "netbsd")]
+ let mem = mremap(
+ slice.as_mut_ptr() as *mut c_void,
+ ONE_K,
+ 10 * ONE_K,
+ MRemapFlags::MAP_REMAPDUP,
+ None,
+ )
+ .unwrap();
+ std::slice::from_raw_parts_mut(mem as *mut u8, 10 * ONE_K)
+ };
+
+ // The first KB should still have the old data in it.
+ assert_eq!(slice[ONE_K - 1], 0xFF);
+
+ // The additional range should be zero-init'd and accessible.
+ assert_eq!(slice[10 * ONE_K - 1], 0x00);
+ slice[10 * ONE_K - 1] = 0xFF;
+ assert_eq!(slice[10 * ONE_K - 1], 0xFF);
+}
+
+#[test]
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
+// Segfaults for unknown reasons under QEMU for 32-bit targets
+#[cfg_attr(all(target_pointer_width = "32", qemu), ignore)]
+fn test_mremap_shrink() {
+ use nix::libc::{c_void, size_t};
+ use nix::sys::mman::{mremap, MRemapFlags};
+ use std::num::NonZeroUsize;
+
+ const ONE_K: size_t = 1024;
+ let ten_one_k = NonZeroUsize::new(10 * ONE_K).unwrap();
+ let slice: &mut [u8] = unsafe {
+ let mem = mmap::<BorrowedFd>(
+ None,
+ ten_one_k,
+ ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
+ MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
+ None,
+ 0,
+ )
+ .unwrap();
+ std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
+ };
+ assert_eq!(slice[ONE_K - 1], 0x00);
+ slice[ONE_K - 1] = 0xFF;
+ assert_eq!(slice[ONE_K - 1], 0xFF);
+
+ let slice: &mut [u8] = unsafe {
+ let mem = mremap(
+ slice.as_mut_ptr() as *mut c_void,
+ ten_one_k.into(),
+ ONE_K,
+ MRemapFlags::empty(),
+ None,
+ )
+ .unwrap();
+ // Since we didn't supply MREMAP_MAYMOVE, the address should be the
+ // same.
+ assert_eq!(mem, slice.as_mut_ptr() as *mut c_void);
+ std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
+ };
+
+ // The first KB should still be accessible and have the old data in it.
+ assert_eq!(slice[ONE_K - 1], 0xFF);
+}
diff --git a/third_party/rust/nix/test/sys/test_prctl.rs b/third_party/rust/nix/test/sys/test_prctl.rs
new file mode 100644
index 0000000000..351213b7ef
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_prctl.rs
@@ -0,0 +1,125 @@
+#[cfg(target_os = "linux")]
+#[cfg(feature = "process")]
+mod test_prctl {
+ use std::ffi::CStr;
+
+ use nix::sys::prctl;
+
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_get_set_subreaper() {
+ let original = prctl::get_child_subreaper().unwrap();
+
+ prctl::set_child_subreaper(true).unwrap();
+ let subreaper = prctl::get_child_subreaper().unwrap();
+ assert!(subreaper);
+
+ prctl::set_child_subreaper(original).unwrap();
+ }
+
+ #[test]
+ fn test_get_set_dumpable() {
+ let original = prctl::get_dumpable().unwrap();
+
+ prctl::set_dumpable(false).unwrap();
+ let dumpable = prctl::get_dumpable().unwrap();
+ assert!(!dumpable);
+
+ prctl::set_dumpable(original).unwrap();
+ }
+
+ #[test]
+ fn test_get_set_keepcaps() {
+ let original = prctl::get_keepcaps().unwrap();
+
+ prctl::set_keepcaps(true).unwrap();
+ let keepcaps = prctl::get_keepcaps().unwrap();
+ assert!(keepcaps);
+
+ prctl::set_keepcaps(original).unwrap();
+ }
+
+ #[test]
+ fn test_get_set_clear_mce_kill() {
+ use prctl::PrctlMCEKillPolicy::*;
+
+ prctl::set_mce_kill(PR_MCE_KILL_LATE).unwrap();
+ let mce = prctl::get_mce_kill().unwrap();
+ assert_eq!(mce, PR_MCE_KILL_LATE);
+
+ prctl::clear_mce_kill().unwrap();
+ let mce = prctl::get_mce_kill().unwrap();
+ assert_eq!(mce, PR_MCE_KILL_DEFAULT);
+ }
+
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_get_set_pdeathsig() {
+ use nix::sys::signal::Signal;
+
+ let original = prctl::get_pdeathsig().unwrap();
+
+ prctl::set_pdeathsig(Signal::SIGUSR1).unwrap();
+ let sig = prctl::get_pdeathsig().unwrap();
+ assert_eq!(sig, Some(Signal::SIGUSR1));
+
+ prctl::set_pdeathsig(original).unwrap();
+ }
+
+ #[test]
+ fn test_get_set_name() {
+ let original = prctl::get_name().unwrap();
+
+ let long_name =
+ CStr::from_bytes_with_nul(b"0123456789abcdefghijklmn\0").unwrap();
+ prctl::set_name(long_name).unwrap();
+ let res = prctl::get_name().unwrap();
+
+ // name truncated by kernel to TASK_COMM_LEN
+ assert_eq!(&long_name.to_str().unwrap()[..15], res.to_str().unwrap());
+
+ let short_name = CStr::from_bytes_with_nul(b"01234567\0").unwrap();
+ prctl::set_name(short_name).unwrap();
+ let res = prctl::get_name().unwrap();
+ assert_eq!(short_name.to_str().unwrap(), res.to_str().unwrap());
+
+ prctl::set_name(&original).unwrap();
+ }
+
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_get_set_timerslack() {
+ let original = prctl::get_timerslack().unwrap();
+
+ let slack = 60_000;
+ prctl::set_timerslack(slack).unwrap();
+ let res = prctl::get_timerslack().unwrap();
+ assert_eq!(slack, res as u64);
+
+ prctl::set_timerslack(original as u64).unwrap();
+ }
+
+ #[test]
+ fn test_disable_enable_perf_events() {
+ prctl::task_perf_events_disable().unwrap();
+ prctl::task_perf_events_enable().unwrap();
+ }
+
+ #[test]
+ fn test_get_set_no_new_privs() {
+ prctl::set_no_new_privs().unwrap();
+ let no_new_privs = prctl::get_no_new_privs().unwrap();
+ assert!(no_new_privs);
+ }
+
+ #[test]
+ fn test_get_set_thp_disable() {
+ let original = prctl::get_thp_disable().unwrap();
+
+ prctl::set_thp_disable(true).unwrap();
+ let thp_disable = prctl::get_thp_disable().unwrap();
+ assert!(thp_disable);
+
+ prctl::set_thp_disable(original).unwrap();
+ }
+}
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..ce048bae60
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_pthread.rs
@@ -0,0 +1,22 @@
+use nix::sys::pthread::*;
+
+#[cfg(any(target_env = "musl", target_os = "redox"))]
+#[test]
+fn test_pthread_self() {
+ let tid = pthread_self();
+ assert!(!tid.is_null());
+}
+
+#[cfg(not(any(target_env = "musl", target_os = "redox")))]
+#[test]
+fn test_pthread_self() {
+ let tid = pthread_self();
+ assert_ne!(tid, 0);
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_pthread_kill_none() {
+ pthread_kill(pthread_self(), None)
+ .expect("Should be able to send signal to my thread.");
+}
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..530560fe17
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_ptrace.rs
@@ -0,0 +1,275 @@
+#[cfg(all(
+ target_os = "linux",
+ any(target_arch = "x86_64", target_arch = "x86"),
+ target_env = "gnu"
+))]
+use memoffset::offset_of;
+use nix::errno::Errno;
+use nix::sys::ptrace;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use nix::sys::ptrace::Options;
+use nix::unistd::getpid;
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use std::mem;
+
+use crate::*;
+
+#[test]
+fn test_ptrace() {
+ // Just make sure ptrace can be called at all, for now.
+ // FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS
+ require_capability!("test_ptrace", CAP_SYS_PTRACE);
+ let err = ptrace::attach(getpid()).unwrap_err();
+ assert!(
+ err == Errno::EPERM || err == Errno::EINVAL || err == Errno::ENOSYS
+ );
+}
+
+// Just make sure ptrace_setoptions can be called at all, for now.
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_ptrace_setoptions() {
+ require_capability!("test_ptrace_setoptions", CAP_SYS_PTRACE);
+ let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD)
+ .unwrap_err();
+ assert_ne!(err, Errno::EOPNOTSUPP);
+}
+
+// Just make sure ptrace_getevent can be called at all, for now.
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_ptrace_getevent() {
+ require_capability!("test_ptrace_getevent", CAP_SYS_PTRACE);
+ let err = ptrace::getevent(getpid()).unwrap_err();
+ assert_ne!(err, Errno::EOPNOTSUPP);
+}
+
+// Just make sure ptrace_getsiginfo can be called at all, for now.
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_ptrace_getsiginfo() {
+ require_capability!("test_ptrace_getsiginfo", CAP_SYS_PTRACE);
+ if let Err(Errno::EOPNOTSUPP) = ptrace::getsiginfo(getpid()) {
+ panic!("ptrace_getsiginfo returns Errno::EOPNOTSUPP!");
+ }
+}
+
+// Just make sure ptrace_setsiginfo can be called at all, for now.
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_ptrace_setsiginfo() {
+ require_capability!("test_ptrace_setsiginfo", CAP_SYS_PTRACE);
+ let siginfo = unsafe { mem::zeroed() };
+ if let Err(Errno::EOPNOTSUPP) = ptrace::setsiginfo(getpid(), &siginfo) {
+ panic!("ptrace_setsiginfo returns Errno::EOPNOTSUPP!");
+ }
+}
+
+#[test]
+fn test_ptrace_cont() {
+ use nix::sys::ptrace;
+ use nix::sys::signal::{raise, Signal};
+ use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
+ use nix::unistd::fork;
+ use nix::unistd::ForkResult::*;
+
+ require_capability!("test_ptrace_cont", CAP_SYS_PTRACE);
+
+ let _m = crate::FORK_MTX.lock();
+
+ // FIXME: qemu-user doesn't implement ptrace on all architectures
+ // and returns ENOSYS in this case.
+ // We (ab)use this behavior to detect the affected platforms
+ // and skip the test then.
+ // On valid platforms the ptrace call should return Errno::EPERM, this
+ // is already tested by `test_ptrace`.
+ let err = ptrace::attach(getpid()).unwrap_err();
+ if err == Errno::ENOSYS {
+ return;
+ }
+
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => {
+ ptrace::traceme().unwrap();
+ // As recommended by ptrace(2), raise SIGTRAP to pause the child
+ // until the parent is ready to continue
+ loop {
+ raise(Signal::SIGTRAP).unwrap();
+ }
+ }
+ Parent { child } => {
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
+ );
+ ptrace::cont(child, None).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
+ );
+ ptrace::cont(child, Some(Signal::SIGKILL)).unwrap();
+ match waitpid(child, None) {
+ Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
+ if pid == child =>
+ {
+ // FIXME It's been observed on some systems (apple) the
+ // tracee may not be killed but remain as a zombie process
+ // affecting other wait based tests. Add an extra kill just
+ // to make sure there are no zombies.
+ let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
+ while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() {
+ let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
+ }
+ }
+ _ => panic!("The process should have been killed"),
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "linux")]
+#[test]
+fn test_ptrace_interrupt() {
+ use nix::sys::ptrace;
+ use nix::sys::signal::Signal;
+ use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
+ use nix::unistd::fork;
+ use nix::unistd::ForkResult::*;
+ use std::thread::sleep;
+ use std::time::Duration;
+
+ require_capability!("test_ptrace_interrupt", CAP_SYS_PTRACE);
+
+ let _m = crate::FORK_MTX.lock();
+
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => loop {
+ sleep(Duration::from_millis(1000));
+ },
+ Parent { child } => {
+ ptrace::seize(child, ptrace::Options::PTRACE_O_TRACESYSGOOD)
+ .unwrap();
+ ptrace::interrupt(child).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::PtraceEvent(child, Signal::SIGTRAP, 128))
+ );
+ ptrace::syscall(child, None).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::PtraceSyscall(child))
+ );
+ ptrace::detach(child, Some(Signal::SIGKILL)).unwrap();
+ match waitpid(child, None) {
+ Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
+ if pid == child =>
+ {
+ let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
+ while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() {
+ let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
+ }
+ }
+ _ => panic!("The process should have been killed"),
+ }
+ }
+ }
+}
+
+// ptrace::{setoptions, getregs} are only available in these platforms
+#[cfg(all(
+ target_os = "linux",
+ any(target_arch = "x86_64", target_arch = "x86"),
+ target_env = "gnu"
+))]
+#[test]
+fn test_ptrace_syscall() {
+ use nix::sys::ptrace;
+ use nix::sys::signal::kill;
+ use nix::sys::signal::Signal;
+ use nix::sys::wait::{waitpid, WaitStatus};
+ use nix::unistd::fork;
+ use nix::unistd::getpid;
+ use nix::unistd::ForkResult::*;
+
+ require_capability!("test_ptrace_syscall", CAP_SYS_PTRACE);
+
+ let _m = crate::FORK_MTX.lock();
+
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => {
+ ptrace::traceme().unwrap();
+ // first sigstop until parent is ready to continue
+ let pid = getpid();
+ kill(pid, Signal::SIGSTOP).unwrap();
+ kill(pid, Signal::SIGTERM).unwrap();
+ unsafe {
+ ::libc::_exit(0);
+ }
+ }
+
+ Parent { child } => {
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::Stopped(child, Signal::SIGSTOP))
+ );
+
+ // set this option to recognize syscall-stops
+ ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACESYSGOOD)
+ .unwrap();
+
+ #[cfg(target_arch = "x86_64")]
+ let get_syscall_id =
+ || ptrace::getregs(child).unwrap().orig_rax as libc::c_long;
+
+ #[cfg(target_arch = "x86")]
+ let get_syscall_id =
+ || ptrace::getregs(child).unwrap().orig_eax as libc::c_long;
+
+ // this duplicates `get_syscall_id` for the purpose of testing `ptrace::read_user`.
+ #[cfg(target_arch = "x86_64")]
+ let rax_offset = offset_of!(libc::user_regs_struct, orig_rax);
+ #[cfg(target_arch = "x86")]
+ let rax_offset = offset_of!(libc::user_regs_struct, orig_eax);
+
+ let get_syscall_from_user_area = || {
+ // Find the offset of `user.regs.rax` (or `user.regs.eax` for x86)
+ let rax_offset = offset_of!(libc::user, regs) + rax_offset;
+ ptrace::read_user(child, rax_offset as _).unwrap()
+ as libc::c_long
+ };
+
+ // kill entry
+ ptrace::syscall(child, None).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::PtraceSyscall(child))
+ );
+ assert_eq!(get_syscall_id(), ::libc::SYS_kill);
+ assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
+
+ // kill exit
+ ptrace::syscall(child, None).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::PtraceSyscall(child))
+ );
+ assert_eq!(get_syscall_id(), ::libc::SYS_kill);
+ assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
+
+ // receive signal
+ ptrace::syscall(child, None).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::Stopped(child, Signal::SIGTERM))
+ );
+
+ // inject signal
+ ptrace::syscall(child, Signal::SIGTERM).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::Signaled(child, Signal::SIGTERM, false))
+ );
+ }
+ }
+}
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..79f75de3b4
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_select.rs
@@ -0,0 +1,82 @@
+use nix::sys::select::*;
+use nix::sys::signal::SigSet;
+use nix::sys::time::{TimeSpec, TimeValLike};
+use nix::unistd::{pipe, write};
+use std::os::unix::io::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd};
+
+#[test]
+pub fn test_pselect() {
+ let _mtx = crate::SIGNAL_MTX.lock();
+
+ let (r1, w1) = pipe().unwrap();
+ write(w1, b"hi!").unwrap();
+ let r1 = unsafe { OwnedFd::from_raw_fd(r1) };
+ let (r2, _w2) = pipe().unwrap();
+ let r2 = unsafe { OwnedFd::from_raw_fd(r2) };
+
+ 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 r1 = unsafe { OwnedFd::from_raw_fd(r1) };
+ let (r2, _w2) = pipe().unwrap();
+ let r2 = unsafe { OwnedFd::from_raw_fd(r2) };
+
+ 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.as_raw_fd(), r2.as_raw_fd()) + 1,
+ &mut fd_set,
+ None,
+ None,
+ &timeout,
+ None
+ )
+ .unwrap()
+ );
+ assert!(fd_set.contains(&r1));
+ assert!(!fd_set.contains(&r2));
+}
+
+macro_rules! generate_fdset_bad_fd_tests {
+ ($fd:expr, $($method:ident),* $(,)?) => {
+ $(
+ #[test]
+ #[should_panic]
+ fn $method() {
+ let bad_fd = unsafe{BorrowedFd::borrow_raw($fd)};
+ FdSet::new().$method(&bad_fd);
+ }
+ )*
+ }
+}
+
+mod test_fdset_too_large_fd {
+ use super::*;
+ use std::convert::TryInto;
+ generate_fdset_bad_fd_tests!(
+ FD_SETSIZE.try_into().unwrap(),
+ insert,
+ remove,
+ contains,
+ );
+}
diff --git a/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..ca25ff9ab0
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_signal.rs
@@ -0,0 +1,143 @@
+#[cfg(not(target_os = "redox"))]
+use nix::errno::Errno;
+use nix::sys::signal::*;
+use nix::unistd::*;
+use std::convert::TryFrom;
+use std::sync::atomic::{AtomicBool, Ordering};
+
+#[test]
+fn test_kill_none() {
+ kill(getpid(), None).expect("Should be able to send signal to myself.");
+}
+
+#[test]
+#[cfg(not(target_os = "fuchsia"))]
+fn test_killpg_none() {
+ killpg(getpgrp(), None)
+ .expect("Should be able to send signal to my process group.");
+}
+
+#[test]
+fn test_old_sigaction_flags() {
+ let _m = crate::SIGNAL_MTX.lock();
+
+ extern "C" fn handler(_: ::libc::c_int) {}
+ let act = SigAction::new(
+ SigHandler::Handler(handler),
+ SaFlags::empty(),
+ SigSet::empty(),
+ );
+ let oact = unsafe { sigaction(SIGINT, &act) }.unwrap();
+ let _flags = oact.flags();
+ let oact = unsafe { sigaction(SIGINT, &act) }.unwrap();
+ let _flags = oact.flags();
+}
+
+#[test]
+fn test_sigprocmask_noop() {
+ sigprocmask(SigmaskHow::SIG_BLOCK, None, None)
+ .expect("this should be an effective noop");
+}
+
+#[test]
+fn test_sigprocmask() {
+ let _m = crate::SIGNAL_MTX.lock();
+
+ // This needs to be a signal that rust doesn't use in the test harness.
+ const SIGNAL: Signal = Signal::SIGCHLD;
+
+ let mut old_signal_set = SigSet::empty();
+ sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
+ .expect("expect to be able to retrieve old signals");
+
+ // Make sure the old set doesn't contain the signal, otherwise the following
+ // test don't make sense.
+ assert!(
+ !old_signal_set.contains(SIGNAL),
+ "the {SIGNAL:?} signal is already blocked, please change to a \
+ different one"
+ );
+
+ // Now block the signal.
+ let mut signal_set = SigSet::empty();
+ signal_set.add(SIGNAL);
+ sigprocmask(SigmaskHow::SIG_BLOCK, Some(&signal_set), None)
+ .expect("expect to be able to block signals");
+
+ // And test it again, to make sure the change was effective.
+ old_signal_set.clear();
+ sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
+ .expect("expect to be able to retrieve old signals");
+ assert!(
+ old_signal_set.contains(SIGNAL),
+ "expected the {SIGNAL:?} to be blocked"
+ );
+
+ // Reset the signal.
+ sigprocmask(SigmaskHow::SIG_UNBLOCK, Some(&signal_set), None)
+ .expect("expect to be able to block signals");
+}
+
+static SIGNALED: AtomicBool = AtomicBool::new(false);
+
+extern "C" fn test_sigaction_handler(signal: libc::c_int) {
+ let signal = Signal::try_from(signal).unwrap();
+ SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
+}
+
+#[cfg(not(target_os = "redox"))]
+extern "C" fn test_sigaction_action(
+ _: libc::c_int,
+ _: *mut libc::siginfo_t,
+ _: *mut libc::c_void,
+) {
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_signal_sigaction() {
+ let _m = crate::SIGNAL_MTX.lock();
+
+ let action_handler = SigHandler::SigAction(test_sigaction_action);
+ assert_eq!(
+ unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(),
+ Errno::ENOTSUP
+ );
+}
+
+#[test]
+fn test_signal() {
+ let _m = crate::SIGNAL_MTX.lock();
+
+ unsafe { signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap();
+ raise(Signal::SIGINT).unwrap();
+ assert_eq!(
+ unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
+ SigHandler::SigIgn
+ );
+
+ let handler = SigHandler::Handler(test_sigaction_handler);
+ assert_eq!(
+ unsafe { signal(Signal::SIGINT, handler) }.unwrap(),
+ SigHandler::SigDfl
+ );
+ raise(Signal::SIGINT).unwrap();
+ assert!(SIGNALED.load(Ordering::Relaxed));
+
+ #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+ assert_eq!(
+ unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
+ handler
+ );
+
+ // System V based OSes (e.g. illumos and Solaris) always resets the
+ // disposition to SIG_DFL prior to calling the signal handler
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ assert_eq!(
+ unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
+ SigHandler::SigDfl
+ );
+
+ // 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..87153c9572
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_signalfd.rs
@@ -0,0 +1,27 @@
+use std::convert::TryFrom;
+
+#[test]
+fn test_signalfd() {
+ use nix::sys::signal::{self, raise, SigSet, Signal};
+ use nix::sys::signalfd::SignalFd;
+
+ // Grab the mutex for altering signals so we don't interfere with other tests.
+ let _m = crate::SIGNAL_MTX.lock();
+
+ // Block the SIGUSR1 signal from automatic processing for this thread
+ let mut mask = SigSet::empty();
+ mask.add(signal::SIGUSR1);
+ mask.thread_block().unwrap();
+
+ let 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::try_from(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..ed1686e87d
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_socket.rs
@@ -0,0 +1,2687 @@
+#[cfg(any(target_os = "linux", target_os = "android"))]
+use crate::*;
+use libc::c_char;
+use nix::sys::socket::{getsockname, AddressFamily, UnixAddr};
+use std::collections::hash_map::DefaultHasher;
+use std::hash::{Hash, Hasher};
+use std::net::{SocketAddrV4, SocketAddrV6};
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::path::Path;
+use std::slice;
+use std::str::FromStr;
+
+#[cfg(target_os = "linux")]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_timestamping() {
+ use nix::sys::socket::{
+ recvmsg, sendmsg, setsockopt, socket, sockopt::Timestamping,
+ ControlMessageOwned, MsgFlags, SockFlag, SockType, SockaddrIn,
+ TimestampingFlag,
+ };
+ use std::io::{IoSlice, IoSliceMut};
+
+ let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap();
+
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ nix::sys::socket::bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+
+ setsockopt(&rsock, Timestamping, &TimestampingFlag::all()).unwrap();
+
+ let sbuf = [0u8; 2048];
+ let mut rbuf = [0u8; 2048];
+ let flags = MsgFlags::empty();
+ let iov1 = [IoSlice::new(&sbuf)];
+ let mut iov2 = [IoSliceMut::new(&mut rbuf)];
+
+ let mut cmsg = cmsg_space!(nix::sys::socket::Timestamps);
+ sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap();
+ let recv =
+ recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, Some(&mut cmsg), flags)
+ .unwrap();
+
+ let mut ts = None;
+ for c in recv.cmsgs() {
+ if let ControlMessageOwned::ScmTimestampsns(timestamps) = c {
+ ts = Some(timestamps.system);
+ }
+ }
+ let ts = ts.expect("ScmTimestampns is present");
+ let sys_time =
+ ::nix::time::clock_gettime(::nix::time::ClockId::CLOCK_REALTIME)
+ .unwrap();
+ let diff = if ts > sys_time {
+ ts - sys_time
+ } else {
+ sys_time - ts
+ };
+ assert!(std::time::Duration::from(diff).as_secs() < 60);
+}
+
+#[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_ptr() as *const c_char, path.len())
+ };
+ assert_eq!(unsafe { &(*addr.as_ptr()).sun_path[..8] }, expect);
+
+ assert_eq!(addr.path(), Some(actual));
+}
+
+fn calculate_hash<T: Hash>(t: &T) -> u64 {
+ let mut s = DefaultHasher::new();
+ t.hash(&mut s);
+ s.finish()
+}
+
+#[test]
+pub fn test_addr_equality_path() {
+ let path = "/foo/bar";
+ let actual = Path::new(path);
+ let addr1 = UnixAddr::new(actual).unwrap();
+ let mut addr2 = addr1;
+
+ unsafe { (*addr2.as_mut_ptr()).sun_path[10] = 127 };
+
+ assert_eq!(addr1, addr2);
+ assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_abstract_sun_path_too_long() {
+ let name = String::from("nix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0testttttnix\0abstract\0test\0make\0sure\0this\0is\0long\0enough");
+ let addr = UnixAddr::new_abstract(name.as_bytes());
+ addr.expect_err("assertion failed");
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_addr_equality_abstract() {
+ let name = String::from("nix\0abstract\0test");
+ let addr1 = UnixAddr::new_abstract(name.as_bytes()).unwrap();
+ let mut addr2 = addr1;
+
+ assert_eq!(addr1, addr2);
+ assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
+
+ unsafe { (*addr2.as_mut_ptr()).sun_path[17] = 127 };
+ assert_ne!(addr1, addr2);
+ assert_ne!(calculate_hash(&addr1), calculate_hash(&addr2));
+}
+
+// Test getting/setting abstract addresses (without unix socket creation)
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_abstract_uds_addr() {
+ let empty = String::new();
+ let addr = UnixAddr::new_abstract(empty.as_bytes()).unwrap();
+ let sun_path: [u8; 0] = [];
+ assert_eq!(addr.as_abstract(), Some(&sun_path[..]));
+
+ let name = String::from("nix\0abstract\0test");
+ let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();
+ let sun_path = [
+ 110u8, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101,
+ 115, 116,
+ ];
+ assert_eq!(addr.as_abstract(), Some(&sun_path[..]));
+ assert_eq!(addr.path(), None);
+
+ // Internally, name is null-prefixed (abstract namespace)
+ assert_eq!(unsafe { (*addr.as_ptr()).sun_path[0] }, 0);
+}
+
+// Test getting an unnamed address (without unix socket creation)
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_unnamed_uds_addr() {
+ use crate::nix::sys::socket::SockaddrLike;
+
+ let addr = UnixAddr::new_unnamed();
+
+ assert!(addr.is_unnamed());
+ assert_eq!(addr.len(), 2);
+ assert!(addr.path().is_none());
+ assert_eq!(addr.path_len(), 0);
+
+ assert!(addr.as_abstract().is_none());
+}
+
+#[test]
+pub fn test_getsockname() {
+ use nix::sys::socket::bind;
+ use nix::sys::socket::{socket, AddressFamily, SockFlag, SockType};
+
+ 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 = UnixAddr::new(&sockname).unwrap();
+ bind(sock.as_raw_fd(), &sockaddr).expect("bind failed");
+ assert_eq!(
+ sockaddr,
+ getsockname(sock.as_raw_fd()).expect("getsockname failed")
+ );
+}
+
+#[test]
+pub fn test_socketpair() {
+ use nix::sys::socket::{socketpair, AddressFamily, SockFlag, SockType};
+ use nix::unistd::{read, write};
+
+ let (fd1, fd2) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ write(fd1.as_raw_fd(), b"hello").unwrap();
+ let mut buf = [0; 5];
+ read(fd2.as_raw_fd(), &mut buf).unwrap();
+
+ assert_eq!(&buf[..], b"hello");
+}
+
+#[test]
+pub fn test_recvmsg_sockaddr_un() {
+ use nix::sys::socket::{
+ self, bind, socket, AddressFamily, MsgFlags, SockFlag, SockType,
+ };
+
+ let tempdir = tempfile::tempdir().unwrap();
+ let sockname = tempdir.path().join("sock");
+ let sock = socket(
+ AddressFamily::Unix,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+ let sockaddr = UnixAddr::new(&sockname).unwrap();
+ bind(sock.as_raw_fd(), &sockaddr).expect("bind failed");
+
+ // Send a message
+ let send_buffer = "hello".as_bytes();
+ if let Err(e) = socket::sendmsg(
+ sock.as_raw_fd(),
+ &[std::io::IoSlice::new(send_buffer)],
+ &[],
+ MsgFlags::empty(),
+ Some(&sockaddr),
+ ) {
+ crate::skip!("Couldn't send ({e:?}), so skipping test");
+ }
+
+ // Receive the message
+ let mut recv_buffer = [0u8; 32];
+ let mut iov = [std::io::IoSliceMut::new(&mut recv_buffer)];
+ let received =
+ socket::recvmsg(sock.as_raw_fd(), &mut iov, None, MsgFlags::empty())
+ .unwrap();
+ // Check the address in the received message
+ assert_eq!(sockaddr, received.address.unwrap());
+}
+
+#[test]
+pub fn test_std_conversions() {
+ use nix::sys::socket::*;
+
+ let std_sa = SocketAddrV4::from_str("127.0.0.1:6789").unwrap();
+ let sock_addr = SockaddrIn::from(std_sa);
+ assert_eq!(std_sa, sock_addr.into());
+
+ let std_sa = SocketAddrV6::from_str("[::1]:6000").unwrap();
+ let sock_addr: SockaddrIn6 = SockaddrIn6::from(std_sa);
+ assert_eq!(std_sa, sock_addr.into());
+}
+
+mod recvfrom {
+ use super::*;
+ use nix::sys::socket::*;
+ use nix::{errno::Errno, Result};
+ use std::thread;
+
+ const MSG: &[u8] = b"Hello, World!";
+
+ fn sendrecv<Fs, Fr>(
+ rsock: RawFd,
+ ssock: RawFd,
+ f_send: Fs,
+ mut f_recv: Fr,
+ ) -> Option<SockaddrStorage>
+ where
+ Fs: Fn(RawFd, &[u8], MsgFlags) -> Result<usize> + Send + 'static,
+ Fr: FnMut(usize, Option<SockaddrStorage>),
+ {
+ let mut buf: [u8; 13] = [0u8; 13];
+ let mut l = 0;
+ let mut from = None;
+
+ let send_thread = thread::spawn(move || {
+ let mut l = 0;
+ while l < std::mem::size_of_val(MSG) {
+ l += f_send(ssock, &MSG[l..], MsgFlags::empty()).unwrap();
+ }
+ });
+
+ while l < std::mem::size_of_val(MSG) {
+ let (len, from_) = recvfrom(rsock, &mut buf[l..]).unwrap();
+ f_recv(len, from_);
+ from = from_;
+ l += len;
+ }
+ assert_eq!(&buf, MSG);
+ send_thread.join().unwrap();
+ from
+ }
+
+ #[test]
+ pub fn stream() {
+ let (fd2, fd1) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ // Ignore from for stream sockets
+ let _ = sendrecv(fd1.as_raw_fd(), fd2.as_raw_fd(), send, |_, _| {});
+ }
+
+ #[test]
+ pub fn udp() {
+ let std_sa = SocketAddrV4::from_str("127.0.0.1:6789").unwrap();
+ let sock_addr = SockaddrIn::from(std_sa);
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ let from = sendrecv(
+ rsock.as_raw_fd(),
+ ssock.as_raw_fd(),
+ move |s, m, flags| sendto(s.as_raw_fd(), m, &sock_addr, flags),
+ |_, _| {},
+ );
+ // UDP sockets should set the from address
+ assert_eq!(AddressFamily::Inet, from.unwrap().family().unwrap());
+ }
+
+ #[cfg(target_os = "linux")]
+ mod udp_offload {
+ use super::*;
+ use nix::sys::socket::sockopt::{UdpGroSegment, UdpGsoSegment};
+ use std::io::IoSlice;
+
+ #[test]
+ // Disable the test under emulation because it fails in Cirrus-CI. Lack
+ // of QEMU support is suspected.
+ #[cfg_attr(qemu, ignore)]
+ pub fn gso() {
+ require_kernel_version!(udp_offload::gso, ">= 4.18");
+
+ // In this test, we send the data and provide a GSO segment size.
+ // Since we are sending the buffer of size 13, six UDP packets
+ // with size 2 and two UDP packet with size 1 will be sent.
+ let segment_size: u16 = 2;
+
+ let sock_addr = SockaddrIn::new(127, 0, 0, 1, 6791);
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+
+ setsockopt(&rsock, UdpGsoSegment, &(segment_size as _))
+ .expect("setsockopt UDP_SEGMENT failed");
+
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+
+ let mut num_packets_received: i32 = 0;
+
+ sendrecv(
+ rsock.as_raw_fd(),
+ ssock.as_raw_fd(),
+ move |s, m, flags| {
+ let iov = [IoSlice::new(m)];
+ let cmsg = ControlMessage::UdpGsoSegments(&segment_size);
+ sendmsg(
+ s.as_raw_fd(),
+ &iov,
+ &[cmsg],
+ flags,
+ Some(&sock_addr),
+ )
+ },
+ {
+ let num_packets_received_ref = &mut num_packets_received;
+
+ move |len, _| {
+ // check that we receive UDP packets with payload size
+ // less or equal to segment size
+ assert!(len <= segment_size as usize);
+ *num_packets_received_ref += 1;
+ }
+ },
+ );
+
+ // Buffer size is 13, we will receive six packets of size 2,
+ // and one packet of size 1.
+ assert_eq!(7, num_packets_received);
+ }
+
+ #[test]
+ // Disable the test on emulated platforms because it fails in Cirrus-CI.
+ // Lack of QEMU support is suspected.
+ #[cfg_attr(qemu, ignore)]
+ pub fn gro() {
+ require_kernel_version!(udp_offload::gro, ">= 5.3");
+
+ // It's hard to guarantee receiving GRO packets. Just checking
+ // that `setsockopt` doesn't fail with error
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+
+ setsockopt(&rsock, UdpGroSegment, &true)
+ .expect("setsockopt UDP_GRO failed");
+ }
+ }
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ ))]
+ #[test]
+ pub fn udp_sendmmsg() {
+ use std::io::IoSlice;
+
+ let std_sa = SocketAddrV4::from_str("127.0.0.1:6793").unwrap();
+ let std_sa2 = SocketAddrV4::from_str("127.0.0.1:6794").unwrap();
+ let sock_addr = SockaddrIn::from(std_sa);
+ let sock_addr2 = SockaddrIn::from(std_sa2);
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+
+ let from = sendrecv(
+ rsock.as_raw_fd(),
+ ssock.as_raw_fd(),
+ move |s, m, flags| {
+ let batch_size = 15;
+ let mut iovs = Vec::with_capacity(1 + batch_size);
+ let mut addrs = Vec::with_capacity(1 + batch_size);
+ let mut data = MultiHeaders::preallocate(1 + batch_size, None);
+ let iov = IoSlice::new(m);
+ // first chunk:
+ iovs.push([iov]);
+ addrs.push(Some(sock_addr));
+
+ for _ in 0..batch_size {
+ iovs.push([iov]);
+ addrs.push(Some(sock_addr2));
+ }
+
+ let res = sendmmsg(s, &mut data, &iovs, addrs, [], flags)?;
+ let mut sent_messages = 0;
+ let mut sent_bytes = 0;
+ for item in res {
+ sent_messages += 1;
+ sent_bytes += item.bytes;
+ }
+ //
+ assert_eq!(sent_messages, iovs.len());
+ assert_eq!(sent_bytes, sent_messages * m.len());
+ Ok(sent_messages)
+ },
+ |_, _| {},
+ );
+ // UDP sockets should set the from address
+ assert_eq!(AddressFamily::Inet, from.unwrap().family().unwrap());
+ }
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ ))]
+ #[test]
+ pub fn udp_recvmmsg() {
+ use nix::sys::socket::{recvmmsg, MsgFlags};
+ use std::io::IoSliceMut;
+
+ const NUM_MESSAGES_SENT: usize = 2;
+ const DATA: [u8; 2] = [1, 2];
+
+ let inet_addr = SocketAddrV4::from_str("127.0.0.1:6798").unwrap();
+ let sock_addr = SockaddrIn::from(inet_addr);
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+
+ let send_thread = thread::spawn(move || {
+ for _ in 0..NUM_MESSAGES_SENT {
+ sendto(
+ ssock.as_raw_fd(),
+ &DATA[..],
+ &sock_addr,
+ MsgFlags::empty(),
+ )
+ .unwrap();
+ }
+ });
+
+ let mut msgs = std::collections::LinkedList::new();
+
+ // Buffers to receive exactly `NUM_MESSAGES_SENT` messages
+ let mut receive_buffers = [[0u8; 32]; NUM_MESSAGES_SENT];
+ msgs.extend(
+ receive_buffers
+ .iter_mut()
+ .map(|buf| [IoSliceMut::new(&mut buf[..])]),
+ );
+
+ let mut data =
+ MultiHeaders::<SockaddrIn>::preallocate(msgs.len(), None);
+
+ let res: Vec<RecvMsg<SockaddrIn>> = recvmmsg(
+ rsock.as_raw_fd(),
+ &mut data,
+ msgs.iter(),
+ MsgFlags::empty(),
+ None,
+ )
+ .expect("recvmmsg")
+ .collect();
+ assert_eq!(res.len(), DATA.len());
+
+ for RecvMsg { address, bytes, .. } in res.into_iter() {
+ assert_eq!(AddressFamily::Inet, address.unwrap().family().unwrap());
+ assert_eq!(DATA.len(), bytes);
+ }
+
+ for buf in &receive_buffers {
+ assert_eq!(&buf[..DATA.len()], DATA);
+ }
+
+ send_thread.join().unwrap();
+ }
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ ))]
+ #[test]
+ pub fn udp_recvmmsg_dontwait_short_read() {
+ use nix::sys::socket::{recvmmsg, MsgFlags};
+ use std::io::IoSliceMut;
+
+ const NUM_MESSAGES_SENT: usize = 2;
+ const DATA: [u8; 4] = [1, 2, 3, 4];
+
+ let inet_addr = SocketAddrV4::from_str("127.0.0.1:6799").unwrap();
+ let sock_addr = SockaddrIn::from(inet_addr);
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+
+ let send_thread = thread::spawn(move || {
+ for _ in 0..NUM_MESSAGES_SENT {
+ sendto(
+ ssock.as_raw_fd(),
+ &DATA[..],
+ &sock_addr,
+ MsgFlags::empty(),
+ )
+ .unwrap();
+ }
+ });
+ // Ensure we've sent all the messages before continuing so `recvmmsg`
+ // will return right away
+ send_thread.join().unwrap();
+
+ let mut msgs = std::collections::LinkedList::new();
+
+ // Buffers to receive >`NUM_MESSAGES_SENT` messages to ensure `recvmmsg`
+ // will return when there are fewer than requested messages in the
+ // kernel buffers when using `MSG_DONTWAIT`.
+ let mut receive_buffers = [[0u8; 32]; NUM_MESSAGES_SENT + 2];
+ msgs.extend(
+ receive_buffers
+ .iter_mut()
+ .map(|buf| [IoSliceMut::new(&mut buf[..])]),
+ );
+
+ let mut data = MultiHeaders::<SockaddrIn>::preallocate(
+ NUM_MESSAGES_SENT + 2,
+ None,
+ );
+
+ let res: Vec<RecvMsg<SockaddrIn>> = recvmmsg(
+ rsock.as_raw_fd(),
+ &mut data,
+ msgs.iter(),
+ MsgFlags::MSG_DONTWAIT,
+ None,
+ )
+ .expect("recvmmsg")
+ .collect();
+ assert_eq!(res.len(), NUM_MESSAGES_SENT);
+
+ for RecvMsg { address, bytes, .. } in res.into_iter() {
+ assert_eq!(AddressFamily::Inet, address.unwrap().family().unwrap());
+ assert_eq!(DATA.len(), bytes);
+ }
+
+ for buf in &receive_buffers[..NUM_MESSAGES_SENT] {
+ assert_eq!(&buf[..DATA.len()], DATA);
+ }
+ }
+
+ #[test]
+ pub fn udp_inet6() {
+ let addr = std::net::Ipv6Addr::from_str("::1").unwrap();
+ let rport = 6789;
+ let rstd_sa = SocketAddrV6::new(addr, rport, 0, 0);
+ let raddr = SockaddrIn6::from(rstd_sa);
+ let sport = 6790;
+ let sstd_sa = SocketAddrV6::new(addr, sport, 0, 0);
+ let saddr = SockaddrIn6::from(sstd_sa);
+ let rsock = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("receive socket failed");
+ match bind(rsock.as_raw_fd(), &raddr) {
+ Err(Errno::EADDRNOTAVAIL) => {
+ println!("IPv6 not available, skipping test.");
+ return;
+ }
+ Err(e) => panic!("bind: {e}"),
+ Ok(()) => (),
+ }
+ let ssock = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ bind(ssock.as_raw_fd(), &saddr).unwrap();
+ let from = sendrecv(
+ rsock.as_raw_fd(),
+ ssock.as_raw_fd(),
+ move |s, m, flags| sendto(s.as_raw_fd(), m, &raddr, flags),
+ |_, _| {},
+ );
+ assert_eq!(AddressFamily::Inet6, from.unwrap().family().unwrap());
+ let osent_addr = from.unwrap();
+ let sent_addr = osent_addr.as_sockaddr_in6().unwrap();
+ assert_eq!(sent_addr.ip(), addr);
+ assert_eq!(sent_addr.port(), sport);
+ }
+}
+
+// Test error handling of our recvmsg wrapper
+#[test]
+pub fn test_recvmsg_ebadf() {
+ use nix::errno::Errno;
+ use nix::sys::socket::{recvmsg, MsgFlags};
+ use std::io::IoSliceMut;
+
+ let mut buf = [0u8; 5];
+ let mut iov = [IoSliceMut::new(&mut buf[..])];
+
+ let fd = -1; // Bad file descriptor
+ let r = recvmsg::<()>(fd.as_raw_fd(), &mut iov, None, MsgFlags::empty());
+
+ assert_eq!(r.err().unwrap(), Errno::EBADF);
+}
+
+// Disable the test on emulated platforms due to a bug in QEMU versions <
+// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_scm_rights() {
+ use nix::sys::socket::{
+ recvmsg, sendmsg, socketpair, AddressFamily, ControlMessage,
+ ControlMessageOwned, MsgFlags, SockFlag, SockType,
+ };
+ use nix::unistd::{close, pipe, read, write};
+ use std::io::{IoSlice, IoSliceMut};
+
+ 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 = [IoSlice::new(b"hello")];
+ let fds = [r];
+ let cmsg = ControlMessage::ScmRights(&fds);
+ assert_eq!(
+ sendmsg::<()>(
+ fd1.as_raw_fd(),
+ &iov,
+ &[cmsg],
+ MsgFlags::empty(),
+ None
+ )
+ .unwrap(),
+ 5
+ );
+ close(r).unwrap();
+ }
+
+ {
+ let mut buf = [0u8; 5];
+
+ let mut iov = [IoSliceMut::new(&mut buf[..])];
+ let mut cmsgspace = cmsg_space!([RawFd; 1]);
+ let msg = recvmsg::<()>(
+ fd2.as_raw_fd(),
+ &mut iov,
+ Some(&mut cmsgspace),
+ MsgFlags::empty(),
+ )
+ .unwrap();
+
+ for cmsg in msg.cmsgs() {
+ if let ControlMessageOwned::ScmRights(fd) = cmsg {
+ assert_eq!(received_r, None);
+ assert_eq!(fd.len(), 1);
+ received_r = Some(fd[0]);
+ } else {
+ panic!("unexpected cmsg");
+ }
+ }
+ assert_eq!(msg.bytes, 5);
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ }
+
+ let received_r = received_r.expect("Did not receive passed fd");
+ // Ensure that the received file descriptor works
+ write(w.as_raw_fd(), b"world").unwrap();
+ let mut buf = [0u8; 5];
+ read(received_r.as_raw_fd(), &mut buf).unwrap();
+ assert_eq!(&buf[..], b"world");
+ close(received_r).unwrap();
+ close(w).unwrap();
+}
+
+// Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_af_alg_cipher() {
+ use nix::sys::socket::sockopt::AlgSetKey;
+ use nix::sys::socket::{
+ accept, bind, sendmsg, setsockopt, socket, AddressFamily, AlgAddr,
+ ControlMessage, MsgFlags, SockFlag, SockType,
+ };
+ use nix::unistd::read;
+ use std::io::IoSlice;
+
+ skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1352");
+ // Travis's seccomp profile blocks AF_ALG
+ // https://docs.docker.com/engine/security/seccomp/
+ skip_if_seccomp!(test_af_alg_cipher);
+
+ let alg_type = "skcipher";
+ let alg_name = "ctr-aes-aesni";
+ // 256-bits secret key
+ let key = vec![0u8; 32];
+ // 16-bytes IV
+ let iv_len = 16;
+ let iv = vec![1u8; iv_len];
+ // 256-bytes plain payload
+ let payload_len = 256;
+ let payload = vec![2u8; payload_len];
+
+ let sock = socket(
+ AddressFamily::Alg,
+ SockType::SeqPacket,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+
+ let sockaddr = AlgAddr::new(alg_type, alg_name);
+ bind(sock.as_raw_fd(), &sockaddr).expect("bind failed");
+
+ assert_eq!(sockaddr.alg_name().to_string_lossy(), alg_name);
+ assert_eq!(sockaddr.alg_type().to_string_lossy(), alg_type);
+
+ setsockopt(&sock, AlgSetKey::default(), &key).expect("setsockopt");
+ let session_socket = accept(sock.as_raw_fd()).expect("accept failed");
+
+ let msgs = [
+ ControlMessage::AlgSetOp(&libc::ALG_OP_ENCRYPT),
+ ControlMessage::AlgSetIv(iv.as_slice()),
+ ];
+ let iov = IoSlice::new(&payload);
+ sendmsg::<()>(
+ session_socket.as_raw_fd(),
+ &[iov],
+ &msgs,
+ MsgFlags::empty(),
+ None,
+ )
+ .expect("sendmsg encrypt");
+
+ // allocate buffer for encrypted data
+ let mut encrypted = vec![0u8; payload_len];
+ let num_bytes =
+ read(session_socket.as_raw_fd(), &mut encrypted).expect("read encrypt");
+ assert_eq!(num_bytes, payload_len);
+
+ let iov = IoSlice::new(&encrypted);
+
+ let iv = vec![1u8; iv_len];
+
+ let msgs = [
+ ControlMessage::AlgSetOp(&libc::ALG_OP_DECRYPT),
+ ControlMessage::AlgSetIv(iv.as_slice()),
+ ];
+ sendmsg::<()>(
+ session_socket.as_raw_fd(),
+ &[iov],
+ &msgs,
+ MsgFlags::empty(),
+ None,
+ )
+ .expect("sendmsg decrypt");
+
+ // allocate buffer for decrypted data
+ let mut decrypted = vec![0u8; payload_len];
+ let num_bytes =
+ read(session_socket.as_raw_fd(), &mut decrypted).expect("read decrypt");
+
+ assert_eq!(num_bytes, payload_len);
+ assert_eq!(decrypted, payload);
+}
+
+// Disable the test on emulated platforms due to not enabled support of AF_ALG
+// in QEMU from rust cross
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_af_alg_aead() {
+ use libc::{ALG_OP_DECRYPT, ALG_OP_ENCRYPT};
+ use nix::fcntl::{fcntl, FcntlArg, OFlag};
+ use nix::sys::socket::sockopt::{AlgSetAeadAuthSize, AlgSetKey};
+ use nix::sys::socket::{
+ accept, bind, sendmsg, setsockopt, socket, AddressFamily, AlgAddr,
+ ControlMessage, MsgFlags, SockFlag, SockType,
+ };
+ use nix::unistd::read;
+ use std::io::IoSlice;
+
+ skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1352");
+ // Travis's seccomp profile blocks AF_ALG
+ // https://docs.docker.com/engine/security/seccomp/
+ skip_if_seccomp!(test_af_alg_aead);
+
+ let auth_size = 4usize;
+ let assoc_size = 16u32;
+
+ let alg_type = "aead";
+ let alg_name = "gcm(aes)";
+ // 256-bits secret key
+ let key = vec![0u8; 32];
+ // 12-bytes IV
+ let iv_len = 12;
+ let iv = vec![1u8; iv_len];
+ // 256-bytes plain payload
+ let payload_len = 256;
+ let mut payload =
+ vec![2u8; payload_len + (assoc_size as usize) + auth_size];
+
+ for i in 0..assoc_size {
+ payload[i as usize] = 10;
+ }
+
+ let len = payload.len();
+
+ for i in 0..auth_size {
+ payload[len - 1 - i] = 0;
+ }
+
+ let sock = socket(
+ AddressFamily::Alg,
+ SockType::SeqPacket,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+
+ let sockaddr = AlgAddr::new(alg_type, alg_name);
+ bind(sock.as_raw_fd(), &sockaddr).expect("bind failed");
+
+ setsockopt(&sock, AlgSetAeadAuthSize, &auth_size)
+ .expect("setsockopt AlgSetAeadAuthSize");
+ setsockopt(&sock, AlgSetKey::default(), &key)
+ .expect("setsockopt AlgSetKey");
+ let session_socket = accept(sock.as_raw_fd()).expect("accept failed");
+
+ let msgs = [
+ ControlMessage::AlgSetOp(&ALG_OP_ENCRYPT),
+ ControlMessage::AlgSetIv(iv.as_slice()),
+ ControlMessage::AlgSetAeadAssoclen(&assoc_size),
+ ];
+
+ let iov = IoSlice::new(&payload);
+ sendmsg::<()>(
+ session_socket.as_raw_fd(),
+ &[iov],
+ &msgs,
+ MsgFlags::empty(),
+ None,
+ )
+ .expect("sendmsg encrypt");
+
+ // allocate buffer for encrypted data
+ let mut encrypted =
+ vec![0u8; (assoc_size as usize) + payload_len + auth_size];
+ let num_bytes =
+ read(session_socket.as_raw_fd(), &mut encrypted).expect("read encrypt");
+ assert_eq!(num_bytes, payload_len + auth_size + (assoc_size as usize));
+
+ for i in 0..assoc_size {
+ encrypted[i as usize] = 10;
+ }
+
+ let iov = IoSlice::new(&encrypted);
+
+ let iv = vec![1u8; iv_len];
+
+ let session_socket = accept(sock.as_raw_fd()).expect("accept failed");
+
+ let msgs = [
+ ControlMessage::AlgSetOp(&ALG_OP_DECRYPT),
+ ControlMessage::AlgSetIv(iv.as_slice()),
+ ControlMessage::AlgSetAeadAssoclen(&assoc_size),
+ ];
+ sendmsg::<()>(
+ session_socket.as_raw_fd(),
+ &[iov],
+ &msgs,
+ MsgFlags::empty(),
+ None,
+ )
+ .expect("sendmsg decrypt");
+
+ // allocate buffer for decrypted data
+ let mut decrypted =
+ vec![0u8; payload_len + (assoc_size as usize) + auth_size];
+ // Starting with kernel 4.9, the interface changed slightly such that the
+ // authentication tag memory is only needed in the output buffer for encryption
+ // and in the input buffer for decryption.
+ // Do not block on read, as we may have fewer bytes than buffer size
+ fcntl(session_socket, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))
+ .expect("fcntl non_blocking");
+ let num_bytes =
+ read(session_socket.as_raw_fd(), &mut decrypted).expect("read decrypt");
+
+ assert!(num_bytes >= payload_len + (assoc_size as usize));
+ assert_eq!(
+ decrypted[(assoc_size as usize)..(payload_len + (assoc_size as usize))],
+ payload[(assoc_size as usize)..payload_len + (assoc_size as usize)]
+ );
+}
+
+// Verify `ControlMessage::Ipv4PacketInfo` for `sendmsg`.
+// This creates a (udp) socket bound to localhost, then sends a message to
+// itself but uses Ipv4PacketInfo to force the source address to be localhost.
+//
+// This would be a more interesting test if we could assume that the test host
+// has more than one IP address (since we could select a different address to
+// test from).
+#[cfg(any(target_os = "linux", target_os = "macos", target_os = "netbsd"))]
+#[test]
+pub fn test_sendmsg_ipv4packetinfo() {
+ use cfg_if::cfg_if;
+ use nix::sys::socket::{
+ bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags,
+ SockFlag, SockType, SockaddrIn,
+ };
+ use std::io::IoSlice;
+
+ let sock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+
+ let sock_addr = SockaddrIn::new(127, 0, 0, 1, 4000);
+
+ bind(sock.as_raw_fd(), &sock_addr).expect("bind failed");
+
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ cfg_if! {
+ if #[cfg(target_os = "netbsd")] {
+ let pi = libc::in_pktinfo {
+ ipi_ifindex: 0, /* Unspecified interface */
+ ipi_addr: libc::in_addr { s_addr: 0 },
+ };
+ } else {
+ let pi = libc::in_pktinfo {
+ ipi_ifindex: 0, /* Unspecified interface */
+ ipi_addr: libc::in_addr { s_addr: 0 },
+ ipi_spec_dst: sock_addr.as_ref().sin_addr,
+ };
+ }
+ }
+
+ let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)];
+
+ sendmsg(
+ sock.as_raw_fd(),
+ &iov,
+ &cmsg,
+ MsgFlags::empty(),
+ Some(&sock_addr),
+ )
+ .expect("sendmsg");
+}
+
+// Verify `ControlMessage::Ipv6PacketInfo` for `sendmsg`.
+// This creates a (udp) socket bound to ip6-localhost, then sends a message to
+// itself but uses Ipv6PacketInfo to force the source address to be
+// ip6-localhost.
+//
+// This would be a more interesting test if we could assume that the test host
+// has more than one IP address (since we could select a different address to
+// test from).
+#[cfg(any(
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "freebsd"
+))]
+#[test]
+pub fn test_sendmsg_ipv6packetinfo() {
+ use nix::errno::Errno;
+ use nix::sys::socket::{
+ bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags,
+ SockFlag, SockType, SockaddrIn6,
+ };
+ use std::io::IoSlice;
+
+ let sock = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+
+ let std_sa = SocketAddrV6::from_str("[::1]:6000").unwrap();
+ let sock_addr: SockaddrIn6 = SockaddrIn6::from(std_sa);
+
+ if let Err(Errno::EADDRNOTAVAIL) = bind(sock.as_raw_fd(), &sock_addr) {
+ println!("IPv6 not available, skipping test.");
+ return;
+ }
+
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ let pi = libc::in6_pktinfo {
+ ipi6_ifindex: 0, /* Unspecified interface */
+ ipi6_addr: sock_addr.as_ref().sin6_addr,
+ };
+
+ let cmsg = [ControlMessage::Ipv6PacketInfo(&pi)];
+
+ sendmsg::<SockaddrIn6>(
+ sock.as_raw_fd(),
+ &iov,
+ &cmsg,
+ MsgFlags::empty(),
+ Some(&sock_addr),
+ )
+ .expect("sendmsg");
+}
+
+// Verify that ControlMessage::Ipv4SendSrcAddr works for sendmsg. This
+// creates a UDP socket bound to all local interfaces (0.0.0.0). It then
+// sends message to itself at 127.0.0.1 while explicitly specifying
+// 127.0.0.1 as the source address through an Ipv4SendSrcAddr
+// (IP_SENDSRCADDR) control message.
+//
+// Note that binding to 0.0.0.0 is *required* on FreeBSD; sendmsg
+// returns EINVAL otherwise. (See FreeBSD's ip(4) man page.)
+#[cfg(any(
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+))]
+#[test]
+pub fn test_sendmsg_ipv4sendsrcaddr() {
+ use nix::sys::socket::{
+ bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags,
+ SockFlag, SockType, SockaddrIn,
+ };
+ use std::io::IoSlice;
+
+ let sock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+
+ let unspec_sock_addr = SockaddrIn::new(0, 0, 0, 0, 0);
+ bind(sock.as_raw_fd(), &unspec_sock_addr).expect("bind failed");
+ let bound_sock_addr: SockaddrIn = getsockname(sock.as_raw_fd()).unwrap();
+ let localhost_sock_addr: SockaddrIn =
+ SockaddrIn::new(127, 0, 0, 1, bound_sock_addr.port());
+
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+ let cmsg = [ControlMessage::Ipv4SendSrcAddr(
+ &localhost_sock_addr.as_ref().sin_addr,
+ )];
+
+ sendmsg(
+ sock.as_raw_fd(),
+ &iov,
+ &cmsg,
+ MsgFlags::empty(),
+ Some(&localhost_sock_addr),
+ )
+ .expect("sendmsg");
+}
+
+/// Tests that passing multiple fds using a single `ControlMessage` works.
+// Disable the test on emulated platforms due to a bug in QEMU versions <
+// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808
+#[cfg_attr(qemu, ignore)]
+#[test]
+fn test_scm_rights_single_cmsg_multiple_fds() {
+ use nix::sys::socket::{
+ recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags,
+ };
+ use std::io::{IoSlice, IoSliceMut};
+ use std::os::unix::io::{AsRawFd, RawFd};
+ use std::os::unix::net::UnixDatagram;
+ use std::thread;
+
+ let (send, receive) = UnixDatagram::pair().unwrap();
+ let thread = thread::spawn(move || {
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+
+ let mut space = cmsg_space!([RawFd; 2]);
+ let msg = recvmsg::<()>(
+ receive.as_raw_fd(),
+ &mut 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(ControlMessageOwned::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!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ });
+
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&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::socket::{
+ recvmsg, sendmsg, socketpair, AddressFamily, MsgFlags, SockFlag,
+ SockType,
+ };
+ use std::io::{IoSlice, IoSliceMut};
+
+ let (fd1, fd2) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+
+ {
+ let iov = [IoSlice::new(b"hello")];
+ assert_eq!(
+ sendmsg::<()>(fd1.as_raw_fd(), &iov, &[], MsgFlags::empty(), None)
+ .unwrap(),
+ 5
+ );
+ }
+
+ {
+ let mut buf = [0u8; 5];
+ let mut iov = [IoSliceMut::new(&mut buf[..])];
+
+ let mut cmsgspace = cmsg_space!([RawFd; 1]);
+ let msg = recvmsg::<()>(
+ fd2.as_raw_fd(),
+ &mut iov,
+ Some(&mut cmsgspace),
+ MsgFlags::empty(),
+ )
+ .unwrap();
+
+ for _ in msg.cmsgs() {
+ panic!("unexpected cmsg");
+ }
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ assert_eq!(msg.bytes, 5);
+ }
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+))]
+#[test]
+fn test_scm_credentials() {
+ use nix::sys::socket::{
+ recvmsg, sendmsg, socketpair, AddressFamily, ControlMessage,
+ ControlMessageOwned, MsgFlags, SockFlag, SockType, UnixCredentials,
+ };
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ use nix::sys::socket::{setsockopt, sockopt::PassCred};
+ use nix::unistd::{getgid, getpid, getuid};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let (send, recv) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ setsockopt(&recv, PassCred, &true).unwrap();
+
+ {
+ let iov = [IoSlice::new(b"hello")];
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ let cred = UnixCredentials::new();
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ let cmsg = ControlMessage::ScmCredentials(&cred);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ let cmsg = ControlMessage::ScmCreds;
+ assert_eq!(
+ sendmsg::<()>(
+ send.as_raw_fd(),
+ &iov,
+ &[cmsg],
+ MsgFlags::empty(),
+ None
+ )
+ .unwrap(),
+ 5
+ );
+ }
+
+ {
+ let mut buf = [0u8; 5];
+ let mut iov = [IoSliceMut::new(&mut buf[..])];
+
+ let mut cmsgspace = cmsg_space!(UnixCredentials);
+ let msg = recvmsg::<()>(
+ recv.as_raw_fd(),
+ &mut iov,
+ Some(&mut cmsgspace),
+ MsgFlags::empty(),
+ )
+ .unwrap();
+ let mut received_cred = None;
+
+ for cmsg in msg.cmsgs() {
+ let cred = match cmsg {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessageOwned::ScmCredentials(cred) => cred,
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessageOwned::ScmCreds(cred) => cred,
+ other => panic!("unexpected cmsg {other:?}"),
+ };
+ 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);
+ }
+ received_cred.expect("no creds received");
+ assert_eq!(msg.bytes, 5);
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ }
+}
+
+/// 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 under emulation
+// see https://bugs.launchpad.net/qemu/+bug/1781280
+#[cfg_attr(qemu, ignore)]
+#[test]
+fn test_scm_credentials_and_rights() {
+ let space = cmsg_space!(libc::ucred, RawFd);
+ test_impl_scm_credentials_and_rights(space);
+}
+
+/// Ensure that passing a an oversized control message buffer to recvmsg
+/// still works.
+#[cfg(any(target_os = "android", target_os = "linux"))]
+// qemu's handling of multiple cmsgs is bugged, ignore tests under emulation
+// see https://bugs.launchpad.net/qemu/+bug/1781280
+#[cfg_attr(qemu, ignore)]
+#[test]
+fn test_too_large_cmsgspace() {
+ let space = vec![0u8; 1024];
+ test_impl_scm_credentials_and_rights(space);
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_impl_scm_credentials_and_rights(mut space: Vec<u8>) {
+ use libc::ucred;
+ use nix::sys::socket::sockopt::PassCred;
+ use nix::sys::socket::{
+ recvmsg, sendmsg, setsockopt, socketpair, ControlMessage,
+ ControlMessageOwned, MsgFlags, SockFlag, SockType,
+ };
+ use nix::unistd::{close, getgid, getpid, getuid, pipe, write};
+ use std::io::{IoSlice, IoSliceMut};
+
+ 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 = [IoSlice::new(b"hello")];
+ let cred = ucred {
+ pid: getpid().as_raw(),
+ uid: getuid().as_raw(),
+ gid: getgid().as_raw(),
+ }
+ .into();
+ let fds = [r];
+ let cmsgs = [
+ ControlMessage::ScmCredentials(&cred),
+ ControlMessage::ScmRights(&fds),
+ ];
+ assert_eq!(
+ sendmsg::<()>(
+ send.as_raw_fd(),
+ &iov,
+ &cmsgs,
+ MsgFlags::empty(),
+ None
+ )
+ .unwrap(),
+ 5
+ );
+ close(r).unwrap();
+ }
+
+ {
+ let mut buf = [0u8; 5];
+ let mut iov = [IoSliceMut::new(&mut buf[..])];
+ let msg = recvmsg::<()>(
+ recv.as_raw_fd(),
+ &mut 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 {
+ ControlMessageOwned::ScmRights(fds) => {
+ assert_eq!(received_r, None, "already received fd");
+ assert_eq!(fds.len(), 1);
+ received_r = Some(fds[0]);
+ }
+ ControlMessageOwned::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_eq!(msg.bytes, 5);
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ }
+
+ let received_r = received_r.expect("Did not receive passed fd");
+ // Ensure that the received file descriptor works
+ write(w.as_raw_fd(), b"world").unwrap();
+ let mut buf = [0u8; 5];
+ read(received_r.as_raw_fd(), &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_named_unixdomain() {
+ use nix::sys::socket::{accept, bind, connect, listen, socket, UnixAddr};
+ use nix::sys::socket::{SockFlag, SockType};
+ use nix::unistd::{read, write};
+ 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 = UnixAddr::new(&sockname).unwrap();
+ bind(s1.as_raw_fd(), &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.as_raw_fd(), &sockaddr).expect("connect failed");
+ write(s2.as_raw_fd(), b"hello").expect("write failed");
+ });
+
+ let s3 = accept(s1.as_raw_fd()).expect("accept failed");
+
+ let mut buf = [0; 5];
+ read(s3.as_raw_fd(), &mut buf).unwrap();
+ thr.join().unwrap();
+
+ assert_eq!(&buf[..], b"hello");
+}
+
+// Test using unnamed unix domain addresses
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_unnamed_unixdomain() {
+ use nix::sys::socket::{getsockname, socketpair};
+ use nix::sys::socket::{SockFlag, SockType};
+
+ let (fd_1, _fd_2) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .expect("socketpair failed");
+
+ let addr_1: UnixAddr =
+ getsockname(fd_1.as_raw_fd()).expect("getsockname failed");
+ assert!(addr_1.is_unnamed());
+}
+
+// Test creating and using unnamed unix domain addresses for autobinding sockets
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_unnamed_unixdomain_autobind() {
+ use nix::sys::socket::{bind, getsockname, socket};
+ use nix::sys::socket::{SockFlag, SockType};
+
+ let fd = socket(
+ AddressFamily::Unix,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+
+ // unix(7): "If a bind(2) call specifies addrlen as `sizeof(sa_family_t)`, or [...], then the
+ // socket is autobound to an abstract address"
+ bind(fd.as_raw_fd(), &UnixAddr::new_unnamed()).expect("bind failed");
+
+ let addr: UnixAddr =
+ getsockname(fd.as_raw_fd()).expect("getsockname failed");
+ let addr = addr.as_abstract().unwrap();
+
+ // changed from 8 to 5 bytes in Linux 2.3.15, and rust's minimum supported Linux version is 3.2
+ // (as of 2022-11)
+ assert_eq!(addr.len(), 5);
+}
+
+// Test creating and using named system control sockets
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+#[test]
+pub fn test_syscontrol() {
+ use nix::errno::Errno;
+ use nix::sys::socket::{
+ socket, SockFlag, SockProtocol, SockType, SysControlAddr,
+ };
+
+ let fd = socket(
+ AddressFamily::System,
+ SockType::Datagram,
+ SockFlag::empty(),
+ SockProtocol::KextControl,
+ )
+ .expect("socket failed");
+ SysControlAddr::from_name(fd.as_raw_fd(), "com.apple.net.utun_control", 0)
+ .expect("resolving sys_control name failed");
+ assert_eq!(
+ SysControlAddr::from_name(fd.as_raw_fd(), "foo.bar.lol", 0).err(),
+ Some(Errno::ENOENT)
+ );
+
+ // requires root privileges
+ // connect(fd.as_raw_fd(), &sockaddr).expect("connect failed");
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+fn loopback_address(
+ family: AddressFamily,
+) -> Option<nix::ifaddrs::InterfaceAddress> {
+ use nix::ifaddrs::getifaddrs;
+ use nix::net::if_::*;
+ use nix::sys::socket::SockaddrLike;
+ use std::io;
+ use std::io::Write;
+
+ let mut addrs = match getifaddrs() {
+ Ok(iter) => iter,
+ Err(e) => {
+ let stdioerr = io::stderr();
+ let mut handle = stdioerr.lock();
+ writeln!(handle, "getifaddrs: {e:?}").unwrap();
+ return None;
+ }
+ };
+ // return first address matching family
+ addrs.find(|ifaddr| {
+ ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK)
+ && ifaddr.address.as_ref().and_then(SockaddrLike::family)
+ == Some(family)
+ })
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+))]
+// qemu doesn't seem to be emulating this correctly in these architectures
+#[cfg_attr(
+ all(
+ qemu,
+ any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc64",
+ )
+ ),
+ ignore
+)]
+#[test]
+pub fn test_recv_ipv4pktinfo() {
+ use nix::net::if_::*;
+ use nix::sys::socket::sockopt::Ipv4PacketInfo;
+ use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
+ use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let lo_ifaddr = loopback_address(AddressFamily::Inet);
+ 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.as_raw_fd(), &lo).expect("bind failed");
+ let sa: SockaddrIn =
+ getsockname(receive.as_raw_fd()).expect("getsockname failed");
+ setsockopt(&receive, Ipv4PacketInfo, &true).expect("setsockopt failed");
+
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa))
+ .expect("sendmsg failed");
+ }
+
+ {
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+
+ let mut space = cmsg_space!(libc::in_pktinfo);
+ let msg = recvmsg::<()>(
+ receive.as_raw_fd(),
+ &mut iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .expect("recvmsg failed");
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+
+ let mut cmsgs = msg.cmsgs();
+ if let Some(ControlMessageOwned::Ipv4PacketInfo(pktinfo)) = cmsgs.next()
+ {
+ let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
+ assert_eq!(
+ pktinfo.ipi_ifindex as libc::c_uint, i,
+ "unexpected ifindex (expected {}, got {})",
+ i, pktinfo.ipi_ifindex
+ );
+ }
+ assert!(cmsgs.next().is_none(), "unexpected additional control msg");
+ assert_eq!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ }
+}
+
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+// qemu doesn't seem to be emulating this correctly in these architectures
+#[cfg_attr(
+ all(
+ qemu,
+ any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc64",
+ )
+ ),
+ ignore
+)]
+#[test]
+pub fn test_recvif() {
+ use nix::net::if_::*;
+ use nix::sys::socket::sockopt::{Ipv4RecvDstAddr, Ipv4RecvIf};
+ use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
+ use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let lo_ifaddr = loopback_address(AddressFamily::Inet);
+ 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.as_raw_fd(), &lo).expect("bind failed");
+ let sa: SockaddrIn =
+ getsockname(receive.as_raw_fd()).expect("getsockname failed");
+ setsockopt(&receive, Ipv4RecvIf, &true)
+ .expect("setsockopt IP_RECVIF failed");
+ setsockopt(&receive, Ipv4RecvDstAddr, &true)
+ .expect("setsockopt IP_RECVDSTADDR failed");
+
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa))
+ .expect("sendmsg failed");
+ }
+
+ {
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+ let mut space = cmsg_space!(libc::sockaddr_dl, libc::in_addr);
+ let msg = recvmsg::<()>(
+ receive.as_raw_fd(),
+ &mut iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .expect("recvmsg failed");
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs");
+
+ let mut rx_recvif = false;
+ let mut rx_recvdstaddr = false;
+ for cmsg in msg.cmsgs() {
+ match cmsg {
+ ControlMessageOwned::Ipv4RecvIf(dl) => {
+ rx_recvif = true;
+ let i = if_nametoindex(lo_name.as_bytes())
+ .expect("if_nametoindex");
+ assert_eq!(
+ dl.sdl_index as libc::c_uint, i,
+ "unexpected ifindex (expected {}, got {})",
+ i, dl.sdl_index
+ );
+ }
+ ControlMessageOwned::Ipv4RecvDstAddr(addr) => {
+ rx_recvdstaddr = true;
+ if let Some(sin) = lo.as_sockaddr_in() {
+ assert_eq!(sin.as_ref().sin_addr.s_addr,
+ addr.s_addr,
+ "unexpected destination address (expected {}, got {})",
+ sin.as_ref().sin_addr.s_addr,
+ addr.s_addr);
+ } else {
+ panic!("unexpected Sockaddr");
+ }
+ }
+ _ => panic!("unexpected additional control msg"),
+ }
+ }
+ assert!(rx_recvif);
+ assert!(rx_recvdstaddr);
+ assert_eq!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_recvif_ipv4() {
+ use nix::sys::socket::sockopt::Ipv4OrigDstAddr;
+ use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
+ use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let lo_ifaddr = loopback_address(AddressFamily::Inet);
+ 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.as_raw_fd(), &lo).expect("bind failed");
+ let sa: SockaddrIn =
+ getsockname(receive.as_raw_fd()).expect("getsockname failed");
+ setsockopt(&receive, Ipv4OrigDstAddr, &true)
+ .expect("setsockopt IP_ORIGDSTADDR failed");
+
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa))
+ .expect("sendmsg failed");
+ }
+
+ {
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+ let mut space = cmsg_space!(libc::sockaddr_in);
+ let msg = recvmsg::<()>(
+ receive.as_raw_fd(),
+ &mut iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .expect("recvmsg failed");
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs");
+
+ let mut rx_recvorigdstaddr = false;
+ for cmsg in msg.cmsgs() {
+ match cmsg {
+ ControlMessageOwned::Ipv4OrigDstAddr(addr) => {
+ rx_recvorigdstaddr = true;
+ if let Some(sin) = lo.as_sockaddr_in() {
+ assert_eq!(sin.as_ref().sin_addr.s_addr,
+ addr.sin_addr.s_addr,
+ "unexpected destination address (expected {}, got {})",
+ sin.as_ref().sin_addr.s_addr,
+ addr.sin_addr.s_addr);
+ } else {
+ panic!("unexpected Sockaddr");
+ }
+ }
+ _ => panic!("unexpected additional control msg"),
+ }
+ }
+ assert!(rx_recvorigdstaddr);
+ assert_eq!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_recvif_ipv6() {
+ use nix::sys::socket::sockopt::Ipv6OrigDstAddr;
+ use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn6};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
+ use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let lo_ifaddr = loopback_address(AddressFamily::Inet6);
+ let (_lo_name, lo) = match lo_ifaddr {
+ Some(ifaddr) => (
+ ifaddr.interface_name,
+ ifaddr.address.expect("Expect IPv6 address on interface"),
+ ),
+ None => return,
+ };
+ let receive = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("receive socket failed");
+ bind(receive.as_raw_fd(), &lo).expect("bind failed");
+ let sa: SockaddrIn6 =
+ getsockname(receive.as_raw_fd()).expect("getsockname failed");
+ setsockopt(&receive, Ipv6OrigDstAddr, &true)
+ .expect("setsockopt IP_ORIGDSTADDR failed");
+
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa))
+ .expect("sendmsg failed");
+ }
+
+ {
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+ let mut space = cmsg_space!(libc::sockaddr_in6);
+ let msg = recvmsg::<()>(
+ receive.as_raw_fd(),
+ &mut iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .expect("recvmsg failed");
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs");
+
+ let mut rx_recvorigdstaddr = false;
+ for cmsg in msg.cmsgs() {
+ match cmsg {
+ ControlMessageOwned::Ipv6OrigDstAddr(addr) => {
+ rx_recvorigdstaddr = true;
+ if let Some(sin) = lo.as_sockaddr_in6() {
+ assert_eq!(sin.as_ref().sin6_addr.s6_addr,
+ addr.sin6_addr.s6_addr,
+ "unexpected destination address (expected {:?}, got {:?})",
+ sin.as_ref().sin6_addr.s6_addr,
+ addr.sin6_addr.s6_addr);
+ } else {
+ panic!("unexpected Sockaddr");
+ }
+ }
+ _ => panic!("unexpected additional control msg"),
+ }
+ }
+ assert!(rx_recvorigdstaddr);
+ assert_eq!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ }
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+// qemu doesn't seem to be emulating this correctly in these architectures
+#[cfg_attr(
+ all(
+ qemu,
+ any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc64",
+ )
+ ),
+ ignore
+)]
+#[test]
+pub fn test_recv_ipv6pktinfo() {
+ use nix::net::if_::*;
+ use nix::sys::socket::sockopt::Ipv6RecvPacketInfo;
+ use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn6};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
+ use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let lo_ifaddr = loopback_address(AddressFamily::Inet6);
+ let (lo_name, lo) = match lo_ifaddr {
+ Some(ifaddr) => (
+ ifaddr.interface_name,
+ ifaddr.address.expect("Expect IPv6 address on interface"),
+ ),
+ None => return,
+ };
+ let receive = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("receive socket failed");
+ bind(receive.as_raw_fd(), &lo).expect("bind failed");
+ let sa: SockaddrIn6 =
+ getsockname(receive.as_raw_fd()).expect("getsockname failed");
+ setsockopt(&receive, Ipv6RecvPacketInfo, &true).expect("setsockopt failed");
+
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa))
+ .expect("sendmsg failed");
+ }
+
+ {
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+
+ let mut space = cmsg_space!(libc::in6_pktinfo);
+ let msg = recvmsg::<()>(
+ receive.as_raw_fd(),
+ &mut iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .expect("recvmsg failed");
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+
+ let mut cmsgs = msg.cmsgs();
+ if let Some(ControlMessageOwned::Ipv6PacketInfo(pktinfo)) = cmsgs.next()
+ {
+ let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
+ assert_eq!(
+ pktinfo.ipi6_ifindex as libc::c_uint, i,
+ "unexpected ifindex (expected {}, got {})",
+ i, pktinfo.ipi6_ifindex
+ );
+ }
+ assert!(cmsgs.next().is_none(), "unexpected additional control msg");
+ assert_eq!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_vsock() {
+ use nix::sys::socket::SockaddrLike;
+ use nix::sys::socket::{AddressFamily, VsockAddr};
+ use std::mem;
+
+ let port: u32 = 3000;
+
+ let addr_local = VsockAddr::new(libc::VMADDR_CID_LOCAL, port);
+ assert_eq!(addr_local.cid(), libc::VMADDR_CID_LOCAL);
+ assert_eq!(addr_local.port(), port);
+
+ let addr_any = VsockAddr::new(libc::VMADDR_CID_ANY, libc::VMADDR_PORT_ANY);
+ assert_eq!(addr_any.cid(), libc::VMADDR_CID_ANY);
+ assert_eq!(addr_any.port(), libc::VMADDR_PORT_ANY);
+
+ assert_ne!(addr_local, addr_any);
+ assert_ne!(calculate_hash(&addr_local), calculate_hash(&addr_any));
+
+ let addr1 = VsockAddr::new(libc::VMADDR_CID_HOST, port);
+ let addr2 = VsockAddr::new(libc::VMADDR_CID_HOST, port);
+ assert_eq!(addr1, addr2);
+ assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
+
+ let addr3 = unsafe {
+ VsockAddr::from_raw(
+ addr2.as_ref() as *const libc::sockaddr_vm as *const libc::sockaddr,
+ Some(mem::size_of::<libc::sockaddr_vm>().try_into().unwrap()),
+ )
+ }
+ .unwrap();
+ assert_eq!(
+ addr3.as_ref().svm_family,
+ AddressFamily::Vsock as libc::sa_family_t
+ );
+ assert_eq!(addr3.as_ref().svm_cid, addr1.cid());
+ assert_eq!(addr3.as_ref().svm_port, addr1.port());
+}
+
+#[cfg(target_os = "macos")]
+#[test]
+pub fn test_vsock() {
+ use nix::sys::socket::SockaddrLike;
+ use nix::sys::socket::{AddressFamily, VsockAddr};
+ use std::mem;
+
+ let port: u32 = 3000;
+
+ // macOS doesn't have a VMADDR_CID_LOCAL, so test with host again
+ let addr_host = VsockAddr::new(libc::VMADDR_CID_HOST, port);
+ assert_eq!(addr_host.cid(), libc::VMADDR_CID_HOST);
+ assert_eq!(addr_host.port(), port);
+
+ let addr_any = VsockAddr::new(libc::VMADDR_CID_ANY, libc::VMADDR_PORT_ANY);
+ assert_eq!(addr_any.cid(), libc::VMADDR_CID_ANY);
+ assert_eq!(addr_any.port(), libc::VMADDR_PORT_ANY);
+
+ assert_ne!(addr_host, addr_any);
+ assert_ne!(calculate_hash(&addr_host), calculate_hash(&addr_any));
+
+ let addr1 = VsockAddr::new(libc::VMADDR_CID_HOST, port);
+ let addr2 = VsockAddr::new(libc::VMADDR_CID_HOST, port);
+ assert_eq!(addr1, addr2);
+ assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
+
+ let addr3 = unsafe {
+ VsockAddr::from_raw(
+ addr2.as_ref() as *const libc::sockaddr_vm as *const libc::sockaddr,
+ Some(mem::size_of::<libc::sockaddr_vm>().try_into().unwrap()),
+ )
+ }
+ .unwrap();
+ assert_eq!(
+ addr3.as_ref().svm_family,
+ AddressFamily::Vsock as libc::sa_family_t
+ );
+ let cid = addr3.as_ref().svm_cid;
+ let port = addr3.as_ref().svm_port;
+ assert_eq!(cid, addr1.cid());
+ assert_eq!(port, addr1.port());
+}
+
+// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+#[cfg(target_os = "linux")]
+#[test]
+fn test_recvmsg_timestampns() {
+ use nix::sys::socket::*;
+ use nix::sys::time::*;
+ use std::io::{IoSlice, IoSliceMut};
+ 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::ReceiveTimestampns, &true).unwrap();
+ let localhost = SockaddrIn::new(127, 0, 0, 1, 0);
+ bind(in_socket.as_raw_fd(), &localhost).unwrap();
+ let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).unwrap();
+ // Get initial time
+ let time0 = SystemTime::now();
+ // Send the message
+ let iov = [IoSlice::new(message)];
+ let flags = MsgFlags::empty();
+ let l = sendmsg(in_socket.as_raw_fd(), &iov, &[], flags, Some(&address))
+ .unwrap();
+ assert_eq!(message.len(), l);
+ // Receive the message
+ let mut buffer = vec![0u8; message.len()];
+ let mut cmsgspace = nix::cmsg_space!(TimeSpec);
+
+ let mut iov = [IoSliceMut::new(&mut buffer)];
+ let r = recvmsg::<()>(
+ in_socket.as_raw_fd(),
+ &mut iov,
+ Some(&mut cmsgspace),
+ flags,
+ )
+ .unwrap();
+ let rtime = match r.cmsgs().next() {
+ Some(ControlMessageOwned::ScmTimestampns(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_nsec() as u32);
+ assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
+ assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
+}
+
+// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+#[cfg(target_os = "linux")]
+#[test]
+fn test_recvmmsg_timestampns() {
+ use nix::sys::socket::*;
+ use nix::sys::time::*;
+ use std::io::{IoSlice, IoSliceMut};
+ 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::ReceiveTimestampns, &true).unwrap();
+ let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap();
+ bind(in_socket.as_raw_fd(), &localhost).unwrap();
+ let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).unwrap();
+ // Get initial time
+ let time0 = SystemTime::now();
+ // Send the message
+ let iov = [IoSlice::new(message)];
+ let flags = MsgFlags::empty();
+ let l = sendmsg(in_socket.as_raw_fd(), &iov, &[], flags, Some(&address))
+ .unwrap();
+ assert_eq!(message.len(), l);
+ // Receive the message
+ let mut buffer = vec![0u8; message.len()];
+ let cmsgspace = nix::cmsg_space!(TimeSpec);
+ let iov = vec![[IoSliceMut::new(&mut buffer)]];
+ let mut data = MultiHeaders::preallocate(1, Some(cmsgspace));
+ let r: Vec<RecvMsg<()>> =
+ recvmmsg(in_socket.as_raw_fd(), &mut data, iov.iter(), flags, None)
+ .unwrap()
+ .collect();
+ let rtime = match r[0].cmsgs().next() {
+ Some(ControlMessageOwned::ScmTimestampns(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_nsec() as u32);
+ assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
+ assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
+}
+
+// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+#[test]
+fn test_recvmsg_rxq_ovfl() {
+ use nix::sys::socket::sockopt::{RcvBuf, RxqOvfl};
+ use nix::sys::socket::*;
+ use nix::Error;
+ use std::io::{IoSlice, IoSliceMut};
+
+ let message = [0u8; 2048];
+ let bufsize = message.len() * 2;
+
+ let in_socket = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ let out_socket = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+
+ let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap();
+ bind(in_socket.as_raw_fd(), &localhost).unwrap();
+
+ let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).unwrap();
+ connect(out_socket.as_raw_fd(), &address).unwrap();
+
+ // Set SO_RXQ_OVFL flag.
+ setsockopt(&in_socket, RxqOvfl, &1).unwrap();
+
+ // Set the receiver buffer size to hold only 2 messages.
+ setsockopt(&in_socket, RcvBuf, &bufsize).unwrap();
+
+ let mut drop_counter = 0;
+
+ for _ in 0..2 {
+ let iov = [IoSlice::new(&message)];
+ let flags = MsgFlags::empty();
+
+ // Send the 3 messages (the receiver buffer can only hold 2 messages)
+ // to create an overflow.
+ for _ in 0..3 {
+ let l = sendmsg(
+ out_socket.as_raw_fd(),
+ &iov,
+ &[],
+ flags,
+ Some(&address),
+ )
+ .unwrap();
+ assert_eq!(message.len(), l);
+ }
+
+ // Receive the message and check the drop counter if any.
+ loop {
+ let mut buffer = vec![0u8; message.len()];
+ let mut cmsgspace = nix::cmsg_space!(u32);
+
+ let mut iov = [IoSliceMut::new(&mut buffer)];
+
+ match recvmsg::<()>(
+ in_socket.as_raw_fd(),
+ &mut iov,
+ Some(&mut cmsgspace),
+ MsgFlags::MSG_DONTWAIT,
+ ) {
+ Ok(r) => {
+ drop_counter = match r.cmsgs().next() {
+ Some(ControlMessageOwned::RxqOvfl(drop_counter)) => {
+ drop_counter
+ }
+ Some(_) => panic!("Unexpected control message"),
+ None => 0,
+ };
+ }
+ Err(Error::EAGAIN) => {
+ break;
+ }
+ _ => {
+ panic!("unknown recvmsg() error");
+ }
+ }
+ }
+ }
+
+ // One packet lost.
+ assert_eq!(drop_counter, 1);
+}
+
+#[cfg(any(target_os = "linux", target_os = "android",))]
+mod linux_errqueue {
+ use super::FromStr;
+ use nix::sys::socket::*;
+ use std::os::unix::io::AsRawFd;
+
+ // Send a UDP datagram to a bogus destination address and observe an ICMP error (v4).
+ //
+ // Disable the test on QEMU because QEMU emulation of IP_RECVERR is broken (as documented on PR
+ // #1514).
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_recverr_v4() {
+ #[repr(u8)]
+ enum IcmpTypes {
+ DestUnreach = 3, // ICMP_DEST_UNREACH
+ }
+ #[repr(u8)]
+ enum IcmpUnreachCodes {
+ PortUnreach = 3, // ICMP_PORT_UNREACH
+ }
+
+ test_recverr_impl::<sockaddr_in, _, _>(
+ "127.0.0.1:6800",
+ AddressFamily::Inet,
+ sockopt::Ipv4RecvErr,
+ libc::SO_EE_ORIGIN_ICMP,
+ IcmpTypes::DestUnreach as u8,
+ IcmpUnreachCodes::PortUnreach as u8,
+ // Closure handles protocol-specific testing and returns generic sock_extended_err for
+ // protocol-independent test impl.
+ |cmsg| {
+ if let ControlMessageOwned::Ipv4RecvErr(ext_err, err_addr) =
+ cmsg
+ {
+ if let Some(origin) = err_addr {
+ // Validate that our network error originated from 127.0.0.1:0.
+ assert_eq!(origin.sin_family, AddressFamily::Inet as _);
+ assert_eq!(
+ origin.sin_addr.s_addr,
+ u32::from_be(0x7f000001)
+ );
+ assert_eq!(origin.sin_port, 0);
+ } else {
+ panic!("Expected some error origin");
+ }
+ *ext_err
+ } else {
+ panic!("Unexpected control message {cmsg:?}");
+ }
+ },
+ )
+ }
+
+ // Essentially the same test as v4.
+ //
+ // Disable the test on QEMU because QEMU emulation of IPV6_RECVERR is broken (as documented on
+ // PR #1514).
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_recverr_v6() {
+ #[repr(u8)]
+ enum IcmpV6Types {
+ DestUnreach = 1, // ICMPV6_DEST_UNREACH
+ }
+ #[repr(u8)]
+ enum IcmpV6UnreachCodes {
+ PortUnreach = 4, // ICMPV6_PORT_UNREACH
+ }
+
+ test_recverr_impl::<sockaddr_in6, _, _>(
+ "[::1]:6801",
+ AddressFamily::Inet6,
+ sockopt::Ipv6RecvErr,
+ libc::SO_EE_ORIGIN_ICMP6,
+ IcmpV6Types::DestUnreach as u8,
+ IcmpV6UnreachCodes::PortUnreach as u8,
+ // Closure handles protocol-specific testing and returns generic sock_extended_err for
+ // protocol-independent test impl.
+ |cmsg| {
+ if let ControlMessageOwned::Ipv6RecvErr(ext_err, err_addr) =
+ cmsg
+ {
+ if let Some(origin) = err_addr {
+ // Validate that our network error originated from localhost:0.
+ assert_eq!(
+ origin.sin6_family,
+ AddressFamily::Inet6 as _
+ );
+ assert_eq!(
+ origin.sin6_addr.s6_addr,
+ std::net::Ipv6Addr::LOCALHOST.octets()
+ );
+ assert_eq!(origin.sin6_port, 0);
+ } else {
+ panic!("Expected some error origin");
+ }
+ *ext_err
+ } else {
+ panic!("Unexpected control message {cmsg:?}");
+ }
+ },
+ )
+ }
+
+ fn test_recverr_impl<SA, OPT, TESTF>(
+ sa: &str,
+ af: AddressFamily,
+ opt: OPT,
+ ee_origin: u8,
+ ee_type: u8,
+ ee_code: u8,
+ testf: TESTF,
+ ) where
+ OPT: SetSockOpt<Val = bool>,
+ TESTF: FnOnce(&ControlMessageOwned) -> libc::sock_extended_err,
+ {
+ use nix::errno::Errno;
+ use std::io::IoSliceMut;
+
+ const MESSAGE_CONTENTS: &str = "ABCDEF";
+ let std_sa = std::net::SocketAddr::from_str(sa).unwrap();
+ let sock_addr = SockaddrStorage::from(std_sa);
+ let sock = socket(af, SockType::Datagram, SockFlag::SOCK_CLOEXEC, None)
+ .unwrap();
+ setsockopt(&sock, opt, &true).unwrap();
+ if let Err(e) = sendto(
+ sock.as_raw_fd(),
+ MESSAGE_CONTENTS.as_bytes(),
+ &sock_addr,
+ MsgFlags::empty(),
+ ) {
+ assert_eq!(e, Errno::EADDRNOTAVAIL);
+ println!("{af:?} not available, skipping test.");
+ return;
+ }
+
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+ let mut cspace = cmsg_space!(libc::sock_extended_err, SA);
+
+ let msg = recvmsg(
+ sock.as_raw_fd(),
+ &mut iovec,
+ Some(&mut cspace),
+ MsgFlags::MSG_ERRQUEUE,
+ )
+ .unwrap();
+ // The sent message / destination associated with the error is returned:
+ assert_eq!(msg.bytes, MESSAGE_CONTENTS.as_bytes().len());
+ // recvmsg(2): "The original destination address of the datagram that caused the error is
+ // supplied via msg_name;" however, this is not literally true. E.g., an earlier version
+ // of this test used 0.0.0.0 (::0) as the destination address, which was mutated into
+ // 127.0.0.1 (::1).
+ assert_eq!(msg.address, Some(sock_addr));
+
+ // Check for expected control message.
+ let ext_err = match msg.cmsgs().next() {
+ Some(cmsg) => testf(&cmsg),
+ None => panic!("No control message"),
+ };
+
+ assert_eq!(ext_err.ee_errno, libc::ECONNREFUSED as u32);
+ assert_eq!(ext_err.ee_origin, ee_origin);
+ // ip(7): ee_type and ee_code are set from the type and code fields of the ICMP (ICMPv6)
+ // header.
+ assert_eq!(ext_err.ee_type, ee_type);
+ assert_eq!(ext_err.ee_code, ee_code);
+ // ip(7): ee_info contains the discovered MTU for EMSGSIZE errors.
+ assert_eq!(ext_err.ee_info, 0);
+
+ let bytes = msg.bytes;
+ assert_eq!(&buf[..bytes], MESSAGE_CONTENTS.as_bytes());
+ }
+}
+
+// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+#[cfg(target_os = "linux")]
+#[test]
+pub fn test_txtime() {
+ use nix::sys::socket::{
+ bind, recvmsg, sendmsg, setsockopt, socket, sockopt, ControlMessage,
+ MsgFlags, SockFlag, SockType, SockaddrIn,
+ };
+ use nix::sys::time::TimeValLike;
+ use nix::time::{clock_gettime, ClockId};
+
+ require_kernel_version!(test_txtime, ">= 5.8");
+
+ let sock_addr = SockaddrIn::from_str("127.0.0.1:6802").unwrap();
+
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+
+ let txtime_cfg = libc::sock_txtime {
+ clockid: libc::CLOCK_MONOTONIC,
+ flags: 0,
+ };
+ setsockopt(&ssock, sockopt::TxTime, &txtime_cfg).unwrap();
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+
+ let sbuf = [0u8; 2048];
+ let iov1 = [std::io::IoSlice::new(&sbuf)];
+
+ let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
+ let delay = std::time::Duration::from_secs(1).into();
+ let txtime = (now + delay).num_nanoseconds() as u64;
+
+ let cmsg = ControlMessage::TxTime(&txtime);
+ sendmsg(
+ ssock.as_raw_fd(),
+ &iov1,
+ &[cmsg],
+ MsgFlags::empty(),
+ Some(&sock_addr),
+ )
+ .unwrap();
+
+ let mut rbuf = [0u8; 2048];
+ let mut iov2 = [std::io::IoSliceMut::new(&mut rbuf)];
+ recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, None, MsgFlags::empty())
+ .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..0e34917325
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_sockopt.rs
@@ -0,0 +1,448 @@
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use crate::*;
+use nix::sys::socket::{
+ getsockopt, setsockopt, socket, sockopt, AddressFamily, SockFlag,
+ SockProtocol, SockType,
+};
+use rand::{thread_rng, Rng};
+use std::os::unix::io::AsRawFd;
+
+// NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not.
+#[cfg(any(target_os = "dragonfly", target_os = "freebsd",))]
+#[test]
+pub fn test_local_peercred_seqpacket() {
+ use nix::{
+ sys::socket::socketpair,
+ unistd::{Gid, Uid},
+ };
+
+ let (fd1, _fd2) = socketpair(
+ AddressFamily::Unix,
+ SockType::SeqPacket,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ let xucred = getsockopt(&fd1, sockopt::LocalPeerCred).unwrap();
+ assert_eq!(xucred.version(), 0);
+ assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
+ assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
+}
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+))]
+#[test]
+pub fn test_local_peercred_stream() {
+ use nix::{
+ sys::socket::socketpair,
+ unistd::{Gid, Uid},
+ };
+
+ let (fd1, _fd2) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ let xucred = getsockopt(&fd1, sockopt::LocalPeerCred).unwrap();
+ assert_eq!(xucred.version(), 0);
+ assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
+ assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
+}
+
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+#[test]
+pub fn test_local_peer_pid() {
+ use nix::sys::socket::socketpair;
+
+ let (fd1, _fd2) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ let pid = getsockopt(&fd1, sockopt::LocalPeerPid).unwrap();
+ assert_eq!(pid, std::process::id() as _);
+}
+
+#[cfg(target_os = "linux")]
+#[test]
+fn is_so_mark_functional() {
+ use nix::sys::socket::sockopt;
+
+ require_capability!("is_so_mark_functional", CAP_NET_ADMIN);
+
+ let s = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ setsockopt(&s, sockopt::Mark, &1337).unwrap();
+ let mark = getsockopt(&s, sockopt::Mark).unwrap();
+ assert_eq!(mark, 1337);
+}
+
+#[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);
+}
+
+#[test]
+fn test_so_tcp_maxseg() {
+ use nix::sys::socket::{accept, bind, connect, listen, SockaddrIn};
+ use nix::unistd::write;
+ use std::net::SocketAddrV4;
+ use std::str::FromStr;
+
+ let std_sa = SocketAddrV4::from_str("127.0.0.1:4001").unwrap();
+ let sock_addr = SockaddrIn::from(std_sa);
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+ listen(&rsock, 10).unwrap();
+ let initial = getsockopt(&rsock, sockopt::TcpMaxSeg).unwrap();
+ // Initial MSS is expected to be 536 (https://tools.ietf.org/html/rfc879#section-1) but some
+ // platforms keep it even lower. This might fail if you've tuned your initial MSS to be larger
+ // than 700
+ cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ let segsize: u32 = 873;
+ assert!(initial < segsize);
+ setsockopt(&rsock, sockopt::TcpMaxSeg, &segsize).unwrap();
+ } else {
+ assert!(initial < 700);
+ }
+ }
+
+ // Connect and check the MSS that was advertised
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ connect(ssock.as_raw_fd(), &sock_addr).unwrap();
+ let rsess = accept(rsock.as_raw_fd()).unwrap();
+ write(rsess, b"hello").unwrap();
+ let actual = getsockopt(&ssock, sockopt::TcpMaxSeg).unwrap();
+ // Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max
+ // TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary.
+ cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ assert!((segsize - 100) <= actual);
+ assert!(actual <= segsize);
+ } else {
+ assert!(initial < actual);
+ assert!(536 < actual);
+ }
+ }
+}
+
+#[test]
+fn test_so_type() {
+ let sockfd = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+
+ assert_eq!(Ok(SockType::Stream), getsockopt(&sockfd, sockopt::SockType));
+}
+
+/// getsockopt(_, sockopt::SockType) should gracefully handle unknown socket
+/// types. Regression test for https://github.com/nix-rust/nix/issues/1819
+#[cfg(any(target_os = "android", target_os = "linux",))]
+#[test]
+fn test_so_type_unknown() {
+ use nix::errno::Errno;
+ use std::os::unix::io::{FromRawFd, OwnedFd};
+
+ require_capability!("test_so_type", CAP_NET_RAW);
+ let raw_fd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_PACKET, 0) };
+ assert!(raw_fd >= 0, "Error opening socket: {}", nix::Error::last());
+ let sockfd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
+
+ assert_eq!(Err(Errno::EINVAL), getsockopt(&sockfd, sockopt::SockType));
+}
+
+// The CI doesn't supported getsockopt and setsockopt on emulated processors.
+// It's believed 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);
+}
+
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_bindtodevice() {
+ skip_if_not_root!("test_bindtodevice");
+
+ let fd = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+
+ let val = getsockopt(&fd, sockopt::BindToDevice).unwrap();
+ setsockopt(&fd, sockopt::BindToDevice, &val).unwrap();
+
+ assert_eq!(getsockopt(&fd, sockopt::BindToDevice).unwrap(), val);
+}
+
+#[test]
+fn test_so_tcp_keepalive() {
+ let fd = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ setsockopt(&fd, sockopt::KeepAlive, &true).unwrap();
+ assert!(getsockopt(&fd, sockopt::KeepAlive).unwrap());
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux"
+ ))]
+ {
+ let x = getsockopt(&fd, sockopt::TcpKeepIdle).unwrap();
+ setsockopt(&fd, sockopt::TcpKeepIdle, &(x + 1)).unwrap();
+ assert_eq!(getsockopt(&fd, sockopt::TcpKeepIdle).unwrap(), x + 1);
+
+ let x = getsockopt(&fd, sockopt::TcpKeepCount).unwrap();
+ setsockopt(&fd, sockopt::TcpKeepCount, &(x + 1)).unwrap();
+ assert_eq!(getsockopt(&fd, sockopt::TcpKeepCount).unwrap(), x + 1);
+
+ let x = getsockopt(&fd, sockopt::TcpKeepInterval).unwrap();
+ setsockopt(&fd, sockopt::TcpKeepInterval, &(x + 1)).unwrap();
+ assert_eq!(getsockopt(&fd, sockopt::TcpKeepInterval).unwrap(), x + 1);
+ }
+}
+
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(qemu, ignore)]
+fn test_get_mtu() {
+ use nix::sys::socket::{bind, connect, SockaddrIn};
+ use std::net::SocketAddrV4;
+ use std::str::FromStr;
+
+ let std_sa = SocketAddrV4::from_str("127.0.0.1:4001").unwrap();
+ let std_sb = SocketAddrV4::from_str("127.0.0.1:4002").unwrap();
+
+ let usock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ SockProtocol::Udp,
+ )
+ .unwrap();
+
+ // Bind and initiate connection
+ bind(usock.as_raw_fd(), &SockaddrIn::from(std_sa)).unwrap();
+ connect(usock.as_raw_fd(), &SockaddrIn::from(std_sb)).unwrap();
+
+ // Loopback connections have 2^16 - the maximum - MTU
+ assert_eq!(getsockopt(&usock, sockopt::IpMtu), Ok(u16::MAX as i32))
+}
+
+#[test]
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+fn test_ttl_opts() {
+ let fd4 = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ setsockopt(&fd4, sockopt::Ipv4Ttl, &1)
+ .expect("setting ipv4ttl on an inet socket should succeed");
+ let fd6 = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ setsockopt(&fd6, sockopt::Ipv6Ttl, &1)
+ .expect("setting ipv6ttl on an inet6 socket should succeed");
+}
+
+#[test]
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+fn test_dontfrag_opts() {
+ let fd4 = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ setsockopt(&fd4, sockopt::IpDontFrag, &true)
+ .expect("setting IP_DONTFRAG on an inet stream socket should succeed");
+ setsockopt(&fd4, sockopt::IpDontFrag, &false).expect(
+ "unsetting IP_DONTFRAG on an inet stream socket should succeed",
+ );
+ let fd4d = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ setsockopt(&fd4d, sockopt::IpDontFrag, &true).expect(
+ "setting IP_DONTFRAG on an inet datagram socket should succeed",
+ );
+ setsockopt(&fd4d, sockopt::IpDontFrag, &false).expect(
+ "unsetting IP_DONTFRAG on an inet datagram socket should succeed",
+ );
+}
+
+#[test]
+#[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+))]
+// Disable the test under emulation because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+fn test_v6dontfrag_opts() {
+ let fd6 = socket(
+ AddressFamily::Inet6,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ setsockopt(&fd6, sockopt::Ipv6DontFrag, &true).expect(
+ "setting IPV6_DONTFRAG on an inet6 stream socket should succeed",
+ );
+ setsockopt(&fd6, sockopt::Ipv6DontFrag, &false).expect(
+ "unsetting IPV6_DONTFRAG on an inet6 stream socket should succeed",
+ );
+ let fd6d = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ setsockopt(&fd6d, sockopt::Ipv6DontFrag, &true).expect(
+ "setting IPV6_DONTFRAG on an inet6 datagram socket should succeed",
+ );
+ setsockopt(&fd6d, sockopt::Ipv6DontFrag, &false).expect(
+ "unsetting IPV6_DONTFRAG on an inet6 datagram socket should succeed",
+ );
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_so_priority() {
+ let fd = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ let priority = 3;
+ setsockopt(&fd, sockopt::Priority, &priority).unwrap();
+ assert_eq!(getsockopt(&fd, sockopt::Priority).unwrap(), priority);
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_ip_tos() {
+ let fd = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ let tos = 0x80; // CS4
+ setsockopt(&fd, sockopt::IpTos, &tos).unwrap();
+ assert_eq!(getsockopt(&fd, sockopt::IpTos).unwrap(), tos);
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+// Disable the test under emulation because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+fn test_ipv6_tclass() {
+ let fd = socket(
+ AddressFamily::Inet6,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ let class = 0x80; // CS4
+ setsockopt(&fd, sockopt::Ipv6TClass, &class).unwrap();
+ assert_eq!(getsockopt(&fd, sockopt::Ipv6TClass).unwrap(), class);
+}
diff --git a/third_party/rust/nix/test/sys/test_stat.rs b/third_party/rust/nix/test/sys/test_stat.rs
new file mode 100644
index 0000000000..426b4b6588
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_stat.rs
@@ -0,0 +1,29 @@
+// The conversion is not useless on all platforms.
+#[allow(clippy::useless_conversion)]
+#[cfg(target_os = "freebsd")]
+#[test]
+fn test_chflags() {
+ use nix::{
+ sys::stat::{fstat, FileFlag},
+ unistd::chflags,
+ };
+ use std::os::unix::io::AsRawFd;
+ use tempfile::NamedTempFile;
+
+ let f = NamedTempFile::new().unwrap();
+
+ let initial = FileFlag::from_bits_truncate(
+ fstat(f.as_raw_fd()).unwrap().st_flags.into(),
+ );
+ // UF_OFFLINE is preserved by all FreeBSD file systems, but not interpreted
+ // in any way, so it's handy for testing.
+ let commanded = initial ^ FileFlag::UF_OFFLINE;
+
+ chflags(f.path(), commanded).unwrap();
+
+ let changed = FileFlag::from_bits_truncate(
+ fstat(f.as_raw_fd()).unwrap().st_flags.into(),
+ );
+
+ assert_eq!(commanded, changed);
+}
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..2897366eff
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_sysinfo.rs
@@ -0,0 +1,20 @@
+use nix::sys::sysinfo::*;
+
+#[test]
+fn sysinfo_works() {
+ let info = sysinfo().unwrap();
+
+ let (l1, l5, l15) = info.load_average();
+ assert!(l1 >= 0.0);
+ assert!(l5 >= 0.0);
+ assert!(l15 >= 0.0);
+
+ info.uptime(); // just test Duration construction
+
+ assert!(
+ info.swap_free() <= info.swap_total(),
+ "more swap available than installed (free: {}, total: {})",
+ info.swap_free(),
+ info.swap_total()
+ );
+}
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..83919378a7
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_termios.rs
@@ -0,0 +1,107 @@
+use std::os::unix::io::{AsFd, AsRawFd};
+use tempfile::tempfile;
+
+use nix::errno::Errno;
+use nix::fcntl;
+use nix::pty::openpty;
+use nix::sys::termios::{self, tcgetattr, LocalFlags, OutputFlags};
+use nix::unistd::{read, write};
+
+/// Helper function analogous to `std::io::Write::write_all`, but for `Fd`s
+fn write_all<Fd: AsFd>(f: Fd, buf: &[u8]) {
+ let mut len = 0;
+ while len < buf.len() {
+ len += write(f.as_fd().as_raw_fd(), &buf[len..]).unwrap();
+ }
+}
+
+// Test tcgetattr on a terminal
+#[test]
+fn test_tcgetattr_pty() {
+ // openpty uses ptname(3) internally
+ let _m = crate::PTSNAME_MTX.lock();
+
+ let pty = openpty(None, None).expect("openpty failed");
+ termios::tcgetattr(&pty.slave).unwrap();
+}
+
+// Test tcgetattr on something that isn't a terminal
+#[test]
+fn test_tcgetattr_enotty() {
+ let file = tempfile().unwrap();
+ assert_eq!(termios::tcgetattr(&file).err(), Some(Errno::ENOTTY));
+}
+
+// Test modifying output flags
+#[test]
+fn test_output_flags() {
+ // openpty uses ptname(3) internally
+ let _m = crate::PTSNAME_MTX.lock();
+
+ // Open one pty to get attributes for the second one
+ let mut termios = {
+ let pty = openpty(None, None).expect("openpty failed");
+ tcgetattr(&pty.slave).expect("tcgetattr failed")
+ };
+
+ // Make sure postprocessing '\r' isn't specified by default or this test is useless.
+ assert!(!termios
+ .output_flags
+ .contains(OutputFlags::OPOST | OutputFlags::OCRNL));
+
+ // Specify that '\r' characters should be transformed to '\n'
+ // OPOST is specified to enable post-processing
+ termios
+ .output_flags
+ .insert(OutputFlags::OPOST | OutputFlags::OCRNL);
+
+ // Open a pty
+ let pty = openpty(None, &termios).unwrap();
+
+ // Write into the master
+ let string = "foofoofoo\r";
+ write_all(&pty.master, string.as_bytes());
+
+ // Read from the slave verifying that the output has been properly transformed
+ let mut buf = [0u8; 10];
+ crate::read_exact(&pty.slave, &mut buf);
+ let transformed_string = "foofoofoo\n";
+ assert_eq!(&buf, transformed_string.as_bytes());
+}
+
+// Test modifying local flags
+#[test]
+fn test_local_flags() {
+ // openpty uses ptname(3) internally
+ let _m = crate::PTSNAME_MTX.lock();
+
+ // Open one pty to get attributes for the second one
+ let mut termios = {
+ let pty = openpty(None, None).unwrap();
+ tcgetattr(&pty.slave).unwrap()
+ };
+
+ // Make sure echo is specified by default or this test is useless.
+ assert!(termios.local_flags.contains(LocalFlags::ECHO));
+
+ // Disable local echo
+ termios.local_flags.remove(LocalFlags::ECHO);
+
+ // Open a new pty with our modified termios settings
+ let pty = openpty(None, &termios).unwrap();
+
+ // Set the master is in nonblocking mode or reading will never return.
+ let flags = fcntl::fcntl(pty.master.as_raw_fd(), fcntl::F_GETFL).unwrap();
+ let new_flags =
+ fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK;
+ fcntl::fcntl(pty.master.as_raw_fd(), fcntl::F_SETFL(new_flags)).unwrap();
+
+ // Write into the master
+ let string = "foofoofoo\r";
+ write_all(&pty.master, string.as_bytes());
+
+ // Try to read from the master, which should not have anything as echoing was disabled.
+ let mut buf = [0u8; 10];
+ let read = read(pty.master.as_raw_fd(), &mut buf).unwrap_err();
+ assert_eq!(read, Errno::EAGAIN);
+}
diff --git a/third_party/rust/nix/test/sys/test_timerfd.rs b/third_party/rust/nix/test/sys/test_timerfd.rs
new file mode 100644
index 0000000000..08e292106c
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_timerfd.rs
@@ -0,0 +1,69 @@
+use nix::sys::time::{TimeSpec, TimeValLike};
+use nix::sys::timerfd::{
+ ClockId, Expiration, TimerFd, TimerFlags, TimerSetTimeFlags,
+};
+use std::time::Instant;
+
+#[test]
+pub fn test_timerfd_oneshot() {
+ let timer =
+ TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
+
+ let before = Instant::now();
+
+ timer
+ .set(
+ Expiration::OneShot(TimeSpec::seconds(1)),
+ TimerSetTimeFlags::empty(),
+ )
+ .unwrap();
+
+ timer.wait().unwrap();
+
+ let millis = before.elapsed().as_millis();
+ assert!(millis > 900);
+}
+
+#[test]
+pub fn test_timerfd_interval() {
+ let timer =
+ TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
+
+ let before = Instant::now();
+ timer
+ .set(
+ Expiration::IntervalDelayed(
+ TimeSpec::seconds(1),
+ TimeSpec::seconds(2),
+ ),
+ TimerSetTimeFlags::empty(),
+ )
+ .unwrap();
+
+ timer.wait().unwrap();
+
+ let start_delay = before.elapsed().as_millis();
+ assert!(start_delay > 900);
+
+ timer.wait().unwrap();
+
+ let interval_delay = before.elapsed().as_millis();
+ assert!(interval_delay > 2900);
+}
+
+#[test]
+pub fn test_timerfd_unset() {
+ let timer =
+ TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
+
+ timer
+ .set(
+ Expiration::OneShot(TimeSpec::seconds(1)),
+ TimerSetTimeFlags::empty(),
+ )
+ .unwrap();
+
+ timer.unset().unwrap();
+
+ assert!(timer.get().unwrap().is_none());
+}
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..fc09465f19
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_uio.rs
@@ -0,0 +1,277 @@
+use nix::sys::uio::*;
+use nix::unistd::*;
+use rand::distributions::Alphanumeric;
+use rand::{thread_rng, Rng};
+use std::fs::OpenOptions;
+use std::io::IoSlice;
+use std::os::unix::io::{FromRawFd, OwnedFd};
+use std::{cmp, iter};
+
+#[cfg(not(target_os = "redox"))]
+use std::io::IoSliceMut;
+
+use tempfile::tempdir;
+#[cfg(not(target_os = "redox"))]
+use tempfile::tempfile;
+
+#[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)
+ .map(char::from)
+ .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(IoSlice::new(b));
+ consumed += slice_len;
+ }
+ let (reader, writer) = pipe().expect("Couldn't create pipe");
+ // FileDesc will close its filedesc (reader).
+ let mut read_buf: Vec<u8> = iter::repeat(0u8).take(128 * 16).collect();
+
+ // Temporary workaround to cope with the existing RawFd pipe(2), should be
+ // removed when pipe(2) becomes I/O-safe.
+ let writer = unsafe { OwnedFd::from_raw_fd(writer) };
+
+ // Blocking io, should write all data.
+ let write_res = writev(&writer, &iovecs);
+ let written = write_res.expect("couldn't write");
+ // Check whether we written all data
+ assert_eq!(to_write.len(), written);
+ let read_res = read(reader, &mut read_buf[..]);
+ let read = read_res.expect("couldn't read");
+ // 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);
+ close(reader).expect("closed reader");
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_readv() {
+ let s: String = thread_rng()
+ .sample_iter(&Alphanumeric)
+ .map(char::from)
+ .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(IoSliceMut::new(&mut v[..]));
+ }
+ let (reader, writer) = pipe().expect("couldn't create pipe");
+ // Blocking io, should write all data.
+ write(writer, &to_write).expect("write failed");
+
+ // Temporary workaround to cope with the existing RawFd pipe(2), should be
+ // removed when pipe(2) becomes I/O-safe.
+ let reader = unsafe { OwnedFd::from_raw_fd(reader) };
+
+ let read = readv(&reader, &mut iovecs[..]).expect("read failed");
+ // 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.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);
+ close(writer).expect("couldn't close writer");
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_pwrite() {
+ use std::io::Read;
+
+ let mut file = tempfile().unwrap();
+ let buf = [1u8; 8];
+ assert_eq!(Ok(8), pwrite(&file, &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, &mut buf, 16));
+ let expected: Vec<_> = (16..32).collect();
+ assert_eq!(&buf[..], &expected[..]);
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+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 = [
+ IoSlice::new(&to_write[0..17]),
+ IoSlice::new(&to_write[17..64]),
+ IoSlice::new(&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, &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(not(any(target_os = "redox", target_os = "haiku")))]
+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 mut iovecs: Vec<_> = buffers
+ .iter_mut()
+ .map(|buf| IoSliceMut::new(&mut buf[..]))
+ .collect();
+ assert_eq!(Ok(100), preadv(&file, &mut iovecs, 100));
+ }
+
+ let all = buffers.concat();
+ assert_eq!(all, expected);
+}
+
+#[test]
+#[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
+// uclibc doesn't implement process_vm_readv
+// qemu-user doesn't implement process_vm_readv/writev on most arches
+#[cfg_attr(qemu, ignore)]
+fn test_process_vm_readv() {
+ use crate::*;
+ use nix::sys::signal::*;
+ use nix::sys::wait::*;
+ use nix::unistd::ForkResult::*;
+
+ require_capability!("test_process_vm_readv", CAP_SYS_PTRACE);
+ let _m = crate::FORK_MTX.lock();
+
+ // Pre-allocate memory in the child, since allocation isn't safe
+ // post-fork (~= async-signal-safe)
+ let mut vector = vec![1u8, 2, 3, 4, 5];
+
+ let (r, w) = pipe().unwrap();
+ match unsafe { 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,
+ &mut [IoSliceMut::new(&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 {
+ 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..d472f1ec19
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_wait.rs
@@ -0,0 +1,257 @@
+use libc::_exit;
+use nix::errno::Errno;
+use nix::sys::signal::*;
+use nix::sys::wait::*;
+use nix::unistd::ForkResult::*;
+use nix::unistd::*;
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_wait_signal() {
+ let _m = crate::FORK_MTX.lock();
+
+ // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe.
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ 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]
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ //target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
+fn test_waitid_signal() {
+ let _m = crate::FORK_MTX.lock();
+
+ // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe.
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => {
+ pause();
+ unsafe { _exit(123) }
+ }
+ Parent { child } => {
+ kill(child, Some(SIGKILL)).expect("Error: Kill Failed");
+ assert_eq!(
+ waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+ Ok(WaitStatus::Signaled(child, SIGKILL, false)),
+ );
+ }
+ }
+}
+
+#[test]
+fn test_wait_exit() {
+ let _m = crate::FORK_MTX.lock();
+
+ // Safe: Child only calls `_exit`, which is async-signal-safe.
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => unsafe {
+ _exit(12);
+ },
+ Parent { child } => {
+ assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 12)));
+ }
+ }
+}
+
+#[cfg(not(target_os = "haiku"))]
+#[test]
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
+fn test_waitid_exit() {
+ let _m = crate::FORK_MTX.lock();
+
+ // Safe: Child only calls `_exit`, which is async-signal-safe.
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => unsafe {
+ _exit(12);
+ },
+ Parent { child } => {
+ assert_eq!(
+ waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+ 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(Errno::EINVAL));
+}
+
+#[test]
+fn test_waitstatus_pid() {
+ let _m = crate::FORK_MTX.lock();
+
+ match unsafe { fork() }.unwrap() {
+ Child => unsafe { _exit(0) },
+ Parent { child } => {
+ let status = waitpid(child, None).unwrap();
+ assert_eq!(status.pid(), Some(child));
+ }
+ }
+}
+
+#[test]
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+fn test_waitid_pid() {
+ let _m = crate::FORK_MTX.lock();
+
+ match unsafe { fork() }.unwrap() {
+ Child => unsafe { _exit(0) },
+ Parent { child } => {
+ let status = waitid(Id::Pid(child), WaitPidFlag::WEXITED).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 crate::*;
+ use libc::_exit;
+ use nix::sys::ptrace::{self, Event, Options};
+ use nix::sys::signal::*;
+ use nix::sys::wait::*;
+ use nix::unistd::ForkResult::*;
+ use nix::unistd::*;
+
+ 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_wait_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
+ ptrace::setoptions(
+ child,
+ Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT,
+ )
+ .expect("setoptions failed");
+
+ // First, stop on the next system call, which will be exit()
+ ptrace::syscall(child, None).expect("syscall failed");
+ assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
+ // Then get the ptrace event for the process exiting
+ ptrace::cont(child, None).expect("cont failed");
+ 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
+ ptrace::cont(child, None).expect("cont failed");
+ assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0)));
+ }
+
+ #[cfg(not(target_env = "uclibc"))]
+ fn ptrace_waitid_parent(child: Pid) {
+ // Wait for the raised SIGTRAP
+ //
+ // Unlike waitpid(), waitid() can distinguish trap events from regular
+ // stop events, so unlike ptrace_wait_parent(), we get a PtraceEvent here
+ assert_eq!(
+ waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+ Ok(WaitStatus::PtraceEvent(child, SIGTRAP, 0)),
+ );
+ // We want to test a syscall stop and a PTRACE_EVENT stop
+ ptrace::setoptions(
+ child,
+ Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT,
+ )
+ .expect("setopts failed");
+
+ // First, stop on the next system call, which will be exit()
+ ptrace::syscall(child, None).expect("syscall failed");
+ assert_eq!(
+ waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+ Ok(WaitStatus::PtraceSyscall(child)),
+ );
+ // Then get the ptrace event for the process exiting
+ ptrace::cont(child, None).expect("cont failed");
+ assert_eq!(
+ waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+ Ok(WaitStatus::PtraceEvent(
+ child,
+ SIGTRAP,
+ Event::PTRACE_EVENT_EXIT as i32
+ )),
+ );
+ // Finally get the normal wait() result, now that the process has exited
+ ptrace::cont(child, None).expect("cont failed");
+ assert_eq!(
+ waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+ Ok(WaitStatus::Exited(child, 0)),
+ );
+ }
+
+ #[test]
+ fn test_wait_ptrace() {
+ require_capability!("test_wait_ptrace", CAP_SYS_PTRACE);
+ let _m = crate::FORK_MTX.lock();
+
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => ptrace_child(),
+ Parent { child } => ptrace_wait_parent(child),
+ }
+ }
+
+ #[test]
+ #[cfg(not(target_env = "uclibc"))]
+ fn test_waitid_ptrace() {
+ require_capability!("test_waitid_ptrace", CAP_SYS_PTRACE);
+ let _m = crate::FORK_MTX.lock();
+
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => ptrace_child(),
+ Parent { child } => ptrace_waitid_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..7e73bb3056
--- /dev/null
+++ b/third_party/rust/nix/test/test.rs
@@ -0,0 +1,120 @@
+#[macro_use]
+extern crate cfg_if;
+#[cfg_attr(not(any(target_os = "redox", target_os = "haiku")), macro_use)]
+extern crate nix;
+
+mod common;
+mod sys;
+#[cfg(not(target_os = "redox"))]
+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;
+#[cfg(not(target_os = "redox"))]
+mod test_net;
+mod test_nix_path;
+#[cfg(target_os = "freebsd")]
+mod test_nmount;
+mod test_poll;
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+mod test_pty;
+mod test_resource;
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ all(target_os = "freebsd", fbsd14),
+ target_os = "linux"
+))]
+mod test_sched;
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"
+))]
+mod test_sendfile;
+mod test_stat;
+mod test_time;
+#[cfg(all(
+ any(
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd"
+ ),
+ feature = "time",
+ feature = "signal"
+))]
+mod test_timer;
+mod test_unistd;
+
+use nix::unistd::{chdir, getcwd, read};
+use parking_lot::{Mutex, RwLock, RwLockWriteGuard};
+use std::os::unix::io::{AsFd, AsRawFd};
+use std::path::PathBuf;
+
+/// Helper function analogous to `std::io::Read::read_exact`, but for `Fd`s
+fn read_exact<Fd: AsFd>(f: Fd, 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.as_fd().as_raw_fd(), remaining).unwrap();
+ }
+}
+
+/// Any test that creates child processes must grab this mutex, regardless
+/// of what it does with those children.
+pub static FORK_MTX: std::sync::Mutex<()> = std::sync::Mutex::new(());
+/// Any test that changes the process's current working directory must grab
+/// the RwLock exclusively. Any process that cares about the current
+/// working directory must grab it shared.
+pub static CWD_LOCK: RwLock<()> = RwLock::new(());
+/// Any test that changes the process's supplementary groups must grab this
+/// mutex
+pub static GROUPS_MTX: Mutex<()> = Mutex::new(());
+/// Any tests that loads or unloads kernel modules must grab this mutex
+pub static KMOD_MTX: Mutex<()> = Mutex::new(());
+/// Any test that calls ptsname(3) must grab this mutex.
+pub static PTSNAME_MTX: Mutex<()> = Mutex::new(());
+/// Any test that alters signal handling must grab this mutex.
+pub static SIGNAL_MTX: Mutex<()> = Mutex::new(());
+
+/// RAII object that restores a test's original directory on drop
+struct DirRestore<'a> {
+ d: PathBuf,
+ _g: RwLockWriteGuard<'a, ()>,
+}
+
+impl<'a> DirRestore<'a> {
+ fn new() -> Self {
+ let guard = crate::CWD_LOCK.write();
+ DirRestore {
+ _g: guard,
+ d: getcwd().unwrap(),
+ }
+ }
+}
+
+impl<'a> Drop for DirRestore<'a> {
+ fn drop(&mut self) {
+ let r = chdir(&self.d);
+ if std::thread::panicking() {
+ r.unwrap();
+ }
+ }
+}
diff --git a/third_party/rust/nix/test/test_clearenv.rs b/third_party/rust/nix/test/test_clearenv.rs
new file mode 100644
index 0000000000..28a7768049
--- /dev/null
+++ b/third_party/rust/nix/test/test_clearenv.rs
@@ -0,0 +1,9 @@
+use std::env;
+
+#[test]
+fn clearenv() {
+ env::set_var("FOO", "BAR");
+ unsafe { nix::env::clearenv() }.unwrap();
+ assert_eq!(env::var("FOO").unwrap_err(), env::VarError::NotPresent);
+ assert_eq!(env::vars().count(), 0);
+}
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..2af4aa5c0a
--- /dev/null
+++ b/third_party/rust/nix/test/test_dir.rs
@@ -0,0 +1,65 @@
+use nix::dir::{Dir, Type};
+use nix::fcntl::OFlag;
+use nix::sys::stat::Mode;
+use std::fs::File;
+use tempfile::tempdir;
+
+#[cfg(test)]
+fn flags() -> OFlag {
+ #[cfg(target_os = "illumos")]
+ let f = OFlag::O_RDONLY | OFlag::O_CLOEXEC;
+
+ #[cfg(not(target_os = "illumos"))]
+ let f = OFlag::O_RDONLY | OFlag::O_CLOEXEC | OFlag::O_DIRECTORY;
+
+ f
+}
+
+#[test]
+#[allow(clippy::unnecessary_sort_by)] // False positive
+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(), flags(), 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(), flags(), 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();
+ let entries3: Vec<_> = dir
+ .into_iter()
+ .map(|e| e.unwrap().file_name().to_owned())
+ .collect();
+ assert_eq!(entries1, entries2);
+ assert_eq!(entries2, entries3);
+}
+
+#[cfg(not(target_os = "haiku"))]
+#[test]
+fn ebadf() {
+ assert_eq!(Dir::from_fd(-1).unwrap_err(), nix::Error::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..5fef04ba9b
--- /dev/null
+++ b/third_party/rust/nix/test/test_fcntl.rs
@@ -0,0 +1,574 @@
+#[cfg(not(target_os = "redox"))]
+use nix::errno::*;
+#[cfg(not(target_os = "redox"))]
+use nix::fcntl::{open, readlink, OFlag};
+#[cfg(not(target_os = "redox"))]
+use nix::fcntl::{openat, readlinkat, renameat};
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ any(
+ target_arch = "x86_64",
+ target_arch = "x32",
+ target_arch = "powerpc",
+ target_arch = "s390x"
+ )
+))]
+use nix::fcntl::{renameat2, RenameFlags};
+#[cfg(not(target_os = "redox"))]
+use nix::sys::stat::Mode;
+#[cfg(not(target_os = "redox"))]
+use nix::unistd::{close, read};
+#[cfg(not(target_os = "redox"))]
+use std::fs::File;
+#[cfg(not(target_os = "redox"))]
+use std::io::prelude::*;
+#[cfg(not(target_os = "redox"))]
+use std::os::unix::fs;
+#[cfg(not(target_os = "redox"))]
+use tempfile::NamedTempFile;
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+// QEMU does not handle openat well enough to satisfy this test
+// https://gitlab.com/qemu-project/qemu/-/issues/829
+#[cfg_attr(qemu, ignore)]
+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]
+#[cfg(not(target_os = "redox"))]
+fn test_renameat() {
+ let old_dir = tempfile::tempdir().unwrap();
+ let old_dirfd =
+ open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let old_path = old_dir.path().join("old");
+ File::create(old_path).unwrap();
+ let new_dir = tempfile::tempdir().unwrap();
+ let new_dirfd =
+ open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
+ assert_eq!(
+ renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
+ Errno::ENOENT
+ );
+ close(old_dirfd).unwrap();
+ close(new_dirfd).unwrap();
+ assert!(new_dir.path().join("new").exists());
+}
+
+#[test]
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ any(
+ target_arch = "x86_64",
+ target_arch = "x32",
+ target_arch = "powerpc",
+ target_arch = "s390x"
+ )
+))]
+fn test_renameat2_behaves_like_renameat_with_no_flags() {
+ let old_dir = tempfile::tempdir().unwrap();
+ let old_dirfd =
+ open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let old_path = old_dir.path().join("old");
+ File::create(old_path).unwrap();
+ let new_dir = tempfile::tempdir().unwrap();
+ let new_dirfd =
+ open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ renameat2(
+ Some(old_dirfd),
+ "old",
+ Some(new_dirfd),
+ "new",
+ RenameFlags::empty(),
+ )
+ .unwrap();
+ assert_eq!(
+ renameat2(
+ Some(old_dirfd),
+ "old",
+ Some(new_dirfd),
+ "new",
+ RenameFlags::empty()
+ )
+ .unwrap_err(),
+ Errno::ENOENT
+ );
+ close(old_dirfd).unwrap();
+ close(new_dirfd).unwrap();
+ assert!(new_dir.path().join("new").exists());
+}
+
+#[test]
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ any(
+ target_arch = "x86_64",
+ target_arch = "x32",
+ target_arch = "powerpc",
+ target_arch = "s390x"
+ )
+))]
+fn test_renameat2_exchange() {
+ let old_dir = tempfile::tempdir().unwrap();
+ let old_dirfd =
+ open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let old_path = old_dir.path().join("old");
+ {
+ let mut old_f = File::create(&old_path).unwrap();
+ old_f.write_all(b"old").unwrap();
+ }
+ let new_dir = tempfile::tempdir().unwrap();
+ let new_dirfd =
+ open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let new_path = new_dir.path().join("new");
+ {
+ let mut new_f = File::create(&new_path).unwrap();
+ new_f.write_all(b"new").unwrap();
+ }
+ renameat2(
+ Some(old_dirfd),
+ "old",
+ Some(new_dirfd),
+ "new",
+ RenameFlags::RENAME_EXCHANGE,
+ )
+ .unwrap();
+ let mut buf = String::new();
+ let mut new_f = File::open(&new_path).unwrap();
+ new_f.read_to_string(&mut buf).unwrap();
+ assert_eq!(buf, "old");
+ buf = "".to_string();
+ let mut old_f = File::open(&old_path).unwrap();
+ old_f.read_to_string(&mut buf).unwrap();
+ assert_eq!(buf, "new");
+ close(old_dirfd).unwrap();
+ close(new_dirfd).unwrap();
+}
+
+#[test]
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ any(
+ target_arch = "x86_64",
+ target_arch = "x32",
+ target_arch = "powerpc",
+ target_arch = "s390x"
+ )
+))]
+fn test_renameat2_noreplace() {
+ let old_dir = tempfile::tempdir().unwrap();
+ let old_dirfd =
+ open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let old_path = old_dir.path().join("old");
+ File::create(old_path).unwrap();
+ let new_dir = tempfile::tempdir().unwrap();
+ let new_dirfd =
+ open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let new_path = new_dir.path().join("new");
+ File::create(new_path).unwrap();
+ assert_eq!(
+ renameat2(
+ Some(old_dirfd),
+ "old",
+ Some(new_dirfd),
+ "new",
+ RenameFlags::RENAME_NOREPLACE
+ )
+ .unwrap_err(),
+ Errno::EEXIST
+ );
+ close(old_dirfd).unwrap();
+ close(new_dirfd).unwrap();
+ assert!(new_dir.path().join("new").exists());
+ assert!(old_dir.path().join("old").exists());
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+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 expected_dir = src.to_str().unwrap();
+
+ assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
+ assert_eq!(
+ readlinkat(dirfd, "b").unwrap().to_str().unwrap(),
+ expected_dir
+ );
+}
+
+/// This test creates a temporary file containing the contents
+/// 'foobarbaz' and uses the `copy_file_range` call to transfer
+/// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
+/// resulting file is read and should contain the contents `bar`.
+/// The from_offset should be updated by the call to reflect
+/// the 3 bytes read (6).
+#[cfg(any(
+ target_os = "linux",
+ // Not available until FreeBSD 13.0
+ all(target_os = "freebsd", fbsd14),
+ target_os = "android"
+))]
+#[test]
+// QEMU does not support copy_file_range. Skip under qemu
+#[cfg_attr(qemu, ignore)]
+fn test_copy_file_range() {
+ use nix::fcntl::copy_file_range;
+ use std::os::unix::io::AsFd;
+
+ const CONTENTS: &[u8] = b"foobarbaz";
+
+ let mut tmp1 = tempfile::tempfile().unwrap();
+ let mut tmp2 = tempfile::tempfile().unwrap();
+
+ tmp1.write_all(CONTENTS).unwrap();
+ tmp1.flush().unwrap();
+
+ let mut from_offset: i64 = 3;
+ copy_file_range(
+ tmp1.as_fd(),
+ Some(&mut from_offset),
+ tmp2.as_fd(),
+ None,
+ 3,
+ )
+ .unwrap();
+
+ let mut res: String = String::new();
+ tmp2.rewind().unwrap();
+ tmp2.read_to_string(&mut res).unwrap();
+
+ assert_eq!(res, String::from("bar"));
+ assert_eq!(from_offset, 6);
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+mod linux_android {
+ use libc::loff_t;
+ use std::io::prelude::*;
+ use std::io::IoSlice;
+ use std::os::unix::prelude::*;
+
+ use nix::fcntl::*;
+ use nix::unistd::{close, pipe, read, write};
+
+ use tempfile::tempfile;
+ #[cfg(target_os = "linux")]
+ use tempfile::NamedTempFile;
+
+ use crate::*;
+
+ #[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 iovecs = [IoSlice::new(&buf1[0..3]), IoSlice::new(&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();
+ }
+
+ #[cfg(target_os = "linux")]
+ #[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());
+ }
+
+ // The tests below are disabled for the listed targets
+ // due to OFD locks not being available in the kernel/libc
+ // versions used in the CI environment, probably because
+ // they run under QEMU.
+
+ #[test]
+ #[cfg(all(target_os = "linux", not(target_env = "musl")))]
+ #[cfg_attr(target_env = "uclibc", ignore)] // uclibc doesn't support OFD locks, but the test should still compile
+ fn test_ofd_write_lock() {
+ use nix::sys::stat::fstat;
+ use std::mem;
+
+ let tmp = NamedTempFile::new().unwrap();
+
+ let fd = tmp.as_raw_fd();
+ let statfs = nix::sys::statfs::fstatfs(tmp.as_file()).unwrap();
+ if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC {
+ // OverlayFS is a union file system. It returns one inode value in
+ // stat(2), but a different one shows up in /proc/locks. So we must
+ // skip the test.
+ skip!("/proc/locks does not work on overlayfs");
+ }
+ let inode = fstat(fd).expect("fstat failed").st_ino as usize;
+
+ let mut flock: libc::flock = unsafe {
+ mem::zeroed() // required for Linux/mips
+ };
+ flock.l_type = libc::F_WRLCK as libc::c_short;
+ flock.l_whence = libc::SEEK_SET as libc::c_short;
+ flock.l_start = 0;
+ flock.l_len = 0;
+ flock.l_pid = 0;
+ fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
+ assert_eq!(
+ Some(("OFDLCK".to_string(), "WRITE".to_string())),
+ lock_info(inode)
+ );
+
+ flock.l_type = libc::F_UNLCK as libc::c_short;
+ fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write unlock failed");
+ assert_eq!(None, lock_info(inode));
+ }
+
+ #[test]
+ #[cfg(all(target_os = "linux", not(target_env = "musl")))]
+ #[cfg_attr(target_env = "uclibc", ignore)] // uclibc doesn't support OFD locks, but the test should still compile
+ fn test_ofd_read_lock() {
+ use nix::sys::stat::fstat;
+ use std::mem;
+
+ let tmp = NamedTempFile::new().unwrap();
+
+ let fd = tmp.as_raw_fd();
+ let statfs = nix::sys::statfs::fstatfs(tmp.as_file()).unwrap();
+ if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC {
+ // OverlayFS is a union file system. It returns one inode value in
+ // stat(2), but a different one shows up in /proc/locks. So we must
+ // skip the test.
+ skip!("/proc/locks does not work on overlayfs");
+ }
+ let inode = fstat(fd).expect("fstat failed").st_ino as usize;
+
+ let mut flock: libc::flock = unsafe {
+ mem::zeroed() // required for Linux/mips
+ };
+ flock.l_type = libc::F_RDLCK as libc::c_short;
+ flock.l_whence = libc::SEEK_SET as libc::c_short;
+ flock.l_start = 0;
+ flock.l_len = 0;
+ flock.l_pid = 0;
+ fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
+ assert_eq!(
+ Some(("OFDLCK".to_string(), "READ".to_string())),
+ lock_info(inode)
+ );
+
+ flock.l_type = libc::F_UNLCK as libc::c_short;
+ fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read unlock failed");
+ assert_eq!(None, lock_info(inode));
+ }
+
+ #[cfg(all(target_os = "linux", not(target_env = "musl")))]
+ fn lock_info(inode: usize) -> Option<(String, String)> {
+ use std::{fs::File, io::BufReader};
+
+ let file = File::open("/proc/locks").expect("open /proc/locks failed");
+ let buf = BufReader::new(file);
+
+ for line in buf.lines() {
+ let line = line.unwrap();
+ let parts: Vec<_> = line.split_whitespace().collect();
+ let lock_type = parts[1];
+ let lock_access = parts[3];
+ let ino_parts: Vec<_> = parts[5].split(':').collect();
+ let ino: usize = ino_parts[2].parse().unwrap();
+ if ino == inode {
+ return Some((lock_type.to_string(), lock_access.to_string()));
+ }
+ }
+ None
+ }
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "wasi",
+ target_env = "uclibc",
+ target_os = "freebsd"
+))]
+mod test_posix_fadvise {
+
+ use nix::errno::Errno;
+ use nix::fcntl::*;
+ use nix::unistd::pipe;
+ use std::os::unix::io::{AsRawFd, RawFd};
+ use tempfile::NamedTempFile;
+
+ #[test]
+ fn test_success() {
+ let tmp = NamedTempFile::new().unwrap();
+ let fd = tmp.as_raw_fd();
+ posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
+ .expect("posix_fadvise failed");
+ }
+
+ #[test]
+ fn test_errno() {
+ let (rd, _wr) = pipe().unwrap();
+ let res = posix_fadvise(
+ rd as RawFd,
+ 0,
+ 100,
+ PosixFadviseAdvice::POSIX_FADV_WILLNEED,
+ );
+ assert_eq!(res, Err(Errno::ESPIPE));
+ }
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "wasi",
+ target_os = "freebsd"
+))]
+mod test_posix_fallocate {
+
+ use nix::errno::Errno;
+ use nix::fcntl::*;
+ use nix::unistd::pipe;
+ use std::{
+ io::Read,
+ os::unix::io::{AsRawFd, RawFd},
+ };
+ use tempfile::NamedTempFile;
+
+ #[test]
+ fn success() {
+ const LEN: usize = 100;
+ let mut tmp = NamedTempFile::new().unwrap();
+ let fd = tmp.as_raw_fd();
+ let res = posix_fallocate(fd, 0, LEN as libc::off_t);
+ match res {
+ Ok(_) => {
+ let mut data = [1u8; LEN];
+ assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
+ assert_eq!(&data[..], &[0u8; LEN][..]);
+ }
+ Err(Errno::EINVAL) => {
+ // POSIX requires posix_fallocate to return EINVAL both for
+ // invalid arguments (i.e. len < 0) and if the operation is not
+ // supported by the file system.
+ // There's no way to tell for sure whether the file system
+ // supports posix_fallocate, so we must pass the test if it
+ // returns EINVAL.
+ }
+ _ => res.unwrap(),
+ }
+ }
+
+ #[test]
+ fn errno() {
+ let (rd, _wr) = pipe().unwrap();
+ let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
+ match err {
+ Errno::EINVAL | Errno::ENODEV | Errno::ESPIPE | Errno::EBADF => (),
+ errno => panic!("unexpected errno {errno}",),
+ }
+ }
+}
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..6f9aaa897f
--- /dev/null
+++ b/third_party/rust/nix/test/test_kmod/mod.rs
@@ -0,0 +1,188 @@
+use crate::*;
+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 = crate::FORK_MTX.lock();
+
+ 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 std::ffi::CString;
+use std::fs::File;
+use std::io::Read;
+
+#[test]
+fn test_finit_and_delete_module() {
+ require_capability!("test_finit_and_delete_module", CAP_SYS_MODULE);
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
+
+ let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
+
+ 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_module_with_params() {
+ require_capability!(
+ "test_finit_and_delete_module_with_params",
+ CAP_SYS_MODULE
+ );
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
+
+ let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
+
+ 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() {
+ require_capability!("test_init_and_delete_module", CAP_SYS_MODULE);
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
+
+ let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
+
+ 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(&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() {
+ require_capability!(
+ "test_init_and_delete_module_with_params",
+ CAP_SYS_MODULE
+ );
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
+
+ let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
+
+ 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(&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() {
+ require_capability!("test_finit_module_invalid", CAP_SYS_MODULE);
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
+
+ let kmod_path = "/dev/zero";
+
+ let f = File::open(kmod_path).expect("unable to open kernel module");
+ let result =
+ finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty());
+
+ assert_eq!(result.unwrap_err(), Errno::EINVAL);
+}
+
+#[test]
+fn test_finit_module_twice_and_delete_module() {
+ require_capability!(
+ "test_finit_module_twice_and_delete_module",
+ CAP_SYS_MODULE
+ );
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
+
+ let (kmod_path, kmod_name, _kmod_dir) = compile_kernel_module();
+
+ 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(), Errno::EEXIST);
+
+ delete_module(
+ &CString::new(kmod_name).unwrap(),
+ DeleteModuleFlags::empty(),
+ )
+ .expect("unable to unload kernel module");
+}
+
+#[test]
+fn test_delete_module_not_loaded() {
+ require_capability!("test_delete_module_not_loaded", CAP_SYS_MODULE);
+ let _m0 = crate::KMOD_MTX.lock();
+ let _m1 = crate::CWD_LOCK.read();
+
+ let result = delete_module(
+ &CString::new("hello").unwrap(),
+ DeleteModuleFlags::empty(),
+ );
+
+ assert_eq!(result.unwrap_err(), 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..5cf00408e8
--- /dev/null
+++ b/third_party/rust/nix/test/test_mount.rs
@@ -0,0 +1,267 @@
+mod common;
+
+// Implementation 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.
+
+#[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;
+
+ static SCRIPT_CONTENTS: &[u8] = b"#!/bin/sh
+exit 23";
+
+ const EXPECTED_STATUS: i32 = 23;
+
+ const NONE: Option<&'static [u8]> = None;
+ #[allow(clippy::bind_instead_of_map)] // False positive
+ pub fn test_mount_tmpfs_without_flags_allows_rwx() {
+ let tempdir = tempfile::tempdir().unwrap();
+
+ 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,
+ 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,
+ 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: {e}. Are unprivileged user namespaces available?").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 {uid} 1\n").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_bind, test_mount_noexec_disallows_exec,
+ test_mount_rdonly_disallows_write,
+ test_mount_tmpfs_without_flags_allows_rwx,
+ };
+ skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1351");
+ 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..1fd8929c17
--- /dev/null
+++ b/third_party/rust/nix/test/test_mq.rs
@@ -0,0 +1,223 @@
+use cfg_if::cfg_if;
+use std::str;
+
+use nix::errno::Errno;
+use nix::mqueue::{
+ mq_attr_member_t, mq_close, mq_open, mq_receive, mq_send, mq_timedreceive,
+};
+use nix::mqueue::{MQ_OFlag, MqAttr};
+use nix::sys::stat::Mode;
+use nix::sys::time::{TimeSpec, TimeValLike};
+use nix::time::{clock_gettime, ClockId};
+
+// Defined as a macro such that the error source is reported as the caller's location.
+macro_rules! assert_attr_eq {
+ ($read_attr:ident, $initial_attr:ident) => {
+ cfg_if! {
+ if #[cfg(any(target_os = "dragonfly", target_os = "netbsd"))] {
+ // NetBSD (and others which inherit its implementation) include other flags
+ // in read_attr, such as those specified by oflag. Just make sure at least
+ // the correct bits are set.
+ assert_eq!($read_attr.flags() & $initial_attr.flags(), $initial_attr.flags());
+ assert_eq!($read_attr.maxmsg(), $initial_attr.maxmsg());
+ assert_eq!($read_attr.msgsize(), $initial_attr.msgsize());
+ assert_eq!($read_attr.curmsgs(), $initial_attr.curmsgs());
+ } else {
+ assert_eq!($read_attr, $initial_attr);
+ }
+ }
+ }
+}
+
+#[test]
+fn test_mq_send_and_receive() {
+ const MSG_SIZE: mq_attr_member_t = 32;
+ let attr = MqAttr::new(0, 10, MSG_SIZE, 0);
+ let mq_name = "/a_nix_test_queue";
+
+ let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
+ let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
+ let r0 = mq_open(mq_name, oflag0, mode, Some(&attr));
+ if let Err(Errno::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_eq!(prio, 1);
+
+ mq_close(mqd1).unwrap();
+ mq_close(mqd0).unwrap();
+ assert_eq!(msg_to_send, str::from_utf8(&buf[0..len]).unwrap());
+}
+
+#[test]
+fn test_mq_timedreceive() {
+ const MSG_SIZE: mq_attr_member_t = 32;
+ let attr = MqAttr::new(0, 10, MSG_SIZE, 0);
+ let mq_name = "/a_nix_test_queue";
+
+ let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
+ let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
+ let r0 = mq_open(mq_name, oflag0, mode, Some(&attr));
+ if let Err(Errno::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 abstime =
+ clock_gettime(ClockId::CLOCK_REALTIME).unwrap() + TimeSpec::seconds(1);
+ let len = mq_timedreceive(&mqd1, &mut buf, &mut prio, &abstime).unwrap();
+ assert_eq!(prio, 1);
+
+ mq_close(mqd1).unwrap();
+ mq_close(mqd0).unwrap();
+ assert_eq!(msg_to_send, str::from_utf8(&buf[0..len]).unwrap());
+}
+
+#[test]
+fn test_mq_getattr() {
+ use nix::mqueue::mq_getattr;
+ const MSG_SIZE: mq_attr_member_t = 32;
+ let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
+ let mq_name = "/attr_test_get_attr";
+ 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(Errno::ENOSYS) = r {
+ println!("message queues not supported or module not loaded?");
+ return;
+ };
+ let mqd = r.unwrap();
+
+ let read_attr = mq_getattr(&mqd).unwrap();
+ assert_attr_eq!(read_attr, initial_attr);
+ mq_close(mqd).unwrap();
+}
+
+// FIXME: Fix failures for mips in QEMU
+#[test]
+#[cfg_attr(
+ all(qemu, any(target_arch = "mips", target_arch = "mips64")),
+ ignore
+)]
+fn test_mq_setattr() {
+ use nix::mqueue::{mq_getattr, mq_setattr};
+ const MSG_SIZE: mq_attr_member_t = 32;
+ let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
+ let mq_name = "/attr_test_get_attr";
+ 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(Errno::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).unwrap();
+ assert_attr_eq!(old_attr, initial_attr);
+
+ // No changes here because according to the Linux man page only
+ // O_NONBLOCK can be set (see tests below)
+ #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
+ {
+ let new_attr_get = mq_getattr(&mqd).unwrap();
+ assert_ne!(new_attr_get, new_attr);
+ }
+
+ let new_attr_non_blocking = MqAttr::new(
+ MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t,
+ 10,
+ MSG_SIZE,
+ 0,
+ );
+ mq_setattr(&mqd, &new_attr_non_blocking).unwrap();
+ let new_attr_get = mq_getattr(&mqd).unwrap();
+
+ // now the O_NONBLOCK flag has been set
+ #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
+ {
+ assert_ne!(new_attr_get, initial_attr);
+ }
+ assert_attr_eq!(new_attr_get, new_attr_non_blocking);
+ mq_close(mqd).unwrap();
+}
+
+// FIXME: Fix failures for mips in QEMU
+#[test]
+#[cfg_attr(
+ all(qemu, any(target_arch = "mips", target_arch = "mips64")),
+ ignore
+)]
+fn test_mq_set_nonblocking() {
+ use nix::mqueue::{mq_getattr, mq_remove_nonblock, mq_set_nonblock};
+ const MSG_SIZE: mq_attr_member_t = 32;
+ let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
+ let mq_name = "/attr_test_get_attr";
+ 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(Errno::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);
+ let o_nonblock_bits = MQ_OFlag::O_NONBLOCK.bits() as mq_attr_member_t;
+ assert_eq!(new_attr.unwrap().flags() & o_nonblock_bits, o_nonblock_bits);
+ mq_remove_nonblock(&mqd).unwrap();
+ let new_attr = mq_getattr(&mqd);
+ assert_eq!(new_attr.unwrap().flags() & o_nonblock_bits, 0);
+ mq_close(mqd).unwrap();
+}
+
+#[test]
+fn test_mq_unlink() {
+ use nix::mqueue::mq_unlink;
+ const MSG_SIZE: mq_attr_member_t = 32;
+ let initial_attr = MqAttr::new(0, 10, MSG_SIZE, 0);
+ let mq_name_opened = "/mq_unlink_test";
+ #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
+ let mq_name_not_opened = "/mq_unlink_test";
+ 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(Errno::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_eq!(res_unlink, Ok(()));
+
+ // NetBSD (and others which inherit its implementation) defer removing the message
+ // queue name until all references are closed, whereas Linux and others remove the
+ // message queue name immediately.
+ #[cfg(not(any(target_os = "dragonfly", target_os = "netbsd")))]
+ {
+ let res_unlink_not_opened = mq_unlink(mq_name_not_opened);
+ assert_eq!(res_unlink_not_opened, Err(Errno::ENOENT));
+ }
+
+ mq_close(mqd).unwrap();
+ let res_unlink_after_close = mq_unlink(mq_name_opened);
+ assert_eq!(res_unlink_after_close, Err(Errno::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..c44655a4c9
--- /dev/null
+++ b/third_party/rust/nix/test/test_net.rs
@@ -0,0 +1,19 @@
+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",
+ target_os = "haiku"
+)))]
+const LOOPBACK: &[u8] = b"lo0";
+
+#[cfg(target_os = "haiku")]
+const LOOPBACK: &[u8] = b"loop";
+
+#[test]
+fn test_if_nametoindex() {
+ if_nametoindex(LOOPBACK).expect("assertion failed");
+}
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..8b13789179
--- /dev/null
+++ b/third_party/rust/nix/test/test_nix_path.rs
@@ -0,0 +1 @@
+
diff --git a/third_party/rust/nix/test/test_nmount.rs b/third_party/rust/nix/test/test_nmount.rs
new file mode 100644
index 0000000000..dec806a55f
--- /dev/null
+++ b/third_party/rust/nix/test/test_nmount.rs
@@ -0,0 +1,49 @@
+use crate::*;
+use nix::{
+ errno::Errno,
+ mount::{unmount, MntFlags, Nmount},
+};
+use std::{ffi::CString, fs::File, path::Path};
+use tempfile::tempdir;
+
+#[test]
+fn ok() {
+ require_mount!("nullfs");
+
+ let mountpoint = tempdir().unwrap();
+ let target = tempdir().unwrap();
+ let _sentry = File::create(target.path().join("sentry")).unwrap();
+
+ let fstype = CString::new("fstype").unwrap();
+ let nullfs = CString::new("nullfs").unwrap();
+ Nmount::new()
+ .str_opt(&fstype, &nullfs)
+ .str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
+ .str_opt_owned("target", target.path().to_str().unwrap())
+ .nmount(MntFlags::empty())
+ .unwrap();
+
+ // Now check that the sentry is visible through the mountpoint
+ let exists = Path::exists(&mountpoint.path().join("sentry"));
+
+ // Cleanup the mountpoint before asserting
+ unmount(mountpoint.path(), MntFlags::empty()).unwrap();
+
+ assert!(exists);
+}
+
+#[test]
+fn bad_fstype() {
+ let mountpoint = tempdir().unwrap();
+ let target = tempdir().unwrap();
+ let _sentry = File::create(target.path().join("sentry")).unwrap();
+
+ let e = Nmount::new()
+ .str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
+ .str_opt_owned("target", target.path().to_str().unwrap())
+ .nmount(MntFlags::empty())
+ .unwrap_err();
+
+ assert_eq!(e.error(), Errno::EINVAL);
+ assert_eq!(e.errmsg(), Some("Invalid fstype"));
+}
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..045ccd3df1
--- /dev/null
+++ b/third_party/rust/nix/test/test_poll.rs
@@ -0,0 +1,82 @@
+use nix::{
+ errno::Errno,
+ poll::{poll, PollFd, PollFlags},
+ unistd::{close, pipe, write},
+};
+use std::os::unix::io::{BorrowedFd, FromRawFd, OwnedFd};
+
+macro_rules! loop_while_eintr {
+ ($poll_expr: expr) => {
+ loop {
+ match $poll_expr {
+ Ok(nfds) => break nfds,
+ Err(Errno::EINTR) => (),
+ Err(e) => panic!("{}", e),
+ }
+ }
+ };
+}
+
+#[test]
+fn test_poll() {
+ let (r, w) = pipe().unwrap();
+ let r = unsafe { OwnedFd::from_raw_fd(r) };
+ let mut fds = [PollFd::new(&r, PollFlags::POLLIN)];
+
+ // Poll an idle pipe. Should timeout
+ let nfds = loop_while_eintr!(poll(&mut fds, 100));
+ assert_eq!(nfds, 0);
+ assert!(!fds[0].revents().unwrap().contains(PollFlags::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(PollFlags::POLLIN));
+ close(w).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 r = unsafe { OwnedFd::from_raw_fd(r) };
+ let mut fds = [PollFd::new(&r, PollFlags::POLLIN)];
+
+ // Poll an idle pipe. Should timeout
+ let sigset = SigSet::empty();
+ let nfds = loop_while_eintr!(ppoll(&mut fds, Some(timeout), Some(sigset)));
+ assert_eq!(nfds, 0);
+ assert!(!fds[0].revents().unwrap().contains(PollFlags::POLLIN));
+
+ write(w, b".").unwrap();
+
+ // Poll a readable pipe. Should return an event.
+ let nfds = ppoll(&mut fds, Some(timeout), None).unwrap();
+ assert_eq!(nfds, 1);
+ assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN));
+ close(w).unwrap();
+}
+
+#[test]
+fn test_pollfd_events() {
+ let fd_zero = unsafe { BorrowedFd::borrow_raw(0) };
+ let mut pfd = PollFd::new(&fd_zero, PollFlags::POLLIN);
+ assert_eq!(pfd.events(), PollFlags::POLLIN);
+ pfd.set_events(PollFlags::POLLOUT);
+ assert_eq!(pfd.events(), PollFlags::POLLOUT);
+}
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..4cc6620c3c
--- /dev/null
+++ b/third_party/rust/nix/test/test_pty.rs
@@ -0,0 +1,276 @@
+use std::fs::File;
+use std::io::{Read, Write};
+use std::os::unix::prelude::*;
+use std::path::Path;
+
+use libc::{_exit, STDOUT_FILENO};
+use nix::fcntl::{open, OFlag};
+use nix::pty::*;
+use nix::sys::stat;
+use nix::sys::termios::*;
+use nix::unistd::{pause, write};
+
+/// Test equivalence of `ptsname` and `ptsname_r`
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_ptsname_equivalence() {
+ let _m = crate::PTSNAME_MTX.lock();
+
+ // 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 = crate::PTSNAME_MTX.lock();
+
+ // Open a new PTTY master
+ let master_fd = posix_openpt(OFlag::O_RDWR).unwrap();
+
+ // Get the name of the slave
+ let slave_name1 = unsafe { ptsname(&master_fd) }.unwrap();
+ let slave_name2 = unsafe { ptsname(&master_fd) }.unwrap();
+ assert_eq!(slave_name1, slave_name2);
+ // Also make sure that the string was actually copied and they point to different parts of
+ // memory.
+ assert_ne!(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();
+
+ // Get the name of the slave
+ let slave_name1 = ptsname_r(&master_fd).unwrap();
+ let slave_name2 = ptsname_r(&master_fd).unwrap();
+ assert_eq!(slave_name1, slave_name2);
+ assert_ne!(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 = crate::PTSNAME_MTX.lock();
+
+ // Open a new PTTY master
+ let master1_fd = posix_openpt(OFlag::O_RDWR).unwrap();
+
+ // Open a second PTTY master
+ let master2_fd = posix_openpt(OFlag::O_RDWR).unwrap();
+
+ // Get the name of the slave
+ let slave_name1 = unsafe { ptsname(&master1_fd) }.unwrap();
+ let slave_name2 = unsafe { ptsname(&master2_fd) }.unwrap();
+ assert_ne!(slave_name1, slave_name2);
+}
+
+/// Common setup for testing PTTY pairs
+fn open_ptty_pair() -> (PtyMaster, File) {
+ let _m = crate::PTSNAME_MTX.lock();
+
+ // Open a new PTTY master
+ let master = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
+
+ // Allow a slave to be generated for it
+ grantpt(&master).expect("grantpt failed");
+ unlockpt(&master).expect("unlockpt failed");
+
+ // Get the name of the slave
+ let slave_name = unsafe { ptsname(&master) }.expect("ptsname failed");
+
+ // Open the slave device
+ let slave_fd =
+ open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty())
+ .unwrap();
+
+ #[cfg(target_os = "illumos")]
+ // TODO: rewrite using ioctl!
+ #[allow(clippy::comparison_chain)]
+ {
+ use libc::{ioctl, I_FIND, I_PUSH};
+
+ // On illumos systems, as per pts(7D), one must push STREAMS modules
+ // after opening a device path returned from ptsname().
+ let ptem = b"ptem\0";
+ let ldterm = b"ldterm\0";
+ let r = unsafe { ioctl(slave_fd, I_FIND, ldterm.as_ptr()) };
+ if r < 0 {
+ panic!("I_FIND failure");
+ } else if r == 0 {
+ if unsafe { ioctl(slave_fd, I_PUSH, ptem.as_ptr()) } < 0 {
+ panic!("I_PUSH ptem failure");
+ }
+ if unsafe { ioctl(slave_fd, I_PUSH, ldterm.as_ptr()) } < 0 {
+ panic!("I_PUSH ldterm failure");
+ }
+ }
+ }
+
+ let slave = unsafe { File::from_raw_fd(slave_fd) };
+
+ (master, slave)
+}
+
+/// Test opening a master/slave PTTY pair
+///
+/// This uses a common `open_ptty_pair` 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
+/// master/slave PTTY pair.
+#[test]
+fn test_open_ptty_pair() {
+ let (_, _) = open_ptty_pair();
+}
+
+/// Put the terminal in raw mode.
+fn make_raw<Fd: AsFd>(fd: Fd) {
+ let mut termios = tcgetattr(&fd).unwrap();
+ cfmakeraw(&mut termios);
+ tcsetattr(&fd, SetArg::TCSANOW, &termios).unwrap();
+}
+
+/// Test `io::Read` on the PTTY master
+#[test]
+fn test_read_ptty_pair() {
+ let (mut master, mut slave) = open_ptty_pair();
+ make_raw(&slave);
+
+ let mut buf = [0u8; 5];
+ slave.write_all(b"hello").unwrap();
+ master.read_exact(&mut buf).unwrap();
+ assert_eq!(&buf, b"hello");
+
+ let mut master = &master;
+ slave.write_all(b"hello").unwrap();
+ master.read_exact(&mut buf).unwrap();
+ assert_eq!(&buf, b"hello");
+}
+
+/// Test `io::Write` on the PTTY master
+#[test]
+fn test_write_ptty_pair() {
+ let (mut master, mut slave) = open_ptty_pair();
+ make_raw(&slave);
+
+ let mut buf = [0u8; 5];
+ master.write_all(b"adios").unwrap();
+ slave.read_exact(&mut buf).unwrap();
+ assert_eq!(&buf, b"adios");
+
+ let mut master = &master;
+ master.write_all(b"adios").unwrap();
+ slave.read_exact(&mut buf).unwrap();
+ assert_eq!(&buf, b"adios");
+}
+
+#[test]
+fn test_openpty() {
+ // openpty uses ptname(3) internally
+ let _m = crate::PTSNAME_MTX.lock();
+
+ let pty = openpty(None, None).unwrap();
+
+ // Writing to one should be readable on the other one
+ let string = "foofoofoo\n";
+ let mut buf = [0u8; 10];
+ write(pty.master.as_raw_fd(), string.as_bytes()).unwrap();
+ crate::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];
+ crate::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.as_raw_fd(), string2.as_bytes()).unwrap();
+ crate::read_exact(&pty.master, &mut buf);
+
+ assert_eq!(&buf, echoed_string2.as_bytes());
+}
+
+#[test]
+fn test_openpty_with_termios() {
+ // openpty uses ptname(3) internally
+ let _m = crate::PTSNAME_MTX.lock();
+
+ // Open one pty to get attributes for the second one
+ let mut termios = {
+ let pty = openpty(None, None).unwrap();
+ tcgetattr(&pty.slave).unwrap()
+ };
+ // Make sure 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
+
+ // Writing to one should be readable on the other one
+ let string = "foofoofoo\n";
+ let mut buf = [0u8; 10];
+ write(pty.master.as_raw_fd(), string.as_bytes()).unwrap();
+ crate::read_exact(&pty.slave, &mut buf);
+
+ assert_eq!(&buf, string.as_bytes());
+
+ // read the echo as well
+ let echoed_string = "foofoofoo\n";
+ crate::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.as_raw_fd(), string2.as_bytes()).unwrap();
+ crate::read_exact(&pty.master, &mut buf);
+
+ assert_eq!(&buf, echoed_string2.as_bytes());
+}
+
+#[test]
+fn test_forkpty() {
+ use nix::sys::signal::*;
+ use nix::sys::wait::wait;
+ use nix::unistd::ForkResult::*;
+ // forkpty calls openpty which uses ptname(3) internally.
+ let _m0 = crate::PTSNAME_MTX.lock();
+ // forkpty spawns a child process
+ let _m1 = crate::FORK_MTX.lock();
+
+ let string = "naninani\n";
+ let echoed_string = "naninani\r\n";
+ let pty = unsafe { forkpty(None, None).unwrap() };
+ match pty.fork_result {
+ Child => {
+ write(STDOUT_FILENO, string.as_bytes()).unwrap();
+ pause(); // we need the child to stay alive until the parent calls read
+ unsafe {
+ _exit(0);
+ }
+ }
+ Parent { child } => {
+ let mut buf = [0u8; 10];
+ assert!(child.as_raw() > 0);
+ crate::read_exact(&pty.master, &mut buf);
+ kill(child, SIGTERM).unwrap();
+ wait().unwrap(); // keep other tests using generic wait from getting our child
+ assert_eq!(&buf, echoed_string.as_bytes());
+ }
+ }
+}
diff --git a/third_party/rust/nix/test/test_resource.rs b/third_party/rust/nix/test/test_resource.rs
new file mode 100644
index 0000000000..2ab581ba29
--- /dev/null
+++ b/third_party/rust/nix/test/test_resource.rs
@@ -0,0 +1,34 @@
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "haiku"
+)))]
+use nix::sys::resource::{getrlimit, setrlimit, Resource};
+
+/// Tests the RLIMIT_NOFILE functionality of getrlimit(), where the resource RLIMIT_NOFILE refers
+/// to the maximum file descriptor number that can be opened by the process (aka the maximum number
+/// of file descriptors that the process can open, since Linux 4.5).
+///
+/// We first fetch the existing file descriptor maximum values using getrlimit(), then edit the
+/// soft limit to make sure it has a new and distinct value to the hard limit. We then setrlimit()
+/// to put the new soft limit in effect, and then getrlimit() once more to ensure the limits have
+/// been updated.
+#[test]
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "haiku"
+)))]
+pub fn test_resource_limits_nofile() {
+ let (mut soft_limit, hard_limit) =
+ getrlimit(Resource::RLIMIT_NOFILE).unwrap();
+
+ soft_limit -= 1;
+ assert_ne!(soft_limit, hard_limit);
+ setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
+
+ let (new_soft_limit, _) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
+ assert_eq!(new_soft_limit, soft_limit);
+}
diff --git a/third_party/rust/nix/test/test_sched.rs b/third_party/rust/nix/test/test_sched.rs
new file mode 100644
index 0000000000..c52616b8bb
--- /dev/null
+++ b/third_party/rust/nix/test/test_sched.rs
@@ -0,0 +1,39 @@
+use nix::sched::{sched_getaffinity, sched_getcpu, sched_setaffinity, CpuSet};
+use nix::unistd::Pid;
+
+#[test]
+fn test_sched_affinity() {
+ // If pid is zero, then the mask of the calling process is returned.
+ let initial_affinity = sched_getaffinity(Pid::from_raw(0)).unwrap();
+ let mut at_least_one_cpu = false;
+ let mut last_valid_cpu = 0;
+ for field in 0..CpuSet::count() {
+ if initial_affinity.is_set(field).unwrap() {
+ at_least_one_cpu = true;
+ last_valid_cpu = field;
+ }
+ }
+ assert!(at_least_one_cpu);
+
+ // Now restrict the running CPU
+ let mut new_affinity = CpuSet::new();
+ new_affinity.set(last_valid_cpu).unwrap();
+ sched_setaffinity(Pid::from_raw(0), &new_affinity).unwrap();
+
+ // And now re-check the affinity which should be only the one we set.
+ let updated_affinity = sched_getaffinity(Pid::from_raw(0)).unwrap();
+ for field in 0..CpuSet::count() {
+ // Should be set only for the CPU we set previously
+ assert_eq!(
+ updated_affinity.is_set(field).unwrap(),
+ field == last_valid_cpu
+ )
+ }
+
+ // Now check that we're also currently running on the CPU in question.
+ let cur_cpu = sched_getcpu().unwrap();
+ assert_eq!(cur_cpu, last_valid_cpu);
+
+ // Finally, reset the initial CPU set
+ sched_setaffinity(Pid::from_raw(0), &initial_affinity).unwrap();
+}
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..b85e030fd3
--- /dev/null
+++ b/third_party/rust/nix/test/test_sendfile.rs
@@ -0,0 +1,217 @@
+use std::io::prelude::*;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use std::os::unix::io::{FromRawFd, OwnedFd};
+
+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 = "dragonfly", 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;
+ // The construct of this `OwnedFd` is a temporary workaround, when `pipe(2)`
+ // becomes I/O-safe:
+ // pub fn pipe() -> std::result::Result<(OwnedFd, OwnedFd), Error>
+ // then it is no longer needed.
+ let wr = unsafe { OwnedFd::from_raw_fd(wr) };
+ let res = sendfile(&wr, &tmp, 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();
+}
+
+#[cfg(target_os = "linux")]
+#[test]
+fn test_sendfile64_linux() {
+ const CONTENTS: &[u8] = b"abcdef123456";
+ let mut tmp = tempfile().unwrap();
+ tmp.write_all(CONTENTS).unwrap();
+
+ let (rd, wr) = pipe().unwrap();
+ let mut offset: libc::off64_t = 5;
+ // The construct of this `OwnedFd` is a temporary workaround, when `pipe(2)`
+ // becomes I/O-safe:
+ // pub fn pipe() -> std::result::Result<(OwnedFd, OwnedFd), Error>
+ // then it is no longer needed.
+ let wr = unsafe { OwnedFd::from_raw_fd(wr) };
+ let res = sendfile64(&wr, &tmp, 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();
+}
+
+#[cfg(target_os = "freebsd")]
+#[test]
+fn test_sendfile_freebsd() {
+ // Declare the content
+ let header_strings =
+ ["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"];
+ let body = "Xabcdef123456";
+ let body_offset = 1;
+ let trailer_strings = ["\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,
+ &wr,
+ 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(target_os = "dragonfly")]
+#[test]
+fn test_sendfile_dragonfly() {
+ // Declare the content
+ let header_strings =
+ ["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"];
+ let body = "Xabcdef123456";
+ let body_offset = 1;
+ let trailer_strings = ["\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,
+ &wr,
+ 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);
+}
+
+#[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,
+ &wr,
+ 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..55f15c0771
--- /dev/null
+++ b/third_party/rust/nix/test/test_stat.rs
@@ -0,0 +1,421 @@
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use std::fs;
+use std::fs::File;
+#[cfg(not(target_os = "redox"))]
+use std::os::unix::fs::symlink;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use std::os::unix::fs::PermissionsExt;
+use std::os::unix::prelude::AsRawFd;
+#[cfg(not(target_os = "redox"))]
+use std::path::Path;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use std::time::{Duration, UNIX_EPOCH};
+
+use libc::mode_t;
+#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
+use libc::{S_IFLNK, S_IFMT};
+
+#[cfg(not(target_os = "redox"))]
+use nix::errno::Errno;
+#[cfg(not(target_os = "redox"))]
+use nix::fcntl;
+#[cfg(any(
+ target_os = "linux",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "netbsd"
+))]
+use nix::sys::stat::lutimes;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use nix::sys::stat::utimensat;
+#[cfg(not(target_os = "redox"))]
+use nix::sys::stat::FchmodatFlags;
+use nix::sys::stat::Mode;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use nix::sys::stat::UtimensatFlags;
+#[cfg(not(target_os = "redox"))]
+use nix::sys::stat::{self};
+use nix::sys::stat::{fchmod, stat};
+#[cfg(not(target_os = "redox"))]
+use nix::sys::stat::{fchmodat, mkdirat};
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use nix::sys::stat::{futimens, utimes};
+
+#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
+use nix::sys::stat::FileStat;
+
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use nix::sys::time::{TimeSpec, TimeVal, TimeValLike};
+#[cfg(not(target_os = "redox"))]
+use nix::unistd::chdir;
+
+#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
+use nix::Result;
+
+#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
+fn assert_stat_results(stat_result: Result<FileStat>) {
+ let stats = stat_result.expect("stat call failed");
+ assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
+ assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
+ assert!(stats.st_mode > 0); // must be positive integer
+ assert_eq!(stats.st_nlink, 1); // there links created, must be 1
+ assert_eq!(stats.st_size, 0); // size is 0 because we did not write anything to the file
+ assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
+ assert!(stats.st_blocks <= 16); // Up to 16 blocks can be allocated for a blank file
+}
+
+#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
+// (Android's st_blocks is ulonglong which is always non-negative.)
+#[cfg_attr(target_os = "android", allow(unused_comparisons))]
+#[allow(clippy::absurd_extreme_comparisons)] // Not absurd on all OSes
+fn assert_lstat_results(stat_result: Result<FileStat>) {
+ let stats = stat_result.expect("stat call failed");
+ assert!(stats.st_dev > 0); // must be positive integer, exact number machine dependent
+ assert!(stats.st_ino > 0); // inode is positive integer, exact number machine dependent
+ assert!(stats.st_mode > 0); // must be positive integer
+
+ // st_mode is c_uint (u32 on Android) while S_IFMT is mode_t
+ // (u16 on Android), and that will be a compile error.
+ // On other platforms they are the same (either both are u16 or u32).
+ assert_eq!(
+ (stats.st_mode as usize) & (S_IFMT as usize),
+ S_IFLNK as usize
+ ); // should be a link
+ assert_eq!(stats.st_nlink, 1); // there links created, must be 1
+ assert!(stats.st_size > 0); // size is > 0 because it points to another file
+ assert!(stats.st_blksize > 0); // must be positive integer, exact number machine dependent
+
+ // st_blocks depends on whether the machine's file system uses fast
+ // or slow symlinks, so just make sure it's not negative
+ assert!(stats.st_blocks >= 0);
+}
+
+#[test]
+#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
+fn test_stat_and_fstat() {
+ use nix::sys::stat::fstat;
+
+ let tempdir = tempfile::tempdir().unwrap();
+ let filename = tempdir.path().join("foo.txt");
+ let file = File::create(&filename).unwrap();
+
+ let stat_result = stat(&filename);
+ assert_stat_results(stat_result);
+
+ let fstat_result = fstat(file.as_raw_fd());
+ assert_stat_results(fstat_result);
+}
+
+#[test]
+#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
+fn test_fstatat() {
+ let tempdir = tempfile::tempdir().unwrap();
+ let filename = tempdir.path().join("foo.txt");
+ File::create(&filename).unwrap();
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty());
+
+ let result =
+ stat::fstatat(dirfd.unwrap(), &filename, fcntl::AtFlags::empty());
+ assert_stat_results(result);
+}
+
+#[test]
+#[cfg(not(any(target_os = "netbsd", target_os = "redox")))]
+fn test_stat_fstat_lstat() {
+ use nix::sys::stat::{fstat, lstat};
+
+ let tempdir = tempfile::tempdir().unwrap();
+ let filename = tempdir.path().join("bar.txt");
+ let linkname = tempdir.path().join("barlink");
+
+ File::create(&filename).unwrap();
+ symlink("bar.txt", &linkname).unwrap();
+ let link = File::open(&linkname).unwrap();
+
+ // should be the same result as calling stat,
+ // since it's a regular file
+ let stat_result = stat(&filename);
+ assert_stat_results(stat_result);
+
+ let lstat_result = lstat(&linkname);
+ assert_lstat_results(lstat_result);
+
+ let fstat_result = fstat(link.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 as mode_t & 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 as mode_t & 0o7777, mode2.bits());
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_fchmodat() {
+ let _dr = crate::DirRestore::new();
+ let tempdir = tempfile::tempdir().unwrap();
+ let filename = "foo.txt";
+ let fullpath = tempdir.path().join(filename);
+ File::create(&fullpath).unwrap();
+
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ let mut mode1 = Mode::empty();
+ mode1.insert(Mode::S_IRUSR);
+ mode1.insert(Mode::S_IWUSR);
+ fchmodat(Some(dirfd), filename, mode1, FchmodatFlags::FollowSymlink)
+ .unwrap();
+
+ let file_stat1 = stat(&fullpath).unwrap();
+ assert_eq!(file_stat1.st_mode as mode_t & 0o7777, mode1.bits());
+
+ chdir(tempdir.path()).unwrap();
+
+ let mut mode2 = Mode::empty();
+ mode2.insert(Mode::S_IROTH);
+ fchmodat(None, filename, mode2, FchmodatFlags::FollowSymlink).unwrap();
+
+ let file_stat2 = stat(&fullpath).unwrap();
+ assert_eq!(file_stat2.st_mode as mode_t & 0o7777, mode2.bits());
+}
+
+/// Asserts that the atime and mtime in a file's metadata match expected values.
+///
+/// The atime and mtime are expressed with a resolution of seconds because some file systems
+/// (like macOS's HFS+) do not have higher granularity.
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn assert_times_eq(
+ exp_atime_sec: u64,
+ exp_mtime_sec: u64,
+ attr: &fs::Metadata,
+) {
+ assert_eq!(
+ Duration::new(exp_atime_sec, 0),
+ attr.accessed().unwrap().duration_since(UNIX_EPOCH).unwrap()
+ );
+ assert_eq!(
+ Duration::new(exp_mtime_sec, 0),
+ attr.modified().unwrap().duration_since(UNIX_EPOCH).unwrap()
+ );
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_utimes() {
+ let tempdir = tempfile::tempdir().unwrap();
+ let fullpath = tempdir.path().join("file");
+ drop(File::create(&fullpath).unwrap());
+
+ utimes(&fullpath, &TimeVal::seconds(9990), &TimeVal::seconds(5550))
+ .unwrap();
+ assert_times_eq(9990, 5550, &fs::metadata(&fullpath).unwrap());
+}
+
+#[test]
+#[cfg(any(
+ target_os = "linux",
+ 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]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_futimens() {
+ let tempdir = tempfile::tempdir().unwrap();
+ let fullpath = tempdir.path().join("file");
+ drop(File::create(&fullpath).unwrap());
+
+ let fd = fcntl::open(&fullpath, fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ futimens(fd, &TimeSpec::seconds(10), &TimeSpec::seconds(20)).unwrap();
+ assert_times_eq(10, 20, &fs::metadata(&fullpath).unwrap());
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_utimensat() {
+ let _dr = crate::DirRestore::new();
+ let tempdir = tempfile::tempdir().unwrap();
+ let filename = "foo.txt";
+ let fullpath = tempdir.path().join(filename);
+ drop(File::create(&fullpath).unwrap());
+
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ utimensat(
+ 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());
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_mkdirat_success_path() {
+ let tempdir = tempfile::tempdir().unwrap();
+ let filename = "example_subdir";
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+ mkdirat(dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed");
+ assert!(Path::exists(&tempdir.path().join(filename)));
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_mkdirat_success_mode() {
+ let expected_bits =
+ stat::SFlag::S_IFDIR.bits() | stat::Mode::S_IRWXU.bits();
+ let tempdir = tempfile::tempdir().unwrap();
+ let filename = "example_subdir";
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+ mkdirat(dirfd, filename, Mode::S_IRWXU).expect("mkdirat failed");
+ let permissions = fs::metadata(tempdir.path().join(filename))
+ .unwrap()
+ .permissions();
+ let mode = permissions.mode();
+ assert_eq!(mode as mode_t, expected_bits)
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_mkdirat_fail() {
+ let tempdir = tempfile::tempdir().unwrap();
+ let not_dir_filename = "example_not_dir";
+ let filename = "example_subdir_dir";
+ let dirfd = fcntl::open(
+ &tempdir.path().join(not_dir_filename),
+ fcntl::OFlag::O_CREAT,
+ stat::Mode::empty(),
+ )
+ .unwrap();
+ let result = mkdirat(dirfd, filename, Mode::S_IRWXU).unwrap_err();
+ assert_eq!(result, Errno::ENOTDIR);
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "haiku",
+ target_os = "redox"
+)))]
+fn test_mknod() {
+ use stat::{lstat, mknod, SFlag};
+
+ let file_name = "test_file";
+ let tempdir = tempfile::tempdir().unwrap();
+ let target = tempdir.path().join(file_name);
+ mknod(&target, SFlag::S_IFREG, Mode::S_IRWXU, 0).unwrap();
+ let mode = lstat(&target).unwrap().st_mode as mode_t;
+ assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
+ assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "haiku",
+ target_os = "redox"
+)))]
+fn test_mknodat() {
+ use fcntl::{AtFlags, OFlag};
+ use nix::dir::Dir;
+ use stat::{fstatat, mknodat, SFlag};
+
+ let file_name = "test_file";
+ let tempdir = tempfile::tempdir().unwrap();
+ let target_dir =
+ Dir::open(tempdir.path(), OFlag::O_DIRECTORY, Mode::S_IRWXU).unwrap();
+ mknodat(
+ target_dir.as_raw_fd(),
+ file_name,
+ SFlag::S_IFREG,
+ Mode::S_IRWXU,
+ 0,
+ )
+ .unwrap();
+ let mode = fstatat(
+ target_dir.as_raw_fd(),
+ file_name,
+ AtFlags::AT_SYMLINK_NOFOLLOW,
+ )
+ .unwrap()
+ .st_mode as mode_t;
+ assert_eq!(mode & libc::S_IFREG, libc::S_IFREG);
+ assert_eq!(mode & libc::S_IRWXU, libc::S_IRWXU);
+}
diff --git a/third_party/rust/nix/test/test_time.rs b/third_party/rust/nix/test/test_time.rs
new file mode 100644
index 0000000000..5f76e61a2d
--- /dev/null
+++ b/third_party/rust/nix/test/test_time.rs
@@ -0,0 +1,59 @@
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+))]
+use nix::time::clock_getcpuclockid;
+use nix::time::{clock_gettime, ClockId};
+
+#[cfg(not(target_os = "redox"))]
+#[test]
+pub fn test_clock_getres() {
+ nix::time::clock_getres(ClockId::CLOCK_REALTIME).expect("assertion failed");
+}
+
+#[test]
+pub fn test_clock_gettime() {
+ clock_gettime(ClockId::CLOCK_REALTIME).expect("assertion failed");
+}
+
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+))]
+#[test]
+pub fn test_clock_getcpuclockid() {
+ let clock_id = clock_getcpuclockid(nix::unistd::Pid::this()).unwrap();
+ clock_gettime(clock_id).unwrap();
+}
+
+#[cfg(not(target_os = "redox"))]
+#[test]
+pub fn test_clock_id_res() {
+ ClockId::CLOCK_REALTIME.res().unwrap();
+}
+
+#[test]
+pub fn test_clock_id_now() {
+ ClockId::CLOCK_REALTIME.now().unwrap();
+}
+
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+))]
+#[test]
+pub fn test_clock_id_pid_cpu_clock_id() {
+ ClockId::pid_cpu_clock_id(nix::unistd::Pid::this())
+ .map(ClockId::now)
+ .unwrap()
+ .unwrap();
+}
diff --git a/third_party/rust/nix/test/test_timer.rs b/third_party/rust/nix/test/test_timer.rs
new file mode 100644
index 0000000000..ffd146867b
--- /dev/null
+++ b/third_party/rust/nix/test/test_timer.rs
@@ -0,0 +1,102 @@
+use nix::sys::signal::{
+ sigaction, SaFlags, SigAction, SigEvent, SigHandler, SigSet, SigevNotify,
+ Signal,
+};
+use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags};
+use nix::time::ClockId;
+use std::convert::TryFrom;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::thread;
+use std::time::{Duration, Instant};
+
+const SIG: Signal = Signal::SIGALRM;
+static ALARM_CALLED: AtomicBool = AtomicBool::new(false);
+
+pub extern "C" fn handle_sigalarm(raw_signal: libc::c_int) {
+ let signal = Signal::try_from(raw_signal).unwrap();
+ if signal == SIG {
+ ALARM_CALLED.store(true, Ordering::Release);
+ }
+}
+
+#[test]
+fn alarm_fires() {
+ // Avoid interfering with other signal using tests by taking a mutex shared
+ // among other tests in this crate.
+ let _m = crate::SIGNAL_MTX.lock();
+ const TIMER_PERIOD: Duration = Duration::from_millis(100);
+
+ //
+ // Setup
+ //
+
+ // Create a handler for the test signal, `SIG`. The handler is responsible
+ // for flipping `ALARM_CALLED`.
+ let handler = SigHandler::Handler(handle_sigalarm);
+ let signal_action =
+ SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty());
+ let old_handler = unsafe {
+ sigaction(SIG, &signal_action)
+ .expect("unable to set signal handler for alarm")
+ };
+
+ // Create the timer. We use the monotonic clock here, though any would do
+ // really. The timer is set to fire every 250 milliseconds with no delay for
+ // the initial firing.
+ let clockid = ClockId::CLOCK_MONOTONIC;
+ let sigevent = SigEvent::new(SigevNotify::SigevSignal {
+ signal: SIG,
+ si_value: 0,
+ });
+ let mut timer =
+ Timer::new(clockid, sigevent).expect("failed to create timer");
+ let expiration = Expiration::Interval(TIMER_PERIOD.into());
+ let flags = TimerSetTimeFlags::empty();
+ timer.set(expiration, flags).expect("could not set timer");
+
+ //
+ // Test
+ //
+
+ // Determine that there's still an expiration tracked by the
+ // timer. Depending on when this runs either an `Expiration::Interval` or
+ // `Expiration::IntervalDelayed` will be present. That is, if the timer has
+ // not fired yet we'll get our original `expiration`, else the one that
+ // represents a delay to the next expiration. We're only interested in the
+ // timer still being extant.
+ match timer.get() {
+ Ok(Some(exp)) => assert!(matches!(
+ exp,
+ Expiration::Interval(..) | Expiration::IntervalDelayed(..)
+ )),
+ _ => panic!("timer lost its expiration"),
+ }
+
+ // Wait for 2 firings of the alarm before checking that it has fired and
+ // been handled at least the once. If we wait for 3 seconds and the handler
+ // is never called something has gone sideways and the test fails.
+ let starttime = Instant::now();
+ loop {
+ thread::sleep(2 * TIMER_PERIOD);
+ if ALARM_CALLED.load(Ordering::Acquire) {
+ break;
+ }
+ if starttime.elapsed() > Duration::from_secs(3) {
+ panic!("Timeout waiting for SIGALRM");
+ }
+ }
+
+ // Cleanup:
+ // 1) deregister the OS's timer.
+ // 2) Wait for a full timer period, since POSIX does not require that
+ // disabling the timer will clear pending signals, and on NetBSD at least
+ // it does not.
+ // 2) Replace the old signal handler now that we've completed the test. If
+ // the test fails this process panics, so the fact we might not get here
+ // is okay.
+ drop(timer);
+ thread::sleep(TIMER_PERIOD);
+ unsafe {
+ sigaction(SIG, &old_handler).expect("unable to reset signal handler");
+ }
+}
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..10284e4127
--- /dev/null
+++ b/third_party/rust/nix/test/test_unistd.rs
@@ -0,0 +1,1393 @@
+use libc::{_exit, mode_t, off_t};
+use nix::errno::Errno;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+use nix::fcntl::readlink;
+use nix::fcntl::OFlag;
+#[cfg(not(target_os = "redox"))]
+use nix::fcntl::{self, open};
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt};
+#[cfg(not(target_os = "redox"))]
+use nix::sys::signal::{
+ sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal,
+};
+use nix::sys::stat::{self, Mode, SFlag};
+use nix::sys::wait::*;
+use nix::unistd::ForkResult::*;
+use nix::unistd::*;
+use std::env;
+#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
+use std::ffi::CString;
+#[cfg(not(target_os = "redox"))]
+use std::fs::DirBuilder;
+use std::fs::{self, File};
+use std::io::Write;
+use std::os::unix::prelude::*;
+#[cfg(not(any(
+ target_os = "fuchsia",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+use std::path::Path;
+use tempfile::{tempdir, tempfile};
+
+use crate::*;
+
+#[test]
+#[cfg(not(any(target_os = "netbsd")))]
+fn test_fork_and_waitpid() {
+ let _m = crate::FORK_MTX.lock();
+
+ // Safe: Child only calls `_exit`, which is signal-safe
+ match unsafe { 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_eq!(pid_t, child),
+
+ // panic, must never happen
+ s @ Ok(_) => {
+ panic!("Child exited {s:?}, should never happen")
+ }
+
+ // 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 = crate::FORK_MTX.lock();
+
+ // Safe: Child only calls `_exit`, which is signal-safe
+ match unsafe { 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
+ mkstemp(&env::temp_dir()).expect_err("assertion failed");
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_mkfifo() {
+ let tempdir = 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 as mode_t);
+ assert_eq!(typ, SFlag::S_IFIFO);
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_mkfifo_directory() {
+ // mkfifo should fail if a directory is given
+ mkfifo(&env::temp_dir(), Mode::S_IRUSR).expect_err("assertion failed");
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+fn test_mkfifoat_none() {
+ let _m = crate::CWD_LOCK.read();
+
+ let tempdir = tempdir().unwrap();
+ let mkfifoat_fifo = tempdir.path().join("mkfifoat_fifo");
+
+ mkfifoat(None, &mkfifoat_fifo, Mode::S_IRUSR).unwrap();
+
+ let stats = stat::stat(&mkfifoat_fifo).unwrap();
+ let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
+ assert_eq!(typ, SFlag::S_IFIFO);
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+fn test_mkfifoat() {
+ use nix::fcntl;
+
+ let tempdir = tempdir().unwrap();
+ let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let mkfifoat_name = "mkfifoat_name";
+
+ mkfifoat(Some(dirfd), mkfifoat_name, Mode::S_IRUSR).unwrap();
+
+ let stats =
+ stat::fstatat(dirfd, mkfifoat_name, fcntl::AtFlags::empty()).unwrap();
+ let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
+ assert_eq!(typ, SFlag::S_IFIFO);
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+fn test_mkfifoat_directory_none() {
+ let _m = crate::CWD_LOCK.read();
+
+ // mkfifoat should fail if a directory is given
+ mkfifoat(None, &env::temp_dir(), Mode::S_IRUSR)
+ .expect_err("assertion failed");
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+fn test_mkfifoat_directory() {
+ // mkfifoat should fail if a directory is given
+ let tempdir = tempdir().unwrap();
+ let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let mkfifoat_dir = "mkfifoat_dir";
+ stat::mkdirat(dirfd, mkfifoat_dir, Mode::S_IRUSR).unwrap();
+
+ mkfifoat(Some(dirfd), mkfifoat_dir, Mode::S_IRUSR)
+ .expect_err("assertion failed");
+}
+
+#[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]
+#[cfg(not(target_os = "redox"))]
+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_eq!(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",
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+fn test_setgroups() {
+ // Skip this test when not run as root as `setgroups()` requires root.
+ skip_if_not_root!("test_setgroups");
+
+ let _m = crate::GROUPS_MTX.lock();
+
+ // 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",
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos"
+)))]
+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 = crate::GROUPS_MTX.lock();
+
+ // 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();
+}
+
+#[cfg(not(any(target_os = "fuchsia", target_os = "redox")))]
+macro_rules! execve_test_factory (
+ ($test_name:ident, $syscall:ident, $exe: expr $(, $pathname:expr, $flags:expr)*) => (
+
+ #[cfg(test)]
+ mod $test_name {
+ use std::ffi::CStr;
+ use super::*;
+
+ const EMPTY: &'static [u8] = b"\0";
+ const DASH_C: &'static [u8] = b"-c\0";
+ const BIGARG: &'static [u8] = b"echo nix!!! && echo foo=$foo && echo baz=$baz\0";
+ const FOO: &'static [u8] = b"foo=bar\0";
+ const BAZ: &'static [u8] = b"baz=quux\0";
+
+ fn syscall_cstr_ref() -> Result<std::convert::Infallible, nix::Error> {
+ $syscall(
+ $exe,
+ $(CString::new($pathname).unwrap().as_c_str(), )*
+ &[CStr::from_bytes_with_nul(EMPTY).unwrap(),
+ CStr::from_bytes_with_nul(DASH_C).unwrap(),
+ CStr::from_bytes_with_nul(BIGARG).unwrap()],
+ &[CStr::from_bytes_with_nul(FOO).unwrap(),
+ CStr::from_bytes_with_nul(BAZ).unwrap()]
+ $(, $flags)*)
+ }
+
+ fn syscall_cstring() -> Result<std::convert::Infallible, nix::Error> {
+ $syscall(
+ $exe,
+ $(CString::new($pathname).unwrap().as_c_str(), )*
+ &[CString::from(CStr::from_bytes_with_nul(EMPTY).unwrap()),
+ CString::from(CStr::from_bytes_with_nul(DASH_C).unwrap()),
+ CString::from(CStr::from_bytes_with_nul(BIGARG).unwrap())],
+ &[CString::from(CStr::from_bytes_with_nul(FOO).unwrap()),
+ CString::from(CStr::from_bytes_with_nul(BAZ).unwrap())]
+ $(, $flags)*)
+ }
+
+ fn common_test(syscall: fn() -> Result<std::convert::Infallible, nix::Error>) {
+ if "execveat" == stringify!($syscall) {
+ // Though undocumented, Docker's default seccomp profile seems to
+ // block this syscall. https://github.com/nix-rust/nix/issues/1122
+ skip_if_seccomp!($test_name);
+ }
+
+ let m = crate::FORK_MTX.lock();
+ // The `exec`d process will write to `writer`, and we'll read that
+ // data from `reader`.
+ let (reader, writer) = pipe().unwrap();
+
+ // 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 unsafe{fork()}.unwrap() {
+ Child => {
+ // Make `writer` be the stdout of the new process.
+ dup2(writer, 1).unwrap();
+ let r = syscall();
+ let _ = std::io::stderr()
+ .write_all(format!("{:?}", r).as_bytes());
+ // Should only get here in event of error
+ unsafe{ _exit(1) };
+ },
+ Parent { child } => {
+ // Wait for the child to exit.
+ let ws = waitpid(child, None);
+ drop(m);
+ assert_eq!(ws, Ok(WaitStatus::Exited(child, 0)));
+ // 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"));
+ }
+ }
+ }
+
+ // These tests frequently fail on musl, probably due to
+ // https://github.com/nix-rust/nix/issues/555
+ #[cfg_attr(target_env = "musl", ignore)]
+ #[test]
+ fn test_cstr_ref() {
+ common_test(syscall_cstr_ref);
+ }
+
+ // These tests frequently fail on musl, probably due to
+ // https://github.com/nix-rust/nix/issues/555
+ #[cfg_attr(target_env = "musl", ignore)]
+ #[test]
+ fn test_cstring() {
+ common_test(syscall_cstring);
+ }
+ }
+
+ )
+);
+
+cfg_if! {
+ if #[cfg(target_os = "android")] {
+ execve_test_factory!(test_execve, execve, CString::new("/system/bin/sh").unwrap().as_c_str());
+ execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd());
+ } else if #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux"))] {
+ // These tests frequently fail on musl, probably due to
+ // https://github.com/nix-rust/nix/issues/555
+ execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
+ execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd());
+ } else if #[cfg(any(target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))] {
+ execve_test_factory!(test_execve, execve, CString::new("/bin/sh").unwrap().as_c_str());
+ // No fexecve() on ios, macos, NetBSD, 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]
+#[cfg(not(target_os = "fuchsia"))]
+fn test_fchdir() {
+ // fchdir changes the process's cwd
+ let _dr = crate::DirRestore::new();
+
+ let tmpdir = tempdir().unwrap();
+ let tmpdir_path = tmpdir.path().canonicalize().unwrap();
+ let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd();
+
+ fchdir(tmpdir_fd).expect("assertion failed");
+ assert_eq!(getcwd().unwrap(), tmpdir_path);
+
+ close(tmpdir_fd).expect("assertion failed");
+}
+
+#[test]
+fn test_getcwd() {
+ // chdir changes the process's cwd
+ let _dr = crate::DirRestore::new();
+
+ let tmpdir = tempdir().unwrap();
+ let tmpdir_path = tmpdir.path().canonicalize().unwrap();
+ chdir(&tmpdir_path).expect("assertion failed");
+ 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;
+ for _ in 0..5 {
+ let newdir = "a".repeat(100);
+ inner_tmp_dir.push(newdir);
+ mkdir(inner_tmp_dir.as_path(), Mode::S_IRWXU)
+ .expect("assertion failed");
+ }
+ chdir(inner_tmp_dir.as_path()).expect("assertion failed");
+ 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 = 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_fchown() {
+ // Testing for anything other than our own UID/GID is hard.
+ let uid = Some(getuid());
+ let gid = Some(getgid());
+
+ let path = tempfile().unwrap();
+ let fd = path.as_raw_fd();
+
+ fchown(fd, uid, gid).unwrap();
+ fchown(fd, uid, None).unwrap();
+ fchown(fd, None, gid).unwrap();
+ fchown(999999999, uid, gid).unwrap_err();
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_fchownat() {
+ let _dr = crate::DirRestore::new();
+ // Testing for anything other than our own UID/GID is hard.
+ let uid = Some(getuid());
+ let gid = Some(getgid());
+
+ let tempdir = 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 offset: off_t = 5;
+ lseek(tmp.as_raw_fd(), offset, Whence::SeekSet).unwrap();
+
+ let mut buf = [0u8; 7];
+ crate::read_exact(&tmp, &mut buf);
+ assert_eq!(b"f123456", &buf);
+}
+
+#[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();
+
+ lseek64(tmp.as_raw_fd(), 5, Whence::SeekSet).unwrap();
+
+ let mut buf = [0u8; 7];
+ crate::read_exact(&tmp, &mut buf);
+ assert_eq!(b"f123456", &buf);
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ macro_rules! require_acct{
+ () => {
+ require_capability!("test_acct", CAP_SYS_PACCT);
+ }
+ }
+ } else if #[cfg(target_os = "freebsd")] {
+ macro_rules! require_acct{
+ () => {
+ skip_if_not_root!("test_acct");
+ skip_if_jailed!("test_acct");
+ }
+ }
+ } else if #[cfg(not(any(target_os = "redox", target_os = "fuchsia", target_os = "haiku")))] {
+ macro_rules! require_acct{
+ () => {
+ skip_if_not_root!("test_acct");
+ }
+ }
+ }
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+fn test_acct() {
+ use std::process::Command;
+ use std::{thread, time};
+ use tempfile::NamedTempFile;
+
+ let _m = crate::FORK_MTX.lock();
+ require_acct!();
+
+ let file = NamedTempFile::new().unwrap();
+ let path = file.path().to_str().unwrap();
+
+ acct::enable(path).unwrap();
+
+ loop {
+ Command::new("echo").arg("Hello world").output().unwrap();
+ let len = fs::metadata(path).unwrap().len();
+ if len > 0 {
+ break;
+ }
+ thread::sleep(time::Duration::from_millis(10));
+ }
+ acct::disable().unwrap();
+}
+
+#[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())
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+#[test]
+fn test_getresuid() {
+ let resuids = getresuid().unwrap();
+ assert_ne!(resuids.real.as_raw(), libc::uid_t::MAX);
+ assert_ne!(resuids.effective.as_raw(), libc::uid_t::MAX);
+ assert_ne!(resuids.saved.as_raw(), libc::uid_t::MAX);
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+#[test]
+fn test_getresgid() {
+ let resgids = getresgid().unwrap();
+ assert_ne!(resgids.real.as_raw(), libc::gid_t::MAX);
+ assert_ne!(resgids.effective.as_raw(), libc::gid_t::MAX);
+ assert_ne!(resgids.saved.as_raw(), libc::gid_t::MAX);
+}
+
+// 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 as mode_t,
+ );
+ // 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 as mode_t,
+ );
+ 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.
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "redox",
+ target_os = "solaris"
+))]
+#[test]
+fn test_pipe2() {
+ use nix::fcntl::{fcntl, FcntlArg, FdFlag};
+
+ 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]
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+fn test_truncate() {
+ let tempdir = 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 = tempdir().unwrap();
+ let path = tempdir.path().join("file");
+
+ let mut file = File::create(&path).unwrap();
+ const CONTENTS: &[u8] = b"12345678";
+ file.write_all(CONTENTS).unwrap();
+
+ ftruncate(&file, 2).unwrap();
+ drop(file);
+
+ let metadata = fs::metadata(&path).unwrap();
+ assert_eq!(2, metadata.len());
+}
+
+// Used in `test_alarm`.
+#[cfg(not(target_os = "redox"))]
+static mut ALARM_CALLED: bool = false;
+
+// Used in `test_alarm`.
+#[cfg(not(target_os = "redox"))]
+pub extern "C" fn alarm_signal_handler(raw_signal: libc::c_int) {
+ assert_eq!(raw_signal, libc::SIGALRM, "unexpected signal: {raw_signal}");
+ unsafe { ALARM_CALLED = true };
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_alarm() {
+ use std::{
+ thread,
+ time::{Duration, Instant},
+ };
+
+ // Maybe other tests that fork interfere with this one?
+ let _m = crate::SIGNAL_MTX.lock();
+
+ 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 3
+ // seconds to be sure.
+ let starttime = Instant::now();
+ loop {
+ thread::sleep(Duration::from_millis(100));
+ if unsafe { ALARM_CALLED } {
+ break;
+ }
+ if starttime.elapsed() > Duration::from_secs(3) {
+ panic!("Timeout waiting for SIGALRM");
+ }
+ }
+
+ // Reset the signal.
+ unsafe {
+ sigaction(Signal::SIGALRM, &old_handler)
+ .expect("unable to set signal handler for alarm");
+ }
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_canceling_alarm() {
+ let _m = crate::SIGNAL_MTX.lock();
+
+ assert_eq!(alarm::cancel(), None);
+
+ assert_eq!(alarm::set(60), None);
+ assert_eq!(alarm::cancel(), Some(60));
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_symlinkat() {
+ let _m = crate::CWD_LOCK.read();
+
+ let tempdir = tempdir().unwrap();
+
+ let target = tempdir.path().join("a");
+ let linkpath = tempdir.path().join("b");
+ symlinkat(&target, None, &linkpath).unwrap();
+ assert_eq!(
+ readlink(&linkpath).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))
+ .unwrap()
+ .to_str()
+ .unwrap(),
+ target
+ );
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_linkat_file() {
+ let tempdir = tempdir().unwrap();
+ let oldfilename = "foo.txt";
+ let oldfilepath = tempdir.path().join(oldfilename);
+
+ let newfilename = "bar.txt";
+ let newfilepath = tempdir.path().join(newfilename);
+
+ // Create file
+ File::create(oldfilepath).unwrap();
+
+ // Get file descriptor for base directory
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ // Attempt hard link file at relative path
+ linkat(
+ Some(dirfd),
+ oldfilename,
+ Some(dirfd),
+ newfilename,
+ LinkatFlags::SymlinkFollow,
+ )
+ .unwrap();
+ assert!(newfilepath.exists());
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_linkat_olddirfd_none() {
+ let _dr = crate::DirRestore::new();
+
+ let tempdir_oldfile = tempdir().unwrap();
+ let oldfilename = "foo.txt";
+ let oldfilepath = tempdir_oldfile.path().join(oldfilename);
+
+ let tempdir_newfile = tempdir().unwrap();
+ let newfilename = "bar.txt";
+ let newfilepath = tempdir_newfile.path().join(newfilename);
+
+ // Create file
+ File::create(oldfilepath).unwrap();
+
+ // Get file descriptor for base directory of new file
+ let dirfd = fcntl::open(
+ tempdir_newfile.path(),
+ fcntl::OFlag::empty(),
+ stat::Mode::empty(),
+ )
+ .unwrap();
+
+ // Attempt hard link file using curent working directory as relative path for old file path
+ chdir(tempdir_oldfile.path()).unwrap();
+ linkat(
+ None,
+ oldfilename,
+ Some(dirfd),
+ newfilename,
+ LinkatFlags::SymlinkFollow,
+ )
+ .unwrap();
+ assert!(newfilepath.exists());
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_linkat_newdirfd_none() {
+ let _dr = crate::DirRestore::new();
+
+ let tempdir_oldfile = tempdir().unwrap();
+ let oldfilename = "foo.txt";
+ let oldfilepath = tempdir_oldfile.path().join(oldfilename);
+
+ let tempdir_newfile = tempdir().unwrap();
+ let newfilename = "bar.txt";
+ let newfilepath = tempdir_newfile.path().join(newfilename);
+
+ // Create file
+ File::create(oldfilepath).unwrap();
+
+ // Get file descriptor for base directory of old file
+ let dirfd = fcntl::open(
+ tempdir_oldfile.path(),
+ fcntl::OFlag::empty(),
+ stat::Mode::empty(),
+ )
+ .unwrap();
+
+ // Attempt hard link file using current working directory as relative path for new file path
+ chdir(tempdir_newfile.path()).unwrap();
+ linkat(
+ Some(dirfd),
+ oldfilename,
+ None,
+ newfilename,
+ LinkatFlags::SymlinkFollow,
+ )
+ .unwrap();
+ assert!(newfilepath.exists());
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+fn test_linkat_no_follow_symlink() {
+ let _m = crate::CWD_LOCK.read();
+
+ let tempdir = tempdir().unwrap();
+ let oldfilename = "foo.txt";
+ let oldfilepath = tempdir.path().join(oldfilename);
+
+ let symoldfilename = "symfoo.txt";
+ let symoldfilepath = tempdir.path().join(symoldfilename);
+
+ let newfilename = "nofollowsymbar.txt";
+ let newfilepath = tempdir.path().join(newfilename);
+
+ // Create file
+ File::create(&oldfilepath).unwrap();
+
+ // Create symlink to file
+ symlinkat(&oldfilepath, None, &symoldfilepath).unwrap();
+
+ // Get file descriptor for base directory
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ // Attempt link symlink of file at relative path
+ linkat(
+ Some(dirfd),
+ symoldfilename,
+ Some(dirfd),
+ newfilename,
+ LinkatFlags::NoSymlinkFollow,
+ )
+ .unwrap();
+
+ // Assert newfile is actually a symlink to oldfile.
+ assert_eq!(
+ readlink(&newfilepath).unwrap().to_str().unwrap(),
+ oldfilepath.to_str().unwrap()
+ );
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_linkat_follow_symlink() {
+ let _m = crate::CWD_LOCK.read();
+
+ let tempdir = tempdir().unwrap();
+ let oldfilename = "foo.txt";
+ let oldfilepath = tempdir.path().join(oldfilename);
+
+ let symoldfilename = "symfoo.txt";
+ let symoldfilepath = tempdir.path().join(symoldfilename);
+
+ let newfilename = "nofollowsymbar.txt";
+ let newfilepath = tempdir.path().join(newfilename);
+
+ // Create file
+ File::create(&oldfilepath).unwrap();
+
+ // Create symlink to file
+ symlinkat(&oldfilepath, None, &symoldfilepath).unwrap();
+
+ // Get file descriptor for base directory
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ // Attempt link target of symlink of file at relative path
+ linkat(
+ Some(dirfd),
+ symoldfilename,
+ Some(dirfd),
+ newfilename,
+ LinkatFlags::SymlinkFollow,
+ )
+ .unwrap();
+
+ let newfilestat = stat::stat(&newfilepath).unwrap();
+
+ // Check the file type of the new link
+ assert_eq!(
+ (stat::SFlag::from_bits_truncate(newfilestat.st_mode as mode_t)
+ & SFlag::S_IFMT),
+ SFlag::S_IFREG
+ );
+
+ // Check the number of hard links to the original file
+ assert_eq!(newfilestat.st_nlink, 2);
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_unlinkat_dir_noremovedir() {
+ let tempdir = tempdir().unwrap();
+ let dirname = "foo_dir";
+ let dirpath = tempdir.path().join(dirname);
+
+ // Create dir
+ DirBuilder::new().recursive(true).create(dirpath).unwrap();
+
+ // Get file descriptor for base directory
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ // Attempt unlink dir at relative path without proper flag
+ let err_result =
+ unlinkat(Some(dirfd), dirname, UnlinkatFlags::NoRemoveDir).unwrap_err();
+ assert!(err_result == Errno::EISDIR || err_result == Errno::EPERM);
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_unlinkat_dir_removedir() {
+ let tempdir = tempdir().unwrap();
+ let dirname = "foo_dir";
+ let dirpath = tempdir.path().join(dirname);
+
+ // Create dir
+ DirBuilder::new().recursive(true).create(&dirpath).unwrap();
+
+ // Get file descriptor for base directory
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ // Attempt unlink dir at relative path with proper flag
+ unlinkat(Some(dirfd), dirname, UnlinkatFlags::RemoveDir).unwrap();
+ assert!(!dirpath.exists());
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_unlinkat_file() {
+ let tempdir = tempdir().unwrap();
+ let filename = "foo.txt";
+ let filepath = tempdir.path().join(filename);
+
+ // Create file
+ File::create(&filepath).unwrap();
+
+ // Get file descriptor for base directory
+ let dirfd =
+ fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty())
+ .unwrap();
+
+ // Attempt unlink file at relative path
+ unlinkat(Some(dirfd), filename, UnlinkatFlags::NoRemoveDir).unwrap();
+ assert!(!filepath.exists());
+}
+
+#[test]
+fn test_access_not_existing() {
+ let tempdir = tempdir().unwrap();
+ let dir = tempdir.path().join("does_not_exist.txt");
+ assert_eq!(
+ access(&dir, AccessFlags::F_OK).err().unwrap(),
+ Errno::ENOENT
+ );
+}
+
+#[test]
+fn test_access_file_exists() {
+ let tempdir = tempdir().unwrap();
+ let path = tempdir.path().join("does_exist.txt");
+ let _file = File::create(path.clone()).unwrap();
+ access(&path, AccessFlags::R_OK | AccessFlags::W_OK)
+ .expect("assertion failed");
+}
+
+//Clippy false positive https://github.com/rust-lang/rust-clippy/issues/9111
+#[allow(clippy::needless_borrow)]
+#[cfg(not(target_os = "redox"))]
+#[test]
+fn test_user_into_passwd() {
+ // get the UID of the "nobody" user
+ #[cfg(not(target_os = "haiku"))]
+ let test_username = "nobody";
+ // "nobody" unavailable on haiku
+ #[cfg(target_os = "haiku")]
+ let test_username = "user";
+
+ let nobody = User::from_name(test_username).unwrap().unwrap();
+ let pwd: libc::passwd = nobody.into();
+ let _: User = (&pwd).into();
+}
+
+/// Tests setting the filesystem UID with `setfsuid`.
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[test]
+fn test_setfsuid() {
+ use std::os::unix::fs::PermissionsExt;
+ use std::{fs, io, thread};
+ require_capability!("test_setfsuid", CAP_SETUID);
+
+ // get the UID of the "nobody" user
+ let nobody = User::from_name("nobody").unwrap().unwrap();
+
+ // create a temporary file with permissions '-rw-r-----'
+ let file = tempfile::NamedTempFile::new_in("/var/tmp").unwrap();
+ let temp_path = file.into_temp_path();
+ let temp_path_2 = temp_path.to_path_buf();
+ let mut permissions = fs::metadata(&temp_path).unwrap().permissions();
+ permissions.set_mode(0o640);
+
+ // spawn a new thread where to test setfsuid
+ thread::spawn(move || {
+ // set filesystem UID
+ let fuid = setfsuid(nobody.uid);
+ // trying to open the temporary file should fail with EACCES
+ let res = fs::File::open(&temp_path);
+ let err = res.expect_err("assertion failed");
+ assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
+
+ // assert fuid actually changes
+ let prev_fuid = setfsuid(Uid::from_raw(-1i32 as u32));
+ assert_ne!(prev_fuid, fuid);
+ })
+ .join()
+ .unwrap();
+
+ // open the temporary file with the current thread filesystem UID
+ fs::File::open(temp_path_2).unwrap();
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+fn test_ttyname() {
+ let fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
+ assert!(fd.as_raw_fd() > 0);
+
+ // on linux, we can just call ttyname on the pty master directly, but
+ // apparently osx requires that ttyname is called on a slave pty (can't
+ // find this documented anywhere, but it seems to empirically be the case)
+ grantpt(&fd).expect("grantpt failed");
+ unlockpt(&fd).expect("unlockpt failed");
+ let sname = unsafe { ptsname(&fd) }.expect("ptsname failed");
+ let fds = open(Path::new(&sname), OFlag::O_RDWR, stat::Mode::empty())
+ .expect("open failed");
+ assert!(fds > 0);
+
+ let name = ttyname(fds).expect("ttyname failed");
+ assert!(name.starts_with("/dev"));
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+fn test_ttyname_not_pty() {
+ let fd = File::open("/dev/zero").unwrap();
+ assert!(fd.as_raw_fd() > 0);
+ assert_eq!(ttyname(fd.as_raw_fd()), Err(Errno::ENOTTY));
+}
+
+#[test]
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+fn test_ttyname_invalid_fd() {
+ assert_eq!(ttyname(-1), Err(Errno::EBADF));
+}
+
+#[test]
+#[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "dragonfly",
+))]
+fn test_getpeereid() {
+ use std::os::unix::net::UnixStream;
+ let (sock_a, sock_b) = UnixStream::pair().unwrap();
+
+ let (uid_a, gid_a) = getpeereid(sock_a.as_raw_fd()).unwrap();
+ let (uid_b, gid_b) = getpeereid(sock_b.as_raw_fd()).unwrap();
+
+ let uid = geteuid();
+ let gid = getegid();
+
+ assert_eq!(uid, uid_a);
+ assert_eq!(gid, gid_a);
+ assert_eq!(uid_a, uid_b);
+ assert_eq!(gid_a, gid_b);
+}
+
+#[test]
+#[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "dragonfly",
+))]
+fn test_getpeereid_invalid_fd() {
+ // getpeereid is not POSIX, so error codes are inconsistent between different Unices.
+ getpeereid(-1).expect_err("assertion failed");
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_faccessat_none_not_existing() {
+ use nix::fcntl::AtFlags;
+ let tempdir = tempfile::tempdir().unwrap();
+ let dir = tempdir.path().join("does_not_exist.txt");
+ assert_eq!(
+ faccessat(None, &dir, AccessFlags::F_OK, AtFlags::empty())
+ .err()
+ .unwrap(),
+ Errno::ENOENT
+ );
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_faccessat_not_existing() {
+ use nix::fcntl::AtFlags;
+ let tempdir = tempfile::tempdir().unwrap();
+ let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let not_exist_file = "does_not_exist.txt";
+ assert_eq!(
+ faccessat(
+ Some(dirfd),
+ not_exist_file,
+ AccessFlags::F_OK,
+ AtFlags::empty(),
+ )
+ .err()
+ .unwrap(),
+ Errno::ENOENT
+ );
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_faccessat_none_file_exists() {
+ use nix::fcntl::AtFlags;
+ let tempdir = tempfile::tempdir().unwrap();
+ let path = tempdir.path().join("does_exist.txt");
+ let _file = File::create(path.clone()).unwrap();
+ assert!(faccessat(
+ None,
+ &path,
+ AccessFlags::R_OK | AccessFlags::W_OK,
+ AtFlags::empty(),
+ )
+ .is_ok());
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_faccessat_file_exists() {
+ use nix::fcntl::AtFlags;
+ let tempdir = tempfile::tempdir().unwrap();
+ let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
+ let exist_file = "does_exist.txt";
+ let path = tempdir.path().join(exist_file);
+ let _file = File::create(path.clone()).unwrap();
+ assert!(faccessat(
+ Some(dirfd),
+ &path,
+ AccessFlags::R_OK | AccessFlags::W_OK,
+ AtFlags::empty(),
+ )
+ .is_ok());
+}
+
+#[test]
+#[cfg(any(
+ all(target_os = "linux", not(target_env = "uclibc")),
+ target_os = "freebsd",
+ target_os = "dragonfly"
+))]
+fn test_eaccess_not_existing() {
+ let tempdir = tempdir().unwrap();
+ let dir = tempdir.path().join("does_not_exist.txt");
+ assert_eq!(
+ eaccess(&dir, AccessFlags::F_OK).err().unwrap(),
+ Errno::ENOENT
+ );
+}
+
+#[test]
+#[cfg(any(
+ all(target_os = "linux", not(target_env = "uclibc")),
+ target_os = "freebsd",
+ target_os = "dragonfly"
+))]
+fn test_eaccess_file_exists() {
+ let tempdir = tempdir().unwrap();
+ let path = tempdir.path().join("does_exist.txt");
+ let _file = File::create(path.clone()).unwrap();
+ eaccess(&path, AccessFlags::R_OK | AccessFlags::W_OK)
+ .expect("assertion failed");
+}