filedescriptor-0.8.2/.cargo_vcs_info.json0000644000000001540000000000100140610ustar { "git": { "sha1": "668b370541d9caa7062451866d837c74f56ff3d9" }, "path_in_vcs": "filedescriptor" }filedescriptor-0.8.2/Cargo.toml0000644000000020740000000000100120620ustar # 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.2" authors = ["Wez Furlong"] 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/wez/wezterm" resolver = "2" [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.2/Cargo.toml.orig000064400000000000000000000011400072674642500155640ustar 00000000000000[package] name = "filedescriptor" version = "0.8.2" authors = ["Wez Furlong"] edition = "2018" repository = "https://github.com/wez/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.2/README.md000064400000000000000000000047770072674642500141770ustar 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.2/src/lib.rs000064400000000000000000000333110072674642500146050ustar 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() } /// 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.2/src/unix.rs000064400000000000000000000402760072674642500150320ustar 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 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 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 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)?; 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.2/src/windows.rs000064400000000000000000000412530072674642500155350ustar 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 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 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"); } }