1use std::{
2 ffi::OsStr,
3 io,
4 path::Path,
5 process::{Child, ExitStatus, Output, Stdio},
6};
7
8use crate::{platform, sandbox::SandboxPolicy};
9
10pub struct Command {
35 inner: std::process::Command,
36 sandbox: Option<SandboxPolicy>,
37}
38
39impl Command {
40 pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
42 Self {
43 inner: std::process::Command::new(program),
44 sandbox: None,
45 }
46 }
47
48 pub fn arg<S: AsRef<OsStr>>(mut self, arg: S) -> Self {
50 self.inner.arg(arg);
51 self
52 }
53
54 pub fn args<I, S>(mut self, args: I) -> Self
56 where
57 I: IntoIterator<Item = S>,
58 S: AsRef<OsStr>,
59 {
60 self.inner.args(args);
61 self
62 }
63
64 pub fn env<K, V>(mut self, key: K, val: V) -> Self
66 where
67 K: AsRef<OsStr>,
68 V: AsRef<OsStr>,
69 {
70 self.inner.env(key, val);
71 self
72 }
73
74 pub fn envs<I, K, V>(mut self, vars: I) -> Self
76 where
77 I: IntoIterator<Item = (K, V)>,
78 K: AsRef<OsStr>,
79 V: AsRef<OsStr>,
80 {
81 self.inner.envs(vars);
82 self
83 }
84
85 pub fn env_remove<K: AsRef<OsStr>>(mut self, key: K) -> Self {
87 self.inner.env_remove(key);
88 self
89 }
90
91 pub fn env_clear(mut self) -> Self {
93 self.inner.env_clear();
94 self
95 }
96
97 pub fn current_dir<P: AsRef<Path>>(mut self, dir: P) -> Self {
99 self.inner.current_dir(dir);
100 self
101 }
102
103 pub fn stdin<T: Into<Stdio>>(mut self, cfg: T) -> Self {
105 self.inner.stdin(cfg);
106 self
107 }
108
109 pub fn stdout<T: Into<Stdio>>(mut self, cfg: T) -> Self {
111 self.inner.stdout(cfg);
112 self
113 }
114
115 pub fn stderr<T: Into<Stdio>>(mut self, cfg: T) -> Self {
117 self.inner.stderr(cfg);
118 self
119 }
120
121 pub fn sandbox(mut self, policy: SandboxPolicy) -> Self {
126 self.sandbox = Some(policy);
127 self
128 }
129
130 pub fn spawn(mut self) -> io::Result<Child> {
132 if let Some(policy) = self.sandbox.take() {
133 self.apply_sandbox_pre_exec(policy)?;
134 }
135 self.inner.spawn()
136 }
137
138 pub fn output(mut self) -> io::Result<Output> {
141 if let Some(policy) = self.sandbox.take() {
142 self.apply_sandbox_pre_exec(policy)?;
143 }
144 self.inner.output()
145 }
146
147 pub fn status(mut self) -> io::Result<ExitStatus> {
150 if let Some(policy) = self.sandbox.take() {
151 self.apply_sandbox_pre_exec(policy)?;
152 }
153 self.inner.status()
154 }
155
156 #[cfg(unix)]
157 fn apply_sandbox_pre_exec(&mut self, policy: SandboxPolicy) -> io::Result<()> {
158 use std::os::unix::process::CommandExt;
159
160 unsafe {
164 self.inner
165 .pre_exec(move || platform::apply_sandbox(&policy).map_err(io::Error::other));
166 }
167 Ok(())
168 }
169
170 #[cfg(target_os = "windows")]
171 fn apply_sandbox_pre_exec(&mut self, _policy: SandboxPolicy) -> io::Result<()> {
172 Ok(())
174 }
175
176 #[cfg(not(any(unix, target_os = "windows")))]
177 fn apply_sandbox_pre_exec(&mut self, _policy: SandboxPolicy) -> io::Result<()> {
178 Err(io::Error::new(
179 io::ErrorKind::Unsupported,
180 "Sandboxing is only supported on Unix platforms (Linux and macOS)",
181 ))
182 }
183}