use std::ffi::{OsStr, OsString};
use std::process::{Child, Command, ExitStatus, Output, Stdio};
use isla_lib::config::Tool;
pub struct SandboxedCommand {
program: OsString,
args: Vec<OsString>,
stdin: Option<Stdio>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
}
impl SandboxedCommand {
pub fn from_tool(tool: &Tool) -> Self {
let mut cmd = Self::new(&tool.executable);
cmd.args(&tool.options);
cmd
}
pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
SandboxedCommand {
program: program.as_ref().to_os_string(),
args: vec![],
stdin: None,
stdout: None,
stderr: None,
}
}
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
self.args.push(arg.as_ref().to_os_string());
self
}
pub fn args<I, S>(&mut self, args: I) -> &mut Self
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
for arg in args.into_iter() {
self.args.push(arg.as_ref().to_os_string());
}
self
}
pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
self.stdin = Some(cfg.into());
self
}
pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
self.stdout = Some(cfg.into());
self
}
pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
self.stderr = Some(cfg.into());
self
}
#[cfg(feature = "sandbox")]
fn sandbox(&mut self) -> Command {
let mut bubblewrap = Command::new("bwrap");
let sandbox_lib = std::env::var("ISLA_SANDBOX").expect("No ISLA_SANDBOX in environment");
bubblewrap.args(&[OsStr::new("--ro-bind"), &self.program, &self.program]);
bubblewrap.args(&["--bind", "/tmp/isla", "/tmp/isla"]);
bubblewrap.args(&["--ro-bind", &sandbox_lib, "/lib"]);
bubblewrap.args(&["--symlink", "/lib", "/lib64"]);
bubblewrap.args(&["--symlink", "/lib", "/usr/lib64"]);
bubblewrap.args(&["--symlink", "/lib", "/usr/lib"]);
bubblewrap.arg("--unshare-all");
bubblewrap.arg("--");
bubblewrap.arg(&self.program);
bubblewrap.args(&self.args);
if let Some(stdin) = self.stdin.take() {
bubblewrap.stdin(stdin);
}
if let Some(stdout) = self.stdout.take() {
bubblewrap.stdout(stdout);
}
if let Some(stderr) = self.stderr.take() {
bubblewrap.stderr(stderr);
}
bubblewrap
}
#[cfg(not(feature = "sandbox"))]
fn sandbox(&mut self) -> Command {
let mut command = Command::new(&self.program);
command.args(&self.args);
if let Some(stdin) = self.stdin.take() {
command.stdin(stdin);
}
if let Some(stdout) = self.stdout.take() {
command.stdout(stdout);
}
if let Some(stderr) = self.stderr.take() {
command.stderr(stderr);
}
command
}
pub fn output(&mut self) -> std::io::Result<Output> {
self.sandbox().output()
}
pub fn spawn(&mut self) -> std::io::Result<Child> {
self.sandbox().spawn()
}
pub fn status(&mut self) -> std::io::Result<ExitStatus> {
self.sandbox().status()
}
}