patches and low-level development discussion
 help / color / mirror / code / Atom feed
From: Alyssa Ross <hi@alyssa.is>
To: devel@spectrum-os.org
Subject: [PATCH v2 6/6] img/app: extract from appvm-{lynx,catgirl}
Date: Sun,  9 Oct 2022 11:40:36 +0000	[thread overview]
Message-ID: <20221009114036.463071-7-hi@alyssa.is> (raw)
In-Reply-To: <20221009114036.463071-1-hi@alyssa.is>

This patch introduces a generic application VM image.  It mounts a
filesystem containing the application, and then launches a "run"
script on that filesystem to start the application.  The Nix store on
the application filesystem is overlaid onto the generic Nix store, so
shared paths don't have to be duplicated in the application
filesystem.

The "appvm" image is part of the Spectrum system — it lives on the
root filesystem, not the user data partition.  Users of course have
the choice not to use the built in image if they don't want to, but
this gives us a default to use for future features like starting VMs
at runtime.

Individual application VMs are now defined in a single Nix file each,
using a VM builder function.  I expect this is how Nix-based VMs would
be defined in the user data partition, and then built with Nix into
Spectrum VM configurations.

The new top-level vm-lib directory is intended to be copied into user
Nix expressions that build VMs, and therefore has to be usable
standalone.  User-defined VMs should not do any path deduplication
with the system-provided base VM, so that the two can independently
update Nixpkgs.  But for VMs that are part of the system (which I
consider the pre-built VMs to be, even though they're currently on the
user data partition), we can safely deduplicate paths that we know to
be present in the base image.  So they go through vm/make-vm.nix,
which is a wrapper around vm-lib/make-vm.nix that does this
deduplication.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
---
 host/initramfs/extfs.nix                      |  19 +--
 host/rootfs/default.nix                       |  11 +-
 {vm/app/lynx => img/app}/Makefile             |  50 ++++---
 {vm/app/catgirl => img/app}/bin               |   0
 {vm/app/lynx => img/app}/default.nix          |  18 +--
 img/app/etc/fstab                             |   8 ++
 {vm/app/catgirl => img/app}/etc/init          |   0
 {vm/app/catgirl => img/app}/etc/mdev.conf     |   0
 {vm/app/lynx => img/app}/etc/mdev/iface       |   2 +-
 {vm/app/catgirl => img/app}/etc/passwd        |   0
 .../catgirl => img/app}/etc/passwd.license    |   0
 {vm/app/catgirl => img/app}/etc/resolv.conf   |   0
 .../app}/etc/s6-linux-init/scripts/rc.init    |   1 +
 .../s6-rc/lynx => img/app/etc/s6-rc/app}/run  |   3 +-
 .../catgirl => img/app/etc/s6-rc/app}/type    |   0
 .../app/etc/s6-rc/app}/type.license           |   0
 .../etc/s6-rc/mdevd-coldplug/dependencies     |   0
 .../app}/etc/s6-rc/mdevd-coldplug/type        |   0
 .../etc/s6-rc/mdevd-coldplug/type.license     |   0
 .../app}/etc/s6-rc/mdevd-coldplug/up          |   0
 .../app}/etc/s6-rc/mdevd/notification-fd      |   0
 .../etc/s6-rc/mdevd/notification-fd.license   |   0
 .../catgirl => img/app}/etc/s6-rc/mdevd/run   |   0
 .../catgirl => img/app}/etc/s6-rc/mdevd/type  |   0
 .../app}/etc/s6-rc/mdevd/type.license         |   0
 .../app}/etc/s6-rc/ok-all/contents            |   0
 .../catgirl => img/app}/etc/s6-rc/ok-all/type |   0
 .../app}/etc/s6-rc/ok-all/type.license        |   0
 .../app}/etc/ssl/certs/ca-certificates.crt    |   0
 {vm/app/lynx => img/app}/shell.nix            |  11 +-
 vm-lib/make-vm.nix                            |  51 +++++++
 vm/app/catgirl.nix                            |  17 +++
 vm/app/catgirl/Makefile                       | 130 ------------------
 vm/app/catgirl/default.nix                    |  96 -------------
 vm/app/catgirl/etc/fstab                      |   6 -
 vm/app/catgirl/etc/mdev/iface                 |  36 -----
 .../catgirl/etc/s6-linux-init/scripts/rc.init |  10 --
 vm/app/catgirl/etc/s6-rc/catgirl/run          |  31 -----
 .../data/appvm-catgirl/providers/net/netvm    |   0
 vm/app/catgirl/shell.nix                      |  17 ---
 vm/app/lynx.nix                               |  15 ++
 vm/app/lynx/bin                               |   1 -
 vm/app/lynx/etc/fstab                         |   6 -
 vm/app/lynx/etc/init                          |   5 -
 vm/app/lynx/etc/mdev.conf                     |   5 -
 vm/app/lynx/etc/passwd                        |   1 -
 vm/app/lynx/etc/passwd.license                |   2 -
 vm/app/lynx/etc/resolv.conf                   |   4 -
 vm/app/lynx/etc/s6-rc/lynx/type               |   1 -
 vm/app/lynx/etc/s6-rc/lynx/type.license       |   2 -
 .../etc/s6-rc/mdevd-coldplug/dependencies     |   4 -
 vm/app/lynx/etc/s6-rc/mdevd-coldplug/type     |   1 -
 .../etc/s6-rc/mdevd-coldplug/type.license     |   2 -
 vm/app/lynx/etc/s6-rc/mdevd-coldplug/up       |   4 -
 vm/app/lynx/etc/s6-rc/mdevd/notification-fd   |   1 -
 .../etc/s6-rc/mdevd/notification-fd.license   |   2 -
 vm/app/lynx/etc/s6-rc/mdevd/run               |   5 -
 vm/app/lynx/etc/s6-rc/mdevd/type              |   1 -
 vm/app/lynx/etc/s6-rc/mdevd/type.license      |   2 -
 vm/app/lynx/etc/s6-rc/ok-all/contents         |   4 -
 vm/app/lynx/etc/s6-rc/ok-all/type             |   1 -
 vm/app/lynx/etc/s6-rc/ok-all/type.license     |   2 -
 vm/app/lynx/etc/ssl/certs/ca-certificates.crt |   1 -
 .../host/data/appvm-lynx/providers/net/netvm  |   0
 vm/make-vm.nix                                |   9 ++
 65 files changed, 159 insertions(+), 439 deletions(-)
 rename {vm/app/lynx => img/app}/Makefile (66%)
 rename {vm/app/catgirl => img/app}/bin (100%)
 rename {vm/app/lynx => img/app}/default.nix (83%)
 create mode 100644 img/app/etc/fstab
 rename {vm/app/catgirl => img/app}/etc/init (100%)
 rename {vm/app/catgirl => img/app}/etc/mdev.conf (100%)
 rename {vm/app/lynx => img/app}/etc/mdev/iface (98%)
 rename {vm/app/catgirl => img/app}/etc/passwd (100%)
 rename {vm/app/catgirl => img/app}/etc/passwd.license (100%)
 rename {vm/app/catgirl => img/app}/etc/resolv.conf (100%)
 rename {vm/app/lynx => img/app}/etc/s6-linux-init/scripts/rc.init (90%)
 rename {vm/app/lynx/etc/s6-rc/lynx => img/app/etc/s6-rc/app}/run (80%)
 rename {vm/app/catgirl/etc/s6-rc/catgirl => img/app/etc/s6-rc/app}/type (100%)
 rename {vm/app/catgirl/etc/s6-rc/catgirl => img/app/etc/s6-rc/app}/type.license (100%)
 rename {vm/app/catgirl => img/app}/etc/s6-rc/mdevd-coldplug/dependencies (100%)
 rename {vm/app/catgirl => img/app}/etc/s6-rc/mdevd-coldplug/type (100%)
 rename {vm/app/catgirl => img/app}/etc/s6-rc/mdevd-coldplug/type.license (100%)
 rename {vm/app/catgirl => img/app}/etc/s6-rc/mdevd-coldplug/up (100%)
 rename {vm/app/catgirl => img/app}/etc/s6-rc/mdevd/notification-fd (100%)
 rename {vm/app/catgirl => img/app}/etc/s6-rc/mdevd/notification-fd.license (100%)
 rename {vm/app/catgirl => img/app}/etc/s6-rc/mdevd/run (100%)
 rename {vm/app/catgirl => img/app}/etc/s6-rc/mdevd/type (100%)
 rename {vm/app/catgirl => img/app}/etc/s6-rc/mdevd/type.license (100%)
 rename {vm/app/catgirl => img/app}/etc/s6-rc/ok-all/contents (100%)
 rename {vm/app/catgirl => img/app}/etc/s6-rc/ok-all/type (100%)
 rename {vm/app/catgirl => img/app}/etc/s6-rc/ok-all/type.license (100%)
 rename {vm/app/catgirl => img/app}/etc/ssl/certs/ca-certificates.crt (100%)
 rename {vm/app/lynx => img/app}/shell.nix (51%)
 create mode 100644 vm-lib/make-vm.nix
 create mode 100644 vm/app/catgirl.nix
 delete mode 100644 vm/app/catgirl/Makefile
 delete mode 100644 vm/app/catgirl/default.nix
 delete mode 100644 vm/app/catgirl/etc/fstab
 delete mode 100755 vm/app/catgirl/etc/mdev/iface
 delete mode 100755 vm/app/catgirl/etc/s6-linux-init/scripts/rc.init
 delete mode 100755 vm/app/catgirl/etc/s6-rc/catgirl/run
 delete mode 100644 vm/app/catgirl/host/data/appvm-catgirl/providers/net/netvm
 delete mode 100644 vm/app/catgirl/shell.nix
 create mode 100644 vm/app/lynx.nix
 delete mode 120000 vm/app/lynx/bin
 delete mode 100644 vm/app/lynx/etc/fstab
 delete mode 100755 vm/app/lynx/etc/init
 delete mode 100644 vm/app/lynx/etc/mdev.conf
 delete mode 100644 vm/app/lynx/etc/passwd
 delete mode 100644 vm/app/lynx/etc/passwd.license
 delete mode 100644 vm/app/lynx/etc/resolv.conf
 delete mode 100644 vm/app/lynx/etc/s6-rc/lynx/type
 delete mode 100644 vm/app/lynx/etc/s6-rc/lynx/type.license
 delete mode 100644 vm/app/lynx/etc/s6-rc/mdevd-coldplug/dependencies
 delete mode 100644 vm/app/lynx/etc/s6-rc/mdevd-coldplug/type
 delete mode 100644 vm/app/lynx/etc/s6-rc/mdevd-coldplug/type.license
 delete mode 100644 vm/app/lynx/etc/s6-rc/mdevd-coldplug/up
 delete mode 100644 vm/app/lynx/etc/s6-rc/mdevd/notification-fd
 delete mode 100644 vm/app/lynx/etc/s6-rc/mdevd/notification-fd.license
 delete mode 100644 vm/app/lynx/etc/s6-rc/mdevd/run
 delete mode 100644 vm/app/lynx/etc/s6-rc/mdevd/type
 delete mode 100644 vm/app/lynx/etc/s6-rc/mdevd/type.license
 delete mode 100644 vm/app/lynx/etc/s6-rc/ok-all/contents
 delete mode 100644 vm/app/lynx/etc/s6-rc/ok-all/type
 delete mode 100644 vm/app/lynx/etc/s6-rc/ok-all/type.license
 delete mode 120000 vm/app/lynx/etc/ssl/certs/ca-certificates.crt
 delete mode 100644 vm/app/lynx/host/data/appvm-lynx/providers/net/netvm
 create mode 100644 vm/make-vm.nix

