Skip to content

Instantly share code, notes, and snippets.

@fengmk2
Last active October 29, 2025 09:47
Show Gist options
  • Select an option

  • Save fengmk2/323b5e15c13d7c9d0c95c6c3cf34730d to your computer and use it in GitHub Desktop.

Select an option

Save fengmk2/323b5e15c13d7c9d0c95c6c3cf34730d to your computer and use it in GitHub Desktop.
libuv may mark stdin/stdout/stderr as close-on-exec, which interferes with Rust's subprocess spawning.
#[cfg(unix)]
fn fix_stdio_streams() {
// libuv may mark stdin/stdout/stderr as close-on-exec, which interferes with Rust's subprocess spawning.
// As a workaround, we clear the FD_CLOEXEC flag on these file descriptors to prevent them
// from being closed when spawning child processes.
//
// For details see https://github.com/libuv/libuv/issues/2062
// Fixed by reference from https://github.com/electron/electron/pull/15555
use std::os::fd::BorrowedFd;
use nix::{
fcntl::{FcntlArg, FdFlag, fcntl},
libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO},
};
// Safe function to clear FD_CLOEXEC flag
fn clear_cloexec(fd: BorrowedFd<'_>) {
// Borrow RawFd as BorrowedFd to satisfy AsFd constraint
if let Ok(flags) = fcntl(fd, FcntlArg::F_GETFD) {
let mut fd_flags = FdFlag::from_bits_retain(flags);
if fd_flags.contains(FdFlag::FD_CLOEXEC) {
fd_flags.remove(FdFlag::FD_CLOEXEC);
// Ignore errors: some fd may be closed
let _ = fcntl(fd, FcntlArg::F_SETFD(fd_flags));
}
}
}
// Clear FD_CLOEXEC on stdin, stdout, stderr
clear_cloexec(unsafe { BorrowedFd::borrow_raw(STDIN_FILENO) });
clear_cloexec(unsafe { BorrowedFd::borrow_raw(STDOUT_FILENO) });
clear_cloexec(unsafe { BorrowedFd::borrow_raw(STDERR_FILENO) });
}
// TODO: should move to vite-command crate later
/// Run a command with the given bin name, arguments, environment variables, and current working directory.
///
/// # Arguments
///
/// * `bin_name`: The name of the binary to run.
/// * `args`: The arguments to pass to the binary.
/// * `envs`: The custom environment variables to set for the command, will be merged with the system environment variables.
/// * `cwd`: The current working directory for the command.
///
/// # Returns
///
/// Returns the exit status of the command.
pub(crate) async fn run_command(
bin_name: &str,
args: &[String],
envs: &HashMap<String, String>,
cwd: impl AsRef<AbsolutePath>,
) -> Result<ExitStatus, Error> {
println!("Running: {} {}", bin_name, args.join(" "));
let mut cmd = Command::new(bin_name);
cmd.args(args)
.envs(envs)
.current_dir(cwd.as_ref())
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit());
// fix stdio streams on unix
#[cfg(unix)]
unsafe {
cmd.pre_exec(|| {
fix_stdio_streams();
Ok(())
});
}
let status = cmd.status().await?;
Ok(status)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment