Skip to main content

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}