diff --git a/host/initramfs/extfs.nix b/host/initramfs/extfs.nix
index 63f436a..dec4017 100644
--- a/host/initramfs/extfs.nix
+++ b/host/initramfs/extfs.nix
@@ -9,27 +9,20 @@ let
     # inherit (foot) terminfo;
   };
 
-  appvm-catgirl = import ../../vm/app/catgirl {
-    inherit config;
-    # inherit (foot) terminfo;
-  };
-
-  appvm-lynx = import ../../vm/app/lynx {
-    inherit config;
-    # inherit (foot) terminfo;
-  };
+  appvm-catgirl = import ../../vm/app/catgirl.nix { inherit config; };
+  appvm-lynx = import ../../vm/app/lynx.nix { inherit config; };
 in
 
 runCommand "ext.ext4" {
   nativeBuildInputs = [ tar2ext4 ];
 } ''
-  mkdir svc
+  mkdir -p svc/data/appvm-{catgirl,lynx}
 
   tar -C ${netvm} -c data | tar -C svc -x
   chmod +w svc/data
-  tar -C ${appvm-catgirl} -c data | tar -C svc -x
-  chmod +w svc/data
-  tar -C ${appvm-lynx} -c data | tar -C svc -x
+
+  tar -C ${appvm-catgirl} -c . | tar -C svc/data/appvm-catgirl -x
+  tar -C ${appvm-lynx} -c . | tar -C svc/data/appvm-lynx -x
 
   tar -cf ext.tar svc
   tar2ext4 -i ext.tar -o $out
diff --git a/host/rootfs/default.nix b/host/rootfs/default.nix
index 56d6ec0..4788628 100644
--- a/host/rootfs/default.nix
+++ b/host/rootfs/default.nix
@@ -75,13 +75,22 @@ let
 
   kernel = pkgs.linux_latest;
 
+  appvm = import ../../img/app {
+    inherit config;
+    inherit (foot) terminfo;
+  };
+
+  # Packages that should be fully linked into /usr,
+  # (not just their bin/* files).
+  usrPackages = [ appvm pkgsGui.mesa.drivers pkgsGui.dejavu_fonts ];
+
   packagesSysroot = runCommand "packages-sysroot" {
     nativeBuildInputs = [ xorg.lndir ];
   } ''
     mkdir -p $out/lib $out/usr/bin
     ln -s ${concatMapStringsSep " " (p: "${p}/bin/*") packages} $out/usr/bin
 
-    for pkg in ${lib.escapeShellArgs [ pkgsGui.mesa.drivers pkgsGui.dejavu_fonts ]}; do
+    for pkg in ${lib.escapeShellArgs usrPackages}; do
         lndir -silent "$pkg" "$out/usr"
     done
 
diff --git a/vm/app/lynx/Makefile b/img/app/Makefile
similarity index 66%
rename from vm/app/lynx/Makefile
rename to img/app/Makefile
index ff42501..c5a4684 100644
--- a/vm/app/lynx/Makefile
+++ b/img/app/Makefile
@@ -7,37 +7,32 @@
 QEMU_KVM = qemu-kvm
 CLOUD_HYPERVISOR = cloud-hypervisor
 
-VMM = qemu
-SCRIPTS = ../../../scripts
+prefix = /usr/local
+imgdir = $(prefix)/img
 
