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:
parent
0519b5499a
commit
ed331542ee
@ -21,5 +21,6 @@ winapi = { version = "0.3", features = [
|
||||
"fileapi",
|
||||
"namedpipeapi",
|
||||
"processthreadsapi",
|
||||
"winsock2"
|
||||
"winsock2",
|
||||
"processenv"
|
||||
]}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user