Skip to content
Merged
Show file tree
Hide file tree
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
15 changes: 5 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions litebox_runner_windows_on_linux_userland/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2024"
anyhow = "1.0.97"
clap = { version = "4.5.33", features = ["derive"] }
litebox = { version = "0.1.0", path = "../litebox" }
litebox_broker_local_userland = { version = "0.1.0", path = "../litebox_broker_local_userland" }
litebox_common_linux = { version = "0.1.0", path = "../litebox_common_linux" }
litebox_platform_linux_userland = { version = "0.1.0", path = "../litebox_platform_linux_userland" }
litebox_shim_windows = { version = "0.1.0", path = "../litebox_shim_windows" }
Expand Down
87 changes: 51 additions & 36 deletions litebox_runner_windows_on_linux_userland/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ extern crate alloc;

use anyhow::{Context as _, Result};
use clap::Parser;
use litebox_broker_local_userland as broker;
use litebox_platform_linux_userland::LinuxUserland;
use std::path::PathBuf;

/// Run Windows PE programs with LiteBox on unmodified Linux.
///
/// The program binary and any initial filesystem contents must be provided inside a tar archive via
/// `--initial-files`. The program path refers to a path inside the tar archive.
/// The program binary and runtime files must be available in the broker-owned file system.
#[derive(Parser, Debug)]
pub struct CliArgs {
/// The program and arguments passed to it (e.g., `/app/program.exe --help`).
///
/// The program path refers to a path inside the tar archive provided via `--initial-files`.
/// The program path refers to a path inside the broker-owned file system.
#[arg(required = true, trailing_var_arg = true, value_hint = clap::ValueHint::CommandWithArguments)]
pub program_and_arguments: Vec<String>,
/// Environment variables passed to the program (`K=V` pairs; can be invoked multiple times).
Expand All @@ -32,18 +32,33 @@ pub struct CliArgs {
/// Allow using unstable options.
#[arg(short = 'Z', long = "unstable")]
pub unstable: bool,
/// Tar archive containing the program and its runtime files.
#[arg(long = "initial-files", value_name = "PATH_TO_TAR", value_hint = clap::ValueHint::FilePath)]
pub initial_files: PathBuf,
/// Broker-supplied Unix socket path for the local control channel.
#[arg(
long = "broker-control-channel",
value_name = "PATH",
value_hint = clap::ValueHint::FilePath,
hide = true,
requires = "unstable",
help_heading = "Unstable Options"
)]
pub broker_control_channel: Option<PathBuf>,
/// Broker-supplied proxy URL for managed HTTP and HTTPS egress.
#[arg(
long = "broker-proxy-url",
value_name = "URL",
hide = true,
requires = "broker_control_channel",
help_heading = "Unstable Options"
)]
pub broker_proxy_url: Option<String>,
}

/// Run Windows PE programs with LiteBox on unmodified Linux.
///
/// # Panics
///
/// Panics if the initial in-memory file system fails to create `/tmp` - those
/// operations cannot fail against a freshly-constructed file system.
pub fn run(cli_args: CliArgs) -> Result<()> {
if cli_args.broker_proxy_url.is_some() {
anyhow::bail!("managed broker proxy is not supported by this runner");
}

tracing_subscriber::fmt()
.with_timer(tracing_subscriber::fmt::time::uptime())
.with_level(true)
Expand All @@ -60,37 +75,37 @@ pub fn run(cli_args: CliArgs) -> Result<()> {
);
}

let tar_file = &cli_args.initial_files;
if tar_file.extension().and_then(|x| x.to_str()) != Some("tar") {
anyhow::bail!("Expected a .tar file, found {}", tar_file.display());
}
let tar_data = std::fs::read(tar_file)
.with_context(|| format!("Could not read tar file at {}", tar_file.display()))?;

