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
impl ExecBuilder
Sourcepub fn new(exec_path: PathBuf) -> Self
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).
Sourcepub fn argv<I, S>(self, argv: I) -> Self
pub fn argv<I, S>(self, argv: I) -> Self
The argv to execute in the guest. First element is the program (resolved via PATH inside the guest).
Sourcepub fn env(self, key: impl Into<String>, value: impl Into<String>) -> Self
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.
Sourcepub fn cwd(self, path: impl Into<String>) -> Self
pub fn cwd(self, path: impl Into<String>) -> Self
Working directory for the spawned process inside the guest.
Sourcepub fn tty(self, on: bool) -> Self
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.
Sourcepub fn winsize(self, cols: u16, rows: u16) -> Self
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.
Sourcepub fn timeout(self, d: Duration) -> Self
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.
Sourcepub fn stage_file(
self,
path: impl Into<String>,
bytes: impl Into<Vec<u8>>,
) -> Self
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()?.
Sourcepub fn stage_file_mode(
self,
path: impl Into<String>,
bytes: impl Into<Vec<u8>>,
mode: u32,
) -> Self
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.
Sourcepub fn chain<I, S>(self, argv: I) -> Self
pub fn chain<I, S>(self, argv: I) -> Self
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.
Sourcepub fn spawn(self) -> Result<ExecChild>
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.
Sourcepub fn output(self) -> Result<ExecOutcome>
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);Sourcepub fn output_resilient(self, retries: u8) -> Result<ExecOutcome>
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).
Sourcepub fn output_with_cancel(self, cancel: Receiver<()>) -> Result<ExecOutcome>
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
impl Clone for ExecBuilder
Source§fn clone(&self) -> ExecBuilder
fn clone(&self) -> ExecBuilder
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more