vm

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 21, 2026 License: AGPL-3.0 Imports: 16 Imported by: 0

Documentation

Overview

Package vm provides VM lifecycle management including the Backend interface abstraction, instance types, state persistence, and QMP communication.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CleanupVMDir

func CleanupVMDir(inst *Instance)

CleanupVMDir removes the VM directory. Used by the daemon when vmhost spawn fails and the VM directory needs to be cleaned up.

func DefaultBackend

func DefaultBackend() string

DefaultBackend returns "qemu" on non-Apple-Silicon platforms.

func MixtapesDir

func MixtapesDir(baseDir string) string

MixtapesDir returns the path to the mixtapes directory.

func PrepareAppleVirtDisk

func PrepareAppleVirtDisk(_ string, _ *Instance) error

PrepareAppleVirtDisk is not available on this platform.

func PrepareQEMUDisk

func PrepareQEMUDisk(baseDir string, inst *Instance, platform *QEMUPlatformConfig) error

PrepareQEMUDisk creates the VM directory and sets up the disk image for a QEMU-backed VM. This runs in the daemon before spawning the vmhost process. It handles:

  • Creating the VM directory
  • Resolving the mixtape image
  • Creating qcow2 overlay or copying raw image
  • Initializing EFI vars (if needed)
  • Saving jcard.toml
  • Writing initial state.json

The inst.Dir field is set on the instance upon return.

func ResolveBackend

func ResolveBackend(baseDir string, cfg interface{ GetMixtape() string }, requestedBackend string) string

ResolveBackend determines the backend type for a VM. Precedence:

  1. Explicit requestedBackend parameter (from CLI flag or config)
  2. MB_BACKEND environment variable
  3. Platform default: "applevirt" on darwin/arm64, "qemu" elsewhere

func ResolveMixtapePath

func ResolveMixtapePath(baseDir, mixtape string) (string, error)

ResolveMixtapePath resolves a mixtape reference to a disk image path. The mixtape string can be "name" (implies :latest) or "name:tag". The on-disk layout is mixtapes/{name}/{tag}/.

It searches for disk images in the following order:

  1. stereos.img (raw, preferred for Apple Virt framework -- from OCI pull)
  2. stereos.qcow2 (qcow2, QEMU -- from OCI pull)
  3. nixos.img (raw, legacy naming)
  4. nixos.qcow2 (qcow2, legacy naming)
  5. mixtapeDir itself as a bare image file

func VMsDir

func VMsDir(baseDir string) string

VMsDir returns the path to the VMs directory.

Types

type Backend

type Backend interface {
	// Up creates and starts a new sandbox VM from the given instance configuration.
	// It creates the VM directory, disk overlay, boots the hypervisor, waits for
	// stereosd to become ready, injects secrets, and mounts shared directories.
	Up(ctx context.Context, inst *Instance) error

	// Start re-boots an existing stopped sandbox. The VM directory and disk
	// must already exist from a previous Up call. It allocates new ports,
	// starts the hypervisor, and runs post-boot provisioning.
	Start(ctx context.Context, inst *Instance) error

	// Down gracefully stops the VM. Sends shutdown via vsock to stereosd first,
	// then falls back to ACPI shutdown via QMP, then force kill after timeout.
	Down(ctx context.Context, inst *Instance, timeout time.Duration) error

	// ForceDown immediately terminates the VM process.
	ForceDown(ctx context.Context, inst *Instance) error

	// Destroy stops the VM (if running) and removes all its on-disk resources.
	Destroy(ctx context.Context, inst *Instance) error

	// Status returns the current state of the VM.
	Status(ctx context.Context, inst *Instance) (State, error)

	// List returns all known VM instances by scanning the vms directory.
	List(ctx context.Context) ([]*Instance, error)

	// LoadInstance reads a persisted VM instance by name.
	LoadInstance(name string) (*Instance, error)
}

Backend abstracts the VM hypervisor. QEMU today, Apple Virtualization.framework and KVM/libvirt in the future. All VM operations go through this interface.

func NewPlatformBackend

func NewPlatformBackend(baseDir string) (Backend, error)

NewPlatformBackend returns the appropriate VM backend for Linux. This returns the QEMU backend configured for KVM acceleration with native vsock support via vhost-vsock-pci.

