windows-sandbox-rs/src/unified_exec/backends/windows_common.rs
192 lines
use crate::ipc_framed::EmptyPayload;use crate::ipc_framed::FramedMessage;use crate::ipc_framed::IPC_PROTOCOL_VERSION;use crate::ipc_framed::Message;use crate::ipc_framed::OutputStream;use crate::ipc_framed::ResizePayload;use crate::ipc_framed::StdinPayload;use crate::ipc_framed::decode_bytes;use crate::ipc_framed::encode_bytes;use anyhow::Result;use codex_utils_pty::ProcessDriver;use codex_utils_pty::SpawnedProcess;use codex_utils_pty::TerminalSize;use codex_utils_pty::spawn_from_driver;use std::fs::File;use tokio::sync::broadcast;use tokio::sync::mpsc;use tokio::sync::oneshot;pub(crate) fn finish_driver_spawn(driver: ProcessDriver, stdin_open: bool) -> SpawnedProcess { let spawned = spawn_from_driver(driver); if !stdin_open { spawned.session.close_stdin(); } spawned}pub(crate) fn normalize_windows_tty_input(bytes: &[u8], previous_was_cr: &mut bool) -> Vec<u8> { let mut normalized = Vec::with_capacity(bytes.len()); for &byte in bytes { if byte == b'\n' { if !*previous_was_cr { normalized.push(b'\r'); } normalized.push(b'\n'); *previous_was_cr = false; } else { normalized.push(byte); *previous_was_cr = byte == b'\r'; } } normalized}pub(crate) fn start_runner_pipe_writer( mut pipe_write: File,) -> std::sync::mpsc::Sender<FramedMessage> { let (outbound_tx, outbound_rx) = std::sync::mpsc::channel::<FramedMessage>(); tokio::task::spawn_blocking(move || { while let Ok(msg) = outbound_rx.recv() { if crate::ipc_framed::write_frame(&mut pipe_write, &msg).is_err() { break; } } }); outbound_tx}pub(crate) fn start_runner_stdin_writer( mut writer_rx: mpsc::Receiver<Vec<u8>>, outbound_tx: std::sync::mpsc::Sender<FramedMessage>, normalize_newlines: bool, stdin_open: bool,) -> tokio::task::JoinHandle<()> { tokio::task::spawn_blocking(move || { let mut previous_was_cr = false; while let Some(bytes) = writer_rx.blocking_recv() { let bytes = if normalize_newlines { normalize_windows_tty_input(&bytes, &mut previous_was_cr) } else { bytes }; let msg = FramedMessage { version: IPC_PROTOCOL_VERSION, message: Message::Stdin { payload: StdinPayload { data_b64: encode_bytes(&bytes), }, }, }; if outbound_tx.send(msg).is_err() { break; } } if stdin_open { let _ = outbound_tx.send(FramedMessage { version: IPC_PROTOCOL_VERSION, message: Message::CloseStdin { payload: EmptyPayload::default(), }, }); } })}pub(crate) fn start_runner_stdout_reader( mut pipe_read: File, stdout_tx: broadcast::Sender<Vec<u8>>, stderr_tx: Option<broadcast::Sender<Vec<u8>>>, exit_tx: oneshot::Sender<i32>,) { std::thread::spawn(move || { loop { let msg = match crate::ipc_framed::read_frame(&mut pipe_read) { Ok(Some(v)) => v, Ok(None) => { send_runner_error( "runner pipe closed before exit", &stdout_tx, stderr_tx.as_ref(), ); let _ = exit_tx.send(-1); break; } Err(err) => { send_runner_error( &format!("runner read failed: {err}"), &stdout_tx, stderr_tx.as_ref(), ); let _ = exit_tx.send(-1); break; } }; match msg.message { Message::Output { payload } => { if let Ok(data) = decode_bytes(&payload.data_b64) { match payload.stream { OutputStream::Stdout => { let _ = stdout_tx.send(data); } OutputStream::Stderr => { if let Some(stderr_tx) = stderr_tx.as_ref() { let _ = stderr_tx.send(data); } else { let _ = stdout_tx.send(data); } } } } } Message::Exit { payload } => { let _ = exit_tx.send(payload.exit_code); break; } Message::Error { payload } => { send_runner_error(&payload.message, &stdout_tx, stderr_tx.as_ref()); let _ = exit_tx.send(-1); break; } Message::SpawnReady { .. } | Message::Stdin { .. } | Message::CloseStdin { .. } | Message::Resize { .. } | Message::SpawnRequest { .. } | Message::Terminate { .. } => {} } } });}pub(crate) fn make_runner_resizer( outbound_tx: std::sync::mpsc::Sender<FramedMessage>,) -> Box<dyn FnMut(TerminalSize) -> Result<()> + Send> { Box::new(move |size: TerminalSize| { outbound_tx .send(FramedMessage { version: IPC_PROTOCOL_VERSION, message: Message::Resize { payload: ResizePayload { rows: size.rows, cols: size.cols, }, }, }) .map_err(|_| anyhow::anyhow!("runner resize pipe closed")) })}fn send_runner_error( message: &str, stdout_tx: &broadcast::Sender<Vec<u8>>, stderr_tx: Option<&broadcast::Sender<Vec<u8>>>,) { let formatted = format!("runner error: {message}\n").into_bytes(); if let Some(stderr_tx) = stderr_tx { let _ = stderr_tx.send(formatted); } else { let _ = stdout_tx.send(formatted); }}