#![deny(clippy::all, clippy::cargo)]
#[macro_use]
mod utils;
mod cgroup_v1;
mod mount;
mod pipe;
mod proc;
mod run;
mod signal;
pub use crate::run::run;
use crate::utils::RawFd;
use std::ffi::{OsStr, OsString};
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
use anyhow::Result;
use clap::Clap;
use memchr::memchr;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Serialize, Deserialize, Clap)]
#[clap(setting(clap::AppSettings::DeriveDisplayOrder))]
pub struct SandboxConfig {
pub bin: PathBuf,
pub args: Vec<OsString>,
#[clap(short = 'e', long)]
pub env: Vec<OsString>,
#[clap(short = 'c', long, value_name = "path")]
pub chroot: Option<PathBuf>,
#[clap(long)]
pub uid: Option<u32>,
#[clap(long)]
pub gid: Option<u32>,
#[clap(long, value_name = "path")]
pub stdin: Option<PathBuf>,
#[clap(long, value_name = "path")]
pub stdout: Option<PathBuf>,
#[clap(long, value_name = "path")]
pub stderr: Option<PathBuf>,
#[clap(long, value_name = "fd", conflicts_with = "stdin")]
pub stdin_fd: Option<RawFd>,
#[clap(long, value_name = "fd", conflicts_with = "stdout")]
pub stdout_fd: Option<RawFd>,
#[clap(long, value_name = "fd", conflicts_with = "stderr")]
pub stderr_fd: Option<RawFd>,
#[clap(short = 't', long, value_name = "milliseconds")]
pub real_time_limit: Option<u64>,
#[clap(long, value_name = "seconds")]
pub rlimit_cpu: Option<u32>,
#[clap(long, value_name = "bytes")]
pub rlimit_as: Option<u64>,
#[clap(long, value_name = "bytes")]
pub rlimit_data: Option<u64>,
#[clap(long, value_name = "bytes")]
pub rlimit_fsize: Option<u64>,
#[clap(long, value_name = "bytes")]
pub cg_limit_memory: Option<u64>,
#[clap(long, value_name = "count")]
pub cg_limit_max_pids: Option<u32>,
#[clap(
long,
value_name = "bindmount",
parse(try_from_os_str = BindMount::try_from_os_str)
)]
pub bindmount_rw: Vec<BindMount>,
#[clap(
short = 'b',
long,
value_name = "bindmount",
parse(try_from_os_str = BindMount::try_from_os_str)
)]
pub bindmount_ro: Vec<BindMount>,
#[clap(
long,
value_name = "path",
min_values = 0,
require_equals = true,
default_missing_value = "/proc"
)]
pub mount_proc: Option<PathBuf>,
#[clap(
long,
value_name = "path",
min_values = 0,
require_equals = true,
default_missing_value = "/tmp"
)]
pub mount_tmpfs: Option<PathBuf>, }
#[derive(Debug, Serialize, Deserialize)]
pub struct BindMount {
pub src: PathBuf, pub dst: PathBuf, }
impl BindMount {
fn try_from_os_str(s: &OsStr) -> Result<Self, String> {
let (src, dst) = match memchr(b':', s.as_bytes()) {
Some(idx) => {
let src = OsStr::from_bytes(&s.as_bytes()[..idx]);
let dst = OsStr::from_bytes(&s.as_bytes()[idx + 1..]);
if src.is_empty() || dst.is_empty() {
return Err("invalid bind mount format".into());
}
(src, dst)
}
None => (s, s),
};
Ok(BindMount {
src: src.into(),
dst: dst.into(),
})
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SandboxOutput {
pub code: i32,
pub signal: i32,
pub real_time: u64, pub sys_time: u64, pub user_time: u64,
pub memory: u64, }