type Instance

type Instance struct {
	// Name is the unique human-readable name for this sandbox.
	Name string `json:"name"`

	// Dir is the path to this VM's resource directory (~/.mb/vms/<name>/).
	Dir string `json:"dir"`

	// PID is the hypervisor process ID (e.g. QEMU PID).
	PID int `json:"pid"`

	// QMPSocket is the path to the QMP unix socket (QEMU only).
	QMPSocket string `json:"qmp_socket"`

	// VsockPort is the host-side port for vsock communication with stereosd.
	// For QEMU with TCP fallback, this is a TCP port on localhost.
	VsockPort int `json:"vsock_port"`

	// SSHPort is the host port forwarded to guest port 22.
	SSHPort int `json:"ssh_port"`

	// VMState is the current lifecycle state.
	VMState State `json:"state"`

	// SSHKeyPath is the path to the ephemeral SSH private key for this
	// sandbox, stored in the VM directory. Used by `mb ssh` to pass
	// -i <key> to OpenSSH.
	SSHKeyPath string `json:"ssh_key_path,omitempty"`

	// Config is the jcard configuration for this instance.
	Config *config.JcardConfig `json:"-"`
	// contains filtered or unexported fields
}

Instance represents a single VM and its on-disk resources.

func LoadInstanceFromDisk

func LoadInstanceFromDisk(baseDir, name string) (*Instance, error)

LoadInstanceFromDisk reads a persisted VM instance by name from the vms directory, regardless of backend type.

func (*Instance) DiskPath

func (inst *Instance) DiskPath() string

DiskPath returns the path to this VM's disk image.

func (*Instance) EFIVarsPath

func (inst *Instance) EFIVarsPath() string

EFIVarsPath returns the path to this VM's writable EFI variable store.

func (*Instance) JcardPath

func (inst *Instance) JcardPath() string

JcardPath returns the path to the saved jcard.toml for this instance.

func (*Instance) LoadState

func (inst *Instance) LoadState() (*StateFile, error)

LoadState reads the state file for this instance.

func (*Instance) PIDFilePath

func (inst *Instance) PIDFilePath() string

PIDFilePath returns the path to the hypervisor PID file.

func (*Instance) QCOWDiskPath

func (inst *Instance) QCOWDiskPath() string

QCOWDiskPath returns the path to this VM's qcow2 overlay disk.

func (*Instance) SerialLogPath

func (inst *Instance) SerialLogPath() string

SerialLogPath returns the path to this VM's serial console log.

func (*Instance) VMHostLogPath

func (inst *Instance) VMHostLogPath() string

VMHostLogPath returns the path to the vmhost log file.

func (*Instance) VMHostPIDPath

func (inst *Instance) VMHostPIDPath() string

VMHostPIDPath returns the path to the vmhost process PID file.

func (*Instance) VMHostSocketPath

func (inst *Instance) VMHostSocketPath() string

VMHostSocketPath returns the path to the vmhost control socket.

type KernelArtifacts

type KernelArtifacts struct {
	Kernel  string // Path to bzImage
	Initrd  string // Path to initrd
	Cmdline string // Kernel command line (contents of cmdline file)
}

KernelArtifacts holds the resolved paths for direct kernel boot. These correspond to the bzImage, initrd, and cmdline files produced by the stereOS build (system.build.kernelArtifacts in image.nix).

func ResolveKernelArtifacts

func ResolveKernelArtifacts(baseDir, mixtape string) *KernelArtifacts

ResolveKernelArtifacts checks if a mixtape has kernel artifacts for direct kernel boot. It looks for bzImage, initrd, and cmdline files in the resolved mixtape directory.

The mixtape string can be "name" (implies latest) or "name:tag". The on-disk layout is mixtapes/{name}/{tag}/.

Returns nil if kernel artifacts are not available (caller should fall back to EFI boot).

type QEMUBackend

type QEMUBackend struct {
	// contains filtered or unexported fields
}

QEMUBackend implements the Backend interface using QEMU. Platform-specific behavior (acceleration, vsock transport, EFI paths) is configured via QEMUPlatformConfig, injected at construction time by the build-tagged NewPlatformBackend() functions.

