Splux Linux

source-based Linux using SPS

SPS · log · files · refs · tags · releases · people · languages · GitHub · commit

cd5640f


Enable s6, runit, dinit, and Shepherd, and honor compiler flags.

Date
Commitcd5640f3591a4332ab27c0c4e68362019bb30900
GitHubmirror

Parents

Stat

 DESIGN.md                                          |  13 +
 Makefile                                           |  19 ++
 README.md                                          |   4 +-
 bin/mkiso                                          |  18 +-
 bin/pkin                                           |   8 +
 bin/setup                                          | 193 ++++++++-----
 examples/sps.conf                                  |   7 +-
 lib/common.sh                                      | 102 ++++++-
 lib/mkiso/seed-bins                                |   5 +
 lib/setup/service.sh                               | 300 ++++++++++++++++++++-
 lib/setup/sv/dinit/NetworkManager                  |   5 +
 lib/setup/sv/dinit/bluetooth                       |   5 +
 lib/setup/sv/dinit/cupsd                           |   5 +
 lib/setup/sv/dinit/dbus                            |   4 +
 lib/setup/sv/dinit/dhcpcd                          |   4 +
 lib/setup/sv/dinit/elogind                         |   5 +
 lib/setup/sv/dinit/iwd                             |   5 +
 lib/setup/sv/dinit/pipewire                        |   5 +
 lib/setup/sv/dinit/power-profiles-daemon           |   5 +
 lib/setup/sv/dinit/sddm                            |   5 +
 lib/setup/sv/dinit/seatd                           |   4 +
 lib/setup/sv/dinit/sshd                            |   4 +
 lib/setup/sv/dinit/wireplumber                     |   5 +
 lib/setup/sv/openrc/NetworkManager                 |  12 +
 lib/setup/sv/openrc/bluetooth                      |  10 +
 lib/setup/sv/openrc/cupsd                          |  11 +
 lib/setup/sv/openrc/dbus                           |  15 ++
 lib/setup/sv/openrc/dhcpcd                         |  10 +
 lib/setup/sv/openrc/elogind                        |  10 +
 lib/setup/sv/openrc/iwd                            |  11 +
 lib/setup/sv/openrc/pipewire                       |  10 +
 lib/setup/sv/openrc/power-profiles-daemon          |  10 +
 lib/setup/sv/openrc/sddm                           |  10 +
 lib/setup/sv/openrc/seatd                          |   6 +
 lib/setup/sv/openrc/sshd                           |   6 +
 lib/setup/sv/openrc/wireplumber                    |  10 +
 lib/setup/sv/runit/NetworkManager/run              |   8 +
 lib/setup/sv/runit/bluetooth/run                   |   8 +
 lib/setup/sv/runit/cupsd/run                       |   8 +
 lib/setup/sv/runit/dbus/run                        |   4 +
 lib/setup/sv/runit/dhcpcd/run                      |   3 +
 lib/setup/sv/runit/elogind/run                     |   8 +
 lib/setup/sv/runit/iwd/run                         |   8 +
 lib/setup/sv/runit/pipewire/run                    |   8 +
 lib/setup/sv/runit/power-profiles-daemon/run       |   8 +
 lib/setup/sv/runit/sddm/run                        |   8 +
 lib/setup/sv/runit/seatd/run                       |   3 +
 lib/setup/sv/runit/sshd/run                        |   3 +
 lib/setup/sv/runit/wireplumber/run                 |   8 +
 lib/setup/sv/s6/NetworkManager/run                 |   8 +
 lib/setup/sv/s6/bluetooth/run                      |   8 +
 lib/setup/sv/s6/cupsd/run                          |   8 +
 lib/setup/sv/s6/dbus/run                           |   4 +
 lib/setup/sv/s6/dhcpcd/run                         |   3 +
 lib/setup/sv/s6/elogind/run                        |   8 +
 lib/setup/sv/s6/iwd/run                            |   8 +
 lib/setup/sv/s6/pipewire/run                       |   8 +
 lib/setup/sv/s6/power-profiles-daemon/run          |   8 +
 lib/setup/sv/s6/sddm/run                           |   8 +
 lib/setup/sv/s6/seatd/run                          |   3 +
 lib/setup/sv/s6/sshd/run                           |   3 +
 lib/setup/sv/s6/wireplumber/run                    |   8 +
 lib/setup/sv/shepherd/NetworkManager.scm           |   9 +
 lib/setup/sv/shepherd/bluetooth.scm                |   9 +
 lib/setup/sv/shepherd/cupsd.scm                    |   9 +
 lib/setup/sv/shepherd/dbus.scm                     |   9 +
 lib/setup/sv/shepherd/dhcpcd.scm                   |   9 +
 lib/setup/sv/shepherd/elogind.scm                  |   9 +
 lib/setup/sv/shepherd/iwd.scm                      |   9 +
 lib/setup/sv/shepherd/pipewire.scm                 |   9 +
 lib/setup/sv/shepherd/power-profiles-daemon.scm    |   9 +
 lib/setup/sv/shepherd/sddm.scm                     |   9 +
 lib/setup/sv/shepherd/seatd.scm                    |   9 +
 lib/setup/sv/shepherd/sshd.scm                     |   9 +
 lib/setup/sv/shepherd/wireplumber.scm              |   9 +
 lib/setup/sv/systemd/NetworkManager.service        |  11 +
 lib/setup/sv/systemd/bluetooth.service             |  11 +
 lib/setup/sv/systemd/cups.service                  |  11 +
 lib/setup/sv/systemd/dbus.service                  |  10 +
 lib/setup/sv/systemd/dhcpcd.service                |   9 +
 lib/setup/sv/systemd/elogind.service               |  11 +
 lib/setup/sv/systemd/iwd.service                   |  11 +
 lib/setup/sv/systemd/pipewire.service              |  11 +
 lib/setup/sv/systemd/power-profiles-daemon.service |  11 +
 lib/setup/sv/systemd/sddm.service                  |  11 +
 lib/setup/sv/systemd/seatd.service                 |   9 +
 lib/setup/sv/systemd/sshd.service                  |   9 +
 lib/setup/sv/systemd/wireplumber.service           |  11 +
 man/pkin.1                                         |   7 +
 man/setup.1                                        |  45 +++-
 man/sps.conf.5                                     |  53 +++-
 tests/test_common.sh                               |  30 +++
 tests/test_mkiso.sh                                |   6 +
 tests/test_services.sh                             |  38 ++-
 tests/test_setup.sh                                |  29 ++
 95 files changed, 1392 insertions(+), 100 deletions(-)

Diff

diff --git a/DESIGN.md b/DESIGN.md
index ba3671b..82623f2 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -87,6 +87,19 @@ post-remove
 
 Hook entries must be regular files, not symlinks.
 
+`pkin` always attempts to enable a mapped service after a successful
+post-install. The map is `$SPS_LIBDIR/setup/services`. The init name is
+read from `$SPS_ROOT/etc/sps/init`. Supported names are `systemd`,
+`openrc`, `s6`, `runit`, `dinit`, and `shepherd`. When the package did
+not ship a file for that init, a stock script is copied from
+`$SPS_LIBDIR/setup/sv/<init>/` and then enabled. Unknown packages and
+`init` set to `none` are no-ops.
+
+`setup --init` installs the matching `init-*` metapackage, drops
+conflicting PID 1 packages from the plan, writes `/etc/sps/init` and
+`/etc/sps/sps.conf` before the first `sget`, and enables every mapped
+service that is present (or can be filled from stock scripts).
+
 ## Repository index
 
 Indexes live under `$SPS_CACHE/indexes`. Each package record is tab-separated
diff --git a/Makefile b/Makefile
index c4e3591..3380561 100644
--- a/Makefile
+++ b/Makefile
@@ -29,6 +29,11 @@ lint:
 		[ -f $$library ] || continue; \
 		$(POSIX_SHELL) -n $$library; \
 	done
