Skip to main content

xtask_todo_lib/devshell/sandbox/
run.rs

1//! Run host binaries against an exported workspace and sync back.
2
3use std::path::Path;
4use std::process::Command;
5
6use super::super::vfs::Vfs;
7use super::elf::restore_execute_bits_for_build_artifacts;
8use super::error::SandboxError;
9use super::export::export_vfs_to_temp_dir;
10use super::paths::find_in_path;
11use super::sync::{host_export_root, sync_host_dir_to_vfs};
12
13/// Run a subprocess with cwd set to `export_dir`.
14///
15/// Child inherits the process stdin/stdout/stderr (redirects for this builtin can be added later).
16/// Returns the process exit status; the caller should then sync back and remove the dir.
17///
18/// # Errors
19/// Returns `SandboxError::ExportFailed` if spawning the process fails (e.g. program not found).
20pub fn run_in_export_dir<P: AsRef<Path>>(
21    export_dir: &Path,
22    program: P,
23    args: &[String],
24) -> Result<std::process::ExitStatus, SandboxError> {
25    let mut cmd = Command::new(program.as_ref());
26    cmd.args(args)
27        .current_dir(export_dir)
28        .stdin(std::process::Stdio::inherit())
29        .stdout(std::process::Stdio::inherit())
30        .stderr(std::process::Stdio::inherit());
31
32    #[cfg(target_os = "linux")]
33    if super::linux_mount::linux_mount_namespace_enabled() {
34        super::linux_mount::apply_linux_private_mount_namespace(&mut cmd);
35    }
36
37    let mut child = cmd.spawn().map_err(SandboxError::ExportFailed)?;
38    child.wait().map_err(SandboxError::ExportFailed)
39}
40
41/// Export VFS subtree at `vfs_path`, run `program` with `args` in that dir, sync changes back, then cleanup.
42/// Returns the child's exit status. Caller should check `status.success()`.
43///
44/// # Isolation
45/// See parent module docs: temp export + host `PATH` binary; optional Linux mount namespace via
46/// `DEVSHELL_RUST_MOUNT_NAMESPACE` (no container engine).
47///
48/// # Errors
49/// Returns `SandboxError` if binary not in PATH (`ExportFailed` with a message), export fails, spawn fails, or sync fails.
50pub fn run_rust_tool(
51    vfs: &mut Vfs,
52    vfs_path: &str,
53    program: &str,
54    args: &[String],
55) -> Result<std::process::ExitStatus, SandboxError> {
56    let export_dir = export_vfs_to_temp_dir(vfs, vfs_path)?;
57    let work_dir = host_export_root(&export_dir, vfs_path);
58    restore_execute_bits_for_build_artifacts(&work_dir)?;
59
60    let program_path = find_in_path(program).ok_or_else(|| {
61        SandboxError::ExportFailed(std::io::Error::new(
62            std::io::ErrorKind::NotFound,
63            format!("{program} not found in PATH"),
64        ))
65    })?;
66    let status = run_in_export_dir(&work_dir, &program_path, args);
67
68    let sync_result = sync_host_dir_to_vfs(&export_dir, vfs_path, vfs);
69    let _ = std::fs::remove_dir_all(&export_dir);
70
71    sync_result?;
72    status
73}