/ src / sandbox.rs
sandbox.rs
  1  pub mod isolation;
  2  
  3  use crate::{root::Root, sandbox::isolation::IsolationPathAction};
  4  use isolation::{IsolatationConfig, IsolationPath};
  5  use std::{
  6      collections::{BTreeMap, VecDeque},
  7      ffi::OsString,
  8      fs::File,
  9      io,
 10      path::Path,
 11      process::{ExitStatus, Stdio},
 12  };
 13  
 14  #[derive(Debug, Hash)]
 15  pub struct SandboxCommand {
 16      config: IsolatationConfig,
 17      program: OsString,
 18      args: VecDeque<OsString>,
 19      envs: BTreeMap<OsString, OsString>,
 20      stdout: Option<Box<Path>>,
 21      stderr: Option<Box<Path>>,
 22  }
 23  
 24  impl SandboxCommand {
 25      pub fn new(program: impl Into<OsString>) -> Self {
 26          Self {
 27              config: IsolatationConfig::new(),
 28              program: program.into(),
 29              args: VecDeque::new(),
 30              envs: BTreeMap::new(),
 31              stdout: None,
 32              stderr: None,
 33          }
 34      }
 35  
 36      pub fn allow_read_path(mut self, path: IsolationPath) -> Self {
 37          self.config
 38              .path_rules
 39              .push((path, IsolationPathAction::AllowRead));
 40          self
 41      }
 42  
 43      pub fn allow_path(mut self, path: IsolationPath) -> Self {
 44          self.config
 45              .path_rules
 46              .push((path, IsolationPathAction::Allow));
 47          self
 48      }
 49  
 50      pub fn deny_path(mut self, path: IsolationPath) -> Self {
 51          self.config
 52              .path_rules
 53              .push((path, IsolationPathAction::Deny));
 54          self
 55      }
 56  
 57      pub fn arg(mut self, arg: impl Into<OsString>) -> Self {
 58          self.args.push_back(arg.into());
 59          self
 60      }
 61  
 62      pub fn args<As, A>(mut self, args: As) -> Self
 63      where
 64          As: IntoIterator<Item = A>,
 65          A: Into<OsString>,
 66      {
 67          for arg in args {
 68              self.args.push_back(arg.into());
 69          }
 70          self
 71      }
 72  
 73      pub fn env<K, V>(mut self, name: K, value: V) -> Self
 74      where
 75          K: Into<OsString>,
 76          V: Into<OsString>,
 77      {
 78          self.envs.insert(name.into(), value.into());
 79          self
 80      }
 81  
 82      pub fn pipe_stdout(mut self, path: impl AsRef<Path>) -> Self {
 83          self.stdout = Some(path.as_ref().to_path_buf().into_boxed_path());
 84          self
 85      }
 86  
 87      pub fn pipe_stderr(mut self, path: impl AsRef<Path>) -> Self {
 88          self.stderr = Some(path.as_ref().to_path_buf().into_boxed_path());
 89          self
 90      }
 91  
 92      pub fn run(self, root: &Root) -> io::Result<SandboxCommandStatus> {
 93          let temp_dir = root.tmp_dir("sandbox-command", &(&self, root));
 94  
 95          let stdout_path = self
 96              .stdout
 97              .unwrap_or_else(|| temp_dir.join("stdout").into_boxed_path());
 98          let stdout = {
 99              let file = File::create(&stdout_path)
100                  .expect("Failed to create file for capturing standard output");
101              Stdio::from(file)
102          };
103  
104          let stderr_path = self
105              .stderr
106              .unwrap_or_else(|| temp_dir.join("stderr").into_boxed_path());
107          let stderr = {
108              let file = File::create(&stderr_path)
109                  .expect("Failed to create file for capturing standard error");
110              Stdio::from(file)
111          };
112  
113          let mut command = isolation::cmd(root, &self.config, &self.program);
114          command
115              .args(self.args)
116              .envs(self.envs)
117              .stdout(stdout)
118              .stderr(stderr)
119              .current_dir(root.path());
120  
121          let status = command.status().unwrap();
122  
123          if !status.success() {
124              temp_dir.keep();
125          }
126  
127          Ok(SandboxCommandStatus {
128              status,
129              stdout: stdout_path,
130              stderr: stderr_path,
131          })
132      }
133  }
134  
135  #[derive(Debug)]
136  pub struct SandboxCommandStatus {
137      pub status: ExitStatus,
138      pub stdout: Box<Path>,
139      pub stderr: Box<Path>,
140  }