-HOST_FILES = host/data/appvm-lynx/providers/net/netvm
+VMM = qemu
+SCRIPTS = ../../scripts
 
 HOST_BUILD_FILES = \
-	build/host/data/appvm-lynx/blk/root.img \
-	build/host/data/appvm-lynx/vmlinux
+	build/host/appvm/blk/root.img \
+	build/host/appvm/vmlinux
 
-# We produce a directory, but that doesn't play nice with Make,
-# because it won't know to update if some file in the directory is
-# changed, or a file is created or removed in a subdirectory.  Using
-# the whole directory could also end up including files that aren't
-# intended to be part of the input, like temporary editor files or
-# .license files.  So for all these reasons, only explicitly listed
-# files are included in the build result.
-build/svc: $(HOST_FILES) $(HOST_BUILD_FILES)
-	rm -rf $@
-	mkdir -p $@
+all: $(HOST_BUILD_FILES)
+.PHONY: all
 
-	tar -c $(HOST_FILES) | tar -C $@ -x --strip-components 1
-	tar -c $(HOST_BUILD_FILES) | tar -C $@ -x --strip-components 2
+install: $(HOST_BUILD_FILES)
+	mkdir -p $(imgdir)
+	tar -c $(HOST_BUILD_FILES) | tar -C $(imgdir) -x --strip-components 2
+.PHONY: install
 
-build/host/data/appvm-lynx/vmlinux: $(VMLINUX)
+build/host/appvm/vmlinux: $(VMLINUX)
 	mkdir -p $$(dirname $@)
 	cp $(VMLINUX) $@
 
-build/host/data/appvm-lynx/blk/root.img: $(SCRIPTS)/make-gpt.sh $(SCRIPTS)/sfdisk-field.awk build/rootfs.ext4
+build/host/appvm/blk/root.img: $(SCRIPTS)/make-gpt.sh $(SCRIPTS)/sfdisk-field.awk build/rootfs.ext4
 	mkdir -p $$(dirname $@)
 	$(SCRIPTS)/make-gpt.sh $@.tmp \
-	    build/rootfs.ext4:4f68bce3-e8cd-4db1-96e7-fbcaf984b709:41e8068d-38d5-4135-ad77-0da704743940:root
+	    build/rootfs.ext4:4f68bce3-e8cd-4db1-96e7-fbcaf984b709:5460386f-2203-4911-8694-91400125c604:root
 	mv $@.tmp $@
 
 # tar2ext4 will leave half a filesystem behind if it's interrupted
@@ -57,6 +52,7 @@ VM_FILES = \
 	etc/s6-linux-init/scripts/rc.init
 VM_DIRS = dev run proc sys \
 	etc/s6-linux-init/env \
+	etc/s6-linux-init/run-image/ext \
 	etc/s6-linux-init/run-image/service
 
 # These are separate because they need to be included, but putting
@@ -78,8 +74,8 @@ build/rootfs.tar: build/empty $(PACKAGES_TAR) $(VM_FILES) $(VM_BUILD_FILES)
 	done
 
 VM_S6_RC_FILES = \
-	etc/s6-rc/lynx/run \
-	etc/s6-rc/lynx/type \
+	etc/s6-rc/app/run \
+	etc/s6-rc/app/type \
 	etc/s6-rc/mdevd-coldplug/dependencies \
 	etc/s6-rc/mdevd-coldplug/type \
 	etc/s6-rc/mdevd-coldplug/up \
@@ -98,9 +94,10 @@ build/etc/s6-rc: $(VM_S6_RC_FILES)
 	    s6-rc-compile $@ $$dir; \
 	    exit=$$?; rm -r $$dir; exit $$exit
 
-run-qemu: build/host/data/appvm-lynx/blk/root.img
+run-qemu: build/host/appvm/blk/root.img
 	$(QEMU_KVM) -m 128 -cpu host -machine q35,kernel=$(KERNEL) -vga none \
-	  -drive file=build/host/data/appvm-lynx/blk/root.img,if=virtio,format=raw,readonly=on \
+	  -drive file=build/host/appvm/blk/root.img,if=virtio,format=raw,readonly=on \
+	  -drive file=$(RUN_IMG),if=virtio,format=raw,readonly=on \
 	  -append "console=ttyS0 root=PARTLABEL=root" \
 	  -netdev user,id=net0 \
 	  -device virtio-net,netdev=net0,mac=0A:B3:EC:00:00:00 \
@@ -109,11 +106,12 @@ run-qemu: build/host/data/appvm-lynx/blk/root.img
 	  -device virtconsole,chardev=virtiocon0
 .PHONY: run-qemu
 
-run-cloud-hypervisor: build/host/data/appvm-lynx/blk/root.img
+run-cloud-hypervisor: build/host/appvm/blk/root.img
 	$(CLOUD_HYPERVISOR) \
 	    --api-socket path=vmm.sock \
 	    --memory size=128M \
-	    --disk path=build/host/data/appvm-lynx/blk/root.img,readonly=on \
+	    --disk path=build/host/appvm/blk/root.img,readonly=on \
+	           path=$(RUN_IMG),readonly=on \
 	    --net tap=tap0,mac=0A:B3:EC:00:00:00 \
 	    --kernel $(KERNEL) \
 	    --cmdline "console=ttyS0 root=PARTLABEL=root" \
diff --git a/vm/app/catgirl/bin b/img/app/bin
similarity index 100%
rename from vm/app/catgirl/bin
rename to img/app/bin
diff --git a/vm/app/lynx/default.nix b/img/app/default.nix
similarity index 83%
rename from vm/app/lynx/default.nix
rename to img/app/default.nix
index 92635f3..e7d5366 100644
--- a/vm/app/lynx/default.nix
+++ b/img/app/default.nix
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: MIT
 # SPDX-FileCopyrightText: 2021-2022 Alyssa Ross <hi@alyssa.is>
 