In the vmhost architecture, each QEMU VM runs as a child process of a dedicated vmhost process. QEMU no longer daemonizes; the vmhost process owns its lifecycle.

func NewQEMUBackend

func NewQEMUBackend(baseDir string, platform *QEMUPlatformConfig) *QEMUBackend

NewQEMUBackend creates a new QEMU backend with the given base directory and platform-specific configuration.

func (*QEMUBackend) Boot

func (q *QEMUBackend) Boot(ctx context.Context, inst *Instance) error

Boot is the exported entry point for vmhost processes. It allocates ports, starts QEMU as a child process, and runs post-boot provisioning (stereosd handshake, secret injection, shared directory mounting). The VM directory and disk must already exist (created by the daemon).

func (*QEMUBackend) Destroy

func (q *QEMUBackend) Destroy(ctx context.Context, inst *Instance) error

Destroy stops the VM and removes all resources.

func (*QEMUBackend) Down

func (q *QEMUBackend) Down(ctx context.Context, inst *Instance, timeout time.Duration) error

Down gracefully stops the VM.

func (*QEMUBackend) ForceDown

func (q *QEMUBackend) ForceDown(_ context.Context, inst *Instance) error

ForceDown immediately terminates the VM process.

func (*QEMUBackend) List

func (q *QEMUBackend) List(ctx context.Context) ([]*Instance, error)

List returns all VM instances owned by the QEMU backend by scanning the vms directory. VMs with a different backend field in state.json are skipped so that multiple backends can coexist in the same base directory.

func (*QEMUBackend) LoadInstance

func (q *QEMUBackend) LoadInstance(name string) (*Instance, error)

LoadInstance reads a persisted VM instance by name.

func (*QEMUBackend) Start

func (q *QEMUBackend) Start(ctx context.Context, inst *Instance) error

Start re-boots an existing stopped sandbox. The VM directory and disk are reused from a previous Up call. It re-reads the saved jcard.toml from the VM directory, allocates new ports, and boots QEMU.

func (*QEMUBackend) Status

func (q *QEMUBackend) Status(_ context.Context, inst *Instance) (State, error)

Status returns the current state of the VM.

func (*QEMUBackend) Up

func (q *QEMUBackend) Up(ctx context.Context, inst *Instance) error

Up creates and starts a new sandbox VM from the given instance configuration. It creates the VM directory, disk, and EFI vars from scratch, then delegates to boot() for port allocation, QEMU launch, and post-boot provisioning.

func (*QEMUBackend) WaitQEMU

func (q *QEMUBackend) WaitQEMU() error

WaitQEMU blocks until the QEMU child process exits. Returns nil on clean exit, or an error if QEMU crashed.

type QEMUPlatformConfig

type QEMUPlatformConfig struct {
	// Accelerator is the QEMU acceleration method: "hvf", "kvm", or "tcg".
	Accelerator string

	// Binary is the QEMU system emulator binary name (e.g.,
	// "qemu-system-aarch64" for ARM64 guests).
	Binary string

	// MachineType is the QEMU machine type (e.g., "virt" for ARM64,
	// "q35" for x86_64). Defaults to "virt" if empty.
	MachineType string

	// EFISearchPaths is an ordered list of paths to search for EFI firmware.
	// The special prefix "{qemu_prefix}" is replaced at runtime with the
	// resolved QEMU installation prefix (parent of the bin/ directory).
	// The first existing file wins.
	EFISearchPaths []string

	// ControlPlaneMode controls how the host communicates with stereosd
	// inside the guest VM.
	//
	//   "tcp"   -- Forward stereosd port via QEMU user-mode networking
	//              (hostfwd). Works on all platforms but the control plane
	//              shares the guest network stack. This is the only option
	//              on macOS/HVF where vsock devices are unavailable.
	//
	//   "vsock" -- Attach a vhost-vsock device and connect via AF_VSOCK.
	//              Requires Linux/KVM. Provides an isolated control plane
	//              that works even with network.mode = "none".
	//
	// Future modes: "virtio-serial" (chardev unix socket via
	// virtio-serial-device, works on macOS without guest networking).
	ControlPlaneMode string

	// VsockDevice is the QEMU device model name for native vsock.
	// Only used when ControlPlaneMode is "vsock".
	// Typically "vhost-vsock-pci" on x86 and "vhost-vsock-device" on ARM.
	VsockDevice string

	// DirectKernelBoot enables -kernel/-initrd/-append boot, bypassing
	// EFI firmware and GRUB. Requires kernel artifacts (bzImage, initrd,
	// cmdline) in the mixtape directory. When enabled and kernel artifacts
	// are available, EFI pflash drives and EFI vars initialization are
	// skipped entirely. Falls back to EFI boot if artifacts are missing.
	DirectKernelBoot bool

	// DiskAIO is the async I/O backend for QEMU disk drives.
	// Set to "io_uring" on Linux 5.1+ for best performance, or leave
	// empty to use QEMU's default (threads). Not supported on macOS.
	DiskAIO string

	// DiskCache is the cache mode for QEMU disk drives.
	// Set to "none" (O_DIRECT) when using io_uring for best performance,
	// or leave empty to use QEMU's default (writeback).
	DiskCache string
}