+	@set -e; for script in lib/setup/sv/runit/*/run lib/setup/sv/s6/*/run \
+		lib/setup/sv/openrc/*; do \
+		[ -f $$script ] || continue; \
+		$(POSIX_SHELL) -n $$script; \
+	done
 	@set -e; for script in lib/mkiso/initrd-init lib/mkiso/live-rc \
 		lib/mkiso/live-plasma lib/mkiso/live-plasma-session \
 		lib/mkiso/live-init lib/mkiso/udhcpc-script; do \
@@ -190,6 +195,20 @@ install:
 		[ -f "$$f" ] || continue; \
 		install -m 0644 $$f "$(DESTDIR)$(LIBDIR)/setup/"; \
 	done
+	@set -e; \
+	if [ -d lib/setup/sv ]; then \
+		find lib/setup/sv -type d | while read -r d; do \
+			install -d "$(DESTDIR)$(LIBDIR)/setup/$${d#lib/setup/}"; \
+		done; \
+		find lib/setup/sv -type f | while read -r f; do \
+			mode=0644; \
+			case $$f in \
+				*/sv/openrc/*|*/sv/runit/*/run|*/sv/runit/*/finish|*/sv/s6/*/run|*/sv/s6/*/finish) \
+					mode=0755 ;; \
+			esac; \
+			install -m $$mode $$f "$(DESTDIR)$(LIBDIR)/setup/$${f#lib/setup/}"; \
+		done; \
+	fi
 
 # Configuration is separate so installation never overwrites administrator
 # policy. Existing files are deliberately left untouched.
diff --git a/README.md b/README.md
index b22985f..dabe910 100644
--- a/README.md
+++ b/README.md
@@ -186,8 +186,8 @@ checklist (`--extra`) plus locale and login-shell screens.
 The installer writes hostname, timezone, keymap, and a first user into the
 target, then copies bundled live recipe trees (when present), runs
 `src update`, and `sget install`. Guided partitioning is optional and waits
-until the last confirmation. Choose systemd or OpenRC
-as PID 1 (`--init`); those packages conflict. `grub-install` still needs
+until the last confirmation. Choose systemd, OpenRC, s6, runit, dinit, or
+Shepherd as PID 1 (`--init`); those packages conflict. `grub-install` still needs
 `--install-bootloader` and `--disk`.
 
 ## SPS Linux live ISO
diff --git a/bin/mkiso b/bin/mkiso
index 8729df6..63c3de0 100755
--- a/bin/mkiso
+++ b/bin/mkiso
@@ -897,6 +897,14 @@ mkiso_verify_compiler()
 				"seeded python is missing the encodings stdlib module"
 		fi
 	fi
+	if [ -e /usr/bin/pod2man ] || [ -L /usr/bin/pod2man ]; then
+		if [ ! -e "$mkiso_live/usr/bin/pod2man" ] &&
+			[ ! -L "$mkiso_live/usr/bin/pod2man" ]
+		then
+			sps_die "$SPS_EX_PACKAGE" \
+				"seeded live gcc is missing pod2man"
+		fi
+	fi
 }
 
 mkiso_verify_plasma()
@@ -1560,15 +1568,7 @@ if [ -f "$mkiso_libdir/udhcpc-script" ]; then
 		"$mkiso_live/etc/sps/udhcpc-script"
 fi
 
-printf '%s\n' \
-	'db /var/lib/sps' \
-	'cache /var/cache/sps' \
-	'build /var/tmp/sps' \
-	'repo_root /usr/src/sps' \
-	"makejobs ${SPS_MAKEJOBS:-$(nproc 2>/dev/null || printf 2)}" \
-	'compression auto' \
-	'preserve etc' \
-	>"$mkiso_live/etc/sps/sps.conf"
+sps_print_default_conf >"$mkiso_live/etc/sps/sps.conf"
 
 mkiso_repos=$mkiso_live/etc/sps/repos.conf
 : >"$mkiso_repos"
diff --git a/bin/pkin b/bin/pkin
index 070a118..00e2089 100755
--- a/bin/pkin
+++ b/bin/pkin
@@ -826,6 +826,14 @@ if ! sps_run_hook post-install; then
     exit 10
 fi
 
+# Enable a mapped service for the init recorded in $SPS_ROOT/etc/sps/init.
+# Stock scripts are copied when the package did not ship this init's file.
+if [ -f "$SPS_LIBDIR/setup/service.sh" ]; then
+    # shellcheck disable=SC1091
+    . "$SPS_LIBDIR/setup/service.sh"
+    setup_service_hook_enable || :
+fi
+
 if [ -n "$sps_old_version" ]; then
     printf '%s: upgraded %s %s-%s -> %s-%s\n' "$sps_prog" "$sps_name" \
         "$sps_old_version" "$sps_old_release" "$sps_version" "$sps_release"
diff --git a/bin/setup b/bin/setup
index c18e038..2d0f4fe 100755
--- a/bin/setup
+++ b/bin/setup
@@ -75,8 +75,10 @@ options:
   --kernel NAME             none, linux, linux-desktop, or custom
   --kernel-config FILE      .config for --kernel custom
   --drivers auto|none       map lspci/cpu to packages (microcode, nouveau, iwd)
-  --init systemd|openrc     PID 1 on the installed system (default: none in
-                            scripts; systemd on the interactive screen)
+  --init systemd|openrc|s6|runit|dinit|shepherd|none
+                            PID 1 on the installed system (default: none in
+                            scripts; live /etc/sps/init or systemd on the
+                            interactive screen)
   --bootloader none|grub    write bootloader notes; never implied
   --install-bootloader      run grub-install on --disk (explicit, dangerous)
   --update|--no-update      run or skip src update before installing
@@ -105,6 +107,58 @@ if [ -f "$SPS_LIBDIR/setup/service.sh" ]; then
 	. "$SPS_LIBDIR/setup/service.sh"
 fi
 
+# True when package $2 cannot be installed with chosen init $1.
+setup_pkg_conflicts_init()
+{
+	setup_ci=$1
+	setup_cp=$2
+	case $setup_ci in
+		systemd)
+			case $setup_cp in
+				elogind|eudev|openrc|init-openrc|s6|s6-linux-init|init-s6|runit|init-runit|dinit|init-dinit|shepherd|init-shepherd)
+					return 0
+					;;
+			esac
+			;;
+		openrc)
+			case $setup_cp in
+				systemd|init-systemd|s6|s6-linux-init|init-s6|runit|init-runit|dinit|init-dinit|shepherd|init-shepherd)
+					return 0
+					;;
+			esac
+			;;
+		s6)
+			case $setup_cp in
+				systemd|init-systemd|openrc|init-openrc|runit|init-runit|dinit|init-dinit|shepherd|init-shepherd)
+					return 0
+					;;
+			esac
+			;;
+		runit)
+			case $setup_cp in
+				systemd|init-systemd|openrc|init-openrc|s6|s6-linux-init|init-s6|dinit|init-dinit|shepherd|init-shepherd)
+					return 0
+					;;
+			esac
+			;;
+		dinit)
+			case $setup_cp in
+				systemd|init-systemd|openrc|init-openrc|s6|s6-linux-init|init-s6|runit|init-runit|shepherd|init-shepherd)
+					return 0
+					;;
+			esac
+			;;
+		shepherd)
+			case $setup_cp in
+				systemd|init-systemd|openrc|init-openrc|s6|s6-linux-init|init-s6|runit|init-runit|dinit|init-dinit)
+					return 0
+					;;
+			esac
+			;;
+	esac
+	return 1
+}
+
 setup_collect_recipe_names()
 {
 	setup_names_out=$1
@@ -964,11 +1018,24 @@ setup_ask_init()
 	if [ -n "$setup_init" ]; then
 		return 0
 	fi
-	setup_init=$(setup_dialog --title "Init system" --default-item systemd \
-		--menu "PID 1 on the installed system. Live images prefer systemd when the ISO was built with it; busybox init is the live fallback so the disc cannot panic if systemd is missing. These packages conflict; sget will refuse both." \
-		16 72 4 \
+	setup_init_default=systemd
+	if [ -f /etc/sps/init ]; then
+		setup_live_init=$(sed -n '1p' /etc/sps/init)
+		case $setup_live_init in
+			systemd|openrc|s6|runit|dinit|shepherd)
+				setup_init_default=$setup_live_init
+				;;
+		esac
+	fi
+	setup_init=$(setup_dialog --title "Init system" --default-item "$setup_init_default" \
+		--menu "PID 1 on the installed system. These packages conflict; sget will refuse more than one. Supervision inits (s6, runit, dinit, Shepherd) pull eudev and elogind the same way OpenRC does. setup copies stock service scripts for the chosen init when a package did not ship its own." \
+		20 76 8 \
 		systemd "systemd (udev, logind, the default)" \
-		openrc "OpenRC plus eudev and elogind") ||
+		openrc "OpenRC plus eudev and elogind" \
+		s6 "s6 plus eudev and elogind" \
+		runit "runit plus eudev and elogind" \
+		dinit "dinit plus eudev and elogind" \
+		shepherd "GNU Shepherd plus eudev, elogind, and Guile") ||
 		setup_fail "$SPS_EX_USAGE" "cancelled"
 }
 
@@ -1293,8 +1360,8 @@ case $setup_drivers in
 	*) setup_fail "$SPS_EX_USAGE" "drivers must be auto or none" ;;
 esac
 case $setup_init in
-	none|systemd|openrc) ;;
-	*) setup_fail "$SPS_EX_USAGE" "init must be systemd, openrc, or none" ;;
+	none|systemd|openrc|s6|runit|dinit|shepherd) ;;
+	*) setup_fail "$SPS_EX_USAGE" "init must be systemd, openrc, s6, runit, dinit, shepherd, or none" ;;
 esac
 case $setup_services in
 	yes|no) ;;
@@ -1457,44 +1524,17 @@ done
 # Chosen init wins over set/package combinations that sget would refuse.
 for setup_item in $setup_extra $setup_enable; do
 	[ -n "$setup_item" ] || continue
-	case $setup_init in
-		systemd)
-			case $setup_item in
-				elogind|eudev|openrc|init-openrc)
-					setup_fail "$SPS_EX_CONFLICT" \
-						"cannot combine --init systemd with $setup_item (elogind/eudev/OpenRC conflict with systemd)"
-					;;
-			esac
-			;;
-		openrc)
-			case $setup_item in
-				systemd|init-systemd)
-					setup_fail "$SPS_EX_CONFLICT" \
-						"cannot combine --init openrc with $setup_item"
-					;;
-			esac
-			;;
-	esac
+	if setup_pkg_conflicts_init "$setup_init" "$setup_item"; then
+		setup_fail "$SPS_EX_CONFLICT" \
+			"cannot combine --init $setup_init with $setup_item"
+	fi
 done
 
 setup_pkg_filtered=$setup_work/packages.filtered
 : >"$setup_pkg_filtered"
 while IFS= read -r setup_pkg || [ -n "$setup_pkg" ]; do
 	[ -n "$setup_pkg" ] || continue
-	setup_drop=0
-	case $setup_init in
-		systemd)
-			case $setup_pkg in
-				elogind|eudev|openrc|init-openrc) setup_drop=1 ;;
-			esac
-			;;
-		openrc)
-			case $setup_pkg in
-				systemd|init-systemd) setup_drop=1 ;;
-			esac
-			;;
-	esac
-	if [ "$setup_drop" -eq 1 ]; then
+	if setup_pkg_conflicts_init "$setup_init" "$setup_pkg"; then
 		printf '%s: dropping %s (conflicts with init %s)\n' \
 			"$sps_prog" "$setup_pkg" "$setup_init" >&2
 		continue
@@ -1512,6 +1552,10 @@ esac
 case $setup_init in
 	systemd) setup_add_pkg init-systemd ;;
 	openrc) setup_add_pkg init-openrc ;;
+	s6) setup_add_pkg init-s6 ;;
+	runit) setup_add_pkg init-runit ;;
+	dinit) setup_add_pkg init-dinit ;;
+	shepherd) setup_add_pkg init-shepherd ;;
 esac
 
 if [ "$setup_drivers" = auto ]; then
@@ -1607,7 +1651,7 @@ setup_summary()
 	if [ "$setup_init" = none ] &&
 	   grep -Eq '^(sddm|plasma-desktop|plasma-full)$' "$setup_pkg_file"
 	then
-		printf '\nNote: init is none; SDDM/Plasma will not start at boot. Choose systemd or OpenRC.\n'
+		printf '\nNote: init is none; SDDM/Plasma will not start at boot. Choose systemd, OpenRC, s6, runit, dinit, or Shepherd.\n'
 	fi
 	if [ "$setup_kernel" = none ]; then
 		case $setup_partition in
@@ -1790,20 +1834,32 @@ EOF
 	cat >"$setup_root/root/sps-init.txt" <<EOF
 Init choice: ${setup_init:-none}
 
-systemd and OpenRC conflict; sget will not install both. After the
-packages land:
-
-  systemd  PID 1 is /usr/lib/systemd/systemd. Enable getty@tty1
-           and, for Plasma, a display manager. /etc/machine-id is
-           created on first boot if missing.
-  openrc   PID 1 is OpenRC. Add services with rc-update. eudev and
-           elogind are in the init-openrc set.
-  none     no init package was requested. Busybox can still provide
-           an init applet if you copy it to /sbin/init yourself.
-
-setup enables known services (SDDM, dbus, sshd, NetworkManager, ...)
-for the chosen init when those packages installed a unit or OpenRC
-script. See /root/sps-services.txt. Pass --services no to skip that.
+These PID 1 packages conflict; sget will not install more than one.
+After the packages land:
+
+  systemd   PID 1 is /usr/lib/systemd/systemd. Enable getty@tty1
+            and, for Plasma, a display manager. /etc/machine-id is
+            created on first boot if missing.
+  openrc    PID 1 is OpenRC. Add services with rc-update. eudev and
+            elogind are in the init-openrc set.
+  s6        PID 1 execs s6-svscan on /run/service. Enabled longruns
+            live in /etc/s6/enabled. eudev and elogind come with
+            init-s6. s6-linux-init-maker is installed for admins
+            who want the official stage tree.
+  runit     PID 1 is runit-init. Stages are /etc/runit/1, 2, 3.
+            Enable with a symlink in /etc/runit/runsvdir/default.
+  dinit     PID 1 is dinit. Enable by linking into /etc/dinit.d/boot.d.
+            Halt/reboot wrappers are named dinit-halt and friends so
+            they do not collide with busybox.
+  shepherd  PID 1 is GNU Shepherd. Services are Scheme files in
+            /etc/shepherd.d. This pulls Guile and Fibers.
+  none      no init package was requested. Busybox can still provide
+            an init applet if you copy it to /sbin/init yourself.
+
+setup copies stock service scripts for the chosen init when a package
+did not ship one, then enables known services (SDDM, dbus, sshd,
+NetworkManager, ...). See /root/sps-services.txt. Pass --services no
+to skip that. Later sget install of a mapped package enables it too.
 EOF
 
 	cat >"$setup_root/root/sps-network.txt" <<EOF
@@ -1960,11 +2016,24 @@ fi
 setup_seed_recipe_trees
 
 # Hooks run during sget, before apply_config. Record the init choice so
-# post-install can enable SDDM and friends for systemd or OpenRC.
+# post-install can enable SDDM and friends for the chosen PID 1.
 mkdir -p "$setup_root/etc/sps" ||
 	setup_fail "$SPS_EX_PERMISSION" "cannot create $setup_root/etc/sps"
 printf '%s\n' "${setup_init:-none}" >"$setup_root/etc/sps/init"
 
+# sget/mkpkg read $SPS_ROOT/etc/sps/sps.conf once SPS_CONFIG is unset.
+# Write it before the first package so makejobs/cflags apply to the
+# source install, not only after apply_config.
+if [ ! -f "$setup_root/etc/sps/sps.conf" ]; then
+	if [ -f /etc/sps/live ] && [ -f /etc/sps/sps.conf ]; then
+		cp /etc/sps/sps.conf "$setup_root/etc/sps/sps.conf" ||
+			setup_fail "$SPS_EX_PERMISSION" "cannot copy live sps.conf"
+	else
+		sps_print_default_conf >"$setup_root/etc/sps/sps.conf" ||
+			setup_fail "$SPS_EX_PERMISSION" "cannot write sps.conf"
+	fi
+fi
+
 if [ "$setup_do_update" -eq 1 ]; then
 	printf '%s: updating source repositories\n' "$sps_prog" >&2
 	src update || setup_fail "$SPS_EX_NETWORK" "src update failed"
@@ -2025,7 +2094,7 @@ if [ "$setup_services" = yes ] &&
    command -v setup_service_enable_present >/dev/null 2>&1
 then
 	case $setup_init in
-		systemd|openrc)
+		systemd|openrc|s6|runit|dinit|shepherd)
 			printf '%s: enabling services after user configuration, init %s\n' \
 				"$sps_prog" "$setup_init" >&2
 			setup_svc_list=$(setup_service_enable_present \
@@ -2034,6 +2103,8 @@ then
 				printf '%s\n' \
 					"Init: $setup_init" \
 					"setup enables known services that were installed." \
+					"Missing unit/script files are filled from stock copies" \
+					"in $SPS_LIBDIR/setup/sv/$setup_init." \
 					"It does not start daemons inside a target chroot." \
 					""
 				if [ -n "$setup_svc_list" ]; then
@@ -2041,9 +2112,8 @@ then
 					printf '%s\n' "$setup_svc_list" | sort -u
 				else
 					printf '%s\n' \
-						'No mapped unit or OpenRC script was present in the target.' \
-						'Install the package files (for example sddm.service) and rerun,' \
-						'or enable the service yourself after first boot.'
+						'No mapped service could be enabled in the target.' \
+						'Install the package and rerun, or enable it yourself after first boot.'
 				fi
 			} >"$setup_root/root/sps-services.txt"
 			;;
@@ -2052,7 +2122,8 @@ then
 Init is ${setup_init:-none}. setup did not enable services.
 
 To have SDDM, sshd, NetworkManager, and similar start at boot, rerun
-setup with --init systemd or --init openrc, or enable them yourself.
+setup with --init systemd, openrc, s6, runit, dinit, or shepherd, or
+enable them yourself.
 EOF
 			;;
 	esac
diff --git a/examples/sps.conf b/examples/sps.conf
index f1e0c5d..8102c0a 100644
--- a/examples/sps.conf
+++ b/examples/sps.conf
@@ -4,8 +4,13 @@ db /var/lib/sps
 cache /var/cache/sps
 build /var/tmp/sps
 repo_root /usr/src/sps
-makejobs 1
+makejobs auto
+cflags -O2 -pipe
+cxxflags -O2 -pipe
+march native
 compression auto
 preserve etc
 
 # Leave arch unset to use `uname -m`.
+# march none disables adding -march= to CFLAGS/CXXFLAGS.
+# cppflags and ldflags occupy the rest of the line the same way cflags does.
diff --git a/lib/common.sh b/lib/common.sh
index 283c5f8..8240fed 100644
--- a/lib/common.sh
+++ b/lib/common.sh
@@ -504,6 +504,39 @@ sps_repo_lines()
 	done
 }
 
+# Number of CPUs for makejobs auto. Never prints zero or a non-integer.
+sps_nproc()
+{
+	sps_n=
+	if command -v nproc >/dev/null 2>&1; then
+		sps_n=$(nproc 2>/dev/null) || sps_n=
+	fi
+	if [ -z "$sps_n" ] && command -v getconf >/dev/null 2>&1; then
+		sps_n=$(getconf _NPROCESSORS_ONLN 2>/dev/null) || sps_n=
+	fi
+	case $sps_n in
+		''|*[!0-9]*|0) printf '%s\n' 1 ;;
+		*) printf '%s\n' "$sps_n" ;;
+	esac
+}
+
+# Default sps.conf written onto live images and into a fresh target before
+# the first sget. Flags occupy the rest of the line; see sps.conf(5).
+sps_print_default_conf()
+{
+	printf '%s\n' \
+		'db /var/lib/sps' \
+		'cache /var/cache/sps' \
+		'build /var/tmp/sps' \
+		'repo_root /usr/src/sps' \
+		'makejobs auto' \
+		'cflags -O2 -pipe' \
+		'cxxflags -O2 -pipe' \
+		'march native' \
+		'compression auto' \
+		'preserve etc'
+}
+
 sps_load_config()
 {
 	[ "${SPS_ROOT+x}" = x ] && sps_env_root=1 || sps_env_root=0
@@ -515,6 +548,11 @@ sps_load_config()
 	[ "${SPS_COMPRESSION+x}" = x ] && sps_env_compression=1 || sps_env_compression=0
 	[ "${SPS_ARCH+x}" = x ] && sps_env_arch=1 || sps_env_arch=0
 	[ "${SPS_PRESERVE+x}" = x ] && sps_env_preserve=1 || sps_env_preserve=0
+	[ "${SPS_CFLAGS+x}" = x ] && sps_env_cflags=1 || sps_env_cflags=0
+	[ "${SPS_CXXFLAGS+x}" = x ] && sps_env_cxxflags=1 || sps_env_cxxflags=0
+	[ "${SPS_CPPFLAGS+x}" = x ] && sps_env_cppflags=1 || sps_env_cppflags=0
+	[ "${SPS_LDFLAGS+x}" = x ] && sps_env_ldflags=1 || sps_env_ldflags=0
+	[ "${SPS_MARCH+x}" = x ] && sps_env_march=1 || sps_env_march=0
 
 	SPS_ROOT=${SPS_ROOT:-/}
 	case $SPS_ROOT in
@@ -535,13 +573,18 @@ sps_load_config()
 	sps_cfg_cache=/var/cache/sps
 	sps_cfg_build=/var/tmp/sps
 	sps_cfg_repo_root=/usr/src/sps
-	sps_cfg_makejobs=1
+	sps_cfg_makejobs=auto
 	sps_cfg_compression=auto
 	sps_cfg_arch=$(uname -m 2>/dev/null || printf '%s' unknown)
 	sps_cfg_preserve=etc
+	sps_cfg_cflags='-O2 -pipe'
+	sps_cfg_cxxflags='-O2 -pipe'
+	sps_cfg_cppflags=
+	sps_cfg_ldflags=
+	sps_cfg_march=native
 
 	if [ -r "$SPS_CONFIG" ]; then
-		while IFS=' 	' read -r sps_key sps_value sps_extra; do
+		while IFS=' 	' read -r sps_key sps_value; do
 			case $sps_key in
 				''|'#'*) continue ;;
 				root) [ "$sps_env_root" -eq 1 ] || sps_cfg_root=$sps_value ;;
@@ -553,6 +596,11 @@ sps_load_config()
 				compression) sps_cfg_compression=$sps_value ;;
 				arch) sps_cfg_arch=$sps_value ;;
 				preserve) sps_cfg_preserve=$sps_value ;;
+				cflags) sps_cfg_cflags=$sps_value ;;
+				cxxflags) sps_cfg_cxxflags=$sps_value ;;
+				cppflags) sps_cfg_cppflags=$sps_value ;;
+				ldflags) sps_cfg_ldflags=$sps_value ;;
+				march) sps_cfg_march=$sps_value ;;
 				repo|git|dir) : ;;
 				*) sps_warn "ignoring unknown configuration key '$sps_key' in $SPS_CONFIG" ;;
 			esac
@@ -574,6 +622,9 @@ sps_load_config()
 	[ "$sps_env_preserve" -eq 1 ] || SPS_PRESERVE=$sps_cfg_preserve
 	[ "$SPS_COMPRESSION" = zst ] && SPS_COMPRESSION=zstd
 
+	if [ "$SPS_MAKEJOBS" = auto ]; then
+		SPS_MAKEJOBS=$(sps_nproc)
+	fi
 	case $SPS_MAKEJOBS in ''|*[!0-9]*|0) sps_die "$SPS_EX_USAGE" "makejobs must be a positive integer" ;; esac
 	case $SPS_COMPRESSION in
 		auto|none|gzip|xz|zstd) ;;
@@ -596,4 +647,51 @@ sps_load_config()
 
 	export SPS_ROOT SPS_DB SPS_CACHE SPS_BUILD SPS_REPO_ROOT SPS_CONFIG SPS_REPOS_CONFIG
 	export SPS_MAKEJOBS SPS_COMPRESSION SPS_ARCH SPS_PRESERVE
+
+	if [ "$sps_env_cflags" -eq 1 ]; then
+		CFLAGS=$SPS_CFLAGS
+	else
+		CFLAGS=$sps_cfg_cflags
+	fi
+	if [ "$sps_env_cxxflags" -eq 1 ]; then
+		CXXFLAGS=$SPS_CXXFLAGS
+	else
+		CXXFLAGS=$sps_cfg_cxxflags
+	fi
+	if [ "$sps_env_cppflags" -eq 1 ]; then
+		CPPFLAGS=$SPS_CPPFLAGS
+	else
+		CPPFLAGS=$sps_cfg_cppflags
+	fi
+	if [ "$sps_env_ldflags" -eq 1 ]; then
+		LDFLAGS=$SPS_LDFLAGS
+	else
+		LDFLAGS=$sps_cfg_ldflags
+	fi
+	if [ "$sps_env_march" -eq 1 ]; then
+		SPS_MARCH=$SPS_MARCH
+	else
+		SPS_MARCH=$sps_cfg_march
+	fi
+	case $SPS_MARCH in
+		none|'') ;;
+		*)
+			case $CFLAGS in
+				*-march=*) ;;
+				*) CFLAGS="${CFLAGS:+$CFLAGS }-march=$SPS_MARCH" ;;
+			esac
+			case $CXXFLAGS in
+				*-march=*) ;;
+				*) CXXFLAGS="${CXXFLAGS:+$CXXFLAGS }-march=$SPS_MARCH" ;;
+			esac
+			;;
+	esac
+	SPS_CFLAGS=$CFLAGS
+	SPS_CXXFLAGS=$CXXFLAGS
+	SPS_CPPFLAGS=$CPPFLAGS
+	SPS_LDFLAGS=$LDFLAGS
+	export CFLAGS CXXFLAGS SPS_CFLAGS SPS_CXXFLAGS SPS_MARCH
+	[ -n "${CPPFLAGS-}" ] && export CPPFLAGS SPS_CPPFLAGS
+	[ -n "${LDFLAGS-}" ] && export LDFLAGS SPS_LDFLAGS
+	return 0
 }
diff --git a/lib/mkiso/seed-bins b/lib/mkiso/seed-bins
index 5da47ed..9697f5f 100644
--- a/lib/mkiso/seed-bins
+++ b/lib/mkiso/seed-bins
@@ -27,6 +27,11 @@
 /usr/bin/python3
 /usr/bin/python3.12
 /usr/bin/perl
+/usr/bin/pod2man
+/usr/bin/pod2text
+/usr/bin/pod2html
+/usr/bin/pod2usage
+/usr/bin/perldoc
 /usr/bin/rsync
 /usr/bin/tar
 /usr/bin/xz
diff --git a/lib/setup/service.sh b/lib/setup/service.sh
index 6ee2a36..15d6210 100644
--- a/lib/setup/service.sh
+++ b/lib/setup/service.sh
@@ -1,10 +1,16 @@
 # Init-aware service enablement for the SPS Linux installer.
 #
-# Sourced, not executed. setup(1) and package post-install hooks call these
-# helpers. They never invent an init; they read /etc/sps/init (or the
-# hook environment) and only enable units/scripts that already exist in
-# the target. Enablement is on-boot; start happens only when the target
-# is the running system and PID 1 matches.
+# Sourced, not executed. setup(1) and pkin(1) call these helpers. They
+# never invent an init; they read /etc/sps/init (or the hook environment)
+# and enable units/scripts for that init. When a package did not ship a
+# file for the chosen init, a stock script from $SPS_LIBDIR/setup/sv is
+# copied into the target and then enabled. Enablement is on-boot; start
+# happens only when the target is the running system and PID 1 matches.
+
+setup_service_known_inits()
+{
+	printf '%s\n' systemd openrc s6 runit dinit shepherd
+}
 
 setup_service_map_file()
 {
@@ -19,6 +25,23 @@ setup_service_map_file()
 	return 1
 }
 
+setup_service_stock_dir()
+{
+	setup_svc_init=$1
+	[ -n "$setup_svc_init" ] || return 1
+	if [ -n "${setup_dir-}" ] && [ -d "$setup_dir/sv/$setup_svc_init" ]; then
+		printf '%s\n' "$setup_dir/sv/$setup_svc_init"
+		return 0
+	fi
+	if [ -n "${SPS_LIBDIR-}" ] &&
+	   [ -d "$SPS_LIBDIR/setup/sv/$setup_svc_init" ]
+	then
+		printf '%s\n' "$SPS_LIBDIR/setup/sv/$setup_svc_init"
+		return 0
+	fi
+	return 1
+}
+
 setup_service_lookup()
 {
 	# $1 = package name. Prints: unit wanted_by rc_name rc_level
@@ -52,6 +75,18 @@ setup_service_root()
 	printf '%s\n' "${setup_svc_root%/}"
 }
 
+setup_service_abs()
+{
+	# $1 root  $2 absolute path in the target
+	setup_svc_root=$(setup_service_root "$1")
+	setup_svc_rel=$2
+	if [ -z "$setup_svc_root" ]; then
+		printf '%s\n' "$setup_svc_rel"
+	else
+		printf '%s%s\n' "$setup_svc_root" "$setup_svc_rel"
+	fi
+}
+
 setup_service_init_of()
 {
 	setup_svc_root=$(setup_service_root "$1")
@@ -98,6 +133,98 @@ setup_service_rc_path()
 	printf '%s\n' "$setup_svc_p"
 }
 
+setup_service_copy_file()
+{
+	# $1 dest  $2 mode  $3 src. No-op if dest already exists.
+	setup_svc_dest=$1
+	setup_svc_mode=$2
+	setup_svc_src=$3
+	[ -f "$setup_svc_src" ] || return 1
+	[ -e "$setup_svc_dest" ] && return 0
+	mkdir -p "${setup_svc_dest%/*}" || return 1
+	cp "$setup_svc_src" "$setup_svc_dest" || return 1
+	chmod "$setup_svc_mode" "$setup_svc_dest"
+}
+
+setup_service_copy_supervised()
+{
+	# $1 srcdir  $2 dstdir. Copy run/finish if dest has no run yet.
+	setup_svc_src=$1
+	setup_svc_dst=$2
+	[ -d "$setup_svc_src" ] || return 1
+	[ -f "$setup_svc_src/run" ] || return 1
+	if [ -f "$setup_svc_dst/run" ]; then
+		return 0
+	fi
+	mkdir -p "$setup_svc_dst" || return 1
+	for setup_svc_f in "$setup_svc_src"/*; do
+		[ -f "$setup_svc_f" ] || continue
+		setup_svc_b=${setup_svc_f##*/}
+		cp "$setup_svc_f" "$setup_svc_dst/$setup_svc_b" || return 1
+		case $setup_svc_b in
+			run|finish) chmod 755 "$setup_svc_dst/$setup_svc_b" ;;
+		esac
+	done
+	return 0
+}
+
+setup_service_ensure_scripts()
+{
+	# $1 root  $2 init  $3 unit  $4 rc_name
+	# Install stock scripts when the package did not ship this init's file.
+	setup_svc_root=$1
+	setup_svc_init=$2
+	setup_svc_unit=$3
+	setup_svc_rc=$4
+	setup_svc_name=$setup_svc_rc
+	if [ -z "$setup_svc_name" ]; then
+		setup_svc_name=${setup_svc_unit%.service}
+	fi
+	[ -n "$setup_svc_name" ] || return 0
+	setup_svc_stock=$(setup_service_stock_dir "$setup_svc_init") ||
+		return 0
+	case $setup_svc_init in
+		systemd)
+			[ -n "$setup_svc_unit" ] || return 0
+			setup_service_copy_file \
+				"$(setup_service_abs "$setup_svc_root" \
+					"/usr/lib/systemd/system/$setup_svc_unit")" \
+				644 "$setup_svc_stock/$setup_svc_unit" || :
+			;;
+		openrc)
+			setup_service_copy_file \
+				"$(setup_service_abs "$setup_svc_root" \
+					"/etc/init.d/$setup_svc_name")" \
+				755 "$setup_svc_stock/$setup_svc_name" || :
+			;;
+		runit)
+			setup_service_copy_supervised \
+				"$setup_svc_stock/$setup_svc_name" \
+				"$(setup_service_abs "$setup_svc_root" \
+					"/etc/sv/$setup_svc_name")" || :
+			;;
+		s6)
+			setup_service_copy_supervised \
+				"$setup_svc_stock/$setup_svc_name" \
+				"$(setup_service_abs "$setup_svc_root" \
+					"/etc/s6/sv/$setup_svc_name")" || :
+			;;
+		dinit)
+			setup_service_copy_file \
+				"$(setup_service_abs "$setup_svc_root" \
+					"/etc/dinit.d/$setup_svc_name")" \
+				644 "$setup_svc_stock/$setup_svc_name" || :
+			;;
+		shepherd)
+			setup_service_copy_file \
+				"$(setup_service_abs "$setup_svc_root" \
+					"/etc/shepherd.d/$setup_svc_name.scm")" \
+				644 "$setup_svc_stock/$setup_svc_name.scm" || :
+			;;
+	esac
+	return 0
+}
+
 setup_service_enable_systemd()
 {
 	setup_svc_root=$(setup_service_root "$1")
@@ -157,6 +284,53 @@ setup_service_enable_openrc()
 	ln -sf "/etc/init.d/$setup_svc_rc" "$setup_svc_rdir/$setup_svc_rc"
 }
 
