fix(core): handle blocking stdin (#21672)
This commit is contained in:
parent
0f1207e825
commit
1dd363706b
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1536,6 +1536,7 @@ dependencies = [
|
|||||||
"ignore",
|
"ignore",
|
||||||
"ignore-files 2.0.0",
|
"ignore-files 2.0.0",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
"mio",
|
||||||
"napi",
|
"napi",
|
||||||
"napi-build",
|
"napi-build",
|
||||||
"napi-derive",
|
"napi-derive",
|
||||||
|
|||||||
@ -47,6 +47,9 @@ crossterm = "0.27.0"
|
|||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi = { version = "0.3", features = ["fileapi"] }
|
winapi = { version = "0.3", features = ["fileapi"] }
|
||||||
|
|
||||||
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
|
mio = "0.8"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ['cdylib']
|
crate-type = ['cdylib']
|
||||||
|
|
||||||
|
|||||||
@ -14,8 +14,12 @@ use napi::{Env, JsFunction};
|
|||||||
use portable_pty::{ChildKiller, CommandBuilder, NativePtySystem, PtySize, PtySystem};
|
use portable_pty::{ChildKiller, CommandBuilder, NativePtySystem, PtySize, PtySystem};
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
|
#[cfg_attr(windows, path = "command/windows.rs")]
|
||||||
|
#[cfg_attr(not(windows), path = "command/unix.rs")]
|
||||||
|
mod os;
|
||||||
|
|
||||||
fn command_builder() -> CommandBuilder {
|
fn command_builder() -> CommandBuilder {
|
||||||
if cfg!(target_os = "windows") {
|
if cfg!(windows) {
|
||||||
let comspec = std::env::var("COMSPEC");
|
let comspec = std::env::var("COMSPEC");
|
||||||
let shell = comspec
|
let shell = comspec
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -191,12 +195,13 @@ pub fn run_command(
|
|||||||
|
|
||||||
// Stdin -> pty stdin
|
// Stdin -> pty stdin
|
||||||
if std::io::stdout().is_tty() {
|
if std::io::stdout().is_tty() {
|
||||||
|
enable_raw_mode().expect("Failed to enter raw terminal mode");
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
enable_raw_mode().expect("Failed to enter raw terminal mode");
|
|
||||||
let mut stdin = std::io::stdin();
|
let mut stdin = std::io::stdin();
|
||||||
#[allow(clippy::redundant_pattern_matching)]
|
|
||||||
// ignore errors that come from copying the stream
|
if let Err(e) = os::write_to_pty(&mut stdin, &mut writer) {
|
||||||
if let Ok(_) = std::io::copy(&mut stdin, &mut writer) {}
|
trace!("Error writing to pty: {:?}", e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +229,7 @@ pub fn nx_fork(
|
|||||||
) -> napi::Result<ChildProcess> {
|
) -> napi::Result<ChildProcess> {
|
||||||
let command = format!(
|
let command = format!(
|
||||||
"node {} {} {}",
|
"node {} {} {}",
|
||||||
handle_path_space(fork_script),
|
os::handle_path_space(fork_script),
|
||||||
psuedo_ipc_path,
|
psuedo_ipc_path,
|
||||||
id
|
id
|
||||||
);
|
);
|
||||||
@ -232,37 +237,3 @@ pub fn nx_fork(
|
|||||||
trace!("nx_fork command: {}", &command);
|
trace!("nx_fork command: {}", &command);
|
||||||
run_command(command, command_dir, js_env, Some(quiet))
|
run_command(command, command_dir, js_env, Some(quiet))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
pub fn handle_path_space(path: String) -> String {
|
|
||||||
use std::os::windows::ffi::OsStrExt;
|
|
||||||
use std::{ffi::OsString, os::windows::ffi::OsStringExt};
|
|
||||||
|
|
||||||
use winapi::um::fileapi::GetShortPathNameW;
|
|
||||||
let wide: Vec<u16> = std::path::PathBuf::from(&path)
|
|
||||||
.as_os_str()
|
|
||||||
.encode_wide()
|
|
||||||
.chain(Some(0))
|
|
||||||
.collect();
|
|
||||||
let mut buffer: Vec<u16> = vec![0; wide.len() * 2];
|
|
||||||
let result =
|
|
||||||
unsafe { GetShortPathNameW(wide.as_ptr(), buffer.as_mut_ptr(), buffer.len() as u32) };
|
|
||||||
if result == 0 {
|
|
||||||
path
|
|
||||||
} else {
|
|
||||||
let len = buffer.iter().position(|&x| x == 0).unwrap();
|
|
||||||
let short_path: String = OsString::from_wide(&buffer[..len])
|
|
||||||
.to_string_lossy()
|
|
||||||
.into_owned();
|
|
||||||
short_path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
fn handle_path_space(path: String) -> String {
|
|
||||||
if path.contains(' ') {
|
|
||||||
format!("'{}'", path)
|
|
||||||
} else {
|
|
||||||
path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
67
packages/nx/src/native/command/unix.rs
Normal file
67
packages/nx/src/native/command/unix.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use std::{
|
||||||
|
io::{Read, Stdin, Write},
|
||||||
|
os::fd::AsRawFd,
|
||||||
|
};
|
||||||
|
|
||||||
|
use mio::{unix::SourceFd, Events};
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
|
pub fn handle_path_space(path: String) -> String {
|
||||||
|
if path.contains(' ') {
|
||||||
|
format!("'{}'", path)
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_to_pty(stdin: &mut Stdin, writer: &mut impl Write) -> anyhow::Result<()> {
|
||||||
|
let mut buffer = [0; 1024];
|
||||||
|
|
||||||
|
let mut poll = mio::Poll::new()?;
|
||||||
|
let mut events = Events::with_capacity(3);
|
||||||
|
|
||||||
|
// Register stdin to the poll instance
|
||||||
|
let token = mio::Token(0);
|
||||||
|
poll.registry()
|
||||||
|
.register(
|
||||||
|
&mut SourceFd(&stdin.as_raw_fd()),
|
||||||
|
token,
|
||||||
|
mio::Interest::READABLE,
|
||||||
|
)
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to register stdin to poll: {}", e))?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Poll for events
|
||||||
|
if let Err(e) = poll.poll(&mut events, None) {
|
||||||
|
if e.kind() == std::io::ErrorKind::Interrupted {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
trace!("poll error: {:?}", e);
|
||||||
|
anyhow::bail!("Failed to poll for events: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
for event in events.iter().map(|x| x.token()) {
|
||||||
|
match event {
|
||||||
|
mio::Token(0) => {
|
||||||
|
// Read data from stdin
|
||||||
|
loop {
|
||||||
|
match stdin.read(&mut buffer) {
|
||||||
|
Ok(n) => {
|
||||||
|
writer.write_all(&buffer[..n])?;
|
||||||
|
writer.flush()?;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if e.kind() == std::io::ErrorKind::WouldBlock {
|
||||||
|
break;
|
||||||
|
} else if e.kind() == std::io::ErrorKind::Interrupted {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
packages/nx/src/native/command/windows.rs
Normal file
31
packages/nx/src/native/command/windows.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use std::io::{Stdin, Write};
|
||||||
|
use std::os::windows::ffi::OsStrExt;
|
||||||
|
use std::{ffi::OsString, os::windows::ffi::OsStringExt};
|
||||||
|
|
||||||
|
use winapi::um::fileapi::GetShortPathNameW;
|
||||||
|
|
||||||
|
pub fn handle_path_space(path: String) -> String {
|
||||||
|
let wide: Vec<u16> = std::path::PathBuf::from(&path)
|
||||||
|
.as_os_str()
|
||||||
|
.encode_wide()
|
||||||
|
.chain(Some(0))
|
||||||
|
.collect();
|
||||||
|
let mut buffer: Vec<u16> = vec![0; wide.len() * 2];
|
||||||
|
let result =
|
||||||
|
unsafe { GetShortPathNameW(wide.as_ptr(), buffer.as_mut_ptr(), buffer.len() as u32) };
|
||||||
|
if result == 0 {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
let len = buffer.iter().position(|&x| x == 0).unwrap();
|
||||||
|
let short_path: String = OsString::from_wide(&buffer[..len])
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
short_path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_to_pty(stdin: &mut Stdin, writer: &mut impl Write) -> anyhow::Result<()> {
|
||||||
|
std::io::copy(stdin, writer)
|
||||||
|
.map_err(|e| anyhow::anyhow!(e))
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user