#![allow(unused_imports)]
use std::ffi::CString;
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
fn main() {
eprintln!(
"dirge-microvm-runner: the microVM sandbox is only supported on Linux and macOS (requires KVM or Hypervisor.framework)."
);
std::process::exit(1);
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn main() {
let config_json = std::env::args()
.nth(1)
.expect("usage: dirge-microvm-runner '<json-config>'");
let config: serde_json::Value =
serde_json::from_str(&config_json).expect("invalid JSON config");
let rootfs_path = config["rootfs_path"].as_str().expect("missing rootfs_path");
let workspace_path = config["workspace_path"]
.as_str()
.expect("missing workspace_path");
let ssh_port: u16 = config["ssh_port"].as_u64().unwrap_or(0) as u16;
let cpus: u8 = config["cpus"].as_u64().unwrap_or(2) as u8;
let memory_mib: u32 = config["memory_mib"].as_u64().unwrap_or(512) as u32;
let exec_cmd: Option<Vec<String>> = config["exec_cmd"].as_array().map(|arr| {
arr.iter()
.filter_map(|v| v.as_str().map(String::from))
.collect()
});
unsafe {
#[cfg(debug_assertions)]
libkrun_sys::krun_set_log_level(0);
let ctx = libkrun_sys::krun_create_ctx();
assert!(ctx >= 0, "krun_create_ctx failed: {ctx}");
let rc = libkrun_sys::krun_set_vm_config(ctx as u32, cpus, memory_mib);
assert!(rc == 0, "krun_set_vm_config failed: {rc}");
#[cfg(target_os = "linux")]
{
let mut rlim: libc::rlimit = std::mem::zeroed();
assert!(
libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) == 0,
"getrlimit(RLIMIT_NOFILE) failed"
);
let max_nofile: libc::rlim_t = 1_048_576;
rlim.rlim_cur = rlim.rlim_max.min(max_nofile);
if rlim.rlim_cur < 4096 {
rlim.rlim_cur = 4096;
}
assert!(
libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) == 0,
"setrlimit(RLIMIT_NOFILE) failed"
);
let nofile_rlimit = format!(
"RLIMIT_NOFILE={}:{}",
rlim.rlim_cur,
rlim.rlim_max.min(max_nofile),
);
let nofile_cstr = CString::new(nofile_rlimit).unwrap();
let rlimits: [*const std::ffi::c_char; 2] = [nofile_cstr.as_ptr(), std::ptr::null()];
let rc = libkrun_sys::krun_set_rlimits(ctx as u32, rlimits.as_ptr());
assert!(rc == 0, "krun_set_rlimits failed: {rc}");
}
#[cfg(target_os = "macos")]
{
let mut rlim: libc::rlimit = std::mem::zeroed();
assert!(
libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) == 0,
"getrlimit(RLIMIT_NOFILE) failed"
);
rlim.rlim_cur = rlim.rlim_max;
assert!(
libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) == 0,
"setrlimit(RLIMIT_NOFILE) failed"
);
}
let root_cstr = CString::new(rootfs_path).unwrap();
let rc = libkrun_sys::krun_set_root(ctx as u32, root_cstr.as_ptr());
assert!(rc == 0, "krun_set_root failed: {rc}");
let workdir_cstr = CString::new("/").unwrap();
let rc = libkrun_sys::krun_set_workdir(ctx as u32, workdir_cstr.as_ptr());
assert!(rc == 0, "krun_set_workdir failed: {rc}");
#[cfg(target_os = "linux")]
{
let ws_cstr = CString::new(workspace_path).unwrap();
let tag_cstr = CString::new("workspace").unwrap();
let rc =
libkrun_sys::krun_add_virtiofs(ctx as u32, tag_cstr.as_ptr(), ws_cstr.as_ptr());
assert!(rc == 0, "krun_add_virtiofs failed: {rc}");
}
#[cfg(target_os = "macos")]
{
let _ = &workspace_path; }
let _ = std::fs::create_dir_all(format!("{rootfs_path}/workspace"));
if ssh_port > 0 {
let port_map_str = format!("{ssh_port}:22");
let pm_cstr = CString::new(port_map_str).unwrap();
let port_maps: [*const std::ffi::c_char; 2] = [pm_cstr.as_ptr(), std::ptr::null()];
let rc = libkrun_sys::krun_set_port_map(ctx as u32, port_maps.as_ptr());
assert!(rc == 0, "krun_set_port_map failed: {rc}");
}
if let Some(cmd) = &exec_cmd {
let exec_path = CString::new(cmd[0].as_bytes()).unwrap();
let argv: Vec<CString> = cmd
.iter()
.skip(1)
.map(|s| CString::new(s.as_bytes()).unwrap())
.collect();
let mut argv_ptrs: Vec<*const std::ffi::c_char> =
argv.iter().map(|s| s.as_ptr()).collect();
argv_ptrs.push(std::ptr::null());
let hostname = CString::new("HOSTNAME=microvm").unwrap();
let home = CString::new("HOME=/root").unwrap();
let path =
CString::new("PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
.unwrap();
let env: Vec<CString> = vec![path, hostname, home];
let mut env_ptrs: Vec<*const std::ffi::c_char> =
env.iter().map(|s| s.as_ptr()).collect();
env_ptrs.push(std::ptr::null());
let rc = libkrun_sys::krun_set_exec(
ctx as u32,
exec_path.as_ptr(),
argv_ptrs.as_ptr(),
env_ptrs.as_ptr(),
);
assert!(rc == 0, "krun_set_exec failed: {rc}");
}
let rc = libkrun_sys::krun_start_enter(ctx as u32);
assert!(rc == 0, "krun_start_enter failed: {rc}");
}
}