interprocess-1.2.1/.cargo_vcs_info.json0000644000000001360000000000100135550ustar { "git": { "sha1": "4cdf61265fec855d7e0ce997fc98f1829e29c861" }, "path_in_vcs": "" }interprocess-1.2.1/Cargo.lock0000644000000307270000000000100115410ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anyhow" version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" [[package]] name = "async-channel" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" dependencies = [ "concurrent-queue", "event-listener", "futures-core", ] [[package]] name = "async-task" version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" [[package]] name = "atomic-waker" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "blocking" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" dependencies = [ "async-channel", "async-task", "atomic-waker", "fastrand", "futures-lite", "once_cell", ] [[package]] name = "bytes" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" [[package]] name = "cache-padded" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "concurrent-queue" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" dependencies = [ "cache-padded", ] [[package]] name = "event-listener" version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" [[package]] name = "fastrand" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" dependencies = [ "instant", ] [[package]] name = "futures" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" [[package]] name = "futures-executor" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" [[package]] name = "futures-lite" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" dependencies = [ "fastrand", "futures-core", "futures-io", "memchr", "parking", "pin-project-lite", "waker-fn", ] [[package]] name = "futures-macro" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "futures-sink" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" [[package]] name = "futures-task" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" [[package]] name = "futures-util" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[package]] name = "interprocess" version = "1.2.1" dependencies = [ "anyhow", "blocking", "cfg-if", "futures", "futures-core", "futures-io", "intmap", "libc", "once_cell", "rustc_version", "spinning", "thiserror", "to_method", "tokio", "winapi", ] [[package]] name = "intmap" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" [[package]] name = "libc" version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "lock_api" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" dependencies = [ "scopeguard", ] [[package]] name = "log" version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" dependencies = [ "cfg-if", ] [[package]] name = "memchr" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "mio" version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" dependencies = [ "libc", "log", "miow", "ntapi", "winapi", ] [[package]] name = "miow" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" dependencies = [ "winapi", ] [[package]] name = "ntapi" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" dependencies = [ "winapi", ] [[package]] name = "num_cpus" version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "once_cell" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] name = "parking" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" [[package]] name = "pin-project-lite" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ "unicode-xid", ] [[package]] name = "quote" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" dependencies = [ "proc-macro2", ] [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" [[package]] name = "slab" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" [[package]] name = "spinning" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" dependencies = [ "lock_api", ] [[package]] name = "syn" version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "thiserror" version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "to_method" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" [[package]] name = "tokio" version = "1.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdc46ca74dd45faeaaf96a8fbe2406f425829705ee62100ccaa9b34a2145cff8" dependencies = [ "autocfg", "bytes", "libc", "memchr", "mio", "num_cpus", "pin-project-lite", "tokio-macros", "winapi", ] [[package]] name = "tokio-macros" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "waker-fn" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" interprocess-1.2.1/Cargo.toml0000644000000047620000000000100115640ustar # 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 = "2018" name = "interprocess" version = "1.2.1" authors = ["Kotauskas "] exclude = [ "/.github/", "/README.tpl", "/Cargo.lock", "/.gitignore", ] description = "Interprocess communication toolkit" documentation = "https://docs.rs/interprocess" readme = "README.md" keywords = [ "ipc", "shared_memory", "pipe", "unix_domain_socket", ] categories = [ "os", "os::unix-apis", "os::windows-apis", "asynchronous", ] license = "MIT OR Apache-2.0" repository = "https://github.com/kotauskas/interprocess" [package.metadata.docs.rs] features = [ "doc_cfg", "tokio_support", ] [dependencies.blocking] version = "1.0" optional = true [dependencies.cfg-if] version = "1.0" [dependencies.futures-core] version = "0.3" optional = true [dependencies.futures-io] version = "0.3" optional = true [dependencies.intmap] version = "0.7" optional = true [dependencies.libc] version = "0.2.137" features = ["extra_traits"] [dependencies.once_cell] version = "1.7" optional = true [dependencies.spinning] version = "0.1" optional = true [dependencies.thiserror] version = "1.0" optional = true [dependencies.to_method] version = "1.1" [dependencies.tokio] version = "1.8" features = [ "net", "time", "io-util", ] optional = true [dev-dependencies.anyhow] version = "1.0.32" [dev-dependencies.futures] version = "0.3" [dev-dependencies.tokio] version = "1.8" features = [ "rt-multi-thread", "io-util", "sync", "macros", ] [build-dependencies.rustc_version] version = "0.4" [features] default = [ "signals", "nonblocking", ] doc_cfg = [] nonblocking = [ "blocking", "futures-core", "futures-io", ] signals = [ "thiserror", "spinning", "intmap", "once_cell", ] tokio_support = [ "tokio", "futures-core", "futures-io", ] [target."cfg(windows)".dependencies.winapi] version = "0.3" features = [ "std", "winbase", "winerror", "processthreadsapi", "fileapi", "handleapi", "namedpipeapi", ] interprocess-1.2.1/Cargo.toml.orig000064400000000000000000000034720072674642500152720ustar 00000000000000# https://doc.rust-lang.org/cargo/reference/manifest.html [package] name = "interprocess" version = "1.2.1" authors = ["Kotauskas "] edition = "2018" license = "MIT OR Apache-2.0" readme = "README.md" documentation = "https://docs.rs/interprocess" repository = "https://github.com/kotauskas/interprocess" description = "Interprocess communication toolkit" categories = ["os", "os::unix-apis", "os::windows-apis", "asynchronous"] keywords = ["ipc", "shared_memory", "pipe", "unix_domain_socket"] exclude = ["/.github/", "/README.tpl", "/Cargo.lock", "/.gitignore"] [features] default = ["signals", "nonblocking"] signals = ["thiserror", "spinning", "intmap", "once_cell"] nonblocking = ["blocking", "futures-core", "futures-io"] tokio_support = ["tokio", "futures-core", "futures-io"] doc_cfg = [] [dependencies] libc = { version = "0.2.137", features = ["extra_traits"] } tokio = { version = "1.8", features = [ "net", "time", "io-util", ], optional = true } thiserror = { version = "1.0", optional = true } spinning = { version = "0.1", optional = true } intmap = { version = "0.7", optional = true } once_cell = { version = "1.7", optional = true } blocking = { version = "1.0", optional = true } futures-core = { version = "0.3", optional = true } futures-io = { version = "0.3", optional = true } to_method = "1.1" cfg-if = "1.0" [build-dependencies] rustc_version = "0.4" [dev-dependencies] tokio = { version = "1.8", features = [ "rt-multi-thread", "io-util", "sync", "macros", ] } futures = "0.3" anyhow = "1.0.32" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = [ "std", "winbase", "winerror", "processthreadsapi", "fileapi", "handleapi", "namedpipeapi", ] } [package.metadata.docs.rs] features = ["doc_cfg", "tokio_support"] interprocess-1.2.1/README.md000064400000000000000000000055750072674642500136700ustar 00000000000000# Interprocess [![Crates.io](https://img.shields.io/crates/v/interprocess)](https://crates.io/crates/interprocess "Interprocess on Crates.io") [![Docs.rs](https://img.shields.io/badge/documentation-docs.rs-informational)](https://docs.rs/interprocess "interprocess on Docs.rs") [![Build Status](https://github.com/kotauskas/interprocess/workflows/Checks%20and%20tests/badge.svg)](https://github.com/kotauskas/interprocess/actions "GitHub Actions page for Interprocess") ![maintenance-status](https://img.shields.io/badge/maintenance-actively%20developed-brightgreen) Interprocess communication toolkit for Rust programs. The crate aims to expose as many platform-specific features as possible while maintaining a uniform interface for all platforms. ## Features ### Interprocess communication primitives `interprocess` provides both OS-specific interfaces for IPC and cross-platform abstractions for them. #### Cross-platform IPC APIs - **Local sockets** – similar to TCP sockets, but use filesystem or namespaced paths instead of ports on `localhost`, depending on the OS, bypassing the network stack entirely; implemented using named pipes on Windows and Unix domain sockets on Unix #### Platform-specific, but present on both Unix-like systems and Windows - **Unnamed pipes** – anonymous file-like objects for communicating privately in one direction, most commonly used to communicate between a child process and its parent - **Signals** – C signals on Windows, POSIX signals on Unix-like OSes *(deprecated)* #### Unix-only - **FIFO files** – special type of file which is similar to unnamed pipes but exists on the filesystem, often referred to as "named pipes" but completely different from Windows named pipes - **Unix domain sockets** – a type of socket which is built around the standard networking APIs but uses filesystem paths instead of ports on `localhost`, optionally using a spearate namespace on Linux akin to Windows named pipes #### Windows-only - **Named pipes** – closely resembles Unix domain sockets, uses a separate namespace instead of on-drive paths ### Asynchronous I/O Currently, only Tokio for local sockets, Unix domain sockets and Windows named pipes is supported. Support for `async-std` is planned. ## Feature gates - **`signals`**, *on* by default – enables support for POSIX signals and C signals. Pulls in additional dependencies. - **`tokio_support`**, *off* by default – enables support for Tokio-powered efficient asynchronous IPC. Cannot simply be named `tokio` because of Cargo limitations. - **`nonblocking`**, *on* by default – deprecated and will be removed, do not use. ## License This crate, along with all community contributions made to it, is dual-licensed under the terms of either the [MIT license] or the [Apache 2.0 license]. [MIT license]: https://choosealicense.com/licenses/mit/ " " [Apache 2.0 license]: https://choosealicense.com/licenses/apache-2.0/ " " interprocess-1.2.1/build.rs000064400000000000000000000163530072674642500140520ustar 00000000000000use rustc_version::{version, Version}; use std::{ env::{var as env_var, var_os as env_var_os}, io::{self, Write}, }; fn main() { let version = version().expect("Rust version detection failed"); if is_unix() { let target = TargetTriplet::fetch(); collect_uds_features(&target); #[cfg(feature = "signals")] collect_signals(&target); } if checkver(&version, 52) { define("unsafe_op_in_unsafe_fn_stable"); } if checkver(&version, 53) { define("io_error_kind_unsupported_stable"); } } fn is_unix() -> bool { env_var_os("CARGO_CFG_UNIX").is_some() } /// This can define the following: /// - `uds_supported` /// - Ancillary data support: /// - `uds_scm_rights` ("passfd") /// - `uds_scm_credentials` ("passcred") /// - Credentials structure flavor: /// - `uds_ucred` /// - `uds_xucred` /// - `uds_sockcred` from NetBSD /// - Socket peer credential accessors: /// - `uds_peercred`, support for `SO_PEERCRED` /// - `uds_getpeerucred` as seen on Solaris /// - `uds_peereid`, exclusive to NetBSD /// - Address length flavors: /// - `uds_sockaddr_un_len_108` /// - `uds_sockaddr_un_len_104`, on the BSD family /// - `uds_sockaddr_un_len_126`, only on Haiku /// - `msghdr`'s `msg_iovlen` type: /// - `uds_msghdr_iovlen_c_int` /// - `uds_msghdr_iovlen_size_t`, on Linux with GNU, Android, uClibc MIPS64, and uClibc x86-64 /// - `msghdr`'s `msg_controllen` type: /// - `uds_msghdr_controllen_socklen_t` /// - `uds_msghdr_controllen_size_t`, on Linux with GNU, Android, uClibc MIPS64, and uClibc x86-64 #[rustfmt::skip] fn collect_uds_features(target: &TargetTriplet) { let (mut uds, mut scm_rights) = (false, true); if (target.os("linux") && target.env_any(&["gnu", "musl", "musleabi", "musleabihf"])) || target.os_any(&["android", "emscripten", "fuchsia", "redox"]) { // "Linux-like" in libc terminology, plus Fuchsia and Redox uds = true; define("uds_sockaddr_un_len_108"); if !target.os("emscripten") { ldefine(&["uds_ucred", "uds_scm_credentials", "uds_peercred"]); } if (target.os("linux") && target.env("gnu")) || (target.os("linux") && target.env("uclibc") && target.arch_any(&["x86_64", "mips64"])) || target.os("android") { ldefine(&["uds_msghdr_iovlen_size_t", "uds_msghdr_controllen_size_t"]); } else { ldefine(&["uds_msghdr_iovlen_c_int", "uds_msghdr_controllen_socklen_t"]); } if target.os_any(&["linux", "android"]) { // Only actual Linux has that... I think? lmao define("uds_linux_namespace"); } } else if target.env("newlib") && target.arch("xtensa") { uds = true; scm_rights = false; ldefine(&[ "sockaddr_un_len_108", "uds_msghdr_iovlen_c_int", "uds_msghdr_controllen_socklen_t", ]); } else if target.os_any(&["freebsd", "openbsd", "netbsd", "dragonfly", "macos", "ios"]) { // The BSD OS family uds = true; ldefine(&[ "uds_sockaddr_un_len_104", "uds_msghdr_iovlen_c_int", "uds_msghdr_controllen_socklen_t", "uds_xucred", ]); if target.os("netbsd") { // NetBSD differs from all other BSDs in that it uses its own // credential structure, sockcred ldefine(&["uds_sockcred", "uds_peereid"]); } else if target.os_any(&["freebsd", "dragonfly", "macos", "ios"]) { define("uds_xucred"); } } else if target.os_any(&["solaris", "illumos"]) { uds = true; ldefine(&[ "uds_sockaddr_un_len_108", "uds_getpeerucred", "uds_msghdr_iovlen_c_int", "uds_msghdr_controllen_socklen_t", ]); } else if target.os("haiku") { uds = true; ldefine(&[ "uds_sockaddr_un_len_126", "uds_ucred", "uds_peercred", "uds_msghdr_iovlen_c_int", "uds_msghdr_controllen_socklen_t", ]); } if uds { if scm_rights { define("uds_scm_rights") }; if !target.arch_any(&["x86", "x86_64"]) { define("uds_ancillary_unsound") }; define("uds_supported"); } } /// This can define the following: /// - `se_basic` (`SIGHUP`, `SIGINT`, `SIGQUIT`, `SIGILL`, `SIGABRT`, `SIGFPE`, `SIGKILL`, `SIGSEGV`, `SIGPIPE`, `SIGALRM`, `SIGTERM`), supported everywhere /// - `se_full_posix_1990` (implies `se_basic`, includes `SIGUSR1`, `SIGUSR2`, `SIGCHLD`, `SIGCONT`, `SIGSTOP`, `SIGTSTP`, `SIGTTIN`, `SIGTTOU`), supported everywhere other than HermitCore /// - `se_base_posix_2001` (implies `se_full_posix_1990`, includes `SIGBUS`, `SIGPROF`, `SIGSYS`, `SIGTRAP`, `SIGVTALRM`, `SIGXCPU`, SIGXFSZ), supported everywhere other than HermitCore /// - Either: /// - `se_sigpoll` /// - `se_sigpoll_is_sigio` /// - `se_sigwinch`, supported everywhere other than HermitCore /// - `se_sigpwr`, supported everywhere other than HermitCore and the BSD family #[rustfmt::skip] #[cfg(feature = "signals")] fn collect_signals(target: &TargetTriplet) { if !is_unix() { return }; if target.os_any( &["linux", "android", "emscripten", "fuchsia", "redox", "haiku", "solaris", "illumos"] ) { ldefine(&["se_basic", "se_full_posix_1990", "se_base_posix_2001", "se_sigwinch", "se_sigpwr"]); if target.os("redox") { define("se_sigpoll_is_sigio"); } else { define("se_sigpoll"); } } else if target.os_any(&["freebsd", "openbsd", "netbsd", "dragonfly", "macos", "ios"]) { ldefine(&["se_basic", "se_full_posix_1990", "se_base_posix_2001", "se_sigwinch"]); } else if target.os("hermit") { define("se_basic"); } } fn checkver(version: &Version, m: u64) -> bool { // A build script is needed for this because the `rustversion` crate has some weird problems // around being used as a crate-level inner attribute. *version >= Version::new(1, m, 0) } fn define(cfg: &str) { ldefine(&[cfg]); } fn ldefine(cfgs: &[&str]) { let stdout_ = io::stdout(); let mut stdout = stdout_.lock(); for i in cfgs { stdout.write_all(b"cargo:rustc-cfg=").unwrap(); stdout.write_all(i.as_ref()).unwrap(); stdout.write_all(b"\n").unwrap(); } } struct TargetTriplet { arch: String, os: String, env: Option, } #[rustfmt::skip] #[allow(dead_code)] // when signals are disabled, some of those are unused impl TargetTriplet { fn fetch() -> Self { Self { arch: env_var("CARGO_CFG_TARGET_ARCH").unwrap(), os: env_var("CARGO_CFG_TARGET_OS").unwrap(), env: env_var("CARGO_CFG_TARGET_ENV").ok(), } } fn arch(&self, arch: &str) -> bool { self.arch == arch } fn arch_any(&self, arches: &[&str]) -> bool { arches.iter().copied().any(|x| x == self.arch) } fn os(&self, os: &str) -> bool { self.os == os } fn os_any(&self, oses: &[&str]) -> bool { oses.iter().copied().any(|x| x == self.os) } fn env(&self, env: &str) -> bool { self.env.as_deref() == Some(env) } fn env_any(&self, envs: &[&str]) -> bool { if let Some(env) = self.env.as_deref() { envs.iter().copied().any(|x| x == env) } else { false } } } interprocess-1.2.1/example_main.rs000064400000000000000000000050050072674642500154020ustar 00000000000000#[allow(unused_macros)] macro_rules! main { (@bmain) => {{ use std::sync::mpsc; let (snd, rcv) = mpsc::channel(); let srv = std::thread::spawn(move || server::main(snd)); let _ = rcv.recv(); if let Err(e) = client::main() { eprintln!("Client exited early with error: {:#}", e); } if let Err(e) = srv.join().expect("server thread panicked") { eprintln!("Server exited early with error: {:#}", e); } Ok(()) }}; () => { mod client; mod server; fn main() -> anyhow::Result<()> { main!(@bmain) } }; ($($pred:tt)*) => { #[cfg(all($($pred)*))] mod client; #[cfg(all($($pred)*))] mod server; #[cfg(all($($pred)*))] fn main() -> anyhow::Result<()> { main!(@bmain); } #[cfg(not(all($($pred)*)))] fn main() -> anyhow::Result<()> { eprintln!("not supported on this platform"); Ok(()) } }; } #[allow(unused_macros)] macro_rules! tokio_main { (@bmain) => {{ use tokio::sync::oneshot; let (snd, rcv) = oneshot::channel(); let a = async { if let Err(e) = main_a(snd).await { eprintln!("Server exited early with error: {:#}", e); } }; let b = async { if rcv.await.is_ok() { if let Err(e) = main_b().await { eprintln!("Client exited early with error: {:#}", e); } } }; tokio::join!(a, b); Ok(()) }}; () => { mod client; mod server; #[tokio::main(flavor = "current_thread")] async fn main() -> anyhow::Result<()> { tokio_main!(@bmain) } }; (nomod $($pred:tt)*) => { #[cfg(all($($pred)*))] #[tokio::main(flavor = "current_thread")] async fn main() -> anyhow::Result<()> { tokio_main!(@bmain) } #[cfg(not(all($($pred)*)))] #[tokio::main(flavor = "current_thread")] async fn main() -> anyhow::Result<()> { eprintln!("not supported on this platform or feature set"); Ok(()) } }; ($($pred:tt)*) => { #[cfg(all($($pred)*))] mod client; #[cfg(all($($pred)*))] mod server; #[cfg(all($($pred)*))] use {server::main as main_a, client::main as main_b}; tokio_main!(nomod $($pred)*); }; } interprocess-1.2.1/examples/local_socket/client.rs000064400000000000000000000045240072674642500205060ustar 00000000000000use anyhow::Context; use interprocess::local_socket::{LocalSocketStream, NameTypeSupport}; use std::io::{prelude::*, BufReader}; pub fn main() -> anyhow::Result<()> { // Pick a name. There isn't a helper function for this, mostly because it's largely unnecessary: // in Rust, `match` is your concise, readable and expressive decision making construct. let name = { // This scoping trick allows us to nicely contain the import inside the `match`, so that if // any imports of variants named `Both` happen down the line, they won't collide with the // enum we're working with here. Maybe someone should make a macro for this. use NameTypeSupport::*; match NameTypeSupport::query() { OnlyPaths => "/tmp/example.sock", OnlyNamespaced | Both => "@example.sock", } }; // Preemptively allocate a sizeable buffer for reading. // This size should be enough and should be easy to find for the allocator. let mut buffer = String::with_capacity(128); // Create our connection. This will block until the server accepts our connection, but will fail // immediately if the server hasn't even started yet; somewhat similar to how happens with TCP, // where connecting to a port that's not bound to any server will send a "connection refused" // response, but that will take twice the ping, the roundtrip time, to reach the client. let conn = LocalSocketStream::connect(name).context("Failed to connect to server")?; // Wrap it into a buffered reader right away so that we could read a single line out of it. let mut conn = BufReader::new(conn); // Write our message into the stream. This will finish either when the whole message has been // writen or if a write operation returns an error. (`.get_mut()` is to get the writer, // `BufReader` doesn't implement a pass-through `Write`.) conn.get_mut() .write_all(b"Hello from client!\n") .context("Socket send failed")?; // We now employ the buffer we allocated prior and read until EOF, which the server will // similarly invoke with `.shutdown()`, verifying validity of UTF-8 on the fly. conn.read_line(&mut buffer) .context("Socket receive failed")?; // Print out the result, getting the newline for free! print!("Server answered: {}", buffer); Ok(()) } interprocess-1.2.1/examples/local_socket/main.rs000064400000000000000000000000540072674642500201460ustar 00000000000000include!("../../example_main.rs"); main!(); interprocess-1.2.1/examples/local_socket/server.rs000064400000000000000000000104740072674642500205370ustar 00000000000000use anyhow::Context; use interprocess::local_socket::{LocalSocketListener, LocalSocketStream, NameTypeSupport}; use std::{ io::{self, prelude::*, BufReader}, sync::mpsc::Sender, }; pub fn main(notify: Sender<()>) -> anyhow::Result<()> { // Define a function that checks for errors in incoming connections. We'll use this to filter // through connections that fail on initialization for one reason or another. fn handle_error(conn: io::Result) -> Option { match conn { Ok(c) => Some(c), Err(e) => { eprintln!("Incoming connection failed: {}", e); None } } } // Pick a name. There isn't a helper function for this, mostly because it's largely unnecessary: // in Rust, `match` is your concise, readable and expressive decision making construct. let name = { // This scoping trick allows us to nicely contain the import inside the `match`, so that if // any imports of variants named `Both` happen down the line, they won't collide with the // enum we're working with here. Maybe someone should make a macro for this. use NameTypeSupport::*; match NameTypeSupport::query() { OnlyPaths => "/tmp/example.sock", OnlyNamespaced | Both => "@example.sock", } }; // Bind our listener. let listener = match LocalSocketListener::bind(name) { Err(e) if e.kind() == io::ErrorKind::AddrInUse => { // One important problem that is easy to handle improperly (or not at all) is the // "corpse sockets" that are left when a program that uses a file-type socket name // terminates its socket server without deleting the file. There's no single strategy // for handling this kind of address-already-occupied error. Services that are supposed // to only exist as a single instance running on a system should check if another // instance is actually running, and if not, delete the socket file. In this example, // we leave this up to the user, but in a real application, you usually don't want to do // that. eprintln!( "\ Error: could not start server because the socket file is occupied. Please check if {} is in use by \ another process and try again.", name, ); return Err(e.into()); } x => x?, }; println!("Server running at {}", name); // Stand-in for the syncronization used, if any, between the client and the server. let _ = notify.send(()); // Preemptively allocate a sizeable buffer for reading at a later moment. This size should be // enough and should be easy to find for the allocator. Since we only have one concurrent // client, there's no need to reallocate the buffer repeatedly. let mut buffer = String::with_capacity(128); for conn in listener.incoming().filter_map(handle_error) { // Wrap the connection into a buffered reader right away // so that we could read a single line out of it. let mut conn = BufReader::new(conn); println!("Incoming connection!"); // Since our client example writes first, the server should read a line and only then send a // response. Otherwise, because reading and writing on a connection cannot be simultaneous // without threads or async, we can deadlock the two processes by having both sides wait for // the write buffer to be emptied by the other. conn.read_line(&mut buffer) .context("Socket receive failed")?; // Now that the read has come through and the client is waiting on the server's write, do // it. (`.get_mut()` is to get the writer, `BufReader` doesn't implement a pass-through // `Write`.) conn.get_mut().write_all(b"Hello from server!\n")?; // Print out the result, getting the newline for free! print!("Client answered: {}", buffer); // Let's add an exit condition to shut the server down gracefully. if buffer == "stop\n" { break; } // Clear the buffer so that the next iteration will display new data instead of messages // stacking on top of one another. buffer.clear(); } Ok(()) } interprocess-1.2.1/examples/lockstep_teletype/client.rs000064400000000000000000000012270072674642500216000ustar 00000000000000use interprocess::local_socket::LocalSocketStream; use std::io::{self, prelude::*, BufReader}; pub fn main() -> anyhow::Result<()> { let mut conn = BufReader::new(LocalSocketStream::connect("/tmp/teletype.sock")?); eprintln!("Teletype client connected to server."); let mut our_turn = true; let mut buffer = String::new(); loop { if our_turn { io::stdin().read_line(&mut buffer)?; conn.get_mut().write_all(buffer.as_ref())?; } else { conn.read_line(&mut buffer)?; io::stdout().write_all(buffer.as_ref())?; } buffer.clear(); our_turn = !our_turn; } } interprocess-1.2.1/examples/lockstep_teletype/main.rs000064400000000000000000000000540072674642500212430ustar 00000000000000include!("../../example_main.rs"); main!(); interprocess-1.2.1/examples/lockstep_teletype/server.rs000064400000000000000000000024010072674642500216230ustar 00000000000000use interprocess::local_socket::{LocalSocketListener, LocalSocketStream}; use std::{ io::{self, prelude::*, BufReader}, sync::mpsc::Sender, }; pub fn main(notify: Sender<()>) -> anyhow::Result<()> { fn handle_error(connection: io::Result) -> LocalSocketStream { match connection { Ok(val) => val, Err(error) => { eprintln!("\n"); panic!("Incoming connection failed: {}", error); } } } let listener = LocalSocketListener::bind("/tmp/teletype.sock")?; // Stand-in for the syncronization used, if any, between the client and the server. let _ = notify.send(()); eprintln!("Teletype server listening for connections."); let mut conn = listener .incoming() .next() .map(handle_error) .map(BufReader::new) .unwrap(); let mut our_turn = false; let mut buffer = String::new(); loop { if our_turn { io::stdin().read_line(&mut buffer)?; conn.get_mut().write_all(buffer.as_ref())?; } else { conn.read_line(&mut buffer)?; io::stdout().write_all(buffer.as_ref())?; } buffer.clear(); our_turn = !our_turn; } } interprocess-1.2.1/examples/tokio_local_socket/client.rs000064400000000000000000000036510072674642500217130ustar 00000000000000use futures::{ io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, try_join, }; use interprocess::local_socket::{tokio::LocalSocketStream, NameTypeSupport}; pub async fn main() -> anyhow::Result<()> { // Pick a name. There isn't a helper function for this, mostly because it's largely unnecessary: // in Rust, `match` is your concise, readable and expressive decision making construct. let name = { // This scoping trick allows us to nicely contain the import inside the `match`, so that if // any imports of variants named `Both` happen down the line, they won't collide with the // enum we're working with here. Maybe someone should make a macro for this. use NameTypeSupport::*; match NameTypeSupport::query() { OnlyPaths => "/tmp/example.sock", OnlyNamespaced | Both => "@example.sock", } }; // Await this here since we can't do a whole lot without a connection. let conn = LocalSocketStream::connect(name).await?; // This consumes our connection and splits it into two halves, // so that we could concurrently act on both. let (reader, mut writer) = conn.into_split(); let mut reader = BufReader::new(reader); // Allocate a sizeable buffer for reading. // This size should be enough and should be easy to find for the allocator. let mut buffer = String::with_capacity(128); // Describe the write operation as writing our whole string. let write = writer.write_all(b"Hello from client!\n"); // Describe the read operation as reading until a newline into our buffer. let read = reader.read_line(&mut buffer); // Concurrently perform both operations. try_join!(write, read)?; // Close the connection a bit earlier than you'd think we would. Nice practice! drop((reader, writer)); // Display the results when we're done! println!("Server answered: {}", buffer.trim()); Ok(()) } interprocess-1.2.1/examples/tokio_local_socket/main.rs000064400000000000000000000001130072674642500213470ustar 00000000000000include!("../../example_main.rs"); tokio_main!(feature = "tokio_support"); interprocess-1.2.1/examples/tokio_local_socket/server.rs000064400000000000000000000067220072674642500217450ustar 00000000000000use futures::{ io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, try_join, }; use interprocess::local_socket::{ tokio::{LocalSocketListener, LocalSocketStream}, NameTypeSupport, }; use std::io; use tokio::sync::oneshot::Sender; pub async fn main(notify: Sender<()>) -> anyhow::Result<()> { // Describe the things we do when we've got a connection ready. async fn handle_conn(conn: LocalSocketStream) -> io::Result<()> { // Split the connection into two halves to process // received and sent data concurrently. let (reader, mut writer) = conn.into_split(); let mut reader = BufReader::new(reader); // Allocate a sizeable buffer for reading. // This size should be enough and should be easy to find for the allocator. let mut buffer = String::with_capacity(128); // Describe the write operation as writing our whole message. let write = writer.write_all(b"Hello from server!\n"); // Describe the read operation as reading into our big buffer. let read = reader.read_line(&mut buffer); // Run both operations concurrently. try_join!(read, write)?; // Dispose of our connection right now and not a moment later because I want to! drop((reader, writer)); // Produce our output! println!("Client answered: {}", buffer.trim()); Ok(()) } // Pick a name. There isn't a helper function for this, mostly because it's largely unnecessary: // in Rust, `match` is your concise, readable and expressive decision making construct. let name = { // This scoping trick allows us to nicely contain the import inside the `match`, so that if // any imports of variants named `Both` happen down the line, they won't collide with the // enum we're working with here. Maybe someone should make a macro for this. use NameTypeSupport::*; match NameTypeSupport::query() { OnlyPaths => "/tmp/example.sock", OnlyNamespaced | Both => "@example.sock", } }; // Create our listener. In a more robust program, we'd check for an // existing socket file that has not been deleted for whatever reason, // ensure it's a socket file and not a normal file, and delete it. let listener = LocalSocketListener::bind(name)?; // Stand-in for the syncronization used, if any, between the client and the server. let _ = notify.send(()); println!("Server running at {}", name); // Set up our loop boilerplate that processes our incoming connections. loop { // Sort out situations when establishing an incoming connection caused an error. let conn = match listener.accept().await { Ok(c) => c, Err(e) => { eprintln!("There was an error with an incoming connection: {}", e); continue; } }; // Spawn new parallel asynchronous tasks onto the Tokio runtime // and hand the connection over to them so that multiple clients // could be processed simultaneously in a lightweight fashion. tokio::spawn(async move { // The outer match processes errors that happen when we're // connecting to something. The inner if-let processes errors that // happen during the connection. if let Err(e) = handle_conn(conn).await { eprintln!("Error while handling connection: {}", e); } }); } } interprocess-1.2.1/examples/tokio_named_pipe/client.rs000064400000000000000000000032350072674642500213500ustar 00000000000000use futures::{ io::{AsyncReadExt, AsyncWriteExt}, try_join, }; use interprocess::os::windows::named_pipe::tokio::*; use std::error::Error; pub async fn main() -> Result<(), Box> { // Await this here since we can't do a whole lot without a connection. let conn = DuplexBytePipeStream::connect("Example")?; // This consumes our connection and splits it into two owned halves, so that we could // concurrently act on both. Take care not to use the .split() method from the futures crate's // AsyncReadExt. let (mut reader, mut writer) = conn.split(); // Preemptively allocate a sizeable buffer for reading. // This size should be enough and should be easy to find for the allocator. let mut buffer = String::with_capacity(128); // Describe the write operation as writing our whole string, waiting for // that to complete, and then shutting down the write half, which sends // an EOF to the other end to help it determine where the message ends. let write = async { writer.write_all(b"Hello from client!").await?; // Because only the trait from futures is implemented for now, it's "close" instead of // "shutdown". writer.close().await?; Ok(()) }; // Describe the read operation as reading until EOF into our big buffer. let read = reader.read_to_string(&mut buffer); // Concurrently perform both operations: write-and-send-EOF and read. try_join!(write, read)?; // Get rid of those here to close the read half too. drop((reader, writer)); // Display the results when we're done! println!("Server answered: {}", buffer.trim()); Ok(()) } interprocess-1.2.1/examples/tokio_named_pipe/main.rs000064400000000000000000000001240072674642500210100ustar 00000000000000include!("../../example_main.rs"); tokio_main!(windows, feature = "tokio_support"); interprocess-1.2.1/examples/tokio_named_pipe/server.rs000064400000000000000000000062200072674642500213750ustar 00000000000000use futures::{ io::{AsyncReadExt, AsyncWriteExt}, try_join, }; use interprocess::os::windows::named_pipe::{tokio::*, PipeListenerOptions}; use std::{error::Error, ffi::OsStr, io}; use tokio::sync::oneshot::Sender; pub async fn main(notify: Sender<()>) -> Result<(), Box> { // Describe the things we do when we've got a connection ready. async fn handle_conn(conn: DuplexBytePipeStream) -> io::Result<()> { // Split the connection into two halves to process // received and sent data concurrently. let (mut reader, mut writer) = conn.split(); // Allocate a sizeable buffer for reading. // This size should be enough and should be easy to find for the allocator. let mut buffer = String::with_capacity(128); // Describe the write operation as first writing our whole message, and // then shutting down the write half to send an EOF to help the other // side determine the end of the transmission. let write = async { writer.write_all(b"Hello from server!").await?; writer.close().await?; Ok(()) }; // Describe the read operation as reading into our big buffer. let read = reader.read_to_string(&mut buffer); // Run both the write-and-send-EOF operation and the read operation concurrently. try_join!(read, write)?; // Dispose of our connection right now and not a moment later because I want to! drop((reader, writer)); // Produce our output! println!("Client answered: {}", buffer.trim()); Ok(()) } static PIPE_NAME: &str = "Example"; // Create our listener. In a more robust program, we'd check for an // existing socket file that has not been deleted for whatever reason, // ensure it's a socket file and not a normal file, and delete it. let listener = PipeListenerOptions::new() .name(OsStr::new(PIPE_NAME)) .create_tokio::()?; // Stand-in for the syncronization used, if any, between the client and the server. let _ = notify.send(()); println!(r"Server running at \\.\pipe\{}", PIPE_NAME); // Set up our loop boilerplate that processes our incoming connections. loop { // Sort out situations when establishing an incoming connection caused an error. let conn = match listener.accept().await { Ok(c) => c, Err(e) => { eprintln!("There was an error with an incoming connection: {}", e); continue; } }; // Spawn new parallel asynchronous tasks onto the Tokio runtime // and hand the connection over to them so that multiple clients // could be processed simultaneously in a lightweight fashion. tokio::spawn(async move { // The outer match processes errors that happen when we're // connecting to something. The inner if-let processes errors that // happen during the connection. if let Err(e) = handle_conn(conn).await { eprintln!("error while handling connection: {}", e); } }); } } interprocess-1.2.1/examples/tokio_udsocket/inner.rs000064400000000000000000000032000072674642500207150ustar 00000000000000use interprocess::os::unix::udsocket::tokio::*; use std::{io, mem::MaybeUninit}; use tokio::{io::ReadBuf, sync::oneshot::Sender, try_join}; pub async fn main(src: &str, dst: &str, notify: Option>) -> io::Result<()> { let socket_path = format!("/tmp/{}", src); // Socket creation happens immediately, no futures here. let socket = UdSocket::bind(socket_path)?; if let Some(n) = notify { let _ = n.send(()); } // So does destination assignment. socket.set_destination(dst)?; // Allocate a stack buffer for reading at a later moment. let mut buffer = [MaybeUninit::::uninit(); 128]; let mut readbuf = ReadBuf::uninit(&mut buffer); let message = format!("Hello from {}!", src); // Describe the write operation, but don't run it yet. // We'll launch it concurrently with the read operation. let write = socket.send(message.as_bytes()); // Describe the read operation, and also don't run it yet. let read = socket.recv(&mut readbuf); // Perform both operations concurrently: the write and the read. try_join!(write, read)?; // Clean up early. Good riddance! drop(socket); // Convert the data that's been read into a string. This checks for UTF-8 // validity, and if invalid characters are found, a new buffer is // allocated to house a modified version of the received data, where // decoding errors are replaced with those diamond-shaped question mark // U+FFFD REPLACEMENT CHARACTER thingies: �. let received_string = String::from_utf8_lossy(readbuf.filled()); println!("Server answered: {}", &received_string); Ok(()) } interprocess-1.2.1/examples/tokio_udsocket/main.rs000064400000000000000000000010410072674642500205270ustar 00000000000000include!("../../example_main.rs"); #[cfg(all(unix, feature = "tokio_support"))] mod inner; #[allow(dead_code)] static A: &str = "side_a"; #[allow(dead_code)] static B: &str = "side_b"; #[cfg(all(unix, feature = "tokio_support"))] pub async fn main_a(notify: tokio::sync::oneshot::Sender<()>) -> std::io::Result<()> { inner::main(A, B, Some(notify)).await } #[cfg(all(unix, feature = "tokio_support"))] pub async fn main_b() -> std::io::Result<()> { inner::main(B, A, None).await } tokio_main!(nomod unix, feature = "tokio_support"); interprocess-1.2.1/examples/tokio_udstream/client.rs000064400000000000000000000030310072674642500210650ustar 00000000000000use interprocess::os::unix::udsocket::tokio::*; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, try_join, }; pub async fn main() -> anyhow::Result<()> { // Await this here since we can't do a whole lot without a connection. let mut conn = UdStream::connect("/tmp/example.sock").await?; // This takes an exclusive borrow of our connection and splits it into two // halves, so that we could concurrently act on both. Take care not to use // the .split() method from the futures crate's AsyncReadExt. let (mut reader, mut writer) = conn.split(); // Allocate a sizeable buffer for reading. // This size should be enough and should be easy to find for the allocator. let mut buffer = String::with_capacity(128); // Describe the write operation as writing our whole string, waiting for // that to complete, and then shutting down the write half, which sends // an EOF to the other end to help it determine where the message ends. let write = async { writer.write_all(b"Hello from client!\n").await?; writer.shutdown()?; Ok(()) }; // Describe the read operation as reading until EOF into our big buffer. let read = reader.read_to_string(&mut buffer); // Concurrently perform both operations: write-and-send-EOF and read. try_join!(write, read)?; // Close the connection a bit earlier than you'd think we would. Nice practice! drop(conn); // Display the results when we're done! println!("Server answered: {}", buffer.trim()); Ok(()) } interprocess-1.2.1/examples/tokio_udstream/main.rs000064400000000000000000000001210072674642500205300ustar 00000000000000include!("../../example_main.rs"); tokio_main!(unix, feature = "tokio_support"); interprocess-1.2.1/examples/tokio_udstream/server.rs000064400000000000000000000060020072674642500211160ustar 00000000000000use interprocess::os::unix::udsocket::tokio::{UdStream, UdStreamListener}; use std::io; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, try_join, sync::oneshot::Sender, }; pub async fn main(notify: Sender<()>) -> anyhow::Result<()> { // Describe the things we do when we've got a connection ready. async fn handle_conn(mut conn: UdStream) -> io::Result<()> { // Split the connection into two halves to process // received and sent data concurrently. let (mut reader, mut writer) = conn.split(); // Allocate a sizeable buffer for reading. // This size should be enough and should be easy to find for the allocator. let mut buffer = String::with_capacity(128); // Describe the write operation as first writing our whole message, and // then shutting down the write half to send an EOF to help the other // side determine the end of the transmission. let write = async { writer.write_all(b"Hello from server!").await?; writer.shutdown()?; Ok(()) }; // Describe the read operation as reading into our big buffer. let read = reader.read_to_string(&mut buffer); // Run both the write-and-send-EOF operation and the read operation concurrently. try_join!(read, write)?; // Dispose of our connection right now and not a moment later because I want to! drop(conn); // Produce our output! println!("Client answered: {}", buffer.trim()); Ok(()) } static SOCKET_PATH: &str = "/tmp/example.sock"; // Create our listener. In a more robust program, we'd check for an // existing socket file that has not been deleted for whatever reason, // ensure it's a socket file and not a normal file, and delete it. let listener = UdStreamListener::bind(SOCKET_PATH)?; // Stand-in for the syncronization used, if any, between the client and the server. let _ = notify.send(()); println!("Server running at {}", SOCKET_PATH); // Set up our loop boilerplate that processes our incoming connections. loop { // Sort out situations when establishing an incoming connection caused an error. let conn = match listener.accept().await { Ok(c) => c, Err(e) => { eprintln!("There was an error with an incoming connection: {}", e); continue; } }; // Spawn new parallel asynchronous tasks onto the Tokio runtime // and hand the connection over to them so that multiple clients // could be processed simultaneously in a lightweight fashion. tokio::spawn(async move { // The outer match processes errors that happen when we're // connecting to something. The inner if-let processes errors that // happen during the connection. if let Err(e) = handle_conn(conn).await { eprintln!("error while handling connection: {}", e); } }); } } interprocess-1.2.1/examples/unidirectional_teletype/client.rs000064400000000000000000000004430072674642500227640ustar 00000000000000use interprocess::local_socket::LocalSocketStream; use std::io; pub fn main() -> anyhow::Result<()> { let mut stream = LocalSocketStream::connect("/tmp/teletype.sock")?; eprintln!("Teletype client connected to server.\n"); io::copy(&mut io::stdin(), &mut stream)?; Ok(()) } interprocess-1.2.1/examples/unidirectional_teletype/main.rs000064400000000000000000000000540072674642500224300ustar 00000000000000include!("../../example_main.rs"); main!(); interprocess-1.2.1/examples/unidirectional_teletype/server.rs000064400000000000000000000014170072674642500230160ustar 00000000000000use interprocess::local_socket::{LocalSocketListener, LocalSocketStream}; use std::{io, sync::mpsc::Sender}; pub fn main(notify: Sender<()>) -> anyhow::Result<()> { fn handle_error(connection: io::Result) -> LocalSocketStream { match connection { Ok(val) => val, Err(error) => { eprintln!("\n"); panic!("Incoming connection failed: {}", error); } } } let listener = LocalSocketListener::bind("/tmp/teletype.sock")?; let _ = notify.send(()); eprintln!("Teletype server listening for connections."); for mut conn in listener.incoming().map(handle_error) { println!("\n"); io::copy(&mut conn, &mut io::stdout())?; } unreachable!() } interprocess-1.2.1/src/lib.rs000064400000000000000000000122210072674642500142760ustar 00000000000000//! Interprocess communication toolkit for Rust programs. The crate aims to expose as many platform-specific features as possible while maintaining a uniform interface for all platforms. //! //! # Features //! ## Interprocess communication primitives //! `interprocess` provides both OS-specific interfaces for IPC and cross-platform abstractions for them. //! //! ### Cross-platform IPC APIs //! - **Local sockets** – similar to TCP sockets, but use filesystem or namespaced paths instead of ports on `localhost`, depending on the OS, bypassing the network stack entirely; implemented using named pipes on Windows and Unix domain sockets on Unix //! //! ### Platform-specific, but present on both Unix-like systems and Windows //! - **Unnamed pipes** – anonymous file-like objects for communicating privately in one direction, most commonly used to communicate between a child process and its parent //! - **Signals** – C signals on Windows, POSIX signals on Unix-like OSes *(deprecated)* //! //! ### Unix-only //! - **FIFO files** – special type of file which is similar to unnamed pipes but exists on the filesystem, often referred to as "named pipes" but completely different from Windows named pipes //! - **Unix domain sockets** – a type of socket which is built around the standard networking APIs but uses filesystem paths instead of ports on `localhost`, optionally using a spearate namespace on Linux akin to Windows named pipes //! //! ### Windows-only //! - **Named pipes** – closely resembles Unix domain sockets, uses a separate namespace instead of on-drive paths //! //! ## Asynchronous I/O //! Currently, only Tokio for local sockets, Unix domain sockets and Windows named pipes is supported. Support for `async-std` is planned. //! //! # Feature gates //! - **`signals`**, *on* by default – enables support for POSIX signals and C signals. Pulls in additional dependencies. //! - **`tokio_support`**, *off* by default – enables support for Tokio-powered efficient asynchronous IPC. Cannot simply be named `tokio` because of Cargo limitations. //! - **`nonblocking`**, *on* by default – deprecated and will be removed, do not use. //! //! # License //! This crate, along with all community contributions made to it, is dual-licensed under the terms of either the [MIT license] or the [Apache 2.0 license]. //! //! [MIT license]: https://choosealicense.com/licenses/mit/ " " //! [Apache 2.0 license]: https://choosealicense.com/licenses/apache-2.0/ " " // TODO mailslots // TODO shared memory // TODO use standard library raw+owned FDs and handles // TODO the Intra Doc Link Sweep // - **Mailslots** – Windows-specific interprocess communication primitive for short messages, potentially even across the network // - **Shared memory** – exposes a nice safe interface for shared memory based on mapping identifiers, with some additional platform-specific extensions #![cfg_attr(feature = "doc_cfg", feature(doc_cfg))] #![deny(rust_2018_idioms)] #![warn(missing_docs)] #![allow(clippy::nonstandard_macro_braces)] #![cfg_attr( unsafe_op_in_unsafe_fn_stable, // This is set by the build script on Rust 1.52+ forbid(unsafe_op_in_unsafe_fn), )] #![cfg_attr(not(unsafe_op_in_unsafe_fn_stable), allow(unused_unsafe))] // If an operating system is not listed here, the `compile_error!` is invoked #[cfg(not(any( // "Linux-like" (src/unix/linux_like/mod.rs in libc) target_os = "linux", target_os = "android", target_os = "emscripten", // Windows. There is just one. target_os = "windows", // "BSD-like" (src/unix/bsd/mod.rs in libc) target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly", target_os = "macos", target_os = "ios", // "Solarish" (src/unix/solarish/mod.rs in libc) target_os = "solaris", target_os = "illumos", // Haiku (src/unix/haiku/mod.rs in libc) target_os = "haiku", // Hermit (src/unix/hermit/mod.rs in libc) target_os = "hermit", // Redox (src/unix/redox/mod.rs in libc) target_os = "redox", )))] compile_error!("Your target operating system is not supported by interprocess – check if yours is in the list of supported systems, and if not, please open an issue on the GitHub repository if you think that it should be included"); #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] compile_error!("Platforms with exotic pointer widths (neither 32-bit nor 64-bit) are not supported by interprocess – if you think that your specific case needs to be accounted for, please open an issue on the GitHub repository"); #[macro_use] mod macros; pub mod local_socket; #[cfg(any(doc, feature = "nonblocking"))] #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "nonblocking")))] #[deprecated(note = "\ does not integrate with async runtimes, leading to poor performance and bugs related to reading \ and writing at the same time (you can't) – see the `tokio` modules for relevant IPC primitives \ or open an issue if you want more async runtimes to be supported as well")] pub mod nonblocking; pub mod unnamed_pipe; //pub mod shared_memory; pub mod os; mod sealed; pub(crate) use sealed::Sealed; mod reliable_read_msg; pub use reliable_read_msg::*; interprocess-1.2.1/src/local_socket/listener.rs000064400000000000000000000062600072674642500200250ustar 00000000000000use { super::{LocalSocketStream, ToLocalSocketName}, std::{ fmt::{self, Debug, Formatter}, io, iter::FusedIterator, }, }; impmod! {local_socket, LocalSocketListener as LocalSocketListenerImpl } /// A local socket server, listening for connections. /// /// # Examples /// - [Basic server](https://github.com/kotauskas/interprocess/blob/main/examples/local_socket/server.rs) pub struct LocalSocketListener { inner: LocalSocketListenerImpl, } impl LocalSocketListener { /// Creates a socket server with the specified local socket name. pub fn bind<'a>(name: impl ToLocalSocketName<'a>) -> io::Result { Ok(Self { inner: LocalSocketListenerImpl::bind(name)?, }) } /// Listens for incoming connections to the socket, blocking until a client is connected. /// /// See [`incoming`] for a convenient way to create a main loop for a server. /// /// [`incoming`]: #method.incoming " " pub fn accept(&self) -> io::Result { Ok(LocalSocketStream { inner: self.inner.accept()?, }) } /// Creates an infinite iterator which calls `accept()` with each iteration. Used together with `for` loops to conveniently create a main loop for a socket server. pub fn incoming(&self) -> Incoming<'_> { Incoming::from(self) } /// Enables or disables the nonblocking mode for the listener. By default, it is disabled. /// /// In nonblocking mode, calling [`accept`] and iterating through [`incoming`] will immediately return a [`WouldBlock`] error if there is no client attempting to connect at the moment instead of blocking until one arrives. /// /// # Platform-specific behavior /// ## Windows /// The nonblocking mode will be also be set for the streams produced by [`accept`] and [`incoming`], both existing and new ones. /// /// [`WouldBlock`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.WouldBlock " " /// [`accept`]: #method.accept " " /// [`incoming`]: #method.incoming " " pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.inner.set_nonblocking(nonblocking) } } impl Debug for LocalSocketListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { Debug::fmt(&self.inner, f) } } /// An infinite iterator over incoming client connections of a [`LocalSocketListener`]. /// /// This iterator is created by the [`incoming`] method on [`LocalSocketListener`] – see its documentation for more. /// /// [`LocalSocketListener`]: struct.LocalSocketListener.html " " /// [`incoming`]: struct.LocalSocketListener.html#method.incoming " " #[derive(Debug)] pub struct Incoming<'a> { listener: &'a LocalSocketListener, } impl<'a> From<&'a LocalSocketListener> for Incoming<'a> { fn from(listener: &'a LocalSocketListener) -> Self { Self { listener } } } impl Iterator for Incoming<'_> { type Item = io::Result; fn next(&mut self) -> Option { Some(self.listener.accept()) } fn size_hint(&self) -> (usize, Option) { (usize::MAX, None) } } impl FusedIterator for Incoming<'_> {} interprocess-1.2.1/src/local_socket/mod.rs000064400000000000000000000057620072674642500167650ustar 00000000000000//! Local sockets, an IPC primitive featuring a server and multiple clients connecting to that server using a filesystem path inside a special namespace, each having a private connection to that server. //! //! Local sockets are not a real IPC method implemented by the OS – they were introduced because of the difference between named pipes on Windows and Unix: named pipes on Windows are almost the same as Unix domain sockets on Linux while Unix named pipes (which are referred to as FIFO files in this crate to avoid confusion) are like unnamed pipes but identifiable with a filesystem path: there's no distinction between writers and the first reader takes all. **Simply put, local sockets use named pipes on Windows and Unix domain sockets on Unix.** //! //! ## Differences from regular sockets //! A few missing features, primarily on Windows, require local sockets to omit some important functionality, because code relying on it wouldn't be portable. Some notable differences are: //! - No `.shutdown()` – your communication protocol must manually negotiate end of transmission. Notably, `.read_to_string()` and `.read_all()` will always block indefinitely at some point. They will be changed to panic instead in a future version. //! - No vectored I/O – Windows named pipes use the Win32 file API, rather than the Winsock API, for read-write operations, so scatter-gather I/O works the same way it does with regular files, which is incompatible with the Rust standard library API for it. //! - No datagram sockets – the difference in semantics between connectionless datagram Ud-sockets and connection-based named message pipes on Windows does not allow bridging those two into a common API. Streams are fast enough anyway, so no big deal, right? //! //! ## Platform-specific namespaces //! There's one more problem regarding platform differences: since only Linux supports putting Ud-sockets in a separate namespace which is isolated from the filesystem, the `LocalSocketName`/`LocalSocketNameBuf` types are used to identify local sockets rather than `OsStr`/`OsString`: on Unix platforms other than Linux, which includes macOS, all flavors of BSD and possibly other Unix-like systems, the only way to name a Ud-socket is to use a filesystem path. As such, those platforms don't have the namespaced socket creation method available. Complicatng matters further, Windows does not support named pipes in the normal filesystem, meaning that namespaced local sockets are the only functional method on Windows. As a way to solve this issue, `LocalSocketName`/`LocalSocketNameBuf` only provide creation in a platform-specific way, meaning that crate users are required to use conditional compilation to decide on the socket names. #[cfg(any(doc, feature = "tokio_support"))] #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "tokio_support")))] pub mod tokio; mod listener; pub use listener::*; mod stream; pub use stream::*; mod name; pub use name::*; mod name_type_support; pub use name_type_support::*; mod to_name; pub use to_name::*; interprocess-1.2.1/src/local_socket/name.rs000064400000000000000000000125730072674642500171240ustar 00000000000000use { super::NameTypeSupport, std::{ borrow::Cow, ffi::{OsStr, OsString}, }, }; /// A name for a local socket. /// /// Due to vast differences between platforms in terms of how local sockets are named, there needs to be a way to store and process those in a unified way while also retaining platform-specific pecularities. `LocalSocketName` aims to bridge the gap between portability and platform-specific correctness. /// /// # Creation /// A separate trait is used to create names from basic strings: [`ToLocalSocketName`](super::ToLocalSocketName). Aside from being conveniently implemented on every single string type in the standard library, it also provides some special processing. Please read its documentation if you haven't already – the rest of this page assumes you did. /// /// # Validity /// As mentioned in the [module-level documentation], not all platforms support all types of local socket names. A name pointing to a filesystem location is only supported on Unix-like systems, and names pointing to an abstract namespace reserved specifically for local sockets are only available on Linux and Windows. Due to the diversity of those differences, `LocalSocketName` does not provide any forced validation by itself – the [`is_supported`] and [`is_always_supported`] checks are not enforced to succeed. Instead, they are intended as helpers for the process of user input validation, if any local socket names are ever read from environment variables, configuration files or other methods of user input. /// /// If an invalid local socket name is used to create a local socket or connect to it, the creation/connection method will fail. /// /// [`to_local_socket_name`]: trait.ToLocalSocketName.html " " /// [module-level documentation]: index.html " " /// [`is_supported`]: #method.is_supported " " /// [`is_always_supported`]: #method.is_always_supported " " pub struct LocalSocketName<'a> { inner: Cow<'a, OsStr>, namespaced: bool, } impl<'a> LocalSocketName<'a> { /// Returns `true` if the type of the name is supported by the OS, `false` otherwise. /// /// The check is performed at runtime. For a conservative compile-time check, see [`.is_always_supported`](Self::is_always_supported). pub fn is_supported(&self) -> bool { self.is_supported_in_nts_type(NameTypeSupport::query()) } /// Returns `true` if the type of the name is supported by the OS, `false` otherwise. /// /// The check is performed at compile-time. For a check which might return a more permissive result on certain platforms by checking for support at runtime, see [`.is_supported()`](Self::is_supported). pub const fn is_always_supported(&self) -> bool { self.is_supported_in_nts_type(NameTypeSupport::ALWAYS_AVAILABLE) } /// Returns `true` if the type of the name is supported by an OS with the specified name type support class, `false` otherwise. /// /// This is mainly a helper function for [`.is_supported()`](Self::is_supported) and [`.is_always_supported()`](Self::is_always_supported), but there's no good reason not to expose it as a public method, so why not? pub const fn is_supported_in_nts_type(&self, nts: NameTypeSupport) -> bool { (self.is_namespaced() && nts.namespace_supported()) || (self.is_path() && nts.paths_supported()) } /// Returns `true` if the value is a namespaced name, `false` otherwise. pub const fn is_namespaced(&self) -> bool { self.namespaced } /// Returns `true` if the value is a filesystem path, `false` otherwise. pub const fn is_path(&self) -> bool { !self.namespaced } /// Returns the name as an `OsStr`. The returned value does not retain the type of the name (whether it was a filesystem path or a namespaced name). /// /// If you need the value as an owned `OsString` instead, see [`.into_inner()`](Self::into_inner). pub fn inner(&'a self) -> &'a OsStr { &self.inner } /// Returns the name as an `OsString`. The returned value does not retain the type of the name (whether it was a filesystem path or a namespaced name). /// /// If you need the value as a borrowed `OsStr` instead, see [`.inner()`](Self::inner). pub fn into_inner(self) -> OsString { self.inner.into_owned() } /// Returns the name as a *borrowed* `Cow<'_, OsStr>`. The returned value does not retain the type of the name (whether it was a filesystem path or a namespaced name). /// /// If you need the value as a borrowed `OsStr`, see [`.inner()`](Self::inner); if you need the value as an owned `OsString`, see [`.into_inner()`](Self::into_inner). If you need to take ownership of the `Cow`, see [`.into_inner_cow()`](Self::into_inner_cow). pub const fn inner_cow(&'a self) -> &'a Cow<'a, OsStr> { &self.inner } /// Returns the name as a `Cow<'_, OsStr>`. The returned value does not retain the type of the name (whether it was a filesystem path or a namespaced name). /// /// If you need the value as a borrowed `OsStr`, see [`inner`]; if you need the value as an owned `OsString`, see [`.into_inner()`](Self::into_inner). If you don't need to take ownership of the `Cow`, see [`.inner_cow()`](Self::inner_cow). pub fn into_inner_cow(self) -> Cow<'a, OsStr> { self.inner } pub(crate) const fn from_raw_parts(inner: Cow<'a, OsStr>, namespaced: bool) -> Self { Self { inner, namespaced } } } interprocess-1.2.1/src/local_socket/name_type_support.rs000064400000000000000000000053370072674642500217610ustar 00000000000000impmod! {local_socket, name_type_support_query as name_type_support_query_impl, NAME_TYPE_ALWAYS_SUPPORTED as NAME_TYPE_ALWAYS_SUPPORTED_REAL, } /// Represents which kinds of identifiers can be used for a local socket's name on the current platform. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum NameTypeSupport { /// Only filesystem paths can be used. /// /// This is true for all Unix/POSIX and Unix-like systems other than Linux. OnlyPaths, /// Only names in an dedicated namespace can be used. /// /// This is true only for Windows. OnlyNamespaced, /// Both of the above options are available. /// /// This is true only for Linux. Both, } impl NameTypeSupport { /// The types of local socket names supported on the current platform regardless of the environment and OS version. /// /// On most platforms, the value is known at compile time, i.e. the support for paths wasn't introduced in a specific version of the OS or isn't known to be supported at all. **Currently, this includes all supported OSes.** If support is added for an OS which added this functionality in a specific version, this constant will be the most restrictive value for that platform, with [`query`] possibly returning the actual value according to the current version of the OS. /// /// Simply put, you should probably just use this value for consistency across platforms, unless you really need a specific name type to be supported. /// /// [`query`]: #method.query " " pub const ALWAYS_AVAILABLE: Self = NAME_TYPE_ALWAYS_SUPPORTED_REAL; /// Returns the types of local socket names supported on the current platform with the current environment. /// /// On most platforms, the value is known at compile time, i.e. the support for one of the types wasn't introduced in an update to the OS or isn't known to be supported at all. **Currently, this includes all supported OSes.** For compatibility with OSes which might add the functionality in the future starting with a specific version, this function isn't a `const fn` – see [`ALWAYS_AVAILABLE`] if you need a constant expression. /// /// [`ALWAYS_AVAILABLE`]: #associatedconstant.ALWAYS_AVAILABLE " " pub fn query() -> Self { name_type_support_query_impl() } /// Returns `true` if, according to `self`, filesystem-based local sockets are supported; `false` otherwise. pub const fn paths_supported(self) -> bool { matches!(self, Self::OnlyPaths | Self::Both) } /// Returns `true` if, according to `self`, namespaced local socket names are supported; `false` otherwise. pub const fn namespace_supported(self) -> bool { matches!(self, Self::OnlyNamespaced | Self::Both) } } interprocess-1.2.1/src/local_socket/stream.rs000064400000000000000000000053420072674642500174730ustar 00000000000000use { super::ToLocalSocketName, std::{ fmt::{self, Debug, Formatter}, io::{self, prelude::*, IoSlice, IoSliceMut}, }, }; impmod! {local_socket, LocalSocketStream as LocalSocketStreamImpl } /// A local socket byte stream, obtained eiter from [`LocalSocketListener`] or by connecting to an existing local socket. /// /// # Examples /// - [Basic client](https://github.com/kotauskas/interprocess/blob/main/examples/local_socket/client.rs) /// /// [`LocalSocketListener`]: struct.LocalSocketListener.html " " pub struct LocalSocketStream { pub(super) inner: LocalSocketStreamImpl, } impl LocalSocketStream { /// Connects to a remote local socket server. pub fn connect<'a>(name: impl ToLocalSocketName<'a>) -> io::Result { Ok(Self { inner: LocalSocketStreamImpl::connect(name)?, }) } /// Retrieves the identifier of the process on the opposite end of the local socket connection. /// /// # Platform-specific behavior /// ## macOS and iOS /// Not supported by the OS, will always generate an error at runtime. pub fn peer_pid(&self) -> io::Result { self.inner.peer_pid() } /// Enables or disables the nonblocking mode for the stream. By default, it is disabled. /// /// In nonblocking mode, reading and writing will immediately return with the [`WouldBlock`] error in situations when they would normally block for an uncontrolled amount of time. The specific situations are: /// - When reading is attempted and there is no new data available; /// - When writing is attempted and the buffer is full due to the other side not yet having read previously sent data. /// /// [`WouldBlock`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.WouldBlock " " pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.inner.set_nonblocking(nonblocking) } } // TODO panic on read-to-end and read-to-string // TODO vectored I/O on Unix impl Read for LocalSocketStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.inner.read(buf) } fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { self.inner.read_vectored(bufs) } } impl Write for LocalSocketStream { fn write(&mut self, buf: &[u8]) -> io::Result { self.inner.write(buf) } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.inner.write_vectored(bufs) } fn flush(&mut self) -> io::Result<()> { self.inner.flush() } } impl Debug for LocalSocketStream { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { Debug::fmt(&self.inner, f) } } impl_handle_manip!(LocalSocketStream); interprocess-1.2.1/src/local_socket/to_name.rs000064400000000000000000000201260072674642500176170ustar 00000000000000use { super::LocalSocketName, std::{ borrow::Cow, ffi::{CStr, CString, OsStr, OsString}, io, path::{Path, PathBuf}, str, }, }; impmod! {local_socket, to_local_socket_name_osstr, to_local_socket_name_osstring, } /// Types which can be converted to a local socket name. /// /// The difference between this trait and [`TryInto`]`<`[`LocalSocketName`]`>` is that the latter does not constrain the error type to be [`io::Error`] and thus is not compatible with many types from the standard library which are widely expected to be convertible to Unix domain socket paths. Additionally, this makes the special syntax for namespaced sockets possible (see below). /// /// ## `@` syntax for namespaced paths /// As mentioned in the [`LocalSocketName` documentation][`LocalSocketName`], there are two types of which local socket names can be: filesystem paths and namespaced names. Those are isolated from each other – there's no portable way to represent one using another, though certain OSes might provide ways to do so – Windows does, for example. To be able to represent both in a platform-independent fashion, a special syntax was implemented in implementations of this trait on types from the standard library: "@ syntax". /// /// The feature, in its core, is extremely simple: if the first character in a string is the @ character, the value of the string is interpreted and stored as a namespaced name (otherwise, it's treated as a filesystem path); the @ character is then removed from the string (by taking a subslice which dosen't include it if a string slice is being used; for owned strings, it's simply removed from the string by shifting the entire string towards the beginning). **[`Path`] and [`PathBuf`] are not affected at all – those have explicit path semantics and therefore cannot logically represent namespaced names.** /// /// This feature is extremely useful both when using hardcoded literals and accepting user input for the path, but sometimes you might want to prevent this behavior. In such a case, you have the following possible approaches: /// - If the string is a [`OsStr`]/[`OsString`], it can be cheaply converted to a [`Path`]/[`PathBuf`], which do not support the @ syntax /// - If the string is a [`str`]/[`String`], it can be cheaply converted to [`OsStr`]/[`OsString`]; then the above method can be applied /// - If the string is a [`CStr`]/[`CString`], it can be converted to [`str`]/[`String`] using the following code: /// ``` /// # use std::{ /// # str::Utf8Error, /// # ffi::{CStr, CString}, /// # }; /// fn cstr_to_str(val: &CStr) -> Result<&str, Utf8Error> { /// std::str::from_utf8(val.to_bytes_with_nul()) /// } /// fn cstring_to_string(val: CString) -> String { /// String::from_utf8_lossy(&val.into_bytes_with_nul()).into() /// } /// ``` /// Then, the method for [`str`]/[`String`] can be applied. /// /// None of the above conversions perform memory allocations – the only expensive one is [`CStr`]/[`CString`] which performs a check for valid UTF-8. /// /// [`LocalSocketName`]: struct.LocalSocketName.html " " /// [`TryInto`]: https://doc.rust-lang.org/std/convert/trait.TryInto.html " " /// [`str`]: https://doc.rust-lang.org/std/primitive.str.html " " /// [`String`]: https://doc.rust-lang.org/std/string/struct.String.html " " /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html " " /// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html " " /// [`CStr`]: https://doc.rust-lang.org/std/ffi/struct.CStr.html " " /// [`CString`]: https://doc.rust-lang.org/std/ffi/struct.CString.html " " /// [`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html " " /// [`PathBuf`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html " " pub trait ToLocalSocketName<'a> { /// Performs the conversion to a local socket name. #[allow(clippy::wrong_self_convention)] // shut the fuck up fn to_local_socket_name(self) -> io::Result>; } /// Converts a borrowed [`Path`] to a borrowed file-type [`LocalSocketName`] with the same lifetime. impl<'a> ToLocalSocketName<'a> for &'a Path { fn to_local_socket_name(self) -> io::Result> { Ok(LocalSocketName::from_raw_parts( Cow::Borrowed(self.as_os_str()), false, )) } } /// Converts an owned [`PathBuf`] to an owned file-type [`LocalSocketName`]. impl ToLocalSocketName<'static> for PathBuf { fn to_local_socket_name(self) -> io::Result> { Ok(LocalSocketName::from_raw_parts( Cow::Owned(self.into_os_string()), false, )) } } /// Converts a borrowed [`OsStr`] to a borrowed [`LocalSocketName`] with the same lifetime. On platforms which don't support namespaced socket names, the result is always a file-type name; on platforms that do, prefixing the name with the `@` character will trim it away and yield a namespaced name instead. See the trait-level documentation for more. impl<'a> ToLocalSocketName<'a> for &'a OsStr { fn to_local_socket_name(self) -> io::Result> { Ok(to_local_socket_name_osstr(self)) } } /// Converts an owned [`OsString`] to an owned [`LocalSocketName`]. On platforms which don't support namespaced socket names, the result is always a file-type name; on platforms that do, prefixing the name with the `@` character will trim it away and yield a namespaced name instead. See the trait-level documentation for more. impl ToLocalSocketName<'static> for OsString { fn to_local_socket_name(self) -> io::Result> { Ok(to_local_socket_name_osstring(self)) } } /// Converts a borrowed [`str`](prim@str) to a borrowed [`LocalSocketName`] with the same lifetime. On platforms which don't support namespaced socket names, the result is always a file-type name; on platforms that do, prefixing the name with the `@` character will trim it away and yield a namespaced name instead. See the trait-level documentation for more. impl<'a> ToLocalSocketName<'a> for &'a str { fn to_local_socket_name(self) -> io::Result> { OsStr::new(self).to_local_socket_name() } } /// Converts an owned [`String`] to an owned [`LocalSocketName`]. On platforms which don't support namespaced socket names, the result is always a file-type name; on platforms that do, prefixing the name with the `@` character will trim it away and yield a namespaced name instead. See the trait-level documentation for more. impl ToLocalSocketName<'static> for String { fn to_local_socket_name(self) -> io::Result> { OsString::from(self).to_local_socket_name() } } /// Converts a borrowed [`CStr`] to a borrowed [`LocalSocketName`] with the same lifetime. **UTF-8 is assumed and the nul terminator is preserved during conversion**. On platforms which don't support namespaced socket names, the result is always a file-type name; on platforms that do, prefixing the name with the `@` character will trim it away and yield a namespaced name instead. See the trait-level documentation for more. // FIXME chop off the nul impl<'a> ToLocalSocketName<'a> for &'a CStr { fn to_local_socket_name(self) -> io::Result> { str::from_utf8(self.to_bytes_with_nul()) .map(|x| to_local_socket_name_osstr(OsStr::new(x))) .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error)) } } /// Converts an owned [`CString`] to an owned [`LocalSocketName`]. **UTF-8 is assumed and the nul terminator is preserved during conversion**. On platforms which don't support namespaced socket names, the result is always a file-type name; on platforms that do, prefixing the name with the `@` character will trim it away and yield a namespaced name instead. See the trait-level documentation for more. impl ToLocalSocketName<'static> for CString { fn to_local_socket_name(self) -> io::Result> { String::from_utf8(self.into_bytes_with_nul()) .map(|x| to_local_socket_name_osstring(OsString::from(x))) .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error)) } } interprocess-1.2.1/src/local_socket/tokio/listener.rs000064400000000000000000000024050072674642500211470ustar 00000000000000use { super::{super::ToLocalSocketName, LocalSocketStream}, std::{ fmt::{self, Debug, Formatter}, io, }, }; #[cfg(feature = "tokio_support")] impmod! {local_socket::tokio, LocalSocketListener as LocalSocketListenerImpl } #[cfg(not(feature = "tokio_support"))] struct LocalSocketListenerImpl; /// A Tokio-based local socket server, listening for connections. /// /// # Example /// - [Basic server](https://github.com/kotauskas/interprocess/blob/main/examples/tokio_local_socket/server.rs) pub struct LocalSocketListener { inner: LocalSocketListenerImpl, } impl LocalSocketListener { /// Creates a socket server with the specified local socket name. pub fn bind<'a>(name: impl ToLocalSocketName<'a>) -> io::Result { Ok(Self { inner: LocalSocketListenerImpl::bind(name)?, }) } /// Listens for incoming connections to the socket, asynchronously waiting until a client is connected. pub async fn accept(&self) -> io::Result { Ok(LocalSocketStream { inner: self.inner.accept().await?, }) } } impl Debug for LocalSocketListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { Debug::fmt(&self.inner, f) } } // TODO: incoming interprocess-1.2.1/src/local_socket/tokio/mod.rs000064400000000000000000000012400072674642500200750ustar 00000000000000//! Asynchronous local sockets which work with the Tokio runtime and event loop. //! //! The Tokio integration allows the local socket streams and listeners to be notified by the OS kernel whenever they're ready to be read from of written to, instead of spawning threads just to put them in a wait state of blocking on the I/O. //! //! Types from this module will *not* work with other async runtimes, such as `async-std` or `smol`, since the Tokio types' methods will panic whenever they're called outside of a Tokio runtime context. Open an issue if you'd like to see other runtimes supported as well. mod listener; pub use listener::*; mod stream; pub use stream::*; interprocess-1.2.1/src/local_socket/tokio/stream/mod.rs000064400000000000000000000063220072674642500213760ustar 00000000000000mod read_half; pub use read_half::*; mod write_half; pub use write_half::*; use { super::super::ToLocalSocketName, futures_io::{AsyncRead, AsyncWrite}, std::{ fmt::{self, Debug, Formatter}, io::{self, IoSlice, IoSliceMut}, pin::Pin, task::{Context, Poll}, }, }; #[cfg(feature = "tokio_support")] impmod! {local_socket::tokio, LocalSocketStream as LocalSocketStreamImpl } #[cfg(not(feature = "tokio_support"))] struct LocalSocketStreamImpl; /// A Tokio-based local socket byte stream, obtained eiter from [`LocalSocketListener`] or by connecting to an existing local socket. /// /// # Examples /// - [Basic client](https://github.com/kotauskas/interprocess/blob/main/examples/tokio_local_socket/client.rs) /// /// [`LocalSocketListener`]: struct.LocalSocketListener.html " " pub struct LocalSocketStream { pub(super) inner: LocalSocketStreamImpl, } impl LocalSocketStream { /// Connects to a remote local socket server. pub async fn connect<'a>(name: impl ToLocalSocketName<'a>) -> io::Result { Ok(Self { inner: LocalSocketStreamImpl::connect(name).await?, }) } /// Splits a stream into a read half and a write half, which can be used to read and write the stream concurrently. pub fn into_split(self) -> (OwnedReadHalf, OwnedWriteHalf) { let (r, w) = self.inner.into_split(); (OwnedReadHalf { inner: r }, OwnedWriteHalf { inner: w }) } /// Retrieves the identifier of the process on the opposite end of the local socket connection. /// /// # Platform-specific behavior /// ## macOS and iOS /// Not supported by the OS, will always generate an error at runtime. pub fn peer_pid(&self) -> io::Result { self.inner.peer_pid() } fn pinproj(&mut self) -> Pin<&mut LocalSocketStreamImpl> { Pin::new(&mut self.inner) } } impl AsyncRead for LocalSocketStream { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { self.pinproj().poll_read(cx, buf) } fn poll_read_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { self.pinproj().poll_read_vectored(cx, bufs) } } impl AsyncWrite for LocalSocketStream { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.pinproj().poll_write(cx, buf) } fn poll_write_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { self.pinproj().poll_write_vectored(cx, bufs) } // Those don't do anything fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproj().poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproj().poll_close(cx) } } impl Debug for LocalSocketStream { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { Debug::fmt(&self.inner, f) } } impl_as_raw_handle!(LocalSocketStream); interprocess-1.2.1/src/local_socket/tokio/stream/read_half.rs000064400000000000000000000034350072674642500225260ustar 00000000000000use { futures_io::AsyncRead, std::{ fmt::{self, Debug, Formatter}, io::{self, IoSliceMut}, pin::Pin, task::{Context, Poll}, }, }; #[cfg(feature = "tokio_support")] impmod! {local_socket::tokio, OwnedReadHalf as OwnedReadHalfImpl } #[cfg(not(feature = "tokio_support"))] struct OwnedReadHalfImpl; /// An owned read half of a Tokio-based local socket stream, obtained by splitting a [`LocalSocketStream`]. /// /// # Examples /// - [Basic client](https://github.com/kotauskas/interprocess/blob/main/examples/tokio_local_socket/client.rs) /// /// [`LocalSocketStream`]: struct.LocalSocketStream.html " " pub struct OwnedReadHalf { pub(super) inner: OwnedReadHalfImpl, } impl OwnedReadHalf { /// Retrieves the identifier of the process on the opposite end of the local socket connection. /// /// # Platform-specific behavior /// ## macOS and iOS /// Not supported by the OS, will always generate an error at runtime. pub fn peer_pid(&self) -> io::Result { self.inner.peer_pid() } fn pinproj(&mut self) -> Pin<&mut OwnedReadHalfImpl> { Pin::new(&mut self.inner) } } impl AsyncRead for OwnedReadHalf { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { self.pinproj().poll_read(cx, buf) } fn poll_read_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { self.pinproj().poll_read_vectored(cx, bufs) } } impl Debug for OwnedReadHalf { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { Debug::fmt(&self.inner, f) } } // TODO can't do this on Unix //impl_as_raw_handle!(OwnedReadHalf); interprocess-1.2.1/src/local_socket/tokio/stream/write_half.rs000064400000000000000000000041170072674642500227430ustar 00000000000000use { futures_io::AsyncWrite, std::{ fmt::{self, Debug, Formatter}, io::{self, IoSlice}, pin::Pin, task::{Context, Poll}, }, }; #[cfg(feature = "tokio_support")] impmod! {local_socket::tokio, OwnedWriteHalf as OwnedWriteHalfImpl } #[cfg(not(feature = "tokio_support"))] struct OwnedWriteHalfImpl; /// An owned write half of a Tokio-based local socket stream, obtained by splitting a [`LocalSocketStream`]. /// /// # Examples /// - [Basic client](https://github.com/kotauskas/interprocess/blob/main/examples/tokio_local_socket/client.rs) /// /// [`LocalSocketStream`]: struct.LocalSocketStream.html " " pub struct OwnedWriteHalf { pub(super) inner: OwnedWriteHalfImpl, } impl OwnedWriteHalf { /// Retrieves the identifier of the process on the opposite end of the local socket connection. /// /// # Platform-specific behavior /// ## macOS and iOS /// Not supported by the OS, will always generate an error at runtime. pub fn peer_pid(&self) -> io::Result { self.inner.peer_pid() } fn pinproj(&mut self) -> Pin<&mut OwnedWriteHalfImpl> { Pin::new(&mut self.inner) } } impl AsyncWrite for OwnedWriteHalf { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.pinproj().poll_write(cx, buf) } fn poll_write_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { self.pinproj().poll_write_vectored(cx, bufs) } // Those don't do anything fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproj().poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproj().poll_close(cx) } } impl Debug for OwnedWriteHalf { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { Debug::fmt(&self.inner, f) } } // TODO can't do this on Unix //impl_as_raw_handle!(OwnedWriteHalf); interprocess-1.2.1/src/macros/handle_and_fd.rs000064400000000000000000000075070072674642500175550ustar 00000000000000macro_rules! impl_as_raw_handle { ($ty:ident) => { #[cfg(doc)] impl $crate::os::windows::imports::AsRawHandle for $ty { #[cfg(windows)] fn as_raw_handle(&self) -> *mut ::std::ffi::c_void { $crate::os::windows::imports::AsRawHandle::as_raw_handle(&self.inner) } } #[cfg(all(not(doc), windows))] impl ::std::os::windows::io::AsRawHandle for $ty { fn as_raw_handle(&self) -> *mut ::std::ffi::c_void { ::std::os::windows::io::AsRawHandle::as_raw_handle(&self.inner) } } #[cfg(doc)] impl $crate::os::unix::imports::AsRawFd for $ty { #[cfg(unix)] fn as_raw_fd(&self) -> ::libc::c_int { $crate::os::unix::imports::AsRawFd::as_raw_fd(&self.inner) } } #[cfg(all(not(doc), unix))] impl $crate::os::unix::imports::AsRawFd for $ty { fn as_raw_fd(&self) -> ::libc::c_int { ::std::os::unix::io::AsRawFd::as_raw_fd(&self.inner) } } }; } macro_rules! impl_into_raw_handle { ($ty:ident) => { #[cfg(doc)] impl $crate::os::windows::imports::IntoRawHandle for $ty { #[cfg(windows)] fn into_raw_handle(self) -> *mut ::std::ffi::c_void { $crate::os::windows::imports::IntoRawHandle::into_raw_handle(self.inner) } } #[cfg(all(not(doc), windows))] impl ::std::os::windows::io::IntoRawHandle for $ty { fn into_raw_handle(self) -> *mut ::std::ffi::c_void { ::std::os::windows::io::IntoRawHandle::into_raw_handle(self.inner) } } #[cfg(doc)] impl $crate::os::unix::imports::IntoRawFd for $ty { #[cfg(unix)] fn into_raw_fd(self) -> ::libc::c_int { $crate::os::unix::imports::IntoRawFd::into_raw_fd(self.inner) } } #[cfg(all(not(doc), unix))] impl ::std::os::unix::io::IntoRawFd for $ty { fn into_raw_fd(self) -> ::libc::c_int { ::std::os::unix::io::IntoRawFd::into_raw_fd(self.inner) } } }; } macro_rules! impl_from_raw_handle { ($ty:ident) => { #[cfg(doc)] impl $crate::os::windows::imports::FromRawHandle for $ty { #[cfg(windows)] unsafe fn from_raw_handle(handle: *mut ::std::ffi::c_void) -> Self { Self { inner: unsafe { $crate::os::windows::imports::FromRawHandle::from_raw_handle(handle) }, } } } #[cfg(all(not(doc), windows))] impl ::std::os::windows::io::FromRawHandle for $ty { unsafe fn from_raw_handle(handle: *mut ::std::ffi::c_void) -> Self { Self { inner: unsafe { ::std::os::windows::io::FromRawHandle::from_raw_handle(handle) }, } } } #[cfg(doc)] impl $crate::os::unix::imports::FromRawFd for $ty { #[cfg(unix)] unsafe fn from_raw_fd(fd: ::libc::c_int) -> Self { Self { inner: unsafe { $crate::os::unix::imports::FromRawFd::from_raw_fd(fd) }, } } } #[cfg(all(not(doc), unix))] impl ::std::os::unix::io::FromRawFd for $ty { unsafe fn from_raw_fd(fd: ::libc::c_int) -> Self { Self { inner: unsafe { ::std::os::unix::io::FromRawFd::from_raw_fd(fd) }, } } } }; } macro_rules! impl_handle_manip { ($ty:ident) => { impl_as_raw_handle!($ty); impl_into_raw_handle!($ty); impl_from_raw_handle!($ty); }; } interprocess-1.2.1/src/macros/import_tables.rs000064400000000000000000000147750072674642500177000ustar 00000000000000macro_rules! import_type_or_make_dummy { ( types {$path:path} :: ( $( // Instruction name, base path part and root repetition block $src_name:ident // The struct name to import $(as $dst_name:ident)? // The name to reexport as $(< $($lt:lifetime),+ $(,)? >)? // Matches, optionally, a separator comma, and a // comma-separated lifetime list within angle // brackets, with an optional trailing comma ),+ , // Mandatory trailing comma for the repitition list, or else teh compiler complains // for some reason ), cfg($pred:meta) // A cfg(...) predicate $(,)? // Optional trailing comma for the whole macro ) => {$( import_type_or_make_dummy!( type {$path}::$src_name $(as $dst_name)? $(< $($lt),+ >)?, cfg($pred), ); )+}; ( type // Instruction name {$path:path}::$src_name:ident // The path to import, and the struct name as $dst_name:ident // The name to reexport as $(< $($lt:lifetime),+ $(,)? >)?, // From this point onwards, the same stuff as above cfg($pred:meta) $(,)? ) => { #[cfg($pred)] pub use $path::{$src_name as $dst_name}; #[cfg(not($pred))] #[derive(Copy, Clone, Debug, Default)] pub struct $dst_name // Expands the struct name $(<$($lt),+>)? // Expands the lifetimes as declaration of generic parameters ($($(::core::marker::PhantomData<& $lt ()>),+)?); // Creates a tuple of PhantomData, // one per lifetime }; ( // Matches the same stuff, but without the `as` type {$path:path}::$name:ident $(< $($lt:lifetime),+ $(,)? >)?, cfg($pred:meta) $(,)? ) => { import_type_or_make_dummy!(type {$path}::$name as $name $(< $($lt),+ >)?, cfg($pred)); }; } macro_rules! import_type_alias_or_make_dummy { ( types {$path:path} :: ( // Instruction name and base path part $( // Root repetition block $src_name:ident // The type alias name to import $(as $dst_name:ident)? // The type alias name to reexport as = $dummy:ty // The fallback re-definition $(,)? // Per-type-alias optional trailing comma ),+ , // Mandatory trailing comma, because an optional one breaks everything for some reason ), cfg($pred:meta) // A cfg(...) predicate $(,)? // Optional trailing comma for whole macro ) => {$( import_type_alias_or_make_dummy!( type {$path}::$src_name $(as $dst_name)? = $dummy, cfg($pred), ); )+}; ( type // Instruction name {$path:path}::$src_name:ident // Path and type alias name to export as $dst_name:ident // The same stuff as in the repeating case, basically = $dummy:ty, cfg($pred:meta) $(,)? ) => { #[cfg($pred)] pub(super) use $path::{$src_name as $dst_name}; #[cfg(not($pred))] pub(super) type $dst_name = $dummy; }; ( type // Same as above, but without the `as` part {$path:path}::$name:ident = $dummy:ty, cfg($pred:meta) $(,)? ) => { import_type_alias_or_make_dummy!(type {$path}::$name as $name = $dummy, cfg($pred)); }; } macro_rules! import_const_or_make_dummy { ( $ty:ty: consts {$path:path} :: ( // Instruction name, constant type and base path part $( // Root repetition block $src_name:ident // The constant name to import $(as $dst_name:ident)? // The constant name to reexport as = $dummy:expr // The fallback re-definition $(,)? // Per-constant optional trailing comma ),+ , // Mandatory trailing comma, because an optional one breaks everything for some reason ), cfg($pred:meta) // A cfg(...) predicate $(,)? // Optional trailing comma for whole macro ) => {$( import_const_or_make_dummy!( $ty: const {$path}::$src_name $(as $dst_name)? = $dummy, cfg($pred), ); )+}; ( $ty:ty: const // Instruction name and constant type {$path:path}::$src_name:ident // Path and constant name to export as $dst_name:ident // The same stuff as in the repeating case, basically = $dummy:expr, cfg($pred:meta) $(,)? ) => { #[cfg($pred)] pub(super) use $path::{$src_name as $dst_name}; #[cfg(not($pred))] pub(super) const $dst_name: $ty = $dummy; }; ( $ty:ty: const // Same as above, but without the `as` part {$path:path}::$name:ident = $dummy:expr, cfg($pred:meta) $(,)? ) => { import_const_or_make_dummy!($ty: const {$path}::$name as $name = $dummy, cfg($pred)); }; } macro_rules! import_trait_or_make_dummy { ( traits {$path:path} :: ( // Instruction name and base path part $( // Root repetition block $(($unsafety:tt))? $src_name:ident // The trait name to import $(as $dst_name:ident)? // The trait name to reexport as ),+ , // Mandatory trailing comma for the repetition, the compiler complains otherwise ), cfg($pred:meta) // A cfg(...) predicate $(,)? // Optional trailing comma for the whole macro ) => {$( import_trait_or_make_dummy!( $(($unsafety))? trait {$path}::$src_name $(as $dst_name)?, cfg($pred), ); )+}; ( $(($unsafety:tt))? trait // Instruction name, and whether the trait is unsafe {$path:path}::$src_name:ident // Path and type alias name to export as $dst_name:ident, // The same stuff as in the repeating case, basically cfg($pred:meta) $(,)? ) => { #[cfg($pred)] pub use $path::{$src_name as $dst_name}; #[cfg(not($pred))] pub $($unsafety)? trait $dst_name {} }; ( $(($unsafety:tt))? trait // Same as above, but without the `as` part {$path:path}::$name:ident, cfg($pred:meta) $(,)? ) => { import_trait_or_make_dummy!($(($unsafety))? trait {$path}::$name as $name, cfg($pred)); }; } interprocess-1.2.1/src/macros/mod.rs000064400000000000000000000005470072674642500156030ustar 00000000000000#[macro_use] mod handle_and_fd; #[macro_use] mod import_tables; macro_rules! impmod { ($($osmod:ident)::+, $($orig:ident $(as $into:ident)?),* $(,)?) => { #[cfg(unix)] use $crate::os::unix::$($osmod)::+::{$($orig $(as $into)?,)*}; #[cfg(windows)] use $crate::os::windows::$($osmod)::+::{$($orig $(as $into)?,)*}; }; } interprocess-1.2.1/src/nonblocking/imports.rs000064400000000000000000000005370072674642500175370ustar 00000000000000#![allow(unused_imports)] use cfg_if::cfg_if; cfg_if! { if #[cfg(feature = "nonblocking")] { pub use blocking::{unblock, Unblock}; pub use { futures_core::{FusedStream, Stream}, futures_io::{AsyncRead, AsyncWrite}, }; } else { pub type Unblock = std::marker::PhantomData; } } interprocess-1.2.1/src/nonblocking/local_socket.rs000064400000000000000000000117100072674642500204770ustar 00000000000000//! Asynchronous local sockets. //! //! See the [blocking version of this module] for more on what those are. //! //! [blocking version of this module]: ../../local_socket/index.html " " use super::imports::*; use crate::local_socket::{self as sync, ToLocalSocketName}; use std::{ io, pin::Pin, sync::Arc, task::{Context, Poll}, }; /// An asynchronous local socket server, listening for connections. #[derive(Debug)] pub struct LocalSocketListener { inner: Arc, } impl LocalSocketListener { /// Creates a socket server with the specified local socket name. pub async fn bind<'a>(name: impl ToLocalSocketName<'_> + Send + 'static) -> io::Result { Ok(Self { inner: Arc::new(unblock(move || sync::LocalSocketListener::bind(name)).await?), }) } /// Listens for incoming connections to the socket, blocking until a client is connected. /// /// See [`incoming`] for a convenient way to create a main loop for a server. /// /// [`incoming`]: #method.incoming " " pub async fn accept(&self) -> io::Result { let s = self.inner.clone(); Ok(LocalSocketStream { inner: Unblock::new(unblock(move || s.accept()).await?), }) } /// Creates an infinite asynchronous stream which calls `accept()` with each iteration. Used together with [`for_each`]/[`try_for_each`] stream adaptors to conveniently create a main loop for a socket server. /// /// # Example /// See struct-level documentation for a complete example which already uses this method. /// /// [`for_each`]: https://docs.rs/futures/*/futures/stream/trait.StreamExt.html#method.for_each " " /// [`try_for_each`]: https://docs.rs/futures/*/futures/stream/trait.TryStreamExt.html#method.try_for_each " " pub fn incoming(&self) -> Incoming { Incoming { inner: Unblock::new(SyncArcIncoming { inner: Arc::clone(&self.inner), }), } } } /// An infinite asynchronous stream over incoming client connections of a [`LocalSocketListener`]. /// /// This stream is created by the [`incoming`] method on [`LocalSocketListener`] – see its documentation for more. /// /// [`LocalSocketListener`]: struct.LocalSocketListener.html " " /// [`incoming`]: struct.LocalSocketListener.html#method.incoming " " #[derive(Debug)] pub struct Incoming { inner: Unblock, } #[cfg(feature = "nonblocking")] impl Stream for Incoming { type Item = Result; fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { let poll = as Stream>::poll_next(Pin::new(&mut self.inner), ctx); match poll { Poll::Ready(val) => { let val = val.map(|val| match val { Ok(inner) => Ok(LocalSocketStream { inner: Unblock::new(inner), }), Err(error) => Err(error), }); Poll::Ready(val) } Poll::Pending => Poll::Pending, } } } #[cfg(feature = "nonblocking")] impl FusedStream for Incoming { fn is_terminated(&self) -> bool { false } } #[derive(Debug)] struct SyncArcIncoming { inner: Arc, } impl Iterator for SyncArcIncoming { type Item = Result; fn next(&mut self) -> Option { Some(self.inner.accept()) } } /// An asynchronous local socket byte stream, obtained eiter from [`LocalSocketListener`] or by connecting to an existing local socket. #[derive(Debug)] pub struct LocalSocketStream { inner: Unblock, } impl LocalSocketStream { /// Connects to a remote local socket server. pub async fn connect<'a>( name: impl ToLocalSocketName<'a> + Send + 'static, ) -> io::Result { Ok(Self { inner: Unblock::new(unblock(move || sync::LocalSocketStream::connect(name)).await?), }) } } #[cfg(feature = "nonblocking")] impl AsyncRead for LocalSocketStream { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { AsyncRead::poll_read(Pin::new(&mut self.inner), cx, buf) } } #[cfg(feature = "nonblocking")] impl AsyncWrite for LocalSocketStream { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { AsyncWrite::poll_write(Pin::new(&mut self.inner), cx, buf) } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { AsyncWrite::poll_flush(Pin::new(&mut self.inner), cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { AsyncWrite::poll_close(Pin::new(&mut self.inner), cx) } } interprocess-1.2.1/src/nonblocking/mod.rs000064400000000000000000000011620072674642500166140ustar 00000000000000//! Non-blocking wrappers for blocking interprocess communication primitives. //! //! Blocking is unacceptable in an async context, as it is the very problem that asynchrony aims to mitigate. This module contains wrappers for the base interprocess primitives, allowing their usage in an asyncronous runtime. //! //! The layout of this module aims to closely resemble the crate root, in that all the modules here mirror their blocking counterparts – check them out for usage examples and details about the differences you may encounter when porting blocking code to an async architecture. mod imports; pub mod local_socket; interprocess-1.2.1/src/os/mod.rs000064400000000000000000000014120072674642500147300ustar 00000000000000//! Platform-specific functionality for various interprocess communication primitives. //! //! This module houses two modules: [`unix`] and [`windows`]. Modules and items for foreign platforms are visible even if they're not available on your platform, so watch out. If you're using [Docs.rs], which enables the nightly-only `doc_cfg` feature by default, everything platform-specific will have a badge next to it which specifies the `cfg(...)` conditions for that item to be available. //! //! [`unix`]: mod.unix.html " " //! [`windows`]: mod.windows.html " " //! [Docs.rs]: https://Docs.rs/ " " #[cfg(any(unix, doc))] #[cfg_attr(feature = "doc_cfg", doc(cfg(unix)))] pub mod unix; #[cfg(any(windows, doc))] #[cfg_attr(feature = "doc_cfg", doc(cfg(windows)))] pub mod windows; interprocess-1.2.1/src/os/unix/cfg_doc_templates.rs000064400000000000000000000027430072674642500206060ustar 00000000000000/* // You can't generate those with macros just yet, so copypasting is the way for now. #[cfg_attr( // uds_ucred template feature = "doc_cfg", doc(cfg(any( all( target_os = "linux", any( target_env = "gnu", target_env = "musl", target_env = "musleabi", target_env = "musleabihf" ) ), target_os = "emscripten", target_os = "redox" ))) )] #[cfg_attr( // uds_linux_namespace template feature = "doc_cfg", doc(cfg(any(target_os = "linux", target_os = "android"))) )] #[cfg_attr( // uds_peercred template feature = "doc_cfg", doc(cfg(any( all( target_os = "linux", any( target_env = "gnu", target_env = "musl", target_env = "musleabi", target_env = "musleabihf" ) ), target_os = "emscripten", target_os = "redox", target_os = "haiku" ))) )] #[cfg_attr( // any(se_sigpoll, se_sigpoll_is_sigio) template feature = "doc_cfg", doc(cfg(any( target_os = "linux", target_os = "android", target_os = "emscripten", target_os = "redox", target_os = "haiku", target_os = "solaris", target_os = "illumos" ))) )] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 template feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] */ interprocess-1.2.1/src/os/unix/fdops.rs000064400000000000000000000100330072674642500162460ustar 00000000000000use super::imports::*; use std::{ io::{self, IoSlice, IoSliceMut}, marker::PhantomData, mem::ManuallyDrop, }; use to_method::To; #[repr(transparent)] pub(super) struct FdOps(pub(super) c_int, PhantomData<*mut ()>); impl FdOps { pub fn new(fd: c_int) -> Self { Self(fd, PhantomData) } pub fn read(&self, buf: &mut [u8]) -> io::Result { let (success, bytes_read) = unsafe { let length_to_read = buf.len(); let size_or_err = libc::read(self.as_raw_fd(), buf.as_mut_ptr() as *mut _, length_to_read); (size_or_err >= 0, size_or_err as usize) }; if success { Ok(bytes_read) } else { Err(io::Error::last_os_error()) } } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { let (success, bytes_read) = unsafe { let num_bufs = bufs.len().try_to::().unwrap_or(c_int::MAX); let size_or_err = libc::readv(self.as_raw_fd(), bufs.as_mut_ptr() as *const _, num_bufs); (size_or_err >= 0, size_or_err as usize) }; if success { Ok(bytes_read) } else { Err(io::Error::last_os_error()) } } pub fn write(&self, buf: &[u8]) -> io::Result { let (success, bytes_written) = unsafe { let length_to_write = buf.len(); let size_or_err = libc::write(self.as_raw_fd(), buf.as_ptr() as *const _, length_to_write); (size_or_err >= 0, size_or_err as usize) }; if success { Ok(bytes_written) } else { Err(io::Error::last_os_error()) } } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { let (success, bytes_written) = unsafe { let num_bufs = bufs.len().try_to::().unwrap_or(c_int::MAX); let size_or_err = libc::writev(self.as_raw_fd(), bufs.as_ptr() as *const _, num_bufs); (size_or_err >= 0, size_or_err as usize) }; if success { Ok(bytes_written) } else { Err(io::Error::last_os_error()) } } pub fn flush(&self) -> io::Result<()> { let success = unsafe { libc::fsync(self.as_raw_fd()) >= 0 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } } impl AsRef for FdOps { fn as_ref(&self) -> &c_int { &self.0 } } impl AsRef for c_int { fn as_ref(&self) -> &FdOps { unsafe { // SAFETY: #[repr(transparent)] guarantees layout compatibility &*(self as *const _ as *const FdOps) } } } impl AsRawFd for FdOps { fn as_raw_fd(&self) -> c_int { self.0 } } impl IntoRawFd for FdOps { fn into_raw_fd(self) -> c_int { let self_ = ManuallyDrop::new(self); self_.as_raw_fd() } } impl FromRawFd for FdOps { unsafe fn from_raw_fd(fd: c_int) -> Self { Self::new(fd) } } impl Drop for FdOps { fn drop(&mut self) { unsafe { close_fd(self.0) }; } } unsafe impl Send for FdOps {} unsafe impl Sync for FdOps {} pub(super) unsafe fn close_fd(fd: i32) { let error = unsafe { let mut error = None; // If the close() call fails, the loop starts and keeps retrying until either the error // value isn't Interrupted (in which case the assertion fails) or the close operation // properly fails with a non-Interrupted error type. Why does Unix even have this // idiotic error type? while libc::close(fd) != 0 { let current_error = io::Error::last_os_error(); if current_error.kind() != io::ErrorKind::Interrupted { // An actual close error happened – return early now error = Some(current_error); break; } } error }; if let Some(e) = error { panic!("failed to close file descriptor: {}", e); } } interprocess-1.2.1/src/os/unix/fifo_file.rs000064400000000000000000000047210072674642500170640ustar 00000000000000//! Creating and using FIFO files, which are also known as "named pipes" but totally different from named pipes on Windows. //! //! On Windows, named pipes can be compared to Unix domain sockets: they can have multiple duplex connections on a single path, and the data can be chosen to either preserve or erase the message boundaries, resulting in a reliable performant implementation of TCP and UDP working in the bounds of a single machine. Those Unix domain sockets are also implemented by `interprocess` – see the [`udsocket`] module for that. //! //! On Linux, named pipes, referred to as "FIFO files" in this crate, are just files which can have a writer and a reader communicating with each other in one direction without message boundaries. If further readers try to open the file, they will simply read nothing at all; if further writers are connected, the data mixes in an unpredictable way, making it unusable. Therefore, FIFOs are to be used specifically to conveniently connect two applications through a known path which works like a pipe and nothing else. //! //! ## Usage //! The [`create_fifo`] function serves for a FIFO file creation. Opening FIFO files works via the standard [`File`]s, opened either only for writing or only for reading. Deleting works the same way as with any regular file, via the [`remove_file`] function. //! //! [`udsocket`]: ../udsocket/index.html " " //! [`create_fifo_file`]: fn.create_fifo.html " " //! [`File`]: https://doc.rust-lang.org/stable/std/fs/struct.File.html " " //! [`remove_file`]: https://doc.rust-lang.org/stable/std/fs/fn.remove_file.html " " use std::{ffi::CString, io, path::Path}; use super::imports::*; /// Creates a FIFO file at the specified path with the specified permissions. /// /// Since the `mode` parameter is masked with the [`umask`], it's best to leave it at `0o777` unless a different value is desired. /// /// ## System calls /// - [`mkfifo`] /// /// [`mkfifo`]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/mkfifo.html " " /// [`umask`]: https://en.wikipedia.org/wiki/Umask " " pub fn create_fifo>(path: P, mode: mode_t) -> io::Result<()> { _create_fifo(path.as_ref(), mode) } fn _create_fifo(path: &Path, mode: mode_t) -> io::Result<()> { let path = CString::new(path.as_os_str().as_bytes())?; let success = unsafe { libc::mkfifo(path.as_bytes_with_nul().as_ptr() as *const _, mode) == 0 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } interprocess-1.2.1/src/os/unix/imports.rs000064400000000000000000000072360072674642500166430ustar 00000000000000#![allow(dead_code, unused_imports, non_camel_case_types)] use cfg_if::cfg_if; import_type_alias_or_make_dummy!(types {libc}::( c_int = i32, pid_t = i32, uid_t = i32, gid_t = i32, mode_t = u32, size_t = usize, ), cfg(unix)); import_type_alias_or_make_dummy!(type {super}::FdOps = (), cfg(unix)); import_trait_or_make_dummy!(traits {std::os::unix::io}::( AsRawFd, IntoRawFd, FromRawFd, ), cfg(unix)); import_trait_or_make_dummy!(traits {std::os::unix::ffi}::( OsStrExt, OsStringExt, ), cfg(unix)); import_type_or_make_dummy!(types {libc}::( sockaddr_un, msghdr, cmsghdr, ), cfg(uds_supported)); import_type_or_make_dummy!(types {std::os::unix::net}::( UnixStream as StdUdStream, UnixListener as StdUdStreamListener, UnixDatagram as StdUdSocket, ), cfg(uds_supported)); #[cfg(not(unix))] pub(super) const _MAX_UDSOCKET_PATH_LEN: usize = 0; #[cfg(uds_supported)] pub(super) use libc::{ iovec, sockaddr, socklen_t, AF_UNIX, FD_CLOEXEC, F_GETFD, F_GETFL, F_SETFD, F_SETFL, O_NONBLOCK, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM, SOCK_STREAM, SOL_SOCKET, }; cfg_if! { if #[cfg(uds_ucred)] { pub(super) use libc::ucred; } else if #[cfg(uds_xucred)] { pub(super) use libc::xucred; pub struct ucred {} } else { pub struct ucred {} } } #[cfg(uds_scm_rights)] pub(super) use libc::SCM_RIGHTS; #[cfg(uds_peercred)] pub(super) use libc::SO_PEERCRED; #[cfg(uds_scm_credentials)] pub(super) use libc::{SCM_CREDENTIALS, SO_PASSCRED}; #[cfg(feature = "signals")] pub(super) use {intmap::IntMap, once_cell::sync::Lazy, spinning::RwLock, thiserror::Error}; #[cfg(se_basic)] pub(super) use libc::{sigaction, SA_NOCLDSTOP, SA_NODEFER, SA_RESETHAND, SA_RESTART, SIG_DFL}; import_const_or_make_dummy!(i32: consts {libc}::( SIGHUP = 0, SIGINT = 1, SIGQUIT = 2, SIGILL = 3, SIGABRT = 4, SIGFPE = 5, SIGKILL = 6, SIGSEGV = 7, SIGPIPE = 8, SIGALRM = 9, SIGTERM = 10, ), cfg(se_basic)); import_const_or_make_dummy!(i32: consts {libc}::( SIGUSR1 = 11, SIGUSR2 = 12, SIGCHLD = 13, SIGCONT = 14, SIGSTOP = 15, SIGTSTP = 16, SIGTTIN = 17, SIGTTOU = 18, ), cfg(se_full_posix_1990)); import_const_or_make_dummy!(i32: consts {libc}::( SIGBUS = 19, SIGURG = 20, SIGPROF = 21, SIGSYS = 22, SIGTRAP = 23, SIGVTALRM = 24, SIGXCPU = 25, SIGXFSZ = 26, ), cfg(se_base_posix_2001)); cfg_if! { if #[cfg(se_sigpoll)] { pub(super) use libc::SIGPOLL; } else if #[cfg(se_sigpoll_is_sigio)] { pub(super) use libc::SIGIO as SIGPOLL; } else { const SIGPOLL: i32 = 27; } } import_type_or_make_dummy!(types {tokio::net}::( UnixListener as TokioUdStreamListener, UnixStream as TokioUdStream, UnixDatagram as TokioUdSocket, ), cfg(all(uds_supported, feature = "tokio_support"))); import_type_or_make_dummy!(types {tokio::net::unix}::( ReadHalf as TokioUdStreamReadHalf<'a>, OwnedReadHalf as TokioUdStreamOwnedReadHalf, WriteHalf as TokioUdStreamWriteHalf<'a>, OwnedWriteHalf as TokioUdStreamOwnedWriteHalf, ), cfg(all(unix, feature = "tokio_support"))); #[cfg(all(unix, feature = "tokio_support"))] pub use tokio::net::unix::ReuniteError as TokioReuniteError; #[cfg(not(all(unix, feature = "tokio_support")))] pub struct TokioReuniteError(pub (), pub ()); import_type_or_make_dummy!(type {tokio::io}::ReadBuf<'a>, cfg(feature = "tokio_support")); import_trait_or_make_dummy!(traits {tokio::io}::( AsyncRead as TokioAsyncRead, AsyncWrite as TokioAsyncWrite, ), cfg(feature = "tokio_support")); import_trait_or_make_dummy!(traits {futures_io}::( AsyncRead as FuturesAsyncRead, AsyncWrite as FuturesAsyncWrite, ), cfg(feature = "tokio_support")); interprocess-1.2.1/src/os/unix/local_socket/listener.rs000064400000000000000000000027760072674642500214410ustar 00000000000000use { super::{local_socket_name_to_ud_socket_path, LocalSocketStream}, crate::{local_socket::ToLocalSocketName, os::unix::udsocket::UdStreamListener}, std::{ fmt::{self, Debug, Formatter}, io, os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}, }, }; pub struct LocalSocketListener { inner: UdStreamListener, } impl LocalSocketListener { pub fn bind<'a>(name: impl ToLocalSocketName<'a>) -> io::Result { let path = local_socket_name_to_ud_socket_path(name.to_local_socket_name()?)?; let inner = UdStreamListener::bind(path)?; Ok(Self { inner }) } pub fn accept(&self) -> io::Result { let inner = self.inner.accept()?; Ok(LocalSocketStream { inner }) } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.inner.set_nonblocking(nonblocking) } } impl Debug for LocalSocketListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("LocalSocketListener") .field("fd", &self.inner.as_raw_fd()) .finish() } } impl AsRawFd for LocalSocketListener { fn as_raw_fd(&self) -> i32 { self.inner.as_raw_fd() } } impl IntoRawFd for LocalSocketListener { fn into_raw_fd(self) -> i32 { self.inner.into_raw_fd() } } impl FromRawFd for LocalSocketListener { unsafe fn from_raw_fd(fd: i32) -> Self { Self { inner: unsafe { UdStreamListener::from_raw_fd(fd) }, } } } interprocess-1.2.1/src/os/unix/local_socket/mod.rs000064400000000000000000000051740072674642500203660ustar 00000000000000//! Adapter module, implements local sockets under Unix. #[cfg(feature = "tokio_support")] pub mod tokio; mod listener; pub use listener::*; mod stream; pub use stream::*; use { crate::{ local_socket::{LocalSocketName, NameTypeSupport}, os::unix::udsocket::UdSocketPath, }, std::{ borrow::Cow, ffi::{CStr, CString, OsStr, OsString}, io, os::unix::ffi::{OsStrExt, OsStringExt}, }, }; fn local_socket_name_to_ud_socket_path(name: LocalSocketName<'_>) -> io::Result> { fn cow_osstr_to_cstr(osstr: Cow<'_, OsStr>) -> io::Result> { match osstr { Cow::Borrowed(val) => { if val.as_bytes().last() == Some(&0) { Ok(Cow::Borrowed( CStr::from_bytes_with_nul(val.as_bytes()) .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error))?, )) } else { let owned = val.to_os_string(); Ok(Cow::Owned(CString::new(owned.into_vec())?)) } } Cow::Owned(val) => Ok(Cow::Owned(CString::new(val.into_vec())?)), } } #[cfg(uds_linux_namespace)] if name.is_namespaced() { return Ok(UdSocketPath::Namespaced(cow_osstr_to_cstr( name.into_inner_cow(), )?)); } Ok(UdSocketPath::File(cow_osstr_to_cstr( name.into_inner_cow(), )?)) } pub fn name_type_support_query() -> NameTypeSupport { NAME_TYPE_ALWAYS_SUPPORTED } #[cfg(uds_linux_namespace)] pub const NAME_TYPE_ALWAYS_SUPPORTED: NameTypeSupport = NameTypeSupport::Both; #[cfg(not(uds_linux_namespace))] pub const NAME_TYPE_ALWAYS_SUPPORTED: NameTypeSupport = NameTypeSupport::OnlyPaths; pub fn to_local_socket_name_osstr(mut val: &OsStr) -> LocalSocketName<'_> { let mut namespaced = false; if let Some(b'@') = val.as_bytes().first().copied() { if val.len() >= 2 { val = OsStr::from_bytes(&val.as_bytes()[1..]); } else { val = OsStr::from_bytes(&[]); } namespaced = true; } LocalSocketName::from_raw_parts(Cow::Borrowed(val), namespaced) } pub fn to_local_socket_name_osstring(mut val: OsString) -> LocalSocketName<'static> { let mut namespaced = false; if let Some(b'@') = val.as_bytes().first().copied() { let new_val = { let mut vec = val.into_vec(); vec.remove(0); OsString::from_vec(vec) }; val = new_val; namespaced = true; } LocalSocketName::from_raw_parts(Cow::Owned(val), namespaced) } interprocess-1.2.1/src/os/unix/local_socket/stream.rs000064400000000000000000000044060072674642500210770ustar 00000000000000use { super::local_socket_name_to_ud_socket_path, crate::{local_socket::ToLocalSocketName, os::unix::udsocket::UdStream}, std::{ fmt::{self, Debug, Formatter}, io::{self, prelude::*, IoSlice, IoSliceMut}, os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}, }, }; pub struct LocalSocketStream { pub(super) inner: UdStream, } impl LocalSocketStream { pub fn connect<'a>(name: impl ToLocalSocketName<'a>) -> io::Result { let path = local_socket_name_to_ud_socket_path(name.to_local_socket_name()?)?; let inner = UdStream::connect(path)?; Ok(Self { inner }) } pub fn peer_pid(&self) -> io::Result { #[cfg(uds_peercred)] { self.inner .get_peer_credentials() .map(|ucred| ucred.pid as u32) } #[cfg(not(uds_peercred))] { Err(io::Error::new(io::ErrorKind::Other, "not supported")) } } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.inner.set_nonblocking(nonblocking) } } impl Read for LocalSocketStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.inner.read(buf) } fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { self.inner.read_vectored(bufs) } } impl Write for LocalSocketStream { fn write(&mut self, buf: &[u8]) -> io::Result { self.inner.write(buf) } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.inner.write_vectored(bufs) } fn flush(&mut self) -> io::Result<()> { self.inner.flush() } } impl Debug for LocalSocketStream { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("LocalSocketStream") .field("fd", &self.inner.as_raw_fd()) .finish() } } impl AsRawFd for LocalSocketStream { fn as_raw_fd(&self) -> i32 { self.inner.as_raw_fd() } } impl IntoRawFd for LocalSocketStream { fn into_raw_fd(self) -> i32 { self.inner.into_raw_fd() } } impl FromRawFd for LocalSocketStream { unsafe fn from_raw_fd(fd: i32) -> Self { Self { inner: unsafe { UdStream::from_raw_fd(fd) }, } } } interprocess-1.2.1/src/os/unix/local_socket/tokio/listener.rs000064400000000000000000000021250072674642500225520ustar 00000000000000use { super::{super::local_socket_name_to_ud_socket_path, LocalSocketStream}, crate::{local_socket::ToLocalSocketName, os::unix::udsocket::tokio::UdStreamListener}, std::{ fmt::{self, Debug, Formatter}, io, os::unix::io::AsRawFd, }, }; pub struct LocalSocketListener { inner: UdStreamListener, } impl LocalSocketListener { pub fn bind<'a>(name: impl ToLocalSocketName<'a>) -> io::Result { let path = local_socket_name_to_ud_socket_path(name.to_local_socket_name()?)?; let inner = UdStreamListener::bind(path)?; Ok(Self { inner }) } pub async fn accept(&self) -> io::Result { let inner = self.inner.accept().await?; Ok(LocalSocketStream { inner }) } } impl Debug for LocalSocketListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("LocalSocketListener") .field("fd", &self.inner.as_raw_fd()) .finish() } } impl AsRawFd for LocalSocketListener { fn as_raw_fd(&self) -> i32 { self.inner.as_raw_fd() } } interprocess-1.2.1/src/os/unix/local_socket/tokio/mod.rs000064400000000000000000000001030072674642500214760ustar 00000000000000mod listener; pub use listener::*; mod stream; pub use stream::*; interprocess-1.2.1/src/os/unix/local_socket/tokio/stream/mod.rs000064400000000000000000000062530072674642500230050ustar 00000000000000mod read_half; pub use read_half::*; mod write_half; pub use write_half::*; use { super::super::local_socket_name_to_ud_socket_path, crate::{local_socket::ToLocalSocketName, os::unix::udsocket::tokio::UdStream}, futures_io::{AsyncRead, AsyncWrite}, std::{ fmt::{self, Debug, Formatter}, io::{self, IoSlice, IoSliceMut}, os::unix::io::AsRawFd, pin::Pin, task::{Context, Poll}, }, }; pub struct LocalSocketStream { pub(super) inner: UdStream, } impl LocalSocketStream { pub async fn connect<'a>(name: impl ToLocalSocketName<'a>) -> io::Result { let path = local_socket_name_to_ud_socket_path(name.to_local_socket_name()?)?; let inner = UdStream::connect(path).await?; Ok(Self { inner }) } pub fn into_split(self) -> (OwnedReadHalf, OwnedWriteHalf) { let (r, w) = self.inner.into_split(); (OwnedReadHalf { inner: r }, OwnedWriteHalf { inner: w }) } pub fn peer_pid(&self) -> io::Result { #[cfg(uds_peercred)] { self.inner .get_peer_credentials() .map(|ucred| ucred.pid as u32) } #[cfg(not(uds_peercred))] { Err(io::Error::new(io::ErrorKind::Other, "not supported")) } } // TODO use those pub unsafe fn _from_raw_fd(fd: i32) -> io::Result { let inner = unsafe { // SAFETY: as per safety contract UdStream::from_raw_fd(fd)? }; Ok(Self { inner }) } pub fn _into_raw_fd(self) -> io::Result { self.inner.into_raw_fd() } fn pinproj(&mut self) -> Pin<&mut UdStream> { Pin::new(&mut self.inner) } } impl AsyncRead for LocalSocketStream { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> std::task::Poll> { self.pinproj().poll_read(cx, buf) } fn poll_read_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { self.pinproj().poll_read_vectored(cx, bufs) } } impl AsyncWrite for LocalSocketStream { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.pinproj().poll_write(cx, buf) } fn poll_write_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { self.pinproj().poll_write_vectored(cx, bufs) } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproj().poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproj().poll_close(cx) } } impl Debug for LocalSocketStream { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("LocalSocketStream") .field("fd", &self.inner.as_raw_fd()) .finish() } } impl AsRawFd for LocalSocketStream { fn as_raw_fd(&self) -> i32 { self.inner.as_raw_fd() } } interprocess-1.2.1/src/os/unix/local_socket/tokio/stream/read_half.rs000064400000000000000000000026630072674642500241340ustar 00000000000000use { crate::os::unix::udsocket::tokio::OwnedReadHalf as OwnedReadHalfImpl, futures_io::AsyncRead, std::{ fmt::{self, Debug, Formatter}, io::{self, IoSliceMut}, pin::Pin, task::{Context, Poll}, }, }; pub struct OwnedReadHalf { pub(super) inner: OwnedReadHalfImpl, } impl OwnedReadHalf { pub fn peer_pid(&self) -> io::Result { #[cfg(uds_peercred)] { self.inner .get_peer_credentials() .map(|ucred| ucred.pid as u32) } #[cfg(not(uds_peercred))] { Err(io::Error::new(io::ErrorKind::Other, "not supported")) } } fn pinproj(&mut self) -> Pin<&mut OwnedReadHalfImpl> { Pin::new(&mut self.inner) } } impl AsyncRead for OwnedReadHalf { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> std::task::Poll> { self.pinproj().poll_read(cx, buf) } fn poll_read_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { self.pinproj().poll_read_vectored(cx, bufs) } } impl Debug for OwnedReadHalf { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_tuple("local_socket::OwnedReadHalf") .field(&self.inner) .finish() } } // TODO as_raw_fd interprocess-1.2.1/src/os/unix/local_socket/tokio/stream/write_half.rs000064400000000000000000000032710072674642500243470ustar 00000000000000use { crate::os::unix::udsocket::tokio::OwnedWriteHalf as OwnedWriteHalfImpl, futures_io::AsyncWrite, std::{ fmt::{self, Debug, Formatter}, io::{self, IoSlice}, pin::Pin, task::{Context, Poll}, }, }; pub struct OwnedWriteHalf { pub(super) inner: OwnedWriteHalfImpl, } impl OwnedWriteHalf { pub fn peer_pid(&self) -> io::Result { #[cfg(uds_peercred)] { self.inner .get_peer_credentials() .map(|ucred| ucred.pid as u32) } #[cfg(not(uds_peercred))] { Err(io::Error::new(io::ErrorKind::Other, "not supported")) } } fn pinproj(&mut self) -> Pin<&mut OwnedWriteHalfImpl> { Pin::new(&mut self.inner) } } impl AsyncWrite for OwnedWriteHalf { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.pinproj().poll_write(cx, buf) } fn poll_write_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { self.pinproj().poll_write_vectored(cx, bufs) } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproj().poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproj().poll_close(cx) } } impl Debug for OwnedWriteHalf { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_tuple("local_socket::OwnedWriteHalf") .field(&self.inner) .finish() } } // TODO as_raw_fd interprocess-1.2.1/src/os/unix/mod.rs000064400000000000000000000030330072674642500157140ustar 00000000000000//! Unix-specific functionality for various interprocess communication primitives, as well as Unix-specific ones. //! //! ## FIFO files //! This type of interprocess communication similar to unnamed pipes in that they are unidirectional byte channels which behave like files. The difference is that FIFO files are actual (pseudo)files on the filesystem and thus can be accessed by unrelated applications (one doesn't need to be spawned by another). //! //! FIFO files are available on all supported systems. //! //! ## Unix domain sockets //! Those are sockets used specifically for local IPC. They support bidirectional connections, identification by file path or inside the abstract Linux socket namespace, optional preservation of message boundaries (`SOCK_DGRAM` UDP-like interface) and transferring file descriptor ownership. //! //! Unix domain sockets are not available on ARM Newlib, but are supported on all other Unix-like systems. #![cfg_attr(not(unix), allow(warnings))] pub(crate) mod imports; #[cfg(unix)] mod fdops; #[cfg(unix)] // pub(self) is just a fancy way of saying priv (i.e. no access modifier), but // we want to make it clear that we're exporting to child modules here rather // than importing for use within this module. pub(self) use fdops::*; pub mod fifo_file; #[cfg(any(doc, all(feature = "signals", se_basic)))] #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "signals")))] pub mod signal; #[cfg(any(doc, uds_supported))] pub mod udsocket; #[cfg(unix)] pub(crate) mod local_socket; #[cfg(unix)] pub(crate) mod unnamed_pipe; interprocess-1.2.1/src/os/unix/signal.rs000064400000000000000000001554550072674642500164320ustar 00000000000000//! Signal support for Unix-like systems. //! //! Signals in Unix are much more functional and versatile than ANSI C signals – there is simply much, much more of them. In addition to that, there is a special group of signals called "real-time signals" (more on those below). //! //! # Main signals //! The [`SignalType`] enumeration provides all standard signals as defined in POSIX.1-2001. More signal types may be added later, which is why exhaustively matching on it is not possible. It can be cheaply converted to a 32-bit integer, though. See its documentation for more on conversions. //! //! The `set_handler` function is used to create an association between a `SignalType` and a signal handling strategy. //! //! # Real-time signals //! In addition to usual signals, there's a special group of signals called "real-time signals". Those signals do not have fixed identifiers and are not generated by the system or kernel. Instead, they can only be sent between processes. //! //! # Signal-safe C functions //! Very few C functions can be called from a signal handler. Allocating memory, using the thread API and manipulating interval timers, for example, is prohibited in a signal handler. Calling a function which is not signal safe results in undefined behavior, i.e. memory unsafety. Rather than excluding certain specific functions, the POSIX specification only speicifies functions which *are* signal-safe. The following C functions are guaranteed to be safe to call from a signal handler: //! - `_Exit` //! - `_exit` //! - `abort` //! - `accept` //! - `access` //! - `aio_error` //! - `aio_return` //! - `aio_suspend` //! - `alarm` //! - `bind` //! - `cfgetispeed` //! - `cfgetospeed` //! - `cfsetispeed` //! - `cfsetospeed` //! - `chdir` //! - `chmod` //! - `chown` //! - `clock_gettime` //! - `close` //! - `connect` //! - `creat` //! - `dup` //! - `dup2` //! - `execle` //! - `execve` //! - `fchmod` //! - `fchown` //! - `fcntl` //! - `fdatasync` //! - `fork` //! - `fpathconf` //! - `fstat` //! - `fsync` //! - `ftruncate` //! - `getegid` //! - `geteuid` //! - `getgid` //! - `getgroups` //! - `getpeername` //! - `getpgrp` //! - `getpid` //! - `getppid` //! - `getsockname` //! - `getsockopt` //! - `getuid` //! - `kill` //! - `link` //! - `listen` //! - `lseek` //! - `lstat` //! - `mkdir` //! - `mkfifo` //! - `open` //! - `pathconf` //! - `pause` //! - `pipe` //! - `poll` //! - `posix_trace_event` //! - `pselect` //! - `raise` //! - `read` //! - `readlink` //! - `recv` //! - `recvfrom` //! - `recvmsg` //! - `rename` //! - `rmdir` //! - `select` //! - `sem_post` //! - `send` //! - `sendmsg` //! - `sendto` //! - `setgid` //! - `setpgid` //! - `setsid` //! - `setsockopt` //! - `setuid` //! - `shutdown` //! - `sigaction` //! - `sigaddset` //! - `sigdelset` //! - `sigemptyset` //! - `sigfillset` //! - `sigismember` //! - `signal` //! - `sigpause` //! - `sigpending` //! - `sigprocmask` //! - `sigqueue` //! - `sigset` //! - `sigsuspend` //! - `sleep` //! - `sockatmark` //! - `socket` //! - `socketpair` //! - `stat` //! - `symlink` //! - `sysconf` //! - `tcdrain` //! - `tcflow` //! - `tcflush` //! - `tcgetattr` //! - `tcgetpgrp` //! - `tcsendbreak` //! - `tcsetattr` //! - `tcsetpgrp` //! - `time` //! - `timer_getoverrun` //! - `timer_gettime` //! - `timer_settime` //! - `times` //! - `umask` //! - `uname` //! - `unlink` //! - `utime` //! - `wait` //! - `waitpid` //! - `write` //! //! Many Rust crates, including the standard library, use signal-unsafe functions not on this list in safe code. For example, `Vec`, `Box` and `Rc`/`Arc` perform memory allocations, and `Mutex`/`RwLock` perform `pthread` calls. For this reason, creating a signal hook is an unsafe operation. //! //! [`SignalType`]: enum.SignalType.html " " use super::imports::*; use cfg_if::cfg_if; use std::{ convert::{TryFrom, TryInto}, error::Error, fmt::{self, Display, Formatter}, io, mem::zeroed, panic, process, }; use to_method::To; cfg_if! { if #[cfg(any( target_os = "linux", target_os = "android", ))] { // don't care about LinuxThreads lmao const SIGRTMIN: i32 = 34; const SIGRTMAX: i32 = 64; } else if #[cfg(target_os = "freebsd")] { const SIGRTMIN: i32 = 65; const SIGRTMAX: i32 = 126; } else if #[cfg(target_os = "netbsd")] { const SIGRTMIN: i32 = 33; const SIGRTMAX: i32 = 63; } else if #[cfg(target_os = "redox")] { const SIGRTMIN: i32 = 27; const SIGRTMAX: i32 = 31; } else { const SIGRTMIN: i32 = 1; // min is smaller than max so that the sloppy calculation works const SIGRTMAX: i32 = 0; } } /// Whether real-time signals are supported at all. /// /// The platforms for which `interprocess` has explicit support for real-time signals are: /// - Linux /// - includes Android /// - FreeBSD /// - NetBSD /// - Redox pub const REALTIME_SIGNALS_SUPPORTED: bool = NUM_REALTIME_SIGNALS != 0; /// How many real-time signals are supported. Remember that real-time signals start from 0, so this number is higher than the highest possible real-time signal by 1. /// /// Platform-specific values for this constant are: /// - **Linux** and **NetBSD**: 31 /// - **FreeBSD**: 62 /// - **Redox**: 5 (does not conform with POSIX) pub const NUM_REALTIME_SIGNALS: u32 = (SIGRTMAX - SIGRTMIN + 1) as u32; /// Returns `true` if the specified signal is a valid real-time signal value, `false` otherwise. #[allow(clippy::absurd_extreme_comparisons)] // For systems where there are no realtime signals pub const fn is_valid_rtsignal(rtsignal: u32) -> bool { rtsignal < NUM_REALTIME_SIGNALS } /// The first field is the current method of handling a specific signal, the second one is the flags which were set for it. #[cfg(all(unix, feature = "signals"))] type HandlerAndFlags = (SignalHandler, i32); #[cfg(all(unix, feature = "signals"))] static HANDLERS: Lazy>> = Lazy::new(|| RwLock::new(IntMap::new())); /// Installs the specified handler for the specified standard signal, using the default values for the flags. /// /// See [`HandlerOptions`] builder if you'd like to customize the flags. /// /// # Example /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(all(unix, feature = "signals"))] { /// use interprocess::os::unix::signal::{self, SignalType, SignalHandler}; /// /// let handler = unsafe { /// // Since signal handlers are restricted to a specific set of C functions, creating a /// // handler from an arbitrary function is unsafe because it might call a function /// // outside the list, and there's no real way to know that at compile time with the /// // current version of Rust. Since we're only using the write() system call here, this /// // is safe. /// SignalHandler::from_fn(|| { /// println!("You pressed Ctrl-C!"); /// }) /// }; /// /// // Install our handler for the KeyboardInterrupt signal type. /// signal::set_handler(SignalType::KeyboardInterrupt, handler)?; /// # } /// # Ok(()) } /// ``` /// /// [`HandlerOptions`]: struct.HandlerOptions.html " " pub fn set_handler(signal_type: SignalType, handler: SignalHandler) -> Result<(), SetHandlerError> { HandlerOptions::for_signal(signal_type) .set_new_handler(handler) .set() } /// Installs the specified handler for the specified unsafe signal, using the default values for the flags. /// /// See [`HandlerOptions`] builder if you'd like to customize the flags. /// /// # Safety /// See the [`set_unsafe`] safety notes. /// /// # Example /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(all(unix, feature = "signals"))] { /// use interprocess::os::unix::signal::{self, SignalType, SignalHandler}; /// /// let handler = unsafe { /// // Since signal handlers are restricted to a specific set of C functions, creating a /// // handler from an arbitrary function is unsafe because it might call a function /// // outside the list, and there's no real way to know that at compile time with the /// // current version of Rust. Since we're only using the write() system call here, this /// // is safe. /// SignalHandler::from_fn(|| { /// println!("Oh no, the motherboard broke!"); /// std::process::abort(); /// }) /// }; /// /// unsafe { /// // Install our handler for the MemoryBusError signal type. /// signal::set_unsafe_handler(SignalType::MemoryBusError, handler)?; /// } /// # } /// # Ok(()) } /// ``` /// /// [`HandlerOptions`]: struct.HandlerOptions.html " " /// [`set_unsafe`]: struct.HandlerOptions.html#method.set_unsafe " " pub unsafe fn set_unsafe_handler( signal_type: SignalType, handler: SignalHandler, ) -> Result<(), SetHandlerError> { unsafe { HandlerOptions::for_signal(signal_type) .set_new_handler(handler) .set_unsafe() } } /// Installs the specified handler for the specified real-time signal, using the default values for the flags. /// /// See [`HandlerOptions`] builder if you'd like to customize the flags. /// /// # Example /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(all(unix, feature = "signals"))] { /// use interprocess::os::unix::signal::{self, SignalHandler}; /// /// let handler = unsafe { /// // Since signal handlers are restricted to a specific set of C functions, creating a /// // handler from an arbitrary function is unsafe because it might call a function /// // outside the list, and there's no real way to know that at compile time with the /// // current version of Rust. Since we're only using the write() system call here, this /// // is safe. /// SignalHandler::from_fn(|| { /// println!("You sent a real-time signal!"); /// }) /// }; /// /// // Install our handler for the real-time signal 0. /// signal::set_rthandler(0, handler)?; /// # } /// # Ok(()) } /// ``` /// /// [`HandlerOptions`]: struct.HandlerOptions.html " " pub fn set_rthandler(rtsignal: u32, handler: SignalHandler) -> Result<(), SetHandlerError> { HandlerOptions::for_rtsignal(rtsignal) .set_new_handler(handler) .set() } unsafe fn install_hook(signum: i32, hook: usize, flags: i32) -> io::Result<()> { let success = { let [mut old_handler, mut new_handler] = unsafe { // SAFETY: sigaction only consists of integers and therefore can // contain an all-0 bit pattern [zeroed::(); 2] }; new_handler.sa_sigaction = hook; new_handler.sa_flags = flags; unsafe { // SAFETY: all the pointers that are being passed come from references and only involve // a single cast, which means that it's just a reference-to-pointer cast and not a // pointer-to-pointer cast (the latter can cast any pointer to any other pointer, but // you need two casts in order to convert a reference to a pointer to an arbitrary type) libc::sigaction(signum, &new_handler as *const _, &mut old_handler as *mut _) != -1 } }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } /// Options for installing a signal handler. /// /// # Example /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(all(unix, feature = "signals"))] { /// use interprocess::os::unix::signal::{self, SignalType, SignalHandler}; /// /// let handler = unsafe { /// // Since signal handlers are restricted to a specific set of C functions, creating a /// // handler from an arbitrary function is unsafe because it might call a function /// // outside the list, and there's no real way to know that at compile time with the /// // current version of Rust. Since we're only using the write() system call here, this /// // is safe. /// SignalHandler::from_fn(|| { /// println!("You pressed Ctrl-C!"); /// }) /// }; /// /// // Let's use the builder to customize the signal handler: /// signal::HandlerOptions::for_signal(SignalType::KeyboardInterrupt) /// .set_new_handler(handler) /// .auto_reset_handler(true) // Let's remove the signal handler after it fires once. /// .system_call_restart(false) // Avoid restarting system calls and let them fail with the /// // Interrupted error type. There normally isn't a reason to /// // do this, but for the sake of the example, let's assume /// // that there is. /// .set()?; // Finalize the builder by installing the handler. /// # } /// # Ok(()) } /// ``` #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[must_use = "the signal handler is attached by calling `.set()`, which consumes the builder"] pub struct HandlerOptions { signal: i32, /// The handler to be set up. If `None`, the handler is not changed by the call. pub handler: Option, /// For the [`ChildProcessEvent`] signal, this option *disables* receiving the signal if it was generated because the child process was suspended (using any signal which suspends a process, such as [`Suspend`] or [`ForceSuspend`]) or [resumed][`Continue`]. /// /// If enabled on a signal which is not [`ChildProcessEvent`], a panic is produced in debug builds when [`set`] is called; in release builds, the flag is simply ignored. /// /// [`ChildProcessEvent`]: enum.SignalType.html#variant.ChildProcessEvent " " /// [`Suspend`]: enum.SignalType.html#variant.Suspend " " /// [`ForceSuspend`]: enum.SignalType.html#variant.ForceSuspend " " /// [`Continue`]: enum.SignalType.html#variant.Continue " " /// [`set`]: #method.set " " pub ignore_child_stop_events: bool, /// Allow the signal handler to interrupt a previous invocation of itself. If disabled, the signal handler will disable its own signal when called and restore previous state when it finishes executing. If not, an infinite amount of signals can be received on top of each other, which is likely a stack overflow risk. /// /// If enabled but the handler is [set to use the default handling method][`Default`] from the OS, a panic is produced in debug builds when [`set`] is called; in release builds, the flag is simply ignored. /// /// [`Default`]: enum.SignalHandler.html#variant.Default " " /// [`set`]: #method.set " " pub recursive_handler: bool, /// Automatically restart certain system calls instead of failing with the [`Interrupted`] error type. Some other system calls are not restarted with this function and may fail with [`Interrupted`] anyway. Consult your manual pages for more details. /// /// [`Interrupted`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.Interrupted " " pub system_call_restart: bool, /// Automatically reset the handler to the default handling method whenever it is executed. /// /// If enabled but the handler is [set to use the default handling method][`Default`] from the OS, a panic is produced in debug builds when [`set`] is called; in release builds, the flag is simply ignored. /// /// [`Default`]: enum.SignalHandler.html#variant.Default " " /// [`set`]: #method.set " " pub auto_reset_handler: bool, } impl HandlerOptions { /// Creates a builder for a handler for the specified signal. pub fn for_signal(signal: SignalType) -> Self { Self { signal: signal.into(), handler: None, ignore_child_stop_events: false, recursive_handler: false, system_call_restart: true, auto_reset_handler: false, } } /// Creates a builder for a handler for the specified real-time signal. /// /// # Panics /// Guaranteed to panic if the specified real-time signal is outside the range of real-time signals supported by the OS. See [`NUM_REALTIME_SIGNALS`]. /// /// [`NUM_REALTIME_SIGNALS`]: constant.NUM_REALTIME_SIGNALS.html " " pub fn for_rtsignal(rtsignal: u32) -> Self { assert!( is_valid_rtsignal(rtsignal), "invalid real-time signal value – check the NUM_REALTIME_SIGNALS constant to see how \ many are supported" ); Self { signal: rtsignal as i32, handler: None, ignore_child_stop_events: false, recursive_handler: false, system_call_restart: true, auto_reset_handler: false, } } /// Sets the handler for the signal to the specified value. If `None`, the old value is used. pub fn set_new_handler(mut self, handler: impl Into>) -> Self { self.handler = handler.into(); self } /// Sets the [`ignore_child_stop_events`] flag to the specified value. /// /// [`ignore_child_stop_events`]: #structfield.ignore_child_stop_events " " pub fn ignore_child_stop_events(mut self, ignore: impl Into) -> Self { self.ignore_child_stop_events = ignore.into(); self } /// Sets the [`recursive_handler`] flag to the specified value. /// /// [`recursive_handler`]: #structfield.recursive_handler " " pub fn recursive_handler(mut self, recursive: impl Into) -> Self { self.recursive_handler = recursive.into(); self } /// Sets the [`system_call_restart`] flag to the specified value. /// /// [`system_call_restart`]: #structfield.system_call_restart " " pub fn system_call_restart(mut self, restart: impl Into) -> Self { self.system_call_restart = restart.into(); self } /// Sets the [`auto_reset_handler`] flag to the specified value. /// /// [`auto_reset_handler`]: #structfield.auto_reset_handler " " pub fn auto_reset_handler(mut self, reset: impl Into) -> Self { self.auto_reset_handler = reset.into(); self } /// Installs the signal handler. pub fn set(self) -> Result<(), SetHandlerError> { if let Ok(val) = SignalType::try_from(self.signal) { if val.is_unsafe() { return Err(SetHandlerError::UnsafeSignal); } } unsafe { self.set_unsafe() } } /// Installs the signal handler, even if the signal being handled is unsafe. /// /// # Safety /// The handler and all code that may or may not execute afterwards must be prepared for the aftermath of what might've caused the signal. /// /// [`SegmentationFault`] or [`BusError`] are most likely caused by undefined behavior invoked from Rust (the former is caused by dereferencing invalid memory, the latter is caused by dereferencing an incorrectly aligned pointer on ISAs like ARM which do not tolerate misaligned pointers), which means that the program is unsound and the only meaningful thing to do is to capture as much information as possible in a safe way – preferably using OS services to create a dump, rather than trying to read the program's global state, which might be irreversibly corrupted – and write the crash dump to some on-disk location. /// /// [`SegmentationFault`]: enum.SignalType.html#variant.SegmentationFault " " /// [`BusError`]: enum.SignalType.html#variant.BusError " " pub unsafe fn set_unsafe(self) -> Result<(), SetHandlerError> { if let Ok(val) = SignalType::try_from(self.signal) { if val.is_unblockable() { return Err(SetHandlerError::UnblockableSignal(val)); } } else if !is_valid_rtsignal(self.signal as u32) { return Err(SetHandlerError::RealTimeSignalOutOfBounds { attempted: self.signal as u32, max: NUM_REALTIME_SIGNALS, }); } let handlers = HANDLERS.read(); let new_flags = self.flags_as_i32(); let mut need_to_upgrade_handle = false; let need_to_install_hook = if let Some((existing_handler, existing_flags)) = handlers.get(self.signal as u64) { // This signal's handler was set before – check if we need to install new flags or if // one is default and another isn't, which would mean that either that no hook was // installed and we have to install one or there was one installed but we need to // install the default hook explicitly. let new_handler = self.handler.unwrap_or_default(); if new_handler != *existing_handler || new_flags != *existing_flags { need_to_upgrade_handle = true; true } else { let one_handler_is_default_and_another_isnt = (new_handler.is_default() && !existing_handler.is_default()) || (!new_handler.is_default() && existing_handler.is_default()); *existing_flags != new_flags || one_handler_is_default_and_another_isnt } } else { need_to_upgrade_handle = true; !self.handler.unwrap_or_default().is_default() }; drop(handlers); if need_to_upgrade_handle { let mut handlers = HANDLERS.write(); let signal_u64 = self.signal as u64; handlers.remove(signal_u64); handlers.insert(signal_u64, (self.handler.unwrap_or_default(), new_flags)); } if need_to_install_hook { let hook_val = match self.handler.unwrap_or_default() { SignalHandler::Default => SIG_DFL, _ => signal_receiver as usize, }; unsafe { // SAFETY: we're using a correct value for the hook install_hook(self.signal, hook_val, new_flags)? } } Ok(()) } fn flags_as_i32(self) -> i32 { if self.handler.unwrap_or_default().is_default() { debug_assert!( !self.recursive_handler, "cannot use the recursive_handler flag with the default handling method", ); } if self.signal != SIGCHLD { debug_assert!( !self.ignore_child_stop_events, "\ cannot use the ignore_child_stop_events flag when the signal to be handled isn't \ ChildProcessEvent", ); } let mut flags = 0; if self.auto_reset_handler { flags |= SA_RESETHAND; } if self.ignore_child_stop_events { flags |= SA_NOCLDSTOP; } if self.recursive_handler { flags |= SA_NODEFER; } if self.system_call_restart { flags |= SA_RESTART; } flags } } /// The error produced when setting a signal handler fails. #[derive(Debug)] #[cfg_attr(all(unix, feature = "signals"), derive(Error))] pub enum SetHandlerError { /// An unsafe signal was attempted to be handled using `set` instead of `set_unsafe`. #[cfg_attr( all(unix, feature = "signals"), error("an unsafe signal was attempted to be handled using `set` instead of `set_unsafe`") )] UnsafeSignal, /// The signal which was attempted to be handled is not allowed to be handled by the POSIX specification. This can either be [`ForceSuspend`] or [`Kill`]. /// /// [`Kill`]: enum.SignalType.html#variant.Kill " " /// [`ForceSuspend`]: enum.SignalType.html#variant.ForceSuspend " " #[cfg_attr( all(unix, feature = "signals"), error("the signal {:?} cannot be handled", .0), )] UnblockableSignal(SignalType), /// The specified real-time signal is not available on this OS. #[cfg_attr( all(unix, feature = "signals"), error( "the real-time signal number {} is not available ({} is the highest possible)", .attempted, .max, ), )] RealTimeSignalOutOfBounds { /// The realtime signal which was attempted to be used. attempted: u32, /// The highest available realtime signal number. max: u32, }, /// An unexpected OS error ocurred during signal handler setup. #[cfg_attr( all(unix, feature = "signals"), error("{}", .0), )] UnexpectedSystemCallFailure(#[cfg_attr(all(unix, feature = "signals"), from)] io::Error), } /// The actual hook which is passed to `sigaction` which dispatches signals according to the global handler map (the `HANDLERS` static). extern "C" fn signal_receiver(signum: i32) { let catched = panic::catch_unwind(|| { let handler_and_flags = { let handlers = HANDLERS.read(); let val = handlers .get(signum as u64) .expect("unregistered signal passed by the OS to the shared receiver"); *val }; match handler_and_flags.0 { SignalHandler::Ignore => {} SignalHandler::Hook(hook) => hook.inner()(), SignalHandler::Default => unreachable!( "signal receiver was unregistered but has been called by the OS anyway" ), } if handler_and_flags.1 & SA_RESETHAND != 0 { // If the signal is set to be reset to default handling, set the record accordingly. let mut handlers = HANDLERS.write(); handlers.remove(signum as u64); let handler_and_flags = (SignalHandler::Default, handler_and_flags.1); handlers.insert(signum as u64, handler_and_flags); } }); // The panic hook already ran, so we only have to abort the process catched.unwrap_or_else(|_| process::abort()); } /// A signal handling method. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum SignalHandler { /// Use the default behavior specified by POSIX. Default, /// Ignore the signal whenever it is received. Ignore, /// Call a function whenever the signal is received. Hook(SignalHook), } impl SignalHandler { /// Returns `true` for the [`Default`] variant, `false` otherwise. /// /// [`Default`]: #variant.Default.html " " pub const fn is_default(self) -> bool { matches!(self, Self::Default) } /// Returns `true` for the [`Ignore`] variant, `false` otherwise. /// /// [`Ignore`]: #variant.Ignore.html " " pub const fn is_ignore(self) -> bool { matches!(self, Self::Ignore) } /// Returns `true` for the [`Hook`] variant, `false` otherwise. /// /// [`Hook`]: #variant.Hook.html " " pub const fn is_hook(self) -> bool { matches!(self, Self::Hook(..)) } /// Creates a handler which calls the specified function. /// /// # Safety /// The function must not call any C functions which are not considered signal-safe. See the [module-level section on signal-safe C functions] for more. /// /// [module-level section on signal-safe C functions]: index.html#signal-safe-c-functions " " pub unsafe fn from_fn(function: fn()) -> Self { Self::Hook(unsafe { SignalHook::from_fn(function) }) } } impl Default for SignalHandler { /// Returns [`SignalHandler::Default`]. /// /// [`SignalHandler::Default`]: #variant.Default " " fn default() -> Self { Self::Default } } impl From for SignalHandler { fn from(op: SignalHook) -> Self { Self::Hook(op) } } /// A function which can be used as a signal handler. #[repr(transparent)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct SignalHook(fn()); impl SignalHook { /// Creates a hook which calls the specified function. /// /// # Safety /// The function must not call any C functions which are not considered signal-safe. See the [module-level section on signal-safe C functions] for more. /// /// [module-level section on signal-safe C functions]: index.html#signal-safe-c-functions " " pub unsafe fn from_fn(function: fn()) -> Self { Self(function) } /// Returns the wrapped function. pub fn inner(self) -> fn() { self.0 } } impl From for fn() { fn from(op: SignalHook) -> Self { op.0 } } /// Sends the specified signal to the specified process. If the specified signal is `None`, no signal is sent and only a privilege check is performed instead. /// /// # Example /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(all(unix, feature = "signals"))] { /// use interprocess::os::unix::signal::{self, SignalType}; /// use std::process; /// /// // Send a Termination signal to the calling process. /// signal::send(SignalType::Termination, process::id())?; /// # } /// # Ok(()) } /// ``` pub fn send(signal: impl Into>, pid: impl Into) -> io::Result<()> { let pid = pid .to::() .try_to::() .unwrap_or_else(|_| panic!("process identifier out of range")); debug_assert_ne!( pid, 0, "to send the signal to the process group of the calling process, use send_to_group instead" ); let success = unsafe { libc::kill(signal.into().map_or(0, Into::into), pid) != -1 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } /// Sends the specified real-time signal to the specified process. If the specified signal is `None`, no signal is sent and only a privilege check is performed instead. /// /// # Example /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(all(unix, feature = "signals"))] { /// use interprocess::os::unix::signal::{self, SignalType}; /// use std::process; /// /// // Send a real-timne signal 0 to the calling process. /// signal::send_rt(0, process::id())?; /// # } /// # Ok(()) } /// ``` pub fn send_rt(signal: impl Into>, pid: impl Into) -> io::Result<()> { let pid = pid .to::() .try_to::() .unwrap_or_else(|_| panic!("process identifier out of range")); debug_assert_ne!( pid, 0, "to send the signal to the process group of the calling process, use send_to_group instead" ); let signal = signal.into().map_or(0, |val| { assert!(is_valid_rtsignal(val), "invalid real-time signal"); val }) as i32; let success = unsafe { libc::kill(signal, pid) != -1 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } /// Sends the specified signal to the specified process group. If the specified signal is `None`, no signal is sent and only a privilege check is performed instead. /// /// # Example /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(all(unix, feature = "signals"))] { /// use interprocess::os::unix::signal::{self, SignalType}; /// use std::process; /// /// // Send a Termination signal to the process group of the calling process. /// signal::send_to_group(SignalType::Termination, 0_u32)?; /// # } /// # Ok(()) } /// ``` pub fn send_to_group(signal: impl Into>, pid: impl Into) -> io::Result<()> { #[allow(clippy::neg_multiply)] // "it's more readable to just negate"? how about no let pid = pid .to::() .try_to::() .unwrap_or_else(|_| panic!("process group identifier out of range")) * -1; let success = unsafe { libc::kill(signal.into().map_or(0, Into::into), pid) != -1 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } /// Sends the specified real-time signal to the specified process. If the specified signal is `None`, no signal is sent and only a privilege check is performed instead. /// /// # Example /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(all(unix, feature = "signals"))] { /// use interprocess::os::unix::signal::{self, SignalType}; /// use std::process; /// /// // Send a real-timne signal 0 to the process group of the calling process. /// signal::send_rt(0_u32, 0_u32)?; /// # } /// # Ok(()) } /// ``` pub fn send_rt_to_group(signal: impl Into>, pid: impl Into) -> io::Result<()> { #[allow(clippy::neg_multiply)] let pid = pid .to::() .try_to::() .unwrap_or_else(|_| panic!("process identifier out of range")) * -1; debug_assert_ne!( pid, 0, "to send the signal to the process group of the calling process, use send_to_group instead" ); let signal = signal.into().map_or(0, |val| { assert!(is_valid_rtsignal(val), "invalid real-time signal"); val }) as i32; let success = unsafe { libc::kill(signal, pid) != -1 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } /// All standard signal types as defined in POSIX.1-2001. /// /// The values can be safely and quickly converted to [`i32`]/[`u32`]. The reverse process involves safety checks, making sure that unknown signal values are never stored. /// /// [`i32`]: https://doc.rust-lang.org/std/primitive.i32.html " " /// [`u32`]: https://doc.rust-lang.org/std/primitive.u32.html " " #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[repr(i32)] #[non_exhaustive] pub enum SignalType { /// `SIGHUP` – lost connection to controlling terminal. Opting out of this signal is recommended if the process does not need to stop after the user who started it logs out. /// /// *Default handler: process termination.* #[cfg(any(doc, se_basic))] Hangup = SIGHUP, /// `SIGINT` – keyboard interrupt, usually sent by pressing `Ctrl`+`C` by the terminal. This signal is typically set to be ignored if the program runs an interactive interface: GUI/TUI, interactive shell (the Python shell, for example) or any other kind of interface which runs in a loop, as opposed to a command-line invocation of the program which reads its standard input or command-line arguments, performs a task and exits. If the interactive interface is running a lengthy operation, a good idea is to temporarily re-enable the signal and abort the lengthy operation if the signal is received, then disable it again. /// /// *Default handler: process termination.* #[cfg(any(doc, se_basic))] KeyboardInterrupt = SIGINT, /// `SIGQUIT` – request to perform a core dump and quit, usually sent by pressing `Ctrl`+`\`. This signal normally should not be overriden or masked out – the core dump is performed automatically by the OS. /// /// *Default handler: process termination with a core dump.* #[cfg(any(doc, se_basic))] QuitAndDump = SIGQUIT, /// `SIGILL` – illegal or malformed instruction exception, generated by the CPU whenever such an instruction is executed. This signal normally should not be overriden or masked out, since it likely means that the executable file or the memory of the process has been corrupted and further execution is a risk of invoking negative consequences. /// /// For reasons described above, **this signal is considered unsafe** – handling it requires using `set_unsafe_handler`. /// /// *Default handler: process termination with a core dump.* #[cfg(any(doc, se_basic))] IllegalInstruction = SIGILL, /// `SIGABRT` – abnormal termination requested. This signal is typically invoked by the program itself, using [`std::process::abort`] or the equivalent C function; still, like any other signal, it can be sent from outside the process. /// /// *Default handler: process termination with a core dump.* /// /// [`std::process::abort`]: https://doc.rust-lang.org/std/process/fn.abort.html " " #[cfg(any(doc, se_basic))] Abort = SIGABRT, /// `SIGFPE` – mathematical exception. This signal is generated whenever an undefined mathematical operation is performed – mainly integer division by zero. /// /// *Default handler: process termination with a core dump.* #[cfg(any(doc, se_basic))] MathException = SIGFPE, /// `SIGKILL` – forced termination. This signal can only be sent using the usual signal sending procedures and, unlike most other signals, cannot be masked out or handled at all. The main purpose for this signal is to stop a program which has masked out all other signals for malicious purposes or has stuck in such a state because of a bug. /// /// *Default handler: process termination, **cannot be overriden or disabled**.* #[cfg(any(doc, se_basic))] Kill = SIGKILL, /// `SIGSEGV` – invaid memory access. This signal is issued by the OS whenever the program tries to access an invalid memory location, such as the `NULL` pointer or simply an address outside the user-mode address space as established by the OS. The only case when this signal can be received by a Rust program is if memory unsafety occurs due to misuse of unsafe code. As such, it should normally not be masked out or handled, as it likely indicates a critical bug (soundness hole), executable file corruption or process memory corruption. /// /// For reasons described above, **this signal is considered unsafe** – handling it requires using `set_unsafe_handler`. /// /// *Default handler: process termination with a core dump.* #[cfg(any(doc, se_basic))] SegmentationFault = SIGSEGV, /// `SIGPIPE` – invalid access to an [unnamed pipe]. This signal is issued by the OS whenever a program attempts to write to an unnamed pipe which has no readers connected to it. If unexpected, this might mean abnormal termination of the process which the pipe was used to communicate with. /// /// *Default handler: process termination.* /// /// [unnamed pipe]: ../../../unnamed_pipe/index.html " " #[cfg(any(doc, se_basic))] BrokenPipe = SIGPIPE, /// `SIGALRM` – "alarm clock" signal. This signal is issued by the OS when the arranged amount of real (wall clock) time expires. This clock can be set using the [`alarm`] and [`setitimer`] system calls. /// /// *Default handler: process termination.* /// /// [`alarm`]: https://www.man7.org/linux/man-pages/man2/alarm.2.html " " /// [`setitimer`]: https://www.man7.org/linux/man-pages/man2/setitimer.2.html " " #[cfg(any(doc, se_basic))] AlarmClock = SIGALRM, /// `SIGTERM` – request for termination. This signal can only be sent using the usual signal sending procedures. Unlike [`KeyboardInterrupt`], this signal is not a request to break out of a lengthy operation, but rather to close the program as a whole. Signal handlers for this signal are expected to perform minimal cleanup and quick state save procedures and then exit. /// /// *Default handler: process termination.* /// /// [`KeyboardInterrupt`]: #variant.KeyboardInterrupt " " #[cfg(any(doc, se_basic))] Termination = SIGTERM, /// `SIGUSR1` – user-defined signal 1. This signal, like [`UserSignal2`], does not have a predefined meaning and is not produced by the OS. For this reason, it is typically used for interprocess communication between two programs familiar with each other, or in language runtimes to signal certain events, such as externally activated immediate garbage collection. /// /// *Default handler: process termination.* /// /// [`UserSignal2`]: #variant.UserSignal2 " " #[cfg(any(doc, se_full_posix_1990))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 templat feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] UserSignal1 = SIGUSR1, /// `SIGUSR2` – user-defined signal 2. This signal is similar to [`UserSignal1`] and has the same properties, but is a distinct signal nonetheless. /// /// *Default handler: process termination.* /// /// [`UserSignal1`]: #variant.UserSignal1 " " #[cfg(any(doc, se_full_posix_1990))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 templat feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] UserSignal2 = SIGUSR2, /// `SIGCHLD` – child process suspended, resumed or terminated. This signal is issued by the OS whenever a child process is suspended/resumed or terminated by a signal or otherwise. /// /// *Default handler: ignore.* #[cfg(any(doc, se_full_posix_1990))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 templat feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] ChildProcessEvent = SIGCHLD, /// `SIGCONT` – resume the process after being suspended. This signal can be sent to a process by an external program when it wishes to resume that process after it being suspended in a stopped state. /// /// *Default handler: continue execution.* #[cfg(any(doc, se_full_posix_1990))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 templat feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] Continue = SIGCONT, /// `SIGSTOP` – forcefully stop a process temporarily. This signal can only be sent to a process by an external program. Unlike [`Suspend`], this signal cannot be masked out or handled, i.e. it is guaranteed to be able to temporarily stop a process from executing without [forcefully terminating it]. The process can then be restarted using [`Continue`]. /// /// *Default handler: temporarily stop process, **cannot be overriden or disabled**.* /// /// [`Suspend`]: #variant.Suspend " " /// [forcefully terminating it]: #variant.Kill " " /// [`Continue`]: #variant.Continue " " #[cfg(any(doc, se_full_posix_1990))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 templat feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] ForceSuspend = SIGSTOP, /// `SIGTSTP` – temporarily stop a process. This signal can only be sent to a process by an external program. Unlike [`ForceSuspend`], this signal can be masked out or handled by the process which is requested to stop. The process can then be restarted using [`Continue`]. /// /// *Default handler: temporarily stop process.* /// /// [`ForceSuspend`]: #variant.ForceSuspend " " /// [`Continue`]: #variant.Continue " " #[cfg(any(doc, se_full_posix_1990))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 templat feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] Suspend = SIGTSTP, /// `SIGTTIN` – attempt to read from standard input while in the background. This signal is issued by the OS whenever a process which is under [job control] tries to read from standard input but is in the background, i.e. temporarily detached from the terminal. /// /// *Default handler: temporarily stop process.* /// /// [job control]: https://en.wikipedia.org/wiki/Job_control_(Unix) " " #[cfg(any(doc, se_full_posix_1990))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 templat feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] TerminalInputWhileInBackground = SIGTTIN, /// `SIGTTOU` – attempt to write to standard output while in the background. This signal is issued by the OS whenever a process which is under [job control] tries to write to standard input but is in the background, i.e. temporarily detached from the terminal. /// /// *Default handler: temporarily stop process.* /// /// [job control]: https://en.wikipedia.org/wiki/Job_control_(Unix) " " #[cfg(any(doc, se_full_posix_1990))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 templat feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] TerminalOutputWhileInBackground = SIGTTOU, /// `SIGPOLL` – watched file descriptor event. This signal is issued by the OS when a file descriptor which has been enabled to interact with this signal has a state update. /// /// *Default handler: process termination.* // TODO more on this #[cfg(any(se_sigpoll, se_sigpoll_is_sigio))] #[cfg_attr( // any(se_sigpoll, se_sigpoll_is_sigio) template feature = "doc_cfg", doc(cfg(any( target_os = "linux", target_os = "android", target_os = "emscripten", target_os = "redox", target_os = "haiku", target_os = "solaris", target_os = "illumos" ))) )] PollNotification = SIGPOLL, /// `SIGBUS` – [bus error]. This signal is issued by the OS when a process does one of the following: /// - **Tries to access an invalid physical address**. Normally, this should never happen – attempts to access invalid *virtual* memory are handled as [segmentation faults], and invalid phyiscal addresses are typically not present in the address space of a program in user-mode. /// - **Performs an incorrectly aligned memory access**. In Rust, this can only happen if unsafe code is misused to construct an incorrectly aligned pointer to a type which requires alignment which is more strict than simple one byte alignment. This is a direct sign of memory unsafety being invoked. /// - **Incorrect x86 segment register**. This can only be acheieved using inline assembly or FFI (calling an external function written in assembly language or with usage of C/C++ inline assembly), and only on the x86 architecture. If an invalid value is loaded into the segment registers, the CPU generates this exception. This is either a sign of a failed advanced unsafe operation or a deliberate attempt to cryptically crash the program. /// /// For reasons described above, **this signal is considered unsafe** – handling it requires using `set_unsafe_handler`. /// /// *Default handler: process termination with a core dump.* /// /// [bus error]: https://en.wikipedia.org/wiki/Bus_error " " /// [segmentation faults]: #variant.SegmentationFault " " #[cfg(any(doc, se_base_posix_2001))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 template feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] MemoryBusError = SIGBUS, /// `SIGPROF` – profiler clock signal. This signal is issued by the OS when the arranged amount of CPU time expires. The time includes not only user-mode CPU time spent in the process, but also kernel-mode CPU time which the OS associates with the process. This is different from [`UserModeProfilerClock`]'s behavior, which counts only user-mode CPU time. This clock can be set using the [`setitimer`] system call. /// /// *Default handler: process termination.* /// /// [`UserModeProfilerClock`]: #variant.UserModeProfilerClock " " /// [`setitimer`]: https://www.man7.org/linux/man-pages/man2/setitimer.2.html " " #[cfg(any(doc, se_base_posix_2001))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 template feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] ProfilerClock = SIGPROF, /// `SIGVTALRM` – user-mode profiler clock signal. This signal is issued by the OS when the arranged amount of CPU time expires. Only user-mode CPU time is counted, in contrast to `ProfilerClock`, which counts kernel-mode time associated by the system with the process as well. This clock can be set using the [`setitimer`] system call. /// /// *Default handler: process termination.* /// /// [`ProfilerClock`]: #variant.ProfilerClock " " /// [`setitimer`]: https://www.man7.org/linux/man-pages/man2/setitimer.2.html " " #[cfg(any(doc, se_base_posix_2001))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 template feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] UserModeProfilerClock = SIGVTALRM, /// `SIGSYS` – attempt to perform an invalid system call. This signal is issued by the OS when a system call receives invalid arguments. Normally, the C library functions used to perform system calls in Rust programs do not generate this signal – the only way to generate it is to send it to a process explicitly, use raw system call instructions or violate [`seccomp`] rules if it is enabled. /// /// *Default handler: process termination with a core dump.* /// /// [`seccomp`]: https://en.wikipedia.org/wiki/Seccomp " " #[cfg(any(doc, se_base_posix_2001))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 template feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] InvalidSystemCall = SIGSYS, /// `SIGTRAP` – software breakpoint. This signal is issued by the OS when a breakpoint instruction is executed. On x86, the instruction to do so is `int 3`. This instruction is typically inserted by debuggers and code injection utilities. /// /// *Default handler: process termination with a core dump.* #[cfg(any(doc, se_base_posix_2001))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 template feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] Breakpoint = SIGTRAP, /// `SIGURG` – [out-of-band data] received on a socket. This signal is issued by the OS when a socket owned by the process receives urgent out-of-band data. /// /// *Default handler: ignore.* /// /// [out-of-band data]: https://en.wikipedia.org/wiki/Out-of-band_data " " #[cfg(any(doc, se_base_posix_2001))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 template feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] OutOfBandDataAvailable = SIGURG, /// `SIGXCPU` – assigned CPU time limit for the process was exceeded. This signal is issued by the OS if a CPU time limit for the process was set, when that limit expires. If not handled quickly, the system will issue a [`Kill`] signal to shut down the process forcefully, i.e. ignoring this signal won't lead to bypassing the limit. The [`setrlimit`] system call is used to set the soft limit for this signal and the hard limit, which invokes [`Kill`]. /// /// *Default handler: process termination with a core dump.* /// /// [`setrlimit`]: https://www.man7.org/linux/man-pages/man2/setrlimit.2.html " " /// [`Kill`]: #variant.Kill " " #[cfg(any(doc, se_base_posix_2001))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 template feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] CpuTimeLimitExceeded = SIGXCPU, /// `SIGXFSZ` – assigned file size limit for the process was exceeded. This signal is issued by the OS if a limit on the size of files which are written to by the process was set, when that limit is exceeded by the process. If ignored/handled, the offending system call fails with an error regardless of the handling method. The [`setrlimit`] system call is used to set the limit. /// /// *Default handler: process termination with a core dump.* /// /// [`setrlimit`]: https://www.man7.org/linux/man-pages/man2/setrlimit.2.html " " #[cfg(any(doc, se_base_posix_2001))] #[cfg_attr( // se_full_posix_1990/se_base_posix_2001 template feature = "doc_cfg", doc(cfg(not(target_os = "hermit"))), )] FileSizeLimitExceeded = SIGXFSZ, } impl SignalType { /// Returns `true` if the value is a special signal which cannot be blocked or handled ([`Kill`] or [`ForceSuspend`]), `false` otherwise. /// /// [`Kill`]: #variant.Kill " " /// [`ForceSuspend`]: #variant.ForceSuspend " " pub const fn is_unblockable(self) -> bool { matches!(self, Self::Kill | Self::ForceSuspend) } /// Returns `true` if the value is an unsafe signal which requires unsafe code when setting a handling method, `false` otherwise. pub const fn is_unsafe(self) -> bool { matches!( self, Self::SegmentationFault | Self::MemoryBusError | Self::IllegalInstruction ) } } impl From for i32 { fn from(op: SignalType) -> Self { op as i32 } } impl From for u32 { fn from(op: SignalType) -> Self { op as u32 } } impl TryFrom for SignalType { type Error = UnknownSignalError; #[rustfmt::skip] fn try_from(value: i32) -> Result { match value { #[cfg(se_basic)] SIGHUP => Ok(Self::Hangup), #[cfg(se_basic)] SIGINT => Ok(Self::KeyboardInterrupt), #[cfg(se_basic)] SIGQUIT => Ok(Self::QuitAndDump), #[cfg(se_basic)] SIGILL => Ok(Self::IllegalInstruction), #[cfg(se_basic)] SIGABRT => Ok(Self::Abort), #[cfg(se_basic)] SIGFPE => Ok(Self::MathException), #[cfg(se_basic)] SIGKILL => Ok(Self::Kill), #[cfg(se_basic)] SIGSEGV => Ok(Self::SegmentationFault), #[cfg(se_basic)] SIGPIPE => Ok(Self::BrokenPipe), #[cfg(se_basic)] SIGALRM => Ok(Self::AlarmClock), #[cfg(se_basic)] SIGTERM => Ok(Self::Termination), #[cfg(se_full_posix_1990)] SIGUSR1 => Ok(Self::UserSignal1), #[cfg(se_full_posix_1990)] SIGUSR2 => Ok(Self::UserSignal2), #[cfg(se_full_posix_1990)] SIGCHLD => Ok(Self::ChildProcessEvent), #[cfg(se_full_posix_1990)] SIGCONT => Ok(Self::Continue), #[cfg(se_full_posix_1990)] SIGSTOP => Ok(Self::ForceSuspend), #[cfg(se_full_posix_1990)] SIGTSTP => Ok(Self::Suspend), #[cfg(se_full_posix_1990)] SIGTTIN => Ok(Self::TerminalInputWhileInBackground), #[cfg(se_full_posix_1990)] SIGTTOU => Ok(Self::TerminalOutputWhileInBackground), #[cfg(any(se_sigpoll, se_sigpoll_is_sigio))] SIGPOLL => Ok(Self::PollNotification), #[cfg(se_full_posix_2001)] SIGBUS => Ok(Self::MemoryBusError), #[cfg(se_full_posix_2001)] SIGPROF => Ok(Self::ProfilerClock), #[cfg(se_base_posix_2001)] SIGSYS => Ok(Self::InvalidSystemCall), #[cfg(se_base_posix_2001)] SIGTRAP => Ok(Self::Breakpoint), #[cfg(se_base_posix_2001)] SIGURG => Ok(Self::OutOfBandDataAvailable), #[cfg(se_base_posix_2001)] SIGVTALRM => Ok(Self::UserModeProfilerClock), #[cfg(se_base_posix_2001)] SIGXCPU => Ok(Self::CpuTimeLimitExceeded), #[cfg(se_base_posix_2001)] SIGXFSZ => Ok(Self::FileSizeLimitExceeded), _ => Err(UnknownSignalError { value }), } } } impl TryFrom for SignalType { type Error = UnknownSignalError; fn try_from(value: u32) -> Result { value.try_into() } } /// Error type returned when a conversion from [`i32`]/[`u32`] to [`SignalType`] fails. /// /// [`i32`]: https://doc.rust-lang.org/std/primitive.i32.html " " /// [`u32`]: https://doc.rust-lang.org/std/primitive.u32.html " " /// [`SignalType`]: enum.SignalType.html " " #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct UnknownSignalError { /// The unknown signal value which was encountered. pub value: i32, } impl Display for UnknownSignalError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "unknown signal value {}", self.value) } } impl fmt::Binary for UnknownSignalError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "unknown signal value {:b}", self.value) } } impl fmt::LowerHex for UnknownSignalError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "unknown signal value {:x}", self.value) } } impl fmt::UpperExp for UnknownSignalError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "unknown signal value {:X}", self.value) } } impl fmt::Octal for UnknownSignalError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "unknown signal value {:o}", self.value) } } impl Error for UnknownSignalError {} interprocess-1.2.1/src/os/unix/udsocket/ancillary.rs000064400000000000000000000364620072674642500207500ustar 00000000000000use super::imports::*; use cfg_if::cfg_if; use std::{ borrow::Cow, iter::{FromIterator, FusedIterator}, mem::size_of, }; /// Ancillary data to be sent through a Unix domain socket or read from an input buffer. /// /// Ancillary data gives unique possibilities to Unix domain sockets which no other POSIX API has: passing file descriptors between two processes which do not have a parent-child relationship. It also can be used to transfer credentials of a process reliably. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum AncillaryData<'a> { /// One or more file descriptors to be sent. FileDescriptors(Cow<'a, [c_int]>), /// Credentials to be sent. The specified values are checked by the system when sent for all users except for the superuser – for senders, this means that the correct values need to be filled out, otherwise, an error is returned; for receivers, this means that the credentials are to be trusted for authentification purposes. For convenience, the [`credentials`] function provides a value which is known to be valid when sent. /// /// [`credentials`]: #method.credentials " " #[cfg(any(doc, uds_ucred))] #[cfg_attr( // uds_ucred template feature = "doc_cfg", doc(cfg(any( all( target_os = "linux", any( target_env = "gnu", target_env = "musl", target_env = "musleabi", target_env = "musleabihf" ) ), target_os = "emscripten", target_os = "redox" ))) )] Credentials { /// The process identificator (PID) for the process. pid: pid_t, /// The user identificator (UID) of the user who started the process. uid: uid_t, /// The group identificator (GID) of the user who started the process. gid: gid_t, }, } impl<'a> AncillaryData<'a> { /// The size of a single `AncillaryData::Credentials` element when packed into the Unix ancillary data format. Useful for allocating a buffer when you expect to receive credentials. pub const ENCODED_SIZE_OF_CREDENTIALS: usize = Self::_ENCODED_SIZE_OF_CREDENTIALS; cfg_if! { if #[cfg(uds_ucred)] { const _ENCODED_SIZE_OF_CREDENTIALS: usize = size_of::() + size_of::(); } /*else if #[cfg(uds_xucred)] { const _ENCODED_SIZE_OF_CREDENTIALS: usize = size_of::() + size_of::(); } */else if #[cfg(unix)] { const _ENCODED_SIZE_OF_CREDENTIALS: usize = size_of::(); } else { const _ENCODED_SIZE_OF_CREDENTIALS: usize = 0; } } /// Calculates the size of an `AncillaryData::FileDescriptors` element with the specified amount of file descriptors when packed into the Unix ancillary data format. Useful for allocating a buffer when you expect to receive a specific amount of file descriptors. pub const fn encoded_size_of_file_descriptors(num_descriptors: usize) -> usize { #[cfg(not(unix))] struct cmsghdr; // ??????????????? size_of::() + num_descriptors * size_of::() } /// Inexpensievly clones `self` by borrowing the `FileDescriptors` variant or copying the `Credentials` variant. #[must_use] pub fn clone_ref(&'a self) -> Self { match *self { Self::FileDescriptors(ref fds) => Self::FileDescriptors(Cow::Borrowed(fds)), #[cfg(uds_ucred)] Self::Credentials { pid, uid, gid } => Self::Credentials { pid, uid, gid }, } } /// Returns the size of an ancillary data element when packed into the Unix ancillary data format. pub fn encoded_size(&self) -> usize { match self { Self::FileDescriptors(fds) => Self::encoded_size_of_file_descriptors(fds.len()), #[cfg(uds_scm_credentials)] Self::Credentials { .. } => Self::ENCODED_SIZE_OF_CREDENTIALS, } } /// Encodes the ancillary data into `EncodedAncillaryData` which is ready to be sent via a Unix domain socket. pub fn encode(op: impl IntoIterator) -> EncodedAncillaryData<'static> { let items = op.into_iter(); let mut buffer = Vec::with_capacity( { let size_hint = items.size_hint(); size_hint.1.unwrap_or(size_hint.0) // If we assume that all ancillary data elements are credentials, we're more than fine. } * Self::ENCODED_SIZE_OF_CREDENTIALS, ); for i in items { let mut cmsg_len = size_of::(); let cmsg_level_bytes = SOL_SOCKET.to_ne_bytes(); let cmsg_type_bytes; match i { AncillaryData::FileDescriptors(fds) => { cmsg_type_bytes = SCM_RIGHTS.to_ne_bytes(); cmsg_len += fds.len() * 4; // #[cfg(target_pointer_width = "64")] // this was here, I don't even remember why, but that // wouldn't compile on a 32-bit machine let cmsg_len_bytes = cmsg_len.to_ne_bytes(); buffer.extend_from_slice(&cmsg_len_bytes); buffer.extend_from_slice(&cmsg_level_bytes); buffer.extend_from_slice(&cmsg_type_bytes); for i in fds.iter().copied() { let desc_bytes = i.to_ne_bytes(); buffer.extend_from_slice(&desc_bytes); } } #[cfg(uds_ucred)] AncillaryData::Credentials { pid, uid, gid } => { cmsg_type_bytes = SCM_RIGHTS.to_ne_bytes(); cmsg_len += size_of::(); // #[cfg(target_pointer_width = "64")] let cmsg_len_bytes = cmsg_len.to_ne_bytes(); let pid_bytes = pid.to_ne_bytes(); let uid_bytes = uid.to_ne_bytes(); let gid_bytes = gid.to_ne_bytes(); buffer.extend_from_slice(&cmsg_len_bytes); buffer.extend_from_slice(&cmsg_level_bytes); buffer.extend_from_slice(&cmsg_type_bytes); buffer.extend_from_slice(&pid_bytes); buffer.extend_from_slice(&uid_bytes); buffer.extend_from_slice(&gid_bytes); } } } EncodedAncillaryData(Cow::Owned(buffer)) } } impl AncillaryData<'static> { /// Fetches the credentials of the process from the system and returns a value which can be safely sent to another process without the system complaining about an unauthorized attempt to impersonate another process/user/group. /// /// If you want to send credentials to another process, this is usually the function you need to obtain the desired ancillary payload. #[cfg(any(doc, uds_ucred))] #[cfg_attr( // uds_ucred template feature = "doc_cfg", doc(cfg(any( all( target_os = "linux", any( target_env = "gnu", target_env = "musl", target_env = "musleabi", target_env = "musleabihf" ) ), target_os = "emscripten", target_os = "redox" ))) )] pub fn credentials() -> Self { Self::Credentials { pid: unsafe { libc::getpid() }, uid: unsafe { libc::getuid() }, gid: unsafe { libc::getgid() }, } } } /// A buffer used for sending ancillary data into Unix domain sockets. #[repr(transparent)] #[derive(Clone, Debug)] pub struct EncodedAncillaryData<'a>(pub Cow<'a, [u8]>); impl<'a> From<&'a [u8]> for EncodedAncillaryData<'a> { fn from(op: &'a [u8]) -> Self { Self(Cow::Borrowed(op)) } } impl From> for EncodedAncillaryData<'static> { fn from(op: Vec) -> Self { Self(Cow::Owned(op)) } } impl<'b> FromIterator> for EncodedAncillaryData<'static> { fn from_iter>>(iter: I) -> Self { AncillaryData::encode(iter) } } impl<'b> From>> for EncodedAncillaryData<'static> { fn from(op: Vec>) -> Self { Self::from_iter(op) } } impl<'b: 'c, 'c> From<&'c [AncillaryData<'b>]> for EncodedAncillaryData<'static> { fn from(op: &'c [AncillaryData<'b>]) -> Self { op.iter().map(AncillaryData::clone_ref).collect::() } } impl<'a> AsRef<[u8]> for EncodedAncillaryData<'a> { fn as_ref(&self) -> &[u8] { &self.0 } } /// A buffer used for receiving ancillary data from Unix domain sockets. /// /// The actual ancillary data can be obtained using the [`decode`] method. /// /// # Example /// See [`UdStream`] or [`UdStreamListener`] for an example of receiving ancillary data. /// /// [`decode`]: #method.decode " " /// [`UdStream`]: struct.UdStream.html#examples " " /// [`UdStreamListener`]: struct.UdStreamListener.html#examples " " #[derive(Debug)] pub enum AncillaryDataBuf<'a> { /// The buffer's storage is borrowed. Borrowed(&'a mut [u8]), /// The buffer's storage is owned by the buffer itself. Owned(Vec), } impl<'a> AncillaryDataBuf<'a> { /// Creates an owned ancillary data buffer with the specified capacity. pub fn owned_with_capacity(capacity: usize) -> Self { Self::Owned(Vec::with_capacity(capacity)) } /// Creates a decoder which decodes the ancillary data buffer into a friendly representation of its contents. /// /// All invalid ancillary data blocks are skipped – if there was garbage data in the buffer to begin with, the resulting buffer will either be empty or contain invalid credentials/file descriptors. This should normally never happen if the data is actually received from a Unix domain socket. pub fn decode(&'a self) -> AncillaryDataDecoder<'a> { AncillaryDataDecoder { buffer: self.as_ref(), i: 0, } } } impl<'a> From<&'a mut [u8]> for AncillaryDataBuf<'a> { fn from(op: &'a mut [u8]) -> Self { Self::Borrowed(op) } } impl From> for AncillaryDataBuf<'static> { fn from(op: Vec) -> Self { Self::Owned(op) } } impl<'a> From<&'a mut AncillaryDataBuf<'a>> for AncillaryDataBuf<'a> { fn from(op: &'a mut AncillaryDataBuf<'a>) -> Self { match op { Self::Borrowed(slice) => Self::Borrowed(slice), Self::Owned(vec) => Self::Borrowed(vec), } } } impl<'a> AsRef<[u8]> for AncillaryDataBuf<'a> { fn as_ref(&self) -> &[u8] { match self { Self::Borrowed(slice) => slice, Self::Owned(vec) => vec, } } } impl<'a> AsMut<[u8]> for AncillaryDataBuf<'a> { fn as_mut(&mut self) -> &mut [u8] { match self { Self::Borrowed(slice) => slice, Self::Owned(vec) => vec, } } } /// An iterator which decodes ancillary data from an ancillary data buffer. /// /// This iterator is created by the [`decode`] method on [`AncillaryDataBuf`] – see its documentation for more. /// /// [`AncillaryDataBuf`]: struct.AncillaryDataBuf.html " " /// [`decode`]: struct.AncillaryDataBuf.html#method.decode " " #[derive(Clone, Debug)] pub struct AncillaryDataDecoder<'a> { buffer: &'a [u8], i: usize, } impl<'a> From<&'a AncillaryDataBuf<'a>> for AncillaryDataDecoder<'a> { fn from(buffer: &'a AncillaryDataBuf<'a>) -> Self { buffer.decode() } } impl<'a> Iterator for AncillaryDataDecoder<'a> { type Item = AncillaryData<'static>; fn next(&mut self) -> Option { fn u32_from_slice(bytes: &[u8]) -> u32 { u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) } fn u64_from_slice(bytes: &[u8]) -> u64 { u64::from_ne_bytes([ bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], ]) } let bytes = self.buffer; let end = bytes.len() - 1; if matches!(bytes.len().checked_sub(self.i), Some(0) | None) { self.i = end; return None; } // The first field is the length, which is a size_t #[cfg(target_pointer_width = "64")] let element_size = { if bytes.len() - self.i < 8 { self.i = end; return None; } u64_from_slice(&bytes[self.i..self.i + 8]) as usize }; #[cfg(target_pointer_width = "32")] let element_size = { if bytes.len() - self.i < 4 { self.i = end; return None; } u32_from_slice(&bytes[self.i..self.i + 4]) as usize }; // The cmsg_level field is always SOL_SOCKET – we don't need it, let's get the // cmsg_type field right away by first getting the offset at which it's // located: #[cfg(target_pointer_width = "64")] let type_offset: usize = 8 + 4; // 8 for cmsg_size, 4 for cmsg_level #[cfg(target_pointer_width = "32")] let type_offset: usize = 4 + 4; // 4 for cmsg_size, 4 for cmsg_level // Now let's get the type itself: let element_type = u32_from_slice(&bytes[self.i + type_offset..=self.i + type_offset + 4]); // The size of cmsg_size, cmsg_level and cmsg_type together let element_offset = type_offset + 4; // Update the counter before returning. self.i += element_offset // cmsg_size, cmsg_level and cmsg_type + element_size; // data size // SAFETY: those are ints lmao match element_type as i32 { SCM_RIGHTS => { // We're reading one or multiple descriptors from the ancillary data payload. // All descriptors are 4 bytes in size – leftover bytes are discarded thanks // to integer division rules let amount_of_descriptors = element_size / 4; let mut descriptors = Vec::::with_capacity(amount_of_descriptors); let mut descriptor_offset = element_offset; for _ in 0..amount_of_descriptors { descriptors.push( // SAFETY: see above u32_from_slice(&bytes[descriptor_offset..descriptor_offset + 4]) as i32, ); descriptor_offset += 4; } Some(AncillaryData::FileDescriptors(Cow::Owned(descriptors))) } #[cfg(uds_ucred)] SCM_CREDENTIALS => { // We're reading a single ucred structure from the ancillary data payload. // SAFETY: those are still ints let pid_offset = element_offset; let pid: pid_t = u32_from_slice(&bytes[pid_offset..pid_offset + 4]) as pid_t; let uid_offset = pid_offset + 4; let uid: uid_t = u32_from_slice(&bytes[uid_offset..uid_offset + 4]) as uid_t; let gid_offset = uid_offset + 4; let gid: gid_t = u32_from_slice(&bytes[gid_offset..gid_offset + 4]) as gid_t; Some(AncillaryData::Credentials { pid, uid, gid }) } _ => self.next(), // Do nothing if we hit corrupted data. } } } impl FusedIterator for AncillaryDataDecoder<'_> {} interprocess-1.2.1/src/os/unix/udsocket/c_wrappers.rs000064400000000000000000000144660072674642500211370ustar 00000000000000use { super::imports::*, std::{ffi::c_void, io, mem::size_of, net::Shutdown, ptr}, }; pub(super) fn create_uds(ty: c_int, nonblocking: bool) -> io::Result { #[allow(unused_mut, clippy::let_and_return)] let ty = { let mut ty = ty; #[cfg(target_os = "linux")] { ty |= libc::SOCK_CLOEXEC; if nonblocking { ty |= libc::SOCK_NONBLOCK; } } ty }; let fd = create_uds_raw(ty)?; #[cfg(not(target_os = "linux"))] { set_nonblocking(&fd, nonblocking)?; set_cloexec(&fd, true)?; } Ok(fd) } fn create_uds_raw(ty: c_int) -> io::Result { let (success, fd) = unsafe { let result = libc::socket(AF_UNIX, ty, 0); (result != -1, result) }; if success { let fdops = unsafe { // SAFETY: we just created this descriptor FdOps::from_raw_fd(fd) }; Ok(fdops) } else { Err(io::Error::last_os_error()) } } /// Binds the specified Ud-socket file descriptor to the given address. /// /// # Safety /// `addr` must be properly null-terminated. pub(super) unsafe fn bind(fd: &FdOps, addr: &sockaddr_un) -> io::Result<()> { let success = unsafe { libc::bind( fd.0, // Double cast because you cannot cast a reference to a pointer of arbitrary type // but you can cast any narrow pointer to any other narrow pointer addr as *const _ as *const sockaddr, size_of::() as u32, ) != -1 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } /// Connects the specified Ud-socket file descriptor to the given address. /// /// # Safety /// `addr` must be properly null-terminated. pub(super) unsafe fn connect(fd: &FdOps, addr: &sockaddr_un) -> io::Result<()> { let success = unsafe { libc::connect( fd.0, addr as *const _ as *const _, size_of::() as u32, ) != -1 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } pub(super) fn listen(fd: &FdOps, backlog: c_int) -> io::Result<()> { let success = unsafe { libc::listen(fd.0, backlog) != -1 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } pub(super) fn set_passcred(fd: &FdOps, passcred: bool) -> io::Result<()> { #[cfg(uds_scm_credentials)] { use std::mem::size_of_val; let passcred = passcred as c_int; let success = unsafe { libc::setsockopt( fd.0, SOL_SOCKET, SO_PASSCRED, &passcred as *const _ as *const _, size_of_val(&passcred) as u32, ) != -1 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } #[cfg(not(uds_scm_credentials))] { let _ = (fd, passcred); Ok(()) } } #[cfg(uds_peercred)] pub(super) fn get_peer_ucred(fd: &FdOps) -> io::Result { use std::mem::zeroed; let mut cred: ucred = unsafe { // SAFETY: it's safe for the ucred structure to be zero-initialized, since // it only contains integers zeroed() }; let mut cred_len = size_of::() as socklen_t; let success = unsafe { libc::getsockopt( fd.0, SOL_SOCKET, SO_PEERCRED, &mut cred as *mut _ as *mut _, &mut cred_len as *mut _, ) } != -1; if success { Ok(cred) } else { Err(io::Error::last_os_error()) } } pub(super) fn set_nonblocking(fd: &FdOps, nonblocking: bool) -> io::Result<()> { let (old_flags, success) = unsafe { // SAFETY: nothing too unsafe about this function. One thing to note is that we're passing // it a null pointer, which is, for some reason, required yet ignored for F_GETFL. let result = libc::fcntl(fd.0, F_GETFL, ptr::null::()); (result, result != -1) }; if !success { return Err(io::Error::last_os_error()); } let new_flags = if nonblocking { old_flags | O_NONBLOCK } else { // Inverting the O_NONBLOCK value sets all the bits in the flag set to 1 except for the // nonblocking flag, which clears the flag when ANDed. old_flags & !O_NONBLOCK }; let success = unsafe { // SAFETY: new_flags is a c_int, as documented in the manpage. libc::fcntl(fd.0, F_SETFL, new_flags) } != -1; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } pub(super) fn get_nonblocking(fd: &FdOps) -> io::Result { let flags = unsafe { // SAFETY: exactly the same as above. libc::fcntl(fd.0, F_GETFL, ptr::null::()) }; if flags != -1 { Ok(flags & O_NONBLOCK != 0) } else { Err(io::Error::last_os_error()) } } pub(super) fn shutdown(fd: &FdOps, how: Shutdown) -> io::Result<()> { let how = match how { Shutdown::Read => SHUT_RD, Shutdown::Write => SHUT_WR, Shutdown::Both => SHUT_RDWR, }; let success = unsafe { libc::shutdown(fd.0, how) != -1 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } #[cfg(not(target_os = "linux"))] mod non_linux { use super::*; pub(super) fn get_fdflags(fd: &FdOps) -> io::Result { let (val, success) = unsafe { let ret = libc::fcntl(fd.0, F_GETFD, 0); (ret, ret != -1) }; if success { Ok(val) } else { Err(io::Error::last_os_error()) } } pub(super) fn set_fdflags(fd: &FdOps, flags: i32) -> io::Result<()> { let success = unsafe { libc::fcntl(fd.0, F_SETFD, flags) != -1 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } pub(super) fn set_cloexec(fd: &FdOps, cloexec: bool) -> io::Result<()> { let mut flags = get_fdflags(fd)? & (!FD_CLOEXEC); // Mask out cloexec to set it to a new value if cloexec { flags |= FD_CLOEXEC; } set_fdflags(fd, flags)?; Ok(()) } } #[cfg(not(target_os = "linux"))] use non_linux::*; interprocess-1.2.1/src/os/unix/udsocket/listener.rs000064400000000000000000000221620072674642500206070ustar 00000000000000#[cfg(uds_supported)] use super::c_wrappers; use super::{imports::*, PathDropGuard, ToUdSocketPath, UdSocketPath, UdStream}; use std::{ fmt::{self, Debug, Formatter}, io, iter::FusedIterator, mem::zeroed, }; use to_method::To; /// A Unix domain byte stream socket server, listening for connections. /// /// All such sockets have the `SOCK_STREAM` socket type; in other words, this is the Unix domain version of a TCP server. /// /// # Examples /// Basic server: /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(unix)] { /// use interprocess::os::unix::udsocket::{UdStream, UdStreamListener}; /// use std::{io::{self, prelude::*}, net::Shutdown}; /// /// fn handle_error(result: io::Result) -> Option { /// match result { /// Ok(val) => Some(val), /// Err(error) => { /// eprintln!("There was an error with an incoming connection: {}", error); /// None /// } /// } /// } /// /// let listener = UdStreamListener::bind("/tmp/example.sock")?; /// // Outside the loop so that we could reuse the memory allocation for every client /// let mut input_string = String::new(); /// for mut conn in listener.incoming() /// // Use filter_map to report all errors with connections and skip those connections in the loop, /// // making the actual server loop part much cleaner than if it contained error handling as well. /// .filter_map(handle_error) { /// conn.write_all(b"Hello from server!")?; /// conn.shutdown(Shutdown::Write)?; /// conn.read_to_string(&mut input_string)?; /// println!("Client answered: {}", input_string); /// input_string.clear(); /// } /// # } /// # Ok(()) } /// ``` pub struct UdStreamListener { // TODO make this not 'static _drop_guard: PathDropGuard<'static>, fd: FdOps, } impl UdStreamListener { /// Creates a new listener socket at the specified address. /// /// If the socket path exceeds the [maximum socket path length] (which includes the first 0 byte when using the [socket namespace]), an error is returned. Errors can also be produced for different reasons, i.e. errors should always be handled regardless of whether the path is known to be short enough or not. /// /// After the socket is dropped, the socket file will be left over. Use [`bind_with_drop_guard()`](Self::bind_with_drop_guard) to mitigate this automatically, even during panics (if unwinding is enabled). /// /// # Example /// See [`ToUdSocketPath`]. /// /// # System calls /// - `socket` /// - `bind` /// /// [maximum socket path length]: const.MAX_UDSOCKET_PATH_LEN.html " " /// [socket namespace]: enum.UdSocketPath.html#namespaced " " /// [`ToUdSocketPath`]: trait.ToUdSocketPath.html " " pub fn bind<'a>(path: impl ToUdSocketPath<'a>) -> io::Result { Self::_bind(path.to_socket_path()?, false, false) } /// Creates a new listener socket at the specified address, remembers the address, and installs a drop guard that will delete the socket file once the socket is dropped. /// /// See the documentation of [`bind()`](Self::bind). pub fn bind_with_drop_guard<'a>(path: impl ToUdSocketPath<'a>) -> io::Result { Self::_bind(path.to_socket_path()?, true, false) } pub(crate) fn _bind( path: UdSocketPath<'_>, keep_drop_guard: bool, nonblocking: bool, ) -> io::Result { let addr = path.borrow().try_to::()?; let fd = c_wrappers::create_uds(SOCK_STREAM, nonblocking)?; unsafe { // SAFETY: addr is well-constructed c_wrappers::bind(&fd, &addr)?; } // FIXME the standard library uses 128 here without an option to change this // number, why? If std has solid reasons to do this, remove this notice and // document the method's behavior on this matter explicitly; otherwise, add // an option to change this value. c_wrappers::listen(&fd, 128)?; c_wrappers::set_passcred(&fd, true)?; let dg = if keep_drop_guard { PathDropGuard { path: path.to_owned(), enabled: true, } } else { PathDropGuard::dummy() }; Ok(Self { fd, _drop_guard: dg, }) } /// Listens for incoming connections to the socket, blocking until a client is connected. /// /// See [`incoming`] for a convenient way to create a main loop for a server. /// /// # Example /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(uds_scm_credentials)] { /// use interprocess::os::unix::udsocket::UdStreamListener; /// /// let listener = UdStreamListener::bind("/tmp/example.sock")?; /// loop { /// match listener.accept() { /// Ok(connection) => { /// println!("New client!"); /// }, /// Err(error) => { /// println!("Incoming connection failed: {}", error); /// }, /// } /// } /// # } /// # Ok(()) } /// ``` /// /// # System calls /// - `accept` /// /// [`incoming`]: #method.incoming " " pub fn accept(&self) -> io::Result { let (success, fd) = unsafe { let result = libc::accept(self.as_raw_fd(), zeroed(), zeroed()); (result != -1, result) }; if success { Ok(unsafe { // SAFETY: we just created the file descriptor, meaning that it's guaranteeed // not to be used elsewhere UdStream::from_raw_fd(fd) }) } else { Err(io::Error::last_os_error()) } } /// Creates an infinite iterator which calls `accept()` with each iteration. Used together with `for` loops to conveniently create a main loop for a socket server. /// /// # Example /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(unix)] { /// use interprocess::os::unix::udsocket::UdStreamListener; /// /// let listener = UdStreamListener::bind("/tmp/example.sock")?; /// // Thanks to incoming(), you get a simple self-documenting infinite server loop /// for connection in listener.incoming() /// .map(|conn| if let Err(error) = conn { /// eprintln!("Incoming connection failed: {}", error); /// }) { /// eprintln!("New client!"); /// # drop(connection); /// } /// # } /// # Ok(()) } /// ``` pub fn incoming(&self) -> Incoming<'_> { Incoming::from(self) } /// Enables or disables the nonblocking mode for the listener. By default, it is disabled. /// /// In nonblocking mode, calls to [`accept`], and, by extension, iteration through [`incoming`] will never wait for a client to become available to connect and will instead return a [`WouldBlock`] error immediately, allowing the thread to perform other useful operations while there are no new client connections to accept. /// /// [`accept`]: #method.accept " " /// [`incoming`]: #method.incoming " " /// [`WouldBlock`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.WouldBlock " " pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { c_wrappers::set_nonblocking(&self.fd, nonblocking) } /// Checks whether the socket is currently in nonblocking mode or not. pub fn is_nonblocking(&self) -> io::Result { c_wrappers::get_nonblocking(&self.fd) } } impl Debug for UdStreamListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("UdStreamListener") .field("fd", &self.as_raw_fd()) .field("has_drop_guard", &self._drop_guard.enabled) .finish() } } impl AsRawFd for UdStreamListener { #[cfg(unix)] fn as_raw_fd(&self) -> c_int { self.fd.as_raw_fd() } } impl IntoRawFd for UdStreamListener { #[cfg(unix)] fn into_raw_fd(self) -> c_int { self.fd.into_raw_fd() } } impl FromRawFd for UdStreamListener { #[cfg(unix)] unsafe fn from_raw_fd(fd: c_int) -> Self { let fd = unsafe { FdOps::from_raw_fd(fd) }; Self { fd, _drop_guard: PathDropGuard::dummy(), } } } /// An infinite iterator over incoming client connections of a [`UdStreamListener`]. /// /// This iterator is created by the [`incoming`] method on [`UdStreamListener`] – see its documentation for more. /// /// [`UdStreamListener`]: struct.UdStreamListener.html " " /// [`incoming`]: struct.UdStreamListener.html#method.incoming " " pub struct Incoming<'a> { listener: &'a UdStreamListener, } impl<'a> Iterator for Incoming<'a> { type Item = io::Result; fn next(&mut self) -> Option { Some(self.listener.accept()) } fn size_hint(&self) -> (usize, Option) { (usize::MAX, None) } } impl FusedIterator for Incoming<'_> {} impl<'a> From<&'a UdStreamListener> for Incoming<'a> { fn from(listener: &'a UdStreamListener) -> Self { Self { listener } } } interprocess-1.2.1/src/os/unix/udsocket/mod.rs000064400000000000000000000065720072674642500175500ustar 00000000000000//! Support for Unix domain sockets, abbreviated here as "Ud-sockets". //! //! Ud-sockets are a special kind of sockets which work in the scope of only one system and use file paths instead of IPv4/IPv6 addresses and 16-bit socket numbers. Aside from their high reliability and convenience for the purposes of IPC (such as filesystem-level privelege management and the similarity to named pipes), they have a unique feature which cannot be replaced by any other form of IPC: **ancillary data**. //! //! # Ancillary data //! Thanks to this feature, Ud-sockets can transfer ownership of a file descriptor to another process, even if it doesn't have a parent-child relationship with the file descriptor owner and thus does not inherit anything via `fork()`. Aside from that, ancillary data can contain credentials of a process, which are validated by the kernel unless the sender is the superuser, meaning that this way of retrieving credentials can be used for authentification. //! //! # Usage //! The [`UdStreamListener`] and [`UdSocket`] types are two starting points, depending on whether you intend to use UDP-like datagrams or TCP-like byte streams. //! //! [`UdStreamListener`]: struct.UdStreamListener.html " " //! [`UdSocket`]: struct.UdSocket.html " " #[cfg(any(doc, feature = "tokio_support"))] #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "tokio_support")))] pub mod tokio; mod ancillary; mod listener; mod path; mod socket; mod stream; mod util; pub use {ancillary::*, listener::*, path::*, socket::*, stream::*}; mod path_drop_guard; use path_drop_guard::*; #[cfg(uds_supported)] mod c_wrappers; use super::imports; use cfg_if::cfg_if; cfg_if! { if #[cfg(not(unix))] { const _MAX_UDSOCKET_PATH_LEN: usize = 0; } else if #[cfg(uds_sockaddr_un_len_108)] { const _MAX_UDSOCKET_PATH_LEN: usize = 108; } else if #[cfg(uds_sockaddr_un_len_104)] { const _MAX_UDSOCKET_PATH_LEN: usize = 104; } else if #[cfg(uds_sockaddr_un_len_126)] { const _MAX_UDSOCKET_PATH_LEN: usize = 126; } else { compile_error!("\ Please fill out MAX_UDSOCKET_PATH_LEN in interprocess/src/os/unix/udsocket/mod.rs for your \ platform if you wish to enable Unix domain socket support for it" ); } } /// The maximum path length for Unix domain sockets. [`UdStreamListener::bind`] panics if the specified path exceeds this value. /// /// When using the [socket namespace], this value is reduced by 1, since enabling the usage of that namespace takes up one character. /// /// ## Value /// The following platforms define the value of this constant as **108**: /// - Linux /// - includes Android /// - Emscripten /// - Redox /// - HermitCore /// - Solaris /// - Illumos /// /// The following platforms define the value of this constant as **104**: /// - FreeBSD /// - OpenBSD /// - NetBSD /// - DragonflyBSD /// - macOS /// - iOS /// /// The following platforms define the value of this constant as **126**: /// - Haiku /// /// [`UdStreamListener::bind`]: struct.UdStreamListener.html#method.bind " " /// [socket namespace]: enum.UdSocketPath.html#namespaced " " // The reason why this constant wraps the underscored one instead of being defined directly is // because that'd require documenting both branches separately. This way, the user-faced // constant has only one definition and one documentation comment block. pub const MAX_UDSOCKET_PATH_LEN: usize = _MAX_UDSOCKET_PATH_LEN; interprocess-1.2.1/src/os/unix/udsocket/path.rs000064400000000000000000000744050072674642500177250ustar 00000000000000use super::{ imports::*, util::{empty_cstr, empty_cstring, eunreachable}, MAX_UDSOCKET_PATH_LEN, }; use std::{ borrow::{Cow, ToOwned}, convert::TryFrom, ffi::{CStr, CString, NulError, OsStr, OsString}, io, mem::{replace, size_of_val, zeroed}, ops::Deref, path::{Path, PathBuf}, ptr, }; /// Represents a name for a Unix domain socket. /// /// The main purpose for this enumeration is to conditionally support the dedicated socket namespace on systems which implement it – for that, the `Namespaced` variant is used. Depending on your system, you might not be seeing it, which is when you'd need the `File` fallback variant, which works on all POSIX-compliant systems. /// /// ## `Namespaced` /// This variant refers to sockets in a dedicated socket namespace, which is fully isolated from the main filesystem and closes sockets automatically when the server which opened the socket shuts down. **This variant is only implemented on Linux, which is why it is not available on other POSIX-conformant systems at compile time, resulting in a compile-time error if usage is attempted.** /// /// ## `File` /// All sockets identified this way are located on the main filesystem and exist as persistent files until deletion, preventing servers from using the same socket without deleting it from the filesystem first. This variant is available on all POSIX-compilant systems. #[derive(Clone, Debug, PartialEq, Eq)] pub enum UdSocketPath<'a> { /// An unnamed socket, identified only by its file descriptor. This is an invalid path value for creating sockets – all attempts to use such a value will result in an error. Unnamed, /// Identifies a socket which is located in the filesystem tree, existing as a file. See the [enum-level documentation] for more. /// /// [enum-level documentation]: #file " " File(Cow<'a, CStr>), /// Identifies a socket in the dedicated socket namespace, where it exists until the server closes it rather than persisting as a file. See the [enum-level documentation] for more. /// /// [enum-level documentation]: #namespaced " " #[cfg(uds_linux_namespace)] #[cfg_attr( // uds_linux_namespace template feature = "doc_cfg", doc(cfg(any(target_os = "linux", target_os = "android"))) )] Namespaced(Cow<'a, CStr>), } impl<'a> UdSocketPath<'a> { /// Returns the path as a [`CStr`]. The resulting value does not include any indication of whether it's a namespaced socket name or a filesystem path. pub fn as_cstr(&'a self) -> &'a CStr { match self { Self::File(cow) => cow.deref(), #[cfg(uds_linux_namespace)] Self::Namespaced(cow) => cow.deref(), Self::Unnamed => empty_cstr(), } } /// Returns the path as an [`OsStr`]. The resulting value does not include any indication of whether it's a namespaced socket name or a filesystem path. pub fn as_osstr(&'a self) -> &'a OsStr { OsStr::from_bytes(self.as_cstr().to_bytes()) } /// Returns the path as a [`CString`]. The resulting value does not include any indication of whether it's a namespaced socket name or a filesystem path. pub fn into_cstring(self) -> CString { match self { Self::File(cow) => cow.into_owned(), #[cfg(uds_linux_namespace)] Self::Namespaced(cow) => cow.into_owned(), Self::Unnamed => empty_cstring(), } } /// Returns the path as an [`OsString`]. The resulting value does not include any indication of whether it's a namespaced socket name or a filesystem path. pub fn into_osstring(self) -> OsString { OsString::from_vec(self.into_cstring().into_bytes()) } /// Ensures that the path is stored as an owned `CString` in place, and returns whether that required cloning or not. If `self` was not referring to any socket ([`Unnamed` variant]), the value is set to an empty `CString` (only nul terminator) of type [`File`]. /// /// [`Unnamed` variant]: #variant.Unnamed " " /// [`File`]: #file " " pub fn make_owned(&mut self) -> bool { let required_cloning = !self.is_owned(); *self = self.to_owned(); required_cloning } /// Converts to a `UdSocketPath<'static>` which stores the path as an owned `CString`, cloning if necessary. // TODO implement ToOwned instead of Clone in 2.0.0 pub fn to_owned(&self) -> UdSocketPath<'static> { match self { Self::File(f) => UdSocketPath::File(Cow::Owned(f.as_ref().to_owned())), #[cfg(uds_linux_namespace)] Self::Namespaced(n) => UdSocketPath::Namespaced(Cow::Owned(n.as_ref().to_owned())), Self::Unnamed => UdSocketPath::Unnamed, } } /// Borrows into another `UdSocketPath<'_>` instance. If borrowed here, reborrows; if owned here, returns a fresh borrow. pub fn borrow(&self) -> UdSocketPath<'_> { match self { UdSocketPath::File(f) => UdSocketPath::File(Cow::Borrowed(f.as_ref())), #[cfg(uds_linux_namespace)] UdSocketPath::Namespaced(n) => UdSocketPath::Namespaced(Cow::Borrowed(n.as_ref())), UdSocketPath::Unnamed => UdSocketPath::Unnamed, } } /// Returns a mutable reference to the underlying `CString`, cloning the borrowed path if it wasn't owned before. pub fn get_cstring_mut(&mut self) -> &mut CString { self.make_owned(); self.try_get_cstring_mut().unwrap_or_else(|| unsafe { // SAFETY: the call to make_owned ensured that there is a CString std::hint::unreachable_unchecked() }) } /// Returns a mutable reference to the underlying `CString` if it's available as owned, otherwise returns `None`. pub fn try_get_cstring_mut(&mut self) -> Option<&mut CString> { let cow = match self { Self::File(cow) => cow, #[cfg(uds_linux_namespace)] Self::Namespaced(cow) => cow, Self::Unnamed => return None, }; match cow { Cow::Owned(cstring) => Some(cstring), Cow::Borrowed(..) => None, } } /// Returns `true` if the path to the socket is stored as an owned `CString`, i.e. if `into_cstring` doesn't require cloning the path; `false` otherwise. // Cannot use `matches!` due to #[cfg(...)] #[allow(clippy::match_like_matches_macro)] pub const fn is_owned(&self) -> bool { match self { Self::File(Cow::Borrowed(..)) => true, #[cfg(uds_linux_namespace)] Self::Namespaced(Cow::Borrowed(..)) => true, _ => false, } } #[cfg(unix)] pub(super) fn write_sockaddr_un_to_self(&mut self, addr: &sockaddr_un, addrlen: usize) { let sun_path_length = (addrlen as isize) - (size_of_val(&addr.sun_family) as isize); let sun_path_length = match usize::try_from(sun_path_length) { Ok(val) => val, Err(..) => { *self = Self::Unnamed; return; } }; if let Some(cstring) = self.try_get_cstring_mut() { let cstring = replace(cstring, empty_cstring()); let mut vec = cstring.into_bytes_with_nul(); let mut _namespaced = false; unsafe { #[cfg(uds_linux_namespace)] let (src_ptr, path_length) = if addr.sun_path[0] == 0 { _namespaced = true; ( addr.sun_path.as_ptr().offset(1) as *const u8, sun_path_length - 1, ) } else { (addr.sun_path.as_ptr() as *const u8, sun_path_length) }; #[cfg(not(uds_linux_namespace))] let (src_ptr, path_length) = { (addr.sun_path.as_ptr() as *const u8, sun_path_length) }; // Fill the space for the name and the nul terminator with nuls vec.resize(path_length, 0); ptr::copy_nonoverlapping(src_ptr, vec.as_mut_ptr(), path_length); }; // If the system added a nul byte as part of the length, remove the one we added ourselves. if vec.last() == Some(&0) && vec[vec.len() - 2] == 0 { vec.pop(); } let new_cstring = CString::new(vec).unwrap_or_else(eunreachable); #[cfg(uds_linux_namespace)] let path_to_write = if _namespaced { UdSocketPath::Namespaced(Cow::Owned(new_cstring)) } else { UdSocketPath::File(Cow::Owned(new_cstring)) }; #[cfg(not(uds_linux_namespace))] let path_to_write = UdSocketPath::File(Cow::Owned(new_cstring)); *self = path_to_write; // Implicitly drops the empty CString we wrote in the beginning } else { let mut _namespaced = false; let mut vec = unsafe { let (src_ptr, path_length) = if addr.sun_path[0] == 0 { ( addr.sun_path.as_ptr().offset(1) as *const u8, sun_path_length - 1, ) } else { (addr.sun_path.as_ptr() as *const u8, sun_path_length) }; let mut vec = vec![0; path_length]; ptr::copy_nonoverlapping(src_ptr, vec.as_mut_ptr(), path_length); vec }; // If the system added a nul byte as part of the length, remove it. if vec.last() == Some(&0) { vec.pop(); } let cstring = CString::new(vec).unwrap_or_else(eunreachable); #[cfg(uds_linux_namespace)] let path_to_write = if _namespaced { UdSocketPath::Namespaced(Cow::Owned(cstring)) } else { UdSocketPath::File(Cow::Owned(cstring)) }; #[cfg(not(uds_linux_namespace))] let path_to_write = UdSocketPath::File(Cow::Owned(cstring)); *self = path_to_write; } } /// Returns `addr_len` to pass to `bind`/`connect`. #[cfg(unix)] pub(super) fn write_self_to_sockaddr_un(&self, addr: &mut sockaddr_un) -> io::Result<()> { let is_namespaced; let len_of_self = self.as_cstr().to_bytes_with_nul().len(); match self { UdSocketPath::File(..) => { is_namespaced = false; if len_of_self > MAX_UDSOCKET_PATH_LEN { return Err(io::Error::new( io::ErrorKind::InvalidInput, format!( "socket path should not be longer than {} bytes", MAX_UDSOCKET_PATH_LEN ), )); } } #[cfg(uds_linux_namespace)] UdSocketPath::Namespaced(..) => { is_namespaced = true; if len_of_self > (MAX_UDSOCKET_PATH_LEN - 1) { return Err(io::Error::new( io::ErrorKind::InvalidInput, format!( "namespaced socket name should not be longer than {} bytes", MAX_UDSOCKET_PATH_LEN - 1 ), )); } } UdSocketPath::Unnamed => { return Err(io::Error::new( io::ErrorKind::InvalidInput, "must provide a name for the socket", )) } } unsafe { ptr::copy_nonoverlapping( self.as_cstr().as_ptr(), if is_namespaced { addr.sun_path.as_mut_ptr().offset(1) } else { addr.sun_path.as_mut_ptr() }, len_of_self, ); } Ok(()) } } impl UdSocketPath<'static> { /// Creates a buffer suitable for usage with [`recv_from`] ([`_ancillary`]/[`_vectored`]/[`_ancillary_vectored`]). The capacity is equal to the [`MAX_UDSOCKET_PATH_LEN`] constant (the nul terminator in the `CString` is included). **The contained value is unspecified – results of reading from the buffer should not be relied upon.** /// /// # Example /// ``` /// # #[cfg(unix)] { /// use interprocess::os::unix::udsocket::{UdSocketPath, MAX_UDSOCKET_PATH_LEN}; /// use std::borrow::Cow; /// /// let path_buffer = UdSocketPath::buffer(); /// match path_buffer { /// UdSocketPath::File(cow) => match cow { /// Cow::Owned(cstring) /// => assert_eq!(cstring.into_bytes_with_nul().capacity(), MAX_UDSOCKET_PATH_LEN), /// Cow::Borrowed(..) => unreachable!(), /// } /// _ => unreachable!(), /// } /// # } /// ``` /// /// [`recv_from`]: struct.UdSocket.html#method.recv_from " " /// [`_ancillary`]: struct.UdSocket.html#method.recv_from " " /// [`_vectored`]: struct.UdSocket.html#method.recv_from_vectored " " /// [`_ancillary_vectored`]: struct.UdSocket.html#method.recv_from_ancillary_vectored " " /// [`MAX_UDSOCKET_PATH_LEN`]: constant.MAX_UDSOCKET_PATH_LEN.html " " pub fn buffer() -> Self { Self::File(Cow::Owned( CString::new(vec![0x2F; MAX_UDSOCKET_PATH_LEN - 1]) .expect("unexpected nul in newly created Vec, possible heap corruption"), )) } /// Constructs a `UdSocketPath::File` value from a `Vec` of bytes, wrapping `CString::new`. pub fn file_from_vec(vec: Vec) -> Result { Ok(Self::File(Cow::Owned(CString::new(vec)?))) } /// Constructs a `UdSocketPath::Namespaced` value from a `Vec` of bytes, wrapping `CString::new`. #[cfg(uds_linux_namespace)] #[cfg_attr( // uds_linux_namespace template feature = "doc_cfg", doc(cfg(any(target_os = "linux", target_os = "android"))) )] pub fn namespaced_from_vec(vec: Vec) -> Result { Ok(Self::Namespaced(Cow::Owned(CString::new(vec)?))) } } impl From> for CString { fn from(path: UdSocketPath<'_>) -> Self { path.into_cstring() } } impl AsRef for UdSocketPath<'_> { fn as_ref(&self) -> &CStr { self.as_cstr() } } impl From> for OsString { fn from(path: UdSocketPath<'_>) -> Self { path.into_osstring() } } impl AsRef for UdSocketPath<'_> { fn as_ref(&self) -> &OsStr { self.as_osstr() } } impl TryFrom> for sockaddr_un { type Error = io::Error; fn try_from(path: UdSocketPath<'_>) -> io::Result { unsafe { let mut addr: sockaddr_un = zeroed(); addr.sun_family = AF_UNIX as _; path.write_self_to_sockaddr_un(&mut addr)?; Ok(addr) } } } /// Trait for types which can be converted to a [path to a Unix domain socket][`UdSocketPath`]. /// /// The difference between this trait and [`TryInto`]`<`[`UdSocketPath`]`>` is that the latter does not constrain the error type to be [`io::Error`] and thus is not compatible with many types from the standard library which are widely expected to be convertible to Unix domain socket paths. Additionally, this makes the special syntax for namespaced sockets possible (see below). /// /// ## `@` syntax for namespaced paths /// On Linux (since it's the only platform which supports [namespaced socket paths]), an extra syntax feature is implemented for string types which don't have file path semantics, i.e. all standard string types except for [`Path`] and [`PathBuf`]. If the first character in a string is `@`, the path is interpreted as a namespaced socket path rather than a normal file path. Read the `UdSocketPath` documentation for more on what that means. There are several ways to opt out of that behavior if you're referring to a socket at a relative path which starts from a `@`: /// - Use [`AsRef`] to convert the string slice type into a [`Path`] which has file path semantics and therefore does not have the `@` syntax enabled, if your string type is [`str`] or [`OsStr`] /// - Prefix the path with `./`, which carries the same meaning from the perspective of the OS but bypasses the `@` check /// - If your string type is [`CStr`] or [`CString`], explicitly construct `UdSocketPath`'s `File` variant with a [`Cow`] wrapping your string value /// /// # Example /// The following example uses the `UdStreamListener::bind` method, but `UdStream::connect` and `UdSocket::bind`/`UdSocket::connect` accept the same argument types too. /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(unix)] { /// use interprocess::os::unix::udsocket::{UdStreamListener, UdSocketPath}; /// use std::{ffi::{CStr, CString}, path::{Path, PathBuf}, borrow::Cow}; /// /// // 1. Use a string literal /// let listener = UdStreamListener::bind("/tmp/example1.sock")?; /// // If we're on Linux, we can also use the abstract socket namespace which exists separately from /// // the filesystem thanks to the special @ sign syntax which works with all string types /// let listener_namespaced = UdStreamListener::bind("@namespaced_socket_1")?; /// /// // 2. Use an owned string /// let listener = UdStreamListener::bind("/tmp/example2.sock".to_string())?; /// // Same story with the namespaced socket here /// let listener_namespaced = UdStreamListener::bind("@namespaced_socket_2")?; /// /// // 3. Use a path slice or an owned path /// let listener_by_path = UdStreamListener::bind(Path::new("/tmp/exmaple3a.sock"))?; /// let listener_by_pathbuf = UdStreamListener::bind(PathBuf::from("/tmp/example3b.sock"))?; /// // The @ syntax doesn't work with Path and PathBuf, since those are explicitly paths at the type /// // level, rather than strings with contextual meaning. Using AsRef to convert an &str slice or /// // an &OsStr slice into a &Path slice is the recommended way to disable the @ syntax. /// /// // 4. Use manual creation /// let cstring = CString::new("/tmp/example4a.sock".to_string().into_bytes())?; /// let path_to_socket = UdSocketPath::File(Cow::Owned(cstring)); /// let listener = UdStreamListener::bind(path_to_socket); /// /// let cstr = CStr::from_bytes_with_nul("/tmp/example4b.sock\0".as_bytes())?; /// let path_to_socket = UdSocketPath::File(Cow::Borrowed(cstr)); /// let listener = UdStreamListener::bind(path_to_socket); /// # } /// # Ok(()) } /// ``` /// /// [`UdSocketPath`]: enum.UdSocketPath.html " " /// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html " " /// [`TryInto`]: https://doc.rust-lang.org/std/convert/trait.TryInto.html " " /// [`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html " " /// [namespaced socket paths]: struct.UdSocketPath.html#namespaced " " /// [`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html " " /// [`PathBuf`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html " " /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html " " /// [`CStr`]: https://doc.rust-lang.org/std/ffi/struct.CStr.html " " /// [`CString`]: https://doc.rust-lang.org/std/ffi/struct.CString.html " " /// [`Cow`]: https://doc.rust-lang.org/std/borrow/enum.Cow.html " " /// [`str`]: https://doc.rust-lang.org/stable/std/primitive.str.html pub trait ToUdSocketPath<'a> { /// Performs the conversion from `self` to a Unix domain socket path. #[allow(clippy::wrong_self_convention)] fn to_socket_path(self) -> io::Result>; } impl<'a> ToUdSocketPath<'a> for UdSocketPath<'a> { /// Accepts explicit `UdSocketPath`s in relevant constructors. fn to_socket_path(self) -> io::Result> { Ok(self) } } impl<'a> ToUdSocketPath<'a> for &'a UdSocketPath<'a> { /// Reborrows an explicit `UdSocketPath` for a smaller lifetime. fn to_socket_path(self) -> io::Result> { Ok(self.borrow()) } } impl<'a> ToUdSocketPath<'a> for &'a CStr { /// Converts a borrowed [`CStr`] to a borrowed `UdSocketPath` with the same lifetime. On platforms which don't support [namespaced socket paths], the variant is always [`File`]; on Linux, which supports namespaced sockets, an extra check for the `@` character is performed. See the trait-level documentation for more. /// /// [`CStr`]: https://doc.rust-lang.org/std/ffi/struct.CStr.html " " /// [`File`]: enum.UdSocketPath.html#file " " /// [namespaced socket paths]: enum.UdSocketPath.html#namespaced " " fn to_socket_path(self) -> io::Result> { // 0x40 is the ASCII code for @, and since UTF-8 is ASCII-compatible, it would work too #[cfg(uds_linux_namespace)] if self.to_bytes().first() == Some(&0x40) { let without_at_sign = &self.to_bytes_with_nul()[1..]; let without_at_sign = unsafe { // SAFETY: it's safe to assume that the second byte comes before the nul // terminator or is that nul terminator itself if the first one is an @ sign CStr::from_bytes_with_nul_unchecked(without_at_sign) }; // Use early return to simplify the conditional inclusion for the @ syntax check. return Ok(UdSocketPath::Namespaced(Cow::Borrowed(without_at_sign))); } Ok(UdSocketPath::File(Cow::Borrowed(self))) } } impl ToUdSocketPath<'static> for CString { /// Converts an owned [`CString`] to a borrowed `UdSocketPath` with the same lifetime. On platforms which don't support [namespaced socket paths], the variant is always [`File`]; on Linux, which supports namespaced sockets, an extra check for the `@` character is performed. See the trait-level documentation for more. /// /// [`CString`]: https://doc.rust-lang.org/std/ffi/struct.CString.html " " /// [`File`]: enum.UdSocketPath.html#file " " /// [namespaced socket paths]: enum.UdSocketPath.html#namespaced " " fn to_socket_path(self) -> io::Result> { #[cfg(uds_linux_namespace)] if self.as_bytes().first() == Some(&0x40) { let without_at_sign = { let mut without_at_sign = self.into_bytes(); without_at_sign.remove(0); unsafe { // SAFETY: see CStr impl for why this is safe in both impls CString::from_vec_unchecked(without_at_sign) } }; // As in the CStr impl, we're using an early return to simplify conditional compilation return Ok(UdSocketPath::Namespaced(Cow::Owned(without_at_sign))); } Ok(UdSocketPath::File(Cow::Owned(self))) } } impl<'a> ToUdSocketPath<'a> for &'a OsStr { /// Converts a borrowed [`OsStr`] to a borrowed `UdSocketPath` with the same lifetime. On platforms which don't support [namespaced socket paths], the variant is always [`File`]; on Linux, which supports namespaced sockets, an extra check for the `@` character is performed. See the trait-level documentation for more. /// /// If the provided string is not nul-terminated, a nul terminator is automatically added by copying the string into owned storage and adding a nul byte on its end. /// /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html " " /// [`File`]: enum.UdSocketPath.html#file " " /// [namespaced socket paths]: enum.UdSocketPath.html#namespaced " " fn to_socket_path(self) -> io::Result> { #[cfg(uds_linux_namespace)] if self.as_bytes().first() == Some(&0x40) { if self.as_bytes().last() != Some(&0) { let mut owned = self.to_owned().into_vec(); owned.remove(0); return Ok(UdSocketPath::Namespaced(Cow::Owned(CString::new(owned)?))); } else { let without_at_sign = self.as_bytes().split_at(1).0; let cstr = CStr::from_bytes_with_nul(without_at_sign) .map_err(|x| io::Error::new(io::ErrorKind::InvalidInput, x))?; return Ok(UdSocketPath::Namespaced(Cow::Borrowed(cstr))); } } if self.as_bytes().last() != Some(&0) { Ok(UdSocketPath::File(Cow::Owned(CString::new( self.to_owned().into_vec(), )?))) } else { let cstr = CStr::from_bytes_with_nul(self.as_bytes()) .map_err(|x| io::Error::new(io::ErrorKind::InvalidInput, x))?; Ok(UdSocketPath::File(Cow::Borrowed(cstr))) } } } impl ToUdSocketPath<'static> for OsString { /// Converts a borrowed [`OsString`] to an owned `UdSocketPath`. On platforms which don't support [namespaced socket paths], the variant is always [`File`]; on Linux, which supports namespaced sockets, an extra check for the `@` character is performed. See the trait-level documentation for more. /// /// If the provided string is not nul-terminated, a nul terminator is automatically added by copying the string into owned storage and adding a nul byte on its end. /// /// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html " " /// [`File`]: enum.UdSocketPath.html#file " " /// [namespaced socket paths]: enum.UdSocketPath.html#namespaced " " fn to_socket_path(self) -> io::Result> { #[cfg(uds_linux_namespace)] if self.as_os_str().as_bytes().first() == Some(&0x40) { let mut without_at_sign = self.into_vec(); without_at_sign.remove(0); return Ok(UdSocketPath::Namespaced(Cow::Owned(CString::new( without_at_sign, )?))); } Ok(UdSocketPath::File(Cow::Owned(CString::new( self.into_vec(), )?))) } } impl<'a> ToUdSocketPath<'a> for &'a Path { /// Converts a borrowed [`Path`] to a borrowed [`UdSocketPath::File`] with the same lifetime. /// /// If the provided string is not nul-terminated, a nul terminator is automatically added by copying the string into owned storage and adding a nul byte on its end. /// /// [`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html " " /// [`UdSocketPath::File`]: struct.UdSocketPath.html#file " " fn to_socket_path(self) -> io::Result> { if self.as_os_str().as_bytes().last() != Some(&0) { let osstring = self.to_owned().into_os_string().into_vec(); let cstring = CString::new(osstring)?; Ok(UdSocketPath::File(Cow::Owned(cstring))) } else { let cstr = CStr::from_bytes_with_nul(self.as_os_str().as_bytes()) .map_err(|x| io::Error::new(io::ErrorKind::InvalidInput, x))?; Ok(UdSocketPath::File(Cow::Borrowed(cstr))) } } } impl ToUdSocketPath<'static> for PathBuf { /// Converts an owned [`PathBuf`] to an owned [`UdSocketPath::File`]. /// /// If the provided string is not nul-terminated, a nul terminator is automatically added by copying the string into owned storage and adding a nul byte on its end. /// /// [`PathBuf`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html " " /// [`UdSocketPath::File`]: struct.UdSocketPath.html#file " " fn to_socket_path(self) -> io::Result> { let cstring = CString::new(self.into_os_string().into_vec())?; Ok(UdSocketPath::File(Cow::Owned(cstring))) } } impl<'a> ToUdSocketPath<'a> for &'a str { /// Converts a borrowed [`str`] to a borrowed `UdSocketPath` with the same lifetime. On platforms which don't support [namespaced socket paths], the variant is always [`File`]; on Linux, which supports namespaced sockets, an extra check for the `@` character is performed. See the trait-level documentation for more. /// /// If the provided string is not nul-terminated, a nul terminator is automatically added by copying the string into owned storage and adding a nul byte on its end. This is done to support normal string literals, since adding `\0` at the end of every single socket name string is tedious and unappealing. /// /// [`str`]: https://doc.rust-lang.org/std/primitive.str.html " " /// [`File`]: enum.UdSocketPath.html#file " " /// [namespaced socket paths]: enum.UdSocketPath.html#namespaced " " fn to_socket_path(self) -> io::Result> { // Use chars().next() instead of raw indexing to account for UTF-8 with BOM #[cfg(uds_linux_namespace)] if self.starts_with('@') { if !self.ends_with('\0') { let mut owned = self.to_owned(); owned.remove(0); return Ok(UdSocketPath::Namespaced(Cow::Owned(CString::new(owned)?))); } else { let without_at_sign = self.split_at(1).0; let cstr = CStr::from_bytes_with_nul(without_at_sign.as_bytes()) .map_err(|x| io::Error::new(io::ErrorKind::InvalidInput, x))?; return Ok(UdSocketPath::Namespaced(Cow::Borrowed(cstr))); } } if !self.ends_with('\0') { Ok(UdSocketPath::File(Cow::Owned(CString::new( self.to_owned(), )?))) } else { let cstr = CStr::from_bytes_with_nul(self.as_bytes()) .map_err(|x| io::Error::new(io::ErrorKind::InvalidInput, x))?; Ok(UdSocketPath::File(Cow::Borrowed(cstr))) } } } impl ToUdSocketPath<'static> for String { /// Converts an owned [`String`] to an owned `UdSocketPath`. On platforms which don't support [namespaced socket paths], the variant is always [`File`]; on Linux, which supports namespaced sockets, an extra check for the `@` character is performed. See the trait-level documentation for more. /// /// If the provided string is not nul-terminated, a nul terminator is automatically added by copying the string into owned storage and adding a nul byte on its end. /// /// [`String`]: https://doc.rust-lang.org/std/string/struct.String.html " " /// [`File`]: enum.UdSocketPath.html#file " " /// [namespaced socket paths]: enum.UdSocketPath.html#namespaced " " fn to_socket_path(self) -> io::Result> { #[cfg(uds_linux_namespace)] if self.starts_with('@') { let mut without_at_sign = self; without_at_sign.remove(0); return Ok(UdSocketPath::Namespaced(Cow::Owned(CString::new( without_at_sign.into_bytes(), )?))); } Ok(UdSocketPath::File(Cow::Owned(CString::new( self.into_bytes(), )?))) } } interprocess-1.2.1/src/os/unix/udsocket/path_drop_guard.rs000064400000000000000000000012040072674642500221160ustar 00000000000000use super::{imports::OsStrExt, UdSocketPath}; use std::{ffi::OsStr, fs::remove_file, ops::Drop}; #[derive(Debug)] pub struct PathDropGuard<'a> { pub path: UdSocketPath<'a>, pub enabled: bool, } impl PathDropGuard<'static> { pub fn dummy() -> Self { Self { path: UdSocketPath::Unnamed, enabled: false, } } } impl<'a> Drop for PathDropGuard<'a> { fn drop(&mut self) { if self.enabled { if let UdSocketPath::File(f) = &self.path { let path = OsStr::from_bytes(f.to_bytes()); let _ = remove_file(path); } } } } interprocess-1.2.1/src/os/unix/udsocket/socket.rs000064400000000000000000000514460072674642500202610ustar 00000000000000#[cfg(uds_supported)] use super::c_wrappers; use super::{ imports::*, util::{check_ancillary_unsound, fill_out_msghdr_r, mk_msghdr_r, mk_msghdr_w}, AncillaryData, AncillaryDataBuf, EncodedAncillaryData, PathDropGuard, ToUdSocketPath, UdSocketPath, }; #[cfg(any(doc, target_os = "linux"))] use crate::{ReliableReadMsg, Sealed}; use std::{ fmt::{self, Debug, Formatter}, io::{self, IoSlice, IoSliceMut}, iter, mem::{size_of_val, zeroed}, }; use to_method::To; /// A datagram socket in the Unix domain. /// /// All such sockets have the `SOCK_DGRAM` socket type; in other words, this is the Unix domain version of a UDP socket. pub struct UdSocket { // TODO make this not 'static _drop_guard: PathDropGuard<'static>, fd: FdOps, } impl UdSocket { /// Creates a new socket that can be referred to by the specified path. /// /// If the socket path exceeds the [maximum socket path length] (which includes the first 0 byte when using the [socket namespace]), an error is returned. Errors can also be produced for different reasons, i.e. errors should always be handled regardless of whether the path is known to be short enough or not. /// /// After the socket is dropped, the socket file will be left over. Use [`bind_with_drop_guard()`](Self::bind_with_drop_guard) to mitigate this automatically, even during panics (if unwinding is enabled). /// /// # Example /// See [`ToUdSocketPath`] for an example of using various string types to specify socket paths. /// /// # System calls /// - `socket` /// - `bind` /// /// [maximum socket path length]: const.MAX_UDSOCKET_PATH_LEN.html " " /// [socket namespace]: enum.UdSocketPath.html#namespaced " " /// [`ToUdSocketPath`]: trait.ToUdSocketPath.html " " pub fn bind<'a>(path: impl ToUdSocketPath<'a>) -> io::Result { Self::_bind(path.to_socket_path()?, false) } /// Creates a new socket that can be referred to by the specified path, remembers the address, and installs a drop guard that will delete the socket file once the socket is dropped. /// /// See the documentation of [`bind()`](Self::bind). pub fn bind_with_drop_guard<'a>(path: impl ToUdSocketPath<'a>) -> io::Result { Self::_bind(path.to_socket_path()?, true) } fn _bind(path: UdSocketPath<'_>, keep_drop_guard: bool) -> io::Result { let addr = path.borrow().try_to::()?; let fd = c_wrappers::create_uds(SOCK_DGRAM, false)?; unsafe { // SAFETY: addr is well-constructed c_wrappers::bind(&fd, &addr)?; } c_wrappers::set_passcred(&fd, true)?; let dg = if keep_drop_guard && matches!(path, UdSocketPath::File(..)) { PathDropGuard { path: path.to_owned(), enabled: true, } } else { PathDropGuard::dummy() }; Ok(Self { fd, _drop_guard: dg, }) } /// Selects the Unix domain socket to send packets to. You can also just use [`.send_to()`](Self::send_to) instead, but supplying the address to the kernel once is more efficient. /// /// # Example /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(unix)] { /// use interprocess::os::unix::udsocket::UdSocket; /// /// let conn = UdSocket::bind("/tmp/side_a.sock")?; /// conn.set_destination("/tmp/side_b.sock")?; /// // Communicate with datagrams here! /// # } /// # Ok(()) } /// ``` /// See [`ToUdSocketPath`] for an example of using various string types to specify socket paths. /// /// # System calls /// - `connect` pub fn set_destination<'a>(&self, path: impl ToUdSocketPath<'a>) -> io::Result<()> { let path = path.to_socket_path()?; self._set_destination(&path) } fn _set_destination(&self, path: &UdSocketPath<'_>) -> io::Result<()> { let addr = path.borrow().try_to::()?; unsafe { // SAFETY: addr is well-constructed c_wrappers::connect(&self.fd, &addr)?; } Ok(()) } /// Incorrect API; do not use. // TODO banish #[deprecated = "\ creates unusable socket that is not bound to any address, use `.set_destination()` instead"] pub fn connect<'a>(path: impl ToUdSocketPath<'a>) -> io::Result { let path = path.to_socket_path()?; Self::_connect(&path, false) } fn _connect(path: &UdSocketPath<'_>, keep_drop_guard: bool) -> io::Result { let fd = c_wrappers::create_uds(SOCK_DGRAM, false)?; c_wrappers::set_passcred(&fd, true)?; let dg = if keep_drop_guard && matches!(path, UdSocketPath::File(..)) { PathDropGuard { path: path.to_owned(), enabled: true, } } else { PathDropGuard::dummy() }; let socket = Self { fd, _drop_guard: dg, }; socket._set_destination(path)?; Ok(socket) } // TODO banish fn add_fake_trunc_flag(x: usize) -> (usize, bool) { (x, false) } /// Receives a single datagram from the socket, returning the size of the received datagram. /// /// *Note: there is an additional meaningless boolean return value which is always `false`. It used to signify whether the datagram was truncated or not, but the functionality was implemented incorrectly and only on Linux, leading to its removal in version 1.2.0. In the next breaking release, 2.0.0, the return value will be changed to just `io::Result`.* /// /// # System calls /// - `read` pub fn recv(&self, buf: &mut [u8]) -> io::Result<(usize, bool)> { self.fd.read(buf).map(Self::add_fake_trunc_flag) } /// Receives a single datagram from the socket, making use of [scatter input] and returning the size of the received datagram. /// /// *Note: there is an additional meaningless boolean return value which is always `false`. It used to signify whether the datagram was truncated or not, but the functionality was implemented incorrectly and only on Linux, leading to its removal in version 1.2.0. In the next breaking release, 2.0.0, the return value will be changed to just `io::Result`.* /// /// # System calls /// - `readv` /// /// [scatter input]: https://en.wikipedia.org/wiki/Vectored_I/O " " pub fn recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<(usize, bool)> { self.fd.read_vectored(bufs).map(Self::add_fake_trunc_flag) } /// Receives a single datagram and ancillary data from the socket. The return value is in the following order: /// - How many bytes of the datagram were received /// - *Deprecated `bool` field (always `false`), see note* /// - How many bytes of ancillary data were received /// - *Another deprecated `bool` field (always `false`), see note* /// /// *Note: there are two additional meaningless boolean return values which are always `false`. They used to signify whether the datagram, and the ancillary data respectively, were truncated or not, but the functionality was implemented incorrectly and only on Linux, leading to its removal in version 1.2.0. In the next breaking release, 2.0.0, the return value will be changed to just `io::Result`.* /// /// # System calls /// - `recvmsg` /// /// [scatter input]: https://en.wikipedia.org/wiki/Vectored_I/O " " pub fn recv_ancillary<'a: 'b, 'b>( &self, buf: &mut [u8], abuf: &'b mut AncillaryDataBuf<'a>, ) -> io::Result<(usize, bool, usize, bool)> { check_ancillary_unsound()?; self.recv_ancillary_vectored(&mut [IoSliceMut::new(buf)], abuf) } /// Receives a single datagram and ancillary data from the socket, making use of [scatter input]. The return value is in the following order: /// - How many bytes of the datagram were received /// - *Deprecated `bool` field (always `false`), see note* /// - How many bytes of ancillary data were received /// - *Another deprecated `bool` field (always `false`), see note* /// /// *Note: there are two additional meaningless boolean return values which are always `false`. They used to signify whether the datagram, and the ancillary data respectively, were truncated or not, but the functionality was implemented incorrectly and only on Linux, leading to its removal in version 1.2.0. In the next breaking release, 2.0.0, the return value will be changed to just `io::Result<(usize, usize)>`.* /// /// # System calls /// - `recvmsg` /// /// [scatter input]: https://en.wikipedia.org/wiki/Vectored_I/O " " #[allow(clippy::useless_conversion)] pub fn recv_ancillary_vectored<'a: 'b, 'b>( &self, bufs: &mut [IoSliceMut<'_>], abuf: &'b mut AncillaryDataBuf<'a>, ) -> io::Result<(usize, bool, usize, bool)> { check_ancillary_unsound()?; let mut hdr = mk_msghdr_r(bufs, abuf.as_mut())?; let (success, bytes_read) = unsafe { let result = libc::recvmsg(self.as_raw_fd(), &mut hdr as *mut _, 0); (result != -1, result as usize) }; if success { Ok((bytes_read, false, hdr.msg_controllen as _, false)) } else { Err(io::Error::last_os_error()) } } /// Receives a single datagram and the source address from the socket, returning how much of the buffer was filled out and whether a part of the datagram was discarded because the buffer was too small. /// /// # System calls /// - `recvmsg` /// - Future versions of `interprocess` may use `recvfrom` instead; for now, this method is a wrapper around [`recv_from_vectored`]. /// /// [`recv_from_vectored`]: #method.recv_from_vectored " " // TODO use recvfrom pub fn recv_from<'a: 'b, 'b>( &self, buf: &mut [u8], addr_buf: &'b mut UdSocketPath<'a>, ) -> io::Result<(usize, bool)> { self.recv_from_vectored(&mut [IoSliceMut::new(buf)], addr_buf) } /// Receives a single datagram and the source address from the socket, making use of [scatter input] and returning how much of the buffer was filled out and whether a part of the datagram was discarded because the buffer was too small. /// /// # System calls /// - `recvmsg` /// /// [scatter input]: https://en.wikipedia.org/wiki/Vectored_I/O " " pub fn recv_from_vectored<'a: 'b, 'b>( &self, bufs: &mut [IoSliceMut<'_>], addr_buf: &'b mut UdSocketPath<'a>, ) -> io::Result<(usize, bool)> { self.recv_from_ancillary_vectored(bufs, &mut AncillaryDataBuf::Owned(Vec::new()), addr_buf) .map(|x| (x.0, x.1)) } /// Receives a single datagram, ancillary data and the source address from the socket. The return value is in the following order: /// - How many bytes of the datagram were received /// - *Deprecated `bool` field (always `false`), see note* /// - How many bytes of ancillary data were received /// - *Another deprecated `bool` field (always `false`), see note* /// /// *Note: there are two additional meaningless boolean return values which are always `false`. They used to signify whether the datagram, and the ancillary data respectively, were truncated or not, but the functionality was implemented incorrectly and only on Linux, leading to its removal in version 1.2.0. In the next breaking release, 2.0.0, the return value will be changed to just `io::Result<(usize, usize)>`.* /// /// # System calls /// - `recvmsg` pub fn recv_from_ancillary<'a: 'b, 'b, 'c: 'd, 'd>( &self, buf: &mut [u8], abuf: &'b mut AncillaryDataBuf<'a>, addr_buf: &'d mut UdSocketPath<'c>, ) -> io::Result<(usize, bool, usize, bool)> { if !abuf.as_ref().is_empty() { // Branching required because recv_from_vectored always uses // recvmsg (no non-ancillary counterpart) check_ancillary_unsound()?; } self.recv_from_ancillary_vectored(&mut [IoSliceMut::new(buf)], abuf, addr_buf) } /// Receives a single datagram, ancillary data and the source address from the socket, making use of [scatter input]. The return value is in the following order: /// - How many bytes of the datagram were received /// - Whether a part of the datagram was discarded because the buffer was too small /// - How many bytes of ancillary data were received /// - Whether some ancillary data was discarded because the buffer was too small /// /// # System calls /// - `recvmsg` /// /// [scatter input]: https://en.wikipedia.org/wiki/Vectored_I/O " " pub fn recv_from_ancillary_vectored<'a: 'b, 'b, 'c: 'd, 'd>( &self, bufs: &mut [IoSliceMut<'_>], abuf: &'b mut AncillaryDataBuf<'a>, addr_buf: &'d mut UdSocketPath<'c>, ) -> io::Result<(usize, bool, usize, bool)> { check_ancillary_unsound()?; // SAFETY: msghdr consists of integers and pointers, all of which are nullable let mut hdr = unsafe { zeroed::() }; // Same goes for sockaddr_un let mut addr_buf_staging = unsafe { zeroed::() }; // It's a void* so the doublecast is mandatory hdr.msg_name = &mut addr_buf_staging as *mut _ as *mut _; hdr.msg_namelen = size_of_val(&addr_buf_staging).try_to::().unwrap(); fill_out_msghdr_r(&mut hdr, bufs, abuf.as_mut())?; let (success, bytes_read) = unsafe { let result = libc::recvmsg(self.as_raw_fd(), &mut hdr as *mut _, 0); (result != -1, result as usize) }; let path_length = hdr.msg_namelen as usize; if success { addr_buf.write_sockaddr_un_to_self(&addr_buf_staging, path_length); Ok((bytes_read, false, hdr.msg_controllen as _, false)) } else { Err(io::Error::last_os_error()) } } /// Returns the size of the next datagram available on the socket without discarding it. /// /// This method is only available on Linux since kernel version 2.2. On lower kernel versions, it will fail; on other platforms, it's absent and thus any usage of it will result in a compile-time error. /// /// # System calls /// - `recv` #[cfg(any(doc, target_os = "linux"))] #[cfg_attr(feature = "doc_cfg", doc(cfg(target_os = "linux")))] pub fn peek_msg_size(&self) -> io::Result { let mut buffer = [0_u8; 0]; let (success, size) = unsafe { let size = libc::recv( self.as_raw_fd(), buffer.as_mut_ptr() as *mut _, buffer.len(), libc::MSG_TRUNC | libc::MSG_PEEK, ); (size != -1, size as usize) }; if success { Ok(size) } else { Err(io::Error::last_os_error()) } } /// Sends a datagram into the socket. /// /// # System calls /// - `write` pub fn send(&self, buf: &[u8]) -> io::Result { self.fd.write(buf) } /// Sends a datagram into the socket, making use of [gather output] for the main data. /// /// /// # System calls /// - `sendmsg` /// - Future versions of `interprocess` may use `writev` instead; for now, this method is a wrapper around [`send_ancillary_vectored`]. /// /// [gather output]: https://en.wikipedia.org/wiki/Vectored_I/O " " /// [`send_ancillary_vectored`]: #method.send_ancillary_vectored " " pub fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { self.send_ancillary_vectored(bufs, iter::empty()) .map(|x| x.0) } /// Sends a datagram and ancillary data into the socket. /// /// The ancillary data buffer is automatically converted from the supplied value, if possible. For that reason, slices and `Vec`s of `AncillaryData` can be passed directly. /// /// # System calls /// - `sendmsg` pub fn send_ancillary<'a>( &self, buf: &[u8], ancillary_data: impl IntoIterator>, ) -> io::Result<(usize, usize)> { check_ancillary_unsound()?; self.send_ancillary_vectored(&[IoSlice::new(buf)], ancillary_data) } /// Sends a datagram and ancillary data into the socket, making use of [gather output] for the main data. /// /// The ancillary data buffer is automatically converted from the supplied value, if possible. For that reason, slices and `Vec`s of `AncillaryData` can be passed directly. /// /// # System calls /// - `sendmsg` /// /// [gather output]: https://en.wikipedia.org/wiki/Vectored_I/O " " pub fn send_ancillary_vectored<'a>( &self, bufs: &[IoSlice<'_>], ancillary_data: impl IntoIterator>, ) -> io::Result<(usize, usize)> { check_ancillary_unsound()?; let abuf = ancillary_data .into_iter() .collect::>(); let hdr = mk_msghdr_w(bufs, abuf.as_ref())?; let (success, bytes_written) = unsafe { let result = libc::sendmsg(self.as_raw_fd(), &hdr as *const _, 0); (result != -1, result as usize) }; if success { Ok((bytes_written, hdr.msg_controllen as _)) } else { Err(io::Error::last_os_error()) } } /// Enables or disables the nonblocking mode for the socket. By default, it is disabled. /// /// In nonblocking mode, calls to the `recv…` methods and the `Read` trait methods will never wait for at least one message to become available; calls to `send…` methods and the `Write` trait methods will never wait for the other side to remove enough bytes from the buffer for the write operation to be performed. Those operations will instead return a [`WouldBlock`] error immediately, allowing the thread to perform other useful operations in the meantime. /// /// [`accept`]: #method.accept " " /// [`incoming`]: #method.incoming " " /// [`WouldBlock`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.WouldBlock " " pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { c_wrappers::set_nonblocking(&self.fd, nonblocking) } /// Checks whether the socket is currently in nonblocking mode or not. pub fn is_nonblocking(&self) -> io::Result { c_wrappers::get_nonblocking(&self.fd) } /// Fetches the credentials of the other end of the connection without using ancillary data. The returned structure contains the process identifier, user identifier and group identifier of the peer. #[cfg(any(doc, uds_peercred))] #[cfg_attr( // uds_peercred template feature = "doc_cfg", doc(cfg(any( all( target_os = "linux", any( target_env = "gnu", target_env = "musl", target_env = "musleabi", target_env = "musleabihf" ) ), target_os = "emscripten", target_os = "redox", target_os = "haiku" ))) )] pub fn get_peer_credentials(&self) -> io::Result { c_wrappers::get_peer_ucred(&self.fd) } } impl Debug for UdSocket { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("UdSocket") .field("fd", &self.as_raw_fd()) .field("has_drop_guard", &self._drop_guard.enabled) .finish() } } #[cfg(any(doc, target_os = "linux"))] #[cfg_attr(feature = "doc_cfg", doc(cfg(target_os = "linux")))] impl ReliableReadMsg for UdSocket { fn read_msg(&mut self, buf: &mut [u8]) -> io::Result>> { let msg_size = self.peek_msg_size()?; if msg_size > buf.len() { let mut new_buffer = vec![0; msg_size]; let len = self.recv(&mut new_buffer)?.0; new_buffer.truncate(len); Ok(Err(new_buffer)) } else { Ok(Ok(self.recv(buf)?.0)) } } fn try_read_msg(&mut self, buf: &mut [u8]) -> io::Result> { let msg_size = self.peek_msg_size()?; if msg_size > buf.len() { Ok(Err(msg_size)) } else { Ok(Ok(self.recv(buf)?.0)) } } } #[cfg(any(doc, target_os = "linux"))] impl Sealed for UdSocket {} impl AsRawFd for UdSocket { #[cfg(unix)] fn as_raw_fd(&self) -> c_int { self.fd.as_raw_fd() } } impl IntoRawFd for UdSocket { #[cfg(unix)] fn into_raw_fd(self) -> c_int { self.fd.into_raw_fd() } } impl FromRawFd for UdSocket { #[cfg(unix)] unsafe fn from_raw_fd(fd: c_int) -> Self { let fd = unsafe { FdOps::from_raw_fd(fd) }; Self { fd, _drop_guard: PathDropGuard::dummy(), } } } interprocess-1.2.1/src/os/unix/udsocket/stream.rs000064400000000000000000000240000072674642500202460ustar 00000000000000#[cfg(uds_supported)] use super::c_wrappers; use super::{ imports::*, util::{check_ancillary_unsound, mk_msghdr_r, mk_msghdr_w}, AncillaryData, AncillaryDataBuf, EncodedAncillaryData, ToUdSocketPath, UdSocketPath, }; use std::{ fmt::{self, Debug, Formatter}, io::{self, IoSlice, IoSliceMut, Read, Write}, iter, net::Shutdown, }; use to_method::To; /// A Unix domain socket byte stream, obtained either from [`UdStreamListener`](super::UdStreamListener) or by connecting to an existing server. /// /// # Examples /// Basic example: /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(unix)] { /// use interprocess::os::unix::udsocket::UdStream; /// use std::io::prelude::*; /// /// let mut conn = UdStream::connect("/tmp/example1.sock")?; /// conn.write_all(b"Hello from client!")?; /// let mut string_buffer = String::new(); /// conn.read_to_string(&mut string_buffer)?; /// println!("Server answered: {}", string_buffer); /// # } /// # Ok(()) } /// ``` pub struct UdStream { fd: FdOps, } impl UdStream { /// Connects to a Unix domain socket server at the specified path. /// /// See [`ToUdSocketPath`] for an example of using various string types to specify socket paths. /// /// # System calls /// - `socket` /// - `connect` pub fn connect<'a>(path: impl ToUdSocketPath<'a>) -> io::Result { Self::_connect(path.to_socket_path()?, false) } pub(crate) fn connect_nonblocking<'a>(path: impl ToUdSocketPath<'a>) -> io::Result { Self::_connect(path.to_socket_path()?, true) } fn _connect(path: UdSocketPath<'_>, nonblocking: bool) -> io::Result { let addr = path.try_to::()?; let fd = c_wrappers::create_uds(SOCK_STREAM, nonblocking)?; unsafe { // SAFETY: addr is well-constructed c_wrappers::connect(&fd, &addr)?; } c_wrappers::set_passcred(&fd, true)?; Ok(Self { fd }) } /// Receives bytes from the socket stream. /// /// # System calls /// - `read` pub fn recv(&self, buf: &mut [u8]) -> io::Result { self.fd.read(buf) } /// Receives bytes from the socket stream, making use of [scatter input] for the main data. /// /// # System calls /// - `readv` /// /// [scatter input]: https://en.wikipedia.org/wiki/Vectored_I/O " " pub fn recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { self.fd.read_vectored(bufs) } /// Receives both bytes and ancillary data from the socket stream. /// /// The ancillary data buffer is automatically converted from the supplied value, if possible. For that reason, mutable slices of bytes (`u8` values) can be passed directly. /// /// # System calls /// - `recvmsg` pub fn recv_ancillary<'a: 'b, 'b>( &self, buf: &mut [u8], abuf: &'b mut AncillaryDataBuf<'a>, ) -> io::Result<(usize, usize)> { check_ancillary_unsound()?; self.recv_ancillary_vectored(&mut [IoSliceMut::new(buf)], abuf) } /// Receives bytes and ancillary data from the socket stream, making use of [scatter input] for the main data. /// /// The ancillary data buffer is automatically converted from the supplied value, if possible. For that reason, mutable slices of bytes (`u8` values) can be passed directly. /// /// # System calls /// - `recvmsg` /// /// [scatter input]: https://en.wikipedia.org/wiki/Vectored_I/O " " #[allow(clippy::useless_conversion)] pub fn recv_ancillary_vectored<'a: 'b, 'b>( &self, bufs: &mut [IoSliceMut<'_>], abuf: &'b mut AncillaryDataBuf<'a>, ) -> io::Result<(usize, usize)> { check_ancillary_unsound()?; let mut hdr = mk_msghdr_r(bufs, abuf.as_mut())?; let (success, bytes_read) = unsafe { let result = libc::recvmsg(self.as_raw_fd(), &mut hdr as *mut _, 0); (result != -1, result as usize) }; if success { Ok((bytes_read, hdr.msg_controllen as _)) } else { Err(io::Error::last_os_error()) } } /// Sends bytes into the socket stream. /// /// # System calls /// - `write` pub fn send(&self, buf: &[u8]) -> io::Result { self.fd.write(buf) } /// Sends bytes into the socket stream, making use of [gather output] for the main data. /// /// # System calls /// - `senv` /// /// [gather output]: https://en.wikipedia.org/wiki/Vectored_I/O " " pub fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { self.fd.write_vectored(bufs) } /// Sends bytes and ancillary data into the socket stream. /// /// The ancillary data buffer is automatically converted from the supplied value, if possible. For that reason, slices and `Vec`s of `AncillaryData` can be passed directly. /// /// # System calls /// - `sendmsg` pub fn send_ancillary<'a>( &self, buf: &[u8], ancillary_data: impl IntoIterator>, ) -> io::Result<(usize, usize)> { check_ancillary_unsound()?; self.send_ancillary_vectored(&[IoSlice::new(buf)], ancillary_data) } /// Sends bytes and ancillary data into the socket stream, making use of [gather output] for the main data. /// /// The ancillary data buffer is automatically converted from the supplied value, if possible. For that reason, slices and `Vec`s of `AncillaryData` can be passed directly. /// /// # System calls /// - `sendmsg` /// /// [gather output]: https://en.wikipedia.org/wiki/Vectored_I/O " " #[allow(clippy::useless_conversion)] pub fn send_ancillary_vectored<'a>( &self, bufs: &[IoSlice<'_>], ancillary_data: impl IntoIterator>, ) -> io::Result<(usize, usize)> { check_ancillary_unsound()?; let abuf = ancillary_data .into_iter() .collect::>(); let hdr = mk_msghdr_w(bufs, abuf.as_ref())?; let (success, bytes_written) = unsafe { let result = libc::sendmsg(self.as_raw_fd(), &hdr as *const _, 0); (result != -1, result as usize) }; if success { Ok((bytes_written, hdr.msg_controllen as _)) } else { Err(io::Error::last_os_error()) } } /// Shuts down the read, write, or both halves of the stream. See [`Shutdown`]. /// /// Attempting to call this method with the same `how` argument multiple times may return `Ok(())` every time or it may return an error the second time it is called, depending on the platform. You must either avoid using the same value twice or ignore the error entirely. pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { c_wrappers::shutdown(&self.fd, how) } /// Enables or disables the nonblocking mode for the stream. By default, it is disabled. /// /// In nonblocking mode, calls to the `recv…` methods and the `Read` trait methods will never wait for at least one byte of data to become available; calls to `send…` methods and the `Write` trait methods will never wait for the other side to remove enough bytes from the buffer for the write operation to be performed. Those operations will instead return a [`WouldBlock`] error immediately, allowing the thread to perform other useful operations in the meantime. /// /// [`accept`]: #method.accept " " /// [`incoming`]: #method.incoming " " /// [`WouldBlock`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.WouldBlock " " pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { c_wrappers::set_nonblocking(&self.fd, nonblocking) } /// Checks whether the stream is currently in nonblocking mode or not. pub fn is_nonblocking(&self) -> io::Result { c_wrappers::get_nonblocking(&self.fd) } /// Fetches the credentials of the other end of the connection without using ancillary data. The returned structure contains the process identifier, user identifier and group identifier of the peer. #[cfg(any(doc, uds_peercred))] #[cfg_attr( // uds_peercred template feature = "doc_cfg", doc(cfg(any( all( target_os = "linux", any( target_env = "gnu", target_env = "musl", target_env = "musleabi", target_env = "musleabihf" ) ), target_os = "emscripten", target_os = "redox", target_os = "haiku" ))) )] pub fn get_peer_credentials(&self) -> io::Result { c_wrappers::get_peer_ucred(&self.fd) } } impl Read for UdStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.fd.read(buf) } fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { let mut abuf = AncillaryDataBuf::Owned(Vec::new()); self.recv_ancillary_vectored(bufs, &mut abuf).map(|x| x.0) } } impl Write for UdStream { fn write(&mut self, buf: &[u8]) -> io::Result { self.fd.write(buf) } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.send_ancillary_vectored(bufs, iter::empty()) .map(|x| x.0) } fn flush(&mut self) -> io::Result<()> { // You cannot flush a socket Ok(()) } } impl Debug for UdStream { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("UdStream") .field("fd", &self.as_raw_fd()) .finish() } } impl AsRawFd for UdStream { #[cfg(unix)] fn as_raw_fd(&self) -> c_int { self.fd.as_raw_fd() } } impl IntoRawFd for UdStream { #[cfg(unix)] fn into_raw_fd(self) -> c_int { self.fd.into_raw_fd() } } impl FromRawFd for UdStream { #[cfg(unix)] unsafe fn from_raw_fd(fd: c_int) -> Self { Self { fd: FdOps::new(fd) } } } interprocess-1.2.1/src/os/unix/udsocket/tokio/listener.rs000064400000000000000000000041470072674642500217370ustar 00000000000000use crate::os::unix::{ imports::*, udsocket::{ tokio::UdStream, ToUdSocketPath, UdSocketPath, UdStreamListener as SyncUdStreamListener, }, }; use std::{convert::TryFrom, io}; /// A Tokio-based Unix domain byte stream socket server, listening for connections. /// /// All such sockets have the `SOCK_STREAM` socket type; in other words, this is the Unix domain version of a TCP server. /// /// Can be freely converted to and from its Tokio counterpart. /// /// # Examples /// - [Basic server](https://github.com/kotauskas/interprocess/blob/main/examples/tokio_udstream/server.rs) #[derive(Debug)] pub struct UdStreamListener(TokioUdStreamListener); impl UdStreamListener { /// Creates a new listener socket at the specified address. /// /// If the socket path exceeds the [maximum socket path length] (which includes the first 0 byte when using the [socket namespace]), an error is returned. Errors can also be produced for different reasons, i.e. errors should always be handled regardless of whether the path is known to be short enough or not. /// /// # Example /// See [`ToUdSocketPath`]. /// /// # System calls /// - `socket` /// - `bind` /// /// [maximum socket path length]: super::super::MAX_UDSOCKET_PATH_LEN /// [socket namespace]: super::super::UdSocketPath::Namespaced pub fn bind<'a>(path: impl ToUdSocketPath<'a>) -> io::Result { Self::_bind(path.to_socket_path()?) } fn _bind(path: UdSocketPath<'_>) -> io::Result { let listener = SyncUdStreamListener::_bind(path, false, true)?; Self::from_sync(listener) } /// Listens for incoming connections to the socket, asynchronously waiting a client is connected. pub async fn accept(&self) -> io::Result { Ok(self.0.accept().await?.0.into()) } tokio_wrapper_conversion_methods!( sync SyncUdStreamListener, std StdUdStreamListener, tokio TokioUdStreamListener); } tokio_wrapper_trait_impls!( for UdStreamListener, sync SyncUdStreamListener, std StdUdStreamListener, tokio TokioUdStreamListener); interprocess-1.2.1/src/os/unix/udsocket/tokio/mod.rs000064400000000000000000000014470072674642500206710ustar 00000000000000//! Asynchronous Ud-sockets which work with the Tokio runtime and event loop. //! //! The Tokio integration allows the Ud-socket streams and listeners to be notified by the OS kernel whenever they're ready to be read from of written to, instead of spawning threads just to put them in a wait state of blocking on the I/O. //! //! Types from this module will *not* work with other async runtimes, such as `async-std` or `smol`, since the Tokio types' methods will panic whenever they're called outside of a Tokio runtime context. Open an issue if you'd like to see other runtimes supported as well. // contains macros, has to go before the other modules #[macro_use] mod util; mod listener; mod socket; mod stream; pub use {listener::*, socket::*, stream::*}; #[cfg(uds_supported)] use super::c_wrappers; interprocess-1.2.1/src/os/unix/udsocket/tokio/socket.rs000064400000000000000000000164120072674642500214000ustar 00000000000000#[cfg(uds_peercred)] use super::c_wrappers; use { crate::os::unix::{imports::*, udsocket}, std::{ convert::TryFrom, future::Future, io, net::Shutdown, pin::Pin, task::{Context, Poll}, }, udsocket::{ToUdSocketPath, UdSocket as SyncUdSocket, UdSocketPath}, }; /// A Unix domain datagram socket, obtained either from [`UdSocketListener`](super::UdSocketListener) or by connecting to an existing server. /// /// # Examples /// - [Basic packet exchange](https://github.com/kotauskas/interprocess/blob/main/examples/tokio_udsocket/inner.rs) #[derive(Debug)] pub struct UdSocket(TokioUdSocket); impl UdSocket { /// Creates an unnamed datagram socket. pub fn unbound() -> io::Result { let socket = TokioUdSocket::unbound()?; Ok(Self(socket)) } /// Creates a named datagram socket assigned to the specified path. This will be the "home" of this socket. Then, packets from somewhere else directed to this socket with [`.send_to()`] or [`.connect()`](Self::connect) will go here. /// /// See [`ToUdSocketPath`] for an example of using various string types to specify socket paths. pub fn bind<'a>(path: impl ToUdSocketPath<'a>) -> io::Result { Self::_bind(path.to_socket_path()?) } fn _bind(path: UdSocketPath<'_>) -> io::Result { let socket = TokioUdSocket::bind(path.as_osstr())?; Ok(Self(socket)) } /// Selects the Unix domain socket to send packets to. You can also just use [`.send_to()`](Self::send_to) instead, but supplying the address to the kernel once is more efficient. /// /// See [`ToUdSocketPath`] for an example of using various string types to specify socket paths. pub fn set_destination<'a>(&self, path: impl ToUdSocketPath<'a>) -> io::Result<()> { self._set_destination(path.to_socket_path()?) } fn _set_destination(&self, path: UdSocketPath<'_>) -> io::Result<()> { self.0.connect(path.as_osstr()) } /// Shuts down the read, write, or both halves of the socket. See [`Shutdown`]. /// /// Attempting to call this method with the same `how` argument multiple times may return `Ok(())` every time or it may return an error the second time it is called, depending on the platform. You must either avoid using the same value twice or ignore the error entirely. pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { self.0.shutdown(how) } /// Receives a single datagram from the socket, advancing the `ReadBuf` cursor by the datagram length. /// /// Uses Tokio's [`ReadBuf`] interface. See `.recv_stdbuf()` for a `&mut [u8]` version. pub async fn recv(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> { // Tokio's .recv() uses &mut [u8] instead of &mut ReadBuf<'_> for some // reason, this works around that struct WrapperFuture<'a, 'b, 'c>(&'a UdSocket, &'b mut ReadBuf<'c>); impl Future for WrapperFuture<'_, '_, '_> { type Output = io::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.0 .0.poll_recv(cx, self.1) } } WrapperFuture(self, buf).await } /// Receives a single datagram from the socket, advancing the `ReadBuf` cursor by the datagram length. /// /// Uses an `std`-like `&mut [u8]` interface. See `.recv()` for a version which uses Tokio's [`ReadBuf`] instead. pub async fn recv_stdbuf(&self, buf: &mut [u8]) -> io::Result { self.0.recv(buf).await } /// Asynchronously waits until readable data arrives to the socket. /// /// May finish spuriously – *do not* perform a blocking read when this future finishes and *do* handle a [`WouldBlock`](io::ErrorKind::WouldBlock) or [`Poll::Pending`]. pub async fn recv_ready(&self) -> io::Result<()> { self.0.readable().await } /// Sends a single datagram into the socket, returning how many bytes were actually sent. pub async fn send(&self, buf: &[u8]) -> io::Result { self.0.send(buf).await } /// Sends a single datagram to the given address, returning how many bytes were actually sent. pub async fn send_to(&self, buf: &[u8], path: impl ToUdSocketPath<'_>) -> io::Result { let path = path.to_socket_path()?; self._send_to(buf, &path).await } async fn _send_to(&self, buf: &[u8], path: &UdSocketPath<'_>) -> io::Result { self.0.send_to(buf, path.as_osstr()).await } /// Asynchronously waits until the socket becomes writable due to the other side freeing up space in its OS receive buffer. /// /// May finish spuriously – *do not* perform a blocking write when this future finishes and *do* handle a [`WouldBlock`](io::ErrorKind::WouldBlock) or [`Poll::Pending`]. pub async fn send_ready(&self) -> io::Result<()> { self.0.writable().await } /// Raw polling interface for receiving datagrams. You probably want `.recv()` instead. pub fn poll_recv(&self, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll> { self.0.poll_recv(cx, buf) } /// Raw polling interface for receiving datagrams with an `std`-like receive buffer. You probably want `.recv_stdbuf()` instead. pub fn poll_recv_stdbuf(&self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { let mut readbuf = ReadBuf::new(buf); self.0.poll_recv(cx, &mut readbuf) } /// Raw polling interface for sending datagrams. You probably want `.send()` instead. pub fn poll_send(&self, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { self.0.poll_send(cx, buf) } /// Raw polling interface for sending datagrams. You probably want `.send_to()` instead. pub fn poll_send_to<'a>( &self, cx: &mut Context<'_>, buf: &[u8], path: impl ToUdSocketPath<'a>, ) -> Poll> { let path = path.to_socket_path()?; self._poll_send_to(cx, buf, &path) } fn _poll_send_to( &self, cx: &mut Context<'_>, buf: &[u8], path: &UdSocketPath<'_>, ) -> Poll> { self.0.poll_send_to(cx, buf, path.as_osstr()) } /// Fetches the credentials of the other end of the connection without using ancillary data. The returned structure contains the process identifier, user identifier and group identifier of the peer. #[cfg(any(doc, uds_peercred))] #[cfg_attr( // uds_peercred template feature = "doc_cfg", doc(cfg(any( all( target_os = "linux", any( target_env = "gnu", target_env = "musl", target_env = "musleabi", target_env = "musleabihf" ) ), target_os = "emscripten", target_os = "redox", target_os = "haiku" ))) )] pub fn get_peer_credentials(&self) -> io::Result { c_wrappers::get_peer_ucred(self.as_raw_fd().as_ref()) } tokio_wrapper_conversion_methods!( sync SyncUdSocket, std StdUdSocket, tokio TokioUdSocket); } tokio_wrapper_trait_impls!( for UdSocket, sync SyncUdSocket, std StdUdSocket, tokio TokioUdSocket); interprocess-1.2.1/src/os/unix/udsocket/tokio/stream/connect_future.rs000064400000000000000000000013250072674642500244230ustar 00000000000000use { crate::os::unix::udsocket::{UdSocketPath, UdStream as SyncUdStream}, std::{ future::Future, io, pin::Pin, task::{Context, Poll}, }, }; pub struct ConnectFuture<'a, 'b> { pub path: &'b UdSocketPath<'a>, } impl Future for ConnectFuture<'_, '_> { type Output = io::Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let path = self.get_mut().path; match SyncUdStream::connect_nonblocking(path) { Err(e) if e.kind() == io::ErrorKind::WouldBlock => { cx.waker().wake_by_ref(); Poll::Pending } els => Poll::Ready(els), } } } interprocess-1.2.1/src/os/unix/udsocket/tokio/stream/mod.rs000064400000000000000000000164750072674642500221730ustar 00000000000000#[cfg(uds_supported)] use super::c_wrappers; use { crate::os::unix::{ imports::*, udsocket::{ToUdSocketPath, UdSocketPath, UdStream as SyncUdStream}, }, std::{ convert::TryFrom, error::Error, fmt::{self, Formatter}, io, net::Shutdown, pin::Pin, task::{Context, Poll}, }, }; mod connect_future; mod read_half; mod write_half; use connect_future::*; pub use {read_half::*, write_half::*}; /// A Unix domain socket byte stream, obtained either from [`UdStreamListener`](super::UdStreamListener) or by connecting to an existing server. /// /// # Examples /// - [Basic client](https://github.com/kotauskas/interprocess/blob/main/examples/tokio_udstream/client.rs) #[derive(Debug)] pub struct UdStream(TokioUdStream); impl UdStream { /// Connects to a Unix domain socket server at the specified path. /// /// See [`ToUdSocketPath`] for an example of using various string types to specify socket paths. pub async fn connect(path: impl ToUdSocketPath<'_>) -> io::Result { let path = path.to_socket_path()?; Self::_connect(&path).await } async fn _connect(path: &UdSocketPath<'_>) -> io::Result { let stream = ConnectFuture { path }.await?; Self::from_sync(stream) } /// Borrows a stream into a read half and a write half, which can be used to read and write the stream concurrently. /// /// This method is more efficient than [`.into_split()`](Self::into_split), but the halves cannot be moved into independently spawned tasks. pub fn split(&mut self) -> (BorrowedReadHalf<'_>, BorrowedWriteHalf<'_>) { let (read_tok, write_tok) = self.0.split(); (BorrowedReadHalf(read_tok), BorrowedWriteHalf(write_tok)) } /// Splits a stream into a read half and a write half, which can be used to read and write the stream concurrently. /// /// Unlike [`.split()`](Self::split), the owned halves can be moved to separate tasks, which comes at the cost of a heap allocation. /// /// Dropping either half will shut it down. This is equivalent to calling [`.shutdown()`](Self::shutdown) on the stream with the corresponding argument. pub fn into_split(self) -> (OwnedReadHalf, OwnedWriteHalf) { let (read_tok, write_tok) = self.0.into_split(); (OwnedReadHalf(read_tok), OwnedWriteHalf(write_tok)) } /// Attempts to put two owned halves of a stream back together and recover the original stream. Succeeds only if the two halves originated from the same call to [`.into_split()`](Self::into_split). pub fn reunite(read: OwnedReadHalf, write: OwnedWriteHalf) -> Result { let (read_tok, write_tok) = (read.0, write.0); let stream_tok = read_tok.reunite(write_tok)?; Ok(Self::from_tokio(stream_tok)) } /// Shuts down the read, write, or both halves of the stream. See [`Shutdown`]. /// /// Attempting to call this method with the same `how` argument multiple times may return `Ok(())` every time or it may return an error the second time it is called, depending on the platform. You must either avoid using the same value twice or ignore the error entirely. pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { c_wrappers::shutdown(self.as_raw_fd().as_ref(), how) } /// Fetches the credentials of the other end of the connection without using ancillary data. The returned structure contains the process identifier, user identifier and group identifier of the peer. #[cfg(any(doc, uds_peercred))] #[cfg_attr( // uds_peercred template feature = "doc_cfg", doc(cfg(any( all( target_os = "linux", any( target_env = "gnu", target_env = "musl", target_env = "musleabi", target_env = "musleabihf" ) ), target_os = "emscripten", target_os = "redox", target_os = "haiku" ))) )] pub fn get_peer_credentials(&self) -> io::Result { c_wrappers::get_peer_ucred(self.as_raw_fd().as_ref()) } fn pinproject(self: Pin<&mut Self>) -> Pin<&mut TokioUdStream> { Pin::new(&mut self.get_mut().0) } tokio_wrapper_conversion_methods!( sync SyncUdStream, std StdUdStream, tokio TokioUdStream); } tokio_wrapper_trait_impls!( for UdStream, sync SyncUdStream, std StdUdStream, tokio TokioUdStream); #[cfg(feature = "tokio_support")] impl TokioAsyncRead for UdStream { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { self.pinproject().poll_read(cx, buf) } } #[cfg(feature = "tokio_support")] impl FuturesAsyncRead for UdStream { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { let mut buf = ReadBuf::new(buf); match self.pinproject().poll_read(cx, &mut buf) { Poll::Ready(Ok(())) => Poll::Ready(Ok(buf.filled().len())), Poll::Ready(Err(e)) => Poll::Ready(Err(e)), Poll::Pending => Poll::Pending, } } } #[cfg(feature = "tokio_support")] impl TokioAsyncWrite for UdStream { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.pinproject().poll_write(cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproject().poll_flush(cx) } /// Finishes immediately. See the `.shutdown()` method. fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproject().poll_shutdown(cx) } } #[cfg(feature = "tokio_support")] impl FuturesAsyncWrite for UdStream { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.pinproject().poll_write(cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproject().poll_flush(cx) } /// Finishes immediately. See the `.shutdown()` method. fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { self.shutdown(Shutdown::Both)?; Poll::Ready(Ok(())) } } /// Error indicating that a read half and a write half were not from the same stream, and thus could not be reunited. #[derive(Debug)] pub struct ReuniteError(pub OwnedReadHalf, pub OwnedWriteHalf); impl Error for ReuniteError {} impl fmt::Display for ReuniteError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_str("tried to reunite halves of different streams") } } impl From for ReuniteError { fn from(TokioReuniteError(read, write): TokioReuniteError) -> Self { let read = OwnedReadHalf::from_tokio(read); let write = OwnedWriteHalf::from_tokio(write); Self(read, write) } } impl From for TokioReuniteError { fn from(ReuniteError(read, write): ReuniteError) -> Self { let read = read.into_tokio(); let write = write.into_tokio(); Self(read, write) } } interprocess-1.2.1/src/os/unix/udsocket/tokio/stream/read_half.rs000064400000000000000000000144140072674642500233100ustar 00000000000000#[cfg(uds_supported)] use super::c_wrappers; use super::{OwnedWriteHalf, ReuniteError, UdStream}; use crate::os::unix::imports::*; use std::{ io, net::Shutdown, pin::Pin, task::{Context, Poll}, }; /// Borrowed read half of a [`UdStream`](super::UdStream), created by [`.split()`](super::UdStream::split). #[derive(Debug)] pub struct BorrowedReadHalf<'a>(pub(super) TokioUdStreamReadHalf<'a>); impl<'a> BorrowedReadHalf<'a> { /// Fetches the credentials of the other end of the connection without using ancillary data. The returned structure contains the process identifier, user identifier and group identifier of the peer. #[cfg(any(doc, uds_peercred))] #[cfg_attr( // uds_peercred template feature = "doc_cfg", doc(cfg(any( all( target_os = "linux", any( target_env = "gnu", target_env = "musl", target_env = "musleabi", target_env = "musleabihf" ) ), target_os = "emscripten", target_os = "redox", target_os = "haiku" ))) )] pub fn get_peer_credentials(&self) -> io::Result { c_wrappers::get_peer_ucred(self.as_stream_raw_fd().as_ref()) } /// Shuts down the read half. /// /// Attempting to call this method multiple times may return `Ok(())` every time or it may return an error the second time it is called, depending on the platform. You must either avoid using the same value twice or ignore the error entirely. pub fn shutdown(&self) -> io::Result<()> { c_wrappers::shutdown(self.as_stream_raw_fd().as_ref(), Shutdown::Read) } /// Returns the underlying file descriptor. Note that this isn't a file descriptor for the read half specifically, but rather for the whole stream, so this isn't exposed as a struct method. fn as_stream_raw_fd(&self) -> c_int { let stream: &TokioUdStream = self.0.as_ref(); stream.as_raw_fd() } fn pinproject(self: Pin<&mut Self>) -> Pin<&mut TokioUdStreamReadHalf<'a>> { Pin::new(&mut self.get_mut().0) } tokio_wrapper_conversion_methods!(tokio_norawfd TokioUdStreamReadHalf<'a>); } #[cfg(feature = "tokio_support")] impl TokioAsyncRead for BorrowedReadHalf<'_> { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { self.pinproject().poll_read(cx, buf) } } #[cfg(feature = "tokio_support")] impl FuturesAsyncRead for BorrowedReadHalf<'_> { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { let mut buf = ReadBuf::new(buf); match self.pinproject().poll_read(cx, &mut buf) { Poll::Ready(Ok(())) => Poll::Ready(Ok(buf.filled().len())), Poll::Ready(Err(e)) => Poll::Ready(Err(e)), Poll::Pending => Poll::Pending, } } } tokio_wrapper_trait_impls!( for BorrowedReadHalf<'a>, tokio_norawfd_lt 'a TokioUdStreamReadHalf<'a>); /// Owned read half of a [`UdStream`](super::UdStream), created by [`.into_split()`](super::UdStream::into_split). #[derive(Debug)] pub struct OwnedReadHalf(pub(super) TokioUdStreamOwnedReadHalf); impl OwnedReadHalf { /// Attempts to put two owned halves of a stream back together and recover the original stream. Succeeds only if the two halves originated from the same call to [`.into_split()`](UdStream::into_split). pub fn reunite_with(self, write: OwnedWriteHalf) -> Result { UdStream::reunite(self, write) } /// Fetches the credentials of the other end of the connection without using ancillary data. The returned structure contains the process identifier, user identifier and group identifier of the peer. #[cfg(any(doc, uds_peercred))] #[cfg_attr( // uds_peercred template feature = "doc_cfg", doc(cfg(any( all( target_os = "linux", any( target_env = "gnu", target_env = "musl", target_env = "musleabi", target_env = "musleabihf" ) ), target_os = "emscripten", target_os = "redox", target_os = "haiku" ))) )] pub fn get_peer_credentials(&self) -> io::Result { c_wrappers::get_peer_ucred(self.as_stream_raw_fd().as_ref()) } /// Shuts down the read half. /// /// Attempting to call this method multiple times may return `Ok(())` every time or it may return an error the second time it is called, depending on the platform. You must either avoid using the same value twice or ignore the error entirely. pub fn shutdown(&self) -> io::Result<()> { c_wrappers::shutdown(self.as_stream_raw_fd().as_ref(), Shutdown::Read) } /// Returns the underlying file descriptor. Note that this isn't a file descriptor for the read half specifically, but rather for the whole stream, so this isn't exposed as a struct method. fn as_stream_raw_fd(&self) -> c_int { let stream: &TokioUdStream = self.0.as_ref(); stream.as_raw_fd() } fn pinproject(self: Pin<&mut Self>) -> Pin<&mut TokioUdStreamOwnedReadHalf> { Pin::new(&mut self.get_mut().0) } tokio_wrapper_conversion_methods!(tokio_norawfd TokioUdStreamOwnedReadHalf); } #[cfg(feature = "tokio_support")] impl TokioAsyncRead for OwnedReadHalf { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { self.pinproject().poll_read(cx, buf) } } #[cfg(feature = "tokio_support")] impl FuturesAsyncRead for OwnedReadHalf { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { let mut buf = ReadBuf::new(buf); match self.pinproject().poll_read(cx, &mut buf) { Poll::Ready(Ok(())) => Poll::Ready(Ok(buf.filled().len())), Poll::Ready(Err(e)) => Poll::Ready(Err(e)), Poll::Pending => Poll::Pending, } } } tokio_wrapper_trait_impls!( for OwnedReadHalf, tokio_norawfd TokioUdStreamOwnedReadHalf); interprocess-1.2.1/src/os/unix/udsocket/tokio/stream/write_half.rs000064400000000000000000000163320072674642500235300ustar 00000000000000#[cfg(uds_supported)] use super::c_wrappers; use super::{OwnedReadHalf, ReuniteError, UdStream}; use crate::os::unix::imports::*; use std::{ io, net::Shutdown, pin::Pin, task::{Context, Poll}, }; /// Borrowed write half of a [`UdStream`](super::UdStream), created by [`.split()`](super::UdStream::split). #[derive(Debug)] pub struct BorrowedWriteHalf<'a>(pub(super) TokioUdStreamWriteHalf<'a>); impl<'a> BorrowedWriteHalf<'a> { /// Fetches the credentials of the other end of the connection without using ancillary data. The returned structure contains the process identifier, user identifier and group identifier of the peer. #[cfg(any(doc, uds_peercred))] #[cfg_attr( // uds_peercred template feature = "doc_cfg", doc(cfg(any( all( target_os = "linux", any( target_env = "gnu", target_env = "musl", target_env = "musleabi", target_env = "musleabihf" ) ), target_os = "emscripten", target_os = "redox", target_os = "haiku" ))) )] pub fn get_peer_credentials(&self) -> io::Result { c_wrappers::get_peer_ucred(self.as_stream_raw_fd().as_ref()) } /// Shuts down the write half. /// /// Attempting to call this method multiple times may return `Ok(())` every time or it may return an error the second time it is called, depending on the platform. You must either avoid using the same value twice or ignore the error entirely. pub fn shutdown(&self) -> io::Result<()> { c_wrappers::shutdown(self.as_stream_raw_fd().as_ref(), Shutdown::Write) } /// Returns the underlying file descriptor. Note that this isn't a file descriptor for the write half specifically, but rather for the whole stream, so this isn't exposed as a struct method. fn as_stream_raw_fd(&self) -> c_int { let stream: &TokioUdStream = self.0.as_ref(); stream.as_raw_fd() } fn pinproject(self: Pin<&mut Self>) -> Pin<&mut TokioUdStreamWriteHalf<'a>> { Pin::new(&mut self.get_mut().0) } tokio_wrapper_conversion_methods!(tokio_norawfd TokioUdStreamWriteHalf<'a>); } #[cfg(feature = "tokio_support")] impl TokioAsyncWrite for BorrowedWriteHalf<'_> { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.pinproject().poll_write(cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproject().poll_flush(cx) } /// Finishes immediately. See the `.shutdown()` method. fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproject().poll_shutdown(cx) } } #[cfg(feature = "tokio_support")] impl FuturesAsyncWrite for BorrowedWriteHalf<'_> { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.pinproject().poll_write(cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproject().poll_flush(cx) } /// Finishes immediately. See the `.shutdown()` method. fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { self.shutdown()?; Poll::Ready(Ok(())) } } tokio_wrapper_trait_impls!( for BorrowedWriteHalf<'a>, tokio_norawfd_lt 'a TokioUdStreamWriteHalf<'a>); /// Owned write half of a [`UdStream`](super::UdStream), created by [`.into_split()`](super::UdStream::into_split). #[derive(Debug)] pub struct OwnedWriteHalf(pub(super) TokioUdStreamOwnedWriteHalf); impl OwnedWriteHalf { /// Attempts to put two owned halves of a stream back together and recover the original stream. Succeeds only if the two halves originated from the same call to [`.into_split()`](UdStream::into_split). pub fn reunite_with(self, read: OwnedReadHalf) -> Result { UdStream::reunite(read, self) } /// Fetches the credentials of the other end of the connection without using ancillary data. The returned structure contains the process identifier, user identifier and group identifier of the peer. #[cfg(any(doc, uds_peercred))] #[cfg_attr( // uds_peercred template feature = "doc_cfg", doc(cfg(any( all( target_os = "linux", any( target_env = "gnu", target_env = "musl", target_env = "musleabi", target_env = "musleabihf" ) ), target_os = "emscripten", target_os = "redox", target_os = "haiku" ))) )] pub fn get_peer_credentials(&self) -> io::Result { c_wrappers::get_peer_ucred(self.as_stream_raw_fd().as_ref()) } /// Shuts down the write half. /// /// Attempting to call this method multiple times may return `Ok(())` every time or it may return an error the second time it is called, depending on the platform. You must either avoid using the same value twice or ignore the error entirely. pub fn shutdown(&self) -> io::Result<()> { c_wrappers::shutdown(self.as_stream_raw_fd().as_ref(), Shutdown::Write) } /// Returns the underlying file descriptor. Note that this isn't a file descriptor for the write half specifically, but rather for the whole stream, so this isn't exposed as a struct method. fn as_stream_raw_fd(&self) -> c_int { let stream: &TokioUdStream = self.0.as_ref(); stream.as_raw_fd() } fn pinproject(self: Pin<&mut Self>) -> Pin<&mut TokioUdStreamOwnedWriteHalf> { Pin::new(&mut self.get_mut().0) } tokio_wrapper_conversion_methods!(tokio_norawfd TokioUdStreamOwnedWriteHalf); } #[cfg(feature = "tokio_support")] impl TokioAsyncWrite for OwnedWriteHalf { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.pinproject().poll_write(cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproject().poll_flush(cx) } /// Finishes immediately. See the `.shutdown()` method. fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { self.shutdown()?; Poll::Ready(Ok(())) } } #[cfg(feature = "tokio_support")] impl FuturesAsyncWrite for OwnedWriteHalf { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.pinproject().poll_write(cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproject().poll_flush(cx) } /// Finishes immediately. See the `.shutdown()` method. fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { self.shutdown()?; Poll::Ready(Ok(())) } } tokio_wrapper_trait_impls!( for OwnedWriteHalf, tokio_norawfd TokioUdStreamOwnedWriteHalf); interprocess-1.2.1/src/os/unix/udsocket/tokio/util.rs000064400000000000000000000120330072674642500210600ustar 00000000000000macro_rules! tokio_wrapper_conversion_methods { (tokio_norawfd $tok:ty) => { /// Unwraps into Tokio's corresponding type. This is a zero-cost operation. pub fn into_tokio(self) -> $tok { self.0 } /// Wraps Tokio's corresponding type. This is a zero-cost operation. pub fn from_tokio(tokio: $tok) -> Self { Self(tokio) } }; (tokio $tok:ty) => { tokio_wrapper_conversion_methods!(tokio_norawfd $tok); /// Creates a Tokio-based async object from a given raw file descriptor. This will also attach the object to the Tokio runtime this function is called in, so calling it outside a runtime will result in an error (which is why the `FromRawFd` trait can't be implemented instead). /// /// # Safety /// The given file descriptor must be valid (i.e. refer to an existing kernel object) and must not be owned by any other file descriptor container. If this is not upheld, an arbitrary file descriptor will be closed when the returned object is dropped. pub unsafe fn from_raw_fd(fd: c_int) -> io::Result { let std = unsafe { FromRawFd::from_raw_fd(fd) }; let tokio = <$tok>::from_std(std)?; Ok(Self(tokio)) } /// Releases ownership of the raw file descriptor, detaches the object from the Tokio runtime (therefore has to be called within the runtime) and returns the file descriptor as an integer. pub fn into_raw_fd(self) -> io::Result { let std = <$tok>::into_std(self.0)?; let fd = IntoRawFd::into_raw_fd(std); Ok(fd) } }; (sync $sync:ty) => { /// Detaches the async object from the Tokio runtime (therefore has to be called within the runtime) and converts it to a blocking one. pub fn into_sync(self) -> io::Result<$sync> { Ok(unsafe { <$sync as FromRawFd>::from_raw_fd(self.into_raw_fd()?) }) } /// Creates a Tokio-based async object from a blocking one. This will also attach the object to the Tokio runtime this function is called in, so calling it outside a runtime will result in an error. pub fn from_sync(sync: $sync) -> io::Result { let fd = IntoRawFd::into_raw_fd(sync); unsafe { Self::from_raw_fd(fd) } } }; (std $std:ty) => { /// Detaches the async object from the Tokio runtime and converts it to a blocking one from the standard library. Returns an error if called outside a Tokio runtime context. pub fn into_std(self) -> io::Result<$std> { Ok(unsafe { <$std as FromRawFd>::from_raw_fd(self.into_raw_fd()?) }) } /// Creates a Tokio-based async object from a blocking one from the standard library. This will also attach the object to the Tokio runtime this function is called in, so calling it outside a runtime will result in an error. pub fn from_std(std: $std) -> io::Result { let fd = IntoRawFd::into_raw_fd(std); unsafe { Self::from_raw_fd(fd) } } }; ($($k:ident $v:ty),+ $(,)?) => { $(tokio_wrapper_conversion_methods!($k $v);)+ }; } macro_rules! tokio_wrapper_trait_impls { (for $slf:ty, @tokio_norawfd {$($gen:tt)*} $tok:ty) => { impl $($gen)* From<$slf> for $tok { fn from(x: $slf) -> Self { x.into_tokio() } } impl $($gen)* From<$tok> for $slf { fn from(tokio: $tok) -> Self { Self::from_tokio(tokio) } } }; (for $slf:ty, tokio_norawfd $tok:ty) => { tokio_wrapper_trait_impls!(for $slf, @tokio_norawfd {} $tok); }; (for $slf:ty, tokio_norawfd_lt $lt:lifetime $tok:ty) => { tokio_wrapper_trait_impls!(for $slf, @tokio_norawfd {<$lt>} $tok); }; (for $slf:ty, tokio $tok:ty) => { tokio_wrapper_trait_impls!(for $slf, tokio_norawfd $tok); impl AsRawFd for $slf { #[cfg(unix)] fn as_raw_fd(&self) -> c_int { self.0.as_raw_fd() } } }; (for $slf:ty, sync $sync:ty) => { impl TryFrom<$slf> for $sync { type Error = io::Error; fn try_from(x: $slf) -> Result { x.into_sync() } } impl TryFrom<$sync> for $slf { type Error = io::Error; fn try_from(sync: $sync) -> Result { Self::from_sync(sync) } } }; (for $slf:ty, std $std:ty) => { impl TryFrom<$slf> for $std { type Error = io::Error; fn try_from(x: $slf) -> Result { x.into_std() } } impl TryFrom<$std> for $slf { type Error = io::Error; fn try_from(std: $std) -> Result { Self::from_std(std) } } }; (for $slf:ty, $($k:ident $v:ty),+ $(,)?) => { $(tokio_wrapper_trait_impls!(for $slf, $k $v);)+ }; } interprocess-1.2.1/src/os/unix/udsocket/util.rs000064400000000000000000000103150072674642500177340ustar 00000000000000use super::imports::*; use cfg_if::cfg_if; use std::{ ffi::{CStr, CString}, hint::unreachable_unchecked, io::{self, IoSlice, IoSliceMut}, mem::zeroed, }; use to_method::To; #[cfg(unix)] #[allow(dead_code)] mod tname { pub static SOCKLEN_T: &str = "`socklen_t`"; pub static SIZE_T: &str = "`size_t`"; pub static C_INT: &str = "`c_int`"; } #[cfg(unix)] cfg_if! { if #[cfg(uds_msghdr_iovlen_c_int)] { pub type MsghdrIovlen = c_int; static MSGHDR_IOVLEN_NAME: &str = tname::C_INT; } else if #[cfg(uds_msghdr_iovlen_size_t)] { pub type MsghdrIovlen = size_t; static MSGHDR_IOVLEN_NAME: &str = tname::SIZE_T; } } #[cfg(unix)] cfg_if! { if #[cfg(uds_msghdr_controllen_socklen_t)] { pub type MsghdrControllen = socklen_t; static MSGHDR_CONTROLLEN_NAME: &str = tname::SOCKLEN_T; } else if #[cfg(uds_msghdr_controllen_size_t)] { pub type MsghdrControllen = size_t; static MSGHDR_CONTROLLEN_NAME: &str = tname::SIZE_T; } } #[cfg(unix)] pub fn to_msghdr_iovlen(iovlen: usize) -> io::Result { iovlen.try_to::().map_err(|_| { io::Error::new( io::ErrorKind::InvalidInput, format!( "number of scatter-gather buffers overflowed {}", MSGHDR_IOVLEN_NAME, ), ) }) } #[cfg(unix)] pub fn to_msghdr_controllen(controllen: usize) -> io::Result { controllen.try_to::().map_err(|_| { io::Error::new( io::ErrorKind::InvalidInput, format!( "ancillary data buffer length overflowed {}", MSGHDR_CONTROLLEN_NAME, ), ) }) } pub fn empty_cstring() -> CString { unsafe { // SAFETY: the value returned by Vec::new() is always empty, thus it // adheres to the contract of CString::new(). CString::new(Vec::new()).unwrap_or_else(|_| unreachable_unchecked()) } } pub fn empty_cstr() -> &'static CStr { unsafe { // SAFETY: a single nul terminator is a valid CStr CStr::from_bytes_with_nul_unchecked(&[0]) } } pub fn fill_out_msghdr_r( hdr: &mut msghdr, iov: &mut [IoSliceMut<'_>], anc: &mut [u8], ) -> io::Result<()> { _fill_out_msghdr( hdr, iov.as_ptr() as *mut _, to_msghdr_iovlen(iov.len())?, anc.as_mut_ptr(), to_msghdr_controllen(anc.len())?, ) } pub fn fill_out_msghdr_w(hdr: &mut msghdr, iov: &[IoSlice<'_>], anc: &[u8]) -> io::Result<()> { _fill_out_msghdr( hdr, iov.as_ptr() as *mut _, to_msghdr_iovlen(iov.len())?, anc.as_ptr() as *mut _, to_msghdr_controllen(anc.len())?, ) } #[cfg(unix)] fn _fill_out_msghdr( hdr: &mut msghdr, iov: *mut iovec, iovlen: MsghdrIovlen, control: *mut u8, controllen: MsghdrControllen, ) -> io::Result<()> { hdr.msg_iov = iov; hdr.msg_iovlen = iovlen; hdr.msg_control = control as *mut _; hdr.msg_controllen = controllen; Ok(()) } pub fn mk_msghdr_r(iov: &mut [IoSliceMut<'_>], anc: &mut [u8]) -> io::Result { let mut hdr = unsafe { // SAFETY: msghdr is plain old data, i.e. an all-zero pattern is allowed zeroed() }; fill_out_msghdr_r(&mut hdr, iov, anc)?; Ok(hdr) } pub fn mk_msghdr_w(iov: &[IoSlice<'_>], anc: &[u8]) -> io::Result { let mut hdr = unsafe { // SAFETY: msghdr is plain old data, i.e. an all-zero pattern is allowed zeroed() }; fill_out_msghdr_w(&mut hdr, iov, anc)?; Ok(hdr) } pub fn check_ancillary_unsound() -> io::Result<()> { if cfg!(uds_ancillary_unsound) { let error_kind = { #[cfg(io_error_kind_unsupported_stable)] { io::ErrorKind::Unsupported } #[cfg(not(io_error_kind_unsupported_stable))] { io::ErrorKind::Other } }; Err(io::Error::new( error_kind, "\ ancillary data has been disabled for non-x86 ISAs in a hotfix because it \ doesn't account for alignment", )) } else { Ok(()) } } pub fn eunreachable(_e: T) -> U { unreachable!() } interprocess-1.2.1/src/os/unix/unnamed_pipe.rs000064400000000000000000000057450072674642500176150ustar 00000000000000use super::FdOps; use crate::{ unnamed_pipe::{UnnamedPipeReader as PubReader, UnnamedPipeWriter as PubWriter}, Sealed, }; use libc::c_int; use std::{ fmt::{self, Debug, Formatter}, io::{self, Read, Write}, mem::ManuallyDrop, os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}, }; pub(crate) fn pipe() -> io::Result<(PubWriter, PubReader)> { let (success, fds) = unsafe { let mut fds: [c_int; 2] = [0; 2]; let result = libc::pipe(fds.as_mut_ptr()); (result == 0, fds) }; if success { unsafe { // SAFETY: we just created both of those file descriptors, which means that neither of // them can be in use elsewhere. let reader = PubReader { inner: UnnamedPipeReader::from_raw_fd(fds[0]), }; let writer = PubWriter { inner: UnnamedPipeWriter::from_raw_fd(fds[1]), }; Ok((writer, reader)) } } else { Err(io::Error::last_os_error()) } } pub(crate) struct UnnamedPipeReader(FdOps); // Please, for the love of Unix gods, don't ever try to implement this for &UnnamedPipeReader, // reading a pipe concurrently is UB and UnnamedPipeReader is Send and Sync. If you do, the // universe will collapse immediately. impl Read for UnnamedPipeReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } } impl Sealed for UnnamedPipeReader {} impl AsRawFd for UnnamedPipeReader { fn as_raw_fd(&self) -> c_int { self.0.as_raw_fd() } } impl IntoRawFd for UnnamedPipeReader { fn into_raw_fd(self) -> c_int { let self_ = ManuallyDrop::new(self); self_.as_raw_fd() } } impl FromRawFd for UnnamedPipeReader { unsafe fn from_raw_fd(fd: c_int) -> Self { Self(unsafe { // SAFETY: guaranteed by safety contract FdOps::from_raw_fd(fd) }) } } impl Debug for UnnamedPipeReader { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("UnnamedPipeReader") .field("fd", &self.as_raw_fd()) .finish() } } pub(crate) struct UnnamedPipeWriter(FdOps); impl Write for UnnamedPipeWriter { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } fn flush(&mut self) -> io::Result<()> { self.0.flush() } } impl Sealed for UnnamedPipeWriter {} impl AsRawFd for UnnamedPipeWriter { #[cfg(unix)] fn as_raw_fd(&self) -> c_int { self.0.as_raw_fd() } } impl IntoRawFd for UnnamedPipeWriter { #[cfg(unix)] fn into_raw_fd(self) -> c_int { self.0.into_raw_fd() } } impl FromRawFd for UnnamedPipeWriter { #[cfg(unix)] unsafe fn from_raw_fd(fd: c_int) -> Self { Self(FdOps::new(fd)) } } impl Debug for UnnamedPipeWriter { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("UnnamedPipeWriter") .field("fd", &self.as_raw_fd()) .finish() } } interprocess-1.2.1/src/os/windows/imports.rs000064400000000000000000000056450072674642500173540ustar 00000000000000#![allow(unused_imports, dead_code, non_camel_case_types)] use std::ffi::c_void; #[cfg(windows)] pub(super) use { std::os::windows::ffi::{OsStrExt, OsStringExt}, winapi::{ shared::winerror::{ERROR_PIPE_BUSY, ERROR_PIPE_CONNECTED}, um::{ fileapi::{CreateFileW, FlushFileBuffers, ReadFile, WriteFile, OPEN_EXISTING}, handleapi::{CloseHandle, DuplicateHandle, INVALID_HANDLE_VALUE}, namedpipeapi::{ ConnectNamedPipe, CreateNamedPipeW, CreatePipe, DisconnectNamedPipe, GetNamedPipeHandleStateW, GetNamedPipeInfo, PeekNamedPipe, SetNamedPipeHandleState, WaitNamedPipeW, }, processthreadsapi::GetCurrentProcess, winbase::{ GetNamedPipeClientProcessId, GetNamedPipeClientSessionId, GetNamedPipeServerProcessId, GetNamedPipeServerSessionId, }, winbase::{ FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_OVERLAPPED, FILE_FLAG_WRITE_THROUGH, PIPE_NOWAIT, PIPE_REJECT_REMOTE_CLIENTS, }, winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE}, }, }, }; import_type_alias_or_make_dummy!(types {winapi::shared::minwindef}::( DWORD = u32, LPVOID = *mut c_void, BOOL = i32, ), cfg(windows)); import_type_alias_or_make_dummy!(type {winapi::shared::ntdef}::HANDLE = *mut c_void, cfg(windows)); import_type_or_make_dummy!(type {winapi::um::minwinbase}::SECURITY_ATTRIBUTES, cfg(windows)); import_const_or_make_dummy!(u32: consts {winapi::um::winbase}::( PIPE_ACCESS_INBOUND = 0, PIPE_ACCESS_OUTBOUND = 1, PIPE_ACCESS_DUPLEX = 2, PIPE_TYPE_BYTE = 1, PIPE_TYPE_MESSAGE = 2, PIPE_READMODE_BYTE = 0, PIPE_READMODE_MESSAGE = 1, ), cfg(windows)); import_trait_or_make_dummy!(traits {std::os::windows::io}::( AsRawHandle, IntoRawHandle, FromRawHandle, ), cfg(windows)); import_trait_or_make_dummy!(traits {futures_io}::( AsyncRead, AsyncWrite, ), cfg(feature = "tokio_support")); import_trait_or_make_dummy!(traits {tokio::io}::( AsyncRead as TokioAsyncRead, AsyncWrite as TokioAsyncWrite, ), cfg(feature = "tokio_support")); import_type_or_make_dummy!( type {tokio::io}::ReadBuf as TokioReadBuf<'a>, cfg(feature = "tokio_support"), ); #[cfg(all(windows, feature = "tokio_support"))] pub(super) use tokio::net::windows::named_pipe::ClientOptions as TokioNPClientOptions; import_type_or_make_dummy!(types {tokio::net::windows::named_pipe}::( NamedPipeClient as TokioNPClient, NamedPipeServer as TokioNPServer, ), cfg(all(windows, feature = "tokio_support"))); #[cfg(all(windows, feature = "signals"))] pub(super) use {intmap::IntMap, once_cell::sync::Lazy, spinning::RwLock, thiserror::Error}; import_const_or_make_dummy!(i32: consts {libc}::( SIG_DFL = 0, SIGABRT = 100, SIGFPE = 101, SIGILL = 102, SIGINT = 103, SIGSEGV = 104, SIGTERM = 105, ), cfg(windows)); interprocess-1.2.1/src/os/windows/local_socket/listener.rs000064400000000000000000000020000072674642500221240ustar 00000000000000use { super::LocalSocketStream, crate::{ local_socket::ToLocalSocketName, os::windows::named_pipe::{ DuplexBytePipeStream as PipeStream, PipeListener as GenericPipeListener, PipeListenerOptions, PipeMode, }, }, std::io, }; type PipeListener = GenericPipeListener; #[derive(Debug)] pub struct LocalSocketListener { inner: PipeListener, } impl LocalSocketListener { pub fn bind<'a>(name: impl ToLocalSocketName<'a>) -> io::Result { let name = name.to_local_socket_name()?; let inner = PipeListenerOptions::new() .name(name.into_inner()) .mode(PipeMode::Bytes) .create()?; Ok(Self { inner }) } pub fn accept(&self) -> io::Result { let inner = self.inner.accept()?; Ok(LocalSocketStream { inner }) } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.inner.set_nonblocking(nonblocking) } } interprocess-1.2.1/src/os/windows/local_socket/mod.rs000064400000000000000000000026130072674642500210700ustar 00000000000000//! Adapter module, implements local sockets under Windows. use { crate::local_socket::{LocalSocketName, NameTypeSupport}, std::{ borrow::Cow, ffi::{OsStr, OsString}, }, }; #[cfg(feature = "tokio_support")] pub mod tokio; mod listener; pub use listener::*; mod stream; pub use stream::*; fn thunk_broken_pipe_to_eof(r: std::io::Result) -> std::io::Result { match r { Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => Ok(0), els => els, } } pub const NAME_TYPE_ALWAYS_SUPPORTED: NameTypeSupport = NameTypeSupport::OnlyNamespaced; pub fn name_type_support_query() -> NameTypeSupport { NAME_TYPE_ALWAYS_SUPPORTED } pub fn to_local_socket_name_osstr(osstr: &OsStr) -> LocalSocketName<'_> { LocalSocketName::from_raw_parts(Cow::Borrowed(osstr), true) } pub fn to_local_socket_name_osstring(osstring: OsString) -> LocalSocketName<'static> { LocalSocketName::from_raw_parts(Cow::Owned(osstring), true) } /* /// Helper function to check whether a series of UTF-16 bytes starts with `\\.\pipe\`. fn has_pipefs_prefix( val: impl IntoIterator, ) -> bool { let pipefs_prefix: [u16; 9] = [ // The string \\.\pipe\ in UTF-16 0x005c, 0x005c, 0x002e, 0x005c, 0x0070, 0x0069, 0x0070, 0x0065, 0x005c, ]; pipefs_prefix.iter().copied().eq(val) }*/ // TODO add Path/PathBuf special-case for \\.\pipe\* interprocess-1.2.1/src/os/windows/local_socket/stream.rs000064400000000000000000000050270072674642500216060ustar 00000000000000use { super::thunk_broken_pipe_to_eof, crate::{ local_socket::ToLocalSocketName, os::windows::named_pipe::DuplexBytePipeStream as PipeStream, }, std::{ ffi::c_void, fmt::{self, Debug, Formatter}, io::{self, prelude::*, IoSlice, IoSliceMut}, os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle}, }, }; pub struct LocalSocketStream { pub(super) inner: PipeStream, } impl LocalSocketStream { pub fn connect<'a>(name: impl ToLocalSocketName<'a>) -> io::Result { let name = name.to_local_socket_name()?; let inner = PipeStream::connect(name.inner())?; Ok(Self { inner }) } pub fn peer_pid(&self) -> io::Result { match self.inner.is_server() { true => self.inner.client_process_id(), false => self.inner.server_process_id(), } } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.inner.set_nonblocking(nonblocking) } } /// Thunks broken pipe errors into EOFs because broken pipe to the writer is what EOF is to the /// reader, but Windows shoehorns both into the former. impl Read for LocalSocketStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { thunk_broken_pipe_to_eof(self.inner.read(buf)) } fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { thunk_broken_pipe_to_eof(self.inner.read_vectored(bufs)) } } impl Write for LocalSocketStream { fn write(&mut self, buf: &[u8]) -> io::Result { self.inner.write(buf) } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.inner.write_vectored(bufs) } fn flush(&mut self) -> io::Result<()> { self.inner.flush() } } impl Debug for LocalSocketStream { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("LocalSocketStream") .field("handle", &self.as_raw_handle()) .finish() } } impl AsRawHandle for LocalSocketStream { fn as_raw_handle(&self) -> *mut c_void { self.inner.as_raw_handle() } } impl IntoRawHandle for LocalSocketStream { fn into_raw_handle(self) -> *mut c_void { self.inner.into_raw_handle() } } impl FromRawHandle for LocalSocketStream { unsafe fn from_raw_handle(handle: *mut c_void) -> Self { let inner = unsafe { // SAFETY: guaranteed via safety contract PipeStream::from_raw_handle(handle) }; Self { inner } } } interprocess-1.2.1/src/os/windows/local_socket/tokio/listener.rs000064400000000000000000000017500072674642500232640ustar 00000000000000use { super::LocalSocketStream, crate::{ local_socket::ToLocalSocketName, os::windows::named_pipe::{ tokio::{ DuplexBytePipeStream as PipeStream, PipeListener as GenericPipeListener, PipeListenerOptionsExt as _, }, PipeListenerOptions, PipeMode, }, }, std::io, }; type PipeListener = GenericPipeListener; #[derive(Debug)] pub struct LocalSocketListener { inner: PipeListener, } impl LocalSocketListener { pub fn bind<'a>(name: impl ToLocalSocketName<'a>) -> io::Result { let name = name.to_local_socket_name()?; let inner = PipeListenerOptions::new() .name(name.into_inner()) .mode(PipeMode::Bytes) .create_tokio()?; Ok(Self { inner }) } pub async fn accept(&self) -> io::Result { let inner = self.inner.accept().await?; Ok(LocalSocketStream { inner }) } } interprocess-1.2.1/src/os/windows/local_socket/tokio/mod.rs000064400000000000000000000002060072674642500222110ustar 00000000000000//! Adapter module, implements Tokio local sockets under Windows. mod listener; pub use listener::*; mod stream; pub use stream::*; interprocess-1.2.1/src/os/windows/local_socket/tokio/stream/mod.rs000064400000000000000000000102140072674642500235040ustar 00000000000000mod read_half; pub use read_half::*; mod write_half; pub use write_half::*; // TODO reunite use { super::super::thunk_broken_pipe_to_eof, crate::{ local_socket::ToLocalSocketName, os::windows::{imports::HANDLE, named_pipe::tokio::DuplexBytePipeStream as PipeStream}, }, futures_core::ready, futures_io::{AsyncRead, AsyncWrite}, std::{ ffi::{c_void, OsStr}, fmt::{self, Debug, Formatter}, future::Future, io, os::windows::io::AsRawHandle, pin::Pin, task::{Context, Poll}, time::Duration, }, tokio::time::{sleep, Instant, Sleep}, }; pub struct LocalSocketStream { pub(super) inner: PipeStream, } impl LocalSocketStream { pub async fn connect<'a>(name: impl ToLocalSocketName<'a>) -> io::Result { let name = name.to_local_socket_name()?; let inner = ConnectFuture::new(name.inner()).await?; Ok(Self { inner }) } pub fn peer_pid(&self) -> io::Result { match self.inner.is_server() { true => self.inner.client_process_id(), false => self.inner.server_process_id(), } } pub fn into_split(self) -> (OwnedReadHalf, OwnedWriteHalf) { let (r, w) = self.inner.split(); (OwnedReadHalf { inner: r }, OwnedWriteHalf { inner: w }) } // TODO use this pub unsafe fn _from_raw_handle(handle: HANDLE) -> io::Result { let inner = unsafe { // SAFETY: as per safety contract PipeStream::from_raw_handle(handle)? }; Ok(Self { inner }) } fn pinproj(&mut self) -> Pin<&mut PipeStream> { Pin::new(&mut self.inner) } } pub struct ConnectFuture<'a> { name: &'a OsStr, timer: Sleep, } impl<'a> ConnectFuture<'a> { const IDLE_TIME: Duration = Duration::from_millis(1); fn new(name: &'a OsStr) -> Self { Self { name, timer: sleep(Duration::new(0, 0)), // FIXME use Duration::ZERO } } fn reset_timer(self: Pin<&mut Self>) { self.pinproj_timer().reset(Instant::now() + Self::IDLE_TIME); } fn pinproj_timer(self: Pin<&mut Self>) -> Pin<&mut Sleep> { unsafe { // SAFETY: requires self to be pinned Pin::new_unchecked(&mut self.get_unchecked_mut().timer) } } } impl Future for ConnectFuture<'_> { type Output = io::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match PipeStream::connect(self.as_ref().name) { Err(e) if e.kind() == io::ErrorKind::WouldBlock => { ready!(self.as_mut().pinproj_timer().poll(cx)); self.as_mut().reset_timer(); Poll::Pending } not_waiting => Poll::Ready(not_waiting), } } } /// Thunks broken pipe errors into EOFs because broken pipe to the writer is what EOF is to the /// reader, but Windows shoehorns both into the former. impl AsyncRead for LocalSocketStream { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { let rslt = self.pinproj().poll_read(cx, buf); let thunked = thunk_broken_pipe_to_eof(ready!(rslt)); Poll::Ready(thunked) } } impl AsyncWrite for LocalSocketStream { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.pinproj().poll_write(cx, buf) } // Those two do nothing fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproj().poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproj().poll_close(cx) } } impl Debug for LocalSocketStream { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("LocalSocketStream") .field("handle", &self.as_raw_handle()) .finish() } } impl AsRawHandle for LocalSocketStream { fn as_raw_handle(&self) -> *mut c_void { self.inner.as_raw_handle() } } interprocess-1.2.1/src/os/windows/local_socket/tokio/stream/read_half.rs000064400000000000000000000035260072674642500246420ustar 00000000000000use { super::thunk_broken_pipe_to_eof, crate::os::windows::{ imports::HANDLE, named_pipe::tokio::ByteReaderPipeStream as ReadHalfImpl, }, futures_core::ready, futures_io::AsyncRead, std::{ ffi::c_void, fmt::{self, Debug, Formatter}, io, os::windows::io::AsRawHandle, pin::Pin, task::{Context, Poll}, }, }; pub struct OwnedReadHalf { pub(super) inner: ReadHalfImpl, } impl OwnedReadHalf { pub fn peer_pid(&self) -> io::Result { match self.inner.is_server() { true => self.inner.client_process_id(), false => self.inner.server_process_id(), } } // TODO use this pub unsafe fn _from_raw_handle(handle: HANDLE) -> io::Result { let inner = unsafe { // SAFETY: as per safety contract ReadHalfImpl::from_raw_handle(handle)? }; Ok(Self { inner }) } fn pinproj(&mut self) -> Pin<&mut ReadHalfImpl> { Pin::new(&mut self.inner) } } /// Thunks broken pipe errors into EOFs because broken pipe to the writer is what EOF is to the /// reader, but Windows shoehorns both into the former. impl AsyncRead for OwnedReadHalf { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { let rslt = self.pinproj().poll_read(cx, buf); let thunked = thunk_broken_pipe_to_eof(ready!(rslt)); Poll::Ready(thunked) } } impl Debug for OwnedReadHalf { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("local_socket::OwnedWriteHalf") .field("handle", &self.as_raw_handle()) .finish() } } impl AsRawHandle for OwnedReadHalf { fn as_raw_handle(&self) -> *mut c_void { self.inner.as_raw_handle() } } interprocess-1.2.1/src/os/windows/local_socket/tokio/stream/write_half.rs000064400000000000000000000035160072674642500250600ustar 00000000000000use { crate::os::windows::{ imports::HANDLE, named_pipe::tokio::ByteWriterPipeStream as WriteHalfImpl, }, futures_io::AsyncWrite, std::{ ffi::c_void, fmt::{self, Debug, Formatter}, io, os::windows::io::AsRawHandle, pin::Pin, task::{Context, Poll}, }, }; pub struct OwnedWriteHalf { pub(super) inner: WriteHalfImpl, } impl OwnedWriteHalf { pub fn peer_pid(&self) -> io::Result { match self.inner.is_server() { true => self.inner.client_process_id(), false => self.inner.server_process_id(), } } // TODO use this pub unsafe fn _from_raw_handle(handle: HANDLE) -> io::Result { let inner = unsafe { // SAFETY: as per safety contract WriteHalfImpl::from_raw_handle(handle)? }; Ok(Self { inner }) } fn pinproj(&mut self) -> Pin<&mut WriteHalfImpl> { Pin::new(&mut self.inner) } } impl AsyncWrite for OwnedWriteHalf { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.pinproj().poll_write(cx, buf) } // Those two do nothing fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproj().poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.pinproj().poll_close(cx) } } impl Debug for OwnedWriteHalf { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("local_socket::OwnedWriteHalf") .field("handle", &self.as_raw_handle()) .finish() } } impl AsRawHandle for OwnedWriteHalf { fn as_raw_handle(&self) -> *mut c_void { self.inner.as_raw_handle() } } interprocess-1.2.1/src/os/windows/mailslot.rs000064400000000000000000000003570072674642500174760ustar 00000000000000//! Mailslots – a Windows-specific IPC primitive designed for short multiple-producer-single-consumer message communication with UDP reliability guarantees, which works both on the local system or across the network. // TODO this thing interprocess-1.2.1/src/os/windows/mod.rs000064400000000000000000000140500072674642500164240ustar 00000000000000//! Windows-specific functionality for various interprocess communication primitives, as well as Windows-specific ones. #![cfg_attr(not(windows), allow(warnings))] pub mod named_pipe; #[cfg(any(doc, feature = "signals"))] #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "signals")))] pub mod signal; pub mod unnamed_pipe; // TODO mailslots //pub mod mailslot; #[cfg(windows)] pub(crate) mod local_socket; pub(crate) mod imports; use imports::*; use std::{io, mem::ManuallyDrop, ptr}; /// Objects which own handles which can be shared with another processes. /// /// On Windows, like with most other operating systems, handles belong to specific processes. You shouldn't just send the value of a handle to another process (with a named pipe, for example) and expect it to work on the other side. For this to work, you need [`DuplicateHandle`] – the Win32 API function which duplicates a handle into the handle table of the specified process (the reciever is referred to by its handle). This trait exposes the `DuplicateHandle` functionality in a safe manner. If the handle is *inheritable*, however, all child processes of a process inherit the handle and thus can use the same value safely without the need to share it. *All Windows handle objects created by this crate are inheritable.* /// /// **Implemented for all types inside this crate which implement [`AsRawHandle`] and are supposed to be shared between processes.** /// /// [`DuplicateHandle`]: https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-duplicatehandle " " /// [`AsRawHandle`]: https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html " " pub trait ShareHandle: AsRawHandle { /// Duplicates the handle to make it accessible in the specified process (taken as a handle to that process) and returns the raw value of the handle which can then be sent via some form of IPC, typically named pipes. This is the only way to use any form of IPC other than named pipes to communicate between two processes which do not have a parent-child relationship or if the handle wasn't created as inheritable, therefore named pipes paired with this are a hard requirement in order to communicate between processes if one wasn't spawned by another. /// /// Backed by [`DuplicateHandle`]. Doesn't require unsafe code since `DuplicateHandle` never leads to undefined behavior if the `lpTargetHandle` parameter is a valid pointer, only creates an error. #[allow(clippy::not_unsafe_ptr_arg_deref)] // Handles are not pointers, they have handle checks fn share(&self, reciever: HANDLE) -> io::Result { let (success, new_handle) = unsafe { let mut new_handle = INVALID_HANDLE_VALUE; let success = DuplicateHandle( GetCurrentProcess(), self.as_raw_handle(), reciever, &mut new_handle, 0, 1, 0, ); (success != 0, new_handle) }; if success { Ok(new_handle) } else { Err(io::Error::last_os_error()) } } } #[cfg(windows)] impl ShareHandle for crate::unnamed_pipe::UnnamedPipeReader {} #[cfg(windows)] impl ShareHandle for unnamed_pipe::UnnamedPipeReader {} #[cfg(windows)] impl ShareHandle for crate::unnamed_pipe::UnnamedPipeWriter {} #[cfg(windows)] impl ShareHandle for unnamed_pipe::UnnamedPipeWriter {} /// Newtype wrapper which defines file I/O operations on a `HANDLE` to a file. #[repr(transparent)] #[derive(Debug)] pub(crate) struct FileHandleOps(pub(crate) HANDLE); impl FileHandleOps { pub fn read(&self, buf: &mut [u8]) -> io::Result { debug_assert!( buf.len() <= DWORD::max_value() as usize, "buffer is bigger than maximum buffer size for ReadFile", ); let (success, num_bytes_read) = unsafe { let mut num_bytes_read: DWORD = 0; let result = ReadFile( self.0, buf.as_mut_ptr() as *mut _, buf.len() as DWORD, &mut num_bytes_read as *mut _, ptr::null_mut(), ); (result != 0, num_bytes_read as usize) }; if success { Ok(num_bytes_read) } else { Err(io::Error::last_os_error()) } } pub fn write(&self, buf: &[u8]) -> io::Result { debug_assert!( buf.len() <= DWORD::max_value() as usize, "buffer is bigger than maximum buffer size for WriteFile", ); let (success, bytes_written) = unsafe { let mut number_of_bytes_written: DWORD = 0; let result = WriteFile( self.0, buf.as_ptr() as *mut _, buf.len() as DWORD, &mut number_of_bytes_written as *mut _, ptr::null_mut(), ); (result != 0, number_of_bytes_written as usize) }; if success { Ok(bytes_written) } else { Err(io::Error::last_os_error()) } } pub fn flush(&self) -> io::Result<()> { let success = unsafe { FlushFileBuffers(self.0) != 0 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } } impl Drop for FileHandleOps { fn drop(&mut self) { let _success = unsafe { CloseHandle(self.0) != 0 }; debug_assert!( _success, "failed to close file handle: {}", io::Error::last_os_error() ); } } #[cfg(windows)] impl AsRawHandle for FileHandleOps { fn as_raw_handle(&self) -> HANDLE { self.0 } } #[cfg(windows)] impl IntoRawHandle for FileHandleOps { fn into_raw_handle(self) -> HANDLE { let self_ = ManuallyDrop::new(self); self_.as_raw_handle() } } #[cfg(windows)] impl FromRawHandle for FileHandleOps { unsafe fn from_raw_handle(op: HANDLE) -> Self { Self(op) } } unsafe impl Send for FileHandleOps {} unsafe impl Sync for FileHandleOps {} // WriteFile and ReadFile are thread-safe, apparently interprocess-1.2.1/src/os/windows/named_pipe/enums.rs000064400000000000000000000202640072674642500211010ustar 00000000000000use super::super::imports::*; use std::{convert::TryFrom, mem}; /// The direction of a named pipe connection, designating who can read data and who can write it. This describes the direction of the data flow unambiguously, so that the meaning of the values is the same for the client and server – [`ClientToServer`] always means client → server, for example. /// /// [`ClientToServer`]: enum.PipeDirection.html#variant.ClientToServer " " // I had to type out both the link to the page and the name of the variant since the link can be clicked from module-level documentation so please don't touch it. #[repr(u32)] // We depend on the fact that DWORD always maps to u32, which, thankfully, will always stay true // since the public WinAPI is supposed to be ABI-compatible. Just keep in mind that the // #[repr(u32)] means that we can transmute this enumeration to the Windows DWORD type. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum PipeDirection { /// Represents a server ← client data flow: clients write data, the server reads it. ClientToServer = PIPE_ACCESS_INBOUND, /// Represents a server → client data flow: the server writes data, clients read it. ServerToClient = PIPE_ACCESS_OUTBOUND, /// Represents a server ⇄ client data flow: the server can write data which then is read by the client, while the client writes data which is read by the server. Duplex = PIPE_ACCESS_DUPLEX, } impl PipeDirection { /// Returns the role which the pipe client will have in this direction setting. /// /// # Usage /// ``` /// # #[cfg(windows)] { /// # use interprocess::os::windows::named_pipe::{PipeDirection, PipeStreamRole}; /// assert_eq!( /// PipeDirection::ClientToServer.client_role(), /// PipeStreamRole::Writer, /// ); /// assert_eq!( /// PipeDirection::ServerToClient.client_role(), /// PipeStreamRole::Reader, /// ); /// assert_eq!( /// PipeDirection::Duplex.client_role(), /// PipeStreamRole::ReaderAndWriter, /// ); /// # } /// ``` pub const fn client_role(self) -> PipeStreamRole { match self { Self::ClientToServer => PipeStreamRole::Writer, Self::ServerToClient => PipeStreamRole::Reader, Self::Duplex => PipeStreamRole::ReaderAndWriter, } } /// Returns the role which the pipe server will have in this direction setting. /// /// # Usage /// ``` /// # #[cfg(windows)] { /// # use interprocess::os::windows::named_pipe::{PipeDirection, PipeStreamRole}; /// assert_eq!( /// PipeDirection::ClientToServer.server_role(), /// PipeStreamRole::Reader, /// ); /// assert_eq!( /// PipeDirection::ServerToClient.server_role(), /// PipeStreamRole::Writer, /// ); /// assert_eq!( /// PipeDirection::Duplex.server_role(), /// PipeStreamRole::ReaderAndWriter, /// ); /// # } /// ``` pub const fn server_role(self) -> PipeStreamRole { match self { Self::ClientToServer => PipeStreamRole::Reader, Self::ServerToClient => PipeStreamRole::Writer, Self::Duplex => PipeStreamRole::ReaderAndWriter, } } } impl TryFrom for PipeDirection { type Error = (); /// Converts a Windows constant to a `PipeDirection` if it's in range. /// /// # Errors /// Returns `Err` if the value is not a valid pipe direction constant. fn try_from(op: DWORD) -> Result { assert!((1..=3).contains(&op)); // See the comment block above for why this is safe. unsafe { mem::transmute(op) } } } impl From for DWORD { fn from(op: PipeDirection) -> Self { unsafe { mem::transmute(op) } } } /// Describes the role of a named pipe stream. In constrast to [`PipeDirection`], the meaning of values here is relative – for example, [`Reader`] means [`ServerToClient`] if you're creating a server and [`ClientToServer`] if you're creating a client. /// /// This enumeration is also not layout-compatible with the `PIPE_ACCESS_*` constants, in contrast to [`PipeDirection`]. /// /// [`PipeDirection`]: enum.PipeDirection.html " " /// [`Reader`]: #variant.Reader " " /// [`ServerToClient`]: enum.PipeDirection.html#variant.ServerToClient " " /// [`ClientToServer`]: enum.PipeDirection.html#variant.ClientToServer " " #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[repr(u32)] pub enum PipeStreamRole { /// The stream only reads data. Reader, /// The stream only writes data. Writer, /// The stream both reads and writes data. ReaderAndWriter, } impl PipeStreamRole { /// Returns the data flow direction of the data stream, assuming that the value describes the role of the server. /// /// # Usage /// ``` /// # #[cfg(windows)] { /// # use interprocess::os::windows::named_pipe::{PipeDirection, PipeStreamRole}; /// assert_eq!( /// PipeStreamRole::Reader.direction_as_server(), /// PipeDirection::ClientToServer, /// ); /// assert_eq!( /// PipeStreamRole::Writer.direction_as_server(), /// PipeDirection::ServerToClient, /// ); /// assert_eq!( /// PipeStreamRole::ReaderAndWriter.direction_as_server(), /// PipeDirection::Duplex, /// ); /// # } /// ``` pub const fn direction_as_server(self) -> PipeDirection { match self { Self::Reader => PipeDirection::ClientToServer, Self::Writer => PipeDirection::ServerToClient, Self::ReaderAndWriter => PipeDirection::Duplex, } } /// Returns the data flow direction of the data stream, assuming that the value describes the role of the client. /// /// # Usage /// ``` /// # #[cfg(windows)] { /// # use interprocess::os::windows::named_pipe::{PipeDirection, PipeStreamRole}; /// assert_eq!( /// PipeStreamRole::Reader.direction_as_client(), /// PipeDirection::ServerToClient, /// ); /// assert_eq!( /// PipeStreamRole::Writer.direction_as_client(), /// PipeDirection::ClientToServer, /// ); /// assert_eq!( /// PipeStreamRole::ReaderAndWriter.direction_as_client(), /// PipeDirection::Duplex, /// ); /// # } /// ``` pub const fn direction_as_client(self) -> PipeDirection { match self { Self::Reader => PipeDirection::ServerToClient, Self::Writer => PipeDirection::ClientToServer, Self::ReaderAndWriter => PipeDirection::Duplex, } } } /// Specifies the mode for a pipe stream. #[repr(u32)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum PipeMode { /// Designates that the pipe stream works in byte stream mode, erasing the boundaries of separate messages. Bytes = PIPE_TYPE_BYTE, /// Designates that the pipe stream works in message stream mode, preserving the boundaries of separate messages yet still allowing to read them in byte stream mode. Messages = PIPE_TYPE_MESSAGE, } impl PipeMode { /// Converts the value into a raw `DWORD`-typed constant, either `PIPE_TYPE_BYTE` or `PIPE_TYPE_MESSAGE` depending on the value. pub const fn to_pipe_type(self) -> DWORD { self as _ } /// Converts the value into a raw `DWORD`-typed constant, either `PIPE_READMODE_BYTE` or `PIPE_READMODE_MESSAGE` depending on the value. pub const fn to_readmode(self) -> DWORD { match self { Self::Bytes => PIPE_READMODE_BYTE, Self::Messages => PIPE_READMODE_MESSAGE, } } } impl TryFrom for PipeMode { type Error = (); /// Converts a Windows constant to a `PipeMode` if it's in range. Both `PIPE_TYPE_*` and `PIPE_READMODE_*` are supported. /// /// # Errors /// Returns `Err` if the value is not a valid pipe stream mode constant. fn try_from(op: DWORD) -> Result { // It's nicer to only match than to check and transmute #[allow(unreachable_patterns)] // PIPE_READMODE_BYTE and PIPE_TYPE_BYTE are equal match op { PIPE_TYPE_BYTE | PIPE_READMODE_BYTE => Ok(Self::Bytes), PIPE_READMODE_MESSAGE | PIPE_TYPE_MESSAGE => Ok(Self::Messages), _ => Err(()), } } } interprocess-1.2.1/src/os/windows/named_pipe/listener.rs000064400000000000000000000363520072674642500216040ustar 00000000000000use { crate::os::windows::{ imports::*, named_pipe::{PipeMode, PipeOps, PipeStream, PipeStreamRole}, }, std::{ borrow::Cow, convert::TryInto, ffi::OsStr, fmt::{self, Debug, Formatter}, io, marker::PhantomData, mem::replace, num::{NonZeroU32, NonZeroU8}, ptr, sync::{ atomic::{AtomicBool, Ordering::Relaxed}, Mutex, }, }, to_method::To, }; /// The server for a named pipe, listening for connections to clients and producing pipe streams. /// /// The only way to create a `PipeListener` is to use [`PipeListenerOptions`]. See its documentation for more. pub struct PipeListener { config: PipeListenerOptions<'static>, // We need the options to create new instances nonblocking: AtomicBool, stored_instance: Mutex, _phantom: PhantomData Stream>, } /// An iterator that infinitely [`accept`]s connections on a [`PipeListener`]. /// /// This iterator is created by the [`incoming`] method on [`PipeListener`]. See its documentation for more. /// /// [`accept`]: struct.PipeListener.html#method.accept " " /// [`incoming`]: struct.PipeListener.html#method.incoming " " pub struct Incoming<'a, Stream: PipeStream> { listener: &'a PipeListener, } impl<'a, Stream: PipeStream> Iterator for Incoming<'a, Stream> { type Item = io::Result; fn next(&mut self) -> Option { Some(self.listener.accept()) } } impl<'a, Stream: PipeStream> IntoIterator for &'a PipeListener { type IntoIter = Incoming<'a, Stream>; type Item = as Iterator>::Item; fn into_iter(self) -> Self::IntoIter { self.incoming() } } impl PipeListener { /// Blocks until a client connects to the named pipe, creating a `Stream` to communicate with the pipe. /// /// See `incoming` for an iterator version of this. pub fn accept(&self) -> io::Result { let instance_to_hand_out = { let mut stored_instance = self.stored_instance.lock().expect("unexpected lock poison"); // Doesn't actually even need to be atomic to begin with, but it's simpler and more // convenient to do this instead. The mutex takes care of ordering. let nonblocking = self.nonblocking.load(Relaxed); stored_instance.connect_server()?; let new_instance = self.create_instance(nonblocking)?; replace(&mut *stored_instance, new_instance) }; Ok(Stream::build(super::stream::Instance::new( instance_to_hand_out, true, ))) } /// Creates an iterator which accepts connections from clients, blocking each time `next()` is called until one connects. pub fn incoming(&self) -> Incoming<'_, Stream> { Incoming { listener: self } } /// Enables or disables the nonblocking mode for all existing instances of the listener and future ones. By default, it is disabled. /// /// This should ideally be done during creation, using the [`nonblocking` field] of the creation options, unless there's a good reason not to. This allows making one less system call during creation. /// /// See the documentation of the aforementioned field for the exact effects of enabling this mode. /// /// [`nonblocking` field]: struct.PipeListenerOptions.html#structfield.nonblocking " " pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let instance = self.stored_instance.lock().expect("unexpected lock poison"); // Doesn't actually even need to be atomic to begin with, but it's simpler and more // convenient to do this instead. The mutex takes care of ordering. self.nonblocking.store(nonblocking, Relaxed); unsafe { super::set_nonblocking_for_stream( instance.as_raw_handle(), Stream::READ_MODE, nonblocking, )?; } // Make it clear that the lock survives until this moment. drop(instance); Ok(()) } fn create_instance(&self, nonblocking: bool) -> io::Result { let handle = self.config.create_instance( false, nonblocking, false, Stream::ROLE, Stream::READ_MODE, )?; // SAFETY: we just created this handle Ok(unsafe { PipeOps::from_raw_handle(handle) }) } } impl Debug for PipeListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("PipeListener") .field("config", &self.config) .field("instance", &self.stored_instance) .field("nonblocking", &self.nonblocking.load(Relaxed)) .finish() } } /// Allows for thorough customization of [`PipeListener`]s during creation. #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] pub struct PipeListenerOptions<'a> { /// Specifies the name for the named pipe. Since the name typically, but not always, is a string literal, an owned string does not need to be provided. pub name: Cow<'a, OsStr>, /// Specifies how data is written into the data stream. This is required in all cases, regardless of whether the pipe is inbound, outbound or duplex, since this affects all data being written into the pipe, not just the data written by the server. pub mode: PipeMode, /// Specifies whether nonblocking mode will be enabled for all stream instances upon creation. By default, it is disabled. /// /// There are two ways in which the listener is affected by nonblocking mode: /// - Whenever [`accept`] is called or [`incoming`] is being iterated through, if there is no client currently attempting to connect to the named pipe server, the method will return immediately with the [`WouldBlock`] error instead of blocking until one arrives. /// - The streams created by [`accept`] and [`incoming`] behave similarly to how client-side streams behave in nonblocking mode. See the documentation for `set_nonblocking` for an explanation of the exact effects. /// /// [`accept`]: struct.PipeListener.html#method.accept /// [`incoming`]: struct.PipeListener.html#method.incoming /// [`WouldBlock`]: io::ErrorKind::WouldBlock pub nonblocking: bool, /// Specifies the maximum amount of instances of the pipe which can be created, i.e. how many clients can be communicated with at once. If set to 1, trying to create multiple instances at the same time will return an error. If set to `None`, no limit is applied. The value 255 is not allowed because of Windows limitations. pub instance_limit: Option, /// Enables write-through mode, which applies only to network connections to the pipe. If enabled, writing to the pipe would always block until all data is delivered to the other end instead of piling up in the kernel's network buffer until a certain amount of data accamulates or a certain period of time passes, which is when the system actually sends the contents of the buffer over the network. /// /// Not required for pipes which are restricted to local connections only. If debug assertions are enabled, setting this parameter on a local-only pipe will cause a panic when the pipe is created; in release builds, creation will successfully complete without any errors and the flag will be completely ignored. pub write_through: bool, /// Enables remote machines to connect to the named pipe over the network. pub accept_remote: bool, /// Specifies how big the input buffer should be. The system will automatically adjust this size to align it as required or clip it by the minimum or maximum buffer size. // TODO change into DWORD, i.e. u32 pub input_buffer_size_hint: usize, /// Specifies how big the output buffer should be. The system will automatically adjust this size to align it as required or clip it by the minimum or maximum buffer size. pub output_buffer_size_hint: usize, /// The default timeout when waiting for a client to connect. Used unless another timeout is specified when waiting for a client. pub wait_timeout: NonZeroU32, } macro_rules! genset { // TODO get rid of this $namel thing when bumping MSRV in 2.0.0 ($name:ident $namel:literal : $ty:ty) => { #[doc = "Sets the [`"] #[doc = $namel] #[doc = "`](#structfield."] #[doc = $namel] #[doc = ") parameter to the specified value."] #[must_use = "builder setters take the entire structure and return the result"] pub fn $name(mut self, $name: impl Into<$ty>) -> Self { self.$name = $name.into(); self } }; ($($name:ident $namel:literal : $ty:ty),+ $(,)?) => { $(genset!($name $namel : $ty);)+ }; } impl<'a> PipeListenerOptions<'a> { /// Creates a new builder with default options. pub fn new() -> Self { Self { name: Cow::Borrowed(OsStr::new("")), mode: PipeMode::Bytes, nonblocking: false, instance_limit: None, write_through: false, accept_remote: false, input_buffer_size_hint: 512, output_buffer_size_hint: 512, wait_timeout: NonZeroU32::new(50).unwrap(), } } /// Clones configuration options which are not owned by value and returns a copy of the original option table which is guaranteed not to borrow anything and thus ascribes to the `'static` lifetime. /// /// This is used instead of the `ToOwned` trait for backwards compatibility – this will be fixed in the next breaking release. pub fn to_owned(&self) -> PipeListenerOptions<'static> { // We need this ugliness because the compiler does not understand that // PipeListenerOptions<'a> can coerce into PipeListenerOptions<'static> if we manually // replace the name field with Cow::Owned and just copy all other elements over thanks // to the fact that they don't contain a mention of the lifetime 'a. Tbh we need an // RFC for this, would be nice. PipeListenerOptions { name: Cow::Owned(self.name.clone().into_owned()), mode: self.mode, nonblocking: self.nonblocking, instance_limit: self.instance_limit, write_through: self.write_through, accept_remote: self.accept_remote, input_buffer_size_hint: self.input_buffer_size_hint, output_buffer_size_hint: self.output_buffer_size_hint, wait_timeout: self.wait_timeout, } } genset!( name "name": Cow<'a, OsStr>, mode "mode": PipeMode, nonblocking "nonblocking": bool, instance_limit "instance_limit": Option, write_through "write_through": bool, accept_remote "accept_remote": bool, input_buffer_size_hint "input_buffer_size_hint": usize, output_buffer_size_hint "output_buffer_size_hint": usize, wait_timeout "wait_timeout": NonZeroU32, ); /// Creates an instance of a pipe for a listener with the specified stream type and with the first-instance flag set to the specified value. pub(super) fn create_instance( &self, first: bool, nonblocking: bool, overlapped: bool, role: PipeStreamRole, read_mode: Option, ) -> io::Result { if read_mode == Some(PipeMode::Messages) && self.mode == PipeMode::Bytes { return Err(io::Error::new( io::ErrorKind::InvalidInput, "\ cannot create pipe server that has byte type but reads messages – have you forgotten to set the \ `mode` field in `PipeListenerOptions`?", )); } let path = super::convert_path(&self.name, None); let open_mode = self.to_open_mode(first, role, overlapped); let pipe_mode = self.to_pipe_mode(read_mode, nonblocking); let (handle, success) = unsafe { // TODO security attributes let handle = CreateNamedPipeW( path.as_ptr(), open_mode, pipe_mode, self.instance_limit.map_or(255, |x| { assert!(x.get() != 255, "cannot set 255 as the named pipe instance limit due to 255 being a reserved value"); x.get().to::() }), self.output_buffer_size_hint.try_into() .expect("output buffer size hint overflowed DWORD"), self.input_buffer_size_hint.try_into() .expect("input buffer size hint overflowed DWORD"), self.wait_timeout.get(), ptr::null_mut(), ); (handle, handle != INVALID_HANDLE_VALUE) }; if success { Ok(handle) } else { Err(io::Error::last_os_error()) } } /// Creates the pipe listener from the builder. The `Stream` generic argument specifies the type of pipe stream that the listener will create, thus determining the direction of the pipe and its mode. /// /// For outbound or duplex pipes, the `mode` parameter must agree with the `Stream`'s `WRITE_MODE`. Otherwise, the call will panic in debug builds or, in release builds, the `WRITE_MODE` will take priority. pub fn create(&self) -> io::Result> { let (owned_config, instance) = self._create(Stream::ROLE, Stream::READ_MODE)?; let nonblocking = owned_config.nonblocking.into(); Ok(PipeListener { config: owned_config, nonblocking, stored_instance: Mutex::new(instance), _phantom: PhantomData, }) } fn _create( &self, role: PipeStreamRole, read_mode: Option, ) -> io::Result<(PipeListenerOptions<'static>, PipeOps)> { let owned_config = self.to_owned(); let instance = { let handle = self.create_instance(true, self.nonblocking, false, role, read_mode)?; unsafe { // SAFETY: we just created this handle, so we know it's unique (and we've checked // that it's valid) PipeOps::from_raw_handle(handle) } }; Ok((owned_config, instance)) } fn to_open_mode(&self, first: bool, role: PipeStreamRole, overlapped: bool) -> DWORD { let mut open_mode = 0_u32; open_mode |= role.direction_as_server().to::(); if first { open_mode |= FILE_FLAG_FIRST_PIPE_INSTANCE; } if self.write_through { open_mode |= FILE_FLAG_WRITE_THROUGH; } if overlapped { open_mode |= FILE_FLAG_OVERLAPPED; } open_mode } fn to_pipe_mode(&self, read_mode: Option, nonblocking: bool) -> DWORD { let mut pipe_mode = 0_u32; pipe_mode |= self.mode.to_pipe_type(); pipe_mode |= read_mode.map_or(0, PipeMode::to_readmode); if nonblocking { pipe_mode |= PIPE_NOWAIT; } if !self.accept_remote { pipe_mode |= PIPE_REJECT_REMOTE_CLIENTS; } pipe_mode } } impl Default for PipeListenerOptions<'_> { fn default() -> Self { Self::new() } } interprocess-1.2.1/src/os/windows/named_pipe/mod.rs000064400000000000000000000077200072674642500205330ustar 00000000000000//! Support for named pipes on Windows. //! //! ## Windows named pipes are not Unix named pipes //! The term "named pipe" refers to completely different things in Unix and Windows. For this reason, Unix named pipes are referred to as "FIFO files" to avoid confusion with the more powerful Windows named pipes. In fact, the only common features for those two is that they both can be located using filesystem paths and they both use a stream interface. The differences can be summed up like this: //! - Windows named pipes are located on a separate filesystem (NPFS – **N**amed **P**ipe **F**ile**s**ystem), while Unix FIFO files live in the shared filesystem tree together with all other files //! - On Linux, the implementation of Unix domain sockets exposes a similar feature: by setting the first byte in the socket file path to `NULL` (`\0`), the socket is placed into a separate namespace instead of being placed on the filesystem; this is a non-standard extension to POSIX and is not available on other Unix systems //! - Windows named pipes have a server and an arbitrary number of clients, meaning that the separate processes connecting to a named pipe have separate connections to the server, while Unix FIFO files don't have the notion of a server or client and thus mix all data written into one sink from which the data is read by one process //! - Windows named pipes can be used over the network, while a Unix FIFO file is still local even if created in a directory which is a mounted network filesystem //! - Windows named pipes can maintain datagram boundaries, allowing both sides of the connection to operate on separate messages rather than on a byte stream, while FIFO files, like any other type of file, expose only a byte stream interface //! //! If you carefully read through this list, you'd notice how Windows named pipes are similar to Unix domain sockets. For this reason, the implementation of "local sockets" in the `local_socket` module of this crate uses named pipes on Windows and Ud-sockets on Unix. // TODO improve docs // TODO add examples // TODO get rid of the dumbass autoflush, literally no reason for me to have added it now that i // actually write proper examples for this // FIXME message streams should have methods instead of I/O traits mod enums; mod listener; mod pipeops; #[macro_use] mod stream; pub use enums::*; pub use listener::*; pub use stream::*; #[cfg(any(doc, feature = "tokio_support"))] #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "tokio_support")))] #[cfg_attr(not(feature = "tokio_support"), allow(unused_imports))] pub mod tokio; use pipeops::*; use { super::imports::*, std::{ ffi::{OsStr, OsString}, io, ptr, }, }; fn convert_path(pipe_name: &OsStr, hostname: Option<&OsStr>) -> Vec { static PREFIX_LITERAL: &str = r"\\"; static PIPEFS_LITERAL: &str = r"\pipe\"; let hostname = hostname.unwrap_or_else(|| OsStr::new(".")); let mut path = OsString::with_capacity( PREFIX_LITERAL.len() + hostname.len() + PIPEFS_LITERAL.len() + pipe_name.len(), ); path.push(PREFIX_LITERAL); path.push(hostname); path.push(PIPEFS_LITERAL); path.push(pipe_name); let mut path = path.encode_wide().collect::>(); path.push(0); // encode_wide does not include the terminating NULL, so we have to add it ourselves path } #[cfg(windows)] unsafe fn set_nonblocking_for_stream( handle: HANDLE, read_mode: Option, nonblocking: bool, ) -> io::Result<()> { let read_mode: u32 = read_mode.map_or(0, PipeMode::to_readmode); // Bitcast the boolean without additional transformations since // the flag is in the first bit. let mut mode: u32 = read_mode | nonblocking as u32; let success = unsafe { SetNamedPipeHandleState( handle, &mut mode as *mut _, ptr::null_mut(), ptr::null_mut(), ) } != 0; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } interprocess-1.2.1/src/os/windows/named_pipe/pipeops.rs000064400000000000000000000214520072674642500214310ustar 00000000000000use crate::os::windows::{imports::*, named_pipe::stream::Instance, FileHandleOps}; use std::{ fmt::{self, Debug, Formatter}, io, mem::ManuallyDrop, ptr, }; /// The actual implementation of a named pipe server or client. #[repr(transparent)] pub struct PipeOps(pub(crate) FileHandleOps); impl PipeOps { /// Reads a message from the pipe instance into the specified buffer, returning the size of the message written as `Ok(Ok(...))`. If the buffer is too small to fit the message, a bigger buffer is allocated and returned as `Ok(Err(...))`, with the exact size and capacity to hold the message. Errors are returned as `Err(Err(...))`. pub fn read_msg(&self, buf: &mut [u8]) -> io::Result>> { match self.try_read_msg(buf)? { Ok(bytes_read) => Ok(Ok(bytes_read)), Err(bytes_left_in_message) => { let mut new_buffer = vec![0; bytes_left_in_message]; let mut _number_of_bytes_read: DWORD = 0; let success = unsafe { ReadFile( self.as_raw_handle(), new_buffer.as_mut_slice().as_mut_ptr() as *mut _, buf.len() as DWORD, &mut _number_of_bytes_read as *mut _, ptr::null_mut(), ) != 0 }; if success { Ok(Err(new_buffer)) } else { Err(io::Error::last_os_error()) } } } } pub fn try_read_msg(&self, buf: &mut [u8]) -> io::Result> { debug_assert!( buf.len() <= DWORD::max_value() as usize, "buffer is bigger than maximum buffer size for ReadFile", ); let bytes_left_in_message = unsafe { let mut bytes_left_in_message: DWORD = 0; let result = PeekNamedPipe( self.as_raw_handle(), ptr::null_mut(), 0, ptr::null_mut(), ptr::null_mut(), &mut bytes_left_in_message as *mut _, ); if result == 0 { return Err(io::Error::last_os_error()); } bytes_left_in_message as usize }; if buf.len() >= bytes_left_in_message { // We already know the exact size of the message which is why this does not matter let mut _number_of_bytes_read: DWORD = 0; let success = unsafe { ReadFile( self.as_raw_handle(), buf.as_mut_ptr() as *mut _, buf.len() as DWORD, &mut _number_of_bytes_read as *mut _, ptr::null_mut(), ) != 0 }; if success { Ok(Ok(bytes_left_in_message)) } else { Err(io::Error::last_os_error()) } } else { Ok(Err(bytes_left_in_message)) } } /// Reads bytes from the named pipe. Mirrors `std::io::Read`. pub fn read_bytes(&self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } /// Writes data to the named pipe. There is no way to check/ensure that the message boundaries will be preserved which is why there's only one function to do this. pub fn write(&self, buf: &[u8]) -> io::Result { self.0.write(buf) } /// Blocks until the client has fully read the buffer. pub fn flush(&self) -> io::Result<()> { self.0.flush() } pub fn get_client_process_id(&self) -> io::Result { unsafe { self.hget(GetNamedPipeClientProcessId) } } pub fn get_client_session_id(&self) -> io::Result { unsafe { self.hget(GetNamedPipeClientSessionId) } } pub fn get_server_process_id(&self) -> io::Result { unsafe { self.hget(GetNamedPipeServerProcessId) } } pub fn get_server_session_id(&self) -> io::Result { unsafe { self.hget(GetNamedPipeServerSessionId) } } unsafe fn hget( &self, f: unsafe extern "system" fn(HANDLE, *mut u32) -> BOOL, ) -> io::Result { let mut x: u32 = 0; let success = unsafe { f(self.0 .0, &mut x as *mut _) != 0 }; if success { Ok(x) } else { Err(io::Error::last_os_error()) } } /// Retrieves whether the pipe is a server or not from the kernel directly. pub fn is_server(&self) -> io::Result { // Source: https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-getnamedpipeinfo const PIPE_IS_SERVER_BIT: u32 = 0x00000001; let flags = self.get_flags()?; Ok((flags & PIPE_IS_SERVER_BIT) != 0) } /// Retrieves whether the pipe has message boundaries or not from the kernel directly. pub fn does_pipe_have_message_boundaries(&self) -> io::Result { // Same source. const PIPE_IS_MESSAGE_BIT: u32 = 0x00000004; let flags = self.get_flags()?; Ok((flags & PIPE_IS_MESSAGE_BIT) != 0) } /* // Disabled because .get_state() is disabled. /// Retrieves whether the pipe is in nonblocking mode or not from the kernel directly. pub fn is_nonblocking(&self) -> io::Result { // Source: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-getnamedpipehandlestatew const PIPE_IS_NONBLOCKING_BIT: u32 = 0x00000001; let state = self.get_state()?; Ok((state & PIPE_IS_NONBLOCKING_BIT) != 0) } */ fn get_flags(&self) -> io::Result { let mut flags: u32 = 0; let success = unsafe { GetNamedPipeInfo( self.0 .0, &mut flags as *mut _, ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), ) != 0 }; if success { Ok(flags) } else { Err(io::Error::last_os_error()) } } // Doesn't work for server-write-only pipes, requires FILE_READ_ATTRIBUTES which I can't get // from CreateNamedPipe. /*fn get_state(&self) -> io::Result { let mut state: u32 = 0; let success = unsafe { GetNamedPipeHandleStateW( self.0 .0, &mut state as *mut _, ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), 0, ) != 0 }; if success { Ok(state) } else { Err(io::Error::last_os_error()) } }*/ /// Blocks until connected. If connected, does not do anything. pub fn connect_server(&self) -> io::Result<()> { let success = unsafe { ConnectNamedPipe(self.as_raw_handle(), ptr::null_mut()) != 0 }; if success { Ok(()) } else { let last_error = io::Error::last_os_error(); if last_error.raw_os_error() == Some(ERROR_PIPE_CONNECTED as i32) { Ok(()) } else { Err(last_error) } } } /// Flushes and disconnects, obviously. pub fn flush_and_disconnect(&self) -> io::Result<()> { self.flush()?; self.disconnect()?; Ok(()) } /// Disconnects without flushing. Drops all data which has been sent but not yet received on the other side, if any. pub fn disconnect(&self) -> io::Result<()> { let success = unsafe { DisconnectNamedPipe(self.as_raw_handle()) != 0 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } /// Called by pipe streams when dropped, used to abstract over the fact that non-async streams flush before returning the pipe to the server while async ones don't. pub fn server_drop_disconnect(&self) { let _ = self.flush_and_disconnect(); } } impl Debug for PipeOps { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { Debug::fmt(&self.0, f) } } #[cfg(windows)] impl AsRawHandle for PipeOps { fn as_raw_handle(&self) -> HANDLE { self.0 .0 // I hate this nested tuple syntax. } } #[cfg(windows)] impl IntoRawHandle for PipeOps { fn into_raw_handle(self) -> HANDLE { let self_ = ManuallyDrop::new(self); self_.as_raw_handle() } } #[cfg(windows)] impl FromRawHandle for PipeOps { unsafe fn from_raw_handle(handle: HANDLE) -> Self { let fho = unsafe { FileHandleOps::from_raw_handle(handle) }; Self(fho) } } // SAFETY: we don't expose reading/writing for immutable references of PipeInstance unsafe impl Sync for PipeOps {} unsafe impl Send for PipeOps {} pub trait PipeStreamInternals { #[cfg(windows)] fn build(instance: Instance) -> Self; } interprocess-1.2.1/src/os/windows/named_pipe/stream.rs000064400000000000000000000527340072674642500212540ustar 00000000000000use crate::os::windows::{ imports::*, named_pipe::{PipeMode, PipeOps, PipeStreamInternals, PipeStreamRole}, AsRawHandle, FromRawHandle, IntoRawHandle, }; use crate::{PartialMsgWriteError, ReliableReadMsg}; use std::{ ffi::OsStr, fmt::{self, Debug, Formatter}, io::{self, Read, Write}, mem::ManuallyDrop, ptr, }; mod inst { use super::*; /// Wrapper for sync `PipeOps` to make the macro work. Will be gone soon once I redesign the API to use generics. pub struct Instance { ops: PipeOps, is_server: bool, } impl Instance { pub fn create_non_taken(ops: PipeOps) -> Self { Self::new(ops, false) } pub fn new(ops: PipeOps, is_server: bool) -> Self { Self { ops, is_server } } pub fn instance(&self) -> &PipeOps { &self.ops } pub fn is_server(&self) -> bool { self.is_server } pub fn is_split(&self) -> bool { // sync pipes don't implement splitting yet false } } } pub(super) use inst::*; macro_rules! create_stream_type_base { ( $ty:ident: extra_methods: {$($extra_methods:tt)*}, doc: $doc:tt ) => { #[doc = $doc] pub struct $ty { instance: Instance, } impl $ty { // fn is_server(&self) -> bool and fn is_client(&self) -> bool // generated by downstream macros $($extra_methods)* fn ops(&self) -> &PipeOps { self.instance.instance() } /// Retrieves the process identifier of the client side of the named pipe connection. pub fn client_process_id(&self) -> io::Result { self.ops().get_client_process_id() } /// Retrieves the session identifier of the client side of the named pipe connection. pub fn client_session_id(&self) -> io::Result { self.ops().get_client_session_id() } /// Retrieves the process identifier of the server side of the named pipe connection. pub fn server_process_id(&self) -> io::Result { self.ops().get_server_process_id() } /// Retrieves the session identifier of the server side of the named pipe connection. pub fn server_session_id(&self) -> io::Result { self.ops().get_server_session_id() } /// Disconnects the named pipe stream without flushing buffers, causing all data in those buffers to be lost. This is much faster (and, in some case, the only finite-time way of ending things) than simply dropping the stream, since, for non-async named pipes, the `Drop` implementation flushes first. /// /// Only makes sense for server-side pipes and will return an error if called on a client stream. *For async pipe streams, this is the same as dropping the pipe.* pub fn disconnect_without_flushing(self) -> io::Result<()> { if self.is_split() { return Err(io::Error::new( io::ErrorKind::Other, "cannot abruptly disconnect a pipe stream which has been split", )); } self.ops().disconnect()?; let self_ = ManuallyDrop::new(self); let instance = unsafe { // SAFETY: ManuallyDrop is used to safely destroy the invalidated original ptr::read(&self_.instance) }; drop(instance); Ok(()) } fn is_split(&self) -> bool { self.instance.is_split() } } #[doc(hidden)] impl crate::Sealed for $ty {} #[doc(hidden)] impl PipeStreamInternals for $ty { #[cfg(windows)] fn build(instance: Instance) -> Self { Self { instance } } } impl Drop for $ty { fn drop(&mut self) { if !self.is_split() { if self.is_server() { let _ = self.ops().server_drop_disconnect(); } } } } impl AsRawHandle for $ty { #[cfg(windows)] fn as_raw_handle(&self) -> HANDLE { self.ops().as_raw_handle() } } impl Debug for $ty { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct(stringify!($ty)) .field("handle", &self.as_raw_handle()) .finish() } } }; } macro_rules! create_stream_type { ( $ty:ident: desired_access: $desired_access:expr, role: $role:expr, read_mode: $read_mode:expr, write_mode: $write_mode:expr, doc: $doc:tt ) => { create_stream_type_base!( $ty: extra_methods: { /// Connects to the specified named pipe (the `\\.\pipe\` prefix is added automatically), blocking until a server instance is dispatched. pub fn connect(name: impl AsRef) -> io::Result { Self::_connect(name.as_ref()) } fn _connect(name: &OsStr) -> io::Result { let pipeops = _connect( name, None, Self::READ_MODE.is_some(), Self::WRITE_MODE.is_some(), WaitTimeout::DEFAULT, )?; Ok(Self { instance: Instance::create_non_taken(pipeops) }) } /// Connects to the specified named pipe at a remote computer (the `\\\pipe\` prefix is added automatically), blocking until a server instance is dispatched. pub fn connect_to_remote(pipe_name: impl AsRef, hostname: impl AsRef) -> io::Result { Self::_connect_to_remote(pipe_name.as_ref(), hostname.as_ref()) } fn _connect_to_remote(pipe_name: &OsStr, hostname: &OsStr) -> io::Result { let pipeops = _connect( pipe_name, Some(hostname), Self::READ_MODE.is_some(), Self::WRITE_MODE.is_some(), WaitTimeout::DEFAULT, )?; Ok(Self { instance: Instance::create_non_taken(pipeops) }) } /// Sets whether the nonblocking mode for the pipe stream is enabled. By default, it is disabled. /// /// In nonblocking mode, attempts to read from the pipe when there is no data available or to write when the buffer has filled up because the receiving side did not read enough bytes in time will never block like they normally do. Instead, a [`WouldBlock`] error is immediately returned, allowing the thread to perform useful actions in the meantime. /// /// *If called on the server side, the flag will be set only for one stream instance.* A listener creation option, [`nonblocking`], and a similar method on the listener, [`set_nonblocking`], can be used to set the mode in bulk for all current instances and future ones. /// /// [`WouldBlock`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.WouldBlock " " /// [`nonblocking`]: struct.PipeListenerOptions.html#structfield.nonblocking " " /// [`set_nonblocking`]: struct.PipeListener.html#method.set_nonblocking " " pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { unsafe { super::set_nonblocking_for_stream(self.as_raw_handle(), Self::READ_MODE, nonblocking) } } /// Returns `true` if the stream was created by a listener (server-side), `false` if it was created by connecting to a server (server-side). pub fn is_server(&self) -> bool { self.instance.is_server() } /// Returns `true` if the stream was created by connecting to a server (client-side), `false` if it was created by a listener (server-side). pub fn is_client(&self) -> bool { !self.is_server() } }, doc: $doc ); impl FromRawHandle for $ty { #[cfg(windows)] unsafe fn from_raw_handle(handle: HANDLE) -> Self { let pipeops = unsafe { // SAFETY: guaranteed via safety contract PipeOps::from_raw_handle(handle) }; let is_server = pipeops.is_server().expect("\ failed to determine if pipe was server-side or client-side during construction from raw handle"); // If the wrapper type tries to read incoming data as messages, that might break if // the underlying pipe has no message boundaries. Let's check for that. if Self::READ_MODE == Some(PipeMode::Messages) { let has_msg_boundaries = pipeops.does_pipe_have_message_boundaries().expect("\ failed to determine whether the pipe preserves message boundaries"); assert!(has_msg_boundaries, "\ stream wrapper type uses a message-based read mode, but the underlying pipe does not preserve \ message boundaries"); } let instance = Instance::new(pipeops, is_server); Self { instance } } } impl IntoRawHandle for $ty { #[cfg(windows)] fn into_raw_handle(self) -> HANDLE { assert!(self.is_client(), "cannot reclaim named pipe instance from server instancer"); let handle = self.ops().as_raw_handle(); handle } } impl PipeStream for $ty { const ROLE: PipeStreamRole = $role; const WRITE_MODE: Option = $write_mode; const READ_MODE: Option = $read_mode; } }; ($( $ty:ident: desired_access: $desired_access:expr, role: $role:expr, read_mode: $read_mode:expr, write_mode: $write_mode:expr, doc: $doc:tt )+) => { $(create_stream_type!( $ty: desired_access: $desired_access, role: $role, read_mode: $read_mode, write_mode: $write_mode, doc: $doc );)+ }; } create_stream_type! { ByteReaderPipeStream: desired_access: GENERIC_READ, role: PipeStreamRole::Reader, read_mode: Some(PipeMode::Bytes), write_mode: None, doc: " [Byte stream reader] for a named pipe. Created either by using `PipeListener` or by connecting to a named pipe server. [Byte stream reader]: https://doc.rust-lang.org/std/io/trait.Read.html " ByteWriterPipeStream: desired_access: GENERIC_WRITE, role: PipeStreamRole::Writer, read_mode: None, write_mode: Some(PipeMode::Bytes), doc: " [Byte stream writer] for a named pipe. Created either by using `PipeListener` or by connecting to a named pipe server. [Byte stream writer]: https://doc.rust-lang.org/std/io/trait.Write.html " DuplexBytePipeStream: desired_access: GENERIC_READ | GENERIC_WRITE, role: PipeStreamRole::ReaderAndWriter, read_mode: Some(PipeMode::Bytes), write_mode: Some(PipeMode::Bytes), doc: " Byte stream [reader] and [writer] for a named pipe. Created either by using `PipeListener` or by connecting to a named pipe server. [reader]: https://doc.rust-lang.org/std/io/trait.Read.html [writer]: https://doc.rust-lang.org/std/io/trait.Write.html " MsgReaderPipeStream: desired_access: GENERIC_READ, role: PipeStreamRole::Reader, read_mode: Some(PipeMode::Messages), write_mode: None, doc: " [Message stream reader] for a named pipe. Created either by using `PipeListener` or by connecting to a named pipe server. [Message stream reader]: https://doc.rust-lang.org/std/io/trait.Read.html " MsgWriterPipeStream: desired_access: GENERIC_WRITE, role: PipeStreamRole::Writer, read_mode: None, write_mode: Some(PipeMode::Messages), doc: " [Message stream writer] for a named pipe. Created either by using `PipeListener` or by connecting to a named pipe server. [Message stream writer]: https://doc.rust-lang.org/std/io/trait.Write.html " DuplexMsgPipeStream: desired_access: GENERIC_READ | GENERIC_WRITE, role: PipeStreamRole::ReaderAndWriter, read_mode: Some(PipeMode::Messages), write_mode: Some(PipeMode::Messages), doc: " Message stream [reader] and [writer] for a named pipe. Created either by using `PipeListener` or by connecting to a named pipe server. [reader]: https://doc.rust-lang.org/std/io/trait.Read.html [writer]: https://doc.rust-lang.org/std/io/trait.Write.html " } impl Read for ByteReaderPipeStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.ops().read_bytes(buf) } } impl Write for ByteWriterPipeStream { fn write(&mut self, buf: &[u8]) -> io::Result { self.ops().write(buf) } fn flush(&mut self) -> io::Result<()> { self.ops().flush() } } impl Read for DuplexBytePipeStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.ops().read_bytes(buf) } } impl Write for DuplexBytePipeStream { fn write(&mut self, buf: &[u8]) -> io::Result { self.ops().write(buf) } fn flush(&mut self) -> io::Result<()> { self.ops().flush() } } impl Read for MsgReaderPipeStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.ops().read_bytes(buf) } } impl ReliableReadMsg for MsgReaderPipeStream { fn read_msg(&mut self, buf: &mut [u8]) -> io::Result>> { self.ops().read_msg(buf) } fn try_read_msg(&mut self, buf: &mut [u8]) -> io::Result> { self.ops().try_read_msg(buf) } } impl Write for MsgWriterPipeStream { fn write(&mut self, buf: &[u8]) -> io::Result { if self.ops().write(buf)? == buf.len() { Ok(buf.len()) } else { Err(io::Error::new(io::ErrorKind::Other, PartialMsgWriteError)) } } fn flush(&mut self) -> io::Result<()> { self.ops().flush() } } impl Read for DuplexMsgPipeStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.ops().read_bytes(buf) } } impl ReliableReadMsg for DuplexMsgPipeStream { fn read_msg(&mut self, buf: &mut [u8]) -> io::Result>> { self.ops().read_msg(buf) } fn try_read_msg(&mut self, buf: &mut [u8]) -> io::Result> { self.ops().try_read_msg(buf) } } impl Write for DuplexMsgPipeStream { fn write(&mut self, buf: &[u8]) -> io::Result { if self.ops().write(buf)? == buf.len() { Ok(buf.len()) } else { Err(io::Error::new(io::ErrorKind::Other, PartialMsgWriteError)) } } fn flush(&mut self) -> io::Result<()> { self.ops().flush() } } /// Defines the properties of pipe stream types. /// /// ## Why there are multiple types of pipe streams /// One of the similarities between Unix domain sockets and Windows named pipes is how both can be used in datagram mode and in byte stream mode, that is, like with sockets, Windows named pipes can both maintain the boundaries between packets or erase those boundaries – the specific behavior can be controlled both during pipe creation and during connection. The reader can still use the stream interface even if the writer maintains datagram boundaries, and vice versa: the system automatically disassembles the datagrams into a byte stream with virtually no cost. /// /// The distinction between datagram-oriented connections and byte streams exists for symmetry with the standard library, where UDP and TCP sockets are represented by different types. The idea behind this is that by separating the two semantic types of sockets into two types, the distinction between those semantics can be enforced at compile time instead of using runtime errors to signal that, for example, a datagram read operation is attempted on a byte stream. /// /// The fact that named pipes can have different data flow directions further increases the amount of various stream types. By restricting the implemented stream traits at compile time, named pipe streams can be used correctly in generic contexts unaware of named pipes without extra runtime checking for the correct pipe direction. pub trait PipeStream: AsRawHandle + IntoRawHandle + FromRawHandle + PipeStreamInternals { /// The data stream flow direction for the pipe. See the [`PipeStreamRole`] enumeration for more on what this means. const ROLE: PipeStreamRole; /// The data stream mode for the pipe. If set to `PipeMode::Bytes`, message boundaries will broken and having `READ_MODE` at `PipeMode::Messages` would be a pipe creation error. /// /// For reader streams, this value has no meaning: if the reader stream belongs to the server (client sends data, server receives), then `READ_MODE` takes the role of this value; if the reader stream belongs to the client, there is no visible difference to how the server writes data since the client specifies its read mode itself anyway. const WRITE_MODE: Option; /// The data stream mode used when reading from the pipe: if `WRITE_MODE` is `PipeMode::Messages` and `READ_MODE` is `PipeMode::Bytes`, the message boundaries will be destroyed when reading even though they are retained when written. See the `PipeMode` enumeration for more on what those modes mean. /// /// For writer streams, this value has no meaning: if the writer stream belongs to the server (server sends data, client receives), then the server doesn't read data at all and thus this does not affect anything; if the writer stream belongs to the client, then the client doesn't read anything and the value is meaningless as well. const READ_MODE: Option; } /// Tries to connect to the specified named pipe (the `\\.\pipe\` prefix is added automatically), returning a named pipe stream of the stream type provided via generic parameters. If there is no available server, returns immediately. /// /// Since named pipes can work across multiple machines, an optional hostname can be supplied. Leave it at `None` if you're using named pipes on the local machine exclusively, which is most likely the case. #[deprecated(note = "\ poor ergonomics: you can't use turbofish syntax due to `impl AsRef` parameters and you \ have to use `None::<&OsStr>` instead of just `None` to provide an empty hostname")] pub fn connect( pipe_name: impl AsRef, hostname: Option>, ) -> io::Result { let pipeops = _connect( pipe_name.as_ref(), hostname.as_ref().map(AsRef::as_ref), Stream::READ_MODE.is_some(), Stream::WRITE_MODE.is_some(), WaitTimeout::DEFAULT, )?; let instance = Instance::create_non_taken(pipeops); Ok(Stream::build(instance)) } fn _connect( pipe_name: &OsStr, hostname: Option<&OsStr>, read: bool, write: bool, timeout: WaitTimeout, ) -> io::Result { let path = super::convert_path(pipe_name, hostname); loop { match connect_without_waiting(&path, read, write) { Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => { wait_for_server(&path, timeout)?; continue; } els => return els, } } } fn connect_without_waiting(path: &[u16], read: bool, write: bool) -> io::Result { let (success, handle) = unsafe { let handle = CreateFileW( path.as_ptr() as *mut _, { let mut access_flags: DWORD = 0; if read { access_flags |= GENERIC_READ; } if write { access_flags |= GENERIC_WRITE; } access_flags }, FILE_SHARE_READ | FILE_SHARE_WRITE, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut(), ); (handle != INVALID_HANDLE_VALUE, handle) }; if success { unsafe { // SAFETY: we just created this handle Ok(PipeOps::from_raw_handle(handle)) } } else { Err(io::Error::last_os_error()) } } #[repr(transparent)] // #[repr(DWORD)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] struct WaitTimeout(u32); impl WaitTimeout { const DEFAULT: Self = Self(0x00000000); //const FOREVER: Self = Self(0xffffffff); } impl From for u32 { fn from(x: WaitTimeout) -> Self { x.0 } } impl Default for WaitTimeout { fn default() -> Self { Self::DEFAULT } } fn wait_for_server(path: &[u16], timeout: WaitTimeout) -> io::Result<()> { let success = unsafe { WaitNamedPipeW(path.as_ptr() as *mut _, timeout.0) != 0 }; if success { Ok(()) } else { Err(io::Error::last_os_error()) } } interprocess-1.2.1/src/os/windows/named_pipe/tokio/listener.rs000064400000000000000000000071030072674642500227210ustar 00000000000000use { crate::{ os::windows::named_pipe::{ enums::{PipeMode, PipeStreamRole}, tokio::{PipeOps, TokioPipeStream}, PipeListenerOptions, }, Sealed, }, std::{ fmt::{self, Debug, Formatter}, io, marker::PhantomData, mem::replace, }, tokio::sync::Mutex, }; /// A Tokio-based async server for a named pipe, asynchronously listening for connections to clients and producing asynchronous pipe streams. /// /// The only way to create a `PipeListener` is to use [`PipeListenerOptions`]. See its documentation for more. pub struct PipeListener { config: PipeListenerOptions<'static>, // We need the options to create new instances stored_instance: Mutex, _phantom: PhantomData Stream>, } impl PipeListener { /// Asynchronously waits until a client connects to the named pipe, creating a `Stream` to communicate with the pipe. pub async fn accept(&self) -> io::Result { let instance_to_hand_out = { let mut stored_instance = self.stored_instance.lock().await; stored_instance.connect_server().await?; let new_instance = self.create_instance()?; replace(&mut *stored_instance, new_instance) }; Ok(Stream::build(instance_to_hand_out.into())) } fn create_instance(&self) -> io::Result { let handle = self.config .create_instance(false, false, true, Stream::ROLE, Stream::READ_MODE)?; // SAFETY: we just created this handle Ok(unsafe { PipeOps::from_raw_handle(handle, true)? }) } } impl Debug for PipeListener { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("PipeListener") .field("config", &self.config) .field("instance", &self.stored_instance) .finish() } } /// Extends [`PipeListenerOptions`] with a constructor method for the Tokio [`PipeListener`]. pub trait PipeListenerOptionsExt: Sealed { /// Creates a Tokio pipe listener from the builder. See the [non-async `create` method on `PipeListenerOptions`](PipeListenerOptions::create) for more. /// /// The `nonblocking` parameter is ignored and forced to be enabled. fn create_tokio(&self) -> io::Result>; } impl PipeListenerOptionsExt for PipeListenerOptions<'_> { fn create_tokio(&self) -> io::Result> { let (owned_config, instance) = _create_tokio(self, Stream::ROLE, Stream::READ_MODE)?; Ok(PipeListener { config: owned_config, stored_instance: Mutex::new(instance), _phantom: PhantomData, }) } } impl Sealed for PipeListenerOptions<'_> {} fn _create_tokio( config: &PipeListenerOptions<'_>, role: PipeStreamRole, read_mode: Option, ) -> io::Result<(PipeListenerOptions<'static>, PipeOps)> { // Shadow to avoid mixing them up. let mut config = config.to_owned(); // Tokio should ideally already set that, but let's do it just in case. config.nonblocking = false; let instance = { let handle = config.create_instance(true, config.nonblocking, true, role, read_mode)?; unsafe { // SAFETY: we just created this handle, so we know it's unique (and we've checked // that it's valid) PipeOps::from_raw_handle(handle, true)? } }; Ok((config, instance)) } interprocess-1.2.1/src/os/windows/named_pipe/tokio/mod.rs000064400000000000000000000013360072674642500216550ustar 00000000000000//! Asynchronous named pipes which work with the Tokio runtime and event loop. //! //! The Tokio integration allows the named pipe streams and listeners to be notified by the OS kernel whenever they're ready to be read from of written to, instead of spawning threads just to put them in a wait state of blocking on the I/O. //! //! Types from this module will *not* work with other async runtimes, such as `async-std` or `smol`, since the Tokio types' methods will panic whenever they're called outside of a Tokio runtime context. Open an issue if you'd like to see other runtimes supported as well. mod listener; mod pipeops; mod stream; pub use listener::*; pub use stream::*; use super::{super::imports, enums}; use pipeops::*; interprocess-1.2.1/src/os/windows/named_pipe/tokio/pipeops.rs000064400000000000000000000132330072674642500225540ustar 00000000000000#![allow(clippy::unnecessary_mut_passed)] // We get &mut with mutexes either way use { crate::os::windows::named_pipe::{ tokio::{imports::*, stream::Instance}, PipeOps as SyncPipeOps, }, futures_core::ready, std::{ fmt::{self, Debug, Formatter}, io::{self, ErrorKind}, task::{Context, Poll}, }, }; macro_rules! same_clsrv { ($nm:ident in $var:ident : {$($t:tt)*}) => { match $var { PipeOps::Client($nm) => {$($t)*}, PipeOps::Server($nm) => {$($t)*}, } } } pub enum PipeOps { Client(TokioNPClient), Server(TokioNPServer), } impl PipeOps { /// Creates a `PipeOps` from a raw Windows API handle. The `server` argument specifies whether it should convert to a Tokio named pipe server struct or a client struct. /// /// # Safety /// See safety notes on Tokio's `from_raw_handle` on relevant types. pub unsafe fn from_raw_handle(handle: HANDLE, server: bool) -> io::Result { // SAFETY: as per safety contract let val = if server { Self::Server(unsafe { TokioNPServer::from_raw_handle(handle)? }) } else { Self::Client(unsafe { TokioNPClient::from_raw_handle(handle)? }) }; Ok(val) } pub fn from_sync_pipeops(sync_pipeops: SyncPipeOps) -> io::Result { let is_server = sync_pipeops.is_server()?; let handle = sync_pipeops.into_raw_handle(); let val = if is_server { Self::Server(unsafe { TokioNPServer::from_raw_handle(handle)? }) } else { Self::Client(unsafe { TokioNPClient::from_raw_handle(handle)? }) }; Ok(val) } pub fn is_server(&self) -> bool { matches!(self, Self::Server(_)) } pub fn is_client(&self) -> bool { matches!(self, Self::Client(_)) } /* Gone because it requires a slightly newer version of Tokio than we can allow as per MSRV TODO: bump Tokio version and implement this pub fn poll_read_readbuf( &self, ctx: &mut Context<'_>, buf: &mut TokioReadBuf<'_>, ) -> Poll> { } */ pub fn poll_read(&self, ctx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { same_clsrv!(s in self: { // Try to ready up right away to avoid spurious system calls. ready!(s.poll_read_ready(ctx))?; loop { // For as long as the read fails... // Check if the failure is because the data isn't ready yet... match s.try_read(buf) { Err(e) if e.kind() == ErrorKind::WouldBlock => ready!(s.poll_read_ready(ctx))?, // If it's not or we're not failing anymore, return verbatim. els => return Poll::Ready(els), } } }) } pub fn poll_write(&self, ctx: &mut Context<'_>, buf: &[u8]) -> Poll> { same_clsrv!(s in self: { // Similar stuff here as above, try to ready up // right away to avoid spurious system calls. ready!(s.poll_write_ready(ctx))?; loop { // For as long as the read fails... // Check if the failure is because the buffer isn't empty enough yet... match s.try_write(buf) { Err(e) if e.kind() == ErrorKind::WouldBlock => ready!(s.poll_read_ready(ctx))?, // If it's not or we're not failing anymore, return verbatim. els => return Poll::Ready(els), } } }) } pub fn poll_flush(&self, _ctx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) // Nothing – Windows can't do async flush } pub fn poll_shutdown(&self, _ctx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) // nah } pub fn get_client_process_id(&self) -> io::Result { unsafe { self.hget(GetNamedPipeClientProcessId) } } pub fn get_client_session_id(&self) -> io::Result { unsafe { self.hget(GetNamedPipeClientSessionId) } } pub fn get_server_process_id(&self) -> io::Result { unsafe { self.hget(GetNamedPipeServerProcessId) } } pub fn get_server_session_id(&self) -> io::Result { unsafe { self.hget(GetNamedPipeServerSessionId) } } unsafe fn hget( &self, f: unsafe extern "system" fn(HANDLE, *mut u32) -> BOOL, ) -> io::Result { let mut x: u32 = 0; let success = unsafe { f(self.as_raw_handle(), &mut x as *mut _) != 0 }; if success { Ok(x) } else { Err(io::Error::last_os_error()) } } pub async fn connect_server(&self) -> io::Result<()> { match self { PipeOps::Client(_) => unimplemented!("connect_server() called on client PipeOps"), PipeOps::Server(s) => s.connect().await, } } pub fn disconnect(&self) -> io::Result<()> { if let PipeOps::Server(s) = self { s.disconnect()?; } Ok(()) } pub fn server_drop_disconnect(&self) { self.disconnect() .expect("failed to disconnect server from client"); } } impl Debug for PipeOps { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { same_clsrv!(s in self: { Debug::fmt(s, f) }) } } #[cfg(windows)] impl AsRawHandle for PipeOps { fn as_raw_handle(&self) -> HANDLE { same_clsrv!(s in self: { s.as_raw_handle() }) } } // Distinct from the non-async PipeStreamInternals which uses the non-async PipeOps. pub trait PipeStreamInternals { #[cfg(windows)] fn build(instance: Instance) -> Self; } interprocess-1.2.1/src/os/windows/named_pipe/tokio/stream.rs000064400000000000000000000542520072674642500223760ustar 00000000000000#[cfg(windows)] use crate::os::windows::imports::ERROR_PIPE_BUSY; use { crate::os::windows::named_pipe::{ convert_path, tokio::{ enums::{PipeMode, PipeStreamRole}, imports::*, PipeOps, PipeStreamInternals, }, PipeOps as SyncPipeOps, }, std::{ ffi::{OsStr, OsString}, fmt::{self, Debug, Formatter}, io, mem::ManuallyDrop, pin::Pin, ptr, task::{Context, Poll}, }, }; mod inst { use { super::*, std::{ fmt::{self, Debug, Formatter}, ops::Deref, sync::{ atomic::{AtomicBool, Ordering::*}, Arc, }, }, }; #[repr(transparent)] pub struct Instance(Arc); struct InstanceInner { ops: PipeOps, split: AtomicBool, } impl InstanceInner { pub fn new(ops: PipeOps) -> Self { Self { ops, split: AtomicBool::new(false), } } } impl Instance { pub fn new(instance: PipeOps) -> Self { let ii = InstanceInner::new(instance); Self(Arc::new(ii)) } pub fn instance(&self) -> &PipeOps { &self.0.deref().ops } pub fn is_server(&self) -> bool { self.instance().is_server() } pub fn is_split(&self) -> bool { // This can be `Relaxed`, because the other split half is either on the same thread and thus // doesn't need synchronization to read the current value here, or it's on a different // thread and all of the relevant synchronization is performed as part of sending it to // another thread (same reasoning as above). self.0.split.load(Relaxed) } pub fn split(&self) -> Self { // This can be a relaxed load because a non-split instance won't ever be shared between // threads. From a correctness standpoint, this could even be a non-atomic load, but because // most architectures already guarantee well-aligned memory accesses to be atomic, there's // no point to writing unsafe code to do that. (Also, this condition obviously signifies // a bug in interprocess that can only lead to creation of excess instances at worst, so // there isn't a real point to making sure it never happens in release mode.) debug_assert!( !self.0.split.load(Relaxed), "cannot split an already split instance" ); // Again, the store doesn't even need to be atomic because it won't happen concurrently. self.0.split.store(true, Relaxed); let refclone = Arc::clone(&self.0); Self(refclone) } } impl Drop for Instance { fn drop(&mut self) { self.0.split.store(false, Release); } } impl From for Instance { fn from(x: PipeOps) -> Self { Self::new(x) } } impl Debug for InstanceInner { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("Instance") // Not deriving to override struct name .field("inner", &self.ops) .field("split", &self.split) .finish() } } impl Debug for Instance { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { Debug::fmt(&self.0, f) // passthrough } } } pub(super) use inst::*; /// Defines the properties of Tokio pipe stream types. /// /// This is the counterpart of the [`PipeStream`](super::super::PipeStream) type for the Tokio integration. pub trait TokioPipeStream: AsRawHandle + PipeStreamInternals { /// The data stream flow direction for the pipe. See the [`PipeStreamRole`] enumeration for more on what this means. const ROLE: PipeStreamRole; /// The data stream mode for the pipe. If set to `PipeMode::Bytes`, message boundaries will broken and having `READ_MODE` at `PipeMode::Messages` would be a pipe creation error. /// /// For reader streams, this value has no meaning: if the reader stream belongs to the server (client sends data, server receives), then `READ_MODE` takes the role of this value; if the reader stream belongs to the client, there is no visible difference to how the server writes data since the client specifies its read mode itself anyway. const WRITE_MODE: Option; /// The data stream mode used when reading from the pipe: if `WRITE_MODE` is `PipeMode::Messages` and `READ_MODE` is `PipeMode::Bytes`, the message boundaries will be destroyed when reading even though they are retained when written. See the `PipeMode` enumeration for more on what those modes mean. /// /// For writer streams, this value has no meaning: if the writer stream belongs to the server (server sends data, client receives), then the server doesn't read data at all and thus this does not affect anything; if the writer stream belongs to the client, then the client doesn't read anything and the value is meaningless as well. const READ_MODE: Option; } macro_rules! create_stream_type { ( $ty:ident: desired_access: $desired_access:expr, role: $role:expr, read_mode: $read_mode:expr, write_mode: $write_mode:expr, extra_methods: {$($extra_methods:tt)*}, doc: $doc:tt ) => { create_stream_type_base!( $ty: extra_methods: { /// Tries to connect to the specified named pipe (the `\\.\pipe\` prefix is added automatically). /// /// If there is no available server, **returns immediately** with the [`WouldBlock`](io::ErrorKind::WouldBlock) error. pub fn connect(name: impl AsRef) -> io::Result { Self::_connect(name.as_ref()) } fn _connect(name: &OsStr) -> io::Result { let pipeops = _connect( name, None, Self::READ_MODE.is_some(), Self::WRITE_MODE.is_some(), )?; let instance = Instance::new(pipeops); Ok(Self { instance }) } /// Tries to connect to the specified named pipe at a remote computer (the `\\\pipe\` prefix is added automatically). /// /// If there is no available server, **returns immediately** with the [`WouldBlock`](io::ErrorKind::WouldBlock) error. pub fn connect_to_remote(pipe_name: impl AsRef, hostname: impl AsRef) -> io::Result { Self::_connect_to_remote(pipe_name.as_ref(), hostname.as_ref()) } fn _connect_to_remote(pipe_name: &OsStr, hostname: &OsStr) -> io::Result { let pipeops = _connect( pipe_name, Some(hostname), Self::READ_MODE.is_some(), Self::WRITE_MODE.is_some(), )?; let instance = Instance::new(pipeops); Ok(Self { instance }) } /// Returns `true` if the stream was created by a listener (server-side), `false` if it was created by connecting to a server (client-side). pub fn is_server(&self) -> bool { matches!(self.ops(), &PipeOps::Server(_)) } /// Returns `true` if the stream was created by connecting to a server (client-side), `false` if it was created by a listener (server-side). pub fn is_client(&self) -> bool { matches!(self.ops(), &PipeOps::Client(_)) } // FIXME: cannot have into_raw_handle just yet, Tokio doesn't expose it /// Creates a Tokio-based async object from a given raw handle. This will also attach the object to the Tokio runtime this function is called in, so calling it outside a runtime will result in an error (which is why the `FromRawHandle` trait can't be implemented instead). /// /// # Safety /// The given handle must be valid (i.e. refer to an existing kernel object) and must not be owned by any other handle container. If this is not upheld, an arbitrary handle will be closed when the returned object is dropped. pub unsafe fn from_raw_handle(handle: HANDLE) -> io::Result { let sync_pipeops = unsafe { // SAFETY: guaranteed via safety contract SyncPipeOps::from_raw_handle(handle) }; // If the wrapper type tries to read incoming data as messages, that might break // if the underlying pipe has no message boundaries. Let's check for that. if Self::READ_MODE == Some(PipeMode::Messages) { let has_msg_boundaries = sync_pipeops.does_pipe_have_message_boundaries() .expect("\ failed to determine whether the pipe preserves message boundaries"); assert!(has_msg_boundaries, "\ stream wrapper type uses a message-based read mode, but the underlying pipe does not preserve \ message boundaries"); } let pipeops = PipeOps::from_sync_pipeops(sync_pipeops)?; let instance = Instance::new(pipeops); Ok(Self { instance }) } $($extra_methods)* }, doc: $doc ); impl TokioPipeStream for $ty { const ROLE: PipeStreamRole = $role; const WRITE_MODE: Option = $write_mode; const READ_MODE: Option = $read_mode; } }; ($( $ty:ident: desired_access: $desired_access:expr, role: $role:expr, read_mode: $read_mode:expr, write_mode: $write_mode:expr, extra_methods: {$($extra_methods:tt)*}, doc: $doc:tt )+) => { $(create_stream_type!( $ty: desired_access: $desired_access, role: $role, read_mode: $read_mode, write_mode: $write_mode, extra_methods: {$($extra_methods)*}, doc: $doc );)+ }; } macro_rules! create_duplex_stream_type { ( $ty:ident: corresponding_reader: $corresponding_reader:ident, corresponding_writer: $corresponding_writer:ident, doc: $doc:tt ) => { create_stream_type!( $ty: desired_access: GENERIC_READ | GENERIC_WRITE, role: PipeStreamRole::ReaderAndWriter, read_mode: $corresponding_reader::READ_MODE, write_mode: $corresponding_writer::WRITE_MODE, extra_methods: { // TODO borrowed split /// Splits the duplex stream into its reading and writing half. pub fn split(self) -> ($corresponding_reader, $corresponding_writer) { let self_ = ManuallyDrop::new(self); let reader_half = self_.instance.split(); let writer_half = unsafe { // SAFETY: ManuallyDrop precludes double free ptr::read(&self_.instance) }; ( $corresponding_reader::build(reader_half), $corresponding_writer::build(writer_half), ) } }, doc: $doc ); }; ($( $ty:ident: corresponding_reader: $corresponding_reader:ident, corresponding_writer: $corresponding_writer:ident, doc: $doc:tt )+) => { $(create_duplex_stream_type!( $ty: corresponding_reader: $corresponding_reader, corresponding_writer: $corresponding_writer, doc: $doc );)+ }; } create_stream_type! { ByteReaderPipeStream: desired_access: GENERIC_READ, role: PipeStreamRole::Reader, read_mode: Some(PipeMode::Bytes), write_mode: None, extra_methods: {}, doc: " [Byte stream reader] for a Tokio-based named pipe. Created either by using `PipeListener` or by connecting to a named pipe server. [Byte stream reader]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncRead.html " ByteWriterPipeStream: desired_access: GENERIC_WRITE, role: PipeStreamRole::Writer, read_mode: None, write_mode: Some(PipeMode::Bytes), extra_methods: {}, doc: " [Byte stream writer] for a Tokio-based named pipe. Created either by using `PipeListener` or by connecting to a named pipe server. [Byte stream writer]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncWrite.html " MsgReaderPipeStream: desired_access: GENERIC_READ, role: PipeStreamRole::Reader, read_mode: Some(PipeMode::Messages), write_mode: None, extra_methods: {}, doc: " [Message stream reader] for a Tokio-based named pipe. Created either by using `PipeListener` or by connecting to a named pipe server. [Message stream reader]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncRead.html " MsgWriterPipeStream: desired_access: GENERIC_WRITE, role: PipeStreamRole::Writer, read_mode: None, write_mode: Some(PipeMode::Messages), extra_methods: {}, doc: " [Message stream writer] for a Tokio-based named pipe. Created either by using `PipeListener` or by connecting to a named pipe server. [Message stream writer]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncWrite.html " } create_duplex_stream_type! { DuplexBytePipeStream: corresponding_reader: ByteReaderPipeStream, corresponding_writer: ByteWriterPipeStream, doc: " Byte stream [reader] and [writer] for a Tokio-based named pipe. Created either by using `PipeListener` or by connecting to a named pipe server. [reader]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncRead.html [writer]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncWrite.html " DuplexMsgPipeStream: corresponding_reader: MsgReaderPipeStream, corresponding_writer: MsgWriterPipeStream, doc: " Message stream [reader] and [writer] for a Tokio-based named pipe. Created either by using `PipeListener` or by connecting to a named pipe server. [reader]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncRead.html [writer]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncWrite.html " } #[cfg(feature = "tokio_support")] impl AsyncRead for ByteReaderPipeStream { fn poll_read( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { self.ops().poll_read(ctx, buf) } } #[cfg(feature = "tokio_support")] impl AsyncRead for &ByteReaderPipeStream { fn poll_read( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { self.ops().poll_read(ctx, buf) } } #[cfg(feature = "tokio_support")] impl AsyncWrite for ByteWriterPipeStream { fn poll_write( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.ops().poll_write(ctx, buf) } fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_flush(ctx) } fn poll_close(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_shutdown(ctx) } } #[cfg(feature = "tokio_support")] impl AsyncWrite for &ByteWriterPipeStream { fn poll_write( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.ops().poll_write(ctx, buf) } fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_flush(ctx) } fn poll_close(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_shutdown(ctx) } } impl AsyncRead for DuplexBytePipeStream { #[cfg(feature = "tokio_support")] fn poll_read( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { self.ops().poll_read(ctx, buf) } } impl AsyncWrite for DuplexBytePipeStream { #[cfg(feature = "tokio_support")] fn poll_write( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.ops().poll_write(ctx, buf) } #[cfg(feature = "tokio_support")] fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_flush(ctx) } #[cfg(feature = "tokio_support")] fn poll_close(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_shutdown(ctx) } } impl AsyncRead for &DuplexBytePipeStream { #[cfg(feature = "tokio_support")] fn poll_read( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { self.ops().poll_read(ctx, buf) } } impl AsyncWrite for &DuplexBytePipeStream { #[cfg(feature = "tokio_support")] fn poll_write( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.ops().poll_write(ctx, buf) } #[cfg(feature = "tokio_support")] fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_flush(ctx) } #[cfg(feature = "tokio_support")] fn poll_close(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_shutdown(ctx) } } impl AsyncRead for MsgReaderPipeStream { #[cfg(feature = "tokio_support")] fn poll_read( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { self.ops().poll_read(ctx, buf) } } impl AsyncRead for &MsgReaderPipeStream { #[cfg(feature = "tokio_support")] fn poll_read( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { self.ops().poll_read(ctx, buf) } } impl AsyncWrite for MsgWriterPipeStream { #[cfg(feature = "tokio_support")] fn poll_write( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.ops().poll_write(ctx, buf) } #[cfg(feature = "tokio_support")] fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_flush(ctx) } #[cfg(feature = "tokio_support")] fn poll_close(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_shutdown(ctx) } } impl AsyncWrite for &MsgWriterPipeStream { #[cfg(feature = "tokio_support")] fn poll_write( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.ops().poll_write(ctx, buf) } #[cfg(feature = "tokio_support")] fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_flush(ctx) } #[cfg(feature = "tokio_support")] fn poll_close(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_shutdown(ctx) } } impl AsyncRead for DuplexMsgPipeStream { #[cfg(feature = "tokio_support")] fn poll_read( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { self.ops().poll_read(ctx, buf) } } impl AsyncWrite for DuplexMsgPipeStream { #[cfg(feature = "tokio_support")] fn poll_write( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.ops().poll_write(ctx, buf) } #[cfg(feature = "tokio_support")] fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_flush(ctx) } #[cfg(feature = "tokio_support")] fn poll_close(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_shutdown(ctx) } } impl AsyncRead for &DuplexMsgPipeStream { #[cfg(feature = "tokio_support")] fn poll_read( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { self.ops().poll_read(ctx, buf) } } impl AsyncWrite for &DuplexMsgPipeStream { #[cfg(feature = "tokio_support")] fn poll_write( self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &[u8], ) -> Poll> { self.ops().poll_write(ctx, buf) } #[cfg(feature = "tokio_support")] fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_flush(ctx) } #[cfg(feature = "tokio_support")] fn poll_close(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { self.ops().poll_shutdown(ctx) } } fn _connect( pipe_name: &OsStr, hostname: Option<&OsStr>, read: bool, write: bool, ) -> io::Result { let name = convert_path(pipe_name, hostname); let name = OsString::from_wide(&name[..]); let name_ref: &OsStr = name.as_ref(); let result = TokioNPClientOptions::new() .read(read) .write(write) .open(name_ref); let client = match result { Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => { Err(io::ErrorKind::WouldBlock.into()) } els => els, }?; let ops = PipeOps::Client(client); Ok(ops) } // TODO connect with wait interprocess-1.2.1/src/os/windows/signal.rs000064400000000000000000000433610072674642500171310ustar 00000000000000//! C signal support on Windows. //! //! A big difference from POSIX platforms is the amount of signals that Windows supports. Signals on Windows are therefore much less useful than POSIX ones. //! //! # Signal safe C functions //! The C standard specifies that calling any functions *other than those ones* from a signal hook **results in undefined behavior**: //! - `abort` //! - `_Exit` //! - `quick_exit` //! - `signal`, but only if it is used for setting a handler for the same signal as the one being currently handled //! - atomic C functions, but only the ones which are lock-free (practically never used in Rust since it has its own atomics which use compiler intrinsics) //! - `atomic_is_lock_free` use super::imports::*; use std::{ convert::{TryFrom, TryInto}, error::Error, fmt::{self, Formatter}, panic, process, }; /// Installs the specified handler for the specified signal. /// /// # Example /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(all(windows, feature = "signals"))] { /// use interprocess::os::windows::signal::{self, SignalType, SignalHandler}; /// /// let handler = unsafe { /// // Since signal handlers are restricted to a specific set of C functions, creating a /// // handler from an arbitrary function is unsafe because it might call a function /// // outside the list, and there's no real way to know that at compile time with the /// // current version of Rust. Since we're only using the write() system call here, this /// // is safe. /// SignalHandler::from_fn(|| { /// println!("You pressed Ctrl-C!"); /// }) /// }; /// /// // Install our handler for the KeyboardInterrupt signal type. /// signal::set_handler(SignalType::KeyboardInterrupt, handler)?; /// # } /// # Ok(()) } /// ``` pub fn set_handler(signal_type: SignalType, handler: SignalHandler) -> Result<(), SetHandlerError> { if signal_type.is_unsafe() { return Err(SetHandlerError::UnsafeSignal); } unsafe { set_unsafe_handler(signal_type, handler) } } /// Installs the specified handler for the specified unsafe signal. /// /// # Safety /// The handler and all code that may or may not execute afterwards must be prepared for the aftermath of what might've caused the signal. /// /// [`SegmentationFault`] or [`IllegalInstruction`] are most likely caused by undefined behavior invoked from Rust (the former is caused by dereferencing invalid memory, the latter is caused by dereferencing an incorrectly aligned pointer on ISAs like ARM which do not tolerate misaligned pointers), which means that the program is unsound and the only meaningful thing to do is to capture as much information as possible in a safe way – preferably using OS services to create a dump, rather than trying to read the program's global state, which might be irreversibly corrupted – and write the crash dump to some on-disk location. /// /// # Example /// ```no_run /// # fn main() -> Result<(), Box> { /// # #[cfg(all(windows, feature = "signals"))] { /// use interprocess::os::windows::signal::{self, SignalType, SignalHandler}; /// /// let handler = unsafe { /// // Since signal handlers are restricted to a specific set of C functions, creating a /// // handler from an arbitrary function is unsafe because it might call a function /// // outside the list, and there's no real way to know that at compile time with the /// // current version of Rust. Since we're only using the write() system call here, this /// // is safe. /// SignalHandler::from_fn(|| { /// println!("Oh no, we're running on an i386!"); /// std::process::abort(); /// }) /// }; /// /// unsafe { /// // Install our handler for the IllegalInstruction signal type. /// signal::set_unsafe_handler(SignalType::IllegalInstruction, handler)?; /// } /// # } /// # Ok(()) } /// ``` /// /// [`SegmentationFault`]: enum.SignalType.html#variant.SegmentationFault " " /// [`IllegalInstruction`]: enum.SignalType.html#variant.IllegalInstruction " " pub unsafe fn set_unsafe_handler( signal_type: SignalType, handler: SignalHandler, ) -> Result<(), SetHandlerError> { let signal_type = signal_type as u64; let handlers = HANDLERS.read(); let new_signal = handlers.get(signal_type).is_none(); drop(handlers); if new_signal { let mut handlers = HANDLERS.write(); handlers.remove(signal_type); handlers.insert(signal_type, handler); drop(handlers); let hook_val = match handler { SignalHandler::Default => SIG_DFL, _ => signal_receiver as usize, }; unsafe { // SAFETY: we're using a correct value for the hook install_hook(signal_type as i32, hook_val) .map_err(|_| SetHandlerError::UnexpectedLibcCallFailure)? } } Ok(()) } #[cfg(all(windows, feature = "signals"))] static HANDLERS: Lazy>> = Lazy::new(|| RwLock::new(IntMap::new())); unsafe fn install_hook(signum: i32, hook: usize) -> Result<(), ()> { let success = unsafe { // SAFETY: hook validity is required via safety contract libc::signal(signum, hook) } != libc::SIG_ERR as _; if success { Ok(()) } else { Err(()) } } /// The actual hook which is passed to `sigaction` which dispatches signals according to the global handler map (the `HANDLERS` static). extern "C" fn signal_receiver(signum: i32) { let catched = panic::catch_unwind(|| { let handler = { let handlers = HANDLERS.read(); let val = handlers .get(signum as u64) .expect("unregistered signal passed by the OS to the shared receiver"); *val }; match handler { SignalHandler::Ignore => {} SignalHandler::Hook(hook) => hook.inner()(), SignalHandler::NoReturnHook(hook) => hook.inner()(), SignalHandler::Default => unreachable!( "signal receiver was unregistered but has been called by the OS anyway" ), } }); // The panic hook already ran, so we only have to abort the process catched.unwrap_or_else(|_| process::abort()); } /// The error produced when setting a signal handler fails. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(all(windows, feature = "signals"), derive(Error))] pub enum SetHandlerError { /// An unsafe signal was attempted to be handled using `set_handler` instead of `set_unsafe_handler`. #[cfg_attr( all(windows, feature = "signals"), error( "\ an unsafe signal was attempted to be handled using `set_handler` instead of `set_unsafe_handler`" ) )] UnsafeSignal, /// the C library call unexpectedly failed without error information. #[cfg_attr( all(windows, feature = "signals"), error("the C library call unexpectedly failed without error information") )] UnexpectedLibcCallFailure, } /// A signal handling method. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum SignalHandler { /// Use the default behavior specified by the C standard. Default, /// Ignore the signal whenever it is received. Ignore, /// Call a function whenever the signal is received. The function can return, execution will continue. Hook(SignalHook), /// Call a function whenever the signal is received. The function must not return. NoReturnHook(NoReturnSignalHook), } impl SignalHandler { /// Returns `true` for the [`Default`] variant, `false` otherwise. /// /// [`Default`]: #variant.Default.html " " pub const fn is_default(self) -> bool { matches!(self, Self::Default) } /// Returns `true` for the [`Ignore`] variant, `false` otherwise. /// /// [`Ignore`]: #variant.Ignore.html " " pub const fn is_ignore(self) -> bool { matches!(self, Self::Ignore) } /// Returns `true` for the [`Hook`] and [`NoReturnHook`] variants, `false` otherwise. /// /// [`Hook`]: #variant.Hook.html " " /// [`NoReturnHook`]: #variant.NoReturnHook.html " " pub const fn is_hook(self) -> bool { matches!(self, Self::Hook(..)) } /// Creates a handler which calls the specified function. /// /// # Safety /// The function must not call any C functions which are not considered signal-safe. See the [module-level section on signal-safe C functions] for more. /// /// [module-level section on signal-safe C functions]: index.html#signal-safe-c-functions " " pub unsafe fn from_fn(function: fn()) -> Self { let hook = unsafe { // SAFETY: hook validity required by safety contract SignalHook::from_fn(function) }; Self::Hook(hook) } /// Creates a handler which calls the specified function and is known to never return. /// /// # Safety /// The function must not call any C functions which are not considered signal-safe. See the [module-level section on signal-safe C functions] for more. /// /// [module-level section on signal-safe C functions]: index.html#signal-safe-c-functions " " pub unsafe fn from_fn_noreturn(function: fn() -> !) -> Self { let hook = unsafe { // SAFETY: hook validity required by safety contract NoReturnSignalHook::from_fn(function) }; Self::NoReturnHook(hook) } } impl Default for SignalHandler { /// Returns [`SignalHandler::Default`]. /// /// [`SignalHandler::Default`]: #variant.Default " " fn default() -> Self { Self::Default } } /// A function which can be used as a signal handler. #[repr(transparent)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct SignalHook(fn()); impl SignalHook { /// Creates a hook which calls the specified function. /// /// # Safety /// The function must not call any C functions which are not considered signal-safe. See the [module-level section on signal-safe functions] for more. /// /// [module-level section on signal-safe functions]: index.html#signal-safe-functions " " pub unsafe fn from_fn(function: fn()) -> Self { Self(function) } /// Returns the wrapped function. pub fn inner(self) -> fn() { self.0 } } impl From for fn() { fn from(op: SignalHook) -> Self { op.0 } } /// A function which can be used as a signal handler, but one which also never returns. #[repr(transparent)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct NoReturnSignalHook(fn() -> !); impl NoReturnSignalHook { /// Creates a hook which calls the specified function. /// /// # Safety /// Same as for the normal [`SignalHook`]. /// /// [`SignalHook`]: struct.SignalHook.html " " pub unsafe fn from_fn(function: fn() -> !) -> Self { Self(function) } /// Returns the wrapped function. pub fn inner(self) -> fn() -> ! { self.0 } } impl From for fn() -> ! { fn from(op: NoReturnSignalHook) -> Self { op.0 } } /// All standard signal types as defined in the C standard. /// /// The values can be safely and quickly converted to [`i32`]/[`u32`]. The reverse process involves safety checks, making sure that unknown signal values are never stored. /// /// [`i32`]: https://doc.rust-lang.org/std/primitive.i32.html " " /// [`u32`]: https://doc.rust-lang.org/std/primitive.u32.html " " #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[repr(i32)] #[non_exhaustive] pub enum SignalType { /// `SIGINT` – keyboard interrupt, usually sent by pressing `Ctrl`+`C` by the terminal. This signal is typically set to be ignored if the program runs an interactive interface: GUI/TUI, interactive shell (the Python shell, for example) or any other kind of interface which runs in a loop, as opposed to a command-line invocation of the program which reads its standard input or command-line arguments, performs a task and exits. If the interactive interface is running a lengthy operation, a good idea is to temporarily re-enable the signal and abort the lengthy operation if the signal is received, then disable it again. /// /// *Default handler: process termination.* KeyboardInterrupt = SIGINT, /// `SIGILL` – illegal or malformed instruction exception, generated by the CPU whenever such an instruction is executed. This signal normally should not be overriden or masked out, since it likely means that the executable file or the memory of the process has been corrupted and further execution is a risk of invoking negative consequences. /// /// For reasons described above, **this signal is considered unsafe** – handling it requires using `set_unsafe_handler`. **Signal hooks for this signal are also required to never return** – those must be wrapped into a `NoReturnSignalHook`. /// /// *Default handler: process termination with a core dump.* IllegalInstruction = SIGILL, /// `SIGABRT` – abnormal termination requested. This signal is typically invoked by the program itself, using [`std::process::abort`] or the equivalent C function; still, like any other signal, it can be sent from outside the process. /// /// *Default handler: process termination with a core dump.* /// /// [`std::process::abort`]: https://doc.rust-lang.org/std/process/fn.abort.html " " Abort = SIGABRT, /// `SIGFPE` – mathematical exception. This signal is generated whenever an undefined mathematical operation is performed – mainly integer division by zero. /// /// **Signal hooks for this signal are required to never return** – those must be wrapped into a `NoReturnSignalHook`. /// /// *Default handler: process termination with a core dump.* MathException = SIGFPE, /// `SIGSEGV` – invaid memory access. This signal is issued by the OS whenever the program tries to access an invalid memory location, such as the `NULL` pointer or simply an address outside the user-mode address space as established by the OS. The only case when this signal can be received by a Rust program is if memory unsafety occurs due to misuse of unsafe code. As such, it should normally not be masked out or handled, as it likely indicates a critical bug (soundness hole), executable file corruption or process memory corruption. /// /// For reasons described above, **this signal is considered unsafe** – handling it requires using `set_unsafe_handler`. **Signal hooks for this signal are also required to never return** – those must be wrapped into a `NoReturnSignalHook`. /// /// *Default handler: process termination with a core dump.* SegmentationFault = SIGSEGV, /// `SIGTERM` – request for termination. This signal can only be sent using the usual signal sending procedures. Unlike [`KeyboardInterrupt`], this signal is not a request to break out of a lengthy operation, but rather to close the program as a whole. Signal handlers for this signal are expected to perform minimal cleanup and quick state save procedures and then exit. /// /// *Default handler: process termination.* /// /// [`KeyboardInterrupt`]: #variant.KeyboardInterrupt " " Termination = SIGTERM, } impl SignalType { /// Returns `true` if the value is a signal which requires its custom handler functions to never return, `false` otherwise. pub const fn requires_diverging_hook(self) -> bool { matches!( self, Self::SegmentationFault | Self::IllegalInstruction | Self::MathException ) } /// Returns `true` if the value is an unsafe signal which requires unsafe code when setting a handling method, `false` otherwise. pub const fn is_unsafe(self) -> bool { matches!(self, Self::SegmentationFault | Self::IllegalInstruction) } } impl From for i32 { fn from(op: SignalType) -> Self { op as i32 } } impl From for u32 { fn from(op: SignalType) -> Self { op as u32 } } impl TryFrom for SignalType { type Error = UnknownSignalError; fn try_from(value: i32) -> Result { match value { SIGINT => Ok(Self::KeyboardInterrupt), SIGILL => Ok(Self::IllegalInstruction), SIGABRT => Ok(Self::Abort), SIGFPE => Ok(Self::MathException), SIGSEGV => Ok(Self::SegmentationFault), SIGTERM => Ok(Self::Termination), _ => Err(UnknownSignalError { value }), } } } impl TryFrom for SignalType { type Error = UnknownSignalError; fn try_from(value: u32) -> Result { value.try_into() } } /// Error type returned when a conversion from [`i32`]/[`u32`] to [`SignalType`] fails. /// /// [`i32`]: https://doc.rust-lang.org/std/primitive.i32.html " " /// [`u32`]: https://doc.rust-lang.org/std/primitive.u32.html " " /// [`SignalType`]: enum.SignalType.html " " #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct UnknownSignalError { /// The unknown signal value which was encountered. pub value: i32, } impl fmt::Display for UnknownSignalError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "unknown signal value {}", self.value) } } impl fmt::Binary for UnknownSignalError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "unknown signal value {:b}", self.value) } } impl fmt::LowerHex for UnknownSignalError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "unknown signal value {:x}", self.value) } } impl fmt::UpperExp for UnknownSignalError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "unknown signal value {:X}", self.value) } } impl fmt::Octal for UnknownSignalError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "unknown signal value {:o}", self.value) } } impl Error for UnknownSignalError {} interprocess-1.2.1/src/os/windows/unnamed_pipe.rs000064400000000000000000000175330072674642500203220ustar 00000000000000//! Platform-specific functionality for unnamed pipes. //! //! Currently, this consists of only the [`UnnamedPipeCreationOptions`] builder, but more might be added. //! //! [`UnnamedPipeCreationOptions`]: struct.UnnamedPipeCreationOptions.html " " // TODO add examples use super::imports::*; use super::FileHandleOps; use crate::unnamed_pipe::{UnnamedPipeReader as PubReader, UnnamedPipeWriter as PubWriter}; use std::{ fmt::{self, Debug, Formatter}, io::{self, Read, Write}, mem::{size_of, zeroed, ManuallyDrop}, num::NonZeroUsize, ptr, }; /// Builder used to create unnamed pipes while supplying additional options. /// /// You can use this instead of the simple [`pipe` function] to supply additional Windows-specific parameters to a pipe. /// /// [`pipe` function]: ../../../unnamed_pipe/fn.pipe.html " " #[non_exhaustive] #[derive(Copy, Clone, Debug)] pub struct UnnamedPipeCreationOptions { /// Specifies whether the resulting pipe can be inherited by child processes. /// /// The default value is `true` and you probably shouldn't modify this, unless you want all child processes to explicitly be unable to use the pipe even if they attempt to use various fishy methods to find the handle in the parent process. pub inheritable: bool, /// A pointer to the [security descriptor] for the pipe. Leave this at the default `NULL` unless you want something specific. /// /// [security descriptor]: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-security_descriptor " " pub security_descriptor: LPVOID, /// A hint on the buffer size for the pipe. There is no way to ensure or check that the system actually uses this exact size, since it's only a hint. Set to `None` to disable the hint and rely entirely on the system's default buffer size. pub buffer_size_hint: Option, } impl UnnamedPipeCreationOptions { /// Starts with the default parameters for the pipe. Identical to `Default::default()`. pub const fn new() -> Self { Self { inheritable: true, security_descriptor: ptr::null_mut(), buffer_size_hint: None, } } /// Specifies whether the resulting pipe can be inherited by child processes. /// /// See the [associated field] for more. /// /// [associated field]: #structfield.inheritable " " #[must_use = "this is not an in-place operation"] pub fn inheritable(mut self, inheritable: bool) -> Self { self.inheritable = inheritable; self } /// Specifies the pointer to the security descriptor for the pipe. /// /// See the [associated field] for more. /// /// [associated field]: #structfield.security_descriptor " " #[must_use = "this is not an in-place operation"] pub fn security_descriptor(mut self, security_descriptor: LPVOID) -> Self { self.security_descriptor = security_descriptor; self } /// Specifies the hint on the buffer size for the pipe. /// /// See the [associated field] for more. /// /// [associated field]: #structfield.buffer_size_hint " " #[must_use = "this is not an in-place operation"] pub fn buffer_size_hint(mut self, buffer_size_hint: Option) -> Self { self.buffer_size_hint = buffer_size_hint; self } /// Extracts the [`SECURITY_ATTRIBUTES`] from the builder. Primarily an implementation detail, but has other uses. /// /// [`SECURITY_ATTRIBUTES`]: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa379560(v=vs.85) pub fn extract_security_attributes(self) -> SECURITY_ATTRIBUTES { // Safe because WinAPI parameter structs are typically rejected if a required field is zero let mut security_attributes = unsafe { zeroed::() }; security_attributes.nLength = size_of::() as u32; security_attributes.lpSecurityDescriptor = self.security_descriptor; security_attributes.bInheritHandle = self.inheritable as i32; security_attributes } /// Creates the pipe and returns its writing and reading ends, or the error if one occurred. /// /// # Safety /// The [`security_descriptor`] field is passed directly to Win32 which is then dereferenced there, resulting in undefined behavior if it was an invalid non-null pointer. For the default configuration, this should never be a concern. /// /// [`security_descriptor`]: #field.security_descriptor " " // TODO have safe and unsafe versions, since most folks don't need security_attributes pub unsafe fn build(self) -> io::Result<(PubWriter, PubReader)> { let hint_raw = match self.buffer_size_hint { Some(num) => num.get(), None => 0, } as u32; let [mut writer, mut reader] = [INVALID_HANDLE_VALUE; 2]; let success = unsafe { CreatePipe( &mut reader as *mut _, &mut writer as *mut _, &mut self.extract_security_attributes() as *mut _, hint_raw, ) } != 0; if success { let (writer, reader) = unsafe { // SAFETY: we just created those handles which means that we own them let writer = PubWriter { inner: UnnamedPipeWriter::from_raw_handle(writer), }; let reader = PubReader { inner: UnnamedPipeReader::from_raw_handle(reader), }; (writer, reader) }; Ok((writer, reader)) } else { Err(io::Error::last_os_error()) } } } impl Default for UnnamedPipeCreationOptions { fn default() -> Self { Self::new() } } unsafe impl Send for UnnamedPipeCreationOptions {} unsafe impl Sync for UnnamedPipeCreationOptions {} pub(crate) fn pipe() -> io::Result<(PubWriter, PubReader)> { unsafe { UnnamedPipeCreationOptions::default().build() } } pub(crate) struct UnnamedPipeReader(FileHandleOps); impl Read for UnnamedPipeReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } } #[cfg(windows)] impl AsRawHandle for UnnamedPipeReader { fn as_raw_handle(&self) -> HANDLE { self.0.as_raw_handle() } } #[cfg(windows)] impl IntoRawHandle for UnnamedPipeReader { fn into_raw_handle(self) -> HANDLE { let self_ = ManuallyDrop::new(self); self_.as_raw_handle() } } #[cfg(windows)] impl FromRawHandle for UnnamedPipeReader { unsafe fn from_raw_handle(handle: HANDLE) -> Self { let fho = unsafe { // SAFETY: validity guaranteed by safety contract FileHandleOps::from_raw_handle(handle) }; Self(fho) } } impl Debug for UnnamedPipeReader { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("UnnamedPipeReader") .field("handle", &self.as_raw_handle()) .finish() } } pub(crate) struct UnnamedPipeWriter(FileHandleOps); impl Write for UnnamedPipeWriter { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } fn flush(&mut self) -> io::Result<()> { self.0.flush() } } #[cfg(windows)] impl AsRawHandle for UnnamedPipeWriter { fn as_raw_handle(&self) -> HANDLE { self.0.as_raw_handle() } } #[cfg(windows)] impl IntoRawHandle for UnnamedPipeWriter { fn into_raw_handle(self) -> HANDLE { let self_ = ManuallyDrop::new(self); self_.as_raw_handle() } } #[cfg(windows)] impl FromRawHandle for UnnamedPipeWriter { unsafe fn from_raw_handle(handle: HANDLE) -> Self { Self(FileHandleOps(handle)) } } impl Debug for UnnamedPipeWriter { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("UnnamedPipeWriter") .field("handle", &self.as_raw_handle()) .finish() } } interprocess-1.2.1/src/reliable_read_msg.rs000064400000000000000000000116160072674642500171570ustar 00000000000000use { super::Sealed, std::{ error::Error, fmt::{self, Display, Formatter}, io, }, }; /// Reading from named pipes with message boundaries reliably, without truncation. /// /// ## The problem /// Unlike a byte stream interface, message-mode named pipes preserve boundaries between different write calls, which is what "message boundary" essentially means. Extracting messages by partial reads is an error-prone task, which is why no such interface is exposed by the operating system – instead, all messages read from a named pipe stream are full messages rather than chunks of messages, which simplifies things to a great degree and is arguably the only proper way of implementing datagram support. /// /// There is one pecularity related to this design: you can't just use a buffer with arbitrary length to successfully read a message. With byte streams, that always works – there either is some data which can be written into that buffer or end of file has been reached, aside from the implied error case which is always a possibility for any kind of I/O. With message streams, however, **there might not always be enough space in a buffer to fetch a whole message**. If the buffer is too small to fetch a message, it won't be written into the buffer, but simply will be ***discarded*** instead. The only way to protect from it being discarded is first checking whether the message fits into the buffer without discarding it and then actually reading it into a suitably large buffer. In such a case, the message needs an alternate channel besides the buffer to somehow get returned. /// /// This brings the discussion specifically to the signature of the `read_msg` method: /// ```no_run /// # use std::io; /// # trait Tr { /// fn read_msg(&mut self, buf: &mut [u8]) -> io::Result>>; /// # } /// ``` /// Setting aside from the `io::Result` part, the "true return value" is `Result>`. The `Ok(...)` variant here means that the message has been successfully read into the buffer and contains the actual size of the message which has been read. The `Err(...)` variant means that the buffer was too small for the message, containing a freshly allocated buffer which is just big enough to fit the message. The usage strategy is to store a buffer, mutably borrow it and pass it to the `read_msg` function, see if it fits inside the buffer, and if it does not, replace the stored buffer with the new one. /// /// The `try_read_msg` method is a convenience function used mainly by implementations of `read_msg` to determine whether it's required to allocate a new buffer or not. It has the following signature: /// ```no_run /// # use std::io; /// # trait Tr { /// fn try_read_msg(&mut self, buf: &mut [u8]) -> io::Result>; /// # } /// ``` /// While it may seem strange how the nested `Result` returns the same type in `Ok` and `Err`, it does this for a semantic reason: the `Ok` variant means that the message was successfully read into the buffer while `Err` means the opposite – that the message was too big – and returns the size which the buffer needs to have. /// /// ## Platform support /// The trait is implemented for: /// - Named pipes on Windows (module `interprocess::os::windows::named_pipe`) /// - Unix domain pipes, but only on Linux (module `interprocess::os::unix::udsocket`) /// - This is because only Linux provides a special flag for `recv` which returns the amount of bytes in the message regardless of the provided buffer size when peeking. pub trait ReliableReadMsg: Sealed { /// Reads one message from the stream into the specified buffer, returning either the size of the message written, a bigger buffer if the one provided was too small, or an error in the outermost `Result` if the operation could not be completed for OS reasons. fn read_msg(&mut self, buf: &mut [u8]) -> io::Result>>; /// Attempts to read one message from the stream into the specified buffer, returning the size of the message, which, depending on whether it was in the `Ok` or `Err` variant, either did fit or did not fit into the provided buffer, respectively; if the operation could not be completed for OS reasons, an error from the outermost `Result` is returned. fn try_read_msg(&mut self, buf: &mut [u8]) -> io::Result>; } /// Marker error indicating that a datagram write operation failed because the amount of bytes which were actually written as reported by the operating system was smaller than the size of the message which was requested to be written. /// /// Always emitted with the `ErrorKind::Other` error type. #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub struct PartialMsgWriteError; impl Display for PartialMsgWriteError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_str("message write operation wrote less than the size of the message") } } impl Error for PartialMsgWriteError {} interprocess-1.2.1/src/sealed.rs000064400000000000000000000004670072674642500147760ustar 00000000000000/// A utility trait that, if used as a supertrait, prevents other crates from implementing the trait. // If the trait itself was pub(crate), it wouldn't work as a supertrait on public traits. We use a // private module instead to make it impossible to name the trait from outside the crate. pub trait Sealed {} interprocess-1.2.1/src/unnamed_pipe.rs000064400000000000000000000106540072674642500162040ustar 00000000000000//! Creation and usage of unnamed pipes. //! //! The distinction between named and unnamed pipes is concisely expressed by their names: where named pipes have names, unnamed pipes have handles. This can both be useful or problematic, depending on the use case. Unnamed pipes work best when a child process is used. With the fork model on Unix-like systems, the handle can be transferred to the child process thanks to the cloned address space; on Windows, inheritable handles can be used. //! //! Another way to use unnamed pipes is to use a named pipe or a Unix domain socket to establish an unnamed pipe connection. It just so happens that this crate supports all three. impmod! {unnamed_pipe, UnnamedPipeReader as UnnamedPipeReaderImpl, UnnamedPipeWriter as UnnamedPipeWriterImpl, pipe as pipe_impl, } use std::{ fmt::{self, Formatter}, io::{self, Read, Write}, }; /// Creates a new pipe with the default creation settings and returns the handles to its writing end and reading end. /// /// The platform-specific builders in the `os` module of the crate might be more helpful if a configuration process for the pipe is needed. pub fn pipe() -> io::Result<(UnnamedPipeWriter, UnnamedPipeReader)> { pipe_impl() } /// A handle to the reading end of an unnamed pipe, created by the [`pipe`] function together with the [writing end]. /// /// The core functionality is exposed in a file-like [`Read`] interface. On Windows, the [`ShareHandle`] and [`As-`][`AsRawHandle`]/[`Into-`][`IntoRawHandle`]/[`FromRawHandle`] traits are also implemented, along with [`As-`][`AsRawFd`]/[`Into-`][`IntoRawFd`]/[`FromRawFd`] on Unix. /// /// [`pipe`]: fn.pipe.html " " /// [writing end]: struct.UnnamedPipeWriter.html " " /// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html " " /// [`ShareHandle`]: ../os/windows/trait.ShareHandle.html " " /// [`AsRawHandle`]: https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html " " /// [`IntoRawHandle`]: https://doc.rust-lang.org/std/os/windows/io/trait.IntoRawHandle.html " " /// [`FromRawHandle`]: https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html " " /// [`AsRawFd`]: https://doc.rust-lang.org/std/os/unix/io/trait.AsRawFd.html " " /// [`IntoRawFd`]: https://doc.rust-lang.org/std/os/unix/io/trait.IntoRawFd.html " " /// [`FromRawFd`]: https://doc.rust-lang.org/std/os/unix/io/trait.FromRawFd.html " " pub struct UnnamedPipeReader { // pub(crate) to allow the platform specific builders to create the public-facing pipe types pub(crate) inner: UnnamedPipeReaderImpl, } impl Read for UnnamedPipeReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.inner.read(buf) } } impl fmt::Debug for UnnamedPipeReader { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.inner, f) } } impl_handle_manip!(UnnamedPipeReader); /// A handle to the writing end of an unnamed pipe, created by the [`pipe`] function together with the [reading end]. /// /// The core functionality is exposed in a file-like [`Write`] interface. On Windows, the [`ShareHandle`] and [`As-`][`AsRawHandle`]/[`Into-`][`IntoRawHandle`]/[`FromRawHandle`] traits are also implemented, along with [`As-`][`AsRawFd`]/[`Into-`][`IntoRawFd`]/[`FromRawFd`] on Unix. /// /// [`pipe`]: fn.pipe.html " " /// [reading end]: struct.UnnamedPipeReader.html " " /// [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html " " /// [`ShareHandle`]: ../os/windows/trait.ShareHandle.html " " /// [`AsRawHandle`]: https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html " " /// [`IntoRawHandle`]: https://doc.rust-lang.org/std/os/windows/io/trait.IntoRawHandle.html " " /// [`FromRawHandle`]: https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html " " /// [`AsRawFd`]: https://doc.rust-lang.org/std/os/unix/io/trait.AsRawFd.html " " /// [`IntoRawFd`]: https://doc.rust-lang.org/std/os/unix/io/trait.IntoRawFd.html " " /// [`FromRawFd`]: https://doc.rust-lang.org/std/os/unix/io/trait.FromRawFd.html " " pub struct UnnamedPipeWriter { pub(crate) inner: UnnamedPipeWriterImpl, } impl Write for UnnamedPipeWriter { fn write(&mut self, data: &[u8]) -> io::Result { self.inner.write(data) } fn flush(&mut self) -> io::Result<()> { self.inner.flush() } } impl fmt::Debug for UnnamedPipeWriter { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.inner, f) } } impl_handle_manip!(UnnamedPipeWriter); interprocess-1.2.1/tests/local_socket/main.rs000064400000000000000000000015020072674642500174710ustar 00000000000000#[path = "../util/mod.rs"] mod util; use util::*; mod no_server; mod stream; use interprocess::local_socket::NameTypeSupport; #[test] fn local_socket_stream() { use stream::*; // If only one name type is supported, this one will choose the supported one. If both are // supported, this will try paths first. util::drive_server_and_multiple_clients(|s, n| server(s, n, false), client); if NameTypeSupport::query() == NameTypeSupport::Both { // Try the namespace now. util::drive_server_and_multiple_clients(|s, n| server(s, n, true), client); } } #[test] fn local_socket_no_server() -> TestResult { // Same as above. no_server::run_and_verify_error(false)?; if NameTypeSupport::query() == NameTypeSupport::Both { no_server::run_and_verify_error(true)?; } Ok(()) } interprocess-1.2.1/tests/local_socket/no_server.rs000064400000000000000000000015020072674642500205470ustar 00000000000000//! Tests what happens when a client attempts to connect to a local socket that doesn't exist. use {super::util::*, anyhow::*, interprocess::local_socket::LocalSocketStream, std::io}; pub fn run_and_verify_error(prefer_namespaced: bool) -> TestResult { use io::ErrorKind::*; let err = match client(prefer_namespaced) { Err(e) => e.downcast::()?, Ok(()) => bail!("client successfully connected to nonexistent server"), }; ensure!( matches!(err.kind(), NotFound | ConnectionRefused), "expected error to be 'not found', received '{}'", err ); Ok(()) } fn client(prefer_namespaced: bool) -> TestResult { let name = NameGen::new_auto(prefer_namespaced).next().unwrap(); LocalSocketStream::connect(name.as_str()).context("Connect failed")?; Ok(()) } interprocess-1.2.1/tests/local_socket/stream.rs000064400000000000000000000036510072674642500200470ustar 00000000000000use { super::{util::*, NameGen}, anyhow::Context, interprocess::local_socket::{LocalSocketListener, LocalSocketStream}, std::{ io::{self, BufRead, BufReader, Write}, sync::{mpsc::Sender, Arc}, }, }; static SERVER_MSG: &str = "Hello from server!\n"; static CLIENT_MSG: &str = "Hello from client!\n"; pub fn server( name_sender: Sender, num_clients: u32, prefer_namespaced: bool, ) -> TestResult { let (name, listener) = NameGen::new_auto(prefer_namespaced) .find_map(|nm| { let l = match LocalSocketListener::bind(&*nm) { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); let mut buffer = String::with_capacity(128); for _ in 0..num_clients { let mut conn = match listener.accept() { Ok(c) => BufReader::new(c), Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; conn.read_line(&mut buffer) .context("Socket receive failed")?; conn.get_mut() .write_all(SERVER_MSG.as_bytes()) .context("Socket send failed")?; assert_eq!(buffer, CLIENT_MSG); buffer.clear(); } Ok(()) } pub fn client(name: Arc) -> TestResult { let mut buffer = String::with_capacity(128); let mut conn = LocalSocketStream::connect(name.as_str()) .context("Connect failed") .map(BufReader::new)?; conn.get_mut() .write_all(CLIENT_MSG.as_bytes()) .context("Socket send failed")?; conn.read_line(&mut buffer) .context("Socket receive failed")?; assert_eq!(buffer, SERVER_MSG); Ok(()) } interprocess-1.2.1/tests/named_pipe/bytes.rs000064400000000000000000000036300072674642500173360ustar 00000000000000use { super::util::{NameGen, TestResult}, anyhow::Context, interprocess::os::windows::named_pipe::{DuplexBytePipeStream, PipeListenerOptions}, std::{ ffi::OsStr, io::{self, prelude::*, BufReader}, sync::{mpsc::Sender, Arc}, }, }; static SERVER_MSG: &str = "Hello from server!\n"; static CLIENT_MSG: &str = "Hello from client!\n"; pub fn server(name_sender: Sender, num_clients: u32) -> TestResult { let (name, listener) = NameGen::new(true) .find_map(|nm| { let rnm: &OsStr = nm.as_ref(); let l = match PipeListenerOptions::new() .name(rnm) .create::() { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); for _ in 0..num_clients { let mut conn = match listener.accept() { Ok(c) => BufReader::new(c), Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; let mut buffer = String::with_capacity(128); conn.read_line(&mut buffer).context("Pipe receive failed")?; assert_eq!(buffer, CLIENT_MSG); conn.get_mut() .write_all(SERVER_MSG.as_bytes()) .context("Pipe send failed")?; } Ok(()) } pub fn client(name: Arc) -> TestResult { let mut buffer = String::with_capacity(128); let mut conn = DuplexBytePipeStream::connect(name.as_str()) .context("Connect failed") .map(BufReader::new)?; conn.get_mut().write_all(CLIENT_MSG.as_bytes())?; conn.read_line(&mut buffer)?; assert_eq!(buffer, SERVER_MSG); Ok(()) } interprocess-1.2.1/tests/named_pipe/bytes_unidir_client_to_server.rs000064400000000000000000000032400072674642500243330ustar 00000000000000use { super::util::{NameGen, TestResult}, anyhow::Context, interprocess::os::windows::named_pipe::{ ByteReaderPipeStream, ByteWriterPipeStream, PipeListenerOptions, }, std::{ ffi::OsStr, io::{self, prelude::*, BufReader}, sync::{mpsc::Sender, Arc}, }, }; static MSG: &str = "Hello from client!\n"; pub fn server(name_sender: Sender, num_clients: u32) -> TestResult { let (name, listener) = NameGen::new(true) .find_map(|nm| { let rnm: &OsStr = nm.as_ref(); let l = match PipeListenerOptions::new() .name(rnm) .create::() { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); let mut buffer = String::with_capacity(128); for _ in 0..num_clients { let mut conn = match listener.accept() { Ok(c) => BufReader::new(c), Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; conn.read_line(&mut buffer).context("Pipe receive failed")?; assert_eq!(buffer, MSG); buffer.clear(); } Ok(()) } pub fn client(name: Arc) -> TestResult { let mut conn = ByteWriterPipeStream::connect(name.as_str()).context("Connect failed")?; conn.write_all(MSG.as_bytes()).context("Pipe send failed")?; conn.flush()?; Ok(()) } interprocess-1.2.1/tests/named_pipe/bytes_unidir_server_to_client.rs000064400000000000000000000032350072674642500243370ustar 00000000000000use { super::util::{NameGen, TestResult}, anyhow::Context, interprocess::os::windows::named_pipe::{ ByteReaderPipeStream, ByteWriterPipeStream, PipeListenerOptions, }, std::{ ffi::OsStr, io::{self, prelude::*, BufReader}, sync::{mpsc::Sender, Arc}, }, }; static MSG: &str = "Hello from server!\n"; pub fn server(name_sender: Sender, num_clients: u32) -> TestResult { let (name, listener) = NameGen::new(true) .find_map(|nm| { let rnm: &OsStr = nm.as_ref(); let l = match PipeListenerOptions::new() .name(rnm) .create::() { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); for _ in 0..num_clients { let mut conn = match listener.accept() { Ok(c) => c, Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; conn.write_all(MSG.as_bytes()).context("Pipe send failed")?; conn.flush()?; } Ok(()) } pub fn client(name: Arc) -> TestResult { let mut buffer = String::with_capacity(128); let mut conn = ByteReaderPipeStream::connect(name.as_str()) .context("Connect failed") .map(BufReader::new)?; conn.read_line(&mut buffer).context("Pipe receive failed")?; assert_eq!(buffer, MSG); Ok(()) } interprocess-1.2.1/tests/named_pipe/main.rs000064400000000000000000000023110072674642500171270ustar 00000000000000#![cfg(windows)] #[path = "../util/mod.rs"] mod util; mod bytes; mod bytes_unidir_client_to_server; mod bytes_unidir_server_to_client; mod msg; mod msg_unidir_client_to_server; mod msg_unidir_server_to_client; #[test] fn named_pipe_bytes() { util::drive_server_and_multiple_clients(bytes::server, bytes::client) } #[test] fn named_pipe_bytes_unidir_client_to_server() { util::drive_server_and_multiple_clients( bytes_unidir_client_to_server::server, bytes_unidir_client_to_server::client, ) } #[test] fn named_pipe_bytes_unidir_server_to_client() { util::drive_server_and_multiple_clients( bytes_unidir_server_to_client::server, bytes_unidir_server_to_client::client, ) } #[test] fn named_pipe_msg() { util::drive_server_and_multiple_clients(msg::server, msg::client) } #[test] fn named_pipe_msg_unidir_client_to_server() { util::drive_server_and_multiple_clients( msg_unidir_client_to_server::server, msg_unidir_client_to_server::client, ) } #[test] fn named_pipe_msg_unidir_server_to_client() { util::drive_server_and_multiple_clients( msg_unidir_server_to_client::server, msg_unidir_server_to_client::client, ) } interprocess-1.2.1/tests/named_pipe/msg.rs000064400000000000000000000056540072674642500170060ustar 00000000000000use { super::util::{NameGen, TestResult}, anyhow::Context, interprocess::os::windows::named_pipe::{DuplexMsgPipeStream, PipeListenerOptions, PipeMode}, std::{ ffi::OsStr, io::{self, prelude::*}, sync::{mpsc::Sender, Arc}, }, }; const SERVER_MSG_1: &[u8] = b"Server message 1"; const SERVER_MSG_2: &[u8] = b"Server message 2"; const CLIENT_MSG_1: &[u8] = b"Client message 1"; const CLIENT_MSG_2: &[u8] = b"Client message 2"; pub fn server(name_sender: Sender, num_clients: u32) -> TestResult { let (name, listener) = NameGen::new(true) .find_map(|nm| { let rnm: &OsStr = nm.as_ref(); let l = match PipeListenerOptions::new() .name(rnm) .mode(PipeMode::Messages) .create::() { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); for _ in 0..num_clients { let mut conn = match listener.accept() { Ok(c) => c, Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; let (mut buf1, mut buf2) = ([0; CLIENT_MSG_1.len()], [0; CLIENT_MSG_2.len()]); let read = conn.read(&mut buf1).context("First pipe receive failed")?; assert_eq!(read, CLIENT_MSG_1.len()); assert_eq!(&buf1[0..read], CLIENT_MSG_1); let read = conn.read(&mut buf2).context("Second pipe receive failed")?; assert_eq!(read, CLIENT_MSG_1.len()); assert_eq!(&buf1[0..read], CLIENT_MSG_1); let written = conn.write(SERVER_MSG_1).context("First pipe send failed")?; assert_eq!(written, SERVER_MSG_1.len()); let written = conn .write(SERVER_MSG_2) .context("Second pipe send failed")?; assert_eq!(written, SERVER_MSG_2.len()); } Ok(()) } pub fn client(name: Arc) -> TestResult { let (mut buf1, mut buf2) = ([0; CLIENT_MSG_1.len()], [0; CLIENT_MSG_2.len()]); let mut conn = DuplexMsgPipeStream::connect(name.as_str()).context("Connect failed")?; let written = conn.write(CLIENT_MSG_1).context("First pipe send failed")?; assert_eq!(written, CLIENT_MSG_1.len()); let written = conn .write(CLIENT_MSG_2) .context("Second pipe send failed")?; assert_eq!(written, CLIENT_MSG_2.len()); let read = conn.read(&mut buf1).context("First pipe receive failed")?; assert_eq!(read, SERVER_MSG_1.len()); assert_eq!(&buf1[0..read], SERVER_MSG_1); let read = conn.read(&mut buf2).context("Second pipe receive failed")?; assert_eq!(read, SERVER_MSG_1.len()); assert_eq!(&buf1[0..read], SERVER_MSG_1); Ok(()) } interprocess-1.2.1/tests/named_pipe/msg_unidir_client_to_server.rs000064400000000000000000000041310072674642500237730ustar 00000000000000use { super::util::{NameGen, TestResult}, anyhow::Context, interprocess::os::windows::named_pipe::{ MsgReaderPipeStream, MsgWriterPipeStream, PipeListenerOptions, PipeMode, }, std::{ ffi::OsStr, io::{self, prelude::*}, sync::{mpsc::Sender, Arc}, }, }; const MSG_1: &[u8] = b"Client message 1"; const MSG_2: &[u8] = b"Client message 2"; pub fn server(name_sender: Sender, num_clients: u32) -> TestResult { let (name, listener) = NameGen::new(true) .find_map(|nm| { let rnm: &OsStr = nm.as_ref(); let l = match PipeListenerOptions::new() .name(rnm) .mode(PipeMode::Messages) .create::() { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); for _ in 0..num_clients { let mut conn = match listener.accept() { Ok(c) => c, Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; let (mut buf1, mut buf2) = ([0; MSG_1.len()], [0; MSG_2.len()]); let read = conn.read(&mut buf1).context("First pipe receive failed")?; assert_eq!(read, MSG_1.len()); assert_eq!(&buf1[0..read], MSG_1); let read = conn.read(&mut buf2).context("Second pipe receive failed")?; assert_eq!(read, MSG_1.len()); assert_eq!(&buf1[0..read], MSG_1); } Ok(()) } pub fn client(name: Arc) -> TestResult { let mut conn = MsgWriterPipeStream::connect(name.as_str()).context("Connect failed")?; let written = conn.write(MSG_1).context("First pipe send failed")?; assert_eq!(written, MSG_1.len()); let written = conn.write(MSG_2).context("Second pipe send failed")?; assert_eq!(written, MSG_2.len()); conn.flush()?; Ok(()) } interprocess-1.2.1/tests/named_pipe/msg_unidir_server_to_client.rs000064400000000000000000000041210072674642500237720ustar 00000000000000use { super::util::{NameGen, TestResult}, anyhow::Context, interprocess::os::windows::named_pipe::{ MsgReaderPipeStream, MsgWriterPipeStream, PipeListenerOptions, PipeMode, }, std::{ ffi::OsStr, io::{self, prelude::*}, sync::{mpsc::Sender, Arc}, }, }; const MSG_1: &[u8] = b"Server message 1"; const MSG_2: &[u8] = b"Server message 2"; pub fn server(name_sender: Sender, num_clients: u32) -> TestResult { let (name, listener) = NameGen::new(true) .find_map(|nm| { let rnm: &OsStr = nm.as_ref(); let l = match PipeListenerOptions::new() .name(rnm) .mode(PipeMode::Messages) .create::() { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); for _ in 0..num_clients { let mut conn = match listener.accept() { Ok(c) => c, Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; let written = conn.write(MSG_1).context("First pipe send failed")?; assert_eq!(written, MSG_1.len()); let written = conn.write(MSG_2).context("Second pipe send failed")?; assert_eq!(written, MSG_2.len()); conn.flush()?; } Ok(()) } pub fn client(name: Arc) -> TestResult { let mut conn = MsgReaderPipeStream::connect(name.as_str()).context("Connect failed")?; let (mut buf1, mut buf2) = ([0; MSG_1.len()], [0; MSG_2.len()]); let read = conn.read(&mut buf1).context("First pipe receive failed")?; assert_eq!(read, MSG_1.len()); assert_eq!(&buf1[0..read], MSG_1); let read = conn.read(&mut buf2).context("Second pipe receive failed")?; assert_eq!(read, MSG_1.len()); assert_eq!(&buf1[0..read], MSG_1); Ok(()) } interprocess-1.2.1/tests/tokio_local_socket/main.rs000064400000000000000000000021500072674642500206760ustar 00000000000000#![cfg(feature = "tokio_support")] #[path = "../util/mod.rs"] mod util; use util::TestResult; mod no_server; mod stream; use {interprocess::local_socket::NameTypeSupport, tokio::try_join}; #[tokio::test] async fn tokio_local_socket_stream() -> TestResult { use stream::*; // If only one name type is supported, this one will choose the supported one. If both are // supported, this will try paths first. let f1 = util::tokio::drive_server_and_multiple_clients(|s, n| server(s, n, false), client); if NameTypeSupport::query() == NameTypeSupport::Both { // Try the namespace now. let f2 = util::tokio::drive_server_and_multiple_clients(|s, n| server(s, n, true), client); try_join!(f1, f2)?; } else { f1.await?; } Ok(()) } #[tokio::test] async fn tokio_local_socket_no_server() -> TestResult { // Same as above. let f1 = no_server::run_and_verify_error(false); if NameTypeSupport::query() == NameTypeSupport::Both { let f2 = no_server::run_and_verify_error(true); try_join!(f1, f2)?; } else { f1.await?; } Ok(()) } interprocess-1.2.1/tests/tokio_local_socket/no_server.rs000064400000000000000000000016130072674642500217570ustar 00000000000000//! Tests what happens when a client attempts to connect to a local socket that doesn't exist. use {super::util::*, anyhow::*, interprocess::local_socket::tokio::LocalSocketStream, std::io}; pub async fn run_and_verify_error(prefer_namespaced: bool) -> TestResult { use io::ErrorKind::*; let err = match client(prefer_namespaced).await { Err(e) => e.downcast::()?, Ok(()) => bail!("client successfully connected to nonexistent server"), }; ensure!( matches!(err.kind(), NotFound | ConnectionRefused), "expected error to be 'not found' or 'connection refused', received '{}'", err ); Ok(()) } async fn client(prefer_namespaced: bool) -> TestResult { let name = NameGen::new_auto(prefer_namespaced).next().unwrap(); LocalSocketStream::connect(name.as_str()) .await .context("Connect failed")?; Ok(()) } interprocess-1.2.1/tests/tokio_local_socket/stream.rs000064400000000000000000000055040072674642500212530ustar 00000000000000use { super::util::{NameGen, TestResult}, anyhow::Context, futures::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, interprocess::local_socket::tokio::{LocalSocketListener, LocalSocketStream}, std::{convert::TryInto, io, sync::Arc}, tokio::{sync::oneshot::Sender, task, try_join}, }; static SERVER_MSG: &str = "Hello from server!\n"; static CLIENT_MSG: &str = "Hello from client!\n"; pub async fn server( name_sender: Sender, num_clients: u32, prefer_namespaced: bool, ) -> TestResult { async fn handle_conn(conn: LocalSocketStream) -> TestResult { let (reader, mut writer) = conn.into_split(); let mut buffer = String::with_capacity(128); let mut reader = BufReader::new(reader); let read = async { reader .read_line(&mut buffer) .await .context("Socket receive failed") }; let write = async { writer .write_all(SERVER_MSG.as_bytes()) .await .context("Socket send failed") }; try_join!(read, write)?; assert_eq!(buffer, CLIENT_MSG); Ok(()) } let (name, listener) = NameGen::new_auto(prefer_namespaced) .find_map(|nm| { let l = match LocalSocketListener::bind(&*nm) { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); let mut tasks = Vec::with_capacity(num_clients.try_into().unwrap()); for _ in 0..num_clients { let conn = match listener.accept().await { Ok(c) => c, Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; tasks.push(task::spawn(handle_conn(conn))); } for task in tasks { task.await .context("Server task panicked")? .context("Server task returned early with error")?; } Ok(()) } pub async fn client(name: Arc) -> TestResult { let mut buffer = String::with_capacity(128); let (reader, mut writer) = LocalSocketStream::connect(name.as_str()) .await .context("Connect failed")? .into_split(); let mut reader = BufReader::new(reader); let read = async { reader .read_line(&mut buffer) .await .context("Socket receive failed") }; let write = async { writer .write_all(CLIENT_MSG.as_bytes()) .await .context("Socket send failed") }; try_join!(read, write)?; assert_eq!(buffer, SERVER_MSG); Ok(()) } interprocess-1.2.1/tests/tokio_named_pipe/bytes.rs000064400000000000000000000063410072674642500205450ustar 00000000000000use { super::util::{NameGen, TestResult}, anyhow::Context, futures::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, interprocess::os::windows::named_pipe::{ tokio::{DuplexBytePipeStream, PipeListenerOptionsExt}, PipeListenerOptions, }, std::{convert::TryInto, ffi::OsStr, io, sync::Arc, time::Duration}, tokio::{sync::oneshot::Sender, task, time::sleep, try_join}, }; static SERVER_MSG: &str = "Hello from server!\n"; static CLIENT_MSG: &str = "Hello from client!\n"; pub async fn server(name_sender: Sender, num_clients: u32) -> TestResult { async fn handle_conn(conn: DuplexBytePipeStream) -> TestResult { let (reader, mut writer) = conn.split(); let mut buffer = String::with_capacity(128); let mut reader = BufReader::new(reader); let read = async { reader .read_line(&mut buffer) .await .context("Pipe receive failed") }; let write = async { writer .write_all(SERVER_MSG.as_bytes()) .await .context("Pipe send failed") }; try_join!(read, write)?; assert_eq!(buffer, CLIENT_MSG); Ok(()) } let (name, listener) = NameGen::new(true) .find_map(|nm| { let rnm: &OsStr = nm.as_ref(); let l = match PipeListenerOptions::new() .name(rnm) .create_tokio::() { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); let mut tasks = Vec::with_capacity(num_clients.try_into().unwrap()); for _ in 0..num_clients { let conn = match listener.accept().await { Ok(c) => c, Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; let task = task::spawn(handle_conn(conn)); tasks.push(task); } for task in tasks { task.await .context("Server task panicked")? .context("Server task returned early with error")?; } Ok(()) } pub async fn client(name: Arc) -> TestResult { let mut buffer = String::with_capacity(128); let (reader, mut writer) = loop { match DuplexBytePipeStream::connect(name.as_str()) { Err(e) if e.kind() == io::ErrorKind::WouldBlock => { sleep(Duration::from_millis(10)).await; continue; } not_busy => break not_busy, } } .context("Connect failed")? .split(); let mut reader = BufReader::new(reader); let read = async { reader .read_line(&mut buffer) .await .context("Pipe receive failed") }; let write = async { writer .write_all(CLIENT_MSG.as_bytes()) .await .context("Pipe send failed") }; try_join!(read, write)?; assert_eq!(buffer, SERVER_MSG); Ok(()) } interprocess-1.2.1/tests/tokio_named_pipe/bytes_unidir_client_to_server.rs000064400000000000000000000047320072674642500255470ustar 00000000000000use { super::util::{NameGen, TestResult}, anyhow::Context, futures::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, interprocess::os::windows::named_pipe::{ tokio::{ByteReaderPipeStream, ByteWriterPipeStream, PipeListenerOptionsExt}, PipeListenerOptions, }, std::{convert::TryInto, ffi::OsStr, io, sync::Arc, time::Duration}, tokio::{sync::oneshot::Sender, task, time::sleep}, }; static MSG: &str = "Hello from client!\n"; pub async fn server(name_sender: Sender, num_clients: u32) -> TestResult { async fn handle_conn(conn: ByteReaderPipeStream) -> TestResult { let mut buffer = String::with_capacity(128); let mut conn = BufReader::new(conn); conn.read_line(&mut buffer) .await .context("Pipe receive failed")?; assert_eq!(buffer, MSG); Ok(()) } let (name, listener) = NameGen::new(true) .find_map(|nm| { let rnm: &OsStr = nm.as_ref(); let l = match PipeListenerOptions::new() .name(rnm) .create_tokio::() { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); let mut tasks = Vec::with_capacity(num_clients.try_into().unwrap()); for _ in 0..num_clients { let conn = match listener.accept().await { Ok(c) => c, Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; let task = task::spawn(handle_conn(conn)); tasks.push(task); } for task in tasks { task.await .context("Server task panicked")? .context("Server task returned early with error")?; } Ok(()) } pub async fn client(name: Arc) -> TestResult { let mut conn = loop { match ByteWriterPipeStream::connect(name.as_str()) { Err(e) if e.kind() == io::ErrorKind::WouldBlock => { sleep(Duration::from_millis(10)).await; continue; } not_busy => break not_busy, } } .context("Connect failed")?; conn.write_all(MSG.as_bytes()) .await .context("Pipe send failed")?; Ok(()) } interprocess-1.2.1/tests/tokio_named_pipe/bytes_unidir_server_to_client.rs000064400000000000000000000047260072674642500255520ustar 00000000000000use { super::util::{NameGen, TestResult}, anyhow::Context, futures::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, interprocess::os::windows::named_pipe::{ tokio::{ByteReaderPipeStream, DuplexBytePipeStream, PipeListenerOptionsExt}, PipeListenerOptions, }, std::{convert::TryInto, ffi::OsStr, io, sync::Arc, time::Duration}, tokio::{sync::oneshot::Sender, task, time::sleep}, }; static MSG: &str = "Hello from server!\n"; pub async fn server(name_sender: Sender, num_clients: u32) -> TestResult { async fn handle_conn(mut conn: DuplexBytePipeStream) -> TestResult { conn.write_all(MSG.as_bytes()) .await .context("Pipe send failed")?; drop(conn); Ok(()) } let (name, listener) = NameGen::new(true) .find_map(|nm| { let rnm: &OsStr = nm.as_ref(); let l = match PipeListenerOptions::new() .name(rnm) .create_tokio::() { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); let mut tasks = Vec::with_capacity(num_clients.try_into().unwrap()); for _ in 0..num_clients { let conn = match listener.accept().await { Ok(c) => c, Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; let task = task::spawn(handle_conn(conn)); tasks.push(task); } for task in tasks { task.await .context("Server task panicked")? .context("Server task returned early with error")?; } Ok(()) } pub async fn client(name: Arc) -> TestResult { let mut buffer = String::with_capacity(128); let mut conn = loop { match ByteReaderPipeStream::connect(name.as_str()) { Err(e) if e.kind() == io::ErrorKind::WouldBlock => { sleep(Duration::from_millis(10)).await; continue; } not_busy => break not_busy, } } .context("Connect failed") .map(BufReader::new)?; conn.read_line(&mut buffer) .await .context("Pipe receive failed")?; assert_eq!(buffer, MSG); Ok(()) } interprocess-1.2.1/tests/tokio_named_pipe/main.rs000064400000000000000000000030600072674642500203360ustar 00000000000000#![cfg(all(windows, feature = "tokio_support"))] #[path = "../util/mod.rs"] mod util; mod bytes; mod bytes_unidir_client_to_server; mod bytes_unidir_server_to_client; mod msg; mod msg_unidir_client_to_server; /*mod msg_unidir_server_to_client;*/ use util::TestResult; #[tokio::test] async fn tokio_named_pipe_bytes() -> TestResult { util::tokio::drive_server_and_multiple_clients(bytes::server, bytes::client).await } #[tokio::test] async fn tokio_named_pipe_bytes_unidir_client_to_server() -> TestResult { util::tokio::drive_server_and_multiple_clients( bytes_unidir_client_to_server::server, bytes_unidir_client_to_server::client, ) .await } #[tokio::test] async fn tokio_named_pipe_bytes_unidir_server_to_client() -> TestResult { util::tokio::drive_server_and_multiple_clients( bytes_unidir_server_to_client::server, bytes_unidir_server_to_client::client, ) .await } #[tokio::test] async fn tokio_named_pipe_msg() -> TestResult { util::tokio::drive_server_and_multiple_clients(msg::server, msg::client).await } #[tokio::test] async fn tokio_named_pipe_msg_unidir_client_to_server() -> TestResult { util::tokio::drive_server_and_multiple_clients( msg_unidir_client_to_server::server, msg_unidir_client_to_server::client, ) .await } /*#[tokio::test] async fn tokio_named_pipe_msg_unidir_server_to_client() -> TestResult { util::tokio::drive_server_and_multiple_clients( msg_unidir_server_to_client::server, msg_unidir_server_to_client::client, ) .await }*/ interprocess-1.2.1/tests/tokio_named_pipe/msg.rs000064400000000000000000000110340072674642500202000ustar 00000000000000use { super::util::{NameGen, TestResult}, anyhow::Context, futures::io::{AsyncReadExt, AsyncWriteExt}, interprocess::os::windows::named_pipe::{ tokio::{DuplexMsgPipeStream, PipeListenerOptionsExt}, PipeListenerOptions, PipeMode, }, std::{convert::TryInto, ffi::OsStr, io, sync::Arc, time::Duration}, tokio::{sync::oneshot::Sender, task, time::sleep, try_join}, }; const SERVER_MSG_1: &[u8] = b"Server message 1"; const SERVER_MSG_2: &[u8] = b"Server message 2"; const CLIENT_MSG_1: &[u8] = b"Client message 1"; const CLIENT_MSG_2: &[u8] = b"Client message 2"; pub async fn server(name_sender: Sender, num_clients: u32) -> TestResult { async fn handle_conn(conn: DuplexMsgPipeStream) -> TestResult { let (mut reader, mut writer) = conn.split(); let (mut buf1, mut buf2) = ([0; CLIENT_MSG_1.len()], [0; CLIENT_MSG_2.len()]); let read = async { let read = reader .read(&mut buf1) .await .context("First pipe receive failed")?; assert_eq!(read, CLIENT_MSG_1.len()); assert_eq!(&buf1[0..read], CLIENT_MSG_1); let read = reader .read(&mut buf2) .await .context("Second pipe receive failed")?; assert_eq!(read, CLIENT_MSG_2.len()); assert_eq!(&buf2[0..read], CLIENT_MSG_2); TestResult::Ok(()) }; let write = async { let written = writer .write(SERVER_MSG_1) .await .context("Pipe send failed")?; assert_eq!(written, SERVER_MSG_1.len()); let written = writer .write(SERVER_MSG_2) .await .context("Pipe send failed")?; assert_eq!(written, SERVER_MSG_2.len()); TestResult::Ok(()) }; try_join!(read, write)?; Ok(()) } let (name, listener) = NameGen::new(true) .find_map(|nm| { let rnm: &OsStr = nm.as_ref(); let l = match PipeListenerOptions::new() .name(rnm) .mode(PipeMode::Messages) .create_tokio::() { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); let mut tasks = Vec::with_capacity(num_clients.try_into().unwrap()); for _ in 0..num_clients { let conn = match listener.accept().await { Ok(c) => c, Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; let task = task::spawn(handle_conn(conn)); tasks.push(task); } for task in tasks { task.await .context("Server task panicked")? .context("Server task returned early with error")?; } Ok(()) } pub async fn client(name: Arc) -> TestResult { let (mut reader, mut writer) = loop { match DuplexMsgPipeStream::connect(name.as_str()) { Err(e) if e.kind() == io::ErrorKind::WouldBlock => { sleep(Duration::from_millis(10)).await; continue; } not_busy => break not_busy, } } .context("Connect failed")? .split(); let (mut buf1, mut buf2) = ([0; SERVER_MSG_1.len()], [0; SERVER_MSG_2.len()]); let read = async { let read = reader .read(&mut buf1) .await .context("First pipe receive failed")?; assert_eq!(read, SERVER_MSG_1.len()); assert_eq!(&buf1[0..read], SERVER_MSG_1); let read = reader .read(&mut buf2) .await .context("Second pipe receive failed")?; assert_eq!(read, SERVER_MSG_2.len()); assert_eq!(&buf2[0..read], SERVER_MSG_2); TestResult::Ok(()) }; let write = async { let written = writer .write(CLIENT_MSG_1) .await .context("First pipe send failed")?; assert_eq!(written, CLIENT_MSG_1.len()); let written = writer .write(CLIENT_MSG_2) .await .context("Second pipe send failed")?; assert_eq!(written, CLIENT_MSG_2.len()); TestResult::Ok(()) }; try_join!(read, write)?; Ok(()) } interprocess-1.2.1/tests/tokio_named_pipe/msg_unidir_client_to_server.rs000064400000000000000000000056660072674642500252160ustar 00000000000000use { super::util::{NameGen, TestResult}, anyhow::Context, futures::io::{AsyncReadExt, AsyncWriteExt}, interprocess::os::windows::named_pipe::{ tokio::{MsgReaderPipeStream, MsgWriterPipeStream, PipeListenerOptionsExt}, PipeListenerOptions, PipeMode, }, std::{convert::TryInto, ffi::OsStr, io, sync::Arc, time::Duration}, tokio::{sync::oneshot::Sender, task, time::sleep}, }; const MSG_1: &[u8] = b"Client message 1"; const MSG_2: &[u8] = b"Client message 2"; pub async fn server(name_sender: Sender, num_clients: u32) -> TestResult { async fn handle_conn(mut conn: MsgReaderPipeStream) -> TestResult { let (mut buf1, mut buf2) = ([0; MSG_1.len()], [0; MSG_2.len()]); let read = conn .read(&mut buf1) .await .context("First pipe receive failed")?; assert_eq!(read, MSG_1.len()); assert_eq!(&buf1[0..read], MSG_1); let read = conn .read(&mut buf2) .await .context("Second pipe receive failed")?; assert_eq!(read, MSG_2.len()); assert_eq!(&buf2[0..read], MSG_2); Ok(()) } let (name, listener) = NameGen::new(true) .find_map(|nm| { let rnm: &OsStr = nm.as_ref(); let l = match PipeListenerOptions::new() .name(rnm) .mode(PipeMode::Messages) .create_tokio::() { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); let mut tasks = Vec::with_capacity(num_clients.try_into().unwrap()); for _ in 0..num_clients { let conn = match listener.accept().await { Ok(c) => c, Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; let task = task::spawn(handle_conn(conn)); tasks.push(task); } for task in tasks { task.await .context("Server task panicked")? .context("Server task returned early with error")?; } Ok(()) } pub async fn client(name: Arc) -> TestResult { let mut conn = loop { match MsgWriterPipeStream::connect(name.as_str()) { Err(e) if e.kind() == io::ErrorKind::WouldBlock => { sleep(Duration::from_millis(10)).await; continue; } not_busy => break not_busy, } } .context("Connect failed")?; let written = conn.write(MSG_1).await.context("First pipe send failed")?; assert_eq!(written, MSG_1.len()); let written = conn.write(MSG_2).await.context("Second pipe send failed")?; assert_eq!(written, MSG_2.len()); Ok(()) } interprocess-1.2.1/tests/tokio_named_pipe/msg_unidir_server_to_client.rs000064400000000000000000000056220072674642500252060ustar 00000000000000use { super::util::{NameGen, TestResult}, anyhow::Context, futures::io::{AsyncReadExt, AsyncWriteExt}, interprocess::os::windows::named_pipe::{ tokio::{DuplexMsgPipeStream, MsgReaderPipeStream, PipeListenerOptionsExt}, PipeListenerOptions, PipeMode, }, std::{convert::TryInto, ffi::OsStr, io, sync::Arc, time::Duration}, tokio::{sync::oneshot::Sender, task, time::sleep}, }; const MSG_1: &[u8] = b"Server message 1"; const MSG_2: &[u8] = b"Server message 2"; pub async fn server(name_sender: Sender, num_clients: u32) -> TestResult { async fn handle_conn(mut conn: DuplexMsgPipeStream) -> TestResult { let written = conn.write(MSG_1).await.context("First pipe send failed")?; assert_eq!(written, MSG_1.len()); let written = conn.write(MSG_2).await.context("Second pipe send failed")?; assert_eq!(written, MSG_2.len()); Ok(()) } let (name, listener) = NameGen::new(true) .find_map(|nm| { let rnm: &OsStr = nm.as_ref(); let l = match PipeListenerOptions::new() .name(rnm) .mode(PipeMode::Messages) .create_tokio::() { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); let mut tasks = Vec::with_capacity(num_clients.try_into().unwrap()); for _ in 0..num_clients { let conn = match listener.accept().await { Ok(c) => c, Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; let task = task::spawn(handle_conn(conn)); tasks.push(task); } for task in tasks { task.await .context("Server task panicked")? .context("Server task returned early with error")?; } Ok(()) } pub async fn client(name: Arc) -> TestResult { let mut conn = loop { match MsgReaderPipeStream::connect(name.as_str()) { Err(e) if e.kind() == io::ErrorKind::WouldBlock => { sleep(Duration::from_millis(10)).await; continue; } not_busy => break not_busy, } } .context("Connect failed")?; let (mut buf1, mut buf2) = ([0; MSG_1.len()], [0; MSG_2.len()]); let read = conn .read(&mut buf1) .await .context("First pipe receive failed")?; assert_eq!(read, MSG_1.len()); assert_eq!(&buf1[0..read], MSG_1); let read = conn .read(&mut buf2) .await .context("Second pipe receive failed")?; assert_eq!(read, MSG_2.len()); assert_eq!(&buf2[0..read], MSG_2); Ok(()) } interprocess-1.2.1/tests/udsocket/datagram.rs000064400000000000000000000056650072674642500175220ustar 00000000000000use { super::util::*, anyhow::Context, interprocess::os::unix::udsocket::{UdStream, UdStreamListener}, std::{ io, sync::{mpsc::Sender, Arc}, }, }; const SERVER_MSG_1: &[u8] = b"Server message 1"; const SERVER_MSG_2: &[u8] = b"Server message 2"; const CLIENT_MSG_1: &[u8] = b"Client message 1"; const CLIENT_MSG_2: &[u8] = b"Client message 2"; pub(super) fn run_with_namegen(namegen: NameGen) { drive_server_and_multiple_clients(move |snd, nc| server(snd, nc, namegen), client); } fn server(name_sender: Sender, num_clients: u32, mut namegen: NameGen) -> TestResult { let (name, listener) = namegen .find_map(|nm| { let l = match UdStreamListener::bind(&*nm) { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); for _ in 0..num_clients { let conn = match listener.accept() { Ok(c) => c, Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; let (mut buf1, mut buf2) = ([0; CLIENT_MSG_1.len()], [0; CLIENT_MSG_2.len()]); let read = conn .recv(&mut buf1) .context("First socket receive failed")?; assert_eq!(read, CLIENT_MSG_1.len()); assert_eq!(&buf1[0..read], CLIENT_MSG_1); let read = conn .recv(&mut buf2) .context("Second socket receive failed")?; assert_eq!(read, CLIENT_MSG_2.len()); assert_eq!(&buf2[0..read], CLIENT_MSG_2); let written = conn .send(SERVER_MSG_1) .context("First socket send failed")?; assert_eq!(written, SERVER_MSG_1.len()); let written = conn .send(SERVER_MSG_2) .context("Second socket send failed")?; assert_eq!(written, SERVER_MSG_2.len()); } Ok(()) } fn client(name: Arc) -> TestResult { let (mut buf1, mut buf2) = ([0; CLIENT_MSG_1.len()], [0; CLIENT_MSG_2.len()]); let conn = UdStream::connect(name.as_str()).context("Connect failed")?; let written = conn .send(CLIENT_MSG_1) .context("First socket send failed")?; assert_eq!(written, CLIENT_MSG_1.len()); let written = conn .send(CLIENT_MSG_2) .context("Second socket send failed")?; assert_eq!(written, CLIENT_MSG_2.len()); let read = conn .recv(&mut buf1) .context("First socket receive failed")?; assert_eq!(read, SERVER_MSG_1.len()); assert_eq!(&buf1[0..read], SERVER_MSG_1); let read = conn .recv(&mut buf2) .context("Second socket receive failed")?; assert_eq!(read, SERVER_MSG_2.len()); assert_eq!(&buf2[0..read], SERVER_MSG_2); Ok(()) } interprocess-1.2.1/tests/udsocket/main.rs000064400000000000000000000007160072674642500166560ustar 00000000000000#![cfg(unix)] #[path = "../util/mod.rs"] mod util; use util::*; mod datagram; mod stream; #[test] fn udsocket_stream() { use stream::*; run_with_namegen(NameGen::new(false)); if cfg!(target_os = "linux") { run_with_namegen(NameGen::new(true)); } } #[test] fn udsocket_datagram() { use datagram::*; run_with_namegen(NameGen::new(false)); if cfg!(target_os = "linux") { run_with_namegen(NameGen::new(true)); } } interprocess-1.2.1/tests/udsocket/stream.rs000064400000000000000000000053030072674642500172220ustar 00000000000000use { super::util::*, anyhow::Context, interprocess::os::unix::udsocket::{UdStream, UdStreamListener}, std::{ io::{self, BufRead, BufReader, Read, Write}, net::Shutdown, sync::{mpsc::Sender, Arc}, }, }; static SERVER_MSG: &str = "Hello from server!\n"; static CLIENT_MSG: &str = "Hello from client!\n"; pub(super) fn run_with_namegen(namegen: NameGen) { drive_server_and_multiple_clients( move |snd, nc| server(snd, nc, namegen, false), |nm| client(nm, false), ); drive_server_and_multiple_clients( move |snd, nc| server(snd, nc, namegen, true), |nm| client(nm, true), ); } fn server( name_sender: Sender, num_clients: u32, mut namegen: NameGen, shutdown: bool, ) -> TestResult { let (name, listener) = namegen .find_map(|nm| { let l = match UdStreamListener::bind(&*nm) { Ok(l) => l, Err(e) if e.kind() == io::ErrorKind::AddrInUse => return None, Err(e) => return Some(Err(e)), }; Some(Ok((nm, l))) }) .unwrap() .context("Listener bind failed")?; let _ = name_sender.send(name); let mut buffer = String::with_capacity(128); for _ in 0..num_clients { let mut conn = match listener.accept() { Ok(c) => BufReader::new(c), Err(e) => { eprintln!("Incoming connection failed: {}", e); continue; } }; if shutdown { conn.read_to_string(&mut buffer) } else { conn.read_line(&mut buffer) } .context("Socket receive failed")?; conn.get_mut() .write_all(SERVER_MSG.as_bytes()) .context("Socket send failed")?; if shutdown { conn.get_mut() .shutdown(Shutdown::Write) .context("Shutdown of writing end failed")?; } assert_eq!(buffer, CLIENT_MSG); buffer.clear(); } Ok(()) } fn client(name: Arc, shutdown: bool) -> TestResult { let mut buffer = String::with_capacity(128); let conn = UdStream::connect(name.as_str()).context("Connect failed")?; let mut conn = BufReader::new(conn); conn.get_mut() .write_all(CLIENT_MSG.as_bytes()) .context("Socket send failed")?; if shutdown { conn.get_mut() .shutdown(Shutdown::Write) .context("Shutdown of writing end failed")?; } if shutdown { conn.read_to_string(&mut buffer) } else { conn.read_line(&mut buffer) } .context("Socket receive failed")?; assert_eq!(buffer, SERVER_MSG); Ok(()) } interprocess-1.2.1/tests/util/choke.rs000064400000000000000000000030730072674642500161560ustar 00000000000000use std::sync::{Arc, Condvar, Mutex, Weak}; /// Choke – a rate-limiting semaphore that does not protect any concurrently accessed resource. #[derive(Debug)] pub struct Choke(Arc); impl Choke { pub fn new(limit: u32) -> Self { let inner = ChokeInner { count: Mutex::new(0), limit, condvar: Condvar::new(), }; Self(Arc::new(inner)) } pub fn take(&self) -> ChokeGuard { let mut lock = Some(self.0.count.lock().unwrap()); loop { let mut c_lock = lock.take().unwrap(); if *c_lock < self.0.limit { *c_lock += 1; return self.make_guard(); } else { let c_lock = self.0.condvar.wait(c_lock).unwrap(); lock = Some(c_lock); } } } fn make_guard(&self) -> ChokeGuard { ChokeGuard(Arc::downgrade(&self.0)) } } impl Clone for Choke { fn clone(&self) -> Self { Self(self.0.clone()) } } #[derive(Debug)] struct ChokeInner { count: Mutex, limit: u32, condvar: Condvar, } impl ChokeInner { fn decrement(&self) { let mut count = self.count.lock().unwrap(); *count = count.checked_sub(1).expect("choke counter underflow"); self.condvar.notify_one(); } } /// A guard for `Choke` that owns one unit towards the limit. pub struct ChokeGuard(Weak); impl Drop for ChokeGuard { fn drop(&mut self) { if let Some(inner) = self.0.upgrade() { inner.decrement(); } } } interprocess-1.2.1/tests/util/mod.rs000064400000000000000000000056470072674642500156550ustar 00000000000000//! Test utilities for allocating an address for the server and then spawning clients to connect to it. #![allow(dead_code)] mod choke; use choke::*; mod xorshift; pub use xorshift::*; mod namegen; pub use namegen::*; #[cfg(feature = "tokio_support")] pub mod tokio; const NUM_CLIENTS: u32 = 20; const NUM_CONCURRENT_CLIENTS: u32 = 4; use { std::{ sync::{ mpsc::{channel, /*Receiver,*/ Sender}, Arc, }, thread, }, to_method::*, }; pub type TestResult = anyhow::Result<()>; /// Waits for the leader closure to reach a point where it sends a message for the follower closure, then runs the follower. Captures Anyhow errors on both sides and panics if any occur, reporting which side produced the error. pub fn drive_pair(leader: Ld, leader_name: &str, follower: Fl, follower_name: &str) where T: Send + 'static, Ld: FnOnce(Sender) -> TestResult + Send + 'static, Fl: FnOnce(T) -> TestResult, { let (sender, receiver) = channel(); let ltname = leader_name.to_lowercase(); let leading_thread = thread::Builder::new() .name(ltname) .spawn(move || leader(sender)) // Lazy .expect() .unwrap_or_else(|e| panic!("{} thread launch failed: {}", leader_name, e)); if let Ok(msg) = receiver.recv() { // If the leader reached the send point, proceed with the follower code let fres = follower(msg); if let Err(e) = fres { panic!("{} exited early with error: {:#}", follower_name, e); } } match leading_thread.join() { Err(_) => panic!("{} panicked", leader_name), Ok(Err(error)) => panic!("{} exited early with error: {:#}", leader_name, error), _ => (), } } pub fn drive_server_and_multiple_clients(server: Srv, client: Clt) where T: Send + Sync + 'static, Srv: FnOnce(Sender, u32) -> TestResult + Send + 'static, Clt: Fn(Arc) -> TestResult + Send + Sync + 'static, { let choke = Choke::new(NUM_CONCURRENT_CLIENTS); let client = Arc::new(client); let client_wrapper = move |msg| { let msg = Arc::new(msg); let mut client_threads = Vec::with_capacity(NUM_CLIENTS.try_to().unwrap()); for _ in 0..NUM_CLIENTS { let choke_guard = choke.take(); let clientc = Arc::clone(&client); let msgc = Arc::clone(&msg); let jhndl = thread::spawn(move || { let _cg = choke_guard; // Send to other thread to drop when client finishes clientc(msgc) }); client_threads.push(jhndl); } for client in client_threads { client.join().expect("Client panicked")?; // Early-return the first error } Ok::<(), anyhow::Error>(()) }; let server_wrapper = move |sender: Sender| server(sender, NUM_CLIENTS); drive_pair(server_wrapper, "Server", client_wrapper, "Client"); } interprocess-1.2.1/tests/util/namegen.rs000064400000000000000000000024160072674642500164770ustar 00000000000000use {super::Xorshift32, interprocess::local_socket::NameTypeSupport}; #[derive(Copy, Clone, Debug)] pub struct NameGen { rng: Xorshift32, namespaced: bool, } impl NameGen { pub fn new(namespaced: bool) -> Self { Self { rng: Xorshift32::from_system_time(), namespaced, } } /// Automatically chooses name type based on OS support and preference. pub fn new_auto(prefer_namespaced: bool) -> Self { let namespaced = { use NameTypeSupport::*; let nts = NameTypeSupport::query(); match (nts, prefer_namespaced) { (OnlyPaths, _) | (Both, false) => false, (OnlyNamespaced, _) | (Both, true) => true, } }; Self::new(namespaced) } fn next_path(&mut self) -> String { format!("/tmp/interprocess-test-{:08x}.sock", self.rng.next()) } fn next_namespaced(&mut self) -> String { format!("@interprocess-test-{:08x}.sock", self.rng.next()) } } impl Iterator for NameGen { type Item = String; fn next(&mut self) -> Option { let name = match self.namespaced { false => self.next_path(), true => self.next_namespaced(), }; Some(name) } } interprocess-1.2.1/tests/util/tokio.rs000064400000000000000000000051670072674642500162200ustar 00000000000000use { super::{TestResult, NUM_CLIENTS, NUM_CONCURRENT_CLIENTS}, anyhow::Context, std::{convert::TryInto, future::Future, sync::Arc}, tokio::{ sync::{ oneshot::{channel, Sender}, Semaphore, }, task, try_join, }, }; /// Waits for the leader closure to reach a point where it sends a message for the follower closure, then runs the follower. Captures Anyhow errors on both sides and panics if any occur, reporting which side produced the error. pub async fn drive_pair( leader: Ld, leader_name: &str, follower: Fl, follower_name: &str, ) -> TestResult where Ld: FnOnce(Sender) -> Ldf, Ldf: Future, Fl: FnOnce(T) -> Flf, Flf: Future, { let (sender, receiver) = channel(); let leading_task = async { leader(sender) .await .with_context(|| format!("{} exited early with error", leader_name)) }; let following_task = async { let msg = receiver.await?; follower(msg) .await .with_context(|| format!("{} exited early with error", follower_name)) }; try_join!(leading_task, following_task).map(|((), ())| ()) } pub async fn drive_server_and_multiple_clients( server: Srv, client: Clt, ) -> TestResult where T: Send + Sync + 'static, Srv: FnOnce(Sender, u32) -> Srvf + Send + 'static, Srvf: Future, Clt: Fn(Arc) -> Cltf + Send + Sync + 'static, Cltf: Future + Send, { let client_wrapper = move |msg| async { let client = Arc::new(client); let choke = Arc::new(Semaphore::new(NUM_CONCURRENT_CLIENTS.try_into().unwrap())); let msg = Arc::new(msg); let mut client_tasks = Vec::with_capacity(NUM_CLIENTS.try_into().unwrap()); for _ in 0..NUM_CLIENTS { let permit = Arc::clone(&choke).acquire_owned().await.unwrap(); let clientc = Arc::clone(&client); let msgc = Arc::clone(&msg); let jhndl = task::spawn(async move { let _prm = permit; // Send to other thread to drop when client finishes clientc(msgc).await }); client_tasks.push(jhndl); } for client in client_tasks { client.await.expect("Client panicked")?; // Early-return the first error } Ok::<(), anyhow::Error>(()) }; let server_wrapper = move |sender: Sender| server(sender, NUM_CLIENTS); drive_pair(server_wrapper, "Server", client_wrapper, "Client").await } interprocess-1.2.1/tests/util/xorshift.rs000064400000000000000000000014070072674642500167320ustar 00000000000000use std::time::{SystemTime, UNIX_EPOCH}; /// The 32-bit variant of the Xorshift PRNG algorithm. /// /// Didn't feel like pulling in the `rand` crate, so have this here beauty instead. #[repr(transparent)] #[derive(Copy, Clone, Debug)] pub struct Xorshift32(pub u32); impl Xorshift32 { pub fn from_system_time() -> Self { let dur = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_else(|e| e.duration()); Self(dur.subsec_nanos()) } pub fn next(&mut self) -> u32 { self.0 ^= self.0 << 13; self.0 ^= self.0 >> 17; self.0 ^= self.0 << 5; self.0 } } impl Iterator for Xorshift32 { type Item = u32; fn next(&mut self) -> Option { Some(self.next()) } }