+setup_service_enable_runit()
+{
+	setup_svc_root=$(setup_service_root "$1")
+	setup_svc_rc=$2
+	[ -n "$setup_svc_rc" ] || return 1
+	setup_svc_sv=$(setup_service_abs "$1" "/etc/sv/$setup_svc_rc")
+	[ -f "$setup_svc_sv/run" ] || return 1
+	setup_svc_dir=$(setup_service_abs "$1" /etc/runit/runsvdir/default)
+	mkdir -p "$setup_svc_dir" || return 1
+	ln -sf "/etc/sv/$setup_svc_rc" "$setup_svc_dir/$setup_svc_rc"
+}
+
+setup_service_enable_s6()
+{
+	setup_svc_root=$(setup_service_root "$1")
+	setup_svc_rc=$2
+	[ -n "$setup_svc_rc" ] || return 1
+	setup_svc_sv=$(setup_service_abs "$1" "/etc/s6/sv/$setup_svc_rc")
+	[ -f "$setup_svc_sv/run" ] || return 1
+	setup_svc_en=$(setup_service_abs "$1" /etc/s6/enabled)
+	mkdir -p "$setup_svc_en" || return 1
+	ln -sf "/etc/s6/sv/$setup_svc_rc" "$setup_svc_en/$setup_svc_rc"
+}
+
+setup_service_enable_dinit()
+{
+	setup_svc_root=$(setup_service_root "$1")
+	setup_svc_rc=$2
+	[ -n "$setup_svc_rc" ] || return 1
+	setup_svc_file=$(setup_service_abs "$1" "/etc/dinit.d/$setup_svc_rc")
+	[ -f "$setup_svc_file" ] || return 1
+	setup_svc_boot=$(setup_service_abs "$1" /etc/dinit.d/boot.d)
+	mkdir -p "$setup_svc_boot" || return 1
+	ln -sf "../$setup_svc_rc" "$setup_svc_boot/$setup_svc_rc"
+}
+
+setup_service_enable_shepherd()
+{
+	setup_svc_root=$(setup_service_root "$1")
+	setup_svc_rc=$2
+	[ -n "$setup_svc_rc" ] || return 1
+	setup_svc_file=$(setup_service_abs "$1" \
+		"/etc/shepherd.d/$setup_svc_rc.scm")
+	[ -f "$setup_svc_file" ] || return 1
+	return 0
+}
+
 setup_service_is_live_root()
 {
 	setup_svc_root=$(setup_service_root "$1")
@@ -173,6 +347,48 @@ setup_service_pid1()
 		printf '%s\n' openrc
 		return 0
 	fi
+	setup_svc_comm=
+	if [ -r /proc/1/comm ]; then
+		setup_svc_comm=$(tr -d '\0\n' </proc/1/comm 2>/dev/null) ||
+			setup_svc_comm=
+	fi
+	setup_svc_cmd=
+	if [ -r /proc/1/cmdline ]; then
+		setup_svc_cmd=$(tr '\0' ' ' </proc/1/cmdline 2>/dev/null) ||
+			setup_svc_cmd=
+	fi
+	case $setup_svc_comm in
+		systemd) printf '%s\n' systemd; return 0 ;;
+		openrc-init) printf '%s\n' openrc; return 0 ;;
+		runit|runit-init) printf '%s\n' runit; return 0 ;;
+		s6-svscan|s6-linux-init*) printf '%s\n' s6; return 0 ;;
+		dinit) printf '%s\n' dinit; return 0 ;;
+		shepherd) printf '%s\n' shepherd; return 0 ;;
+	esac
+	case $setup_svc_cmd in
+		*systemd*) printf '%s\n' systemd; return 0 ;;
+		*openrc*) printf '%s\n' openrc; return 0 ;;
+		*runit*) printf '%s\n' runit; return 0 ;;
+		*s6-svscan*|*s6-linux-init*) printf '%s\n' s6; return 0 ;;
+		*dinit*) printf '%s\n' dinit; return 0 ;;
+		*shepherd*) printf '%s\n' shepherd; return 0 ;;
+	esac
+	if [ -d /run/runit ]; then
+		printf '%s\n' runit
+		return 0
+	fi
+	if [ -d /run/s6 ] || [ -d /run/s6-linux-init ]; then
+		printf '%s\n' s6
+		return 0
+	fi
+	if [ -d /run/dinit ] || [ -S /run/dinitctl ]; then
+		printf '%s\n' dinit
+		return 0
+	fi
+	if [ -d /run/shepherd ] || [ -S /run/shepherd/socket ]; then
+		printf '%s\n' shepherd
+		return 0
+	fi
 	printf '%s\n' none
 }
 
