#!/usr/bin/env bash # Build and boot the desktop configuration in a QEMU VM (see modules/nixos/vm.nix). # # ./vm.sh build and start the VM # ./vm.sh --fresh the same, but start from an empty VM disk # # The VM renders on the host GPU through virgl. QEMU's own window can't get a GL # context on the NVIDIA driver, so it renders headless on a Mesa GPU (the # Ryzen's integrated Radeon here) and is shown over VNC on this machine only: # vncviewer localhost:5905 (e.g. nix run nixpkgs#tigervnc -- localhost:5905) # # The VM's disk is desktop.qcow2 in this directory (git ignores it). A QEMU # monitor socket, vm-monitor.sock, takes screenshots and key presses from # scripts, e.g.: echo 'screendump shot.ppm' | socat - UNIX-CONNECT:vm-monitor.sock # The VM's console (boot messages, then a login prompt) goes to vm-serial.log. # # SSH: ssh -p 2222 vizie@localhost, with the key in $VM_SSH_KEY (a .pub file, # default ~/.ssh/id_ed25519.pub). The key is shared with this VM only. set -euo pipefail cd "$(dirname "$(readlink -f "$0")")" here=$PWD if [ "${1:-}" = --fresh ]; then rm -f desktop.qcow2 fi # The first render node whose driver isn't nvidia. rendernode= for node in /dev/dri/renderD*; do driver=$(basename "$(readlink -f "/sys/class/drm/$(basename "$node")/device/driver")") if [ "$driver" != nvidia ]; then rendernode=$node; break; fi done if [ -z "$rendernode" ]; then echo "No non-NVIDIA GPU found for the VM to render on." >&2 exit 1 fi vm=$(nix build --no-link --print-out-paths .#nixosConfigurations.desktop.config.system.build.vm) # Outside NixOS (e.g. on CachyOS, before the switch) there is no # /run/opengl-driver for the Nix-built QEMU to find GPU drivers in; give it the # Mesa from the same nixpkgs. if [ ! -e /run/opengl-driver ]; then mesa=$(nix build --no-link --print-out-paths .#nixosConfigurations.desktop.pkgs.mesa) export GBM_BACKENDS_PATH=$mesa/lib/gbm export LIBGL_DRIVERS_PATH=$mesa/lib/dri export __EGL_VENDOR_LIBRARY_FILENAMES=$mesa/share/glvnd/egl_vendor.d/50_mesa.json fi export NIX_DISK_IMAGE="$here/desktop.qcow2" # A temporary folder for the VM's sockets and /tmp/xchg, with the SSH key in it. vmtmp=$(mktemp -d -t dot-vm.XXXXXXXX) trap 'rm -rf -- "${vmtmp:?}"' EXIT export TMPDIR=$vmtmp USE_TMPDIR=1 mkdir -p "$vmtmp/xchg" key=${VM_SSH_KEY:-$HOME/.ssh/id_ed25519.pub} if [ -f "$key" ]; then install -m644 "$key" "$vmtmp/xchg/authorized_keys"; fi export QEMU_OPTS="${QEMU_OPTS:-} -display egl-headless,rendernode=$rendernode -vnc 127.0.0.1:5 -k no -monitor unix:$here/vm-monitor.sock,server,nowait -serial file:$here/vm-serial.log" echo "VM starting; watch it with: vncviewer localhost:5905" "$vm/bin/run-desktop-vm"