use std::io::Write;
use std::process::{Command, Stdio};
use std::time::Instant;
use anyhow::{Context, Result, bail};
use clap::Subcommand;
use bob::config::{Config, PkgsrcEnv};
use bob::logging;
use bob::sandbox::Sandbox;
#[derive(Debug, Subcommand)]
pub enum SandboxCmd {
Create,
Destroy,
Exec,
List,
}
pub fn run(config: &Config, cmd: SandboxCmd) -> Result<()> {
match cmd {
SandboxCmd::Create => {
logging::init_stderr_if_enabled();
let sandbox = Sandbox::new(config);
if !sandbox.enabled() {
bail!("No sandboxes configured");
}
sandbox.create_all(config.build_threads())?;
}
SandboxCmd::Destroy => {
logging::init_stderr_if_enabled();
let sandbox = Sandbox::new(config);
if !sandbox.enabled() {
bail!("No sandboxes configured");
}
sandbox.destroy_all()?;
}
SandboxCmd::Exec => {
logging::init(config.dbdir(), config.log_level())?;
exec(config)?;
}
SandboxCmd::List => {
let sandbox = Sandbox::new(config);
if !sandbox.enabled() {
bail!("No sandboxes configured");
}
sandbox.list_all()?;
}
}
Ok(())
}
fn exec(config: &Config) -> Result<()> {
let sandbox = Sandbox::new(config);
if !sandbox.enabled() {
bail!("No sandboxes configured");
}
print!("Creating sandbox...");
let _ = std::io::stdout().flush();
let start = Instant::now();
let id = sandbox.claim_id()?;
let basic_envs = config.script_env(None);
let result = (|| -> Result<()> {
if !sandbox.run_pre_build(Some(id), config, basic_envs)? {
println!(" failed ({:.1}s)", start.elapsed().as_secs_f32());
bail!("pre-build script failed");
}
println!(" done ({:.1}s)", start.elapsed().as_secs_f32());
println!("Entering sandbox {}...", sandbox.path(id).display());
let mut cmd = Command::new("/usr/sbin/chroot");
cmd.arg(sandbox.path(id)).arg("/bin/sh").arg("-i");
sandbox.apply_environment(&mut cmd);
cmd.env("PS1", format!("sandbox:{} $PWD# ", id));
cmd.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit());
let status = cmd.status().context("Failed to run chroot shell")?;
if !status.success() {
bail!("Shell exited with {}", status);
}
Ok(())
})();
let pkgsrc_env = PkgsrcEnv::fetch(config, &sandbox, Some(id)).ok();
let envs = config.script_env(pkgsrc_env.as_ref());
match sandbox.run_post_build(Some(id), config, envs) {
Ok(true) => {}
Ok(false) => eprintln!("Warning: post-build script failed"),
Err(e) => eprintln!("Warning: post-build script error: {e}"),
}
print!("Destroying sandbox...");
let _ = std::io::stdout().flush();
let start = Instant::now();
sandbox.destroy(id)?;
println!(" done ({:.1}s)", start.elapsed().as_secs_f32());
result
}