@@ -198,6 +414,32 @@ setup_service_maybe_start()
 				rc-service "$setup_svc_rc" start 2>/dev/null || :
 			fi
 			;;
+		runit)
+			[ -n "$setup_svc_rc" ] || return 0
+			if command -v sv >/dev/null 2>&1; then
+				sv start "$setup_svc_rc" 2>/dev/null || :
+			fi
+			;;
+		s6)
+			[ -n "$setup_svc_rc" ] || return 0
+			if command -v s6-svc >/dev/null 2>&1 &&
+			   [ -d /run/service/"$setup_svc_rc" ]
+			then
+				s6-svc -u /run/service/"$setup_svc_rc" 2>/dev/null || :
+			fi
+			;;
+		dinit)
+			[ -n "$setup_svc_rc" ] || return 0
+			if command -v dinitctl >/dev/null 2>&1; then
+				dinitctl start "$setup_svc_rc" 2>/dev/null || :
+			fi
+			;;
+		shepherd)
+			[ -n "$setup_svc_rc" ] || return 0
+			if command -v herd >/dev/null 2>&1; then
+				herd start "$setup_svc_rc" 2>/dev/null || :
+			fi
+			;;
 	esac
 	return 0
 }
@@ -209,11 +451,17 @@ setup_service_enable_one()
 	setup_svc_init=$2
 	setup_svc_pkg=$3
 	setup_svc_fields=$(setup_service_lookup "$setup_svc_pkg") || return 1
+	# shellcheck disable=SC2086
 	set -- $setup_svc_fields
 	setup_svc_unit=$1
 	setup_svc_wanted=$2
 	setup_svc_rc=$3
 	setup_svc_level=$4
+	setup_service_ensure_scripts "$setup_svc_root" "$setup_svc_init" \
+		"$setup_svc_unit" "$setup_svc_rc"
+	if [ -z "$setup_svc_rc" ]; then
+		setup_svc_rc=${setup_svc_unit%.service}
+	fi
 	setup_svc_did=0
 	case $setup_svc_init in
 		systemd)
@@ -232,6 +480,38 @@ setup_service_enable_one()
 				setup_svc_did=1
 			fi
 			;;
+		runit)
+			if [ -n "$setup_svc_rc" ] &&
+			   setup_service_enable_runit "$setup_svc_root" \
+				"$setup_svc_rc"
+			then
+				setup_svc_did=1
+			fi
+			;;
+		s6)
+			if [ -n "$setup_svc_rc" ] &&
+			   setup_service_enable_s6 "$setup_svc_root" \
+				"$setup_svc_rc"
+			then
+				setup_svc_did=1
+			fi
+			;;
+		dinit)
+			if [ -n "$setup_svc_rc" ] &&
+			   setup_service_enable_dinit "$setup_svc_root" \
+				"$setup_svc_rc"
+			then
+				setup_svc_did=1
+			fi
+			;;
+		shepherd)
+			if [ -n "$setup_svc_rc" ] &&
+			   setup_service_enable_shepherd "$setup_svc_root" \
+				"$setup_svc_rc"
+			then
+				setup_svc_did=1
+			fi
+			;;
 		*)
 			return 1
 			;;
