Skip to main content

mvm_apple_container/
lib.rs

1//! Apple Virtualization.framework backend for mvm.
2//!
3//! On macOS (Apple Silicon), this crate uses `objc2-virtualization` to call
4//! Virtualization.framework directly from Rust. VMs boot with VZLinuxBootLoader
5//! using our Nix-built kernel + ext4 rootfs — same as Firecracker, sub-second
6//! startup, no OCI, no XPC daemon, no Swift.
7//!
8//! On other platforms, all functions return "not available" errors.
9
10#[cfg(target_os = "macos")]
11mod macos;
12
13/// Check if Apple Virtualization is available on this platform.
14pub fn is_available() -> bool {
15    #[cfg(target_os = "macos")]
16    {
17        cfg!(target_arch = "aarch64")
18    }
19    #[cfg(not(target_os = "macos"))]
20    {
21        false
22    }
23}
24
25/// Start a VM from a local kernel + ext4 rootfs using Virtualization.framework.
26pub fn start(
27    id: &str,
28    kernel_path: &str,
29    rootfs_path: &str,
30    cpus: u32,
31    memory_mib: u64,
32) -> Result<(), String> {
33    #[cfg(target_os = "macos")]
34    {
35        macos::start_vm(id, kernel_path, rootfs_path, cpus, memory_mib)
36    }
37    #[cfg(not(target_os = "macos"))]
38    {
39        let _ = (id, kernel_path, rootfs_path, cpus, memory_mib);
40        Err("Apple Virtualization not available on this platform".to_string())
41    }
42}
43
44/// Stop a running VM.
45pub fn stop(id: &str) -> Result<(), String> {
46    #[cfg(target_os = "macos")]
47    {
48        macos::stop_vm(id)
49    }
50    #[cfg(not(target_os = "macos"))]
51    {
52        let _ = id;
53        Err("Apple Virtualization not available on this platform".to_string())
54    }
55}
56
57/// Install a launchd agent to run the VM in the background.
58/// Uses resolved kernel/rootfs paths directly (build already done).
59/// Ensure the binary has the virtualization entitlement, signing if needed.
60/// On non-macOS this is a no-op.
61pub fn ensure_signed() {
62    #[cfg(target_os = "macos")]
63    {
64        macos::ensure_signed();
65    }
66}
67
68pub fn install_launchd_direct(
69    id: &str,
70    kernel_path: &str,
71    rootfs_path: &str,
72    cpus: u32,
73    memory_mib: u64,
74    ports: &[String],
75) -> Result<(), String> {
76    #[cfg(target_os = "macos")]
77    {
78        macos::install_launchd_direct(id, kernel_path, rootfs_path, cpus, memory_mib, ports)
79    }
80    #[cfg(not(target_os = "macos"))]
81    {
82        let _ = (id, kernel_path, rootfs_path, cpus, memory_mib, ports);
83        Err("launchd not available on this platform".to_string())
84    }
85}
86
87/// Discover the guest's IP address via ARP scanning.
88pub fn discover_guest_ip(timeout_secs: u64) -> Option<String> {
89    #[cfg(target_os = "macos")]
90    {
91        macos::discover_guest_ip(std::time::Duration::from_secs(timeout_secs))
92    }
93    #[cfg(not(target_os = "macos"))]
94    {
95        let _ = timeout_secs;
96        None
97    }
98}
99
100/// Start a port proxy from localhost:host_port to guest tcp/guest_port via vsock.
101pub fn start_port_proxy(vm_id: &str, host_port: u16, guest_port: u16) {
102    #[cfg(target_os = "macos")]
103    {
104        macos::start_port_proxy(vm_id, host_port, guest_port);
105    }
106    #[cfg(not(target_os = "macos"))]
107    {
108        let _ = (vm_id, host_port, guest_port);
109    }
110}
111
112/// Connect to the guest vsock on the given port, returning a Unix stream.
113///
114/// The VM must have been started in this process (in-process VM tracking).
115/// Returns a `UnixStream` wrapping the vsock connection's file descriptor.
116pub fn vsock_connect(id: &str, port: u32) -> Result<std::os::unix::net::UnixStream, String> {
117    #[cfg(target_os = "macos")]
118    {
119        macos::vsock_connect(id, port)
120    }
121    #[cfg(not(target_os = "macos"))]
122    {
123        let _ = (id, port);
124        Err("Apple Virtualization not available on this platform".to_string())
125    }
126}
127
128/// Guest agent vsock port.
129pub const GUEST_AGENT_PORT: u32 = 52;
130
131/// List running VM IDs.
132pub fn list_ids() -> Vec<String> {
133    #[cfg(target_os = "macos")]
134    {
135        macos::list_vm_ids()
136    }
137    #[cfg(not(target_os = "macos"))]
138    {
139        vec![]
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn test_is_available() {
149        let _ = is_available();
150    }
151
152    #[test]
153    fn test_list_ids_empty() {
154        // No VMs running in test
155        let _ = list_ids();
156    }
157}