diff --git a/minion-cli/src/main.rs b/minion-cli/src/main.rs index edee7e58..84602082 100644 --- a/minion-cli/src/main.rs +++ b/minion-cli/src/main.rs @@ -137,6 +137,7 @@ async fn main() { shared_items: options.exposed_paths, cpu_time_limit: Duration::from_millis(u64::from(options.time_limit)), real_time_limit: Duration::from_millis(u64::from(options.time_limit * 3)), + extensions: None, }) .unwrap(); diff --git a/minion-ffi/src/lib.rs b/minion-ffi/src/lib.rs index 19e8fc97..cf9911c1 100644 --- a/minion-ffi/src/lib.rs +++ b/minion-ffi/src/lib.rs @@ -192,6 +192,7 @@ pub unsafe extern "C" fn minion_sandbox_create( ), isolation_root, shared_items, + extensions: None, }; let d = backend.0.new_sandbox(opts); let d = d.unwrap(); diff --git a/minion-tests/src/worker.rs b/minion-tests/src/worker.rs index 62750cfa..8ad1883c 100644 --- a/minion-tests/src/worker.rs +++ b/minion-tests/src/worker.rs @@ -27,6 +27,7 @@ async fn inner_main(test_cases: &[&'static dyn TestCase]) { dest: "/me".into(), kind: minion::SharedItemKind::Readonly, }], + extensions: None, }; let sandbox = backend.new_sandbox(opts).expect("can not create sandbox"); let opts = minion::ChildProcessOptions { diff --git a/src/check.rs b/src/check.rs index 05640d26..4fe97449 100644 --- a/src/check.rs +++ b/src/check.rs @@ -2,7 +2,7 @@ pub fn check(res: &mut CheckResult) { #[cfg(target_os = "linux")] { - crate::linux::check::check(&crate::linux::Settings::default(), res); + crate::linux::check::check(&crate::linux::BackendSettings::default(), res); } } diff --git a/src/erased.rs b/src/erased.rs index 91b1f029..2806cc8a 100644 --- a/src/erased.rs +++ b/src/erased.rs @@ -4,6 +4,7 @@ //! Please note that this API is not type-safe. For example, if you pass //! `Sandbox` instance to another backend, it will panic. +use anyhow::Context as _; use futures_util::{FutureExt, TryFutureExt}; /// Type-erased `Sandbox` @@ -89,13 +90,35 @@ impl ChildProcess for C { /// Type-erased `Backend` pub trait Backend: Send + Sync + 'static { - fn new_sandbox(&self, options: crate::SandboxOptions) -> anyhow::Result>; + fn new_sandbox( + &self, + options: crate::SandboxOptions, + ) -> anyhow::Result>; fn spawn(&self, options: ChildProcessOptions) -> anyhow::Result>; } impl Backend for B { - fn new_sandbox(&self, options: crate::SandboxOptions) -> anyhow::Result> { - let sb = ::new_sandbox(&self, options)?; + fn new_sandbox( + &self, + options: crate::SandboxOptions, + ) -> anyhow::Result> { + let exts = match options.extensions { + Some(e) => serde_json::from_value(e) + .map(Some) + .context("failed to parse sandbox settings extensions")?, + None => None, + }; + + let down_options = crate::SandboxOptions { + max_alive_process_count: options.max_alive_process_count, + memory_limit: options.memory_limit, + cpu_time_limit: options.cpu_time_limit, + real_time_limit: options.real_time_limit, + isolation_root: options.isolation_root, + shared_items: options.shared_items, + extensions: exts, + }; + let sb = ::new_sandbox(&self, down_options)?; Ok(Box::new(sb)) } @@ -123,6 +146,6 @@ pub type ChildProcessOptions = crate::ChildProcessOptions>; /// Returns backend instance pub fn setup() -> anyhow::Result> { Ok(Box::new(crate::linux::LinuxBackend::new( - crate::linux::Settings::new(), + crate::linux::BackendSettings::new(), )?)) } diff --git a/src/lib.rs b/src/lib.rs index 72e4e973..c98aefb5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,12 @@ pub trait Backend: Debug + Send + Sync + 'static { type Error: StdError + Send + Sync + 'static; type Sandbox: Sandbox; type ChildProcess: ChildProcess; - fn new_sandbox(&self, options: SandboxOptions) -> Result; + /// Backend-specific sandbox settings + type SandboxOptionsExtensions: serde::de::DeserializeOwned; + fn new_sandbox( + &self, + options: SandboxOptions, + ) -> Result; fn spawn( &self, options: ChildProcessOptions, @@ -80,7 +85,7 @@ pub struct ResourceUsageData { } #[derive(Serialize, Deserialize, Debug, Clone)] -pub struct SandboxOptions { +pub struct SandboxOptions { pub max_alive_process_count: u32, /// Memory limit for all processes in cgroup, in bytes pub memory_limit: u64, @@ -90,9 +95,11 @@ pub struct SandboxOptions { pub real_time_limit: Duration, pub isolation_root: PathBuf, pub shared_items: Vec, + /// Backend-specific extensions + pub extensions: Option, } -impl SandboxOptions { +impl SandboxOptions { fn make_relative<'a>(&self, p: &'a Path) -> &'a Path { if p.starts_with("/") { p.strip_prefix("/").unwrap() diff --git a/src/linux.rs b/src/linux.rs index 818472e8..a7b8ae1c 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -208,7 +208,7 @@ fn spawn(mut options: ChildProcessOptions) -> Result Self { - Settings { + BackendSettings { cgroup_prefix: "/minion".into(), allow_unsupported_mount_namespace: false, cgroupfs: std::env::var_os("MINION_CGROUPFS") @@ -236,14 +236,19 @@ impl Default for Settings { } } -impl Settings { - pub fn new() -> Settings { +impl BackendSettings { + pub fn new() -> BackendSettings { Default::default() } } + +/// Extended sandbox settings +#[derive(serde::Deserialize, Default, Debug)] +pub struct LinuxSandboxOptionsExtensions {} + #[derive(Debug)] pub struct LinuxBackend { - settings: Settings, + settings: BackendSettings, cgroup_driver: Arc, } @@ -251,7 +256,11 @@ impl Backend for LinuxBackend { type Error = Error; type Sandbox = LinuxSandbox; type ChildProcess = LinuxChildProcess; - fn new_sandbox(&self, mut options: SandboxOptions) -> Result { + type SandboxOptionsExtensions = LinuxSandboxOptionsExtensions; + fn new_sandbox( + &self, + mut options: SandboxOptions, + ) -> Result { options.postprocess(); let sb = unsafe { LinuxSandbox::create(options, &self.settings, self.cgroup_driver.clone())? }; @@ -267,7 +276,7 @@ impl Backend for LinuxBackend { } impl LinuxBackend { - pub fn new(settings: Settings) -> Result { + pub fn new(settings: BackendSettings) -> Result { self::check::run_all_feature_checks(); let cgroup_driver = Arc::new(cgroup::Driver::new(&settings)?); Ok(LinuxBackend { diff --git a/src/linux/cgroup.rs b/src/linux/cgroup.rs index 3e6ae733..055c93d5 100644 --- a/src/linux/cgroup.rs +++ b/src/linux/cgroup.rs @@ -198,7 +198,7 @@ impl Driver { #[tracing::instrument] pub(in crate::linux) fn new( - settings: &crate::linux::Settings, + settings: &crate::linux::BackendSettings, ) -> Result { let mut configs = Vec::new(); for &cgroup_version in &[CgroupVersion::V1, CgroupVersion::V2] { diff --git a/src/linux/check.rs b/src/linux/check.rs index d5e0ca3f..d5d6a9e6 100644 --- a/src/linux/check.rs +++ b/src/linux/check.rs @@ -1,7 +1,7 @@ use crate::linux::cgroup::Driver; /// `crate::check()` on linux -pub fn check(settings: &crate::linux::Settings, res: &mut crate::check::CheckResult) { +pub fn check(settings: &crate::linux::BackendSettings, res: &mut crate::check::CheckResult) { if !pidfd_supported() { res.warning("PID file descriptors not supported") } diff --git a/src/linux/sandbox.rs b/src/linux/sandbox.rs index 17f1b647..2cbcfb37 100644 --- a/src/linux/sandbox.rs +++ b/src/linux/sandbox.rs @@ -3,7 +3,7 @@ use crate::{ jail_common, pipe::setup_pipe, util::{IpcSocketExt, Pid}, - zygote, Error, + zygote, Error, LinuxSandboxOptionsExtensions, }, ExitCode, Sandbox, SandboxOptions, }; @@ -55,7 +55,7 @@ pub struct LinuxSandbox(Arc); #[repr(C)] struct LinuxSandboxInner { id: String, - options: SandboxOptions, + options: SandboxOptions, zygote_sock: Mutex, zygote_pid: Pid, state: SandboxState, @@ -66,7 +66,7 @@ struct LinuxSandboxInner { #[derive(Debug)] struct LinuxSandboxDebugHelper<'a> { id: &'a str, - options: &'a SandboxOptions, + options: &'a SandboxOptions, zygote_sock: RawFd, zygote_pid: Pid, state: SandboxState, @@ -152,8 +152,8 @@ impl LinuxSandbox { } pub(in crate::linux) unsafe fn create( - options: SandboxOptions, - settings: &crate::linux::Settings, + options: SandboxOptions, + settings: &crate::linux::BackendSettings, cgroup_driver: Arc, ) -> Result { let jail_id = jail_common::gen_jail_id();