os_pipe-0.9.1/.gitignore010064400017500001750000000000221341521771100133640ustar0000000000000000Cargo.lock target os_pipe-0.9.1/.travis.yml010064400017500001750000000010211354544505400135140ustar0000000000000000language: rust os: - linux - osx rust: - stable - beta - nightly # code coverage (Linux only) after_success: | [[ "$TRAVIS_OS_NAME" = "linux" ]] && sudo apt-get install libcurl4-openssl-dev libelf-dev libdw-dev && wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build && cmake .. && make && sudo make install && cd ../.. && kcov --coveralls-id=$TRAVIS_JOB_ID --exclude-pattern=/.cargo target/kcov target/debug/os_pipe-* os_pipe-0.9.1/Cargo.toml.orig010064400017500001750000000007471355015541500143030ustar0000000000000000[package] name = "os_pipe" version = "0.9.1" authors = ["Jack O'Connor"] description = "a cross-platform library for opening OS pipes" repository = "https://github.com/oconnor663/os_pipe.rs" readme = "README.md" documentation = "https://docs.rs/os_pipe" license = "MIT" edition = "2018" [target.'cfg(not(windows))'.dependencies] libc = "0.2.62" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.5", features = ["handleapi", "namedpipeapi", "processthreadsapi", "winnt"] } os_pipe-0.9.1/Cargo.toml0000644000000017630000000000000105450ustar00# 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 believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "os_pipe" version = "0.9.1" authors = ["Jack O'Connor"] description = "a cross-platform library for opening OS pipes" documentation = "https://docs.rs/os_pipe" readme = "README.md" license = "MIT" repository = "https://github.com/oconnor663/os_pipe.rs" [target."cfg(not(windows))".dependencies.libc] version = "0.2.62" [target."cfg(windows)".dependencies.winapi] version = "0.3.5" features = ["handleapi", "namedpipeapi", "processthreadsapi", "winnt"] os_pipe-0.9.1/LICENSE010064400017500001750000000020261341521771100124070ustar0000000000000000The MIT License (MIT) 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. os_pipe-0.9.1/README.md010064400017500001750000000072371355015745300127000ustar0000000000000000# os_pipe.rs [![Travis build](https://travis-ci.org/oconnor663/os_pipe.rs.svg?branch=master)](https://travis-ci.org/oconnor663/os_pipe.rs) [![AppVeyor build](https://ci.appveyor.com/api/projects/status/89o6o64nxfl80s78/branch/master?svg=true)](https://ci.appveyor.com/project/oconnor663/os-pipe-rs/branch/master) [![crates.io](https://img.shields.io/crates/v/os_pipe.svg)](https://crates.io/crates/os_pipe) [![docs.rs](https://docs.rs/os_pipe/badge.svg)](https://docs.rs/os_pipe) A cross-platform library for opening OS pipes. The standard library uses pipes to read output from child processes, but it doesn't expose a way to create them directly. This crate fills that gap with the `pipe` function. It also includes some helpers for passing pipes to the `std::process::Command` API. - [Docs](https://docs.rs/os_pipe) - [Crate](https://crates.io/crates/os_pipe) - [Repo](https://github.com/oconnor663/os_pipe.rs) Usage note: The main purpose of `os_pipe` is to support the higher-level [`duct`](https://github.com/oconnor663/duct.rs) library, which handles most of the same use cases with much less code and no risk of deadlocks. `duct` can run the entire example below in one line of code. ## Changes - 0.9.1 - Fix a bug on Windows where `try_clone` was making pipes inheritable, which led to deadlocks. - 0.9.0 - Set `edition = "2018"` in Cargo.toml. - Minimum supported compiler version is now 1.31.0. - Removed dependency on the `nix` crate. Now only depends on `libc` on Unix. - 0.8.0 - Remove the `From<...> for File` impls. While treating a pipe or a tty as a file works pretty smoothly on Unix, it's questionable on Windows. For example, `File::metadata` may return an error, or it might succeed but then incorrectly return `true` from `is_file`. Now that the standard library's `Stdin`/`Stdout`/`Stderr` types all implement `AsRawFd`/`AsRawHandle`, callers who know what they're doing can use those interfaces, rather than relying on `os_pipe`. ## Example Join the stdout and stderr of a child process into a single stream, and read it. To do that we open a pipe, duplicate its write end, and pass those writers as the child's stdout and stderr. Then we can read combined output from the read end of the pipe. We have to be careful to close the write ends first though, or reading will block waiting for EOF. ```rust use os_pipe::pipe; use std::io::prelude::*; use std::process::{Command, Stdio}; // This command prints "foo" to stdout and "bar" to stderr. It // works on both Unix and Windows, though there are whitespace // differences that we'll account for at the bottom. let shell_command = "echo foo && echo bar >&2"; // Ritual magic to run shell commands on different platforms. let (shell, flag) = if cfg!(windows) { ("cmd.exe", "/C") } else { ("sh", "-c") }; let mut child = Command::new(shell); child.arg(flag); child.arg(shell_command); // Here's the interesting part. Open a pipe, copy its write end, and // give both copies to the child. let (mut reader, writer) = pipe().unwrap(); let writer_clone = writer.try_clone().unwrap(); child.stdout(writer); child.stderr(writer_clone); // Now start the child running. let mut handle = child.spawn().unwrap(); // Very important when using pipes: This parent process is still // holding its copies of the write ends, and we have to close them // before we read, otherwise the read end will never report EOF. The // Command object owns the writers now, and dropping it closes them. drop(child); // Finally we can read all the output and clean up the child. let mut output = String::new(); reader.read_to_string(&mut output).unwrap(); handle.wait().unwrap(); assert!(output.split_whitespace().eq(vec!["foo", "bar"])); ``` os_pipe-0.9.1/README.tpl010064400017500001750000000007561341521771100130700ustar0000000000000000# {{crate}}.rs [![Travis build](https://travis-ci.org/oconnor663/os_pipe.rs.svg?branch=master)](https://travis-ci.org/oconnor663/os_pipe.rs) [![AppVeyor build](https://ci.appveyor.com/api/projects/status/89o6o64nxfl80s78/branch/master?svg=true)](https://ci.appveyor.com/project/oconnor663/os-pipe-rs/branch/master) [![crates.io](https://img.shields.io/crates/v/os_pipe.svg)](https://crates.io/crates/os_pipe) [![docs.rs](https://docs.rs/os_pipe/badge.svg)](https://docs.rs/os_pipe) {{readme}} os_pipe-0.9.1/appveyor.yml010064400017500001750000000015401354544503700140020ustar0000000000000000environment: matrix: - TARGET: x86_64-pc-windows-msvc VERSION: 1.31.0 - TARGET: i686-pc-windows-msvc VERSION: 1.31.0 - TARGET: i686-pc-windows-gnu VERSION: 1.31.0 - TARGET: x86_64-pc-windows-msvc VERSION: beta - TARGET: i686-pc-windows-msvc VERSION: beta - TARGET: i686-pc-windows-gnu VERSION: beta - TARGET: x86_64-pc-windows-msvc VERSION: nightly - TARGET: i686-pc-windows-msvc VERSION: nightly - TARGET: i686-pc-windows-gnu VERSION: nightly install: - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:VERSION}-${env:TARGET}.exe" - rust-%VERSION%-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin - SET PATH=%PATH%;C:\MinGW\bin - rustc -V - cargo -V build: false test_script: - cargo test --verbose os_pipe-0.9.1/src/bin/cat.rs010064400017500001750000000004761341521771100140650ustar0000000000000000#![deny(warnings)] /// Windows doesn't have a native equivalent for cat, so we use this little /// Rust implementation instead. use std::io::{copy, stdin, stdout}; fn main() { let stdin_handle = stdin(); let stdout_handle = stdout(); copy(&mut stdin_handle.lock(), &mut stdout_handle.lock()).unwrap(); } os_pipe-0.9.1/src/bin/cat_both.rs010064400017500001750000000007631341521771100151000ustar0000000000000000//! This little test binary reads stdin, and then writes what it read to both //! stdout and stderr, with a little tag to differentiate them. We use it to //! test duping the standard file descriptors. #![deny(warnings)] use std::io; use std::io::prelude::*; fn main() { let mut input = Vec::new(); io::stdin().read_to_end(&mut input).unwrap(); print!("stdout: "); io::stdout().write_all(&input).unwrap(); eprint!("stderr: "); io::stderr().write_all(&input).unwrap(); } os_pipe-0.9.1/src/bin/swap.rs010064400017500001750000000015361354544015400142710ustar0000000000000000#![deny(warnings)] /// This little test binary reads stdin and write what it reads to both /// stdout and stderr. It depends on os_pipe's parent_* functions, and /// we use it to test them. use std::env::args_os; use std::ffi::OsString; use std::process::Command; fn main() { let stdin = os_pipe::dup_stdin().unwrap(); let stdout = os_pipe::dup_stdout().unwrap(); let stderr = os_pipe::dup_stderr().unwrap(); let args: Vec = args_os().collect(); let mut child = Command::new(&args[1]); child.args(&args[2..]); // Swap stdout and stderr in the child. Set stdin too, just for testing, // though this should be the same as the default behavior. child.stdin(stdin); child.stdout(stderr); child.stderr(stdout); // Run the child. This method is kind of confusingly named... child.status().unwrap(); } os_pipe-0.9.1/src/lib.rs010064400017500001750000000414651355015665700133320ustar0000000000000000//! A cross-platform library for opening OS pipes. //! //! The standard library uses pipes to read output from child processes, //! but it doesn't expose a way to create them directly. This crate //! fills that gap with the `pipe` function. It also includes some //! helpers for passing pipes to the `std::process::Command` API. //! //! - [Docs](https://docs.rs/os_pipe) //! - [Crate](https://crates.io/crates/os_pipe) //! - [Repo](https://github.com/oconnor663/os_pipe.rs) //! //! Usage note: The main purpose of `os_pipe` is to support the //! higher-level [`duct`](https://github.com/oconnor663/duct.rs) //! library, which handles most of the same use cases with much less //! code and no risk of deadlocks. `duct` can run the entire example //! below in one line of code. //! //! # Changes //! //! - 0.9.1 //! - Fix a bug on Windows where `try_clone` was making pipes inheritable, //! which led to deadlocks. //! - 0.9.0 //! - Set `edition = "2018"` in Cargo.toml. //! - Minimum supported compiler version is now 1.31.0. //! - Removed dependency on the `nix` crate. Now only depends on `libc` on //! Unix. //! - 0.8.0 //! - Remove the `From<...> for File` impls. While treating a pipe or a tty as //! a file works pretty smoothly on Unix, it's questionable on Windows. For //! example, `File::metadata` may return an error, or it might succeed but //! then incorrectly return `true` from `is_file`. Now that the standard //! library's `Stdin`/`Stdout`/`Stderr` types all implement //! `AsRawFd`/`AsRawHandle`, callers who know what they're doing can use //! those interfaces, rather than relying on `os_pipe`. //! //! # Example //! //! Join the stdout and stderr of a child process into a single stream, //! and read it. To do that we open a pipe, duplicate its write end, and //! pass those writers as the child's stdout and stderr. Then we can //! read combined output from the read end of the pipe. We have to be //! careful to close the write ends first though, or reading will block //! waiting for EOF. //! //! ```rust //! use os_pipe::pipe; //! use std::io::prelude::*; //! use std::process::{Command, Stdio}; //! //! // This command prints "foo" to stdout and "bar" to stderr. It //! // works on both Unix and Windows, though there are whitespace //! // differences that we'll account for at the bottom. //! let shell_command = "echo foo && echo bar >&2"; //! //! // Ritual magic to run shell commands on different platforms. //! let (shell, flag) = if cfg!(windows) { ("cmd.exe", "/C") } else { ("sh", "-c") }; //! //! let mut child = Command::new(shell); //! child.arg(flag); //! child.arg(shell_command); //! //! // Here's the interesting part. Open a pipe, copy its write end, and //! // give both copies to the child. //! let (mut reader, writer) = pipe().unwrap(); //! let writer_clone = writer.try_clone().unwrap(); //! child.stdout(writer); //! child.stderr(writer_clone); //! //! // Now start the child running. //! let mut handle = child.spawn().unwrap(); //! //! // Very important when using pipes: This parent process is still //! // holding its copies of the write ends, and we have to close them //! // before we read, otherwise the read end will never report EOF. The //! // Command object owns the writers now, and dropping it closes them. //! drop(child); //! //! // Finally we can read all the output and clean up the child. //! let mut output = String::new(); //! reader.read_to_string(&mut output).unwrap(); //! handle.wait().unwrap(); //! assert!(output.split_whitespace().eq(vec!["foo", "bar"])); //! ``` use std::fs::File; use std::io; use std::process::Stdio; /// The reading end of a pipe, returned by [`pipe`](fn.pipe.html). /// /// `PipeReader` implements `Into`, so you can pass it as an argument to /// `Command::stdin` to spawn a child process that reads from the pipe. #[derive(Debug)] pub struct PipeReader(File); impl PipeReader { pub fn try_clone(&self) -> io::Result { // Do *not* use File::try_clone here. It's buggy on windows. See // comments on windows.rs::dup(). sys::dup(&self.0).map(PipeReader) } } impl io::Read for PipeReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } } impl<'a> io::Read for &'a PipeReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { let mut file_ref = &self.0; file_ref.read(buf) } } impl From for Stdio { fn from(p: PipeReader) -> Stdio { p.0.into() } } /// The writing end of a pipe, returned by [`pipe`](fn.pipe.html). /// /// `PipeWriter` implements `Into`, so you can pass it as an argument to /// `Command::stdout` or `Command::stderr` to spawn a child process that writes /// to the pipe. #[derive(Debug)] pub struct PipeWriter(File); impl PipeWriter { pub fn try_clone(&self) -> io::Result { // Do *not* use File::try_clone here. It's buggy on windows. See // comments on windows.rs::dup(). sys::dup(&self.0).map(PipeWriter) } } impl io::Write for PipeWriter { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } fn flush(&mut self) -> io::Result<()> { self.0.flush() } } impl<'a> io::Write for &'a PipeWriter { fn write(&mut self, buf: &[u8]) -> io::Result { let mut file_ref = &self.0; file_ref.write(buf) } fn flush(&mut self) -> io::Result<()> { let mut file_ref = &self.0; file_ref.flush() } } impl From for Stdio { fn from(p: PipeWriter) -> Stdio { p.0.into() } } /// Open a new pipe and return a [`PipeReader`] and [`PipeWriter`] pair. /// /// This corresponds to the `pipe2` library call on Posix and the /// `CreatePipe` library call on Windows (though these implementation /// details might change). Pipes are non-inheritable, so new child /// processes won't receive a copy of them unless they're explicitly /// passed as stdin/stdout/stderr. /// /// [`PipeReader`]: struct.PipeReader.html /// [`PipeWriter`]: struct.PipeWriter.html pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { sys::pipe() } /// Get a duplicated copy of the current process's standard input, as a /// [`PipeReader`]. /// /// Reading directly from this pipe isn't recommended, because it's not /// synchronized with [`std::io::stdin`]. [`PipeReader`] implements /// [`Into`], so it can be passed directly to [`Command::stdin`]. This is /// equivalent to [`Stdio::inherit`], though, so it's usually not necessary /// unless you need a collection of different pipes. /// /// [`std::io::stdin`]: https://doc.rust-lang.org/std/io/fn.stdin.html /// [`PipeReader`]: struct.PipeReader.html /// [`Into`]: https://doc.rust-lang.org/std/process/struct.Stdio.html /// [`Command::stdin`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.stdin /// [`Stdio::inherit`]: https://doc.rust-lang.org/std/process/struct.Stdio.html#method.inherit pub fn dup_stdin() -> io::Result { sys::dup(&io::stdin()).map(PipeReader) } /// Get a duplicated copy of the current process's standard output, as a /// [`PipeWriter`](struct.PipeWriter.html). /// /// Writing directly to this pipe isn't recommended, because it's not /// synchronized with [`std::io::stdout`]. [`PipeWriter`] implements /// [`Into`], so it can be passed directly to [`Command::stdout`] or /// [`Command::stderr`]. This can be useful if you want the child's stderr to go /// to the parent's stdout. /// /// [`std::io::stdout`]: https://doc.rust-lang.org/std/io/fn.stdout.html /// [`PipeWriter`]: struct.PipeWriter.html /// [`Into`]: https://doc.rust-lang.org/std/process/struct.Stdio.html /// [`Command::stdout`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.stdout /// [`Command::stderr`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.stderr /// [`Stdio::inherit`]: https://doc.rust-lang.org/std/process/struct.Stdio.html#method.inherit pub fn dup_stdout() -> io::Result { sys::dup(&io::stdout()).map(PipeWriter) } /// Get a duplicated copy of the current process's standard error, as a /// [`PipeWriter`](struct.PipeWriter.html). /// /// Writing directly to this pipe isn't recommended, because it's not /// synchronized with [`std::io::stderr`]. [`PipeWriter`] implements /// [`Into`], so it can be passed directly to [`Command::stdout`] or /// [`Command::stderr`]. This can be useful if you want the child's stdout to go /// to the parent's stderr. /// /// [`std::io::stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html /// [`PipeWriter`]: struct.PipeWriter.html /// [`Into`]: https://doc.rust-lang.org/std/process/struct.Stdio.html /// [`Command::stdout`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.stdout /// [`Command::stderr`]: https://doc.rust-lang.org/std/process/struct.Command.html#method.stderr /// [`Stdio::inherit`]: https://doc.rust-lang.org/std/process/struct.Stdio.html#method.inherit pub fn dup_stderr() -> io::Result { sys::dup(&io::stderr()).map(PipeWriter) } #[cfg(not(windows))] #[path = "unix.rs"] mod sys; #[cfg(windows)] #[path = "windows.rs"] mod sys; #[cfg(test)] mod tests { use std::env::consts::EXE_EXTENSION; use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::Once; use std::thread; fn path_to_exe(name: &str) -> PathBuf { // This project defines some associated binaries for testing, and we shell out to them in // these tests. `cargo test` doesn't automatically build associated binaries, so this // function takes care of building them explicitly, with the right debug/release flavor. static CARGO_BUILD_ONCE: Once = Once::new(); CARGO_BUILD_ONCE.call_once(|| { let mut build_command = Command::new("cargo"); build_command.args(&["build", "--quiet"]); if !cfg!(debug_assertions) { build_command.arg("--release"); } let build_status = build_command.status().unwrap(); assert!( build_status.success(), "Cargo failed to build associated binaries." ); }); let flavor = if cfg!(debug_assertions) { "debug" } else { "release" }; Path::new("target") .join(flavor) .join(name) .with_extension(EXE_EXTENSION) } #[test] fn test_pipe_some_data() { let (mut reader, mut writer) = crate::pipe().unwrap(); // A small write won't fill the pipe buffer, so it won't block this thread. writer.write_all(b"some stuff").unwrap(); drop(writer); let mut out = String::new(); reader.read_to_string(&mut out).unwrap(); assert_eq!(out, "some stuff"); } #[test] fn test_pipe_some_data_with_refs() { // As with `File`, there's a second set of impls for shared // refs. Test those. let (reader, writer) = crate::pipe().unwrap(); let mut reader_ref = &reader; { let mut writer_ref = &writer; // A small write won't fill the pipe buffer, so it won't block this thread. writer_ref.write_all(b"some stuff").unwrap(); } drop(writer); let mut out = String::new(); reader_ref.read_to_string(&mut out).unwrap(); assert_eq!(out, "some stuff"); } #[test] fn test_pipe_no_data() { let (mut reader, writer) = crate::pipe().unwrap(); drop(writer); let mut out = String::new(); reader.read_to_string(&mut out).unwrap(); assert_eq!(out, ""); } #[test] fn test_pipe_a_megabyte_of_data_from_another_thread() { let data = vec![0xff; 1_000_000]; let data_copy = data.clone(); let (mut reader, mut writer) = crate::pipe().unwrap(); let joiner = thread::spawn(move || { writer.write_all(&data_copy).unwrap(); // This drop happens automatically, so writing it out here is mostly // just for clarity. For what it's worth, it also guards against // accidentally forgetting to drop if we switch to scoped threads or // something like that and change this to a non-moving closure. The // explicit drop forces `writer` to move. drop(writer); }); let mut out = Vec::new(); reader.read_to_end(&mut out).unwrap(); joiner.join().unwrap(); assert_eq!(out, data); } #[test] fn test_pipes_are_not_inheritable() { // Create pipes for a child process. let (input_reader, mut input_writer) = crate::pipe().unwrap(); let (mut output_reader, output_writer) = crate::pipe().unwrap(); // Create a bunch of duplicated copies, which we'll close later. This // tests that duplication preserves non-inheritability. let ir_dup = input_reader.try_clone().unwrap(); let iw_dup = input_writer.try_clone().unwrap(); let or_dup = output_reader.try_clone().unwrap(); let ow_dup = output_writer.try_clone().unwrap(); // Spawn the child. Note that this temporary Command object takes // ownership of our copies of the child's stdin and stdout, and then // closes them immediately when it drops. That stops us from blocking // our own read below. We use our own simple implementation of cat for // compatibility with Windows. let mut child = Command::new(path_to_exe("cat")) .stdin(input_reader) .stdout(output_writer) .spawn() .unwrap(); // Drop all the dups now that the child is spawned. drop(ir_dup); drop(iw_dup); drop(or_dup); drop(ow_dup); // Write to the child's stdin. This is a small write, so it shouldn't // block. input_writer.write_all(b"hello").unwrap(); drop(input_writer); // Read from the child's stdout. If this child has accidentally // inherited the write end of its own stdin, then it will never exit, // and this read will block forever. That's what this test is all // about. let mut output = Vec::new(); output_reader.read_to_end(&mut output).unwrap(); child.wait().unwrap(); // Confirm that we got the right bytes. assert_eq!(b"hello", &*output); } #[test] fn test_parent_handles() { // This test invokes the `swap` test program, which uses parent_stdout() and // parent_stderr() to swap the outputs for another child that it spawns. // Create pipes for a child process. let (reader, mut writer) = crate::pipe().unwrap(); // Write input. This shouldn't block because it's small. Then close the write end, or else // the child will hang. writer.write_all(b"quack").unwrap(); drop(writer); // Use `swap` to run `cat_both`. `cat_both will read "quack" from stdin // and write it to stdout and stderr with different tags. But because we // run it inside `swap`, the tags in the output should be backwards. let output = Command::new(path_to_exe("swap")) .arg(path_to_exe("cat_both")) .stdin(reader) .output() .unwrap(); // Check for a clean exit. assert!( output.status.success(), "child process returned {:#?}", output ); // Confirm that we got the right bytes. assert_eq!(b"stderr: quack", &*output.stdout); assert_eq!(b"stdout: quack", &*output.stderr); } #[test] fn test_parent_handles_dont_close() { // Open and close each parent pipe multiple times. If this closes the // original, subsequent opens should fail. let stdin = crate::dup_stdin().unwrap(); drop(stdin); let stdin = crate::dup_stdin().unwrap(); drop(stdin); let stdout = crate::dup_stdout().unwrap(); drop(stdout); let stdout = crate::dup_stdout().unwrap(); drop(stdout); let stderr = crate::dup_stderr().unwrap(); drop(stderr); let stderr = crate::dup_stderr().unwrap(); drop(stderr); } #[test] fn test_try_clone() { let (reader, writer) = crate::pipe().unwrap(); let mut reader_clone = reader.try_clone().unwrap(); let mut writer_clone = writer.try_clone().unwrap(); // A small write won't fill the pipe buffer, so it won't block this thread. writer_clone.write_all(b"some stuff").unwrap(); drop(writer); drop(writer_clone); let mut out = String::new(); reader_clone.read_to_string(&mut out).unwrap(); assert_eq!(out, "some stuff"); } #[test] fn test_debug() { let (reader, writer) = crate::pipe().unwrap(); format!("{:?} {:?}", reader, writer); } } os_pipe-0.9.1/src/unix.rs010064400017500001750000000052171355015537400135350ustar0000000000000000use crate::PipeReader; use crate::PipeWriter; use libc::c_int; use std::fs::File; use std::io; use std::mem::ManuallyDrop; use std::os::unix::prelude::*; // We need to atomically create pipes and set the CLOEXEC flag on them. This is // done with the pipe2() API. However, macOS doesn't support pipe2. There, all // we can do is call pipe() followed by fcntl(), and hope that no other threads // fork() in between. The following code is copied from the nix crate, where it // works but is deprecated. #[cfg(any( target_os = "android", target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", target_os = "linux", target_os = "netbsd", target_os = "openbsd" ))] fn pipe2_cloexec() -> io::Result<(c_int, c_int)> { let mut fds: [c_int; 2] = [0; 2]; let res = unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) }; if res != 0 { return Err(io::Error::last_os_error()); } Ok((fds[0], fds[1])) } #[cfg(any(target_os = "ios", target_os = "macos"))] fn pipe2_cloexec() -> io::Result<(c_int, c_int)> { let mut fds: [c_int; 2] = [0; 2]; let res = unsafe { libc::pipe(fds.as_mut_ptr()) }; if res != 0 { return Err(io::Error::last_os_error()); } let res = unsafe { libc::fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC) }; if res != 0 { return Err(io::Error::last_os_error()); } let res = unsafe { libc::fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC) }; if res != 0 { return Err(io::Error::last_os_error()); } Ok((fds[0], fds[1])) } pub(crate) fn pipe() -> io::Result<(PipeReader, PipeWriter)> { let (read_fd, write_fd) = pipe2_cloexec()?; unsafe { Ok(( PipeReader::from_raw_fd(read_fd), PipeWriter::from_raw_fd(write_fd), )) } } pub(crate) fn dup(wrapper: &F) -> io::Result { let fd = wrapper.as_raw_fd(); let temp_file = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) }); temp_file.try_clone() } impl IntoRawFd for PipeReader { fn into_raw_fd(self) -> RawFd { self.0.into_raw_fd() } } impl AsRawFd for PipeReader { fn as_raw_fd(&self) -> RawFd { self.0.as_raw_fd() } } impl FromRawFd for PipeReader { unsafe fn from_raw_fd(fd: RawFd) -> PipeReader { PipeReader(File::from_raw_fd(fd)) } } impl IntoRawFd for PipeWriter { fn into_raw_fd(self) -> RawFd { self.0.into_raw_fd() } } impl AsRawFd for PipeWriter { fn as_raw_fd(&self) -> RawFd { self.0.as_raw_fd() } } impl FromRawFd for PipeWriter { unsafe fn from_raw_fd(fd: RawFd) -> PipeWriter { PipeWriter(File::from_raw_fd(fd)) } } os_pipe-0.9.1/src/windows.rs010064400017500001750000000066231355015537400142460ustar0000000000000000use crate::PipeReader; use crate::PipeWriter; use std::fs::File; use std::io; use std::os::windows::prelude::*; use std::ptr; use winapi::shared::minwindef::BOOL; use winapi::shared::ntdef::{HANDLE, PHANDLE}; use winapi::um::handleapi::DuplicateHandle; use winapi::um::namedpipeapi; use winapi::um::processthreadsapi::GetCurrentProcess; use winapi::um::winnt::DUPLICATE_SAME_ACCESS; pub(crate) fn pipe() -> io::Result<(PipeReader, PipeWriter)> { let mut read_pipe: HANDLE = ptr::null_mut(); let mut write_pipe: HANDLE = ptr::null_mut(); let ret = unsafe { // NOTE: These pipes do not support IOCP. We might want to emulate // anonymous pipes with CreateNamedPipe, as Rust's stdlib does. namedpipeapi::CreatePipe( &mut read_pipe as PHANDLE, &mut write_pipe as PHANDLE, ptr::null_mut(), 0, ) }; if ret == 0 { Err(io::Error::last_os_error()) } else { unsafe { Ok(( PipeReader::from_raw_handle(read_pipe as _), PipeWriter::from_raw_handle(write_pipe as _), )) } } } pub(crate) fn dup(wrapper: &F) -> io::Result { // We rely on ("abuse") std::fs::File for a lot of descriptor/handle // operations. (For example, setting F_DUPFD_CLOEXEC on Unix is a // compatibility mess.) However, in the particular case of try_clone on // Windows, the standard library has a bug where duplicated handles end up // inheritable when they shouldn't be. See // https://github.com/rust-lang/rust/pull/65316. This leads to races where // child processes can inherit each other's handles, which tends to cause // deadlocks when the handle in question is a stdout pipe. To get that // right, we explicitly make the necessary system calls here, just like // libstd apart from that one flag. let source_handle = wrapper.as_raw_handle() as HANDLE; let desired_access = 0; // Ignored because of DUPLICATE_SAME_ACCESS. let inherit_handle = false as BOOL; // <-- Libstd sets this to true! let options = DUPLICATE_SAME_ACCESS; let mut duplicated_handle = 0 as HANDLE; let ret = unsafe { let current_process = GetCurrentProcess(); DuplicateHandle( current_process, source_handle, current_process, &mut duplicated_handle, desired_access, inherit_handle, options, ) }; if ret == 0 { Err(io::Error::last_os_error()) } else { unsafe { Ok(File::from_raw_handle(duplicated_handle as RawHandle)) } } } impl IntoRawHandle for PipeReader { fn into_raw_handle(self) -> RawHandle { self.0.into_raw_handle() } } impl AsRawHandle for PipeReader { fn as_raw_handle(&self) -> RawHandle { self.0.as_raw_handle() } } impl FromRawHandle for PipeReader { unsafe fn from_raw_handle(handle: RawHandle) -> PipeReader { PipeReader(File::from_raw_handle(handle)) } } impl IntoRawHandle for PipeWriter { fn into_raw_handle(self) -> RawHandle { self.0.into_raw_handle() } } impl AsRawHandle for PipeWriter { fn as_raw_handle(&self) -> RawHandle { self.0.as_raw_handle() } } impl FromRawHandle for PipeWriter { unsafe fn from_raw_handle(handle: RawHandle) -> PipeWriter { PipeWriter(File::from_raw_handle(handle)) } } os_pipe-0.9.1/.cargo_vcs_info.json0000644000000001120000000000000125320ustar00{ "git": { "sha1": "b34383b222680b2aa4550fc1f1b89a8733d70e5c" } } os_pipe-0.9.1/Cargo.lock0000644000000031570000000000000105210ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "libc" version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "os_pipe" version = "0.9.1" dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"