Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 42 additions & 6 deletions codex-rs/windows-sandbox-rs/src/provisioning_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ use windows_sys::Win32::Foundation::ERROR_NO_DATA;
use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY;
use windows_sys::Win32::Foundation::ERROR_PIPE_NOT_CONNECTED;
use windows_sys::Win32::Foundation::ERROR_SEM_TIMEOUT;
use windows_sys::Win32::Foundation::ERROR_SERVICE_DOES_NOT_EXIST;
use windows_sys::Win32::Foundation::ERROR_SERVICE_MARKED_FOR_DELETE;
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::Security::SC_HANDLE;
use windows_sys::Win32::Storage::FileSystem::SECURITY_IMPERSONATION;
Expand All @@ -35,6 +37,7 @@ use windows_sys::Win32::System::Pipes::WaitNamedPipeW;
use windows_sys::Win32::System::Services;

const PROVISIONING_TIMEOUT: Duration = Duration::from_secs(120);
const SERVICE_STARTUP_TIMEOUT: Duration = Duration::from_secs(5);

mod group_change;
mod refresh_retry;
Expand Down Expand Up @@ -256,6 +259,13 @@ fn exchange_request(
) -> anyhow::Result<crate::SandboxProvisioningResponse> {
verify_server(pipe.as_raw_handle() as HANDLE)
.context("authenticate provisioning pipe server")?;
if Instant::now() >= deadline {
return Err(io::Error::new(
io::ErrorKind::TimedOut,
"sandbox provisioning deadline expired before sending request",
)
.into());
}
crate::write_provisioning_frame(&mut *pipe, request)
.context("send sandbox provisioning request")?;
read_response(pipe, deadline)
Expand Down Expand Up @@ -286,6 +296,9 @@ fn read_response(
}

fn connect(deadline: Instant) -> anyhow::Result<Option<File>> {
// Startup should not hold up the desktop's readiness check for the full
// provisioning timeout. Keep the caller's original deadline for real work.
let startup_deadline = deadline.min(Instant::now() + SERVICE_STARTUP_TIMEOUT);
let pipe_name = crate::windows_sandbox_service_pipe_name()?;
let open_pipe = || {
OpenOptions::new()
Expand All @@ -297,6 +310,31 @@ fn connect(deadline: Instant) -> anyhow::Result<Option<File>> {

let pipe_name = crate::to_wide(&pipe_name);
loop {
match query_service_status() {
Ok(status) if status.dwCurrentState == Services::SERVICE_START_PENDING => {
let remaining = startup_deadline.saturating_duration_since(Instant::now());
if remaining.is_zero() {
return Ok(None);
}
std::thread::sleep(remaining.min(Duration::from_millis(25)));
continue;
}
Ok(status) if status.dwCurrentState == Services::SERVICE_RUNNING => {}
Ok(_) => return Ok(None),
Err(error)
if error.downcast_ref::<io::Error>().is_some_and(|error| {
matches!(
error.raw_os_error(),
Some(code)
if code == ERROR_SERVICE_DOES_NOT_EXIST as i32
|| code == ERROR_SERVICE_MARKED_FOR_DELETE as i32
)
}) =>
{
return Ok(None);
}
Err(error) => return Err(error),
}
match open_pipe() {
Ok(pipe) => return Ok(Some(pipe)),
Err(error) if error.raw_os_error() == Some(ERROR_FILE_NOT_FOUND as i32) => {
Expand All @@ -312,12 +350,10 @@ fn connect(deadline: Instant) -> anyhow::Result<Option<File>> {
.max(1);
if unsafe { WaitNamedPipeW(pipe_name.as_ptr(), wait_ms) } == 0 {
let error = io::Error::last_os_error();
if matches!(
error.raw_os_error(),
Some(code)
if code == ERROR_FILE_NOT_FOUND as i32
|| code == ERROR_SEM_TIMEOUT as i32
) {
if error.raw_os_error() == Some(ERROR_FILE_NOT_FOUND as i32) {
continue;
}
if error.raw_os_error() == Some(ERROR_SEM_TIMEOUT as i32) {
return Ok(None);
}
return Err(error).context("wait for sandbox provisioning pipe");
Expand Down
Loading