QEMUPlatformConfig contains platform-specific settings for the QEMU backend. It is populated by the build-tagged NewPlatformBackend() functions and injected into QEMUBackend at construction time.

This struct captures the differences between running QEMU on macOS/HVF (Apple Silicon) vs. Linux/KVM, without scattering platform conditionals throughout the QEMU backend code.

func (*QEMUPlatformConfig) DefaultMachineType

func (p *QEMUPlatformConfig) DefaultMachineType() string

DefaultMachineType returns the machine type, defaulting to "virt".

type QMPClient

type QMPClient struct {
	// contains filtered or unexported fields
}

QMPClient communicates with a running QEMU instance over its QMP (QEMU Machine Protocol) unix domain socket.

func DialQMP

func DialQMP(socketPath string) (*QMPClient, error)

DialQMP connects to a QMP unix socket, reads the greeting, and negotiates capabilities to enter command mode.

func (*QMPClient) Close

func (c *QMPClient) Close() error

Close closes the QMP connection.

func (*QMPClient) QueryStatus

func (c *QMPClient) QueryStatus() (string, error)

QueryStatus returns the VM run state (e.g. "running", "paused", "shutdown").

func (*QMPClient) Quit

func (c *QMPClient) Quit() error

Quit force-kills the VM process.

func (*QMPClient) Shutdown

func (c *QMPClient) Shutdown() error

Shutdown sends ACPI power-off (clean shutdown, systemd handles it).

type State

type State string

State represents the lifecycle state of a VM.

const (
	StateCreated State = "created"
	StateRunning State = "running"
	StateStopped State = "stopped"
	StateError   State = "error"
)

type StateFile

type StateFile struct {
	Name        string    `json:"name"`
	CreatedAt   time.Time `json:"created_at"`
	Mixtape     string    `json:"mixtape"`
	CPUs        int       `json:"cpus"`
	Memory      string    `json:"memory"`
	Disk        string    `json:"disk"`
	NetworkMode string    `json:"network_mode"`
	SSHPort     int       `json:"ssh_port"`
	VsockPort   int       `json:"vsock_port"`
	ConfigPath  string    `json:"config_path,omitempty"`

	// SSHKeyPath is the path to the ephemeral SSH private key, relative
	// to the VM directory or absolute. Persisted so the key survives
	// daemon restarts.
	SSHKeyPath string `json:"ssh_key_path,omitempty"`

	// Backend identifies which Backend implementation owns this VM.
	Backend string `json:"backend,omitempty"`

	// PlatformData holds opaque backend-specific persistent state.
	// Each backend can use this to store identity or configuration data
	// that must survive across daemon restarts and up/down cycles.
	//
	// Examples:
	//   - Apple Virt: serialized vz.GenericMachineIdentifier (gives the
	//     guest a stable MAC address, EFI NVRAM, etc.)
	//   - QEMU: could store a generated machine UUID for SMBIOS identity
	//     (-smbios type=1,uuid=...) if needed in the future.
	PlatformData []byte `json:"platform_data,omitempty"`
}

StateFile is the persistent metadata for a VM stored in state.json.

func LoadStateFromDisk

func LoadStateFromDisk(baseDir, name string) (*StateFile, error)

LoadStateFromDisk reads the state.json for a named VM.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL