mirror of
https://github.com/openai/codex.git
synced 2026-10-19 17:28:46 +00:00
## Why Passing extra descriptors to a PTY previously selected behavior intended for inherited escalation sockets: no EOF on stdin close, different signal exit statuses, and no default `SHELL`. Launch attachments need ordinary PTY behavior and must survive execution even when marked `FD_CLOEXEC`. ## What changed - Add `ChildFds::Attached` alongside `ChildFds::Inherited` to distinguish launch attachments from legacy inherited descriptors. - Preserve ordinary EOF, default `SHELL`, and signal exit status behavior for PTY attachments. - Allow explicitly supplied close-on-exec descriptors through pipe and PTY launches without changing the parent's descriptor flags, using child-side flag updates and macOS spawn inheritance actions. - Update existing callers to use `ChildFds::Inherited`. ## Testing Add Unix coverage for attachment accessibility, unchanged parent descriptor flags, default `SHELL`, and signal exit statuses. Extend the PTY stdin-draining test to cover attachments and EOF delivery. GitOrigin-RevId: 864aedc6c5717d8b43c9d71057c9f54409834509
143 lines
5.3 KiB
Rust
143 lines
5.3 KiB
Rust
use std::collections::HashMap;
|
|
use std::path::Path;
|
|
|
|
use anyhow::Context;
|
|
use anyhow::Result;
|
|
use codex_protocol::config_types::WindowsSandboxLevel;
|
|
use codex_protocol::models::PermissionProfile;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
use codex_utils_pty::SpawnedProcess;
|
|
use codex_utils_pty::TerminalSize;
|
|
|
|
use crate::SandboxType;
|
|
use crate::WindowsSandboxFilesystemOverrides;
|
|
use crate::WindowsSandboxProxySettingsMode;
|
|
use crate::terminal_queries::respond_to_terminal_queries;
|
|
|
|
/// Windows-specific inputs for an executor-native process spawn.
|
|
// TODO(anp): Reconcile the Windows backend copy with the supplied sandbox
|
|
// context (TurnEnvironment::sandbox_context for turns), preserving this launch snapshot.
|
|
pub struct WindowsSandboxSpawnRequest<'a> {
|
|
pub permission_profile: &'a PermissionProfile,
|
|
pub workspace_roots: &'a [AbsolutePathBuf],
|
|
pub windows_sandbox_level: WindowsSandboxLevel,
|
|
pub proxy_enforced: bool,
|
|
pub network_proxy_restricting_sid: Option<&'a str>,
|
|
pub proxy_settings_mode: WindowsSandboxProxySettingsMode,
|
|
pub filesystem_overrides: Option<&'a WindowsSandboxFilesystemOverrides>,
|
|
}
|
|
|
|
/// Executor-native process launch request shared by local and exec-server execution.
|
|
pub struct SpawnRequest<'a> {
|
|
pub command: &'a [String],
|
|
pub cwd: &'a Path,
|
|
pub env: &'a HashMap<String, String>,
|
|
pub arg0: &'a Option<String>,
|
|
pub sandbox: SandboxType,
|
|
pub windows_sandbox: Option<WindowsSandboxSpawnRequest<'a>>,
|
|
pub tty: bool,
|
|
pub stdin_open: bool,
|
|
pub inherited_fds: codex_utils_pty::ChildFds<'a>,
|
|
}
|
|
|
|
/// Spawn a process using the backend selected by the prepared sandbox request.
|
|
#[tracing::instrument(name = "codex.process.spawn", skip_all)]
|
|
pub async fn spawn_process(request: SpawnRequest<'_>) -> Result<SpawnedProcess> {
|
|
let tty = request.tty;
|
|
let finish_spawn = |spawned| {
|
|
if tty {
|
|
respond_to_terminal_queries(spawned)
|
|
} else {
|
|
spawned
|
|
}
|
|
};
|
|
|
|
if request.sandbox == SandboxType::WindowsRestrictedToken {
|
|
#[cfg(target_os = "windows")]
|
|
{
|
|
let windows = request
|
|
.windows_sandbox
|
|
.context("missing Windows sandbox spawn request")?;
|
|
let codex_home = codex_utils_home_dir::find_codex_home()
|
|
.context("windows sandbox: failed to resolve codex_home")?;
|
|
let empty_paths = &[];
|
|
let overrides = windows.filesystem_overrides;
|
|
|
|
return codex_windows_sandbox::spawn_windows_sandbox_session_for_level(
|
|
codex_windows_sandbox::WindowsSandboxSessionRequest {
|
|
permission_profile: windows.permission_profile,
|
|
workspace_roots: windows.workspace_roots,
|
|
codex_home: codex_home.as_path(),
|
|
command: request.command.to_vec(),
|
|
cwd: request.cwd,
|
|
env_map: request.env.clone(),
|
|
windows_sandbox_level: windows.windows_sandbox_level,
|
|
proxy_enforced: windows.proxy_enforced,
|
|
network_proxy_restricting_sid: windows
|
|
.network_proxy_restricting_sid
|
|
.map(str::to_owned),
|
|
proxy_settings_mode: windows.proxy_settings_mode,
|
|
timeout_ms: None,
|
|
read_roots_override: overrides
|
|
.and_then(|value| value.read_roots_override.as_deref()),
|
|
read_roots_include_platform_defaults: overrides
|
|
.is_some_and(|value| value.read_roots_include_platform_defaults),
|
|
write_roots_override: overrides
|
|
.and_then(|value| value.write_roots_override.as_deref()),
|
|
deny_read_paths_override: overrides.map_or(empty_paths, |value| {
|
|
value.additional_deny_read_paths.as_slice()
|
|
}),
|
|
deny_write_paths_override: overrides.map_or(empty_paths, |value| {
|
|
value.additional_deny_write_paths.as_slice()
|
|
}),
|
|
tty: request.tty,
|
|
stdin_open: request.stdin_open,
|
|
},
|
|
)
|
|
.await
|
|
.map(finish_spawn);
|
|
}
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
anyhow::bail!("Windows sandbox process spawn is unavailable on this platform");
|
|
}
|
|
|
|
let (program, args) = request
|
|
.command
|
|
.split_first()
|
|
.context("missing program for process spawn")?;
|
|
let spawned = if tty {
|
|
codex_utils_pty::pty::spawn_process(
|
|
program,
|
|
args,
|
|
request.cwd,
|
|
request.env,
|
|
request.arg0,
|
|
TerminalSize::default(),
|
|
request.inherited_fds,
|
|
)
|
|
.await
|
|
} else if request.stdin_open {
|
|
codex_utils_pty::pipe::spawn_process(
|
|
program,
|
|
args,
|
|
request.cwd,
|
|
request.env,
|
|
request.arg0,
|
|
request.inherited_fds.as_slice(),
|
|
)
|
|
.await
|
|
} else {
|
|
codex_utils_pty::pipe::spawn_process_no_stdin(
|
|
program,
|
|
args,
|
|
request.cwd,
|
|
request.env,
|
|
request.arg0,
|
|
request.inherited_fds.as_slice(),
|
|
)
|
|
.await
|
|
};
|
|
spawned.map(finish_spawn)
|
|
}
|