filedescriptor-0.8.3/.cargo_vcs_info.json0000644000000001540000000000100140620ustar { "git": { "sha1": "4c619a44dc07ad611d28a8af0416f25462d6b3ad" }, "path_in_vcs": "filedescriptor" }filedescriptor-0.8.3/Cargo.lock0000644000000046670000000000100120520ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "filedescriptor" version = "0.8.3" dependencies = [ "libc", "thiserror", "winapi", ] [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[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" filedescriptor-0.8.3/Cargo.toml0000644000000024210000000000100120570ustar # 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 = "filedescriptor" version = "0.8.3" authors = ["Wez Furlong"] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "More ergonomic wrappers around RawFd and RawHandle" documentation = "https://docs.rs/filedescriptor" readme = "README.md" keywords = [ "socketpair", "pipe", "poll", "filedescriptor", ] license = "MIT" repository = "https://github.com/wezterm/wezterm" resolver = "2" [lib] name = "filedescriptor" path = "src/lib.rs" [dependencies.libc] version = "0.2" [dependencies.thiserror] version = "1.0" [target."cfg(windows)".dependencies.winapi] version = "0.3" features = [ "winuser", "handleapi", "fileapi", "namedpipeapi", "processthreadsapi", "winsock2", "processenv", ] filedescriptor-0.8.3/Cargo.toml.orig000064400000000000000000000011441046102023000155410ustar 00000000000000[package] name = "filedescriptor" version = "0.8.3" authors = ["Wez Furlong"] edition = "2018" repository = "https://github.com/wezterm/wezterm" description = "More ergonomic wrappers around RawFd and RawHandle" license = "MIT" documentation = "https://docs.rs/filedescriptor" readme = "README.md" keywords = ["socketpair", "pipe", "poll", "filedescriptor"] [dependencies] thiserror = "1.0" libc = "0.2" [target."cfg(windows)".dependencies] winapi = { version = "0.3", features = [ "winuser", "handleapi", "fileapi", "namedpipeapi", "processthreadsapi", "winsock2", "processenv" ]} filedescriptor-0.8.3/LICENSE.md000064400000000000000000000020541046102023000142570ustar 00000000000000MIT License Copyright (c) 2018 Wez Furlong Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. filedescriptor-0.8.3/README.md000064400000000000000000000047771046102023000141500ustar 00000000000000 The purpose of this crate is to make it a bit more ergonomic for portable applications that need to work with the platform level `RawFd` and `RawHandle` types. Rather than conditionally using `RawFd` and `RawHandle`, the `FileDescriptor` type can be used to manage ownership, duplicate, read and write. ## FileDescriptor This is a bit of a contrived example, but demonstrates how to avoid the conditional code that would otherwise be required to deal with calling `as_raw_fd` and `as_raw_handle`: ``` use filedescriptor::{FileDescriptor, FromRawFileDescriptor, Result}; use std::io::Write; fn get_stdout() -> Result { let stdout = std::io::stdout(); let handle = stdout.lock(); FileDescriptor::dup(&handle) } fn print_something() -> Result<()> { get_stdout()?.write(b"hello")?; Ok(()) } ``` ## Pipe The `Pipe` type makes it more convenient to create a pipe and manage the lifetime of both the read and write ends of that pipe. ``` use filedescriptor::Pipe; use std::io::{Read, Write}; let mut pipe = Pipe::new()?; pipe.write.write(b"hello")?; drop(pipe.write); let mut s = String::new(); pipe.read.read_to_string(&mut s)?; assert_eq!(s, "hello"); ``` ## Socketpair The `socketpair` function returns a pair of connected `SOCK_STREAM` sockets and functions both on posix and windows systems. ``` use std::io::{Read, Write}; let (mut a, mut b) = filedescriptor::socketpair()?; a.write(b"hello")?; drop(a); let mut s = String::new(); b.read_to_string(&mut s)?; assert_eq!(s, "hello"); ``` ## Polling The `mio` crate offers powerful and scalable IO multiplexing, but there are some situations where `mio` doesn't fit. The `filedescriptor` crate offers a `poll(2)` compatible interface suitable for testing the readiness of a set of file descriptors. On unix systems this is a very thin wrapper around `poll(2)`, except on macOS where it is actually a wrapper around the `select(2)` interface. On Windows systems the winsock `WSAPoll` function is used instead. ``` use filedescriptor::*; use std::time::Duration; use std::io::{Read, Write}; let (mut a, mut b) = filedescriptor::socketpair()?; let mut poll_array = [pollfd { fd: a.as_socket_descriptor(), events: POLLIN, revents: 0 }]; // sleeps for 20 milliseconds because `a` is not yet ready assert_eq!(poll(&mut poll_array, Some(Duration::from_millis(20)))?, 0); b.write(b"hello")?; // Now a is ready for read assert_eq!(poll(&mut poll_array, Some(Duration::from_millis(20)))?, 1); ``` filedescriptor-0.8.3/src/lib.rs000064400000000000000000000337161046102023000145670ustar 00000000000000//! The purpose of this crate is to make it a bit more ergonomic for portable //! applications that need to work with the platform level `RawFd` and //! `RawHandle` types. //! //! Rather than conditionally using `RawFd` and `RawHandle`, the `FileDescriptor` //! type can be used to manage ownership, duplicate, read and write. //! //! ## FileDescriptor //! //! This is a bit of a contrived example, but demonstrates how to avoid //! the conditional code that would otherwise be required to deal with //! calling `as_raw_fd` and `as_raw_handle`: //! //! ``` //! use filedescriptor::{FileDescriptor, FromRawFileDescriptor, Result}; //! use std::io::Write; //! //! fn get_stdout() -> Result { //! let stdout = std::io::stdout(); //! let handle = stdout.lock(); //! FileDescriptor::dup(&handle) //! } //! //! fn print_something() -> Result<()> { //! get_stdout()?.write(b"hello")?; //! Ok(()) //! } //! ``` //! //! ## Pipe //! The `Pipe` type makes it more convenient to create a pipe and manage //! the lifetime of both the read and write ends of that pipe. //! //! ``` //! use filedescriptor::{Pipe, Error}; //! use std::io::{Read, Write}; //! //! let mut pipe = Pipe::new()?; //! pipe.write.write(b"hello")?; //! drop(pipe.write); //! //! let mut s = String::new(); //! pipe.read.read_to_string(&mut s)?; //! assert_eq!(s, "hello"); //! # Ok::<(), Error>(()) //! ``` //! //! ## Socketpair //! The `socketpair` function returns a pair of connected `SOCK_STREAM` //! sockets and functions both on posix and windows systems. //! //! ``` //! use std::io::{Read, Write}; //! use filedescriptor::Error; //! //! let (mut a, mut b) = filedescriptor::socketpair()?; //! a.write(b"hello")?; //! drop(a); //! //! let mut s = String::new(); //! b.read_to_string(&mut s)?; //! assert_eq!(s, "hello"); //! # Ok::<(), Error>(()) //! ``` //! //! ## Polling //! The `mio` crate offers powerful and scalable IO multiplexing, but there //! are some situations where `mio` doesn't fit. The `filedescriptor` crate //! offers a `poll(2)` compatible interface suitable for testing the readiness //! of a set of file descriptors. On unix systems this is a very thin wrapper //! around `poll(2)`, except on macOS where it is actually a wrapper around //! the `select(2)` interface. On Windows systems the winsock `WSAPoll` //! function is used instead. //! //! ``` //! use filedescriptor::*; //! use std::time::Duration; //! use std::io::{Read, Write}; //! //! let (mut a, mut b) = filedescriptor::socketpair()?; //! let mut poll_array = [pollfd { //! fd: a.as_socket_descriptor(), //! events: POLLIN, //! revents: 0 //! }]; //! // sleeps for 20 milliseconds because `a` is not yet ready //! assert_eq!(poll(&mut poll_array, Some(Duration::from_millis(20)))?, 0); //! //! b.write(b"hello")?; //! //! // Now a is ready for read //! assert_eq!(poll(&mut poll_array, Some(Duration::from_millis(20)))?, 1); //! //! # Ok::<(), Error>(()) //! ``` #[cfg(unix)] mod unix; #[cfg(unix)] pub use crate::unix::*; #[cfg(windows)] mod windows; #[cfg(windows)] pub use crate::windows::*; use thiserror::Error; #[derive(Error, Debug)] #[non_exhaustive] pub enum Error { #[error("failed to create a pipe")] Pipe(#[source] std::io::Error), #[error("failed to create a socketpair")] Socketpair(#[source] std::io::Error), #[error("failed to create a socket")] Socket(#[source] std::io::Error), #[error("failed to bind a socket")] Bind(#[source] std::io::Error), #[error("failed to fetch socket name")] Getsockname(#[source] std::io::Error), #[error("failed to set socket to listen mode")] Listen(#[source] std::io::Error), #[error("failed to connect socket")] Connect(#[source] std::io::Error), #[error("failed to accept socket")] Accept(#[source] std::io::Error), #[error("fcntl read failed")] Fcntl(#[source] std::io::Error), #[error("failed to set cloexec")] Cloexec(#[source] std::io::Error), #[error("failed to change non-blocking mode")] FionBio(#[source] std::io::Error), #[error("poll failed")] Poll(#[source] std::io::Error), #[error("dup of fd {fd} failed")] Dup { fd: i64, source: std::io::Error }, #[error("dup of fd {src_fd} to fd {dest_fd} failed")] Dup2 { src_fd: i64, dest_fd: i64, source: std::io::Error, }, #[error("Illegal fd value {0}")] IllegalFdValue(i64), #[error("fd value {0} too large to use with select(2)")] FdValueOutsideFdSetSize(i64), #[error("Only socket descriptors can change their non-blocking mode on Windows")] OnlySocketsNonBlocking, #[error("SetStdHandle failed")] SetStdHandle(#[source] std::io::Error), #[error("IoError")] Io(#[from] std::io::Error), } pub type Result = std::result::Result; /// `AsRawFileDescriptor` is a platform independent trait for returning /// a non-owning reference to the underlying platform file descriptor /// type. pub trait AsRawFileDescriptor { fn as_raw_file_descriptor(&self) -> RawFileDescriptor; } /// `IntoRawFileDescriptor` is a platform independent trait for converting /// an instance into the underlying platform file descriptor type. pub trait IntoRawFileDescriptor { fn into_raw_file_descriptor(self) -> RawFileDescriptor; } /// `FromRawFileDescriptor` is a platform independent trait for creating /// an instance from the underlying platform file descriptor type. /// Because the platform file descriptor type has no inherent ownership /// management, the `from_raw_file_descriptor` function is marked as unsafe /// to indicate that care must be taken by the caller to ensure that it /// is used appropriately. pub trait FromRawFileDescriptor { unsafe fn from_raw_file_descriptor(fd: RawFileDescriptor) -> Self; } pub trait AsRawSocketDescriptor { fn as_socket_descriptor(&self) -> SocketDescriptor; } pub trait IntoRawSocketDescriptor { fn into_socket_descriptor(self) -> SocketDescriptor; } pub trait FromRawSocketDescriptor { unsafe fn from_socket_descriptor(fd: SocketDescriptor) -> Self; } /// `OwnedHandle` allows managing the lifetime of the platform `RawFileDescriptor` /// type. It is exposed in the interface of this crate primarily for convenience /// on Windows where the system handle type is used for a variety of objects /// that don't support reading and writing. #[derive(Debug)] pub struct OwnedHandle { handle: RawFileDescriptor, handle_type: HandleType, } impl OwnedHandle { /// Create a new handle from some object that is convertible into /// the system `RawFileDescriptor` type. This consumes the parameter /// and replaces it with an `OwnedHandle` instance. pub fn new(f: F) -> Self { let handle = f.into_raw_file_descriptor(); Self { handle, handle_type: Self::probe_handle_type(handle), } } /// Attempt to duplicate the underlying handle and return an /// `OwnedHandle` wrapped around the duplicate. Since the duplication /// requires kernel resources that may not be available, this is a /// potentially fallible operation. /// The returned handle has a separate lifetime from the source, but /// references the same object at the kernel level. pub fn try_clone(&self) -> Result { Self::dup_impl(self, self.handle_type) } /// Attempt to duplicate the underlying handle from an object that is /// representable as the system `RawFileDescriptor` type and return an /// `OwnedHandle` wrapped around the duplicate. Since the duplication /// requires kernel resources that may not be available, this is a /// potentially fallible operation. /// The returned handle has a separate lifetime from the source, but /// references the same object at the kernel level. pub fn dup(f: &F) -> Result { Self::dup_impl(f, Default::default()) } } /// `FileDescriptor` is a thin wrapper on top of the `OwnedHandle` type that /// exposes the ability to Read and Write to the platform `RawFileDescriptor`. /// /// This is a bit of a contrived example, but demonstrates how to avoid /// the conditional code that would otherwise be required to deal with /// calling `as_raw_fd` and `as_raw_handle`: /// /// ``` /// use filedescriptor::{FileDescriptor, FromRawFileDescriptor, Result}; /// use std::io::Write; /// /// fn get_stdout() -> Result { /// let stdout = std::io::stdout(); /// let handle = stdout.lock(); /// FileDescriptor::dup(&handle) /// } /// /// fn print_something() -> Result<()> { /// get_stdout()?.write(b"hello")?; /// Ok(()) /// } /// ``` #[derive(Debug)] pub struct FileDescriptor { handle: OwnedHandle, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum StdioDescriptor { Stdin, Stdout, Stderr, } impl FileDescriptor { /// Create a new descriptor from some object that is convertible into /// the system `RawFileDescriptor` type. This consumes the parameter /// and replaces it with a `FileDescriptor` instance. pub fn new(f: F) -> Self { let handle = OwnedHandle::new(f); Self { handle } } /// Attempt to duplicate the underlying handle from an object that is /// representable as the system `RawFileDescriptor` type and return a /// `FileDescriptor` wrapped around the duplicate. Since the duplication /// requires kernel resources that may not be available, this is a /// potentially fallible operation. /// The returned handle has a separate lifetime from the source, but /// references the same object at the kernel level. pub fn dup(f: &F) -> Result { OwnedHandle::dup(f).map(|handle| Self { handle }) } /// Attempt to duplicate the underlying handle and return a /// `FileDescriptor` wrapped around the duplicate. Since the duplication /// requires kernel resources that may not be available, this is a /// potentially fallible operation. /// The returned handle has a separate lifetime from the source, but /// references the same object at the kernel level. pub fn try_clone(&self) -> Result { self.handle.try_clone().map(|handle| Self { handle }) } /// A convenience method for creating a `std::process::Stdio` object /// to be used for eg: redirecting the stdio streams of a child /// process. The `Stdio` is created using a duplicated handle so /// that the source handle remains alive. pub fn as_stdio(&self) -> Result { self.as_stdio_impl() } /// A convenience method for creating a `std::fs::File` object. /// The `File` is created using a duplicated handle so /// that the source handle remains alive. pub fn as_file(&self) -> Result { self.as_file_impl() } /// Attempt to change the non-blocking IO mode of the file descriptor. /// Not all kinds of file descriptor can be placed in non-blocking mode /// on all systems, and some file descriptors will claim to be in /// non-blocking mode but it will have no effect. /// File descriptors based on sockets are the most portable type /// that can be successfully made non-blocking. pub fn set_non_blocking(&mut self, non_blocking: bool) -> Result<()> { self.set_non_blocking_impl(non_blocking) } /// Attempt to redirect stdio to the underlying handle and return /// a `FileDescriptor` wrapped around the original stdio source. /// Since the redirection requires kernel resources that may not be /// available, this is a potentially fallible operation. /// Supports stdin, stdout, and stderr redirections. pub fn redirect_stdio(f: &F, stdio: StdioDescriptor) -> Result { Self::redirect_stdio_impl(f, stdio) } } /// Represents the readable and writable ends of a pair of descriptors /// connected via a kernel pipe. /// /// ``` /// use filedescriptor::{Pipe, Error}; /// use std::io::{Read,Write}; /// /// let mut pipe = Pipe::new()?; /// pipe.write.write(b"hello")?; /// drop(pipe.write); /// /// let mut s = String::new(); /// pipe.read.read_to_string(&mut s)?; /// assert_eq!(s, "hello"); /// # Ok::<(), Error>(()) /// ``` pub struct Pipe { /// The readable end of the pipe pub read: FileDescriptor, /// The writable end of the pipe pub write: FileDescriptor, } use std::time::Duration; /// Examines a set of FileDescriptors to see if some of them are ready for I/O, /// or if certain events have occurred on them. /// /// This uses the system native readiness checking mechanism, which on Windows /// means that it does NOT use IOCP and that this only works with sockets on /// Windows. If you need IOCP then the `mio` crate is recommended for a much /// more scalable solution. /// /// On macOS, the `poll(2)` implementation has problems when used with eg: pty /// descriptors, so this implementation of poll uses the `select(2)` interface /// under the covers. That places a limit on the maximum file descriptor value /// that can be passed to poll. If a file descriptor is out of range then an /// error will returned. This limitation could potentially be lifted in the /// future. /// /// On Windows, `WSAPoll` is used to implement readiness checking, which has /// the consequence that it can only be used with sockets. /// /// If `duration` is `None`, then `poll` will block until any of the requested /// events are ready. Otherwise, `duration` specifies how long to wait for /// readiness before giving up. /// /// The return value is the number of entries that were satisfied; `0` means /// that none were ready after waiting for the specified duration. /// /// The `pfd` array is mutated and the `revents` field is updated to indicate /// which of the events were received. pub fn poll(pfd: &mut [pollfd], duration: Option) -> Result { poll_impl(pfd, duration) } /// Create a pair of connected sockets /// /// This implementation creates a pair of SOCK_STREAM sockets. pub fn socketpair() -> Result<(FileDescriptor, FileDescriptor)> { socketpair_impl() } filedescriptor-0.8.3/src/unix.rs000064400000000000000000000417631046102023000150050ustar 00000000000000use crate::{ AsRawFileDescriptor, AsRawSocketDescriptor, Error, FileDescriptor, FromRawFileDescriptor, FromRawSocketDescriptor, IntoRawFileDescriptor, IntoRawSocketDescriptor, OwnedHandle, Pipe, Result, StdioDescriptor, }; use std::os::unix::prelude::*; pub(crate) type HandleType = (); /// `RawFileDescriptor` is a platform independent type alias for the /// underlying platform file descriptor type. It is primarily useful /// for avoiding using `cfg` blocks in platform independent code. pub type RawFileDescriptor = RawFd; /// `SocketDescriptor` is a platform independent type alias for the /// underlying platform socket descriptor type. It is primarily useful /// for avoiding using `cfg` blocks in platform independent code. pub type SocketDescriptor = RawFd; impl AsRawFileDescriptor for T { fn as_raw_file_descriptor(&self) -> RawFileDescriptor { self.as_raw_fd() } } impl IntoRawFileDescriptor for T { fn into_raw_file_descriptor(self) -> RawFileDescriptor { self.into_raw_fd() } } impl FromRawFileDescriptor for T { unsafe fn from_raw_file_descriptor(fd: RawFileDescriptor) -> Self { Self::from_raw_fd(fd) } } impl AsRawSocketDescriptor for T { fn as_socket_descriptor(&self) -> SocketDescriptor { self.as_raw_fd() } } impl IntoRawSocketDescriptor for T { fn into_socket_descriptor(self) -> SocketDescriptor { self.into_raw_fd() } } impl FromRawSocketDescriptor for T { unsafe fn from_socket_descriptor(fd: SocketDescriptor) -> Self { Self::from_raw_fd(fd) } } impl Drop for OwnedHandle { fn drop(&mut self) { unsafe { libc::close(self.handle); } } } impl std::os::fd::AsFd for OwnedHandle { fn as_fd(&self) -> std::os::fd::BorrowedFd { unsafe { std::os::fd::BorrowedFd::borrow_raw(self.handle) } } } impl AsRawFd for OwnedHandle { fn as_raw_fd(&self) -> RawFd { self.handle } } impl IntoRawFd for OwnedHandle { fn into_raw_fd(self) -> RawFd { let fd = self.handle; std::mem::forget(self); fd } } impl FromRawFd for OwnedHandle { unsafe fn from_raw_fd(fd: RawFd) -> Self { Self { handle: fd, handle_type: (), } } } impl OwnedHandle { /// Helper function to set the close-on-exec flag for a raw descriptor fn cloexec(&mut self) -> Result<()> { let flags = unsafe { libc::fcntl(self.handle, libc::F_GETFD) }; if flags == -1 { return Err(Error::Fcntl(std::io::Error::last_os_error())); } let result = unsafe { libc::fcntl(self.handle, libc::F_SETFD, flags | libc::FD_CLOEXEC) }; if result == -1 { Err(Error::Cloexec(std::io::Error::last_os_error())) } else { Ok(()) } } fn non_atomic_dup(fd: RawFd) -> Result { let duped = unsafe { libc::dup(fd) }; if duped == -1 { Err(Error::Dup { fd: fd.into(), source: std::io::Error::last_os_error(), }) } else { let mut owned = OwnedHandle { handle: duped, handle_type: (), }; owned.cloexec()?; Ok(owned) } } fn non_atomic_dup2(fd: RawFd, dest_fd: RawFd) -> Result { let duped = unsafe { libc::dup2(fd, dest_fd) }; if duped == -1 { Err(Error::Dup2 { src_fd: fd.into(), dest_fd: dest_fd.into(), source: std::io::Error::last_os_error(), }) } else { let mut owned = OwnedHandle { handle: duped, handle_type: (), }; owned.cloexec()?; Ok(owned) } } #[inline] pub(crate) fn dup_impl( fd: &F, handle_type: HandleType, ) -> Result { let fd = fd.as_raw_file_descriptor(); let duped = unsafe { libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, 0) }; if duped == -1 { let err = std::io::Error::last_os_error(); if let Some(libc::EINVAL) = err.raw_os_error() { // We may be running on eg: WSL or an old kernel that // doesn't support F_DUPFD_CLOEXEC; fall back. Self::non_atomic_dup(fd) } else { Err(Error::Dup { fd: fd.into(), source: err, }) } } else { Ok(OwnedHandle { handle: duped, handle_type, }) } } #[inline] pub(crate) unsafe fn dup2_impl(fd: &F, dest_fd: RawFd) -> Result { let fd = fd.as_raw_file_descriptor(); #[cfg(not(target_os = "linux"))] return Self::non_atomic_dup2(fd, dest_fd); #[cfg(target_os = "linux")] { let duped = libc::dup3(fd, dest_fd, libc::O_CLOEXEC); if duped == -1 { let err = std::io::Error::last_os_error(); if let Some(libc::EINVAL) = err.raw_os_error() { // We may be running on eg: WSL or an old kernel that // doesn't support O_CLOEXEC; fall back. Self::non_atomic_dup2(fd, dest_fd) } else { Err(Error::Dup2 { src_fd: fd.into(), dest_fd: dest_fd.into(), source: err, }) } } else { Ok(OwnedHandle { handle: duped, handle_type: (), }) } } } pub(crate) fn probe_handle_type(_handle: RawFileDescriptor) -> HandleType { () } } impl std::io::Read for FileDescriptor { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let size = unsafe { libc::read(self.handle.handle, buf.as_mut_ptr() as *mut _, buf.len()) }; if size == -1 { Err(std::io::Error::last_os_error()) } else { Ok(size as usize) } } } impl std::io::Write for FileDescriptor { fn write(&mut self, buf: &[u8]) -> std::io::Result { let size = unsafe { libc::write(self.handle.handle, buf.as_ptr() as *const _, buf.len()) }; if size == -1 { Err(std::io::Error::last_os_error()) } else { Ok(size as usize) } } fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } impl std::os::fd::AsFd for FileDescriptor { fn as_fd(&self) -> std::os::fd::BorrowedFd { self.handle.as_fd() } } impl AsRawFd for FileDescriptor { fn as_raw_fd(&self) -> RawFd { self.handle.as_raw_fd() } } impl IntoRawFd for FileDescriptor { fn into_raw_fd(self) -> RawFd { self.handle.into_raw_fd() } } impl FromRawFd for FileDescriptor { unsafe fn from_raw_fd(fd: RawFd) -> Self { Self { handle: OwnedHandle::from_raw_fd(fd), } } } impl FileDescriptor { #[inline] pub(crate) fn as_stdio_impl(&self) -> Result { let duped = OwnedHandle::dup(self)?; let fd = duped.into_raw_fd(); let stdio = unsafe { std::process::Stdio::from_raw_fd(fd) }; Ok(stdio) } #[inline] pub(crate) fn as_file_impl(&self) -> Result { let duped = OwnedHandle::dup(self)?; let fd = duped.into_raw_fd(); let stdio = unsafe { std::fs::File::from_raw_fd(fd) }; Ok(stdio) } #[inline] pub(crate) fn set_non_blocking_impl(&mut self, non_blocking: bool) -> Result<()> { let on = if non_blocking { 1 } else { 0 }; let res = unsafe { libc::ioctl(self.handle.as_raw_file_descriptor(), libc::FIONBIO, &on) }; if res != 0 { Err(Error::FionBio(std::io::Error::last_os_error())) } else { Ok(()) } } /// Attempt to duplicate the underlying handle from an object that is /// representable as the system `RawFileDescriptor` type and assign it to /// a destination file descriptor. It then returns a `FileDescriptor` /// wrapped around the duplicate. Since the duplication requires kernel /// resources that may not be available, this is a potentially fallible operation. /// The returned handle has a separate lifetime from the source, but /// references the same object at the kernel level. pub unsafe fn dup2(f: &F, dest_fd: RawFd) -> Result { OwnedHandle::dup2_impl(f, dest_fd).map(|handle| Self { handle }) } /// Helper function to unset the close-on-exec flag for a raw descriptor fn no_cloexec(fd: RawFd) -> Result<()> { let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) }; if flags == -1 { return Err(Error::Fcntl(std::io::Error::last_os_error())); } let result = unsafe { libc::fcntl(fd, libc::F_SETFD, flags & !libc::FD_CLOEXEC) }; if result == -1 { Err(Error::Cloexec(std::io::Error::last_os_error())) } else { Ok(()) } } pub(crate) fn redirect_stdio_impl( f: &F, stdio: StdioDescriptor, ) -> Result { let std_descriptor = match stdio { StdioDescriptor::Stdin => libc::STDIN_FILENO, StdioDescriptor::Stdout => libc::STDOUT_FILENO, StdioDescriptor::Stderr => libc::STDERR_FILENO, }; let std_original = FileDescriptor::dup(&std_descriptor)?; // Assign f into std_descriptor, then convert to an fd so that // we don't close it when the returned FileDescriptor is dropped. // Then we discard/ignore the fd because it is nominally owned by // the stdio machinery for the process let _ = unsafe { FileDescriptor::dup2(f, std_descriptor) }?.into_raw_fd(); Self::no_cloexec(std_descriptor)?; Ok(std_original) } } impl Pipe { #[cfg(target_os = "linux")] pub fn new() -> Result { let mut fds = [-1i32; 2]; let res = unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) }; if res == -1 { Err(Error::Pipe(std::io::Error::last_os_error())) } else { let read = FileDescriptor { handle: OwnedHandle { handle: fds[0], handle_type: (), }, }; let write = FileDescriptor { handle: OwnedHandle { handle: fds[1], handle_type: (), }, }; Ok(Pipe { read, write }) } } #[cfg(not(target_os = "linux"))] pub fn new() -> Result { let mut fds = [-1i32; 2]; let res = unsafe { libc::pipe(fds.as_mut_ptr()) }; if res == -1 { Err(Error::Pipe(std::io::Error::last_os_error())) } else { let mut read = FileDescriptor { handle: OwnedHandle { handle: fds[0], handle_type: (), }, }; let mut write = FileDescriptor { handle: OwnedHandle { handle: fds[1], handle_type: (), }, }; read.handle.cloexec()?; write.handle.cloexec()?; Ok(Pipe { read, write }) } } } #[cfg(target_os = "linux")] #[doc(hidden)] pub fn socketpair_impl() -> Result<(FileDescriptor, FileDescriptor)> { let mut fds = [-1i32; 2]; let res = unsafe { libc::socketpair( libc::PF_LOCAL, libc::SOCK_STREAM | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr(), ) }; if res == -1 { Err(Error::Socketpair(std::io::Error::last_os_error())) } else { let read = FileDescriptor { handle: OwnedHandle { handle: fds[0], handle_type: (), }, }; let write = FileDescriptor { handle: OwnedHandle { handle: fds[1], handle_type: (), }, }; Ok((read, write)) } } #[cfg(not(target_os = "linux"))] #[doc(hidden)] pub fn socketpair_impl() -> Result<(FileDescriptor, FileDescriptor)> { let mut fds = [-1i32; 2]; let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; if res == -1 { Err(Error::Socketpair(std::io::Error::last_os_error())) } else { let mut read = FileDescriptor { handle: OwnedHandle { handle: fds[0], handle_type: (), }, }; let mut write = FileDescriptor { handle: OwnedHandle { handle: fds[1], handle_type: (), }, }; read.handle.cloexec()?; write.handle.cloexec()?; Ok((read, write)) } } pub use libc::{pollfd, POLLERR, POLLHUP, POLLIN, POLLOUT}; use std::time::Duration; #[cfg(not(target_os = "macos"))] #[doc(hidden)] pub fn poll_impl(pfd: &mut [pollfd], duration: Option) -> Result { let poll_result = unsafe { libc::poll( pfd.as_mut_ptr(), pfd.len() as _, duration .map(|wait| wait.as_millis() as libc::c_int) .unwrap_or(-1), ) }; if poll_result < 0 { Err(Error::Poll(std::io::Error::last_os_error())) } else { Ok(poll_result as usize) } } // macOS has a broken poll(2) implementation, so we introduce a layer to deal with that here #[cfg(target_os = "macos")] mod macos { use super::*; use libc::{fd_set, timeval, FD_ISSET, FD_SET, FD_SETSIZE, FD_ZERO, POLLERR, POLLIN, POLLOUT}; use std::os::unix::io::RawFd; struct FdSet { set: fd_set, } #[inline] fn check_fd(fd: RawFd) -> Result<()> { if fd < 0 { return Err(Error::IllegalFdValue(fd.into())); } if fd as usize >= FD_SETSIZE { return Err(Error::FdValueOutsideFdSetSize(fd.into())); } Ok(()) } impl FdSet { pub fn new() -> Self { unsafe { let mut set = std::mem::MaybeUninit::uninit(); FD_ZERO(set.as_mut_ptr()); Self { set: set.assume_init(), } } } pub fn add(&mut self, fd: RawFd) -> Result<()> { check_fd(fd)?; unsafe { FD_SET(fd, &mut self.set); } Ok(()) } pub fn contains(&mut self, fd: RawFd) -> bool { check_fd(fd).unwrap(); unsafe { FD_ISSET(fd, &mut self.set) } } } fn materialize(set: &mut Option) -> &mut FdSet { set.get_or_insert_with(FdSet::new) } fn set_ptr(set: &mut Option) -> *mut fd_set { set.as_mut() .map(|s| &mut s.set as *mut _) .unwrap_or_else(std::ptr::null_mut) } fn is_set(set: &mut Option, fd: RawFd) -> bool { set.as_mut().map(|s| s.contains(fd)).unwrap_or(false) } pub fn poll_impl(pfd: &mut [pollfd], duration: Option) -> Result { let mut read_set = None; let mut write_set = None; let mut exception_set = None; let mut nfds = 0; for item in pfd.iter_mut() { item.revents = 0; nfds = nfds.max(item.fd); if item.events & POLLIN != 0 { materialize(&mut read_set).add(item.fd)?; } if item.events & POLLOUT != 0 { materialize(&mut write_set).add(item.fd)?; } materialize(&mut exception_set).add(item.fd)?; } let mut timeout = duration.map(|d| timeval { tv_sec: d.as_secs() as _, tv_usec: d.subsec_micros() as _, }); let res = unsafe { libc::select( nfds + 1, set_ptr(&mut read_set), set_ptr(&mut write_set), set_ptr(&mut exception_set), timeout .as_mut() .map(|t| t as *mut _) .unwrap_or_else(std::ptr::null_mut), ) }; if res < 0 { Err(std::io::Error::last_os_error().into()) } else { for item in pfd.iter_mut() { if is_set(&mut read_set, item.fd) { item.revents |= POLLIN; } if is_set(&mut write_set, item.fd) { item.revents |= POLLOUT; } if is_set(&mut exception_set, item.fd) { item.revents |= POLLERR; } } Ok(res as usize) } } } #[cfg(target_os = "macos")] #[doc(hidden)] pub use macos::poll_impl; filedescriptor-0.8.3/src/windows.rs000064400000000000000000000421221046102023000155020ustar 00000000000000use crate::{ AsRawFileDescriptor, AsRawSocketDescriptor, Error, FileDescriptor, FromRawFileDescriptor, FromRawSocketDescriptor, IntoRawFileDescriptor, IntoRawSocketDescriptor, OwnedHandle, Pipe, Result, StdioDescriptor, }; use std::io::{self, Error as IoError}; use std::os::windows::prelude::*; use std::ptr; use std::sync::Once; use std::time::Duration; use winapi::shared::ws2def::{AF_INET, INADDR_LOOPBACK, SOCKADDR_IN}; use winapi::um::fileapi::*; use winapi::um::handleapi::*; use winapi::um::minwinbase::SECURITY_ATTRIBUTES; use winapi::um::namedpipeapi::{CreatePipe, GetNamedPipeInfo}; use winapi::um::processenv::{GetStdHandle, SetStdHandle}; use winapi::um::processthreadsapi::*; use winapi::um::winbase::{FILE_TYPE_CHAR, FILE_TYPE_DISK, FILE_TYPE_PIPE}; use winapi::um::winnt::HANDLE; use winapi::um::winsock2::{ accept, bind, closesocket, connect, getsockname, getsockopt, htonl, ioctlsocket, listen, recv, send, WSAGetLastError, WSAPoll, WSASocketW, WSAStartup, INVALID_SOCKET, SOCKET, SOCK_STREAM, SOL_SOCKET, SO_ERROR, WSADATA, WSAENOTSOCK, WSA_FLAG_NO_HANDLE_INHERIT, }; pub use winapi::um::winsock2::{POLLERR, POLLHUP, POLLIN, POLLOUT, WSAPOLLFD as pollfd}; /// `RawFileDescriptor` is a platform independent type alias for the /// underlying platform file descriptor type. It is primarily useful /// for avoiding using `cfg` blocks in platform independent code. pub type RawFileDescriptor = RawHandle; /// `SocketDescriptor` is a platform independent type alias for the /// underlying platform socket descriptor type. It is primarily useful /// for avoiding using `cfg` blocks in platform independent code. pub type SocketDescriptor = SOCKET; const STD_INPUT_HANDLE: u32 = 4294967286; // -10 const STD_OUTPUT_HANDLE: u32 = 4294967285; // -11 const STD_ERROR_HANDLE: u32 = 4294967284; // -12 #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub(crate) enum HandleType { Char, Disk, Pipe, Socket, Unknown, } impl Default for HandleType { fn default() -> Self { HandleType::Unknown } } impl AsRawFileDescriptor for T { fn as_raw_file_descriptor(&self) -> RawFileDescriptor { self.as_raw_handle() } } impl IntoRawFileDescriptor for T { fn into_raw_file_descriptor(self) -> RawFileDescriptor { self.into_raw_handle() } } impl FromRawFileDescriptor for T { unsafe fn from_raw_file_descriptor(handle: RawHandle) -> Self { Self::from_raw_handle(handle) } } impl AsRawSocketDescriptor for T { fn as_socket_descriptor(&self) -> SocketDescriptor { self.as_raw_socket() as SocketDescriptor } } impl IntoRawSocketDescriptor for T { fn into_socket_descriptor(self) -> SocketDescriptor { self.into_raw_socket() as SocketDescriptor } } impl FromRawSocketDescriptor for T { unsafe fn from_socket_descriptor(handle: SocketDescriptor) -> Self { Self::from_raw_socket(handle as _) } } unsafe impl Send for OwnedHandle {} unsafe impl Sync for OwnedHandle {} impl OwnedHandle { fn probe_handle_type_if_unknown(handle: RawHandle, handle_type: HandleType) -> HandleType { match handle_type { HandleType::Unknown => Self::probe_handle_type(handle), t => t, } } pub(crate) fn probe_handle_type(handle: RawHandle) -> HandleType { let handle = handle as HANDLE; match unsafe { GetFileType(handle) } { FILE_TYPE_CHAR => HandleType::Char, FILE_TYPE_DISK => HandleType::Disk, FILE_TYPE_PIPE => { // Could be a pipe or a socket. Test if for pipeness let mut flags = 0; let mut out_buf = 0; let mut in_buf = 0; let mut inst = 0; if unsafe { GetNamedPipeInfo(handle, &mut flags, &mut out_buf, &mut in_buf, &mut inst) } != 0 { HandleType::Pipe } else { // It's probably a socket, but it may be a special device used // when piping between WSL and native win32 apps. let mut err = 0; let mut errsize = std::mem::size_of_val(&err) as _; if unsafe { getsockopt( handle as _, SOL_SOCKET, SO_ERROR, &mut err as *mut _ as *mut i8, &mut errsize, ) != 0 && WSAGetLastError() == WSAENOTSOCK } { HandleType::Pipe } else { HandleType::Socket } } } _ => HandleType::Unknown, } } fn is_socket_handle(&self) -> bool { match self.handle_type { HandleType::Socket => true, HandleType::Unknown => Self::probe_handle_type(self.handle) == HandleType::Socket, _ => false, } } } impl Drop for OwnedHandle { fn drop(&mut self) { if self.handle != INVALID_HANDLE_VALUE as _ && !self.handle.is_null() { unsafe { if self.is_socket_handle() { closesocket(self.handle as _); } else { CloseHandle(self.handle as _); } }; } } } impl FromRawHandle for OwnedHandle { unsafe fn from_raw_handle(handle: RawHandle) -> Self { OwnedHandle { handle, handle_type: Self::probe_handle_type(handle), } } } impl OwnedHandle { #[inline] pub(crate) fn dup_impl(f: &F, handle_type: HandleType) -> Result { let handle = f.as_raw_file_descriptor(); if handle == INVALID_HANDLE_VALUE as _ || handle.is_null() { return Ok(OwnedHandle { handle, handle_type, }); } let handle_type = Self::probe_handle_type_if_unknown(handle, handle_type); let proc = unsafe { GetCurrentProcess() }; let mut duped = INVALID_HANDLE_VALUE; let ok = unsafe { DuplicateHandle( proc, handle as *mut _, proc, &mut duped, 0, 0, // not inheritable winapi::um::winnt::DUPLICATE_SAME_ACCESS, ) }; if ok == 0 { Err(IoError::last_os_error().into()) } else { Ok(OwnedHandle { handle: duped as *mut _, handle_type, }) } } } impl AsRawHandle for OwnedHandle { fn as_raw_handle(&self) -> RawHandle { self.handle } } impl IntoRawHandle for OwnedHandle { fn into_raw_handle(self) -> RawHandle { let handle = self.handle; std::mem::forget(self); handle } } impl FileDescriptor { #[inline] pub(crate) fn as_stdio_impl(&self) -> Result { let duped = self.handle.try_clone()?; let handle = duped.into_raw_handle(); let stdio = unsafe { std::process::Stdio::from_raw_handle(handle) }; Ok(stdio) } #[inline] pub(crate) fn as_file_impl(&self) -> Result { let duped = self.handle.try_clone()?; let handle = duped.into_raw_handle(); let stdio = unsafe { std::fs::File::from_raw_handle(handle) }; Ok(stdio) } #[inline] pub(crate) fn set_non_blocking_impl(&mut self, non_blocking: bool) -> Result<()> { if !self.handle.is_socket_handle() { return Err(Error::OnlySocketsNonBlocking); } let mut on = if non_blocking { 1 } else { 0 }; let res = unsafe { ioctlsocket( self.as_raw_socket() as SOCKET, winapi::um::winsock2::FIONBIO, &mut on, ) }; if res != 0 { Err(Error::FionBio(std::io::Error::last_os_error())) } else { Ok(()) } } pub(crate) fn redirect_stdio_impl( f: &F, stdio: StdioDescriptor, ) -> Result { let std_handle = match stdio { StdioDescriptor::Stdin => STD_INPUT_HANDLE, StdioDescriptor::Stdout => STD_OUTPUT_HANDLE, StdioDescriptor::Stderr => STD_ERROR_HANDLE, }; let raw_std_handle = unsafe { GetStdHandle(std_handle) } as *mut _; let std_original = unsafe { FileDescriptor::from_raw_handle(raw_std_handle) }; let cloned_handle = OwnedHandle::dup(f)?; if unsafe { SetStdHandle(std_handle, cloned_handle.into_raw_handle() as *mut _) } == 0 { Err(Error::SetStdHandle(std::io::Error::last_os_error())) } else { Ok(std_original) } } } impl IntoRawHandle for FileDescriptor { fn into_raw_handle(self) -> RawHandle { self.handle.into_raw_handle() } } impl AsRawHandle for FileDescriptor { fn as_raw_handle(&self) -> RawHandle { self.handle.as_raw_handle() } } impl FromRawHandle for FileDescriptor { unsafe fn from_raw_handle(handle: RawHandle) -> FileDescriptor { Self { handle: OwnedHandle::from_raw_handle(handle), } } } impl IntoRawSocket for FileDescriptor { fn into_raw_socket(self) -> RawSocket { // FIXME: this isn't a guaranteed conversion! debug_assert!(self.handle.is_socket_handle()); self.handle.into_raw_handle() as RawSocket } } impl AsRawSocket for FileDescriptor { fn as_raw_socket(&self) -> RawSocket { // FIXME: this isn't a guaranteed conversion! debug_assert!(self.handle.is_socket_handle()); self.handle.as_raw_handle() as RawSocket } } impl AsSocket for FileDescriptor { fn as_socket(&self) -> BorrowedSocket { unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } } } impl FromRawSocket for FileDescriptor { unsafe fn from_raw_socket(handle: RawSocket) -> FileDescriptor { Self { handle: OwnedHandle::from_raw_handle(handle as RawHandle), } } } impl io::Read for FileDescriptor { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { if self.handle.is_socket_handle() { // It's important to use the winsock functions to read/write // even though ReadFile and WriteFile technically work; only // the winsock functions respect non-blocking mode. let num_read = unsafe { recv( self.as_socket_descriptor(), buf.as_mut_ptr() as *mut _, buf.len() as _, 0, ) }; if num_read < 0 { Err(IoError::last_os_error()) } else { Ok(num_read as usize) } } else { let mut num_read = 0; let ok = unsafe { ReadFile( self.handle.as_raw_handle() as *mut _, buf.as_mut_ptr() as *mut _, buf.len() as _, &mut num_read, ptr::null_mut(), ) }; if ok == 0 { let err = IoError::last_os_error(); if err.kind() == std::io::ErrorKind::BrokenPipe { Ok(0) } else { Err(err) } } else { Ok(num_read as usize) } } } } impl io::Write for FileDescriptor { fn write(&mut self, buf: &[u8]) -> std::io::Result { if self.handle.is_socket_handle() { let num_wrote = unsafe { send( self.as_socket_descriptor(), buf.as_ptr() as *const _, buf.len() as _, 0, ) }; if num_wrote < 0 { Err(IoError::last_os_error()) } else { Ok(num_wrote as usize) } } else { let mut num_wrote = 0; let ok = unsafe { WriteFile( self.handle.as_raw_handle() as *mut _, buf.as_ptr() as *const _, buf.len() as u32, &mut num_wrote, ptr::null_mut(), ) }; if ok == 0 { Err(IoError::last_os_error()) } else { Ok(num_wrote as usize) } } } fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } impl Pipe { pub fn new() -> Result { let mut sa = SECURITY_ATTRIBUTES { nLength: std::mem::size_of::() as u32, lpSecurityDescriptor: ptr::null_mut(), bInheritHandle: 0, }; let mut read: HANDLE = INVALID_HANDLE_VALUE as _; let mut write: HANDLE = INVALID_HANDLE_VALUE as _; if unsafe { CreatePipe(&mut read, &mut write, &mut sa, 0) } == 0 { Err(Error::Pipe(IoError::last_os_error())) } else { Ok(Pipe { read: FileDescriptor { handle: OwnedHandle { handle: read as _, handle_type: HandleType::Pipe, }, }, write: FileDescriptor { handle: OwnedHandle { handle: write as _, handle_type: HandleType::Pipe, }, }, }) } } } fn init_winsock() { static START: Once = Once::new(); START.call_once(|| unsafe { let mut data: WSADATA = std::mem::zeroed(); let ret = WSAStartup( 0x202, // version 2.2 &mut data, ); assert_eq!(ret, 0, "failed to initialize winsock"); }); } fn socket(af: i32, sock_type: i32, proto: i32) -> Result { let s = unsafe { WSASocketW( af, sock_type, proto, ptr::null_mut(), 0, WSA_FLAG_NO_HANDLE_INHERIT, ) }; if s == INVALID_SOCKET { Err(Error::Socket(IoError::last_os_error())) } else { Ok(FileDescriptor { handle: OwnedHandle { handle: s as _, handle_type: HandleType::Socket, }, }) } } #[doc(hidden)] pub fn socketpair_impl() -> Result<(FileDescriptor, FileDescriptor)> { init_winsock(); let s = socket(AF_INET, SOCK_STREAM, 0)?; let mut in_addr: SOCKADDR_IN = unsafe { std::mem::zeroed() }; in_addr.sin_family = AF_INET as _; unsafe { *in_addr.sin_addr.S_un.S_addr_mut() = htonl(INADDR_LOOPBACK); } unsafe { if bind( s.as_raw_handle() as _, std::mem::transmute(&in_addr), std::mem::size_of_val(&in_addr) as _, ) != 0 { return Err(Error::Bind(IoError::last_os_error())); } } let mut addr_len = std::mem::size_of_val(&in_addr) as i32; unsafe { if getsockname( s.as_raw_handle() as _, std::mem::transmute(&mut in_addr), &mut addr_len, ) != 0 { return Err(Error::Getsockname(IoError::last_os_error())); } } unsafe { if listen(s.as_raw_handle() as _, 1) != 0 { return Err(Error::Listen(IoError::last_os_error())); } } let client = socket(AF_INET, SOCK_STREAM, 0)?; unsafe { if connect( client.as_raw_handle() as _, std::mem::transmute(&in_addr), addr_len, ) != 0 { return Err(Error::Connect(IoError::last_os_error())); } } let server = unsafe { accept(s.as_raw_handle() as _, ptr::null_mut(), ptr::null_mut()) }; if server == INVALID_SOCKET { return Err(Error::Accept(IoError::last_os_error())); } let server = FileDescriptor { handle: OwnedHandle { handle: server as _, handle_type: HandleType::Socket, }, }; Ok((server, client)) } #[doc(hidden)] pub fn poll_impl(pfd: &mut [pollfd], duration: Option) -> Result { let poll_result = unsafe { WSAPoll( pfd.as_mut_ptr(), pfd.len() as _, duration .map(|wait| wait.as_millis() as libc::c_int) .unwrap_or(-1), ) }; if poll_result < 0 { Err(std::io::Error::last_os_error().into()) } else { Ok(poll_result as usize) } } #[cfg(test)] mod test { use std::io::{Read, Write}; #[test] fn socketpair() { let (mut a, mut b) = super::socketpair_impl().unwrap(); a.write_all(b"hello").unwrap(); let mut buf = [0u8; 5]; assert_eq!(b.read(&mut buf).unwrap(), 5); assert_eq!(&buf, b"hello"); } }