patches and low-level development discussion
 help / color / mirror / code / Atom feed
From: Demi Marie Obenour <demiobenour@gmail.com>
To: Spectrum OS Development <devel@spectrum-os.org>
Cc: Demi Marie Obenour <demiobenour@gmail.com>, Alyssa Ross <hi@alyssa.is>
Subject: [PATCH v6 6/8] Support updates via systemd-sysupdate
Date: Sat, 29 Nov 2025 04:50:03 -0500	[thread overview]
Message-ID: <20251129-updates-v6-6-9edb87a2e509@gmail.com> (raw)
In-Reply-To: <20251129-updates-v6-0-9edb87a2e509@gmail.com>

Include a new `spectrum-update` command to update the system.  This
tells the new sys.appvm-systemd-sysupdate VM to download the updates
into a staging directory using systemd-sysupdate.  The host then runs
systemd-sysupdate to apply the updates itself.

sys.appvm-systemd-sysupdate uses host-provided information to fetch the
update.  This allows editing files on the host to change the update URL
and signing key.

systemd-sysupdate requires /boot to be mounted so that systemd-sysupdate
can update the unified kernel image.  systemd-sysupdate also requires
that /tmp is writable so that it can store temporary files, so put a
tmpfs there.  Furthermore, there needs to be a directory for storing
downloaded updates.  Create /home so that users can mount their
persistent data there.

The directory the VM downloads updates into is *not* reset (wiped)
before or after the update.  This allows the VM to know if the system is
already up to date.  Otherwise, it would redownload the entire
multi-gigabyte update image.

Updates are currently not compressed.  This should be changed in the
future, but it would add a small amount of additional complexity.  In
particular, the script generating the update directory would need to
generate a SHA256SUMS containing the hash of both the compressed and
uncompressed versions.  More importantly, the VM must not be able to
make the host use the compressed version.  This would be a potential
security risk because decompression happens before signature
verification.

Signed-off-by: Demi Marie Obenour <demiobenour@gmail.com>
Reviewed-by: Alyssa Ross <hi@alyssa.is>
---
Changes since v5:
- Remove statement about GnuPG that does not apply to the version in
  Nixpkgs.
- Delete stale comment.
- Rebase and fix merge conflict.

Changes since v4:
- Do not strip leading and trailing whitespace from update URLs.
- Create a single script that does the work.  Pass the paths to curl and
  systemd sysupdate to it as environment variables.  Inline the awk
  script into it.
- Rebase and fix merge conflict.

Changes since v3:

- Move builtins.path from lib/config.nix to host/rootfs/default.nix.
- Change config options from "update-url" to "updateUrl" and
  "update-signing-key" to "updateSigningKey".

Changes since v2:

- Generate the transfer files in the guest, not the host.
- Do not use an environment file.
- Reject URLs that cannot work.
- Escape sed metacharacters.
- Escape backslashes for systemd-sysupdate.
- Do not validate update URLs at build time, only at runtime.
- Reject only update URLs that cannot possibly work.
- Set the partition UUIDs according to systemd's recommendation.
- Do not rely on finding partitions by label.
- Strip leading and trailing whitespace from the update URL.
- Rename the update command from `update` to `spectrum-update`.
- Delete the list of steps.  Replace it with comments in the script.

The awk script in the VM rejects URLs that contain whitespace.  This is
because they can't work, and passing them to systemd-sysupdate would
require figuring out how to escape them.  Rejecting such bogus URLs is
simpler than preventing them from being mangled.
---
 host/rootfs/Makefile                               | 17 ++++-
 host/rootfs/default.nix                            | 16 +++-
 host/rootfs/file-list.mk                           |  7 ++
 host/rootfs/image/etc/fstab                        |  1 +
 .../image/etc/sysupdate.d/50-verity.transfer       | 20 +++++
 host/rootfs/image/etc/sysupdate.d/60-root.transfer | 20 +++++
 .../image/etc/sysupdate.d/70-kernel.transfer       | 20 +++++
 .../image/etc/vm-sysupdate.d/50-verity.transfer    | 18 +++++
 .../image/etc/vm-sysupdate.d/60-root.transfer      | 18 +++++
 .../image/etc/vm-sysupdate.d/70-kernel.transfer    | 18 +++++
 host/rootfs/image/usr/bin/spectrum-update          | 87 ++++++++++++++++++++++
 host/rootfs/os-release.in                          | 15 ++++
 lib/config.default.nix                             |  2 +
 lib/fake-update-signing-key.gpg                    |  3 +
 vm/app/systemd-sysupdate/default.nix               | 26 +++++++
 vm/app/systemd-sysupdate/download-update           | 68 +++++++++++++++++
 16 files changed, 350 insertions(+), 6 deletions(-)

diff --git a/host/rootfs/Makefile b/host/rootfs/Makefile
index 065722d48951a17182ed94e168796700652db7b9..c9c51a06962156d0fc1b2823bc3edb1678c70502 100644
--- a/host/rootfs/Makefile
+++ b/host/rootfs/Makefile
@@ -10,6 +10,7 @@ include file-list.mk
 ROOT_FS = build
 
 DIRS = \