-{ config ? import ../../../nix/eval-config.nix {}
+{ config ? import ../../nix/eval-config.nix {}
 , terminfo ? config.pkgs.foot.terminfo
 }:
 
@@ -9,16 +9,16 @@ config.pkgs.pkgsStatic.callPackage (
 
 { lib, stdenvNoCC, runCommand, writeReferencesToFile, buildPackages
 , jq, s6-rc, tar2ext4, util-linux
-, busybox, cacert, execline, kmod, lynx, mdevd, s6, s6-linux-init
+, busybox, cacert, execline, kmod, mdevd, s6, s6-linux-init
 }:
 
 let
   inherit (lib) cleanSource cleanSourceWith concatMapStringsSep hasSuffix;
 
-  scripts = import ../../../scripts { inherit config; };
+  scripts = import ../../scripts { inherit config; };
 
   packages = [
-    execline kmod lynx mdevd s6 s6-linux-init s6-rc
+    execline kmod mdevd s6 s6-linux-init s6-rc
 
     (busybox.override {
       extraConfig = ''
@@ -64,7 +64,7 @@ let
 in
 
 stdenvNoCC.mkDerivation {
-  name = "spectrum-appvm-lynx";
+  name = "spectrum-appvm";
 
   src = cleanSourceWith {
     filter = name: _type:
@@ -78,15 +78,11 @@ stdenvNoCC.mkDerivation {
   PACKAGES_TAR = packagesTar;
   VMLINUX = "${kernel.dev}/vmlinux";
 
-  makeFlags = [ "SCRIPTS=${scripts}" ];
-
-  installPhase = ''
-    mv build/svc $out
-  '';
+  makeFlags = [ "SCRIPTS=${scripts}" "prefix=$(out)" ];
 
   enableParallelBuilding = true;
 
-  passthru = { inherit kernel; };
+  passthru = { inherit kernel packagesSysroot; };
 
   meta = with lib; {
     license = licenses.eupl12;
diff --git a/img/app/etc/fstab b/img/app/etc/fstab
new file mode 100644
index 0000000..95bfe2b
--- /dev/null
+++ b/img/app/etc/fstab
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2020-2022 Alyssa Ross <hi@alyssa.is>
+proc		/proc		proc	defaults					0	0
+devpts		/dev/pts	devpts	defaults,gid=4,mode=620				0	0
+tmpfs		/dev/shm	tmpfs	defaults					0	0
+sysfs		/sys		sysfs	defaults					0	0
+LABEL=ext	/run/ext	ext4	ro						0	0
+store		/nix/store	overlay	ro,lowerdir=/nix/store:/run/ext/nix/store	0	0
diff --git a/vm/app/catgirl/etc/init b/img/app/etc/init
similarity index 100%
rename from vm/app/catgirl/etc/init
rename to img/app/etc/init
diff --git a/vm/app/catgirl/etc/mdev.conf b/img/app/etc/mdev.conf
similarity index 100%
rename from vm/app/catgirl/etc/mdev.conf
rename to img/app/etc/mdev.conf
diff --git a/vm/app/lynx/etc/mdev/iface b/img/app/etc/mdev/iface
similarity index 98%
rename from vm/app/lynx/etc/mdev/iface
rename to img/app/etc/mdev/iface
index eb91e33..d8ceda5 100755
--- a/vm/app/lynx/etc/mdev/iface
+++ b/img/app/etc/mdev/iface
@@ -33,4 +33,4 @@ foreground {
   }
 }
 
-s6-rc -u change lynx
+s6-rc -u change app
diff --git a/vm/app/catgirl/etc/passwd b/img/app/etc/passwd
similarity index 100%
rename from vm/app/catgirl/etc/passwd
rename to img/app/etc/passwd
diff --git a/vm/app/catgirl/etc/passwd.license b/img/app/etc/passwd.license
similarity index 100%
rename from vm/app/catgirl/etc/passwd.license
rename to img/app/etc/passwd.license
diff --git a/vm/app/catgirl/etc/resolv.conf b/img/app/etc/resolv.conf
similarity index 100%
rename from vm/app/catgirl/etc/resolv.conf
rename to img/app/etc/resolv.conf
diff --git a/vm/app/lynx/etc/s6-linux-init/scripts/rc.init b/img/app/etc/s6-linux-init/scripts/rc.init
similarity index 90%
rename from vm/app/lynx/etc/s6-linux-init/scripts/rc.init
rename to img/app/etc/s6-linux-init/scripts/rc.init
index 1016d0c..b46afb7 100755
--- a/vm/app/lynx/etc/s6-linux-init/scripts/rc.init
+++ b/img/app/etc/s6-linux-init/scripts/rc.init
@@ -5,6 +5,7 @@
 if { s6-rc-init -c /etc/s6-rc /run/service }
 
 if { mkdir -p /dev/pts /dev/shm }
+if { modprobe overlay }
 if { mount -a }
 
 s6-rc change ok-all
diff --git a/vm/app/lynx/etc/s6-rc/lynx/run b/img/app/etc/s6-rc/app/run
similarity index 80%
rename from vm/app/lynx/etc/s6-rc/lynx/run
rename to img/app/etc/s6-rc/app/run
index d0e7a83..2a628b7 100755
--- a/vm/app/lynx/etc/s6-rc/lynx/run
+++ b/img/app/etc/s6-rc/app/run
@@ -22,6 +22,5 @@ fdmove -c 2 0
 foreground { clear }
 unexport ?
 
-# Run lynx, then a login shell to allow for debugging.
-foreground { lynx https://spectrum-os.org/ }
+foreground { /run/ext/run }
 exec -l sh
diff --git a/vm/app/catgirl/etc/s6-rc/catgirl/type b/img/app/etc/s6-rc/app/type
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/catgirl/type
rename to img/app/etc/s6-rc/app/type
diff --git a/vm/app/catgirl/etc/s6-rc/catgirl/type.license b/img/app/etc/s6-rc/app/type.license
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/catgirl/type.license
rename to img/app/etc/s6-rc/app/type.license
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/dependencies b/img/app/etc/s6-rc/mdevd-coldplug/dependencies
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/mdevd-coldplug/dependencies
rename to img/app/etc/s6-rc/mdevd-coldplug/dependencies
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/type b/img/app/etc/s6-rc/mdevd-coldplug/type
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/mdevd-coldplug/type
rename to img/app/etc/s6-rc/mdevd-coldplug/type
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/type.license b/img/app/etc/s6-rc/mdevd-coldplug/type.license
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/mdevd-coldplug/type.license
rename to img/app/etc/s6-rc/mdevd-coldplug/type.license
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd-coldplug/up b/img/app/etc/s6-rc/mdevd-coldplug/up
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/mdevd-coldplug/up
rename to img/app/etc/s6-rc/mdevd-coldplug/up
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd/notification-fd b/img/app/etc/s6-rc/mdevd/notification-fd
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/mdevd/notification-fd
rename to img/app/etc/s6-rc/mdevd/notification-fd
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd/notification-fd.license b/img/app/etc/s6-rc/mdevd/notification-fd.license
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/mdevd/notification-fd.license
rename to img/app/etc/s6-rc/mdevd/notification-fd.license
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd/run b/img/app/etc/s6-rc/mdevd/run
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/mdevd/run
rename to img/app/etc/s6-rc/mdevd/run
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd/type b/img/app/etc/s6-rc/mdevd/type
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/mdevd/type
rename to img/app/etc/s6-rc/mdevd/type
diff --git a/vm/app/catgirl/etc/s6-rc/mdevd/type.license b/img/app/etc/s6-rc/mdevd/type.license
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/mdevd/type.license
rename to img/app/etc/s6-rc/mdevd/type.license
diff --git a/vm/app/catgirl/etc/s6-rc/ok-all/contents b/img/app/etc/s6-rc/ok-all/contents
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/ok-all/contents
rename to img/app/etc/s6-rc/ok-all/contents
diff --git a/vm/app/catgirl/etc/s6-rc/ok-all/type b/img/app/etc/s6-rc/ok-all/type
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/ok-all/type
rename to img/app/etc/s6-rc/ok-all/type
diff --git a/vm/app/catgirl/etc/s6-rc/ok-all/type.license b/img/app/etc/s6-rc/ok-all/type.license
similarity index 100%
rename from vm/app/catgirl/etc/s6-rc/ok-all/type.license
rename to img/app/etc/s6-rc/ok-all/type.license
diff --git a/vm/app/catgirl/etc/ssl/certs/ca-certificates.crt b/img/app/etc/ssl/certs/ca-certificates.crt
similarity index 100%
rename from vm/app/catgirl/etc/ssl/certs/ca-certificates.crt
rename to img/app/etc/ssl/certs/ca-certificates.crt
diff --git a/vm/app/lynx/shell.nix b/img/app/shell.nix
similarity index 51%
rename from vm/app/lynx/shell.nix
rename to img/app/shell.nix
index 852b246..83dcd76 100644
--- a/vm/app/lynx/shell.nix
+++ b/img/app/shell.nix
@@ -1,7 +1,9 @@
 # SPDX-License-Identifier: MIT
-# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
+# SPDX-FileCopyrightText: 2021-2022 Alyssa Ross <hi@alyssa.is>
 
-{ config ? import ../../../nix/eval-config.nix {} }:
+{ config ? import ../../nix/eval-config.nix {}
+, run ? ../../vm/app/catgirl.nix
+}:
 
 with config.pkgs;
 
@@ -14,4 +16,9 @@ with config.pkgs;
   ];
 
   KERNEL = "${passthru.kernel.dev}/vmlinux";
+
+  runDef = import run { inherit config; };
+  shellHook = ''
+    export RUN_IMG="$(printf "%s\n" "$runDef"/blk/run.img)"
+  '';
 })
diff --git a/vm-lib/make-vm.nix b/vm-lib/make-vm.nix
new file mode 100644
index 0000000..2c50ca5
--- /dev/null
+++ b/vm-lib/make-vm.nix
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2022 Alyssa Ross <hi@alyssa.is>
+
+{ pkgs ? import <nixpkgs> {}
+
+# Paths that are present in the base image that will start this VM's
+# run script, and don't so need to be duplicated in the extension
+# partition's store.
+, basePaths ? builtins.toFile "null" ""
+}:
+
+pkgs.pkgsStatic.callPackage (
+
+{ lib, runCommand, writeReferencesToFile, e2fsprogs, tar2ext4 }:
+
+{ run, providers ? {} }:
+
+let
+  inherit (lib)
+    any attrValues concatLists concatStrings hasInfix mapAttrsToList;
+in
+
+assert !(any (hasInfix "\n") (concatLists (attrValues providers)));
+
+runCommand "spectrum-vm" {
+  nativeBuildInputs = [ e2fsprogs tar2ext4 ];
+
+  providerDirs = concatStrings (concatLists
+    (mapAttrsToList (kind: map (vm: "${kind}/${vm}\n")) providers));
+  passAsFile = [ "providerDirs" ];
+} ''
+  mkdir -p "$out"/{blk,providers}
+
+  mkdir root
+  cd root
+  ln -s ${run} run
+  comm -23 <(sort ${writeReferencesToFile run}) \
+      <(sort ${writeReferencesToFile basePaths}) |
+      tar -cf ../run.tar --verbatim-files-from -T - run
+  tar2ext4 -i ../run.tar -o "$out/blk/run.img"
+  e2label "$out/blk/run.img" ext
+
+  pushd "$out/providers"
+  xargs -rd '\n' dirname -- < "$providerDirsPath" | xargs -rd '\n' mkdir -p --
+  xargs -rd '\n' touch -- < "$providerDirsPath"
+  popd
+
+  ln -s /usr/img/appvm/blk/root.img "$out/blk"
+  ln -s /usr/img/appvm/vmlinux "$out"
+''
+) {}
diff --git a/vm/app/catgirl.nix b/vm/app/catgirl.nix
new file mode 100644
index 0000000..a4c05e3
--- /dev/null
+++ b/vm/app/catgirl.nix
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2021-2022 Alyssa Ross <hi@alyssa.is>
+
+{ config ? import ../../../nix/eval-config.nix {} }:
+
+import ../make-vm.nix { inherit config; } {
+  providers.net = [ "netvm" ];
+  run = config.pkgs.pkgsStatic.callPackage (
+    { writeScript, catgirl }:
+    writeScript "run-catgirl" ''
+      #!/bin/execlineb -P
+      foreground { printf "IRC nick (to join #spectrum): " }
+      backtick -E nick { head -1 }
+      ${catgirl}/bin/catgirl -h irc.libera.chat -j "#spectrum" -n $nick
+    ''
+  ) { };
+}
diff --git a/vm/app/catgirl/Makefile b/vm/app/catgirl/Makefile
deleted file mode 100644
index 8129c8e..0000000
--- a/vm/app/catgirl/Makefile
+++ /dev/null
@@ -1,130 +0,0 @@
-# SPDX-License-Identifier: EUPL-1.2+
-# SPDX-FileCopyrightText: 2021-2022 Alyssa Ross <hi@alyssa.is>
-
-# qemu-kvm is non-standard, but is present in at least Fedora and
-# Nixpkgs.  If you don't have qemu-kvm, you'll need to set e.g.
-# QEMU_KVM = qemu-system-x86_64 -enable-kvm.
-QEMU_KVM = qemu-kvm
-CLOUD_HYPERVISOR = cloud-hypervisor
-
-VMM = qemu
-SCRIPTS = ../../../scripts
-
-HOST_FILES = host/data/appvm-catgirl/providers/net/netvm
-
-HOST_BUILD_FILES = \
-	build/host/data/appvm-catgirl/blk/root.img \
-	build/host/data/appvm-catgirl/vmlinux
-
-# We produce a directory, but that doesn't play nice with Make,
-# because it won't know to update if some file in the directory is
-# changed, or a file is created or removed in a subdirectory.  Using
-# the whole directory could also end up including files that aren't
-# intended to be part of the input, like temporary editor files or
-# .license files.  So for all these reasons, only explicitly listed
-# files are included in the build result.
-build/svc: $(HOST_FILES) $(HOST_BUILD_FILES)
-	rm -rf $@
-	mkdir -p $@
-
-	tar -c $(HOST_FILES) | tar -C $@ -x --strip-components 1
-	tar -c $(HOST_BUILD_FILES) | tar -C $@ -x --strip-components 2
-
-build/host/data/appvm-catgirl/vmlinux: $(VMLINUX)
-	mkdir -p $$(dirname $@)
-	cp $(VMLINUX) $@
-
-build/host/data/appvm-catgirl/blk/root.img: $(SCRIPTS)/make-gpt.sh $(SCRIPTS)/sfdisk-field.awk build/rootfs.ext4
-	mkdir -p $$(dirname $@)
-	$(SCRIPTS)/make-gpt.sh $@.tmp \
-	    build/rootfs.ext4:4f68bce3-e8cd-4db1-96e7-fbcaf984b709:0d2f5f77-eb9c-453a-9463-daafcb5ce2b2:root
-	mv $@.tmp $@
-
-# tar2ext4 will leave half a filesystem behind if it's interrupted
-# half way through.
-build/rootfs.ext4: build/rootfs.tar
-	mkdir -p $$(dirname $@)
-	tar2ext4 -i build/rootfs.tar -o $@.tmp
-	mv $@.tmp $@
-
-VM_FILES = \
-	etc/fstab \
-	etc/init \
-	etc/mdev.conf \
-	etc/mdev/iface \
-	etc/passwd \
-	etc/resolv.conf \
-	etc/s6-linux-init/scripts/rc.init
-VM_DIRS = dev run proc sys \
-	etc/s6-linux-init/env \
-	etc/s6-linux-init/run-image/service
-
-# These are separate because they need to be included, but putting
-# them as make dependencies would confuse make.
-VM_LINKS = bin etc/ssl/certs/ca-certificates.crt
-
-VM_BUILD_FILES = build/etc/s6-rc
-VM_MOUNTPOINTS = dev run proc sys
-
-build/empty:
-	mkdir -p $@
-
-build/rootfs.tar: build/empty $(PACKAGES_TAR) $(VM_FILES) $(VM_BUILD_FILES)
-	cp --no-preserve=mode -f $(PACKAGES_TAR) $@
-	tar $(TARFLAGS) --append -f $@ $(VM_FILES) $(VM_LINKS)
-	echo $(VM_BUILD_FILES) | cut -d/ -f2 | \
-	    tar $(TARFLAGS) --append -f $@ -C build -T -
-	for m in $(VM_DIRS); do \
-	    tar $(TARFLAGS) --append -hf $@ --xform="s,.*,$$m," build/empty ; \
-	done
-
-VM_S6_RC_FILES = \
-	etc/s6-rc/catgirl/run \
-	etc/s6-rc/catgirl/type \
-	etc/s6-rc/mdevd-coldplug/dependencies \
-	etc/s6-rc/mdevd-coldplug/type \
-	etc/s6-rc/mdevd-coldplug/up \
-	etc/s6-rc/mdevd/notification-fd \
-	etc/s6-rc/mdevd/run \
-	etc/s6-rc/mdevd/type \
-	etc/s6-rc/ok-all/contents \
-	etc/s6-rc/ok-all/type
-
-build/etc/s6-rc: $(VM_S6_RC_FILES)
-	mkdir -p $$(dirname $@)
-	rm -rf $@
-
-	dir=$$(mktemp -d) && \
-	    tar -c $(VM_S6_RC_FILES) | tar -C $$dir -x --strip-components 2 && \
-	    s6-rc-compile $@ $$dir; \
-	    exit=$$?; rm -r $$dir; exit $$exit
-
-run-qemu: build/host/data/appvm-catgirl/blk/root.img
-	$(QEMU_KVM) -m 128 -cpu host -machine q35,kernel=$(KERNEL) -vga none \
-	  -drive file=build/host/data/appvm-catgirl/blk/root.img,if=virtio,format=raw,readonly=on \
-	  -append "console=ttyS0 root=PARTLABEL=root" \
-	  -netdev user,id=net0 \
-	  -device virtio-net,netdev=net0,mac=0A:B3:EC:00:00:00 \
-	  -chardev vc,id=virtiocon0 \
-	  -device virtio-serial-pci \
-	  -device virtconsole,chardev=virtiocon0
-.PHONY: run-qemu
-
-run-cloud-hypervisor: build/host/data/appvm-catgirl/blk/root.img
-	$(CLOUD_HYPERVISOR) \
-	    --api-socket path=vmm.sock \
-	    --memory size=128M \
-	    --disk path=build/host/data/appvm-catgirl/blk/root.img,readonly=on \
-	    --net tap=tap0,mac=0A:B3:EC:00:00:00 \
-	    --kernel $(KERNEL) \
-	    --cmdline "console=ttyS0 root=PARTLABEL=root" \
-	    --console tty \
-	    --serial pty
-.PHONY: run-cloud-hypervisor
-
-run: run-$(VMM)
-.PHONY: run
-
-clean:
-	rm -rf build
-.PHONY: clean
diff --git a/vm/app/catgirl/default.nix b/vm/app/catgirl/default.nix
deleted file mode 100644
index d83392d..0000000
--- a/vm/app/catgirl/default.nix
+++ /dev/null
@@ -1,96 +0,0 @@
-# SPDX-License-Identifier: MIT
-# SPDX-FileCopyrightText: 2021-2022 Alyssa Ross <hi@alyssa.is>
-
-{ config ? import ../../../nix/eval-config.nix {}
-, terminfo ? config.pkgs.foot.terminfo
-}:
-
-config.pkgs.pkgsStatic.callPackage (
-
-{ lib, stdenvNoCC, runCommand, writeReferencesToFile, buildPackages
-, jq, s6-rc, tar2ext4, util-linux
-, busybox, cacert, catgirl, execline, kmod, mdevd, s6, s6-linux-init
-}:
-
-let
-  inherit (lib) cleanSource cleanSourceWith concatMapStringsSep hasSuffix;
-
-  scripts = import ../../../scripts { inherit config; };
-
-  packages = [
-    catgirl execline kmod mdevd s6 s6-linux-init s6-rc
-
-    (busybox.override {
-      extraConfig = ''
-        CONFIG_DEPMOD n
-        CONFIG_INSMOD n
-        CONFIG_LSMOD n
-        CONFIG_MODINFO n
-        CONFIG_MODPROBE n
-        CONFIG_RMMOD n
-      '';
-    })
-  ];
-
-  packagesSysroot = runCommand "packages-sysroot" {
-    inherit packages;
-    passAsFile = [ "packages" ];
-  } ''
-    mkdir -p $out/usr/bin $out/usr/share
-    ln -s ${concatMapStringsSep " " (p: "${p}/bin/*") packages} $out/usr/bin
-    ln -s ${kernel}/lib "$out"
-    ln -s ${terminfo}/share/terminfo $out/usr/share
-    ln -s ${cacert}/etc/ssl $out/usr/share
-  '';
-
-  packagesTar = runCommand "packages.tar" {} ''
-    cd ${packagesSysroot}
-    tar -cf $out --verbatim-files-from \
-        -T ${writeReferencesToFile packagesSysroot} .
-  '';
-
-  kernel = buildPackages.linux.override {
-    structuredExtraConfig = with lib.kernel; {
-      VIRTIO = yes;
-      VIRTIO_PCI = yes;
-      VIRTIO_BLK = yes;
-      VIRTIO_CONSOLE = yes;
-      EXT4_FS = yes;
-      DRM_BOCHS = yes;
-      DRM = yes;
-      AGP = yes;
-    };
-  };
-in
-
-stdenvNoCC.mkDerivation {
-  name = "spectrum-appvm-catgirl";
-
-  src = cleanSourceWith {
-    filter = name: _type:
-      name != "${toString ./.}/build" &&
-      !(hasSuffix ".nix" name);
-    src = cleanSource ./.;
-  };
-
-  nativeBuildInputs = [ jq s6-rc tar2ext4 util-linux ];
-
-  PACKAGES_TAR = packagesTar;
-  VMLINUX = "${kernel.dev}/vmlinux";
-
-  makeFlags = [ "SCRIPTS=${scripts}" ];
-
-  installPhase = ''
-    mv build/svc $out
-  '';
-
-  enableParallelBuilding = true;
-
-  passthru = { inherit kernel; };
-
-  meta = with lib; {
-    license = licenses.eupl12;
-    platforms = platforms.linux;
-  };
-}
-) {}
diff --git a/vm/app/catgirl/etc/fstab b/vm/app/catgirl/etc/fstab
deleted file mode 100644
index 6a82ecc..0000000
--- a/vm/app/catgirl/etc/fstab
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: CC0-1.0
-# SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is>
-proc	/proc		proc	defaults		0	0
-devpts	/dev/pts	devpts	defaults,gid=4,mode=620	0	0
-tmpfs	/dev/shm	tmpfs	defaults		0	0
-sysfs	/sys		sysfs	defaults		0	0
diff --git a/vm/app/catgirl/etc/mdev/iface b/vm/app/catgirl/etc/mdev/iface
deleted file mode 100755
index 6d917fc..0000000
--- a/vm/app/catgirl/etc/mdev/iface
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/execlineb -P
-# SPDX-License-Identifier: EUPL-1.2+
-# SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is>
-
-importas -i INTERFACE INTERFACE
-
-if { test $INTERFACE != lo }
-
-# Our IP is encoded in the NIC-specific portion of the interface's MAC
-# address.
-backtick -E LOCAL_IP {
-  awk -F: "{printf \"100.64.%d.%d\\n\", \"0x\" $5, \"0x\" $6}"
-  /sys/class/net/${INTERFACE}/address
-}
-
-if { ip address add ${LOCAL_IP}/32 dev $INTERFACE }
-if { ip link set $INTERFACE up }
-if { ip route add 169.254.0.1 dev $INTERFACE }
-if { ip route add default via 169.254.0.1 dev $INTERFACE }
-
-# Try to wait for the network to be up.
-# If we time out, well, there's not much we can do, so just carry on.
-# In future, it would be better if the network VM notified us about
-# network changes.
-foreground { printf "Waiting for network… " }
-foreground {
-  ifte { echo "Connected." } { echo "Timed out." }
-  pipeline { seq 10 }
-  forstdin _
-  if -n {
-    redirfd -w 2 /dev/null
-    wget -qT 6 -O /dev/null http://ipv4.connman.net/online/status.html
-  }
-}
-
-s6-rc -u change catgirl
diff --git a/vm/app/catgirl/etc/s6-linux-init/scripts/rc.init b/vm/app/catgirl/etc/s6-linux-init/scripts/rc.init
deleted file mode 100755
index 1016d0c..0000000
--- a/vm/app/catgirl/etc/s6-linux-init/scripts/rc.init
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/execlineb -P
-# SPDX-License-Identifier: EUPL-1.2+
-# SPDX-FileCopyrightText: 2020-2022 Alyssa Ross <hi@alyssa.is>
-
-if { s6-rc-init -c /etc/s6-rc /run/service }
-
-if { mkdir -p /dev/pts /dev/shm }
-if { mount -a }
-
-s6-rc change ok-all
diff --git a/vm/app/catgirl/etc/s6-rc/catgirl/run b/vm/app/catgirl/etc/s6-rc/catgirl/run
deleted file mode 100755
index 41ae0aa..0000000
--- a/vm/app/catgirl/etc/s6-rc/catgirl/run
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/execlineb -P
-# SPDX-License-Identifier: EUPL-1.2+
-# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
-
-export TERM foot
-export TERMINFO_DIRS /usr/share/terminfo
-export TMPDIR /run
-
-backtick USER { id -un }
-backtick HOME {
-  importas -i user USER
-  homeof $user
-}
-
-importas -i home HOME
-cd $home
-
-redirfd -u 0 /dev/hvc0
-fdmove -c 1 0
-fdmove -c 2 0
-
-foreground { clear }
-unexport ?
-
-# Run catgirl, then a login shell to allow for debugging.
-foreground { printf "IRC nick (to join #spectrum): " }
-foreground {
-  backtick -E nick { head -1 }
-  catgirl -h irc.libera.chat -j "#spectrum" -n $nick
-}
-exec -l sh
diff --git a/vm/app/catgirl/host/data/appvm-catgirl/providers/net/netvm b/vm/app/catgirl/host/data/appvm-catgirl/providers/net/netvm
deleted file mode 100644
index e69de29..0000000
diff --git a/vm/app/catgirl/shell.nix b/vm/app/catgirl/shell.nix
deleted file mode 100644
index 852b246..0000000
--- a/vm/app/catgirl/shell.nix
+++ /dev/null
@@ -1,17 +0,0 @@
-# SPDX-License-Identifier: MIT
-# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
-
-{ config ? import ../../../nix/eval-config.nix {} }:
-
-with config.pkgs;
-
-(import ./. { inherit config; }).overrideAttrs (
-{ passthru ? {}, nativeBuildInputs ? [], ... }:
-
-{
-  nativeBuildInputs = nativeBuildInputs ++ [
-    cloud-hypervisor jq qemu_kvm reuse
-  ];
-
-  KERNEL = "${passthru.kernel.dev}/vmlinux";
-})
diff --git a/vm/app/lynx.nix b/vm/app/lynx.nix
new file mode 100644
index 0000000..00d449e
--- /dev/null
+++ b/vm/app/lynx.nix
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2021-2022 Alyssa Ross <hi@alyssa.is>
+
+{ config ? import ../../../nix/eval-config.nix {} }:
+
+import ../make-vm.nix { inherit config; } {
+  providers.net = [ "netvm" ];
+  run = config.pkgs.pkgsStatic.callPackage (
+    { writeScript, lynx }:
+    writeScript "run-lynx" ''
+      #!/bin/execlineb -P
+      ${lynx}/bin/lynx https://spectrum-os.org
+    ''
+  ) { };
+}
diff --git a/vm/app/lynx/bin b/vm/app/lynx/bin
deleted file mode 120000
index 1e881ed..0000000
--- a/vm/app/lynx/bin
+++ /dev/null
@@ -1 +0,0 @@
-usr/bin
\ No newline at end of file
diff --git a/vm/app/lynx/etc/fstab b/vm/app/lynx/etc/fstab
deleted file mode 100644
index 6a82ecc..0000000
--- a/vm/app/lynx/etc/fstab
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: CC0-1.0
-# SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is>
-proc	/proc		proc	defaults		0	0
-devpts	/dev/pts	devpts	defaults,gid=4,mode=620	0	0
-tmpfs	/dev/shm	tmpfs	defaults		0	0
-sysfs	/sys		sysfs	defaults		0	0
diff --git a/vm/app/lynx/etc/init b/vm/app/lynx/etc/init
deleted file mode 100755
index 6424e22..0000000
--- a/vm/app/lynx/etc/init
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/execlineb -s0
-# SPDX-License-Identifier: EUPL-1.2+
-# SPDX-FileCopyrightText: 2022 Alyssa Ross <hi@alyssa.is>
-
-/bin/s6-linux-init -Bc /etc/s6-linux-init -- $@
diff --git a/vm/app/lynx/etc/mdev.conf b/vm/app/lynx/etc/mdev.conf
deleted file mode 100644
index f114719..0000000
--- a/vm/app/lynx/etc/mdev.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: EUPL-1.2+
-# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
-
--$MODALIAS=.* 0:0 660 +importas -iu MODALIAS MODALIAS modprobe -q $MODALIAS
-$INTERFACE=.* 0:0 660 ! +/etc/mdev/iface
diff --git a/vm/app/lynx/etc/passwd b/vm/app/lynx/etc/passwd
deleted file mode 100644
index 29f3b25..0000000
--- a/vm/app/lynx/etc/passwd
+++ /dev/null
@@ -1 +0,0 @@
-root:x:0:0:System administrator:/:/bin/sh
diff --git a/vm/app/lynx/etc/passwd.license b/vm/app/lynx/etc/passwd.license
deleted file mode 100644
index 2b3b032..0000000
--- a/vm/app/lynx/etc/passwd.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-License-Identifier: CC0-1.0
-SPDX-FileCopyrightText: 2020 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/lynx/etc/resolv.conf b/vm/app/lynx/etc/resolv.conf
deleted file mode 100644
index 7fcdf3a..0000000
--- a/vm/app/lynx/etc/resolv.conf
+++ /dev/null
@@ -1,4 +0,0 @@
-# SPDX-License-Identifier: CC0-1.0
-# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
-
-nameserver 1.1.1.1
diff --git a/vm/app/lynx/etc/s6-rc/lynx/type b/vm/app/lynx/etc/s6-rc/lynx/type
deleted file mode 100644
index 5883cff..0000000
--- a/vm/app/lynx/etc/s6-rc/lynx/type
+++ /dev/null
@@ -1 +0,0 @@
-longrun
diff --git a/vm/app/lynx/etc/s6-rc/lynx/type.license b/vm/app/lynx/etc/s6-rc/lynx/type.license
deleted file mode 100644
index c49c11b..0000000
--- a/vm/app/lynx/etc/s6-rc/lynx/type.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-License-Identifier: CC0-1.0
-SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/lynx/etc/s6-rc/mdevd-coldplug/dependencies b/vm/app/lynx/etc/s6-rc/mdevd-coldplug/dependencies
deleted file mode 100644
index 59b02b7..0000000
--- a/vm/app/lynx/etc/s6-rc/mdevd-coldplug/dependencies
+++ /dev/null
@@ -1,4 +0,0 @@
-# SPDX-License-Identifier: CC0-1.0
-# SPDX-FileCopyrightText: 2020 Alyssa Ross <hi@alyssa.is>
-#
-mdevd
diff --git a/vm/app/lynx/etc/s6-rc/mdevd-coldplug/type b/vm/app/lynx/etc/s6-rc/mdevd-coldplug/type
deleted file mode 100644
index bdd22a1..0000000
--- a/vm/app/lynx/etc/s6-rc/mdevd-coldplug/type
+++ /dev/null
@@ -1 +0,0 @@
-oneshot
diff --git a/vm/app/lynx/etc/s6-rc/mdevd-coldplug/type.license b/vm/app/lynx/etc/s6-rc/mdevd-coldplug/type.license
deleted file mode 100644
index 2b3b032..0000000
--- a/vm/app/lynx/etc/s6-rc/mdevd-coldplug/type.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-License-Identifier: CC0-1.0
-SPDX-FileCopyrightText: 2020 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/lynx/etc/s6-rc/mdevd-coldplug/up b/vm/app/lynx/etc/s6-rc/mdevd-coldplug/up
deleted file mode 100644
index 8698f7d..0000000
--- a/vm/app/lynx/etc/s6-rc/mdevd-coldplug/up
+++ /dev/null
@@ -1,4 +0,0 @@
-# SPDX-License-Identifier: EUPL-1.2+
-# SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is>
-
-mdevd-coldplug
diff --git a/vm/app/lynx/etc/s6-rc/mdevd/notification-fd b/vm/app/lynx/etc/s6-rc/mdevd/notification-fd
deleted file mode 100644
index 00750ed..0000000
--- a/vm/app/lynx/etc/s6-rc/mdevd/notification-fd
+++ /dev/null
@@ -1 +0,0 @@
-3
diff --git a/vm/app/lynx/etc/s6-rc/mdevd/notification-fd.license b/vm/app/lynx/etc/s6-rc/mdevd/notification-fd.license
deleted file mode 100644
index 2b3b032..0000000
--- a/vm/app/lynx/etc/s6-rc/mdevd/notification-fd.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-License-Identifier: CC0-1.0
-SPDX-FileCopyrightText: 2020 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/lynx/etc/s6-rc/mdevd/run b/vm/app/lynx/etc/s6-rc/mdevd/run
deleted file mode 100644
index 6dacb13..0000000
--- a/vm/app/lynx/etc/s6-rc/mdevd/run
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/execlineb -P
-# SPDX-License-Identifier: EUPL-1.2+
-# SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is>
-
-mdevd -D3
diff --git a/vm/app/lynx/etc/s6-rc/mdevd/type b/vm/app/lynx/etc/s6-rc/mdevd/type
deleted file mode 100644
index 5883cff..0000000
--- a/vm/app/lynx/etc/s6-rc/mdevd/type
+++ /dev/null
@@ -1 +0,0 @@
-longrun
diff --git a/vm/app/lynx/etc/s6-rc/mdevd/type.license b/vm/app/lynx/etc/s6-rc/mdevd/type.license
deleted file mode 100644
index 2b3b032..0000000
--- a/vm/app/lynx/etc/s6-rc/mdevd/type.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-License-Identifier: CC0-1.0
-SPDX-FileCopyrightText: 2020 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/lynx/etc/s6-rc/ok-all/contents b/vm/app/lynx/etc/s6-rc/ok-all/contents
deleted file mode 100644
index c4ea84f..0000000
--- a/vm/app/lynx/etc/s6-rc/ok-all/contents
+++ /dev/null
@@ -1,4 +0,0 @@
-# SPDX-License-Identifier: CC0-1.0
-# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
-#
-mdevd-coldplug
diff --git a/vm/app/lynx/etc/s6-rc/ok-all/type b/vm/app/lynx/etc/s6-rc/ok-all/type
deleted file mode 100644
index 757b422..0000000
--- a/vm/app/lynx/etc/s6-rc/ok-all/type
+++ /dev/null
@@ -1 +0,0 @@
-bundle
diff --git a/vm/app/lynx/etc/s6-rc/ok-all/type.license b/vm/app/lynx/etc/s6-rc/ok-all/type.license
deleted file mode 100644
index c49c11b..0000000
--- a/vm/app/lynx/etc/s6-rc/ok-all/type.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-License-Identifier: CC0-1.0
-SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
diff --git a/vm/app/lynx/etc/ssl/certs/ca-certificates.crt b/vm/app/lynx/etc/ssl/certs/ca-certificates.crt
deleted file mode 120000
index 42d8e23..0000000
--- a/vm/app/lynx/etc/ssl/certs/ca-certificates.crt
+++ /dev/null
@@ -1 +0,0 @@
-/usr/share/ssl/certs/ca-bundle.crt
\ No newline at end of file
diff --git a/vm/app/lynx/host/data/appvm-lynx/providers/net/netvm b/vm/app/lynx/host/data/appvm-lynx/providers/net/netvm
deleted file mode 100644
index e69de29..0000000
diff --git a/vm/make-vm.nix b/vm/make-vm.nix
new file mode 100644
index 0000000..0d7c1f9
--- /dev/null
+++ b/vm/make-vm.nix
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2022 Alyssa Ross <hi@alyssa.is>
+
+{ config ? import ../nix/eval-config.nix {} }:
+
+import ../vm-lib/make-vm.nix {
+  inherit (config) pkgs;
+  basePaths = (import ../img/app { inherit config; }).packagesSysroot;
+}
-- 
2.37.1



  parent reply	other threads:[~2022-10-09 11:41 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-09 11:40 [PATCH v2 0/6] Introduce a shared base for application VMs Alyssa Ross
2022-10-09 11:40 ` [PATCH v2 1/6] host/start-vm: support multiple block devices Alyssa Ross
2022-11-14  1:14   ` Alyssa Ross
2022-10-09 11:40 ` [PATCH v2 2/6] scripts/make-gpt.sh: add support for labels Alyssa Ross
2022-11-14  1:14   ` Alyssa Ross
2022-10-09 11:40 ` [PATCH v2 3/6] vm: build GPT images Alyssa Ross
2022-11-14  1:14   ` Alyssa Ross
2022-10-09 11:40 ` [PATCH v2 4/6] host/start-vm: boot using partition label Alyssa Ross
2022-11-14  1:14   ` Alyssa Ross
2022-10-09 11:40 ` [PATCH v2 5/6] release: rename from "img" Alyssa Ross
2022-11-14  1:14   ` Alyssa Ross
2022-10-09 11:40 ` Alyssa Ross [this message]
2022-11-14  1:14   ` [PATCH v2 6/6] img/app: extract from appvm-{lynx,catgirl} Alyssa Ross
2022-10-10 23:28 [PATCH 00/22] Implement managing VMs with Nix Alyssa Ross
2022-10-10 23:29 ` [PATCH v2 6/6] img/app: extract from appvm-{lynx,catgirl} Alyssa Ross

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221009114036.463071-7-hi@alyssa.is \
    --to=hi@alyssa.is \
    --cc=devel@spectrum-os.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://spectrum-os.org/git/crosvm
	https://spectrum-os.org/git/doc
	https://spectrum-os.org/git/mktuntap
	https://spectrum-os.org/git/nixpkgs
	https://spectrum-os.org/git/spectrum
	https://spectrum-os.org/git/ucspi-vsock
	https://spectrum-os.org/git/www

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).