let platform = LinuxUserland::new();
let shim_builder = litebox_shim_windows::WindowsShimBuilder::new(platform);
let control_socket = cli_args
.broker_control_channel
.as_deref()
.context("file operations require --broker-control-channel")?;
let broker::BrokerConnection {
local,
notifications,
coordinator,
positional_io_fds: _broker_positional_io_fds,
shutdown_fd: _broker_shutdown_fd,
} = litebox_platform_linux_userland::with_guest_signals_blocked(|| {
broker::connect(control_socket)
})?;
let litebox = litebox::LiteBox::new_with_broker_local(platform, local);
coordinator.install_dispatch(litebox.broker_failure_dispatcher());
litebox_platform_linux_userland::with_guest_signals_blocked(|| {
broker::start_notification_receiver(
notifications,
coordinator,
litebox.broker_notification_dispatcher(),
)
})?;
let shim_builder =
litebox_shim_windows::WindowsShimBuilder::new_with_litebox(platform, litebox);

let (program_path, program_args) = cli_args
.program_and_arguments
.split_first()
.context("program path missing - clap should have required at least one argument")?;

let initial_file_system = {
let in_mem = litebox::fs::in_mem::InMem::new_initialized([(
"/tmp",
litebox::fs::in_mem::InitialNode::Directory {
mode: litebox::fs::Mode::RWXU | litebox::fs::Mode::RWXG | litebox::fs::Mode::RWXO,
owner: litebox::fs::UserInfo {
user: 1000,
group: 1000,
},
},
)]);

shim_builder.default_fs(in_mem, tar_data.into())
};
let initial_file_system = std::sync::Arc::new(initial_file_system);

let shim = shim_builder.build();
let argv = std::iter::once(program_path.as_str())
.chain(program_args.iter().map(String::as_str))
Expand All @@ -113,7 +128,7 @@ pub fn run(cli_args: CliArgs) -> Result<()> {
}

let program = shim
.load_program(initial_file_system, program_path, argv, envp)
.load_program(program_path, argv, envp)
.context("failed to load Windows PE program")?;
// SAFETY: `WindowsShimEntrypoints::init` populates `rip`/`rsp`/`eflags` inside
// `run_thread` before the initial guest thread executes, so the `PtRegs::default()`
Expand Down
2 changes: 1 addition & 1 deletion litebox_runner_windows_userland/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ litebox_common_linux = { version = "0.1.0", path = "../litebox_common_linux" }
litebox_platform_windows_userland = { version = "0.1.0", path = "../litebox_platform_windows_userland" }
litebox_shim_windows = { version = "0.1.0", path = "../litebox_shim_windows" }
litebox_util_log = { version = "0.1.0", path = "../litebox_util_log", features = ["backend_tracing"] }
memmap2 = "0.9.8"

tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }

[dev-dependencies]
Expand Down
88 changes: 17 additions & 71 deletions litebox_runner_windows_userland/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,15 @@ use anyhow::{Context as _, Result};
use clap::Parser;
use litebox_broker_local_userland as broker;
use litebox_platform_windows_userland::{GuestTlsMode, WindowsUserland};
use memmap2::Mmap;
use std::path::{Path, PathBuf};

fn mmapped_file(path: impl AsRef<Path>) -> Result<&'static [u8]> {
let path = path.as_ref();
let file = std::fs::File::open(path)
.with_context(|| format!("Could not open tar file at {}", path.display()))?;
let data = {
// SAFETY: The runner maps the input read-only and does not modify it. The caller must ensure
// the tar file is not modified externally while the guest is running.
//
// Leak the mapping so the borrowed tar data remains valid for the process-lifetime file
// system.
Box::leak(Box::new(unsafe { Mmap::map(&file) }.with_context(
|| format!("Could not map tar file at {}", path.display()),
)?))
};
Ok(data)
}

