use std::borrow::Cow;
use std::ffi::OsStr;
use std::os::fd::AsRawFd;
use std::process::Command;
use anyhow::Result;
use cap_std_ext::camino::{Utf8Path, Utf8PathBuf};
use cap_std_ext::cap_std::fs::Dir;
use crate::CommandRunExt;
#[derive(Debug)]
pub struct BwrapCmd<'a> {
chroot_path: Cow<'a, Utf8Path>,
bind_mounts: Vec<(&'a str, &'a str)>,
env_vars: Vec<(&'a str, &'a str)>,
}
impl<'a> BwrapCmd<'a> {
#[allow(dead_code)]
pub fn new_with_dir(path: &'a Dir) -> Self {
let fd_path: String = format!("/proc/self/fd/{}", path.as_raw_fd());
Self {
chroot_path: Cow::Owned(Utf8PathBuf::from(&fd_path)),
bind_mounts: Vec::new(),
env_vars: Vec::new(),
}
}
pub fn new(path: &'a Utf8Path) -> Self {
Self {
chroot_path: Cow::Borrowed(path),
bind_mounts: Vec::new(),
env_vars: Vec::new(),
}
}
pub fn bind(
mut self,
source: &'a impl AsRef<Utf8Path>,
target: &'a impl AsRef<Utf8Path>,
) -> Self {
self.bind_mounts
.push((source.as_ref().as_str(), target.as_ref().as_str()));
self
}
pub fn setenv(mut self, key: &'a str, value: &'a str) -> Self {
self.env_vars.push((key, value));
self
}
pub fn set_default_path(self) -> Self {
self.setenv(
"PATH",
"/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin",
)
}
fn build_command<S: AsRef<OsStr>>(&self, args: impl IntoIterator<Item = S>) -> Command {
let mut cmd = Command::new("bwrap");
cmd.args(["--bind", self.chroot_path.as_str(), "/"]);
cmd.args(["--proc", "/proc"]);
cmd.args(["--dev-bind", "/dev", "/dev"]);
cmd.args(["--bind", "/sys", "/sys"]);
cmd.args(["--tmpfs", "/run"]);
cmd.args(["--bind", "/run", "/run"]);
for (source, target) in &self.bind_mounts {
cmd.args(["--bind", source, target]);
}
for (key, value) in &self.env_vars {
cmd.args(["--setenv", key, value]);
}
cmd.arg("--");
cmd.args(args);
cmd
}
pub fn run<S: AsRef<OsStr>>(self, args: impl IntoIterator<Item = S>) -> Result<()> {
self.build_command(args)
.log_debug()
.run_inherited_with_cmd_context()
}
pub fn run_get_string<S: AsRef<OsStr>>(
self,
args: impl IntoIterator<Item = S>,
) -> Result<String> {
self.build_command(args).log_debug().run_get_string()
}
}