@@ -245,7 +525,8 @@ setup_service_enable_one()
 
 setup_service_enable_present()
 {
-	# Enable every mapped service whose unit/script is already in $1.
+	# Enable every mapped service whose unit/script is already in $1,
+	# installing stock scripts first when the chosen init needs them.
 	setup_svc_root=$1
 	setup_svc_init=$2
 	setup_svc_map=$(setup_service_map_file) || return 0
@@ -266,14 +547,15 @@ setup_service_enable_present()
 
 setup_service_hook_enable()
 {
-	# Called from package post-install hooks. SPS_PACKAGE and SPS_ROOT
-	# are set by pkin(1). A missing map or unknown package is not an error.
+	# Called from pkin(1) after every post-install, and from package
+	# hooks. SPS_PACKAGE and SPS_ROOT are set by pkin. A missing map
+	# or unknown package is not an error.
 	setup_svc_pkg=${SPS_PACKAGE-}
 	[ -n "$setup_svc_pkg" ] || return 0
 	setup_svc_root=${SPS_ROOT:-/}
 	setup_svc_init=$(setup_service_init_of "$setup_svc_root")
 	case $setup_svc_init in
-		systemd|openrc) ;;
+		systemd|openrc|s6|runit|dinit|shepherd) ;;
 		*) return 0 ;;
 	esac
 	setup_service_enable_one "$setup_svc_root" "$setup_svc_init" \
