Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162//! Native bridge to macOS Virtualization.framework for code-browser's local//! sandboxes. Loaded by the Electron main process as a napi addon (feature//! `node`); `vz` is also used directly by the `vzbench` binary.
#[cfg(target_os = "macos")]pub mod vz;
#[cfg(feature = "node")]mod node { use napi_derive::napi;
/// Whether this host can run Virtualization.framework VMs at all. /// (The process also needs the `com.apple.security.virtualization` /// entitlement to actually start one.) #[napi] pub fn is_supported() -> bool { #[cfg(target_os = "macos")] unsafe { objc2_virtualization::VZVirtualMachine::isSupported() } #[cfg(not(target_os = "macos"))] false }}
/// napi surface over `vz::Vm`. All blocking work runs on libuv's threadpool/// (AsyncTask), never on Electron's main thread.#[cfg(all(feature = "node", target_os = "macos"))]mod vm_node { use std::path::PathBuf; use std::sync::Arc; use std::time::Duration;
use napi::bindgen_prelude::AsyncTask; use napi::{Env, Error, Result, Task}; use napi_derive::napi;
use crate::vz;
#[napi(object)] pub struct ShareOptions { pub tag: String, pub path: Option<String>, pub read_only: bool, }
#[napi(object)] pub struct DiskOptions { pub path: String, pub read_only: bool, }
#[napi(object)] pub struct VmOptions { pub kernel: String, pub initrd: Option<String>, pub cmdline: String, pub cpus: u32, pub memory_mib: u32, pub shares: Vec<ShareOptions>, /// virtio-blk disks in order: /dev/vda, /dev/vdb, ... pub disks: Option<Vec<DiskOptions>>, pub console_log: Option<String>, }
enum Op { Start, Stop, SetShare { tag: String, path: Option<PathBuf>, read_only: bool }, Connect { port: u32, timeout_ms: u32 }, }
pub struct VmTask { vm: Arc<vz::Vm>, op: Op, }
impl Task for VmTask { type Output = i32; type JsValue = i32;
fn compute(&mut self) -> Result<i32> { let r = match &self.op { Op::Start => self.vm.start().map(|_| 0), Op::Stop => self.vm.stop().map(|_| 0), Op::SetShare { tag, path, read_only } => self.vm.set_share(tag, path.as_deref(), *read_only).map(|_| 0), Op::Connect { port, timeout_ms } => { use std::os::fd::IntoRawFd; self.vm .wait_for_port(*port, Duration::from_millis(*timeout_ms as u64)) .map(|s| s.into_raw_fd()) } }; r.map_err(Error::from_reason) }
fn resolve(&mut self, _env: Env, out: i32) -> Result<i32> { Ok(out) } }
#[napi] pub struct VzVm { vm: Arc<vz::Vm>, }
#[napi] impl VzVm { #[napi(constructor)] pub fn new(opts: VmOptions) -> Result<Self> { let cfg = vz::VmConfig { kernel: opts.kernel.into(), initrd: opts.initrd.map(Into::into), cmdline: opts.cmdline, cpus: opts.cpus as usize, memory_mib: opts.memory_mib as u64, shares: opts .shares .into_iter() .map(|s| vz::Share { tag: s.tag, path: s.path.map(Into::into), read_only: s.read_only }) .collect(), disks: opts .disks .unwrap_or_default() .into_iter() .map(|d| vz::Disk { path: d.path.into(), read_only: d.read_only }) .collect(), console_log: opts.console_log.map(Into::into), machine_id: None, }; Ok(VzVm { vm: Arc::new(vz::Vm::new(&cfg).map_err(Error::from_reason)?) }) }
fn task(&self, op: Op) -> AsyncTask<VmTask> { AsyncTask::new(VmTask { vm: self.vm.clone(), op }) }
#[napi(ts_return_type = "Promise<void>")] pub fn start(&self) -> AsyncTask<VmTask> { self.task(Op::Start) }
#[napi(ts_return_type = "Promise<void>")] pub fn stop(&self) -> AsyncTask<VmTask> { self.task(Op::Stop) }
/// Point a virtio-fs device at a host directory (null detaches). Works while running. #[napi(ts_return_type = "Promise<void>")] pub fn set_share(&self, tag: String, path: Option<String>, read_only: bool) -> AsyncTask<VmTask> { self.task(Op::SetShare { tag, path: path.map(Into::into), read_only }) }
/// Connect to a guest vsock port, retrying until it listens. Resolves to a /// socket fd owned by the caller (wrap with `new net.Socket({ fd })`). #[napi(ts_return_type = "Promise<number>")] pub fn connect(&self, port: u32, timeout_ms: u32) -> AsyncTask<VmTask> { self.task(Op::Connect { port, timeout_ms }) } }}