Node helpers and utilities
The terminal, QMP servers and snapshot files for Node, and the parsers, encoders and errors both platforms share.
warm64/wemu-node
Node's host and the pieces the wemu-system-aarch64 command is built from. Import WEMU itself from warm64.
import { WEMU } from "warm64";
import { nodeWEMUHost, attachStdio, serveQmp, fileSnapshots } from "warm64/wemu-node";nodeWEMUHost()
The host for Node. See Hosts.
attachStdio()
Connects the terminal to the machine, as -serial stdio and -nographic do: the console on standard input and output, the terminal in raw mode, and the Ctrl+A keys (x quits, c switches to the monitor, b sends a break, h lists them).
attachStdio(vm, { monitor?: boolean, monitorOnly?: boolean }): () => void| Option | Description |
|---|---|
monitor | Let Ctrl+A c switch to the monitor, as -nographic does. |
monitorOnly | Start in the monitor, as -monitor stdio does. |
Returns a function that detaches the terminal again.
const vm = await WEMU.launch("-m 1G -kernel vmlinuz-virt -initrd initramfs-virt -append console=ttyAMA0 -nographic", nodeWEMUHost());
attachStdio(vm, { monitor: true });
process.exitCode = (await vm.wait()).code;serveQmp()
Serves QMP to outside clients, as -qmp does.
serveQmp(vm, endpoint): Promise<{ close(): void }>endpoint is a parsed -qmp value: a Unix socket, a TCP port or stdio. Every client gets the QMP greeting, negotiates with qmp_capabilities, and from then on receives every event.
import { parseWEMUArgs } from "warm64";
const { qmp } = parseWEMUArgs("-qmp unix:/tmp/wemu.sock,server,nowait -kernel vmlinuz-virt");
const server = await serveQmp(vm, qmp[0]);fileSnapshots()
A snapshot store that keeps each savevm as a file named NAME.warm64snap in a directory. Characters other than letters, digits, ., _ and - become _.
const host = { ...nodeWEMUHost(), snapshots: fileSnapshots("./snapshots") };nodeWEMUHost({ snapshotDir }) does the same.
main()
The whole wemu-system-aarch64 command: parses argv, launches, attaches the terminal and QMP servers, and resolves with the exit status.
process.exitCode = await main(process.argv.slice(2));imgMain()
The whole wemu-img command: imgMain(argv) returns the exit status.
Utilities
Exported from warm64.
parseWEMUArgs()
parseWEMUArgs(args: string | string[]): WEMUConfigParses a command line the way WEMU.launch() does, without launching anything, and returns the WEMUConfig a machine would be built from. Throws WEMUError for a line that would be refused.
splitCommandLine()
splitCommandLine(line: string): string[]Splits a command line as a POSIX shell would (quotes and backslashes), dropping a leading qemu-system-* or wemu-system-*.
parseSubopts()
parseSubopts(value: string, impliedKey?: string): Record<string, string>Splits QEMU's key=value,key=value form, where ,, is a literal comma and a leading bare word belongs to impliedKey.
parseSize()
parseSize(value: string, unit = 1): number"1G" to bytes. A bare number is multiplied by unit.
encodePng() and encodePpm()
encodePng(width: number, height: number, pixels: Uint8Array): Uint8Array
encodePpm(width: number, height: number, pixels: Uint8Array): Uint8ArrayEncode a8r8g8b8 pixels (as a display's frame receives them) as a PNG or a PPM image, the formats screendump writes.
QCODE_TO_LINUX
QEMU's key names ("ret", "a", "ctrl"…) to Linux key codes, as send-key and input-send-event use them.
DEVICE_DRIVERS
The -device drivers this machine accepts, and the kind of each.
VERSION
The package version, "0.1.0".
WEMUConfig
The parsed command line: what parseWEMUArgs() returns and vm.config holds. WEMU.launch() accepts one in place of a command line.
interface WEMUConfig {
machine: { type: "virt"; gicVersion: 2 | 3; virtualization: boolean; iommu: "none" | "smmuv3"; dumpdtb?: string };
cpu: { model: string; off: Set<string>; max: boolean }; // off: features turned off, such as "sve"
accel: "tcg";
memory: number; // bytes
smp: { cpus: number };
kernel?: string; initrd?: string; append: string; dtb?: string; bios?: string;
pflash: string[];
drives: WEMUDrive[];
netdevs: WEMUNetdev[];
devices: WEMUDevice[];
chardevs: WEMUChardev[];
fsdevs: WEMUFsdev[];
serials: WEMUSerial[];
monitor: WEMUMonitor;
qmp: WEMUQmp[];
display: "none" | "gui";
gl?: boolean;
audiodevs: { id: string; driver: string; path?: string }[];
nographic: boolean;
startPaused: boolean; // -S
actions: WEMUActions;
snapshot: boolean; // -snapshot
rtc: { base: "utc" | "localtime" | number; clock: "host" | "vm" | "rt" };
name?: string; uuid?: string;
boot?: { order?: string; menu?: boolean };
loadvm?: string; incoming?: string;
usb: boolean;
version: boolean; help: boolean;
ignored: string[]; // options accepted and ignored, as written
}The parts:
interface WEMUDrive {
id: string; file?: string; if: "virtio" | "none" | "pflash"; format: string;
readonly: boolean; discard: "ignore" | "unmap"; snapshot: boolean; media: "disk" | "cdrom";
unit?: number; bound: boolean; // bound: named by a -device
}
interface WEMUNetdev {
id: string; type: "user" | "socket"; connect?: string;
net?: string; host?: string; dns?: string; dhcpstart?: string; bound: boolean;
}
interface WEMUDevice { driver: string; props: Record<string, string> }
interface WEMUChardev {
id: string; backend: "stdio" | "null" | "file" | "socket" | "pty" | "memory" | "ringbuf";
props: Record<string, string>;
}
interface WEMUFsdev { id: string; path?: string; mountTag?: string; bound: boolean }
type WEMUSerial = { kind: "stdio"; monitor: boolean } | { kind: "none" } | { kind: "null" }
| { kind: "file"; path: string } | { kind: "chardev"; id: string } | { kind: "vc" };
type WEMUMonitor = { kind: "stdio" } | { kind: "none" } | { kind: "chardev"; id: string; mode: "readline" | "control" };
type WEMUQmp = { kind: "unix"; path: string; server: boolean; wait: boolean }
| { kind: "tcp"; host: string; port: number; server: boolean; wait: boolean }
| { kind: "stdio" } | { kind: "chardev"; id: string };
interface WEMUActions {
shutdown: "poweroff" | "pause"; reboot: "reset" | "shutdown"; panic: "pause" | "shutdown" | "none";
}Types
Every type the package exports, and where it's described:
| Type | From | Described in |
|---|---|---|
WEMUHost, WEMUCoreKind, WasmSource | warm64 | Hosts |
WEMUDiskFile, DiskBacking | warm64 | openDisk |
WEMUDisplay, WEMUAudio, WEMUSnapshotStore | warm64 | display, audio, snapshots |
Connector, StackSocket, SocketHandlers | warm64 | connector |
BrowserWEMUHostOptions | warm64 | browserWEMUHost() |
NodeWEMUHostOptions | warm64/wemu-node | nodeWEMUHost() |
WEMURunState, QmpEvent, WEMUShare | warm64 | WEMU |
WEMUConfig and its parts | warm64 | above |
Package entry points
| Import | What |
|---|---|
warm64 | WEMU, the browser host and everything shared. |
warm64/wemu-node | Node's host and helpers. |
warm64/wasm, warm64/wasm64, warm64/wasm-threads, warm64/wasm64-threads, warm64/wasm-gpu, warm64/wasm-gpu-threads | The core modules' files, for bundlers that copy assets. |
The commands wemu-system-aarch64 and wemu-img are the package's bin entries.
Errors
WEMUError
Thrown by WEMU.launch() and parseWEMUArgs() for a command line that is refused. Its message is what qemu-system-aarch64 would print, prefixed wemu-system-aarch64: .
try {
await WEMU.launch("-enable-kvm -kernel vmlinuz-virt", host);
} catch (e) {
if (e instanceof WEMUError) console.error(e.message);
// wemu-system-aarch64: KVM is not available here (the CPU is emulated, as with -accel tcg)
}QmpError
Rejects vm.qmp() and vm.hmp() when QEMU would answer with an error.
| Property | Description |
|---|---|
class | QEMU's error class: GenericError, CommandNotFound, DeviceNotFound… |
desc | The message. |