Skip to content

Commit

Permalink
Add Windows support
Browse files Browse the repository at this point in the history
  • Loading branch information
Kestrer committed Sep 28, 2020
1 parent 1ef3be0 commit dfc92ca
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 29 deletions.
8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ authors = ["elichai2 <[email protected]>"]
repository = "https://github.com/elichai/stdio-override"
readme = "README.md"
edition = "2018"
description = "Rust library for overriding Stdin/Stdout/Stderr with a different File Descriptor"
categories = ["os::unix-apis", "development-tools::debugging"]
keywords = ["unix", "sockets", "fd", "file", "io"]
description = "Rust library for overriding Stdin/Stdout/Stderr with a different stream"
categories = ["os", "development-tools::debugging"]
keywords = ["crossplatform", "sockets", "fd", "file", "io"]


[dependencies]
doc-comment = { version = "0.3", optional = true }

[target.'cfg(unix)'.dependencies]
libc = "0.2"
[target.'cfg(windows)'.dependencies]
winapi = "0.3"

[dev-dependencies]
os_pipe = "0.9.2"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![Documentation](https://docs.rs/stdio-override/badge.svg)](https://docs.rs/stdio-override)
![License](https://img.shields.io/crates/l/stdio-override.svg)

A Rust library to easily override Stdio file descriptors in Rust
A Rust library to easily override Stdio streams in Rust. It works on Unix and Windows platforms.

* [Documentation](https://docs.rs/stdio-override)

Expand Down
44 changes: 21 additions & 23 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

//! # Stdio-Override
//!
//! This crate provides a library for overriding Stdio file descriptors. <br>
//! It provides a guard for the replacement so that when the guard is dropped the file descriptors are switched back
//! and the replacement File Descriptor will be closed.
//! This crate provides a library for overriding Stdio streams. <br>
//! It provides a guard for the replacement so that when the guard is dropped the streams are switched back
//! and the replacement stream will be closed.
//!
//! You can replace a standard stream twice, just keep in mind that each guard, when dropped, will
//! replace stdout with the stdout that existed when it was created. This means that if you don't
Expand Down Expand Up @@ -85,10 +85,11 @@ use std::io::{self, IoSlice, IoSliceMut, Read, Write};
use std::mem::ManuallyDrop;
use std::path::Path;

#[cfg(not(any(unix)))]
compile_error!("stdio-override only supports Unix");
#[cfg(not(any(unix, windows)))]
compile_error!("stdio-override only supports Unix and Windows");

#[cfg_attr(unix, path = "unix.rs")]
#[cfg_attr(windows, path = "windows.rs")]
mod imp;

/// An overridden standard input.
Expand All @@ -101,17 +102,16 @@ pub struct StdinOverride {
reset: bool,
}
impl StdinOverride {
/// Read standard input from the raw file descriptor. The file descriptor must be readable.
/// Read standard input from the raw file descriptor or handle. It must be readable.
///
/// The file descriptor is not owned, so it is your job to close it later. Closing it while
/// this exists will not close the standard error.
/// The stream is not owned, so it is your job to close it later. Closing it while this exists
/// will not close the standard error.
pub fn from_raw(raw: imp::Raw) -> io::Result<Self> {
Ok(Self { original: ManuallyDrop::new(imp::override_stdin(raw, false)?), reset: false })
}
/// Read standard input from the owned raw file descriptor. The file descriptor must be
/// readable.
/// Read standard input from the owned raw file descriptor or handle. It must be readable.
///
/// The file descriptor is owned, and so you must not use it after passing it to this function.
/// The stream is owned, and so you must not use it after passing it to this function.
pub fn from_raw_owned(raw: imp::Raw) -> io::Result<Self> {
Ok(Self { original: ManuallyDrop::new(imp::override_stdin(raw, true)?), reset: false })
}
Expand Down Expand Up @@ -177,17 +177,16 @@ pub struct StdoutOverride {
reset: bool,
}
impl StdoutOverride {
/// Redirect standard output to the raw file descriptor. The file descriptor must be writable.
/// Redirect standard output to the raw file descriptor or handle. It must be writable.
///
/// The file descriptor is not owned, so it is your job to close it later. Closing it while
/// this exists will not close the standard output.
/// The stream is not owned, so it is your job to close it later. Closing it while this exists
/// will not close the standard output.
pub fn from_raw(raw: imp::Raw) -> io::Result<Self> {
Ok(Self { original: ManuallyDrop::new(imp::override_stdout(raw, false)?), reset: false })
}
/// Redirect standard output to the owned raw file descriptor. The file descriptor must be
/// writable.
/// Redirect standard output to the owned raw file descriptor or handle. It must be writable.
///
/// The file descriptor is owned, and so you must not use it after passing it to this function.
/// The stream is owned, and so you must not use it after passing it to this function.
pub fn from_raw_owned(raw: imp::Raw) -> io::Result<Self> {
Ok(Self { original: ManuallyDrop::new(imp::override_stdout(raw, true)?), reset: false })
}
Expand Down Expand Up @@ -259,17 +258,16 @@ pub struct StderrOverride {
reset: bool,
}
impl StderrOverride {
/// Redirect standard error to the raw file descriptor. The file descriptor must be writable.
/// Redirect standard error to the raw file descriptor or handle. It must be writable.
///
/// The file descriptor is not owned, so it is your job to close it later. Closing it while
/// this exists will not close the standard error.
/// The stream is not owned, so it is your job to close it later. Closing it while this exists
/// will not close the standard error.
pub fn from_raw(raw: imp::Raw) -> io::Result<Self> {
Ok(Self { original: ManuallyDrop::new(imp::override_stderr(raw, false)?), reset: false })
}
/// Redirect standard error to the owned raw file descriptor. The file descriptor must be
/// writable.
/// Redirect standard error to the owned raw file descriptor or handle. It must be writable.
///
/// The file descriptor is owned, and so you must not use it after passing it to this function.
/// The stream is owned, and so you must not use it after passing it to this function.
pub fn from_raw_owned(raw: imp::Raw) -> io::Result<Self> {
Ok(Self { original: ManuallyDrop::new(imp::override_stderr(raw, true)?), reset: false })
}
Expand Down
5 changes: 3 additions & 2 deletions src/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::fs::File;
use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};

use libc::c_int;
use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};

pub(crate) use std::os::unix::io::{AsRawFd as AsRaw, IntoRawFd as IntoRaw, RawFd as Raw};
Expand Down Expand Up @@ -72,8 +73,8 @@ fn test_original() -> io::Result<()> {
Ok(())
}

fn io_res<T: PartialEq + From<i8>>(res: T) -> io::Result<T> {
if res == T::from(-1) {
fn io_res(res: c_int) -> io::Result<c_int> {
if res == -1 {
Err(io::Error::last_os_error())
} else {
Ok(res)
Expand Down
102 changes: 102 additions & 0 deletions src/windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use std::fs::File;
use std::io;
use std::os::windows::FromRawHandle;
use std::ptr;

use winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE};
use winapi::um::handleapi::{CloseHandle, DuplicateHandle, GetHandleInformation, INVALID_HANDLE_VALUE};
use winapi::um::processenv::{GetStdHandle, SetStdHandle};
use winapi::um::processthreadsapi::GetCurrentProcess;
use winapi::um::winbase::HANDLE_FLAG_INHERIT;
use winapi::um::winbase::{STD_ERROR_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE};
use winapi::um::winnt::DUPLICATE_SAME_ACCESS;

pub(crate) use std::os::windows::io::{AsRawHandle as AsRaw, IntoRawHandle as IntoRaw, RawHandle as Raw};

pub(crate) fn as_raw(io: &impl AsRawHandle) -> RawHandle {
io.as_raw_handle()
}
pub(crate) fn into_raw(io: impl IntoRawHandle) -> RawHandle {
io.into_raw_handle()
}

pub(crate) fn override_stdin(io: RawHandle, owned: bool) -> io::Result<File> {
override_stdio(STD_INPUT_HANDLE, owned)
}
pub(crate) fn override_stdout(io: RawHandle, owned: bool) -> io::Result<File> {
override_stdio(STD_OUTPUT_HANDLE, owned)
}
pub(crate) fn override_stderr(io: RawHandle, owned: bool) -> io::Result<File> {
override_stdio(STD_ERROR_HANDLE, owned)
}

pub(crate) fn reset_stdin(old: RawHandle) -> io::Result<()> {
reset_stdio(STD_INPUT_HANDLE, old)
}
pub(crate) fn reset_stdout(old: RawHandle) -> io::Result<()> {
reset_stdio(STD_OUTPUT_HANDLE, old)
}
pub(crate) fn reset_stderr(old: RawHandle) -> io::Result<()> {
reset_stdio(STD_ERROR_HANDLE, old)
}

fn override_stdio(stdio: DWORD, other: RawHandle, owned: bool) -> io::Result<File> {
let original = handle_res(unsafe { GetStdHandle(stdio) })?;

let other = if owned {
other
} else {
// If it isn't owned, duplicate the handle to prevent closing the original handle from
// closing the stdio handle.

let process = unsafe { GetCurrentProcess() };

let mut handle_information = 0;
io_res(unsafe { GetHandleInformation(other, &mut handle_information as *mut DWORD) })?;
let inherit_handle = if handle_information & HANDLE_FLAG_INHERIT == HANDLE_FLAG_INHERIT { TRUE } else { FALSE };

let mut target = ptr::null_mut();
io_res(unsafe {
DuplicateHandle(
process,
other,
process,
&mut target as *mut RawHandle,
0, // ignored
inherit_handle,
DUPLICATE_SAME_ACCESS,
)
})?;

target
};

io_res(unsafe { SetStdHandle(stdio, other) })?;

Ok(unsafe { File::from_raw_handle(original) })
}
fn reset_stdio(stdio: DWORD, other: RawHandle) -> io::Result<()> {
let current = handle_res(unsafe { GetStdHandle(stdio) })?;

io_res(unsafe { SetStdHandle(stdio, other) })?;

io_res(unsafe { CloseHandle(current) })?;

Ok(())
}

fn io_res(res: BOOL) -> io::Result<()> {
if res == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}

fn handle_res(res: RawHandle) -> io::Result<RawHandle> {
if res == INVALID_HANDLE_VALUE {
Err(io::Error::last_os_error())
} else {
Ok(res)
}
}

0 comments on commit dfc92ca

Please sign in to comment.