diff --git a/lib/setup/sv/dinit/NetworkManager b/lib/setup/sv/dinit/NetworkManager
new file mode 100644
index 0000000..fca481e
--- /dev/null
+++ b/lib/setup/sv/dinit/NetworkManager
@@ -0,0 +1,5 @@
+type = process
+command = /usr/sbin/NetworkManager --no-daemon
+restart = true
+smooth-recovery = true
+depends-on = dbus
diff --git a/lib/setup/sv/dinit/bluetooth b/lib/setup/sv/dinit/bluetooth
new file mode 100644
index 0000000..af66e30
--- /dev/null
+++ b/lib/setup/sv/dinit/bluetooth
@@ -0,0 +1,5 @@
+type = process
+command = /usr/libexec/bluetooth/bluetoothd
+restart = true
+smooth-recovery = true
+depends-on = dbus
diff --git a/lib/setup/sv/dinit/cupsd b/lib/setup/sv/dinit/cupsd
new file mode 100644
index 0000000..90a1ea6
--- /dev/null
+++ b/lib/setup/sv/dinit/cupsd
@@ -0,0 +1,5 @@
+type = process
+command = /usr/sbin/cupsd -f
+restart = true
+smooth-recovery = true
+depends-on = dbus
diff --git a/lib/setup/sv/dinit/dbus b/lib/setup/sv/dinit/dbus
new file mode 100644
index 0000000..b356f3b
--- /dev/null
+++ b/lib/setup/sv/dinit/dbus
@@ -0,0 +1,4 @@
+type = process
+command = /usr/bin/dbus-daemon --system --nofork --nopidfile --nosyslog
+restart = true
+smooth-recovery = true
diff --git a/lib/setup/sv/dinit/dhcpcd b/lib/setup/sv/dinit/dhcpcd
new file mode 100644
index 0000000..10791cd
--- /dev/null
+++ b/lib/setup/sv/dinit/dhcpcd
@@ -0,0 +1,4 @@
+type = process
+command = /usr/sbin/dhcpcd --nobackground
+restart = true
+smooth-recovery = true
diff --git a/lib/setup/sv/dinit/elogind b/lib/setup/sv/dinit/elogind
new file mode 100644
index 0000000..c89977f
--- /dev/null
+++ b/lib/setup/sv/dinit/elogind
@@ -0,0 +1,5 @@
+type = process
+command = /usr/libexec/elogind
+restart = true
+smooth-recovery = true
+depends-on = dbus
diff --git a/lib/setup/sv/dinit/iwd b/lib/setup/sv/dinit/iwd
new file mode 100644
index 0000000..3e4fd51
--- /dev/null
+++ b/lib/setup/sv/dinit/iwd
@@ -0,0 +1,5 @@
+type = process
+command = /usr/libexec/iwd
+restart = true
+smooth-recovery = true
+depends-on = dbus
diff --git a/lib/setup/sv/dinit/pipewire b/lib/setup/sv/dinit/pipewire
new file mode 100644
index 0000000..323d528
--- /dev/null
+++ b/lib/setup/sv/dinit/pipewire
@@ -0,0 +1,5 @@
+type = process
+command = /usr/bin/pipewire
+restart = true
+smooth-recovery = true
+depends-on = dbus
diff --git a/lib/setup/sv/dinit/power-profiles-daemon b/lib/setup/sv/dinit/power-profiles-daemon
new file mode 100644
index 0000000..7550b2f
--- /dev/null
+++ b/lib/setup/sv/dinit/power-profiles-daemon
@@ -0,0 +1,5 @@
+type = process
+command = /usr/libexec/power-profiles-daemon
+restart = true
+smooth-recovery = true
+depends-on = dbus
diff --git a/lib/setup/sv/dinit/sddm b/lib/setup/sv/dinit/sddm
new file mode 100644
index 0000000..aeab9e3
--- /dev/null
+++ b/lib/setup/sv/dinit/sddm
@@ -0,0 +1,5 @@
+type = process
+command = /usr/bin/sddm
+restart = true
+smooth-recovery = true
+depends-on = dbus
diff --git a/lib/setup/sv/dinit/seatd b/lib/setup/sv/dinit/seatd
new file mode 100644
index 0000000..9a8c8e9
--- /dev/null
+++ b/lib/setup/sv/dinit/seatd
@@ -0,0 +1,4 @@
+type = process
+command = /usr/bin/seatd -g video
+restart = true
+smooth-recovery = true
diff --git a/lib/setup/sv/dinit/sshd b/lib/setup/sv/dinit/sshd
new file mode 100644
index 0000000..333257d
--- /dev/null
+++ b/lib/setup/sv/dinit/sshd
@@ -0,0 +1,4 @@
+type = process
+command = /usr/sbin/sshd -D
+restart = true
+smooth-recovery = true
diff --git a/lib/setup/sv/dinit/wireplumber b/lib/setup/sv/dinit/wireplumber
new file mode 100644
index 0000000..228282c
--- /dev/null
+++ b/lib/setup/sv/dinit/wireplumber
@@ -0,0 +1,5 @@
+type = process
+command = /usr/bin/wireplumber
+restart = true
+smooth-recovery = true
+depends-on = dbus
diff --git a/lib/setup/sv/openrc/NetworkManager b/lib/setup/sv/openrc/NetworkManager
new file mode 100755
index 0000000..f0a0c00
--- /dev/null
+++ b/lib/setup/sv/openrc/NetworkManager
@@ -0,0 +1,12 @@
+#!/sbin/openrc-run
+description="NetworkManager"
+command="/usr/sbin/NetworkManager"
+command_args="--no-daemon"
+command_background="yes"
+pidfile="/run/NetworkManager.pid"
+
+depend() {
+	need dbus
+	after dbus
+	provide net
+}
diff --git a/lib/setup/sv/openrc/bluetooth b/lib/setup/sv/openrc/bluetooth
new file mode 100755
index 0000000..5c3c0d1
--- /dev/null
+++ b/lib/setup/sv/openrc/bluetooth
@@ -0,0 +1,10 @@
+#!/sbin/openrc-run
+description="Bluetooth daemon"
+command="/usr/libexec/bluetooth/bluetoothd"
+command_background="yes"
+pidfile="/run/bluetooth.pid"
+
+depend() {
+	need dbus
+	after dbus
+}
diff --git a/lib/setup/sv/openrc/cupsd b/lib/setup/sv/openrc/cupsd
new file mode 100755
index 0000000..e2566df
--- /dev/null
+++ b/lib/setup/sv/openrc/cupsd
@@ -0,0 +1,11 @@
+#!/sbin/openrc-run
+description="CUPS printing service"
+command="/usr/sbin/cupsd"
+command_args="-f"
+command_background="yes"
+pidfile="/run/cupsd.pid"
+
+depend() {
+	need dbus
+	after dbus
+}
diff --git a/lib/setup/sv/openrc/dbus b/lib/setup/sv/openrc/dbus
new file mode 100755
index 0000000..f67ebfe
--- /dev/null
+++ b/lib/setup/sv/openrc/dbus
@@ -0,0 +1,15 @@
+#!/sbin/openrc-run
+description="D-Bus system message bus"
+command="/usr/bin/dbus-daemon"
+command_args="--system --nofork --nopidfile --nosyslog"
+command_background="yes"
+pidfile="/run/dbus.pid"
+
+depend() {
+	need localmount
+	provide dbus
+}
+
+start_pre() {
+	mkdir -p /run/dbus /var/lib/dbus
+}
diff --git a/lib/setup/sv/openrc/dhcpcd b/lib/setup/sv/openrc/dhcpcd
new file mode 100755
index 0000000..cec1604
--- /dev/null
+++ b/lib/setup/sv/openrc/dhcpcd
@@ -0,0 +1,10 @@
+#!/sbin/openrc-run
+description="DHCP client daemon"
+command="/usr/sbin/dhcpcd"
+command_args="--nobackground"
+command_background="yes"
+pidfile="/run/dhcpcd.pid"
+
+depend() {
+	provide net
+}
diff --git a/lib/setup/sv/openrc/elogind b/lib/setup/sv/openrc/elogind
new file mode 100755
index 0000000..089d9c1
--- /dev/null
+++ b/lib/setup/sv/openrc/elogind
@@ -0,0 +1,10 @@
+#!/sbin/openrc-run
+description="elogind login manager"
+command="/usr/libexec/elogind"
+command_background="yes"
+pidfile="/run/elogind.pid"
+
+depend() {
+	need dbus
+	after dbus
+}
diff --git a/lib/setup/sv/openrc/iwd b/lib/setup/sv/openrc/iwd
new file mode 100755
index 0000000..b7bb685
--- /dev/null
+++ b/lib/setup/sv/openrc/iwd
@@ -0,0 +1,11 @@
+#!/sbin/openrc-run
+description="iNet wireless daemon"
+command="/usr/libexec/iwd"
+command_background="yes"
+pidfile="/run/iwd.pid"
+
+depend() {
+	need dbus
+	after dbus
+	provide net
+}
diff --git a/lib/setup/sv/openrc/pipewire b/lib/setup/sv/openrc/pipewire
new file mode 100755
index 0000000..c99bf09
--- /dev/null
+++ b/lib/setup/sv/openrc/pipewire
@@ -0,0 +1,10 @@
+#!/sbin/openrc-run
+description="PipeWire multimedia server"
+command="/usr/bin/pipewire"
+command_background="yes"
+pidfile="/run/pipewire.pid"
+
+depend() {
+	need dbus
+	after dbus
+}
diff --git a/lib/setup/sv/openrc/power-profiles-daemon b/lib/setup/sv/openrc/power-profiles-daemon
new file mode 100755
index 0000000..eee4954
--- /dev/null
+++ b/lib/setup/sv/openrc/power-profiles-daemon
@@ -0,0 +1,10 @@
+#!/sbin/openrc-run
+description="Power profiles daemon"
+command="/usr/libexec/power-profiles-daemon"
+command_background="yes"
+pidfile="/run/power-profiles-daemon.pid"
+
+depend() {
+	need dbus
+	after dbus
+}
diff --git a/lib/setup/sv/openrc/sddm b/lib/setup/sv/openrc/sddm
new file mode 100755
index 0000000..55afe20
--- /dev/null
+++ b/lib/setup/sv/openrc/sddm
@@ -0,0 +1,10 @@
+#!/sbin/openrc-run
+description="Simple Desktop Display Manager"
+command="/usr/bin/sddm"
+command_background="yes"
+pidfile="/run/sddm.pid"
+
+depend() {
+	need dbus
+	after dbus
+}
diff --git a/lib/setup/sv/openrc/seatd b/lib/setup/sv/openrc/seatd
new file mode 100755
index 0000000..2acc752
--- /dev/null
+++ b/lib/setup/sv/openrc/seatd
@@ -0,0 +1,6 @@
+#!/sbin/openrc-run
+description="seat management daemon"
+command="/usr/bin/seatd"
+command_args="-g video"
+command_background="yes"
+pidfile="/run/seatd.pid"
diff --git a/lib/setup/sv/openrc/sshd b/lib/setup/sv/openrc/sshd
new file mode 100755
index 0000000..e3f73c5
--- /dev/null
+++ b/lib/setup/sv/openrc/sshd
@@ -0,0 +1,6 @@
+#!/sbin/openrc-run
+description="OpenSSH daemon"
+command="/usr/sbin/sshd"
+command_args="-D"
+command_background="yes"
+pidfile="/run/sshd.pid"
diff --git a/lib/setup/sv/openrc/wireplumber b/lib/setup/sv/openrc/wireplumber
new file mode 100755
index 0000000..7dbfb15
--- /dev/null
+++ b/lib/setup/sv/openrc/wireplumber
@@ -0,0 +1,10 @@
+#!/sbin/openrc-run
+description="WirePlumber session manager"
+command="/usr/bin/wireplumber"
+command_background="yes"
+pidfile="/run/wireplumber.pid"
+
+depend() {
+	need dbus
+	after dbus
+}
diff --git a/lib/setup/sv/runit/NetworkManager/run b/lib/setup/sv/runit/NetworkManager/run
new file mode 100755
index 0000000..8e226f1
--- /dev/null
+++ b/lib/setup/sv/runit/NetworkManager/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/sbin/NetworkManager --no-daemon
diff --git a/lib/setup/sv/runit/bluetooth/run b/lib/setup/sv/runit/bluetooth/run
new file mode 100755
index 0000000..6202590
--- /dev/null
+++ b/lib/setup/sv/runit/bluetooth/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/libexec/bluetooth/bluetoothd
diff --git a/lib/setup/sv/runit/cupsd/run b/lib/setup/sv/runit/cupsd/run
new file mode 100755
index 0000000..1ab8d08
--- /dev/null
+++ b/lib/setup/sv/runit/cupsd/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/sbin/cupsd -f
diff --git a/lib/setup/sv/runit/dbus/run b/lib/setup/sv/runit/dbus/run
new file mode 100755
index 0000000..486471a
--- /dev/null
+++ b/lib/setup/sv/runit/dbus/run
@@ -0,0 +1,4 @@
+#!/bin/sh
+exec 2>&1
+mkdir -p /run/dbus /var/lib/dbus
+exec /usr/bin/dbus-daemon --system --nofork --nopidfile --nosyslog
diff --git a/lib/setup/sv/runit/dhcpcd/run b/lib/setup/sv/runit/dhcpcd/run
new file mode 100755
index 0000000..ad3de1d
--- /dev/null
+++ b/lib/setup/sv/runit/dhcpcd/run
@@ -0,0 +1,3 @@
+#!/bin/sh
+exec 2>&1
+exec /usr/sbin/dhcpcd --nobackground
diff --git a/lib/setup/sv/runit/elogind/run b/lib/setup/sv/runit/elogind/run
new file mode 100755
index 0000000..e25b294
--- /dev/null
+++ b/lib/setup/sv/runit/elogind/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/libexec/elogind
diff --git a/lib/setup/sv/runit/iwd/run b/lib/setup/sv/runit/iwd/run
new file mode 100755
index 0000000..28c9b6c
--- /dev/null
+++ b/lib/setup/sv/runit/iwd/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/libexec/iwd
diff --git a/lib/setup/sv/runit/pipewire/run b/lib/setup/sv/runit/pipewire/run
new file mode 100755
index 0000000..2a9c89a
--- /dev/null
+++ b/lib/setup/sv/runit/pipewire/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/bin/pipewire
diff --git a/lib/setup/sv/runit/power-profiles-daemon/run b/lib/setup/sv/runit/power-profiles-daemon/run
new file mode 100755
index 0000000..9ee3756
--- /dev/null
+++ b/lib/setup/sv/runit/power-profiles-daemon/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/libexec/power-profiles-daemon
diff --git a/lib/setup/sv/runit/sddm/run b/lib/setup/sv/runit/sddm/run
new file mode 100755
index 0000000..a408aa9
--- /dev/null
+++ b/lib/setup/sv/runit/sddm/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/bin/sddm
diff --git a/lib/setup/sv/runit/seatd/run b/lib/setup/sv/runit/seatd/run
new file mode 100755
index 0000000..24e9ba2
--- /dev/null
+++ b/lib/setup/sv/runit/seatd/run
@@ -0,0 +1,3 @@
+#!/bin/sh
+exec 2>&1
+exec /usr/bin/seatd -g video
diff --git a/lib/setup/sv/runit/sshd/run b/lib/setup/sv/runit/sshd/run
new file mode 100755
index 0000000..8056a0e
--- /dev/null
+++ b/lib/setup/sv/runit/sshd/run
@@ -0,0 +1,3 @@
+#!/bin/sh
+exec 2>&1
+exec /usr/sbin/sshd -D
diff --git a/lib/setup/sv/runit/wireplumber/run b/lib/setup/sv/runit/wireplumber/run
new file mode 100755
index 0000000..1d78959
--- /dev/null
+++ b/lib/setup/sv/runit/wireplumber/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/bin/wireplumber
diff --git a/lib/setup/sv/s6/NetworkManager/run b/lib/setup/sv/s6/NetworkManager/run
new file mode 100755
index 0000000..8e226f1
--- /dev/null
+++ b/lib/setup/sv/s6/NetworkManager/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/sbin/NetworkManager --no-daemon
diff --git a/lib/setup/sv/s6/bluetooth/run b/lib/setup/sv/s6/bluetooth/run
new file mode 100755
index 0000000..6202590
--- /dev/null
+++ b/lib/setup/sv/s6/bluetooth/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/libexec/bluetooth/bluetoothd
diff --git a/lib/setup/sv/s6/cupsd/run b/lib/setup/sv/s6/cupsd/run
new file mode 100755
index 0000000..1ab8d08
--- /dev/null
+++ b/lib/setup/sv/s6/cupsd/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/sbin/cupsd -f
diff --git a/lib/setup/sv/s6/dbus/run b/lib/setup/sv/s6/dbus/run
new file mode 100755
index 0000000..486471a
--- /dev/null
+++ b/lib/setup/sv/s6/dbus/run
@@ -0,0 +1,4 @@
+#!/bin/sh
+exec 2>&1
+mkdir -p /run/dbus /var/lib/dbus
+exec /usr/bin/dbus-daemon --system --nofork --nopidfile --nosyslog
diff --git a/lib/setup/sv/s6/dhcpcd/run b/lib/setup/sv/s6/dhcpcd/run
new file mode 100755
index 0000000..ad3de1d
--- /dev/null
+++ b/lib/setup/sv/s6/dhcpcd/run
@@ -0,0 +1,3 @@
+#!/bin/sh
+exec 2>&1
+exec /usr/sbin/dhcpcd --nobackground
diff --git a/lib/setup/sv/s6/elogind/run b/lib/setup/sv/s6/elogind/run
new file mode 100755
index 0000000..e25b294
--- /dev/null
+++ b/lib/setup/sv/s6/elogind/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/libexec/elogind
diff --git a/lib/setup/sv/s6/iwd/run b/lib/setup/sv/s6/iwd/run
new file mode 100755
index 0000000..28c9b6c
--- /dev/null
+++ b/lib/setup/sv/s6/iwd/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/libexec/iwd
diff --git a/lib/setup/sv/s6/pipewire/run b/lib/setup/sv/s6/pipewire/run
new file mode 100755
index 0000000..2a9c89a
--- /dev/null
+++ b/lib/setup/sv/s6/pipewire/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/bin/pipewire
diff --git a/lib/setup/sv/s6/power-profiles-daemon/run b/lib/setup/sv/s6/power-profiles-daemon/run
new file mode 100755
index 0000000..9ee3756
--- /dev/null
+++ b/lib/setup/sv/s6/power-profiles-daemon/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/libexec/power-profiles-daemon
diff --git a/lib/setup/sv/s6/sddm/run b/lib/setup/sv/s6/sddm/run
new file mode 100755
index 0000000..a408aa9
--- /dev/null
+++ b/lib/setup/sv/s6/sddm/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/bin/sddm
diff --git a/lib/setup/sv/s6/seatd/run b/lib/setup/sv/s6/seatd/run
new file mode 100755
index 0000000..24e9ba2
--- /dev/null
+++ b/lib/setup/sv/s6/seatd/run
@@ -0,0 +1,3 @@
+#!/bin/sh
+exec 2>&1
+exec /usr/bin/seatd -g video
diff --git a/lib/setup/sv/s6/sshd/run b/lib/setup/sv/s6/sshd/run
new file mode 100755
index 0000000..8056a0e
--- /dev/null
+++ b/lib/setup/sv/s6/sshd/run
@@ -0,0 +1,3 @@
+#!/bin/sh
+exec 2>&1
+exec /usr/sbin/sshd -D
diff --git a/lib/setup/sv/s6/wireplumber/run b/lib/setup/sv/s6/wireplumber/run
new file mode 100755
index 0000000..1d78959
--- /dev/null
+++ b/lib/setup/sv/s6/wireplumber/run
@@ -0,0 +1,8 @@
+#!/bin/sh
+exec 2>&1
+if [ -x /usr/bin/sv ]; then
+	sv start dbus >/dev/null 2>&1 || :
+elif [ -x /usr/bin/s6-svwait ] && [ -d /run/service/dbus ]; then
+	s6-svwait /run/service/dbus >/dev/null 2>&1 || :
+fi
+exec /usr/bin/wireplumber
diff --git a/lib/setup/sv/shepherd/NetworkManager.scm b/lib/setup/sv/shepherd/NetworkManager.scm
new file mode 100644
index 0000000..3801843
--- /dev/null
+++ b/lib/setup/sv/shepherd/NetworkManager.scm
@@ -0,0 +1,9 @@
+(use-modules (shepherd service))
+(register-services
+ (list (service
+        '(NetworkManager)
+        #:requirement '(dbus)
+        #:documentation "NetworkManager"
+        #:start (make-forkexec-constructor '("/usr/sbin/NetworkManager" "--no-daemon"))
+        #:stop (make-kill-destructor)
+        #:respawn? #t)))
diff --git a/lib/setup/sv/shepherd/bluetooth.scm b/lib/setup/sv/shepherd/bluetooth.scm
new file mode 100644
index 0000000..348317c
--- /dev/null
+++ b/lib/setup/sv/shepherd/bluetooth.scm
@@ -0,0 +1,9 @@
+(use-modules (shepherd service))
+(register-services
+ (list (service
+        '(bluetooth)
+        #:requirement '(dbus)
+        #:documentation "Bluetooth daemon"
+        #:start (make-forkexec-constructor '("/usr/libexec/bluetooth/bluetoothd"))
+        #:stop (make-kill-destructor)
+        #:respawn? #t)))
diff --git a/lib/setup/sv/shepherd/cupsd.scm b/lib/setup/sv/shepherd/cupsd.scm
new file mode 100644
index 0000000..daf0e3d
--- /dev/null
+++ b/lib/setup/sv/shepherd/cupsd.scm
@@ -0,0 +1,9 @@
+(use-modules (shepherd service))
+(register-services
+ (list (service
+        '(cupsd)
+        #:requirement '(dbus)
+        #:documentation "CUPS printing service"
+        #:start (make-forkexec-constructor '("/usr/sbin/cupsd" "-f"))
+        #:stop (make-kill-destructor)
+        #:respawn? #t)))
diff --git a/lib/setup/sv/shepherd/dbus.scm b/lib/setup/sv/shepherd/dbus.scm
new file mode 100644
index 0000000..60f0415
--- /dev/null
+++ b/lib/setup/sv/shepherd/dbus.scm
@@ -0,0 +1,9 @@
+(use-modules (shepherd service))
+(register-services
+ (list (service
+        '(dbus)
+        #:requirement '()
+        #:documentation "D-Bus system message bus"
+        #:start (make-forkexec-constructor '("/usr/bin/dbus-daemon" "--system" "--nofork" "--nopidfile" "--nosyslog"))
+        #:stop (make-kill-destructor)
+        #:respawn? #t)))
diff --git a/lib/setup/sv/shepherd/dhcpcd.scm b/lib/setup/sv/shepherd/dhcpcd.scm
new file mode 100644
index 0000000..230303f
--- /dev/null
+++ b/lib/setup/sv/shepherd/dhcpcd.scm
@@ -0,0 +1,9 @@
+(use-modules (shepherd service))
+(register-services
+ (list (service
+        '(dhcpcd)
+        #:requirement '()
+        #:documentation "DHCP client daemon"
+        #:start (make-forkexec-constructor '("/usr/sbin/dhcpcd" "--nobackground"))
+        #:stop (make-kill-destructor)
+        #:respawn? #t)))
diff --git a/lib/setup/sv/shepherd/elogind.scm b/lib/setup/sv/shepherd/elogind.scm
new file mode 100644
index 0000000..5605ae1
--- /dev/null
+++ b/lib/setup/sv/shepherd/elogind.scm
@@ -0,0 +1,9 @@
+(use-modules (shepherd service))
+(register-services
+ (list (service
+        '(elogind)
+        #:requirement '(dbus)
+        #:documentation "elogind login manager"
+        #:start (make-forkexec-constructor '("/usr/libexec/elogind"))
+        #:stop (make-kill-destructor)
+        #:respawn? #t)))
diff --git a/lib/setup/sv/shepherd/iwd.scm b/lib/setup/sv/shepherd/iwd.scm
new file mode 100644
index 0000000..80de4f1
--- /dev/null
+++ b/lib/setup/sv/shepherd/iwd.scm
@@ -0,0 +1,9 @@
+(use-modules (shepherd service))
+(register-services
+ (list (service
+        '(iwd)
+        #:requirement '(dbus)
+        #:documentation "iNet wireless daemon"
+        #:start (make-forkexec-constructor '("/usr/libexec/iwd"))
+        #:stop (make-kill-destructor)
+        #:respawn? #t)))
diff --git a/lib/setup/sv/shepherd/pipewire.scm b/lib/setup/sv/shepherd/pipewire.scm
new file mode 100644
index 0000000..f462b30
--- /dev/null
+++ b/lib/setup/sv/shepherd/pipewire.scm
@@ -0,0 +1,9 @@
+(use-modules (shepherd service))
+(register-services
+ (list (service
+        '(pipewire)
+        #:requirement '(dbus)
+        #:documentation "PipeWire multimedia server"
+        #:start (make-forkexec-constructor '("/usr/bin/pipewire"))
+        #:stop (make-kill-destructor)
+        #:respawn? #t)))
diff --git a/lib/setup/sv/shepherd/power-profiles-daemon.scm b/lib/setup/sv/shepherd/power-profiles-daemon.scm
new file mode 100644
index 0000000..f311fbc
--- /dev/null
+++ b/lib/setup/sv/shepherd/power-profiles-daemon.scm
@@ -0,0 +1,9 @@
+(use-modules (shepherd service))
+(register-services
+ (list (service
+        '(power-profiles-daemon)
+        #:requirement '(dbus)
+        #:documentation "Power profiles daemon"
+        #:start (make-forkexec-constructor '("/usr/libexec/power-profiles-daemon"))
+        #:stop (make-kill-destructor)
+        #:respawn? #t)))
diff --git a/lib/setup/sv/shepherd/sddm.scm b/lib/setup/sv/shepherd/sddm.scm
new file mode 100644
index 0000000..9331189
--- /dev/null
+++ b/lib/setup/sv/shepherd/sddm.scm
@@ -0,0 +1,9 @@
+(use-modules (shepherd service))
+(register-services
+ (list (service
+        '(sddm)
+        #:requirement '(dbus)
+        #:documentation "Simple Desktop Display Manager"
+        #:start (make-forkexec-constructor '("/usr/bin/sddm"))
+        #:stop (make-kill-destructor)
+        #:respawn? #t)))
diff --git a/lib/setup/sv/shepherd/seatd.scm b/lib/setup/sv/shepherd/seatd.scm
new file mode 100644
index 0000000..ddd0162
--- /dev/null
+++ b/lib/setup/sv/shepherd/seatd.scm
@@ -0,0 +1,9 @@
+(use-modules (shepherd service))
+(register-services
+ (list (service
+        '(seatd)
+        #:requirement '()
+        #:documentation "seat management daemon"
+        #:start (make-forkexec-constructor '("/usr/bin/seatd" "-g" "video"))
+        #:stop (make-kill-destructor)
+        #:respawn? #t)))
diff --git a/lib/setup/sv/shepherd/sshd.scm b/lib/setup/sv/shepherd/sshd.scm
new file mode 100644
index 0000000..c593d4f
--- /dev/null
+++ b/lib/setup/sv/shepherd/sshd.scm
@@ -0,0 +1,9 @@
+(use-modules (shepherd service))
+(register-services
+ (list (service
+        '(sshd)
+        #:requirement '()
+        #:documentation "OpenSSH daemon"
+        #:start (make-forkexec-constructor '("/usr/sbin/sshd" "-D"))
+        #:stop (make-kill-destructor)
+        #:respawn? #t)))
diff --git a/lib/setup/sv/shepherd/wireplumber.scm b/lib/setup/sv/shepherd/wireplumber.scm
new file mode 100644
index 0000000..cd349e2
--- /dev/null
+++ b/lib/setup/sv/shepherd/wireplumber.scm
@@ -0,0 +1,9 @@
+(use-modules (shepherd service))
+(register-services
+ (list (service
+        '(wireplumber)
+        #:requirement '(dbus)
+        #:documentation "WirePlumber session manager"
+        #:start (make-forkexec-constructor '("/usr/bin/wireplumber"))
+        #:stop (make-kill-destructor)
+        #:respawn? #t)))
diff --git a/lib/setup/sv/systemd/NetworkManager.service b/lib/setup/sv/systemd/NetworkManager.service
new file mode 100644
index 0000000..ec98913
--- /dev/null
+++ b/lib/setup/sv/systemd/NetworkManager.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=NetworkManager
+After=dbus.service
+Wants=dbus.service
+
+[Service]
+ExecStart=/usr/sbin/NetworkManager --no-daemon
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/lib/setup/sv/systemd/bluetooth.service b/lib/setup/sv/systemd/bluetooth.service
new file mode 100644
index 0000000..746cf04
--- /dev/null
+++ b/lib/setup/sv/systemd/bluetooth.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Bluetooth daemon
+After=dbus.service
+Wants=dbus.service
+
+[Service]
+ExecStart=/usr/libexec/bluetooth/bluetoothd
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/lib/setup/sv/systemd/cups.service b/lib/setup/sv/systemd/cups.service
new file mode 100644
index 0000000..ef51683
--- /dev/null
+++ b/lib/setup/sv/systemd/cups.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=CUPS printing service
+After=dbus.service
+Wants=dbus.service
+
+[Service]
+ExecStart=/usr/sbin/cupsd -f
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/lib/setup/sv/systemd/dbus.service b/lib/setup/sv/systemd/dbus.service
new file mode 100644
index 0000000..e6f9cc0
--- /dev/null
+++ b/lib/setup/sv/systemd/dbus.service
@@ -0,0 +1,10 @@
+[Unit]
+Description=D-Bus system message bus
+
+[Service]
+ExecStartPre=/bin/sh -c 'mkdir -p /run/dbus /var/lib/dbus'
+ExecStart=/usr/bin/dbus-daemon --system --nofork --nopidfile --nosyslog
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/lib/setup/sv/systemd/dhcpcd.service b/lib/setup/sv/systemd/dhcpcd.service
new file mode 100644
index 0000000..a01af61
--- /dev/null
+++ b/lib/setup/sv/systemd/dhcpcd.service
@@ -0,0 +1,9 @@
+[Unit]
+Description=DHCP client daemon
+
+[Service]
+ExecStart=/usr/sbin/dhcpcd --nobackground
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/lib/setup/sv/systemd/elogind.service b/lib/setup/sv/systemd/elogind.service
new file mode 100644
index 0000000..3788be2
--- /dev/null
+++ b/lib/setup/sv/systemd/elogind.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=elogind login manager
+After=dbus.service
+Wants=dbus.service
+
+[Service]
+ExecStart=/usr/libexec/elogind
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/lib/setup/sv/systemd/iwd.service b/lib/setup/sv/systemd/iwd.service
new file mode 100644
index 0000000..c624bc9
--- /dev/null
+++ b/lib/setup/sv/systemd/iwd.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=iNet wireless daemon
+After=dbus.service
+Wants=dbus.service
+
+[Service]
+ExecStart=/usr/libexec/iwd
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/lib/setup/sv/systemd/pipewire.service b/lib/setup/sv/systemd/pipewire.service
new file mode 100644
index 0000000..8903960
--- /dev/null
+++ b/lib/setup/sv/systemd/pipewire.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=PipeWire multimedia server
+After=dbus.service
+Wants=dbus.service
+
+[Service]
+ExecStart=/usr/bin/pipewire
+Restart=on-failure
+
+[Install]
+WantedBy=default.target
diff --git a/lib/setup/sv/systemd/power-profiles-daemon.service b/lib/setup/sv/systemd/power-profiles-daemon.service
new file mode 100644
index 0000000..104a806
--- /dev/null
+++ b/lib/setup/sv/systemd/power-profiles-daemon.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Power profiles daemon
+After=dbus.service
+Wants=dbus.service
+
+[Service]
+ExecStart=/usr/libexec/power-profiles-daemon
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/lib/setup/sv/systemd/sddm.service b/lib/setup/sv/systemd/sddm.service
new file mode 100644
index 0000000..60cd4df
--- /dev/null
+++ b/lib/setup/sv/systemd/sddm.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Simple Desktop Display Manager
+After=dbus.service
+Wants=dbus.service
+
+[Service]
+ExecStart=/usr/bin/sddm
+Restart=on-failure
+
+[Install]
+WantedBy=graphical.target
diff --git a/lib/setup/sv/systemd/seatd.service b/lib/setup/sv/systemd/seatd.service
new file mode 100644
index 0000000..e56c062
--- /dev/null
+++ b/lib/setup/sv/systemd/seatd.service
@@ -0,0 +1,9 @@
+[Unit]
+Description=seat management daemon
+
+[Service]
+ExecStart=/usr/bin/seatd -g video
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/lib/setup/sv/systemd/sshd.service b/lib/setup/sv/systemd/sshd.service
new file mode 100644
index 0000000..fe63c0e
--- /dev/null
+++ b/lib/setup/sv/systemd/sshd.service
@@ -0,0 +1,9 @@
+[Unit]
+Description=OpenSSH daemon
+
+[Service]
+ExecStart=/usr/sbin/sshd -D
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/lib/setup/sv/systemd/wireplumber.service b/lib/setup/sv/systemd/wireplumber.service
new file mode 100644
index 0000000..bcf433f
--- /dev/null
+++ b/lib/setup/sv/systemd/wireplumber.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=WirePlumber session manager
+After=dbus.service
+Wants=dbus.service
+
+[Service]
+ExecStart=/usr/bin/wireplumber
+Restart=on-failure
+
+[Install]
+WantedBy=default.target
diff --git a/man/pkin.1 b/man/pkin.1
index 7c4d254..6560fcf 100644
--- a/man/pkin.1
+++ b/man/pkin.1
@@ -33,6 +33,13 @@ new packaged default changed, it is written beside the file as
 Visible package hooks may run before/after install or upgrade. A failing
 pre-install hook aborts before payload installation. A failing post-install
 hook returns status 10 after committed package state and history are retained.
+After a successful post-install,
+.B pkin
+enables a mapped service for the init recorded in
+.I $SPS_ROOT/etc/sps/init
+(systemd, OpenRC, s6, runit, dinit, or Shepherd). Stock scripts under
+.I $SPS_LIBDIR/setup/sv
+are copied when the package did not ship a file for that init.
 .SH OWNERSHIP
 When run as uid 0, new payload files and package-created directories are
 normalized to uid/gid 0 while keeping their packaged modes. Existing shared
diff --git a/man/setup.1 b/man/setup.1
index ad95db7..86979dc 100644
--- a/man/setup.1
+++ b/man/setup.1
@@ -46,17 +46,27 @@ need
 with
 .B --disk .
 .B --init systemd
-or
+,
 .B --init openrc
+,
+.B --init s6
+,
+.B --init runit
+,
+.B --init dinit
+, or
+.B --init shepherd
 selects PID 1;
 .B sget
-will not install both.
+will not install more than one of those families.
 After packages land,
 .B setup
-enables known services (SDDM, dbus, sshd, NetworkManager, and others
-listed in
-.I $SPS_LIBDIR/setup/services )
-for that init. Pass
+copies stock service scripts from
+.I $SPS_LIBDIR/setup/sv
+when a package did not ship a file for the chosen init, then enables
+known services (SDDM, dbus, sshd, NetworkManager, and others listed in
+.I $SPS_LIBDIR/setup/services ).
+Pass
 .B --services no
 to skip enablement.
 Live images prefer systemd. If the build host has no systemd binaries,
@@ -188,19 +198,26 @@ see
 .B --flatpak yes|no
 Install the Flatpak set and attempt to add Flathub after packages land.
 .TP
-.B --init systemd|openrc
+.B --init systemd|openrc|s6|runit|dinit|shepherd|none
 PID 1 on the installed system. Scripts default to none so a fixture
-install does not pull systemd. The interactive screen defaults to
-systemd. The power set does not pull elogind; OpenRC gets elogind from
-.B init-openrc
-so a Plasma install can use systemd without a conflict.
+install does not pull an init. The interactive screen defaults to the
+live image's
+.I /etc/sps/init
+when that file names a known init, otherwise systemd.
+Supervision inits pull eudev and elogind the same way OpenRC does.
+The power set does not pull elogind; systemd gets logind from systemd
+itself so a Plasma install can use systemd without a conflict.
 .TP
 .B --services yes|no
 Enable known services for the chosen init after packages install
-(default yes). Does not start daemons inside a target chroot; first
-boot starts them. Package post-install hooks do the same for later
+(default yes). Missing unit or script files are filled from stock
+copies under
+.IR $SPS_LIBDIR/setup/sv .
+Does not start daemons inside a target chroot; first boot starts them.
+.B pkin
+does the same after every later
 .B sget install
-of extras such as sddm.
+of a mapped package such as sddm.
 .TP
 .B --format yes|no
 Required
diff --git a/man/sps.conf.5 b/man/sps.conf.5
index 0581570..4c6760a 100644
--- a/man/sps.conf.5
+++ b/man/sps.conf.5
@@ -33,6 +33,49 @@ The checkout for repository NAME is
 .BI makejobs " count"
 Positive per-package build parallelism exported to recipes as
 .BR MAKEJOBS .
+The word
+.B auto
+uses
+.BR nproc (1)
+(or
+.B getconf _NPROCESSORS_ONLN
+). Default:
+.BR auto .
+.TP
+.BI cflags " flags"
+.TQ
+.BI cxxflags " flags"
+.TQ
+.BI cppflags " flags"
+.TQ
+.BI ldflags " flags"
+Compiler and linker flags. The value is the rest of the line, so
+.B cflags -O2 -pipe
+is valid. Defaults are
+.B -O2 -pipe
+for
+.B cflags
+and
+.BR cxxflags .
+Empty
+.B cppflags
+and
+.B ldflags
+are omitted from the environment.
+.TP
+.BI march " cpu"
+CPU for
+.B -march=
+appended to
+.B cflags
+and
+.B cxxflags
+when those flags do not already contain
+.BR -march= .
+Use
+.B none
+to skip. Default:
+.BR native .
 .TP
 .BI compression " type"
 Package compression:
@@ -92,6 +135,11 @@ configuration order breaks equal priorities. Repository names must be unique.
 .BR SPS_BUILD ,
 .BR SPS_REPO_ROOT ,
 .BR SPS_MAKEJOBS ,
+.BR SPS_CFLAGS ,
+.BR SPS_CXXFLAGS ,
+.BR SPS_CPPFLAGS ,
+.BR SPS_LDFLAGS ,
+.BR SPS_MARCH ,
 .BR SPS_COMPRESSION ,
 .BR SPS_ARCH ,
 and
@@ -108,7 +156,10 @@ db /var/lib/sps
 cache /var/cache/sps
 build /var/tmp/sps
 repo_root /usr/src/sps
-makejobs 8
+makejobs auto
+cflags -O2 -pipe
+cxxflags -O2 -pipe
+march native
 compression zstd
 preserve etc
 
diff --git a/tests/test_common.sh b/tests/test_common.sh
index b7bb46b..c0a4c71 100755
--- a/tests/test_common.sh
+++ b/tests/test_common.sh
@@ -48,3 +48,33 @@ created=$(sps_mktemp_dir "$tmp" common)
 [ -d "$created" ]
 
 printf '%s\n' 'common configuration tests passed'
+
+# Rest-of-line compiler flags, march none, and makejobs auto.
+{
+	printf '%s\n' 'makejobs auto'
+	printf '%s\n' 'cflags -O2 -pipe -fno-lto'
+	printf '%s\n' 'cxxflags -O2 -pipe -fno-lto'
+	printf '%s\n' 'march none'
+} > "$config_file"
+unset SPS_MAKEJOBS SPS_CFLAGS SPS_CXXFLAGS SPS_MARCH CFLAGS CXXFLAGS
+SPS_ROOT=$test_root
+SPS_CONFIG=$config_file
+SPS_REPOS_CONFIG=$repos_file
+export SPS_ROOT SPS_CONFIG SPS_REPOS_CONFIG
+sps_load_config
+case $SPS_MAKEJOBS in
+	''|*[!0-9]*|0) printf '%s\n' "makejobs auto produced '$SPS_MAKEJOBS'" >&2; exit 1 ;;
+esac
+[ "$CFLAGS" = '-O2 -pipe -fno-lto' ] || {
+	printf '%s\n' "cflags rest-of-line failed: '$CFLAGS'" >&2
+	exit 1
+}
+[ "$CXXFLAGS" = '-O2 -pipe -fno-lto' ] || {
+	printf '%s\n' "cxxflags rest-of-line failed: '$CXXFLAGS'" >&2
+	exit 1
+}
+case $CFLAGS in
+	*-march=*) printf '%s\n' "march none still added -march: '$CFLAGS'" >&2; exit 1 ;;
+esac
+
+printf '%s\n' 'compiler flag configuration tests passed'
diff --git a/tests/test_mkiso.sh b/tests/test_mkiso.sh
index 73067f3..d058c2a 100755
--- a/tests/test_mkiso.sh
+++ b/tests/test_mkiso.sh
@@ -278,6 +278,12 @@ grep -q /usr/bin/msgfmt "$project_dir/lib/mkiso/seed-bins" ||
 	fail 'seeded ISO must include msgfmt for alsa-utils translations'
 grep -q /usr/bin/xgettext "$project_dir/lib/mkiso/seed-bins" ||
 	fail 'seeded ISO must include xgettext with the gettext tools'
+grep -q /usr/bin/pod2man "$project_dir/lib/mkiso/seed-bins" ||
+	fail 'seeded ISO must include pod2man for OpenSSL and Perl docs'
+grep -q 'seeded live gcc is missing pod2man' "$mkiso" ||
+	fail 'mkiso must refuse a disc whose host has pod2man but the image does not'
+grep -q sps_print_default_conf "$mkiso" ||
+	fail 'mkiso must write a default sps.conf with compiler flags'
 grep -q mkiso_install_meson "$mkiso" ||
 	fail 'seeded ISO must install mesonbuild next to meson.py'
 grep -q mkiso_copy_python_stdlib "$mkiso" ||
diff --git a/tests/test_services.sh b/tests/test_services.sh
index 5c0db97..b49bf53 100644
--- a/tests/test_services.sh
+++ b/tests/test_services.sh
@@ -1,5 +1,5 @@
 #!/bin/sh
-# setup service map: enable systemd units and OpenRC scripts in a fake root.
+# setup service map: enable systemd, OpenRC, and supervision inits in a fake root.
 
 set -eu
 
@@ -70,6 +70,42 @@ setup_service_hook_enable || fail 'hook_enable with openrc failed'
 [ -L "$root/etc/runlevels/default/sshd" ] ||
 	fail 'openssh hook did not enable sshd for OpenRC'
 
+# Stock scripts are copied when the package did not ship this init's file.
+root2=$tmp/stock
+mkdir -p "$root2/etc/sps"
+printf '%s\n' runit >"$root2/etc/sps/init"
+enabled=$(setup_service_enable_one "$root2" runit sddm) ||
+	fail 'runit enable of sddm from stock scripts failed'
+[ -f "$root2/etc/sv/sddm/run" ] || fail 'stock runit sddm/run was not copied'
+[ -L "$root2/etc/runit/runsvdir/default/sddm" ] ||
+	fail 'runit sddm was not linked into runsvdir/default'
+
+enabled=$(setup_service_enable_one "$root2" s6 dbus) ||
+	fail 's6 enable of dbus from stock scripts failed'
+[ -f "$root2/etc/s6/sv/dbus/run" ] || fail 'stock s6 dbus/run was not copied'
+[ -L "$root2/etc/s6/enabled/dbus" ] || fail 's6 dbus was not linked into enabled'
+
+enabled=$(setup_service_enable_one "$root2" dinit NetworkManager) ||
+	fail 'dinit enable of NetworkManager from stock scripts failed'
+[ -f "$root2/etc/dinit.d/NetworkManager" ] ||
+	fail 'stock dinit NetworkManager was not copied'
+[ -L "$root2/etc/dinit.d/boot.d/NetworkManager" ] ||
+	fail 'dinit NetworkManager was not linked into boot.d'
+
+enabled=$(setup_service_enable_one "$root2" shepherd sshd) ||
+	fail 'shepherd enable of sshd from stock scripts failed'
+[ -f "$root2/etc/shepherd.d/sshd.scm" ] ||
+	fail 'stock shepherd sshd.scm was not copied'
+
+# Do not overwrite a package-shipped run script.
+mkdir -p "$root2/etc/sv/sshd"
+printf '%s\n' '#!/bin/sh' 'exec /opt/custom/sshd' >"$root2/etc/sv/sshd/run"
+chmod 755 "$root2/etc/sv/sshd/run"
+setup_service_enable_one "$root2" runit openssh >/dev/null ||
+	fail 'runit enable of shipped sshd failed'
+grep -q '/opt/custom/sshd' "$root2/etc/sv/sshd/run" ||
+	fail 'stock runit script overwrote a package-shipped run file'
+
 if grep -q '^package[[:space:]]*elogind' "$project_dir/lib/setup/sets/power.set"
 then
 	fail 'power set must not pull elogind (conflicts with systemd)'
diff --git a/tests/test_setup.sh b/tests/test_setup.sh
index 760aeaa..d3528e8 100755
--- a/tests/test_setup.sh
+++ b/tests/test_setup.sh
@@ -297,6 +297,8 @@ grep -q 'live gcc cannot create executables' "$setup" ||
 	fail 'setup must probe live gcc before sget install'
 grep -q 'live meson cannot run' "$setup" ||
 	fail 'setup must probe live meson before sget install'
+grep -q sps_print_default_conf "$setup" ||
+	fail 'setup must write sps.conf before sget install'
 
 plan=$("$setup" --plan --target /mnt --profile plasma-desktop --init systemd \
 	--disable qol-cli --disable power --disable firmware)
@@ -322,6 +324,19 @@ plan=$("$setup" --plan --target /mnt --profile plasma-desktop --init openrc \
 	--disable qol-cli)
 contains "$plan" 'init-openrc'
 
+plan=$("$setup" --plan --target /mnt --profile minimal --init s6 --disable qol-cli)
+contains "$plan" 'Init:       s6'
+contains "$plan" 'init-s6'
+
+plan=$("$setup" --plan --target /mnt --profile minimal --init runit --disable qol-cli)
+contains "$plan" 'init-runit'
+
+plan=$("$setup" --plan --target /mnt --profile minimal --init dinit --disable qol-cli)
+contains "$plan" 'init-dinit'
+
+plan=$("$setup" --plan --target /mnt --profile minimal --init shepherd --disable qol-cli)
+contains "$plan" 'init-shepherd'
+
 set +e
 "$setup" --plan --target /mnt --profile minimal --init systemd \
 	--disable qol-cli --extra elogind >/dev/null 2>"$tmp/err"
@@ -330,6 +345,20 @@ set -e
 [ "$st" -eq 7 ] || fail "systemd + extra elogind returned $st"
 contains "$(cat "$tmp/err")" 'elogind'
 
+set +e
+"$setup" --plan --target /mnt --profile minimal --init systemd \
+	--disable qol-cli --extra s6 >/dev/null 2>"$tmp/err"
+st=$?
+set -e
+[ "$st" -eq 7 ] || fail "systemd + extra s6 returned $st"
+
+plan=$("$setup" --plan --target /mnt --profile plasma-desktop --init s6 \
+	--disable qol-cli --disable firmware)
+contains "$plan" 'init-s6'
+case $plan in
+	*init-systemd*) fail 'plasma + s6 must not pull init-systemd' ;;
+esac
+
 plan=$("$setup" --plan --target /mnt --profile minimal --init systemd \
 	--disable qol-cli --extra sddm --services no)
 contains "$plan" 'sddm'