1
1
mirror of https://github.com/wez/wezterm.git synced 2024-08-17 10:10:23 +03:00

Adding ability to redirect std to filedescriptor but only on windows for now

Adding unix part of std redirect code. Also modifying existing changes based on feedback from the initial PR.

More changes based on feedback from PR.

Non linux unix OS's forced to use dup2 instead of dup3

Avoiding disposal issues with fd.

Small bit of refactoring plus some more ammendments based on more PR feedback.

Non linux compile issue fix.

Adding return

Another fix

Another fix

More amendments

fmt correction

closes: https://github.com/wez/wezterm/pull/816
closes: https://github.com/wez/wezterm/pull/788
closes: https://github.com/wez/wezterm/issues/786
This commit is contained in:
Robin Bernon 2021-05-13 00:05:12 +01:00 committed by Wez Furlong
parent 0519b5499a
commit ed331542ee
4 changed files with 135 additions and 1 deletions

View File

@ -21,5 +21,6 @@ winapi = { version = "0.3", features = [
"fileapi",
"namedpipeapi",
"processthreadsapi",
"winsock2"
"winsock2",
"processenv"
]}

View File

@ -210,6 +210,13 @@ 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
@ -257,6 +264,18 @@ impl FileDescriptor {
pub fn set_non_blocking(&mut self, non_blocking: bool) -> anyhow::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: AsRawFileDescriptor>(
f: &F,
stdio: StdioDescriptor,
) -> anyhow::Result<Self> {
Self::redirect_stdio_impl(f, stdio)
}
}
/// Represents the readable and writable ends of a pair of descriptors

View File

@ -1,6 +1,7 @@
use crate::{
AsRawFileDescriptor, AsRawSocketDescriptor, FileDescriptor, FromRawFileDescriptor,
FromRawSocketDescriptor, IntoRawFileDescriptor, IntoRawSocketDescriptor, OwnedHandle, Pipe,
StdioDescriptor,
};
use anyhow::bail;
use std::os::unix::prelude::*;
@ -122,6 +123,25 @@ impl OwnedHandle {
}
}
fn non_atomic_dup2(fd: RawFd, dest_fd: RawFd) -> anyhow::Result<Self> {
let duped = unsafe { libc::dup2(fd, dest_fd) };
if duped == -1 {
bail!(
"dup2 of fd {} and dest_fd {} failed: {:?}",
fd,
dest_fd,
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<F: AsRawFileDescriptor>(
fd: &F,
@ -146,6 +166,43 @@ impl OwnedHandle {
}
}
#[inline]
pub(crate) unsafe fn dup2_impl<F: AsRawFileDescriptor>(
fd: &F,
dest_fd: RawFd,
) -> anyhow::Result<Self> {
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.
return Self::non_atomic_dup2(fd, dest_fd);
} else {
bail!(
"dup2 of fd {} and dest_fd {} failed: {:?}",
fd,
dest_fd,
err
)
}
} else {
return Ok(OwnedHandle {
handle: duped,
handle_type: (),
});
}
}
}
pub(crate) fn probe_handle_type(_handle: RawFileDescriptor) -> HandleType {
()
}
@ -217,6 +274,33 @@ impl FileDescriptor {
}
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: AsRawFileDescriptor>(f: &F, dest_fd: RawFd) -> anyhow::Result<Self> {
OwnedHandle::dup2_impl(f, dest_fd).map(|handle| Self { handle })
}
pub(crate) fn redirect_stdio_impl<F: AsRawFileDescriptor>(
f: &F,
stdio: StdioDescriptor,
) -> anyhow::Result<Self> {
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();
Ok(std_original)
}
}
impl Pipe {

View File

@ -1,6 +1,7 @@
use crate::{
AsRawFileDescriptor, AsRawSocketDescriptor, FileDescriptor, FromRawFileDescriptor,
FromRawSocketDescriptor, IntoRawFileDescriptor, IntoRawSocketDescriptor, OwnedHandle, Pipe,
StdioDescriptor,
};
use anyhow::bail;
use std::io::{self, Error as IoError};
@ -15,6 +16,7 @@ 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;
@ -35,6 +37,10 @@ pub type RawFileDescriptor = RawHandle;
/// 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,
@ -255,6 +261,30 @@ impl FileDescriptor {
}
Ok(())
}
pub(crate) fn redirect_stdio_impl<F: AsRawFileDescriptor>(
f: &F,
stdio: StdioDescriptor,
) -> anyhow::Result<Self> {
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 {
bail!(
"failed to redirect stdio to file handle: {:?}",
std::io::Error::last_os_error()
);
}
Ok(std_original)
}
}
impl IntoRawHandle for FileDescriptor {