Skip to main content
Glama

microsandbox

by microsandbox
msbrun.rs12 kB
//! `msbrun` is a polymorphic binary that can operate in three modes: MicroVM, supervisor, or sandbox server. //! //! # Overview //! //! This binary provides a unified interface for running either: //! - A MicroVM that provides an isolated execution environment //! - A supervisor process that can manage and monitor child processes //! - A sandbox server that can orchestrate sandboxes //! //! ## Usage //! //! ### MicroVM Mode //! //! To run as a MicroVM: //! ```bash //! msbrun microvm \ //! --log-level=3 \ //! --native-rootfs=/path/to/rootfs \ //! --overlayfs-rootfs=/path/to/rootfs \ //! --num-vcpus=2 \ //! --memory-mib=1024 \ //! --workdir-path=/app \ //! --exec-path=/usr/bin/python3 \ //! --mapped-dirs=/host/path:/guest/path \ //! --port-maps=8080:80 \ //! --scope=public \ //! --ip=192.168.1.1 \ //! --subnet=192.168.1.0/24 \ //! --envs=KEY=VALUE \ //! -- -m http.server 8080 //! ``` //! //! ### Supervisor Mode //! //! To run as a supervisor: //! ```bash //! msbrun supervisor \ //! --log-dir=/path/to/logs \ //! --child-name=my_vm \ //! --sandbox-db-path=/path/to/msbrun.db \ //! --log-level=3 \ //! --native-rootfs=/path/to/rootfs \ //! --overlayfs-rootfs=/path/to/rootfs \ //! --num-vcpus=2 \ //! --memory-mib=1024 \ //! --workdir-path=/app \ //! --exec-path=/usr/bin/python3 \ //! --mapped-dirs=/host/path:/guest/path \ //! --port-maps=8080:80 \ //! --envs=KEY=VALUE \ //! --forward-output \ //! --scope=public \ //! --ip=192.168.1.1 \ //! --subnet=192.168.1.0/24 \ //! -- -m http.server 8080 //! ``` use std::env; use anyhow::Result; use clap::Parser; use microsandbox_cli::{McrunArgs, McrunSubcommand}; use microsandbox_core::{ config::{EnvPair, PathPair, PortPair}, runtime::MicroVmMonitor, vm::{MicroVm, Rootfs}, }; use microsandbox_utils::runtime::Supervisor; //-------------------------------------------------------------------------------------------------- // Functions: main //-------------------------------------------------------------------------------------------------- #[tokio::main] async fn main() -> Result<()> { // Parse command line arguments let args = McrunArgs::parse(); match args.subcommand { McrunSubcommand::Microvm { log_level, native_rootfs, overlayfs_layer, num_vcpus, memory_mib, workdir_path, exec_path, env, mapped_dir, port_map, scope, ip, subnet, args, } => { tracing_subscriber::fmt::init(); tracing::debug!("log_level: {:#?}", log_level); tracing::debug!("native_rootfs: {:#?}", native_rootfs); tracing::debug!("overlayfs_layer: {:#?}", overlayfs_layer); tracing::debug!("num_vcpus: {:#?}", num_vcpus); tracing::debug!("memory_mib: {:#?}", memory_mib); tracing::debug!("workdir_path: {:#?}", workdir_path); tracing::debug!("exec_path: {:#?}", exec_path); tracing::debug!("env: {:#?}", env); tracing::debug!("mapped_dir: {:#?}", mapped_dir); tracing::debug!("port_map: {:#?}", port_map); tracing::debug!("scope: {:#?}", scope); tracing::debug!("ip: {:#?}", ip); tracing::debug!("subnet: {:#?}", subnet); tracing::debug!("args: {:#?}", args); // Check that only one of native_rootfs or overlayfs_layer is provided let rootfs = match (native_rootfs, overlayfs_layer.is_empty()) { (Some(path), true) => Rootfs::Native(path), (None, false) => Rootfs::Overlayfs(overlayfs_layer), (Some(_), false) => { anyhow::bail!("Cannot specify both native_rootfs and overlayfs_rootfs") } (None, true) => { anyhow::bail!("Must specify either native_rootfs or overlayfs_rootfs") } }; tracing::info!("rootfs: {:#?}", rootfs); // Parse mapped directories let mapped_dir: Vec<PathPair> = mapped_dir .iter() .map(|s| s.parse()) .collect::<Result<_, _>>()?; // Parse port mappings let port_map: Vec<PortPair> = port_map .iter() .map(|s| s.parse()) .collect::<Result<_, _>>()?; // Parse environment variables let env: Vec<EnvPair> = env.iter().map(|s| s.parse()).collect::<Result<_, _>>()?; // Create and configure MicroVM let mut builder = MicroVm::builder().rootfs(rootfs).exec_path(exec_path); // Set num vcpus if provided if let Some(num_vcpus) = num_vcpus { builder = builder.num_vcpus(num_vcpus); } // Set memory mib if provided if let Some(memory_mib) = memory_mib { builder = builder.memory_mib(memory_mib); } // Set log level if provided if let Some(log_level) = log_level { builder = builder.log_level(log_level.try_into()?); } // Set working directory if provided if let Some(workdir_path) = workdir_path { builder = builder.workdir_path(workdir_path); } // Set mapped dirs if provided if !mapped_dir.is_empty() { builder = builder.mapped_dirs(mapped_dir); } // Set port map if provided if !port_map.is_empty() { builder = builder.port_map(port_map); } // Set scope if provided if let Some(scope) = scope { builder = builder.scope(scope.parse()?); } // Set ip if provided if let Some(ip) = ip { builder = builder.ip(ip.parse()?); } // Set subnet if provided if let Some(subnet) = subnet { builder = builder.subnet(subnet.parse()?); } // Set env if provided if !env.is_empty() { builder = builder.env(env); } // Set args if provided if !args.is_empty() { builder = builder.args(args.iter().map(|s| s.as_str())); } // Build and start the MicroVM let vm = builder.build()?; tracing::info!("starting µvm"); vm.start()?; } McrunSubcommand::Supervisor { log_dir, sandbox_db_path, sandbox_name, config_file, config_last_modified, log_level, forward_output, native_rootfs, overlayfs_layer, num_vcpus, memory_mib, workdir_path, exec_path, env, mapped_dir, port_map, scope, ip, subnet, args, } => { tracing_subscriber::fmt::init(); tracing::info!("setting up supervisor"); // Get current executable path let child_exe = env::current_exe()?; // Get supervisor PID let supervisor_pid = std::process::id(); // Get rootfs let rootfs = match (&native_rootfs, &overlayfs_layer.is_empty()) { (Some(path), true) => Rootfs::Native(path.clone()), (None, false) => Rootfs::Overlayfs(overlayfs_layer.clone()), (Some(_), false) => { anyhow::bail!("Cannot specify both native_rootfs and overlayfs_rootfs") } (None, true) => { anyhow::bail!("Must specify either native_rootfs or overlayfs_rootfs") } }; // Create microvm monitor let process_monitor = MicroVmMonitor::new( supervisor_pid, sandbox_db_path, sandbox_name, config_file, config_last_modified, log_dir.clone(), rootfs.clone(), forward_output, ) .await?; // Compose child arguments let mut child_args = vec!["microvm".to_string(), format!("--exec-path={}", exec_path)]; // Set num vcpus if provided if let Some(num_vcpus) = num_vcpus { child_args.push(format!("--num-vcpus={}", num_vcpus)); } // Set memory mib if provided if let Some(memory_mib) = memory_mib { child_args.push(format!("--memory-mib={}", memory_mib)); } // Set workdir path if provided if let Some(workdir_path) = workdir_path { child_args.push(format!("--workdir-path={}", workdir_path)); } // Set native rootfs if provided if let Some(native_rootfs) = native_rootfs { child_args.push(format!("--native-rootfs={}", native_rootfs.display())); } // Set overlayfs rootfs if provided if !overlayfs_layer.is_empty() { for path in overlayfs_layer { child_args.push(format!("--overlayfs-layer={}", path.display())); } } // Set env if provided if !env.is_empty() { for env in env { child_args.push(format!("--env={}", env)); } } // Set mapped dirs if provided if !mapped_dir.is_empty() { for dir in mapped_dir { child_args.push(format!("--mapped-dir={}", dir)); } } // Set port map if provided if !port_map.is_empty() { for port_map in port_map { child_args.push(format!("--port-map={}", port_map)); } } // Set scope if provided if let Some(scope) = scope { child_args.push(format!("--scope={}", scope)); } // Set ip if provided if let Some(ip) = ip { child_args.push(format!("--ip={}", ip)); } // Set subnet if provided if let Some(subnet) = subnet { child_args.push(format!("--subnet={}", subnet)); } // Set log level if provided if let Some(log_level) = log_level { child_args.push(format!("--log-level={}", log_level)); } // Set args if provided if !args.is_empty() { child_args.push("--".to_string()); for arg in args { child_args.push(arg); } } // Compose child environment variables let mut child_envs = Vec::<(String, String)>::new(); // Only pass RUST_LOG if it's set in the environment if let Ok(rust_log) = std::env::var("RUST_LOG") { tracing::debug!("using existing RUST_LOG: {:?}", rust_log); child_envs.push(("RUST_LOG".to_string(), rust_log)); } // Create and start supervisor let mut supervisor = Supervisor::new(child_exe, child_args, child_envs, log_dir, process_monitor); supervisor.start().await?; } } // NOTE: Force exit to make process actually exit when supervisor runs a child in TTY mode. // Otherwise, the process will not exit by itself and will wait for enter key to be pressed. std::process::exit(0); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/microsandbox/microsandbox'

If you have feedback or need assistance with the MCP directory API, please join our Discord server