/// Runs a Windows PE program with LiteBox on unmodified Windows and returns its exit code.
///
/// The program binary and any initial filesystem contents must be provided inside a tar archive via
/// `--initial-files`. The program path refers to a path inside the tar archive.
/// The program binary and runtime files must be available in the broker-owned file system.
#[derive(Parser, Debug)]
pub struct CliArgs {
/// The program and arguments passed to it (e.g., `/app/program.exe --help`).
///
/// The program path refers to a path inside the tar archive provided via `--initial-files`.
/// The program path refers to a path inside the broker-owned file system.
#[arg(required = true, trailing_var_arg = true, value_hint = clap::ValueHint::CommandWithArguments)]
pub program_and_arguments: Vec<String>,
/// Environment variables passed to the program (`K=V` pairs; can be invoked multiple times).
Expand All @@ -60,17 +40,9 @@ pub struct CliArgs {
help_heading = "Unstable Options"
)]
pub broker_control_channel: Option<std::ffi::OsString>,
/// Tar archive containing the program and its runtime files.
#[arg(long = "initial-files", value_name = "PATH_TO_TAR", value_hint = clap::ValueHint::FilePath)]
pub initial_files: PathBuf,
}

/// Run Windows PE programs with LiteBox on unmodified Windows.
///
/// # Panics
///
/// Panics if the initial in-memory file system fails to create `/tmp` — those
/// operations cannot fail against a freshly-constructed file system.
pub fn run(cli_args: CliArgs) -> Result<i32> {
tracing_subscriber::fmt()
.with_timer(tracing_subscriber::fmt::time::uptime())
Expand All @@ -82,56 +54,30 @@ pub fn run(cli_args: CliArgs) -> Result<i32> {
)
.init();

let tar_file = &cli_args.initial_files;
if tar_file.extension().and_then(|x| x.to_str()) != Some("tar") {
anyhow::bail!("Expected a .tar file, found {}", tar_file.display());
}
let tar_data = mmapped_file(tar_file)?;

let platform = WindowsUserland::new();
WindowsUserland::set_guest_tls_mode(GuestTlsMode::Windows);
let broker_connection = cli_args
let control_pipe = cli_args
.broker_control_channel
.as_deref()
.map(broker::connect)
.transpose()?;
let shim_builder = if let Some(broker_connection) = broker_connection {
let broker::BrokerConnection {
local,
notifications,
} = broker_connection;
let litebox = litebox::LiteBox::new_with_broker_local(platform, local);
broker::start_notification_receiver(
notifications,
litebox.broker_notification_dispatcher(),
litebox.broker_failure_dispatcher(),
)?;
litebox_shim_windows::WindowsShimBuilder::new_with_litebox(platform, litebox)
} else {
litebox_shim_windows::WindowsShimBuilder::new(platform)
};
.context("file operations require --broker-control-channel")?;
let broker::BrokerConnection {
local,
notifications,
} = broker::connect(control_pipe)?;
let litebox = litebox::LiteBox::new_with_broker_local(platform, local);
broker::start_notification_receiver(
notifications,
litebox.broker_notification_dispatcher(),
litebox.broker_failure_dispatcher(),
)?;
let shim_builder =
litebox_shim_windows::WindowsShimBuilder::new_with_litebox(platform, litebox);

let (program_path, program_args) = cli_args
.program_and_arguments
.split_first()
.context("program path missing — clap should have required at least one argument")?;

let initial_file_system = {
let in_mem = litebox::fs::in_mem::InMem::new_initialized([(
"/tmp",
litebox::fs::in_mem::InitialNode::Directory {
mode: litebox::fs::Mode::RWXU | litebox::fs::Mode::RWXG | litebox::fs::Mode::RWXO,
owner: litebox::fs::UserInfo {
user: 1000,
group: 1000,
},
},
)]);

shim_builder.default_fs(in_mem, tar_data.into())
};
let initial_file_system = std::sync::Arc::new(initial_file_system);

let shim = shim_builder.build();
let argv = std::iter::once(program_path.as_str())
.chain(program_args.iter().map(String::as_str))
Expand All @@ -154,7 +100,7 @@ pub fn run(cli_args: CliArgs) -> Result<i32> {
}

let program = shim
.load_program(initial_file_system, program_path, argv, envp)
.load_program(program_path, argv, envp)
.context("failed to load Windows PE program")?;
// SAFETY: `WindowsShimEntrypoints::init` populates `rip`/`rsp`/`eflags` inside
// `run_thread` before the initial guest thread executes, so the `PtRegs::default()`
Expand Down
Loading
Loading