From 4c4a93a480e332c9e223fea9458320b5392934e0 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Sun, 30 Jun 2024 18:23:07 +1000 Subject: [PATCH] Initial implementation of anonymous_pipe Co-authored-by: Alphyr <47725341+a1phyr@users.noreply.github.com> Co-authored-by: Jubilee <46493976+workingjubilee@users.noreply.github.com> Signed-off-by: Jiahao XU --- std/Cargo.toml | 56 ++++++++-- std/src/lib.rs | 2 + std/src/pipe.rs | 130 ++++++++++++++++++++++ std/src/sys/anonymous_pipe/mod.rs | 18 +++ std/src/sys/anonymous_pipe/tests.rs | 20 ++++ std/src/sys/anonymous_pipe/unix.rs | 103 +++++++++++++++++ std/src/sys/anonymous_pipe/unsupported.rs | 26 +++++ std/src/sys/anonymous_pipe/windows.rs | 109 ++++++++++++++++++ std/src/sys/mod.rs | 2 + std/src/sys/pal/unix/fd.rs | 5 + std/src/sys/pal/unix/pipe.rs | 9 ++ std/src/sys/pal/unsupported/pipe.rs | 15 ++- std/src/sys/pal/windows/c/bindings.txt | 1 + std/src/sys/pal/windows/c/windows_sys.rs | 1 + std/src/sys/pal/windows/handle.rs | 7 ++ std/src/sys/pal/windows/pipe.rs | 24 +++- std/tests/pipe_subprocess.rs | 39 +++++++ 17 files changed, 551 insertions(+), 16 deletions(-) create mode 100644 std/src/pipe.rs create mode 100644 std/src/sys/anonymous_pipe/mod.rs create mode 100644 std/src/sys/anonymous_pipe/tests.rs create mode 100644 std/src/sys/anonymous_pipe/unix.rs create mode 100644 std/src/sys/anonymous_pipe/unsupported.rs create mode 100644 std/src/sys/anonymous_pipe/windows.rs create mode 100644 std/tests/pipe_subprocess.rs diff --git a/std/Cargo.toml b/std/Cargo.toml index b991b1cf22dd8..321d73b76a138 100644 --- a/std/Cargo.toml +++ b/std/Cargo.toml @@ -20,8 +20,12 @@ core = { path = "../core", public = true } compiler_builtins = { version = "0.1.105" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } -hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep-of-std'] } -std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] } +hashbrown = { version = "0.14", default-features = false, features = [ + 'rustc-dep-of-std', +] } +std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = [ + 'rustc-dep-of-std', +] } # Dependencies of the `backtrace` crate rustc-demangle = { version = "0.1.24", features = ['rustc-dep-of-std'] } @@ -31,13 +35,27 @@ miniz_oxide = { version = "0.7.0", optional = true, default-features = false } addr2line = { version = "0.22.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true } +libc = { version = "0.2.153", default-features = false, features = [ + 'rustc-dep-of-std', +], public = true } [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies] -object = { version = "0.36.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } +object = { version = "0.36.0", default-features = false, optional = true, features = [ + 'read_core', + 'elf', + 'macho', + 'pe', + 'unaligned', + 'archive', +] } [target.'cfg(target_os = "aix")'.dependencies] -object = { version = "0.36.0", default-features = false, optional = true, features = ['read_core', 'xcoff', 'unaligned', 'archive'] } +object = { version = "0.36.0", default-features = false, optional = true, features = [ + 'read_core', + 'xcoff', + 'unaligned', + 'archive', +] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } @@ -47,13 +65,19 @@ rand_xorshift = "0.3.0" dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] } [target.x86_64-fortanix-unknown-sgx.dependencies] -fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public = true } +fortanix-sgx-abi = { version = "0.5.0", features = [ + 'rustc-dep-of-std', +], public = true } [target.'cfg(target_os = "hermit")'.dependencies] -hermit-abi = { version = "0.4.0", features = ['rustc-dep-of-std'], public = true } +hermit-abi = { version = "0.4.0", features = [ + 'rustc-dep-of-std', +], public = true } [target.'cfg(target_os = "wasi")'.dependencies] -wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } +wasi = { version = "0.11.0", features = [ + 'rustc-dep-of-std', +], default-features = false } [target.'cfg(target_os = "uefi")'.dependencies] r-efi = { version = "4.2.0", features = ['rustc-dep-of-std'] } @@ -61,9 +85,9 @@ r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std'] } [features] backtrace = [ - 'addr2line/rustc-dep-of-std', - 'object/rustc-dep-of-std', - 'miniz_oxide/rustc-dep-of-std', + 'addr2line/rustc-dep-of-std', + 'object/rustc-dep-of-std', + 'miniz_oxide/rustc-dep-of-std', ] panic-unwind = ["panic_unwind"] @@ -77,7 +101,10 @@ llvm-libunwind = ["unwind/llvm-libunwind"] system-llvm-libunwind = ["unwind/system-llvm-libunwind"] # Make panics and failed asserts immediately abort without formatting any message -panic_immediate_abort = ["core/panic_immediate_abort", "alloc/panic_immediate_abort"] +panic_immediate_abort = [ + "core/panic_immediate_abort", + "alloc/panic_immediate_abort", +] # Choose algorithms that are optimized for binary size instead of runtime performance optimize_for_size = ["core/optimize_for_size", "alloc/optimize_for_size"] @@ -97,6 +124,11 @@ threads = 125 # Maximum heap size heap_size = 0x8000000 +[[test]] +name = "pipe-subprocess" +path = "tests/pipe_subprocess.rs" +harness = false + [[bench]] name = "stdbenches" path = "benches/lib.rs" diff --git a/std/src/lib.rs b/std/src/lib.rs index f0a73a308a4a4..0fd2edc21acbd 100644 --- a/std/src/lib.rs +++ b/std/src/lib.rs @@ -590,6 +590,8 @@ pub mod panic; #[unstable(feature = "core_pattern_types", issue = "none")] pub mod pat; pub mod path; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub mod pipe; pub mod process; pub mod sync; pub mod time; diff --git a/std/src/pipe.rs b/std/src/pipe.rs new file mode 100644 index 0000000000000..f251b57a7cca6 --- /dev/null +++ b/std/src/pipe.rs @@ -0,0 +1,130 @@ +//! Module for anonymous pipe +//! +//! ``` +//! #![feature(anonymous_pipe)] +//! +//! # #[cfg(miri)] fn main() {} +//! # #[cfg(not(miri))] +//! # fn main() -> std::io::Result<()> { +//! let (reader, writer) = std::pipe::pipe()?; +//! # Ok(()) +//! # } +//! ``` + +use crate::{ + io, + sys::anonymous_pipe::{pipe as pipe_inner, AnonPipe}, +}; + +/// Create anonymous pipe that is close-on-exec and blocking. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[inline] +pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) +} + +/// Read end of the anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeReader(pub(crate) AnonPipe); + +/// Write end of the anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeWriter(pub(crate) AnonPipe); + +impl PipeReader { + /// Create a new [`PipeReader`] instance that shares the same underlying file description. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +impl PipeWriter { + /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for &PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for &PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} diff --git a/std/src/sys/anonymous_pipe/mod.rs b/std/src/sys/anonymous_pipe/mod.rs new file mode 100644 index 0000000000000..74875677cf3e7 --- /dev/null +++ b/std/src/sys/anonymous_pipe/mod.rs @@ -0,0 +1,18 @@ +cfg_if::cfg_if! { + if #[cfg(unix)] { + mod unix; + pub(crate) use unix::{AnonPipe, pipe}; + + #[cfg(all(test, not(miri)))] + mod tests; + } else if #[cfg(windows)] { + mod windows; + pub(crate) use windows::{AnonPipe, pipe}; + + #[cfg(all(test, not(miri)))] + mod tests; + } else { + mod unsupported; + pub(crate) use unsupported::{AnonPipe, pipe}; + } +} diff --git a/std/src/sys/anonymous_pipe/tests.rs b/std/src/sys/anonymous_pipe/tests.rs new file mode 100644 index 0000000000000..f5ea583eefe0d --- /dev/null +++ b/std/src/sys/anonymous_pipe/tests.rs @@ -0,0 +1,20 @@ +use crate::{ + io::{Read, Write}, + pipe::pipe, +}; + +#[test] +fn pipe_creation_clone_and_rw() { + let (rx, tx) = pipe().unwrap(); + + tx.try_clone().unwrap().write_all(b"12345").unwrap(); + drop(tx); + + let mut rx2 = rx.try_clone().unwrap(); + drop(rx); + + let mut s = String::new(); + rx2.read_to_string(&mut s).unwrap(); + drop(rx2); + assert_eq!(s, "12345"); +} diff --git a/std/src/sys/anonymous_pipe/unix.rs b/std/src/sys/anonymous_pipe/unix.rs new file mode 100644 index 0000000000000..ddbf1d7334fe0 --- /dev/null +++ b/std/src/sys/anonymous_pipe/unix.rs @@ -0,0 +1,103 @@ +use crate::{ + io, + os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}, + pipe::{PipeReader, PipeWriter}, + process::Stdio, + sys::{fd::FileDesc, pipe::anon_pipe}, + sys_common::{FromInner, IntoInner}, +}; + +pub(crate) type AnonPipe = FileDesc; + +#[inline] +pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsFd for PipeReader { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawFd for PipeReader { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedFd { + fn from(pipe: PipeReader) -> Self { + FileDesc::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawFd for PipeReader { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FileDesc::from_raw_fd(raw_fd)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawFd for PipeReader { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeReader) -> Self { + Self::from(OwnedFd::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsFd for PipeWriter { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawFd for PipeWriter { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedFd { + fn from(pipe: PipeWriter) -> Self { + FileDesc::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawFd for PipeWriter { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FileDesc::from_raw_fd(raw_fd)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawFd for PipeWriter { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeWriter) -> Self { + Self::from(OwnedFd::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeReader { + fn from(owned_fd: OwnedFd) -> Self { + Self(FileDesc::from_inner(owned_fd)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeWriter { + fn from(owned_fd: OwnedFd) -> Self { + Self(FileDesc::from_inner(owned_fd)) + } +} diff --git a/std/src/sys/anonymous_pipe/unsupported.rs b/std/src/sys/anonymous_pipe/unsupported.rs new file mode 100644 index 0000000000000..5962b69203ee2 --- /dev/null +++ b/std/src/sys/anonymous_pipe/unsupported.rs @@ -0,0 +1,26 @@ +use crate::{ + io, + pipe::{PipeReader, PipeWriter}, + process::Stdio, +}; + +pub(crate) use crate::sys::pipe::AnonPipe; + +#[inline] +pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + Err(io::Error::UNSUPPORTED_PLATFORM) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeReader) -> Self { + pipe.0.diverge() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeWriter) -> Self { + pipe.0.diverge() + } +} diff --git a/std/src/sys/anonymous_pipe/windows.rs b/std/src/sys/anonymous_pipe/windows.rs new file mode 100644 index 0000000000000..81f95aa286a9c --- /dev/null +++ b/std/src/sys/anonymous_pipe/windows.rs @@ -0,0 +1,109 @@ +use crate::{ + io, + os::windows::io::{ + AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, + }, + pipe::{PipeReader, PipeWriter}, + process::Stdio, + sys::{handle::Handle, pipe::unnamed_anon_pipe}, + sys_common::{FromInner, IntoInner}, +}; + +pub(crate) type AnonPipe = Handle; + +#[inline] +pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + unnamed_anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsHandle for PipeReader { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.as_handle() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawHandle for PipeReader { + fn as_raw_handle(&self) -> RawHandle { + self.0.as_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawHandle for PipeReader { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self(Handle::from_raw_handle(raw_handle)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawHandle for PipeReader { + fn into_raw_handle(self) -> RawHandle { + self.0.into_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedHandle { + fn from(pipe: PipeReader) -> Self { + Handle::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeReader) -> Self { + Self::from(OwnedHandle::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsHandle for PipeWriter { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.as_handle() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawHandle for PipeWriter { + fn as_raw_handle(&self) -> RawHandle { + self.0.as_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawHandle for PipeWriter { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self(Handle::from_raw_handle(raw_handle)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawHandle for PipeWriter { + fn into_raw_handle(self) -> RawHandle { + self.0.into_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedHandle { + fn from(pipe: PipeWriter) -> Self { + Handle::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeWriter) -> Self { + Self::from(OwnedHandle::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeReader { + fn from(owned_handle: OwnedHandle) -> Self { + Self(Handle::from_inner(owned_handle)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeWriter { + fn from(owned_handle: OwnedHandle) -> Self { + Self(Handle::from_inner(owned_handle)) + } +} diff --git a/std/src/sys/mod.rs b/std/src/sys/mod.rs index e50758ce00d8b..202997b749513 100644 --- a/std/src/sys/mod.rs +++ b/std/src/sys/mod.rs @@ -7,6 +7,8 @@ mod pal; mod personality; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; pub mod exit_guard; diff --git a/std/src/sys/pal/unix/fd.rs b/std/src/sys/pal/unix/fd.rs index 1701717db597c..10ae3c3ab570d 100644 --- a/std/src/sys/pal/unix/fd.rs +++ b/std/src/sys/pal/unix/fd.rs @@ -82,6 +82,11 @@ const fn max_iov() -> usize { } impl FileDesc { + #[inline] + pub fn try_clone(&self) -> io::Result { + self.duplicate() + } + pub fn read(&self, buf: &mut [u8]) -> io::Result { let ret = cvt(unsafe { libc::read( diff --git a/std/src/sys/pal/unix/pipe.rs b/std/src/sys/pal/unix/pipe.rs index 33db24e77e4da..8762af614f17e 100644 --- a/std/src/sys/pal/unix/pipe.rs +++ b/std/src/sys/pal/unix/pipe.rs @@ -9,6 +9,7 @@ use crate::sys_common::{FromInner, IntoInner}; // Anonymous pipes //////////////////////////////////////////////////////////////////////////////// +#[derive(Debug)] pub struct AnonPipe(FileDesc); pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { @@ -46,6 +47,10 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { } impl AnonPipe { + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(Self) + } + pub fn read(&self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } @@ -79,6 +84,10 @@ impl AnonPipe { pub fn is_write_vectored(&self) -> bool { self.0.is_write_vectored() } + + pub fn as_file_desc(&self) -> &FileDesc { + &self.0 + } } impl IntoInner for AnonPipe { diff --git a/std/src/sys/pal/unsupported/pipe.rs b/std/src/sys/pal/unsupported/pipe.rs index d7d8f297ae586..781eafe2f1a6b 100644 --- a/std/src/sys/pal/unsupported/pipe.rs +++ b/std/src/sys/pal/unsupported/pipe.rs @@ -1,8 +1,21 @@ -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::{ + fmt, + io::{self, BorrowedCursor, IoSlice, IoSliceMut}, +}; pub struct AnonPipe(!); +impl fmt::Debug for AnonPipe { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + impl AnonPipe { + pub fn try_clone(&self) -> io::Result { + self.0 + } + pub fn read(&self, _buf: &mut [u8]) -> io::Result { self.0 } diff --git a/std/src/sys/pal/windows/c/bindings.txt b/std/src/sys/pal/windows/c/bindings.txt index 5ad4a3731d822..7423bbe769432 100644 --- a/std/src/sys/pal/windows/c/bindings.txt +++ b/std/src/sys/pal/windows/c/bindings.txt @@ -2462,6 +2462,7 @@ Windows.Win32.System.LibraryLoader.GetProcAddress Windows.Win32.System.Performance.QueryPerformanceCounter Windows.Win32.System.Performance.QueryPerformanceFrequency Windows.Win32.System.Pipes.CreateNamedPipeW +Windows.Win32.System.Pipes.CreatePipe Windows.Win32.System.Pipes.NAMED_PIPE_MODE Windows.Win32.System.Pipes.PIPE_ACCEPT_REMOTE_CLIENTS Windows.Win32.System.Pipes.PIPE_CLIENT_END diff --git a/std/src/sys/pal/windows/c/windows_sys.rs b/std/src/sys/pal/windows/c/windows_sys.rs index fea00fec9ae59..91c7d7ebc5e0d 100644 --- a/std/src/sys/pal/windows/c/windows_sys.rs +++ b/std/src/sys/pal/windows/c/windows_sys.rs @@ -14,6 +14,7 @@ windows_targets::link!("kernel32.dll" "system" fn CreateEventW(lpeventattributes windows_targets::link!("kernel32.dll" "system" fn CreateFileW(lpfilename : PCWSTR, dwdesiredaccess : u32, dwsharemode : FILE_SHARE_MODE, lpsecurityattributes : *const SECURITY_ATTRIBUTES, dwcreationdisposition : FILE_CREATION_DISPOSITION, dwflagsandattributes : FILE_FLAGS_AND_ATTRIBUTES, htemplatefile : HANDLE) -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn CreateHardLinkW(lpfilename : PCWSTR, lpexistingfilename : PCWSTR, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CreateNamedPipeW(lpname : PCWSTR, dwopenmode : FILE_FLAGS_AND_ATTRIBUTES, dwpipemode : NAMED_PIPE_MODE, nmaxinstances : u32, noutbuffersize : u32, ninbuffersize : u32, ndefaulttimeout : u32, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn CreatePipe(hreadpipe : *mut HANDLE, hwritepipe : *mut HANDLE, lppipeattributes : *const SECURITY_ATTRIBUTES, nsize : u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CreateProcessW(lpapplicationname : PCWSTR, lpcommandline : PWSTR, lpprocessattributes : *const SECURITY_ATTRIBUTES, lpthreadattributes : *const SECURITY_ATTRIBUTES, binherithandles : BOOL, dwcreationflags : PROCESS_CREATION_FLAGS, lpenvironment : *const core::ffi::c_void, lpcurrentdirectory : PCWSTR, lpstartupinfo : *const STARTUPINFOW, lpprocessinformation : *mut PROCESS_INFORMATION) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CreateSymbolicLinkW(lpsymlinkfilename : PCWSTR, lptargetfilename : PCWSTR, dwflags : SYMBOLIC_LINK_FLAGS) -> BOOLEAN); windows_targets::link!("kernel32.dll" "system" fn CreateThread(lpthreadattributes : *const SECURITY_ATTRIBUTES, dwstacksize : usize, lpstartaddress : LPTHREAD_START_ROUTINE, lpparameter : *const core::ffi::c_void, dwcreationflags : THREAD_CREATION_FLAGS, lpthreadid : *mut u32) -> HANDLE); diff --git a/std/src/sys/pal/windows/handle.rs b/std/src/sys/pal/windows/handle.rs index aaa1831dcc24d..706062ab984e7 100644 --- a/std/src/sys/pal/windows/handle.rs +++ b/std/src/sys/pal/windows/handle.rs @@ -17,6 +17,7 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; /// An owned container for `HANDLE` object, closing them on Drop. /// /// All methods are inherited through a `Deref` impl to `RawHandle` +#[derive(Debug)] pub struct Handle(OwnedHandle); impl Handle { @@ -136,6 +137,12 @@ impl Handle { } } + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut me = self; + + Read::read_to_end(&mut me, buf) + } + pub unsafe fn read_overlapped( &self, buf: &mut [mem::MaybeUninit], diff --git a/std/src/sys/pal/windows/pipe.rs b/std/src/sys/pal/windows/pipe.rs index 7a309b9638bd2..d5e2356116f22 100644 --- a/std/src/sys/pal/windows/pipe.rs +++ b/std/src/sys/pal/windows/pipe.rs @@ -1,7 +1,7 @@ use crate::os::windows::prelude::*; use crate::ffi::OsStr; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::mem; use crate::path::Path; use crate::ptr; @@ -39,6 +39,23 @@ pub struct Pipes { pub theirs: AnonPipe, } +/// Create true unnamed anonymous pipe. +pub fn unnamed_anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { + let mut read_pipe = c::INVALID_HANDLE_VALUE; + let mut write_pipe = c::INVALID_HANDLE_VALUE; + + let ret = unsafe { c::CreatePipe(&mut read_pipe, &mut write_pipe, ptr::null_mut(), 0) }; + + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(( + AnonPipe::from_inner(unsafe { Handle::from_raw_handle(read_pipe) }), + AnonPipe::from_inner(unsafe { Handle::from_raw_handle(write_pipe) }), + )) + } +} + /// Although this looks similar to `anon_pipe` in the Unix module it's actually /// subtly different. Here we'll return two pipes in the `Pipes` return value, /// but one is intended for "us" where as the other is intended for "someone @@ -181,7 +198,7 @@ pub fn spawn_pipe_relay( their_handle_inheritable: bool, ) -> io::Result { // We need this handle to live for the lifetime of the thread spawned below. - let source = source.duplicate()?; + let source = source.try_clone()?; // create a new pair of anon pipes. let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?; @@ -237,7 +254,8 @@ impl AnonPipe { pub fn into_handle(self) -> Handle { self.inner } - fn duplicate(&self) -> io::Result { + + pub fn try_clone(&self) -> io::Result { self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner }) } diff --git a/std/tests/pipe_subprocess.rs b/std/tests/pipe_subprocess.rs new file mode 100644 index 0000000000000..c2278098b9b3f --- /dev/null +++ b/std/tests/pipe_subprocess.rs @@ -0,0 +1,39 @@ +#![feature(anonymous_pipe)] + +fn main() { + #[cfg(all(not(miri), any(unix, windows)))] + { + use std::{env, io::Read, pipe::pipe, process}; + + if env::var("I_AM_THE_CHILD").is_ok() { + child(); + } else { + parent(); + } + + fn parent() { + let me = env::current_exe().unwrap(); + + let (rx, tx) = pipe().unwrap(); + assert!( + process::Command::new(me) + .env("I_AM_THE_CHILD", "1") + .stdout(tx) + .status() + .unwrap() + .success() + ); + + let mut s = String::new(); + (&rx).read_to_string(&mut s).unwrap(); + drop(rx); + assert_eq!(s, "Heloo,\n"); + + println!("Test pipe_subprocess.rs success"); + } + + fn child() { + println!("Heloo,"); + } + } +}