Skip to main content

ExecBuilder

Struct ExecBuilder 

Source
pub struct ExecBuilder { /* private fields */ }
Expand description

Configurable spawn for crate::Vm::exec. Built via crate::Vm::exec_builder or constructed inline by Vm::exec.

let image = Image::from_snapshot("path/to/snapshot")?;
let vm = Vm::start(&image, &VmConfig::new())?;
let mut child = vm.exec_builder()
    .argv(["sh", "-c", "echo hi && exit 7"])
    .env("FOO", "bar")
    .spawn()?;
let status = child.wait()?;
assert_eq!(status.code(), Some(7));

Implementations§

Source§

impl ExecBuilder

Source

pub fn new(exec_path: PathBuf) -> Self

Construct a builder that dials a specific exec-socket path. Most embedders should use crate::Vm::exec_builder instead; this lower-level constructor is for tooling that already has a router daemon running and just wants to dial a known socket (e.g. the supermachine exec CLI).

Source

pub fn argv<I, S>(self, argv: I) -> Self
where I: IntoIterator<Item = S>, S: Into<String>,

The argv to execute in the guest. First element is the program (resolved via PATH inside the guest).

Source

pub fn env(self, key: impl Into<String>, value: impl Into<String>) -> Self

Set an environment variable for the spawned process. Repeatable. The agent merges these on top of its own environment, so PATH, HOME, etc. are inherited unless you explicitly override them.

Source

pub fn cwd(self, path: impl Into<String>) -> Self

Working directory for the spawned process inside the guest.

Source

pub fn tty(self, on: bool) -> Self

Allocate a pseudo-terminal. stdin/stdout pass through the pty master; stderr is empty (the pty merges fd 1 + fd 2). Use this for interactive shells.

Source

pub fn winsize(self, cols: u16, rows: u16) -> Self

Initial terminal size for tty mode. Has no effect without .tty(true). Use ExecChild::resize to change later.

Source

pub fn timeout(self, d: Duration) -> Self

Hard wall-clock timeout. If the process is still running when the deadline fires, the watchdog sends SIGKILL and the resulting ExecOutcome has timed_out = true. Only honored by ExecBuilder::output (the streaming ExecBuilder::spawn path leaves cancellation to the caller, who already has ExecChild::signal).

Reliable: timed_out = true is always set when the deadline fires.

Process group: the agent runs setpgid(0, 0) in the child after fork, then forwards SIGNAL to the entire process group via kill(-pid, sig). Forking children (e.g. sh -c "rustc x.rs && /tmp/x") get killed transitively — no orphaned processes blocking the EXIT frame. Verified to take ~500 ms end-to-end on a 500 ms timeout across sleep, sh -c sleep, busy loops, and shell pipelines.

Source

pub fn stage_file( self, path: impl Into<String>, bytes: impl Into<Vec<u8>>, ) -> Self

Stage bytes at path inside the guest before exec runs. Atomic (write+rename). Folded into the same vsock RPC as the exec request — eliminates the separate Vm::write_file round-trip. Repeatable for multiple files.

Use when your workload starts with a known-small input file (source code, a test config) — replace vm.write_file(p, b)?; vm.exec_builder().argv([…]).output()? with vm.exec_builder().stage_file(p, b).argv([…]).output()?.

Source

pub fn stage_file_mode( self, path: impl Into<String>, bytes: impl Into<Vec<u8>>, mode: u32, ) -> Self

Like Self::stage_file but also sets the file mode.

Source

pub fn chain<I, S>(self, argv: I) -> Self
where I: IntoIterator<Item = S>, S: Into<String>,

Append a second (third, …) argv to run after the previous one succeeds (&& semantics). Stops on first non-zero exit. Removes the need for a sh -c "X && Y" wrapper — the agent forks each command directly in the guest.

Source

pub fn spawn(self) -> Result<ExecChild>

Dial the agent, send the REQUEST, and return the ExecChild handle. Use this when you want to stream stdin/stdout/stderr yourself; for a one-call “run, drain, collect” pattern use ExecBuilder::output.

Source

pub fn output(self) -> Result<ExecOutcome>

Run to completion, collecting stdout + stderr + exit status into one ExecOutcome. Blocks until the process exits (or the configured ExecBuilder::timeout fires and the process is killed).

Mirrors std::process::Command::output. Use this for the common “exec a command, look at the result” case; reach for ExecBuilder::spawn only when you need to stream stdio.

let image = Image::from_snapshot("path/to/snapshot")?;
let vm = Vm::start(&image, &VmConfig::new())?;
let out = vm.exec_builder()
    .argv(["sh", "-c", "echo hi; echo err >&2; exit 7"])
    .timeout(Duration::from_secs(30))
    .output()?;
assert_eq!(out.status.code(), Some(7));
assert_eq!(out.stdout, b"hi\n");
assert_eq!(out.stderr, b"err\n");
assert!(!out.timed_out);
Source

pub fn output_resilient(self, retries: u8) -> Result<ExecOutcome>

Like output but transparently retries a connection-SETUP failure — the agent closed before the workload produced ANY output or EXIT (see ExecEof::is_setup_failure) — up to retries times with a short linear backoff, re-dialing a fresh connection each time. ONLY that case is retried: a mid-stream drop (output already delivered) and a timeout/cancel are returned unchanged, because a retry there could double-execute a side-effecting command.

Semantics are at-least-once — a workload whose EXIT frame was lost after it already ran (rare) could run twice — so use this only for idempotent workloads. retries == 0 is exactly output (no clone, no retry, the default).

Source

pub fn output_with_cancel(self, cancel: Receiver<()>) -> Result<ExecOutcome>

Like Self::output but additionally interrupts the running process if cancel receives a value (or is dropped). The interrupt path is best-effort SIGKILL via the agent followed by a host-side ExecChild::abort — so cancel works even if the worker / agent has already died.

timeout (set via Self::timeout) and cancel are orthogonal: BOTH fire SIGKILL when their trigger arrives; whichever wins kills the child. ExecOutcome::timed_out reports whether the timeout specifically fired; the cancel path doesn’t surface a distinguishing flag (the caller already knows they cancelled).

Typical use:

let (cancel_tx, cancel_rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
    wait_for_user_ctrl_c();
    let _ = cancel_tx.send(());
});
let outcome = vm.exec_builder(["./run.sh"])
    .timeout(Duration::from_secs(60))
    .output_with_cancel(cancel_rx)?;

Trait Implementations§

Source§

impl Clone for ExecBuilder

Source§

fn clone(&self) -> ExecBuilder

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more