+	boot \
 	dev \
 	etc/s6-linux-init/env \
 	etc/s6-linux-init/run-image/configs \
@@ -33,15 +34,17 @@ DIRS = \
 	etc/s6-linux-init/run-image/vm/by-id \
 	etc/s6-linux-init/run-image/vm/by-name \
 	ext \
+	home \
 	proc \
 	run \
-	sys
+	sys \
+	tmp
 
 FIFOS = \
 	etc/s6-linux-init/run-image/service/s6-svscan-log/fifo \
 	etc/s6-linux-init/run-image/service/s6-linux-init-shutdownd/fifo
 
-BUILD_FILES = build/etc/s6-rc
+BUILD_FILES = build/etc/s6-rc build/etc/os-release build/etc/update-url
 
 # This rule produces three files but Make only (portably)
 # supports one output per rule.  Instead of resorting to temporary
@@ -63,12 +66,22 @@ $(ROOT_FS_IMAGE): ../../scripts/make-erofs.sh $(PACKAGES_FILE) $(FILES) $(BUILD_
 	mkdir -p $(ROOT_FS) && \
 	{ \
 	    cat $(PACKAGES_FILE) ;\
+	    printf '%s\n%s\n' "$$UPDATE_SIGNING_KEY" /etc/systemd/import-pubring.gpg; \
 	    for file in $(FILES) $(LINKS); do printf '%s\n%s\n' $$file "$${file#image/}"; done ;\
 	    for file in $(BUILD_FILES); do printf '%s\n%s\n' $$file $${file#build/}; done ;\
 	    printf 'build/empty\n%s\n' $(DIRS) ;\
 	    printf 'build/fifo\n%s\n' $(FIFOS) ;\
 	} | ../../scripts/make-erofs.sh $@
 
+build/etc/update-url:
+	mkdir -p build/etc
+	# might have metacharacters, so avoid interpolation
+	printf %s\\n "$${UPDATE_URL:?'update URL empty or missing'}" > build/etc/update-url
+
+build/etc/os-release:
+	mkdir -p build/etc
+	sed 's/@VERSION@/$(VERSION)/g' < os-release.in > build/etc/os-release
+
 build/fifo:
 	mkdir -p build
 	mkfifo -m 0600 $@
diff --git a/host/rootfs/default.nix b/host/rootfs/default.nix
index 4fe9058abdfaa1df7d63b84a629708d4d99388f4..b441a517f3bbb78f84d8566ca6dfd9181d0302be 100644
--- a/host/rootfs/default.nix
+++ b/host/rootfs/default.nix
@@ -15,6 +15,7 @@ pkgsMusl.callPackage (
 , jq, kmod, mdevd, mesa, s6, s6-linux-init, socat, systemd
 , util-linuxMinimal, virtiofsd, westonLite, xdg-desktop-portal
 , xdg-desktop-portal-gtk, xdg-desktop-portal-spectrum-host
+, btrfs-progs
 }:
 
 let
@@ -24,9 +25,9 @@ let
     trivial;
 
   packages = [
-    cloud-hypervisor cosmic-files crosvm cryptsetup dbus execline
-    fuse3 inotify-tools iproute2 jq kmod mdevd s6 s6-linux-init s6-rc
-    socat spectrum-host-tools systemd util-linuxMinimal virtiofsd
+    btrfs-progs cloud-hypervisor cosmic-files crosvm cryptsetup dbus
+    execline fuse3 inotify-tools iproute2 jq kmod mdevd s6 s6-linux-init
+    s6-rc socat spectrum-host-tools util-linuxMinimal virtiofsd
     xdg-desktop-portal-spectrum-host
 
     (foot.override { allowPgo = false; })
@@ -56,18 +57,20 @@ let
   # https://inbox.vuxu.org/musl/20251017-dlopen-use-rpath-of-caller-dso-v1-1-46c69eda1473@iscas.ac.cn/
   usrPackages = [
     appvm dejavu_fonts kmod.lib mesa westonLite kernel.modules
-    firmware netvm
+    firmware netvm systemd
   ];
 
   appvms = {
     appvm-firefox = callSpectrumPackage ../../vm/app/firefox.nix {};
     appvm-foot = callSpectrumPackage ../../vm/app/foot.nix {};
     appvm-gnome-text-editor = callSpectrumPackage ../../vm/app/gnome-text-editor.nix {};
+    appvm-systemd-sysupdate = callSpectrumPackage ../../vm/app/systemd-sysupdate {};
   };
 
   packagesSysroot = runCommand "packages-sysroot" {
     depsBuildBuild = [ inkscape ];
     nativeBuildInputs = [ xorg.lndir ];
+    src = builtins.path { name = "os-release"; path = ./os-release.in; };
   } ''
     mkdir -p $out/usr/bin $out/usr/share/dbus-1/services \
       $out/usr/share/icons/hicolor/20x20/apps
@@ -118,6 +121,11 @@ stdenvNoCC.mkDerivation {
       printf "%s\n/\n" ${packagesSysroot} >$out
       sed p ${writeClosure [ packagesSysroot] } >>$out
     '';
+    UPDATE_SIGNING_KEY = builtins.path {
+      name = "signing-key";
+      path = config.updateSigningKey;
+    };
+    UPDATE_URL = config.updateUrl;
     VERSION = config.version;
   };
 
diff --git a/host/rootfs/file-list.mk b/host/rootfs/file-list.mk
index 613a9e7c692c8a3b1d556c3cd599ffaccf4372c4..56f693e53b918751bcc4b8614f027edde6312040 100644
--- a/host/rootfs/file-list.mk
+++ b/host/rootfs/file-list.mk
@@ -41,13 +41,20 @@ FILES = \
 	image/etc/s6-linux-init/scripts/rc.init \
 	image/etc/s6-linux-init/scripts/rc.shutdown \
 	image/etc/s6-linux-init/scripts/rc.shutdown.final \
+	image/etc/sysupdate.d/50-verity.transfer \
+	image/etc/sysupdate.d/60-root.transfer \
+	image/etc/sysupdate.d/70-kernel.transfer \
 	image/etc/udev/rules.d/99-spectrum.rules \
+	image/etc/vm-sysupdate.d/50-verity.transfer \
+	image/etc/vm-sysupdate.d/60-root.transfer \
+	image/etc/vm-sysupdate.d/70-kernel.transfer \
 	image/etc/xdg/weston/autolaunch \
 	image/etc/xdg/weston/weston.ini \
 	image/usr/bin/assign-devices \
 	image/usr/bin/create-vm-dependencies \
 	image/usr/bin/run-appimage \
 	image/usr/bin/run-vmm \
+	image/usr/bin/spectrum-update \
 	image/usr/bin/vm-console \
 	image/usr/bin/vm-import \
 	image/usr/bin/vm-start \
diff --git a/host/rootfs/image/etc/fstab b/host/rootfs/image/etc/fstab
index 5dc9b2a3c4dff62ee49b2d827f53b45b7781a60f..6230d910a23339925fea0f2ffbc2baa5241ce3f2 100644
--- a/host/rootfs/image/etc/fstab
+++ b/host/rootfs/image/etc/fstab
@@ -4,3 +4,4 @@ proc	/proc		proc	defaults	0	0
 devpts	/dev/pts	devpts	gid=5,mode=620	0	0
 tmpfs	/dev/shm	tmpfs	defaults	0	0
 sysfs	/sys		sysfs	defaults	0	0
+tmpfs	/tmp		tmpfs	defaults	0	0
diff --git a/host/rootfs/image/etc/sysupdate.d/50-verity.transfer b/host/rootfs/image/etc/sysupdate.d/50-verity.transfer
new file mode 100644
index 0000000000000000000000000000000000000000..9cd64b58ae55d55d378d99f5701f1ecef867e436
--- /dev/null
+++ b/host/rootfs/image/etc/sysupdate.d/50-verity.transfer
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+# Uses example code from systemd man pages which is under MIT-0
+# (no attribution required).
+[Transfer]
+ProtectVersion=%A
+
+[Source]
+Type=url-file
+Path=file:///run/updater
+MatchPattern=Spectrum_@v_@u.verity
+
+[Target]
+Type=partition
+Path=auto
+MatchPattern=Spectrum_@v.verity
+MatchPartitionType=root-verity
+PartitionFlags=0
+ReadOnly=1
diff --git a/host/rootfs/image/etc/sysupdate.d/60-root.transfer b/host/rootfs/image/etc/sysupdate.d/60-root.transfer
new file mode 100644
index 0000000000000000000000000000000000000000..cd12d2bd2b4ecd9bb5c7d26cc7c27a4bdb74cac8
--- /dev/null
+++ b/host/rootfs/image/etc/sysupdate.d/60-root.transfer
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+# Uses example code from systemd man pages which is under MIT-0
+# (no attribution required).
+[Transfer]
+ProtectVersion=%A
+
+[Source]
+Type=url-file
+Path=file:///run/updater
+MatchPattern=Spectrum_@v_@u.root
+
+[Target]
+Type=partition
+Path=auto
+MatchPattern=Spectrum_@v
+MatchPartitionType=root
+PartitionFlags=0
+ReadOnly=1
diff --git a/host/rootfs/image/etc/sysupdate.d/70-kernel.transfer b/host/rootfs/image/etc/sysupdate.d/70-kernel.transfer
new file mode 100644
index 0000000000000000000000000000000000000000..e4190587a6bb127cb7315f38d59e48cf279318a4
--- /dev/null
+++ b/host/rootfs/image/etc/sysupdate.d/70-kernel.transfer
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+# Uses example code from systemd man pages which is under MIT-0
+# (no attribution required).
+[Transfer]
+ProtectVersion=%A
+
+[Source]
+Type=url-file
+Path=file:///run/updater
+MatchPattern=Spectrum_@v.efi
+
+[Target]
+Type=regular-file
+Path=/EFI/Linux
+PathRelativeTo=boot
+MatchPattern=Spectrum_@v.efi
+Mode=0644
+InstancesMax=2
diff --git a/host/rootfs/image/etc/vm-sysupdate.d/50-verity.transfer b/host/rootfs/image/etc/vm-sysupdate.d/50-verity.transfer
new file mode 100644
index 0000000000000000000000000000000000000000..ab4997c83605e3820a22b0b2178dcd76dfcf787e
--- /dev/null
+++ b/host/rootfs/image/etc/vm-sysupdate.d/50-verity.transfer
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+# Uses example code from systemd man pages which is under MIT-0
+# (no attribution required).
+[Transfer]
+Verify=yes
+
+[Source]
+Type=url-file
+Path=@UPDATE_URL@
+MatchPattern=Spectrum_@v_@u.verity
+
+[Target]
+Type=regular-file
+Path=/run/virtiofs/virtiofs0/updates
+MatchPattern=Spectrum_@v_@u.verity
+Mode=0644
diff --git a/host/rootfs/image/etc/vm-sysupdate.d/60-root.transfer b/host/rootfs/image/etc/vm-sysupdate.d/60-root.transfer
new file mode 100644
index 0000000000000000000000000000000000000000..8a3175684f1697e0eca443eb0a1a97176e4f66d4
--- /dev/null
+++ b/host/rootfs/image/etc/vm-sysupdate.d/60-root.transfer
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+# Uses example code from systemd man pages which is under MIT-0
+# (no attribution required).
+[Transfer]
+Verify=yes
+
+[Source]
+Type=url-file
+Path=@UPDATE_URL@
+MatchPattern=Spectrum_@v_@u.root
+
+[Target]
+Type=regular-file
+Path=/run/virtiofs/virtiofs0/updates
+MatchPattern=Spectrum_@v_@u.root
+Mode=0644
diff --git a/host/rootfs/image/etc/vm-sysupdate.d/70-kernel.transfer b/host/rootfs/image/etc/vm-sysupdate.d/70-kernel.transfer
new file mode 100644
index 0000000000000000000000000000000000000000..cb181239d71c5a6d0a5b3652d5534a23eda64183
--- /dev/null
+++ b/host/rootfs/image/etc/vm-sysupdate.d/70-kernel.transfer
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+# Uses example code from systemd man pages which is under MIT-0
+# (no attribution required).
+[Transfer]
+Verify=yes
+
+[Source]
+Type=url-file
+Path=@UPDATE_URL@
+MatchPattern=Spectrum_@v.efi
+
+[Target]
+Type=regular-file
+Path=/run/virtiofs/virtiofs0/updates
+MatchPattern=Spectrum_@v.efi
+Mode=0644
diff --git a/host/rootfs/image/usr/bin/spectrum-update b/host/rootfs/image/usr/bin/spectrum-update
new file mode 100755
index 0000000000000000000000000000000000000000..bf5b0b0c50cb5381af177c6df6a05d215f775489
--- /dev/null
+++ b/host/rootfs/image/usr/bin/spectrum-update
@@ -0,0 +1,87 @@
+#!/bin/execlineb -WS1
+# SPDX-License-Identifier: EUPL-1.2+
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+if { mkdir -p -m 0700 /run/updater }
+
+# Take a global lock to avoid races.
+s6-setlock /run/update-lock
+
+foreground { redirfd -w 2 /dev/null rmdir -- $1 }
+if { umask 0077 mkdir -p -- $1 }
+cd $1
+foreground {
+  # If this exists already that is okay.
+  foreground { redirfd -w 2 /dev/null btrfs subvolume create -- shared }
+
+  # Delete any stale temporary files.  Delete any existing signature
+  # files.  If the VM is still running (it should not be), the VM might
+  # have write access to the directory.  However, updates-dir-check is
+  # safe against that.
+  if { updates-dir-check cleanup shared }
+
+  if {
+    foreground {
+      # TODO: suppress only "subvolume does not exist" errors.
+      redirfd -w 2 /dev/null
+      btrfs subvolume delete snapshot
+    }
+    rm -f snapshot
+  }
+
+  backtick -E update_vm_id {
+    backtick -E id_path { readlink /run/vm/by-name/sys.appvm-systemd-sysupdate }
+    basename -- $id_path
+  }
+
+  # Set up /etc with what the VM needs.  The VM will overlay this
+  # on its own /etc.
+  #
+  # In the future, this should use a bind mount instead of copying
+  # into a tmpfs.  However, this would significantly complicate the
+  # cleanup code.  Deleting fs/etc would require undoing the bind
+  # mounts instead of rm -rf.  Once this code is in a separate mount
+  # namespace, the copies should be replaced by bind mounts.
+  if {
+    if { rm -rf -- /run/vm/by-id/${update_vm_id}/fs/etc }
+    umask 022
+    if { mkdir -p -- /run/vm/by-id/${update_vm_id}/fs/updates /run/vm/by-id/${update_vm_id}/fs/etc/systemd }
+    if { cp -R -- /etc/vm-sysupdate.d /etc/update-url /run/vm/by-id/${update_vm_id}/fs/etc }
+    cp -- /etc/systemd/import-pubring.gpg /run/vm/by-id/${update_vm_id}/fs/etc/systemd
+  }
+
+  # If the directory is already mounted, unmount it.  This prevents a
+  # confusing error from mount.
+  foreground { redirfd -w 2 /dev/null umount -- /run/vm/by-id/${update_vm_id}/fs/updates }
+
+  # Share the update directory with the VM.
+  if { mount --bind -- shared /run/vm/by-id/${update_vm_id}/fs/updates }
+
+  # Start the update VM.
+  if { vm-start $update_vm_id }
+
+  # Wait for the VM to exit.
+  # TODO: This is racy.  If the update finishes before this code runs,
+  # the s6-svwait call will fail.
+  if { s6-svwait -D /run/service/vmm/instance/${update_vm_id} }
+
+  # Remove the bind mount.
+  if { umount -- /run/vm/by-id/${update_vm_id}/fs/updates }
+
+  # Ensure that the VM cannot change the directory
+  # while systemd-sysupdate is using it.
+  if { btrfs subvolume snapshot -- shared snapshot }
+
+  # Validate the update directory.  Delete any stale temporary files.
+  # Check that a signature file was downloaded.
+  if { updates-dir-check check snapshot }
+
+  unshare --mount
+  if { mount --bind -o ro -- snapshot /run/updater }
+
+  /usr/lib/systemd/systemd-sysupdate update
+}
+importas -i sysupdate_exit_status ?
+# Clean up.
+foreground { btrfs subvolume delete -- snapshot }
+exit $sysupdate_exit_status
diff --git a/host/rootfs/os-release.in b/host/rootfs/os-release.in
new file mode 100644
index 0000000000000000000000000000000000000000..d6e699e82f87dcb1c4656ac19d4e9986282f14a5
--- /dev/null
+++ b/host/rootfs/os-release.in
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+NAME="Spectrum OS"
+ID=spectrum
+PRETTY_NAME="Spectrum @VERSION@"
+VERSION=@VERSION@
+VERSION_ID=@VERSION@
+IMAGE_ID=spectrum-root
+IMAGE_VERSION=@VERSION@
+RELEASE_TYPE=development
+HOME_URL="https://spectrum-os.org"
+BUG_REPORT_URL="mailto:discuss@spectrum-os.org"
+ANSI_COLOR="1;34"
+VENDOR_NAME=Spectrum
+VENDOR_URL="https://spectrum-os.org"
diff --git a/lib/config.default.nix b/lib/config.default.nix
index 489c231490a8b66aa01f50053b25646060f7f963..f6b70fa5e8431bef79222c10c79e8015f7fe65be 100644
--- a/lib/config.default.nix
+++ b/lib/config.default.nix
@@ -5,4 +5,6 @@
   pkgsFun = import ./nixpkgs.default.nix;
   pkgsArgs = {};
   version = "0.0.0";
+  updateUrl = "https://your-spectrum-os-update-server.invalid/download-directory";
+  updateSigningKey = ./fake-update-signing-key.gpg;
 }
diff --git a/lib/fake-update-signing-key.gpg b/lib/fake-update-signing-key.gpg
new file mode 100644
index 0000000000000000000000000000000000000000..12e18f4c7c740e31692e1f1975282fa72ac1f2e3
--- /dev/null
+++ b/lib/fake-update-signing-key.gpg
@@ -0,0 +1,3 @@
+SPDX-License-Identifier: CC0-1.0
+SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+NOT A VALID KEY - UPDATES WILL NOT WORK
diff --git a/vm/app/systemd-sysupdate/default.nix b/vm/app/systemd-sysupdate/default.nix
new file mode 100644
index 0000000000000000000000000000000000000000..db64f0105da6e1f9951d13f67a2ca8527ae6f73d
--- /dev/null
+++ b/vm/app/systemd-sysupdate/default.nix
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2023 Alyssa Ross <hi@alyssa.is>
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+
+import ../../../lib/call-package.nix (
+{ callSpectrumPackage, curl, lib, src
+, runCommand, systemd, writeScript
+}:
+
+let
+  downloadUpdate = builtins.path {
+    name = "download-update";
+    path = ./download-update;
+  };
+in
+
+callSpectrumPackage ../../make-vm.nix {} {
+  providers.net = [ "sys.netvm" ];
+  type = "nix";
+  run = writeScript "run-script" ''
+#!/usr/bin/execlineb -WS0
+export CURL_PATH ${curl}/bin/curl
+export SYSTEMD_SYSUPDATE_PATH ${systemd}/lib/systemd/systemd-sysupdate
+${downloadUpdate}
+'';
+}) (_: {})
diff --git a/vm/app/systemd-sysupdate/download-update b/vm/app/systemd-sysupdate/download-update
new file mode 100755
index 0000000000000000000000000000000000000000..eada41c6c8ad5edcedd9f4d76b76492e0b8be826
--- /dev/null
+++ b/vm/app/systemd-sysupdate/download-update
@@ -0,0 +1,68 @@
+#!/usr/bin/env -S execlineb -WS0
+# SPDX-License-Identifier: EUPL-1.2+
+# SPDX-FileCopyrightText: 2025 Demi Marie Obenour <demiobenour@gmail.com>
+export LC_ALL C
+export LANGUAGE C
+if { mount -toverlay -olowerdir=/run/virtiofs/virtiofs0/etc:/etc -- overlay /etc }
+backtick tmpdir { mktemp -d /tmp/sysupdate-XXXXXX }
+# Not a useless use of cat: if there are NUL bytes in the URL
+# busybox's awk might misbehave.
+backtick update_url { cat /etc/update-url }
+if {
+  backtick sed_rhs {
+    # Use awk to both validate the URL and to escape sed metacharacters.
+    # Reject URLs with control characters, query parameters, or fragments.
+    # They *cannot* work and so are rejected to produce better error messages.
+    #
+    # curl rejects control characters with "Malformed input to a URL function".
+    # Fragment specifiers ("#") and query parameters ("?") break concatenating
+    # /SHA256SUMS and /SHA256SUMS.sha256.asc onto the update URL.  Also, it is
+    # simpler to reject update URLs that contain whitespace than to try to
+    # escape them.
+    #
+    # Backslash needs to be escaped once for systemd-sysupdate and again for sed.
+    # Ampersand needs to be escaped once for sed.
+    awk "BEGIN {
+      update_url = ENVIRON[\"update_url\"];
+      if (update_url ~ /^[^\\001-\\040?#\\x7F]+$/) {
+        # Use & to avoid extra escaping (16 or 32 backslashes!)
+        # and a divergence between POSIX and GNU awk.
+        gsub(/\\\\/, \"&&&&\", update_url);
+        gsub(/&/, \"\\\\\\\\&\", update_url);
+        print update_url;
+        exit 0;
+      } else {
+        print ARGV[2] > \"/dev/stderr\";
+        exit 100;
+      }
+    }" -- $3
+    "Bad update URL from host: control characters, whitespace, query parameters, and fragment specifiers not allowed"
+  }
+  elglob -w -0 transfer_file_ /etc/vm-sysupdate.d/*.transfer
+  forx -E transfer_file { $transfer_file_ }
+  backtick target_basename {
+    basename -- $transfer_file
+  }
+  multisubstitute {
+    importas -iuS sed_rhs
+    importas -iuS target_basename
+    importas -iuS tmpdir
+    define sed_input $transfer_file
+  }
+  redirfd -w 1 ${tmpdir}/${target_basename}
+  sed -E -- "s#@UPDATE_URL@#${sed_rhs}#g" $sed_input
+}
+multisubstitute {
+  importas -iuS update_url
+  importas -iuS CURL_PATH
+  importas -iuS SYSTEMD_SYSUPDATE_PATH
+  importas -iuS tmpdir
+}
+if { $SYSTEMD_SYSUPDATE_PATH --definitions=${tmpdir} update }
+# [ and ] are allowed in update URLs so that IPv6 addresses work, but
+# they cause globbing in the curl command-line tool by default.  Use --globoff
+# to disable this feature.
+if { $CURL_PATH -L --proto-redir =http,https --globoff
+     -o /run/virtiofs/virtiofs0/updates/SHA256SUMS -- ${update_url}/SHA256SUMS }
+$CURL_PATH -L --proto-redir =http,https --globoff
+     -o /run/virtiofs/virtiofs0/updates/SHA256SUMS.sha256.asc -- ${update_url}/SHA256SUMS.sha256.asc

-- 
2.52.0


  parent reply	other threads:[~2025-11-29  9:51 UTC|newest]

Thread overview: 177+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-29 10:12 [PATCH 0/7] System updates based on systemd-sysupdate Demi Marie Obenour
2025-10-29 10:12 ` [PATCH 1/7] host/rootfs: Use full util-linux and systemd Demi Marie Obenour
2025-10-29 11:36   ` Alyssa Ross
2025-11-01  3:25     ` Demi Marie Obenour
2025-11-01 12:13       ` Alyssa Ross
2025-11-06  9:15         ` Demi Marie Obenour
2025-10-29 10:12 ` [PATCH 2/7] release/combined: Compress installation image Demi Marie Obenour
2025-10-29 11:50   ` Alyssa Ross
2025-10-29 16:51     ` Alyssa Ross
2025-11-01 22:15       ` Demi Marie Obenour
2025-11-02  0:18         ` Demi Marie Obenour
2025-11-02 12:05           ` Alyssa Ross
2025-11-02 14:42             ` Alyssa Ross
2025-11-02 19:38             ` Demi Marie Obenour
2025-10-29 10:12 ` [PATCH 3/7] tools: Add directory checker for updates Demi Marie Obenour
2025-10-29 12:01   ` Alyssa Ross
2025-10-31 20:31     ` Demi Marie Obenour
2025-11-01 12:17       ` Alyssa Ross
2025-11-01 14:09         ` Alyssa Ross
2025-11-01 18:36         ` Demi Marie Obenour
2025-11-02 12:18           ` Alyssa Ross
2025-11-02 12:43             ` Alyssa Ross
2025-11-02 19:34               ` Demi Marie Obenour
2025-11-04 15:26                 ` Alyssa Ross
2025-11-02 19:21             ` Demi Marie Obenour
2025-11-04 15:27               ` Alyssa Ross
2025-11-04 22:56                 ` Demi Marie Obenour
2025-11-06 10:15                   ` Alyssa Ross
2025-10-29 10:12 ` [PATCH 4/7] Adjust partition layout to support updates Demi Marie Obenour
2025-10-29 15:49   ` Alyssa Ross
2025-10-29 10:12 ` [PATCH 5/7] release: add install step Demi Marie Obenour
2025-10-29 12:20   ` Alyssa Ross
2025-10-29 10:12 ` [PATCH 6/7] Factor out dm-verity build rules Demi Marie Obenour
2025-10-29 12:22   ` Alyssa Ross
2025-10-31  6:39     ` Demi Marie Obenour
2025-10-29 10:12 ` [PATCH 7/7] Support updates via systemd-sysupdate Demi Marie Obenour
2025-10-29 15:48   ` Alyssa Ross
2025-11-12 22:14 ` [PATCH v2 0/8] System updates based on systemd-sysupdate Demi Marie Obenour
2025-11-12 22:14   ` [PATCH v2 1/8] host/rootfs: Install all programs from util-linuxMinimal Demi Marie Obenour
2025-11-13 12:35     ` Alyssa Ross
2025-11-12 22:14   ` [PATCH v2 2/8] host/rootfs: Install systemd-pull Demi Marie Obenour
2025-11-13 15:22     ` Alyssa Ross
2025-11-13 23:46       ` Demi Marie Obenour
2025-11-14 11:59         ` Alyssa Ross
2025-11-12 22:14   ` [PATCH v2 3/8] tools: Add directory checker for updates Demi Marie Obenour
2025-11-13 13:21     ` Alyssa Ross
2025-11-13 17:53       ` Demi Marie Obenour
2025-11-13 18:01         ` Alyssa Ross
2025-11-13 18:03           ` Demi Marie Obenour
2025-11-14 13:08             ` Alyssa Ross
2025-11-14 18:37               ` Demi Marie Obenour
2025-11-15 15:20                 ` Alyssa Ross
2025-11-12 22:14   ` [PATCH v2 4/8] Adjust partition layout to support updates Demi Marie Obenour
2025-11-13 16:00     ` Alyssa Ross
2025-11-12 22:14   ` [PATCH v2 5/8] release: Create directory with system update Demi Marie Obenour
2025-11-13 16:04     ` Alyssa Ross
2025-11-13 18:23       ` Demi Marie Obenour
2025-11-13 19:09         ` Alyssa Ross
2025-11-12 22:15   ` [PATCH v2 6/8] Support updates via systemd-sysupdate Demi Marie Obenour
2025-11-13 16:44     ` Alyssa Ross
2025-11-13 20:25       ` Demi Marie Obenour
2025-11-14 12:14         ` Alyssa Ross
2025-11-14 23:16           ` Demi Marie Obenour
2025-11-20 14:56             ` Alyssa Ross
2025-11-20 19:42               ` Demi Marie Obenour
2025-11-12 22:15   ` [PATCH v2 7/8] Documentation: Update support Demi Marie Obenour
2025-11-13 16:49     ` Alyssa Ross
2025-11-13 22:24       ` Demi Marie Obenour
2025-11-14 12:16         ` Alyssa Ross
2025-11-12 22:15   ` [PATCH v2 8/8] lib/config.nix: Validate configuration parameters Demi Marie Obenour
2025-11-13 17:16     ` Alyssa Ross
2025-11-19  8:18   ` [PATCH v3 00/14] System updates based on systemd-sysupdate Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 01/14] host/rootfs: Install all programs from util-linuxMinimal Demi Marie Obenour
2025-11-19 14:14       ` Alyssa Ross
2025-11-20  0:12         ` Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 02/14] host/rootfs: Install systemd-pull Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 03/14] tools: Add directory checker for updates Demi Marie Obenour
2025-11-19 14:45       ` Alyssa Ross
2025-11-19 23:58         ` Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 04/14] scripts: port make-gpt.sh to bash Demi Marie Obenour
2025-11-20 10:28       ` Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 05/14] scripts/make-gpt.sh: Allow specifying partition size Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 06/14] Support generating multiple partition UUIDs Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 07/14] scripts: Use shell expansion to get partition path Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 08/14] Use OS version to set partition labels and UKI name Demi Marie Obenour
2025-11-20 12:11       ` Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 09/14] release: Compress installation images and remove live image Demi Marie Obenour
2025-11-20 12:14       ` Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 10/14] Add B partitions to installation images Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 11/14] release: Create directory with system update Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 12/14] Support updates via systemd-sysupdate Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 13/14] Documentation: Update support Demi Marie Obenour
2025-11-19  8:18     ` [PATCH v3 14/14] Validate configuration parameters Demi Marie Obenour
2025-11-22  1:23     ` [PATCH v4 00/14] System updates based on systemd-sysupdate Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 01/14] host/rootfs: Install all programs from util-linuxMinimal Demi Marie Obenour
2025-11-25 11:56         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 02/14] host/rootfs: Install systemd-pull Demi Marie Obenour
2025-11-25  7:36         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 03/14] tools: Add directory checker for updates Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 04/14] scripts: port make-gpt.sh to bash Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 05/14] scripts/make-gpt.sh: Allow specifying partition size Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 06/14] Support generating multiple partition UUIDs Demi Marie Obenour
2025-11-25 13:02         ` Alyssa Ross
2025-11-26 18:26           ` Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 07/14] scripts: Use shell expansion to get partition path Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 08/14] release: Compress installation images and remove live image Demi Marie Obenour
2025-11-25 13:19         ` Alyssa Ross
2025-11-25 22:38           ` Demi Marie Obenour
2025-11-28 11:09             ` Alyssa Ross
2025-11-28 19:45               ` Demi Marie Obenour
2025-11-22  1:23       ` [PATCH v4 09/14] Use OS version to set partition labels and UKI name Demi Marie Obenour
2025-11-25 14:11         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 10/14] Add B partitions to installation images Demi Marie Obenour
2025-11-25 16:31         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 11/14] release: Create directory with system update Demi Marie Obenour
2025-11-25 16:50         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 12/14] Support updates via systemd-sysupdate Demi Marie Obenour
2025-11-25 17:54         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 13/14] Documentation: Update support Demi Marie Obenour
2025-11-25 18:00         ` Alyssa Ross
2025-11-22  1:23       ` [PATCH v4 14/14] Validate configuration parameters Demi Marie Obenour
2025-11-25 18:06         ` Alyssa Ross
2025-11-25 12:22       ` [PATCH v4 00/14] System updates based on systemd-sysupdate Alyssa Ross
2025-11-26 19:40       ` [PATCH v5 00/13] " Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 01/13] tools: Add directory checker for updates Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 02/13] scripts: port make-gpt.sh to bash Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 03/13] scripts/make-gpt.sh: Allow specifying partition size Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 04/13] Port scripts/format-uuid.sh to awk Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 05/13] Use set and a command substitution to set UUID variables Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 06/13] scripts: Use shell expansion to get partition path Demi Marie Obenour
2025-11-28 11:20           ` Alyssa Ross
2025-11-26 19:40         ` [PATCH v5 07/13] release: Compress installation images and remove live image Demi Marie Obenour
2025-11-28 11:21           ` Alyssa Ross
2025-11-26 19:40         ` [PATCH v5 08/13] Use OS version to set partition labels and UKI name Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 09/13] Add B partitions to installation images Demi Marie Obenour
2025-11-28 11:23           ` Alyssa Ross
2025-11-26 19:40         ` [PATCH v5 10/13] release: Create directory with system update Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 11/13] Support updates via systemd-sysupdate Demi Marie Obenour
2025-11-28 13:47           ` Alyssa Ross
2025-11-28 20:27             ` Demi Marie Obenour
2025-11-28 20:41               ` Alyssa Ross
2025-11-28 20:44                 ` Demi Marie Obenour
2025-11-28 21:08                   ` Alyssa Ross
2025-11-28 21:28                     ` Demi Marie Obenour
2025-11-28 21:30                       ` Alyssa Ross
2025-11-26 19:40         ` [PATCH v5 12/13] Documentation: Update support Demi Marie Obenour
2025-11-26 19:40         ` [PATCH v5 13/13] Validate configuration parameters Demi Marie Obenour
2025-11-29  9:49         ` [PATCH v6 0/8] System updates based on systemd-sysupdate Demi Marie Obenour
2025-11-29  9:49           ` [PATCH v6 1/8] tools: Add directory checker for updates Demi Marie Obenour
2025-11-29 11:16             ` Alyssa Ross
2025-11-29  9:49           ` [PATCH v6 2/8] release: Compress installation images and remove live image Demi Marie Obenour
2025-11-29 11:16             ` Alyssa Ross
2025-11-29  9:50           ` [PATCH v6 3/8] Use OS version to set partition labels and UKI name Demi Marie Obenour
2025-11-29 11:16             ` Alyssa Ross
2025-11-29  9:50           ` [PATCH v6 4/8] Add B partitions to installation images Demi Marie Obenour
2025-11-29 11:16             ` Alyssa Ross
2025-11-29  9:50           ` [PATCH v6 5/8] release: Create directory with system update Demi Marie Obenour
2025-11-29 11:16             ` Alyssa Ross
2025-11-29  9:50           ` Demi Marie Obenour [this message]
2025-11-29 11:16             ` [PATCH v6 6/8] Support updates via systemd-sysupdate Alyssa Ross
2025-11-29  9:50           ` [PATCH v6 7/8] Documentation: Update support Demi Marie Obenour
2025-11-30 21:46             ` Alyssa Ross
2025-11-29  9:50           ` [PATCH v6 8/8] Validate configuration parameters Demi Marie Obenour
2025-11-26 19:33     ` [PATCH v4 00/13] System updates based on systemd-sysupdate Demi Marie Obenour
2025-11-26 19:33       ` [PATCH v4 01/13] tools: Add directory checker for updates Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 02/13] scripts: port make-gpt.sh to bash Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 03/13] scripts/make-gpt.sh: Allow specifying partition size Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 04/13] Port scripts/format-uuid.sh to awk Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 05/13] Use set and a command substitution to set UUID variables Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 06/13] scripts: Use shell expansion to get partition path Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 07/13] release: Compress installation images and remove live image Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 08/13] Use OS version to set partition labels and UKI name Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 09/13] Add B partitions to installation images Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 10/13] release: Create directory with system update Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 11/13] Support updates via systemd-sysupdate Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 12/13] Documentation: Update support Demi Marie Obenour
2025-11-26 19:34       ` [PATCH v4 13/13] Validate configuration parameters Demi Marie Obenour

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=20251129-updates-v6-6-9edb87a2e509@gmail.com \
    --to=demiobenour@gmail.com \
    --cc=devel@spectrum-os.org \
    --cc=hi@alyssa.is \
    /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).