runtime/backlog/host.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4// Copyright (c) 2026 2youg1 and the sprawling contributors
5
6//! Host command ownership at the Backlog spawn boundary.
7
8use super::ceiling::Mark;
9use super::{Backlog, Body, Claim, Member, Started, Tail, process, storage};
10use kernel::{Address, AxCode, AxError, RunId};
11use std::process::{Command, Stdio};
12
13impl Backlog {
14 /// Starts a command, waits out the short window, and hands back
15 /// either its result or a handle onto it.
16 ///
17 /// The command's program, arguments, working directory and
18 /// environment are the caller's; this module owns only where the
19 /// output goes and how the waiting is done. A command that outlives
20 /// its window is owed to `owner` and to no other run.
21 ///
22 /// # Errors
23 /// `E_TOOL_UNAVAILABLE` when the program cannot be started, and
24 /// `E_STORAGE_FATAL` when the place its output goes cannot be
25 /// written.
26 pub fn run(
27 &self,
28 owner: RunId,
29 scope: &Address,
30 what: String,
31 mut command: Command,
32 ) -> Result<Started, AxError> {
33 let id = self.mint()?;
34 let dir = self.scratch.dir(id);
35 std::fs::create_dir_all(&dir).map_err(|err| storage(&dir, &err))?;
36 let out = std::fs::File::create(dir.join("out")).map_err(|err| storage(&dir, &err))?;
37 let errs = std::fs::File::create(dir.join("err")).map_err(|err| storage(&dir, &err))?;
38 command
39 .stdin(Stdio::null())
40 .stdout(Stdio::from(out))
41 .stderr(Stdio::from(errs));
42 let child = command.spawn().map_err(|err| {
43 AxError::failure(
44 AxCode::ToolUnavailable,
45 "start a command",
46 format!("{what}: {err}"),
47 )
48 .with_recovery("check the program name, or use the shell arm")
49 })?;
50 self.enrol(
51 id,
52 Member {
53 scope: scope.clone(),
54 what: what.clone(),
55 body: Body::Command {
56 child: process::Process::Host(child),
57 dir: dir.clone(),
58 claim: Claim::Window(owner),
59 tail: Tail::default(),
60 ceiling: Mark::NotAsked,
61 },
62 },
63 )?;
64 self.watch(id, owner, what, dir)
65 }
66}