use std::path::{Path, PathBuf};
use std::collections::HashMap;
use std::os::unix::process::CommandExt;
use std::os::fd::AsRawFd;
use std::process::Output;
mod isolate_sys;
use isolate_sys as system;
#[derive(Debug)]
pub enum IsolateError {
MemFd(std::io::Error),
Command(std::io::Error),
BindmountConfig(std::io::Error),
}
#[derive(Debug)]
#[must_use]
pub struct Isolate {
isolate_name: &'static str,
func: fn() -> (),
new_network: bool,
root_fs_size: u32,
bindmounts: HashMap<PathBuf, PathBuf>,
}
impl Isolate {
pub fn new(isolate_name: &'static str, func: fn() -> ()) -> Isolate {
Isolate {
isolate_name,
func,
new_network: true,
root_fs_size: 10,
bindmounts: HashMap::new(),
}
}
pub fn add_bind_mount(mut self, src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Isolate {
let src = src.as_ref();
let dst = dst.as_ref();
let _old = self.bindmounts.insert(src.into(), dst.into());
self
}
pub fn set_rootfs_size(mut self, size: u32) -> Self {
self.root_fs_size = size;
self
}
pub fn new_network(mut self, new_network: bool) -> Self {
self.new_network = new_network;
self
}
pub fn run(isolate_name: &'static str, envs: &HashMap<String, String>) -> Result<Output, IsolateError> {
let memfd = system::create_memfd_from_self_exe()?;
std::process::Command::new(format!("/proc/self/fd/{}", memfd.as_raw_fd()))
.arg0(isolate_name)
.env_clear()
.envs(envs)
.output()
.map_err(IsolateError::Command)
}
pub fn main_hook<F: Fn(&'static str) -> Isolate>(isolate_name: &'static str, builder: F) {
struct TempDirCleanup(pub PathBuf);
impl Drop for TempDirCleanup {
fn drop(&mut self) {
std::fs::remove_dir(&self.0)
.expect("tmpfs dir was not empty");
}
}
let mut args = std::env::args();
if let Some(arg0) = args.next() {
if arg0 == isolate_name {
let isolate = builder(isolate_name);
let tempdir = system::make_tempdir(isolate_name);
let tempdir_clean = TempDirCleanup(tempdir.clone());
let mut child_stack = Vec::with_capacity(system::CHILD_STACK_SIZE);
let (_child_pid, child_pidfd) = isolate.isolate_and_run(&mut child_stack, tempdir.clone());
let child_ret = system::wait_for_child(child_pidfd);
drop(tempdir_clean);
std::process::exit(child_ret);
}
}
}
fn isolate_and_run(&self, child_stack: &mut [u8], tempdir: PathBuf) -> (libc::pid_t, libc::id_t) {
let new_network = self.new_network;
let data = system::IsolateConfigData::new(
self.isolate_name,
self.bindmounts.clone(),
self.func,
self.root_fs_size,
tempdir);
system::clone_into_namespace(child_stack, data, new_network)
}
}