use std::{
ffi::OsStr,
io,
path::Path,
process::{Child, ExitStatus, Output, Stdio},
};
use crate::{platform, sandbox::SandboxPolicy};
pub struct Command {
inner: std::process::Command,
sandbox: Option<SandboxPolicy>,
}
impl Command {
pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
Self {
inner: std::process::Command::new(program),
sandbox: None,
}
}
pub fn arg<S: AsRef<OsStr>>(mut self, arg: S) -> Self {
self.inner.arg(arg);
self
}
pub fn args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
self.inner.args(args);
self
}
pub fn env<K, V>(mut self, key: K, val: V) -> Self
where
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
self.inner.env(key, val);
self
}
pub fn envs<I, K, V>(mut self, vars: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
self.inner.envs(vars);
self
}
pub fn env_remove<K: AsRef<OsStr>>(mut self, key: K) -> Self {
self.inner.env_remove(key);
self
}
pub fn env_clear(mut self) -> Self {
self.inner.env_clear();
self
}
pub fn current_dir<P: AsRef<Path>>(mut self, dir: P) -> Self {
self.inner.current_dir(dir);
self
}
pub fn stdin<T: Into<Stdio>>(mut self, cfg: T) -> Self {
self.inner.stdin(cfg);
self
}
pub fn stdout<T: Into<Stdio>>(mut self, cfg: T) -> Self {
self.inner.stdout(cfg);
self
}
pub fn stderr<T: Into<Stdio>>(mut self, cfg: T) -> Self {
self.inner.stderr(cfg);
self
}
pub fn sandbox(mut self, policy: SandboxPolicy) -> Self {
self.sandbox = Some(policy);
self
}
pub fn spawn(mut self) -> io::Result<Child> {
if let Some(policy) = self.sandbox.take() {
self.apply_sandbox_pre_exec(policy)?;
}
self.inner.spawn()
}
pub fn output(mut self) -> io::Result<Output> {
if let Some(policy) = self.sandbox.take() {
self.apply_sandbox_pre_exec(policy)?;
}
self.inner.output()
}
pub fn status(mut self) -> io::Result<ExitStatus> {
if let Some(policy) = self.sandbox.take() {
self.apply_sandbox_pre_exec(policy)?;
}
self.inner.status()
}
#[cfg(unix)]
fn apply_sandbox_pre_exec(&mut self, policy: SandboxPolicy) -> io::Result<()> {
use std::os::unix::process::CommandExt;
unsafe {
self.inner
.pre_exec(move || platform::apply_sandbox(&policy).map_err(io::Error::other));
}
Ok(())
}
#[cfg(target_os = "windows")]
fn apply_sandbox_pre_exec(&mut self, _policy: SandboxPolicy) -> io::Result<()> {
Ok(())
}
#[cfg(not(any(unix, target_os = "windows")))]
fn apply_sandbox_pre_exec(&mut self, _policy: SandboxPolicy) -> io::Result<()> {
Err(io::Error::new(
io::ErrorKind::Unsupported,
"Sandboxing is only supported on Unix platforms (Linux and macOS)",
))
}
}