test: Rename testsuite-XX units to match test name
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Sat, 11 May 2024 17:17:13 +0000 (19:17 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 14 May 2024 10:43:28 +0000 (12:43 +0200)
Having these named differently than the test itself mostly creates
unecessary confusion and makes writing logic against the tests harder
so let's rename the testsuite-xx units and scripts to just use the
test name itself.

753 files changed:
mkosi.images/system/mkosi.conf
test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.service [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.socket [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.service [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.target [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.service [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.target [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/hello-after-sleep.target [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/hello.service [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-and-pullin.target [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-indirect.target [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-only.target [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/sleep-infinity-simple.service [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/sleep.service [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.service [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.target [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.service [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.sh [new file with mode: 0755]
test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.target [new file with mode: 0644]
test/TEST-03-JOBS/TEST-03-JOBS.units/unstoppable.service [new file with mode: 0644]
test/TEST-03-JOBS/meson.build
test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/delegated-cgroup-filtering.service [new file with mode: 0644]
test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/forever-print-hola.service [new file with mode: 0644]
test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/logs-filtering.service [new file with mode: 0644]
test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/silent-success.service [new file with mode: 0644]
test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/verbose-success.service [new file with mode: 0644]
test/TEST-04-JOURNAL/meson.build
test/TEST-04-JOURNAL/test.sh
test/TEST-06-SELINUX/TEST-06-SELINUX.units/hola.service [new file with mode: 0644]
test/TEST-06-SELINUX/meson.build
test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.service [new file with mode: 0644]
test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.sh [new file with mode: 0755]
test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-1.service [new file with mode: 0644]
test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-2.service [new file with mode: 0644]
test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-3.service [new file with mode: 0644]
test/TEST-07-PID1/TEST-07-PID1.units/issue2467.service [new file with mode: 0644]
test/TEST-07-PID1/TEST-07-PID1.units/issue2467.socket [new file with mode: 0644]
test/TEST-07-PID1/TEST-07-PID1.units/issue2730-alias.mount [new symlink]
test/TEST-07-PID1/TEST-07-PID1.units/issue2730.mount [new file with mode: 0644]
test/TEST-07-PID1/TEST-07-PID1.units/issue27953.service [new file with mode: 0644]
test/TEST-07-PID1/TEST-07-PID1.units/issue3166-fail-on-restart.service [new file with mode: 0644]
test/TEST-07-PID1/TEST-07-PID1.units/local-fs.target.wants/issue2730.mount [new symlink]
test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-no.socket [new file with mode: 0644]
test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-yes.socket [new file with mode: 0644]
test/TEST-07-PID1/meson.build
test/TEST-08-INITRD/test.sh
test/TEST-13-NSPAWN/test.sh
test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh [new file with mode: 0755]
test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-runtime.service [new file with mode: 0644]
test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-start.service [new file with mode: 0644]
test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-stop.service [new file with mode: 0644]
test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-all.service [new file with mode: 0644]
test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-runtime.service [new file with mode: 0644]
test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-start.service [new file with mode: 0644]
test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-stop.service [new file with mode: 0644]
test/TEST-16-EXTEND-TIMEOUT/meson.build
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-binds-to.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-bound-by.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-fail.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-1.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-2.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-3.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-4.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-5.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-6.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-7.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-8.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-9.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-namespaced.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-non-namespaced.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-oneshot-restartforce.sh [new file with mode: 0755]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server.socket [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server@.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-one.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-two.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-fail.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-upheld.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-uphold.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-short-lived.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-depends-wants.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-wants.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-success.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-upheldby-install.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-uphold.service [new file with mode: 0644]
test/TEST-23-UNIT-FILE/meson.build
test/TEST-30-ONCLOCKCHANGE/TEST-30-ONCLOCKCHANGE.units/systemd-timedated.service.d/watchdog.conf [new file with mode: 0644]
test/TEST-30-ONCLOCKCHANGE/meson.build
test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.service [new file with mode: 0644]
test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.sh [new file with mode: 0755]
test/TEST-52-HONORFIRSTSHUTDOWN/meson.build
test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.path [new file with mode: 0644]
test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.service [new file with mode: 0644]
test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577-dep.service [new file with mode: 0644]
test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.path [new file with mode: 0644]
test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.service [new file with mode: 0644]
test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.path [new file with mode: 0644]
test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.service [new file with mode: 0644]
test/TEST-63-PATH/TEST-63-PATH.units/test63.path [new file with mode: 0644]
test/TEST-63-PATH/TEST-63-PATH.units/test63.service [new file with mode: 0644]
test/TEST-63-PATH/meson.build
test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-nopin.service [new file with mode: 0644]
test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.service [new file with mode: 0644]
test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh [new file with mode: 0755]
test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.target [new file with mode: 0644]
test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify.service [new file with mode: 0644]
test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/test.sh [new file with mode: 0755]
test/TEST-80-NOTIFYACCESS/meson.build
test/integration-test-wrapper.py
test/meson.build
test/test-functions
test/testsuite-03.units/always-activating.service [deleted file]
test/testsuite-03.units/always-activating.socket [deleted file]
test/testsuite-03.units/fails-on-restart-restartdirect.service [deleted file]
test/testsuite-03.units/fails-on-restart-restartdirect.target [deleted file]
test/testsuite-03.units/fails-on-restart.service [deleted file]
test/testsuite-03.units/fails-on-restart.target [deleted file]
test/testsuite-03.units/hello-after-sleep.target [deleted file]
test/testsuite-03.units/hello.service [deleted file]
test/testsuite-03.units/propagatestopto-and-pullin.target [deleted file]
test/testsuite-03.units/propagatestopto-indirect.target [deleted file]
test/testsuite-03.units/propagatestopto-only.target [deleted file]
test/testsuite-03.units/sleep-infinity-simple.service [deleted file]
test/testsuite-03.units/sleep.service [deleted file]
test/testsuite-03.units/succeeds-on-restart-restartdirect.service [deleted file]
test/testsuite-03.units/succeeds-on-restart-restartdirect.target [deleted file]
test/testsuite-03.units/succeeds-on-restart.service [deleted file]
test/testsuite-03.units/succeeds-on-restart.sh [deleted file]
test/testsuite-03.units/succeeds-on-restart.target [deleted file]
test/testsuite-03.units/unstoppable.service [deleted file]
test/testsuite-04.units/delegated-cgroup-filtering.service [deleted file]
test/testsuite-04.units/forever-print-hola.service [deleted file]
test/testsuite-04.units/logs-filtering.service [deleted file]
test/testsuite-04.units/silent-success.service [deleted file]
test/testsuite-04.units/verbose-success.service [deleted file]
test/testsuite-06.units/hola.service [deleted file]
test/testsuite-07.units/issue14566-repro.service [deleted file]
test/testsuite-07.units/issue14566-repro.sh [deleted file]
test/testsuite-07.units/issue16115-repro-1.service [deleted file]
test/testsuite-07.units/issue16115-repro-2.service [deleted file]
test/testsuite-07.units/issue16115-repro-3.service [deleted file]
test/testsuite-07.units/issue2467.service [deleted file]
test/testsuite-07.units/issue2467.socket [deleted file]
test/testsuite-07.units/issue2730-alias.mount [deleted symlink]
test/testsuite-07.units/issue2730.mount [deleted file]
test/testsuite-07.units/issue27953.service [deleted file]
test/testsuite-07.units/issue3166-fail-on-restart.service [deleted file]
test/testsuite-07.units/local-fs.target.wants/issue2730.mount [deleted symlink]
test/testsuite-07.units/pass-fds-to-exec-no.socket [deleted file]
test/testsuite-07.units/pass-fds-to-exec-yes.socket [deleted file]
test/testsuite-16.units/extend-timeout.sh [deleted file]
test/testsuite-16.units/fail-runtime.service [deleted file]
test/testsuite-16.units/fail-start.service [deleted file]
test/testsuite-16.units/fail-stop.service [deleted file]
test/testsuite-16.units/success-all.service [deleted file]
test/testsuite-16.units/success-runtime.service [deleted file]
test/testsuite-16.units/success-start.service [deleted file]
test/testsuite-16.units/success-stop.service [deleted file]
test/testsuite-23.units/testsuite-23-binds-to.service [deleted file]
test/testsuite-23.units/testsuite-23-bound-by.service [deleted file]
test/testsuite-23.units/testsuite-23-fail.service [deleted file]
test/testsuite-23.units/testsuite-23-joins-namespace-of-1.service [deleted file]
test/testsuite-23.units/testsuite-23-joins-namespace-of-2.service [deleted file]
test/testsuite-23.units/testsuite-23-joins-namespace-of-3.service [deleted file]
test/testsuite-23.units/testsuite-23-joins-namespace-of-4.service [deleted file]
test/testsuite-23.units/testsuite-23-joins-namespace-of-5.service [deleted file]
test/testsuite-23.units/testsuite-23-joins-namespace-of-6.service [deleted file]
test/testsuite-23.units/testsuite-23-joins-namespace-of-7.service [deleted file]
test/testsuite-23.units/testsuite-23-joins-namespace-of-8.service [deleted file]
test/testsuite-23.units/testsuite-23-joins-namespace-of-9.service [deleted file]
test/testsuite-23.units/testsuite-23-namespaced.service [deleted file]
test/testsuite-23.units/testsuite-23-non-namespaced.service [deleted file]
test/testsuite-23.units/testsuite-23-oneshot-restartforce.sh [deleted file]
test/testsuite-23.units/testsuite-23-openfile-server.socket [deleted file]
test/testsuite-23.units/testsuite-23-openfile-server@.service [deleted file]
test/testsuite-23.units/testsuite-23-prop-stop-one.service [deleted file]
test/testsuite-23.units/testsuite-23-prop-stop-two.service [deleted file]
test/testsuite-23.units/testsuite-23-retry-fail.service [deleted file]
test/testsuite-23.units/testsuite-23-retry-upheld.service [deleted file]
test/testsuite-23.units/testsuite-23-retry-uphold.service [deleted file]
test/testsuite-23.units/testsuite-23-short-lived.service [deleted file]
test/testsuite-23.units/testsuite-23-specifier-j-depends-wants.service [deleted file]
test/testsuite-23.units/testsuite-23-specifier-j-wants.service [deleted file]
test/testsuite-23.units/testsuite-23-success.service [deleted file]
test/testsuite-23.units/testsuite-23-upheldby-install.service [deleted file]
test/testsuite-23.units/testsuite-23-uphold.service [deleted file]
test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf [deleted file]
test/testsuite-52.units/test-honor-first-shutdown.service [deleted file]
test/testsuite-52.units/test-honor-first-shutdown.sh [deleted file]
test/testsuite-63.units/test63-glob.path [deleted file]
test/testsuite-63.units/test63-glob.service [deleted file]
test/testsuite-63.units/test63-issue-24577-dep.service [deleted file]
test/testsuite-63.units/test63-issue-24577.path [deleted file]
test/testsuite-63.units/test63-issue-24577.service [deleted file]
test/testsuite-63.units/test63-pr-30768.path [deleted file]
test/testsuite-63.units/test63-pr-30768.service [deleted file]
test/testsuite-63.units/test63.path [deleted file]
test/testsuite-63.units/test63.service [deleted file]
test/testsuite-80.units/fdstore-nopin.service [deleted file]
test/testsuite-80.units/fdstore-pin.service [deleted file]
test/testsuite-80.units/fdstore-pin.sh [deleted file]
test/testsuite-80.units/fdstore-pin.target [deleted file]
test/testsuite-80.units/notify.service [deleted file]
test/testsuite-80.units/test.sh [deleted file]
test/units/TEST-01-BASIC.service [new file with mode: 0644]
test/units/TEST-01-BASIC.sh [new file with mode: 0755]
test/units/TEST-02-UNITTESTS.service [new file with mode: 0644]
test/units/TEST-02-UNITTESTS.sh [new file with mode: 0755]
test/units/TEST-03-JOBS.service [new file with mode: 0644]
test/units/TEST-03-JOBS.sh [new file with mode: 0755]
test/units/TEST-04-JOURNAL.LogFilterPatterns.sh [new file with mode: 0755]
test/units/TEST-04-JOURNAL.SYSTEMD_JOURNAL_COMPRESS.sh [new file with mode: 0755]
test/units/TEST-04-JOURNAL.bsod.sh [new file with mode: 0755]
test/units/TEST-04-JOURNAL.cat.sh [new file with mode: 0755]
test/units/TEST-04-JOURNAL.corrupted-journals.sh [new file with mode: 0755]
test/units/TEST-04-JOURNAL.fss.sh [new file with mode: 0755]
test/units/TEST-04-JOURNAL.journal-append.sh [new file with mode: 0755]
test/units/TEST-04-JOURNAL.journal-corrupt.sh [new file with mode: 0755]
test/units/TEST-04-JOURNAL.journal-gatewayd.sh [new file with mode: 0755]
test/units/TEST-04-JOURNAL.journal-remote.sh [new file with mode: 0755]
test/units/TEST-04-JOURNAL.journal.sh [new file with mode: 0755]
test/units/TEST-04-JOURNAL.service [new file with mode: 0644]
test/units/TEST-04-JOURNAL.sh [new file with mode: 0755]
test/units/TEST-05-RLIMITS.effective-limit.sh [new file with mode: 0755]
test/units/TEST-05-RLIMITS.rlimit.sh [new file with mode: 0755]
test/units/TEST-05-RLIMITS.service [new file with mode: 0644]
test/units/TEST-05-RLIMITS.sh [new file with mode: 0755]
test/units/TEST-06-SELINUX.service [new file with mode: 0644]
test/units/TEST-06-SELINUX.sh [new file with mode: 0755]
test/units/TEST-07-PID1.aux-scope.sh [new file with mode: 0755]
test/units/TEST-07-PID1.exec-context.sh [new file with mode: 0755]
test/units/TEST-07-PID1.exec-deserialization.sh [new file with mode: 0755]
test/units/TEST-07-PID1.exec-timestamps.sh [new file with mode: 0755]
test/units/TEST-07-PID1.issue-14566.sh [new file with mode: 0755]
test/units/TEST-07-PID1.issue-16115.sh [new file with mode: 0755]
test/units/TEST-07-PID1.issue-1981.sh [new file with mode: 0755]
test/units/TEST-07-PID1.issue-2467.sh [new file with mode: 0755]
test/units/TEST-07-PID1.issue-27953.sh [new file with mode: 0755]
test/units/TEST-07-PID1.issue-30412.sh [new file with mode: 0755]
test/units/TEST-07-PID1.issue-3166.sh [new file with mode: 0755]
test/units/TEST-07-PID1.issue-3171.sh [new file with mode: 0755]
test/units/TEST-07-PID1.main-PID-change.sh [new file with mode: 0755]
test/units/TEST-07-PID1.mount-invalid-chars.sh [new file with mode: 0755]
test/units/TEST-07-PID1.poll-limit.sh [new file with mode: 0755]
test/units/TEST-07-PID1.pr-31351.sh [new file with mode: 0755]
test/units/TEST-07-PID1.private-network.sh [new file with mode: 0755]
test/units/TEST-07-PID1.service [new file with mode: 0644]
test/units/TEST-07-PID1.sh [new file with mode: 0755]
test/units/TEST-07-PID1.socket-pass-fds.sh [new file with mode: 0755]
test/units/TEST-07-PID1.type-exec-parallel.sh [new file with mode: 0755]
test/units/TEST-08-INITRD.service [new file with mode: 0644]
test/units/TEST-08-INITRD.sh [new file with mode: 0755]
test/units/TEST-09-REBOOT.journal.sh [new file with mode: 0755]
test/units/TEST-09-REBOOT.service [new file with mode: 0644]
test/units/TEST-09-REBOOT.sh [new file with mode: 0755]
test/units/TEST-13-NSPAWN.importctl.sh [new file with mode: 0755]
test/units/TEST-13-NSPAWN.machinectl.sh [new file with mode: 0755]
test/units/TEST-13-NSPAWN.nspawn-oci.sh [new file with mode: 0755]
test/units/TEST-13-NSPAWN.nspawn.sh [new file with mode: 0755]
test/units/TEST-13-NSPAWN.nss-mymachines.sh [new file with mode: 0755]
test/units/TEST-13-NSPAWN.service [new file with mode: 0644]
test/units/TEST-13-NSPAWN.sh [new file with mode: 0755]
test/units/TEST-15-DROPIN.service [new file with mode: 0644]
test/units/TEST-15-DROPIN.sh [new file with mode: 0755]
test/units/TEST-16-EXTEND-TIMEOUT.service [new file with mode: 0644]
test/units/TEST-16-EXTEND-TIMEOUT.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.00.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.01.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.02.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.03.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.04.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.05.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.06.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.07.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.08.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.09.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.10.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.11.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.12.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.13.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.credentials.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.link-property.sh [new file with mode: 0755]
test/units/TEST-17-UDEV.service [new file with mode: 0644]
test/units/TEST-17-UDEV.sh [new file with mode: 0755]
test/units/TEST-18-FAILUREACTION.service [new file with mode: 0644]
test/units/TEST-18-FAILUREACTION.sh [new file with mode: 0755]
test/units/TEST-19-CGROUP.ExitType-cgroup.sh [new file with mode: 0755]
test/units/TEST-19-CGROUP.cleanup-slice.sh [new file with mode: 0755]
test/units/TEST-19-CGROUP.delegate.sh [new file with mode: 0755]
test/units/TEST-19-CGROUP.service [new file with mode: 0644]
test/units/TEST-19-CGROUP.sh [new file with mode: 0755]
test/units/TEST-21-DFUZZERE.service [new file with mode: 0644]
test/units/TEST-21-DFUZZERE.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.01.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.02.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.03.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.04.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.05.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.06.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.07.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.08.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.09.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.10.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.11.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.12.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.13.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.14.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.15.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.16.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.17.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.18.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.19.sh [new file with mode: 0755]
test/units/TEST-22-TMPFILES.service [new file with mode: 0644]
test/units/TEST-22-TMPFILES.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE-openfile-child.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE-short-lived.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.ExecReload.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.ExecStopPost.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.JoinsNamespaceOf.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.RuntimeDirectoryPreserve.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.StandardOutput.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.Upholds.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.clean-unit.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.exec-command-ex.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.oneshot-restart.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.openfile.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.percentj-wantedby.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.service [new file with mode: 0644]
test/units/TEST-23-UNIT-FILE.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.start-stop-no-reload.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.statedir.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.success-failure.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.type-exec.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.utmp.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.verify-unit-files.sh [new file with mode: 0755]
test/units/TEST-23-UNIT-FILE.whoami.sh [new file with mode: 0755]
test/units/TEST-24-CRYPTSETUP.service [new file with mode: 0644]
test/units/TEST-24-CRYPTSETUP.sh [new file with mode: 0755]
test/units/TEST-25-IMPORT.service [new file with mode: 0644]
test/units/TEST-25-IMPORT.sh [new file with mode: 0755]
test/units/TEST-26-SYSTEMCTL.service [new file with mode: 0644]
test/units/TEST-26-SYSTEMCTL.sh [new file with mode: 0755]
test/units/TEST-29-PORTABLE.service [new file with mode: 0644]
test/units/TEST-29-PORTABLE.sh [new file with mode: 0755]
test/units/TEST-30-ONCLOCKCHANGE.service [new file with mode: 0644]
test/units/TEST-30-ONCLOCKCHANGE.sh [new file with mode: 0755]
test/units/TEST-31-DEVICE-ENUMERATION.service [new file with mode: 0644]
test/units/TEST-31-DEVICE-ENUMERATION.sh [new file with mode: 0755]
test/units/TEST-32-OOMPOLICY.service [new file with mode: 0644]
test/units/TEST-32-OOMPOLICY.sh [new file with mode: 0755]
test/units/TEST-34-DYNAMICUSERMIGRATE.service [new file with mode: 0644]
test/units/TEST-34-DYNAMICUSERMIGRATE.sh [new file with mode: 0755]
test/units/TEST-35-LOGIN.service [new file with mode: 0644]
test/units/TEST-35-LOGIN.sh [new file with mode: 0755]
test/units/TEST-36-NUMAPOLICY.service [new file with mode: 0644]
test/units/TEST-36-NUMAPOLICY.sh [new file with mode: 0755]
test/units/TEST-38-FREEZER-sleep.service [new file with mode: 0644]
test/units/TEST-38-FREEZER.service [new file with mode: 0644]
test/units/TEST-38-FREEZER.sh [new file with mode: 0755]
test/units/TEST-43-PRIVATEUSER-UNPRIV.service [new file with mode: 0644]
test/units/TEST-43-PRIVATEUSER-UNPRIV.sh [new file with mode: 0755]
test/units/TEST-44-LOG-NAMESPACE.service [new file with mode: 0644]
test/units/TEST-44-LOG-NAMESPACE.sh [new file with mode: 0755]
test/units/TEST-45-TIMEDATE.service [new file with mode: 0644]
test/units/TEST-45-TIMEDATE.sh [new file with mode: 0755]
test/units/TEST-46-HOMED.service [new file with mode: 0644]
test/units/TEST-46-HOMED.sh [new file with mode: 0755]
test/units/TEST-50-DISSECT.DDI.sh [new file with mode: 0755]
test/units/TEST-50-DISSECT.dissect.sh [new file with mode: 0755]
test/units/TEST-50-DISSECT.mountfsd.sh [new file with mode: 0755]
test/units/TEST-50-DISSECT.service [new file with mode: 0644]
test/units/TEST-50-DISSECT.sh [new file with mode: 0755]
test/units/TEST-50-DISSECT.sysext.sh [new file with mode: 0755]
test/units/TEST-52-HONORFIRSTSHUTDOWN.service [new file with mode: 0644]
test/units/TEST-52-HONORFIRSTSHUTDOWN.sh [new file with mode: 0755]
test/units/TEST-53-ISSUE-16347.service [new file with mode: 0644]
test/units/TEST-53-ISSUE-16347.sh [new file with mode: 0755]
test/units/TEST-54-CREDS.service [new file with mode: 0644]
test/units/TEST-54-CREDS.sh [new file with mode: 0755]
test/units/TEST-55-OOMD-testbloat.service [new file with mode: 0644]
test/units/TEST-55-OOMD-testchill.service [new file with mode: 0644]
test/units/TEST-55-OOMD-testmunch.service [new file with mode: 0644]
test/units/TEST-55-OOMD-workload.slice [new file with mode: 0644]
test/units/TEST-55-OOMD.service [new file with mode: 0644]
test/units/TEST-55-OOMD.sh [new file with mode: 0755]
test/units/TEST-58-REPART.service [new file with mode: 0644]
test/units/TEST-58-REPART.sh [new file with mode: 0755]
test/units/TEST-59-RELOADING-RESTART.service [new file with mode: 0644]
test/units/TEST-59-RELOADING-RESTART.sh [new file with mode: 0755]
test/units/TEST-60-MOUNT-RATELIMIT.service [new file with mode: 0644]
test/units/TEST-60-MOUNT-RATELIMIT.sh [new file with mode: 0755]
test/units/TEST-62-RESTRICT-IFACES-1.service [new file with mode: 0644]
test/units/TEST-62-RESTRICT-IFACES-2.service [new file with mode: 0644]
test/units/TEST-62-RESTRICT-IFACES-3.service [new file with mode: 0644]
test/units/TEST-62-RESTRICT-IFACES-4.service [new file with mode: 0644]
test/units/TEST-62-RESTRICT-IFACES-5.service [new file with mode: 0644]
test/units/TEST-62-RESTRICT-IFACES-6.service [new file with mode: 0644]
test/units/TEST-62-RESTRICT-IFACES.service [new file with mode: 0644]
test/units/TEST-62-RESTRICT-IFACES.sh [new file with mode: 0755]
test/units/TEST-63-PATH.service [new file with mode: 0644]
test/units/TEST-63-PATH.sh [new file with mode: 0755]
test/units/TEST-64-UDEV-STORAGE.service [new file with mode: 0644]
test/units/TEST-64-UDEV-STORAGE.sh [new file with mode: 0755]
test/units/TEST-65-ANALYZE.service [new file with mode: 0644]
test/units/TEST-65-ANALYZE.sh [new file with mode: 0755]
test/units/TEST-66-DEVICE-ISOLATION-device-isolation.service [new file with mode: 0644]
test/units/TEST-66-DEVICE-ISOLATION.sh [new file with mode: 0755]
test/units/TEST-66-DEVICEISOLATION.service [new file with mode: 0644]
test/units/TEST-67-INTEGRITY.service [new file with mode: 0644]
test/units/TEST-67-INTEGRITY.sh [new file with mode: 0755]
test/units/TEST-68-PROPAGATE-EXIT-STATUS.service [new file with mode: 0644]
test/units/TEST-68-PROPAGATE-EXIT-STATUS.sh [new file with mode: 0755]
test/units/TEST-69-SHUTDOWN.service [new file with mode: 0644]
test/units/TEST-70-TPM2.creds.sh [new file with mode: 0755]
test/units/TEST-70-TPM2.cryptenroll.sh [new file with mode: 0755]
test/units/TEST-70-TPM2.cryptsetup.sh [new file with mode: 0755]
test/units/TEST-70-TPM2.measure.sh [new file with mode: 0755]
test/units/TEST-70-TPM2.pcrextend.sh [new file with mode: 0755]
test/units/TEST-70-TPM2.pcrlock.sh [new file with mode: 0755]
test/units/TEST-70-TPM2.service [new file with mode: 0644]
test/units/TEST-70-TPM2.sh [new file with mode: 0755]
test/units/TEST-70-TPM2.tpm2-setup.sh [new file with mode: 0755]
test/units/TEST-71-HOSTNAME.service [new file with mode: 0644]
test/units/TEST-71-HOSTNAME.sh [new file with mode: 0755]
test/units/TEST-72-SYSUPDATE.service [new file with mode: 0644]
test/units/TEST-72-SYSUPDATE.sh [new file with mode: 0755]
test/units/TEST-73-LOCALE.service [new file with mode: 0644]
test/units/TEST-73-LOCALE.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.battery-check.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.bootctl.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.busctl.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.capsule.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.cgls.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.cgtop.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.coredump.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.delta.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.escape.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.firstboot.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.id128.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.machine-id-setup.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.modules-load.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.mount.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.network-generator.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.networkctl.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.path.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.pstore.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.run.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.service [new file with mode: 0644]
test/units/TEST-74-AUX-UTILS.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.socket.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.ssh.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.varlinkctl.sh [new file with mode: 0755]
test/units/TEST-74-AUX-UTILS.vpick.sh [new file with mode: 0755]
test/units/TEST-75-RESOLVED.service [new file with mode: 0644]
test/units/TEST-75-RESOLVED.sh [new file with mode: 0755]
test/units/TEST-76-SYSCTL.service [new file with mode: 0644]
test/units/TEST-76-SYSCTL.sh [new file with mode: 0755]
test/units/TEST-78-SIGQUEUE.service [new file with mode: 0644]
test/units/TEST-78-SIGQUEUE.sh [new file with mode: 0755]
test/units/TEST-79-MEMPRESS.service [new file with mode: 0644]
test/units/TEST-79-MEMPRESS.sh [new file with mode: 0755]
test/units/TEST-80-NOTIFYACCESS.service [new file with mode: 0644]
test/units/TEST-80-NOTIFYACCESS.sh [new file with mode: 0755]
test/units/TEST-81-GENERATORS.debug-generator.sh [new file with mode: 0755]
test/units/TEST-81-GENERATORS.environment-d-generator.sh [new file with mode: 0755]
test/units/TEST-81-GENERATORS.fstab-generator.sh [new file with mode: 0755]
test/units/TEST-81-GENERATORS.getty-generator.sh [new file with mode: 0755]
test/units/TEST-81-GENERATORS.run-generator.sh [new file with mode: 0755]
test/units/TEST-81-GENERATORS.service [new file with mode: 0644]
test/units/TEST-81-GENERATORS.sh [new file with mode: 0755]
test/units/TEST-81-GENERATORS.system-update-generator.sh [new file with mode: 0755]
test/units/TEST-82-SOFTREBOOT.service [new file with mode: 0644]
test/units/TEST-82-SOFTREBOOT.sh [new file with mode: 0755]
test/units/TEST-83-BTRFS.service [new file with mode: 0644]
test/units/TEST-83-BTRFS.sh [new file with mode: 0755]
test/units/TEST-84-STORAGETM.service [new file with mode: 0644]
test/units/TEST-84-STORAGETM.sh [new file with mode: 0755]
test/units/test-control.sh
test/units/testsuite-01.service [deleted file]
test/units/testsuite-01.sh [deleted file]
test/units/testsuite-02.service [deleted file]
test/units/testsuite-02.sh [deleted file]
test/units/testsuite-03.service [deleted file]
test/units/testsuite-03.sh [deleted file]
test/units/testsuite-04.LogFilterPatterns.sh [deleted file]
test/units/testsuite-04.SYSTEMD_JOURNAL_COMPRESS.sh [deleted file]
test/units/testsuite-04.bsod.sh [deleted file]
test/units/testsuite-04.cat.sh [deleted file]
test/units/testsuite-04.corrupted-journals.sh [deleted file]
test/units/testsuite-04.fss.sh [deleted file]
test/units/testsuite-04.journal-append.sh [deleted file]
test/units/testsuite-04.journal-corrupt.sh [deleted file]
test/units/testsuite-04.journal-gatewayd.sh [deleted file]
test/units/testsuite-04.journal-remote.sh [deleted file]
test/units/testsuite-04.journal.sh [deleted file]
test/units/testsuite-04.service [deleted file]
test/units/testsuite-04.sh [deleted file]
test/units/testsuite-05.effective-limit.sh [deleted file]
test/units/testsuite-05.rlimit.sh [deleted file]
test/units/testsuite-05.service [deleted file]
test/units/testsuite-05.sh [deleted file]
test/units/testsuite-06.service [deleted file]
test/units/testsuite-06.sh [deleted file]
test/units/testsuite-07.aux-scope.sh [deleted file]
test/units/testsuite-07.exec-context.sh [deleted file]
test/units/testsuite-07.exec-deserialization.sh [deleted file]
test/units/testsuite-07.exec-timestamps.sh [deleted file]
test/units/testsuite-07.issue-14566.sh [deleted file]
test/units/testsuite-07.issue-16115.sh [deleted file]
test/units/testsuite-07.issue-1981.sh [deleted file]
test/units/testsuite-07.issue-2467.sh [deleted file]
test/units/testsuite-07.issue-27953.sh [deleted file]
test/units/testsuite-07.issue-30412.sh [deleted file]
test/units/testsuite-07.issue-3166.sh [deleted file]
test/units/testsuite-07.issue-3171.sh [deleted file]
test/units/testsuite-07.main-PID-change.sh [deleted file]
test/units/testsuite-07.mount-invalid-chars.sh [deleted file]
test/units/testsuite-07.poll-limit.sh [deleted file]
test/units/testsuite-07.pr-31351.sh [deleted file]
test/units/testsuite-07.private-network.sh [deleted file]
test/units/testsuite-07.service [deleted file]
test/units/testsuite-07.sh [deleted file]
test/units/testsuite-07.socket-pass-fds.sh [deleted file]
test/units/testsuite-07.type-exec-parallel.sh [deleted file]
test/units/testsuite-08.service [deleted file]
test/units/testsuite-08.sh [deleted file]
test/units/testsuite-09.journal.sh [deleted file]
test/units/testsuite-09.service [deleted file]
test/units/testsuite-09.sh [deleted file]
test/units/testsuite-13.importctl.sh [deleted file]
test/units/testsuite-13.machinectl.sh [deleted file]
test/units/testsuite-13.nspawn-oci.sh [deleted file]
test/units/testsuite-13.nspawn.sh [deleted file]
test/units/testsuite-13.nss-mymachines.sh [deleted file]
test/units/testsuite-13.service [deleted file]
test/units/testsuite-13.sh [deleted file]
test/units/testsuite-15.service [deleted file]
test/units/testsuite-15.sh [deleted file]
test/units/testsuite-16.service [deleted file]
test/units/testsuite-16.sh [deleted file]
test/units/testsuite-17.00.sh [deleted file]
test/units/testsuite-17.01.sh [deleted file]
test/units/testsuite-17.02.sh [deleted file]
test/units/testsuite-17.03.sh [deleted file]
test/units/testsuite-17.04.sh [deleted file]
test/units/testsuite-17.05.sh [deleted file]
test/units/testsuite-17.06.sh [deleted file]
test/units/testsuite-17.07.sh [deleted file]
test/units/testsuite-17.08.sh [deleted file]
test/units/testsuite-17.09.sh [deleted file]
test/units/testsuite-17.10.sh [deleted file]
test/units/testsuite-17.11.sh [deleted file]
test/units/testsuite-17.12.sh [deleted file]
test/units/testsuite-17.13.sh [deleted file]
test/units/testsuite-17.credentials.sh [deleted file]
test/units/testsuite-17.link-property.sh [deleted file]
test/units/testsuite-17.service [deleted file]
test/units/testsuite-17.sh [deleted file]
test/units/testsuite-18.service [deleted file]
test/units/testsuite-18.sh [deleted file]
test/units/testsuite-19.ExitType-cgroup.sh [deleted file]
test/units/testsuite-19.cleanup-slice.sh [deleted file]
test/units/testsuite-19.delegate.sh [deleted file]
test/units/testsuite-19.service [deleted file]
test/units/testsuite-19.sh [deleted file]
test/units/testsuite-21.service [deleted file]
test/units/testsuite-21.sh [deleted file]
test/units/testsuite-22.01.sh [deleted file]
test/units/testsuite-22.02.sh [deleted file]
test/units/testsuite-22.03.sh [deleted file]
test/units/testsuite-22.04.sh [deleted file]
test/units/testsuite-22.05.sh [deleted file]
test/units/testsuite-22.06.sh [deleted file]
test/units/testsuite-22.07.sh [deleted file]
test/units/testsuite-22.08.sh [deleted file]
test/units/testsuite-22.09.sh [deleted file]
test/units/testsuite-22.10.sh [deleted file]
test/units/testsuite-22.11.sh [deleted file]
test/units/testsuite-22.12.sh [deleted file]
test/units/testsuite-22.13.sh [deleted file]
test/units/testsuite-22.14.sh [deleted file]
test/units/testsuite-22.15.sh [deleted file]
test/units/testsuite-22.16.sh [deleted file]
test/units/testsuite-22.17.sh [deleted file]
test/units/testsuite-22.18.sh [deleted file]
test/units/testsuite-22.19.sh [deleted file]
test/units/testsuite-22.service [deleted file]
test/units/testsuite-22.sh [deleted file]
test/units/testsuite-23-openfile-child.sh [deleted file]
test/units/testsuite-23-short-lived.sh [deleted file]
test/units/testsuite-23.ExecReload.sh [deleted file]
test/units/testsuite-23.ExecStopPost.sh [deleted file]
test/units/testsuite-23.JoinsNamespaceOf.sh [deleted file]
test/units/testsuite-23.RuntimeDirectoryPreserve.sh [deleted file]
test/units/testsuite-23.StandardOutput.sh [deleted file]
test/units/testsuite-23.Upholds.sh [deleted file]
test/units/testsuite-23.clean-unit.sh [deleted file]
test/units/testsuite-23.exec-command-ex.sh [deleted file]
test/units/testsuite-23.oneshot-restart.sh [deleted file]
test/units/testsuite-23.openfile.sh [deleted file]
test/units/testsuite-23.percentj-wantedby.sh [deleted file]
test/units/testsuite-23.runtime-bind-paths.sh [deleted file]
test/units/testsuite-23.service [deleted file]
test/units/testsuite-23.sh [deleted file]
test/units/testsuite-23.start-stop-no-reload.sh [deleted file]
test/units/testsuite-23.statedir.sh [deleted file]
test/units/testsuite-23.success-failure.sh [deleted file]
test/units/testsuite-23.type-exec.sh [deleted file]
test/units/testsuite-23.utmp.sh [deleted file]
test/units/testsuite-23.verify-unit-files.sh [deleted file]
test/units/testsuite-23.whoami.sh [deleted file]
test/units/testsuite-24.service [deleted file]
test/units/testsuite-24.sh [deleted file]
test/units/testsuite-25.service [deleted file]
test/units/testsuite-25.sh [deleted file]
test/units/testsuite-26.service [deleted file]
test/units/testsuite-26.sh [deleted file]
test/units/testsuite-29.service [deleted file]
test/units/testsuite-29.sh [deleted file]
test/units/testsuite-30.service [deleted file]
test/units/testsuite-30.sh [deleted file]
test/units/testsuite-31.service [deleted file]
test/units/testsuite-31.sh [deleted file]
test/units/testsuite-32.service [deleted file]
test/units/testsuite-32.sh [deleted file]
test/units/testsuite-34.service [deleted file]
test/units/testsuite-34.sh [deleted file]
test/units/testsuite-35.service [deleted file]
test/units/testsuite-35.sh [deleted file]
test/units/testsuite-36.service [deleted file]
test/units/testsuite-36.sh [deleted file]
test/units/testsuite-38-sleep.service [deleted file]
test/units/testsuite-38.service [deleted file]
test/units/testsuite-38.sh [deleted file]
test/units/testsuite-43.service [deleted file]
test/units/testsuite-43.sh [deleted file]
test/units/testsuite-44.service [deleted file]
test/units/testsuite-44.sh [deleted file]
test/units/testsuite-45.service [deleted file]
test/units/testsuite-45.sh [deleted file]
test/units/testsuite-46.service [deleted file]
test/units/testsuite-46.sh [deleted file]
test/units/testsuite-50.DDI.sh [deleted file]
test/units/testsuite-50.dissect.sh [deleted file]
test/units/testsuite-50.mountfsd.sh [deleted file]
test/units/testsuite-50.service [deleted file]
test/units/testsuite-50.sh [deleted file]
test/units/testsuite-50.sysext.sh [deleted file]
test/units/testsuite-52.service [deleted file]
test/units/testsuite-52.sh [deleted file]
test/units/testsuite-53.service [deleted file]
test/units/testsuite-53.sh [deleted file]
test/units/testsuite-54.service [deleted file]
test/units/testsuite-54.sh [deleted file]
test/units/testsuite-55-testbloat.service [deleted file]
test/units/testsuite-55-testchill.service [deleted file]
test/units/testsuite-55-testmunch.service [deleted file]
test/units/testsuite-55-workload.slice [deleted file]
test/units/testsuite-55.service [deleted file]
test/units/testsuite-55.sh [deleted file]
test/units/testsuite-58.service [deleted file]
test/units/testsuite-58.sh [deleted file]
test/units/testsuite-59.service [deleted file]
test/units/testsuite-59.sh [deleted file]
test/units/testsuite-60.service [deleted file]
test/units/testsuite-60.sh [deleted file]
test/units/testsuite-62-1.service [deleted file]
test/units/testsuite-62-2.service [deleted file]
test/units/testsuite-62-3.service [deleted file]
test/units/testsuite-62-4.service [deleted file]
test/units/testsuite-62-5.service [deleted file]
test/units/testsuite-62-6.service [deleted file]
test/units/testsuite-62.service [deleted file]
test/units/testsuite-62.sh [deleted file]
test/units/testsuite-63.service [deleted file]
test/units/testsuite-63.sh [deleted file]
test/units/testsuite-64.service [deleted file]
test/units/testsuite-64.sh [deleted file]
test/units/testsuite-65.service [deleted file]
test/units/testsuite-65.sh [deleted file]
test/units/testsuite-66-deviceisolation.service [deleted file]
test/units/testsuite-66.service [deleted file]
test/units/testsuite-66.sh [deleted file]
test/units/testsuite-67.service [deleted file]
test/units/testsuite-67.sh [deleted file]
test/units/testsuite-68.service [deleted file]
test/units/testsuite-68.sh [deleted file]
test/units/testsuite-69.service [deleted file]
test/units/testsuite-70.creds.sh [deleted file]
test/units/testsuite-70.cryptenroll.sh [deleted file]
test/units/testsuite-70.cryptsetup.sh [deleted file]
test/units/testsuite-70.measure.sh [deleted file]
test/units/testsuite-70.pcrextend.sh [deleted file]
test/units/testsuite-70.pcrlock.sh [deleted file]
test/units/testsuite-70.service [deleted file]
test/units/testsuite-70.sh [deleted file]
test/units/testsuite-70.tpm2-setup.sh [deleted file]
test/units/testsuite-71.service [deleted file]
test/units/testsuite-71.sh [deleted file]
test/units/testsuite-72.service [deleted file]
test/units/testsuite-72.sh [deleted file]
test/units/testsuite-73.service [deleted file]
test/units/testsuite-73.sh [deleted file]
test/units/testsuite-74.battery-check.sh [deleted file]
test/units/testsuite-74.bootctl.sh [deleted file]
test/units/testsuite-74.busctl.sh [deleted file]
test/units/testsuite-74.capsule.sh [deleted file]
test/units/testsuite-74.cgls.sh [deleted file]
test/units/testsuite-74.cgtop.sh [deleted file]
test/units/testsuite-74.coredump.sh [deleted file]
test/units/testsuite-74.delta.sh [deleted file]
test/units/testsuite-74.escape.sh [deleted file]
test/units/testsuite-74.firstboot.sh [deleted file]
test/units/testsuite-74.id128.sh [deleted file]
test/units/testsuite-74.machine-id-setup.sh [deleted file]
test/units/testsuite-74.modules-load.sh [deleted file]
test/units/testsuite-74.mount.sh [deleted file]
test/units/testsuite-74.network-generator.sh [deleted file]
test/units/testsuite-74.networkctl.sh [deleted file]
test/units/testsuite-74.path.sh [deleted file]
test/units/testsuite-74.pstore.sh [deleted file]
test/units/testsuite-74.run.sh [deleted file]
test/units/testsuite-74.service [deleted file]
test/units/testsuite-74.sh [deleted file]
test/units/testsuite-74.socket.sh [deleted file]
test/units/testsuite-74.ssh.sh [deleted file]
test/units/testsuite-74.varlinkctl.sh [deleted file]
test/units/testsuite-74.vpick.sh [deleted file]
test/units/testsuite-75.service [deleted file]
test/units/testsuite-75.sh [deleted file]
test/units/testsuite-76.service [deleted file]
test/units/testsuite-76.sh [deleted file]
test/units/testsuite-78.service [deleted file]
test/units/testsuite-78.sh [deleted file]
test/units/testsuite-79.service [deleted file]
test/units/testsuite-79.sh [deleted file]
test/units/testsuite-80.service [deleted file]
test/units/testsuite-80.sh [deleted file]
test/units/testsuite-81.debug-generator.sh [deleted file]
test/units/testsuite-81.environment-d-generator.sh [deleted file]
test/units/testsuite-81.fstab-generator.sh [deleted file]
test/units/testsuite-81.getty-generator.sh [deleted file]
test/units/testsuite-81.run-generator.sh [deleted file]
test/units/testsuite-81.service [deleted file]
test/units/testsuite-81.sh [deleted file]
test/units/testsuite-81.system-update-generator.sh [deleted file]
test/units/testsuite-82.service [deleted file]
test/units/testsuite-82.sh [deleted file]
test/units/testsuite-83.service [deleted file]
test/units/testsuite-83.sh [deleted file]
test/units/testsuite-84.service [deleted file]
test/units/testsuite-84.sh [deleted file]
test/units/util.sh

index f7f32a07e6e2cb425bf7710be36cfb154c7fdab9..7f1c5f571eec52e7f5d060d9547e6a93ea721404 100644 (file)
@@ -20,7 +20,7 @@ ExtraTrees=
         %O/minimal-1.root-%a.raw:/usr/share/minimal_1.raw
         %O/minimal-1.root-%a-verity.raw:/usr/share/minimal_1.verity
         %O/minimal-1.root-%a-verity-sig.raw:/usr/share/minimal_1.verity.sig
-        %O/minimal-base:/usr/share/testsuite-13-container-template
+        %O/minimal-base:/usr/share/TEST-13-NSPAWN-container-template
 
 Packages=
         acl
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.service
new file mode 100644 (file)
index 0000000..93ddb85
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Service that never leaves state ACTIVATING
+Requires=always-activating.socket
+After=always-activating.socket
+
+[Service]
+Type=notify
+ExecStart=bash -c 'sleep infinity'
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.socket b/test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.socket
new file mode 100644 (file)
index 0000000..c76fed2
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=Socket that activates always-activating.service
+
+[Socket]
+ListenStream=/run/test-03-always-activating.sock
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.service
new file mode 100644 (file)
index 0000000..60ffd7a
--- /dev/null
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Fail on restart
+StartLimitIntervalSec=1m
+StartLimitBurst=3
+
+[Service]
+Type=oneshot
+ExecStart=false
+Restart=on-failure
+RestartMode=direct
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.target
new file mode 100644 (file)
index 0000000..58e2561
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=fails-on-restart-restartdirect.service
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.service
new file mode 100644 (file)
index 0000000..fb7e7ae
--- /dev/null
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Fail on restart
+StartLimitIntervalSec=1m
+StartLimitBurst=3
+
+[Service]
+Type=oneshot
+ExecStart=false
+Restart=on-failure
+RestartMode=normal
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.target
new file mode 100644 (file)
index 0000000..865fb2a
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=fails-on-restart.service
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/hello-after-sleep.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/hello-after-sleep.target
new file mode 100644 (file)
index 0000000..b0ddb30
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Sleep for a minute, then say hello.
+Wants=sleep.service hello.service
+After=sleep.service
+Before=hello.service
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/hello.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/hello.service
new file mode 100644 (file)
index 0000000..0c3f2f8
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Hello World
+
+[Service]
+ExecStart=/bin/echo "Hello World"
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-and-pullin.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-and-pullin.target
new file mode 100644 (file)
index 0000000..8e409af
--- /dev/null
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=sleep-infinity-simple.service
+After=sleep-infinity-simple.service
+PropagatesStopTo=sleep-infinity-simple.service
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-indirect.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-indirect.target
new file mode 100644 (file)
index 0000000..e8229a7
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+PropagatesStopTo=propagatestopto-and-pullin.target
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-only.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-only.target
new file mode 100644 (file)
index 0000000..327b7c1
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+PropagatesStopTo=sleep-infinity-simple.service
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/sleep-infinity-simple.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/sleep-infinity-simple.service
new file mode 100644 (file)
index 0000000..211346d
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Sleep infinitely
+
+[Service]
+Type=simple
+ExecStart=/bin/sleep infinity
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/sleep.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/sleep.service
new file mode 100644 (file)
index 0000000..32c2037
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Sleep for 1 minute
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sleep 60
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.service
new file mode 100644 (file)
index 0000000..a2b9c33
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-03-JOBS.units/succeeds-on-restart.sh
+Restart=on-failure
+RestartMode=direct
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.target
new file mode 100644 (file)
index 0000000..2cf3c60
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=succeeds-on-restart-restartdirect.service
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.service
new file mode 100644 (file)
index 0000000..d0e9c2b
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-03-JOBS.units/succeeds-on-restart.sh
+Restart=on-failure
+RestartMode=normal
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.sh b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.sh
new file mode 100755 (executable)
index 0000000..1428b18
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+if [[ ! -f "/succeeds-on-restart.ko" ]]
+then
+    touch "/succeeds-on-restart.ko"
+    exit 1
+else
+    rm "/succeeds-on-restart.ko"
+    exit 0
+fi
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.target
new file mode 100644 (file)
index 0000000..eb82f47
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=succeeds-on-restart.service
diff --git a/test/TEST-03-JOBS/TEST-03-JOBS.units/unstoppable.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/unstoppable.service
new file mode 100644 (file)
index 0000000..6eb7c19
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/bin/echo "I'm unstoppable!"
+ExecStop=/bin/systemctl start --no-block unstoppable.service
index 8dec5f37e73a80d31833a772fa16883b02a07615..3484d21ba2f156a16bbaebe18740f6c25932336c 100644 (file)
@@ -5,3 +5,5 @@ integration_tests += [
                 'name' : fs.name(meson.current_source_dir()),
         },
 ]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-03-JOBS.units']
diff --git a/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/delegated-cgroup-filtering.service b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/delegated-cgroup-filtering.service
new file mode 100644 (file)
index 0000000..a12b12a
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Test service for delegated logs filtering
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/delegated_cgroup_filtering_payload.sh
+Delegate=yes
+SyslogLevel=notice
diff --git a/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/forever-print-hola.service b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/forever-print-hola.service
new file mode 100644 (file)
index 0000000..9273d6d
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=ForeverPrintHola service
+
+[Service]
+Type=exec
+ExecStart=sh -x -c 'while :; do printf "Hola\n" || touch /tmp/i-lose-my-logs; sleep 1; done'
diff --git a/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/logs-filtering.service b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/logs-filtering.service
new file mode 100644 (file)
index 0000000..a5aba18
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Log filtering unit
+
+[Service]
+Type=oneshot
+ExecStart=sh -c 'echo "Logging from the service, and ~more~ foo bar" && sleep 2'
+SyslogLevel=notice
diff --git a/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/silent-success.service b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/silent-success.service
new file mode 100644 (file)
index 0000000..3d83f87
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Silent successful service
+
+[Service]
+LogLevelMax=notice
+ExecStart=/bin/true
diff --git a/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/verbose-success.service b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/verbose-success.service
new file mode 100644 (file)
index 0000000..f4a86fd
--- /dev/null
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Verbose successful service
+
+[Service]
+Type=oneshot
+# Sleep so that the cgroup is still there when journald processes the log message which is required for
+# journald to add the expected fields to the log message.
+ExecStart=sleep 2
+ExecStart=echo success
+ExecStart=sleep 2
index 184f70ab720bbfab14df975559b48a53f762949f..5a0b073c02bc5a1392a2bfddc37ee243721af89b 100644 (file)
@@ -8,3 +8,5 @@ integration_tests += [
                 'storage' : 'persistent',
         },
 ]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-04-JOURNAL.units']
index f8f3a4b3e32cf3f521b3e44ece906004497dbe57..a7aa71fce321a93bc6d742b83547cb17dd4a753e 100755 (executable)
@@ -20,7 +20,7 @@ test_append_files() {
     # Since we nuke the journal repeatedly during this test, let's redirect
     # stdout/stderr to the console as well to make the test a bit more debug-able.
     if ! get_bool "${INTERACTIVE_DEBUG:-}"; then
-        dropin_dir="${workspace:?}/etc/systemd/system/testsuite-04.service.d/"
+        dropin_dir="${workspace:?}/etc/systemd/system/TEST-04-JOURNAL.service.d/"
         mkdir -p "$dropin_dir"
         printf '[Service]\nStandardOutput=journal+console\nStandardError=journal+console' >"$dropin_dir/99-stdout.conf"
     fi
diff --git a/test/TEST-06-SELINUX/TEST-06-SELINUX.units/hola.service b/test/TEST-06-SELINUX/TEST-06-SELINUX.units/hola.service
new file mode 100644 (file)
index 0000000..94f9b47
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+ExecStart=/bin/echo Start Hola
+ExecReload=/bin/echo Reload Hola
+ExecStop=/bin/echo Stop Hola
+RemainAfterExit=yes
index 408e41ff287b1bf1731a425dba1f565c6b8c5957..e3cf0030c503277f3dd13763534d4b09891ff9ac 100644 (file)
@@ -10,3 +10,5 @@ integration_tests += [
                 'firmware' : 'uefi',
         },
 ]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-06-SELINUX.units']
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.service
new file mode 100644 (file)
index 0000000..757f978
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Issue 14566 Repro
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-07-PID1.units/%N.sh
+ExecStopPost=/bin/true
+KillMode=mixed
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.sh b/test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.sh
new file mode 100755 (executable)
index 0000000..74fa760
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+sleep infinity &
+echo $! >/leakedtestpid
+wait $!
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-1.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-1.service
new file mode 100644 (file)
index 0000000..90252b3
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Issue 16115 Repro with on-abnormal
+
+[Service]
+Type=simple
+Restart=on-abnormal
+ExecCondition=/bin/false
+ExecStart=sleep 100
+RestartSec=1
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-2.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-2.service
new file mode 100644 (file)
index 0000000..7c65691
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Issue 16115 Repro with on-failure
+
+[Service]
+Type=simple
+Restart=on-failure
+ExecCondition=/bin/false
+ExecStart=sleep 100
+RestartSec=1
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-3.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-3.service
new file mode 100644 (file)
index 0000000..c68f93d
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Issue 22257 Repro with Restart=always
+
+[Service]
+Type=simple
+Restart=always
+ExecCondition=/bin/false
+ExecStart=sleep 100
+RestartSec=1
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue2467.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue2467.service
new file mode 100644 (file)
index 0000000..99d886f
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=issue2467.socket
+ConditionPathExistsGlob=/tmp/nonexistent
+# Make sure we hit the socket trigger limit in the test and not the service start limit.
+StartLimitInterval=1000
+StartLimitBurst=1000
+
+[Service]
+ExecStart=true
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue2467.socket b/test/TEST-07-PID1/TEST-07-PID1.units/issue2467.socket
new file mode 100644 (file)
index 0000000..209b6bb
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Socket]
+ListenStream=/run/test.ctl
+# We might not be fast enough to hit the default limit (20 triggers per 2 secs)
+# in certain environments, i.e. when running without KVM or when collecting
+# coverage. Let's help it a bit in such cases by lowering the limit to 10 seconds.
+TriggerLimitIntervalSec=10
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue2730-alias.mount b/test/TEST-07-PID1/TEST-07-PID1.units/issue2730-alias.mount
new file mode 120000 (symlink)
index 0000000..802f026
--- /dev/null
@@ -0,0 +1 @@
+issue2730.mount
\ No newline at end of file
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue2730.mount b/test/TEST-07-PID1/TEST-07-PID1.units/issue2730.mount
new file mode 100644 (file)
index 0000000..2ac76c0
--- /dev/null
@@ -0,0 +1,8 @@
+[Mount]
+What=tmpfs
+Where=/issue2730
+Type=tmpfs
+
+[Install]
+WantedBy=local-fs.target
+Alias=issue2730-alias.mount
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue27953.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue27953.service
new file mode 100644 (file)
index 0000000..f441067
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=This unit should not remain active once the shell process exits
+
+[Service]
+Type=oneshot
+ExecStart=sh -c 'sleep infinity & exit 0'
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/issue3166-fail-on-restart.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue3166-fail-on-restart.service
new file mode 100644 (file)
index 0000000..b8695d8
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Fail on restart
+StartLimitIntervalSec=1m
+StartLimitBurst=3
+
+[Service]
+Type=exec
+ExecStart=false
+Restart=always
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/local-fs.target.wants/issue2730.mount b/test/TEST-07-PID1/TEST-07-PID1.units/local-fs.target.wants/issue2730.mount
new file mode 120000 (symlink)
index 0000000..70a8534
--- /dev/null
@@ -0,0 +1 @@
+../issue2730.mount
\ No newline at end of file
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-no.socket b/test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-no.socket
new file mode 100644 (file)
index 0000000..8b7964b
--- /dev/null
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test if ExecXYZ= commands don't inherit listen FDs when PassFileDescriptorsToExec= is unset
+
+[Socket]
+# With Accept= set we don't need a corresponding service unit
+Accept=yes
+FileDescriptorName=foo
+ListenStream=127.0.0.1:1234
+ListenStream=[::1]:1234
+PassFileDescriptorsToExec=no
+ExecStartPre=\
+        test ExecStartPre -a \
+             -z ${LISTEN_FDS} -a \
+             -z ${LISTEN_FDNAMES} -a \
+             ! -e /dev/fd/3 -a \
+             ! -e /dev/fd/4
+ExecStartPost=\
+        test ExecStartPost -a \
+             -z ${LISTEN_FDS} -a \
+             -z ${LISTEN_FDNAMES} -a \
+             ! -e /dev/fd/3 -a \
+             ! -e /dev/fd/4
+ExecStopPre=\
+        test ExecStopPre -a \
+             -z ${LISTEN_FDS} -a \
+             -z ${LISTEN_FDNAMES} -a \
+             ! -e /dev/fd/3 -a \
+             ! -e /dev/fd/4
+ExecStopPost=\
+        test ExecStopPost -a \
+             -z ${LISTEN_FDS} -a \
+             -z ${LISTEN_FDNAMES} -a \
+             ! -e /dev/fd/3 -a \
+             ! -e /dev/fd/4
diff --git a/test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-yes.socket b/test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-yes.socket
new file mode 100644 (file)
index 0000000..bff192d
--- /dev/null
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test if ExecXYZ= commands inherit listen FDs when PassFileDescriptorsToExec= is set
+
+[Socket]
+# With Accept= set we don't need a corresponding service unit
+Accept=yes
+FileDescriptorName=foo
+ListenStream=127.0.0.1:1234
+ListenStream=[::1]:1234
+PassFileDescriptorsToExec=yes
+# ExecStartPre runs before we create sockets. Nothing to pass.
+ExecStartPre=\
+        test ExecStartPre -a \
+             -z ${LISTEN_FDS} -a \
+             -z ${LISTEN_FDNAMES} -a \
+             ! -e /dev/fd/3 -a \
+             ! -e /dev/fd/4
+ExecStartPost=\
+        test ExecStartPost -a \
+             ${LISTEN_FDS} = 2 -a \
+             ${LISTEN_FDNAMES} = foo:foo -a \
+             -S /dev/fd/3 -a \
+             -S /dev/fd/4
+ExecStopPre=\
+        test "ExecStopPre" -a \
+             ${LISTEN_FDS} = 2 -a \
+             ${LISTEN_FDNAMES} = foo:foo -a \
+             -S /dev/fd/3 -a \
+             -S /dev/fd/4
+ExecStopPost=\
+        test "ExecStopPost" -a \
+             ${LISTEN_FDS} = 2 -a \
+             ${LISTEN_FDNAMES} = foo:foo -a \
+             -S /dev/fd/3 -a \
+             -S /dev/fd/4
index 8dec5f37e73a80d31833a772fa16883b02a07615..76ed3e75814f50573aac9b8bfa8d65c898556f76 100644 (file)
@@ -5,3 +5,5 @@ integration_tests += [
                 'name' : fs.name(meson.current_source_dir()),
         },
 ]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-07-PID1.units']
index 3ad60f1a949d4d24a88c8fe8f548a2c39f7b6656..badb6cb31a161da8fbb5b7b795c0bfe9bd6f7dd0 100755 (executable)
@@ -80,7 +80,7 @@ check_result_qemu_hook() {
 
 # Setup a one shot service in initrd that creates a dummy bind mount under /run
 # to check if the mount persists though the initrd transition. The "check" part
-# is in the respective testsuite-08.sh script.
+# is in the respective TEST-08-INITRD.sh script.
 #
 # See: https://github.com/systemd/systemd/issues/28452
 run_qemu_hook() {
index 744e793ac2e8008401c338452331319a0db17fde..9a0404f4b0c11f5480cac9ebb4cb82d22c21f68c 100755 (executable)
@@ -13,7 +13,7 @@ TEST_FORCE_NEWIMAGE=1
 
 test_append_files() {
     local workspace="${1:?}"
-    local container="$workspace/usr/share/testsuite-13-container-template"
+    local container="$workspace/usr/share/TEST-13-NSPAWN-container-template"
 
     # For virtual wlan interface.
     instmods mac80211_hwsim
diff --git a/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
new file mode 100755 (executable)
index 0000000..45a18b9
--- /dev/null
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# sleep interval (seconds)
+sleep_interval="${sleep_interval:-1}"
+# extend_timeout_interval second(s)
+extend_timeout_interval="${extend_timeout_interval:-1}"
+# number of sleep_intervals before READY=1
+start_intervals="${start_intervals:-10}"
+# number of sleep_intervals before exiting
+stop_intervals="${stop_intervals:-10}"
+# run intervals, number of sleep_intervals to run
+run_intervals="${run_intervals:-7}"
+
+# We convert to usec
+extend_timeout_interval=$((extend_timeout_interval * 1000000))
+
+# shellcheck disable=SC2064
+trap "{ touch /${SERVICE}.terminated; exit 1; }" SIGTERM SIGABRT
+
+rm -f "/${SERVICE}".*
+touch "/${SERVICE}.startfail"
+
+systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
+while [[ $start_intervals -gt 0 ]]
+do
+    sleep "$sleep_interval"
+    start_intervals=$((start_intervals - 1))
+    systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
+done
+
+systemd-notify --ready --status="Waiting for your request"
+
+touch "/${SERVICE}.runtimefail"
+rm "/${SERVICE}.startfail"
+
+systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
+while [[ $run_intervals -gt 0 ]]
+do
+    sleep "$sleep_interval"
+    run_intervals=$((run_intervals - 1))
+    systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
+done
+
+systemd-notify STOPPING=1
+
+touch "/${SERVICE}.stopfail"
+rm "/${SERVICE}.runtimefail"
+
+systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
+while [[ $stop_intervals -gt 0 ]]
+do
+    sleep "$sleep_interval"
+    stop_intervals=$((stop_intervals - 1))
+    systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
+done
+
+touch "/${SERVICE}.success"
+rm "/${SERVICE}.stopfail"
+
+exit 0
diff --git a/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-runtime.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-runtime.service
new file mode 100644 (file)
index 0000000..7380994
--- /dev/null
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: Fail Runtime (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after RuntimeSecMax.)
+
+[Service]
+# EXTEND_TIMEOUT_USEC on runtime start (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval)
+# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=4
+RuntimeMaxSec=10
+Environment=SERVICE=fail_runtime extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=2 stop_intervals=0
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
diff --git a/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-start.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-start.service
new file mode 100644 (file)
index 0000000..984d8f9
--- /dev/null
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: Fail Start (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStartSec.)
+
+[Service]
+
+# EXTEND_TIMEOUT_USEC on startup and 7 seconds from start. Systemd will expect one at 7+5 (extend_timeout_interval)
+# seconds  this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
+Type=notify
+TimeoutStartSec=10
+TimeoutStopSec=4
+RuntimeMaxSec=4
+Environment=SERVICE=fail_start extend_timeout_interval=5 sleep_interval=7 start_intervals=2 run_intervals=0 stop_intervals=0
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
diff --git a/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-stop.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-stop.service
new file mode 100644 (file)
index 0000000..4293d7e
--- /dev/null
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: Fail Stop (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStopSec.)
+
+[Service]
+# EXTEND_TIMEOUT_USEC on stop (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval)
+# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=10
+RuntimeMaxSec=4
+Environment=SERVICE=fail_stop extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=0 stop_intervals=2
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
+# Due to 6041a7ee2c1bbff6301082f192fc1b0882400d42 SIGTERM isn't sent as the service shuts down with STOPPING=1
+# This file makes the test assess.sh quicker by notifing it that this test has finished.
+ExecStopPost=/bin/bash -c '[[ $SERVICE_RESULT == timeout && $EXIT_CODE == killed ]] && touch /fail_runtime.terminated'
diff --git a/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-all.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-all.service
new file mode 100644 (file)
index 0000000..7f26084
--- /dev/null
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: EXTEND_TIMEOUT_USEC Success - extend timeout on all services
+
+[Service]
+
+# Normal success - startup / runtime / shutdown all take 8 seconds which is within the EXTEND_TIMEOUT_USEC=4 seconds interval
+# runtime is 8+8+8 seconds. so we are relying on the EXTEND_TIMEOUT_USEC to exceed all stages, Start, Runtime and Stop.
+# success occurs after 24 seconds
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=4
+RuntimeMaxSec=4
+Environment=SERVICE=success_all extend_timeout_interval=4 sleep_interval=2 start_intervals=3 run_intervals=3 stop_intervals=3
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
diff --git a/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-runtime.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-runtime.service
new file mode 100644 (file)
index 0000000..33a36f3
--- /dev/null
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: Success Runtime (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < RuntimeMaxSec)
+
+[Service]
+
+# EXTEND_TIMEOUT_USEC=4 second once during runtime, but sleep for 6 seconds.
+# Runtime is 6 seconds and < RuntimeMaxSec so still successful.
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=4
+RuntimeMaxSec=8
+Environment=SERVICE=success_runtime extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=1 stop_intervals=0
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
diff --git a/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-start.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-start.service
new file mode 100644 (file)
index 0000000..0453b34
--- /dev/null
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: Success Start (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStartSec)
+
+[Service]
+# EXTEND_TIMEOUT_USEC=4 second interval once at startup, but sleep 6 seconds.
+# Therefore startup is 6 seconds and < TimeoutStartSec so still successful.
+Type=notify
+TimeoutStartSec=8
+TimeoutStopSec=4
+RuntimeMaxSec=4
+Environment=SERVICE=success_start extend_timeout_interval=4 sleep_interval=6 start_intervals=1 run_intervals=0 stop_intervals=0
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
diff --git a/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-stop.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-stop.service
new file mode 100644 (file)
index 0000000..3719f54
--- /dev/null
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Testsuite: Success Stop (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStopSec)
+
+[Service]
+# EXTEND_TIMEOUT_USEC=4 seconds once during shutdown, but sleep for 6 seconds.
+# Therefore stop time is 6 seconds and < TimeoutStopSec so still successful.
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=8
+RuntimeMaxSec=4
+Environment=SERVICE=success_stop extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=0 stop_intervals=1
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
index 8dec5f37e73a80d31833a772fa16883b02a07615..75cf2029f98190d47b23c8435189c4eb24e2eee8 100644 (file)
@@ -5,3 +5,5 @@ integration_tests += [
                 'name' : fs.name(meson.current_source_dir()),
         },
 ]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-16-EXTEND-TIMEOUT.units']
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-binds-to.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-binds-to.service
new file mode 100644 (file)
index 0000000..022c37e
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Unit with BindsTo=
+BindsTo=TEST-23-UNIT-FILE-bound-by.service
+After=TEST-23-UNIT-FILE-bound-by.service
+
+[Service]
+ExecStart=sleep infinity
+# --kill-who= (no 'm') to check that the short form is accepted
+ExecStopPost=systemctl kill --kill-whom=main -sRTMIN+1 TEST-23-UNIT-FILE.service
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-bound-by.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-bound-by.service
new file mode 100644 (file)
index 0000000..c999c2e
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Unit with BoundBy=
+
+[Service]
+ExecStart=sleep 0.7
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-fail.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-fail.service
new file mode 100644 (file)
index 0000000..b78e6b4
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Failing unit
+OnFailure=TEST-23-UNIT-FILE-uphold.service
+
+[Service]
+ExecStart=false
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-1.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-1.service
new file mode 100644 (file)
index 0000000..47f0452
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=notify
+NotifyAccess=all
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-2.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-2.service
new file mode 100644 (file)
index 0000000..42dd445
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-1.service
+
+[Service]
+Type=oneshot
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=test -e /tmp/shared-private-file
+ExecStart=touch /tmp/hoge
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-3.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-3.service
new file mode 100644 (file)
index 0000000..f745481
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-1.service
+
+[Service]
+Type=oneshot
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=test -e /tmp/shared-private-file
+ExecStart=test -e /tmp/hoge
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-4.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-4.service
new file mode 100644 (file)
index 0000000..a0ff403
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-5.service
+
+[Service]
+Type=notify
+NotifyAccess=all
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-5.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-5.service
new file mode 100644 (file)
index 0000000..c3d316b
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=test -e /tmp/shared-private-file
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-6.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-6.service
new file mode 100644 (file)
index 0000000..9227bda
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-8.service
+
+[Service]
+Type=notify
+NotifyAccess=all
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=bash -c 'touch /tmp/shared-private-file-x && systemd-notify --ready && sleep infinity'
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-7.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-7.service
new file mode 100644 (file)
index 0000000..a5d62c6
--- /dev/null
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-8.service
+
+[Service]
+Type=oneshot
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=test -e /tmp/shared-private-file-x
+ExecStart=test ! -e /tmp/shared-private-file-y
+ExecStart=touch /tmp/hoge
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-8.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-8.service
new file mode 100644 (file)
index 0000000..8e4944a
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=notify
+NotifyAccess=all
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStartPre=test -e /tmp/shared-private-file-x
+ExecStartPre=test -e /tmp/hoge
+ExecStart=bash -c 'touch /tmp/shared-private-file-y && systemd-notify --ready && sleep infinity'
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-9.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-9.service
new file mode 100644 (file)
index 0000000..a30f92a
--- /dev/null
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-8.service
+
+[Service]
+Type=oneshot
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=test -e /tmp/shared-private-file-x
+ExecStart=test -e /tmp/shared-private-file-y
+ExecStart=test -e /tmp/hoge
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-namespaced.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-namespaced.service
new file mode 100644 (file)
index 0000000..82eb74c
--- /dev/null
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+RuntimeMaxSec=300
+# Adding a new mounts at runtime works if the unit is in the active state,
+# so use Type=notify to make sure there's no race condition in the test
+Type=notify
+RemainAfterExit=yes
+MountAPIVFS=yes
+PrivateTmp=yes
+BindPaths=/run/TEST-23-UNIT-FILE-marker-fixed:/tmp/testfile-marker-fixed
+InaccessiblePaths=/run/inaccessible
+ExecStartPre=grep -q -F MARKER_FIXED /tmp/testfile-marker-fixed
+ExecStart=sh -c 'systemd-notify --ready; until grep -q -F MARKER_RUNTIME /tmp/testfile-marker-runtime; do sleep 0.1; done; test ! -f /run/inaccessible/testfile-marker-fixed'
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-non-namespaced.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-non-namespaced.service
new file mode 100644 (file)
index 0000000..699b608
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+RuntimeMaxSec=5
+Type=notify
+RemainAfterExit=yes
+ExecStart=sh -c 'systemd-notify --ready; until grep -q -F MARKER_RUNTIME /tmp/testfile-marker-runtime; do sleep 0.1; done; exit 0'
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-oneshot-restartforce.sh b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-oneshot-restartforce.sh
new file mode 100755 (executable)
index 0000000..4c9e10b
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if [[ -f "$1" ]]; then
+    exit 0
+fi
+
+touch "$1"
+exit 2
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server.socket b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server.socket
new file mode 100644 (file)
index 0000000..c8f7595
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=OpenFile= test server socket
+
+[Socket]
+ListenStream=/tmp/test.sock
+Accept=yes
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server@.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server@.service
new file mode 100644 (file)
index 0000000..a57c804
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=OpenFile= test server service
+
+[Service]
+ExecStart=echo "Socket"
+StandardInput=socket
+StandardOutput=socket
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-one.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-one.service
new file mode 100644 (file)
index 0000000..0739aaf
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Stop Propagation Receiver
+Wants=TEST-23-UNIT-FILE-prop-stop-two.service
+After=TEST-23-UNIT-FILE-prop-stop-two.service
+StopPropagatedFrom=TEST-23-UNIT-FILE-prop-stop-two.service
+
+[Service]
+ExecStart=sleep infinity
+ExecStopPost=systemctl kill --kill-whom=main -sUSR2 TEST-23-UNIT-FILE.service
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-two.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-two.service
new file mode 100644 (file)
index 0000000..b2bb869
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Stop Propagation Sender
+
+[Service]
+ExecStart=sleep 1.5
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-fail.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-fail.service
new file mode 100644 (file)
index 0000000..319757a
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Failed Dependency Unit
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=sh -c "if [ -f /tmp/TEST-23-UNIT-FILE-retry-fail ]; then exit 0; else exit 1; fi"
+Restart=no
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-upheld.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-upheld.service
new file mode 100644 (file)
index 0000000..722a3b4
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Upheld Unit
+Requires=TEST-23-UNIT-FILE-retry-fail.service
+After=TEST-23-UNIT-FILE-retry-fail.service
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=echo ok
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-uphold.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-uphold.service
new file mode 100644 (file)
index 0000000..829ea5c
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Upholding Unit
+Upholds=TEST-23-UNIT-FILE-retry-upheld.service
+
+[Service]
+ExecStart=sleep infinity
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-short-lived.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-short-lived.service
new file mode 100644 (file)
index 0000000..ac9583f
--- /dev/null
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Shortlived Unit
+StopWhenUnneeded=yes
+
+# Bump up the start limit logic, so that we can be restarted frequently enough
+StartLimitBurst=15
+StartLimitIntervalSec=1h
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/TEST-23-UNIT-FILE-short-lived.sh
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-depends-wants.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-depends-wants.service
new file mode 100644 (file)
index 0000000..c45edd9
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Dependent service for percent-j specifier
+
+[Service]
+Type=oneshot
+ExecStart=touch /tmp/test-specifier-j-wants
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-wants.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-wants.service
new file mode 100644 (file)
index 0000000..5aeee9f
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Wants with percent-j specifier
+Wants=TEST-23-UNIT-FILE-specifier-j-depends-%j.service
+After=TEST-23-UNIT-FILE-specifier-j-depends-%j.service
+
+[Service]
+Type=oneshot
+ExecStart=test -f /tmp/test-specifier-j-%j
+ExecStart=touch /tmp/tetsuite-23-specifier-j-done
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-success.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-success.service
new file mode 100644 (file)
index 0000000..1ae6271
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Succeeding unit
+OnSuccess=TEST-23-UNIT-FILE-fail.service
+
+[Service]
+ExecStart=true
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-upheldby-install.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-upheldby-install.service
new file mode 100644 (file)
index 0000000..edb8fcb
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Unit that sets UpheldBy= through [Install]
+
+[Service]
+ExecStart=sleep infinity
+
+[Install]
+UpheldBy=TEST-23-UNIT-FILE-retry-uphold.service
diff --git a/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-uphold.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-uphold.service
new file mode 100644 (file)
index 0000000..36c5dae
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Upholding Unit
+Upholds=TEST-23-UNIT-FILE-short-lived.service
+
+[Service]
+ExecStart=sleep infinity
index 8dec5f37e73a80d31833a772fa16883b02a07615..3f44662ae3a9bfcb3df079651c7c49edf28e4d5d 100644 (file)
@@ -5,3 +5,5 @@ integration_tests += [
                 'name' : fs.name(meson.current_source_dir()),
         },
 ]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-23-UNIT-FILE.units']
diff --git a/test/TEST-30-ONCLOCKCHANGE/TEST-30-ONCLOCKCHANGE.units/systemd-timedated.service.d/watchdog.conf b/test/TEST-30-ONCLOCKCHANGE/TEST-30-ONCLOCKCHANGE.units/systemd-timedated.service.d/watchdog.conf
new file mode 100644 (file)
index 0000000..e2e25d5
--- /dev/null
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Service]
+WatchdogSec=10min
index 8dec5f37e73a80d31833a772fa16883b02a07615..174ec42867f0a79fe31d981846329373d9957b6f 100644 (file)
@@ -5,3 +5,5 @@ integration_tests += [
                 'name' : fs.name(meson.current_source_dir()),
         },
 ]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-30-ONCLOCKCHANGE.units']
diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.service b/test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.service
new file mode 100644 (file)
index 0000000..bbeac3a
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Honor First Shutdown feature
+After=multi-user.target
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-52-HONORFIRSTSHUTDOWN.units/%N.sh
+ExecStop=sh -c 'kill -KILL $MAINPID'
+FailureAction=reboot
+
+[Install]
+WantedBy=multi-user.target
diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.sh b/test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.sh
new file mode 100755 (executable)
index 0000000..0cb574f
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+echo "Honor first shutdown test script"
+sleep infinity;
index 8dec5f37e73a80d31833a772fa16883b02a07615..08503e882214a321589075abb50d2cb54716b21a 100644 (file)
@@ -5,3 +5,5 @@ integration_tests += [
                 'name' : fs.name(meson.current_source_dir()),
         },
 ]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-52-HONORFIRSTSHUTDOWN.units']
diff --git a/test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.path b/test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.path
new file mode 100644 (file)
index 0000000..5f237a9
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Path]
+PathExistsGlob=/tmp/test63-glob*
diff --git a/test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.service
new file mode 100644 (file)
index 0000000..3f49dd4
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+ExecStartPre=sh -c 'test "$TRIGGER_PATH" = /tmp/test63-glob-foo'
+ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = test63-glob.path'
+ExecStart=systemd-notify --ready
+RemainAfterExit=yes
+Type=notify
diff --git a/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577-dep.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577-dep.service
new file mode 100644 (file)
index 0000000..e332ea4
--- /dev/null
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+ExecStart=bash -c 'sleep infinity'
diff --git a/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.path b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.path
new file mode 100644 (file)
index 0000000..80ba1db
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Path]
+PathExists=/tmp/hoge
diff --git a/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.service
new file mode 100644 (file)
index 0000000..568518b
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=test63-issue-24577-dep.service
+After=test63-issue-24577-dep.service
+
+[Service]
+Type=oneshot
+ExecStart=bash -c 'sleep infinity'
diff --git a/test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.path b/test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.path
new file mode 100644 (file)
index 0000000..b541358
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Path]
+PathChanged=/tmp/copyme
diff --git a/test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.service
new file mode 100644 (file)
index 0000000..5739084
--- /dev/null
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+ExecStart=cp -v /tmp/copyme /tmp/copied
+# once cp exits, service goes into deactivating state and then runs ExecStop
+ExecStop=flock -e /tmp/noexit true
diff --git a/test/TEST-63-PATH/TEST-63-PATH.units/test63.path b/test/TEST-63-PATH/TEST-63-PATH.units/test63.path
new file mode 100644 (file)
index 0000000..64d5ed6
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Path]
+PathExists=/tmp/test63
+# Make the unit friendly to slower machines
+TriggerLimitIntervalSec=10
+TriggerLimitBurst=10
diff --git a/test/TEST-63-PATH/TEST-63-PATH.units/test63.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63.service
new file mode 100644 (file)
index 0000000..01a928b
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+ConditionPathExists=/tmp/nonexistent
+
+[Service]
+ExecStartPre=sh -c 'test "$TRIGGER_PATH" = /tmp/test63'
+ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = test63.path'
+ExecStart=true
index 8dec5f37e73a80d31833a772fa16883b02a07615..4aa3afdf7a39656cdd387a53d35396204fde4f37 100644 (file)
@@ -5,3 +5,5 @@ integration_tests += [
                 'name' : fs.name(meson.current_source_dir()),
         },
 ]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-63-PATH.units']
diff --git a/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-nopin.service b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-nopin.service
new file mode 100644 (file)
index 0000000..f658853
--- /dev/null
@@ -0,0 +1,6 @@
+[Service]
+Type=notify
+NotifyAccess=all
+FileDescriptorStoreMax=10
+FileDescriptorStorePreserve=restart
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh 0
diff --git a/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.service b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.service
new file mode 100644 (file)
index 0000000..393b5ac
--- /dev/null
@@ -0,0 +1,6 @@
+[Service]
+Type=notify
+NotifyAccess=all
+FileDescriptorStoreMax=10
+FileDescriptorStorePreserve=yes
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh 1
diff --git a/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh
new file mode 100755 (executable)
index 0000000..4cb041a
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+PINNED="$1"
+COUNTER="/tmp/fdstore-invoked.$PINNED"
+FILE="/tmp/fdstore-data.$PINNED"
+
+# This script is called six times: thrice from a service unit where the fdstore
+# is pinned, and thrice where it isn't. The second iteration of each series is
+# a restart, the third a stop followed by a start
+
+if [ -e "$COUNTER" ] ; then
+    read -r N < "$COUNTER"
+else
+    N=0
+fi
+
+echo "Invocation #$N with PINNED=$PINNED."
+
+if [ "$N" -eq 0 ] ; then
+    # First iteration
+    test "${LISTEN_FDS:-0}" -eq 0
+    test ! -e "$FILE"
+    echo waldi > "$FILE"
+    systemd-notify --fd=3 --fdname="fd-$N-$PINNED" 3< "$FILE"
+elif [ "$N" -eq 1 ] || { [ "$N" -eq 2 ] && [ "$PINNED" -eq 1 ]; } ; then
+    # Second iteration, or iteration with pinning on
+    test "${LISTEN_FDS:-0}" -eq 1
+    # We reopen fd #3 here, so that the read offset is at zero each time (hence no <&3 here…)
+    read -r word < /proc/self/fd/3
+    test "$word" = "waldi"
+else
+    test "${LISTEN_FDS:-0}" -eq 0
+    test -e "$FILE"
+fi
+
+if [ "$N" -ge 2 ] ; then
+    rm "$COUNTER" "$FILE"
+else
+    echo $((N + 1)) > "$COUNTER"
+fi
+
+systemd-notify --ready --status="Ready"
+
+exec sleep infinity
diff --git a/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.target b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.target
new file mode 100644 (file)
index 0000000..319b7e1
--- /dev/null
@@ -0,0 +1,3 @@
+[Unit]
+After=fdstore-pin.service fdstore-nopin.service
+Wants=fdstore-pin.service fdstore-nopin.service
diff --git a/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify.service b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify.service
new file mode 100644 (file)
index 0000000..5693be6
--- /dev/null
@@ -0,0 +1,4 @@
+[Service]
+Type=notify
+NotifyAccess=all
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/test.sh
diff --git a/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/test.sh b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/test.sh
new file mode 100755 (executable)
index 0000000..565ed8d
--- /dev/null
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+sync_in() {
+    read -r x < /tmp/syncfifo2
+    test "$x" = "$1"
+}
+
+sync_out() {
+    echo "$1" > /tmp/syncfifo1
+}
+
+export SYSTEMD_LOG_LEVEL=debug
+
+echo "toplevel PID: $BASHPID"
+
+systemd-notify --status="Test starts"
+sync_out a
+sync_in b
+(
+    echo "subshell PID: $BASHPID"
+
+    # Make us main process
+    systemd-notify --pid="$BASHPID"
+
+    # Lock down access to just us
+    systemd-notify "NOTIFYACCESS=main"
+
+    # This should still work
+    systemd-notify --status="Sending READY=1 in an unprivileged process"
+
+    # Send as subprocess of the subshell, this should not work
+    systemd-notify --ready --pid=self --status "BOGUS1"
+
+    sync_out c
+    sync_in d
+
+    # Move main process back to toplevel
+    systemd-notify --pid=parent "MAINPID=$$"
+
+    # Should be dropped again
+    systemd-notify --status="BOGUS2" --pid=parent
+
+    # Apparently, bash will automatically invoke the last command in a subshell
+    # via a simple execve() rather than fork()ing first. But we want that the
+    # previous command uses the subshell's PID, hence let's insert a final,
+    # bogus redundant command as last command to run in the subshell, so that
+    # bash can't optimize things like that.
+    echo "bye"
+)
+
+echo "toplevel again: $BASHPID"
+
+systemd-notify --ready --status="OK"
+systemd-notify "NOTIFYACCESS=none"
+systemd-notify --status="BOGUS3"
+
+sync_out e
+
+exec sleep infinity
index 8dec5f37e73a80d31833a772fa16883b02a07615..f78c6fd732ec4cef1e8bc1276dd2c8208f860937 100644 (file)
@@ -5,3 +5,5 @@ integration_tests += [
                 'name' : fs.name(meson.current_source_dir()),
         },
 ]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-80-NOTIFYACCESS.units']
index c41ca8e55a0b637c2ae09422a99771daf1d96763..6e2c0aede72790c0a58485f239282190cb556f6a 100755 (executable)
@@ -43,7 +43,6 @@ def main():
     parser.add_argument('--meson-source-dir', required=True, type=Path)
     parser.add_argument('--meson-build-dir', required=True, type=Path)
     parser.add_argument('--test-name', required=True)
-    parser.add_argument('--test-number', required=True)
     parser.add_argument('--storage', required=True)
     parser.add_argument('--firmware', required=True)
     parser.add_argument('--slow', action=argparse.BooleanOptionalAction)
@@ -59,7 +58,7 @@ def main():
         exit(77)
 
     name = args.test_name + (f"-{i}" if (i := os.getenv("MESON_TEST_ITERATION")) else "")
-    test_unit = f"testsuite-{args.test_number}.service"
+    test_unit = f"{args.test_name}.service"
 
     dropin = textwrap.dedent(
         """\
@@ -130,7 +129,7 @@ def main():
         '--kernel-command-line-extra',
         ' '.join([
             'systemd.hostname=H',
-            f"SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-{args.test_number}.units:/usr/lib/systemd/tests/testdata/units:",
+            f"SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/{args.test_name}.units:/usr/lib/systemd/tests/testdata/units:",
             f"systemd.unit={test_unit}",
             'systemd.mask=systemd-networkd-wait-online.service',
             *(
index 37c6a671ad8285c173348e31588f6aa06d492925..26fbd562829a9900f6359e778b15b78f2ecefaf2 100644 (file)
@@ -1,75 +1,5 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
-if install_tests
-        foreach subdir : [
-                'auxv',
-                'journal-data',
-                'knot-data',
-                'test-journals',
-                'units',
-                'test-execute',
-                'test-fstab-generator',
-                'test-path',
-                'test-path-util',
-                'test-umount',
-                'test-network',
-                'test-network-generator-conversion',
-                'testsuite-03.units',
-                'testsuite-04.units',
-                'testsuite-06.units',
-                'testsuite-07.units',
-                'testsuite-16.units',
-                'testsuite-23.units',
-                'testsuite-30.units',
-                'testsuite-52.units',
-                'testsuite-63.units',
-                'testsuite-80.units',
-        ]
-                # install_subdir() before meson 1.3.0 does not handle symlinks correctly (it follows them
-                # instead of copying the symlink) so we use rsync instead.
-                if meson.version().version_compare('<1.3.0')
-                        if not rsync.found()
-                                error('rsync is required to install the integration test data')
-                        endif
-
-                        rsync_r = rsync.full_path() + ' -rlpt --exclude .gitattributes --exclude 25-default.link -- "@0@" "${DESTDIR:-}@1@"'
-                        meson.add_install_script(sh, '-c',
-                                                 rsync_r.format(meson.current_source_dir() / subdir, testdata_dir))
-                else
-                        install_subdir(subdir,
-                                       exclude_files : ['.gitattributes', '25-default.link'],
-                                       install_dir : testdata_dir,
-                                       follow_symlinks : false)
-                endif
-        endforeach
-
-        # test-network/conf/25-default.link is a local symlink that becomes dangling when installed, so we
-        # exclude it and create the correct symlink here.
-        meson.add_install_script(sh, '-c', ln_s.format(networkdir / '99-default.link',
-                                                       testdata_dir / 'test-network/conf/25-default.link'))
-
-        install_data(kbd_model_map,
-                     install_dir : testdata_dir + '/test-keymap-util')
-
-        if conf.get('HAVE_ZSTD') == 1 and efi_arch != ''
-                install_subdir('test-bcd',
-                               exclude_files : '.gitattributes',
-                               install_dir : testdata_dir)
-        endif
-        if conf.get('ENABLE_RESOLVE') == 1
-                install_subdir('test-resolve',
-                               exclude_files : '.gitattributes',
-                               install_dir : testdata_dir)
-        endif
-
-        # The unit tests implemented as shell scripts expect to find testdata/
-        # in the directory where they are stored.
-        meson.add_install_script(sh, '-c', ln_s.format(testdata_dir,
-                                                       unittestsdir / 'testdata'))
-endif
-
-############################################################
-
 if conf.get('ENABLE_SYSUSERS') == 1
         test_sysusers_sh = configure_file(
                 input : 'test-sysusers.sh.in',
@@ -353,6 +283,20 @@ integration_test_template = {
         'firmware' : 'linux',
         'slow' : false,
 }
+testdata_subdirs = [
+        'auxv',
+        'journal-data',
+        'knot-data',
+        'test-journals',
+        'units',
+        'test-execute',
+        'test-fstab-generator',
+        'test-path',
+        'test-path-util',
+        'test-umount',
+        'test-network',
+        'test-network-generator-conversion',
+]
 
 foreach dirname : [
         'TEST-01-BASIC',
@@ -423,13 +367,10 @@ foreach dirname : [
 endforeach
 
 foreach integration_test : integration_tests
-        integration_test_number = integration_test['name'].split('-')[1]
-
         integration_test_args = [
                 '--meson-source-dir', meson.project_source_root(),
                 '--meson-build-dir', meson.project_build_root(),
                 '--test-name', integration_test['name'],
-                '--test-number', integration_test_number,
                 '--storage', integration_test['storage'],
                 '--firmware', integration_test['firmware'],
         ]
@@ -463,3 +404,48 @@ foreach integration_test : integration_tests
                 suite : 'integration-tests',
         )
 endforeach
+
+if install_tests
+        foreach subdir : testdata_subdirs
+                # install_subdir() before meson 1.3.0 does not handle symlinks correctly (it follows them
+                # instead of copying the symlink) so we use rsync instead.
+                if meson.version().version_compare('<1.3.0')
+                        if not rsync.found()
+                                error('rsync is required to install the integration test data')
+                        endif
+
+                        rsync_r = rsync.full_path() + ' -rlpt --exclude .gitattributes --exclude 25-default.link -- "@0@" "${DESTDIR:-}@1@"'
+                        meson.add_install_script(sh, '-c',
+                                                 rsync_r.format(meson.current_source_dir() / subdir, testdata_dir))
+                else
+                        install_subdir(subdir,
+                                       exclude_files : ['.gitattributes', '25-default.link'],
+                                       install_dir : testdata_dir,
+                                       follow_symlinks : false)
+                endif
+        endforeach
+
+        # test-network/conf/25-default.link is a local symlink that becomes dangling when installed, so we
+        # exclude it and create the correct symlink here.
+        meson.add_install_script(sh, '-c', ln_s.format(networkdir / '99-default.link',
+                                                       testdata_dir / 'test-network/conf/25-default.link'))
+
+        install_data(kbd_model_map,
+                     install_dir : testdata_dir + '/test-keymap-util')
+
+        if conf.get('HAVE_ZSTD') == 1 and efi_arch != ''
+                install_subdir('test-bcd',
+                               exclude_files : '.gitattributes',
+                               install_dir : testdata_dir)
+        endif
+        if conf.get('ENABLE_RESOLVE') == 1
+                install_subdir('test-resolve',
+                               exclude_files : '.gitattributes',
+                               install_dir : testdata_dir)
+        endif
+
+        # The unit tests implemented as shell scripts expect to find testdata/
+        # in the directory where they are stored.
+        meson.add_install_script(sh, '-c', ln_s.format(testdata_dir,
+                                                       unittestsdir / 'testdata'))
+endif
index 6e889f838b51c70c92db0cb8c9ddb12342b89d68..2168854addfc8a4c600213957b1995ba6ef00e21 100644 (file)
@@ -146,10 +146,9 @@ if ! [[ "$TESTNAME" =~ ^TEST\-([0-9]+)\-.+$ ]]; then
     echo "ERROR: Test name '$TESTNAME' is not in the expected format: TEST-[0-9]+-*" >&2
     exit 1
 fi
-TESTID="${BASH_REMATCH[1]:?}"
 
-if [[ ! -f "$TEST_UNITS_DIR/testsuite-$TESTID.service" ]]; then
-    echo "ERROR: Test '$TESTNAME' is missing its service file '$TEST_UNITS_DIR/testsuite-$TESTID.service" >&2
+if [[ ! -f "$TEST_UNITS_DIR/$TESTNAME.service" ]]; then
+    echo "ERROR: Test '$TESTNAME' is missing its service file '$TEST_UNITS_DIR/$TESTNAME.service" >&2
     exit 1
 fi
 
@@ -566,9 +565,9 @@ run_qemu() {
         "loglevel=2"
         "init=$PATH_TO_INIT"
         "console=$CONSOLE"
-        "SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$1.units:/usr/lib/systemd/tests/testdata/units:"
+        "SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/$1.units:/usr/lib/systemd/tests/testdata/units:"
         "systemd.unit=testsuite.target"
-        "systemd.wants=testsuite-$1.service"
+        "systemd.wants=$1.service"
         "noresume"
         "oops=panic"
         ${TEST_MATCH_SUBTEST:+"systemd.setenv=TEST_MATCH_SUBTEST=$TEST_MATCH_SUBTEST"}
@@ -658,13 +657,13 @@ run_nspawn() {
         "--register=no"
         "--kill-signal=SIGKILL"
         "--directory=${1:?}"
-        "--setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$2.units:/usr/lib/systemd/tests/testdata/units:"
-        "--machine=TEST-$TESTID"
+        "--setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/$2.units:/usr/lib/systemd/tests/testdata/units:"
+        "--machine=$2"
     )
     local kernel_params=(
         "$PATH_TO_INIT"
         "systemd.unit=testsuite.target"
-        "systemd.wants=testsuite-$2.service"
+        "systemd.wants=$2.service"
         ${TEST_MATCH_SUBTEST:+"systemd.setenv=TEST_MATCH_SUBTEST=$TEST_MATCH_SUBTEST"}
         ${TEST_MATCH_TESTCASE:+"systemd.setenv=TEST_MATCH_TESTCASE=$TEST_MATCH_TESTCASE"}
     )
@@ -1745,10 +1744,10 @@ save_journal() {
         dest_name="system.journal"
     fi
 
-    # Show messages from the testsuite-XX.service or messages with priority "warning" and higher
+    # Show messages from the TEST-XX-XXX.service or messages with priority "warning" and higher
     echo " --- $source_dir ---"
     "$JOURNALCTL" --all --no-pager --no-hostname -o short-monotonic -D "$source_dir" \
-        _SYSTEMD_UNIT="testsuite-${TESTID:?}.service" + SYSLOG_IDENTIFIER="testsuite-$TESTID.sh" + \
+        _SYSTEMD_UNIT="${TESTNAME:?}.service" + SYSLOG_IDENTIFIER="$TESTNAME.sh" + \
         PRIORITY=4 + PRIORITY=3 + PRIORITY=2 + PRIORITY=1 + PRIORITY=0
 
     if get_bool "$save"; then
@@ -3325,11 +3324,11 @@ test_setup() {
 }
 
 test_run() {
-    local test_id="${1:?}"
+    local test_name="${1:?}"
     mount_initdir
 
     if ! get_bool "${TEST_NO_QEMU:=}"; then
-        if run_qemu "$test_id"; then
+        if run_qemu "$test_name"; then
             check_result_qemu || { echo "qemu test failed"; return 1; }
         else
             dwarn "can't run qemu, skipping"
@@ -3337,7 +3336,7 @@ test_run() {
     fi
     if ! get_bool "${TEST_NO_NSPAWN:=}"; then
         mount_initdir
-        if run_nspawn "${initdir:?}" "$test_id"; then
+        if run_nspawn "${initdir:?}" "$test_name"; then
             check_result_nspawn "$initdir" || { echo "nspawn-root test failed"; return 1; }
         else
             dwarn "can't run systemd-nspawn, skipping"
@@ -3345,7 +3344,7 @@ test_run() {
 
         if get_bool "${RUN_IN_UNPRIVILEGED_CONTAINER:=}"; then
             dir="$TESTDIR/unprivileged-nspawn-root"
-            if NSPAWN_ARGUMENTS="-U --private-network ${NSPAWN_ARGUMENTS:-}" run_nspawn "$dir" "$test_id"; then
+            if NSPAWN_ARGUMENTS="-U --private-network ${NSPAWN_ARGUMENTS:-}" run_nspawn "$dir" "$test_name"; then
                 check_result_nspawn "$dir" || { echo "unprivileged-nspawn-root test failed"; return 1; }
             else
                 dwarn "can't run systemd-nspawn, skipping"
@@ -3401,7 +3400,7 @@ do_test() {
         case $1 in
             --run)
                 echo "${testname} RUN: $TEST_DESCRIPTION"
-                test_run "$TESTID"
+                test_run "$TESTNAME"
                 ret=$?
                 if [ $ret -eq 0 ]; then
                     echo "${testname} RUN: $TEST_DESCRIPTION [OK]"
@@ -3433,7 +3432,7 @@ do_test() {
                     test_setup_cleanup </dev/null >>"$TESTLOG" 2>&1 || ret=$?
                 fi
                 if [ $ret -eq 0 ]; then
-                    test_run "$TESTID" </dev/null >>"$TESTLOG" 2>&1 || ret=$?
+                    test_run "$TESTNAME" </dev/null >>"$TESTLOG" 2>&1 || ret=$?
                 fi
                 test_cleanup
                 if [ $ret -eq 0 ]; then
diff --git a/test/testsuite-03.units/always-activating.service b/test/testsuite-03.units/always-activating.service
deleted file mode 100644 (file)
index 93ddb85..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Unit]
-Description=Service that never leaves state ACTIVATING
-Requires=always-activating.socket
-After=always-activating.socket
-
-[Service]
-Type=notify
-ExecStart=bash -c 'sleep infinity'
diff --git a/test/testsuite-03.units/always-activating.socket b/test/testsuite-03.units/always-activating.socket
deleted file mode 100644 (file)
index c76fed2..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Unit]
-Description=Socket that activates always-activating.service
-
-[Socket]
-ListenStream=/run/test-03-always-activating.sock
diff --git a/test/testsuite-03.units/fails-on-restart-restartdirect.service b/test/testsuite-03.units/fails-on-restart-restartdirect.service
deleted file mode 100644 (file)
index 60ffd7a..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Fail on restart
-StartLimitIntervalSec=1m
-StartLimitBurst=3
-
-[Service]
-Type=oneshot
-ExecStart=false
-Restart=on-failure
-RestartMode=direct
diff --git a/test/testsuite-03.units/fails-on-restart-restartdirect.target b/test/testsuite-03.units/fails-on-restart-restartdirect.target
deleted file mode 100644 (file)
index 58e2561..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=fails-on-restart-restartdirect.service
diff --git a/test/testsuite-03.units/fails-on-restart.service b/test/testsuite-03.units/fails-on-restart.service
deleted file mode 100644 (file)
index fb7e7ae..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Fail on restart
-StartLimitIntervalSec=1m
-StartLimitBurst=3
-
-[Service]
-Type=oneshot
-ExecStart=false
-Restart=on-failure
-RestartMode=normal
diff --git a/test/testsuite-03.units/fails-on-restart.target b/test/testsuite-03.units/fails-on-restart.target
deleted file mode 100644 (file)
index 865fb2a..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=fails-on-restart.service
diff --git a/test/testsuite-03.units/hello-after-sleep.target b/test/testsuite-03.units/hello-after-sleep.target
deleted file mode 100644 (file)
index b0ddb30..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Sleep for a minute, then say hello.
-Wants=sleep.service hello.service
-After=sleep.service
-Before=hello.service
diff --git a/test/testsuite-03.units/hello.service b/test/testsuite-03.units/hello.service
deleted file mode 100644 (file)
index 0c3f2f8..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Hello World
-
-[Service]
-ExecStart=/bin/echo "Hello World"
diff --git a/test/testsuite-03.units/propagatestopto-and-pullin.target b/test/testsuite-03.units/propagatestopto-and-pullin.target
deleted file mode 100644 (file)
index 8e409af..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=sleep-infinity-simple.service
-After=sleep-infinity-simple.service
-PropagatesStopTo=sleep-infinity-simple.service
diff --git a/test/testsuite-03.units/propagatestopto-indirect.target b/test/testsuite-03.units/propagatestopto-indirect.target
deleted file mode 100644 (file)
index e8229a7..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-PropagatesStopTo=propagatestopto-and-pullin.target
diff --git a/test/testsuite-03.units/propagatestopto-only.target b/test/testsuite-03.units/propagatestopto-only.target
deleted file mode 100644 (file)
index 327b7c1..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-PropagatesStopTo=sleep-infinity-simple.service
diff --git a/test/testsuite-03.units/sleep-infinity-simple.service b/test/testsuite-03.units/sleep-infinity-simple.service
deleted file mode 100644 (file)
index 211346d..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Sleep infinitely
-
-[Service]
-Type=simple
-ExecStart=/bin/sleep infinity
diff --git a/test/testsuite-03.units/sleep.service b/test/testsuite-03.units/sleep.service
deleted file mode 100644 (file)
index 32c2037..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Sleep for 1 minute
-
-[Service]
-Type=oneshot
-ExecStart=/bin/sleep 60
diff --git a/test/testsuite-03.units/succeeds-on-restart-restartdirect.service b/test/testsuite-03.units/succeeds-on-restart-restartdirect.service
deleted file mode 100644 (file)
index b05f2f8..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-03.units/succeeds-on-restart.sh
-Restart=on-failure
-RestartMode=direct
diff --git a/test/testsuite-03.units/succeeds-on-restart-restartdirect.target b/test/testsuite-03.units/succeeds-on-restart-restartdirect.target
deleted file mode 100644 (file)
index 2cf3c60..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=succeeds-on-restart-restartdirect.service
diff --git a/test/testsuite-03.units/succeeds-on-restart.service b/test/testsuite-03.units/succeeds-on-restart.service
deleted file mode 100644 (file)
index d7b3c7a..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-03.units/succeeds-on-restart.sh
-Restart=on-failure
-RestartMode=normal
diff --git a/test/testsuite-03.units/succeeds-on-restart.sh b/test/testsuite-03.units/succeeds-on-restart.sh
deleted file mode 100755 (executable)
index 1428b18..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-if [[ ! -f "/succeeds-on-restart.ko" ]]
-then
-    touch "/succeeds-on-restart.ko"
-    exit 1
-else
-    rm "/succeeds-on-restart.ko"
-    exit 0
-fi
diff --git a/test/testsuite-03.units/succeeds-on-restart.target b/test/testsuite-03.units/succeeds-on-restart.target
deleted file mode 100644 (file)
index eb82f47..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=succeeds-on-restart.service
diff --git a/test/testsuite-03.units/unstoppable.service b/test/testsuite-03.units/unstoppable.service
deleted file mode 100644 (file)
index 6eb7c19..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStart=/bin/echo "I'm unstoppable!"
-ExecStop=/bin/systemctl start --no-block unstoppable.service
diff --git a/test/testsuite-04.units/delegated-cgroup-filtering.service b/test/testsuite-04.units/delegated-cgroup-filtering.service
deleted file mode 100644 (file)
index a12b12a..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Unit]
-Description=Test service for delegated logs filtering
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/delegated_cgroup_filtering_payload.sh
-Delegate=yes
-SyslogLevel=notice
diff --git a/test/testsuite-04.units/forever-print-hola.service b/test/testsuite-04.units/forever-print-hola.service
deleted file mode 100644 (file)
index 9273d6d..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=ForeverPrintHola service
-
-[Service]
-Type=exec
-ExecStart=sh -x -c 'while :; do printf "Hola\n" || touch /tmp/i-lose-my-logs; sleep 1; done'
diff --git a/test/testsuite-04.units/logs-filtering.service b/test/testsuite-04.units/logs-filtering.service
deleted file mode 100644 (file)
index a5aba18..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-[Unit]
-Description=Log filtering unit
-
-[Service]
-Type=oneshot
-ExecStart=sh -c 'echo "Logging from the service, and ~more~ foo bar" && sleep 2'
-SyslogLevel=notice
diff --git a/test/testsuite-04.units/silent-success.service b/test/testsuite-04.units/silent-success.service
deleted file mode 100644 (file)
index 3d83f87..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Silent successful service
-
-[Service]
-LogLevelMax=notice
-ExecStart=/bin/true
diff --git a/test/testsuite-04.units/verbose-success.service b/test/testsuite-04.units/verbose-success.service
deleted file mode 100644 (file)
index f4a86fd..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Verbose successful service
-
-[Service]
-Type=oneshot
-# Sleep so that the cgroup is still there when journald processes the log message which is required for
-# journald to add the expected fields to the log message.
-ExecStart=sleep 2
-ExecStart=echo success
-ExecStart=sleep 2
diff --git a/test/testsuite-06.units/hola.service b/test/testsuite-06.units/hola.service
deleted file mode 100644 (file)
index 94f9b47..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=oneshot
-ExecStart=/bin/echo Start Hola
-ExecReload=/bin/echo Reload Hola
-ExecStop=/bin/echo Stop Hola
-RemainAfterExit=yes
diff --git a/test/testsuite-07.units/issue14566-repro.service b/test/testsuite-07.units/issue14566-repro.service
deleted file mode 100644 (file)
index 5680596..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Issue 14566 Repro
-
-[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-07.units/%N.sh
-ExecStopPost=/bin/true
-KillMode=mixed
diff --git a/test/testsuite-07.units/issue14566-repro.sh b/test/testsuite-07.units/issue14566-repro.sh
deleted file mode 100755 (executable)
index 74fa760..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-sleep infinity &
-echo $! >/leakedtestpid
-wait $!
diff --git a/test/testsuite-07.units/issue16115-repro-1.service b/test/testsuite-07.units/issue16115-repro-1.service
deleted file mode 100644 (file)
index 90252b3..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Issue 16115 Repro with on-abnormal
-
-[Service]
-Type=simple
-Restart=on-abnormal
-ExecCondition=/bin/false
-ExecStart=sleep 100
-RestartSec=1
diff --git a/test/testsuite-07.units/issue16115-repro-2.service b/test/testsuite-07.units/issue16115-repro-2.service
deleted file mode 100644 (file)
index 7c65691..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Issue 16115 Repro with on-failure
-
-[Service]
-Type=simple
-Restart=on-failure
-ExecCondition=/bin/false
-ExecStart=sleep 100
-RestartSec=1
diff --git a/test/testsuite-07.units/issue16115-repro-3.service b/test/testsuite-07.units/issue16115-repro-3.service
deleted file mode 100644 (file)
index c68f93d..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Issue 22257 Repro with Restart=always
-
-[Service]
-Type=simple
-Restart=always
-ExecCondition=/bin/false
-ExecStart=sleep 100
-RestartSec=1
diff --git a/test/testsuite-07.units/issue2467.service b/test/testsuite-07.units/issue2467.service
deleted file mode 100644 (file)
index 99d886f..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=issue2467.socket
-ConditionPathExistsGlob=/tmp/nonexistent
-# Make sure we hit the socket trigger limit in the test and not the service start limit.
-StartLimitInterval=1000
-StartLimitBurst=1000
-
-[Service]
-ExecStart=true
diff --git a/test/testsuite-07.units/issue2467.socket b/test/testsuite-07.units/issue2467.socket
deleted file mode 100644 (file)
index 209b6bb..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Socket]
-ListenStream=/run/test.ctl
-# We might not be fast enough to hit the default limit (20 triggers per 2 secs)
-# in certain environments, i.e. when running without KVM or when collecting
-# coverage. Let's help it a bit in such cases by lowering the limit to 10 seconds.
-TriggerLimitIntervalSec=10
diff --git a/test/testsuite-07.units/issue2730-alias.mount b/test/testsuite-07.units/issue2730-alias.mount
deleted file mode 120000 (symlink)
index 802f026..0000000
+++ /dev/null
@@ -1 +0,0 @@
-issue2730.mount
\ No newline at end of file
diff --git a/test/testsuite-07.units/issue2730.mount b/test/testsuite-07.units/issue2730.mount
deleted file mode 100644 (file)
index 2ac76c0..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Mount]
-What=tmpfs
-Where=/issue2730
-Type=tmpfs
-
-[Install]
-WantedBy=local-fs.target
-Alias=issue2730-alias.mount
diff --git a/test/testsuite-07.units/issue27953.service b/test/testsuite-07.units/issue27953.service
deleted file mode 100644 (file)
index f441067..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=This unit should not remain active once the shell process exits
-
-[Service]
-Type=oneshot
-ExecStart=sh -c 'sleep infinity & exit 0'
diff --git a/test/testsuite-07.units/issue3166-fail-on-restart.service b/test/testsuite-07.units/issue3166-fail-on-restart.service
deleted file mode 100644 (file)
index b8695d8..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Fail on restart
-StartLimitIntervalSec=1m
-StartLimitBurst=3
-
-[Service]
-Type=exec
-ExecStart=false
-Restart=always
diff --git a/test/testsuite-07.units/local-fs.target.wants/issue2730.mount b/test/testsuite-07.units/local-fs.target.wants/issue2730.mount
deleted file mode 120000 (symlink)
index 70a8534..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../issue2730.mount
\ No newline at end of file
diff --git a/test/testsuite-07.units/pass-fds-to-exec-no.socket b/test/testsuite-07.units/pass-fds-to-exec-no.socket
deleted file mode 100644 (file)
index 8b7964b..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Test if ExecXYZ= commands don't inherit listen FDs when PassFileDescriptorsToExec= is unset
-
-[Socket]
-# With Accept= set we don't need a corresponding service unit
-Accept=yes
-FileDescriptorName=foo
-ListenStream=127.0.0.1:1234
-ListenStream=[::1]:1234
-PassFileDescriptorsToExec=no
-ExecStartPre=\
-        test ExecStartPre -a \
-             -z ${LISTEN_FDS} -a \
-             -z ${LISTEN_FDNAMES} -a \
-             ! -e /dev/fd/3 -a \
-             ! -e /dev/fd/4
-ExecStartPost=\
-        test ExecStartPost -a \
-             -z ${LISTEN_FDS} -a \
-             -z ${LISTEN_FDNAMES} -a \
-             ! -e /dev/fd/3 -a \
-             ! -e /dev/fd/4
-ExecStopPre=\
-        test ExecStopPre -a \
-             -z ${LISTEN_FDS} -a \
-             -z ${LISTEN_FDNAMES} -a \
-             ! -e /dev/fd/3 -a \
-             ! -e /dev/fd/4
-ExecStopPost=\
-        test ExecStopPost -a \
-             -z ${LISTEN_FDS} -a \
-             -z ${LISTEN_FDNAMES} -a \
-             ! -e /dev/fd/3 -a \
-             ! -e /dev/fd/4
diff --git a/test/testsuite-07.units/pass-fds-to-exec-yes.socket b/test/testsuite-07.units/pass-fds-to-exec-yes.socket
deleted file mode 100644 (file)
index bff192d..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Test if ExecXYZ= commands inherit listen FDs when PassFileDescriptorsToExec= is set
-
-[Socket]
-# With Accept= set we don't need a corresponding service unit
-Accept=yes
-FileDescriptorName=foo
-ListenStream=127.0.0.1:1234
-ListenStream=[::1]:1234
-PassFileDescriptorsToExec=yes
-# ExecStartPre runs before we create sockets. Nothing to pass.
-ExecStartPre=\
-        test ExecStartPre -a \
-             -z ${LISTEN_FDS} -a \
-             -z ${LISTEN_FDNAMES} -a \
-             ! -e /dev/fd/3 -a \
-             ! -e /dev/fd/4
-ExecStartPost=\
-        test ExecStartPost -a \
-             ${LISTEN_FDS} = 2 -a \
-             ${LISTEN_FDNAMES} = foo:foo -a \
-             -S /dev/fd/3 -a \
-             -S /dev/fd/4
-ExecStopPre=\
-        test "ExecStopPre" -a \
-             ${LISTEN_FDS} = 2 -a \
-             ${LISTEN_FDNAMES} = foo:foo -a \
-             -S /dev/fd/3 -a \
-             -S /dev/fd/4
-ExecStopPost=\
-        test "ExecStopPost" -a \
-             ${LISTEN_FDS} = 2 -a \
-             ${LISTEN_FDNAMES} = foo:foo -a \
-             -S /dev/fd/3 -a \
-             -S /dev/fd/4
diff --git a/test/testsuite-16.units/extend-timeout.sh b/test/testsuite-16.units/extend-timeout.sh
deleted file mode 100755 (executable)
index 45a18b9..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# sleep interval (seconds)
-sleep_interval="${sleep_interval:-1}"
-# extend_timeout_interval second(s)
-extend_timeout_interval="${extend_timeout_interval:-1}"
-# number of sleep_intervals before READY=1
-start_intervals="${start_intervals:-10}"
-# number of sleep_intervals before exiting
-stop_intervals="${stop_intervals:-10}"
-# run intervals, number of sleep_intervals to run
-run_intervals="${run_intervals:-7}"
-
-# We convert to usec
-extend_timeout_interval=$((extend_timeout_interval * 1000000))
-
-# shellcheck disable=SC2064
-trap "{ touch /${SERVICE}.terminated; exit 1; }" SIGTERM SIGABRT
-
-rm -f "/${SERVICE}".*
-touch "/${SERVICE}.startfail"
-
-systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
-while [[ $start_intervals -gt 0 ]]
-do
-    sleep "$sleep_interval"
-    start_intervals=$((start_intervals - 1))
-    systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
-done
-
-systemd-notify --ready --status="Waiting for your request"
-
-touch "/${SERVICE}.runtimefail"
-rm "/${SERVICE}.startfail"
-
-systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
-while [[ $run_intervals -gt 0 ]]
-do
-    sleep "$sleep_interval"
-    run_intervals=$((run_intervals - 1))
-    systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
-done
-
-systemd-notify STOPPING=1
-
-touch "/${SERVICE}.stopfail"
-rm "/${SERVICE}.runtimefail"
-
-systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
-while [[ $stop_intervals -gt 0 ]]
-do
-    sleep "$sleep_interval"
-    stop_intervals=$((stop_intervals - 1))
-    systemd-notify EXTEND_TIMEOUT_USEC="$extend_timeout_interval"
-done
-
-touch "/${SERVICE}.success"
-rm "/${SERVICE}.stopfail"
-
-exit 0
diff --git a/test/testsuite-16.units/fail-runtime.service b/test/testsuite-16.units/fail-runtime.service
deleted file mode 100644 (file)
index 1d74963..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: Fail Runtime (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after RuntimeSecMax.)
-
-[Service]
-# EXTEND_TIMEOUT_USEC on runtime start (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval)
-# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=4
-RuntimeMaxSec=10
-Environment=SERVICE=fail_runtime extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=2 stop_intervals=0
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/fail-start.service b/test/testsuite-16.units/fail-start.service
deleted file mode 100644 (file)
index 38aebf2..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: Fail Start (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStartSec.)
-
-[Service]
-
-# EXTEND_TIMEOUT_USEC on startup and 7 seconds from start. Systemd will expect one at 7+5 (extend_timeout_interval)
-# seconds  this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
-Type=notify
-TimeoutStartSec=10
-TimeoutStopSec=4
-RuntimeMaxSec=4
-Environment=SERVICE=fail_start extend_timeout_interval=5 sleep_interval=7 start_intervals=2 run_intervals=0 stop_intervals=0
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/fail-stop.service b/test/testsuite-16.units/fail-stop.service
deleted file mode 100644 (file)
index 1910ac9..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: Fail Stop (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStopSec.)
-
-[Service]
-# EXTEND_TIMEOUT_USEC on stop (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval)
-# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=10
-RuntimeMaxSec=4
-Environment=SERVICE=fail_stop extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=0 stop_intervals=2
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
-# Due to 6041a7ee2c1bbff6301082f192fc1b0882400d42 SIGTERM isn't sent as the service shuts down with STOPPING=1
-# This file makes the test assess.sh quicker by notifing it that this test has finished.
-ExecStopPost=/bin/bash -c '[[ $SERVICE_RESULT == timeout && $EXIT_CODE == killed ]] && touch /fail_runtime.terminated'
diff --git a/test/testsuite-16.units/success-all.service b/test/testsuite-16.units/success-all.service
deleted file mode 100644 (file)
index 1fdc363..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: EXTEND_TIMEOUT_USEC Success - extend timeout on all services
-
-[Service]
-
-# Normal success - startup / runtime / shutdown all take 8 seconds which is within the EXTEND_TIMEOUT_USEC=4 seconds interval
-# runtime is 8+8+8 seconds. so we are relying on the EXTEND_TIMEOUT_USEC to exceed all stages, Start, Runtime and Stop.
-# success occurs after 24 seconds
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=4
-RuntimeMaxSec=4
-Environment=SERVICE=success_all extend_timeout_interval=4 sleep_interval=2 start_intervals=3 run_intervals=3 stop_intervals=3
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/success-runtime.service b/test/testsuite-16.units/success-runtime.service
deleted file mode 100644 (file)
index 2f5744d..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: Success Runtime (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < RuntimeMaxSec)
-
-[Service]
-
-# EXTEND_TIMEOUT_USEC=4 second once during runtime, but sleep for 6 seconds.
-# Runtime is 6 seconds and < RuntimeMaxSec so still successful.
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=4
-RuntimeMaxSec=8
-Environment=SERVICE=success_runtime extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=1 stop_intervals=0
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/success-start.service b/test/testsuite-16.units/success-start.service
deleted file mode 100644 (file)
index be518e9..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: Success Start (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStartSec)
-
-[Service]
-# EXTEND_TIMEOUT_USEC=4 second interval once at startup, but sleep 6 seconds.
-# Therefore startup is 6 seconds and < TimeoutStartSec so still successful.
-Type=notify
-TimeoutStartSec=8
-TimeoutStopSec=4
-RuntimeMaxSec=4
-Environment=SERVICE=success_start extend_timeout_interval=4 sleep_interval=6 start_intervals=1 run_intervals=0 stop_intervals=0
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/success-stop.service b/test/testsuite-16.units/success-stop.service
deleted file mode 100644 (file)
index 2bd3e3e..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite: Success Stop (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStopSec)
-
-[Service]
-# EXTEND_TIMEOUT_USEC=4 seconds once during shutdown, but sleep for 6 seconds.
-# Therefore stop time is 6 seconds and < TimeoutStopSec so still successful.
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=8
-RuntimeMaxSec=4
-Environment=SERVICE=success_stop extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=0 stop_intervals=1
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
diff --git a/test/testsuite-23.units/testsuite-23-binds-to.service b/test/testsuite-23.units/testsuite-23-binds-to.service
deleted file mode 100644 (file)
index 9a3e31b..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Unit with BindsTo=
-BindsTo=testsuite-23-bound-by.service
-After=testsuite-23-bound-by.service
-
-[Service]
-ExecStart=sleep infinity
-# --kill-who= (no 'm') to check that the short form is accepted
-ExecStopPost=systemctl kill --kill-whom=main -sRTMIN+1 testsuite-23.service
diff --git a/test/testsuite-23.units/testsuite-23-bound-by.service b/test/testsuite-23.units/testsuite-23-bound-by.service
deleted file mode 100644 (file)
index c999c2e..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Unit with BoundBy=
-
-[Service]
-ExecStart=sleep 0.7
diff --git a/test/testsuite-23.units/testsuite-23-fail.service b/test/testsuite-23.units/testsuite-23-fail.service
deleted file mode 100644 (file)
index 5978107..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Failing unit
-OnFailure=testsuite-23-uphold.service
-
-[Service]
-ExecStart=false
diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-1.service b/test/testsuite-23.units/testsuite-23-joins-namespace-of-1.service
deleted file mode 100644 (file)
index 47f0452..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=notify
-NotifyAccess=all
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-2.service b/test/testsuite-23.units/testsuite-23-joins-namespace-of-2.service
deleted file mode 100644 (file)
index 36b4c27..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-1.service
-
-[Service]
-Type=oneshot
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=test -e /tmp/shared-private-file
-ExecStart=touch /tmp/hoge
diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-3.service b/test/testsuite-23.units/testsuite-23-joins-namespace-of-3.service
deleted file mode 100644 (file)
index 9094445..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-1.service
-
-[Service]
-Type=oneshot
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=test -e /tmp/shared-private-file
-ExecStart=test -e /tmp/hoge
diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-4.service b/test/testsuite-23.units/testsuite-23-joins-namespace-of-4.service
deleted file mode 100644 (file)
index a70f8fc..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-5.service
-
-[Service]
-Type=notify
-NotifyAccess=all
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-5.service b/test/testsuite-23.units/testsuite-23-joins-namespace-of-5.service
deleted file mode 100644 (file)
index c3d316b..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=oneshot
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=test -e /tmp/shared-private-file
diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-6.service b/test/testsuite-23.units/testsuite-23-joins-namespace-of-6.service
deleted file mode 100644 (file)
index dae533c..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-8.service
-
-[Service]
-Type=notify
-NotifyAccess=all
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=bash -c 'touch /tmp/shared-private-file-x && systemd-notify --ready && sleep infinity'
diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-7.service b/test/testsuite-23.units/testsuite-23-joins-namespace-of-7.service
deleted file mode 100644 (file)
index 60c083a..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-8.service
-
-[Service]
-Type=oneshot
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=test -e /tmp/shared-private-file-x
-ExecStart=test ! -e /tmp/shared-private-file-y
-ExecStart=touch /tmp/hoge
diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-8.service b/test/testsuite-23.units/testsuite-23-joins-namespace-of-8.service
deleted file mode 100644 (file)
index 8e4944a..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=notify
-NotifyAccess=all
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStartPre=test -e /tmp/shared-private-file-x
-ExecStartPre=test -e /tmp/hoge
-ExecStart=bash -c 'touch /tmp/shared-private-file-y && systemd-notify --ready && sleep infinity'
diff --git a/test/testsuite-23.units/testsuite-23-joins-namespace-of-9.service b/test/testsuite-23.units/testsuite-23-joins-namespace-of-9.service
deleted file mode 100644 (file)
index 6c64873..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-8.service
-
-[Service]
-Type=oneshot
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=test -e /tmp/shared-private-file-x
-ExecStart=test -e /tmp/shared-private-file-y
-ExecStart=test -e /tmp/hoge
diff --git a/test/testsuite-23.units/testsuite-23-namespaced.service b/test/testsuite-23.units/testsuite-23-namespaced.service
deleted file mode 100644 (file)
index 46c27ca..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-RuntimeMaxSec=300
-# Adding a new mounts at runtime works if the unit is in the active state,
-# so use Type=notify to make sure there's no race condition in the test
-Type=notify
-RemainAfterExit=yes
-MountAPIVFS=yes
-PrivateTmp=yes
-BindPaths=/run/testsuite-23-marker-fixed:/tmp/testfile-marker-fixed
-InaccessiblePaths=/run/inaccessible
-ExecStartPre=grep -q -F MARKER_FIXED /tmp/testfile-marker-fixed
-ExecStart=sh -c 'systemd-notify --ready; until grep -q -F MARKER_RUNTIME /tmp/testfile-marker-runtime; do sleep 0.1; done; test ! -f /run/inaccessible/testfile-marker-fixed'
diff --git a/test/testsuite-23.units/testsuite-23-non-namespaced.service b/test/testsuite-23.units/testsuite-23-non-namespaced.service
deleted file mode 100644 (file)
index 699b608..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-RuntimeMaxSec=5
-Type=notify
-RemainAfterExit=yes
-ExecStart=sh -c 'systemd-notify --ready; until grep -q -F MARKER_RUNTIME /tmp/testfile-marker-runtime; do sleep 0.1; done; exit 0'
diff --git a/test/testsuite-23.units/testsuite-23-oneshot-restartforce.sh b/test/testsuite-23.units/testsuite-23-oneshot-restartforce.sh
deleted file mode 100755 (executable)
index 4c9e10b..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if [[ -f "$1" ]]; then
-    exit 0
-fi
-
-touch "$1"
-exit 2
diff --git a/test/testsuite-23.units/testsuite-23-openfile-server.socket b/test/testsuite-23.units/testsuite-23-openfile-server.socket
deleted file mode 100644 (file)
index c8f7595..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=OpenFile= test server socket
-
-[Socket]
-ListenStream=/tmp/test.sock
-Accept=yes
diff --git a/test/testsuite-23.units/testsuite-23-openfile-server@.service b/test/testsuite-23.units/testsuite-23-openfile-server@.service
deleted file mode 100644 (file)
index a57c804..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-[Unit]
-Description=OpenFile= test server service
-
-[Service]
-ExecStart=echo "Socket"
-StandardInput=socket
-StandardOutput=socket
diff --git a/test/testsuite-23.units/testsuite-23-prop-stop-one.service b/test/testsuite-23.units/testsuite-23-prop-stop-one.service
deleted file mode 100644 (file)
index 0318955..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Stop Propagation Receiver
-Wants=testsuite-23-prop-stop-two.service
-After=testsuite-23-prop-stop-two.service
-StopPropagatedFrom=testsuite-23-prop-stop-two.service
-
-[Service]
-ExecStart=sleep infinity
-ExecStopPost=systemctl kill --kill-whom=main -sUSR2 testsuite-23.service
diff --git a/test/testsuite-23.units/testsuite-23-prop-stop-two.service b/test/testsuite-23.units/testsuite-23-prop-stop-two.service
deleted file mode 100644 (file)
index b2bb869..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Stop Propagation Sender
-
-[Service]
-ExecStart=sleep 1.5
diff --git a/test/testsuite-23.units/testsuite-23-retry-fail.service b/test/testsuite-23.units/testsuite-23-retry-fail.service
deleted file mode 100644 (file)
index 2e0972c..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Failed Dependency Unit
-
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStart=sh -c "if [ -f /tmp/testsuite-23-retry-fail ]; then exit 0; else exit 1; fi"
-Restart=no
diff --git a/test/testsuite-23.units/testsuite-23-retry-upheld.service b/test/testsuite-23.units/testsuite-23-retry-upheld.service
deleted file mode 100644 (file)
index 3c20e43..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Upheld Unit
-Requires=testsuite-23-retry-fail.service
-After=testsuite-23-retry-fail.service
-
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStart=echo ok
diff --git a/test/testsuite-23.units/testsuite-23-retry-uphold.service b/test/testsuite-23.units/testsuite-23-retry-uphold.service
deleted file mode 100644 (file)
index 7f15f06..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Upholding Unit
-Upholds=testsuite-23-retry-upheld.service
-
-[Service]
-ExecStart=sleep infinity
diff --git a/test/testsuite-23.units/testsuite-23-short-lived.service b/test/testsuite-23.units/testsuite-23-short-lived.service
deleted file mode 100644 (file)
index 2dcb2ae..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Shortlived Unit
-StopWhenUnneeded=yes
-
-# Bump up the start limit logic, so that we can be restarted frequently enough
-StartLimitBurst=15
-StartLimitIntervalSec=1h
-
-[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/units/testsuite-23-short-lived.sh
diff --git a/test/testsuite-23.units/testsuite-23-specifier-j-depends-wants.service b/test/testsuite-23.units/testsuite-23-specifier-j-depends-wants.service
deleted file mode 100644 (file)
index c45edd9..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Dependent service for percent-j specifier
-
-[Service]
-Type=oneshot
-ExecStart=touch /tmp/test-specifier-j-wants
diff --git a/test/testsuite-23.units/testsuite-23-specifier-j-wants.service b/test/testsuite-23.units/testsuite-23-specifier-j-wants.service
deleted file mode 100644 (file)
index 9abb257..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Wants with percent-j specifier
-Wants=testsuite-23-specifier-j-depends-%j.service
-After=testsuite-23-specifier-j-depends-%j.service
-
-[Service]
-Type=oneshot
-ExecStart=test -f /tmp/test-specifier-j-%j
-ExecStart=touch /tmp/tetsuite-23-specifier-j-done
diff --git a/test/testsuite-23.units/testsuite-23-success.service b/test/testsuite-23.units/testsuite-23-success.service
deleted file mode 100644 (file)
index 2ace6fa..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Succeeding unit
-OnSuccess=testsuite-23-fail.service
-
-[Service]
-ExecStart=true
diff --git a/test/testsuite-23.units/testsuite-23-upheldby-install.service b/test/testsuite-23.units/testsuite-23-upheldby-install.service
deleted file mode 100644 (file)
index bcfacd2..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Unit that sets UpheldBy= through [Install]
-
-[Service]
-ExecStart=sleep infinity
-
-[Install]
-UpheldBy=testsuite-23-retry-uphold.service
diff --git a/test/testsuite-23.units/testsuite-23-uphold.service b/test/testsuite-23.units/testsuite-23-uphold.service
deleted file mode 100644 (file)
index 67f0ac8..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Upholding Unit
-Upholds=testsuite-23-short-lived.service
-
-[Service]
-ExecStart=sleep infinity
diff --git a/test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf b/test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf
deleted file mode 100644 (file)
index e2e25d5..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Service]
-WatchdogSec=10min
diff --git a/test/testsuite-52.units/test-honor-first-shutdown.service b/test/testsuite-52.units/test-honor-first-shutdown.service
deleted file mode 100644 (file)
index abcf578..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Honor First Shutdown feature
-After=multi-user.target
-
-[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-52.units/%N.sh
-ExecStop=sh -c 'kill -KILL $MAINPID'
-FailureAction=reboot
-
-[Install]
-WantedBy=multi-user.target
diff --git a/test/testsuite-52.units/test-honor-first-shutdown.sh b/test/testsuite-52.units/test-honor-first-shutdown.sh
deleted file mode 100755 (executable)
index 0cb574f..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-echo "Honor first shutdown test script"
-sleep infinity;
diff --git a/test/testsuite-63.units/test63-glob.path b/test/testsuite-63.units/test63-glob.path
deleted file mode 100644 (file)
index 5f237a9..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Path]
-PathExistsGlob=/tmp/test63-glob*
diff --git a/test/testsuite-63.units/test63-glob.service b/test/testsuite-63.units/test63-glob.service
deleted file mode 100644 (file)
index 3f49dd4..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-ExecStartPre=sh -c 'test "$TRIGGER_PATH" = /tmp/test63-glob-foo'
-ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = test63-glob.path'
-ExecStart=systemd-notify --ready
-RemainAfterExit=yes
-Type=notify
diff --git a/test/testsuite-63.units/test63-issue-24577-dep.service b/test/testsuite-63.units/test63-issue-24577-dep.service
deleted file mode 100644 (file)
index e332ea4..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-Type=oneshot
-ExecStart=bash -c 'sleep infinity'
diff --git a/test/testsuite-63.units/test63-issue-24577.path b/test/testsuite-63.units/test63-issue-24577.path
deleted file mode 100644 (file)
index 80ba1db..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Path]
-PathExists=/tmp/hoge
diff --git a/test/testsuite-63.units/test63-issue-24577.service b/test/testsuite-63.units/test63-issue-24577.service
deleted file mode 100644 (file)
index 568518b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Requires=test63-issue-24577-dep.service
-After=test63-issue-24577-dep.service
-
-[Service]
-Type=oneshot
-ExecStart=bash -c 'sleep infinity'
diff --git a/test/testsuite-63.units/test63-pr-30768.path b/test/testsuite-63.units/test63-pr-30768.path
deleted file mode 100644 (file)
index b541358..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Path]
-PathChanged=/tmp/copyme
diff --git a/test/testsuite-63.units/test63-pr-30768.service b/test/testsuite-63.units/test63-pr-30768.service
deleted file mode 100644 (file)
index 5739084..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-ExecStart=cp -v /tmp/copyme /tmp/copied
-# once cp exits, service goes into deactivating state and then runs ExecStop
-ExecStop=flock -e /tmp/noexit true
diff --git a/test/testsuite-63.units/test63.path b/test/testsuite-63.units/test63.path
deleted file mode 100644 (file)
index 64d5ed6..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Path]
-PathExists=/tmp/test63
-# Make the unit friendly to slower machines
-TriggerLimitIntervalSec=10
-TriggerLimitBurst=10
diff --git a/test/testsuite-63.units/test63.service b/test/testsuite-63.units/test63.service
deleted file mode 100644 (file)
index 01a928b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-ConditionPathExists=/tmp/nonexistent
-
-[Service]
-ExecStartPre=sh -c 'test "$TRIGGER_PATH" = /tmp/test63'
-ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = test63.path'
-ExecStart=true
diff --git a/test/testsuite-80.units/fdstore-nopin.service b/test/testsuite-80.units/fdstore-nopin.service
deleted file mode 100644 (file)
index 29ffd23..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Service]
-Type=notify
-NotifyAccess=all
-FileDescriptorStoreMax=10
-FileDescriptorStorePreserve=restart
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/fdstore-pin.sh 0
diff --git a/test/testsuite-80.units/fdstore-pin.service b/test/testsuite-80.units/fdstore-pin.service
deleted file mode 100644 (file)
index 913daa2..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Service]
-Type=notify
-NotifyAccess=all
-FileDescriptorStoreMax=10
-FileDescriptorStorePreserve=yes
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/fdstore-pin.sh 1
diff --git a/test/testsuite-80.units/fdstore-pin.sh b/test/testsuite-80.units/fdstore-pin.sh
deleted file mode 100755 (executable)
index 4cb041a..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-PINNED="$1"
-COUNTER="/tmp/fdstore-invoked.$PINNED"
-FILE="/tmp/fdstore-data.$PINNED"
-
-# This script is called six times: thrice from a service unit where the fdstore
-# is pinned, and thrice where it isn't. The second iteration of each series is
-# a restart, the third a stop followed by a start
-
-if [ -e "$COUNTER" ] ; then
-    read -r N < "$COUNTER"
-else
-    N=0
-fi
-
-echo "Invocation #$N with PINNED=$PINNED."
-
-if [ "$N" -eq 0 ] ; then
-    # First iteration
-    test "${LISTEN_FDS:-0}" -eq 0
-    test ! -e "$FILE"
-    echo waldi > "$FILE"
-    systemd-notify --fd=3 --fdname="fd-$N-$PINNED" 3< "$FILE"
-elif [ "$N" -eq 1 ] || { [ "$N" -eq 2 ] && [ "$PINNED" -eq 1 ]; } ; then
-    # Second iteration, or iteration with pinning on
-    test "${LISTEN_FDS:-0}" -eq 1
-    # We reopen fd #3 here, so that the read offset is at zero each time (hence no <&3 here…)
-    read -r word < /proc/self/fd/3
-    test "$word" = "waldi"
-else
-    test "${LISTEN_FDS:-0}" -eq 0
-    test -e "$FILE"
-fi
-
-if [ "$N" -ge 2 ] ; then
-    rm "$COUNTER" "$FILE"
-else
-    echo $((N + 1)) > "$COUNTER"
-fi
-
-systemd-notify --ready --status="Ready"
-
-exec sleep infinity
diff --git a/test/testsuite-80.units/fdstore-pin.target b/test/testsuite-80.units/fdstore-pin.target
deleted file mode 100644 (file)
index 319b7e1..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-[Unit]
-After=fdstore-pin.service fdstore-nopin.service
-Wants=fdstore-pin.service fdstore-nopin.service
diff --git a/test/testsuite-80.units/notify.service b/test/testsuite-80.units/notify.service
deleted file mode 100644 (file)
index 196b076..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-[Service]
-Type=notify
-NotifyAccess=all
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/test.sh
diff --git a/test/testsuite-80.units/test.sh b/test/testsuite-80.units/test.sh
deleted file mode 100755 (executable)
index 565ed8d..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-sync_in() {
-    read -r x < /tmp/syncfifo2
-    test "$x" = "$1"
-}
-
-sync_out() {
-    echo "$1" > /tmp/syncfifo1
-}
-
-export SYSTEMD_LOG_LEVEL=debug
-
-echo "toplevel PID: $BASHPID"
-
-systemd-notify --status="Test starts"
-sync_out a
-sync_in b
-(
-    echo "subshell PID: $BASHPID"
-
-    # Make us main process
-    systemd-notify --pid="$BASHPID"
-
-    # Lock down access to just us
-    systemd-notify "NOTIFYACCESS=main"
-
-    # This should still work
-    systemd-notify --status="Sending READY=1 in an unprivileged process"
-
-    # Send as subprocess of the subshell, this should not work
-    systemd-notify --ready --pid=self --status "BOGUS1"
-
-    sync_out c
-    sync_in d
-
-    # Move main process back to toplevel
-    systemd-notify --pid=parent "MAINPID=$$"
-
-    # Should be dropped again
-    systemd-notify --status="BOGUS2" --pid=parent
-
-    # Apparently, bash will automatically invoke the last command in a subshell
-    # via a simple execve() rather than fork()ing first. But we want that the
-    # previous command uses the subshell's PID, hence let's insert a final,
-    # bogus redundant command as last command to run in the subshell, so that
-    # bash can't optimize things like that.
-    echo "bye"
-)
-
-echo "toplevel again: $BASHPID"
-
-systemd-notify --ready --status="OK"
-systemd-notify "NOTIFYACCESS=none"
-systemd-notify --status="BOGUS3"
-
-sync_out e
-
-exec sleep infinity
diff --git a/test/units/TEST-01-BASIC.service b/test/units/TEST-01-BASIC.service
new file mode 100644 (file)
index 0000000..9074e09
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-01-BASIC
+After=multi-user.target
+Wants=systemd-resolved.service systemd-networkd.service
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-01-BASIC.sh b/test/units/TEST-01-BASIC.sh
new file mode 100755 (executable)
index 0000000..bb3ff2f
--- /dev/null
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check if the colored --version output behaves correctly
+SYSTEMD_COLORS=256 systemctl --version
+
+# Check if we properly differentiate between a full systemd setup and a "light"
+# version of it that's done during daemon-reexec
+#
+# See: https://github.com/systemd/systemd/issues/27106
+if systemd-detect-virt -q --container; then
+    # We initialize /run/systemd/container only during a full setup
+    test -e /run/systemd/container
+    cp -afv /run/systemd/container /tmp/container
+    rm -fv /run/systemd/container
+    systemctl daemon-reexec
+    test ! -e /run/systemd/container
+    cp -afv /tmp/container /run/systemd/container
+else
+    # We bring the loopback netdev up only during a full setup, so it should
+    # not get brought back up during reexec if we disable it beforehand
+    [[ "$(ip -o link show lo)" =~ LOOPBACK,UP ]]
+    ip link set lo down
+    [[ "$(ip -o link show lo)" =~ state\ DOWN ]]
+    systemctl daemon-reexec
+    [[ "$(ip -o link show lo)" =~ state\ DOWN ]]
+    ip link set lo up
+
+    # We also disable coredumps only during a full setup
+    sysctl -w kernel.core_pattern=dont-overwrite-me
+    systemctl daemon-reexec
+    diff <(echo dont-overwrite-me) <(sysctl --values kernel.core_pattern)
+fi
+
+# Collect failed units & do one daemon-reload to a basic sanity check
+systemctl --state=failed --no-legend --no-pager | tee /failed
+test ! -s /failed
+systemctl daemon-reload
+
+# Check that the early setup is actually skipped on reexec.
+# If the early setup is done more than once, then several timestamps,
+# e.g. SecurityStartTimestamp, are re-initialized, and causes an ABRT
+# of systemd-analyze blame. See issue #27187.
+systemd-analyze blame
+
+# Test for 'systemd-update-utmp runlevel' vs 'systemctl daemon-reexec'.
+# See issue #27163.
+# shellcheck disable=SC2034
+if [[ -x /usr/lib/systemd/systemd-update-utmp ]]; then
+    for _ in {0..10}; do
+        systemctl daemon-reexec &
+        pid_reexec=$!
+        # shellcheck disable=SC2034
+        for _ in {0..10}; do
+            SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-update-utmp runlevel
+        done
+        wait "$pid_reexec"
+    done
+fi
+
+touch /testok
diff --git a/test/units/TEST-02-UNITTESTS.service b/test/units/TEST-02-UNITTESTS.service
new file mode 100644 (file)
index 0000000..dea2c4f
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-02-UNITTESTS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-02-UNITTESTS.sh b/test/units/TEST-02-UNITTESTS.sh
new file mode 100755 (executable)
index 0000000..7d44f97
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if ! systemd-detect-virt -qc && [[ "${TEST_CMDLINE_NEWLINE:-}" != bar ]]; then
+    cat /proc/cmdline
+    echo >&2 "Expected TEST_CMDLINE_NEWLINE=bar from the kernel command line"
+    exit 1
+fi
+
+# If we're running with TEST_PREFER_NSPAWN=1 limit the set of tests we run
+# in QEMU to only those that can't run in a container to avoid running
+# the same tests again in a, most likely, very slow environment
+if ! systemd-detect-virt -qc && [[ "${TEST_PREFER_NSPAWN:-0}" -ne 0 ]]; then
+    TESTS_GLOB="test-loop-block"
+else
+    TESTS_GLOB=${TESTS_GLOB:-test-*}
+fi
+
+NPROC=$(nproc)
+MAX_QUEUE_SIZE=${NPROC:-2}
+
+# Reset state
+rm -fv /failed /skipped /testok
+touch /lock
+
+if ! systemd-detect-virt -qc; then
+    # Make sure ping works for unprivileged users (for test-bpf-firewall)
+    sysctl net.ipv4.ping_group_range="0 2147483647"
+fi
+
+# Check & report test results
+# Arguments:
+#   $1: test path
+#   $2: test exit code
+run_test() {
+    if [[ $# -ne 1 ]]; then
+        echo >&2 "run_test: missing arguments"
+        exit 1
+    fi
+
+    local test="$1"
+    local name="${test##*/}"
+
+    echo "Executing test $name as unit $name.service"
+
+    systemd-run --quiet --property Delegate=1 --unit="$name" --wait "$test" && ret=0 || ret=$?
+
+    exec {LOCK_FD}> /lock
+    flock --exclusive ${LOCK_FD}
+
+    if [[ $ret -ne 0 && $ret != 77 && $ret != 127 ]]; then
+        echo "$name failed with $ret"
+        echo "$name" >>/failed-tests
+        {
+            echo "--- $name begin ---"
+            journalctl --unit="$name" --no-hostname -o short-monotonic
+            echo "--- $name end ---"
+        } >>/failed
+    elif [[ $ret == 77 || $ret == 127 ]]; then
+        echo "$name skipped"
+        echo "$name" >>/skipped-tests
+        {
+            echo "--- $name begin ---"
+            journalctl --unit="$name" --no-hostname -o short-monotonic
+            echo "--- $name end ---"
+        } >>/skipped
+    else
+        echo "$name OK"
+        echo "$name" >>/testok
+    fi
+
+    exec {LOCK_FD}<&-
+}
+
+export -f run_test
+
+find /usr/lib/systemd/tests/unit-tests/ -maxdepth 1 -type f -name "${TESTS_GLOB}" -print0 |
+    xargs -0 -I {} --max-procs="$MAX_QUEUE_SIZE" bash -ec "run_test {}"
+
+# Test logs are sometimes lost, as the system shuts down immediately after
+journalctl --sync
+
+test ! -s /failed
+touch /testok
diff --git a/test/units/TEST-03-JOBS.service b/test/units/TEST-03-JOBS.service
new file mode 100644 (file)
index 0000000..836f962
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-03-JOBS
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-03-JOBS.sh b/test/units/TEST-03-JOBS.sh
new file mode 100755 (executable)
index 0000000..115b941
--- /dev/null
@@ -0,0 +1,169 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Simple test for that daemon-reexec works in container.
+# See: https://github.com/systemd/systemd/pull/23883
+systemctl daemon-reexec
+
+# Test merging of a --job-mode=ignore-dependencies job into a previously
+# installed job.
+
+systemctl start --no-block hello-after-sleep.target
+
+systemctl list-jobs >/root/list-jobs.txt
+until grep 'sleep\.service.*running' /root/list-jobs.txt; do
+    systemctl list-jobs >/root/list-jobs.txt
+done
+
+grep 'hello\.service.*waiting' /root/list-jobs.txt
+
+# This is supposed to finish quickly, not wait for sleep to finish.
+START_SEC=$(date -u '+%s')
+systemctl start --job-mode=ignore-dependencies hello
+END_SEC=$(date -u '+%s')
+ELAPSED=$((END_SEC-START_SEC))
+
+test "$ELAPSED" -lt 3
+
+# sleep should still be running, hello not.
+systemctl list-jobs >/root/list-jobs.txt
+grep 'sleep\.service.*running' /root/list-jobs.txt
+grep 'hello\.service' /root/list-jobs.txt && exit 1
+systemctl stop sleep.service hello-after-sleep.target
+
+# Some basic testing that --show-transaction does something useful
+(! systemctl is-active systemd-importd)
+systemctl -T start systemd-importd
+systemctl is-active systemd-importd
+systemctl --show-transaction stop systemd-importd
+(! systemctl is-active systemd-importd)
+
+# Test for a crash when enqueuing a JOB_NOP when other job already exists
+systemctl start --no-block hello-after-sleep.target
+# hello.service should still be waiting, so these try-restarts will collapse
+# into NOPs.
+systemctl try-restart --job-mode=fail hello.service
+systemctl try-restart hello.service
+systemctl stop hello.service sleep.service hello-after-sleep.target
+
+# TODO: add more job queueing/merging tests here.
+
+# Test that restart propagates to activating units
+systemctl -T --no-block start always-activating.service
+systemctl list-jobs | grep 'always-activating.service'
+ACTIVATING_ID_PRE=$(systemctl show -P InvocationID always-activating.service)
+systemctl -T start always-activating.socket # Wait for the socket to come up
+systemctl -T restart always-activating.socket
+ACTIVATING_ID_POST=$(systemctl show -P InvocationID always-activating.service)
+[ "$ACTIVATING_ID_PRE" != "$ACTIVATING_ID_POST" ] || exit 1
+
+# Test for irreversible jobs
+systemctl start unstoppable.service
+
+# This is expected to fail with 'job cancelled'
+systemctl stop unstoppable.service && exit 1
+# But this should succeed
+systemctl stop --job-mode=replace-irreversibly unstoppable.service
+
+# We're going to shutdown soon. Let's see if it succeeds when
+# there's an active service that tries to be unstoppable.
+# Shutdown of the container/VM will hang if not.
+systemctl start unstoppable.service
+
+# Test waiting for a started units to terminate again
+cat <<EOF >/run/systemd/system/wait2.service
+[Unit]
+Description=Wait for 2 seconds
+[Service]
+ExecStart=sh -ec 'sleep 2'
+EOF
+cat <<EOF >/run/systemd/system/wait5fail.service
+[Unit]
+Description=Wait for 5 seconds and fail
+[Service]
+ExecStart=sh -ec 'sleep 5; false'
+EOF
+
+# wait2 succeeds
+START_SEC=$(date -u '+%s')
+systemctl start --wait wait2.service
+END_SEC=$(date -u '+%s')
+ELAPSED=$((END_SEC-START_SEC))
+[[ "$ELAPSED" -ge 2 ]] && [[ "$ELAPSED" -le 4 ]] || exit 1
+
+# wait5fail fails, so systemctl should fail
+START_SEC=$(date -u '+%s')
+(! systemctl start --wait wait2.service wait5fail.service)
+END_SEC=$(date -u '+%s')
+ELAPSED=$((END_SEC-START_SEC))
+[[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1
+
+# Test time-limited scopes
+START_SEC=$(date -u '+%s')
+set +e
+systemd-run --scope --property=RuntimeMaxSec=3s sleep 10
+RESULT=$?
+END_SEC=$(date -u '+%s')
+ELAPSED=$((END_SEC-START_SEC))
+[[ "$ELAPSED" -ge 3 ]] && [[ "$ELAPSED" -le 5 ]] || exit 1
+[[ "$RESULT" -ne 0 ]] || exit 1
+
+# Test transactions with cycles
+# Provides coverage for issues like https://github.com/systemd/systemd/issues/26872
+for i in {0..19}; do
+    cat >"/run/systemd/system/transaction-cycle$i.service" <<EOF
+[Unit]
+After=transaction-cycle$(((i + 1) % 20)).service
+Requires=transaction-cycle$(((i + 1) % 20)).service
+
+[Service]
+ExecStart=true
+EOF
+done
+systemctl daemon-reload
+for i in {0..19}; do
+    systemctl start "transaction-cycle$i.service"
+done
+
+# Test PropagatesStopTo= when restart (issue #26839)
+systemctl start propagatestopto-and-pullin.target
+systemctl --quiet is-active propagatestopto-and-pullin.target
+
+systemctl restart propagatestopto-and-pullin.target
+systemctl --quiet is-active propagatestopto-and-pullin.target
+systemctl --quiet is-active sleep-infinity-simple.service
+
+systemctl start propagatestopto-only.target
+systemctl --quiet is-active propagatestopto-only.target
+systemctl --quiet is-active sleep-infinity-simple.service
+
+systemctl restart propagatestopto-only.target
+assert_rc 3 systemctl --quiet is-active sleep-infinity-simple.service
+
+systemctl start propagatesstopto-indirect.target propagatestopto-and-pullin.target
+systemctl --quiet is-active propagatestopto-indirect.target
+systemctl --quiet is-active propagatestopto-and-pullin.target
+
+systemctl restart propagatestopto-indirect.target
+assert_rc 3 systemctl --quiet is-active propagatestopto-and-pullin.target
+assert_rc 3 systemctl --quiet is-active sleep-infinity-simple.service
+
+# Test restart mode direct
+systemctl start succeeds-on-restart-restartdirect.target
+assert_rc 0 systemctl --quiet is-active succeeds-on-restart-restartdirect.target
+
+systemctl start fails-on-restart-restartdirect.target || :
+assert_rc 3 systemctl --quiet is-active fails-on-restart-restartdirect.target
+
+systemctl start succeeds-on-restart.target || :
+assert_rc 3 systemctl --quiet is-active succeeds-on-restart.target
+
+systemctl start fails-on-restart.target || :
+assert_rc 3 systemctl --quiet is-active fails-on-restart.target
+
+touch /testok
diff --git a/test/units/TEST-04-JOURNAL.LogFilterPatterns.sh b/test/units/TEST-04-JOURNAL.LogFilterPatterns.sh
new file mode 100755 (executable)
index 0000000..dfa9652
--- /dev/null
@@ -0,0 +1,78 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+ . "$(dirname "$0")"/util.sh
+
+add_logs_filtering_override() {
+    local unit="${1:?}"
+    local override_name="${2:?}"
+    local log_filter="${3:-}"
+
+    mkdir -p "/run/systemd/system/$unit.d/"
+    echo -ne "[Service]\nLogFilterPatterns=$log_filter" >"/run/systemd/system/$unit.d/$override_name.conf"
+    systemctl daemon-reload
+}
+
+run_service_and_fetch_logs() {
+    local unit="${1:?}"
+    local start
+
+    start="$(date '+%Y-%m-%d %T.%6N')"
+    systemctl start "$unit"
+    journalctl --sync
+    journalctl -q -u "$unit" -S "$start" -p notice
+}
+
+if cgroupfs_supports_user_xattrs; then
+    # Accept all log messages
+    add_logs_filtering_override "logs-filtering.service" "00-reset" ""
+    [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+    add_logs_filtering_override "logs-filtering.service" "01-allow-all" ".*"
+    [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+    # Discard all log messages
+    add_logs_filtering_override "logs-filtering.service" "02-discard-all" "~.*"
+    [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+    # Accept all test messages
+    add_logs_filtering_override "logs-filtering.service" "03-reset" ""
+    [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+    # Discard all test messages
+    add_logs_filtering_override "logs-filtering.service" "04-discard-gg" "~.*gg.*"
+    [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+    # Deny filter takes precedence
+    add_logs_filtering_override "logs-filtering.service" "05-allow-all-but-too-late" ".*"
+    [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+    # Use tilde in a deny pattern
+    add_logs_filtering_override "logs-filtering.service" "06-reset" ""
+    add_logs_filtering_override "logs-filtering.service" "07-prevent-tilde" "~~more~"
+    [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+    # Only allow a pattern that won't be matched
+    add_logs_filtering_override "logs-filtering.service" "08-reset" ""
+    add_logs_filtering_override "logs-filtering.service" "09-allow-only-non-existing" "non-existing string"
+    [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+    # Allow a pattern starting with a tilde
+    add_logs_filtering_override "logs-filtering.service" "10-allow-with-escape-char" "\\\\x7emore~"
+    [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+    add_logs_filtering_override "logs-filtering.service" "11-reset" ""
+    add_logs_filtering_override "logs-filtering.service" "12-allow-with-spaces" "foo bar"
+    [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
+
+    add_logs_filtering_override "delegated-cgroup-filtering.service" "00-allow-all" ".*"
+    [[ -n $(run_service_and_fetch_logs "delegated-cgroup-filtering.service") ]]
+
+    add_logs_filtering_override "delegated-cgroup-filtering.service" "01-discard-hello" "~hello"
+    [[ -z $(run_service_and_fetch_logs "delegated-cgroup-filtering.service") ]]
+
+    rm -rf /run/systemd/system/{logs-filtering,delegated-cgroup-filtering}.service.d
+fi
diff --git a/test/units/TEST-04-JOURNAL.SYSTEMD_JOURNAL_COMPRESS.sh b/test/units/TEST-04-JOURNAL.SYSTEMD_JOURNAL_COMPRESS.sh
new file mode 100755 (executable)
index 0000000..96d096d
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# https://bugzilla.redhat.com/show_bug.cgi?id=2183546
+mkdir /run/systemd/system/systemd-journald.service.d
+MACHINE_ID="$(</etc/machine-id)"
+
+# Reset the start-limit counters, as we're going to restart journald a couple of times
+systemctl reset-failed systemd-journald.service
+
+for c in NONE XZ LZ4 ZSTD; do
+    cat >/run/systemd/system/systemd-journald.service.d/compress.conf <<EOF
+[Service]
+Environment=SYSTEMD_JOURNAL_COMPRESS=${c}
+EOF
+    systemctl daemon-reload
+    systemctl restart systemd-journald.service
+    journalctl --rotate
+
+    ID="$(systemd-id128 new)"
+    systemd-cat -t "$ID" /bin/bash -c "for ((i=0;i<100;i++)); do echo -n hoge with ${c}; done; echo"
+    journalctl --sync
+    timeout 10 bash -c "until SYSTEMD_LOG_LEVEL=debug journalctl --verify --quiet --file /var/log/journal/$MACHINE_ID/system.journal 2>&1 | grep -q -F 'compress=${c}'; do sleep .5; done"
+
+    # $SYSTEMD_JOURNAL_COMPRESS= also works for journal-remote
+    if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then
+        for cc in NONE XZ LZ4 ZSTD; do
+            rm -f /tmp/foo.journal
+            SYSTEMD_JOURNAL_COMPRESS="${cc}" /usr/lib/systemd/systemd-journal-remote --split-mode=none -o /tmp/foo.journal --getter="journalctl -b -o export -t $ID"
+            SYSTEMD_LOG_LEVEL=debug journalctl --verify --quiet --file /tmp/foo.journal 2>&1 | grep -q -F "compress=${cc}"
+            journalctl -t "$ID" -o cat --file /tmp/foo.journal | grep -q -F "hoge with ${c}"
+        done
+    fi
+done
+
+rm /run/systemd/system/systemd-journald.service.d/compress.conf
+systemctl daemon-reload
+systemctl restart systemd-journald.service
+systemctl reset-failed systemd-journald.service
+journalctl --rotate
diff --git a/test/units/TEST-04-JOURNAL.bsod.sh b/test/units/TEST-04-JOURNAL.bsod.sh
new file mode 100755 (executable)
index 0000000..83feb89
--- /dev/null
@@ -0,0 +1,123 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if systemd-detect-virt -cq; then
+    echo "This test requires a VM, skipping the test" | tee --append /skipped
+    exit 0
+fi
+
+if [[ ! -x /usr/lib/systemd/systemd-bsod ]]; then
+    echo "systemd-bsod is not installed, skipping the test" | tee --append /skipped
+    exit 0
+fi
+
+# shellcheck disable=SC2317
+at_exit() {
+    local EC=$?
+
+    if [[ $EC -ne 0 ]] && [[ -e /tmp/console.dump ]]; then
+        cat /tmp/console.dump
+    fi
+
+    if mountpoint -q /var/log/journal; then
+        # In order to preserve the journal from the just run test we need to do a little dance, as
+        # --relinquish-var is not a "true" opposite of --flush, meaning that it won't move the existing
+        # journal(s) from /var/log/ to /run/log/. To do that, let's rotate the journal first, so all
+        # important bits are in the archived journal(s)...
+        journalctl --rotate
+        # ...then instruct sd-journald to write further entries to the runtime journal...
+        journalctl --relinquish-var
+        # ...make sure there are no outstanding writes to the persistent journal that might block us from
+        # unmounting the tmpfs...
+        journalctl --sync
+        # ...move the archived journals to the runtime storage...
+        mv -v "/var/log/journal/$(</etc/machine-id)"/system@*.journal "/run/log/journal/$(</etc/machine-id)/"
+        # ...get rid of the tmpfs on /var/log/journal/...
+        umount /var/log/journal
+        # ...and finally flush everything to the "real" persistent journal, so we can collect it after the
+        # test finishes.
+        journalctl --flush
+    fi
+
+    return 0
+}
+
+vcs_dump_and_check() {
+    local expected_message="${1:?}"
+
+    # It might take a while before the systemd-bsod stuff appears on the VCS,
+    # so try it a couple of times
+    for _ in {0..9}; do
+        setterm --term linux --dump --file /tmp/console.dump
+        if grep -aq "Press any key to exit" /tmp/console.dump &&
+           grep -aq "$expected_message" /tmp/console.dump &&
+           grep -aq "The current boot has failed" /tmp/console.dump; then
+
+            return 0
+        fi
+
+        sleep .5
+    done
+
+    return 1
+}
+
+# Since systemd-bsod always fetches only the first emergency message from the
+# current boot, let's temporarily overmount /var/log/journal with a tmpfs,
+# as we're going to wipe it multiple times, but we need to keep the original
+# journal intact for the other tests to work correctly.
+trap at_exit EXIT
+mount -t tmpfs tmpfs /var/log/journal
+systemctl restart systemd-journald
+
+systemctl stop systemd-bsod
+
+# Since we just wiped the journal, there should be no emergency messages and
+# systemd-bsod should be just a no-op
+timeout 10s /usr/lib/systemd/systemd-bsod
+setterm --term linux --dump --file /tmp/console.dump
+(! grep "The current boot has failed" /tmp/console.dump)
+
+# systemd-bsod should pick up emergency messages only with UID=0, so let's check
+# that as well
+systemd-run --user --machine testuser@ --wait --pipe systemd-cat -p emerg echo "User emergency message"
+systemd-cat -p emerg echo "Root emergency message"
+journalctl --sync
+# Set $SYSTEMD_COLORS so systemd-bsod also prints out the QR code
+SYSTEMD_COLORS=256 /usr/lib/systemd/systemd-bsod &
+PID=$!
+vcs_dump_and_check "Root emergency message"
+grep -aq "Scan the QR code" /tmp/console.dump
+# TODO: check if systemd-bsod exits on a key press (didn't figure this one out yet)
+kill $PID
+timeout 10 bash -c "while kill -0 $PID; do sleep .5; done"
+
+# Wipe the journal
+journalctl --vacuum-size=1 --rotate
+(! journalctl -q -b -p emerg --grep .)
+
+# Check the systemd-bsod.service as well
+# Note: the systemd-bsod.service unit has ConditionVirtualization=no, so let's
+# temporarily override it just for the test
+mkdir /run/systemd/system/systemd-bsod.service.d
+printf '[Unit]\nConditionVirtualization=\n' >/run/systemd/system/systemd-bsod.service.d/99-override.conf
+systemctl daemon-reload
+systemctl start systemd-bsod
+systemd-cat -p emerg echo "Service emergency message"
+vcs_dump_and_check "Service emergency message"
+systemctl status systemd-bsod
+systemctl stop systemd-bsod
+
+# Wipe the journal
+journalctl --vacuum-size=1 --rotate
+(! journalctl -q -b -p emerg --grep .)
+
+# Same as above, but make sure the service responds to signals even when there are
+# no "emerg" messages, see systemd/systemd#30084
+(! systemctl is-active systemd-bsod)
+systemctl start systemd-bsod
+timeout 5s bash -xec 'until systemctl is-active systemd-bsod; do sleep .5; done'
+timeout 5s systemctl stop systemd-bsod
+timeout 5s bash -xec 'while systemctl is-active systemd-bsod; do sleep .5; done'
diff --git a/test/units/TEST-04-JOURNAL.cat.sh b/test/units/TEST-04-JOURNAL.cat.sh
new file mode 100755 (executable)
index 0000000..bef3e18
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemctl enable --now systemd-journald@cat-test.socket
+
+systemd-cat --namespace cat-test env CAT_TEST_RESULT=1
+
+timeout 30 bash -c "until systemctl -q is-active systemd-journald@cat-test.service; do sleep .5; done"
+
+journalctl --namespace cat-test --grep "JOURNAL_STREAM="
+journalctl --namespace cat-test --grep "CAT_TEST_RESULT=1"
+
+systemctl disable --now systemd-journald@cat-test.socket
diff --git a/test/units/TEST-04-JOURNAL.corrupted-journals.sh b/test/units/TEST-04-JOURNAL.corrupted-journals.sh
new file mode 100755 (executable)
index 0000000..479011e
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+JOURNAL_DIR="$(mktemp -d)"
+REMOTE_OUT="$(mktemp -d)"
+# tar on C8S doesn't support the --zstd option
+unzstd --stdout "/usr/lib/systemd/tests/testdata/test-journals/afl-corrupted-journals.tar.zst" | tar -xC "$JOURNAL_DIR/"
+while read -r file; do
+    filename="${file##*/}"
+    unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}"
+done < <(find /usr/lib/systemd/tests/testdata/test-journals/corrupted/ -name "*.zst")
+# First, try each of them sequentially. Skip this part when running with plain
+# QEMU, as it is excruciatingly slow
+# Note: we care only about exit code 124 (timeout) and special bash exit codes
+# >124 (like signals)
+if [[ "$(systemd-detect-virt -v)" != "qemu" ]]; then
+    while read -r file; do
+        timeout 10 journalctl --file="$file" --boot >/dev/null || [[ $? -lt 124 ]]
+        timeout 10 journalctl --file="$file" --verify >/dev/null || [[ $? -lt 124 ]]
+        timeout 10 journalctl --file="$file" --output=export >/dev/null || [[ $? -lt 124 ]]
+        timeout 10 journalctl --file="$file" --fields >/dev/null || [[ $? -lt 124 ]]
+        timeout 10 journalctl --file="$file" --list-boots >/dev/null || [[ $? -lt 124 ]]
+        if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then
+            timeout 10 /usr/lib/systemd/systemd-journal-remote \
+                            --getter="journalctl --file=$file --output=export" \
+                            --split-mode=none \
+                            --output="$REMOTE_OUT/system.journal" || [[ $? -lt 124 ]]
+            timeout 10 journalctl --directory="$REMOTE_OUT" >/dev/null || [[ $? -lt 124 ]]
+            rm -f "$REMOTE_OUT"/*
+        fi
+    done < <(find "$JOURNAL_DIR" -type f)
+fi
+# And now all at once
+timeout 30 journalctl --directory="$JOURNAL_DIR" --boot >/dev/null || [[ $? -lt 124 ]]
+timeout 30 journalctl --directory="$JOURNAL_DIR" --verify >/dev/null || [[ $? -lt 124 ]]
+timeout 30 journalctl --directory="$JOURNAL_DIR" --output=export >/dev/null || [[ $? -lt 124 ]]
+timeout 30 journalctl --directory="$JOURNAL_DIR" --fields >/dev/null || [[ $? -lt 124 ]]
+timeout 30 journalctl --directory="$JOURNAL_DIR" --list-boots >/dev/null || [[ $? -lt 124 ]]
+if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then
+    timeout 30 /usr/lib/systemd/systemd-journal-remote \
+                    --getter="journalctl --directory=$JOURNAL_DIR --output=export" \
+                    --split-mode=none \
+                    --output="$REMOTE_OUT/system.journal" || [[ $? -lt 124 ]]
+    timeout 30 journalctl --directory="$REMOTE_OUT" >/dev/null || [[ $? -lt 124 ]]
+    rm -f "$REMOTE_OUT"/*
+fi
diff --git a/test/units/TEST-04-JOURNAL.fss.sh b/test/units/TEST-04-JOURNAL.fss.sh
new file mode 100755 (executable)
index 0000000..03351b8
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Forward Secure Sealing
+
+if ! journalctl --version | grep -qF +GCRYPT; then
+    echo "Built without gcrypt, skipping the FSS tests"
+    exit 0
+fi
+
+journalctl --force --setup-keys --interval=2 |& tee /tmp/fss
+FSS_VKEY="$(sed -rn '/([a-f0-9]{6}\-){3}[a-f0-9]{6}\/[a-f0-9]+\-[a-f0-9]+/p' /tmp/fss)"
+[[ -n "$FSS_VKEY" ]]
+
+# Generate some buzz in the journal and wait until the FSS key is changed
+# at least once
+systemd-cat cat /etc/os-release
+sleep 4
+# Seal the journal
+journalctl --rotate
+# Verification should fail without a valid FSS key
+(! journalctl --verify)
+(! journalctl --verify --verify-key="")
+(! journalctl --verify --verify-key="000000-000000-000000-000000/00000000-00000")
+# FIXME: ignore --verify result until #27532 is resolved
+journalctl --verify --verify-key="$FSS_VKEY" || :
+
+# Sealing + systemd-journal-remote
+/usr/lib/systemd/systemd-journal-remote --getter="journalctl -n 5 -o export" \
+                                        --split-mode=none \
+                                        --seal=yes \
+                                        --output=/tmp/sealed.journal
+(! journalctl --file=/tmp/sealed.journal --verify)
+(! journalctl --file=/tmp/sealed.journal --verify --verify-key="")
+(! journalctl --file=/tmp/sealed.journal --verify --verify-key="000000-000000-000000-000000/00000000-00000")
+# FIXME: ignore --verify result until #27532 is resolved
+journalctl --file=/tmp/sealed.journal --verify --verify-key="$FSS_VKEY" || :
+rm -f /tmp/sealed.journal
+
+# Return back to a journal without FSS
+rm -fv "/var/log/journal/$(</etc/machine-id)/fss"
+journalctl --rotate --vacuum-size=1
+# FIXME: ignore --verify result until #27532 is resolved
+journalctl --verify || :
diff --git a/test/units/TEST-04-JOURNAL.journal-append.sh b/test/units/TEST-04-JOURNAL.journal-append.sh
new file mode 100755 (executable)
index 0000000..35f9433
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# test-journal-append corrupts the journal file by flipping a bit at a given offset and
+# following it by a write to check if we handle appending messages to corrupted journals
+# gracefully
+
+TEST_JOURNAL_APPEND=/usr/lib/systemd/tests/unit-tests/manual/test-journal-append
+
+[[ -x "$TEST_JOURNAL_APPEND" ]]
+
+# Corrupt the first ~1024 bytes, this should be pretty quick
+"$TEST_JOURNAL_APPEND" --sequential --start-offset=0 --iterations=350 --iteration-step=3
+
+# Skip most of the test when running without acceleration, as it's excruciatingly slow
+# (this shouldn't be an issue, as it should run in nspawn as well)
+if ! [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
+    # Corrupt the beginning of every 1K block between 1K - 32K
+    for ((i = 1024; i <= (32 * 1024); i += 1024)); do
+        "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
+    done
+
+    # Corrupt the beginning of every 16K block between 32K - 128K
+    for ((i = (32 * 1024); i <= (256 * 1024); i += (16 * 1024))); do
+        "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
+    done
+
+    # Corrupt the beginning of every 128K block between 128K - 1M
+    for ((i = (128 * 1024); i <= (1 * 1024 * 1024); i += (128 * 1024))); do
+        "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
+    done
+
+    # And finally the beginning of every 1M block between 1M and 8M
+    for ((i = (1 * 1024 * 1024); i < (8 * 1024 * 1024); i += (1 * 1024 * 1024))); do
+        "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
+    done
+
+    if [[ "$(nproc)" -ge 2 ]]; then
+        # Try to corrupt random bytes throughout the journal
+        "$TEST_JOURNAL_APPEND" --iterations=25
+    fi
+else
+    "$TEST_JOURNAL_APPEND" --iterations=10
+fi
diff --git a/test/units/TEST-04-JOURNAL.journal-corrupt.sh b/test/units/TEST-04-JOURNAL.journal-corrupt.sh
new file mode 100755 (executable)
index 0000000..c25cf54
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+journalctl --rotate --vacuum-files=1
+# Nuke all archived journals, so we start with a clean slate
+rm -f "/var/log/journal/$(</etc/machine-id)"/system@*.journal
+rm -f "/var/log/journal/$(</etc/machine-id)"/user-*@*.journal
+journalctl --header | grep path
+
+# Make sure the user instance is active when we rotate journals
+loginctl enable-linger testuser
+systemd-run --unit user-sleep.service --user -M testuser@ sleep infinity
+
+for _ in {0..9}; do
+    journalctl --rotate
+    journalctl --sync
+    SYSTEMD_LOG_LEVEL=debug journalctl -n1 -q
+    (! journalctl -n0 -q |& grep corrupted)
+done
+
+systemctl stop --user -M testuser@ user-sleep.service
+loginctl disable-linger testuser
+
+journalctl --sync
+journalctl --rotate --vacuum-files=1
+# Nuke all archived journals, so we start with a clean slate
+rm -f "/var/log/journal/$(</etc/machine-id)"/system@*.journal
+rm -f "/var/log/journal/$(</etc/machine-id)/"user-*@*.journal
+journalctl --header | grep path
+
+for _ in {0..9}; do
+    journalctl --rotate --vacuum-files=1
+    journalctl --sync
+    SYSTEMD_LOG_LEVEL=debug journalctl -n1 -q
+    (! journalctl -n0 -q |& grep corrupted)
+done
diff --git a/test/units/TEST-04-JOURNAL.journal-gatewayd.sh b/test/units/TEST-04-JOURNAL.journal-gatewayd.sh
new file mode 100755 (executable)
index 0000000..9c7a3d0
--- /dev/null
@@ -0,0 +1,223 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if [[ ! -x /usr/lib/systemd/systemd-journal-gatewayd ]]; then
+    echo "Built without systemd-journal-gatewayd support, skipping the test"
+    exit 0
+fi
+
+LOG_FILE="$(mktemp)"
+
+at_exit() {
+    if [[ $? -ne 0 ]]; then
+        # The $LOG_FILE is potentially huge (as it might be a full copy of the current journal), so let's
+        # dump it at debug level under a specific syslog tag, so it's clearly separated from the actual test
+        # journal; things get very confusing otherwise.
+        systemd-cat -t log-file-dump -p debug cat "$LOG_FILE"
+    fi
+
+    rm -f "$LOG_FILE"
+}
+
+trap at_exit EXIT
+
+TEST_MESSAGE="-= This is a test message $RANDOM =-"
+TEST_TAG="$(systemd-id128 new)"
+
+BEFORE_TIMESTAMP="$(date +%s)"
+echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
+sleep 1
+journalctl --sync
+TEST_CURSOR="$(journalctl -q -t "$TEST_TAG" -n 0 --show-cursor | awk '{ print $3; }')"
+BOOT_CURSOR="$(journalctl -q -b -n 0 --show-cursor | awk '{ print $3; }')"
+AFTER_TIMESTAMP="$(date +%s)"
+
+/usr/lib/systemd/systemd-journal-gatewayd --version
+/usr/lib/systemd/systemd-journal-gatewayd --help
+
+# Default configuration (HTTP, socket activated)
+systemctl start systemd-journal-gatewayd.socket
+
+# /browse
+# We should get redirected to /browse by default
+curl -LSfs http://localhost:19531 >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+(! curl -LSfs http://localhost:19531/foo/bar/baz)
+(! curl -LSfs http://localhost:19531/foo/../../../bar/../baz)
+
+# /entries
+# Accept: text/plain should be the default
+curl -LSfs http://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs --header "Accept: text/plain" http://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?boot >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+# Show 10 entries starting from $BOOT_CURSOR, skip the first 5
+curl -LSfs \
+     --header "Accept: application/json" \
+     --header "Range: entries=$BOOT_CURSOR:5:10" \
+     http://localhost:19531/entries >"$LOG_FILE"
+jq -se "length == 10" "$LOG_FILE"
+# Check if the specified cursor refers to an existing entry and return just that entry
+curl -LSfs \
+     --header "Accept: application/json" \
+     --header "Range: entries=$TEST_CURSOR" \
+     http://localhost:19531/entries?discrete >"$LOG_FILE"
+jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+# Check entry is present (resp. absent) when filtering by timestamp
+curl -LSfs \
+     --header "Range: realtime=$BEFORE_TIMESTAMP:" \
+     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs \
+     --header "Range: realtime=:$AFTER_TIMESTAMP" \
+     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs \
+     --header "Accept: application/json" \
+     --header "Range: realtime=:$BEFORE_TIMESTAMP" \
+     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 0" "$LOG_FILE"
+curl -LSfs \
+     --header "Accept: application/json" \
+     --header "Range: realtime=$AFTER_TIMESTAMP:" \
+     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 0" "$LOG_FILE"
+# Check positive and negative skip when filtering by timestamp
+echo "-= This is a second test message =-" | systemd-cat -t "$TEST_TAG"
+journalctl --sync
+TEST2_CURSOR="$(journalctl -q -t "$TEST_TAG" -n 0 --show-cursor | awk '{ print $3; }')"
+echo "-= This is a third test message =-" | systemd-cat -t "$TEST_TAG"
+journalctl --sync
+sleep 1
+END_TIMESTAMP="$(date +%s)"
+curl -LSfs \
+     --header "Accept: application/json" \
+     --header "Range: realtime=$BEFORE_TIMESTAMP::1:1" \
+     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
+curl -LSfs \
+     --header "Accept: application/json" \
+     --header "Range: realtime=$END_TIMESTAMP::-1:1" \
+     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
+jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
+
+# No idea how to properly parse this (jq won't cut it), so let's at least do some sanity checks that every
+# line is either empty or begins with data:
+curl -LSfs --header "Accept: text/event-stream" http://localhost:19531/entries >"$LOG_FILE"
+awk '!/^(data: \{.+\}|)$/ { exit 1; }' "$LOG_FILE"
+# Same thing as journalctl --output=export
+mkdir /tmp/remote-journal
+curl -LSfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries >"$LOG_FILE"
+/usr/lib/systemd/systemd-journal-remote --output=/tmp/remote-journal/system.journal --split-mode=none "$LOG_FILE"
+journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE"
+rm -rf /tmp/remote-journal/*
+# Let's do the same thing again, but let systemd-journal-remote spawn curl itself
+/usr/lib/systemd/systemd-journal-remote --url=http://localhost:19531/entries \
+                                        --output=/tmp/remote-journal/system.journal \
+                                        --split-mode=none
+journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE"
+rm -rf /tmp/remote-journal
+
+# /machine
+curl -LSfs http://localhost:19531/machine >"$LOG_FILE"
+jq . "$LOG_FILE"
+
+# /fields
+curl -LSfs http://localhost:19531/fields/MESSAGE >"$LOG_FILE"
+grep -qE -- "$TEST_MESSAGE" "$LOG_FILE"
+curl -LSfs http://localhost:19531/fields/_TRANSPORT
+(! curl -LSfs http://localhost:19531/fields)
+(! curl -LSfs http://localhost:19531/fields/foo-bar-baz)
+
+systemctl stop systemd-journal-gatewayd.{socket,service}
+
+if ! command -v openssl >/dev/null; then
+    echo "openssl command not available, skipping the HTTPS tests"
+    exit 0
+fi
+
+# Generate a self-signed certificate for systemd-journal-gatewayd
+#
+# Note: older OpenSSL requires a config file with some extra options, unfortunately
+cat >/tmp/openssl.conf <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = CZ
+L = Brno
+O = Foo
+OU = Bar
+CN = localhost
+EOF
+openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 7 \
+            -config /tmp/openssl.conf \
+            -keyout /tmp/key.pem -out /tmp/cert.pem
+# Start HTTPS version of gatewayd via the systemd-socket-activate tool to give it some coverage as well
+systemd-socket-activate --listen=19531 -- \
+    /usr/lib/systemd/systemd-journal-gatewayd \
+        --cert=/tmp/cert.pem \
+        --key=/tmp/key.pem \
+        --file="/var/log/journal/*/*.journal" &
+GATEWAYD_PID=$!
+sleep 1
+
+# Do a limited set of tests, since the underlying code should be the same past the HTTPS transport
+curl -LSfsk https://localhost:19531 >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+curl -LSfsk https://localhost:19531/entries >"$LOG_FILE"
+grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
+curl -LSfsk --header "Accept: application/json" https://localhost:19531/entries >"$LOG_FILE"
+jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
+curl -LSfsk https://localhost:19531/machine >"$LOG_FILE"
+jq . "$LOG_FILE"
+curl -LSfsk https://localhost:19531/fields/_TRANSPORT
+
+kill "$GATEWAYD_PID"
+
+# Test a couple of error scenarios
+GATEWAYD_FILE="$(mktemp /tmp/test-gatewayd-XXX.journal)"
+
+/usr/lib/systemd/systemd-journal-remote --output="$GATEWAYD_FILE" --getter="journalctl -n5 -o export"
+systemd-run --unit="test-gatewayd.service" --socket-property="ListenStream=19531" \
+            /usr/lib/systemd/systemd-journal-gatewayd --file="$GATEWAYD_FILE"
+
+# Call an unsupported endpoint together with some garbage data - gatewayd should not send garbage in return
+# See: https://github.com/systemd/systemd/issues/9858
+OUT="$(mktemp)"
+for _ in {0..4}; do
+    (! curl --fail-with-body -d "please process this🐱 $RANDOM" -L http://localhost:19531/upload | tee "$OUT")
+    (! grep '[^[:print:]]' "$OUT")
+done
+(! curl --fail-with-body --upload-file "$GATEWAYD_FILE" -L http://localhost:19531/upload | tee "$OUT")
+(! grep '[^[:print:]]' "$OUT")
+rm -rf "$OUT"
+
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+# Nuke the file behind the /browse endpoint
+mv /usr/share/systemd/gatewayd/browse.html /usr/share/systemd/gatewayd/browse.html.bak
+(! curl --fail-with-body -L http://localhost:19531/browse)
+mv /usr/share/systemd/gatewayd/browse.html.bak /usr/share/systemd/gatewayd/browse.html
+curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
+grep -qF "<title>Journal</title>" "$LOG_FILE"
+
+# Nuke the journal file
+mv "$GATEWAYD_FILE" "$GATEWAYD_FILE.bak"
+(! curl --fail-with-body -L http://localhost:19531/fields/_PID)
+mv "$GATEWAYD_FILE.bak" "$GATEWAYD_FILE"
+curl -LSfs http://localhost:19531/fields/_PID
+
+systemctl stop test-gatewayd.{socket,service}
+rm -f "$GATEWAYD_FILE"
diff --git a/test/units/TEST-04-JOURNAL.journal-remote.sh b/test/units/TEST-04-JOURNAL.journal-remote.sh
new file mode 100755 (executable)
index 0000000..c7b99b1
--- /dev/null
@@ -0,0 +1,230 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+if [[ ! -x /usr/lib/systemd/systemd-journal-remote || ! -x /usr/lib/systemd/systemd-journal-upload ]]; then
+    echo "Built without systemd-journal-remote/upload support, skipping the test"
+    exit 0
+fi
+
+if ! command -v openssl >/dev/null; then
+    echo "openssl command not available, skipping the tests"
+    exit 0
+fi
+
+at_exit() {
+    set +e
+
+    systemctl stop systemd-journal-upload
+    systemctl stop systemd-journal-remote.{socket,service}
+    # Remove any remote journals on exit, so we don't try to export them together
+    # with the local journals, causing a mess
+    rm -rf /var/log/journal/remote
+}
+
+trap at_exit EXIT
+
+TEST_MESSAGE="-= This is a test message $RANDOM =-"
+TEST_TAG="$(systemd-id128 new)"
+
+echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
+journalctl --sync
+
+/usr/lib/systemd/systemd-journal-remote --version
+/usr/lib/systemd/systemd-journal-remote --help
+/usr/lib/systemd/systemd-journal-upload --version
+/usr/lib/systemd/systemd-journal-upload --help
+
+# Generate a self-signed certificate for systemd-journal-remote
+#
+# Note: older OpenSSL requires a config file with some extra options, unfortunately
+# Note2: /run here is used on purpose, since the systemd-journal-remote service uses PrivateTmp=yes
+mkdir -p /run/systemd/journal-remote-tls
+cat >/tmp/openssl.conf <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = CZ
+L = Brno
+O = Foo
+OU = Bar
+CN = localhost
+EOF
+openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 7 \
+            -config /tmp/openssl.conf \
+            -keyout /run/systemd/journal-remote-tls/key.pem \
+            -out /run/systemd/journal-remote-tls/cert.pem
+chown -R systemd-journal-remote /run/systemd/journal-remote-tls
+
+# Configure journal-upload to upload journals to journal-remote without client certificates
+mkdir -p /run/systemd/journal-{remote,upload}.conf.d
+cat >/run/systemd/journal-remote.conf.d/99-test.conf <<EOF
+[Remote]
+SplitMode=host
+ServerKeyFile=/run/systemd/journal-remote-tls/key.pem
+ServerCertificateFile=/run/systemd/journal-remote-tls/cert.pem
+TrustedCertificateFile=-
+EOF
+cat >/run/systemd/journal-upload.conf.d/99-test.conf <<EOF
+[Upload]
+URL=https://localhost:19532
+ServerKeyFile=-
+ServerCertificateFile=-
+TrustedCertificateFile=-
+EOF
+systemd-analyze cat-config systemd/journal-remote.conf
+systemd-analyze cat-config systemd/journal-upload.conf
+
+systemctl restart systemd-journal-remote.socket
+systemctl restart systemd-journal-upload
+timeout 15 bash -xec 'until systemctl -q is-active systemd-journal-remote.service; do sleep 1; done'
+systemctl status systemd-journal-{remote,upload}
+
+# It may take a bit until the whole journal is transferred
+timeout 30 bash -xec "until journalctl --directory=/var/log/journal/remote --identifier='$TEST_TAG' --grep='$TEST_MESSAGE'; do sleep 1; done"
+
+systemctl stop systemd-journal-upload
+systemctl stop systemd-journal-remote.{socket,service}
+rm -rf /var/log/journal/remote/*
+
+# Now let's do the same, but with a full PKI setup
+#
+# journal-upload keeps the cursor of the last uploaded message, so let's send a fresh one
+echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
+journalctl --sync
+
+mkdir /run/systemd/remote-pki
+cat >/run/systemd/remote-pki/ca.conf <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = CZ
+L = Brno
+O = Foo
+OU = Bar
+CN = Test CA
+
+[ v3_ca ]
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+basicConstraints = CA:true
+EOF
+cat >/run/systemd/remote-pki/client.conf <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = CZ
+L = Brno
+O = Foo
+OU = Bar
+CN = Test Client
+EOF
+cat >/run/systemd/remote-pki/server.conf <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = CZ
+L = Brno
+O = Foo
+OU = Bar
+CN = localhost
+EOF
+# Generate a dummy CA
+openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 7 \
+            -extensions v3_ca \
+            -config /run/systemd/remote-pki/ca.conf \
+            -keyout /run/systemd/remote-pki/ca.key \
+            -out /run/systemd/remote-pki/ca.crt
+openssl x509 -in /run/systemd/remote-pki/ca.crt -noout -text
+echo 01 >/run/systemd/remote-pki/ca.srl
+# Generate a client key and signing request
+openssl req -nodes -newkey rsa:2048 -sha256 \
+            -config /run/systemd/remote-pki/client.conf \
+            -keyout /run/systemd/remote-pki/client.key \
+            -out /run/systemd/remote-pki/client.csr
+# Sign the request with the CA key
+openssl x509 -req -days 7 \
+             -in /run/systemd/remote-pki/client.csr \
+             -CA /run/systemd/remote-pki/ca.crt \
+             -CAkey /run/systemd/remote-pki/ca.key \
+             -out /run/systemd/remote-pki/client.crt
+# And do the same for the server
+openssl req -nodes -newkey rsa:2048 -sha256 \
+            -config /run/systemd/remote-pki/server.conf \
+            -keyout /run/systemd/remote-pki/server.key \
+            -out /run/systemd/remote-pki/server.csr
+openssl x509 -req -days 7 \
+             -in /run/systemd/remote-pki/server.csr \
+             -CA /run/systemd/remote-pki/ca.crt \
+             -CAkey /run/systemd/remote-pki/ca.key \
+             -out /run/systemd/remote-pki/server.crt
+chown -R systemd-journal-remote:systemd-journal /run/systemd/remote-pki
+chmod -R g+rwX /run/systemd/remote-pki
+
+# Reconfigure journal-upload/journal remote with the new keys
+cat >/run/systemd/journal-remote.conf.d/99-test.conf <<EOF
+[Remote]
+SplitMode=host
+ServerKeyFile=/run/systemd/remote-pki/server.key
+ServerCertificateFile=/run/systemd/remote-pki/server.crt
+TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
+EOF
+cat >/run/systemd/journal-upload.conf.d/99-test.conf <<EOF
+[Upload]
+URL=https://localhost:19532
+ServerKeyFile=/run/systemd/remote-pki/client.key
+ServerCertificateFile=/run/systemd/remote-pki/client.crt
+TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
+EOF
+systemd-analyze cat-config systemd/journal-remote.conf
+systemd-analyze cat-config systemd/journal-upload.conf
+
+systemctl restart systemd-journal-remote.socket
+systemctl restart systemd-journal-upload
+timeout 15 bash -xec 'until systemctl -q is-active systemd-journal-remote.service; do sleep 1; done'
+systemctl status systemd-journal-{remote,upload}
+
+# It may take a bit until the whole journal is transferred
+timeout 30 bash -xec "until journalctl --directory=/var/log/journal/remote --identifier='$TEST_TAG' --grep='$TEST_MESSAGE'; do sleep 1; done"
+
+systemctl stop systemd-journal-upload
+systemctl stop systemd-journal-remote.{socket,service}
+
+# Let's test if journal-remote refuses connection from journal-upload with invalid client certs
+#
+# We should end up with something like this:
+#    systemd-journal-remote[726]: Client is not authorized
+#    systemd-journal-upload[738]: Upload to https://localhost:19532/upload failed with code 401:
+#    systemd[1]: systemd-journal-upload.service: Main process exited, code=exited, status=1/FAILURE
+#    systemd[1]: systemd-journal-upload.service: Failed with result 'exit-code'.
+#
+cat >/run/systemd/journal-upload.conf.d/99-test.conf <<EOF
+[Upload]
+URL=https://localhost:19532
+ServerKeyFile=/run/systemd/journal-remote-tls/key.pem
+ServerCertificateFile=/run/systemd/journal-remote-tls/cert.pem
+TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
+EOF
+systemd-analyze cat-config systemd/journal-upload.conf
+mkdir -p /run/systemd/system/systemd-journal-upload.service.d
+cat >/run/systemd/system/systemd-journal-upload.service.d/99-test.conf <<EOF
+[Service]
+Restart=no
+EOF
+systemctl daemon-reload
+chgrp -R systemd-journal /run/systemd/journal-remote-tls
+chmod -R g+rwX /run/systemd/journal-remote-tls
+
+systemctl restart systemd-journal-upload
+timeout 10 bash -xec 'while [[ "$(systemctl show -P ActiveState systemd-journal-upload)" != failed ]]; do sleep 1; done'
+(! systemctl status systemd-journal-upload)
diff --git a/test/units/TEST-04-JOURNAL.journal.sh b/test/units/TEST-04-JOURNAL.journal.sh
new file mode 100755 (executable)
index 0000000..bb4f66d
--- /dev/null
@@ -0,0 +1,289 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Rotation/flush test, see https://github.com/systemd/systemd/issues/19895
+journalctl --relinquish-var
+[[ "$(systemd-detect-virt -v)" == "qemu" ]] && ITERATIONS=10 || ITERATIONS=50
+for ((i = 0; i < ITERATIONS; i++)); do
+    dd if=/dev/urandom bs=1M count=1 | base64 | systemd-cat
+done
+journalctl --rotate
+# Let's test varlinkctl a bit, i.e. implement the equivalent of 'journalctl --rotate' via varlinkctl
+varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Rotate '{}'
+journalctl --flush
+varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.FlushToVar '{}'
+journalctl --sync
+varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Synchronize '{}'
+journalctl --rotate --vacuum-size=8M
+
+# Reset the ratelimit buckets for the subsequent tests below.
+systemctl restart systemd-journald
+
+# Test stdout stream
+write_and_match() {
+    local input="${1:?}"
+    local expected="${2?}"
+    local id
+    shift 2
+
+    id="$(systemd-id128 new)"
+    echo -ne "$input" | systemd-cat -t "$id" "$@"
+    journalctl --sync
+    diff <(echo -ne "$expected") <(journalctl -b -o cat -t "$id")
+}
+# Skip empty lines
+write_and_match "\n\n\n" "" --level-prefix false
+write_and_match "<5>\n<6>\n<7>\n" "" --level-prefix true
+# Remove trailing spaces
+write_and_match "Trailing spaces \t \n" "Trailing spaces\n" --level-prefix false
+write_and_match "<5>Trailing spaces \t \n" "Trailing spaces\n" --level-prefix true
+# Don't remove leading spaces
+write_and_match " \t Leading spaces\n" " \t Leading spaces\n" --level-prefix false
+write_and_match "<5> \t Leading spaces\n" " \t Leading spaces\n" --level-prefix true
+
+# --output-fields restricts output
+ID="$(systemd-id128 new)"
+echo -ne "foo" | systemd-cat -t "$ID" --level-prefix false
+# Let's test varlinkctl a bit, i.e. implement the equivalent of 'journalctl --sync' via varlinkctl
+varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Synchronize '{}'
+journalctl -b -o export --output-fields=MESSAGE,FOO --output-fields=PRIORITY,MESSAGE -t "$ID" >/tmp/output
+[[ $(wc -l </tmp/output) -eq 9 ]]
+grep -q '^__CURSOR=' /tmp/output
+grep -q '^MESSAGE=foo$' /tmp/output
+grep -q '^PRIORITY=6$' /tmp/output
+(! grep '^FOO=' /tmp/output)
+(! grep '^SYSLOG_FACILITY=' /tmp/output)
+
+# --truncate shows only first line, skip under asan due to logger
+ID="$(systemd-id128 new)"
+echo -e 'HEAD\nTAIL\nTAIL' | systemd-cat -t "$ID"
+journalctl --sync
+journalctl -b -t "$ID" | grep -q HEAD
+journalctl -b -t "$ID" | grep -q TAIL
+journalctl -b -t "$ID" --truncate-newline | grep -q HEAD
+journalctl -b -t "$ID" --truncate-newline | grep -q -v TAIL
+
+# '-b all' negates earlier use of -b (-b and -m are otherwise exclusive)
+journalctl -b -1 -b all -m >/dev/null
+
+# -b always behaves like -b0
+journalctl -q -b-1 -b0 | head -1 >/tmp/expected
+journalctl -q -b-1 -b | head -1 >/tmp/output
+diff /tmp/expected /tmp/output
+# ... even when another option follows (both of these should fail due to -m)
+{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 >/tmp/expected
+{ journalctl -ball -b  -m 2>&1 || :; } | head -1 >/tmp/output
+diff /tmp/expected /tmp/output
+
+# https://github.com/systemd/systemd/issues/13708
+ID=$(systemd-id128 new)
+systemd-cat -t "$ID" bash -c 'echo parent; (echo child) & wait' &
+PID=$!
+wait $PID
+journalctl --sync
+# We can drop this grep when https://github.com/systemd/systemd/issues/13937
+# has a fix.
+journalctl -b -o export -t "$ID" --output-fields=_PID | grep '^_PID=' >/tmp/output
+[[ $(wc -l </tmp/output) -eq 2 ]]
+grep -q "^_PID=$PID" /tmp/output
+grep -vq "^_PID=$PID" /tmp/output
+
+# https://github.com/systemd/systemd/issues/15654
+ID=$(systemd-id128 new)
+printf "This will\nusually fail\nand be truncated\n" >/tmp/expected
+systemd-cat -t "$ID" /bin/sh -c 'env echo -n "This will";echo;env echo -n "usually fail";echo;env echo -n "and be truncated";echo;'
+journalctl --sync
+journalctl -b -o cat -t "$ID" >/tmp/output
+diff /tmp/expected /tmp/output
+[[ $(journalctl -b -o cat -t "$ID" --output-fields=_TRANSPORT | grep -Pc "^stdout$") -eq 3 ]]
+[[ $(journalctl -b -o cat -t "$ID" --output-fields=_LINE_BREAK | grep -Pc "^pid-change$") -eq 3 ]]
+[[ $(journalctl -b -o cat -t "$ID" --output-fields=_PID | sort -u | grep -c "^.*$") -eq 3 ]]
+[[ $(journalctl -b -o cat -t "$ID" --output-fields=MESSAGE | grep -Pc "^(This will|usually fail|and be truncated)$") -eq 3 ]]
+
+# test that LogLevelMax can also suppress logging about services, not only by services
+systemctl start silent-success
+journalctl --sync
+[[ -z "$(journalctl -b -q -u silent-success.service)" ]]
+
+# Test syslog identifiers exclusion
+systemctl start verbose-success.service
+journalctl --sync
+[[ -n "$(journalctl -b -q -u verbose-success.service -t systemd)" ]]
+[[ -n "$(journalctl -b -q -u verbose-success.service -t echo)" ]]
+[[ -n "$(journalctl -b -q -u verbose-success.service -T systemd)" ]]
+[[ -n "$(journalctl -b -q -u verbose-success.service -T echo)" ]]
+[[ -z "$(journalctl -b -q -u verbose-success.service -T echo -T '(echo)' -T sleep -T '(sleep)' -T systemd -T '(systemd)' -T systemd-executor)" ]]
+
+# Exercise the matching machinery
+SYSTEMD_LOG_LEVEL=debug journalctl -b -n 1 /dev/null /dev/zero /dev/null /dev/null /dev/null
+journalctl -b -n 1 /bin/true /bin/false
+journalctl -b -n 1 /bin/true + /bin/false
+journalctl -b -n 1 -r --unit "systemd*"
+
+systemd-run --user -M "testuser@.host" /bin/echo hello
+journalctl --sync
+journalctl -b -n 1 -r --user-unit "*"
+
+(! journalctl -b /dev/lets-hope-this-doesnt-exist)
+(! journalctl -b /dev/null /dev/zero /dev/this-also-shouldnt-exist)
+(! journalctl -b --unit "this-unit-should-not-exist*")
+
+# Facilities & priorities
+journalctl --facility help
+journalctl --facility help | grep -F 'kern'
+journalctl --facility help | grep -F 'mail'
+journalctl --facility kern -n 1
+journalctl --facility syslog --priority 0..3 -n 1
+journalctl --facility syslog --priority 3..0 -n 1
+journalctl --facility user --priority 0..0 -n 1
+journalctl --facility daemon --priority warning -n 1
+journalctl --facility daemon --priority warning..info -n 1
+journalctl --facility daemon --priority notice..crit -n 1
+journalctl --facility daemon --priority 5..crit -n 1
+
+# Assorted combinations
+journalctl -o help
+journalctl -o help | grep -F 'short'
+journalctl -o help | grep -F 'export'
+journalctl -q -n all -a | grep . >/dev/null
+journalctl -q --no-full | grep . >/dev/null
+journalctl -q --user --system | grep . >/dev/null
+journalctl --namespace "*" | grep . >/dev/null
+journalctl --namespace "" | grep . >/dev/null
+journalctl -q --namespace "+foo-bar-baz-$RANDOM" | grep . >/dev/null
+(! journalctl -q --namespace "foo-bar-baz-$RANDOM" | grep .)
+journalctl --root / | grep . >/dev/null
+journalctl --cursor "t=0;t=-1;t=0;t=0x0" | grep . >/dev/null
+journalctl --header | grep system.journal
+journalctl --field _EXE | grep . >/dev/null
+journalctl --no-hostname --utc --catalog | grep . >/dev/null
+# Exercise executable_is_script() and the related code, e.g. `journalctl -b /path/to/a/script.sh` should turn
+# into ((_EXE=/bin/bash AND _COMM=script.sh) AND _BOOT_ID=c002e3683ba14fa8b6c1e12878386514)
+journalctl -b "$(readlink -f "$0")" | grep . >/dev/null
+journalctl -b "$(systemd-id128 boot-id)" | grep . >/dev/null
+journalctl --since yesterday --reverse | grep . >/dev/null
+journalctl --machine .host | grep . >/dev/null
+# Log something that journald will forward to wall
+echo "Oh no!" | systemd-cat -t "emerg$RANDOM" -p emerg --stderr-priority emerg
+
+TAG="$(systemd-id128 new)"
+echo "Foo Bar Baz" | systemd-cat -t "$TAG"
+journalctl --sync
+# Relevant excerpt from journalctl(1):
+#   If the pattern is all lowercase, matching is case insensitive. Otherwise, matching is case sensitive.
+#   This can be overridden with the --case-sensitive option
+journalctl -e -t "$TAG" --grep "Foo Bar Baz"
+journalctl -e -t "$TAG" --grep "foo bar baz"
+(! journalctl -e -t "$TAG" --grep "foo Bar baz")
+journalctl -e -t "$TAG" --case-sensitive=false --grep "foo Bar baz"
+
+(! journalctl --facility hopefully-an-unknown-facility)
+(! journalctl --priority hello-world)
+(! journalctl --priority 0..128)
+(! journalctl --priority 0..systemd)
+
+# Other options
+journalctl --disk-usage
+journalctl --dmesg -n 1
+journalctl --fields
+journalctl --list-boots
+journalctl --update-catalog
+journalctl --list-catalog
+
+# Add new tests before here, the journald restarts below
+# may make tests flappy.
+
+# Don't lose streams on restart
+systemctl start forever-print-hola
+sleep 3
+systemctl restart systemd-journald
+sleep 3
+systemctl stop forever-print-hola
+[[ ! -f "/tmp/i-lose-my-logs" ]]
+
+# https://github.com/systemd/systemd/issues/4408
+rm -f /tmp/i-lose-my-logs
+systemctl start forever-print-hola
+sleep 3
+systemctl kill --signal=SIGKILL systemd-journald
+sleep 3
+[[ ! -f "/tmp/i-lose-my-logs" ]]
+systemctl stop forever-print-hola
+
+set +o pipefail
+# https://github.com/systemd/systemd/issues/15528
+journalctl --follow --file=/var/log/journal/*/* | head -n1 | grep .
+# https://github.com/systemd/systemd/issues/24565
+journalctl --follow --merge | head -n1 | grep .
+set -o pipefail
+
+# https://github.com/systemd/systemd/issues/26746
+rm -f /tmp/issue-26746-log /tmp/issue-26746-cursor
+ID="$(systemd-id128 new)"
+journalctl -t "$ID" --follow --cursor-file=/tmp/issue-26746-cursor | tee /tmp/issue-26746-log &
+systemd-cat -t "$ID" /bin/sh -c 'echo hogehoge'
+# shellcheck disable=SC2016
+timeout 10 bash -c 'until [[ -f /tmp/issue-26746-log && "$(cat /tmp/issue-26746-log)" =~ hogehoge ]]; do sleep .5; done'
+pkill -TERM journalctl
+timeout 10 bash -c 'until test -f /tmp/issue-26746-cursor; do sleep .5; done'
+CURSOR_FROM_FILE="$(cat /tmp/issue-26746-cursor)"
+CURSOR_FROM_JOURNAL="$(journalctl -t "$ID" --output=export MESSAGE=hogehoge | sed -n -e '/__CURSOR=/ { s/__CURSOR=//; p }')"
+test "$CURSOR_FROM_FILE" = "$CURSOR_FROM_JOURNAL"
+
+# Check that the seqnum field at least superficially works
+systemd-cat echo "ya"
+journalctl --sync
+SEQNUM1=$(journalctl -o export -n 1 | grep -Ea "^__SEQNUM=" | cut -d= -f2)
+systemd-cat echo "yo"
+journalctl --sync
+SEQNUM2=$(journalctl -o export -n 1 | grep -Ea "^__SEQNUM=" | cut -d= -f2)
+test "$SEQNUM2" -gt "$SEQNUM1"
+
+# Test for journals without RTC
+# See: https://github.com/systemd/systemd/issues/662
+JOURNAL_DIR="$(mktemp -d)"
+while read -r file; do
+    filename="${file##*/}"
+    unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}"
+done < <(find /usr/lib/systemd/tests/testdata/test-journals/no-rtc -name "*.zst")
+
+journalctl --directory="$JOURNAL_DIR" --list-boots --output=json >/tmp/lb1
+diff -u /tmp/lb1 - <<'EOF'
+[{"index":-3,"boot_id":"5ea5fc4f82a14186b5332a788ef9435e","first_entry":1666569600994371,"last_entry":1666584266223608},{"index":-2,"boot_id":"bea6864f21ad4c9594c04a99d89948b0","first_entry":1666569601005945,"last_entry":1666584347230411},{"index":-1,"boot_id":"4c708e1fd0744336be16f3931aa861fb","first_entry":1666569601017222,"last_entry":1666584354649355},{"index":0,"boot_id":"35e8501129134edd9df5267c49f744a4","first_entry":1666569601009823,"last_entry":1666584438086856}]
+EOF
+rm -rf "$JOURNAL_DIR" /tmp/lb1
+
+# Check that using --after-cursor/--cursor-file= together with journal filters doesn't
+# skip over entries matched by the filter
+# See: https://github.com/systemd/systemd/issues/30288
+UNIT_NAME="test-cursor-$RANDOM.service"
+CURSOR_FILE="$(mktemp)"
+# Generate some messages we can match against
+journalctl --cursor-file="$CURSOR_FILE" -n1
+systemd-run --unit="$UNIT_NAME" --wait --service-type=exec bash -ec "sleep 2; set -x; echo hello; echo world; set +x; sleep 2"
+journalctl --sync
+# --after-cursor= + --unit=
+# The format of the "Starting ..." message depends on StatusUnitFormat=, so match only the beginning
+# which should be enough in this case
+[[ "$(journalctl -n 1 -p info -o cat --unit="$UNIT_NAME" --after-cursor="$(<"$CURSOR_FILE")" _PID=1 )" =~ ^Starting\  ]]
+# There should be no such messages before the cursor
+[[ -z "$(journalctl -n 1 -p info -o cat --unit="$UNIT_NAME" --after-cursor="$(<"$CURSOR_FILE")" --reverse)" ]]
+# --cursor-file= + a journal filter
+diff <(journalctl --cursor-file="$CURSOR_FILE" -p info -o cat _SYSTEMD_UNIT="$UNIT_NAME") - <<EOF
++ echo hello
+hello
++ echo world
+world
++ set +x
+EOF
+rm -f "$CURSOR_FILE"
+
+# Check that --until works with --after-cursor and --lines/-n
+# See: https://github.com/systemd/systemd/issues/31776
+CURSOR_FILE="$(mktemp)"
+journalctl -q -n 0 --cursor-file="$CURSOR_FILE"
+TIMESTAMP="$(journalctl -q -n 1 --cursor="$(<"$CURSOR_FILE")" --output=short-unix | cut -d ' ' -f 1 | cut -d '.' -f 1)"
+[[ -z "$(journalctl -q -n 10 --after-cursor="$(<"$CURSOR_FILE")" --until "@$((TIMESTAMP - 3))")" ]]
+rm -f "$CURSOR_FILE"
diff --git a/test/units/TEST-04-JOURNAL.service b/test/units/TEST-04-JOURNAL.service
new file mode 100644 (file)
index 0000000..63a0104
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-04-JOURNAL
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-04-JOURNAL.sh b/test/units/TEST-04-JOURNAL.sh
new file mode 100755 (executable)
index 0000000..9c2a033
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
diff --git a/test/units/TEST-05-RLIMITS.effective-limit.sh b/test/units/TEST-05-RLIMITS.effective-limit.sh
new file mode 100755 (executable)
index 0000000..3ff8e83
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+pre=test05
+cat >/run/systemd/system/"$pre"alpha.slice <<EOF
+[Slice]
+MemoryMax=40M
+MemoryHigh=40M
+TasksMax=400
+EOF
+
+cat >/run/systemd/system/"$pre"alpha-beta.slice <<EOF
+[Slice]
+MemoryMax=10M
+MemoryHigh=10M
+TasksMax=100
+EOF
+
+cat >/run/systemd/system/"$pre"alpha-beta-gamma.slice <<EOF
+[Slice]
+MemoryMax=20M
+MemoryHigh=20M
+TasksMax=200
+EOF
+
+systemctl daemon-reload
+
+srv=probe.service
+slc0="$pre"alpha.slice
+slc="$pre"alpha-beta-gamma.slice
+
+systemd-run --unit "$srv" --slice "$slc"  \
+    -p MemoryMax=5M \
+    -p MemoryHigh=5M \
+    -p TasksMax=50 \
+    sleep inf
+
+# Compare with inequality because test can run in a constrained container
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
+
+systemctl stop "$srv"
+
+systemd-run --unit "$srv" --slice "$slc"  \
+    sleep inf
+
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "10485760"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "10485760"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "100"
+
+systemctl set-property "$slc0" \
+    MemoryMax=5M \
+    MemoryHigh=5M \
+    TasksMax=50
+
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
+
+systemctl stop "$srv"
+
+rm -f /run/systemd/system/"$pre"* || :
diff --git a/test/units/TEST-05-RLIMITS.rlimit.sh b/test/units/TEST-05-RLIMITS.rlimit.sh
new file mode 100755 (executable)
index 0000000..6b425d4
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+P=/run/systemd/system.conf.d
+mkdir $P
+
+cat >$P/rlimits.conf <<EOF
+[Manager]
+DefaultLimitNOFILE=10000:16384
+EOF
+
+systemctl daemon-reload
+
+[[ "$(systemctl show -P DefaultLimitNOFILESoft)" = "10000" ]]
+[[ "$(systemctl show -P DefaultLimitNOFILE)" = "16384" ]]
+
+[[ "$(systemctl show -P LimitNOFILESoft TEST-05-RLIMITS.service)" = "10000" ]]
+[[ "$(systemctl show -P LimitNOFILE TEST-05-RLIMITS.service)" = "16384" ]]
+
+# shellcheck disable=SC2016
+systemd-run --wait -t bash -c '[[ "$(ulimit -n -S)" = "10000" ]]'
+# shellcheck disable=SC2016
+systemd-run --wait -t bash -c '[[ "$(ulimit -n -H)" = "16384" ]]'
diff --git a/test/units/TEST-05-RLIMITS.service b/test/units/TEST-05-RLIMITS.service
new file mode 100644 (file)
index 0000000..ab72d8f
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-05-RLIMITS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-05-RLIMITS.sh b/test/units/TEST-05-RLIMITS.sh
new file mode 100755 (executable)
index 0000000..9c2a033
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
diff --git a/test/units/TEST-06-SELINUX.service b/test/units/TEST-06-SELINUX.service
new file mode 100644 (file)
index 0000000..c4c1d87
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-06-SELINUX
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-06-SELINUX.sh b/test/units/TEST-06-SELINUX.sh
new file mode 100755 (executable)
index 0000000..937a040
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+. /etc/os-release
+if ! [[ "$ID" =~ centos|fedora ]]; then
+    echo "Skipping because only CentOS and Fedora support SELinux tests" >>/skipped
+    exit 77
+fi
+
+# Note: ATTOW the following checks should work with both Fedora and upstream reference policy
+#       (with or without MCS/MLS)
+
+sestatus
+
+# We should end up in permissive mode
+[[ "$(getenforce)" == "Permissive" ]]
+
+# Check PID 1's context
+PID1_CONTEXT="$(ps -h -o label 1)"
+[[ "$PID1_CONTEXT" =~ ^system_u:system_r:init_t(:s0)?$ ]]
+# The same label should be attached to all PID 1's journal messages
+journalctl -q -b -p info -n 5 --grep . _SELINUX_CONTEXT="$PID1_CONTEXT"
+
+# Check context on a couple of arbitrarily-selected files/directories
+[[ "$(stat --printf %C /run/systemd/journal/)" =~ ^system_u:object_r:(syslogd_runtime_t|syslogd_var_run_t)(:s0)?$ ]]
+[[ "$(stat --printf %C /run/systemd/notify)" =~ ^system_u:object_r:(init_runtime_t|init_var_run_t)(:s0)?$ ]]
+[[ "$(stat --printf %C /run/systemd/sessions/)" =~ ^system_u:object_r:(systemd_sessions_runtime_t|systemd_logind_sessions_t)(:s0)?$ ]]
+
+# Check if our SELinux-related functionality works
+#
+# Since the SELinux policies vary wildly, use a context from some existing file
+# as our test context
+CONTEXT="$(stat -c %C /proc/sys/kernel/core_pattern)"
+
+[[ "$(systemd-run --wait --pipe -p SELinuxContext="$CONTEXT" cat /proc/self/attr/current | tr -d '\0')" == "$CONTEXT" ]]
+(! systemd-run --wait --pipe -p SELinuxContext="foo:bar:baz" cat /proc/self/attr/current)
+(! systemd-run --wait --pipe -p ConditionSecurity='selinux' false)
+systemd-run --wait --pipe -p ConditionSecurity='!selinux' false
+
+NSPAWN_ARGS=(systemd-nspawn -q --volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id)
+[[ "$("${NSPAWN_ARGS[@]}" cat /proc/self/attr/current | tr -d '\0')" != "$CONTEXT" ]]
+[[ "$("${NSPAWN_ARGS[@]}" --selinux-context="$CONTEXT" cat /proc/self/attr/current | tr -d '\0')" == "$CONTEXT" ]]
+[[ "$("${NSPAWN_ARGS[@]}" stat --printf %C /run)" != "$CONTEXT" ]]
+[[ "$("${NSPAWN_ARGS[@]}" --selinux-apifs-context="$CONTEXT" stat --printf %C /run)" == "$CONTEXT" ]]
+[[ "$("${NSPAWN_ARGS[@]}" --selinux-apifs-context="$CONTEXT" --tmpfs=/tmp stat --printf %C /tmp)" == "$CONTEXT" ]]
+
+touch /testok
diff --git a/test/units/TEST-07-PID1.aux-scope.sh b/test/units/TEST-07-PID1.aux-scope.sh
new file mode 100755 (executable)
index 0000000..8e2a99c
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+export SYSTEMD_PAGER=cat
+
+if ! grep -q pidfd_open /proc/kallsyms; then
+    echo "pidfds not available, skipping the test..."
+    exit 0
+fi
+
+systemd-run --unit test-aux-scope.service \
+            --service-type notify -p Slice=aux.slice -p TasksMax=99 -p CPUWeight=199 -p IPAccounting=yes \
+            /usr/lib/systemd/tests/unit-tests/manual/test-aux-scope
+kill -s USR1 "$(systemctl show --value --property MainPID test-aux-scope.service)"
+
+timeout 30s bash -xec 'until systemctl is-active test-aux-scope.scope; do sleep 1; done'
+
+systemctl status test-aux-scope.service
+# shellcheck disable=SC2009
+test "$(ps -eo pid,unit | grep -c test-aux-scope.service)" = 1
+
+systemctl status test-aux-scope.scope
+# shellcheck disable=SC2009
+test "$(ps -eo pid,unit | grep -c test-aux-scope.scope)" = 10
+
+test "$(systemctl show -p Slice --value test-aux-scope.scope)" = aux.slice
+test "$(systemctl show -p TasksMax --value test-aux-scope.scope)" = 99
+test "$(systemctl show -p CPUWeight --value test-aux-scope.scope)" = 199
+test "$(systemctl show -p IPAccounting --value test-aux-scope.scope)" = yes
+
+systemctl stop test-aux-scope.scope
+systemctl stop test-aux-scope.service
diff --git a/test/units/TEST-07-PID1.exec-context.sh b/test/units/TEST-07-PID1.exec-context.sh
new file mode 100755 (executable)
index 0000000..a3379ef
--- /dev/null
@@ -0,0 +1,386 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Make sure the unit's exec context matches its configuration
+# See: https://github.com/systemd/systemd/pull/29552
+
+# Even though hidepid= was introduced in kernel 3.3, we support only
+# the post 5.8 implementation that allows us to apply the option per-instance,
+# instead of the whole namespace. To distinguish between these two implementations
+# lets check if we can mount procfs with a named value (e.g. hidepid=off), since
+# support for this was introduced in the same commit as the per-instance stuff
+proc_supports_option() {
+    local option="${1:?}"
+    local proc_tmp ec
+
+    proc_tmp="$(mktemp -d)"
+    mount -t proc -o "$option" proc "$proc_tmp" && ec=0 || ec=$?
+    mountpoint -q "$proc_tmp" && umount -q "$proc_tmp"
+    rm -rf "$proc_tmp"
+
+    return $ec
+}
+
+# In coverage builds we disable ProtectSystem= and ProtectHome= via a service.d
+# dropin in /etc. This dropin has, unfortunately, higher priority than
+# the transient stuff from systemd-run. Let's just skip the following tests
+# in that case instead of complicating the test setup even more */
+if [[ -z "${COVERAGE_BUILD_DIR:-}" ]]; then
+    if ! systemd-detect-virt -cq && command -v bootctl >/dev/null; then
+        boot_path="$(bootctl --print-boot-path)"
+        esp_path="$(bootctl --print-esp-path)"
+
+        # If the mount points are handled by automount units, make sure we trigger
+        # them before proceeding further
+        ls -l "$boot_path" "$esp_path"
+    fi
+
+    systemd-run --wait --pipe -p ProtectSystem=yes \
+        bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test -w /etc; test -w /var"
+    systemd-run --wait --pipe -p ProtectSystem=full \
+        bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test ! -w /etc; test -w /var"
+    systemd-run --wait --pipe -p ProtectSystem=strict \
+        bash -xec "test ! -w /; test ! -w /etc; test ! -w /var; test -w /dev; test -w /proc"
+    systemd-run --wait --pipe -p ProtectSystem=no \
+        bash -xec "test -w /; test -w /etc; test -w /var; test -w /dev; test -w /proc"
+
+    MARK="$(mktemp /root/.exec-context.XXX)"
+    systemd-run --wait --pipe -p ProtectHome=yes \
+        bash -xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test ! -e $MARK"
+    systemd-run --wait --pipe -p ProtectHome=read-only \
+        bash -xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test -e $MARK"
+    systemd-run --wait --pipe -p ProtectHome=tmpfs \
+        bash -xec "test -w /home; test -w /root; test -w /run/user; test ! -e $MARK"
+    systemd-run --wait --pipe -p ProtectHome=no \
+        bash -xec "test -w /home; test -w /root; test -w /run/user; test -e $MARK"
+    rm -f "$MARK"
+fi
+
+if proc_supports_option "hidepid=off"; then
+    systemd-run --wait --pipe -p ProtectProc=noaccess -p User=testuser \
+        bash -xec 'test -e /proc/1; test ! -r /proc/1; test -r /proc/$$$$/comm'
+    systemd-run --wait --pipe -p ProtectProc=invisible -p User=testuser \
+        bash -xec 'test ! -e /proc/1; test -r /proc/$$$$/comm'
+    systemd-run --wait --pipe -p ProtectProc=ptraceable -p User=testuser \
+        bash -xec 'test ! -e /proc/1; test -r /proc/$$$$/comm'
+    systemd-run --wait --pipe -p ProtectProc=ptraceable -p User=testuser -p AmbientCapabilities=CAP_SYS_PTRACE \
+        bash -xec 'test -r /proc/1; test -r /proc/$$$$/comm'
+    systemd-run --wait --pipe -p ProtectProc=default -p User=testuser \
+        bash -xec 'test -r /proc/1; test -r /proc/$$$$/comm'
+fi
+
+if proc_supports_option "subset=pid"; then
+    systemd-run --wait --pipe -p ProcSubset=pid -p User=testuser \
+        bash -xec "test -r /proc/1/comm; test ! -e /proc/cpuinfo"
+    systemd-run --wait --pipe -p ProcSubset=all -p User=testuser \
+        bash -xec "test -r /proc/1/comm; test -r /proc/cpuinfo"
+fi
+
+if ! systemd-detect-virt -cq; then
+    systemd-run --wait --pipe -p ProtectKernelLogs=yes -p User=testuser \
+        bash -xec "test ! -r /dev/kmsg"
+    systemd-run --wait --pipe -p ProtectKernelLogs=no -p User=testuser \
+        bash -xec "test -r /dev/kmsg"
+fi
+
+systemd-run --wait --pipe -p BindPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
+    bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; ! mountpoint /usr"
+systemd-run --wait --pipe -p BindReadOnlyPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
+    bash -xec "test ! -w /etc; test ! -w /mnt; ! mountpoint /usr"
+# Make sure we properly serialize/deserialize paths with spaces
+# See: https://github.com/systemd/systemd/issues/30747
+touch "/tmp/test file with spaces"
+systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces" \
+    bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/test file with spaces'"
+systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces:/tmp/destination\ wi\:th\ spaces" \
+    bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/destination wi:th spaces'"
+
+# Check if we correctly serialize, deserialize, and set directives that
+# have more complex internal handling
+if ! systemd-detect-virt -cq; then
+    # Funny detail: this originally used the underlying rootfs device, but that,
+    # for some reason, caused "divide error" in kernel, followed by a kernel panic
+    TEMPFILE="$(mktemp)"
+    LODEV="$(losetup --show -f "$TEMPFILE")"
+    ROOT_DEV_MAJ_MIN="$(lsblk -nro MAJ:MIN "$LODEV")"
+    EXPECTED_IO_MAX="$ROOT_DEV_MAJ_MIN rbps=1000 wbps=1000000000000 riops=2000000000 wiops=4000"
+    EXPECTED_IO_LATENCY="$ROOT_DEV_MAJ_MIN target=69000"
+    SERVICE_NAME="test-io-directives-$RANDOM.service"
+    CGROUP_PATH="/sys/fs/cgroup/system.slice/$SERVICE_NAME"
+
+    # IO*=
+    ARGUMENTS=(
+        # Throw in a couple of invalid entries just to test things out
+        -p IOReadBandwidthMax="/foo/bar 1M"
+        -p IOReadBandwidthMax="/foo/baz 1M"
+        -p IOReadBandwidthMax="$LODEV 1M"
+        -p IOReadBandwidthMax="$LODEV 1K"
+        -p IOWriteBandwidthMax="$LODEV 1G"
+        -p IOWriteBandwidthMax="$LODEV 1T"
+        -p IOReadIOPSMax="$LODEV 2G"
+        -p IOWriteIOPSMax="$LODEV 4K"
+        -p IODeviceLatencyTargetSec="$LODEV 666ms"
+        -p IODeviceLatencyTargetSec="/foo/bar 69ms"
+        -p IODeviceLatencyTargetSec="$LODEV 69ms"
+        -p IOReadBandwidthMax="/foo/bar 1M"
+        -p IOReadBandwidthMax="/foo/baz 1M"
+        # TODO: IODeviceWeight= doesn't work on loop devices and virtual disks
+        -p IODeviceWeight="$LODEV 999"
+        -p IODeviceWeight="/foo/bar 999"
+    )
+
+    systemctl set-property system.slice IOAccounting=yes
+    # io.latency not available by default on Debian stable
+    if [[ -e /sys/fs/cgroup/system.slice/io.latency ]]; then
+        systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
+            bash -xec "diff <(echo $EXPECTED_IO_MAX) $CGROUP_PATH/io.max; diff <(echo $EXPECTED_IO_LATENCY) $CGROUP_PATH/io.latency"
+    fi
+
+    # CPUScheduling=
+    ARGUMENTS=(
+        -p CPUSchedulingPolicy=rr   # ID: 2
+        -p CPUSchedulingPolicy=fifo # ID: 1
+        -p CPUSchedulingPriority=5  # Actual prio: 94 (99 - prio)
+        -p CPUSchedulingPriority=10 # Actual prio: 89 (99 - prio)
+    )
+
+    systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
+        bash -xec 'grep -E "^policy\s*:\s*1$" /proc/self/sched; grep -E "^prio\s*:\s*89$" /proc/self/sched'
+
+    # Device*=
+    ARGUMENTS=(
+        -p DevicePolicy=closed
+        -p DevicePolicy=strict
+        -p DeviceAllow="char-mem rm"  # Allow read & mknod for /dev/{null,zero,...}
+        -p DeviceAllow="$LODEV rw"
+        -p DeviceAllow="$LODEV w" # Allow write for the loop
+        # Everything else should be disallowed per the strict policy
+    )
+
+    systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
+        bash -xec "test -r /dev/null; test ! -w /dev/null; test ! -r $LODEV; test -w $LODEV; test ! -r /dev/tty; test ! -w /dev/tty"
+
+    if ! systemctl --version | grep -qF -- "-BPF_FRAMEWORK"; then
+        # SocketBind*=
+        ARGUMENTS=(
+            -p SocketBindAllow=
+            -p SocketBindAllow=1234
+            -p SocketBindAllow=ipv4:udp:any
+            -p SocketBindAllow=ipv6:6666
+            # Everything but the last assignment is superfluous, but it still exercises
+            # the parsing machinery
+            -p SocketBindDeny=
+            -p SocketBindDeny=1111
+            -p SocketBindDeny=ipv4:1111
+            -p SocketBindDeny=ipv4:any
+            -p SocketBindDeny=ipv4:tcp:any
+            -p SocketBindDeny=ipv4:udp:10000-11000
+            -p SocketBindDeny=ipv6:1111
+            -p SocketBindDeny=any
+        )
+
+        # We should fail with EPERM when trying to bind to a socket not on the allow list
+        # (nc exits with 2 in that case)
+        systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
+            bash -xec 'timeout 1s nc -l 127.0.0.1 9999; exit 42'
+        systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
+            bash -xec 'timeout 1s nc -l ::1 9999; exit 42'
+        systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
+            bash -xec 'timeout 1s nc -6 -u -l ::1 9999; exit 42'
+        systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
+            bash -xec 'timeout 1s nc -4 -l 127.0.0.1 6666; exit 42'
+        systemd-run --wait -p SuccessExitStatus="1 2" --pipe -p SocketBindDeny=any \
+            bash -xec 'timeout 1s nc -l 127.0.0.1 9999; exit 42'
+        # Consequently, we should succeed when binding to a socket on the allow list
+        # and keep listening on it until we're killed by `timeout` (EC 124)
+        systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
+            bash -xec 'timeout 1s nc -4 -l 127.0.0.1 1234; exit 1'
+        systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
+            bash -xec 'timeout 1s nc -4 -u -l 127.0.0.1 5678; exit 1'
+        systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
+            bash -xec 'timeout 1s nc -6 -l ::1 1234; exit 1'
+        systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
+            bash -xec 'timeout 1s nc -6 -l ::1 6666; exit 1'
+    fi
+
+    losetup -d "$LODEV"
+    rm -f "$TEMPFILE"
+fi
+
+# {Cache,Configuration,Logs,Runtime,State}Directory=
+ARGUMENTS=(
+    -p CacheDirectory="foo/bar/baz also\ with\ spaces"
+    -p CacheDirectory="foo"
+    -p CacheDirectory="context"
+    -p CacheDirectoryMode="0123"
+    -p CacheDirectoryMode="0666"
+    -p ConfigurationDirectory="context/foo also_context/bar context/nested/baz context/semi\:colon"
+    -p ConfigurationDirectoryMode="0400"
+    -p LogsDirectory="context/foo"
+    -p LogsDirectory=""
+    -p LogsDirectory="context/a/very/nested/logs/dir"
+    -p RuntimeDirectory="context/with\ spaces"
+    # Note: {Runtime,State,Cache,Logs}Directory= directives support the directory:symlink syntax, which
+    #       requires an additional level of escaping for the colon character
+    -p RuntimeDirectory="also_context:a\ symlink\ with\ \\\:\ col\\\:ons\ and\ \ spaces"
+    -p RuntimeDirectoryPreserve=yes
+    -p StateDirectory="context"
+    -p StateDirectory="./././././././context context context"
+    -p StateDirectoryMode="0000"
+)
+
+rm -rf /run/context
+systemd-run --wait --pipe "${ARGUMENTS[@]}" \
+    bash -xec '[[ $CACHE_DIRECTORY == "/var/cache/also with spaces:/var/cache/context:/var/cache/foo:/var/cache/foo/bar/baz" ]];
+               [[ $(stat -c "%a" "${CACHE_DIRECTORY##*:}") == 666 ]]'
+systemd-run --wait --pipe "${ARGUMENTS[@]}" \
+    bash -xec '[[ $CONFIGURATION_DIRECTORY == /etc/also_context/bar:/etc/context/foo:/etc/context/nested/baz:/etc/context/semi:colon ]];
+               [[ $(stat -c "%a" "${CONFIGURATION_DIRECTORY%%:*}") == 400 ]]'
+systemd-run --wait --pipe "${ARGUMENTS[@]}" \
+    bash -xec '[[ $LOGS_DIRECTORY == /var/log/context/a/very/nested/logs/dir:/var/log/context/foo ]];
+               [[ $(stat -c "%a" "${LOGS_DIRECTORY##*:}") == 755 ]]'
+systemd-run --wait --pipe "${ARGUMENTS[@]}" \
+    bash -xec '[[ $RUNTIME_DIRECTORY == "/run/also_context:/run/context/with spaces" ]];
+               [[ $(stat -c "%a" "${RUNTIME_DIRECTORY##*:}") == 755 ]];
+               [[ $(stat -c "%a" "${RUNTIME_DIRECTORY%%:*}") == 755 ]]'
+systemd-run --wait --pipe "${ARGUMENTS[@]}" \
+    bash -xec '[[ $STATE_DIRECTORY == /var/lib/context ]]; [[ $(stat -c "%a" $STATE_DIRECTORY) == 0 ]]'
+test -d "/run/context/with spaces"
+test -s "/run/a symlink with : col:ons and  spaces"
+rm -rf /var/{cache,lib,log}/context /etc/{also_,}context
+
+# Limit*=
+#
+# Note: keep limits of LimitDATA= and LimitAS= unlimited, otherwise ASan (LSan)
+# won't be able to mmap the shadow maps
+ARGUMENTS=(
+    -p LimitCPU=15
+    -p LimitCPU=10:15         # ulimit -t
+    -p LimitFSIZE=96G         # ulimit -f
+    -p LimitDATA=8T:infinity
+    -p LimitDATA=infinity     # ulimit -d
+    -p LimitSTACK=8M          # ulimit -s
+    -p LimitCORE=infinity
+    -p LimitCORE=17M          # ulimit -c
+    -p LimitRSS=27G           # ulimit -m
+    -p LimitNOFILE=7:127      # ulimit -n
+    -p LimitAS=infinity       # ulimit -v
+    -p LimitNPROC=1
+    -p LimitNPROC=64:infinity # ulimit -u
+    -p LimitMEMLOCK=37M       # ulimit -l
+    -p LimitLOCKS=19:1021     # ulimit -x
+    -p LimitSIGPENDING=21     # ulimit -i
+    -p LimitMSGQUEUE=666      # ulimit -q
+    -p LimitNICE=4            # ulimit -e
+    -p LimitRTPRIO=8          # ulimit -r
+    -p LimitRTTIME=666666     # ulimit -R
+)
+# Do all the checks in one giant inline shell blob to avoid the overhead of spawning
+# a new service for each check
+#
+# Note: ulimit shows storage-related values in 1024-byte increments*
+# Note2: ulimit -R requires bash >= 5.1
+#
+# * in POSIX mode -c a -f options show values in 512-byte increments; let's hope
+#   we never run in the POSIX mode
+systemd-run --wait --pipe "${ARGUMENTS[@]}" \
+    bash -xec 'KB=1; MB=$((KB * 1024)); GB=$((MB * 1024)); TB=$((GB * 1024));
+               : CPU;        [[ $(ulimit -St) -eq 10 ]];           [[ $(ulimit -Ht) -eq 15 ]];
+               : FSIZE;      [[ $(ulimit -Sf) -eq $((96 * GB)) ]]; [[ $(ulimit -Hf) -eq $((96 * GB)) ]];
+               : DATA;       [[ $(ulimit -Sd) == unlimited  ]];    [[ $(ulimit -Hd) == unlimited ]];
+               : STACK;      [[ $(ulimit -Ss) -eq $((8 * MB)) ]];  [[ $(ulimit -Hs) -eq $((8 * MB)) ]];
+               : CORE;       [[ $(ulimit -Sc) -eq $((17 * MB)) ]]; [[ $(ulimit -Hc) -eq $((17 * MB)) ]];
+               : RSS;        [[ $(ulimit -Sm) -eq $((27 * GB)) ]]; [[ $(ulimit -Hm) -eq $((27 * GB)) ]];
+               : NOFILE;     [[ $(ulimit -Sn) -eq 7 ]];            [[ $(ulimit -Hn) -eq 127 ]];
+               : AS;         [[ $(ulimit -Sv) == unlimited ]];     [[ $(ulimit -Hv) == unlimited ]];
+               : NPROC;      [[ $(ulimit -Su) -eq 64 ]];           [[ $(ulimit -Hu) == unlimited ]];
+               : MEMLOCK;    [[ $(ulimit -Sl) -eq $((37 * MB)) ]]; [[ $(ulimit -Hl) -eq $((37 * MB)) ]];
+               : LOCKS;      [[ $(ulimit -Sx) -eq 19 ]];           [[ $(ulimit -Hx) -eq 1021 ]];
+               : SIGPENDING; [[ $(ulimit -Si) -eq 21 ]];           [[ $(ulimit -Hi) -eq 21 ]];
+               : MSGQUEUE;   [[ $(ulimit -Sq) -eq 666 ]];          [[ $(ulimit -Hq) -eq 666 ]];
+               : NICE;       [[ $(ulimit -Se) -eq 4 ]];            [[ $(ulimit -He) -eq 4 ]];
+               : RTPRIO;     [[ $(ulimit -Sr) -eq 8 ]];            [[ $(ulimit -Hr) -eq 8 ]];
+               ulimit -R || exit 0;
+               : RTTIME;     [[ $(ulimit -SR) -eq 666666 ]];       [[ $(ulimit -HR) -eq 666666 ]];'
+
+# RestrictFileSystems=
+#
+# Note: running instrumented binaries requires at least /proc to be accessible, so let's
+#       skip the test when we're running under sanitizers
+#
+# Note: $GCOV_ERROR_LOG is used during coverage runs to suppress errors when creating *.gcda files,
+#       since gcov can't access the restricted filesystem (as expected)
+if [[ ! -v ASAN_OPTIONS ]] && systemctl --version | grep "+BPF_FRAMEWORK" && kernel_supports_lsm bpf; then
+    ROOTFS="$(df --output=fstype /usr/bin | sed --quiet 2p)"
+    systemd-run --wait --pipe -p RestrictFileSystems="" ls /
+    systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar" ls /
+    (! systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS" ls /proc)
+    (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="foo" ls /)
+    systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar baz proc" ls /proc
+    systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /proc
+    systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /sys/fs/cgroup
+
+    systemd-run --wait --pipe -p RestrictFileSystems="~" ls /
+    systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls /
+    systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls /
+    (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="~$ROOTFS" ls /)
+    (! systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls /proc)
+    (! systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls /proc)
+    (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /proc)
+    (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /sys)
+    systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /
+    (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /proc)
+    (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /dev)
+    (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /sys)
+fi
+
+# Make sure we properly (de)serialize various string arrays, including whitespaces
+# See: https://github.com/systemd/systemd/issues/31214
+systemd-run --wait --pipe -p Environment="FOO='bar4    '" \
+            bash -xec '[[ $FOO == "bar4    " ]]'
+systemd-run --wait --pipe -p Environment="FOO='bar4    ' BAR='\n\n'" \
+            bash -xec "[[ \$FOO == 'bar4    ' && \$BAR == $'\n\n' ]]"
+systemd-run --wait --pipe -p Environment='FOO="bar4  \\  "' -p Environment="BAR='\n\t'" \
+            bash -xec "[[ \$FOO == 'bar4  \\  ' && \$BAR == $'\n\t' ]]"
+TEST_ENV_FILE="/tmp/test-env-file-$RANDOM-    "
+cat >"$TEST_ENV_FILE" <<EOF
+FOO="env file    "
+BAR="
+    "
+EOF
+systemd-run --wait --pipe cat "$TEST_ENV_FILE"
+systemd-run --wait --pipe -p ReadOnlyPaths="'$TEST_ENV_FILE'" \
+            bash -xec '[[ ! -w "$TEST_ENV_FILE" ]]'
+systemd-run --wait --pipe -p PrivateTmp=yes -p BindReadOnlyPaths="'$TEST_ENV_FILE':'/tmp/bar-    '" \
+            bash -xec '[[ -e "/tmp/bar-    " && ! -w "/tmp/bar-    " ]]'
+systemd-run --wait --pipe -p EnvironmentFile="$TEST_ENV_FILE" \
+            bash -xec "[[ \$FOO == 'env file    ' && \$BAR == $'\n    ' ]]"
+rm -f "$TEST_ENV_FILE"
+# manager_serialize()/manager_deserialize() uses similar machinery
+systemctl unset-environment FOO_WITH_SPACES
+systemctl set-environment FOO_WITH_SPACES="foo   " FOO_WITH_TABS="foo\t\t\t"
+systemctl show-environment
+systemctl show-environment | grep -F "FOO_WITH_SPACES=$'foo   '"
+systemctl show-environment | grep -F "FOO_WITH_TABS=$'foo\\\\t\\\\t\\\\t'"
+systemctl daemon-reexec
+systemctl show-environment
+systemctl show-environment | grep -F "FOO_WITH_SPACES=$'foo   '"
+systemctl show-environment | grep -F "FOO_WITH_TABS=$'foo\\\\t\\\\t\\\\t'"
+
+# Ensure that clean-up codepaths work correctly if activation ultimately fails
+touch /run/not-a-directory
+mkdir /tmp/root
+touch /tmp/root/foo
+chmod +x /tmp/root/foo
+(! systemd-run --wait --pipe false)
+(! systemd-run --wait --pipe --unit "test-dynamicuser-fail" -p DynamicUser=yes -p WorkingDirectory=/nonexistent true)
+(! systemd-run --wait --pipe -p RuntimeDirectory=not-a-directory true)
+(! systemd-run --wait --pipe -p RootDirectory=/tmp/root this-shouldnt-exist)
+(! systemd-run --wait --pipe -p RootDirectory=/tmp/root /foo)
+(! systemd-run --wait --pipe --service-type=oneshot -p ExecStartPre=-/foo/bar/baz -p ExecStart=-/foo/bar/baz -p RootDirectory=/tmp/root -- "- foo")
diff --git a/test/units/TEST-07-PID1.exec-deserialization.sh b/test/units/TEST-07-PID1.exec-deserialization.sh
new file mode 100755 (executable)
index 0000000..04f17c8
--- /dev/null
@@ -0,0 +1,221 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+setup_base_unit() {
+    local unit_path="${1:?}"
+    local log_file="${2:?}"
+    local unit_name="${unit_path##*/}"
+
+    cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+EOF
+    systemctl daemon-reload
+
+    systemctl --job-mode=replace --no-block start "$unit_name"
+    # Wait until the unit leaves the "inactive" state
+    timeout 5s bash -xec "while [[ \"\$(systemctl show -P ActiveState $unit_name)\" == inactive ]]; do sleep .1; done"
+    # Sleep for 1 second from the unit start to get well "into" the first (or second) ExecStart= directive
+    sleep 1
+}
+
+check_output() {
+    local unit_name="${1:?}"
+    local log_file="${2:?}"
+    local expected="${3?}"
+    local unit_name="${unit_path##*/}"
+
+    # Wait until the unit becomes inactive before checking the log
+    timeout 10s bash -xec "while [[ \"\$(systemctl show -P ActiveState $unit_name)\" != inactive ]]; do sleep .5; done"
+
+    diff "$log_file" <(echo -ne "$expected")
+}
+
+testcase_no_change() {
+    local unit_path log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-no-change-XXX.service)"
+    log_file="$(mktemp)"
+
+    setup_base_unit "$unit_path" "$log_file"
+
+    # Simple sanity test without any reordering shenanignans, to check if the base unit works as expected.
+    check_output "$unit_path" "$log_file" "foo\n"
+
+    rm -f "$unit_path" "$log_file"
+}
+
+testcase_swapped() {
+    local unit_path log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-swapped-XXX.service)"
+    log_file="$(mktemp)"
+
+    setup_base_unit "$unit_path" "$log_file"
+
+    # Swap the two ExecStart= lines.
+    #
+    # Since we should be in the first "sleep" of the base unit, after replacing the unit with the following
+    # one we should continue running from the respective "ExecStart=sleep 3" line, which is now the last
+    # one, resulting no output in the final log file.
+    cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo foo >>$log_file"
+ExecStart=sleep 3
+EOF
+    systemctl daemon-reload
+
+    check_output "$unit_path" "$log_file" ""
+
+    rm -f "$unit_path" "$log_file"
+}
+
+testcase_added_before() {
+    local unit_path log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-added-before-XXX.service)"
+    log_file="$(mktemp)"
+
+    setup_base_unit "$unit_path" "$log_file"
+
+    # Add one new ExecStart= before the existing ones.
+    #
+    # Since, after reload, we should continue running from the "sleep 3" statement, the newly added "echo
+    # bar" one will have no effect and we should end up with the same output as in the previous case.
+    cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo bar >>$log_file"
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+EOF
+    systemctl daemon-reload
+
+    check_output "$unit_path" "$log_file" "foo\n"
+
+    rm -f "$unit_path" "$log_file"
+}
+
+testcase_added_after() {
+    local unit_path log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-added-after-XXX.service)"
+    log_file="$(mktemp)"
+
+    setup_base_unit "$unit_path" "$log_file"
+
+    # Add an ExecStart= line after the existing ones.
+    #
+    # Same case as above, except the newly added ExecStart= should get executed, as it was added after the
+    # "sleep 3" statement.
+    cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+ExecStart=bash -c "echo bar >>$log_file"
+EOF
+    systemctl daemon-reload
+
+    check_output "$unit_path" "$log_file" "foo\nbar\n"
+
+    rm -f "$unit_path" "$log_file"
+}
+
+testcase_interleaved() {
+    local unit_path log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-interleaved-XXX.service)"
+    log_file="$(mktemp)"
+
+    setup_base_unit "$unit_path" "$log_file"
+
+    # Combination of the two previous cases.
+    cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo baz >>$log_file"
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+ExecStart=bash -c "echo bar >>$log_file"
+EOF
+    systemctl daemon-reload
+
+    check_output "$unit_path" "$log_file" "foo\nbar\n"
+
+    rm -f "$unit_path" "$log_file"
+}
+
+testcase_removal() {
+    local unit_path log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-removal-XXX.service)"
+    log_file="$(mktemp)"
+
+    setup_base_unit "$unit_path" "$log_file"
+
+    # Remove the currently executed ExecStart= line.
+    #
+    # In this case we completely drop the currently executed "sleep 3" statement, so after reload systemd
+    # should complain that the currently executed command vanished and simply finish executing the unit,
+    # resulting in an empty log.
+    cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo bar >>$log_file"
+ExecStart=bash -c "echo baz >>$log_file"
+EOF
+    systemctl daemon-reload
+
+    check_output "$unit_path" "$log_file" ""
+
+    rm -f "$unit_path" "$log_file"
+}
+
+testcase_issue_6533() {
+    local unit_path unit_name log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-issue-6533-XXX.service)"
+    unit_name="${unit_path##*/}"
+    log_file="$(mktemp)"
+
+    cat >"$unit_path" <<EOF
+[Service]
+Type=simple
+ExecStart=/bin/sleep 5
+EOF
+    systemctl daemon-reload
+
+    systemctl --job-mode=replace --no-block start "$unit_name"
+    sleep 2
+
+    # Make sure we try to execute the next command only for oneshot services, as for other types we allow
+    # only one ExecStart= directive.
+    #
+    # See: https://github.com/systemd/systemd/issues/6533
+    cat >"$unit_path" <<EOF
+[Service]
+Type=simple
+ExecStart=/bin/sleep 5
+ExecStart=bash -c "echo foo >>$log_file"
+EOF
+    systemctl daemon-reload
+
+    check_output "$unit_path" "$log_file" ""
+    (! journalctl -b --grep "Freezing execution" _PID=1)
+}
+
+mkdir -p /run/systemd/system/
+run_testcases
+systemctl daemon-reload
diff --git a/test/units/TEST-07-PID1.exec-timestamps.sh b/test/units/TEST-07-PID1.exec-timestamps.sh
new file mode 100755 (executable)
index 0000000..0211166
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check that timestamps of a Type=notify service are consistent
+
+systemd-run --service-type notify --property NotifyAccess=all --unit notify.service --wait sh -c 'systemd-notify --ready; exit 1' || :
+
+start=$(systemctl show --property=ExecMainStartTimestampMonotonic --value notify.service)
+handoff=$(systemctl show --property=ExecMainHandoffTimestampMonotonic --value notify.service)
+active=$(systemctl show --property=ActiveEnterTimestampMonotonic --value notify.service)
+exit=$(systemctl show --property=ExecMainExitTimestampMonotonic --value notify.service)
+
+[[ $start -le $handoff ]]
+[[ $handoff -le $active ]]
+[[ $active -le $exit ]]
diff --git a/test/units/TEST-07-PID1.issue-14566.sh b/test/units/TEST-07-PID1.issue-14566.sh
new file mode 100755 (executable)
index 0000000..d4be5b5
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test that KillMode=mixed does not leave left over processes with ExecStopPost=
+# Issue: https://github.com/systemd/systemd/issues/14566
+
+if [[ -n "${ASAN_OPTIONS:-}" ]]; then
+    # Temporarily skip this test when running with sanitizers due to a deadlock
+    # See: https://bugzilla.redhat.com/show_bug.cgi?id=2098125
+    echo "Sanitizers detected, skipping the test..."
+    exit 0
+fi
+
+systemctl start issue14566-repro
+sleep 4
+systemctl status issue14566-repro
+
+leaked_pid=$(cat /leakedtestpid)
+
+systemctl stop issue14566-repro
+sleep 4
+
+# Leaked PID will still be around if we're buggy.
+# I personally prefer to see 42.
+ps -p "$leaked_pid" && exit 42
+
+exit 0
diff --git a/test/units/TEST-07-PID1.issue-16115.sh b/test/units/TEST-07-PID1.issue-16115.sh
new file mode 100755 (executable)
index 0000000..8f63826
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test ExecCondition= does not restart on abnormal or failure
+# Issue: https://github.com/systemd/systemd/issues/16115
+
+systemctl start issue16115-repro-1
+systemctl start issue16115-repro-2
+systemctl start issue16115-repro-3
+sleep 5 # wait a bit in case there are restarts so we can count them below
+
+[[ "$(systemctl show issue16115-repro-1 -P NRestarts)" == "0" ]]
+[[ "$(systemctl show issue16115-repro-2 -P NRestarts)" == "0" ]]
+[[ "$(systemctl show issue16115-repro-3 -P NRestarts)" == "0" ]]
diff --git a/test/units/TEST-07-PID1.issue-1981.sh b/test/units/TEST-07-PID1.issue-1981.sh
new file mode 100755 (executable)
index 0000000..dcfa9b1
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Segmentation fault in timer_enter_waiting while masking a unit
+# Issue: https://github.com/systemd/systemd/issues/1981
+
+at_exit() {
+    set +e
+
+    systemctl stop my.timer my.service
+    rm -f /run/systemd/system/my.{service,timer}
+    systemctl daemon-reload
+}
+
+trap at_exit EXIT
+
+mkdir -p /run/systemd/system
+
+cat >/run/systemd/system/my.service <<\EOF
+[Service]
+Type=oneshot
+ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = my.timer'
+ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_REALTIME_USEC"'
+ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_MONOTONIC_USEC"'
+ExecStart=echo Timer runs me
+EOF
+
+cat >/run/systemd/system/my.timer <<EOF
+[Timer]
+OnBootSec=10s
+OnUnitInactiveSec=1h
+EOF
+
+systemctl unmask my.timer
+systemctl start my.timer
+
+mkdir -p /run/systemd/system/my.timer.d/
+cat >/run/systemd/system/my.timer.d/override.conf <<EOF
+[Timer]
+OnBootSec=10s
+OnUnitInactiveSec=1h
+EOF
+
+systemctl daemon-reload
+systemctl mask my.timer
diff --git a/test/units/TEST-07-PID1.issue-2467.sh b/test/units/TEST-07-PID1.issue-2467.sh
new file mode 100755 (executable)
index 0000000..de0577b
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Don't start services every few ms if condition fails
+# Issue: https://github.com/systemd/systemd/issues/2467
+
+rm -f /tmp/nonexistent
+systemctl start issue2467.socket
+nc -i20 -w20 -U /run/test.ctl || :
+
+# TriggerLimitIntervalSec= by default is set to 2s. A "sleep 10" should give
+# systemd enough time even on slower machines, to reach the trigger limit.
+# shellcheck disable=SC2016
+timeout 10 bash -c 'until [[ "$(systemctl show issue2467.socket -P ActiveState)" == failed ]]; do sleep .5; done'
+[[ "$(systemctl show issue2467.socket -P Result)" == trigger-limit-hit ]]
diff --git a/test/units/TEST-07-PID1.issue-27953.sh b/test/units/TEST-07-PID1.issue-27953.sh
new file mode 100755 (executable)
index 0000000..8659970
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check if the unit doesn't remain in active state after the main PID exits
+# Issue: https://github.com/systemd/systemd/issues/27953
+
+systemctl start issue27953.service
+timeout 10 sh -c 'while systemctl is-active issue27953.service; do sleep .5; done'
+[[ "$(systemctl show -P ExitType issue27953.service)" == main ]]
diff --git a/test/units/TEST-07-PID1.issue-30412.sh b/test/units/TEST-07-PID1.issue-30412.sh
new file mode 100755 (executable)
index 0000000..c1cb00e
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check that socket FDs are not double closed on error: https://github.com/systemd/systemd/issues/30412
+
+mkdir -p /run/systemd/system
+
+rm -f /tmp/badbin
+touch /tmp/badbin
+chmod 744 /tmp/badbin
+
+cat >/run/systemd/system/badbin_assert.service <<EOF
+[Service]
+ExecStart=/tmp/badbin
+Restart=no
+EOF
+
+cat >/run/systemd/system/badbin_assert.socket <<EOF
+[Socket]
+ListenStream=@badbin_assert.socket
+FlushPending=yes
+EOF
+
+systemctl daemon-reload
+systemctl start badbin_assert.socket
+
+socat - ABSTRACT-CONNECT:badbin_assert.socket
+
+timeout 10 sh -c 'while systemctl is-active badbin_assert.service; do sleep .5; done'
+[[ "$(systemctl show -P ExecMainStatus badbin_assert.service)" == 203 ]]
diff --git a/test/units/TEST-07-PID1.issue-3166.sh b/test/units/TEST-07-PID1.issue-3166.sh
new file mode 100755 (executable)
index 0000000..6677901
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Service doesn't enter the "failed" state
+# Issue: https://github.com/systemd/systemd/issues/3166
+
+systemctl --no-block start issue3166-fail-on-restart.service
+active_state="$(systemctl show --value --property ActiveState issue3166-fail-on-restart.service)"
+while [[ "$active_state" == "activating" || "$active_state" =~ ^(in)?active$ ]]; do
+    sleep .5
+    active_state="$(systemctl show --value --property ActiveState issue3166-fail-on-restart.service)"
+done
+systemctl is-failed issue3166-fail-on-restart.service || exit 1
+[[ "$(systemctl show --value --property NRestarts issue3166-fail-on-restart.service)" -le 3 ]] || exit 1
diff --git a/test/units/TEST-07-PID1.issue-3171.sh b/test/units/TEST-07-PID1.issue-3171.sh
new file mode 100755 (executable)
index 0000000..374df54
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# SocketGroup lost on daemon-reload with unit moving away temporarily
+# Issue: https://github.com/systemd/systemd/issues/3171
+
+echo "g adm - - -" | systemd-sysusers -
+
+U=/run/systemd/system/issue-3171.socket
+cat >$U <<EOF
+[Unit]
+Description=Test 12 socket
+[Socket]
+Accept=yes
+ListenStream=/run/issue-3171.socket
+SocketGroup=adm
+SocketMode=0660
+EOF
+
+cat >/run/systemd/system/issue-3171@.service <<EOF
+[Unit]
+Description=Test service
+[Service]
+StandardInput=socket
+ExecStart=sh -x -c cat
+EOF
+
+systemctl start issue-3171.socket
+systemctl is-active issue-3171.socket
+[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
+echo A | nc -w1 -U /run/issue-3171.socket
+
+mv $U ${U}.disabled
+systemctl daemon-reload
+systemctl is-active issue-3171.socket
+[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
+echo B | nc -w1 -U /run/issue-3171.socket && exit 1
+
+mv ${U}.disabled $U
+systemctl daemon-reload
+systemctl is-active issue-3171.socket
+echo C | nc -w1 -U /run/issue-3171.socket && exit 1
+[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
+
+systemctl restart issue-3171.socket
+systemctl is-active issue-3171.socket
+echo D | nc -w1 -U /run/issue-3171.socket
+[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
diff --git a/test/units/TEST-07-PID1.main-PID-change.sh b/test/units/TEST-07-PID1.main-PID-change.sh
new file mode 100755 (executable)
index 0000000..16f3510
--- /dev/null
@@ -0,0 +1,172 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test changing the main PID
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# The main service PID should be the parent bash process
+MAINPID="${PPID:?}"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+# Start a test process inside of our own cgroup
+sleep infinity &
+INTERNALPID=$!
+disown
+
+# Start a test process outside of our own cgroup
+systemd-run -p DynamicUser=1 --unit=test-sleep.service /bin/sleep infinity
+EXTERNALPID="$(systemctl show -P MainPID test-sleep.service)"
+
+# Update our own main PID to the external test PID, this should work
+systemd-notify MAINPID="$EXTERNALPID"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$EXTERNALPID"
+
+# Update our own main PID to the internal test PID, this should work, too
+systemd-notify MAINPID=$INTERNALPID
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$INTERNALPID"
+
+# Update it back to our own PID, this should also work
+systemd-notify MAINPID="$MAINPID"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+# Try to set it to PID 1, which it should ignore, because that's the manager
+systemd-notify MAINPID=1
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+# Try to set it to PID 0, which is invalid and should be ignored
+systemd-notify MAINPID=0
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+# Try to set it to a valid but non-existing PID, which should be ignored. (Note
+# that we set the PID to a value well above any known /proc/sys/kernel/pid_max,
+# which means we can be pretty sure it doesn't exist by coincidence)
+systemd-notify MAINPID=1073741824
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+# Change it again to the external PID, without privileges this time. This should be ignored, because the PID is from outside of our cgroup and we lack privileges.
+systemd-notify --uid=1000 MAINPID="$EXTERNALPID"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+# Change it again to the internal PID, without privileges this time. This should work, as the process is on our cgroup, and that's enough even if we lack privileges.
+systemd-notify --uid=1000 MAINPID="$INTERNALPID"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$INTERNALPID"
+
+# Update it back to our own PID, this should also work
+systemd-notify --uid=1000 MAINPID="$MAINPID"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
+
+cat >/tmp/test-mainpid.sh <<\EOF
+#!/usr/bin/env bash
+
+set -eux
+set -o pipefail
+
+# Create a number of children, and make one the main one
+sleep infinity &
+disown
+
+sleep infinity &
+MAINPID=$!
+disown
+
+sleep infinity &
+disown
+
+echo $MAINPID >/run/mainpidsh/pid
+EOF
+chmod +x /tmp/test-mainpid.sh
+
+systemd-run --unit=test-mainpidsh.service \
+            -p StandardOutput=tty \
+            -p StandardError=tty \
+            -p Type=forking \
+            -p RuntimeDirectory=mainpidsh \
+            -p PIDFile=/run/mainpidsh/pid \
+            /tmp/test-mainpid.sh
+test "$(systemctl show -P MainPID test-mainpidsh.service)" -eq "$(cat /run/mainpidsh/pid)"
+
+cat >/tmp/test-mainpid2.sh <<\EOF
+#!/usr/bin/env bash
+
+set -eux
+set -o pipefail
+
+# Create a number of children, and make one the main one
+sleep infinity &
+disown
+
+sleep infinity &
+MAINPID=$!
+disown
+
+sleep infinity &
+disown
+
+echo $MAINPID >/run/mainpidsh2/pid
+chown 1001:1001 /run/mainpidsh2/pid
+EOF
+chmod +x /tmp/test-mainpid2.sh
+
+systemd-run --unit=test-mainpidsh2.service \
+            -p StandardOutput=tty \
+            -p StandardError=tty \
+            -p Type=forking \
+            -p RuntimeDirectory=mainpidsh2 \
+            -p PIDFile=/run/mainpidsh2/pid \
+            /tmp/test-mainpid2.sh
+test "$(systemctl show -P MainPID test-mainpidsh2.service)" -eq "$(cat /run/mainpidsh2/pid)"
+
+cat >/dev/shm/test-mainpid3.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+set -o pipefail
+
+sleep infinity &
+disown
+
+sleep infinity &
+disown
+
+sleep infinity &
+disown
+
+# Let's try to play games, and link up a privileged PID file
+ln -s ../mainpidsh/pid /run/mainpidsh3/pid
+
+# Quick assertion that the link isn't dead
+test -f /run/mainpidsh3/pid
+EOF
+chmod 755 /dev/shm/test-mainpid3.sh
+
+# This has to fail, as we shouldn't accept the dangerous PID file, and then
+# inotify-wait on it to be corrected which we never do.
+(! systemd-run \
+    --unit=test-mainpidsh3.service \
+    -p StandardOutput=tty \
+    -p StandardError=tty \
+    -p Type=forking \
+    -p RuntimeDirectory=mainpidsh3 \
+    -p PIDFile=/run/mainpidsh3/pid \
+    -p DynamicUser=1 \
+    `# Make sanitizers happy when DynamicUser=1 pulls in instrumented systemd NSS modules` \
+    -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
+    -p TimeoutStartSec=2s \
+    /dev/shm/test-mainpid3.sh)
+
+# Test that this failed due to timeout, and not some other error
+test "$(systemctl show -P Result test-mainpidsh3.service)" = timeout
+
+# Test that scope units work
+systemd-run --scope --unit test-true.scope /bin/true
+test "$(systemctl show -P Result test-true.scope)" = success
+
+# Test that user scope units work as well
+
+systemctl start user@4711.service
+runas testuser systemd-run --scope --user --unit test-true.scope /bin/true
+test "$(systemctl show -P Result test-true.scope)" = success
diff --git a/test/units/TEST-07-PID1.mount-invalid-chars.sh b/test/units/TEST-07-PID1.mount-invalid-chars.sh
new file mode 100755 (executable)
index 0000000..a879334
--- /dev/null
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Don't send invalid characters over dbus if a mount contains them
+
+at_exit() {
+    mountpoint -q /proc/1/mountinfo && umount /proc/1/mountinfo
+    [[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab.bak /etc/fstab
+    rm -f /run/systemd/system/foo-*.mount
+    systemctl daemon-reload
+}
+
+trap at_exit EXIT
+
+# Check invalid characters directly in /proc/mountinfo
+#
+# This is a bit tricky (and hacky), since we have to temporarily replace
+# PID 1's /proc/mountinfo, but we have to keep the original mounts intact,
+# otherwise systemd would unmount them on reload
+TMP_MOUNTINFO="$(mktemp)"
+
+cp /proc/1/mountinfo "$TMP_MOUNTINFO"
+# Add a mount entry with a "Unicode non-character" in it
+LANG="C.UTF-8" printf '69 1 252:2 / /foo/mountinfo rw,relatime shared:1 - cifs //foo\ufffebar rw,seclabel\n' >>"$TMP_MOUNTINFO"
+mount --bind "$TMP_MOUNTINFO" /proc/1/mountinfo
+systemctl daemon-reload
+# On affected versions this would throw an error:
+#   Failed to get properties: Bad message
+systemctl status foo-mountinfo.mount
+
+umount /proc/1/mountinfo
+systemctl daemon-reload
+rm -f "$TMP_MOUNTINFO"
+
+# Check invalid characters in a mount unit
+#
+# systemd already handles this and refuses to load the invalid string, e.g.:
+#   foo-fstab.mount:9: String is not UTF-8 clean, ignoring assignment: What=//localhost/foo���bar
+#
+# a) Unit generated from /etc/fstab
+[[ -e /etc/fstab ]] && cp -f /etc/fstab /tmp/fstab.bak
+
+LANG="C.UTF-8" printf '//localhost/foo\ufffebar  /foo/fstab  cifs defaults 0 0\n' >/etc/fstab
+systemctl daemon-reload
+[[ "$(systemctl show -P UnitFileState foo-fstab.mount)" == bad ]]
+
+# b) Unit generated from /etc/fstab (but the invalid character is in options)
+LANG="C.UTF-8" printf '//localhost/foobar  /foo/fstab/opt  cifs nosuid,a\ufffeb,noexec 0 0\n' >/etc/fstab
+systemctl daemon-reload
+[[ "$(systemctl show -P UnitFileState foo-fstab-opt.mount)" == bad ]]
+rm -f /etc/fstab
+
+[[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab.bak /etc/fstab
+systemctl daemon-reload
+
+# c) Mount unit
+mkdir -p /run/systemd/system
+LANG="C.UTF-8" printf '[Mount]\nWhat=//localhost/foo\ufffebar\nWhere=/foo/unit\nType=cifs\nOptions=noexec\n' >/run/systemd/system/foo-unit.mount
+systemctl daemon-reload
+[[ "$(systemctl show -P UnitFileState foo-unit.mount)" == bad ]]
+rm -f /run/systemd/system/foo-unit.mount
+
+# d) Mount unit (but the invalid character is in Options=)
+mkdir -p /run/systemd/system
+LANG="C.UTF-8" printf '[Mount]\nWhat=//localhost/foobar\nWhere=/foo/unit/opt\nType=cifs\nOptions=noexec,a\ufffeb,nosuid\n' >/run/systemd/system/foo-unit-opt.mount
+systemctl daemon-reload
+[[ "$(systemctl show -P UnitFileState foo-unit-opt.mount)" == bad ]]
+rm -f /run/systemd/system/foo-unit-opt.mount
diff --git a/test/units/TEST-07-PID1.poll-limit.sh b/test/units/TEST-07-PID1.poll-limit.sh
new file mode 100755 (executable)
index 0000000..ca988b2
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemd-analyze log-level debug
+
+cat >/run/systemd/system/floodme@.service <<EOF
+[Service]
+ExecStart=true
+EOF
+
+cat >/run/systemd/system/floodme.socket <<EOF
+[Socket]
+ListenStream=/tmp/floodme
+PollLimitIntervalSec=10s
+Accept=yes
+PollLimitBurst=3
+EOF
+
+systemctl daemon-reload
+systemctl start floodme.socket
+
+START=$(date +%s%N)
+
+# Trigger this 100 times in a flood
+for _ in {1..100}; do
+    logger -u /tmp/floodme foo &
+done
+
+# Let some time pass
+sleep 5
+
+END=$(date +%s%N)
+
+PASSED=$((END-START))
+
+# Calculate (round up) how many trigger events could have happened in the passed time
+MAXCOUNT=$(((PASSED+10000000000)*3/10000000000))
+
+# We started 100 connection attempts, but only 3 should have gone through, as per limit
+test "$(systemctl show -P NAccepted floodme.socket)" -le "$MAXCOUNT"
+
+systemctl stop floodme.socket floodme@*.service
+
+rm /run/systemd/system/floodme@.service /run/systemd/system/floodme.socket /tmp/floodme
+
+systemctl daemon-reload
diff --git a/test/units/TEST-07-PID1.pr-31351.sh b/test/units/TEST-07-PID1.pr-31351.sh
new file mode 100755 (executable)
index 0000000..f6911f2
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+cat >/run/systemd/system/nonexistent-execstart-exit-status.service <<EOF
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=-/foo/bar/not-exist
+EOF
+
+systemctl start nonexistent-execstart-exit-status.service
+systemctl is-active nonexistent-execstart-exit-status.service
+assert_eq "$(systemctl show nonexistent-execstart-exit-status.service -P Result)" "success"
+(( $(systemctl show nonexistent-execstart-exit-status.service -P ExecMainStatus) > 0 ))
+
+systemctl stop nonexistent-execstart-exit-status.service
+rm /run/systemd/system/nonexistent-execstart-exit-status.service
diff --git a/test/units/TEST-07-PID1.private-network.sh b/test/units/TEST-07-PID1.private-network.sh
new file mode 100755 (executable)
index 0000000..37658f7
--- /dev/null
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# For issue https://github.com/systemd/systemd/issues/29526
+systemd-run -p PrivateNetwork=yes --wait /bin/true
diff --git a/test/units/TEST-07-PID1.service b/test/units/TEST-07-PID1.service
new file mode 100644 (file)
index 0000000..92302bf
--- /dev/null
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-07-PID1
+
+[Service]
+Type=oneshot
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+NotifyAccess=all
+# Issue: https://github.com/systemd/systemd/issues/2691
+ExecStop=sh -c 'kill -SEGV $$$$'
+RemainAfterExit=yes
+TimeoutStopSec=270s
diff --git a/test/units/TEST-07-PID1.sh b/test/units/TEST-07-PID1.sh
new file mode 100755 (executable)
index 0000000..873d638
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+# Issue: https://github.com/systemd/systemd/issues/2730
+# See TEST-07-PID1/test.sh for the first "half" of the test
+mountpoint /issue2730
+
+run_subtests
+
+touch /testok
+systemctl --no-block exit 123
diff --git a/test/units/TEST-07-PID1.socket-pass-fds.sh b/test/units/TEST-07-PID1.socket-pass-fds.sh
new file mode 100755 (executable)
index 0000000..a61b1c0
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test PassFileDescriptorsToExec= option in socket units
+
+for u in pass-fds-to-exec-{no,yes}.socket; do
+    systemctl start "$u"
+    systemctl stop "$u"
+done
diff --git a/test/units/TEST-07-PID1.type-exec-parallel.sh b/test/units/TEST-07-PID1.type-exec-parallel.sh
new file mode 100755 (executable)
index 0000000..30f80c5
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Make sure that we never mistake a process starting but failing quickly for a process failing to start, with Type=exec.
+# See https://github.com/systemd/systemd/pull/30799
+
+seq 25 | xargs -n 1 -P 0 systemd-run -p Type=exec /bin/false
diff --git a/test/units/TEST-08-INITRD.service b/test/units/TEST-08-INITRD.service
new file mode 100644 (file)
index 0000000..2db35cf
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-08-INITRD
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-08-INITRD.sh b/test/units/TEST-08-INITRD.sh
new file mode 100755 (executable)
index 0000000..58d9bea
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if systemd-detect-virt -qc; then
+    echo >&2 "This test can't run in a container"
+    exit 1
+fi
+
+# This test requires systemd to run in the initrd as well, which is not the case
+# for mkinitrd-based initrd (Ubuntu/Debian)
+if [[ "$(systemctl show -P InitRDTimestampMonotonic)" -eq 0 ]]; then
+    echo "systemd didn't run in the initrd, skipping the test"
+    touch /skipped
+    exit 77
+fi
+
+# We should've created a mount under /run in initrd (see the other half of the test)
+# that should've survived the transition from initrd to the real system
+test -d /run/initrd-mount-target
+mountpoint /run/initrd-mount-target
+[[ -e /run/initrd-mount-target/hello-world ]]
+
+# Copy the prepared shutdown initrd to its intended location. Check the respective
+# test.sh file for details
+mkdir -p /run/initramfs
+cp -r /shutdown-initrd/* /run/initramfs/
+
+touch /testok
diff --git a/test/units/TEST-09-REBOOT.journal.sh b/test/units/TEST-09-REBOOT.journal.sh
new file mode 100755 (executable)
index 0000000..5248d70
--- /dev/null
@@ -0,0 +1,116 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+get_first_boot_id() {
+    journalctl -b "${1:?}" -o json -n +1 | jq -r '._BOOT_ID'
+}
+
+get_last_boot_id() {
+    journalctl -b "${1:?}" -o json -n 1 | jq -r '._BOOT_ID'
+}
+
+get_first_timestamp() {
+    journalctl -b "${1:?}" -o json -n +1 | jq -r '.__REALTIME_TIMESTAMP'
+}
+
+get_last_timestamp() {
+    journalctl -b "${1:?}" -o json -n 1 | jq -r '.__REALTIME_TIMESTAMP'
+}
+
+# Issue: #29275, second part
+# Now let's check if the boot entries are in the correct/expected order
+index=0
+SYSTEMD_LOG_LEVEL=debug journalctl --list-boots
+journalctl --list-boots -o json | jq -r '.[] | [.index, .boot_id, .first_entry, .last_entry] | @tsv' |
+    while read -r offset boot_id first_ts last_ts; do
+        : "Boot #$((++index)) ($offset) with ID $boot_id"
+
+        # Try the "regular" (non-json) variants first, as they provide a helpful
+        # error message if something is not right
+        SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$index"
+        SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$offset"
+        SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$boot_id"
+
+        # Check the boot ID of the first entry
+        entry_boot_id="$(get_first_boot_id "$index")"
+        assert_eq "$entry_boot_id" "$boot_id"
+        entry_boot_id="$(get_first_boot_id "$offset")"
+        assert_eq "$entry_boot_id" "$boot_id"
+        entry_boot_id="$(get_first_boot_id "$boot_id")"
+        assert_eq "$entry_boot_id" "$boot_id"
+
+        # Check the timestamp of the first entry
+        entry_ts="$(get_first_timestamp "$index")"
+        assert_eq "$entry_ts" "$first_ts"
+        entry_ts="$(get_first_timestamp "$offset")"
+        assert_eq "$entry_ts" "$first_ts"
+        entry_ts="$(get_first_timestamp "$boot_id")"
+        assert_eq "$entry_ts" "$first_ts"
+
+        # Check the boot ID of the last entry
+        entry_boot_id="$(get_last_boot_id "$index")"
+        assert_eq "$entry_boot_id" "$boot_id"
+        entry_boot_id="$(get_last_boot_id "$offset")"
+        assert_eq "$entry_boot_id" "$boot_id"
+        entry_boot_id="$(get_last_boot_id "$boot_id")"
+        assert_eq "$entry_boot_id" "$boot_id"
+
+        # Check the timestamp of the last entry
+        if [[ "$offset" != "0" ]]; then
+            entry_ts="$(get_last_timestamp "$index")"
+            assert_eq "$entry_ts" "$last_ts"
+            entry_ts="$(get_last_timestamp "$offset")"
+            assert_eq "$entry_ts" "$last_ts"
+            entry_ts="$(get_last_timestamp "$boot_id")"
+            assert_eq "$entry_ts" "$last_ts"
+        fi
+    done
+
+verify_seqnum() {
+    if [[ "$REBOOT_COUNT" -ne "$NUM_REBOOT" ]]; then
+        return 0
+    fi
+
+    journalctl --flush
+    journalctl --sync
+
+    ls -lR /var/log/journal/
+    ls -lR /run/log/journal/
+
+    journalctl --system --header
+
+    (! journalctl --system -q -o short-monotonic -u systemd-journald.service --grep 'Journal file uses a different sequence number ID, rotating')
+
+    set +x
+    previous_seqnum=0
+    previous_seqnum_id=
+    previous_boot_id=
+    journalctl --system -q -o json | jq -r '[.__SEQNUM, .__SEQNUM_ID, ._BOOT_ID] | @tsv' |
+        while read -r seqnum seqnum_id boot_id; do
+
+            if [[ -n "$previous_seqnum_id" ]]; then
+                if ! test "$seqnum" -gt "$previous_seqnum"; then
+                    echo "seqnum=$seqnum is not greater than previous_seqnum=$previous_seqnum"
+                    echo "seqnum_id=$seqnum_id, previous_seqnum_id=$previous_seqnum_id"
+                    echo "boot_id=$boot_id, previous_boot_id=$previous_boot_id"
+                    return 1
+                fi
+
+                assert_eq "$seqnum_id" "$previous_seqnum_id"
+            fi
+
+            previous_seqnum="$seqnum"
+            previous_seqnum_id="$seqnum_id"
+            previous_boot_id="$boot_id"
+        done
+    set -x
+
+    return 0
+}
+
+verify_seqnum
diff --git a/test/units/TEST-09-REBOOT.service b/test/units/TEST-09-REBOOT.service
new file mode 100644 (file)
index 0000000..6c957ec
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-09-REBOOT
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-09-REBOOT.sh b/test/units/TEST-09-REBOOT.sh
new file mode 100755 (executable)
index 0000000..85630b6
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+export NUM_REBOOT=4
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemd-cat echo "Reboot count: $REBOOT_COUNT"
+systemd-cat journalctl --list-boots
+
+run_subtests
+
+if [[ "$REBOOT_COUNT" -lt "$NUM_REBOOT" ]]; then
+    systemctl_final reboot
+elif [[ "$REBOOT_COUNT" -gt "$NUM_REBOOT" ]]; then
+    assert_not_reached
+fi
+
+touch /testok
diff --git a/test/units/TEST-13-NSPAWN.importctl.sh b/test/units/TEST-13-NSPAWN.importctl.sh
new file mode 100755 (executable)
index 0000000..a13e3fd
--- /dev/null
@@ -0,0 +1,66 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export PAGER=
+
+at_exit() {
+    set +e
+    umount -l -R /var/lib/confexts
+    rm -f /var/tmp/importtest /var/tmp/importtest2 /var/tmp/importtest.tar.gz /var/tmp/importtest2.tar.gz
+}
+
+trap at_exit EXIT
+
+systemctl service-log-level systemd-importd debug
+
+# Mount tmpfs over /var/lib/confexts to not pollute the image
+mkdir -p /var/lib/confexts
+mount -t tmpfs tmpfs /var/lib/confexts -o mode=755
+
+importctl
+importctl --no-pager --help
+importctl --version
+importctl list-transfers
+importctl list-transfers --no-legend --no-ask-password
+importctl list-transfers -j
+importctl list-images
+importctl list-images --no-legend --no-ask-password
+importctl list-images -j
+
+(! importctl cancel-transfer 4711)
+
+dd if=/dev/urandom of=/var/tmp/importtest bs=4096 count=10
+
+importctl import-raw --class=confext /var/tmp/importtest
+cmp /var/tmp/importtest /var/lib/confexts/importtest.raw
+importctl export-raw --class=confext importtest /var/tmp/importtest2
+cmp /var/tmp/importtest /var/tmp/importtest2
+
+(! importctl pull-raw --class=confext file:///var/tmp/importtest)
+importctl pull-raw --verify=no --class=confext file:///var/tmp/importtest importtest3
+cmp /var/tmp/importtest /var/lib/confexts/importtest3.raw
+
+tar czf /var/tmp/importtest.tar.gz -C /var/tmp importtest
+
+importctl import-tar --class=confext /var/tmp/importtest.tar.gz importtest4
+cmp /var/tmp/importtest /var/lib/confexts/importtest4/importtest
+
+importctl export-tar --class=confext importtest4 /var/tmp/importtest2.tar.gz
+importctl import-tar --class=confext /var/tmp/importtest2.tar.gz importtest5
+cmp /var/tmp/importtest /var/lib/confexts/importtest5/importtest
+
+importctl import-fs --class=confext /var/lib/confexts/importtest5 importtest6
+cmp /var/tmp/importtest /var/lib/confexts/importtest6/importtest
+
+(! importctl pull-tar --class=confext file:///var/tmp/importtest.tar.gz importtest7)
+importctl pull-tar --class=confext --verify=no file:///var/tmp/importtest.tar.gz importtest7
+cmp /var/tmp/importtest /var/lib/confexts/importtest7/importtest
+
+importctl list-images
+importctl list-images -j
diff --git a/test/units/TEST-13-NSPAWN.machinectl.sh b/test/units/TEST-13-NSPAWN.machinectl.sh
new file mode 100755 (executable)
index 0000000..6e2ad16
--- /dev/null
@@ -0,0 +1,221 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export PAGER=
+
+at_exit() {
+    set +e
+
+    machinectl status long-running >/dev/null && machinectl kill --signal=KILL long-running
+    mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done"
+    [[ -n "${NSPAWN_FRAGMENT:-}" ]] && rm -f "/etc/systemd/nspawn/$NSPAWN_FRAGMENT" "/var/lib/machines/$NSPAWN_FRAGMENT"
+    rm -f /run/systemd/nspawn/*.nspawn
+}
+
+trap at_exit EXIT
+
+systemctl service-log-level systemd-machined debug
+systemctl service-log-level systemd-importd debug
+
+# Mount temporary directory over /var/lib/machines to not pollute the image
+mkdir -p /var/lib/machines
+mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
+
+# Create a couple of containers we can refer to in tests
+for i in {0..4}; do
+    create_dummy_container "/var/lib/machines/container$i"
+    machinectl start "container$i"
+done
+# Create one "long running" container with some basic signal handling
+create_dummy_container /var/lib/machines/long-running
+cat >/var/lib/machines/long-running/sbin/init <<\EOF
+#!/usr/bin/bash -x
+
+PID=0
+
+trap "touch /poweroff" RTMIN+4
+trap "touch /reboot" INT
+trap "touch /trap" TRAP
+trap 'kill $PID' EXIT
+
+# We need to wait for the sleep process asynchronously in order to allow
+# bash to process signals
+sleep infinity &
+PID=$!
+while :; do
+    wait || :
+done
+EOF
+machinectl start long-running
+
+machinectl
+machinectl --no-pager --help
+machinectl --version
+machinectl list
+machinectl list --no-legend --no-ask-password
+
+machinectl status long-running long-running long-running
+machinectl status --full long-running
+machinectl status --quiet --lines=1 long-running
+machinectl status --lines=0 --max-addresses=0 long-running
+machinectl status --machine=testuser@.host long-running
+machinectl status --output=help long-running
+while read -r output; do
+    machinectl status --output="$output" long-running
+done < <(machinectl --output=help)
+
+machinectl show
+machinectl show --all
+machinectl show --all --machine=root@
+machinectl show --all --machine=testuser@
+[[ "$(machinectl show --property=PoolPath --value)" == "/var/lib/machines" ]]
+machinectl show long-running
+machinectl show long-running long-running long-running --all
+[[ "$(machinectl show --property=RootDirectory --value long-running)" == "/var/lib/machines/long-running" ]]
+
+machinectl enable long-running
+test -L /etc/systemd/system/machines.target.wants/systemd-nspawn@long-running.service
+machinectl enable long-running long-running long-running container1
+machinectl disable long-running
+test ! -L /etc/systemd/system/machines.target.wants/systemd-nspawn@long-running.service
+machinectl disable long-running long-running long-running container1
+
+[[ "$(machinectl shell testuser@ /usr/bin/bash -c 'echo -ne $FOO')" == "" ]]
+[[ "$(machinectl shell --setenv=FOO=bar testuser@ /usr/bin/bash -c 'echo -ne $FOO')" == "bar" ]]
+
+[[ "$(machinectl show --property=State --value long-running)" == "running" ]]
+# Equivalent to machinectl kill --signal=SIGRTMIN+4 --kill-whom=leader
+rm -f /var/lib/machines/long-running/poweroff
+machinectl poweroff long-running
+timeout 10 bash -c "until test -e /var/lib/machines/long-running/poweroff; do sleep .5; done"
+# Equivalent to machinectl kill --signal=SIGINT --kill-whom=leader
+rm -f /var/lib/machines/long-running/reboot
+machinectl reboot long-running
+timeout 10 bash -c "until test -e /var/lib/machines/long-running/reboot; do sleep .5; done"
+# Skip machinectl terminate for now, as it doesn't play well with our "init"
+rm -f /var/lib/machines/long-running/trap
+machinectl kill --signal=SIGTRAP --kill-whom=leader long-running
+timeout 10 bash -c "until test -e /var/lib/machines/long-running/trap; do sleep .5; done"
+# Multiple machines at once
+machinectl poweroff long-running long-running long-running
+machinectl reboot long-running long-running long-running
+machinectl kill --signal=SIGTRAP --kill-whom=leader long-running long-running long-running
+# All used signals should've been caught by a handler
+[[ "$(machinectl show --property=State --value long-running)" == "running" ]]
+
+cp /etc/machine-id /tmp/foo
+machinectl copy-to long-running /tmp/foo /root/foo
+test -f /var/lib/machines/long-running/root/foo
+machinectl copy-from long-running /root/foo /tmp/bar
+diff /tmp/foo /tmp/bar
+rm -f /tmp/{foo,bar}
+
+# machinectl bind is covered by testcase_check_machinectl_bind() in nspawn tests
+
+machinectl list-images
+machinectl list-images --no-legend
+machinectl image-status
+machinectl image-status container1
+machinectl image-status container1 container1 container{0..4}
+machinectl show-image
+machinectl show-image container1
+machinectl show-image container1 container1 container{0..4}
+
+machinectl clone container1 clone1
+machinectl show-image clone1
+machinectl rename clone1 clone2
+(! machinectl show-image clone1)
+machinectl show-image clone2
+if lsattr -d /var/lib/machines >/dev/null; then
+    [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]]
+    machinectl read-only clone2 yes
+    [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == yes ]]
+    machinectl read-only clone2 no
+    [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]]
+fi
+machinectl remove clone2
+for i in {0..4}; do
+    machinectl clone container1 "clone$i"
+done
+machinectl remove clone{0..4}
+for i in {0..4}; do
+    machinectl clone container1 ".hidden$i"
+done
+machinectl list-images --all
+test -d /var/lib/machines/.hidden1
+machinectl clean
+test ! -d /var/lib/machines/.hidden1
+
+# Prepare a simple raw container
+mkdir -p /tmp/mnt
+dd if=/dev/zero of=/var/tmp/container.raw bs=1M count=256
+mkfs.ext4 /var/tmp/container.raw
+mount -o loop /var/tmp/container.raw /tmp/mnt
+cp -r /var/lib/machines/container1/* /tmp/mnt
+umount /tmp/mnt
+# Try to import it, run it, export it, and re-import it
+machinectl import-raw /var/tmp/container.raw container-raw
+[[ "$(machinectl show-image --property=Type --value container-raw)" == "raw" ]]
+machinectl start container-raw
+machinectl export-raw container-raw /var/tmp/container-export.raw
+machinectl import-raw /var/tmp/container-export.raw container-raw-reimport
+[[ "$(machinectl show-image --property=Type --value container-raw-reimport)" == "raw" ]]
+rm -f /var/tmp/container{,-export}.raw
+
+# Prepare a simple tar.gz container
+tar -pczf /var/tmp/container.tar.gz -C /var/lib/machines/container1 .
+# Try to import it, run it, export it, and re-import it
+machinectl import-tar /var/tmp/container.tar.gz container-tar
+[[ "$(machinectl show-image --property=Type --value container-tar)" =~ directory|subvolume ]]
+machinectl start container-tar
+machinectl export-tar container-tar /var/tmp/container-export.tar.gz
+machinectl import-tar /var/tmp/container-export.tar.gz container-tar-reimport
+[[ "$(machinectl show-image --property=Type --value container-tar-reimport)" =~ directory|subvolume ]]
+rm -f /var/tmp/container{,-export}.tar.gz
+
+# Try to import a container directory & run it
+cp -r /var/lib/machines/container1 /var/tmp/container.dir
+machinectl import-fs /var/tmp/container.dir container-dir
+[[ "$(machinectl show-image --property=Type --value container-dir)" =~ directory|subvolume ]]
+machinectl start container-dir
+rm -fr /var/tmp/container.dir
+
+timeout 10 bash -c "until machinectl clean --all; do sleep .5; done"
+
+NSPAWN_FRAGMENT="machinectl-test-$RANDOM.nspawn"
+cat >"/var/lib/machines/$NSPAWN_FRAGMENT" <<EOF
+[Exec]
+Boot=true
+EOF
+machinectl cat "$NSPAWN_FRAGMENT"
+EDITOR=true script -qec "machinectl edit $NSPAWN_FRAGMENT" /dev/null
+test -f "/etc/systemd/nspawn/$NSPAWN_FRAGMENT"
+diff "/var/lib/machines/$NSPAWN_FRAGMENT" "/etc/systemd/nspawn/$NSPAWN_FRAGMENT"
+
+cat >/tmp/fragment.nspawn <<EOF
+[Exec]
+Boot=false
+EOF
+machinectl cat /tmp/fragment.nspawn
+EDITOR="cp /tmp/fragment.nspawn" script -qec "machinectl edit $NSPAWN_FRAGMENT" /dev/null
+diff /tmp/fragment.nspawn "/etc/systemd/nspawn/$NSPAWN_FRAGMENT"
+
+for opt in format lines machine max-addresses output setenv verify; do
+    (! machinectl status "--$opt=" long-running)
+    (! machinectl status "--$opt=-1" long-running)
+    (! machinectl status "--$opt=''" long-running)
+done
+(! machinectl show "")
+(! machinectl enable)
+(! machinectl enable "")
+(! machinectl disable)
+(! machinectl disable "")
+(! machinectl read-only container1 "")
+(! machinectl read-only container1 foo)
+(! machinectl read-only container1 -- -1)
diff --git a/test/units/TEST-13-NSPAWN.nspawn-oci.sh b/test/units/TEST-13-NSPAWN.nspawn-oci.sh
new file mode 100755 (executable)
index 0000000..65dcc96
--- /dev/null
@@ -0,0 +1,467 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+export SYSTEMD_LOG_TARGET=journal
+
+# shellcheck disable=SC2317
+at_exit() {
+    set +e
+
+    mountpoint -q /var/lib/machines && umount /var/lib/machines
+    [[ -n "${DEV:-}" ]] && rm -f "$DEV"
+    [[ -n "${NETNS:-}" ]] && umount "$NETNS" && rm -f "$NETNS"
+    [[ -n "${TMPDIR:-}" ]] && rm -fr "$TMPDIR"
+    rm -f /run/systemd/nspawn/*.nspawn
+}
+
+trap at_exit EXIT
+
+# Mount temporary directory over /var/lib/machines to not pollute the image
+mkdir -p /var/lib/machines
+mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
+
+# Setup a couple of dirs/devices for the OCI containers
+DEV="$(mktemp -u /dev/oci-dev-XXX)"
+mknod -m 666 "$DEV" b 42 42
+NETNS="$(mktemp /var/tmp/netns.XXX)"
+mount --bind /proc/self/ns/net "$NETNS"
+TMPDIR="$(mktemp -d)"
+touch "$TMPDIR/hello"
+OCI="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.oci-bundle.XXX)"
+create_dummy_container "$OCI/rootfs"
+mkdir -p "$OCI/rootfs/opt/var"
+mkdir -p "$OCI/rootfs/opt/readonly"
+
+# Let's start with a simple config
+cat >"$OCI/config.json" <<EOF
+{
+    "ociVersion" : "1.0.0",
+    "root" : {
+            "path" : "rootfs"
+    },
+    "mounts" : [
+        {
+            "destination" : "/root",
+            "type" : "tmpfs",
+            "source" : "tmpfs"
+        }
+    ]
+}
+EOF
+systemd-nspawn --oci-bundle="$OCI" bash -xec 'mountpoint /root'
+
+# And now for something a bit more involved
+# Notes:
+#   - the hooks are parsed & processed, but never executed
+#   - set sysctl's are parsed but never used?
+#       - same goes for arg_sysctl in nspawn.c
+cat >"$OCI/config.json" <<EOF
+{
+    "ociVersion" : "1.0.0",
+    "hostname" : "my-oci-container",
+    "root" : {
+            "path" : "rootfs",
+            "readonly" : false
+    },
+    "mounts" : [
+        {
+            "destination" : "/root",
+            "type" : "tmpfs",
+            "source" : "tmpfs"
+        },
+        ${COVERAGE_BUILD_DIR:+"{ \"destination\" : \"$COVERAGE_BUILD_DIR\" },"}
+        {
+            "destination" : "/var",
+            "type" : "none",
+            "source" : "$TMPDIR",
+            "options" : ["rbind", "rw"]
+        }
+    ],
+    "process" : {
+        "terminal" : false,
+        "consoleSize" : {
+            "height" : 25,
+            "width" : 80
+        },
+        "user" : {
+            "uid" : 0,
+            "gid" : 0,
+            "additionalGids" : [5, 6]
+        },
+        "env" : [
+            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+            "FOO=bar"
+        ],
+        "cwd" : "/root",
+        "args" : [
+            "bash",
+            "-xe",
+            "/entrypoint.sh"
+        ],
+        "noNewPrivileges" : true,
+        "oomScoreAdj" : 20,
+        "capabilities" : {
+            "bounding" : [
+                "CAP_AUDIT_WRITE",
+                "CAP_KILL",
+                "CAP_NET_BIND_SERVICE"
+            ],
+            "permitted" : [
+                "CAP_AUDIT_WRITE",
+                "CAP_KILL",
+                "CAP_NET_BIND_SERVICE"
+            ],
+            "inheritable" : [
+                "CAP_AUDIT_WRITE",
+                "CAP_KILL",
+                "CAP_NET_BIND_SERVICE"
+            ],
+            "effective" : [
+                "CAP_AUDIT_WRITE",
+                "CAP_KILL"
+            ],
+            "ambient" : [
+                "CAP_NET_BIND_SERVICE"
+            ]
+        },
+        "rlimits" : [
+            {
+                "type" : "RLIMIT_NOFILE",
+                "soft" : 1024,
+                "hard" : 1024
+            },
+            {
+                "type" : "RLIMIT_RTPRIO",
+                "soft" : 5,
+                "hard" : 10
+            }
+        ]
+    },
+    "linux" : {
+        "namespaces" : [
+            {
+                "type" : "mount"
+            },
+            {
+                "type" : "network",
+                "path" : "$NETNS"
+            },
+            {
+                "type" : "pid"
+            },
+            {
+                "type" : "uts"
+            }
+        ],
+        "uidMappings" : [
+            {
+                "containerID" : 0,
+                "hostID" : 1000,
+                "size" : 100
+            }
+        ],
+        "gidMappings" : [
+            {
+                "containerID" : 0,
+                "hostID" : 1000,
+                "size" : 100
+            }
+        ],
+        "devices" : [
+            {
+                "type" : "c",
+                "path" : "/dev/zero",
+                "major" : 1,
+                "minor" : 5,
+                "fileMode" : 444
+            },
+            {
+                "type" : "b",
+                "path" : "$DEV",
+                "major" : 4,
+                "minor" : 2,
+                "fileMode" : 666,
+                "uid" : 0,
+                "gid" : 0
+            }
+        ],
+        "resources" : {
+            "devices" : [
+                {
+                    "allow" : false,
+                    "access" : "m"
+                },
+                {
+                    "allow" : true,
+                    "type" : "b",
+                    "major" : 4,
+                    "minor" : 2,
+                    "access" : "rwm"
+                }
+            ],
+            "memory" : {
+                "limit" : 134217728,
+                "reservation" : 33554432,
+                "swap" : 268435456
+            },
+            "cpu" : {
+                "shares" : 1024,
+                "quota" : 1000000,
+                "period" : 500000,
+                "cpus" : "0-7"
+            },
+            "blockIO" : {
+                "weight" : 10,
+                "weightDevice" : [
+                    {
+                        "major" : 4,
+                        "minor" : 2,
+                        "weight" : 500
+                    }
+                ],
+                "throttleReadBpsDevice" : [
+                    {
+                        "major" : 4,
+                        "minor" : 2,
+                        "rate" : 500
+                    }
+                ],
+                "throttleWriteBpsDevice" : [
+                    {
+                        "major" : 4,
+                        "minor" : 2,
+                        "rate" : 500
+                    }
+                ],
+                "throttleReadIOPSDevice" : [
+                    {
+                        "major" : 4,
+                        "minor" : 2,
+                        "rate" : 500
+                    }
+                ],
+                "throttleWriteIOPSDevice" : [
+                    {
+                        "major" : 4,
+                        "minor" : 2,
+                        "rate" : 500
+                    }
+                ]
+            },
+            "pids" : {
+                "limit" : 1024
+            }
+        },
+        "sysctl" : {
+            "kernel.domainname" : "foo.bar",
+            "vm.swappiness" : "60"
+        },
+        "seccomp" : {
+            "defaultAction" : "SCMP_ACT_ALLOW",
+            "architectures" : [
+                "SCMP_ARCH_ARM",
+                "SCMP_ARCH_X86_64"
+            ],
+            "syscalls" : [
+                {
+                    "names" : [
+                        "lchown",
+                        "chmod"
+                    ],
+                    "action" : "SCMP_ACT_ERRNO",
+                    "args" : [
+                        {
+                            "index" : 0,
+                            "value" : 1,
+                            "op" : "SCMP_CMP_NE"
+                        },
+                        {
+                            "index" : 1,
+                            "value" : 2,
+                            "valueTwo" : 3,
+                            "op" : "SCMP_CMP_MASKED_EQ"
+                        }
+                    ]
+                }
+            ]
+        },
+        "rootfsPropagation" : "shared",
+        "maskedPaths" : [
+            "/proc/kcore",
+            "/root/nonexistent"
+        ],
+        "readonlyPaths" : [
+            "/proc/sys",
+            "/opt/readonly"
+        ]
+    },
+    "hooks" : {
+        "prestart" : [
+            {
+                "path" : "/bin/sh",
+                "args" : [
+                    "-xec",
+                    "echo \$PRESTART_FOO >/prestart"
+                ],
+                "env" : [
+                    "PRESTART_FOO=prestart_bar",
+                    "ALSO_FOO=also_bar"
+                ],
+                "timeout" : 666
+            },
+            {
+                "path" : "/bin/touch",
+                "args" : [
+                    "/tmp/also-prestart"
+                ]
+            }
+        ],
+        "poststart" : [
+            {
+                "path" : "/bin/sh",
+                "args" : [
+                    "touch",
+                    "/poststart"
+                ]
+            }
+        ],
+        "poststop" : [
+            {
+                "path" : "/bin/sh",
+                "args" : [
+                    "touch",
+                    "/poststop"
+                ]
+            }
+        ]
+    },
+    "annotations" : {
+        "hello.world" : "1",
+        "foo" : "bar"
+    }
+}
+EOF
+# Create a simple "entrypoint" script that validates that the container
+# is created correctly according to the OCI config
+cat >"$OCI/rootfs/entrypoint.sh" <<EOF
+#!/usr/bin/bash -e
+
+# Mounts
+mountpoint /root
+mountpoint /var
+test -e /var/hello
+
+# Process
+[[ "\$PWD" == /root ]]
+[[ "\$FOO" == bar ]]
+
+# Process - rlimits
+[[ "\$(ulimit -S -n)" -eq 1024 ]]
+[[ "\$(ulimit -H -n)" -eq 1024 ]]
+[[ "\$(ulimit -S -r)" -eq 5 ]]
+[[ "\$(ulimit -H -r)" -eq 10 ]]
+[[ "\$(hostname)" == my-oci-container ]]
+
+# Linux - devices
+test -c /dev/zero
+test -b "$DEV"
+[[ "\$(stat -c '%t:%T' "$DEV")" == 4:2 ]]
+
+# Linux - maskedPaths
+test -e /proc/kcore
+cat /proc/kcore && exit 1
+test ! -e /root/nonexistent
+
+# Linux - readonlyPaths
+touch /opt/readonly/foo && exit 1
+
+exit 0
+EOF
+timeout 30 systemd-nspawn --oci-bundle="$OCI"
+
+# Test a couple of invalid configs
+INVALID_SNIPPETS=(
+    # Invalid object
+    '"foo" : { }'
+    '"process" : { "foo" : [ ] }'
+    # Non-absolute mount
+    '"mounts" : [ { "destination" : "foo", "type" : "tmpfs", "source" : "tmpfs" } ]'
+    # Invalid rlimit
+    '"process" : { "rlimits" : [ { "type" : "RLIMIT_FOO", "soft" : 0, "hard" : 0 } ] }'
+    # rlimit without RLIMIT_ prefix
+    '"process" : { "rlimits" : [ { "type" : "CORE", "soft" : 0, "hard" : 0 } ] }'
+    # Invalid env assignment
+    '"process" : { "env" : [ "foo" ] }'
+    '"process" : { "env" : [ "foo=bar", 1 ] }'
+    # Invalid process args
+    '"process" : { "args" : [ ] }'
+    '"process" : { "args" : [ "" ] }'
+    '"process" : { "args" : [ "foo", 1 ] }'
+    # Invalid capabilities
+    '"process" : { "capabilities" : { "bounding" : [ 1 ] } }'
+    '"process" : { "capabilities" : { "bounding" : [ "FOO_BAR" ] } }'
+    # Unsupported option (without JSON_PERMISSIVE)
+    '"linux" : { "resources" : { "cpu" : { "realtimeRuntime" : 1 } } }'
+    # Invalid namespace
+    '"linux" : { "namespaces" : [ { "type" : "foo" } ] }'
+    # Namespace path for a non-network namespace
+    '"linux" : { "namespaces" : [ { "type" : "user", "path" : "/foo/bar" } ] }'
+    # Duplicate namespace
+    '"linux" : { "namespaces" : [ { "type" : "ipc" }, { "type" : "ipc" } ] }'
+    # Invalid device type
+    '"linux" : { "devices" : [ { "type" : "foo", "path" : "/dev/foo" } ] }'
+    # Invalid cgroups path
+    '"linux" : { "cgroupsPath" : "/foo/bar/baz" }'
+    '"linux" : { "cgroupsPath" : "foo/bar/baz" }'
+    # Invalid sysctl assignments
+    '"linux" : { "sysctl" : { "vm.swappiness" : 60 } }'
+    '"linux" : { "sysctl" : { "foo..bar" : "baz" } }'
+    # Invalid seccomp assignments
+    '"linux" : { "seccomp" : { } }'
+    '"linux" : { "seccomp" : { "defaultAction" : 1 } }'
+    '"linux" : { "seccomp" : { "defaultAction" : "foo" } }'
+    '"linux" : { "seccomp" : { "defaultAction" : "SCMP_ACT_ALLOW", "syscalls" : [ { "action" : "SCMP_ACT_ERRNO", "names" : [ ] } ] } }'
+    # Invalid masked paths
+    '"linux" : { "maskedPaths" : [ "/foo", 1 ] }'
+    '"linux" : { "maskedPaths" : [ "/foo", "bar" ] }'
+    # Invalid read-only paths
+    '"linux" : { "readonlyPaths" : [ "/foo", 1 ] }'
+    '"linux" : { "readonlyPaths" : [ "/foo", "bar" ] }'
+    # Invalid hooks
+    '"hooks" : { "prestart" : [ { "path" : "/bin/sh", "timeout" : 0 } ] }'
+    # Invalid annotations
+    '"annotations" : { "" : "bar" }'
+    '"annotations" : { "foo" : 1 }'
+)
+
+for snippet in "${INVALID_SNIPPETS[@]}"; do
+    : "Snippet: $snippet"
+    cat >"$OCI/config.json" <<EOF
+{
+    "ociVersion" : "1.0.0",
+    "root" : {
+            "path" : "rootfs"
+    },
+    $snippet
+}
+EOF
+    (! systemd-nspawn --oci-bundle="$OCI" sh -c 'echo hello')
+done
+
+# Invalid OCI bundle version
+cat >"$OCI/config.json" <<EOF
+{
+    "ociVersion" : "6.6.6",
+    "root" : {
+            "path" : "rootfs"
+    }
+}
+EOF
+(! systemd-nspawn --oci-bundle="$OCI" sh -c 'echo hello')
diff --git a/test/units/TEST-13-NSPAWN.nspawn.sh b/test/units/TEST-13-NSPAWN.nspawn.sh
new file mode 100755 (executable)
index 0000000..7901e98
--- /dev/null
@@ -0,0 +1,977 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+#
+# Notes on coverage: when collecting coverage we need the $BUILD_DIR present
+# and writable in the container as well. To do this in the least intrusive way,
+# two things are going on in the background (only when built with -Db_coverage=true):
+#   1) the systemd-nspawn@.service is copied to /etc/systemd/system/ with
+#      --bind=$BUILD_DIR appended to the ExecStart= line
+#   2) each create_dummy_container() call also creates an .nspawn file in /run/systemd/nspawn/
+#      with the last fragment from the path used as a name
+#
+# The first change is quite self-contained and applies only to containers run
+# with machinectl. The second one might cause some unexpected side-effects, namely:
+#   - nspawn config (setting) files don't support dropins, so tests that test
+#     the config files might need some tweaking (as seen below with
+#     the $COVERAGE_BUILD_DIR shenanigans) since they overwrite the .nspawn file
+#   - also a note - if /etc/systemd/nspawn/cont-name.nspawn exists, it takes
+#     precedence and /run/systemd/nspawn/cont-name.nspawn won't be read even
+#     if it exists
+#   - also a note 2 - --bind= overrides any Bind= from a config file
+#   - in some cases we don't create a test container using create_dummy_container(),
+#     so in that case an explicit call to coverage_create_nspawn_dropin() is needed
+#
+# However, even after jumping through all these hooks, there still might (and is)
+# some "incorrectly" missing coverage, especially in the window between spawning
+# the inner child process and bind-mounting the coverage $BUILD_DIR
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+
+export SYSTEMD_LOG_LEVEL=debug
+export SYSTEMD_LOG_TARGET=journal
+
+at_exit() {
+    set +e
+
+    mountpoint -q /var/lib/machines && umount --recursive /var/lib/machines
+    rm -f /run/systemd/nspawn/*.nspawn
+}
+
+trap at_exit EXIT
+
+# check cgroup-v2
+IS_CGROUPSV2_SUPPORTED=no
+mkdir -p /tmp/cgroup2
+if mount -t cgroup2 cgroup2 /tmp/cgroup2; then
+    IS_CGROUPSV2_SUPPORTED=yes
+    umount /tmp/cgroup2
+fi
+rmdir /tmp/cgroup2
+
+# check cgroup namespaces
+IS_CGNS_SUPPORTED=no
+if [[ -f /proc/1/ns/cgroup ]]; then
+    IS_CGNS_SUPPORTED=yes
+fi
+
+IS_USERNS_SUPPORTED=no
+# On some systems (e.g. CentOS 7) the default limit for user namespaces
+# is set to 0, which causes the following unshare syscall to fail, even
+# with enabled user namespaces support. By setting this value explicitly
+# we can ensure the user namespaces support to be detected correctly.
+sysctl -w user.max_user_namespaces=10000
+if unshare -U bash -c :; then
+    IS_USERNS_SUPPORTED=yes
+fi
+
+# Mount temporary directory over /var/lib/machines to not pollute the image
+mkdir -p /var/lib/machines
+mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
+
+testcase_sanity() {
+    local template root image uuid tmpdir
+
+    tmpdir="$(mktemp -d)"
+    template="$(mktemp -d /tmp/nspawn-template.XXX)"
+    create_dummy_container "$template"
+    # Create a simple image from the just created container template
+    image="$(mktemp /var/lib/machines/TEST-13-NSPAWN.image-XXX.img)"
+    dd if=/dev/zero of="$image" bs=1M count=256
+    mkfs.ext4 "$image"
+    mkdir -p /mnt
+    mount -o loop "$image" /mnt
+    cp -r "$template"/* /mnt/
+    umount /mnt
+
+    systemd-nspawn --help --no-pager
+    systemd-nspawn --version
+
+    # --template=
+    root="$(mktemp -u -d /var/lib/machines/TEST-13-NSPAWN.sanity.XXX)"
+    coverage_create_nspawn_dropin "$root"
+    (! systemd-nspawn --directory="$root" bash -xec 'echo hello')
+    # Initialize $root from $template (the $root directory must not exist, hence
+    # the `mktemp -u` above)
+    systemd-nspawn --directory="$root" --template="$template" bash -xec 'echo hello'
+    systemd-nspawn --directory="$root" bash -xec 'echo hello; touch /initialized'
+    test -e "$root/initialized"
+    # Check if the $root doesn't get re-initialized once it's not empty
+    systemd-nspawn --directory="$root" --template="$template" bash -xec 'echo hello'
+    test -e "$root/initialized"
+
+    systemd-nspawn --directory="$root" --ephemeral bash -xec 'touch /ephemeral'
+    test ! -e "$root/ephemeral"
+    (! systemd-nspawn --directory="$root" \
+                      --read-only \
+                      bash -xec 'touch /nope')
+    test ! -e "$root/nope"
+    systemd-nspawn --image="$image" bash -xec 'echo hello'
+
+    # --volatile=
+    touch "$root/usr/has-usr"
+    # volatile(=yes): rootfs is tmpfs, /usr/ from the OS tree is mounted read only
+    systemd-nspawn --directory="$root"\
+                   --volatile \
+                   bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
+    test ! -e "$root/nope"
+    test ! -e "$root/usr/read-only"
+    systemd-nspawn --directory="$root"\
+                   --volatile=yes \
+                   bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
+    test ! -e "$root/nope"
+    test ! -e "$root/usr/read-only"
+    # volatile=state: rootfs is read-only, /var/ is tmpfs
+    systemd-nspawn --directory="$root" \
+                   --volatile=state \
+                   bash -xec 'test -e /usr/has-usr; mountpoint /var; touch /read-only && exit 1; touch /var/nope'
+    test ! -e "$root/read-only"
+    test ! -e "$root/var/nope"
+    # volatile=state: tmpfs overlay is mounted over rootfs
+    systemd-nspawn --directory="$root" \
+                   --volatile=overlay \
+                   bash -xec 'test -e /usr/has-usr; touch /nope; touch /var/also-nope; touch /usr/nope-too'
+    test ! -e "$root/nope"
+    test ! -e "$root/var/also-nope"
+    test ! -e "$root/usr/nope-too"
+
+    # --machine=, --hostname=
+    systemd-nspawn --directory="$root" \
+                   --machine="foo-bar.baz" \
+                   bash -xec '[[ $(hostname) == foo-bar.baz ]]'
+    systemd-nspawn --directory="$root" \
+                   --hostname="hello.world.tld" \
+                   bash -xec '[[ $(hostname) == hello.world.tld ]]'
+    systemd-nspawn --directory="$root" \
+                   --machine="foo-bar.baz" \
+                   --hostname="hello.world.tld" \
+                   bash -xec '[[ $(hostname) == hello.world.tld ]]'
+
+    # --uuid=
+    rm -f "$root/etc/machine-id"
+    uuid="deadbeef-dead-dead-beef-000000000000"
+    systemd-nspawn --directory="$root" \
+                   --uuid="$uuid" \
+                   bash -xec "[[ \$container_uuid == $uuid ]]"
+
+    # --as-pid2
+    systemd-nspawn --directory="$root" bash -xec '[[ $$ -eq 1 ]]'
+    systemd-nspawn --directory="$root" --as-pid2 bash -xec '[[ $$ -eq 2 ]]'
+
+    # --user=
+    # "Fake" getent passwd's bare minimum, so we don't have to pull it in
+    # with all the DSO shenanigans
+    cat >"$root/bin/getent" <<\EOF
+#!/bin/bash
+
+if [[ $# -eq 0 ]]; then
+    :
+elif [[ $1 == passwd ]]; then
+    echo "testuser:x:1000:1000:testuser:/:/bin/sh"
+elif [[ $1 == initgroups ]]; then
+    echo "testuser"
+fi
+EOF
+    chmod +x "$root/bin/getent"
+    systemd-nspawn --directory="$root" bash -xec '[[ $USER == root ]]'
+    systemd-nspawn --directory="$root" --user=testuser bash -xec '[[ $USER == testuser ]]'
+
+    # --settings= + .nspawn files
+    mkdir -p /run/systemd/nspawn/
+    uuid="deadbeef-dead-dead-beef-000000000000"
+    echo -ne "[Exec]\nMachineID=deadbeef-dead-dead-beef-111111111111" >/run/systemd/nspawn/foo-bar.nspawn
+    systemd-nspawn --directory="$root" \
+                   --machine=foo-bar \
+                   --settings=yes \
+                   bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]'
+    systemd-nspawn --directory="$root" \
+                   --machine=foo-bar \
+                   --uuid="$uuid" \
+                   --settings=yes \
+                   bash -xec "[[ \$container_uuid == $uuid ]]"
+    systemd-nspawn --directory="$root" \
+                   --machine=foo-bar \
+                   --uuid="$uuid" \
+                   --settings=override \
+                   bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]'
+    systemd-nspawn --directory="$root" \
+                   --machine=foo-bar \
+                   --uuid="$uuid" \
+                   --settings=trusted \
+                   bash -xec "[[ \$container_uuid == $uuid ]]"
+
+    # Mounts
+    mkdir "$tmpdir"/{1,2,3}
+    touch "$tmpdir/1/one" "$tmpdir/2/two" "$tmpdir/3/three"
+    touch "$tmpdir/foo"
+    # --bind=
+    systemd-nspawn --directory="$root" \
+                   ${COVERAGE_BUILD_DIR:+--bind="$COVERAGE_BUILD_DIR"} \
+                   --bind="$tmpdir:/foo" \
+                   --bind="$tmpdir:/also-foo:noidmap,norbind" \
+                   bash -xec 'test -e /foo/foo; touch /foo/bar; test -e /also-foo/bar'
+    test -e "$tmpdir/bar"
+    # --bind-ro=
+    systemd-nspawn --directory="$root" \
+                   --bind-ro="$tmpdir:/foo" \
+                   --bind-ro="$tmpdir:/bar:noidmap,norbind" \
+                   bash -xec 'test -e /foo/foo; touch /foo/baz && exit 1; touch /bar && exit 1; true'
+    # --inaccessible=
+    systemd-nspawn --directory="$root" \
+                   --inaccessible=/var \
+                   bash -xec 'touch /var/foo && exit 1; true'
+    # --tmpfs=
+    systemd-nspawn --directory="$root" \
+                   --tmpfs=/var:rw,nosuid,noexec \
+                   bash -xec 'touch /var/nope'
+    test ! -e "$root/var/nope"
+    # --overlay=
+    systemd-nspawn --directory="$root" \
+                   --overlay="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \
+                   bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/foo'
+    test -e "$tmpdir/3/foo"
+    # --overlay-ro=
+    systemd-nspawn --directory="$root" \
+                   --overlay-ro="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \
+                   bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/nope && exit 1; true'
+    test ! -e "$tmpdir/3/nope"
+    rm -fr "$tmpdir"
+
+    # --port (sanity only)
+    systemd-nspawn --network-veth --directory="$root" --port=80 --port=90 true
+    systemd-nspawn --network-veth --directory="$root" --port=80:8080 true
+    systemd-nspawn --network-veth --directory="$root" --port=tcp:80 true
+    systemd-nspawn --network-veth --directory="$root" --port=tcp:80:8080 true
+    systemd-nspawn --network-veth --directory="$root" --port=udp:80 true
+    systemd-nspawn --network-veth --directory="$root" --port=udp:80:8080 --port=tcp:80:8080 true
+    (! systemd-nspawn --network-veth --directory="$root" --port= true)
+    (! systemd-nspawn --network-veth --directory="$root" --port=-1 true)
+    (! systemd-nspawn --network-veth --directory="$root" --port=: true)
+    (! systemd-nspawn --network-veth --directory="$root" --port=icmp:80:8080 true)
+    (! systemd-nspawn --network-veth --directory="$root" --port=tcp::8080 true)
+    (! systemd-nspawn --network-veth --directory="$root" --port=8080: true)
+    # Exercise adding/removing ports from an interface
+    systemd-nspawn --directory="$root" \
+                   --network-veth \
+                   --port=6667 \
+                   --port=80:8080 \
+                   --port=udp:53 \
+                   --port=tcp:22:2222 \
+                   bash -xec 'ip addr add dev host0 10.0.0.10/24; ip a; ip addr del dev host0 10.0.0.10/24'
+
+    # --load-credential=, --set-credential=
+    echo "foo bar" >/tmp/cred.path
+    systemd-nspawn --directory="$root" \
+                   --load-credential=cred.path:/tmp/cred.path \
+                   --set-credential="cred.set:hello world" \
+                   bash -xec '[[ "$(</run/host/credentials/cred.path)" == "foo bar" ]]; [[ "$(</run/host/credentials/cred.set)" == "hello world" ]]'
+    # Combine with --user to ensure creds are still readable
+    systemd-nspawn --directory="$root" \
+                   --user=testuser \
+                   --no-new-privileges=yes \
+                   --load-credential=cred.path:/tmp/cred.path \
+                   --set-credential="cred.set:hello world" \
+                   bash -xec '[[ "$(</run/host/credentials/cred.path)" == "foo bar" ]]; [[ "$(</run/host/credentials/cred.set)" == "hello world" ]]'
+    rm -f /tmp/cred.path
+
+    # Assorted tests
+    systemd-nspawn --directory="$root" --suppress-sync=yes bash -xec 'echo hello'
+    systemd-nspawn --capability=help
+    systemd-nspawn --resolv-conf=help
+    systemd-nspawn --timezone=help
+
+    # Handling of invalid arguments
+    opts=(
+        bind
+        bind-ro
+        bind-user
+        chdir
+        console
+        inaccessible
+        kill-signal
+        link-journal
+        load-credential
+        network-{interface,macvlan,ipvlan,veth-extra,bridge,zone}
+        no-new-privileges
+        oom-score-adjust
+        overlay
+        overlay-ro
+        personality
+        pivot-root
+        port
+        private-users
+        private-users-ownership
+        register
+        resolv-conf
+        rlimit
+        root-hash
+        root-hash-sig
+        set-credential
+        settings
+        suppress-sync
+        timezone
+        tmpfs
+        uuid
+    )
+    for opt in "${opts[@]}"; do
+        (! systemd-nspawn "--$opt")
+        [[ "$opt" == network-zone ]] && continue
+        (! systemd-nspawn "--$opt=''")
+        (! systemd-nspawn "--$opt=%\$š")
+    done
+    (! systemd-nspawn --volatile="")
+    (! systemd-nspawn --volatile=-1)
+    (! systemd-nspawn --rlimit==)
+}
+
+nspawn_settings_cleanup() {
+    for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-ipvlan{1,2}; do
+        ip link del "$dev" || :
+    done
+
+    return 0
+}
+
+testcase_nspawn_settings() {
+    local root container dev private_users wlan_names
+
+    mkdir -p /run/systemd/nspawn
+    root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.nspawn-settings.XXX)"
+    container="$(basename "$root")"
+    create_dummy_container "$root"
+    rm -f "/etc/systemd/nspawn/$container.nspawn"
+    mkdir -p "$root/tmp" "$root"/opt/{tmp,inaccessible,also-inaccessible}
+
+    # add virtual wlan interfaces
+    if modprobe mac80211_hwsim radios=2; then
+        wlan_names="wlan0 wlan1:wl-renamed1"
+    fi
+
+    for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do
+        ip link add "$dev" type dummy
+    done
+    udevadm settle
+    ip link property add dev sd-shared3 altname sd-altname3 altname sd-altname-tooooooooooooo-long
+    ip link
+    trap nspawn_settings_cleanup RETURN
+
+    # Let's start with one huge config to test as much as we can at once
+    cat >"/run/systemd/nspawn/$container.nspawn" <<EOF
+[Exec]
+Boot=no
+Ephemeral=no
+ProcessTwo=no
+Parameters=bash /entrypoint.sh "foo bar" 'bar baz'
+Environment=FOO=bar
+Environment=BAZ="hello world"
+User=root
+WorkingDirectory=/tmp
+Capability=CAP_BLOCK_SUSPEND CAP_BPF CAP_CHOWN
+DropCapability=CAP_AUDIT_CONTROL CAP_AUDIT_WRITE
+AmbientCapability=CAP_BPF CAP_CHOWN
+NoNewPrivileges=no
+MachineID=f28f129b51874b1280a89421ec4b4ad4
+PrivateUsers=no
+NotifyReady=no
+SystemCallFilter=@basic-io @chown
+SystemCallFilter=~ @clock
+LimitNOFILE=1024:2048
+LimitRTPRIO=8:16
+OOMScoreAdjust=32
+CPUAffinity=0,0-5,1-5
+Hostname=nspawn-settings
+ResolvConf=copy-host
+Timezone=delete
+LinkJournal=no
+SuppressSync=no
+
+[Files]
+ReadOnly=no
+Volatile=no
+TemporaryFileSystem=/tmp
+TemporaryFileSystem=/opt/tmp
+Inaccessible=/opt/inaccessible
+Inaccessible=/opt/also-inaccessible
+PrivateUsersOwnership=auto
+Overlay=+/var::/var
+${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
+
+[Network]
+Private=yes
+VirtualEthernet=yes
+VirtualEthernetExtra=my-fancy-veth1
+VirtualEthernetExtra=fancy-veth2:my-fancy-veth2
+Interface=sd-shared1 sd-shared2:sd-renamed2 sd-shared3:sd-altname3 ${wlan_names:-}
+MACVLAN=sd-macvlan1 sd-macvlan2:my-macvlan2 sd-macvlanloong
+IPVLAN=sd-ipvlan1 sd-ipvlan2:my-ipvlan2 sd-ipvlanlooong
+Zone=sd-zone0
+Port=80
+Port=81:8181
+Port=tcp:60
+Port=udp:60:61
+EOF
+    cat >"$root/entrypoint.sh" <<\EOF
+#!/bin/bash
+set -ex
+
+env
+
+[[ "$1" == "foo bar" ]]
+[[ "$2" == "bar baz" ]]
+
+[[ "$USER" == root ]]
+[[ "$FOO" == bar ]]
+[[ "$BAZ" == "hello world" ]]
+[[ "$PWD" == /tmp ]]
+[[ "$container_uuid" == f28f129b-5187-4b12-80a8-9421ec4b4ad4 ]]
+[[ "$(ulimit -S -n)" -eq 1024 ]]
+[[ "$(ulimit -H -n)" -eq 2048 ]]
+[[ "$(ulimit -S -r)" -eq 8 ]]
+[[ "$(ulimit -H -r)" -eq 16 ]]
+[[ "$(</proc/self/oom_score_adj)" -eq 32 ]]
+[[ "$(hostname)" == nspawn-settings ]]
+[[ -e /etc/resolv.conf ]]
+[[ ! -e /etc/localtime ]]
+
+mountpoint /tmp
+touch /tmp/foo
+mountpoint /opt/tmp
+touch /opt/tmp/foo
+touch /opt/inaccessible/foo && exit 1
+touch /opt/also-inaccessible/foo && exit 1
+mountpoint /var
+
+ip link
+ip link | grep host-only && exit 1
+ip link | grep host0@
+ip link | grep my-fancy-veth1@
+ip link | grep my-fancy-veth2@
+ip link | grep sd-shared1
+ip link | grep sd-renamed2
+ip link | grep sd-shared3
+ip link | grep sd-altname3
+ip link | grep sd-altname-tooooooooooooo-long
+ip link | grep mv-sd-macvlan1@
+ip link | grep my-macvlan2@
+ip link | grep iv-sd-ipvlan1@
+ip link | grep my-ipvlan2@
+EOF
+    if [[ -n "${wlan_names:-}" ]]; then
+        cat >>"$root/entrypoint.sh" <<\EOF
+ip link | grep wlan0
+ip link | grep wl-renamed1
+EOF
+    fi
+
+    timeout 30 systemd-nspawn --directory="$root"
+
+    # And now for stuff that needs to run separately
+    #
+    # Note on the condition below: since our container tree is owned by root,
+    # both "yes" and "identity" private users settings will behave the same
+    # as PrivateUsers=0:65535, which makes BindUser= fail as the UID already
+    # exists there, so skip setting it in such case
+    for private_users in "131072:65536" yes identity pick; do
+        cat >"/run/systemd/nspawn/$container.nspawn" <<EOF
+[Exec]
+Hostname=private-users
+PrivateUsers=$private_users
+
+[Files]
+PrivateUsersOwnership=auto
+BindUser=
+$([[ "$private_users" =~ (yes|identity) ]] || echo "BindUser=testuser")
+${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
+EOF
+        cat "/run/systemd/nspawn/$container.nspawn"
+        chown -R root:root "$root"
+        systemd-nspawn --directory="$root" bash -xec '[[ "$(hostname)" == private-users ]]'
+    done
+
+    rm -fr "$root" "/run/systemd/nspawn/$container.nspawn"
+}
+
+bind_user_cleanup() {
+    userdel --force --remove nspawn-bind-user-1
+    userdel --force --remove nspawn-bind-user-2
+}
+
+testcase_bind_user() {
+    local root
+
+    root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.bind-user.XXX)"
+    create_dummy_container "$root"
+    useradd --create-home --user-group nspawn-bind-user-1
+    useradd --create-home --user-group nspawn-bind-user-2
+    trap bind_user_cleanup RETURN
+    touch /home/nspawn-bind-user-1/foo
+    touch /home/nspawn-bind-user-2/bar
+    # Add a couple of POSIX ACLs to test the patch-uid stuff
+    mkdir -p "$root/opt"
+    setfacl -R -m 'd:u:nspawn-bind-user-1:rwX' -m 'u:nspawn-bind-user-1:rwX' "$root/opt"
+    setfacl -R -m 'd:g:nspawn-bind-user-1:rwX' -m 'g:nspawn-bind-user-1:rwX' "$root/opt"
+
+    systemd-nspawn --directory="$root" \
+                   --private-users=pick \
+                   --bind-user=nspawn-bind-user-1 \
+                   bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo'
+
+    systemd-nspawn --directory="$root" \
+                   --private-users=pick \
+                   --private-users-ownership=chown \
+                   --bind-user=nspawn-bind-user-1 \
+                   --bind-user=nspawn-bind-user-2 \
+                   bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo; test -e /run/host/home/nspawn-bind-user-2/bar'
+    chown -R root:root "$root"
+
+    # User/group name collision
+    echo "nspawn-bind-user-2:x:1000:1000:nspawn-bind-user-2:/home/nspawn-bind-user-2:/bin/bash" >"$root/etc/passwd"
+    (! systemd-nspawn --directory="$root" \
+                      --private-users=pick \
+                      --bind-user=nspawn-bind-user-1 \
+                      --bind-user=nspawn-bind-user-2 \
+                      true)
+    rm -f "$root/etc/passwd"
+
+    echo "nspawn-bind-user-2:x:1000:" >"$root/etc/group"
+    (! systemd-nspawn --directory="$root" \
+                      --private-users=pick \
+                      --bind-user=nspawn-bind-user-1 \
+                      --bind-user=nspawn-bind-user-2 \
+                      true)
+    rm -f "$root/etc/group"
+
+    rm -fr "$root"
+}
+
+testcase_bind_tmp_path() {
+    # https://github.com/systemd/systemd/issues/4789
+    local root
+
+    root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.bind-tmp-path.XXX)"
+    create_dummy_container "$root"
+    : >/tmp/bind
+    systemd-nspawn --register=no \
+                   --directory="$root" \
+                   --bind=/tmp/bind \
+                   bash -c 'test -e /tmp/bind'
+
+    rm -fr "$root" /tmp/bind
+}
+
+testcase_norbind() {
+    # https://github.com/systemd/systemd/issues/13170
+    local root
+
+    root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.norbind-path.XXX)"
+    mkdir -p /tmp/binddir/subdir
+    echo -n "outer" >/tmp/binddir/subdir/file
+    mount -t tmpfs tmpfs /tmp/binddir/subdir
+    echo -n "inner" >/tmp/binddir/subdir/file
+    create_dummy_container "$root"
+
+    systemd-nspawn --register=no \
+                   --directory="$root" \
+                   --bind=/tmp/binddir:/mnt:norbind \
+                   bash -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; exit 1; fi'
+
+    umount /tmp/binddir/subdir
+    rm -fr "$root" /tmp/binddir/
+}
+
+rootidmap_cleanup() {
+    local dir="${1:?}"
+
+    mountpoint -q "$dir/bind" && umount "$dir/bind"
+    rm -fr "$dir"
+}
+
+testcase_rootidmap() {
+    local root cmd permissions
+    local owner=1000
+
+    root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.rootidmap-path.XXX)"
+    # Create ext4 image, as ext4 supports idmapped-mounts.
+    mkdir -p /tmp/rootidmap/bind
+    dd if=/dev/zero of=/tmp/rootidmap/ext4.img bs=4k count=2048
+    mkfs.ext4 /tmp/rootidmap/ext4.img
+    mount /tmp/rootidmap/ext4.img /tmp/rootidmap/bind
+    trap "rootidmap_cleanup /tmp/rootidmap/" RETURN
+
+    touch /tmp/rootidmap/bind/file
+    chown -R "$owner:$owner" /tmp/rootidmap/bind
+
+    create_dummy_container "$root"
+    cmd='PERMISSIONS=$(stat -c "%u:%g" /mnt/file); if [[ $PERMISSIONS != "0:0" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /mnt/other_file'
+    if ! SYSTEMD_LOG_TARGET=console \
+            systemd-nspawn --register=no \
+                           --directory="$root" \
+                           --bind=/tmp/rootidmap/bind:/mnt:rootidmap \
+                           bash -c "$cmd" |& tee nspawn.out; then
+        if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then
+            echo "idmapped mounts are not supported, skipping the test..."
+            return 0
+        fi
+
+        return 1
+    fi
+
+    permissions=$(stat -c "%u:%g" /tmp/rootidmap/bind/other_file)
+    if [[ $permissions != "$owner:$owner" ]]; then
+        echo "*** wrong permissions: $permissions"
+        [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1
+    fi
+}
+
+owneridmap_cleanup() {
+    local dir="${1:?}"
+
+    mountpoint -q "$dir/bind" && umount "$dir/bind"
+    rm -fr "$dir"
+}
+
+testcase_owneridmap() {
+    local root cmd permissions
+    local owner=1000
+
+    root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.owneridmap-path.XXX)"
+    # Create ext4 image, as ext4 supports idmapped-mounts.
+    mkdir -p /tmp/owneridmap/bind
+    dd if=/dev/zero of=/tmp/owneridmap/ext4.img bs=4k count=2048
+    mkfs.ext4 /tmp/owneridmap/ext4.img
+    mount /tmp/owneridmap/ext4.img /tmp/owneridmap/bind
+    trap "owneridmap_cleanup /tmp/owneridmap/" RETURN
+
+    touch /tmp/owneridmap/bind/file
+    chown -R "$owner:$owner" /tmp/owneridmap/bind
+
+    # Allow users to read and execute / in order to execute binaries
+    chmod o+rx "$root"
+
+    create_dummy_container "$root"
+
+    # --user=
+    # "Fake" getent passwd's bare minimum, so we don't have to pull it in
+    # with all the DSO shenanigans
+    cat >"$root/bin/getent" <<\EOF
+#!/bin/bash
+
+if [[ $# -eq 0 ]]; then
+    :
+elif [[ $1 == passwd ]]; then
+    echo "testuser:x:1010:1010:testuser:/:/bin/sh"
+elif [[ $1 == initgroups ]]; then
+    echo "testuser"
+fi
+EOF
+    chmod +x "$root/bin/getent"
+
+    mkdir -p "$root/home/testuser"
+    chown 1010:1010 "$root/home/testuser"
+
+    cmd='PERMISSIONS=$(stat -c "%u:%g" /home/testuser/file); if [[ $PERMISSIONS != "1010:1010" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /home/testuser/other_file'
+    if ! SYSTEMD_LOG_TARGET=console \
+            systemd-nspawn --register=no \
+                           --directory="$root" \
+                           -U \
+                           --user=testuser \
+                           --bind=/tmp/owneridmap/bind:/home/testuser:owneridmap \
+                           ${COVERAGE_BUILD_DIR:+--bind="$COVERAGE_BUILD_DIR"} \
+                           /usr/bin/bash -c "$cmd" |& tee nspawn.out; then
+        if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then
+            echo "idmapped mounts are not supported, skipping the test..."
+            return 0
+        fi
+
+        return 1
+    fi
+
+    permissions=$(stat -c "%u:%g" /tmp/owneridmap/bind/other_file)
+    if [[ $permissions != "$owner:$owner" ]]; then
+        echo "*** wrong permissions: $permissions"
+        [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1
+    fi
+}
+
+testcase_notification_socket() {
+    # https://github.com/systemd/systemd/issues/4944
+    local root
+    local cmd='echo a | nc -U -u -w 1 /run/host/notify'
+
+    root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.check_notification_socket.XXX)"
+    create_dummy_container "$root"
+
+    systemd-nspawn --register=no --directory="$root" bash -x -c "$cmd"
+    systemd-nspawn --register=no --directory="$root" -U bash -x -c "$cmd"
+
+    rm -fr "$root"
+}
+
+testcase_os_release() {
+    local root entrypoint os_release_source
+
+    root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.os-release.XXX)"
+    create_dummy_container "$root"
+    entrypoint="$root/entrypoint.sh"
+    cat >"$entrypoint" <<\EOF
+#!/usr/bin/bash -ex
+
+. /tmp/os-release
+[[ -n "${ID:-}" && "$ID" != "$container_host_id" ]] && exit 1
+[[ -n "${VERSION_ID:-}" && "$VERSION_ID" != "$container_host_version_id" ]] && exit 1
+[[ -n "${BUILD_ID:-}" && "$BUILD_ID" != "$container_host_build_id" ]] && exit 1
+[[ -n "${VARIANT_ID:-}" && "$VARIANT_ID" != "$container_host_variant_id" ]] && exit 1
+
+cd /tmp
+(cd /run/host && md5sum os-release) | md5sum -c
+EOF
+    chmod +x "$entrypoint"
+
+    os_release_source="/etc/os-release"
+    if [[ ! -r "$os_release_source" ]]; then
+        os_release_source="/usr/lib/os-release"
+    elif [[ -L "$os_release_source" ]]; then
+        # Ensure that /etc always wins if available
+        cp --remove-destination -fv /usr/lib/os-release /etc/os-release
+        echo MARKER=1 >>/etc/os-release
+    fi
+
+    systemd-nspawn --register=no \
+                   --directory="$root" \
+                   --bind="$os_release_source:/tmp/os-release" \
+                   "${entrypoint##"$root"}"
+
+    if grep -q MARKER /etc/os-release; then
+        ln -svrf /usr/lib/os-release /etc/os-release
+    fi
+
+    rm -fr "$root"
+}
+
+testcase_machinectl_bind() {
+    local service_path service_name root container_name ec
+    local cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep .5; done; exit 1;'
+
+    root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.machinectl-bind.XXX)"
+    create_dummy_container "$root"
+    container_name="$(basename "$root")"
+
+    service_path="$(mktemp /run/systemd/system/nspawn-machinectl-bind-XXX.service)"
+    service_name="${service_path##*/}"
+    cat >"$service_path" <<EOF
+[Service]
+Type=notify
+ExecStart=systemd-nspawn --directory="$root" --notify-ready=no /usr/bin/bash -xec "$cmd"
+EOF
+
+    systemctl daemon-reload
+    systemctl start "$service_name"
+    touch /tmp/marker
+    machinectl bind --mkdir "$container_name" /tmp/marker
+
+    timeout 10 bash -c "while [[ '\$(systemctl show -P SubState $service_name)' == running ]]; do sleep .2; done"
+    ec="$(systemctl show -P ExecMainStatus "$service_name")"
+    systemctl stop "$service_name"
+
+    rm -fr "$root" "$service_path"
+
+    return "$ec"
+}
+
+testcase_selinux() {
+    # Basic test coverage to avoid issues like https://github.com/systemd/systemd/issues/19976
+    if ! command -v selinuxenabled >/dev/null || ! selinuxenabled; then
+        echo >&2 "SELinux is not enabled, skipping SELinux-related tests"
+        return 0
+    fi
+
+    local root
+
+    root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.selinux.XXX)"
+    create_dummy_container "$root"
+    chcon -R -t container_t "$root"
+
+    systemd-nspawn --register=no \
+                   --boot \
+                   --directory="$root" \
+                   --selinux-apifs-context=system_u:object_r:container_file_t:s0:c0,c1 \
+                   --selinux-context=system_u:system_r:container_t:s0:c0,c1
+
+    rm -fr "$root"
+}
+
+testcase_ephemeral_config() {
+    # https://github.com/systemd/systemd/issues/13297
+    local root container_name
+
+    root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.ephemeral-config.XXX)"
+    create_dummy_container "$root"
+    container_name="$(basename "$root")"
+
+    mkdir -p /run/systemd/nspawn/
+    rm -f "/etc/systemd/nspawn/$container_name.nspawn"
+    cat >"/run/systemd/nspawn/$container_name.nspawn" <<EOF
+[Files]
+${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
+BindReadOnly=/tmp/ephemeral-config
+EOF
+    touch /tmp/ephemeral-config
+
+    systemd-nspawn --register=no \
+                   --directory="$root" \
+                   --ephemeral \
+                   bash -x -c "test -f /tmp/ephemeral-config"
+
+    systemd-nspawn --register=no \
+                   --directory="$root" \
+                   --ephemeral \
+                   --machine=foobar \
+                   bash -x -c "! test -f /tmp/ephemeral-config"
+
+    rm -fr "$root" "/run/systemd/nspawn/$container_name.nspawn"
+}
+
+matrix_run_one() {
+    local cgroupsv2="${1:?}"
+    local use_cgns="${2:?}"
+    local api_vfs_writable="${3:?}"
+    local root
+
+    if [[ "$cgroupsv2" == "yes" && "$IS_CGROUPSV2_SUPPORTED" == "no" ]]; then
+        echo >&2 "Unified cgroup hierarchy is not supported, skipping..."
+        return 0
+    fi
+
+    if [[ "$use_cgns" == "yes" && "$IS_CGNS_SUPPORTED" == "no" ]];  then
+        echo >&2 "CGroup namespaces are not supported, skipping..."
+        return 0
+    fi
+
+    root="$(mktemp -d "/var/lib/machines/TEST-13-NSPAWN.unified-$1-cgns-$2-api-vfs-writable-$3.XXX")"
+    create_dummy_container "$root"
+
+    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+        systemd-nspawn --register=no \
+                       --directory="$root" \
+                       --boot
+
+    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+        systemd-nspawn --register=no \
+                       --directory="$root" \
+                       --private-network \
+                       --boot
+
+    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+        systemd-nspawn --register=no \
+                       --directory="$root" \
+                       --private-users=pick \
+                       --boot; then
+        [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "network" ]] && return 1
+    else
+        [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "network" ]] && return 1
+    fi
+
+    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+        systemd-nspawn --register=no \
+                       --directory="$root" \
+                       --private-network \
+                       --private-users=pick \
+                       --boot; then
+        [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "yes" ]] && return 1
+    else
+        [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "yes" ]] && return 1
+    fi
+
+    local netns_opt="--network-namespace-path=/proc/self/ns/net"
+    local net_opt
+    local net_opts=(
+        "--network-bridge=lo"
+        "--network-interface=lo"
+        "--network-ipvlan=lo"
+        "--network-macvlan=lo"
+        "--network-veth"
+        "--network-veth-extra=lo"
+        "--network-zone=zone"
+    )
+
+    # --network-namespace-path and network-related options cannot be used together
+    for net_opt in "${net_opts[@]}"; do
+        echo "$netns_opt in combination with $net_opt should fail"
+        if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+            systemd-nspawn --register=no \
+                           --directory="$root" \
+                           --boot \
+                           "$netns_opt" \
+                           "$net_opt"; then
+            echo >&2 "unexpected pass"
+            return 1
+        fi
+    done
+
+    # allow combination of --network-namespace-path and --private-network
+    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+        systemd-nspawn --register=no \
+                       --directory="$root" \
+                       --boot \
+                       --private-network \
+                       "$netns_opt"
+
+    # test --network-namespace-path works with a network namespace created by "ip netns"
+    ip netns add nspawn_test
+    netns_opt="--network-namespace-path=/run/netns/nspawn_test"
+    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
+        systemd-nspawn --register=no \
+                       --directory="$root" \
+                       --network-namespace-path=/run/netns/nspawn_test \
+                       ip a | grep -v -E '^1: lo.*UP'
+    ip netns del nspawn_test
+
+    rm -fr "$root"
+
+    return 0
+}
+
+testcase_check_os_release() {
+    # https://github.com/systemd/systemd/issues/29185
+    local base common_opts root
+
+    base="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.check_os_release_base.XXX)"
+    root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.check_os_release.XXX)"
+    create_dummy_container "$base"
+    cp -d "$base"/{bin,sbin,lib,lib64} "$root/"
+    common_opts=(
+        --boot
+        --register=no
+        --directory="$root"
+        --bind-ro="$base/usr:/usr"
+    )
+
+    # Empty /etc/ & /usr/
+    (! systemd-nspawn "${common_opts[@]}")
+    (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}")
+    (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=foo systemd-nspawn "${common_opts[@]}")
+    SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}"
+
+    # Empty /usr/ + a broken /etc/os-release -> /usr/os-release symlink
+    ln -svrf "$root/etc/os-release" "$root/usr/os-release"
+    (! systemd-nspawn "${common_opts[@]}")
+    (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}")
+    SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}"
+
+    rm -fr "$root" "$base"
+}
+
+run_testcases
+
+for api_vfs_writable in yes no network; do
+    matrix_run_one no  no  $api_vfs_writable
+    matrix_run_one yes no  $api_vfs_writable
+    matrix_run_one no  yes $api_vfs_writable
+    matrix_run_one yes yes $api_vfs_writable
+done
diff --git a/test/units/TEST-13-NSPAWN.nss-mymachines.sh b/test/units/TEST-13-NSPAWN.nss-mymachines.sh
new file mode 100755 (executable)
index 0000000..817431b
--- /dev/null
@@ -0,0 +1,136 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+    set +e
+
+    machinectl kill --signal=KILL nss-mymachines-{noip,singleip,manyips}
+    mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done"
+    rm -f /run/systemd/nspawn/*.nspawn
+}
+
+trap at_exit EXIT
+
+# Mount temporary directory over /var/lib/machines to not pollute the image
+mkdir -p /var/lib/machines
+mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
+
+# Create a bunch of containers that:
+# 1) Have no IP addresses assigned
+create_dummy_container /var/lib/machines/nss-mymachines-noip
+cat >/var/lib/machines/nss-mymachines-noip/sbin/init <<\EOF
+#!/usr/bin/bash -ex
+
+ip addr show dev ve-noip
+touch /initialized
+sleep infinity &
+# Run the sleep command asynchronously, so bash is able to process signals
+while :; do
+    wait || :
+done
+EOF
+# 2) Have one IP address assigned (IPv4 only)
+create_dummy_container /var/lib/machines/nss-mymachines-singleip
+cat >/var/lib/machines/nss-mymachines-singleip/sbin/init <<\EOF
+#!/usr/bin/bash -ex
+
+ip addr add 10.1.0.2/24 dev ve-singleip
+ip addr show dev ve-singleip
+touch /initialized
+sleep infinity &
+while :; do
+    wait || :
+done
+EOF
+# 3) Have bunch of IP addresses assigned (both IPv4 and IPv6)
+create_dummy_container /var/lib/machines/nss-mymachines-manyips
+cat >/var/lib/machines/nss-mymachines-manyips/sbin/init <<\EOF
+#!/usr/bin/bash -ex
+
+ip addr add 10.2.0.2/24 dev ve-manyips
+for i in {100..120}; do
+    ip addr add 10.2.0.$i/24 dev ve-manyips
+done
+ip addr add fd00:dead:beef:cafe::2/64 dev ve-manyips nodad
+ip addr show dev ve-manyips
+touch /initialized
+sleep infinity
+while :; do
+    wait || :
+done
+EOF
+# Create the respective .nspawn config files
+mkdir -p /run/systemd/nspawn
+for container in noip singleip manyips; do
+    cat >"/run/systemd/nspawn/nss-mymachines-$container.nspawn" <<EOF
+[Exec]
+Boot=yes
+
+[Network]
+VirtualEthernetExtra=ve-$container
+EOF
+done
+
+# Start the containers and wait until all of them are initialized
+machinectl start nss-mymachines-{noip,singleip,manyips}
+for container in nss-mymachines-{noip,singleip,manyips}; do
+    timeout 30 bash -xec "while [[ ! -e /var/lib/machines/$container/initialized ]]; do sleep .5; done"
+done
+
+# We need to configure the dummy interfaces on the "outside" as well for `getent {ahosts4,ahosts6}` to work
+# properly. This is caused by getaddrinfo() calling _check_pf() that iterates through all interfaces and
+# notes if any of them has an IPv4/IPv6 - this is then used together with AF_INET/AF_INET6 to determine if we
+# can ever return a valid answer, and if we configured the container interfaces only in the container, we
+# would have no valid IPv4/IPv6 on the "outside" (as we don't set up any other netdev) which would make
+# getaddrinfo() return EAI_NONAME without ever asking nss-mymachines.
+ip addr add 10.1.0.1/24 dev ve-singleip
+ip addr add 10.2.0.1/24 dev ve-manyips
+ip addr add fd00:dead:beef:cafe::1/64 dev ve-manyips nodad
+
+getent hosts -s mymachines
+getent ahosts -s mymachines
+
+# And finally check if we can resolve the containers via nss-mymachines
+for database in hosts ahosts{,v4,v6}; do
+    (! getent "$database" -s mymachines nss-mymachines-noip)
+done
+
+run_and_grep "^10\.1\.0\.2\s+nss-mymachines-singleip$" getent hosts -s mymachines nss-mymachines-singleip
+run_and_grep "^10\.1\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-singleip
+run_and_grep "^10\.1\.0\.2\s+STREAM" getent ahostsv4 -s mymachines nss-mymachines-singleip
+run_and_grep "^::ffff:10\.1\.0\.2\s+STREAM" getent ahostsv6 -s mymachines nss-mymachines-singleip
+
+run_and_grep "^fd00:dead:beef:cafe::2\s+nss-mymachines-manyips$" getent hosts -s mymachines nss-mymachines-manyips
+run_and_grep "^fd00:dead:beef:cafe::2\s+STREAM" getent ahosts -s mymachines nss-mymachines-manyips
+run_and_grep "^10\.2\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-manyips
+for i in {100..120}; do
+    run_and_grep "^10\.2\.0\.$i\s+STREAM" getent ahosts -s mymachines nss-mymachines-manyips
+    run_and_grep "^10\.2\.0\.$i\s+STREAM" getent ahostsv4 -s mymachines nss-mymachines-manyips
+done
+run_and_grep "^fd00:dead:beef:cafe::2\s+STREAM" getent ahostsv6 -s mymachines nss-mymachines-manyips
+(! run_and_grep "^fd00:" getent ahostsv4 -s mymachines nss-mymachines-manyips)
+(! run_and_grep "^10\.2:" getent ahostsv6 -s mymachines nss-mymachines-manyips)
+
+# Multiple machines at once
+run_and_grep "^10\.1\.0\.2\s+nss-mymachines-singleip$" getent hosts -s mymachines nss-mymachines-{singleip,manyips}
+run_and_grep "^fd00:dead:beef:cafe::2\s+nss-mymachines-manyips$" getent hosts -s mymachines nss-mymachines-{singleip,manyips}
+run_and_grep "^10\.1\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-{singleip,manyips}
+run_and_grep "^10\.2\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-{singleip,manyips}
+run_and_grep "^fd00:dead:beef:cafe::2\s+STREAM" getent ahosts -s mymachines nss-mymachines-{singleip,manyips}
+
+for database in hosts ahosts ahostsv4 ahostsv6; do
+    (! getent "$database" -s mymachines foo-bar-baz)
+done
+
+# getgrid(), getgrnam(), getpwuid(), and getpwnam() are explicitly handled by nss-mymachines, so probe them
+# as well
+(! getent group -s mymachines foo 11)
+(! getent passwd -s mymachines foo 11)
+
+machinectl stop nss-mymachines-{noip,singleip,manyips}
diff --git a/test/units/TEST-13-NSPAWN.service b/test/units/TEST-13-NSPAWN.service
new file mode 100644 (file)
index 0000000..95c9a02
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-13-NSPAWN
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-13-NSPAWN.sh b/test/units/TEST-13-NSPAWN.sh
new file mode 100755 (executable)
index 0000000..dd7f274
--- /dev/null
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+FSTYPE="$(stat --file-system --format "%T" /)"
+
+if [[ "$FSTYPE" == "fuseblk" ]]; then
+    echo "Root filesystem is virtiofs, skipping"
+    exit 77
+fi
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
diff --git a/test/units/TEST-15-DROPIN.service b/test/units/TEST-15-DROPIN.service
new file mode 100644 (file)
index 0000000..10af93f
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-15-DROPIN
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-15-DROPIN.sh b/test/units/TEST-15-DROPIN.sh
new file mode 100755 (executable)
index 0000000..f5bd8d8
--- /dev/null
@@ -0,0 +1,713 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+clear_unit() {
+    local unit_name="${1:?}"
+    local base suffix
+
+    systemctl stop "$unit_name" 2>/dev/null || :
+    rm -f  /{etc,run,usr/lib}/systemd/system/"$unit_name"
+    rm -fr /{etc,run,usr/lib}/systemd/system/"$unit_name".d
+    rm -fr /{etc,run,usr/lib}/systemd/system/"$unit_name".{wants,requires}
+    if [[ $unit_name == *@* ]]; then
+        base="${unit_name%@*}"
+        suffix="${unit_name##*.}"
+        systemctl stop "$base@"*."$suffix" 2>/dev/null || :
+        rm -f  /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix"
+        rm -fr /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix".d
+        rm -fr /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix".{wants,requires}
+    fi
+}
+
+clear_units() {
+    for u in "$@"; do
+        clear_unit "$u"
+    done
+    systemctl daemon-reload
+}
+
+create_service() {
+    local service_name="${1:?}"
+    clear_units "${service_name}".service
+
+    cat >/etc/systemd/system/"$service_name".service <<EOF
+[Unit]
+Description=$service_name unit
+
+[Service]
+ExecStart=sleep 100000
+EOF
+    mkdir -p /{etc,run,usr/lib}/systemd/system/"$service_name".service.{d,wants,requires}
+}
+
+create_services() {
+    for u in "$@"; do
+        create_service "$u"
+    done
+}
+
+check_ok() {
+    x="$(systemctl show --value -p "${2:?}" "${1:?}")"
+    case "$x" in
+        *${3:?}*) return 0 ;;
+        *)        return 1 ;;
+    esac
+}
+
+check_ko() {
+    ! check_ok "$@"
+}
+
+testcase_basic_dropins() {
+    echo "Testing basic dropins..."
+
+    echo "*** test a wants b wants c"
+    create_services test15-a test15-b test15-c
+    ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/
+    ln -s ../test15-c.service /etc/systemd/system/test15-b.service.wants/
+    check_ok test15-a Wants test15-b.service
+    check_ok test15-b Wants test15-c.service
+
+    echo "*** test a wants,requires b"
+    create_services test15-a test15-b test15-c
+    ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/
+    ln -s ../test15-b.service /etc/systemd/system/test15-a.service.requires/
+    check_ok test15-a Wants test15-b.service
+    check_ok test15-a Requires test15-b.service
+
+    echo "*** test a wants nonexistent"
+    create_service test15-a
+    ln -s ../nonexistent.service /etc/systemd/system/test15-a.service.wants/
+    check_ok test15-a Wants nonexistent.service
+    systemctl start test15-a
+    systemctl stop  test15-a
+
+    echo "*** test a requires nonexistent"
+    ln -sf ../nonexistent.service /etc/systemd/system/test15-a.service.requires/
+    systemctl daemon-reload
+    check_ok test15-a Requires nonexistent.service
+
+    # 'b' is already loaded when 'c' pulls it in via a dropin.
+    echo "*** test a,c require b"
+    create_services test15-a test15-b test15-c
+    ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
+    ln -sf ../test15-b.service /etc/systemd/system/test15-c.service.requires/
+    systemctl start test15-a
+    check_ok test15-c Requires test15-b.service
+    systemctl stop test15-a test15-b
+
+    # 'b'  is already loaded when 'c' pulls it in via an alias dropin.
+    echo "*** test a wants alias"
+    create_services test15-a test15-b test15-c
+    ln -sf test15-c.service /etc/systemd/system/test15-c1.service
+    ln -sf ../test15-c.service  /etc/systemd/system/test15-a.service.wants/
+    ln -sf ../test15-c1.service /etc/systemd/system/test15-b.service.wants/
+    systemctl start test15-a
+    check_ok test15-a Wants test15-c.service
+    check_ok test15-b Wants test15-c.service
+    systemctl stop test15-a test15-c
+
+    echo "*** test service.d/ top level drop-in"
+    create_services test15-a test15-b
+    check_ko test15-a ExecCondition "/bin/echo a"
+    check_ko test15-b ExecCondition "/bin/echo b"
+    mkdir -p /run/systemd/system/service.d
+    cat >/run/systemd/system/service.d/override.conf <<EOF
+[Service]
+ExecCondition=/bin/echo %n
+EOF
+    systemctl daemon-reload
+    check_ok test15-a ExecCondition "/bin/echo test15-a"
+    check_ok test15-b ExecCondition "/bin/echo test15-b"
+    rm -rf /run/systemd/system/service.d
+
+    clear_units test15-{a,b,c,c1}.service
+}
+
+testcase_linked_units() {
+    echo "Testing linked units..."
+    echo "*** test linked unit (same basename)"
+
+    create_service test15-a
+    mv /etc/systemd/system/test15-a.service /
+    ln -s /test15-a.service /etc/systemd/system/
+    ln -s test15-a.service /etc/systemd/system/test15-b.service
+
+    check_ok test15-a Names test15-a.service
+    check_ok test15-a Names test15-b.service
+
+    echo "*** test linked unit (cross basename)"
+
+    mv /test15-a.service /test15-a@.scope
+    ln -fs /test15-a@.scope /etc/systemd/system/test15-a.service
+    systemctl daemon-reload
+
+    check_ok test15-a Names test15-a.service
+    check_ok test15-a Names test15-b.service
+    check_ko test15-a Names test15-a@     # test15-a@.scope is the symlink target.
+                                          # Make sure it is completely ignored.
+
+    rm /test15-a@.scope
+    clear_units test15-{a,b}.service
+}
+
+testcase_template_alias() {
+    echo "Testing instance alias..."
+    echo "*** forward"
+
+    create_service test15-a@
+    ln -s test15-a@inst.service /etc/systemd/system/test15-b@inst.service  # alias
+
+    check_ok test15-a@inst Names test15-a@inst.service
+    check_ok test15-a@inst Names test15-b@inst.service
+
+    check_ok test15-a@other Names test15-a@other.service
+    check_ko test15-a@other Names test15-b@other.service
+
+    echo "*** reverse"
+
+    systemctl daemon-reload
+
+    check_ok test15-b@inst Names test15-a@inst.service
+    check_ok test15-b@inst Names test15-b@inst.service
+
+    check_ko test15-b@other Names test15-a@other.service
+    check_ok test15-b@other Names test15-b@other.service
+
+    clear_units test15-{a,b}@.service
+}
+
+testcase_hierarchical_service_dropins() {
+    echo "Testing hierarchical service dropins..."
+    echo "*** test service.d/ top level drop-in"
+    create_services a-b-c
+    check_ko a-b-c ExecCondition "echo service.d"
+    check_ko a-b-c ExecCondition "echo a-.service.d"
+    check_ko a-b-c ExecCondition "echo a-b-.service.d"
+    check_ko a-b-c ExecCondition "echo a-b-c.service.d"
+
+    for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do
+        mkdir -p "/run/systemd/system/$dropin"
+        cat >"/run/systemd/system/$dropin/override.conf" <<EOF
+[Service]
+ExecCondition=echo $dropin
+EOF
+        systemctl daemon-reload
+        check_ok a-b-c ExecCondition "echo $dropin"
+
+        # Check that we can start a transient service in presence of the drop-ins
+        systemd-run -u a-b-c2.service -p Description='sleepy' sleep infinity
+
+        # The transient setting replaces the default
+        check_ok a-b-c2.service Description "sleepy"
+
+        # The override takes precedence for ExecCondition
+        # (except the last iteration when it only applies to the other service)
+        if [ "$dropin" != "a-b-c.service.d" ]; then
+            check_ok a-b-c2.service ExecCondition "echo $dropin"
+        fi
+
+        # Check that things are the same after a reload
+        systemctl daemon-reload
+        check_ok a-b-c2.service Description "sleepy"
+        if [ "$dropin" != "a-b-c.service.d" ]; then
+            check_ok a-b-c2.service ExecCondition "echo $dropin"
+        fi
+
+        systemctl stop a-b-c2.service
+    done
+    for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do
+        rm -rf "/run/systemd/system/$dropin"
+    done
+
+    clear_units a-b-c.service
+}
+
+testcase_hierarchical_slice_dropins() {
+    echo "Testing hierarchical slice dropins..."
+    echo "*** test slice.d/ top level drop-in"
+    # Slice units don't even need a fragment, so we test the defaults here
+    check_ok a-b-c.slice Description "Slice /a/b/c"
+    check_ok a-b-c.slice MemoryMax "infinity"
+
+    # Test drop-ins
+    for dropin in slice.d a-.slice.d a-b-.slice.d a-b-c.slice.d; do
+        mkdir -p "/run/systemd/system/$dropin"
+        cat >"/run/systemd/system/$dropin/override.conf" <<EOF
+[Slice]
+MemoryMax=1000000000
+EOF
+        systemctl daemon-reload
+        check_ok a-b-c.slice MemoryMax "1000000000"
+
+        busctl call \
+               org.freedesktop.systemd1 \
+               /org/freedesktop/systemd1 \
+               org.freedesktop.systemd1.Manager \
+               StartTransientUnit 'ssa(sv)a(sa(sv))' \
+               'a-b-c.slice' 'replace' \
+               2 \
+               'Description' s 'slice too' \
+               'MemoryMax' t 1000000002 \
+               0
+
+        # The override takes precedence for MemoryMax
+        check_ok a-b-c.slice MemoryMax "1000000000"
+        # The transient setting replaces the default
+        check_ok a-b-c.slice Description "slice too"
+
+        # Check that things are the same after a reload
+        systemctl daemon-reload
+        check_ok a-b-c.slice MemoryMax "1000000000"
+        check_ok a-b-c.slice Description "slice too"
+
+        busctl call \
+               org.freedesktop.systemd1 \
+               /org/freedesktop/systemd1 \
+               org.freedesktop.systemd1.Manager \
+               StopUnit 'ss' \
+               'a-b-c.slice' 'replace'
+
+        rm -f "/run/systemd/system/$dropin/override.conf"
+    done
+
+    # Test unit with a fragment
+    cat >/run/systemd/system/a-b-c.slice <<EOF
+[Slice]
+MemoryMax=1000000001
+EOF
+    systemctl daemon-reload
+    check_ok a-b-c.slice MemoryMax "1000000001"
+
+    clear_units a-b-c.slice
+}
+
+testcase_transient_service_dropins() {
+    echo "Testing dropins for a transient service..."
+    echo "*** test transient service drop-ins"
+
+    mkdir -p /etc/systemd/system/service.d
+    mkdir -p /etc/systemd/system/a-.service.d
+    mkdir -p /etc/systemd/system/a-b-.service.d
+    mkdir -p /etc/systemd/system/a-b-c.service.d
+
+    echo -e '[Service]\nStandardInputText=aaa' >/etc/systemd/system/service.d/drop1.conf
+    echo -e '[Service]\nStandardInputText=bbb' >/etc/systemd/system/a-.service.d/drop2.conf
+    echo -e '[Service]\nStandardInputText=ccc' >/etc/systemd/system/a-b-.service.d/drop3.conf
+    echo -e '[Service]\nStandardInputText=ddd' >/etc/systemd/system/a-b-c.service.d/drop4.conf
+
+    # There's no fragment yet, so this fails
+    systemctl cat a-b-c.service && exit 1
+
+    # xxx → eHh4Cg==
+    systemd-run -u a-b-c.service -p StandardInputData=eHh4Cg== sleep infinity
+
+    data=$(systemctl show -P StandardInputData a-b-c.service)
+    # xxx\naaa\n\bbb\nccc\nddd\n → eHh4…
+    test "$data" = "eHh4CmFhYQpiYmIKY2NjCmRkZAo="
+
+    # Do a reload and check again
+    systemctl daemon-reload
+    data=$(systemctl show -P StandardInputData a-b-c.service)
+    test "$data" = "eHh4CmFhYQpiYmIKY2NjCmRkZAo="
+
+    clear_units a-b-c.service
+    rm /etc/systemd/system/service.d/drop1.conf \
+       /etc/systemd/system/a-.service.d/drop2.conf \
+       /etc/systemd/system/a-b-.service.d/drop3.conf
+}
+
+testcase_transient_slice_dropins() {
+    echo "Testing dropins for a transient slice..."
+    echo "*** test transient slice drop-ins"
+
+    # FIXME: implement reloading of individual units.
+    #
+    # The settings here are loaded twice. For most settings it doesn't matter,
+    # but Documentation is not deduplicated, so we current get repeated entried
+    # which is a bug.
+
+    mkdir -p /etc/systemd/system/slice.d
+    mkdir -p /etc/systemd/system/a-.slice.d
+    mkdir -p /etc/systemd/system/a-b-.slice.d
+    mkdir -p /etc/systemd/system/a-b-c.slice.d
+
+    echo -e '[Unit]\nDocumentation=man:drop1' >/etc/systemd/system/slice.d/drop1.conf
+    echo -e '[Unit]\nDocumentation=man:drop2' >/etc/systemd/system/a-.slice.d/drop2.conf
+    echo -e '[Unit]\nDocumentation=man:drop3' >/etc/systemd/system/a-b-.slice.d/drop3.conf
+    echo -e '[Unit]\nDocumentation=man:drop4' >/etc/systemd/system/a-b-c.slice.d/drop4.conf
+
+    # Invoke daemon-reload to make sure that the call below doesn't fail
+    systemctl daemon-reload
+
+    # No fragment is required, so this works
+    systemctl cat a-b-c.slice
+
+    busctl call \
+           org.freedesktop.systemd1 \
+           /org/freedesktop/systemd1 \
+           org.freedesktop.systemd1.Manager \
+           StartTransientUnit 'ssa(sv)a(sa(sv))' \
+           'a-b-c.slice' 'replace' \
+           1 \
+           'Documentation' as 1 'man:drop5' \
+           0
+
+    data=$(systemctl show -P Documentation a-b-c.slice)
+    test "$data" = "man:drop1 man:drop2 man:drop3 man:drop4 man:drop5 man:drop1 man:drop2 man:drop3 man:drop4"
+
+    # Do a reload and check again
+    systemctl daemon-reload
+    data=$(systemctl show -P Documentation a-b-c.slice)
+    test "$data" = "man:drop5 man:drop1 man:drop2 man:drop3 man:drop4"
+
+    clear_units a-b-c.slice
+    rm /etc/systemd/system/slice.d/drop1.conf \
+       /etc/systemd/system/a-.slice.d/drop2.conf \
+       /etc/systemd/system/a-b-.slice.d/drop3.conf
+}
+
+testcase_template_dropins() {
+    echo "Testing template dropins..."
+
+    create_services foo bar@ yup@
+
+    # Declare some deps to check if the body was loaded
+    cat >>/etc/systemd/system/bar@.service <<EOF
+[Unit]
+After=bar-template-after.device
+EOF
+
+    cat >>/etc/systemd/system/yup@.service <<EOF
+[Unit]
+After=yup-template-after.device
+EOF
+
+    ln -s /etc/systemd/system/bar@.service /etc/systemd/system/foo.service.wants/bar@1.service
+    check_ok foo Wants bar@1.service
+
+    echo "*** test bar-alias@.service→bar@.service, but instance symlinks point to yup@.service ***"
+    ln -s bar@.service  /etc/systemd/system/bar-alias@.service
+    ln -s bar@1.service /etc/systemd/system/bar-alias@1.service
+    ln -s yup@.service  /etc/systemd/system/bar-alias@2.service
+    ln -s yup@3.service /etc/systemd/system/bar-alias@3.service
+
+    # create some dropin deps
+    mkdir -p /etc/systemd/system/bar@{,0,1,2,3}.service.requires/
+    mkdir -p /etc/systemd/system/yup@{,0,1,2,3}.service.requires/
+    mkdir -p /etc/systemd/system/bar-alias@{,0,1,2,3}.service.requires/
+
+    ln -s ../bar-template-requires.device /etc/systemd/system/bar@.service.requires/
+    ln -s ../bar-0-requires.device /etc/systemd/system/bar@0.service.requires/
+    ln -s ../bar-1-requires.device /etc/systemd/system/bar@1.service.requires/
+    ln -s ../bar-2-requires.device /etc/systemd/system/bar@2.service.requires/
+    ln -s ../bar-3-requires.device /etc/systemd/system/bar@3.service.requires/
+
+    ln -s ../yup-template-requires.device /etc/systemd/system/yup@.service.requires/
+    ln -s ../yup-0-requires.device /etc/systemd/system/yup@0.service.requires/
+    ln -s ../yup-1-requires.device /etc/systemd/system/yup@1.service.requires/
+    ln -s ../yup-2-requires.device /etc/systemd/system/yup@2.service.requires/
+    ln -s ../yup-3-requires.device /etc/systemd/system/yup@3.service.requires/
+
+    ln -s ../bar-alias-template-requires.device /etc/systemd/system/bar-alias@.service.requires/
+    ln -s ../bar-alias-0-requires.device /etc/systemd/system/bar-alias@0.service.requires/
+    ln -s ../bar-alias-1-requires.device /etc/systemd/system/bar-alias@1.service.requires/
+    ln -s ../bar-alias-2-requires.device /etc/systemd/system/bar-alias@2.service.requires/
+    ln -s ../bar-alias-3-requires.device /etc/systemd/system/bar-alias@3.service.requires/
+
+    systemctl daemon-reload
+
+    echo '*** bar@0 is aliased by bar-alias@0 ***'
+    systemctl show -p Names,Requires bar@0
+    systemctl show -p Names,Requires bar-alias@0
+    check_ok bar@0 Names bar@0
+    check_ok bar@0 Names bar-alias@0
+
+    check_ok bar@0 After bar-template-after.device
+
+    check_ok bar@0 Requires bar-0-requires.device
+    check_ok bar@0 Requires bar-alias-0-requires.device
+    check_ok bar@0 Requires bar-template-requires.device
+    check_ok bar@0 Requires bar-alias-template-requires.device
+    check_ko bar@0 Requires yup-template-requires.device
+
+    check_ok bar-alias@0 After bar-template-after.device
+
+    check_ok bar-alias@0 Requires bar-0-requires.device
+    check_ok bar-alias@0 Requires bar-alias-0-requires.device
+    check_ok bar-alias@0 Requires bar-template-requires.device
+    check_ok bar-alias@0 Requires bar-alias-template-requires.device
+    check_ko bar-alias@0 Requires yup-template-requires.device
+    check_ko bar-alias@0 Requires yup-0-requires.device
+
+    echo '*** bar@1 is aliased by bar-alias@1 ***'
+    systemctl show -p Names,Requires bar@1
+    systemctl show -p Names,Requires bar-alias@1
+    check_ok bar@1 Names bar@1
+    check_ok bar@1 Names bar-alias@1
+
+    check_ok bar@1 After bar-template-after.device
+
+    check_ok bar@1 Requires bar-1-requires.device
+    check_ok bar@1 Requires bar-alias-1-requires.device
+    check_ok bar@1 Requires bar-template-requires.device
+    # See https://github.com/systemd/systemd/pull/13119#discussion_r308145418
+    check_ok bar@1 Requires bar-alias-template-requires.device
+    check_ko bar@1 Requires yup-template-requires.device
+    check_ko bar@1 Requires yup-1-requires.device
+
+    check_ok bar-alias@1 After bar-template-after.device
+
+    check_ok bar-alias@1 Requires bar-1-requires.device
+    check_ok bar-alias@1 Requires bar-alias-1-requires.device
+    check_ok bar-alias@1 Requires bar-template-requires.device
+    check_ok bar-alias@1 Requires bar-alias-template-requires.device
+    check_ko bar-alias@1 Requires yup-template-requires.device
+    check_ko bar-alias@1 Requires yup-1-requires.device
+
+    echo '*** bar-alias@2 aliases yup@2, bar@2 is independent ***'
+    systemctl show -p Names,Requires bar@2
+    systemctl show -p Names,Requires bar-alias@2
+    check_ok bar@2 Names bar@2
+    check_ko bar@2 Names bar-alias@2
+
+    check_ok bar@2 After bar-template-after.device
+
+    check_ok bar@2 Requires bar-2-requires.device
+    check_ko bar@2 Requires bar-alias-2-requires.device
+    check_ok bar@2 Requires bar-template-requires.device
+    check_ko bar@2 Requires bar-alias-template-requires.device
+    check_ko bar@2 Requires yup-template-requires.device
+    check_ko bar@2 Requires yup-2-requires.device
+
+    check_ko bar-alias@2 After bar-template-after.device
+
+    check_ko bar-alias@2 Requires bar-2-requires.device
+    check_ok bar-alias@2 Requires bar-alias-2-requires.device
+    check_ko bar-alias@2 Requires bar-template-requires.device
+    check_ok bar-alias@2 Requires bar-alias-template-requires.device
+    check_ok bar-alias@2 Requires yup-template-requires.device
+    check_ok bar-alias@2 Requires yup-2-requires.device
+
+    echo '*** bar-alias@3 aliases yup@3, bar@3 is independent ***'
+    systemctl show -p Names,Requires bar@3
+    systemctl show -p Names,Requires bar-alias@3
+    check_ok bar@3 Names bar@3
+    check_ko bar@3 Names bar-alias@3
+
+    check_ok bar@3 After bar-template-after.device
+
+    check_ok bar@3 Requires bar-3-requires.device
+    check_ko bar@3 Requires bar-alias-3-requires.device
+    check_ok bar@3 Requires bar-template-requires.device
+    check_ko bar@3 Requires bar-alias-template-requires.device
+    check_ko bar@3 Requires yup-template-requires.device
+    check_ko bar@3 Requires yup-3-requires.device
+
+    check_ko bar-alias@3 After bar-template-after.device
+
+    check_ko bar-alias@3 Requires bar-3-requires.device
+    check_ok bar-alias@3 Requires bar-alias-3-requires.device
+    check_ko bar-alias@3 Requires bar-template-requires.device
+    check_ok bar-alias@3 Requires bar-alias-template-requires.device
+    check_ok bar-alias@3 Requires yup-template-requires.device
+    check_ok bar-alias@3 Requires yup-3-requires.device
+
+    clear_units foo.service {bar,yup,bar-alias}@{,1,2,3}.service
+}
+
+testcase_alias_dropins() {
+    echo "Testing alias dropins..."
+
+    echo "*** test a wants b1 alias of b"
+    create_services test15-a test15-b
+    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+    ln -sf ../test15-b1.service /etc/systemd/system/test15-a.service.wants/
+    check_ok test15-a Wants test15-b.service
+    systemctl start test15-a
+    systemctl --quiet is-active test15-b
+    systemctl stop test15-a test15-b
+    rm /etc/systemd/system/test15-b1.service
+    clear_units test15-{a,b}.service
+
+    # Check that dependencies don't vary.
+    echo "*** test 2"
+    create_services test15-a test15-x test15-y
+    mkdir -p /etc/systemd/system/test15-a1.service.wants/
+    ln -sf test15-a.service /etc/systemd/system/test15-a1.service
+    ln -sf ../test15-x.service /etc/systemd/system/test15-a.service.wants/
+    ln -sf ../test15-y.service /etc/systemd/system/test15-a1.service.wants/
+    check_ok test15-a1 Wants test15-x.service # see [1]
+    check_ok test15-a1 Wants test15-y.service
+    systemctl start test15-a
+    check_ok test15-a1 Wants test15-x.service # see [2]
+    check_ok test15-a1 Wants test15-y.service
+    systemctl stop test15-a test15-x test15-y
+    rm /etc/systemd/system/test15-a1.service
+
+    clear_units test15-{a,x,y}.service
+}
+
+testcase_masked_dropins() {
+    echo "Testing masked dropins..."
+
+    create_services test15-a test15-b
+
+    # 'b' is masked for both deps
+    echo "*** test a wants,requires b is masked"
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b.service
+    check_ko test15-a Wants test15-b.service
+    check_ko test15-a Requires test15-b.service
+
+    # 'a' wants 'b' and 'b' is masked at a lower level
+    echo "*** test a wants b, mask override"
+    ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.wants/test15-b.service
+    ln -sf /dev/null /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
+    check_ok test15-a Wants test15-b.service
+
+    # 'a' wants 'b' and 'b' is masked at a higher level
+    echo "*** test a wants b, mask"
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
+    ln -sf ../test15-b.service /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
+    check_ko test15-a Wants test15-b.service
+
+    # 'a' is masked but has an override config file
+    echo "*** test a is masked but has an override"
+    create_services test15-a test15-b
+    ln -sf /dev/null /etc/systemd/system/test15-a.service
+    cat >/usr/lib/systemd/system/test15-a.service.d/override.conf <<EOF
+[Unit]
+After=test15-b.service
+EOF
+    check_ok test15-a UnitFileState masked
+
+    # 'b1' is an alias for 'b': masking 'b' dep should not influence 'b1' dep
+    echo "*** test a wants b, b1, and one is masked"
+    create_services test15-a test15-b
+    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
+    ln -sf ../test15-b1.service /usr/lib/systemd/system/test15-a.service.wants/test15-b1.service
+    systemctl cat test15-a
+    systemctl show -p Wants,Requires test15-a
+    systemctl cat test15-b1
+    systemctl show -p Wants,Requires test15-b1
+    check_ok test15-a Wants test15-b.service
+    check_ko test15-a Wants test15-b1.service # the alias does not show up in the list of units
+    rm /etc/systemd/system/test15-b1.service
+
+    # 'b1' is an alias for 'b': masking 'b1' should not influence 'b' dep
+    echo "*** test a wants b, alias dep is masked"
+    create_services test15-a test15-b
+    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b1.service
+    ln -sf ../test15-b.service /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
+    check_ok test15-a Wants test15-b.service
+    check_ko test15-a Wants test15-b1.service # the alias does not show up in the list of units
+    rm /etc/systemd/system/test15-b1.service
+
+    # 'a' has Wants=b.service but also has a masking
+    # dropin 'b': 'b' should still be pulled in.
+    echo "*** test a wants b both ways"
+    create_services test15-a test15-b
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
+    cat >/usr/lib/systemd/system/test15-a.service.d/wants-b.conf <<EOF
+[Unit]
+Wants=test15-b.service
+EOF
+    check_ok test15-a Wants test15-b.service
+
+    # mask a dropin that points to an nonexistent unit.
+    echo "*** test a wants nonexistent is masked"
+    create_services test15-a
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/nonexistent.service
+    ln -sf ../nonexistent.service /usr/lib/systemd/system/test15-a.service.requires/
+    check_ko test15-a Requires nonexistent.service
+
+    # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is
+    # masked at a higher level.
+    echo "*** test a wants b is masked"
+    create_services test15-a test15-b test15-c
+    ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
+    ln -sf ../test15-b.service /run/systemd/system/test15-c.service.requires/
+    ln -sf /dev/null /etc/systemd/system/test15-c.service.requires/test15-b.service
+    systemctl start test15-a
+    check_ko test15-c Requires test15-b.service
+    systemctl stop test15-a test15-b
+
+    # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is
+    # masked at a lower level.
+    echo "*** test a requires b is masked"
+    create_services test15-a test15-b test15-c
+    ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
+    ln -sf ../test15-b.service /etc/systemd/system/test15-c.service.requires/
+    ln -sf /dev/null /run/systemd/system/test15-c.service.requires/test15-b.service
+    systemctl start test15-a
+    check_ok test15-c Requires test15-b.service
+    systemctl stop test15-a test15-b
+
+    # 'a' requires 2 aliases of 'b' and one of them is a mask.
+    echo "*** test a requires alias of b, other alias masked"
+    create_services test15-a test15-b
+    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+    ln -sf test15-b.service /etc/systemd/system/test15-b2.service
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b1.service
+    ln -sf ../test15-b1.service /run/systemd/system/test15-a.service.requires/
+    ln -sf ../test15-b2.service /usr/lib/systemd/system/test15-a.service.requires/
+    check_ok test15-a Requires test15-b
+
+    # Same as above but now 'b' is masked.
+    echo "*** test a requires alias of b, b dep masked"
+    create_services test15-a test15-b
+    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+    ln -sf test15-b.service /etc/systemd/system/test15-b2.service
+    ln -sf ../test15-b1.service /run/systemd/system/test15-a.service.requires/
+    ln -sf ../test15-b2.service /usr/lib/systemd/system/test15-a.service.requires/
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b.service
+    check_ok test15-a Requires test15-b
+
+    clear_units test15-{a,b}.service
+}
+
+testcase_invalid_dropins() {
+    echo "Testing invalid dropins..."
+    # Assertion failed on earlier versions, command exits unsuccessfully on later versions
+    systemctl cat nonexistent@.service || true
+    create_services a
+    systemctl daemon-reload
+    # Assertion failed on earlier versions, command exits unsuccessfully on later versions
+    systemctl cat a@.service || true
+    systemctl stop a
+    clear_units a.service
+    return 0
+}
+
+testcase_symlink_dropin_directory() {
+    # For issue #21920.
+    echo "Testing symlink drop-in directory..."
+    create_services test15-a
+    rmdir /{etc,run,usr/lib}/systemd/system/test15-a.service.d
+    mkdir -p /tmp/TEST-15-DROPIN-test15-a-dropin-directory
+    ln -s /tmp/TEST-15-DROPIN-test15-a-dropin-directory /etc/systemd/system/test15-a.service.d
+    cat >/tmp/TEST-15-DROPIN-test15-a-dropin-directory/override.conf <<EOF
+[Unit]
+Description=hogehoge
+EOF
+    ln -s /tmp/TEST-15-DROPIN-test15-a-dropin-directory-nonexistent /run/systemd/system/test15-a.service.d
+    touch /tmp/TEST-15-DROPIN-test15-a-dropin-directory-regular
+    ln -s /tmp/TEST-15-DROPIN-test15-a-dropin-directory-regular /usr/lib/systemd/system/test15-a.service.d
+    check_ok test15-a Description hogehoge
+
+    clear_units test15-a.service
+}
+
+run_testcases
+
+touch /testok
diff --git a/test/units/TEST-16-EXTEND-TIMEOUT.service b/test/units/TEST-16-EXTEND-TIMEOUT.service
new file mode 100644 (file)
index 0000000..d5494ae
--- /dev/null
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-16-EXTEND-TIMEOUT
+# Testsuite: Assess all other testsuite-*.services worked as expected
+
+Wants=success-all.service
+Wants=success-start.service
+Wants=success-runtime.service
+Wants=success-stop.service
+Wants=fail-start.service
+Wants=fail-stop.service
+Wants=fail-runtime.service
+StopWhenUnneeded=yes
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+Type=exec
+TimeoutStartSec=infinity
+ExecStartPre=/usr/lib/systemd/tests/testdata/units/%N.sh
+ExecStart=true
diff --git a/test/units/TEST-16-EXTEND-TIMEOUT.sh b/test/units/TEST-16-EXTEND-TIMEOUT.sh
new file mode 100755 (executable)
index 0000000..c60995a
--- /dev/null
@@ -0,0 +1,119 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+rm -f /test.log
+
+TESTLOG=/test.log.XXXXXXXX
+
+wait_for()
+{
+    local service="${1:-wait_for: missing service argument}"
+    local result="${2:-success}"
+    local time="${3:-45}"
+
+    while [[ ! -f /${service}.terminated && ! -f /${service}.success && $time -gt 0 ]]; do
+        sleep 1
+        time=$((time - 1))
+    done
+
+    if [[ ! -f /${service}.${result} ]]; then
+        journalctl -u "${service/_/-}.service" >>"$TESTLOG"
+    fi
+}
+
+wait_for_timeout()
+{
+    local unit="$1"
+    local time="$2"
+
+    while [[ $time -gt 0 ]]; do
+        if [[ "$(systemctl show --property=Result "$unit")" == "Result=timeout" ]]; then
+            return 0
+        fi
+
+        sleep 1
+        time=$((time - 1))
+    done
+
+    journalctl -u "$unit" >>"$TESTLOG"
+
+    return 1
+}
+
+# This checks all stages, start, runtime and stop, can be extended by
+# EXTEND_TIMEOUT_USEC
+
+wait_for success_all
+
+# These check that EXTEND_TIMEOUT_USEC that occurs at greater than the
+# extend timeout interval but less then the stage limit (TimeoutStartSec,
+# RuntimeMaxSec, TimeoutStopSec) still succeed.
+
+wait_for success_start
+wait_for success_runtime
+wait_for success_stop
+
+# These ensure that EXTEND_TIMEOUT_USEC will still timeout in the
+# appropriate stage, after the stage limit, when the EXTEND_TIMEOUT_USEC
+# message isn't sent within the extend timeout interval.
+
+wait_for fail_start startfail
+wait_for fail_stop stopfail
+wait_for fail_runtime runtimefail
+
+# These ensure that RuntimeMaxSec is honored for scope and service units
+# when they are created.
+runtime_max_sec=5
+
+systemd-run \
+    --property=RuntimeMaxSec=${runtime_max_sec}s \
+    -u runtime-max-sec-test-1.service \
+    /usr/bin/sh -c "while true; do sleep 1; done"
+wait_for_timeout runtime-max-sec-test-1.service $((runtime_max_sec + 2))
+
+systemd-run \
+    --property=RuntimeMaxSec=${runtime_max_sec}s \
+    --scope \
+    -u runtime-max-sec-test-2.scope \
+    /usr/bin/sh -c "while true; do sleep 1; done" &
+wait_for_timeout runtime-max-sec-test-2.scope $((runtime_max_sec + 2))
+
+# These ensure that RuntimeMaxSec is honored for scope and service
+# units if the value is changed and then the manager is reloaded.
+systemd-run \
+    -u runtime-max-sec-test-3.service \
+    /usr/bin/sh -c "while true; do sleep 1; done"
+mkdir -p /etc/systemd/system/runtime-max-sec-test-3.service.d/
+cat > /etc/systemd/system/runtime-max-sec-test-3.service.d/override.conf << EOF
+[Service]
+RuntimeMaxSec=${runtime_max_sec}s
+EOF
+systemctl daemon-reload
+wait_for_timeout runtime-max-sec-test-3.service $((runtime_max_sec + 2))
+
+systemd-run \
+    --scope \
+    -u runtime-max-sec-test-4.scope \
+    /usr/bin/sh -c "while true; do sleep 1; done" &
+
+# Wait until the unit is running to avoid race with creating the override.
+until systemctl is-active runtime-max-sec-test-4.scope; do
+    sleep 1
+done
+mkdir -p /etc/systemd/system/runtime-max-sec-test-4.scope.d/
+cat > /etc/systemd/system/runtime-max-sec-test-4.scope.d/override.conf << EOF
+[Scope]
+RuntimeMaxSec=${runtime_max_sec}s
+EOF
+systemctl daemon-reload
+wait_for_timeout runtime-max-sec-test-4.scope $((runtime_max_sec + 2))
+
+if [[ -f "$TESTLOG" ]]; then
+    # no mv
+    cp "$TESTLOG" /test.log
+    exit 1
+fi
+
+touch /testok
diff --git a/test/units/TEST-17-UDEV.00.sh b/test/units/TEST-17-UDEV.00.sh
new file mode 100755 (executable)
index 0000000..d2aec60
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Tests for issue #28588 and #28653.
+
+# On boot, services need to be started in the following order:
+# 1. systemd-tmpfiles-setup-dev-early.service
+# 2. systemd-sysusers.service
+# 3. systemd-tmpfiles-setup-dev.service
+# 4. systemd-udevd.service
+
+output="$(systemctl show --property After --value systemd-udevd.service)"
+assert_in "systemd-tmpfiles-setup-dev-early.service" "$output"
+assert_in "systemd-sysusers.service" "$output"
+assert_in "systemd-tmpfiles-setup-dev.service" "$output"
+
+output="$(systemctl show --property After --value systemd-tmpfiles-setup-dev.service)"
+assert_in "systemd-tmpfiles-setup-dev-early.service" "$output"
+assert_in "systemd-sysusers.service" "$output"
+
+output="$(systemctl show --property After --value systemd-sysusers.service)"
+assert_in "systemd-tmpfiles-setup-dev-early.service" "$output"
+
+check_owner_and_mode() {
+    local dev=${1?}
+    local user=${2?}
+    local group=${3?}
+    local mode=${4:-}
+
+    if [[ -e "$dev" ]]; then
+        assert_in "$user" "$(stat --format=%U "$dev")"
+        assert_in "$group" "$(stat --format=%G "$dev")"
+        if [[ -n "$mode" ]]; then
+            assert_in "$mode" "$(stat --format=%#0a "$dev")"
+        fi
+    fi
+
+    return 0
+}
+
+# Check owner and access mode specified in static-nodes-permissions.conf
+check_owner_and_mode /dev/snd/seq      root audio 0660
+check_owner_and_mode /dev/snd/timer    root audio 0660
+check_owner_and_mode /dev/loop-control root disk  0660
+check_owner_and_mode /dev/net/tun      root root  0666
+check_owner_and_mode /dev/fuse         root root  0666
+check_owner_and_mode /dev/vfio/vfio    root root  0666
+check_owner_and_mode /dev/kvm          root kvm
+check_owner_and_mode /dev/vhost-net    root kvm
+check_owner_and_mode /dev/vhost-vsock  root kvm
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.01.sh b/test/units/TEST-17-UDEV.01.sh
new file mode 100755 (executable)
index 0000000..44f36f5
--- /dev/null
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+mkdir -p /run/udev/rules.d/
+
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+udevadm trigger --settle /dev/sda
+
+while : ; do
+    (
+        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
+        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
+        systemctl show -p WantedBy foobar.service | grep -q -v sda
+        systemctl show -p WantedBy waldo.service | grep -q -v sda
+    ) && break
+
+    sleep .5
+done
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="block", KERNEL=="sda", OPTIONS="log_level=debug"
+ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="foobar.service"
+EOF
+udevadm control --reload
+udevadm trigger --settle /dev/sda
+
+while : ; do
+    (
+        udevadm info /dev/sda | grep -q SYSTEMD_WANTS=foobar.service
+        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
+        systemctl show -p WantedBy foobar.service | grep -q sda
+        systemctl show -p WantedBy waldo.service | grep -q -v sda
+    ) && break
+
+    sleep .5
+done
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="block", KERNEL=="sda", OPTIONS="log_level=debug"
+ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="waldo.service"
+EOF
+udevadm control --reload
+udevadm trigger --settle /dev/sda
+
+while : ; do
+    (
+        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
+        udevadm info /dev/sda | grep -q SYSTEMD_WANTS=waldo.service
+        systemctl show -p WantedBy foobar.service | grep -q -v sda
+        systemctl show -p WantedBy waldo.service | grep -q sda
+    ) && break
+
+    sleep .5
+done
+
+rm /run/udev/rules.d/50-testsuite.rules
+
+udevadm control --reload
+udevadm trigger --settle /dev/sda
+
+while : ; do
+    (
+        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
+        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
+        systemctl show -p WantedBy foobar.service | grep -q -v sda
+        systemctl show -p WantedBy waldo.service | grep -q -v sda
+    ) && break
+
+    sleep .5
+done
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.02.sh b/test/units/TEST-17-UDEV.02.sh
new file mode 100755 (executable)
index 0000000..b232fca
--- /dev/null
@@ -0,0 +1,182 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+# disable shellcheck warning about '"aaa"' type quotation
+# shellcheck disable=SC2016
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+mkdir -p /run/udev/rules.d/
+
+# test for ID_RENAMING= udev property and device unit state
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+ACTION=="remove", GOTO="hoge_end"
+SUBSYSTEM!="net", GOTO="hoge_end"
+KERNEL!="hoge", GOTO="hoge_end"
+
+OPTIONS="log_level=debug"
+
+# emulate renaming
+ACTION=="online", ENV{ID_RENAMING}="1"
+
+LABEL="hoge_end"
+EOF
+
+udevadm control --log-priority=debug --reload --timeout=30
+
+ip link add hoge type dummy
+udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
+assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
+
+udevadm trigger --action=online --settle /sys/devices/virtual/net/hoge
+assert_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "inactive" ]]; do sleep .5; done'
+
+udevadm trigger --action=move --settle /sys/devices/virtual/net/hoge
+assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
+
+# test for renaming interface with NAME= (issue #25106)
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+ACTION!="add", GOTO="hoge_end"
+SUBSYSTEM!="net", GOTO="hoge_end"
+
+OPTIONS="log_level=debug"
+
+KERNEL=="hoge",  NAME="foobar"
+KERNEL=="foobar", NAME="hoge"
+
+LABEL="hoge_end"
+EOF
+
+udevadm control --log-priority=debug --reload --timeout=30
+
+udevadm trigger --action=add --settle /sys/devices/virtual/net/hoge
+udevadm wait --timeout=30 --settle /sys/devices/virtual/net/foobar
+assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/foobar)"
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "active" ]]; do sleep .5; done'
+
+udevadm trigger --action=add --settle /sys/devices/virtual/net/foobar
+udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
+assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "inactive" ]]; do sleep .5; done'
+
+# cleanup
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload --timeout=30
+
+# test for renaming interface with an external tool (issue #16967)
+
+ip link set hoge name foobar
+udevadm wait --timeout=30 --settle /sys/devices/virtual/net/foobar
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "active" ]]; do sleep .5; done'
+
+ip link set foobar name hoge
+udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "inactive" ]]; do sleep .5; done'
+timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "inactive" ]]; do sleep .5; done'
+
+# cleanup
+ip link del hoge
+
+# shellcheck disable=SC2317
+teardown_netif_renaming_conflict() {
+    set +ex
+
+    if [[ -n "$KILL_PID" ]]; then
+        kill "$KILL_PID"
+    fi
+
+    rm -rf "$TMPDIR"
+
+    rm -f /run/udev/rules.d/50-testsuite.rules
+    udevadm control --reload --timeout=30
+
+    ip link del hoge
+    ip link del foobar
+}
+
+test_netif_renaming_conflict() {
+    local since found=
+
+    trap teardown_netif_renaming_conflict RETURN
+
+    cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+ACTION!="add", GOTO="hoge_end"
+SUBSYSTEM!="net", GOTO="hoge_end"
+
+OPTIONS="log_level=debug"
+
+KERNEL=="foobar", NAME="hoge"
+
+LABEL="hoge_end"
+EOF
+
+    udevadm control --log-priority=debug --reload --timeout=30
+
+    ip link add hoge type dummy
+    udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
+
+    TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
+    udevadm monitor --udev --property --subsystem-match=net >"$TMPDIR"/monitor.txt &
+    KILL_PID="$!"
+
+    # make sure that 'udevadm monitor' actually monitor uevents
+    sleep 1
+
+    since="$(date '+%H:%M:%S')"
+
+    # add another interface which will conflict with an existing interface
+    ip link add foobar type dummy
+
+    for _ in {1..40}; do
+        if (
+            grep -q 'ACTION=add' "$TMPDIR"/monitor.txt
+            grep -q 'DEVPATH=/devices/virtual/net/foobar' "$TMPDIR"/monitor.txt
+            grep -q 'SUBSYSTEM=net' "$TMPDIR"/monitor.txt
+            grep -q 'INTERFACE=foobar' "$TMPDIR"/monitor.txt
+            grep -q 'ID_NET_DRIVER=dummy' "$TMPDIR"/monitor.txt
+            grep -q 'ID_NET_NAME=foobar' "$TMPDIR"/monitor.txt
+            # Even when network interface renaming is failed, SYSTEMD_ALIAS with the conflicting name will be broadcast.
+            grep -q 'SYSTEMD_ALIAS=/sys/subsystem/net/devices/hoge' "$TMPDIR"/monitor.txt
+            grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt
+            grep -q 'UDEV_WORKER_ERRNO=17' "$TMPDIR"/monitor.txt
+            grep -q 'UDEV_WORKER_ERRNO_NAME=EEXIST' "$TMPDIR"/monitor.txt
+        ); then
+            cat "$TMPDIR"/monitor.txt
+            found=1
+            break
+        fi
+        sleep .5
+    done
+    test -n "$found"
+
+    timeout 30 bash -c "until journalctl _PID=1 _COMM=systemd --since $since | grep -q 'foobar: systemd-udevd failed to process the device, ignoring: File exists'; do sleep 1; done"
+    # check if the invalid SYSTEMD_ALIAS property for the interface foobar is ignored by PID1
+    assert_eq "$(systemctl show --property=SysFSPath --value /sys/subsystem/net/devices/hoge)" "/sys/devices/virtual/net/hoge"
+}
+
+test_netif_renaming_conflict
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.03.sh b/test/units/TEST-17-UDEV.03.sh
new file mode 100755 (executable)
index 0000000..d6b3162
--- /dev/null
@@ -0,0 +1,114 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+
+TMPDIR=
+TEST_RULE="/run/udev/rules.d/49-test.rules"
+TEST_CONF="/run/udev/udev.conf.d/test-17.conf"
+KILL_PID=
+
+setup() {
+    mkdir -p "${TEST_RULE%/*}"
+    mkdir -p /run/udev/udev.conf.d
+
+    cat >"${TEST_RULE}" <<EOF
+ACTION!="add", GOTO="test_end"
+SUBSYSTEM!="mem", GOTO="test_end"
+KERNEL!="null", GOTO="test_end"
+
+OPTIONS="log_level=debug"
+PROGRAM=="/bin/touch /tmp/test-udev-marker"
+PROGRAM!="/bin/sleep 60", ENV{PROGRAM_RESULT}="KILLED"
+
+LABEL="test_end"
+EOF
+    cat >"$TEST_CONF" <<EOF
+event_timeout=10
+timeout_signal=SIGABRT
+EOF
+
+    systemctl restart systemd-udevd.service
+}
+
+# shellcheck disable=SC2317
+teardown() {
+    set +e
+
+    if [[ -n "$KILL_PID" ]]; then
+        kill "$KILL_PID"
+    fi
+
+    rm -rf "$TMPDIR"
+    rm -f "$TEST_RULE" "$TEST_CONF"
+    systemctl restart systemd-udevd.service
+}
+
+run_test_timeout() {
+    TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
+    udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
+    KILL_PID="$!"
+
+    SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
+
+    for _ in {1..40}; do
+        if grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt; then
+            sleep .5
+            kill "$KILL_PID"
+            KILL_PID=
+
+            cat "$TMPDIR"/monitor.txt
+            (! grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt)
+            (! grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt)
+            (! grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt)
+            grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt
+            rm -rf "$TMPDIR"
+            return 0
+        fi
+        sleep .5
+    done
+
+    return 1
+}
+
+run_test_killed() {
+    local killed=
+
+    TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
+    udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
+    KILL_PID="$!"
+
+    rm -f /tmp/test-udev-marker
+    SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
+
+    for _ in {1..40}; do
+        if [[ -z "$killed" ]]; then
+            if [[ -e /tmp/test-udev-marker ]]; then
+                killall --signal ABRT --regexp udev-worker
+                killed=1
+            fi
+        elif grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt; then
+            sleep .5
+            kill "$KILL_PID"
+            KILL_PID=
+
+            cat "$TMPDIR"/monitor.txt
+            grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt
+            grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt
+            grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt
+            (! grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt)
+            rm -rf "$TMPDIR"
+            return 0
+        fi
+        sleep .5
+    done
+
+    return 1
+}
+
+trap teardown EXIT
+
+setup
+run_test_timeout
+run_test_killed
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.04.sh b/test/units/TEST-17-UDEV.04.sh
new file mode 100755 (executable)
index 0000000..d1c3c85
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+mkdir -p /run/udev/rules.d/
+
+test ! -f /run/udev/tags/added/c1:3
+test ! -f /run/udev/tags/changed/c1:3
+udevadm info /dev/null | grep -E 'E: (TAGS|CURRENT_TAGS)=.*:(added|changed):' && exit 1
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
+ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", TAG+="added"
+ACTION=="change", SUBSYSTEM=="mem", KERNEL=="null", TAG+="changed"
+EOF
+
+udevadm control --reload
+SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
+
+test -f /run/udev/tags/added/c1:3
+test ! -f /run/udev/tags/changed/c1:3
+udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*'
+udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*'
+udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' && { echo 'unexpected TAGS='; exit 1; }
+udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*' && { echo 'unexpected CURRENT_TAGS='; exit 1; }
+
+SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action change /dev/null
+
+test -f /run/udev/tags/added/c1:3
+test -f /run/udev/tags/changed/c1:3
+udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*'
+udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' && { echo 'unexpected CURRENT_TAGS='; exit 1; }
+udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*'
+udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*'
+
+SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
+
+test -f /run/udev/tags/added/c1:3
+test -f /run/udev/tags/changed/c1:3
+udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*'
+udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*'
+udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*'
+udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*' && { echo 'unexpected CURRENT_TAGS='; exit 1; }
+
+rm /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.05.sh b/test/units/TEST-17-UDEV.05.sh
new file mode 100755 (executable)
index 0000000..60be31a
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+mkdir -p /run/udev/rules.d/
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
+ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", IMPORT{program}="/bin/echo -e HOGE=aa\\\\x20\\\\x20\\\\x20bb\nFOO=\\\\x20aaa\\\\x20\n\n\n"
+EOF
+
+udevadm control --reload
+SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
+
+test -f /run/udev/data/c1:3
+udevadm info /dev/null | grep -q 'E: HOGE=aa\\x20\\x20\\x20bb'
+udevadm info /dev/null | grep -q 'E: FOO=\\x20aaa\\x20'
+
+rm /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.06.sh b/test/units/TEST-17-UDEV.06.sh
new file mode 100755 (executable)
index 0000000..6d83645
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# tests for udev watch
+
+function check_validity() {
+    local f ID_OR_HANDLE
+
+    for f in /run/udev/watch/*; do
+        ID_OR_HANDLE="$(readlink "$f")"
+        test -L "/run/udev/watch/${ID_OR_HANDLE}"
+        test "$(readlink "/run/udev/watch/${ID_OR_HANDLE}")" = "$(basename "$f")"
+    done
+}
+
+function check() {
+    for _ in {1..2}; do
+        systemctl restart systemd-udevd.service
+        udevadm control --ping
+        udevadm settle
+        check_validity
+
+        for _ in {1..2}; do
+            udevadm trigger -w --action add --subsystem-match=block
+            check_validity
+        done
+
+        for _ in {1..2}; do
+            udevadm trigger -w --action change --subsystem-match=block
+            check_validity
+        done
+    done
+}
+
+mkdir -p /run/udev/rules.d/
+
+cat >/run/udev/rules.d/00-debug.rules <<EOF
+SUBSYSTEM=="block", KERNEL=="sda*", OPTIONS="log_level=debug"
+EOF
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+ACTION=="add", SUBSYSTEM=="block", KERNEL=="sda", OPTIONS:="watch"
+EOF
+
+check
+
+MAJOR=$(udevadm info /dev/sda | grep -e '^E: MAJOR=' | sed -e 's/^E: MAJOR=//')
+MINOR=$(udevadm info /dev/sda | grep -e '^E: MINOR=' | sed -e 's/^E: MINOR=//')
+test -L "/run/udev/watch/b${MAJOR}:${MINOR}"
+
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+ACTION=="change", SUBSYSTEM=="block", KERNEL=="sda", OPTIONS:="nowatch"
+EOF
+
+check
+
+MAJOR=$(udevadm info /dev/sda | grep -e '^E: MAJOR=' | sed -e 's/^E: MAJOR=//')
+MINOR=$(udevadm info /dev/sda | grep -e '^E: MINOR=' | sed -e 's/^E: MINOR=//')
+test ! -e "/run/udev/watch/b${MAJOR}:${MINOR}"
+
+rm /run/udev/rules.d/00-debug.rules
+rm /run/udev/rules.d/50-testsuite.rules
+
+udevadm control --reload
+systemctl reset-failed systemd-udevd.service
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.07.sh b/test/units/TEST-17-UDEV.07.sh
new file mode 100755 (executable)
index 0000000..629393a
--- /dev/null
@@ -0,0 +1,205 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+wait_service_active() {(
+    set +ex
+    for i in {1..20}; do
+        (( i > 1 )) && sleep 0.5
+        if systemctl --quiet is-active "${1?}"; then
+            return 0
+        fi
+    done
+    return 1
+)}
+
+wait_service_inactive() {(
+    set +ex
+    for i in {1..20}; do
+        (( i > 1 )) && sleep 0.5
+        systemctl --quiet is-active "${1?}"
+        if [[ "$?" == "3" ]]; then
+            return 0
+        fi
+    done
+    return 1
+)}
+
+mkdir -p /run/systemd/system
+cat >/run/systemd/system/both.service <<EOF
+[Service]
+ExecStart=sleep 1000
+EOF
+
+cat >/run/systemd/system/on-add.service <<EOF
+[Service]
+ExecStart=sleep 1000
+EOF
+
+cat >/run/systemd/system/on-change.service <<EOF
+[Service]
+ExecStart=sleep 1000
+EOF
+
+systemctl daemon-reload
+
+mkdir -p /run/udev/rules.d/
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="net", KERNEL=="dummy9?", OPTIONS="log_level=debug"
+SUBSYSTEM=="net", KERNEL=="dummy9?", ACTION=="add",    TAG+="systemd", ENV{SYSTEMD_WANTS}+="both.service", ENV{SYSTEMD_WANTS}+="on-add.service"
+SUBSYSTEM=="net", KERNEL=="dummy9?", ACTION=="change", TAG+="systemd", ENV{SYSTEMD_WANTS}+="both.service", ENV{SYSTEMD_WANTS}+="on-change.service"
+EOF
+
+udevadm control --reload
+
+# StopWhenUnneeded=no
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+systemctl stop both.service on-add.service
+
+udevadm trigger --action=change --settle /sys/class/net/dummy99
+udevadm info /sys/class/net/dummy99
+wait_service_active both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+wait_service_active on-change.service
+systemctl stop both.service on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+assert_rc 3 systemctl --quiet is-active both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+# StopWhenUnneeded=yes
+cat >/run/systemd/system/both.service <<EOF
+[Unit]
+StopWhenUnneeded=yes
+
+[Service]
+ExecStart=sleep 1000
+Type=simple
+EOF
+
+cat >/run/systemd/system/on-add.service <<EOF
+[Unit]
+StopWhenUnneeded=yes
+
+[Service]
+ExecStart=sleep 1000
+Type=simple
+EOF
+
+cat >/run/systemd/system/on-change.service <<EOF
+[Unit]
+StopWhenUnneeded=yes
+
+[Service]
+ExecStart=echo changed
+RemainAfterExit=true
+Type=oneshot
+EOF
+
+systemctl daemon-reload
+
+# StopWhenUnneeded=yes (single device, only add event)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+wait_service_inactive both.service
+wait_service_inactive on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+# StopWhenUnneeded=yes (single device, add and change event)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+udevadm trigger --action=change --settle /sys/class/net/dummy99
+assert_rc 0 systemctl --quiet is-active both.service
+wait_service_inactive on-add.service
+wait_service_active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+wait_service_inactive both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+wait_service_inactive on-change.service
+
+# StopWhenUnneeded=yes (multiple devices, only add events)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link add dummy98 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy98
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link del dummy98
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy98
+wait_service_inactive both.service
+wait_service_inactive on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+# StopWhenUnneeded=yes (multiple devices, add and change events)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link add dummy98 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy98
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+udevadm trigger --action=change --settle /sys/class/net/dummy99
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+wait_service_active on-change.service
+
+ip link del dummy98
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy98
+assert_rc 0 systemctl --quiet is-active both.service
+wait_service_inactive on-add.service
+assert_rc 0 systemctl --quiet is-active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+wait_service_inactive both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+wait_service_inactive on-change.service
+
+# cleanup
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+
+rm -f /run/systemd/system/on-add.service
+rm -f /run/systemd/system/on-change.service
+systemctl daemon-reload
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.08.sh b/test/units/TEST-17-UDEV.08.sh
new file mode 100755 (executable)
index 0000000..e570c69
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# This is a test for issue #24518.
+
+mkdir -p /run/udev/rules.d/
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug", TAG+="systemd"
+SUBSYSTEM=="mem", KERNEL=="null", ACTION=="add",    SYMLINK+="test/symlink-to-null-on-add",    ENV{SYSTEMD_ALIAS}+="/sys/test/alias-to-null-on-add"
+SUBSYSTEM=="mem", KERNEL=="null", ACTION=="change", SYMLINK+="test/symlink-to-null-on-change", ENV{SYSTEMD_ALIAS}+="/sys/test/alias-to-null-on-change"
+EOF
+
+udevadm control --reload
+
+udevadm trigger --settle --action add /dev/null
+for i in {1..20}; do
+    ((i > 1)) && sleep .5
+
+    (
+        systemctl -q is-active /dev/test/symlink-to-null-on-add
+        ! systemctl -q is-active /dev/test/symlink-to-null-on-change
+        systemctl -q is-active /sys/test/alias-to-null-on-add
+        ! systemctl -q is-active /sys/test/alias-to-null-on-change
+    ) && break
+done
+assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-add
+assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-change
+assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-add
+assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-change
+
+udevadm trigger --settle --action change /dev/null
+for i in {1..20}; do
+    ((i > 1)) && sleep .5
+
+    (
+        ! systemctl -q is-active /dev/test/symlink-to-null-on-add
+        systemctl -q is-active /dev/test/symlink-to-null-on-change
+        ! systemctl -q is-active /sys/test/alias-to-null-on-add
+        systemctl -q is-active /sys/test/alias-to-null-on-change
+    ) && break
+done
+assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-add
+assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-change
+assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-add
+assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-change
+
+udevadm trigger --settle --action add /dev/null
+for i in {1..20}; do
+    ((i > 1)) && sleep .5
+
+    (
+        systemctl -q is-active /dev/test/symlink-to-null-on-add
+        ! systemctl -q is-active /dev/test/symlink-to-null-on-change
+        systemctl -q is-active /sys/test/alias-to-null-on-add
+        ! systemctl -q is-active /sys/test/alias-to-null-on-change
+    ) && break
+done
+assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-add
+assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-change
+assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-add
+assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-change
+
+# cleanup
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.09.sh b/test/units/TEST-17-UDEV.09.sh
new file mode 100755 (executable)
index 0000000..9993196
--- /dev/null
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# This is a test for issue #24987.
+
+mkdir -p /run/udev/rules.d/
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM!="mem", GOTO="test-end"
+KERNEL!="null", GOTO="test-end"
+ACTION=="remove", GOTO="test-end"
+
+# add 100 * 100byte of properties
+$(for i in {1..100}; do printf 'ENV{XXX%03i}="0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"\n' "$i"; done)
+
+LABEL="test-end"
+EOF
+
+udevadm control --reload
+
+TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
+SYSTEMD_LOG_LEVEL=debug udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt 2>&1 &
+KILL_PID="$!"
+
+FOUND=
+for _ in {1..40}; do
+    if grep -F 'UDEV - the event which udev sends out after rule processing' "$TMPDIR"/monitor.txt; then
+        FOUND=1
+        break
+    fi
+    sleep .5
+done
+[[ -n "$FOUND" ]]
+
+udevadm trigger --verbose --settle --action add /dev/null
+
+FOUND=
+for _ in {1..40}; do
+    if ! grep -e 'UDEV *\[[0-9.]*\] *add *\/devices\/virtual\/mem\/null (mem)' "$TMPDIR"/monitor.txt; then
+        sleep .5
+        continue
+    fi
+
+    FOUND=1
+    for i in {1..100}; do
+        if ! grep -F "$(printf 'XXX%03i=0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' "$i")" "$TMPDIR"/monitor.txt; then
+            FOUND=
+            break
+        fi
+    done
+    if [[ -n "$FOUND" ]]; then
+        break;
+    fi
+
+    sleep .5
+done
+[[ -n "$FOUND" ]]
+
+# cleanup
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+
+kill "$KILL_PID"
+rm -rf "$TMPDIR"
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.10.sh b/test/units/TEST-17-UDEV.10.sh
new file mode 100755 (executable)
index 0000000..a2b8b14
--- /dev/null
@@ -0,0 +1,254 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Coverage test for udevadm
+
+# shellcheck disable=SC2317
+cleanup_17_10() {
+    set +e
+
+    losetup -d "$loopdev"
+    rm -f "$blk"
+
+    ip link delete "$netdev"
+}
+
+# Set up some test devices
+trap cleanup_17_10 EXIT
+
+netdev=dummy17.10
+ip link add $netdev type dummy
+
+blk="$(mktemp)"
+dd if=/dev/zero of="$blk" bs=1M count=1
+loopdev="$(losetup --show -f "$blk")"
+
+udevadm -h
+
+udevadm control -e
+udevadm control -l emerg
+udevadm control -l alert
+udevadm control -l crit
+udevadm control -l err
+udevadm control -l warning
+udevadm control -l notice
+udevadm control --log-level info
+udevadm control --log-level debug
+(! udevadm control -l hello)
+udevadm control -s
+udevadm control -S
+udevadm control -R
+udevadm control -p HELLO=world
+udevadm control -m 42
+udevadm control --ping -t 5
+udevadm control --load-credentials
+udevadm control -h
+
+udevadm info /dev/null
+udevadm info /sys/class/net/$netdev
+udevadm info "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
+udevadm info --property DEVNAME /sys/class/net/$netdev
+udevadm info --property DEVNAME --value /sys/class/net/$netdev
+udevadm info --property HELLO /sys/class/net/$netdev
+udevadm info -p class/net/$netdev
+udevadm info -p /class/net/$netdev
+udevadm info --json=off -p class/net/$netdev
+udevadm info --json=pretty -p class/net/$netdev | jq .
+udevadm info --json=short -p class/net/$netdev | jq .
+udevadm info -n null
+udevadm info -q all /sys/class/net/$netdev
+udevadm info -q name /dev/null
+udevadm info -q path /sys/class/net/$netdev
+udevadm info -q property /sys/class/net/$netdev
+udevadm info -q symlink /sys/class/net/$netdev
+udevadm info -q name -r /dev/null
+udevadm info --query symlink --root /sys/class/net/$netdev
+(! udevadm info -q hello -r /sys/class/net/$netdev)
+udevadm info -a /sys/class/net/$netdev
+udevadm info -t >/dev/null
+udevadm info --tree /sys/class/net/$netdev
+udevadm info -x /sys/class/net/$netdev
+udevadm info -x -q path /sys/class/net/$netdev
+udevadm info -P TEST_ /sys/class/net/$netdev
+udevadm info -d /dev/null
+udevadm info -e >/dev/null
+udevadm info -e --json=off >/dev/null
+udevadm info -e --json=pretty | jq . >/dev/null
+udevadm info -e --json=short | jq . >/dev/null
+udevadm info -e --subsystem-match acpi >/dev/null
+udevadm info -e --subsystem-nomatch acpi >/dev/null
+udevadm info -e --attr-match ifindex=2 >/dev/null
+udevadm info -e --attr-nomatch ifindex=2 >/dev/null
+udevadm info -e --property-match SUBSYSTEM=acpi >/dev/null
+udevadm info -e --tag-match systemd >/dev/null
+udevadm info -e --sysname-match lo >/dev/null
+udevadm info -e --name-match /sys/class/net/$netdev >/dev/null
+udevadm info -e --parent-match /sys/class/net/$netdev >/dev/null
+udevadm info -e --initialized-match >/dev/null
+udevadm info -e --initialized-nomatch >/dev/null
+# udevadm info -c
+udevadm info -w /sys/class/net/$netdev
+udevadm info --wait-for-initialization=5 /sys/class/net/$netdev
+udevadm info -h
+
+assert_rc 124 timeout 1 udevadm monitor
+assert_rc 124 timeout 1 udevadm monitor -k
+assert_rc 124 timeout 1 udevadm monitor -u
+assert_rc 124 timeout 1 udevadm monitor -s net
+assert_rc 124 timeout 1 udevadm monitor --subsystem-match net/$netdev
+assert_rc 124 timeout 1 udevadm monitor -t systemd
+assert_rc 124 timeout 1 udevadm monitor --tag-match hello
+udevadm monitor -h
+
+udevadm settle
+udevadm settle -t 5
+udevadm settle -E /sys/class/net/$netdev
+udevadm settle -h
+
+udevadm test /dev/null
+udevadm info /sys/class/net/$netdev
+udevadm test "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
+udevadm test -a add /sys/class/net/$netdev
+udevadm test -a change /sys/class/net/$netdev
+udevadm test -a move /sys/class/net/$netdev
+udevadm test -a online /sys/class/net/$netdev
+udevadm test -a offline /sys/class/net/$netdev
+udevadm test -a bind /sys/class/net/$netdev
+udevadm test -a unbind /sys/class/net/$netdev
+udevadm test -a help /sys/class/net/$netdev
+udevadm test --action help
+(! udevadm test -a hello /sys/class/net/$netdev)
+udevadm test -N early /sys/class/net/$netdev
+udevadm test -N late /sys/class/net/$netdev
+udevadm test --resolve-names never /sys/class/net/$netdev
+(! udevadm test -N hello /sys/class/net/$netdev)
+udevadm test -h
+
+# udevadm test-builtin path_id "$loopdev"
+udevadm test-builtin net_id /sys/class/net/$netdev
+udevadm test-builtin net_id "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
+udevadm test-builtin -a add net_id /sys/class/net/$netdev
+udevadm test-builtin -a remove net_id /sys/class/net/$netdev
+udevadm test-builtin -a change net_id /sys/class/net/$netdev
+udevadm test-builtin -a move net_id /sys/class/net/$netdev
+udevadm test-builtin -a online net_id /sys/class/net/$netdev
+udevadm test-builtin -a offline net_id /sys/class/net/$netdev
+udevadm test-builtin -a bind net_id /sys/class/net/$netdev
+udevadm test-builtin -a unbind net_id /sys/class/net/$netdev
+udevadm test-builtin -a help net_id /sys/class/net/$netdev
+udevadm test-builtin net_setup_link /sys/class/net/$netdev
+udevadm test-builtin blkid "$loopdev"
+udevadm test-builtin input_id /sys/class/net/$netdev
+udevadm test-builtin keyboard /dev/null
+# udevadm test-builtin kmod /sys/class/net/$netdev
+udevadm test-builtin uaccess /dev/null
+# udevadm test-builtin usb_id dev/null
+(! udevadm test-builtin hello /sys/class/net/$netdev)
+# systemd-hwdb update is extremely slow when combined with sanitizers and run
+# in a VM without acceleration, so let's just skip the one particular test
+# if we detect this combination
+if ! [[ -v ASAN_OPTIONS && "$(systemd-detect-virt -v)" == "qemu" ]]; then
+    modprobe scsi_debug
+    scsidev=$(readlink -f /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/[0-9]*)
+    mkdir -p /etc/udev/hwdb.d
+    cat >/etc/udev/hwdb.d/99-test.hwdb <<EOF
+scsi:*
+  ID_TEST=test
+EOF
+    systemd-hwdb update
+
+    udevadm test-builtin hwdb "$scsidev"
+
+    rmmod scsi_debug || :
+    rm -fv /etc/udev/hwdb.d/99-test.hwdb
+    systemd-hwdb update
+fi
+
+
+udevadm trigger
+udevadm trigger /dev/null
+udevadm trigger /sys/class/net/$netdev
+udevadm trigger "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
+udevadm trigger -v /sys/class/net/$netdev
+udevadm trigger -n /sys/class/net/$netdev
+udevadm trigger -q /sys/class/net/$netdev
+udevadm trigger -t all /sys/class/net/$netdev
+udevadm trigger -t devices /sys/class/net/$netdev
+udevadm trigger --type subsystems /sys/class/net/$netdev
+(! udevadm trigger -t hello /sys/class/net/$netdev)
+udevadm trigger -c add /sys/class/net/$netdev
+udevadm trigger -c remove /sys/class/net/$netdev
+udevadm trigger -c change /sys/class/net/$netdev
+udevadm trigger -c move /sys/class/net/$netdev
+udevadm trigger -c online /sys/class/net/$netdev
+udevadm trigger -c offline /sys/class/net/$netdev
+udevadm trigger -c bind /sys/class/net/$netdev
+udevadm trigger -c unbind /sys/class/net/$netdev
+udevadm trigger -c help /sys/class/net/$netdev
+udevadm trigger --action help /sys/class/net/$netdev
+(! udevadm trigger -c hello /sys/class/net/$netdev)
+udevadm trigger --prioritized-subsystem block
+udevadm trigger --prioritized-subsystem block,net
+udevadm trigger --prioritized-subsystem hello
+udevadm trigger -s net
+udevadm trigger -S net
+udevadm trigger -a subsystem=net
+udevadm trigger --attr-match hello=world
+udevadm trigger -p DEVNAME=null
+udevadm trigger --property-match HELLO=world
+udevadm trigger -g systemd
+udevadm trigger --tag-match hello
+udevadm trigger -y net
+udevadm trigger --sysname-match hello
+udevadm trigger --name-match /sys/class/net/$netdev
+udevadm trigger --name-match /sys/class/net/$netdev --name-match /dev/null
+udevadm trigger -b /sys/class/net/$netdev
+udevadm trigger --parent-match /sys/class/net/$netdev --name-match /dev/null
+udevadm trigger --initialized-match
+udevadm trigger --initialized-nomatch
+udevadm trigger -w
+udevadm trigger --uuid /sys/class/net/$netdev
+udevadm settle -t 300
+udevadm trigger --wait-daemon
+udevadm settle -t 300
+udevadm trigger --wait-daemon=5
+udevadm trigger -h
+
+# https://github.com/systemd/systemd/issues/29863
+if [[ "$(systemd-detect-virt -v)" != "qemu" ]]; then
+    udevadm control --log-level=0
+    for _ in {0..9}; do
+        timeout 30 udevadm trigger --settle
+    done
+    udevadm control --log-level=debug
+fi
+
+udevadm wait /dev/null
+udevadm wait /sys/class/net/$netdev
+udevadm wait -t 5 /sys/class/net/$netdev
+udevadm wait --initialized true /sys/class/net/$netdev
+udevadm wait --initialized false /sys/class/net/$netdev
+(! udevadm wait --initialized hello /sys/class/net/$netdev)
+assert_rc 124 timeout 5 udevadm wait --removed /sys/class/net/$netdev
+udevadm wait --settle /sys/class/net/$netdev
+udevadm wait -h
+
+udevadm lock --help
+udevadm lock --version
+for i in /dev/block/*; do
+    udevadm lock --device "$i" --print
+    udevadm lock --device "$i" true
+    (! udevadm lock --device "$i" false)
+done
+for i in / /usr; do
+    udevadm lock --backing "$i" --print
+    udevadm lock --backing "$i" true
+    (! udevadm lock --backing "$i" false)
+done
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.11.sh b/test/units/TEST-17-UDEV.11.sh
new file mode 100755 (executable)
index 0000000..42b925f
--- /dev/null
@@ -0,0 +1,447 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# Test for udevadm verify.
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# shellcheck disable=SC2317
+cleanup() {
+    cd /
+    rm -rf "${workdir}"
+    workdir=
+}
+
+workdir="$(mktemp -d)"
+trap cleanup EXIT
+cd "${workdir}"
+
+cat >"${workdir}/default_output_1_success" <<EOF
+
+1 udev rules files have been checked.
+  Success: 1
+  Fail:    0
+EOF
+cat >"${workdir}/default_output_1_fail" <<EOF
+
+1 udev rules files have been checked.
+  Success: 0
+  Fail:    1
+EOF
+cat >"${workdir}/output_0_files" <<EOF
+
+0 udev rules files have been checked.
+  Success: 0
+  Fail:    0
+EOF
+
+test_number=0
+rules=
+exp=
+err=
+out=
+next_test_number() {
+    : $((++test_number))
+
+    local num_str
+    num_str=$(printf %05d "${test_number}")
+
+    rules="sample-${num_str}.rules"
+    exp="sample-${num_str}.exp"
+    err="sample-${num_str}.err"
+    exo="sample-${num_str}.exo"
+    out="sample-${num_str}.out"
+}
+
+assert_0_impl() {
+    udevadm verify "$@" >"${out}"
+    if [ -f "${exo}" ]; then
+        diff -u "${exo}" "${out}"
+    elif [ -f "${rules}" ]; then
+        diff -u "${workdir}/default_output_1_success" "${out}"
+    fi
+}
+
+assert_0() {
+    assert_0_impl "$@"
+    next_test_number
+}
+
+assert_1_impl() {
+    local rc
+    set +e
+    udevadm verify "$@" >"${out}" 2>"${err}"
+    rc=$?
+    set -e
+    assert_eq "$rc" 1
+
+    if [ -f "${exp}" ]; then
+        diff -u "${exp}" "${err}"
+    fi
+
+    if [ -f "${exo}" ]; then
+        diff -u "${exo}" "${out}"
+    elif [ -f "${rules}" ]; then
+        diff -u "${workdir}/default_output_1_fail" "${out}"
+    fi
+}
+
+assert_1() {
+    assert_1_impl "$@"
+    next_test_number
+}
+
+# initialize variables
+next_test_number
+
+assert_0 -h
+assert_0 --help
+assert_0 -V
+assert_0 --version
+assert_0 /dev/null
+
+# unrecognized option '--unknown'
+assert_1 --unknown
+# option requires an argument -- 'N'
+assert_1 -N
+# --resolve-names= takes "early" or "never"
+assert_1 -N now
+# option '--resolve-names' requires an argument
+assert_1 --resolve-names
+# --resolve-names= takes "early" or "never"
+assert_1 --resolve-names=now
+# Failed to parse rules file ./nosuchfile: No such file or directory
+assert_1 ./nosuchfile
+# Failed to parse rules file ./nosuchfile: No such file or directory
+cat >"${exo}" <<EOF
+
+3 udev rules files have been checked.
+  Success: 2
+  Fail:    1
+EOF
+assert_1 /dev/null ./nosuchfile /dev/null
+
+rules_dir='etc/udev/rules.d'
+mkdir -p "${rules_dir}"
+# No rules files found in $PWD
+assert_1 --root="${workdir}"
+
+# Directory without rules.
+cp "${workdir}/output_0_files" "${exo}"
+assert_0 "${rules_dir}"
+
+# Directory with a loop.
+ln -s . "${rules_dir}/loop.rules"
+assert_1 "${rules_dir}"
+rm "${rules_dir}/loop.rules"
+
+# Empty rules.
+touch "${rules_dir}/empty.rules"
+assert_0 --root="${workdir}"
+: >"${exo}"
+assert_0 --root="${workdir}" --no-summary
+
+# Directory with a single *.rules file.
+cp "${workdir}/default_output_1_success" "${exo}"
+assert_0 "${rules_dir}"
+
+# Combination of --root= and FILEs is not supported.
+assert_1 --root="${workdir}" /dev/null
+# No rules files found in nosuchdir
+assert_1 --root=nosuchdir
+
+cd "${rules_dir}"
+
+# UDEV_LINE_SIZE 16384
+printf '%16383s\n' ' ' >"${rules}"
+assert_0 "${rules}"
+
+# Failed to parse rules file ${rules}: No buffer space available
+printf '%16384s\n' ' ' >"${rules}"
+echo "Failed to parse rules file ${rules}: No buffer space available" >"${exp}"
+assert_1 "${rules}"
+
+{
+    printf 'RUN+="/bin/true",%8174s\\\n' ' '
+    printf 'RUN+="/bin/false"%8174s\\\n' ' '
+    echo
+} >"${rules}"
+assert_0 "${rules}"
+
+printf 'RUN+="/bin/true"%8176s\\\n #\n' ' ' ' ' >"${rules}"
+echo >>"${rules}"
+cat >"${exp}" <<EOF
+${rules}:5 Line is too long, ignored.
+${rules}: udev rules check failed.
+EOF
+assert_1 "${rules}"
+
+printf '\\\n' >"${rules}"
+cat >"${exp}" <<EOF
+${rules}:1 Unexpected EOF after line continuation, line ignored.
+${rules}: udev rules check failed.
+EOF
+assert_1 "${rules}"
+
+test_syntax_error() {
+    local rule msg
+
+    rule="$1"; shift
+    msg="$1"; shift
+
+    printf '%s\n' "${rule}" >"${rules}"
+    cat >"${exp}" <<EOF
+${rules}:1 ${msg}
+${rules}: udev rules check failed.
+EOF
+    assert_1 "${rules}"
+}
+
+test_style_error() {
+    local rule msg
+
+    rule="$1"; shift
+    msg="$1"; shift
+
+    printf '%s\n' "${rule}" >"${rules}"
+    cat >"${exp}" <<EOF
+${rules}:1 ${msg}
+${rules}: udev rules have style issues.
+EOF
+    assert_0_impl --no-style "${rules}"
+    assert_1_impl "${rules}"
+    next_test_number
+}
+
+test_syntax_error '=' 'Invalid key/value pair, ignoring.'
+test_syntax_error 'ACTION{a}=="b"' 'Invalid attribute for ACTION.'
+test_syntax_error 'ACTION:="b"' 'Invalid operator for ACTION.'
+test_syntax_error 'ACTION=="b"' 'The line has no effect, ignoring.'
+test_syntax_error 'DEVPATH{a}=="b"' 'Invalid attribute for DEVPATH.'
+test_syntax_error 'DEVPATH:="b"' 'Invalid operator for DEVPATH.'
+test_syntax_error 'KERNEL{a}=="b"' 'Invalid attribute for KERNEL.'
+test_syntax_error 'KERNEL:="b"' 'Invalid operator for KERNEL.'
+test_syntax_error 'KERNELS{a}=="b"' 'Invalid attribute for KERNELS.'
+test_syntax_error 'KERNELS:="b"' 'Invalid operator for KERNELS.'
+test_syntax_error 'SYMLINK{a}=="b"' 'Invalid attribute for SYMLINK.'
+test_syntax_error 'SYMLINK:="%?"' 'Invalid value "%?" for SYMLINK (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'NAME{a}=="b"' 'Invalid attribute for NAME.'
+test_syntax_error 'NAME-="b"' 'Invalid operator for NAME.'
+test_syntax_error 'NAME+="a"' "NAME key takes '==', '!=', '=', or ':=' operator, assuming '='."
+test_syntax_error 'NAME:=""' 'Ignoring NAME="", as udev will not delete any network interfaces.'
+test_syntax_error 'NAME="%k"' 'Ignoring NAME="%k", as it will take no effect.'
+test_syntax_error 'ENV=="b"' 'Invalid attribute for ENV.'
+test_syntax_error 'ENV{a}-="b"' 'Invalid operator for ENV.'
+test_syntax_error 'ENV{a}:="b"' "ENV key takes '==', '!=', '=', or '+=' operator, assuming '='."
+test_syntax_error 'ENV{ACTION}="b"' "Invalid ENV attribute. 'ACTION' cannot be set."
+test_syntax_error 'CONST=="b"' 'Invalid attribute for CONST.'
+test_syntax_error 'CONST{a}=="b"' 'Invalid attribute for CONST.'
+test_syntax_error 'CONST{arch}="b"' 'Invalid operator for CONST.'
+test_syntax_error 'TAG{a}=="b"' 'Invalid attribute for TAG.'
+test_syntax_error 'TAG:="a"' "TAG key takes '==', '!=', '=', or '+=' operator, assuming '='."
+test_syntax_error 'TAG="%?"' 'Invalid value "%?" for TAG (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'TAGS{a}=="b"' 'Invalid attribute for TAGS.'
+test_syntax_error 'TAGS:="a"' 'Invalid operator for TAGS.'
+test_syntax_error 'SUBSYSTEM{a}=="b"' 'Invalid attribute for SUBSYSTEM.'
+test_syntax_error 'SUBSYSTEM:="b"' 'Invalid operator for SUBSYSTEM.'
+test_syntax_error 'SUBSYSTEM=="bus", NAME="b"' '"bus" must be specified as "subsystem".'
+test_syntax_error 'SUBSYSTEMS{a}=="b"' 'Invalid attribute for SUBSYSTEMS.'
+test_syntax_error 'SUBSYSTEMS:="b"' 'Invalid operator for SUBSYSTEMS.'
+test_syntax_error 'DRIVER{a}=="b"' 'Invalid attribute for DRIVER.'
+test_syntax_error 'DRIVER:="b"' 'Invalid operator for DRIVER.'
+test_syntax_error 'DRIVERS{a}=="b"' 'Invalid attribute for DRIVERS.'
+test_syntax_error 'DRIVERS:="b"' 'Invalid operator for DRIVERS.'
+test_syntax_error 'ATTR="b"' 'Invalid attribute for ATTR.'
+test_syntax_error 'ATTR{%}="b"' 'Invalid attribute "%" for ATTR (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'ATTR{a}-="b"' 'Invalid operator for ATTR.'
+test_syntax_error 'ATTR{a}+="b"' "ATTR key takes '==', '!=', or '=' operator, assuming '='."
+test_syntax_error 'ATTR{a}="%?"' 'Invalid value "%?" for ATTR (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'SYSCTL=""' 'Invalid attribute for SYSCTL.'
+test_syntax_error 'SYSCTL{%}="b"' 'Invalid attribute "%" for SYSCTL (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'SYSCTL{a}-="b"' 'Invalid operator for SYSCTL.'
+test_syntax_error 'SYSCTL{a}+="b"' "SYSCTL key takes '==', '!=', or '=' operator, assuming '='."
+test_syntax_error 'SYSCTL{a}="%?"' 'Invalid value "%?" for SYSCTL (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'ATTRS=""' 'Invalid attribute for ATTRS.'
+test_syntax_error 'ATTRS{%}=="b", NAME="b"' 'Invalid attribute "%" for ATTRS (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'ATTRS{a}-="b"' 'Invalid operator for ATTRS.'
+test_syntax_error 'ATTRS{device/}!="a", NAME="b"' "'device' link may not be available in future kernels."
+test_syntax_error 'ATTRS{../}!="a", NAME="b"' 'Direct reference to parent sysfs directory, may break in future kernels.'
+test_syntax_error 'TEST{a}=="b"' "Failed to parse mode 'a': Invalid argument"
+test_syntax_error 'TEST{0}=="%", NAME="b"' 'Invalid value "%" for TEST (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'TEST{0644}="b"' 'Invalid operator for TEST.'
+test_syntax_error 'PROGRAM{a}=="b"' 'Invalid attribute for PROGRAM.'
+test_syntax_error 'PROGRAM-="b"' 'Invalid operator for PROGRAM.'
+test_syntax_error 'PROGRAM=="%", NAME="b"' 'Invalid value "%" for PROGRAM (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'IMPORT="b"' 'Invalid attribute for IMPORT.'
+test_syntax_error 'IMPORT{a}="b"' 'Invalid attribute for IMPORT.'
+test_syntax_error 'IMPORT{a}-="b"' 'Invalid operator for IMPORT.'
+test_syntax_error 'IMPORT{file}=="%", NAME="b"' 'Invalid value "%" for IMPORT (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'IMPORT{builtin}!="foo"' 'Unknown builtin command: foo'
+test_syntax_error 'RESULT{a}=="b"' 'Invalid attribute for RESULT.'
+test_syntax_error 'RESULT:="b"' 'Invalid operator for RESULT.'
+test_syntax_error 'OPTIONS{a}="b"' 'Invalid attribute for OPTIONS.'
+test_syntax_error 'OPTIONS-="b"' 'Invalid operator for OPTIONS.'
+test_syntax_error 'OPTIONS!="b"' 'Invalid operator for OPTIONS.'
+test_syntax_error 'OPTIONS+="link_priority=a"' "Failed to parse link priority 'a': Invalid argument"
+test_syntax_error 'OPTIONS:="log_level=a"' "Failed to parse log level 'a': Invalid argument"
+test_syntax_error 'OPTIONS="a", NAME="b"' "Invalid value for OPTIONS key, ignoring: 'a'"
+test_syntax_error 'OWNER{a}="b"' 'Invalid attribute for OWNER.'
+test_syntax_error 'OWNER-="b"' 'Invalid operator for OWNER.'
+test_syntax_error 'OWNER!="b"' 'Invalid operator for OWNER.'
+test_syntax_error 'OWNER+="0"' "OWNER key takes '=' or ':=' operator, assuming '='."
+test_syntax_error 'OWNER=":nosuchuser:"' "Unknown user ':nosuchuser:', ignoring."
+test_syntax_error 'GROUP{a}="b"' 'Invalid attribute for GROUP.'
+test_syntax_error 'GROUP-="b"' 'Invalid operator for GROUP.'
+test_syntax_error 'GROUP!="b"' 'Invalid operator for GROUP.'
+test_syntax_error 'GROUP+="0"' "GROUP key takes '=' or ':=' operator, assuming '='."
+test_syntax_error 'GROUP=":nosuchgroup:"' "Unknown group ':nosuchgroup:', ignoring."
+test_syntax_error 'MODE{a}="b"' 'Invalid attribute for MODE.'
+test_syntax_error 'MODE-="b"' 'Invalid operator for MODE.'
+test_syntax_error 'MODE!="b"' 'Invalid operator for MODE.'
+test_syntax_error 'MODE+="0"' "MODE key takes '=' or ':=' operator, assuming '='."
+test_syntax_error 'MODE="%"' 'Invalid value "%" for MODE (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'SECLABEL="b"' 'Invalid attribute for SECLABEL.'
+test_syntax_error 'SECLABEL{a}="%"' 'Invalid value "%" for SECLABEL (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'SECLABEL{a}!="b"' 'Invalid operator for SECLABEL.'
+test_syntax_error 'SECLABEL{a}-="b"' 'Invalid operator for SECLABEL.'
+test_syntax_error 'SECLABEL{a}:="b"' "SECLABEL key takes '=' or '+=' operator, assuming '='."
+test_syntax_error 'RUN=="b"' 'Invalid operator for RUN.'
+test_syntax_error 'RUN-="b"' 'Invalid operator for RUN.'
+test_syntax_error 'RUN="%"' 'Invalid value "%" for RUN (char 1: invalid substitution type), ignoring.'
+test_syntax_error 'RUN{builtin}+="foo"' "Unknown builtin command 'foo', ignoring."
+test_syntax_error 'GOTO{a}="b"' 'Invalid attribute for GOTO.'
+test_syntax_error 'GOTO=="b"' 'Invalid operator for GOTO.'
+test_syntax_error 'NAME="a", GOTO="b"' 'GOTO="b" has no matching label, ignoring.'
+test_syntax_error 'GOTO="a", GOTO="b"
+LABEL="a"' 'Contains multiple GOTO keys, ignoring GOTO="b".'
+test_syntax_error 'LABEL{a}="b"' 'Invalid attribute for LABEL.'
+test_syntax_error 'LABEL=="b"' 'Invalid operator for LABEL.'
+test_style_error 'LABEL="b"' 'style: LABEL="b" is unused.'
+test_syntax_error 'a="b"' "Invalid key 'a'."
+test_syntax_error 'KERNEL=="", KERNEL=="?*", NAME="a"' 'conflicting match expressions, the line has no effect.'
+test_syntax_error 'KERNEL=="abc", KERNEL!="abc", NAME="b"' 'conflicting match expressions, the line has no effect.'
+test_syntax_error 'KERNEL=="|a|b", KERNEL!="b|a|", NAME="c"' 'conflicting match expressions, the line has no effect.'
+test_syntax_error 'KERNEL=="a|b", KERNEL=="c|d|e", NAME="f"' 'conflicting match expressions, the line has no effect.'
+# shellcheck disable=SC2016
+test_syntax_error 'ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{DISKSEQ}!="?*", ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"' \
+                  'conflicting match expressions, the line has no effect.'
+test_syntax_error 'ACTION=="a*", ACTION=="bc*", NAME="d"' 'conflicting match expressions, the line has no effect.'
+test_syntax_error 'ACTION=="a*|bc*", ACTION=="d*|ef*", NAME="g"' 'conflicting match expressions, the line has no effect.'
+test_syntax_error 'KERNEL!="", KERNEL=="?*", NAME="a"' 'duplicate expressions.'
+test_syntax_error 'KERNEL=="|a|b", KERNEL=="b|a|", NAME="c"' 'duplicate expressions.'
+# shellcheck disable=SC2016
+test_syntax_error 'ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{DISKSEQ}=="?*", ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"' \
+                  'duplicate expressions.'
+test_style_error ',ACTION=="a", NAME="b"' 'style: stray leading comma.'
+test_style_error ' ,ACTION=="a", NAME="b"' 'style: stray leading comma.'
+test_style_error ', ACTION=="a", NAME="b"' 'style: stray leading comma.'
+test_style_error 'ACTION=="a", NAME="b",' 'style: stray trailing comma.'
+test_style_error 'ACTION=="a", NAME="b", ' 'style: stray trailing comma.'
+test_style_error 'ACTION=="a" NAME="b"' 'style: a comma between tokens is expected.'
+test_style_error 'ACTION=="a",, NAME="b"' 'style: more than one comma between tokens.'
+test_style_error 'ACTION=="a" , NAME="b"' 'style: stray whitespace before comma.'
+test_style_error 'ACTION=="a",NAME="b"' 'style: whitespace after comma is expected.'
+test_syntax_error 'RESULT=="a", PROGRAM="b"' 'Reordering RESULT check after PROGRAM assignment.'
+test_syntax_error 'RESULT=="a*", PROGRAM="b", RESULT=="*c", PROGRAM="d"' \
+                  'Reordering RESULT check after PROGRAM assignment.'
+
+cat >"${rules}" <<'EOF'
+KERNEL=="a|b", KERNEL=="a|c", NAME="d"
+KERNEL=="a|b", KERNEL!="a|c", NAME="d"
+KERNEL!="a", KERNEL!="b", NAME="c"
+KERNEL=="|a", KERNEL=="|b", NAME="c"
+KERNEL=="*", KERNEL=="a*", NAME="b"
+KERNEL=="a*", KERNEL=="c*|ab*", NAME="d"
+PROGRAM="a", RESULT=="b"
+EOF
+assert_0 "${rules}"
+
+echo 'GOTO="a"' >"${rules}"
+cat >"${exp}" <<EOF
+${rules}:1 GOTO="a" has no matching label, ignoring.
+${rules}:1 The line has no effect any more, dropping.
+${rules}: udev rules check failed.
+EOF
+assert_1 "${rules}"
+
+cat >"${rules}" <<'EOF'
+GOTO="a"
+LABEL="a"
+EOF
+assert_0 "${rules}"
+
+cat >"${rules}" <<'EOF'
+GOTO="b"
+LABEL="b"
+LABEL="b"
+EOF
+cat >"${exp}" <<EOF
+${rules}:3 style: LABEL="b" is unused.
+${rules}: udev rules have style issues.
+EOF
+assert_0_impl --no-style "${rules}"
+assert_1_impl "${rules}"
+
+cat >"${rules}" <<'EOF'
+GOTO="a"
+LABEL="a", LABEL="b"
+EOF
+cat >"${exp}" <<EOF
+${rules}:2 Contains multiple LABEL keys, ignoring LABEL="a".
+${rules}:1 GOTO="a" has no matching label, ignoring.
+${rules}:1 The line has no effect any more, dropping.
+${rules}:2 style: LABEL="b" is unused.
+${rules}: udev rules check failed.
+EOF
+assert_1 "${rules}"
+
+cat >"${rules}" <<'EOF'
+KERNEL!="", KERNEL=="?*", KERNEL=="", NAME="a"
+EOF
+cat >"${exp}" <<EOF
+${rules}:1 duplicate expressions.
+${rules}:1 conflicting match expressions, the line has no effect.
+${rules}: udev rules check failed.
+EOF
+assert_1 "${rules}"
+
+cat >"${rules}" <<'EOF'
+ACTION=="a"NAME="b"
+EOF
+cat >"${exp}" <<EOF
+${rules}:1 style: a comma between tokens is expected.
+${rules}:1 style: whitespace between tokens is expected.
+${rules}: udev rules have style issues.
+EOF
+assert_0_impl --no-style "${rules}"
+assert_1_impl "${rules}"
+next_test_number
+
+cat >"${rules}" <<'EOF'
+ACTION=="a" ,NAME="b"
+EOF
+cat >"${exp}" <<EOF
+${rules}:1 style: stray whitespace before comma.
+${rules}:1 style: whitespace after comma is expected.
+${rules}: udev rules have style issues.
+EOF
+assert_0_impl --no-style "${rules}"
+assert_1_impl "${rules}"
+next_test_number
+
+# udevadm verify --root
+sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"
+cd -
+assert_1 --root="${workdir}"
+cd -
+
+# udevadm verify path/
+sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"
+cd -
+assert_1 "${rules_dir}"
+cd -
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.12.sh b/test/units/TEST-17-UDEV.12.sh
new file mode 100755 (executable)
index 0000000..ccc91bf
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+create_link_file() {
+    name=${1?}
+
+    mkdir -p /run/systemd/network/
+    cat >/run/systemd/network/10-test.link <<EOF
+[Match]
+Kind=dummy
+MACAddress=00:50:56:c0:00:18
+
+[Link]
+Name=$name
+AlternativeName=test1 test2 test3 test4
+EOF
+    udevadm control --reload
+}
+
+udevadm control --log-level=debug
+
+create_link_file test1
+ip link add address 00:50:56:c0:00:18 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/test1
+output=$(ip link show dev test1)
+if ! [[ "$output" =~ altname ]]; then
+    echo "alternative name for network interface not supported, skipping test."
+    exit 0
+fi
+assert_not_in "altname test1" "$output"
+assert_in "altname test2" "$output"
+assert_in "altname test3" "$output"
+assert_in "altname test4" "$output"
+
+# By triggering add event, Name= and AlternativeNames= are re-applied
+create_link_file test2
+udevadm trigger --action add --settle /sys/class/net/test1
+udevadm wait --settle --timeout=30 /sys/class/net/test2
+output=$(ip link show dev test2)
+assert_in "altname test1" "$output"
+assert_not_in "altname test2" "$output"
+assert_in "altname test3" "$output"
+assert_in "altname test4" "$output"
+
+# Name= and AlternativeNames= are not applied on move event
+create_link_file test3
+udevadm trigger --action move --settle /sys/class/net/test2
+udevadm wait --settle --timeout=30 /sys/class/net/test2
+output=$(ip link show dev test2)
+assert_in "altname test1" "$output"
+assert_not_in "altname test2" "$output"
+assert_in "altname test3" "$output"
+assert_in "altname test4" "$output"
+
+# Test move event triggered by manual renaming
+ip link set dev test2 name hoge
+udevadm wait --settle --timeout=30 /sys/class/net/hoge
+output=$(ip link show dev hoge)
+assert_in "altname test1" "$output"
+assert_not_in "altname test2" "$output"
+assert_in "altname test3" "$output"
+assert_in "altname test4" "$output"
+assert_not_in "altname hoge" "$output"
+
+# Re-test add event
+udevadm trigger --action add --settle /sys/class/net/hoge
+udevadm wait --settle --timeout=30 /sys/class/net/test3
+output=$(ip link show dev test3)
+assert_in "altname test1" "$output"
+assert_in "altname test2" "$output"
+assert_not_in "altname test3" "$output"
+assert_in "altname test4" "$output"
+assert_not_in "altname hoge" "$output"
+
+# cleanup
+ip link del dev test3
+
+rm -f /run/systemd/network/10-test.link
+udevadm control --reload --log-level=info
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.13.sh b/test/units/TEST-17-UDEV.13.sh
new file mode 100755 (executable)
index 0000000..d9dfdd7
--- /dev/null
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Test for `udevadm control -p`
+
+test_not_property() {
+    assert_eq "$(udevadm info --query property --property "$2" --value "$1")" ""
+}
+
+test_property() {
+    assert_eq "$(udevadm info --query property --property "$2" --value "$1")" "$3"
+}
+
+# shellcheck disable=SC2317
+cleanup() {
+    set +e
+
+    udevadm control -p FOO= -p BAR=
+
+    rm -f "$rules"
+}
+
+# Set up a test device
+trap cleanup EXIT
+
+rules="/run/udev/rules.d/99-test-17.13.rules"
+
+mkdir -p "${rules%/*}"
+cat > "$rules" <<'EOF'
+ENV{FOO}=="?*", ENV{PROP_FOO}="$env{FOO}"
+ENV{BAR}=="?*", ENV{PROP_BAR}="$env{BAR}"
+EOF
+
+udevadm control --reload
+
+test_not_property /dev/null PROP_FOO
+test_not_property /dev/null PROP_BAR
+
+: Setting of a property works
+
+udevadm control --property FOO=foo
+udevadm trigger --action change --settle /dev/null
+test_property /dev/null PROP_FOO foo
+test_not_property /dev/null PROP_BAR
+
+: Change of a property works
+
+udevadm control --property FOO=goo
+udevadm trigger --action change --settle /dev/null
+test_property /dev/null PROP_FOO goo
+
+: Removal of a property works
+
+udevadm control --property FOO=
+udevadm trigger --action change --settle /dev/null
+test_not_property /dev/null PROP_FOO
+
+: Repeated removal of a property does nothing
+
+udevadm control --property FOO=
+udevadm trigger --action change --settle /dev/null
+test_not_property /dev/null PROP_FOO
+
+: Multiple properties can be set at once
+
+udevadm control --property FOO=foo --property BAR=bar
+udevadm trigger --action change --settle /dev/null
+test_property /dev/null PROP_FOO foo
+test_property /dev/null PROP_BAR bar
+
+: Multiple setting of the same property is handled correctly
+
+udevadm control --property FOO=foo --property FOO=42
+udevadm trigger --action change --settle /dev/null
+test_property /dev/null PROP_FOO 42
+
+: Mix of settings and removals of the same property is handled correctly
+
+udevadm control -p FOO= -p FOO=foo -p BAR=car -p BAR=
+udevadm trigger --action change --settle /dev/null
+test_property /dev/null PROP_FOO foo
+test_not_property /dev/null PROP_BAR
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.credentials.sh b/test/units/TEST-17-UDEV.credentials.sh
new file mode 100755 (executable)
index 0000000..42d3883
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+if [[ $(systemctl is-enabled systemd-udev-load-credentials.service) == not-found ]]; then
+    echo "Missing systemd-udev-load-credentials.service" >>/skipped
+    exit 0
+fi
+
+at_exit() {
+    rm -f /run/credstore/udev.*
+    rm -f /run/udev/udev.conf.d/*
+    rm -f /run/udev/rules.d/*
+    rm -rf /run/systemd/system/systemd-udev-load-credentials.service.d
+}
+
+trap at_exit EXIT
+
+mkdir -p /run/credstore
+cat > /run/credstore/udev.conf.50-testme <<EOF
+udev_log=debug
+EOF
+cat > /run/credstore/udev.rules.50-testme <<EOF
+SUBSYSTEM=="net", OPTIONS="log_level=debug"
+EOF
+
+systemctl edit systemd-udev-load-credentials.service --stdin --drop-in=50-testme.conf <<EOF
+[Service]
+LoadCredential=udev.conf.50-testme
+LoadCredential=udev.rules.50-testme
+EOF
+
+systemctl restart systemd-udev-load-credentials.service
+
+diff /run/credstore/udev.conf.50-testme /run/udev/udev.conf.d/50-testme.conf
+diff /run/credstore/udev.rules.50-testme /run/udev/rules.d/50-testme.rules
diff --git a/test/units/TEST-17-UDEV.link-property.sh b/test/units/TEST-17-UDEV.link-property.sh
new file mode 100755 (executable)
index 0000000..517cc3f
--- /dev/null
@@ -0,0 +1,203 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+udevadm control --log-level=debug
+
+mkdir -p /run/systemd/network/
+cat >/run/systemd/network/10-test.link <<EOF
+[Match]
+Kind=dummy
+MACAddress=00:50:56:c0:00:19
+
+[Link]
+Name=test1
+EOF
+
+mkdir /run/systemd/network/10-test.link.d
+cat >/run/systemd/network/10-test.link.d/10-override.conf <<EOF
+[Link]
+Property=HOGE=foo BAR=baz SHOULD_BE_UNSET=unset
+UnsetProperty=SHOULD_BE_UNSET
+EOF
+
+udevadm control --reload
+
+ip link add address 00:50:56:c0:00:19 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/test1
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_in "HOGE=foo" "$output"
+assert_in "BAR=baz" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+cat >/run/systemd/network/10-test.link.d/11-override.conf <<EOF
+[Link]
+Property=
+Property=HOGE2=foo2 BAR2=baz2 SHOULD_BE_UNSET=unset
+ImportProperty=HOGE
+EOF
+
+udevadm control --reload
+
+udevadm trigger --settle --action add /sys/class/net/test1
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_in "HOGE=foo" "$output"
+assert_in "HOGE2=foo2" "$output"
+assert_not_in "BAR=" "$output"
+assert_in "BAR2=baz2" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+# On change event, .link file will not be applied.
+udevadm trigger --settle --action change /sys/class/net/test1
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+### testing with udevadm test-builtin
+output=$(udevadm test-builtin --action add net_setup_link /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_in "HOGE2=foo2" "$output"
+assert_not_in "BAR=" "$output"
+assert_in "BAR2=baz2" "$output"
+assert_in "SHOULD_BE_UNSET=" "$output"  # this is expected, as an empty assignment is also logged.
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+# check that test-builtin command does not update udev database.
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+output=$(udevadm test-builtin --action change net_setup_link /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+### testing with udevadm test
+output=$(udevadm test --action add /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_in "HOGE2=foo2" "$output"
+assert_not_in "BAR=" "$output"
+assert_in "BAR2=baz2" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+# check that test command does not update udev database.
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+output=$(udevadm test --action change /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "HOGE=" "$output"
+assert_not_in "HOGE2=" "$output"
+assert_not_in "BAR=" "$output"
+assert_not_in "BAR2=" "$output"
+assert_not_in "SHOULD_BE_UNSET=" "$output"
+assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
+assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
+assert_in "ID_NET_NAME=test1" "$output"
+
+# test for specifiers
+cat >/run/systemd/network/10-test.link.d/12-override.conf <<EOF
+[Link]
+Property=
+Property=LINK_VERSION=%v
+EOF
+
+udevadm control --reload
+
+output=$(udevadm test --action add /sys/class/net/test1)
+assert_in "LINK_VERSION=$(uname -r)" "$output"
+
+udevadm trigger --settle --action add /sys/class/net/test1
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_in "LINK_VERSION=$(uname -r)" "$output"
+
+# test for constant properties
+cat >/run/systemd/network/10-test.link.d/13-override.conf <<EOF
+[Link]
+Property=
+Property=ACTION=foo IFINDEX=bar
+UnsetProperty=DEVPATH
+EOF
+
+udevadm control --reload
+
+output=$(udevadm test --action add /sys/class/net/test1)
+assert_in "ACTION=add" "$output"
+assert_not_in "ACTION=foo" "$output"
+assert_in "IFINDEX=" "$output"
+assert_not_in "IFINDEX=bar" "$output"
+assert_in "DEVPATH=" "$output"
+
+udevadm trigger --settle --action add /sys/class/net/test1
+output=$(udevadm info --query property /sys/class/net/test1)
+assert_not_in "ACTION=foo" "$output"
+assert_in "IFINDEX=" "$output"
+assert_not_in "IFINDEX=bar" "$output"
+assert_in "DEVPATH=" "$output"
+
+# cleanup
+ip link del dev test1
+
+rm -f /run/systemd/network/10-test.link
+rm -rf /run/systemd/network/10-test.link.d
+udevadm control --reload --log-level=info
+
+exit 0
diff --git a/test/units/TEST-17-UDEV.service b/test/units/TEST-17-UDEV.service
new file mode 100644 (file)
index 0000000..d218d72
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-17-UDEV
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-17-UDEV.sh b/test/units/TEST-17-UDEV.sh
new file mode 100755 (executable)
index 0000000..14ceeba
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+udevadm settle
+
+run_subtests
+
+touch /testok
diff --git a/test/units/TEST-18-FAILUREACTION.service b/test/units/TEST-18-FAILUREACTION.service
new file mode 100644 (file)
index 0000000..16d90a1
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-18-FAILUREACTION
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-18-FAILUREACTION.sh b/test/units/TEST-18-FAILUREACTION.sh
new file mode 100755 (executable)
index 0000000..364c20d
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemd-run --wait -p FailureAction=poweroff true
+(! systemd-run --wait -p SuccessAction=poweroff false)
+
+if ! test -f /firstphase ; then
+    echo OK >/firstphase
+    systemd-run --wait -p SuccessAction=reboot true
+else
+    echo OK >/testok
+    systemd-run --wait -p FailureAction=exit -p FailureActionExitStatus=123 false
+fi
+
+sleep infinity
diff --git a/test/units/TEST-19-CGROUP.ExitType-cgroup.sh b/test/units/TEST-19-CGROUP.ExitType-cgroup.sh
new file mode 100755 (executable)
index 0000000..cd221d7
--- /dev/null
@@ -0,0 +1,102 @@
+#!/usr/bin/env bash
+set -eux
+
+# Test ExitType=cgroup
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if [[ "$(get_cgroup_hierarchy)" != unified ]]; then
+    echo "Skipping $0 as we're not running with the unified cgroup hierarchy"
+    exit 0
+fi
+
+systemd-analyze log-level debug
+
+# Multiple level process tree, parent process stays up
+cat >/tmp/test19-exit-cgroup.sh <<EOF
+#!/usr/bin/env bash
+set -eux
+
+# process tree: systemd -> sleep
+sleep infinity &
+disown
+
+# process tree: systemd -> bash -> bash -> sleep
+((sleep infinity); true) &
+
+systemd-notify --ready
+
+# Run the stop/kill command
+\$1 &
+
+# process tree: systemd -> bash -> sleep
+sleep infinity
+EOF
+chmod +x /tmp/test19-exit-cgroup.sh
+
+# service should be stopped cleanly
+systemd-run --wait \
+           --unit=one \
+           --property="Type=notify" \
+           --property="ExitType=cgroup" \
+           /tmp/test19-exit-cgroup.sh 'systemctl stop one'
+
+# same thing with a truthy exec condition
+systemd-run --wait \
+            --unit=two \
+            --property="Type=notify" \
+            --property="ExitType=cgroup" \
+            --property="ExecCondition=true" \
+            /tmp/test19-exit-cgroup.sh 'systemctl stop two'
+
+# false exec condition: systemd-run should exit immediately with status code: 1
+(! systemd-run --wait \
+               --unit=three \
+               --property="Type=notify" \
+               --property="ExitType=cgroup" \
+               --property="ExecCondition=false" \
+               /tmp/test19-exit-cgroup.sh)
+
+# service should exit uncleanly (main process exits with SIGKILL)
+(! systemd-run --wait \
+               --unit=four \
+               --property="Type=notify" \
+               --property="ExitType=cgroup" \
+               /tmp/test19-exit-cgroup.sh 'systemctl kill --signal 9 four')
+
+
+# Multiple level process tree, parent process exits quickly
+cat >/tmp/test19-exit-cgroup-parentless.sh <<EOF
+#!/usr/bin/env bash
+set -eux
+
+# process tree: systemd -> sleep
+sleep infinity &
+
+# process tree: systemd -> bash -> sleep
+((sleep infinity); true) &
+
+systemd-notify --ready
+
+# Run the stop/kill command after this bash process exits
+(sleep 1; \$1) &
+EOF
+chmod +x /tmp/test19-exit-cgroup-parentless.sh
+
+# service should be stopped cleanly
+systemd-run --wait \
+            --unit=five \
+            --property="Type=notify" \
+            --property="ExitType=cgroup" \
+            /tmp/test19-exit-cgroup-parentless.sh 'systemctl stop five'
+
+# service should still exit cleanly despite SIGKILL (the main process already exited cleanly)
+systemd-run --wait \
+            --unit=six \
+            --property="Type=notify" \
+            --property="ExitType=cgroup" \
+            /tmp/test19-exit-cgroup-parentless.sh 'systemctl kill --signal 9 six'
+
+
+systemd-analyze log-level info
diff --git a/test/units/TEST-19-CGROUP.cleanup-slice.sh b/test/units/TEST-19-CGROUP.cleanup-slice.sh
new file mode 100755 (executable)
index 0000000..5d63160
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+
+# Create service with KillMode=none inside a slice
+cat <<EOF >/run/systemd/system/test19cleanup.service
+[Unit]
+Description=Test 19 cleanup Service
+[Service]
+Slice=test19cleanup.slice
+Type=exec
+ExecStart=sleep infinity
+KillMode=none
+EOF
+cat <<EOF >/run/systemd/system/test19cleanup.slice
+[Unit]
+Description=Test 19 cleanup Slice
+EOF
+
+# Start service
+systemctl start test19cleanup.service
+assert_rc 0 systemd-cgls /test19cleanup.slice
+
+pid=$(systemctl show --property MainPID --value test19cleanup)
+ps "$pid"
+
+# Stop slice
+# The sleep process will not be killed because of KillMode=none
+# Since there is still a process running under it, the /test19cleanup.slice cgroup won't be removed
+systemctl stop test19cleanup.slice
+
+ps "$pid"
+
+# Kill sleep process manually
+kill -s TERM "$pid"
+while kill -0 "$pid" 2>/dev/null; do sleep 0.1; done
+
+timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice/test19cleanup.service >& /dev/null; do sleep .5; done'
+assert_rc 1 systemd-cgls /test19cleanup.slice/test19cleanup.service
+
+# Check that empty cgroup /test19cleanup.slice has been removed
+timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice >& /dev/null; do sleep .5; done'
+assert_rc 1 systemd-cgls /test19cleanup.slice
diff --git a/test/units/TEST-19-CGROUP.delegate.sh b/test/units/TEST-19-CGROUP.delegate.sh
new file mode 100755 (executable)
index 0000000..74d36c4
--- /dev/null
@@ -0,0 +1,115 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test cgroup delegation in the unified hierarchy
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if [[ "$(get_cgroup_hierarchy)" != unified ]]; then
+    echo "Skipping $0 as we're not running with the unified cgroup hierarchy"
+    exit 0
+fi
+
+at_exit() {
+    set +e
+    userdel -r test
+}
+
+systemd-run --wait \
+            --unit=test-0.service \
+            --property="DynamicUser=1" \
+            --property="Delegate=" \
+            test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \
+                 -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.procs -a \
+                 -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.subtree_control
+
+# Test if this also works for some of the more recent attrs the kernel might or might not support
+for attr in cgroup.threads memory.oom.group memory.reclaim ; do
+
+    if grep -q "$attr" /sys/kernel/cgroup/delegate ; then
+        systemd-run --wait \
+                    --unit=test-0.service \
+                    --property="DynamicUser=1" \
+                    --property="Delegate=" \
+                    test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \
+                    -w /sys/fs/cgroup/system.slice/test-0.service/"$attr"
+    fi
+done
+
+systemd-run --wait \
+            --unit=test-1.service \
+            --property="DynamicUser=1" \
+            --property="Delegate=memory pids" \
+            grep -q memory /sys/fs/cgroup/system.slice/test-1.service/cgroup.controllers
+
+systemd-run --wait \
+            --unit=test-2.service \
+            --property="DynamicUser=1" \
+            --property="Delegate=memory pids" \
+            grep -q pids /sys/fs/cgroup/system.slice/test-2.service/cgroup.controllers
+
+# "io" is not among the controllers enabled by default for all units, verify that
+grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
+
+# Run a service with "io" enabled, and verify it works
+systemd-run --wait \
+            --unit=test-3.service \
+            --property="IOAccounting=yes" \
+            --property="Slice=system-foo-bar-baz.slice"  \
+            grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test-3.service/cgroup.controllers
+
+# We want to check if "io" is removed again from the controllers
+# list. However, PID 1 (rightfully) does this asynchronously. In order
+# to force synchronization on this, let's start a short-lived service
+# which requires PID 1 to refresh the cgroup tree, so that we can
+# verify that this all works.
+systemd-run --wait --unit=test-4.service true
+
+# And now check again, "io" should have vanished
+grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
+
+# Check that unprivileged delegation works for scopes
+useradd test ||:
+systemd-run --uid=test \
+            --property="User=test" \
+            --property="Delegate=yes" \
+            --slice workload.slice \
+            --unit test-workload0.scope\
+            --scope \
+            test -w /sys/fs/cgroup/workload.slice/test-workload0.scope -a \
+                 -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.procs -a \
+                 -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.subtree_control
+
+# Verify that DelegateSubgroup= affects ownership correctly
+unit="test-subgroup-$RANDOM.service"
+systemd-run --wait \
+            --unit="$unit" \
+            --property="DynamicUser=1" \
+            --property="Delegate=pids" \
+            --property="DelegateSubgroup=foo" \
+            test -w "/sys/fs/cgroup/system.slice/$unit" -a \
+                 -w "/sys/fs/cgroup/system.slice/$unit/foo"
+
+# Check that for the subgroup also attributes that aren't covered by
+# regular (i.e. main cgroup) delegation ownership rules are delegated properly
+if test -f /sys/fs/cgroup/cgroup.max.depth; then
+    unit="test-subgroup-$RANDOM.service"
+    systemd-run --wait \
+                --unit="$unit" \
+                --property="DynamicUser=1" \
+                --property="Delegate=pids" \
+                --property="DelegateSubgroup=zzz" \
+                test -w "/sys/fs/cgroup/system.slice/$unit/zzz/cgroup.max.depth"
+fi
+
+# Check that the invoked process itself is also in the subgroup
+unit="test-subgroup-$RANDOM.service"
+systemd-run --wait \
+            --unit="$unit" \
+            --property="DynamicUser=1" \
+            --property="Delegate=pids" \
+            --property="DelegateSubgroup=bar" \
+            grep -q -x -F "0::/system.slice/$unit/bar" /proc/self/cgroup
diff --git a/test/units/TEST-19-CGROUP.service b/test/units/TEST-19-CGROUP.service
new file mode 100644 (file)
index 0000000..2094a12
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-19-CGROUP
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-19-CGROUP.sh b/test/units/TEST-19-CGROUP.sh
new file mode 100755 (executable)
index 0000000..60c53cd
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+. /etc/os-release
+# FIXME: This test fails on opensuse with the following error and others:
+# Apr 25 10:24:04 H (cat)[910]: device-mapper: create ioctl on ... failed: Device or resource busy
+if [[ "$ID" =~ "opensuse" ]]; then
+    echo "Skipping due to known unexpected behaviour in OpenSUSE kernels" >>/skipped
+    exit 77
+fi
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
diff --git a/test/units/TEST-21-DFUZZERE.service b/test/units/TEST-21-DFUZZERE.service
new file mode 100644 (file)
index 0000000..a5f77d0
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Fuzz our D-Bus interfaces with dfuzzer
+After=dbus.service multi-user.target
+Wants=dbus.service multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /skipped /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-21-DFUZZERE.sh b/test/units/TEST-21-DFUZZERE.sh
new file mode 100755 (executable)
index 0000000..08ebfd9
--- /dev/null
@@ -0,0 +1,134 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# check dfuzzer is present before testing
+if ! command -v dfuzzer &>/dev/null; then
+    echo "dfuzzer is not installed, skipping" | tee --append /skipped
+    exit 77
+fi
+
+# Save the end.service state before we start fuzzing, as it might get changed
+# on the fly by one of the fuzzers
+systemctl list-jobs | grep -F 'end.service' && SHUTDOWN_AT_EXIT=1 || SHUTDOWN_AT_EXIT=0
+
+# shellcheck disable=SC2317
+at_exit() {
+    set +e
+    # We have to call the end.service/poweroff explicitly even if it's specified on
+    # the kernel cmdline via systemd.wants=end.service, since dfuzzer calls
+    # org.freedesktop.systemd1.Manager.ClearJobs() which drops the service
+    # from the queue
+    if [[ $SHUTDOWN_AT_EXIT -ne 0 ]] && ! systemctl poweroff; then
+        # PID1 is down let's try to save the journal
+        journalctl --sync      # journal can be down as well so let's ignore exit codes here
+        systemctl -ff poweroff # sync() and reboot(RB_POWER_OFF)
+    fi
+}
+
+add_suppression() {
+    local interface="${1:?}"
+    local suppression="${2:?}"
+
+    sed -i "\%\[$interface\]%a$suppression" /etc/dfuzzer.conf
+}
+
+trap at_exit EXIT
+
+systemctl log-level info
+
+# FIXME: systemd-run doesn't play well with daemon-reexec
+# See: https://github.com/systemd/systemd/issues/27204
+add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:Reexecute FIXME"
+
+add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:SoftReboot destructive"
+add_suppression "org.freedesktop.login1" "Sleep destructive"
+
+# Skip calling start and stop methods on unit objects, as doing that is not only time consuming, but it also
+# starts/stops units that interfere with the machine state. The actual code paths should be covered (to some
+# degree) by the respective method counterparts on the manager object.
+for method in Start Stop Restart ReloadOrRestart ReloadOrTryRestart Kill; do
+    add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Unit:$method"
+done
+
+cat /etc/dfuzzer.conf
+
+# TODO
+#   * check for possibly newly introduced buses?
+BUS_LIST=(
+    org.freedesktop.home1
+    org.freedesktop.hostname1
+    org.freedesktop.import1
+    org.freedesktop.locale1
+    org.freedesktop.login1
+    org.freedesktop.machine1
+    org.freedesktop.portable1
+    org.freedesktop.resolve1
+    org.freedesktop.systemd1
+    org.freedesktop.timedate1
+)
+
+# systemd-oomd requires PSI
+if tail -n +1 /proc/pressure/{cpu,io,memory}; then
+    BUS_LIST+=(
+        org.freedesktop.oom1
+    )
+fi
+
+# Some services require specific conditions:
+#   - systemd-timesyncd can't run in a container
+#   - systemd-networkd can run in a container if it has CAP_NET_ADMIN capability
+if ! systemd-detect-virt --container; then
+    BUS_LIST+=(
+        org.freedesktop.network1
+        org.freedesktop.timesync1
+    )
+elif busctl introspect org.freedesktop.network1 / &>/dev/null; then
+    BUS_LIST+=(
+        org.freedesktop.network1
+    )
+fi
+
+SESSION_BUS_LIST=(
+    org.freedesktop.systemd1
+)
+
+# Maximum payload size generated by dfuzzer (in bytes) - default: 50K
+PAYLOAD_MAX=50000
+# Tweak the maximum payload size if we're running under sanitizers, since
+# with larger payloads we start hitting reply timeouts
+if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
+    PAYLOAD_MAX=10000 # 10K
+fi
+
+# Overmount /var/lib/machines with a size-limited tmpfs, as fuzzing
+# the org.freedesktop.machine1 stuff makes quite a mess
+mount -t tmpfs -o size=50M tmpfs /var/lib/machines
+
+# Fuzz both the system and the session buses (where applicable)
+for bus in "${BUS_LIST[@]}"; do
+    echo "Bus: $bus (system)"
+    systemd-run --pipe --wait \
+                -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus"
+
+    # Let's reload the systemd daemon to test (de)serialization as well
+    systemctl daemon-reload
+    # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved
+    systemctl daemon-reexec
+done
+
+umount /var/lib/machines
+
+for bus in "${SESSION_BUS_LIST[@]}"; do
+    echo "Bus: $bus (session)"
+    systemd-run --machine 'testuser@.host' --user --pipe --wait \
+                -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus"
+
+    # Let's reload the systemd user daemon to test (de)serialization as well
+    systemctl --machine 'testuser@.host' --user daemon-reload
+    # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved
+    systemctl --machine 'testuser@.host' --user daemon-reexec
+done
+
+touch /testok
diff --git a/test/units/TEST-22-TMPFILES.01.sh b/test/units/TEST-22-TMPFILES.01.sh
new file mode 100755 (executable)
index 0000000..2276b75
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# With "e" don't attempt to set permissions when file doesn't exist, see
+# https://github.com/systemd/systemd/pull/6682.
+set -eux
+set -o pipefail
+
+rm -fr /tmp/test
+
+echo "e /tmp/test - root root 1d" | systemd-tmpfiles --create -
+
+test ! -e /tmp/test
diff --git a/test/units/TEST-22-TMPFILES.02.sh b/test/units/TEST-22-TMPFILES.02.sh
new file mode 100755 (executable)
index 0000000..f191ae3
--- /dev/null
@@ -0,0 +1,183 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Basic tests for types creating directories
+set -eux
+set -o pipefail
+
+rm -fr /tmp/{C,d,D,e}
+mkdir  /tmp/{C,d,D,e}
+
+#
+# 'd'
+#
+mkdir /tmp/d/2
+chmod 777 /tmp/d/2
+
+systemd-tmpfiles --dry-run --create - <<EOF
+d     /tmp/d/1    0755 daemon daemon - -
+d     /tmp/d/2    0755 daemon daemon - -
+EOF
+
+test ! -d /tmp/d/1
+test -d /tmp/d/2
+
+systemd-tmpfiles --create - <<EOF
+d     /tmp/d/1    0755 daemon daemon - -
+d     /tmp/d/2    0755 daemon daemon - -
+EOF
+
+test -d /tmp/d/1
+test "$(stat -c %U:%G:%a /tmp/d/1)" = "daemon:daemon:755"
+
+test -d /tmp/d/2
+test "$(stat -c %U:%G:%a /tmp/d/2)" = "daemon:daemon:755"
+
+#
+# 'D'
+#
+mkdir /tmp/D/2
+chmod 777 /tmp/D/2
+touch /tmp/D/2/foo
+
+systemd-tmpfiles --create - <<EOF
+D     /tmp/D/1    0755 daemon daemon - -
+D     /tmp/D/2    0755 daemon daemon - -
+EOF
+
+test -d /tmp/D/1
+test "$(stat -c %U:%G:%a /tmp/D/1)" = "daemon:daemon:755"
+
+test -d /tmp/D/2
+test "$(stat -c %U:%G:%a /tmp/D/2)" = "daemon:daemon:755"
+
+systemd-tmpfiles --remove - <<EOF
+D     /tmp/D/2    0755 daemon daemon - -
+EOF
+
+# the content of '2' should be removed
+test "$(echo /tmp/D/2/*)" = "/tmp/D/2/*"
+
+#
+# 'e'
+#
+mkdir -p /tmp/e/2/{d1,d2}
+chmod 777 /tmp/e/2
+chmod 777 /tmp/e/2/d*
+
+systemd-tmpfiles --create - <<EOF
+e     /tmp/e/1     0755 daemon daemon - -
+e     /tmp/e/2/*   0755 daemon daemon - -
+EOF
+
+test ! -d /tmp/e/1
+
+test -d /tmp/e/2
+test "$(stat -c %U:%G:%a /tmp/e/2)" = "root:root:777"
+
+test -d /tmp/e/2/d1
+test "$(stat -c %U:%G:%a /tmp/e/2/d1)" = "daemon:daemon:755"
+test -d /tmp/e/2/d2
+test "$(stat -c %U:%G:%a /tmp/e/2/d2)" = "daemon:daemon:755"
+
+# 'e' operates on directories only
+mkdir -p /tmp/e/3/{d1,d2}
+chmod 777 /tmp/e/3
+chmod 777 /tmp/e/3/d*
+touch /tmp/e/3/f1
+chmod 644 /tmp/e/3/f1
+
+systemd-tmpfiles --create - <<EOF
+e     /tmp/e/3/*   0755 daemon daemon - -
+EOF
+
+# the directories should have been processed although systemd-tmpfiles failed
+# previously due to the presence of a file.
+test -d /tmp/e/3/d1
+test "$(stat -c %U:%G:%a /tmp/e/3/d1)" = "daemon:daemon:755"
+test -d /tmp/e/3/d2
+test "$(stat -c %U:%G:%a /tmp/e/3/d2)" = "daemon:daemon:755"
+
+test -f /tmp/e/3/f1
+test "$(stat -c %U:%G:%a /tmp/e/3/f1)" = "root:root:644"
+
+#
+# 'C'
+#
+
+mkdir /tmp/C/{0,1,2,3}-origin
+touch /tmp/C/{1,2,3}-origin/f1
+chmod 755 /tmp/C/{1,2,3}-origin/f1
+
+mkdir /tmp/C/{2,3}
+touch /tmp/C/3/f1
+
+systemd-tmpfiles --dry-run --create - <<EOF
+C     /tmp/C/1    0755 daemon daemon - /tmp/C/1-origin
+C     /tmp/C/2    0755 daemon daemon - /tmp/C/2-origin
+EOF
+
+test ! -d /tmp/C/1
+test -d /tmp/C/2
+
+systemd-tmpfiles --create - <<EOF
+C     /tmp/C/1    0755 daemon daemon - /tmp/C/1-origin
+C     /tmp/C/2    0755 daemon daemon - /tmp/C/2-origin
+EOF
+
+test -d /tmp/C/1
+test "$(stat -c %U:%G:%a /tmp/C/1/f1)" = "daemon:daemon:755"
+test -d /tmp/C/2
+test "$(stat -c %U:%G:%a /tmp/C/2/f1)" = "daemon:daemon:755"
+
+systemd-tmpfiles --create - <<EOF
+C     /tmp/C/3    0755 daemon daemon - /tmp/C/3-origin
+C     /tmp/C/4    0755 daemon daemon - /tmp/C/definitely-missing
+EOF
+
+test "$(stat -c %U:%G:%a /tmp/C/3/f1)" = "root:root:644"
+test ! -e /tmp/C/4
+
+touch /tmp/C/3-origin/f{2,3,4}
+echo -n ABC > /tmp/C/3/f1
+
+systemd-tmpfiles --create - <<EOF
+C+     /tmp/C/3    0755 daemon daemon - /tmp/C/3-origin
+EOF
+
+# Test that the trees got merged, even though /tmp/C/3 already exists.
+test -e /tmp/C/3/f1
+test -e /tmp/C/3/f2
+test -e /tmp/C/3/f3
+test -e /tmp/C/3/f4
+
+# Test that /tmp/C/3/f1 did not get overwritten.
+test "$(cat /tmp/C/3/f1)" = "ABC"
+
+# Check that %U expands to 0, both in the path and in the argument.
+home='/tmp/C'
+systemd-tmpfiles --create - <<EOF
+C     $home/%U    - - - - $home/%U-origin
+EOF
+
+test -d "$home/0"
+
+# Check that %h expands to $home, both in the path and in the argument.
+HOME="$home" \
+systemd-tmpfiles --create - <<EOF
+C     %h/5    - - - - %h/3-origin
+EOF
+
+test -f "$home/5/f1"
+
+# Check that %h in the path is expanded, but
+# the result of this expansion is not expanded once again.
+root='/tmp/C/6'
+home='/%U'
+mkdir -p "$root/usr/share/factory$home"
+HOME="$home" \
+systemd-tmpfiles --create --root="$root" - <<EOF
+C     %h    - - - -
+EOF
+
+test -d "$root$home"
diff --git a/test/units/TEST-22-TMPFILES.03.sh b/test/units/TEST-22-TMPFILES.03.sh
new file mode 100755 (executable)
index 0000000..d158498
--- /dev/null
@@ -0,0 +1,275 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Basic tests for types creating/writing files
+set -eux
+set -o pipefail
+
+rm -fr /tmp/{f,F,w}
+mkdir  /tmp/{f,F,w}
+touch /tmp/file-owned-by-root
+
+#
+# 'f'
+#
+systemd-tmpfiles --dry-run --create - <<EOF
+f     /tmp/f/1    0644 - - - -
+f     /tmp/f/2    0644 - - - This string should be written
+EOF
+
+test ! -e /tmp/f/1
+test ! -e /tmp/f/2
+
+systemd-tmpfiles --create - <<EOF
+f     /tmp/f/1    0644 - - - -
+f     /tmp/f/2    0644 - - - This string should be written
+EOF
+
+### '1' should exist and be empty
+test -f /tmp/f/1; test ! -s /tmp/f/1
+test "$(stat -c %U:%G:%a /tmp/f/1)" = "root:root:644"
+
+test "$(stat -c %U:%G:%a /tmp/f/2)" = "root:root:644"
+test "$(< /tmp/f/2)" = "This string should be written"
+
+### The perms are supposed to be updated even if the file already exists.
+systemd-tmpfiles --create - <<EOF
+f     /tmp/f/1    0666 daemon daemon - This string should not be written
+EOF
+
+# file should be empty
+test ! -s /tmp/f/1
+test "$(stat -c %U:%G:%a /tmp/f/1)" = "daemon:daemon:666"
+
+### But we shouldn't try to set perms on an existing file which is not a
+### regular one.
+mkfifo /tmp/f/fifo
+chmod 644 /tmp/f/fifo
+
+(! systemd-tmpfiles --create -) <<EOF
+f     /tmp/f/fifo    0666 daemon daemon - This string should not be written
+EOF
+
+test -p /tmp/f/fifo
+test "$(stat -c %U:%G:%a /tmp/f/fifo)" = "root:root:644"
+
+### 'f' should not follow symlinks.
+ln -s missing /tmp/f/dangling
+ln -s /tmp/file-owned-by-root /tmp/f/symlink
+
+(! systemd-tmpfiles --create -) <<EOF
+f     /tmp/f/dangling    0644 daemon daemon - -
+f     /tmp/f/symlink     0644 daemon daemon - -
+EOF
+test ! -e /tmp/f/missing
+test "$(stat -c %U:%G:%a /tmp/file-owned-by-root)" = "root:root:644"
+
+### Handle read-only filesystem gracefully: we shouldn't fail if the target
+### already exists and have the correct perms.
+mkdir /tmp/f/rw-fs
+mkdir /tmp/f/ro-fs
+
+touch /tmp/f/rw-fs/foo
+chmod 644 /tmp/f/rw-fs/foo
+
+mount -o bind,ro /tmp/f/rw-fs /tmp/f/ro-fs
+
+systemd-tmpfiles --create - <<EOF
+f     /tmp/f/ro-fs/foo    0644 - - - - This string should not be written
+EOF
+test -f /tmp/f/ro-fs/foo; test ! -s /tmp/f/ro-fs/foo
+
+(! systemd-tmpfiles --create -) <<EOF
+f     /tmp/f/ro-fs/foo    0666 - - - -
+EOF
+test "$(stat -c %U:%G:%a /tmp/f/fifo)" = "root:root:644"
+
+(! systemd-tmpfiles --create -) <<EOF
+f     /tmp/f/ro-fs/bar    0644 - - - -
+EOF
+test ! -e /tmp/f/ro-fs/bar
+
+### 'f' shouldn't follow unsafe paths.
+mkdir /tmp/f/daemon
+ln -s /root /tmp/f/daemon/unsafe-symlink
+chown -R --no-dereference daemon:daemon /tmp/f/daemon
+
+(! systemd-tmpfiles --create -) <<EOF
+f     /tmp/f/daemon/unsafe-symlink/exploit    0644 daemon daemon - -
+EOF
+test ! -e /tmp/f/daemon/unsafe-symlink/exploit
+
+#
+# 'F'
+#
+echo "This should be truncated" >/tmp/F/truncated
+echo "This should be truncated" >/tmp/F/truncated-with-content
+
+systemd-tmpfiles --create - <<EOF
+F     /tmp/F/created                0644 - - - -
+F     /tmp/F/created-with-content   0644 - - - new content
+F     /tmp/F/truncated              0666 daemon daemon - -
+F     /tmp/F/truncated-with-content 0666 daemon daemon - new content
+EOF
+
+test -f /tmp/F/created; test ! -s /tmp/F/created
+test -f /tmp/F/created-with-content
+test "$(< /tmp/F/created-with-content)" = "new content"
+test -f /tmp/F/truncated; test ! -s /tmp/F/truncated
+test "$(stat -c %U:%G:%a /tmp/F/truncated)" = "daemon:daemon:666"
+test -s /tmp/F/truncated-with-content
+test "$(stat -c %U:%G:%a /tmp/F/truncated-with-content)" = "daemon:daemon:666"
+
+### We shouldn't try to truncate anything but regular files since the behavior is
+### unspecified in the other cases.
+mkfifo /tmp/F/fifo
+
+(! systemd-tmpfiles --create -) <<EOF
+F     /tmp/F/fifo                0644 - - - -
+EOF
+
+test -p /tmp/F/fifo
+
+### 'F' should not follow symlinks.
+ln -s missing /tmp/F/dangling
+ln -s /tmp/file-owned-by-root /tmp/F/symlink
+
+(! systemd-tmpfiles --create -) <<EOF
+f     /tmp/F/dangling    0644 daemon daemon - -
+f     /tmp/F/symlink     0644 daemon daemon - -
+EOF
+test ! -e /tmp/F/missing
+test "$(stat -c %U:%G:%a /tmp/file-owned-by-root)" = "root:root:644"
+
+### Handle read-only filesystem gracefully: we shouldn't fail if the target
+### already exists and is empty.
+mkdir /tmp/F/rw-fs
+mkdir /tmp/F/ro-fs
+
+touch /tmp/F/rw-fs/foo
+chmod 644 /tmp/F/rw-fs/foo
+
+mount -o bind,ro /tmp/F/rw-fs /tmp/F/ro-fs
+
+systemd-tmpfiles --create - <<EOF
+F     /tmp/F/ro-fs/foo    0644 - - - -
+EOF
+test -f /tmp/F/ro-fs/foo; test ! -s /tmp/F/ro-fs/foo
+
+echo "truncating is not allowed anymore" >/tmp/F/rw-fs/foo
+(! systemd-tmpfiles --create -) <<EOF
+F     /tmp/F/ro-fs/foo    0644 - - - -
+EOF
+
+(! systemd-tmpfiles --create -) <<EOF
+F     /tmp/F/ro-fs/foo    0644 - - - - This string should not be written
+EOF
+test -f /tmp/F/ro-fs/foo
+grep -q 'truncating is not allowed' /tmp/F/ro-fs/foo
+
+# Trying to change the perms should fail.
+: >/tmp/F/rw-fs/foo
+(! systemd-tmpfiles --create -) <<EOF
+F     /tmp/F/ro-fs/foo    0666 - - - -
+EOF
+test "$(stat -c %U:%G:%a /tmp/F/ro-fs/foo)" = "root:root:644"
+
+### Try to create a new file.
+(! systemd-tmpfiles --create -) <<EOF
+F     /tmp/F/ro-fs/bar    0644 - - - -
+EOF
+test ! -e /tmp/F/ro-fs/bar
+
+### 'F' shouldn't follow unsafe paths.
+mkdir /tmp/F/daemon
+ln -s /root /tmp/F/daemon/unsafe-symlink
+chown -R --no-dereference daemon:daemon /tmp/F/daemon
+
+(! systemd-tmpfiles --create -) <<EOF
+F     /tmp/F/daemon/unsafe-symlink/exploit    0644 daemon daemon - -
+EOF
+test ! -e /tmp/F/daemon/unsafe-symlink/exploit
+
+#
+# 'w'
+#
+touch /tmp/w/overwritten
+touch /tmp/w/appended
+
+### nop if the target does not exist.
+systemd-tmpfiles --dry-run --create - <<EOF
+w     /tmp/w/unexistent    0644 - - - new content
+EOF
+test ! -e /tmp/w/unexistent
+
+systemd-tmpfiles --create - <<EOF
+w     /tmp/w/unexistent    0644 - - - new content
+EOF
+test ! -e /tmp/w/unexistent
+
+### no argument given -> fails.
+(! systemd-tmpfiles --create -) <<EOF
+w     /tmp/w/unexistent    0644 - - - -
+EOF
+
+### write into an empty file.
+systemd-tmpfiles --dry-run --create - <<EOF
+w     /tmp/w/overwritten    0644 - - - old content
+EOF
+test -f /tmp/w/overwritten
+test -z "$(< /tmp/w/overwritten)"
+
+systemd-tmpfiles --create - <<EOF
+w     /tmp/w/overwritten    0644 - - - old content
+EOF
+test -f /tmp/w/overwritten
+test "$(< /tmp/w/overwritten)" = "old content"
+
+### old content is overwritten
+systemd-tmpfiles --dry-run --create - <<EOF
+w     /tmp/w/overwritten    0644 - - - new content
+EOF
+test -f /tmp/w/overwritten
+test "$(< /tmp/w/overwritten)" = "old content"
+
+systemd-tmpfiles --create - <<EOF
+w     /tmp/w/overwritten    0644 - - - new content
+EOF
+test -f /tmp/w/overwritten
+test "$(< /tmp/w/overwritten)" = "new content"
+
+### append lines
+systemd-tmpfiles --create - <<EOF
+w+    /tmp/w/appended    0644 - - - 1
+w+    /tmp/w/appended    0644 - - - 2\n
+w+    /tmp/w/appended    0644 - - - 3
+EOF
+test -f /tmp/w/appended
+test "$(< /tmp/w/appended)" = "$(echo -ne '12\n3')"
+
+### writing into an 'exotic' file should be allowed.
+systemd-tmpfiles --dry-run --create - <<EOF
+w     /dev/null    - - - - new content
+EOF
+
+systemd-tmpfiles --create - <<EOF
+w     /dev/null    - - - - new content
+EOF
+
+### 'w' follows symlinks
+ln -s ./overwritten /tmp/w/symlink
+systemd-tmpfiles --create - <<EOF
+w     /tmp/w/symlink    - - - - $(readlink -e /tmp/w/symlink)
+EOF
+readlink -e /tmp/w/symlink
+test "$(< /tmp/w/overwritten)" = "/tmp/w/overwritten"
+
+### 'w' shouldn't follow unsafe paths.
+mkdir /tmp/w/daemon
+ln -s /root /tmp/w/daemon/unsafe-symlink
+chown -R --no-dereference daemon:daemon /tmp/w/daemon
+
+(! systemd-tmpfiles --create -) <<EOF
+f     /tmp/w/daemon/unsafe-symlink/exploit    0644 daemon daemon - -
+EOF
+test ! -e /tmp/w/daemon/unsafe-symlink/exploit
diff --git a/test/units/TEST-22-TMPFILES.04.sh b/test/units/TEST-22-TMPFILES.04.sh
new file mode 100755 (executable)
index 0000000..2c0ffe3
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Basic tests for types creating fifos
+set -eux
+set -o pipefail
+
+rm -fr /tmp/p
+mkdir  /tmp/p
+touch  /tmp/p/f1
+
+systemd-tmpfiles --dry-run --create - <<EOF
+p     /tmp/p/fifo1    0666 - - - -
+EOF
+
+test ! -p /tmp/p/fifo1
+
+systemd-tmpfiles --create - <<EOF
+p     /tmp/p/fifo1    0666 - - - -
+EOF
+
+test -p /tmp/p/fifo1
+test "$(stat -c %U:%G:%a /tmp/p/fifo1)" = "root:root:666"
+
+# Refuse to overwrite an existing file. Error is not propagated.
+systemd-tmpfiles --create - <<EOF
+p     /tmp/p/f1    0666 - - - -
+EOF
+
+test -f /tmp/p/f1
+
+# unless '+' prefix is used
+systemd-tmpfiles --create - <<EOF
+p+     /tmp/p/f1    0666 - - - -
+EOF
+
+test -p /tmp/p/f1
+test "$(stat -c %U:%G:%a /tmp/p/f1)" = "root:root:666"
+
+#
+# Must be fixed
+#
+# mkdir /tmp/p/daemon
+# #ln -s /root /tmp/F/daemon/unsafe-symlink
+# chown -R --no-dereference daemon:daemon /tmp/p/daemon
+#
+# systemd-tmpfiles --create - <<EOF
+# p      /tmp/p/daemon/fifo2    0666 daemon daemon - -
+# EOF
diff --git a/test/units/TEST-22-TMPFILES.05.sh b/test/units/TEST-22-TMPFILES.05.sh
new file mode 100755 (executable)
index 0000000..d78e7ee
--- /dev/null
@@ -0,0 +1,54 @@
+#! /bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+rm -fr /tmp/{z,Z}
+mkdir  /tmp/{z,Z}
+
+#
+# 'z'
+#
+mkdir /tmp/z/d{1,2}
+touch /tmp/z/f1 /tmp/z/d1/f11 /tmp/z/d2/f21
+
+systemd-tmpfiles --dry-run --create - <<EOF
+z     /tmp/z/f1    0755 daemon daemon - -
+z     /tmp/z/d1    0755 daemon daemon - -
+EOF
+
+test "$(stat -c %U /tmp/z/f1)" = "$USER"
+test "$(stat -c %U /tmp/z/d1)" = "$USER"
+test "$(stat -c %U /tmp/z/d1/f11)" = "$USER"
+
+systemd-tmpfiles --create - <<EOF
+z     /tmp/z/f1    0755 daemon daemon - -
+z     /tmp/z/d1    0755 daemon daemon - -
+EOF
+
+test "$(stat -c %U:%G /tmp/z/f1)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/z/d1)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/z/d1/f11)" = "root:root"
+
+systemd-tmpfiles --create - <<EOF
+z     /tmp/z/d2/*    0755 daemon daemon - -
+EOF
+
+test "$(stat -c %U:%G /tmp/z/d2/f21)" = "daemon:daemon"
+
+#
+# 'Z'
+#
+mkdir /tmp/Z/d1 /tmp/Z/d1/d11
+touch /tmp/Z/f1 /tmp/Z/d1/f11 /tmp/Z/d1/d11/f111
+
+systemd-tmpfiles --create - <<EOF
+Z     /tmp/Z/f1    0755 daemon daemon - -
+Z     /tmp/Z/d1    0755 daemon daemon - -
+EOF
+
+test "$(stat -c %U:%G /tmp/Z/f1)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/Z/d1)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/Z/d1/d11)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/Z/d1/f11)" = "daemon:daemon"
+test "$(stat -c %U:%G /tmp/Z/d1/d11/f111)" = "daemon:daemon"
diff --git a/test/units/TEST-22-TMPFILES.06.sh b/test/units/TEST-22-TMPFILES.06.sh
new file mode 100755 (executable)
index 0000000..45aea9c
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Inspired by https://github.com/systemd/systemd/issues/9508
+set -eux
+set -o pipefail
+
+test_snippet() {
+    # First call with --dry-run to test the code paths
+    systemd-tmpfiles --dry-run "$@" - <<EOF
+d /var/tmp/foobar-test-06
+d /var/tmp/foobar-test-06/important
+R /var/tmp/foobar-test-06
+EOF
+
+    systemd-tmpfiles "$@" - <<EOF
+d /var/tmp/foobar-test-06
+d /var/tmp/foobar-test-06/important
+R /var/tmp/foobar-test-06
+EOF
+}
+
+test_snippet --create --remove
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+
+test_snippet --remove
+test ! -f /var/tmp/foobar-test-06
+test ! -f /var/tmp/foobar-test-06/important
+
+test_snippet --create
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+
+touch /var/tmp/foobar-test-06/something-else
+
+test_snippet --create
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+test -f /var/tmp/foobar-test-06/something-else
+
+test_snippet --create --remove
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+test ! -f /var/tmp/foobar-test-06/something-else
diff --git a/test/units/TEST-22-TMPFILES.07.sh b/test/units/TEST-22-TMPFILES.07.sh
new file mode 100755 (executable)
index 0000000..de20d5e
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Verifies the issues described by https://github.com/systemd/systemd/issues/10191
+set -eux
+set -o pipefail
+
+rm -rf /tmp/test-prefix
+
+mkdir /tmp/test-prefix
+touch /tmp/test-prefix/file
+
+systemd-tmpfiles --remove - <<EOF
+r /tmp/test-prefix
+r /tmp/test-prefix/file
+EOF
+
+test ! -f /tmp/test-prefix/file
+test ! -f /tmp/test-prefix
+
+mkdir /tmp/test-prefix
+touch /tmp/test-prefix/file
+
+systemd-tmpfiles --remove - <<EOF
+r /tmp/test-prefix/file
+r /tmp/test-prefix
+EOF
+
+test ! -f /tmp/test-prefix/file
+test ! -f /tmp/test-prefix
diff --git a/test/units/TEST-22-TMPFILES.08.sh b/test/units/TEST-22-TMPFILES.08.sh
new file mode 100755 (executable)
index 0000000..40fafd3
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Verify tmpfiles can run in a root directory under a path prefix that contains
+# directories owned by unprivileged users, for example when a root file system
+# is mounted in a regular user's home directory.
+#
+# https://github.com/systemd/systemd/pull/11820
+set -eux
+set -o pipefail
+
+rm -fr /tmp/root /tmp/user
+mkdir -p /tmp/root /tmp/user/root
+chown daemon:daemon /tmp/user
+
+# Verify the command works as expected with no prefix or a root-owned prefix.
+echo 'd /tmp/root/test1' | systemd-tmpfiles --create -
+test -d /tmp/root/test1
+echo 'd /test2' | systemd-tmpfiles --root=/tmp/root --create -
+test -d /tmp/root/test2
+
+# Verify the command fails to write to a root-owned subdirectory under an
+# unprivileged user's directory when it's not part of the prefix, as expected
+# by the unsafe_transition function.
+echo 'd /tmp/user/root/test' | (! systemd-tmpfiles --create -)
+test ! -e /tmp/user/root/test
+echo 'd /user/root/test' | (! systemd-tmpfiles --root=/tmp --create -)
+test ! -e /tmp/user/root/test
+
+# Verify the above works when all user-owned directories are in the prefix.
+echo 'd /test' | systemd-tmpfiles --root=/tmp/user/root --create -
+test -d /tmp/user/root/test
diff --git a/test/units/TEST-22-TMPFILES.09.sh b/test/units/TEST-22-TMPFILES.09.sh
new file mode 100755 (executable)
index 0000000..0857773
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Make sure that the "stat" output is not locale dependent.
+export LANG=C LC_ALL=C
+
+# first, create file without suid/sgid
+systemd-tmpfiles --create - <<EOF
+f     /tmp/xxx    0755 1 1 - -
+f     /tmp/yyy    0755 1 1 - -
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
+
+# then, add suid/sgid
+systemd-tmpfiles --create - <<EOF
+f     /tmp/xxx    04755
+f     /tmp/yyy    02755
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:4755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:2755"
+
+# then, chown the files to somebody else
+systemd-tmpfiles --create - <<EOF
+f     /tmp/xxx    - 2 2
+f     /tmp/yyy    - 2 2
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:2:2:4755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:2:2:2755"
+
+# then, chown the files to a third user/group but also drop to a mask that has
+# both more and fewer bits set
+systemd-tmpfiles --create - <<EOF
+f     /tmp/xxx    0770 3 3
+f     /tmp/yyy    0770 3 3
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:3:3:770"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:3:3:770"
+
+# return to the beginning
+systemd-tmpfiles --create - <<EOF
+f     /tmp/xxx    0755 1 1 - -
+f     /tmp/yyy    0755 1 1 - -
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
+
+# remove everything
+systemd-tmpfiles --remove - <<EOF
+r /tmp/xxx
+r /tmp/yyy
+EOF
diff --git a/test/units/TEST-22-TMPFILES.10.sh b/test/units/TEST-22-TMPFILES.10.sh
new file mode 100755 (executable)
index 0000000..99052c8
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemd-tmpfiles --create - <<EOF
+f /tmp/xxx1 0644 - - - foo
+f /tmp/xxx2 0644 - - - foo bar
+f /tmp/xxx3 0644 - - - foo\x20bar
+f /tmp/xxx4 0644 - - - \x20foobar
+f /tmp/xxx5 0644 - - - foobar\x20
+f /tmp/xxx6 0644 - - -  foo bar
+f /tmp/xxx7 0644 - - -  foo bar \n
+f /tmp/xxx8 0644 - - - " foo bar "
+f /tmp/xxx9 0644 - - - ' foo bar '
+EOF
+
+echo -n "foo" | cmp /tmp/xxx1 -
+echo -n "foo bar" | cmp /tmp/xxx2 -
+echo -n "foo bar" | cmp /tmp/xxx3 -
+echo -n " foobar" | cmp /tmp/xxx4 -
+echo -n "foobar " | cmp /tmp/xxx5 -
+echo -n "foo bar" | cmp /tmp/xxx6 -
+echo "foo bar " | cmp /tmp/xxx7 -
+echo -n "\" foo bar \"" | cmp /tmp/xxx8 -
+echo -n "' foo bar '" | cmp /tmp/xxx9 -
+
+rm /tmp/xxx{1,2,3,4,5,6,7,8,9}
diff --git a/test/units/TEST-22-TMPFILES.11.sh b/test/units/TEST-22-TMPFILES.11.sh
new file mode 100755 (executable)
index 0000000..6f75888
--- /dev/null
@@ -0,0 +1,249 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+set -x
+
+rm -fr /tmp/x
+mkdir  /tmp/x
+
+#
+# 'x'
+#
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --clean --dry-run - <<EOF
+d /tmp/x - - - 0
+x /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test -f /tmp/x/2/y1
+test -f /tmp/x/2/y2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+x /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test ! -f /tmp/x/z1
+test ! -f /tmp/x/z2
+
+#
+# 'X'
+#
+
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --dry-run --clean - <<EOF
+d /tmp/x - - - 0
+X /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test -f /tmp/x/2/y1
+test -f /tmp/x/2/y2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+X /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test ! -f /tmp/x/1/x1
+test ! -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test ! -f /tmp/x/z1
+test ! -f /tmp/x/z2
+
+#
+# 'x' with glob
+#
+
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --dry-run --clean - <<EOF
+d /tmp/x - - - 0
+x /tmp/x/[1345]
+x /tmp/x/z*
+EOF
+
+find /tmp/x | sort
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test -f /tmp/x/2/y1
+test -f /tmp/x/2/y2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+x /tmp/x/[1345]
+x /tmp/x/z*
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+#
+# 'X' with glob
+#
+
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --dry-run --clean - <<EOF
+d /tmp/x - - - 0
+X /tmp/x/[1345]
+X /tmp/x/?[12]
+EOF
+
+find /tmp/x | sort
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test -f /tmp/x/2/y1
+test -f /tmp/x/2/y2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+X /tmp/x/[1345]
+X /tmp/x/?[12]
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test ! -f /tmp/x/1/x1
+test ! -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+#
+# 'x' with 'r'
+#
+
+mkdir -p /tmp/x/{1,2}/a
+touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
+
+systemd-tmpfiles --dry-run --clean - <<EOF
+# x/X is not supposed to influence r
+x /tmp/x/1/a
+X /tmp/x/2/a
+r /tmp/x/1
+r /tmp/x/2
+EOF
+
+test -f /tmp/x/1/a/x1
+test -f /tmp/x/1/a/x2
+test -f /tmp/x/2/a/y1
+test -f /tmp/x/2/a/y2
+
+systemd-tmpfiles --clean - <<EOF
+# x/X is not supposed to influence r
+x /tmp/x/1/a
+X /tmp/x/2/a
+r /tmp/x/1
+r /tmp/x/2
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -d /tmp/x/1/a
+test -f /tmp/x/1/a/x1
+test -f /tmp/x/1/a/x2
+test -f /tmp/x/2/a/y1
+test -f /tmp/x/2/a/y2
+
+#
+# 'x' with 'R'
+#
+
+mkdir -p /tmp/x/{1,2}/a
+touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
+
+systemd-tmpfiles --dry-run --remove - <<EOF
+# Check that X is honoured below R
+X /tmp/x/1/a
+X /tmp/x/2/a
+R /tmp/x/1
+EOF
+
+test -f /tmp/x/1/a/x1
+test -f /tmp/x/1/a/x2
+test -f /tmp/x/2/a/y1
+test -f /tmp/x/2/a/y2
+
+systemd-tmpfiles --remove - <<EOF
+# Check that X is honoured below R
+X /tmp/x/1/a
+X /tmp/x/2/a
+R /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -d /tmp/x/1/a
+test -f /tmp/x/1/a/x1
+test -f /tmp/x/1/a/x2
+test -f /tmp/x/2/a/y1
+test -f /tmp/x/2/a/y2
+
+#
+# 'r/R/D' and non-directories
+#
+
+touch /tmp/x/{11,22,33}
+
+systemd-tmpfiles --dry-run --remove - <<EOF
+# Check that X is honoured below R
+r /tmp/x/11
+R /tmp/x/22
+D /tmp/x/33
+EOF
+
+test -f /tmp/x/11
+test -f /tmp/x/22
+test -f /tmp/x/33
+
+systemd-tmpfiles --remove - <<EOF
+# Check that X is honoured below R
+r /tmp/x/11
+R /tmp/x/22
+D /tmp/x/33
+EOF
+
+find /tmp/x | sort
+test ! -f /tmp/x/11
+test ! -f /tmp/x/22
+test -f /tmp/x/33
diff --git a/test/units/TEST-22-TMPFILES.12.sh b/test/units/TEST-22-TMPFILES.12.sh
new file mode 100755 (executable)
index 0000000..57788da
--- /dev/null
@@ -0,0 +1,206 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Test the "Age" parameter (with age-by) for systemd-tmpfiles.
+set -e
+set -x
+
+# Test directory structure looks like this:
+#   /tmp/ageby/
+#   ├── d1
+#   │   ├── f1
+#   │   ├── f2
+#   │   ├── f3
+#   │   └── f4
+#   ├── d2
+#   │   ├── f1
+#   │   ├── f2
+#   ...
+
+export SYSTEMD_LOG_LEVEL="debug"
+
+rm -rf /tmp/ageby
+mkdir -p /tmp/ageby/d{1..4}
+
+# TODO: There is probably a better way to figure this out.
+# Test for [bB] age-by arguments only on filesystems that expose
+# the creation time. Note that this is _not_ an accurate way to
+# check if the filesystem or kernel version don't provide the
+# timestamp. But, if the timestamp is visible in "stat" it is a
+# good indicator that the test can be run.
+TEST_TMPFILES_AGEBY_BTIME=${TEST_TMPFILES_AGEBY_BTIME:-0}
+if stat --format "%w" /tmp/ageby 2>/dev/null | grep -qv '^[\?\-]$'; then
+    TEST_TMPFILES_AGEBY_BTIME=1
+fi
+
+touch -a --date "2 minutes ago" /tmp/ageby/d1/f1
+touch -m --date "4 minutes ago" /tmp/ageby/d2/f1
+
+# Create a bunch of other files.
+touch /tmp/ageby/d{1,2}/f{2..4}
+
+# For "ctime".
+touch /tmp/ageby/d3/f1
+chmod +x /tmp/ageby/d3/f1
+sleep 1
+
+# For "btime".
+touch /tmp/ageby/d4/f1
+sleep 1
+
+# More files with recent "{a,b}time" values.
+touch /tmp/ageby/d{3,4}/f{2..4}
+
+# Check for cleanup of "f1" in each of "/tmp/d{1..4}".
+systemd-tmpfiles --dry-run --clean - <<-EOF
+d /tmp/ageby/d1 - - - a:1m -
+e /tmp/ageby/d2 - - - m:3m -
+D /tmp/ageby/d3 - - - c:2s -
+EOF
+
+for d in d{1..3}; do
+    test -f "/tmp/ageby/${d}/f1"
+done
+
+systemd-tmpfiles --clean - <<-EOF
+d /tmp/ageby/d1 - - - a:1m -
+e /tmp/ageby/d2 - - - m:3m -
+D /tmp/ageby/d3 - - - c:2s -
+EOF
+
+for d in d{1..3}; do
+    test ! -f "/tmp/ageby/${d}/f1"
+done
+
+if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
+    systemd-tmpfiles --clean - <<-EOF
+d /tmp/ageby/d4 - - - b:1s -
+EOF
+
+    test ! -f "/tmp/ageby/d4/f1"
+else
+    # Remove the file manually.
+    rm "/tmp/ageby/d4/f1"
+fi
+
+# Check for an invalid "age" and "age-by" arguments.
+for a in ':' ':1s' '2:1h' 'nope:42h' '"  :7m"' 'm:' '::' '"+r^w-x:2/h"' 'b ar::64'; do
+    systemd-tmpfiles --clean - <<EOF 2>&1 | grep -q -F 'Invalid age'
+d /tmp/ageby - - - ${a} -
+EOF
+done
+
+for d in d{1..4}; do
+    for f in f{2..4}; do
+        test -f "/tmp/ageby/${d}/${f}"
+    done
+done
+
+# Check for parsing with whitespace, repeated values
+# for "age-by" (valid arguments).
+for a in '"  a:24h"' 'cccaab:2h' '" aa : 4h"' '" a A B C c:1h"'; do
+    systemd-tmpfiles --clean - <<EOF
+d /tmp/ageby - - - ${a} -
+EOF
+done
+
+for d in d{1..4}; do
+    for f in f{2..4}; do
+        test -f "/tmp/ageby/${d}/${f}"
+    done
+done
+
+# Check that all files are removed if the "Age" is
+# set to "0" (regardless of "age-by" argument).
+systemd-tmpfiles --clean - <<-EOF
+d /tmp/ageby/d1 - - - abc:0 -
+e /tmp/ageby/d2 - - - cmb:0 -
+EOF
+
+for d in d{1,2}; do
+    for f in f{2..4}; do
+        test ! -f "/tmp/ageby/${d}/${f}"
+    done
+done
+
+# Check for combinations:
+#   - "/tmp/ageby/d3/f2" has file timestamps that
+#     are older than the specified age, it will be
+#     removed
+#   - "/tmp/ageby/d4/f2", has not aged for the given
+#     timestamp combination, it will not be removed
+touch -a -m --date "4 minutes ago" /tmp/ageby/d3/f2
+touch -a -m --date "8 minutes ago" /tmp/ageby/d4/f2
+systemd-tmpfiles --clean - <<-EOF
+e /tmp/ageby/d3 - - - am:3m -
+D /tmp/ageby/d4 - - - mc:7m -
+EOF
+
+test ! -f "/tmp/ageby/d3/f2"
+test -f "/tmp/ageby/d4/f2"
+
+# Check that all files are removed if only "Age" is set to 0.
+systemd-tmpfiles --clean - <<-EOF
+e /tmp/ageby/d3 - - - 0s
+d /tmp/ageby/d4 - - - 0s
+EOF
+
+for d in d{3,4}; do
+    for f in f{2..4}; do
+        test ! -f "/tmp/ageby/$d/${f}"
+    done
+done
+
+# Check "age-by" argument for sub-directories in "/tmp/ageby".
+systemd-tmpfiles --clean - <<-EOF
+d /tmp/ageby/ - - - A:1m -
+EOF
+
+for d in d{1..4}; do
+    test -d "/tmp/ageby/${d}"
+done
+
+# Check for combinations.
+touch -a -m --date "5 seconds ago" /tmp/ageby/d{1,2}
+systemd-tmpfiles --clean - <<-EOF
+e /tmp/ageby/ - - - AM:4s -
+EOF
+
+for d in d{1,2}; do
+    test ! -d "/tmp/ageby/${d}"
+done
+
+for d in d{3,4}; do
+    test -d "/tmp/ageby/${d}"
+done
+
+# Check "btime" for directories.
+if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
+    systemd-tmpfiles --clean - <<-EOF
+d /tmp/ageby/ - - - B:8s -
+EOF
+
+    for d in d{3,4}; do
+        test -d "/tmp/ageby/${d}"
+    done
+fi
+
+# To bump "atime".
+touch -a --date "1 second ago" /tmp/ageby/d3
+systemd-tmpfiles --clean - <<-EOF
+d /tmp/ageby/ - - - A:2s -
+EOF
+
+test -d /tmp/ageby/d3
+test ! -d /tmp/ageby/d4
+
+# Check if sub-directories are removed regardless
+# of "age-by", when "Age" is set to "0".
+systemd-tmpfiles --clean - <<-EOF
+D /tmp/ageby/ - - - AM:0 -
+EOF
+
+test ! -d /tmp/ageby/d3
+
+# Cleanup the test directory (fail if not empty).
+rmdir /tmp/ageby
diff --git a/test/units/TEST-22-TMPFILES.13.sh b/test/units/TEST-22-TMPFILES.13.sh
new file mode 100755 (executable)
index 0000000..33ef451
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Tests for configuration directory and file precedences
+#
+set -eux
+
+rm -f  /{usr/lib,etc}/tmpfiles.d/{L,w}-*.conf
+rm -fr /tmp/precedence/{L,w}
+
+mkdir -p /{usr/lib,etc}/tmpfiles.d
+mkdir -p /tmp/precedence/{L,w}
+
+#
+# 'L'
+#
+ln -s /dev/null /tmp/precedence/L
+
+# Overwrite the existing symlink
+cat >/usr/lib/tmpfiles.d/L-z.conf<<EOF
+L+ /tmp/precedence/L - - - - /usr/lib/tmpfiles.d/L-z.conf
+EOF
+
+systemd-tmpfiles --create
+test "$(readlink /tmp/precedence/L)" = "/usr/lib/tmpfiles.d/L-z.conf"
+
+# Files in /etc should override those in /usr
+cat >/etc/tmpfiles.d/L-z.conf<<EOF
+L+ /tmp/precedence/L - - - - /etc/tmpfiles.d/L-z.conf
+EOF
+
+systemd-tmpfiles --create
+test "$(readlink /tmp/precedence/L)" = "/etc/tmpfiles.d/L-z.conf"
+
+# /usr/…/L-a.conf has higher prio than /etc/…/L-z.conf
+cat >/usr/lib/tmpfiles.d/L-a.conf<<EOF
+L+ /tmp/precedence/L - - - - /usr/lib/tmpfiles.d/L-a.conf
+EOF
+
+systemd-tmpfiles --create
+test "$(readlink /tmp/precedence/L)" = "/usr/lib/tmpfiles.d/L-a.conf"
+
+# Files in /etc should override those in /usr
+cat >/etc/tmpfiles.d/L-a.conf<<EOF
+L+ /tmp/precedence/L - - - - /etc/tmpfiles.d/L-a.conf
+EOF
+
+systemd-tmpfiles --create
+test "$(readlink /tmp/precedence/L)" = "/etc/tmpfiles.d/L-a.conf"
+
+#
+# 'w'
+#
+touch /tmp/precedence/w/f
+
+# Multiple configuration files specifying 'w+' for the same path is allowed.
+for i in a c; do
+    cat >/usr/lib/tmpfiles.d/w-$i.conf<<EOF
+w+ /tmp/precedence/w/f - - - - /usr/lib/tmpfiles.d/w-$i.conf\n
+EOF
+    cat >/etc/tmpfiles.d/w-$i.conf<<EOF
+w+ /tmp/precedence/w/f - - - - /etc/tmpfiles.d/w-$i.conf\n
+EOF
+done
+
+cat >/usr/lib/tmpfiles.d/w-b.conf<<EOF
+w+ /tmp/precedence/w/f - - - - /usr/lib/tmpfiles.d/w-b.conf\n
+EOF
+
+systemd-tmpfiles --create
+cmp /tmp/precedence/w/f <<EOF
+/etc/tmpfiles.d/w-a.conf
+/usr/lib/tmpfiles.d/w-b.conf
+/etc/tmpfiles.d/w-c.conf
+EOF
diff --git a/test/units/TEST-22-TMPFILES.14.sh b/test/units/TEST-22-TMPFILES.14.sh
new file mode 100755 (executable)
index 0000000..2132de7
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Tests for the ":" uid/gid/mode modifier
+#
+set -eux
+
+rm -rf /tmp/someinode
+
+systemd-tmpfiles --create - <<EOF
+d /tmp/someinode :0123 :1 :1
+EOF
+test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:1:1:123"
+
+systemd-tmpfiles --create - <<EOF
+d /tmp/someinode :0321 :2 :2
+EOF
+test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:1:1:123"
+
+systemd-tmpfiles --create - <<EOF
+d /tmp/someinode 0321 2 2
+EOF
+test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:2:2:321"
+
+systemd-tmpfiles --create - <<EOF
+d /tmp/someinode :0123 :1 :1
+EOF
+test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:2:2:321"
+
+rm -rf /tmp/someinode
+
+systemd-tmpfiles --create - <<EOF
+d /tmp/someinode :0123 :1 :1
+EOF
+test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:1:1:123"
+
+rm -rf /tmp/someinode
diff --git a/test/units/TEST-22-TMPFILES.15.sh b/test/units/TEST-22-TMPFILES.15.sh
new file mode 100755 (executable)
index 0000000..6ee0e63
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Check specifier expansion in L lines.
+#
+set -eux
+
+rm -fr /tmp/L
+mkdir  /tmp/L
+
+# Check that %h expands to $home.
+home='/somewhere'
+dst='/tmp/L/1'
+src="$home"
+HOME="$home" \
+systemd-tmpfiles --dry-run --create - <<EOF
+L     $dst    - - - - %h
+EOF
+test ! -h "$dst"
+
+HOME="$home" \
+systemd-tmpfiles --create - <<EOF
+L     $dst    - - - - %h
+EOF
+test "$(readlink "$dst")" = "$src"
+
+# Check that %h in the path is expanded, but
+# the result of this expansion is not expanded once again.
+root='/tmp/L/2'
+home='/%U'
+src="/usr/share/factory$home"
+mkdir -p "$root$src"
+dst="$root$home"
+HOME="$home" \
+systemd-tmpfiles --create --dry-run --root="$root" - <<EOF
+L     %h    - - - -
+EOF
+test ! -h "$dst"
+
+HOME="$home" \
+systemd-tmpfiles --create --root="$root" - <<EOF
+L     %h    - - - -
+EOF
+test "$(readlink "$dst")" = "$src"
diff --git a/test/units/TEST-22-TMPFILES.16.sh b/test/units/TEST-22-TMPFILES.16.sh
new file mode 100755 (executable)
index 0000000..3d3d0c8
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Test for conditionalized execute bit ('X' bit)
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+rm -f /tmp/acl_exec
+touch /tmp/acl_exec
+
+# No ACL set yet
+systemd-tmpfiles --dry-run --create - <<EOF
+a /tmp/acl_exec - - - - u:root:rwX
+EOF
+assert_not_in 'user:root:rw-' "$(getfacl -Ec /tmp/acl_exec)"
+
+systemd-tmpfiles --create - <<EOF
+a /tmp/acl_exec - - - - u:root:rwX
+EOF
+assert_in 'user:root:rw-' "$(getfacl -Ec /tmp/acl_exec)"
+
+# Set another ACL and append
+setfacl -m g:root:x /tmp/acl_exec
+
+systemd-tmpfiles --create - <<EOF
+a+ /tmp/acl_exec - - - - u:root:rwX
+EOF
+acl="$(getfacl -Ec /tmp/acl_exec)"
+assert_in 'user:root:rwx' "$acl"
+assert_in 'group:root:--x' "$acl"
+
+# Reset ACL (no append)
+systemd-tmpfiles --create - <<EOF
+a /tmp/acl_exec - - - - u:root:rwX
+EOF
+assert_in 'user:root:rw-' "$(getfacl -Ec /tmp/acl_exec)"
+
+rm -f /tmp/acl_exec
diff --git a/test/units/TEST-22-TMPFILES.17.sh b/test/units/TEST-22-TMPFILES.17.sh
new file mode 100755 (executable)
index 0000000..f43aba5
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Test for C-style escapes in file names and contents
+set -eux
+set -o pipefail
+
+data="\x20foo\nbar"
+dst="/tmp/x/\x20a\nb"
+
+systemd-tmpfiles --create - <<EOF
+f     "$dst" 0644 0 0 - $data
+EOF
+
+diff "$(printf "/tmp/x/\x20a\nb")" <(printf "\x20foo\nbar")
diff --git a/test/units/TEST-22-TMPFILES.18.sh b/test/units/TEST-22-TMPFILES.18.sh
new file mode 100755 (executable)
index 0000000..5d24197
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Tests for the --purge switch
+#
+set -eux
+set -o pipefail
+
+export SYSTEMD_LOG_LEVEL=debug
+
+c='
+d /tmp/somedir
+f /tmp/somedir/somefile - - - - baz
+'
+
+systemd-tmpfiles --create - <<<"$c"
+test -f /tmp/somedir/somefile
+grep -q baz /tmp/somedir/somefile
+
+systemd-tmpfiles --purge --dry-run - <<<"$c"
+test -f /tmp/somedir/somefile
+grep -q baz /tmp/somedir/somefile
+
+systemd-tmpfiles --purge - <<<"$c"
+test ! -f /tmp/somedir/somefile
+test ! -d /tmp/somedir/
+
+systemd-tmpfiles --create --purge --dry-run - <<<"$c"
+test ! -f /tmp/somedir/somefile
+test ! -d /tmp/somedir/
+
+systemd-tmpfiles --create --purge - <<<"$c"
+test -f /tmp/somedir/somefile
+grep -q baz /tmp/somedir/somefile
diff --git a/test/units/TEST-22-TMPFILES.19.sh b/test/units/TEST-22-TMPFILES.19.sh
new file mode 100755 (executable)
index 0000000..c5a0966
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Tests for character and block device creation
+#
+set -eux
+set -o pipefail
+
+rm -rf /tmp/dev
+mkdir /tmp/dev
+
+# We are running tests in /tmp, which would normally be mounted nodev,
+# so we only try with --dry-run.
+
+systemd-tmpfiles --dry-run --create - <<EOF
+c /tmp/dev/char  - - - - 11:12
+b /tmp/dev/block - - - - 11:14
+EOF
+
+test ! -e /tmp/dev/char
+test ! -e /tmp/dev/block
+
+systemd-tmpfiles --dry-run --create - <<EOF
+c+ /tmp/dev/char  - - - - 11:12
+b+ /tmp/dev/block - - - - 11:14
+EOF
+
+test ! -e /tmp/dev/char
+test ! -e /tmp/dev/block
diff --git a/test/units/TEST-22-TMPFILES.service b/test/units/TEST-22-TMPFILES.service
new file mode 100644 (file)
index 0000000..a5ed660
--- /dev/null
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-22-TMPFILES
+After=systemd-tmpfiles-setup.service
+Before=getty-pre.target
+Wants=getty-pre.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-22-TMPFILES.sh b/test/units/TEST-22-TMPFILES.sh
new file mode 100755 (executable)
index 0000000..9c2a033
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
diff --git a/test/units/TEST-23-UNIT-FILE-openfile-child.sh b/test/units/TEST-23-UNIT-FILE-openfile-child.sh
new file mode 100755 (executable)
index 0000000..4828b9d
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+assert_eq "$LISTEN_FDS" "$1"
+assert_eq "$LISTEN_FDNAMES" "$2"
+
+for ((i = 3; i < 3 + LISTEN_FDS; i++)); do
+    read -r -u "$i" text
+    assert_eq "$text" "${!i}" # Dereference $i to get i'th arg
+done
diff --git a/test/units/TEST-23-UNIT-FILE-short-lived.sh b/test/units/TEST-23-UNIT-FILE-short-lived.sh
new file mode 100755 (executable)
index 0000000..4b13070
--- /dev/null
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+
+if [ -f /tmp/TEST-23-UNIT-FILE.counter ] ; then
+    read -r counter < /tmp/TEST-23-UNIT-FILE.counter
+    counter=$((counter + 1))
+else
+    counter=0
+fi
+
+echo "$counter" >/tmp/TEST-23-UNIT-FILE.counter
+
+if [ "$counter" -eq 5 ] ; then
+    systemctl kill --kill-whom=main -sUSR1 TEST-23-UNIT-FILE.service
+fi
+
+exec sleep 1.5
diff --git a/test/units/TEST-23-UNIT-FILE.ExecReload.sh b/test/units/TEST-23-UNIT-FILE.ExecReload.sh
new file mode 100755 (executable)
index 0000000..d544ce6
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test ExecReload= (PR #13098)
+
+systemd-analyze log-level debug
+
+export SYSTEMD_PAGER=
+SERVICE_PATH="$(mktemp /etc/systemd/system/execreloadXXX.service)"
+SERVICE_NAME="${SERVICE_PATH##*/}"
+
+echo "[#1] Failing ExecReload= should not kill the service"
+cat >"$SERVICE_PATH" <<EOF
+[Service]
+ExecStart=sleep infinity
+ExecReload=/bin/false
+EOF
+
+systemctl daemon-reload
+systemctl start "$SERVICE_NAME"
+systemctl status "$SERVICE_NAME"
+# The reload SHOULD fail but SHOULD NOT affect the service state
+(! systemctl reload "$SERVICE_NAME")
+systemctl status "$SERVICE_NAME"
+systemctl stop "$SERVICE_NAME"
+
+
+echo "[#2] Failing ExecReload= should not kill the service (multiple ExecReload=)"
+cat >"$SERVICE_PATH" <<EOF
+[Service]
+ExecStart=sleep infinity
+ExecReload=/bin/true
+ExecReload=/bin/false
+ExecReload=/bin/true
+EOF
+
+systemctl daemon-reload
+systemctl start "$SERVICE_NAME"
+systemctl status "$SERVICE_NAME"
+# The reload SHOULD fail but SHOULD NOT affect the service state
+(! systemctl reload "$SERVICE_NAME")
+systemctl status "$SERVICE_NAME"
+systemctl stop "$SERVICE_NAME"
+
+echo "[#3] Failing ExecReload=- should not affect reload's exit code"
+cat >"$SERVICE_PATH" <<EOF
+[Service]
+ExecStart=sleep infinity
+ExecReload=-/bin/false
+EOF
+
+systemctl daemon-reload
+systemctl start "$SERVICE_NAME"
+systemctl status "$SERVICE_NAME"
+systemctl reload "$SERVICE_NAME"
+systemctl status "$SERVICE_NAME"
+systemctl stop "$SERVICE_NAME"
+
+systemd-analyze log-level info
diff --git a/test/units/TEST-23-UNIT-FILE.ExecStopPost.sh b/test/units/TEST-23-UNIT-FILE.ExecStopPost.sh
new file mode 100755 (executable)
index 0000000..aeaf3aa
--- /dev/null
@@ -0,0 +1,104 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+
+# Test that ExecStopPost= is always run
+
+systemd-analyze log-level debug
+
+systemd-run --unit=simple1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple \
+    -p ExecStopPost='/bin/touch /run/simple1' true
+test -f /run/simple1
+
+(! systemd-run --unit=simple2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple \
+    -p ExecStopPost='/bin/touch /run/simple2' false)
+test -f /run/simple2
+
+systemd-run --unit=exec1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec \
+    -p ExecStopPost='/bin/touch /run/exec1' sleep 1
+test -f /run/exec1
+
+(! systemd-run --unit=exec2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec \
+   -p ExecStopPost='/bin/touch /run/exec2' sh -c 'sleep 1; false')
+test -f /run/exec2
+
+cat >/tmp/forking1.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+
+sleep 4 &
+MAINPID=\$!
+disown
+
+systemd-notify MAINPID=\$MAINPID
+EOF
+chmod +x /tmp/forking1.sh
+
+systemd-run --unit=forking1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec \
+        -p ExecStopPost='/bin/touch /run/forking1' /tmp/forking1.sh
+test -f /run/forking1
+
+cat >/tmp/forking2.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+
+(sleep 4; exit 1) &
+MAINPID=\$!
+disown
+
+systemd-notify MAINPID=\$MAINPID
+EOF
+chmod +x /tmp/forking2.sh
+
+(! systemd-run --unit=forking2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec \
+    -p ExecStopPost='/bin/touch /run/forking2' /tmp/forking2.sh)
+test -f /run/forking2
+
+systemd-run --unit=oneshot1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot \
+    -p ExecStopPost='/bin/touch /run/oneshot1' true
+test -f /run/oneshot1
+
+(! systemd-run --unit=oneshot2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot \
+    -p ExecStopPost='/bin/touch /run/oneshot2' false)
+test -f /run/oneshot2
+
+systemd-run --unit=dbus1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost \
+    -p ExecStopPost='/bin/touch /run/dbus1' \
+    busctl call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus RequestName su systemd.test.ExecStopPost 4 || :
+test -f /run/dbus1
+
+systemd-run --unit=dbus2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost \
+     -p ExecStopPost='/bin/touch /run/dbus2' true
+test -f /run/dbus2
+
+# https://github.com/systemd/systemd/issues/19920
+(! systemd-run --unit=dbus3.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus \
+    -p ExecStopPost='/bin/touch /run/dbus3' true)
+
+cat >/tmp/notify1.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+
+systemd-notify --ready
+EOF
+chmod +x /tmp/notify1.sh
+
+systemd-run --unit=notify1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify \
+    -p ExecStopPost='/bin/touch /run/notify1' /tmp/notify1.sh
+test -f /run/notify1
+
+(! systemd-run --unit=notify2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify \
+    -p ExecStopPost='/bin/touch /run/notify2' true)
+test -f /run/notify2
+
+systemd-run --unit=idle1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle1' true
+test -f /run/idle1
+
+(! systemd-run --unit=idle2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle \
+     -p ExecStopPost='/bin/touch /run/idle2' false)
+test -f /run/idle2
+
+systemd-analyze log-level info
diff --git a/test/units/TEST-23-UNIT-FILE.JoinsNamespaceOf.sh b/test/units/TEST-23-UNIT-FILE.JoinsNamespaceOf.sh
new file mode 100755 (executable)
index 0000000..4fd68d8
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eux
+set -o pipefail
+
+# Test JoinsNamespaceOf= with PrivateTmp=yes
+
+systemd-analyze log-level debug
+systemd-analyze log-target journal
+
+# simple case
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-1.service
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-2.service
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-3.service
+systemctl stop TEST-23-UNIT-FILE-joins-namespace-of-1.service
+
+# inverse dependency
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-4.service
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-5.service
+systemctl stop TEST-23-UNIT-FILE-joins-namespace-of-4.service
+
+# transitive dependency
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-6.service
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-7.service
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-8.service
+systemctl start TEST-23-UNIT-FILE-joins-namespace-of-9.service
+systemctl stop TEST-23-UNIT-FILE-joins-namespace-of-6.service
+systemctl stop TEST-23-UNIT-FILE-joins-namespace-of-8.service
+
+systemd-analyze log-level info
diff --git a/test/units/TEST-23-UNIT-FILE.RuntimeDirectoryPreserve.sh b/test/units/TEST-23-UNIT-FILE.RuntimeDirectoryPreserve.sh
new file mode 100755 (executable)
index 0000000..ca57702
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+# Test RuntimeDirectoryPreserve=yes
+
+at_exit() {
+    set +e
+
+    rm -fr /run/hoge /tmp/aaa
+}
+
+trap at_exit EXIT
+
+systemd-mount -p RuntimeDirectory=hoge -p RuntimeDirectoryPreserve=yes -t tmpfs tmpfs /tmp/aaa
+
+touch /run/hoge/foo
+touch /tmp/aaa/bbb
+
+systemctl restart tmp-aaa.mount
+
+test -e /run/hoge/foo
+test ! -e /tmp/aaa/bbb
diff --git a/test/units/TEST-23-UNIT-FILE.StandardOutput.sh b/test/units/TEST-23-UNIT-FILE.StandardOutput.sh
new file mode 100755 (executable)
index 0000000..a951b13
--- /dev/null
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test StandardOutput=file:
+
+systemd-analyze log-level debug
+
+systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-one \
+            -p StandardOutput=file:/tmp/stdout \
+            -p StandardError=file:/tmp/stderr \
+            -p Type=exec \
+            sh -c 'echo x ; echo y >&2'
+cmp /tmp/stdout <<EOF
+x
+EOF
+cmp /tmp/stderr <<EOF
+y
+EOF
+
+systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-two \
+            -p StandardOutput=file:/tmp/stdout \
+            -p StandardError=file:/tmp/stderr \
+            -p Type=exec \
+            sh -c 'echo z ; echo a >&2'
+cmp /tmp/stdout <<EOF
+z
+EOF
+cmp /tmp/stderr <<EOF
+a
+EOF
+
+systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-three \
+            -p StandardOutput=append:/tmp/stdout \
+            -p StandardError=append:/tmp/stderr \
+            -p Type=exec \
+            sh -c 'echo b ; echo c >&2'
+cmp /tmp/stdout <<EOF
+z
+b
+EOF
+cmp /tmp/stderr <<EOF
+a
+c
+EOF
+
+systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-four \
+            -p StandardOutput=truncate:/tmp/stdout \
+            -p StandardError=truncate:/tmp/stderr \
+            -p Type=exec \
+            sh -c 'echo a ; echo b >&2'
+cmp /tmp/stdout <<EOF
+a
+EOF
+cmp /tmp/stderr <<EOF
+b
+EOF
+
+systemd-analyze log-level info
diff --git a/test/units/TEST-23-UNIT-FILE.Upholds.sh b/test/units/TEST-23-UNIT-FILE.Upholds.sh
new file mode 100755 (executable)
index 0000000..20d2d2b
--- /dev/null
@@ -0,0 +1,99 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eux
+set -o pipefail
+
+# Test OnSuccess= + Uphold= + PropagatesStopTo= + BindsTo=
+
+systemd-analyze log-level debug
+systemd-analyze log-target journal
+
+# Idea is this:
+#    1. we start TEST-23-UNIT-FILE-success.service
+#    2. which through OnSuccess= starts TEST-23-UNIT-FILE-fail.service,
+#    3. which through OnFailure= starts TEST-23-UNIT-FILE-uphold.service,
+#    4. which through Uphold= starts/keeps TEST-23-UNIT-FILE-short-lived.service running,
+#    5. which will sleep 1s when invoked, and on the 5th invocation send us a SIGUSR1
+#    6. once we got that we finish cleanly
+
+sigusr1=0
+trap sigusr1=1 SIGUSR1
+
+trap -p SIGUSR1
+
+systemctl start TEST-23-UNIT-FILE-success.service
+
+while [ "$sigusr1" -eq 0 ] ; do
+    sleep .5
+done
+
+systemctl stop TEST-23-UNIT-FILE-uphold.service
+
+systemctl enable TEST-23-UNIT-FILE-upheldby-install.service
+
+# Idea is this:
+#    1. we start TEST-23-UNIT-FILE-retry-uphold.service
+#    2. which through Uphold= starts TEST-23-UNIT-FILE-retry-upheld.service
+#    3. which through Requires= starts TEST-23-UNIT-FILE-retry-fail.service
+#    4. which fails as /tmp/TEST-23-UNIT-FILE-retry-fail does not exist, so TEST-23-UNIT-FILE-retry-upheld.service
+#       is no longer restarted
+#    5. we create /tmp/TEST-23-UNIT-FILE-retry-fail
+#    6. now TEST-23-UNIT-FILE-retry-upheld.service will be restarted since upheld, and its dependency will
+#       be satisfied
+
+rm -f /tmp/TEST-23-UNIT-FILE-retry-fail
+systemctl start TEST-23-UNIT-FILE-retry-uphold.service
+systemctl is-active TEST-23-UNIT-FILE-upheldby-install.service
+
+until systemctl is-failed TEST-23-UNIT-FILE-retry-fail.service ; do
+    sleep .5
+done
+
+(! systemctl is-active TEST-23-UNIT-FILE-retry-upheld.service)
+
+touch /tmp/TEST-23-UNIT-FILE-retry-fail
+
+until systemctl is-active TEST-23-UNIT-FILE-retry-upheld.service ; do
+    sleep .5
+done
+
+systemctl stop TEST-23-UNIT-FILE-retry-uphold.service TEST-23-UNIT-FILE-retry-fail.service TEST-23-UNIT-FILE-retry-upheld.service
+
+# Idea is this:
+#    1. we start TEST-23-UNIT-FILE-prop-stop-one.service
+#    2. which through Wants=/After= pulls in TEST-23-UNIT-FILE-prop-stop-two.service as well
+#    3. TEST-23-UNIT-FILE-prop-stop-one.service then sleeps indefinitely
+#    4. TEST-23-UNIT-FILE-prop-stop-two.service sleeps a short time and exits
+#    5. the StopPropagatedFrom= dependency between the two should ensure *both* will exit as result
+#    6. an ExecStopPost= line on TEST-23-UNIT-FILE-prop-stop-one.service will send us a SIGUSR2
+#    7. once we got that we finish cleanly
+
+sigusr2=0
+trap sigusr2=1 SIGUSR2
+
+systemctl start TEST-23-UNIT-FILE-prop-stop-one.service
+
+while [ "$sigusr2" -eq 0 ] ; do
+    sleep .5
+done
+
+
+# Idea is this:
+#    1. we start TEST-23-UNIT-FILE-binds-to.service
+#    2. which through BindsTo=/After= pulls in TEST-23-UNIT-FILE-bound-by.service as well
+#    3. TEST-23-UNIT-FILE-bound-by.service suddenly dies
+#    4. TEST-23-UNIT-FILE-binds-to.service should then also be pulled down (it otherwise just hangs)
+#    6. an ExecStopPost= line on TEST-23-UNIT-FILE-binds-to.service will send us a SIGRTMIN1+1
+#    7. once we got that we finish cleanly
+
+sigrtmin1=0
+trap sigrtmin1=1 SIGRTMIN+1
+
+systemctl start TEST-23-UNIT-FILE-binds-to.service
+
+while [ "$sigrtmin1" -eq 0 ] ; do
+    sleep .5
+done
+
+systemd-analyze log-level info
diff --git a/test/units/TEST-23-UNIT-FILE.clean-unit.sh b/test/units/TEST-23-UNIT-FILE.clean-unit.sh
new file mode 100755 (executable)
index 0000000..a883243
--- /dev/null
@@ -0,0 +1,329 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+# Test unit configuration/state/cache/log/runtime data cleanup
+
+at_exit() {
+    set +e
+
+    rm -fr /{etc,run,var/lib,var/cache,var/log}/test-service
+    rm -fr /{etc,run,var/lib,var/cache,var/log}/private/test-service
+    rm -fr /{etc,run,var/lib,var/cache,var/log}/hoge
+    rm -fr /{etc,run,var/lib,var/cache,var/log}/test-socket
+}
+
+trap at_exit EXIT
+
+cat >/run/systemd/system/test-service.service <<EOF
+[Service]
+ConfigurationDirectory=test-service
+RuntimeDirectory=test-service
+StateDirectory=test-service
+CacheDirectory=test-service
+LogsDirectory=test-service
+RuntimeDirectoryPreserve=yes
+ExecStart=sleep infinity
+Type=exec
+EOF
+
+systemctl daemon-reload
+
+test ! -e /etc/test-service
+test ! -e /run/test-service
+test ! -e /var/lib/test-service
+test ! -e /var/cache/test-service
+test ! -e /var/log/test-service
+
+systemctl start test-service
+
+test -d /etc/test-service
+test -d /run/test-service
+test -d /var/lib/test-service
+test -d /var/cache/test-service
+test -d /var/log/test-service
+
+(! systemctl clean test-service)
+
+systemctl stop test-service
+
+test -d /etc/test-service
+test -d /run/test-service
+test -d /var/lib/test-service
+test -d /var/cache/test-service
+test -d /var/log/test-service
+
+systemctl clean test-service --what=configuration
+
+test ! -e /etc/test-service
+test -d /run/test-service
+test -d /var/lib/test-service
+test -d /var/cache/test-service
+test -d /var/log/test-service
+
+systemctl clean test-service
+
+test ! -e /etc/test-service
+test ! -e /run/test-service
+test -d /var/lib/test-service
+test ! -e /var/cache/test-service
+test -d /var/log/test-service
+
+systemctl clean test-service --what=logs
+
+test ! -e /etc/test-service
+test ! -e /run/test-service
+test -d /var/lib/test-service
+test ! -e /var/cache/test-service
+test ! -e /var/log/test-service
+
+systemctl clean test-service --what=all
+
+test ! -e /etc/test-service
+test ! -e /run/test-service
+test ! -e /var/lib/test-service
+test ! -e /var/cache/test-service
+test ! -e /var/log/test-service
+
+cat >/run/systemd/system/test-service.service <<EOF
+[Service]
+DynamicUser=yes
+ConfigurationDirectory=test-service
+RuntimeDirectory=test-service
+StateDirectory=test-service
+CacheDirectory=test-service
+LogsDirectory=test-service
+RuntimeDirectoryPreserve=yes
+ExecStart=sleep infinity
+Type=exec
+EOF
+
+systemctl daemon-reload
+
+test ! -e /etc/test-service
+test ! -e /run/test-service
+test ! -e /var/lib/test-service
+test ! -e /var/cache/test-service
+test ! -e /var/log/test-service
+
+systemctl restart test-service
+
+test -d /etc/test-service
+test -d /run/private/test-service
+test -d /var/lib/private/test-service
+test -d /var/cache/private/test-service
+test -d /var/log/private/test-service
+test -L /run/test-service
+test -L /var/lib/test-service
+test -L /var/cache/test-service
+test -L /var/log/test-service
+
+(! systemctl clean test-service)
+
+systemctl stop test-service
+
+test -d /etc/test-service
+test -d /run/private/test-service
+test -d /var/lib/private/test-service
+test -d /var/cache/private/test-service
+test -d /var/log/private/test-service
+test -L /run/test-service
+test -L /var/lib/test-service
+test -L /var/cache/test-service
+test -L /var/log/test-service
+
+systemctl clean test-service --what=configuration
+
+test ! -d /etc/test-service
+test -d /run/private/test-service
+test -d /var/lib/private/test-service
+test -d /var/cache/private/test-service
+test -d /var/log/private/test-service
+test -L /run/test-service
+test -L /var/lib/test-service
+test -L /var/cache/test-service
+test -L /var/log/test-service
+
+systemctl clean test-service
+
+test ! -d /etc/test-service
+test ! -d /run/private/test-service
+test -d /var/lib/private/test-service
+test ! -d /var/cache/private/test-service
+test -d /var/log/private/test-service
+test ! -L /run/test-service
+test -L /var/lib/test-service
+test ! -L /var/cache/test-service
+test -L /var/log/test-service
+
+systemctl clean test-service --what=logs
+
+test ! -d /etc/test-service
+test ! -d /run/private/test-service
+test -d /var/lib/private/test-service
+test ! -d /var/cache/private/test-service
+test ! -d /var/log/private/test-service
+test ! -L /run/test-service
+test -L /var/lib/test-service
+test ! -L /var/cache/test-service
+test ! -L /var/log/test-service
+
+systemctl clean test-service --what=all
+
+test ! -d /etc/test-service
+test ! -d /run/private/test-service
+test ! -d /var/lib/private/test-service
+test ! -d /var/cache/private/test-service
+test ! -d /var/log/private/test-service
+test ! -L /run/test-service
+test ! -L /var/lib/test-service
+test ! -L /var/cache/test-service
+test ! -L /var/log/test-service
+
+cat >/run/systemd/system/tmp-hoge.mount <<EOF
+[Mount]
+What=tmpfs
+Type=tmpfs
+ConfigurationDirectory=hoge
+RuntimeDirectory=hoge
+StateDirectory=hoge
+CacheDirectory=hoge
+LogsDirectory=hoge
+EOF
+
+systemctl daemon-reload
+
+test ! -e /etc/hoge
+test ! -e /run/hoge
+test ! -e /var/lib/hoge
+test ! -e /var/cache/hoge
+test ! -e /var/log/hoge
+
+systemctl start tmp-hoge.mount
+
+test -d /etc/hoge
+test -d /run/hoge
+test -d /var/lib/hoge
+test -d /var/cache/hoge
+test -d /var/log/hoge
+
+(! systemctl clean tmp-hoge.mount)
+
+test -d /etc/hoge
+test -d /run/hoge
+test -d /var/lib/hoge
+test -d /var/cache/hoge
+test -d /var/log/hoge
+
+systemctl stop tmp-hoge.mount
+
+test -d /etc/hoge
+test ! -d /run/hoge
+test -d /var/lib/hoge
+test -d /var/cache/hoge
+test -d /var/log/hoge
+
+systemctl clean tmp-hoge.mount --what=configuration
+
+test ! -d /etc/hoge
+test ! -d /run/hoge
+test -d /var/lib/hoge
+test -d /var/cache/hoge
+test -d /var/log/hoge
+
+systemctl clean tmp-hoge.mount
+
+test ! -d /etc/hoge
+test ! -d /run/hoge
+test -d /var/lib/hoge
+test ! -d /var/cache/hoge
+test -d /var/log/hoge
+
+systemctl clean tmp-hoge.mount --what=logs
+
+test ! -d /etc/hoge
+test ! -d /run/hoge
+test -d /var/lib/hoge
+test ! -d /var/cache/hoge
+test ! -d /var/log/hoge
+
+systemctl clean tmp-hoge.mount --what=all
+
+test ! -d /etc/hoge
+test ! -d /run/hoge
+test ! -d /var/lib/hoge
+test ! -d /var/cache/hoge
+test ! -d /var/log/hoge
+
+cat >/run/systemd/system/test-service.socket <<EOF
+[Socket]
+ListenSequentialPacket=/run/test-service.socket
+RemoveOnStop=yes
+ExecStartPre=true
+ConfigurationDirectory=test-socket
+RuntimeDirectory=test-socket
+StateDirectory=test-socket
+CacheDirectory=test-socket
+LogsDirectory=test-socket
+EOF
+
+systemctl daemon-reload
+
+test ! -e /etc/test-socket
+test ! -e /run/test-socket
+test ! -e /var/lib/test-socket
+test ! -e /var/cache/test-socket
+test ! -e /var/log/test-socket
+
+systemctl start test-service.socket
+
+test -d /etc/test-socket
+test -d /run/test-socket
+test -d /var/lib/test-socket
+test -d /var/cache/test-socket
+test -d /var/log/test-socket
+
+(! systemctl clean test-service.socket)
+
+systemctl stop test-service.socket
+
+test -d /etc/test-socket
+test ! -d /run/test-socket
+test -d /var/lib/test-socket
+test -d /var/cache/test-socket
+test -d /var/log/test-socket
+
+systemctl clean test-service.socket --what=configuration
+
+test ! -e /etc/test-socket
+test ! -d /run/test-socket
+test -d /var/lib/test-socket
+test -d /var/cache/test-socket
+test -d /var/log/test-socket
+
+systemctl clean test-service.socket
+
+test ! -e /etc/test-socket
+test ! -e /run/test-socket
+test -d /var/lib/test-socket
+test ! -e /var/cache/test-socket
+test -d /var/log/test-socket
+
+systemctl clean test-service.socket --what=logs
+
+test ! -e /etc/test-socket
+test ! -e /run/test-socket
+test -d /var/lib/test-socket
+test ! -e /var/cache/test-socket
+test ! -e /var/log/test-socket
+
+systemctl clean test-service.socket --what=all
+
+test ! -e /etc/test-socket
+test ! -e /run/test-socket
+test ! -e /var/lib/test-socket
+test ! -e /var/cache/test-socket
+test ! -e /var/log/test-socket
diff --git a/test/units/TEST-23-UNIT-FILE.exec-command-ex.sh b/test/units/TEST-23-UNIT-FILE.exec-command-ex.sh
new file mode 100755 (executable)
index 0000000..f926e7d
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test ExecXYZEx= service unit dbus hookups
+
+systemd-analyze log-level debug
+
+declare -A property
+
+property[1_one]=ExecCondition
+property[2_two]=ExecStartPre
+property[3_three]=ExecStart
+property[4_four]=ExecStartPost
+property[5_five]=ExecReload
+property[6_six]=ExecStop
+property[7_seven]=ExecStopPost
+
+# These should all get upgraded to the corresponding Ex property as the non-Ex variant
+# does not support the ":" prefix (no-env-expand).
+for c in "${!property[@]}"; do
+    systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property[$c]}=:/bin/echo \${$c}" /bin/true
+    systemctl show -p "${property[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
+    systemctl show -p "${property[$c]}Ex" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
+done
+
+declare -A property_ex
+
+property_ex[1_one_ex]=ExecConditionEx
+property_ex[2_two_ex]=ExecStartPreEx
+property_ex[3_three_ex]=ExecStartEx
+property_ex[4_four_ex]=ExecStartPostEx
+property_ex[5_five_ex]=ExecReloadEx
+property_ex[6_six_ex]=ExecStopEx
+property_ex[7_seven_ex]=ExecStopPostEx
+
+for c in "${!property_ex[@]}"; do
+    systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property_ex[$c]}=:/bin/echo \${$c}" /bin/true
+    systemctl show -p "${property_ex[$c]%??}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
+    systemctl show -p "${property_ex[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
+done
+
+systemd-analyze log-level info
diff --git a/test/units/TEST-23-UNIT-FILE.oneshot-restart.sh b/test/units/TEST-23-UNIT-FILE.oneshot-restart.sh
new file mode 100755 (executable)
index 0000000..d06dbaa
--- /dev/null
@@ -0,0 +1,101 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Test oneshot unit restart on failure
+
+# wait this many secs for each test service to succeed in what is being tested
+MAX_SECS=60
+
+systemctl log-level debug
+
+# test one: Restart=on-failure should restart the service
+(! systemd-run --unit=oneshot-restart-one -p Type=oneshot -p Restart=on-failure /bin/bash -c "exit 1")
+
+for ((secs = 0; secs < MAX_SECS; secs++)); do
+    [[ "$(systemctl show oneshot-restart-one.service -P NRestarts)" -le 0 ]] || break
+    sleep 1
+done
+if [[ "$(systemctl show oneshot-restart-one.service -P NRestarts)" -le 0 ]]; then
+    exit 1
+fi
+
+TMP_FILE="/tmp/test-23-oneshot-restart-test$RANDOM"
+
+: >$TMP_FILE
+
+# test two: make sure StartLimitBurst correctly limits the number of restarts
+# and restarts execution of the unit from the first ExecStart=
+(! systemd-run --unit=oneshot-restart-two \
+        -p StartLimitIntervalSec=120 \
+        -p StartLimitBurst=3 \
+        -p Type=oneshot \
+        -p Restart=on-failure \
+        -p ExecStart="/bin/bash -c 'printf a >>$TMP_FILE'" /bin/bash -c "exit 1")
+
+# wait for at least 3 restarts
+for ((secs = 0; secs < MAX_SECS; secs++)); do
+    [[ $(cat $TMP_FILE) != "aaa" ]] || break
+    sleep 1
+done
+if [[ $(cat $TMP_FILE) != "aaa" ]]; then
+    exit 1
+fi
+
+# wait for 5 more seconds to make sure there aren't excess restarts
+sleep 5
+if [[ $(cat $TMP_FILE) != "aaa" ]]; then
+    exit 1
+fi
+rm "$TMP_FILE"
+
+# Test RestartForceExitStatus=. Note that success exit statuses are meant to be skipped
+
+TMP_FILE="/tmp/test-23-oneshot-restart-test$RANDOM"
+UNIT_NAME="TEST-23-UNIT-FILE-oneshot-restartforce.service"
+ONSUCCESS_UNIT_NAME="TEST-23-UNIT-FILE-oneshot-restartforce-onsuccess.service"
+FIFO_FILE="/tmp/test-23-oneshot-restart-test-fifo"
+
+cat >"/run/systemd/system/$UNIT_NAME" <<EOF
+[Unit]
+OnSuccess=$ONSUCCESS_UNIT_NAME
+
+[Service]
+Type=oneshot
+RestartForceExitStatus=0 2
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-oneshot-restartforce.sh "$TMP_FILE"
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+cat >"/run/systemd/system/$ONSUCCESS_UNIT_NAME" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c 'echo finished >$FIFO_FILE'
+EOF
+
+mkfifo "$FIFO_FILE"
+
+# Pin the unit in memory
+systemctl enable "$UNIT_NAME"
+# Initial run should fail
+(! systemctl start "$UNIT_NAME")
+# Wait for OnSuccess=
+read -r x <"$FIFO_FILE"
+assert_eq "$x" "finished"
+
+cmp -b <(systemctl show "$UNIT_NAME" -p Result -p NRestarts -p SubState) <<EOF
+Result=success
+NRestarts=1
+SubState=dead
+EOF
+
+systemctl disable "$UNIT_NAME"
+rm "$TMP_FILE" /run/systemd/system/{"$UNIT_NAME","$ONSUCCESS_UNIT_NAME"} "$FIFO_FILE"
+
+systemctl log-level info
diff --git a/test/units/TEST-23-UNIT-FILE.openfile.sh b/test/units/TEST-23-UNIT-FILE.openfile.sh
new file mode 100755 (executable)
index 0000000..644b6f4
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+    set +e
+
+    rm -rf /tmp/test-open-file/
+}
+
+trap at_exit EXIT
+
+systemctl log-level debug
+
+# Existing files
+
+mkdir /tmp/test-open-file
+echo "Open" >'/tmp/test-open-file/open.txt'
+echo "File" >'/tmp/test-open-file/file:colon.txt'
+
+systemd-run -p DynamicUser=yes -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
+            -p OpenFile='/tmp/test-open-file/open.txt::read-only' \
+            -p OpenFile='/tmp/test-open-file/file\x3Acolon.txt:colon' \
+            -p RemainAfterExit=yes \
+            --unit=test-23-openfile-existing.service \
+            --service-type=oneshot \
+            /usr/lib/systemd/tests/testdata/units/TEST-23-UNIT-FILE-openfile-child.sh 2 "open.txt:colon" "Open" "File"
+
+cmp <(systemctl show -p OpenFile test-23-openfile-existing.service) <<EOF
+OpenFile=/tmp/test-open-file/open.txt::read-only
+OpenFile=/tmp/test-open-file/file\\x3acolon.txt:colon
+EOF
+
+systemctl stop test-23-openfile-existing.service
+
+# Sockets
+
+systemctl start TEST-23-UNIT-FILE-openfile-server.socket
+
+systemd-run -p OpenFile=/tmp/test.sock:socket:read-only \
+            --wait \
+            /usr/lib/systemd/tests/testdata/units/TEST-23-UNIT-FILE-openfile-child.sh 1 "socket" "Socket"
+
+systemctl stop TEST-23-UNIT-FILE-openfile-server.socket
+
+# Ignore when missing
+
+assert_rc 202 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only --wait true
+assert_rc 0 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only,graceful --wait true
+
+systemctl log-level info
diff --git a/test/units/TEST-23-UNIT-FILE.percentj-wantedby.sh b/test/units/TEST-23-UNIT-FILE.percentj-wantedby.sh
new file mode 100755 (executable)
index 0000000..c091bd6
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+# Ensure %j Wants directives work
+systemd-run --wait \
+            --property="Type=oneshot" \
+            --property="Wants=TEST-23-UNIT-FILE-specifier-j-wants.service" \
+            --property="After=TEST-23-UNIT-FILE-specifier-j-wants.service" \
+            true
+
+test -f /tmp/tetsuite-23-specifier-j-done
diff --git a/test/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh b/test/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh
new file mode 100755 (executable)
index 0000000..3a78234
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# Test adding new BindPaths while unit is already running
+
+at_exit() {
+    set +e
+
+    rm -f /run/TEST-23-UNIT-FILE-marker-{fixed,runtime}
+    rm -fr /run/inaccessible
+}
+
+trap at_exit EXIT
+
+echo "MARKER_FIXED" >/run/TEST-23-UNIT-FILE-marker-fixed
+mkdir /run/inaccessible
+
+systemctl start TEST-23-UNIT-FILE-namespaced.service
+
+# Ensure that inaccessible paths aren't bypassed by the runtime setup,
+(! systemctl bind --mkdir TEST-23-UNIT-FILE-namespaced.service /run/TEST-23-UNIT-FILE-marker-fixed /run/inaccessible/testfile-marker-fixed)
+
+echo "MARKER_WRONG" >/run/TEST-23-UNIT-FILE-marker-wrong
+echo "MARKER_RUNTIME" >/run/TEST-23-UNIT-FILE-marker-runtime
+
+# Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount)
+systemctl bind --mkdir TEST-23-UNIT-FILE-namespaced.service /run/TEST-23-UNIT-FILE-marker-wrong /tmp/testfile-marker-runtime
+test "$(systemctl show -P SubState TEST-23-UNIT-FILE-namespaced.service)" = "running"
+systemctl bind --mkdir TEST-23-UNIT-FILE-namespaced.service /run/TEST-23-UNIT-FILE-marker-runtime /tmp/testfile-marker-runtime
+
+timeout 10 bash -xec 'while [[ "$(systemctl show -P SubState TEST-23-UNIT-FILE-namespaced.service)" == running ]]; do sleep .5; done'
+systemctl is-active TEST-23-UNIT-FILE-namespaced.service
+
+# Now test that systemctl bind fails when attempted on a non-namespaced unit
+systemctl start TEST-23-UNIT-FILE-non-namespaced.service
+
+(! systemctl bind --mkdir TEST-23-UNIT-FILE-non-namespaced.service /run/TEST-23-UNIT-FILE-marker-runtime /tmp/testfile-marker-runtime)
+
+timeout 10 bash -xec 'while [[ "$(systemctl show -P SubState TEST-23-UNIT-FILE-non-namespaced.service)" == running ]]; do sleep .5; done'
+(! systemctl is-active TEST-23-UNIT-FILE-non-namespaced.service)
diff --git a/test/units/TEST-23-UNIT-FILE.service b/test/units/TEST-23-UNIT-FILE.service
new file mode 100644 (file)
index 0000000..e45435b
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-23-UNIT-FILE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-23-UNIT-FILE.sh b/test/units/TEST-23-UNIT-FILE.sh
new file mode 100755 (executable)
index 0000000..a929c8b
--- /dev/null
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+# Note: the signal shenanigans are necessary for the Upholds= tests
+run_subtests_with_signals SIGUSR1 SIGUSR2 SIGRTMIN+1
+
+touch /testok
diff --git a/test/units/TEST-23-UNIT-FILE.start-stop-no-reload.sh b/test/units/TEST-23-UNIT-FILE.start-stop-no-reload.sh
new file mode 100755 (executable)
index 0000000..61a6592
--- /dev/null
@@ -0,0 +1,93 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+# Test start & stop operations without daemon-reload
+
+at_exit() {
+    set +e
+
+    rm -f /run/systemd/system/TEST-23-UNIT-FILE-no-reload.{service,target}
+}
+
+trap at_exit EXIT
+
+cat >/run/systemd/system/TEST-23-UNIT-FILE-no-reload.target <<EOF
+[Unit]
+Wants=TEST-23-UNIT-FILE-no-reload.service
+EOF
+
+systemctl daemon-reload
+
+systemctl start TEST-23-UNIT-FILE-no-reload.target
+
+# The filesystem on the test image, despite being ext4, seems to have a mtime
+# granularity of one second, which means the manager's unit cache won't be
+# marked as dirty when writing the unit file, unless we wait at least a full
+# second after the previous daemon-reload.
+# May 07 23:12:20 H TEST-23-UNIT-FILE.sh[30]: + cat
+# May 07 23:12:20 H TEST-23-UNIT-FILE.sh[30]: + ls -l --full-time /etc/systemd/system/TEST-23-UNIT-FILE-no-reload.service
+# May 07 23:12:20 H TEST-23-UNIT-FILE.sh[52]: -rw-r--r-- 1 root root 50 2020-05-07 23:12:20.000000000 +0100 /
+# May 07 23:12:20 H TEST-23-UNIT-FILE.sh[30]: + stat -f --format=%t /etc/systemd/system/TEST-23-UNIT-FILE-no-reload.servic
+# May 07 23:12:20 H TEST-23-UNIT-FILE.sh[53]: ef53
+sleep 3.1
+
+cat >/run/systemd/system/TEST-23-UNIT-FILE-no-reload.service <<EOF
+[Service]
+ExecStart=sleep infinity
+EOF
+
+systemctl start TEST-23-UNIT-FILE-no-reload.service
+
+systemctl is-active TEST-23-UNIT-FILE-no-reload.service
+
+# Stop and remove, and try again to exercise https://github.com/systemd/systemd/issues/15992
+systemctl stop TEST-23-UNIT-FILE-no-reload.service
+rm -f /run/systemd/system/TEST-23-UNIT-FILE-no-reload.service
+systemctl daemon-reload
+
+sleep 3.1
+
+cat >/run/systemd/system/TEST-23-UNIT-FILE-no-reload.service <<EOF
+[Service]
+ExecStart=sleep infinity
+EOF
+
+# Start a non-existing unit first, so that the cache is reloaded for an unrelated
+# reason. Starting the existing unit later should still work thanks to the check
+# for the last load attempt vs cache timestamp.
+systemctl start TEST-23-UNIT-FILE-no-reload-nonexistent.service || true
+
+systemctl start TEST-23-UNIT-FILE-no-reload.service
+
+systemctl is-active TEST-23-UNIT-FILE-no-reload.service
+
+# Stop and remove, and try again to exercise the transaction setup code path by
+# having the target pull in the unloaded but available unit
+systemctl stop TEST-23-UNIT-FILE-no-reload.service TEST-23-UNIT-FILE-no-reload.target
+rm -f /run/systemd/system/TEST-23-UNIT-FILE-no-reload.service /run/systemd/system/TEST-23-UNIT-FILE-no-reload.target
+systemctl daemon-reload
+
+sleep 3.1
+
+cat >/run/systemd/system/TEST-23-UNIT-FILE-no-reload.target <<EOF
+[Unit]
+Conflicts=shutdown.target
+Wants=TEST-23-UNIT-FILE-no-reload.service
+EOF
+
+systemctl daemon-reload
+
+systemctl start TEST-23-UNIT-FILE-no-reload.target
+
+cat >/run/systemd/system/TEST-23-UNIT-FILE-no-reload.service <<EOF
+[Service]
+ExecStart=sleep infinity
+EOF
+
+systemctl restart TEST-23-UNIT-FILE-no-reload.target
+
+systemctl is-active TEST-23-UNIT-FILE-no-reload.service
diff --git a/test/units/TEST-23-UNIT-FILE.statedir.sh b/test/units/TEST-23-UNIT-FILE.statedir.sh
new file mode 100755 (executable)
index 0000000..b592314
--- /dev/null
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+# Test unit configuration/state/cache/log/runtime data cleanup
+
+export HOME=/root
+export XDG_RUNTIME_DIR=/run/user/0
+
+systemctl start user@0.service
+
+( ! test -d "$HOME"/.local/state/foo)
+( ! test -d "$HOME"/.config/foo)
+
+systemd-run --user -p StateDirectory=foo --wait /bin/true
+
+test -d "$HOME"/.local/state/foo
+( ! test -L "$HOME"/.local/state/foo)
+( ! test -d "$HOME"/.config/foo)
+
+systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
+
+test -d "$HOME"/.local/state/foo
+( ! test -L "$HOME"/.local/state/foo)
+test -d "$HOME"/.config/foo
+
+rmdir "$HOME"/.local/state/foo "$HOME"/.config/foo
+
+systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
+
+test -d "$HOME"/.local/state/foo
+( ! test -L "$HOME"/.local/state/foo)
+test -d "$HOME"/.config/foo
+
+rmdir "$HOME"/.local/state/foo "$HOME"/.config/foo
+
+# Now trigger an update scenario by creating a config dir first
+systemd-run --user -p ConfigurationDirectory=foo --wait /bin/true
+
+( ! test -d "$HOME"/.local/state/foo)
+test -d "$HOME"/.config/foo
+
+# This will look like an update and result in a symlink
+systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
+
+test -d "$HOME"/.local/state/foo
+test -L "$HOME"/.local/state/foo
+test -d "$HOME"/.config/foo
+
+test "$(readlink "$HOME"/.local/state/foo)" = ../../.config/foo
+
+# Check that this will work safely a second time
+systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
+
+rm "$HOME"/.local/state/foo
+rmdir "$HOME"/.config/foo
diff --git a/test/units/TEST-23-UNIT-FILE.success-failure.sh b/test/units/TEST-23-UNIT-FILE.success-failure.sh
new file mode 100755 (executable)
index 0000000..8fc9596
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test OnSuccess=/OnFailure= in combination
+
+systemd-analyze log-level debug
+
+# Start-up should fail, but the automatic restart should fix it
+(! systemctl start success-failure-test )
+
+# Wait until the first invocation finished & failed
+while test ! -f /tmp/success-failure-test-ran ; do
+    sleep .5
+done
+
+# Wait until the second invocation finished & succeeded
+while test ! -f /tmp/success-failure-test-ran2 ; do
+    sleep .5
+done
+
+# Verify it is indeed running
+systemctl is-active -q success-failure-test
+
+# The above should have caused the failure service to start (asynchronously)
+while test "$(systemctl is-active success-failure-test-failure)" != "active" ; do
+    sleep .5
+done
+
+# But the success service should not have started
+test "$(systemctl is-active success-failure-test-success)" = "inactive"
+
+systemctl stop success-failure-test-failure
+
+# Do a clean kill of the service now
+systemctl kill success-failure-test
+
+# This should result in the success service to start
+while test "$(systemctl is-active success-failure-test-success)" != "active" ; do
+    sleep .5
+done
+
+# But the failure service should not have started again
+test "$(systemctl is-active success-failure-test-failure)" = "inactive"
+
+systemctl stop success-failure-test success-failure-test-success
+
+systemd-analyze log-level info
diff --git a/test/units/TEST-23-UNIT-FILE.type-exec.sh b/test/units/TEST-23-UNIT-FILE.type-exec.sh
new file mode 100755 (executable)
index 0000000..87f32cc
--- /dev/null
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Test Type=exec
+
+systemd-analyze log-level debug
+
+# Create a binary for which execve() will fail
+touch /tmp/brokenbinary
+chmod +x /tmp/brokenbinary
+
+# These three commands should succeed.
+systemd-run --unit=exec-one -p Type=simple /bin/sleep infinity
+systemd-run --unit=exec-two -p Type=simple -p User=idontexist /bin/sleep infinity
+systemd-run --unit=exec-three -p Type=simple /tmp/brokenbinary
+
+# And now, do the same with Type=exec, where the latter two should fail
+systemd-run --unit=exec-four -p Type=exec /bin/sleep infinity
+(! systemd-run --unit=exec-five -p Type=exec -p User=idontexist /bin/sleep infinity)
+(! systemd-run --unit=exec-six -p Type=exec /tmp/brokenbinary)
+
+systemd-run --unit=exec-seven -p KillSignal=SIGTERM -p RestartKillSignal=SIGINT -p Type=exec /bin/sleep infinity
+# Both TERM and SIGINT happen to have the same number on all architectures
+test "$(systemctl show --value -p KillSignal exec-seven.service)" -eq 15
+test "$(systemctl show --value -p RestartKillSignal exec-seven.service)" -eq 2
+
+systemctl restart exec-seven.service
+systemctl stop exec-seven.service
+
+# For issue #20933
+
+# Should work normally
+busctl call \
+    org.freedesktop.systemd1 /org/freedesktop/systemd1 \
+    org.freedesktop.systemd1.Manager StartTransientUnit \
+    "ssa(sv)a(sa(sv))" test-20933-ok.service replace 1 \
+      ExecStart "a(sasb)" 1 \
+        /usr/bin/sleep 2 /usr/bin/sleep 1 true \
+    0
+
+# DBus call should fail but not crash systemd
+(! busctl call \
+    org.freedesktop.systemd1 /org/freedesktop/systemd1 \
+    org.freedesktop.systemd1.Manager StartTransientUnit \
+    "ssa(sv)a(sa(sv))" test-20933-bad.service replace 1 \
+      ExecStart "a(sasb)" 1 \
+        /usr/bin/sleep 0 true \
+    0)
+
+# Same but with the empty argv in the middle
+(! busctl call \
+    org.freedesktop.systemd1 /org/freedesktop/systemd1 \
+    org.freedesktop.systemd1.Manager StartTransientUnit \
+    "ssa(sv)a(sa(sv))" test-20933-bad-middle.service replace 1 \
+      ExecStart "a(sasb)" 3 \
+        /usr/bin/sleep 2 /usr/bin/sleep 1 true \
+        /usr/bin/sleep 0                  true \
+        /usr/bin/sleep 2 /usr/bin/sleep 1 true \
+    0)
+
+systemd-analyze log-level info
diff --git a/test/units/TEST-23-UNIT-FILE.utmp.sh b/test/units/TEST-23-UNIT-FILE.utmp.sh
new file mode 100755 (executable)
index 0000000..4f84315
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+USER="test-23-utmp"
+
+cleanup() {
+    userdel "$USER"
+}
+
+trap cleanup EXIT
+useradd "$USER"
+
+assert_eq "$(systemd-run -qP -p UtmpIdentifier=test -p UtmpMode=user -p User=$USER whoami)" "$USER"
+assert_eq "$(systemd-run -qP -p UtmpIdentifier=test -p UtmpMode=user whoami)" "$(whoami)"
diff --git a/test/units/TEST-23-UNIT-FILE.verify-unit-files.sh b/test/units/TEST-23-UNIT-FILE.verify-unit-files.sh
new file mode 100755 (executable)
index 0000000..3e83d44
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# Verify our own unit files (where applicable)
+
+# This is generated by meson during build
+UNIT_FILE_LIST="/usr/lib/systemd/tests/testdata/installed-unit-files.txt"
+
+if [[ ! -f "$UNIT_FILE_LIST" ]]; then
+    echo "Couldn't find the list of installed units, skipping the test"
+    exit 0
+fi
+
+if ! command -v systemd-analyze >/dev/null; then
+    echo "Built without systemd-analyze, skipping the test"
+    exit 0
+fi
+
+mapfile -t UNIT_FILES <"$UNIT_FILE_LIST"
+
+if [[ "${#UNIT_FILES[@]}" -le 0 ]]; then
+    echo >&2 "The unit file list is empty, this is most likely a bug"
+    exit 1
+fi
+
+for unit_file in "${UNIT_FILES[@]}"; do
+    # Skip the check for a couple of units, namely:
+    #   - syslog.socket: the corresponding syslog.service might not be installed
+    #   - rc-local.service: compat API, /etc/rc.d/rc.local most likely won't be present
+    if [[ "$unit_file" =~ /(syslog.socket|rc-local.service)$ ]]; then
+        continue
+    fi
+
+    # Skip missing unit files - this is useful for $NO_BUILD runs, where certain unit files might be dropped
+    # in distro packaging
+    if [[ ! -e "$unit_file" ]]; then
+        echo "$unit_file not found, skipping"
+        continue
+    fi
+
+    systemd-analyze --recursive-errors=no --man=no verify "$unit_file"
+done
diff --git a/test/units/TEST-23-UNIT-FILE.whoami.sh b/test/units/TEST-23-UNIT-FILE.whoami.sh
new file mode 100755 (executable)
index 0000000..b538d94
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+test "$(systemctl whoami)" = TEST-23-UNIT-FILE.service
+test "$(systemctl whoami $$)" = TEST-23-UNIT-FILE.service
+
+systemctl whoami 1 $$ 1 | cmp - /dev/fd/3 3<<'EOF'
+init.scope
+TEST-23-UNIT-FILE.service
+init.scope
+EOF
diff --git a/test/units/TEST-24-CRYPTSETUP.service b/test/units/TEST-24-CRYPTSETUP.service
new file mode 100644 (file)
index 0000000..e192d1c
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-24-CRYPTSETUP
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-24-CRYPTSETUP.sh b/test/units/TEST-24-CRYPTSETUP.sh
new file mode 100755 (executable)
index 0000000..0822b86
--- /dev/null
@@ -0,0 +1,273 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# TODO:
+#   - /proc/cmdline parsing
+#   - expect + interactive auth?
+
+# We set up an encrypted /var partition which should get mounted automatically
+# on boot
+mountpoint /var
+
+systemctl --state=failed --no-legend --no-pager | tee /failed
+if [[ -s /failed ]]; then
+    echo >&2 "Found units in failed state"
+    exit 1
+fi
+
+at_exit() {
+    set +e
+
+    mountpoint -q /proc/cmdline && umount /proc/cmdline
+    rm -f /etc/crypttab
+    [[ -e /tmp/crypttab.bak ]] && cp -fv /tmp/crypttab.bak /etc/crypttab
+    [[ -n "${STORE_LOOP:-}" ]] && losetup -d "$STORE_LOOP"
+    [[ -n "${WORKDIR:-}" ]] && rm -rf "$WORKDIR"
+
+    systemctl daemon-reload
+}
+
+trap at_exit EXIT
+
+cryptsetup_start_and_check() {
+    local expect_fail=0
+    local umount_header_and_key=0
+    local ec volume unit
+
+    if [[ "${1:?}" == "-f" ]]; then
+        expect_fail=1
+        shift
+    fi
+
+    if [[ "${1:?}" == "-u" ]]; then
+        umount_header_and_key=1
+        shift
+    fi
+
+    for volume in "$@"; do
+        unit="systemd-cryptsetup@$volume.service"
+
+        # The unit existence check should always pass
+        [[ "$(systemctl show -P LoadState "$unit")" == loaded ]]
+        systemctl list-unit-files "$unit"
+
+        systemctl start "$unit" && ec=0 || ec=$?
+        if [[ "$expect_fail" -ne 0 ]]; then
+            if [[ "$ec" -eq 0 ]]; then
+                echo >&2 "Unexpected pass when starting $unit"
+                return 1
+            fi
+
+            return 0
+        fi
+
+        if [[ "$ec" -ne 0 ]]; then
+            echo >&2 "Unexpected fail when starting $unit"
+            return 1
+        fi
+
+        if [[ "$umount_header_and_key" -ne 0 ]]; then
+            umount "$TMPFS_DETACHED_KEYFILE"
+            umount "$TMPFS_DETACHED_HEADER"
+            udevadm settle --timeout=30
+        fi
+
+        systemctl status "$unit"
+        test -e "/dev/mapper/$volume"
+        systemctl stop "$unit"
+        test ! -e "/dev/mapper/$volume"
+    done
+
+    return 0
+}
+
+# Note: some stuff (especially TPM-related) is already tested by TEST-70-TPM2,
+#       so focus more on other areas instead
+
+# Use a common workdir to make the cleanup easier
+WORKDIR="$(mktemp -d)"
+
+# Prepare a couple of LUKS2-encrypted disk images
+#
+# 1) Image with an empty password
+IMAGE_EMPTY="$WORKDIR/empty.img)"
+IMAGE_EMPTY_KEYFILE="$WORKDIR/empty.keyfile"
+IMAGE_EMPTY_KEYFILE_ERASE="$WORKDIR/empty-erase.keyfile"
+IMAGE_EMPTY_KEYFILE_ERASE_FAIL="$WORKDIR/empty-erase-fail.keyfile)"
+truncate -s 32M "$IMAGE_EMPTY"
+echo -n passphrase >"$IMAGE_EMPTY_KEYFILE"
+chmod 0600 "$IMAGE_EMPTY_KEYFILE"
+cryptsetup luksFormat --batch-mode \
+                      --pbkdf pbkdf2 \
+                      --pbkdf-force-iterations 1000 \
+                      --use-urandom \
+                      "$IMAGE_EMPTY" "$IMAGE_EMPTY_KEYFILE"
+PASSWORD=passphrase NEWPASSWORD="" systemd-cryptenroll --password "$IMAGE_EMPTY"
+# Duplicate the key file to test keyfile-erase as well
+cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE"
+# The key should get erased even on a failed attempt, so test that too
+cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
+
+# 2) Image with a detached header and a key file offset + size
+IMAGE_DETACHED="$WORKDIR/detached.img"
+IMAGE_DETACHED_KEYFILE="$WORKDIR/detached.keyfile"
+IMAGE_DETACHED_KEYFILE2="$WORKDIR/detached.keyfile2"
+IMAGE_DETACHED_HEADER="$WORKDIR/detached.header"
+truncate -s 32M "$IMAGE_DETACHED"
+dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE" count=64 bs=1
+dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE2" count=32 bs=1
+chmod 0600 "$IMAGE_DETACHED_KEYFILE" "$IMAGE_DETACHED_KEYFILE2"
+cryptsetup luksFormat --batch-mode \
+                      --pbkdf pbkdf2 \
+                      --pbkdf-force-iterations 1000 \
+                      --use-urandom \
+                      --header "$IMAGE_DETACHED_HEADER" \
+                      --keyfile-offset 32 \
+                      --keyfile-size 16 \
+                      "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE"
+# Also, add a second key file to key slot 8
+# Note: --key-slot= behaves as --new-key-slot= when used alone for backwards compatibility
+cryptsetup luksAddKey --batch-mode \
+                      --header "$IMAGE_DETACHED_HEADER" \
+                      --key-file "$IMAGE_DETACHED_KEYFILE" \
+                      --keyfile-offset 32 \
+                      --keyfile-size 16 \
+                      --key-slot 8 \
+                      "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE2"
+
+# Prepare a couple of dummy devices we'll store a copy of the detached header
+# and one of the keys on to test if systemd-cryptsetup correctly mounts them
+# when necessary
+STORE_IMAGE="$WORKDIR/store.img"
+truncate -s 64M "$STORE_IMAGE"
+STORE_LOOP="$(losetup --show --find --partscan "$STORE_IMAGE")"
+sfdisk "$STORE_LOOP" <<EOF
+label: gpt
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=header_store size=32M
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=keyfile_store
+EOF
+udevadm settle --timeout=30
+mkdir -p /mnt
+mkfs.ext4 -L header_store "/dev/disk/by-partlabel/header_store"
+mount "/dev/disk/by-partlabel/header_store" /mnt
+cp "$IMAGE_DETACHED_HEADER" /mnt/header
+umount /mnt
+mkfs.ext4 -L keyfile_store "/dev/disk/by-partlabel/keyfile_store"
+mount "/dev/disk/by-partlabel/keyfile_store" /mnt
+cp "$IMAGE_DETACHED_KEYFILE2" /mnt/keyfile
+umount /mnt
+
+# Also copy the key and header on a tmpfs that we will umount after unlocking
+TMPFS_DETACHED_KEYFILE="$(mktemp -d)"
+TMPFS_DETACHED_HEADER="$(mktemp -d)"
+mount -t tmpfs -o size=32M tmpfs "$TMPFS_DETACHED_KEYFILE"
+mount -t tmpfs -o size=32M tmpfs "$TMPFS_DETACHED_HEADER"
+cp "$IMAGE_DETACHED_KEYFILE" "$TMPFS_DETACHED_KEYFILE/keyfile"
+cp "$IMAGE_DETACHED_HEADER" "$TMPFS_DETACHED_HEADER/header"
+
+udevadm settle --timeout=30
+
+# Prepare our test crypttab
+[[ -e /etc/crypttab ]] && cp -fv /etc/crypttab /tmp/crypttab.bak
+cat >/etc/crypttab <<EOF
+# headless should translate to headless=1
+empty_key            $IMAGE_EMPTY    $IMAGE_EMPTY_KEYFILE            headless,x-systemd.device-timeout=1m
+empty_key_erase      $IMAGE_EMPTY    $IMAGE_EMPTY_KEYFILE_ERASE      headless=1,keyfile-erase=1
+empty_key_erase_fail $IMAGE_EMPTY    $IMAGE_EMPTY_KEYFILE_ERASE_FAIL headless=1,keyfile-erase=1,keyfile-offset=4
+# Empty passphrase without try-empty-password(=yes) shouldn't work
+empty_fail0          $IMAGE_EMPTY    -                               headless=1
+empty_fail1          $IMAGE_EMPTY    -                               headless=1,try-empty-password=0
+empty0               $IMAGE_EMPTY    -                               headless=1,try-empty-password
+empty1               $IMAGE_EMPTY    -                               headless=1,try-empty-password=1
+# This one expects the key to be under /{etc,run}/cryptsetup-keys.d/empty_nokey.key
+empty_nokey          $IMAGE_EMPTY    -                               headless=1
+empty_pkcs11_auto    $IMAGE_EMPTY    -                               headless=1,pkcs11-uri=auto
+
+detached             $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32,keyfile-size=16
+detached_store0      $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=/header:LABEL=header_store,keyfile-offset=32,keyfile-size=16
+detached_store1      $IMAGE_DETACHED /keyfile:LABEL=keyfile_store    headless=1,header=$IMAGE_DETACHED_HEADER
+detached_store2      $IMAGE_DETACHED /keyfile:LABEL=keyfile_store    headless=1,header=/header:LABEL=header_store
+detached_fail0       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32
+detached_fail1       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=$IMAGE_DETACHED_HEADER
+detached_fail2       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1
+detached_fail3       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=16,keyfile-size=16
+detached_fail4       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32,keyfile-size=8
+detached_slot0       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2        headless=1,header=$IMAGE_DETACHED_HEADER
+detached_slot1       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2        headless=1,header=$IMAGE_DETACHED_HEADER,key-slot=8
+detached_slot_fail   $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2        headless=1,header=$IMAGE_DETACHED_HEADER,key-slot=0
+detached_nofail      $IMAGE_DETACHED $TMPFS_DETACHED_KEYFILE/keyfile headless=1,header=$TMPFS_DETACHED_HEADER/header,keyfile-offset=32,keyfile-size=16,nofail
+EOF
+
+# Temporarily drop luks.name=/luks.uuid= from the kernel command line, as it makes
+# systemd-cryptsetup-generator ignore mounts from /etc/crypttab that are not also
+# specified on the kernel command line
+sed -r 's/luks.(name|uuid)=[^[:space:]+]//' /proc/cmdline >/tmp/cmdline.tmp
+mount --bind /tmp/cmdline.tmp /proc/cmdline
+# Run the systemd-cryptsetup-generator once explicitly, to collect coverage,
+# as during daemon-reload we run generators in a sandbox
+mkdir -p /tmp/systemd-cryptsetup-generator.out
+/usr/lib/systemd/system-generators/systemd-cryptsetup-generator /tmp/systemd-cryptsetup-generator.out/
+systemctl daemon-reload
+systemctl list-unit-files "systemd-cryptsetup@*"
+
+cryptsetup_start_and_check empty_key
+test -e "$IMAGE_EMPTY_KEYFILE_ERASE"
+cryptsetup_start_and_check empty_key_erase
+test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE"
+test -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
+cryptsetup_start_and_check -f empty_key_erase_fail
+test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
+cryptsetup_start_and_check -f empty_fail{0..1}
+cryptsetup_start_and_check empty{0..1}
+# First, check if we correctly fail without any key
+cryptsetup_start_and_check -f empty_nokey
+# And now provide the key via /{etc,run}/cryptsetup-keys.d/
+mkdir -p /run/cryptsetup-keys.d
+cp "$IMAGE_EMPTY_KEYFILE" /run/cryptsetup-keys.d/empty_nokey.key
+cryptsetup_start_and_check empty_nokey
+
+if [[ -r /etc/softhsm2.conf ]]; then
+    # Test unlocking with a PKCS#11 token
+    export SOFTHSM2_CONF="/etc/softhsm2.conf"
+
+    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+    cryptsetup_start_and_check empty_pkcs11_auto
+    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+
+    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey;type=cert" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+    cryptsetup_start_and_check empty_pkcs11_auto
+    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+
+    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey;type=public" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+    cryptsetup_start_and_check empty_pkcs11_auto
+    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+
+    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+    cryptsetup_start_and_check empty_pkcs11_auto
+    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+
+    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey;type=cert" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+    cryptsetup_start_and_check empty_pkcs11_auto
+    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+
+    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey;type=public" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+    cryptsetup_start_and_check empty_pkcs11_auto
+    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+fi
+
+cryptsetup_start_and_check detached
+cryptsetup_start_and_check detached_store{0..2}
+cryptsetup_start_and_check -f detached_fail{0..4}
+cryptsetup_start_and_check detached_slot{0..1}
+cryptsetup_start_and_check -f detached_slot_fail
+cryptsetup_start_and_check -u detached_nofail
+
+touch /testok
diff --git a/test/units/TEST-25-IMPORT.service b/test/units/TEST-25-IMPORT.service
new file mode 100644 (file)
index 0000000..503eabb
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-25-IMPORT
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-25-IMPORT.sh b/test/units/TEST-25-IMPORT.sh
new file mode 100755 (executable)
index 0000000..b298c50
--- /dev/null
@@ -0,0 +1,143 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+export SYSTEMD_PAGER=cat
+
+dd if=/dev/urandom of=/var/tmp/testimage.raw bs=$((1024*1024+7)) count=5
+
+# Test import
+machinectl import-raw /var/tmp/testimage.raw
+machinectl image-status testimage
+test -f /var/lib/machines/testimage.raw
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
+
+# Test export
+machinectl export-raw testimage /var/tmp/testimage2.raw
+cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
+rm /var/tmp/testimage2.raw
+
+# Test compressed export (gzip)
+machinectl export-raw testimage /var/tmp/testimage2.raw.gz
+gunzip /var/tmp/testimage2.raw.gz
+cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
+rm /var/tmp/testimage2.raw
+
+# Test clone
+machinectl clone testimage testimage3
+test -f /var/lib/machines/testimage3.raw
+machinectl image-status testimage3
+test -f /var/lib/machines/testimage.raw
+machinectl image-status testimage
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage3.raw
+
+# Test removal
+machinectl remove testimage
+test ! -f /var/lib/machines/testimage.raw
+(! machinectl image-status testimage)
+
+# Test export of clone
+machinectl export-raw testimage3 /var/tmp/testimage3.raw
+cmp /var/tmp/testimage.raw /var/tmp/testimage3.raw
+rm /var/tmp/testimage3.raw
+
+# Test rename
+machinectl rename testimage3 testimage4
+test -f /var/lib/machines/testimage4.raw
+machinectl image-status testimage4
+test ! -f /var/lib/machines/testimage3.raw
+(! machinectl image-status testimage3)
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage4.raw
+
+# Test export of rename
+machinectl export-raw testimage4 /var/tmp/testimage4.raw
+cmp /var/tmp/testimage.raw /var/tmp/testimage4.raw
+rm /var/tmp/testimage4.raw
+
+# Test removal
+machinectl remove testimage4
+test ! -f /var/lib/machines/testimage4.raw
+(! machinectl image-status testimage4)
+
+# → And now, let's test directory trees ← #
+
+# Set up a directory we can import
+mkdir /var/tmp/scratch
+mv /var/tmp/testimage.raw /var/tmp/scratch/
+touch /var/tmp/scratch/anotherfile
+mkdir /var/tmp/scratch/adirectory
+echo "piep" >/var/tmp/scratch/adirectory/athirdfile
+
+# Test import-fs
+machinectl import-fs /var/tmp/scratch/
+test -d /var/lib/machines/scratch
+machinectl image-status scratch
+
+# Test export-tar
+machinectl export-tar scratch /var/tmp/scratch.tar.gz
+test -f /var/tmp/scratch.tar.gz
+mkdir /var/tmp/extract
+(cd /var/tmp/extract ; tar xzf /var/tmp/scratch.tar.gz)
+diff -r /var/tmp/scratch/ /var/tmp/extract/
+rm -rf /var/tmp/extract
+
+# Test import-tar
+machinectl import-tar /var/tmp/scratch.tar.gz scratch2
+test -d /var/lib/machines/scratch2
+machinectl image-status scratch2
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch2
+
+# Test removal
+machinectl remove scratch
+test ! -f /var/lib/machines/scratch
+(! machinectl image-status scratch)
+
+# Test clone
+machinectl clone scratch2 scratch3
+test -d /var/lib/machines/scratch2
+machinectl image-status scratch2
+test -d /var/lib/machines/scratch3
+machinectl image-status scratch3
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch3
+
+# Test removal
+machinectl remove scratch2
+test ! -f /var/lib/machines/scratch2
+(! machinectl image-status scratch2)
+
+# Test rename
+machinectl rename scratch3 scratch4
+test -d /var/lib/machines/scratch4
+machinectl image-status scratch4
+test ! -f /var/lib/machines/scratch3
+(! machinectl image-status scratch3)
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch4
+
+# Test removal
+machinectl remove scratch4
+test ! -f /var/lib/machines/scratch4
+(! machinectl image-status scratch4)
+
+# Test import-tar hyphen/stdin pipe behavior
+# shellcheck disable=SC2002
+cat /var/tmp/scratch.tar.gz | machinectl import-tar - scratch5
+test -d /var/lib/machines/scratch5
+machinectl image-status scratch5
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch5
+
+# Test export-tar hyphen/stdout pipe behavior
+mkdir -p /var/tmp/extract
+machinectl export-tar scratch5 - | tar xvf - -C /var/tmp/extract/
+diff -r /var/tmp/scratch/ /var/tmp/extract/
+rm -rf /var/tmp/extract
+
+rm -rf /var/tmp/scratch
+
+# Test removal
+machinectl remove scratch5
+test ! -f /var/lib/machines/scratch5
+(! machinectl image-status scratch5)
+
+touch /testok
diff --git a/test/units/TEST-26-SYSTEMCTL.service b/test/units/TEST-26-SYSTEMCTL.service
new file mode 100644 (file)
index 0000000..d8fdaff
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-26-SYSTEMCTL
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-26-SYSTEMCTL.sh b/test/units/TEST-26-SYSTEMCTL.sh
new file mode 100755 (executable)
index 0000000..117ffb8
--- /dev/null
@@ -0,0 +1,501 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+    if [[ -v UNIT_NAME && -e "/usr/lib/systemd/system/$UNIT_NAME" ]]; then
+        rm -fvr "/usr/lib/systemd/system/$UNIT_NAME" "/etc/systemd/system/$UNIT_NAME.d" "+4"
+    fi
+
+    rm -f /etc/init.d/issue-24990
+    return 0
+}
+
+# Create a simple unit file for testing
+# Note: the service file is created under /usr on purpose to test
+#       the 'revert' verb as well
+export UNIT_NAME="systemctl-test-$RANDOM.service"
+export UNIT_NAME2="systemctl-test-$RANDOM.service"
+
+cat >"/usr/lib/systemd/system/$UNIT_NAME" <<\EOF
+[Unit]
+Description=systemctl test
+
+[Service]
+ExecStart=sleep infinity
+ExecReload=true
+
+# For systemctl clean
+CacheDirectory=%n
+ConfigurationDirectory=%n
+LogsDirectory=%n
+RuntimeDirectory=%n
+StateDirectory=%n
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+# Configure the preset setting for the unit file
+mkdir /run/systemd/system-preset/
+echo "disable $UNIT_NAME" >/run/systemd/system-preset/99-systemd-test.preset
+
+EDITOR='true' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
+[ ! -e "/etc/systemd/system/$UNIT_NAME.d/override.conf" ]
+
+printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' >"+4"
+EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
+printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf"
+
+printf '%b'   '[Service]\n' 'ExecStart=\n' 'ExecStart=sleep 10d' >"+4"
+EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
+printf '%s\n' '[Service]'   'ExecStart='   'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf"
+
+systemctl edit "$UNIT_NAME" --stdin --drop-in=override2.conf <<EOF
+[Unit]
+Description=spectacular
+# this comment should remain
+
+EOF
+printf '%s\n' '[Unit]'   'Description=spectacular' '# this comment should remain' | \
+    cmp - "/etc/systemd/system/$UNIT_NAME.d/override2.conf"
+
+# Test simultaneous editing of two units and creation of drop-in for a nonexistent unit
+systemctl edit "$UNIT_NAME" "$UNIT_NAME2" --stdin --force --drop-in=override2.conf <<<'[X-Section]'
+printf '%s\n' '[X-Section]' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override2.conf"
+printf '%s\n' '[X-Section]' | cmp - "/etc/systemd/system/$UNIT_NAME2.d/override2.conf"
+
+# Double free when editing a template unit (#26483)
+EDITOR='true' script -ec 'systemctl edit user@0' /dev/null
+
+# Argument help
+systemctl --state help
+systemctl --signal help
+systemctl --type help
+
+# list-dependencies
+systemctl list-dependencies systemd-journald
+systemctl list-dependencies --after systemd-journald
+systemctl list-dependencies --before systemd-journald
+systemctl list-dependencies --after --reverse systemd-journald
+systemctl list-dependencies --before --reverse systemd-journald
+systemctl list-dependencies --plain systemd-journald
+
+# list-* verbs
+systemctl list-units
+systemctl list-units --recursive
+systemctl list-units --type=socket
+systemctl list-units --type=service,timer
+# Compat: --type= allows load states for compatibility reasons
+systemctl list-units --type=loaded
+systemctl list-units --type=loaded,socket
+systemctl list-units --legend=yes -a "systemd-*"
+systemctl list-units --state=active
+systemctl list-units --with-dependencies systemd-journald.service
+systemctl list-units --with-dependencies --after systemd-journald.service
+systemctl list-units --with-dependencies --before --reverse systemd-journald.service
+systemctl list-sockets
+systemctl list-sockets --legend=no -a "*journal*"
+systemctl list-sockets --show-types
+systemctl list-sockets --state=listening
+systemctl list-timers -a -l
+systemctl list-jobs
+systemctl list-jobs --after
+systemctl list-jobs --before
+systemctl list-jobs --after --before
+systemctl list-jobs "*"
+systemctl list-dependencies sysinit.target --type=socket,mount
+systemctl list-dependencies multi-user.target --state=active
+systemctl list-dependencies sysinit.target --state=mounted --all
+systemctl list-paths
+systemctl list-paths --legend=no -a "systemd*"
+
+test_list_unit_files() {
+    systemctl list-unit-files "$@"
+    systemctl list-unit-files "$@" "*journal*"
+}
+
+test_list_unit_files
+test_list_unit_files --root=/
+
+# is-* verbs
+# Should return 4 for a missing unit file
+assert_rc 4 systemctl --quiet is-active not-found.service
+assert_rc 4 systemctl --quiet is-failed not-found.service
+assert_rc 4 systemctl --quiet is-enabled not-found.service
+# is-active: return 3 when the unit exists but inactive
+assert_rc 3 systemctl --quiet is-active "$UNIT_NAME"
+# is-enabled: return 1 when the unit exists but disabled
+assert_rc 1 systemctl --quiet is-enabled "$UNIT_NAME"
+
+# Basic service management
+systemctl start --show-transaction "$UNIT_NAME"
+systemctl status -n 5 "$UNIT_NAME"
+systemctl is-active "$UNIT_NAME"
+systemctl reload -T "$UNIT_NAME"
+systemctl restart -T "$UNIT_NAME"
+systemctl try-restart --show-transaction "$UNIT_NAME"
+systemctl try-reload-or-restart --show-transaction "$UNIT_NAME"
+timeout 10 systemctl kill --wait "$UNIT_NAME"
+(! systemctl is-active "$UNIT_NAME")
+systemctl restart "$UNIT_NAME"
+systemctl is-active "$UNIT_NAME"
+systemctl restart "$UNIT_NAME"
+systemctl stop "$UNIT_NAME"
+(! systemctl is-active "$UNIT_NAME")
+
+assert_eq "$(systemctl is-system-running)" "$(systemctl is-failed)"
+
+# enable/disable/preset
+test_enable_disable_preset() {
+    (! systemctl is-enabled "$@" "$UNIT_NAME")
+    systemctl enable "$@" "$UNIT_NAME"
+    systemctl is-enabled "$@" -l "$UNIT_NAME"
+    # We created a preset file for this unit above with a "disable" policy
+    systemctl preset "$@" "$UNIT_NAME"
+    (! systemctl is-enabled "$@" "$UNIT_NAME")
+    systemctl reenable "$@" "$UNIT_NAME"
+    systemctl is-enabled "$@" "$UNIT_NAME"
+    systemctl preset "$@" --preset-mode=enable-only "$UNIT_NAME"
+    systemctl is-enabled "$@" "$UNIT_NAME"
+    systemctl preset "$@" --preset-mode=disable-only "$UNIT_NAME"
+    (! systemctl is-enabled "$@" "$UNIT_NAME")
+    systemctl enable "$@" --runtime "$UNIT_NAME"
+    [[ -e "/run/systemd/system/multi-user.target.wants/$UNIT_NAME" ]]
+    systemctl is-enabled "$@" "$UNIT_NAME"
+    systemctl disable "$@" "$UNIT_NAME"
+    # The unit should be still enabled, as we didn't use the --runtime switch
+    systemctl is-enabled "$@" "$UNIT_NAME"
+    systemctl disable "$@" --runtime "$UNIT_NAME"
+    (! systemctl is-enabled "$@" "$UNIT_NAME")
+}
+
+test_enable_disable_preset
+test_enable_disable_preset --root=/
+
+# mask/unmask/revert
+test_mask_unmask_revert() {
+    systemctl disable "$@" "$UNIT_NAME"
+    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
+    systemctl mask "$@" "$UNIT_NAME"
+    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]]
+    systemctl unmask "$@" "$UNIT_NAME"
+    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
+    systemctl mask "$@" "$UNIT_NAME"
+    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]]
+    systemctl revert "$@" "$UNIT_NAME"
+    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
+    systemctl mask "$@" --runtime "$UNIT_NAME"
+    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]]
+    # This should be a no-op without the --runtime switch
+    systemctl unmask "$@" "$UNIT_NAME"
+    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]]
+    systemctl unmask "$@" --runtime "$UNIT_NAME"
+    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
+}
+
+test_mask_unmask_revert
+test_mask_unmask_revert --root=/
+
+# disable --now with template unit
+cat >/run/systemd/system/test-disable@.service <<EOF
+[Service]
+ExecStart=sleep infinity
+
+[Install]
+WantedBy=multi-user.target
+EOF
+systemctl enable --now test-disable@1.service test-disable@2.service
+systemctl is-active test-disable@1.service
+systemctl is-active test-disable@2.service
+systemctl disable --now test-disable@.service
+for u in test-disable@{1,2}.service; do
+    (! systemctl is-active "$u")
+    (! systemctl is-enabled "$u")
+done
+rm /run/systemd/system/test-disable@.service
+
+# add-wants/add-requires
+(! systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service")
+systemctl add-wants "$UNIT_NAME" "systemd-journald.service"
+systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service"
+(! systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service")
+systemctl add-requires "$UNIT_NAME" "systemd-journald.service"
+systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service"
+
+# set-property
+systemctl set-property "$UNIT_NAME" IPAccounting=yes MemoryMax=1234567
+systemctl cat "$UNIT_NAME"
+# These properties should be saved to a persistent storage
+grep -r "IPAccounting=yes" "/etc/systemd/system.control/${UNIT_NAME}.d/"
+grep -r "MemoryMax=1234567" "/etc/systemd/system.control/${UNIT_NAME}.d"
+systemctl revert "$UNIT_NAME"
+(! grep -r "IPAccounting=" "/etc/systemd/system.control/${UNIT_NAME}.d/")
+(! grep -r "MemoryMax=" "/etc/systemd/system.control/${UNIT_NAME}.d/")
+# Same stuff, but with --runtime, which should use /run
+systemctl set-property --runtime "$UNIT_NAME" CPUAccounting=no CPUQuota=10%
+systemctl cat "$UNIT_NAME"
+grep -r "CPUAccounting=no" "/run/systemd/system.control/${UNIT_NAME}.d/"
+grep -r "CPUQuota=10%" "/run/systemd/system.control/${UNIT_NAME}.d/"
+systemctl revert "$UNIT_NAME"
+(! grep -r "CPUAccounting=" "/run/systemd/system.control/${UNIT_NAME}.d/")
+(! grep -r "CPUQuota=" "/run/systemd/system.control/${UNIT_NAME}.d/")
+
+# Failed-unit related tests
+(! systemd-run --wait --unit "failed.service" /bin/false)
+systemctl is-failed failed.service
+systemctl --state=failed | grep failed.service
+systemctl --failed | grep failed.service
+systemctl reset-failed "fail*.service"
+(! systemctl is-failed failed.service)
+
+# clean
+systemctl restart "$UNIT_NAME"
+systemctl stop "$UNIT_NAME"
+# Check if the directories from *Directory= directives exist
+# (except RuntimeDirectory= in /run, which is removed when the unit is stopped)
+for path in /var/lib /var/cache /var/log /etc; do
+    [[ -e "$path/$UNIT_NAME" ]]
+done
+# Run the cleanup
+for what in "" configuration state cache logs runtime all; do
+    systemctl clean ${what:+--what="$what"} "$UNIT_NAME"
+done
+# All respective directories should be removed
+for path in /run /var/lib /var/cache /var/log /etc; do
+    [[ ! -e "$path/$UNIT_NAME" ]]
+done
+
+# --timestamp
+for value in pretty us µs utc us+utc µs+utc; do
+    systemctl show -P KernelTimestamp --timestamp="$value"
+done
+
+# set-default/get-default
+test_get_set_default() {
+    target="$(systemctl get-default "$@")"
+    systemctl set-default "$@" emergency.target
+    [[ "$(systemctl get-default "$@")" == emergency.target ]]
+    systemctl set-default "$@" "$target"
+    [[ "$(systemctl get-default "$@")" == "$target" ]]
+}
+
+test_get_set_default
+test_get_set_default --root=/
+
+# show/status
+systemctl show --property ""
+# Pick a heavily sandboxed unit for the best effect on coverage
+systemctl show systemd-logind.service
+systemctl status
+# Ignore the exit code in this case, as it might try to load non-existing units
+systemctl status -a >/dev/null || :
+systemctl status -a --state active,running,plugged >/dev/null
+systemctl status "systemd-*.timer"
+systemctl status "systemd-journald*.socket"
+systemctl status "sys-devices-*-ttyS0.device"
+systemctl status -- -.mount
+systemctl status 1
+
+# --marked
+systemctl restart "$UNIT_NAME"
+systemctl set-property "$UNIT_NAME" Markers=needs-restart
+systemctl show -P Markers "$UNIT_NAME" | grep needs-restart
+systemctl reload-or-restart --marked
+(! systemctl show -P Markers "$UNIT_NAME" | grep needs-restart)
+
+# --dry-run with destructive verbs
+# kexec is skipped intentionally, as it requires a bit more involved setup
+VERBS=(
+    default
+    emergency
+    exit
+    halt
+    hibernate
+    hybrid-sleep
+    poweroff
+    reboot
+    rescue
+    suspend
+    suspend-then-hibernate
+)
+
+for verb in "${VERBS[@]}"; do
+    systemctl --dry-run "$verb"
+
+    if [[ "$verb" =~ (halt|poweroff|reboot) ]]; then
+        systemctl --dry-run --message "Hello world" "$verb"
+        systemctl --dry-run --no-wall "$verb"
+        systemctl --dry-run -f "$verb"
+        systemctl --dry-run -ff "$verb"
+    fi
+done
+
+# Aux verbs & assorted checks
+systemctl is-active "*-journald.service"
+systemctl cat "*udevd*"
+systemctl cat "$UNIT_NAME"
+systemctl help "$UNIT_NAME"
+systemctl service-watchdogs
+systemctl service-watchdogs "$(systemctl service-watchdogs)"
+
+# show/set-environment
+# Make sure PATH is set
+systemctl show-environment | grep -q '^PATH='
+# Let's add an entry and override a built-in one
+systemctl set-environment PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/testaddition FOO=BAR
+# Check that both are set
+systemctl show-environment | grep -q '^PATH=.*testaddition$'
+systemctl show-environment | grep -q '^FOO=BAR$'
+systemctl daemon-reload
+# Check again after the reload
+systemctl show-environment | grep -q '^PATH=.*testaddition$'
+systemctl show-environment | grep -q '^FOO=BAR$'
+# Check that JSON output is supported
+systemctl show-environment --output=json | grep -q '^{.*"FOO":"BAR".*}$'
+# Drop both
+systemctl unset-environment FOO PATH
+# Check that one is gone and the other reverted to the built-in
+systemctl show-environment | grep '^FOO=$' && exit 1
+systemctl show-environment | grep '^PATH=.*testaddition$' && exit 1
+systemctl show-environment | grep -q '^PATH='
+# Check import-environment
+export IMPORT_THIS=hello
+export IMPORT_THIS_TOO=world
+systemctl import-environment IMPORT_THIS IMPORT_THIS_TOO
+systemctl show-environment | grep "^IMPORT_THIS=$IMPORT_THIS"
+systemctl show-environment | grep "^IMPORT_THIS_TOO=$IMPORT_THIS_TOO"
+systemctl unset-environment IMPORT_THIS IMPORT_THIS_TOO
+(! systemctl show-environment | grep "^IMPORT_THIS=")
+(! systemctl show-environment | grep "^IMPORT_THIS_TOO=")
+
+# test for sysv-generator (issue #24990)
+if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then
+    # This is configurable via -Dsysvinit-path=, but we can't get the value
+    # at runtime, so let's just support the two most common paths for now.
+    [[ -d /etc/rc.d/init.d ]] && SYSVINIT_PATH="/etc/rc.d/init.d" || SYSVINIT_PATH="/etc/init.d"
+
+    # OpenSUSE leaves sysvinit-path enabled, which means systemd-sysv-generator is built
+    # but may not create the directory if there's no services that use it.
+    mkdir -p "$SYSVINIT_PATH"
+
+    # invalid dependency
+    cat >"${SYSVINIT_PATH:?}/issue-24990" <<\EOF
+#!/bin/bash
+
+### BEGIN INIT INFO
+# Provides:test1 test2
+# Required-Start:test1 $remote_fs $network
+# Required-Stop:test1 $remote_fs $network
+# Description:Test
+# Short-Description: Test
+### END INIT INFO
+
+case "$1" in
+    start)
+        echo "Starting issue-24990.service"
+        sleep 1000 &
+        ;;
+    stop)
+        echo "Stopping issue-24990.service"
+        sleep 10 &
+        ;;
+    *)
+        echo "Usage: service test {start|stop|restart|status}"
+        ;;
+esac
+EOF
+
+    chmod +x "$SYSVINIT_PATH/issue-24990"
+    systemctl daemon-reload
+    [[ -L /run/systemd/generator.late/test1.service ]]
+    [[ -L /run/systemd/generator.late/test2.service ]]
+    assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
+    assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
+    output=$(systemctl cat issue-24990)
+    assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
+    assert_in "Description=LSB: Test" "$output"
+    assert_in "After=test1.service" "$output"
+    assert_in "After=remote-fs.target" "$output"
+    assert_in "After=network-online.target" "$output"
+    assert_in "Wants=network-online.target" "$output"
+    assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
+    assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
+    systemctl status issue-24990 || :
+    systemctl show issue-24990
+    assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
+    assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
+
+    if ! systemctl is-active network-online.target; then
+        systemctl start network-online.target
+    fi
+
+    systemctl restart issue-24990
+    systemctl stop issue-24990
+
+    # valid dependency
+    cat >"$SYSVINIT_PATH/issue-24990" <<\EOF
+#!/bin/bash
+
+### BEGIN INIT INFO
+# Provides:test1 test2
+# Required-Start:$remote_fs
+# Required-Stop:$remote_fs
+# Description:Test
+# Short-Description: Test
+### END INIT INFO
+
+case "$1" in
+    start)
+        echo "Starting issue-24990.service"
+        sleep 1000 &
+        ;;
+    stop)
+        echo "Stopping issue-24990.service"
+        sleep 10 &
+        ;;
+    *)
+        echo "Usage: service test {start|stop|restart|status}"
+        ;;
+esac
+EOF
+
+    chmod +x "$SYSVINIT_PATH/issue-24990"
+    systemctl daemon-reload
+    [[ -L /run/systemd/generator.late/test1.service ]]
+    [[ -L /run/systemd/generator.late/test2.service ]]
+    assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
+    assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
+    output=$(systemctl cat issue-24990)
+    assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
+    assert_in "Description=LSB: Test" "$output"
+    assert_in "After=remote-fs.target" "$output"
+    assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
+    assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
+    systemctl status issue-24990 || :
+    systemctl show issue-24990
+    assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
+    assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
+
+    systemctl restart issue-24990
+    systemctl stop issue-24990
+fi
+
+# %J in WantedBy= causes ABRT (#26467)
+cat >/run/systemd/system/test-WantedBy.service <<EOF
+[Service]
+ExecStart=true
+
+[Install]
+WantedBy=user-%i@%J.service
+EOF
+systemctl daemon-reload
+systemctl enable --now test-WantedBy.service || :
+systemctl daemon-reload
+
+touch /testok
diff --git a/test/units/TEST-29-PORTABLE.service b/test/units/TEST-29-PORTABLE.service
new file mode 100644 (file)
index 0000000..035c6bf
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-29-PORTABLE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-29-PORTABLE.sh b/test/units/TEST-29-PORTABLE.sh
new file mode 100755 (executable)
index 0000000..27c24a0
--- /dev/null
@@ -0,0 +1,371 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+install_extension_images
+
+# Set longer timeout for slower machines, e.g. non-KVM vm.
+mkdir -p /run/systemd/system.conf.d
+cat >/run/systemd/system.conf.d/10-timeout.conf <<EOF
+[Manager]
+DefaultEnvironment=SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
+ManagerEnvironment=SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
+EOF
+
+systemctl daemon-reexec
+
+export SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
+
+udevadm control --log-level debug
+
+ARGS=()
+STATE_DIRECTORY=/var/lib/private/
+if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
+    # If we're running under sanitizers, we need to use a less restrictive
+    # profile, otherwise LSan syscall would get blocked by seccomp
+    ARGS+=(--profile=trusted)
+    # With the trusted profile DynamicUser is disabled, so the storage is not in private/
+    STATE_DIRECTORY=/var/lib/
+fi
+
+systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service'
+systemd-dissect --no-pager /usr/share/minimal_1.raw | grep -q '✓ portable service'
+systemd-dissect --no-pager /tmp/app0.raw | grep -q '✓ sysext for portable service'
+systemd-dissect --no-pager /tmp/app1.raw | grep -q '✓ sysext for portable service'
+systemd-dissect --no-pager /tmp/conf0.raw | grep -q '✓ confext for portable service'
+
+export SYSTEMD_LOG_LEVEL=debug
+mkdir -p /run/systemd/system/systemd-portabled.service.d/
+cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf
+[Service]
+Environment=SYSTEMD_LOG_LEVEL=debug
+EOF
+
+portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0
+
+portablectl is-attached minimal-app0
+portablectl inspect /usr/share/minimal_0.raw minimal-app0.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-foo.service
+systemctl is-active minimal-app0-bar.service && exit 1
+
+portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0
+
+portablectl is-attached minimal-app0
+portablectl inspect /usr/share/minimal_0.raw minimal-app0.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-bar.service
+systemctl is-active minimal-app0-foo.service && exit 1
+
+portablectl list | grep -q -F "minimal_1"
+busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
+
+portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0
+
+portablectl list | grep -q -F "No images."
+busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
+
+# Ensure we don't regress (again) when using --force
+
+portablectl "${ARGS[@]}" attach --force --now --runtime /usr/share/minimal_0.raw minimal-app0
+
+portablectl is-attached --force minimal-app0
+portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-foo.service
+systemctl is-active minimal-app0-bar.service && exit 1
+
+portablectl "${ARGS[@]}" reattach --force --now --runtime /usr/share/minimal_1.raw minimal-app0
+
+portablectl is-attached --force minimal-app0
+portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-bar.service
+systemctl is-active minimal-app0-foo.service && exit 1
+
+portablectl list | grep -q -F "minimal_1"
+busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
+
+portablectl detach --force --now --runtime /usr/share/minimal_1.raw minimal-app0
+
+portablectl list | grep -q -F "No images."
+busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
+
+# portablectl also works with directory paths rather than images
+
+unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw
+unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw
+
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0
+
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-foo.service
+systemctl is-active minimal-app0-bar.service && exit 1
+
+portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0
+
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-bar.service
+systemctl is-active minimal-app0-foo.service && exit 1
+
+portablectl list | grep -q -F "minimal_1"
+busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
+
+portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0
+
+portablectl list | grep -q -F "No images."
+busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
+
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
+
+systemctl is-active app0.service
+status="$(portablectl is-attached --extension app0 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
+
+grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
+
+portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0
+
+systemctl is-active app0.service
+status="$(portablectl is-attached --extension app0 minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_1.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
+
+portablectl detach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0
+
+# Ensure versioned images are accepted without needing to use --force to override the extension-release
+# matching
+
+cp /tmp/app0.raw /tmp/app0_1.0.raw
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_0.raw app0
+
+systemctl is-active app0.service
+status="$(portablectl is-attached --extension app0_1 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl detach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_1.raw app0
+rm -f /tmp/app0_1.0.raw
+
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
+
+# Ensure that adding or removing a version to the image doesn't break reattaching
+cp /tmp/app1.raw /tmp/app1_2.raw
+portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1_2.raw /usr/share/minimal_1.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1_2 minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1 minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl detach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1
+portablectl "${ARGS[@]}" attach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
+systemctl daemon-reload
+systemctl restart app1.service
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl detach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
+
+# Ensure vpick works, including reattaching to a new image
+mkdir -p /tmp/app1.v/
+cp /tmp/app1.raw /tmp/app1.v/app1_1.0.raw
+cp /tmp/app1_2.raw /tmp/app1.v/app1_2.0.raw
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1_2.0.raw minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+rm -f /tmp/app1.v/app1_2.0.raw
+portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1_1.0.raw minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl detach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_0.raw app1
+rm -f /tmp/app1.v/app1_1.0.raw
+
+# Ensure that the combination of read-only images, state directory and dynamic user works, and that
+# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while
+# after the service is attached before the file appears.
+grep -q -F bar "${STATE_DIRECTORY}/app0/foo"
+grep -q -F baz "${STATE_DIRECTORY}/app1/foo"
+
+# Ensure that we can override the check on extension-release.NAME
+cp /tmp/app0.raw /tmp/app10.raw
+portablectl "${ARGS[@]}" attach --force --now --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0
+
+systemctl is-active app0.service
+status="$(portablectl is-attached --extension /tmp/app10.raw /usr/share/minimal_0.raw)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/app10.raw"
+
+# Ensure that we can detach even when an image has been deleted already (stop the unit manually as
+# portablectl won't find it)
+rm -f /tmp/app10.raw
+systemctl stop app0.service
+portablectl detach --force --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0
+
+# portablectl also accepts confexts
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0
+
+systemctl is-active app0.service
+status="$(portablectl is-attached --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl inspect --force --cat --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/conf0.raw"
+
+portablectl detach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0
+
+# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
+portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
+test -f /run/portables/app0.raw
+test -f /run/portables/minimal_0.raw
+test -f /run/systemd/system.attached/app0.service
+test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
+portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
+
+# Ensure that when two portables share the same base image, removing one doesn't remove the other too
+
+portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
+portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
+
+status="$(portablectl is-attached --extension app0 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+
+(! portablectl detach --runtime /usr/share/minimal_0.raw app)
+
+status="$(portablectl is-attached --extension app0 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+
+# Ensure 'portablectl list' shows the correct status for both images
+portablectl list
+portablectl list | grep -F "minimal_0" | grep -q -F "attached-runtime"
+portablectl list | grep -F "app0" | grep -q -F "attached-runtime"
+portablectl list | grep -F "app1" | grep -q -F "attached-runtime"
+
+portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app
+
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "attached-runtime" ]]
+
+portablectl detach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app
+
+# portablectl also works with directory paths rather than images
+
+mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
+mount /tmp/app0.raw /tmp/app0
+mount /tmp/app1.raw /tmp/app1
+mount /usr/share/minimal_0.raw /tmp/rootdir
+
+# Fix up os-release to drop the valid PORTABLE_SERVICES field (because we are
+# bypassing the sysext logic in portabled here it will otherwise not see the
+# extensions additional valid prefix)
+grep -v "^PORTABLE_PREFIXES=" /tmp/rootdir/etc/os-release >/tmp/os-release-fix/etc/os-release
+
+mount -t overlay overlay -o lowerdir=/tmp/os-release-fix:/tmp/app1:/tmp/rootdir /tmp/overlay
+
+grep . /tmp/overlay/usr/lib/extension-release.d/*
+grep . /tmp/overlay/etc/os-release
+
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/overlay app1
+
+systemctl is-active app1.service
+
+portablectl detach --now --runtime overlay app1
+
+umount /tmp/overlay
+
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+
+systemctl is-active app0.service
+systemctl is-active app1.service
+
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service
+
+grep -q -F "LogExtraFields=PORTABLE=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app0.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app0.service.d/20-portable.conf
+
+grep -q -F "LogExtraFields=PORTABLE=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app1.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app1.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app1.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
+grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app1.service.d/20-portable.conf
+
+portablectl detach --clean --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+
+# Ensure --clean remove state and other directories belonging to the portable image being detached
+test ! -d /var/lib/app0
+test ! -d /run/app0
+
+# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
+portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+test -d /run/portables/app0
+test -d /run/portables/app1
+test -d /run/portables/rootdir
+test -f /run/systemd/system.attached/app0.service
+test -f /run/systemd/system.attached/app1.service
+test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
+test -L /run/systemd/system.attached/app1.service.d/10-profile.conf
+portablectl detach --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+
+# Attempt to disable the app unit during detaching. Requires --copy=symlink to reproduce.
+# Provides coverage for https://github.com/systemd/systemd/issues/23481
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
+portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
+# attach and detach again to check if all drop-in configs are removed even if the main unit files are removed
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
+portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
+
+umount /tmp/rootdir
+umount /tmp/app0
+umount /tmp/app1
+
+# Lack of ID field in os-release should be rejected, but it caused a crash in the past instead
+mkdir -p /tmp/emptyroot/usr/lib
+mkdir -p /tmp/emptyext/usr/lib/extension-release.d
+touch /tmp/emptyroot/usr/lib/os-release
+touch /tmp/emptyext/usr/lib/extension-release.d/extension-release.emptyext
+
+# Remote peer disconnected -> portabled crashed
+res="$(! portablectl attach --extension /tmp/emptyext /tmp/emptyroot 2> >(grep "Remote peer disconnected"))"
+test -z "${res}"
+
+touch /testok
diff --git a/test/units/TEST-30-ONCLOCKCHANGE.service b/test/units/TEST-30-ONCLOCKCHANGE.service
new file mode 100644 (file)
index 0000000..253f7b5
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-30-ONCLOCKCHANGE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-30-ONCLOCKCHANGE.sh b/test/units/TEST-30-ONCLOCKCHANGE.sh
new file mode 100755 (executable)
index 0000000..83698b8
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemd-analyze log-level debug
+
+systemctl disable --now systemd-timesyncd.service
+
+timedatectl set-timezone Europe/Berlin
+timedatectl set-time 1980-10-15
+
+systemd-run --on-timezone-change touch /tmp/timezone-changed
+systemd-run --on-clock-change touch /tmp/clock-changed
+
+test ! -f /tmp/timezone-changed
+test ! -f /tmp/clock-changed
+
+timedatectl set-timezone Europe/Kyiv
+
+while test ! -f /tmp/timezone-changed ; do sleep .5 ; done
+
+timedatectl set-time 2018-1-1
+
+while test ! -f /tmp/clock-changed ; do sleep .5 ; done
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-31-DEVICE-ENUMERATION.service b/test/units/TEST-31-DEVICE-ENUMERATION.service
new file mode 100644 (file)
index 0000000..f0e78a9
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-31-DEVICE-ENUMERATION
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-31-DEVICE-ENUMERATION.sh b/test/units/TEST-31-DEVICE-ENUMERATION.sh
new file mode 100755 (executable)
index 0000000..5e99302
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if journalctl -b -t systemd --grep '(?<!loop.|diskseq-.)\.device: Changed plugged -> dead'; then
+    exit 1
+fi
+
+touch /testok
diff --git a/test/units/TEST-32-OOMPOLICY.service b/test/units/TEST-32-OOMPOLICY.service
new file mode 100644 (file)
index 0000000..50f5823
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-32-OOMPOLICY
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
+MemoryAccounting=yes
diff --git a/test/units/TEST-32-OOMPOLICY.sh b/test/units/TEST-32-OOMPOLICY.sh
new file mode 100755 (executable)
index 0000000..046b8b9
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Let's run this test only if the "memory.oom.group" cgroupfs attribute
+# exists. This test is a bit too strict, since the "memory.events"/"oom_kill"
+# logic has been around since a longer time than "memory.oom.group", but it's
+# an easier thing to test for, and also: let's not get confused by older
+# kernels where the concept was still new.
+
+if test -f /sys/fs/cgroup/system.slice/TEST-32-OOMPOLICY.service/memory.oom.group; then
+    systemd-analyze log-level debug
+
+    # Run a service that is guaranteed to be the first candidate for OOM killing
+    systemd-run --unit=oomtest.service \
+                -p Type=exec -p OOMScoreAdjust=1000 -p OOMPolicy=stop -p MemoryAccounting=yes \
+                sleep infinity
+
+    # Trigger an OOM killer run
+    echo 1 >/proc/sys/kernel/sysrq
+    echo f >/proc/sysrq-trigger
+
+    while : ; do
+        STATE="$(systemctl show -P ActiveState oomtest.service)"
+        [ "$STATE" = "failed" ] && break
+        sleep .5
+    done
+
+    RESULT="$(systemctl show -P Result oomtest.service)"
+    test "$RESULT" = "oom-kill"
+
+    systemd-analyze log-level info
+fi
+
+touch /testok
diff --git a/test/units/TEST-34-DYNAMICUSERMIGRATE.service b/test/units/TEST-34-DYNAMICUSERMIGRATE.service
new file mode 100644 (file)
index 0000000..6917afe
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-34-DYNAMICUSERMIGRATE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-34-DYNAMICUSERMIGRATE.sh b/test/units/TEST-34-DYNAMICUSERMIGRATE.sh
new file mode 100755 (executable)
index 0000000..d15b675
--- /dev/null
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemd-analyze log-level debug
+
+test_directory() {
+    local directory="$1"
+    local path="$2"
+
+    # cleanup for previous invocation
+    for i in xxx xxx2 yyy zzz x:yz x:yz2; do
+        rm -rf "${path:?}/${i}" "${path:?}/private/${i}"
+    done
+
+    # Set everything up without DynamicUser=1
+
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz touch "${path}"/zzz/test
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}=zzz:xxx zzz:xxx2" -p TemporaryFileSystem="${path}" bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test"
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
+    (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing)
+
+    test -d "${path}"/zzz
+    test ! -L "${path}"/zzz
+    test ! -e "${path}"/private/zzz
+
+    test ! -e "${path}"/xxx
+    test ! -e "${path}"/private/xxx
+    test ! -e "${path}"/xxx2
+    test ! -e "${path}"/private/xxx2
+    test -L "${path}"/yyy
+    test ! -e "${path}"/private/yyy
+
+    test -f "${path}"/zzz/test
+    test ! -e "${path}"/zzz/test-missing
+
+    # Convert to DynamicUser=1
+
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}=zzz:xxx zzz:xxx2" \
+                -p TemporaryFileSystem="${path}" -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test"
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
+    (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing)
+
+    test -L "${path}"/zzz
+    test -d "${path}"/private/zzz
+
+    test ! -e "${path}"/xxx
+    test ! -e "${path}"/private/xxx
+    test ! -e "${path}"/xxx2
+    test ! -e "${path}"/private/xxx2
+    test -L "${path}"/yyy # previous symlink is not removed
+    test ! -e "${path}"/private/yyy
+
+    test -f "${path}"/zzz/test
+    test ! -e "${path}"/zzz/test-missing
+
+    # Convert back
+
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}" test -f "${path}"/xxx/test
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}=zzz:xxx zzz:xxx2" -p TemporaryFileSystem="${path}" bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test"
+    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
+    (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing)
+
+    test -d "${path}"/zzz
+    test ! -L "${path}"/zzz
+    test ! -e "${path}"/private/zzz
+
+    test ! -e "${path}"/xxx
+    test ! -e "${path}"/private/xxx
+    test ! -e "${path}"/xxx2
+    test ! -e "${path}"/private/xxx2
+    test -L "${path}"/yyy
+    test ! -e "${path}"/private/yyy
+
+    test -f "${path}"/zzz/test
+    test ! -e "${path}"/zzz/test-missing
+
+    # Exercise the unit parsing paths too
+    cat >/run/systemd/system/testservice-34.service <<EOF
+[Service]
+Type=oneshot
+TemporaryFileSystem=${path}
+RuntimeDirectoryPreserve=yes
+${directory}=zzz:x\:yz zzz:x\:yz2
+ExecStart=test -f ${path}/x:yz2/test
+ExecStart=test -f ${path}/x:yz/test
+ExecStart=test -f ${path}/zzz/test
+EOF
+    systemctl daemon-reload
+    systemctl start --wait testservice-34.service
+
+    test -d "${path}"/zzz
+    test ! -L "${path}"/zzz
+    test ! -e "${path}"/private/zzz
+
+    test ! -L "${path}"/x:yz
+    test ! -L "${path}"/x:yz2
+}
+
+test_check_writable() {
+    # cleanup for previous invocation
+    for i in aaa quux waldo xxx; do
+        rm -rf "/var/lib/$i" "/var/lib/private/$i"
+    done
+
+    cat >/run/systemd/system/testservice-34-check-writable.service <<\EOF
+[Unit]
+Description=Check writable directories when DynamicUser= with StateDirectory=
+
+[Service]
+# Relevant only for sanitizer runs
+EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
+
+Type=oneshot
+DynamicUser=yes
+StateDirectory=waldo quux/pief aaa/bbb aaa aaa/ccc xxx/yyy:aaa/111 xxx:aaa/222 xxx/zzz:aaa/333
+
+# Make sure that the state directories are really the only writable directory besides the obvious candidates
+ExecStart=bash -c ' \
+    set -eux; \
+    set -o pipefail; \
+    declare -a writable_dirs; \
+    readarray -t writable_dirs < <(find / \( -path /var/tmp -o -path /tmp -o -path /proc -o -path /dev/mqueue -o -path /dev/shm -o \
+                                              -path /sys/fs/bpf -o -path /dev/.lxc -o -path /sys/devices/system/cpu \) \
+                                   -prune -o -type d -writable -print 2>/dev/null | sort -u); \
+    [[ "$${#writable_dirs[@]}" == "8" ]]; \
+    [[ "$${writable_dirs[0]}" == "/var/lib/private/aaa" ]]; \
+    [[ "$${writable_dirs[1]}" == "/var/lib/private/aaa/bbb" ]]; \
+    [[ "$${writable_dirs[2]}" == "/var/lib/private/aaa/ccc" ]]; \
+    [[ "$${writable_dirs[3]}" == "/var/lib/private/quux/pief" ]]; \
+    [[ "$${writable_dirs[4]}" == "/var/lib/private/waldo" ]]; \
+    [[ "$${writable_dirs[5]}" == "/var/lib/private/xxx" ]]; \
+    [[ "$${writable_dirs[6]}" == "/var/lib/private/xxx/yyy" ]]; \
+    [[ "$${writable_dirs[7]}" == "/var/lib/private/xxx/zzz" ]]; \
+'
+EOF
+    systemctl daemon-reload
+    systemctl start testservice-34-check-writable.service
+}
+
+test_directory "StateDirectory" "/var/lib"
+test_directory "RuntimeDirectory" "/run"
+test_directory "CacheDirectory" "/var/cache"
+test_directory "LogsDirectory" "/var/log"
+
+test_check_writable
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-35-LOGIN.service b/test/units/TEST-35-LOGIN.service
new file mode 100644 (file)
index 0000000..0599f61
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-35-LOGIN
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-35-LOGIN.sh b/test/units/TEST-35-LOGIN.sh
new file mode 100755 (executable)
index 0000000..0f4b770
--- /dev/null
@@ -0,0 +1,738 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+cleanup_test_user() (
+    set +ex
+
+    pkill -u "$(id -u logind-test-user)"
+    sleep 1
+    pkill -KILL -u "$(id -u logind-test-user)"
+    userdel -r logind-test-user
+
+    return 0
+)
+
+setup_test_user() {
+    mkdir -p /var/spool/cron /var/spool/mail
+    useradd -m -s /bin/bash logind-test-user
+    trap cleanup_test_user EXIT
+}
+
+test_write_dropin() {
+    systemctl edit --runtime --stdin systemd-logind.service --drop-in=debug.conf <<EOF
+[Service]
+Environment=SYSTEMD_LOG_LEVEL=debug
+EOF
+
+    # We test "coldplug" (completely stop and start logind) here. So we need to preserve
+    # the fdstore, which might contain session leader pidfds. This is extremely rare use case
+    # and shall not be considered fully supported.
+    # See also: https://github.com/systemd/systemd/pull/30610#discussion_r1440507850
+    systemctl edit --runtime --stdin systemd-logind.service --drop-in=fdstore-preserve.conf <<EOF
+[Service]
+FileDescriptorStorePreserve=yes
+EOF
+
+    systemctl restart systemd-logind.service
+}
+
+testcase_properties() {
+    mkdir -p /run/systemd/logind.conf.d
+
+    cat >/run/systemd/logind.conf.d/kill-user-processes.conf <<EOF
+[Login]
+KillUserProcesses=no
+EOF
+
+    systemctl restart systemd-logind.service
+    assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager KillUserProcesses)" "b false"
+
+    cat >/run/systemd/logind.conf.d/kill-user-processes.conf <<EOF
+[Login]
+KillUserProcesses=yes
+EOF
+
+    systemctl restart systemd-logind.service
+    assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager KillUserProcesses)" "b true"
+
+    rm -rf /run/systemd/logind.conf.d
+}
+
+testcase_sleep_automated() {
+    assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager SleepOperation)" 'as 3 "suspend-then-hibernate" "suspend" "hibernate"'
+
+    mkdir -p /run/systemd/logind.conf.d
+
+    cat >/run/systemd/logind.conf.d/sleep-operations.conf <<EOF
+[Login]
+SleepOperation=suspend hybrid-sleep
+EOF
+
+    systemctl restart systemd-logind.service
+
+    assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager SleepOperation)" 'as 2 "hybrid-sleep" "suspend"'
+
+    busctl call org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager CanSleep
+
+    rm -rf /run/systemd/logind.conf.d
+}
+
+testcase_started() {
+    local pid
+
+    systemctl restart systemd-logind.service
+
+    # should start at boot, not with D-BUS activation
+    pid=$(systemctl show systemd-logind.service -p ExecMainPID --value)
+
+    # loginctl should succeed
+    loginctl
+
+    # logind should still be running
+    assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
+}
+
+wait_suspend() {
+    timeout "${1?}" bash -c "while [[ ! -e /run/suspend.flag ]]; do sleep 1; done"
+    rm /run/suspend.flag
+}
+
+teardown_suspend() (
+    set +eux
+
+    pkill evemu-device
+
+    rm -rf /run/systemd/system/systemd-suspend.service.d
+    systemctl daemon-reload
+
+    rm -f /run/udev/rules.d/70-logindtest-lid.rules
+    udevadm control --reload
+
+    return 0
+)
+
+testcase_suspend_on_lid() {
+    local pid input_name lid_dev
+
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping suspend test in container"
+        return
+    fi
+    if ! grep -s -q mem /sys/power/state; then
+        echo "suspend not supported on this testbed, skipping"
+        return
+    fi
+    if ! command -v evemu-device >/dev/null; then
+        echo "command evemu-device not found, skipping"
+        return
+    fi
+    if ! command -v evemu-event >/dev/null; then
+        echo "command evemu-event not found, skipping"
+        return
+    fi
+
+    trap teardown_suspend RETURN
+
+    # save pid
+    pid=$(systemctl show systemd-logind.service -p ExecMainPID --value)
+
+    # create fake suspend
+    mkdir -p /run/systemd/system/systemd-suspend.service.d
+    cat >/run/systemd/system/systemd-suspend.service.d/override.conf <<EOF
+[Service]
+ExecStart=
+ExecStart=touch /run/suspend.flag
+EOF
+    systemctl daemon-reload
+
+    # create fake lid switch
+    mkdir -p /run/udev/rules.d
+    cat >/run/udev/rules.d/70-logindtest-lid.rules <<EOF
+SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="Fake Lid Switch", TAG+="power-switch"
+EOF
+    udevadm control --reload
+
+    cat >/run/lidswitch.evemu <<EOF
+# EVEMU 1.2
+# Input device name: "Lid Switch"
+# Input device ID: bus 0x19 vendor 0000 product 0x05 version 0000
+# Supported events:
+#   Event type 0 (EV_SYN)
+#     Event code 0 (SYN_REPORT)
+#     Event code 5 (FF_STATUS_MAX)
+#   Event type 5 (EV_SW)
+#     Event code 0 (SW_LID)
+# Properties:
+N: Fake Lid Switch
+I: 0019 0000 0005 0000
+P: 00 00 00 00 00 00 00 00
+B: 00 21 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 02 00 00 00 00 00 00 00 00
+B: 03 00 00 00 00 00 00 00 00
+B: 04 00 00 00 00 00 00 00 00
+B: 05 01 00 00 00 00 00 00 00
+B: 11 00 00 00 00 00 00 00 00
+B: 12 00 00 00 00 00 00 00 00
+B: 15 00 00 00 00 00 00 00 00
+B: 15 00 00 00 00 00 00 00 00
+EOF
+
+    evemu-device /run/lidswitch.evemu &
+
+    timeout 20 bash -c 'until grep "^Fake Lid Switch" /sys/class/input/*/device/name; do sleep .5; done'
+    input_name=$(grep -l '^Fake Lid Switch' /sys/class/input/*/device/name || :)
+    if [[ -z "$input_name" ]]; then
+        echo "cannot find fake lid switch." >&2
+        exit 1
+    fi
+    input_name=${input_name%/device/name}
+    lid_dev=/dev/${input_name#/sys/class/}
+    udevadm info --wait-for-initialization=10s "$lid_dev"
+    udevadm settle
+
+    # close lid
+    evemu-event "$lid_dev" --sync --type 5 --code 0 --value 1
+    # need to wait for 30s suspend inhibition after boot
+    wait_suspend 31
+    # open lid again
+    evemu-event "$lid_dev" --sync --type 5 --code 0 --value 0
+
+    # waiting for 30s inhibition time between suspends
+    sleep 30
+
+    # now closing lid should cause instant suspend
+    evemu-event "$lid_dev" --sync --type 5 --code 0 --value 1
+    wait_suspend 2
+    evemu-event "$lid_dev" --sync --type 5 --code 0 --value 0
+
+    assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
+}
+
+testcase_shutdown() {
+    local pid
+
+    # save pid
+    pid=$(systemctl show systemd-logind.service -p ExecMainPID --value)
+
+    # scheduled shutdown with wall message
+    shutdown 2>&1
+    sleep 5
+    shutdown -c || :
+    # logind should still be running
+    assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
+
+    # scheduled shutdown without wall message
+    shutdown --no-wall 2>&1
+    sleep 5
+    shutdown -c --no-wall || true
+    assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
+}
+
+cleanup_session() (
+    set +ex
+
+    local uid s
+
+    uid=$(id -u logind-test-user)
+
+    loginctl disable-linger logind-test-user
+
+    systemctl stop getty@tty2.service
+
+    for s in $(loginctl --no-legend list-sessions | grep -v manager | awk '$3 == "logind-test-user" { print $1 }'); do
+        echo "INFO: stopping session $s"
+        loginctl terminate-session "$s"
+    done
+
+    loginctl terminate-user logind-test-user
+
+    if ! timeout 30 bash -c "while loginctl --no-legend | grep -q logind-test-user; do sleep 1; done"; then
+        echo "WARNING: session for logind-test-user still active, ignoring."
+    fi
+
+    pkill -u "$uid"
+    sleep 1
+    pkill -KILL -u "$uid"
+
+    if ! timeout 30 bash -c "while systemctl is-active --quiet user@${uid}.service; do sleep 1; done"; then
+        echo "WARNING: user@${uid}.service is still active, ignoring."
+    fi
+
+    if ! timeout 30 bash -c "while systemctl is-active --quiet user-runtime-dir@${uid}.service; do sleep 1; done"; then
+        echo "WARNING: user-runtime-dir@${uid}.service is still active, ignoring."
+    fi
+
+    if ! timeout 30 bash -c "while systemctl is-active --quiet user-${uid}.slice; do sleep 1; done"; then
+        echo "WARNING: user-${uid}.slice is still active, ignoring."
+    fi
+
+    rm -rf /run/systemd/system/getty@tty2.service.d
+    systemctl daemon-reload
+
+    return 0
+)
+
+teardown_session() (
+    set +ex
+
+    cleanup_session
+
+    rm -f /run/udev/rules.d/70-logindtest-scsi_debug-user.rules
+    udevadm control --reload
+    rmmod scsi_debug
+
+    return 0
+)
+
+check_session() (
+    set +ex
+
+    local seat session leader_pid
+
+    if [[ $(loginctl --no-legend | grep -v manager | grep -c "logind-test-user") != 1 ]]; then
+        echo "no session or multiple sessions for logind-test-user." >&2
+        return 1
+    fi
+
+    seat=$(loginctl --no-legend | grep -v manager | grep 'logind-test-user *seat' | awk '{ print $4 }')
+    if [[ -z "$seat" ]]; then
+        echo "no seat found for user logind-test-user" >&2
+        return 1
+    fi
+
+    session=$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
+    if [[ -z "$session" ]]; then
+        echo "no session found for user logind-test-user" >&2
+        return 1
+    fi
+
+    if ! loginctl session-status "$session" | grep -q "Unit: session-${session}\.scope"; then
+        echo "cannot find scope unit for session $session" >&2
+        return 1
+    fi
+
+    leader_pid=$(loginctl session-status "$session" | awk '$1 == "Leader:" { print $2 }')
+    if [[ -z "$leader_pid" ]]; then
+        echo "cannot found leader process for session $session" >&2
+        return 1
+    fi
+
+    # cgroup v1: "1:name=systemd:/user.slice/..."; unified hierarchy: "0::/user.slice"
+    if ! grep -q -E '(name=systemd|^0:):.*session.*scope' /proc/"$leader_pid"/cgroup; then
+        echo "FAIL: process $leader_pid is not in the session cgroup" >&2
+        cat /proc/self/cgroup
+        return 1
+    fi
+)
+
+create_session() {
+    # login with the test user to start a session
+    mkdir -p /run/systemd/system/getty@tty2.service.d
+    cat >/run/systemd/system/getty@tty2.service.d/override.conf <<EOF
+[Service]
+Type=simple
+ExecStart=
+ExecStart=-/sbin/agetty --autologin logind-test-user --noclear %I $TERM
+Restart=no
+EOF
+    systemctl daemon-reload
+
+    systemctl restart getty@tty2.service
+
+    # check session
+    for i in {1..30}; do
+        (( i > 1 )) && sleep 1
+        check_session && break
+    done
+    check_session
+    assert_eq "$(loginctl --no-legend | grep -v manager | awk '$3=="logind-test-user" { print $7 }')" "tty2"
+}
+
+testcase_sanity_check() {
+    # Exercise basic loginctl options
+
+    if [[ ! -c /dev/tty2 ]]; then
+        echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
+        return
+    fi
+
+    trap cleanup_session RETURN
+    create_session
+
+    # Run most of the loginctl commands from a user session to make
+    # the seat/session autodetection work-ish
+    systemd-run --user --pipe --wait -M "logind-test-user@.host" bash -eux <<\EOF
+    loginctl list-sessions
+    loginctl list-sessions -j
+    loginctl list-sessions --json=short
+    loginctl session-status
+    loginctl show-session
+    loginctl show-session -P DelayInhibited
+
+    # We're not in the same session scope, so in this case we need to specify
+    # the session ID explicitly
+    session=$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1; exit; }')
+    loginctl kill-session --signal=SIGCONT "$session"
+    # FIXME(?)
+    #loginctl kill-session --signal=SIGCONT --kill-whom=leader "$session"
+
+    loginctl list-users
+    loginctl user-status
+    loginctl show-user -a
+    loginctl show-user -P IdleAction
+    loginctl kill-user --signal=SIGCONT ""
+
+    loginctl list-seats
+    loginctl seat-status
+    loginctl show-seat
+    loginctl show-seat -P IdleActionUSec
+EOF
+
+    # Requires root privileges
+    loginctl lock-sessions
+    loginctl unlock-sessions
+    loginctl flush-devices
+}
+
+testcase_session() {
+    local dev
+
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping ACL tests in container"
+        return
+    fi
+
+    if [[ ! -c /dev/tty2 ]]; then
+        echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
+        return
+    fi
+
+    trap teardown_session RETURN
+
+    create_session
+
+    # scsi_debug should not be loaded yet
+    if [[ -d /sys/bus/pseudo/drivers/scsi_debug ]]; then
+        echo "scsi_debug module is already loaded." >&2
+        exit 1
+    fi
+
+    # we use scsi_debug to create new devices which we can put ACLs on
+    # tell udev about the tagging, so that logind can pick it up
+    mkdir -p /run/udev/rules.d
+    cat >/run/udev/rules.d/70-logindtest-scsi_debug-user.rules <<EOF
+SUBSYSTEM=="block", ATTRS{model}=="scsi_debug*", TAG+="uaccess"
+EOF
+    udevadm control --reload
+
+    # coldplug: logind started with existing device
+    systemctl stop systemd-logind.service
+    modprobe scsi_debug
+    timeout 30 bash -c 'until ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null; do sleep 1; done'
+    dev=/dev/$(ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null)
+    if [[ ! -b "$dev" ]]; then
+        echo "cannot find suitable scsi block device" >&2
+        exit 1
+    fi
+    udevadm settle
+    udevadm info "$dev"
+
+    # trigger logind and activate session
+    loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')"
+
+    # check ACL
+    sleep 1
+    assert_in "user:logind-test-user:rw-" "$(getfacl -p "$dev")"
+
+    # hotplug: new device appears while logind is running
+    rmmod scsi_debug
+    modprobe scsi_debug
+    timeout 30 bash -c 'until ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null; do sleep 1; done'
+    dev=/dev/$(ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null)
+    if [[ ! -b "$dev" ]]; then
+        echo "cannot find suitable scsi block device" >&2
+        exit 1
+    fi
+    udevadm settle
+
+    # check ACL
+    sleep 1
+    assert_in "user:logind-test-user:rw-" "$(getfacl -p "$dev")"
+}
+
+teardown_lock_idle_action() (
+    set +eux
+
+    rm -f /run/systemd/logind.conf.d/idle-action-lock.conf
+    systemctl restart systemd-logind.service
+
+    cleanup_session
+
+    return 0
+)
+
+testcase_lock_idle_action() {
+    local ts
+
+    if [[ ! -c /dev/tty2 ]]; then
+        echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
+        return
+    fi
+
+    if loginctl --no-legend | grep -v manager | grep -q logind-test-user; then
+        echo >&2 "Session of the 'logind-test-user' is already present."
+        exit 1
+    fi
+
+    trap teardown_lock_idle_action RETURN
+
+    create_session
+
+    ts="$(date '+%H:%M:%S')"
+
+    mkdir -p /run/systemd/logind.conf.d
+    cat >/run/systemd/logind.conf.d/idle-action-lock.conf <<EOF
+[Login]
+IdleAction=lock
+IdleActionSec=1s
+EOF
+    systemctl restart systemd-logind.service
+
+    # Wait for 35s, in that interval all sessions should have become idle
+    # and "Lock" signal should have been sent out. Then we wrote to tty to make
+    # session active again and next we slept for another 35s so sessions have
+    # become idle again. 'Lock' signal is sent out for each session, we have at
+    # least one session, so minimum of 2 "Lock" signals must have been sent.
+    timeout 35 bash -c "while [[ \"\$(journalctl -b -u systemd-logind.service --since=$ts | grep -c 'Sent message type=signal .* member=Lock')\" -lt 1 ]]; do sleep 1; done"
+
+    # We need to know that a new message was sent after waking up,
+    # so we must track how many happened before sleeping to check we have extra.
+    locks="$(journalctl -b -u systemd-logind.service --since="$ts" | grep -c 'Sent message type=signal .* member=Lock')"
+
+    # Wakeup
+    touch /dev/tty2
+
+    # Wait again
+    timeout 35 bash -c "while [[ \"\$(journalctl -b -u systemd-logind.service --since=$ts | grep -c 'Sent message type=signal .* member=Lock')\" -lt $((locks + 1)) ]]; do sleep 1; done"
+
+    if [[ "$(journalctl -b -u systemd-logind.service --since="$ts" | grep -c 'System idle. Will be locked now.')" -lt 2 ]]; then
+        echo >&2 "System haven't entered idle state at least 2 times."
+        exit 1
+    fi
+}
+
+testcase_session_properties() {
+    local s
+
+    if [[ ! -c /dev/tty2 ]]; then
+        echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
+        return
+    fi
+
+    trap cleanup_session RETURN
+    create_session
+
+    s=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
+    /usr/lib/systemd/tests/unit-tests/manual/test-session-properties "/org/freedesktop/login1/session/_3${s?}" /dev/tty2
+}
+
+testcase_list_users_sessions_seats() {
+    local session seat
+
+    if [[ ! -c /dev/tty2 ]]; then
+        echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
+        return
+    fi
+
+    trap cleanup_session RETURN
+    create_session
+
+    # Activate the session
+    loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')"
+
+    session=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
+    : check that we got a valid session id
+    busctl get-property org.freedesktop.login1 "/org/freedesktop/login1/session/_3${session?}" org.freedesktop.login1.Session Id
+    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $2 }')" "$(id -ru logind-test-user)"
+    seat=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $4 }')
+    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $6 }')" user
+    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $7 }')" tty2
+    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $8 }')" no
+    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $9 }')" '-'
+
+    loginctl list-seats --no-legend | grep -Fwq "${seat?}"
+
+    assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $1 }')" "$(id -ru logind-test-user)"
+    assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" no
+    assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $4 }')" active
+
+    loginctl enable-linger logind-test-user
+    assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" yes
+
+    for s in $(loginctl list-sessions --no-legend | grep tty | awk '$3 == "logind-test-user" { print $1 }'); do
+        loginctl terminate-session "$s"
+    done
+    if ! timeout 30 bash -c "while loginctl --no-legend | grep tty | grep -q logind-test-user; do sleep 1; done"; then
+        echo "WARNING: session for logind-test-user still active, ignoring."
+        return
+    fi
+
+    assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $4 }')" lingering
+}
+
+teardown_stop_idle_session() (
+    set +eux
+
+    rm -f /run/systemd/logind.conf.d/stop-idle-session.conf
+    systemctl restart systemd-logind.service
+
+    cleanup_session
+)
+
+testcase_stop_idle_session() {
+    local id ts
+
+    if [[ ! -c /dev/tty2 ]]; then
+        echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
+        return
+    fi
+
+    create_session
+    trap teardown_stop_idle_session RETURN
+
+    id="$(loginctl --no-legend | grep tty | awk '$3 == "logind-test-user" { print $1; }')"
+    ts="$(date '+%H:%M:%S')"
+
+    mkdir -p /run/systemd/logind.conf.d
+    cat >/run/systemd/logind.conf.d/stop-idle-session.conf <<EOF
+[Login]
+StopIdleSessionSec=2s
+EOF
+    systemctl restart systemd-logind.service
+    sleep 5
+
+    assert_eq "$(journalctl -b -u systemd-logind.service --since="$ts" --grep "Session \"$id\" of user \"logind-test-user\" is idle, stopping." | wc -l)" 1
+    assert_eq "$(loginctl --no-legend | grep -v manager | grep -c "logind-test-user")" 0
+}
+
+testcase_ambient_caps() {
+    local PAMSERVICE TRANSIENTUNIT SCRIPT
+
+    # Verify that pam_systemd works and assigns ambient caps as it should
+
+    if ! grep -q 'CapAmb:' /proc/self/status ; then
+        echo "ambient caps not available, skipping test." >&2
+        return
+    fi
+
+    typeset -i BND MASK
+
+    # Get PID 1's bounding set
+    BND="0x$(grep 'CapBnd:' /proc/1/status | cut -d: -f2 | tr -d '[:space:]')"
+
+    # CAP_CHOWN | CAP_KILL
+    MASK=$(((1 << 0) | (1 << 5)))
+
+    if [ $((BND & MASK)) -ne "$MASK" ] ; then
+        echo "CAP_CHOWN or CAP_KILL not available in bounding set, skipping test." >&2
+        return
+    fi
+
+    PAMSERVICE="pamserv$RANDOM"
+    TRANSIENTUNIT="capwakealarm$RANDOM.service"
+    SCRIPT="/tmp/capwakealarm$RANDOM.sh"
+
+    cat > /etc/pam.d/"$PAMSERVICE" <<EOF
+auth sufficient    pam_unix.so
+auth required      pam_deny.so
+account sufficient pam_unix.so
+account required   pam_permit.so
+session optional   pam_systemd.so default-capability-ambient-set=CAP_CHOWN,CAP_KILL debug
+session required   pam_unix.so
+EOF
+
+    cat > "$SCRIPT" <<'EOF'
+#!/bin/bash
+set -ex
+typeset -i AMB MASK
+AMB="0x$(grep 'CapAmb:' /proc/self/status | cut -d: -f2 | tr -d '[:space:]')"
+MASK=$(((1 << 0) | (1 << 5)))
+test "$AMB" -eq "$MASK"
+EOF
+
+    chmod +x "$SCRIPT"
+
+    systemd-run -u "$TRANSIENTUNIT" -p PAMName="$PAMSERVICE" -p Type=oneshot -p User=logind-test-user -p StandardError=tty "$SCRIPT"
+
+    rm -f "$SCRIPT" "$PAMSERVICE"
+}
+
+background_at_return() {
+    rm -f /etc/pam.d/"$PAMSERVICE"
+    unset PAMSERVICE
+}
+
+testcase_background() {
+
+    local uid TRANSIENTUNIT1 TRANSIENTUNIT2
+
+    uid=$(id -u logind-test-user)
+
+    systemctl stop user@"$uid".service
+
+    PAMSERVICE="pamserv$RANDOM"
+    TRANSIENTUNIT1="bg$RANDOM.service"
+    TRANSIENTUNIT2="bgg$RANDOM.service"
+
+    trap background_at_return RETURN
+
+    cat > /etc/pam.d/"$PAMSERVICE" <<EOF
+auth sufficient    pam_unix.so
+auth required      pam_deny.so
+account sufficient pam_unix.so
+account required   pam_permit.so
+session optional   pam_systemd.so debug
+session required   pam_unix.so
+EOF
+
+    systemd-run -u "$TRANSIENTUNIT1" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_CLASS=background-light" -p Type=exec -p User=logind-test-user sleep infinity
+
+    # This was a 'light' background service, hence the service manager should not be running
+    (! systemctl is-active user@"$uid".service )
+
+    systemctl stop "$TRANSIENTUNIT1"
+
+    systemd-run -u "$TRANSIENTUNIT2" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_CLASS=background" -p Type=exec -p User=logind-test-user sleep infinity
+
+    # This was a regular background service, hence the service manager should be running
+    systemctl is-active user@"$uid".service
+
+    systemctl stop "$TRANSIENTUNIT2"
+
+    systemctl stop user@"$uid".service
+}
+
+setup_test_user
+test_write_dropin
+run_testcases
+
+touch /testok
diff --git a/test/units/TEST-36-NUMAPOLICY.service b/test/units/TEST-36-NUMAPOLICY.service
new file mode 100644 (file)
index 0000000..5746dc1
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-36-NUMAPOLICY
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-36-NUMAPOLICY.sh b/test/units/TEST-36-NUMAPOLICY.sh
new file mode 100755 (executable)
index 0000000..02ca88f
--- /dev/null
@@ -0,0 +1,352 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck disable=SC2317
+at_exit() {
+    # shellcheck disable=SC2181
+    if [[ $? -ne 0 ]]; then
+        # We're exiting with a non-zero EC, let's dump test artifacts
+        # for easier debugging
+        [[ -v straceLog && -f "$straceLog" ]] && cat "$straceLog"
+        [[ -v journalLog && -f "$journalLog" ]] && cat "$journalLog"
+    fi
+}
+
+trap at_exit EXIT
+
+systemd-analyze log-level debug
+systemd-analyze log-target journal
+
+# Log files
+straceLog='strace.log'
+journalLog='journal.log'
+
+# Systemd config files
+testUnit='numa-test.service'
+testUnitFile="/run/systemd/system/$testUnit"
+testUnitNUMAConf="$testUnitFile.d/numa.conf"
+
+# Sleep constants (we should probably figure out something better but nothing comes to mind)
+sleepAfterStart=3
+
+# Journal cursor for easier navigation
+journalCursorFile="jounalCursorFile"
+
+startStrace() {
+    coproc strace -qq -p 1 -o "$straceLog" -e set_mempolicy -s 1024 ${1:+"$1"}
+    # Wait for strace to properly "initialize", i.e. until PID 1 has the TracerPid
+    # field set to the current strace's PID
+    until awk -v spid="$COPROC_PID" '/^TracerPid:/ {exit !($2 == spid);}' /proc/1/status; do sleep 0.1; done
+}
+
+stopStrace() {
+    [[ -v COPROC_PID ]] || return
+
+    local PID=$COPROC_PID
+    kill -s TERM "$PID"
+    # Make sure the strace process is indeed dead
+    while kill -0 "$PID" 2>/dev/null; do sleep 0.1; done
+}
+
+startJournalctl() {
+    : >"$journalCursorFile"
+    # Save journal's cursor for later navigation
+    journalctl --no-pager --cursor-file="$journalCursorFile" -n0 -ocat
+}
+
+stopJournalctl() {
+    local unit="${1:-init.scope}"
+    # Using journalctl --sync should be better than using SIGRTMIN+1, as
+    # the --sync wait until the synchronization is complete
+    echo "Force journald to write all queued messages"
+    journalctl --sync
+    journalctl -u "$unit" --cursor-file="$journalCursorFile" >"$journalLog"
+}
+
+checkNUMA() {
+    # NUMA enabled system should have at least NUMA node0
+    test -e /sys/devices/system/node/node0
+}
+
+writePID1NUMAPolicy() {
+    cat >"$confDir/numa.conf" <<EOF
+[Manager]
+NUMAPolicy=${1:?}
+NUMAMask=${2:-""}
+EOF
+}
+
+writeTestUnit() {
+    mkdir -p "$testUnitFile.d/"
+    printf "[Service]\nExecStart=sleep 3600\n" >"$testUnitFile"
+}
+
+writeTestUnitNUMAPolicy() {
+    cat >"$testUnitNUMAConf" <<EOF
+[Service]
+NUMAPolicy=${1:?}
+NUMAMask=${2:-""}
+EOF
+    systemctl daemon-reload
+}
+
+pid1ReloadWithStrace() {
+    startStrace
+    systemctl daemon-reload
+    sleep $sleepAfterStart
+    stopStrace
+}
+
+pid1ReloadWithJournal() {
+    startJournalctl
+    systemctl daemon-reload
+    stopJournalctl
+}
+
+pid1StartUnitWithStrace() {
+    startStrace '-f'
+    systemctl start "${1:?}"
+    sleep $sleepAfterStart
+    stopStrace
+}
+
+pid1StartUnitWithJournal() {
+    startJournalctl
+    systemctl start "${1:?}"
+    sleep $sleepAfterStart
+    stopJournalctl
+}
+
+pid1StopUnit() {
+    systemctl stop "${1:?}"
+}
+
+systemctlCheckNUMAProperties() {
+    local UNIT_NAME="${1:?}"
+    local NUMA_POLICY="${2:?}"
+    local NUMA_MASK="${3:-""}"
+    local LOGFILE
+
+    LOGFILE="$(mktemp)"
+
+    systemctl show -p NUMAPolicy "$UNIT_NAME" >"$LOGFILE"
+    grep "NUMAPolicy=$NUMA_POLICY" "$LOGFILE"
+
+    : >"$LOGFILE"
+
+    if [ -n "$NUMA_MASK" ]; then
+        systemctl show -p NUMAMask "$UNIT_NAME" >"$LOGFILE"
+        grep "NUMAMask=$NUMA_MASK" "$LOGFILE"
+    fi
+}
+
+writeTestUnit
+
+# Create systemd config drop-in directory
+confDir="/run/systemd/system.conf.d/"
+mkdir -p "$confDir"
+
+if ! checkNUMA; then
+    echo >&2 "NUMA is not supported on this machine, switching to a simple sanity check"
+
+    echo "PID1 NUMAPolicy=default && NUMAMask=0 check without NUMA support"
+    writePID1NUMAPolicy "default" "0"
+    startJournalctl
+    systemctl daemon-reload
+    stopJournalctl
+    grep "NUMA support not available, ignoring" "$journalLog"
+
+    echo "systemd-run NUMAPolicy=default && NUMAMask=0 check without NUMA support"
+    runUnit='numa-systemd-run-test.service'
+    startJournalctl
+    systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit "$runUnit" sleep 1000
+    sleep $sleepAfterStart
+    pid1StopUnit "$runUnit"
+    stopJournalctl "$runUnit"
+    grep "NUMA support not available, ignoring" "$journalLog"
+
+else
+    echo "PID1 NUMAPolicy support - Default policy w/o mask"
+    writePID1NUMAPolicy "default"
+    pid1ReloadWithStrace
+    # Kernel requires that nodemask argument is set to NULL when setting default policy
+    grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
+
+    echo "PID1 NUMAPolicy support - Default policy w/ mask"
+    writePID1NUMAPolicy "default" "0"
+    pid1ReloadWithStrace
+    grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
+
+    echo "PID1 NUMAPolicy support - Bind policy w/o mask"
+    writePID1NUMAPolicy "bind"
+    pid1ReloadWithJournal
+    grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog"
+
+    echo "PID1 NUMAPolicy support - Bind policy w/ mask"
+    writePID1NUMAPolicy "bind" "0"
+    pid1ReloadWithStrace
+    grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" "$straceLog"
+
+    echo "PID1 NUMAPolicy support - Interleave policy w/o mask"
+    writePID1NUMAPolicy "interleave"
+    pid1ReloadWithJournal
+    grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog"
+
+    echo "PID1 NUMAPolicy support - Interleave policy w/ mask"
+    writePID1NUMAPolicy "interleave" "0"
+    pid1ReloadWithStrace
+    grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" "$straceLog"
+
+    echo "PID1 NUMAPolicy support - Preferred policy w/o mask"
+    writePID1NUMAPolicy "preferred"
+    pid1ReloadWithJournal
+    # Preferred policy with empty node mask is actually allowed and should reset allocation policy to default
+    grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog" && { echo >&2 "unexpected pass"; exit 1; }
+
+    echo "PID1 NUMAPolicy support - Preferred policy w/ mask"
+    writePID1NUMAPolicy "preferred" "0"
+    pid1ReloadWithStrace
+    grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" "$straceLog"
+
+    echo "PID1 NUMAPolicy support - Local policy w/o mask"
+    writePID1NUMAPolicy "local"
+    pid1ReloadWithStrace
+    # Kernel requires that nodemask argument is set to NULL when setting default policy
+    # The unpatched versions of strace don't recognize the MPOL_LOCAL constant and
+    # return a numerical constant instead (with a comment):
+    #   set_mempolicy(0x4 /* MPOL_??? */, NULL, 0) = 0
+    # Let's cover this scenario as well
+    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
+
+    echo "PID1 NUMAPolicy support - Local policy w/ mask"
+    writePID1NUMAPolicy "local" "0"
+    pid1ReloadWithStrace
+    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
+
+    echo "Unit file NUMAPolicy support - Default policy w/o mask"
+    writeTestUnitNUMAPolicy "default"
+    pid1StartUnitWithStrace "$testUnit"
+    systemctlCheckNUMAProperties "$testUnit" "default"
+    pid1StopUnit "$testUnit"
+    grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
+
+    echo "Unit file NUMAPolicy support - Default policy w/ mask"
+    writeTestUnitNUMAPolicy "default" "0"
+    pid1StartUnitWithStrace "$testUnit"
+    systemctlCheckNUMAProperties "$testUnit" "default" "0"
+    pid1StopUnit $testUnit
+    # Mask must be ignored
+    grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
+
+    echo "Unit file NUMAPolicy support - Bind policy w/o mask"
+    writeTestUnitNUMAPolicy "bind"
+    pid1StartUnitWithJournal "$testUnit"
+    pid1StopUnit "$testUnit"
+    [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]]
+
+    echo "Unit file NUMAPolicy support - Bind policy w/ mask"
+    writeTestUnitNUMAPolicy "bind" "0"
+    pid1StartUnitWithStrace "$testUnit"
+    systemctlCheckNUMAProperties "$testUnit" "bind" "0"
+    pid1StopUnit "$testUnit"
+    grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" "$straceLog"
+
+    echo "Unit file NUMAPolicy support - Interleave policy w/o mask"
+    writeTestUnitNUMAPolicy "interleave"
+    pid1StartUnitWithStrace "$testUnit"
+    pid1StopUnit "$testUnit"
+    [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]]
+
+    echo "Unit file NUMAPolicy support - Interleave policy w/ mask"
+    writeTestUnitNUMAPolicy "interleave" "0"
+    pid1StartUnitWithStrace "$testUnit"
+    systemctlCheckNUMAProperties "$testUnit" "interleave" "0"
+    pid1StopUnit "$testUnit"
+    grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" "$straceLog"
+
+    echo "Unit file NUMAPolicy support - Preferred policy w/o mask"
+    writeTestUnitNUMAPolicy "preferred"
+    pid1StartUnitWithJournal "$testUnit"
+    systemctlCheckNUMAProperties "$testUnit" "preferred"
+    pid1StopUnit "$testUnit"
+    [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]] && { echo >&2 "unexpected pass"; exit 1; }
+
+    echo "Unit file NUMAPolicy support - Preferred policy w/ mask"
+    writeTestUnitNUMAPolicy "preferred" "0"
+    pid1StartUnitWithStrace "$testUnit"
+    systemctlCheckNUMAProperties "$testUnit" "preferred" "0"
+    pid1StopUnit "$testUnit"
+    grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" "$straceLog"
+
+    echo "Unit file NUMAPolicy support - Local policy w/o mask"
+    writeTestUnitNUMAPolicy "local"
+    pid1StartUnitWithStrace "$testUnit"
+    systemctlCheckNUMAProperties "$testUnit" "local"
+    pid1StopUnit "$testUnit"
+    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
+
+    echo "Unit file NUMAPolicy support - Local policy w/ mask"
+    writeTestUnitNUMAPolicy "local" "0"
+    pid1StartUnitWithStrace "$testUnit"
+    systemctlCheckNUMAProperties "$testUnit" "local" "0"
+    pid1StopUnit "$testUnit"
+    # Mask must be ignored
+    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
+
+    echo "Unit file CPUAffinity=NUMA support"
+    writeTestUnitNUMAPolicy "bind" "0"
+    echo "CPUAffinity=numa" >>"$testUnitNUMAConf"
+    systemctl daemon-reload
+    systemctl start "$testUnit"
+    systemctlCheckNUMAProperties "$testUnit" "bind" "0"
+    cpulist="$(cat /sys/devices/system/node/node0/cpulist)"
+    affinity_systemd="$(systemctl show --value -p CPUAffinity "$testUnit")"
+    [ "$cpulist" = "$affinity_systemd" ]
+    pid1StopUnit "$testUnit"
+
+    echo "systemd-run NUMAPolicy support"
+    runUnit='numa-systemd-run-test.service'
+
+    systemd-run -p NUMAPolicy=default --unit "$runUnit" sleep 1000
+    systemctlCheckNUMAProperties "$runUnit" "default"
+    pid1StopUnit "$runUnit"
+
+    systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit "$runUnit" sleep 1000
+    systemctlCheckNUMAProperties "$runUnit" "default" ""
+    pid1StopUnit "$runUnit"
+
+    systemd-run -p NUMAPolicy=bind -p NUMAMask=0 --unit "$runUnit" sleep 1000
+    systemctlCheckNUMAProperties "$runUnit" "bind" "0"
+    pid1StopUnit "$runUnit"
+
+    systemd-run -p NUMAPolicy=interleave -p NUMAMask=0 --unit "$runUnit" sleep 1000
+    systemctlCheckNUMAProperties "$runUnit" "interleave" "0"
+    pid1StopUnit "$runUnit"
+
+    systemd-run -p NUMAPolicy=preferred -p NUMAMask=0 --unit "$runUnit" sleep 1000
+    systemctlCheckNUMAProperties "$runUnit" "preferred" "0"
+    pid1StopUnit "$runUnit"
+
+    systemd-run -p NUMAPolicy=local --unit "$runUnit" sleep 1000
+    systemctlCheckNUMAProperties "$runUnit" "local"
+    pid1StopUnit "$runUnit"
+
+    systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit "$runUnit" sleep 1000
+    systemctlCheckNUMAProperties "$runUnit" "local" ""
+    pid1StopUnit "$runUnit"
+
+    systemd-run -p NUMAPolicy=local -p NUMAMask=0 -p CPUAffinity=numa --unit "$runUnit" sleep 1000
+    systemctlCheckNUMAProperties "$runUnit" "local" ""
+    systemctl cat "$runUnit" | grep -q 'CPUAffinity=numa'
+    pid1StopUnit "$runUnit"
+fi
+
+# Cleanup
+rm -rf "$confDir"
+systemctl daemon-reload
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-38-FREEZER-sleep.service b/test/units/TEST-38-FREEZER-sleep.service
new file mode 100644 (file)
index 0000000..1bb9ddf
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+ExecStart=sleep 3600
diff --git a/test/units/TEST-38-FREEZER.service b/test/units/TEST-38-FREEZER.service
new file mode 100644 (file)
index 0000000..ac77836
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-38-FREEZER
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-38-FREEZER.sh b/test/units/TEST-38-FREEZER.sh
new file mode 100755 (executable)
index 0000000..0759784
--- /dev/null
@@ -0,0 +1,363 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2317
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+systemd-analyze log-level debug
+
+unit=TEST-38-FREEZER-sleep.service
+
+start_test_service() {
+    systemctl daemon-reload
+    systemctl start "${unit}"
+}
+
+dbus_freeze() {
+    local name object_path suffix
+
+    suffix="${1##*.}"
+    name="${1%".$suffix"}"
+    object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
+
+    busctl call \
+           org.freedesktop.systemd1 \
+           "${object_path}" \
+           org.freedesktop.systemd1.Unit \
+           Freeze
+}
+
+dbus_thaw() {
+    local name object_path suffix
+
+    suffix="${1##*.}"
+    name="${1%".$suffix"}"
+    object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
+
+    busctl call \
+           org.freedesktop.systemd1 \
+           "${object_path}" \
+           org.freedesktop.systemd1.Unit \
+           Thaw
+}
+
+dbus_freeze_unit() {
+    busctl call \
+           org.freedesktop.systemd1 \
+           /org/freedesktop/systemd1 \
+           org.freedesktop.systemd1.Manager \
+           FreezeUnit \
+           s \
+           "$1"
+}
+
+dbus_thaw_unit() {
+    busctl call \
+           org.freedesktop.systemd1 \
+           /org/freedesktop/systemd1 \
+           org.freedesktop.systemd1.Manager \
+           ThawUnit \
+           s \
+           "$1"
+}
+
+dbus_can_freeze() {
+    local name object_path suffix
+
+    suffix="${1##*.}"
+    name="${1%".$suffix"}"
+    object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
+
+    busctl get-property \
+           org.freedesktop.systemd1 \
+           "${object_path}" \
+           org.freedesktop.systemd1.Unit \
+           CanFreeze
+}
+
+check_freezer_state() {
+    local name object_path suffix
+
+    suffix="${1##*.}"
+    name="${1%".$suffix"}"
+    object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
+
+    for _ in {0..10}; do
+        state=$(busctl get-property \
+                       org.freedesktop.systemd1 \
+                       "${object_path}" \
+                       org.freedesktop.systemd1.Unit \
+                       FreezerState | cut -d " " -f2 | tr -d '"')
+
+        # Ignore the intermediate freezing & thawing states in case we check
+        # the unit state too quickly
+        [[ "$state" =~ ^(freezing|thawing) ]] || break
+        sleep .5
+    done
+
+    [ "$state" = "$2" ] || {
+        echo "error: unexpected freezer state, expected: $2, actual: $state" >&2
+        exit 1
+    }
+}
+
+check_cgroup_state() {
+    # foo.unit -> /system.slice/foo.unit/
+    # foo.slice/ -> /foo.slice/./
+    # foo.slice/foo.unit -> /foo.slice/foo.unit/
+    local slice unit
+    unit="${1##*/}"
+    slice="${1%"$unit"}"
+    slice="${slice%/}"
+    grep -q "frozen $2" /sys/fs/cgroup/"${slice:-system.slice}"/"${unit:-.}"/cgroup.events
+}
+
+testcase_dbus_api() {
+    echo "Test that DBus API works:"
+    echo -n "  - Freeze(): "
+    dbus_freeze "${unit}"
+    check_freezer_state "${unit}" "frozen"
+    check_cgroup_state "$unit" 1
+    echo "[ OK ]"
+
+    echo -n "  - Thaw(): "
+    dbus_thaw "${unit}"
+    check_freezer_state "${unit}" "running"
+    check_cgroup_state "$unit" 0
+    echo "[ OK ]"
+
+    echo -n "  - FreezeUnit(): "
+    dbus_freeze_unit "${unit}"
+    check_freezer_state "${unit}" "frozen"
+    check_cgroup_state "$unit" 1
+    echo "[ OK ]"
+
+    echo -n "  - ThawUnit(): "
+    dbus_thaw_unit "${unit}"
+    check_freezer_state "${unit}" "running"
+    check_cgroup_state "$unit" 0
+    echo "[ OK ]"
+
+    echo -n "  - CanFreeze(): "
+    output=$(dbus_can_freeze "${unit}")
+    [ "$output" = "b true" ]
+    echo "[ OK ]"
+
+    echo
+}
+
+testcase_systemctl() {
+    echo "Test that systemctl freeze/thaw verbs:"
+
+    systemctl start "$unit"
+
+    echo -n "  - freeze: "
+    systemctl freeze "$unit"
+    check_freezer_state "${unit}" "frozen"
+    check_cgroup_state "$unit" 1
+    # Freezing already frozen unit should be NOP and return quickly
+    timeout 3s systemctl freeze "$unit"
+    echo "[ OK ]"
+
+    echo -n "  - thaw: "
+    systemctl thaw "$unit"
+    check_freezer_state "${unit}" "running"
+    check_cgroup_state "$unit" 0
+    # Likewise thawing already running unit shouldn't block
+    timeout 3s systemctl thaw "$unit"
+    echo "[ OK ]"
+
+    systemctl stop "$unit"
+
+    echo
+}
+
+testcase_systemctl_show() {
+    echo "Test systemctl show integration:"
+
+    systemctl start "$unit"
+
+    echo -n "  - FreezerState property: "
+    state=$(systemctl show -p FreezerState --value "$unit")
+    [ "$state" = "running" ]
+    systemctl freeze "$unit"
+    state=$(systemctl show -p FreezerState --value "$unit")
+    [ "$state" = "frozen" ]
+    systemctl thaw "$unit"
+    echo "[ OK ]"
+
+    echo -n "  - CanFreeze property: "
+    state=$(systemctl show -p CanFreeze --value "$unit")
+    [ "$state" = "yes" ]
+    echo "[ OK ]"
+
+    systemctl stop "$unit"
+    echo
+}
+
+testcase_recursive() {
+    local slice="bar.slice"
+    local unit="baz.service"
+
+    systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
+
+    echo "Test recursive freezing:"
+
+    echo -n "  - freeze/thaw parent: "
+    systemctl freeze "$slice"
+    check_freezer_state "$slice" "frozen"
+    check_freezer_state "$unit" "frozen-by-parent"
+    check_cgroup_state "$slice/" 1
+    check_cgroup_state "$slice/$unit" 1
+    systemctl thaw "$slice"
+    check_freezer_state "$slice" "running"
+    check_freezer_state "$unit" "running"
+    check_cgroup_state "$slice/" 0
+    check_cgroup_state "$slice/$unit" 0
+    echo "[ OK ]"
+
+    echo -n "  - child freeze/thaw during frozen parent: "
+    systemctl freeze "$slice"
+    check_freezer_state "$slice" "frozen"
+    check_freezer_state "$unit" "frozen-by-parent"
+    check_cgroup_state "$slice/" 1
+    check_cgroup_state "$slice/$unit" 1
+    systemctl freeze "$unit"
+    check_freezer_state "$slice" "frozen"
+    check_freezer_state "$unit" "frozen"
+    check_cgroup_state "$slice/" 1
+    check_cgroup_state "$slice/$unit" 1
+    systemctl thaw "$unit"
+    check_freezer_state "$slice" "frozen"
+    check_freezer_state "$unit" "frozen-by-parent"
+    check_cgroup_state "$slice/" 1
+    check_cgroup_state "$slice/$unit" 1
+    systemctl thaw "$slice"
+    check_freezer_state "$slice" "running"
+    check_freezer_state "$unit" "running"
+    check_cgroup_state "$slice/" 0
+    check_cgroup_state "$slice/$unit" 0
+    echo "[ OK ]"
+
+    echo -n "  - pre-frozen child not thawed by parent: "
+    systemctl freeze "$unit"
+    check_freezer_state "$slice" "running"
+    check_freezer_state "$unit" "frozen"
+    check_cgroup_state "$slice/" 0
+    check_cgroup_state "$slice/$unit" 1
+    systemctl freeze "$slice"
+    check_freezer_state "$slice" "frozen"
+    check_freezer_state "$unit" "frozen"
+    check_cgroup_state "$slice/" 1
+    check_cgroup_state "$slice/$unit" 1
+    systemctl thaw "$slice"
+    check_freezer_state "$slice" "running"
+    check_freezer_state "$unit" "frozen"
+    check_cgroup_state "$slice/" 0
+    check_cgroup_state "$slice/$unit" 1
+    echo "[ OK ]"
+
+    echo -n "  - pre-frozen child demoted and thawed by parent: "
+    systemctl freeze "$slice"
+    check_freezer_state "$slice" "frozen"
+    check_freezer_state "$unit" "frozen"
+    check_cgroup_state "$slice/" 1
+    check_cgroup_state "$slice/$unit" 1
+    systemctl thaw "$unit"
+    check_freezer_state "$slice" "frozen"
+    check_freezer_state "$unit" "frozen-by-parent"
+    check_cgroup_state "$slice/" 1
+    check_cgroup_state "$slice/$unit" 1
+    systemctl thaw "$slice"
+    check_freezer_state "$slice" "running"
+    check_freezer_state "$unit" "running"
+    check_cgroup_state "$slice/" 0
+    check_cgroup_state "$slice/$unit" 0
+    echo "[ OK ]"
+
+    echo -n "  - child promoted and not thawed by parent: "
+    systemctl freeze "$slice"
+    check_freezer_state "$slice" "frozen"
+    check_freezer_state "$unit" "frozen-by-parent"
+    check_cgroup_state "$slice/" 1
+    check_cgroup_state "$slice/$unit" 1
+    systemctl freeze "$unit"
+    check_freezer_state "$slice" "frozen"
+    check_freezer_state "$unit" "frozen"
+    check_cgroup_state "$slice/" 1
+    check_cgroup_state "$slice/$unit" 1
+    systemctl thaw "$slice"
+    check_freezer_state "$slice" "running"
+    check_freezer_state "$unit" "frozen"
+    check_cgroup_state "$slice/" 0
+    check_cgroup_state "$slice/$unit" 1
+    echo "[ OK ]"
+
+    echo -n "  - can't stop a frozen unit: "
+    (! systemctl -q stop "$unit" )
+    echo "[ OK ]"
+    systemctl thaw "$unit"
+
+    systemctl stop "$unit"
+    systemctl stop "$slice"
+
+    echo
+}
+
+testcase_preserve_state() {
+    local slice="bar.slice"
+    local unit="baz.service"
+
+    systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
+
+    echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):"
+
+    echo -n "  - freeze from outside: "
+    echo 1 >/sys/fs/cgroup/"$slice"/cgroup.freeze
+    # Give kernel some time to freeze the slice
+    sleep 1
+
+    # Our state should not be affected
+    check_freezer_state "$slice" "running"
+    check_freezer_state "$unit" "running"
+
+    # However actual kernel state should be frozen
+    check_cgroup_state "$slice/" 1
+    check_cgroup_state "$slice/$unit" 1
+    echo "[ OK ]"
+
+    echo -n "  - thaw from outside: "
+    echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze
+    sleep 1
+
+    check_freezer_state "$unit" "running"
+    check_freezer_state "$slice" "running"
+    check_cgroup_state "$slice/" 0
+    check_cgroup_state "$slice/$unit" 0
+    echo "[ OK ]"
+
+    echo -n "  - thaw from outside while inner service is frozen: "
+    systemctl freeze "$unit"
+    check_freezer_state "$unit" "frozen"
+    echo 1 >/sys/fs/cgroup/"$slice"/cgroup.freeze
+    echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze
+    check_freezer_state "$slice" "running"
+    check_freezer_state "$unit" "frozen"
+    echo "[ OK ]"
+
+    systemctl thaw "$unit"
+    systemctl stop "$unit"
+    systemctl stop "$slice"
+
+    echo
+}
+
+if [[ -e /sys/fs/cgroup/system.slice/cgroup.freeze ]]; then
+    start_test_service
+    run_testcases
+fi
+
+touch /testok
diff --git a/test/units/TEST-43-PRIVATEUSER-UNPRIV.service b/test/units/TEST-43-PRIVATEUSER-UNPRIV.service
new file mode 100644 (file)
index 0000000..e36afea
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-43-PRIVATEUSER-UNPRIV
+After=systemd-logind.service user@4711.service
+Wants=user@4711.service
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-43-PRIVATEUSER-UNPRIV.sh b/test/units/TEST-43-PRIVATEUSER-UNPRIV.sh
new file mode 100755 (executable)
index 0000000..165af47
--- /dev/null
@@ -0,0 +1,145 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+install_extension_images
+
+if [[ "$(sysctl -ne kernel.apparmor_restrict_unprivileged_userns)" -eq 1 ]]; then
+    echo "Cannot create unprivileged user namespaces" >/skipped
+    exit 77
+fi
+
+systemd-analyze log-level debug
+
+runas testuser systemd-run --wait --user --unit=test-private-users \
+    -p PrivateUsers=yes -P echo hello
+
+runas testuser systemctl --user log-level debug
+
+runas testuser systemd-run --wait --user --unit=test-private-tmp-innerfile \
+    -p PrivateTmp=yes \
+    -P touch /tmp/innerfile.txt
+# File should not exist outside the job's tmp directory.
+test ! -e /tmp/innerfile.txt
+
+touch /tmp/outerfile.txt
+# File should not appear in unit's private tmp.
+runas testuser systemd-run --wait --user --unit=test-private-tmp-outerfile \
+    -p PrivateTmp=yes \
+    -P test ! -e /tmp/outerfile.txt
+
+# Confirm that creating a file in home works
+runas testuser systemd-run --wait --user --unit=test-unprotected-home \
+    -P touch /home/testuser/works.txt
+test -e /home/testuser/works.txt
+
+# Confirm that creating a file in home is blocked under read-only
+(! runas testuser systemd-run --wait --user --unit=test-protect-home-read-only \
+    -p ProtectHome=read-only \
+    -P bash -c '
+        test -e /home/testuser/works.txt || exit 10
+        touch /home/testuser/blocked.txt && exit 11
+    ')
+test ! -e /home/testuser/blocked.txt
+
+# Check that tmpfs hides the whole directory
+runas testuser systemd-run --wait --user --unit=test-protect-home-tmpfs \
+    -p ProtectHome=tmpfs \
+    -P test ! -e /home/testuser
+
+# Confirm that home, /root, and /run/user are inaccessible under "yes"
+# shellcheck disable=SC2016
+runas testuser systemd-run --wait --user --unit=test-protect-home-yes \
+    -p ProtectHome=yes \
+    -P bash -c '
+        test "$(stat -c %a /home)" = "0"
+        test "$(stat -c %a /root)" = "0"
+        test "$(stat -c %a /run/user)" = "0"
+    '
+
+# Confirm we cannot change groups because we only have one mapping in the user
+# namespace (no CAP_SETGID in the parent namespace to write the additional
+# mapping of the user supplied group and thus cannot change groups to an
+# unmapped group ID)
+(! runas testuser systemd-run --wait --user --unit=test-group-fail \
+    -p PrivateUsers=yes -p Group=daemon \
+    -P true)
+
+# Check that with a new user namespace we can bind mount
+# files and use a different root directory
+runas testuser systemd-run --wait --user --unit=test-bind-mount \
+    -p BindPaths=/dev/null:/etc/os-release \
+    test ! -s /etc/os-release
+
+runas testuser systemd-run --wait --user --unit=test-read-write \
+    -p ReadOnlyPaths=/ \
+    -p ReadWritePaths="/var /run /tmp" \
+    -p NoExecPaths=/ -p ExecPaths=/usr \
+    test ! -w /etc/os-release
+
+runas testuser systemd-run --wait --user --unit=test-caps \
+    -p PrivateUsers=yes -p AmbientCapabilities=CAP_SYS_ADMIN \
+    -p CapabilityBoundingSet=CAP_SYS_ADMIN \
+    test -s /etc/os-release
+
+runas testuser systemd-run --wait --user --unit=test-devices \
+    -p PrivateDevices=yes -p PrivateIPC=yes \
+    sh -c "ls -1 /dev/ | wc -l | grep -q -F 18"
+
+# Same check as test/test-execute/exec-privatenetwork-yes.service
+runas testuser systemd-run --wait --user --unit=test-network \
+    -p PrivateNetwork=yes \
+    /bin/sh -x -c '! ip link | grep -E "^[0-9]+: " | grep -Ev ": (lo|(erspan|gre|gretap|ip_vti|ip6_vti|ip6gre|ip6tnl|sit|tunl)0@.*):"'
+
+(! runas testuser systemd-run --wait --user --unit=test-hostname \
+    -p ProtectHostname=yes \
+    hostnamectl hostname foo)
+
+(! runas testuser systemd-run --wait --user --unit=test-clock \
+    -p ProtectClock=yes \
+    timedatectl set-time "2012-10-30 18:17:16")
+
+(! runas testuser systemd-run --wait --user --unit=test-kernel-tunable \
+    -p ProtectKernelTunables=yes \
+    sh -c "echo 0 >/proc/sys/user/max_user_namespaces")
+
+(! runas testuser systemd-run --wait --user --unit=test-kernel-mod \
+    -p ProtectKernelModules=yes \
+    sh -c "modprobe -r overlay && modprobe overlay")
+
+if sysctl kernel.dmesg_restrict=0; then
+    (! runas testuser systemd-run --wait --user --unit=test-kernel-log \
+        -p ProtectKernelLogs=yes -p LogNamespace=yes \
+        dmesg)
+fi
+
+unsquashfs -no-xattrs -d /tmp/img /usr/share/minimal_0.raw
+runas testuser systemd-run --wait --user --unit=test-root-dir \
+    -p RootDirectory=/tmp/img \
+    grep MARKER=1 /etc/os-release
+
+mkdir /tmp/img_bind
+mount --bind /tmp/img /tmp/img_bind
+runas testuser systemd-run --wait --user --unit=test-root-dir-bind \
+    -p RootDirectory=/tmp/img_bind -p MountFlags=private \
+    grep MARKER=1 /etc/os-release
+umount /tmp/img_bind
+
+# Unprivileged overlayfs was added to Linux 5.11, so try to detect it first
+mkdir -p /tmp/a /tmp/b /tmp/c
+if unshare --mount --user --map-root-user mount -t overlay overlay /tmp/c -o lowerdir=/tmp/a:/tmp/b; then
+    unsquashfs -no-xattrs -d /tmp/app2 /tmp/app1.raw
+    runas testuser systemd-run --wait --user --unit=test-extension-dir \
+        -p ExtensionDirectories=/tmp/app2 \
+        -p TemporaryFileSystem=/run -p RootDirectory=/tmp/img \
+        -p MountAPIVFS=yes \
+        grep PORTABLE_PREFIXES=app1 /usr/lib/extension-release.d/extension-release.app2
+fi
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-44-LOG-NAMESPACE.service b/test/units/TEST-44-LOG-NAMESPACE.service
new file mode 100644 (file)
index 0000000..6189d3a
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-44-LOG-NAMESPACE
+Before=getty-pre.target
+Wants=getty-pre.target
+Wants=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
+After=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-44-LOG-NAMESPACE.sh b/test/units/TEST-44-LOG-NAMESPACE.sh
new file mode 100755 (executable)
index 0000000..0819a4b
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+
+systemd-analyze log-level debug
+
+journalctl --list-namespaces -o json | jq .
+
+systemd-run --wait -p LogNamespace=foobar echo "hello world"
+systemd-run --wait -p LogNamespace=foobaz echo "hello world"
+
+journalctl --namespace=foobar --sync
+journalctl --namespace=foobaz --sync
+ls -l /var/log/journal/
+journalctl --list-namespaces
+
+journalctl -o cat --namespace=foobar >/tmp/hello-world
+journalctl -o cat >/tmp/no-hello-world
+
+journalctl --list-namespaces | grep foobar
+journalctl --list-namespaces | grep foobaz
+journalctl --list-namespaces -o json | jq .
+[[ "$(journalctl --root=/tmp --list-namespaces --quiet)" == "" ]]
+
+grep "^hello world$" /tmp/hello-world
+(! grep "^hello world$" /tmp/no-hello-world)
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-45-TIMEDATE.service b/test/units/TEST-45-TIMEDATE.service
new file mode 100644 (file)
index 0000000..b16ce99
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-45-TIMEDATE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-45-TIMEDATE.sh b/test/units/TEST-45-TIMEDATE.sh
new file mode 100755 (executable)
index 0000000..dff3ed0
--- /dev/null
@@ -0,0 +1,408 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+testcase_timedatectl() {
+    timedatectl --no-pager --help
+    timedatectl --version
+
+    timedatectl
+    timedatectl --no-ask-password
+    timedatectl status --machine=testuser@.host
+    timedatectl status
+    timedatectl show
+    timedatectl show --all
+    timedatectl show -p NTP
+    timedatectl show -p NTP --value
+    timedatectl list-timezones
+
+    if ! systemd-detect-virt -qc; then
+        systemctl enable --runtime --now systemd-timesyncd
+        timedatectl timesync-status
+        timedatectl show-timesync
+    fi
+}
+
+restore_timezone() {
+    if [[ -f /tmp/timezone.bak ]]; then
+        mv /tmp/timezone.bak /etc/timezone
+    else
+        rm -f /etc/timezone
+    fi
+}
+
+testcase_timezone() {
+    local ORIG_TZ=
+
+    # Debian/Ubuntu specific file
+    if [[ -f /etc/timezone ]]; then
+        mv /etc/timezone /tmp/timezone.bak
+    fi
+
+    trap restore_timezone RETURN
+
+    if [[ -L /etc/localtime ]]; then
+        ORIG_TZ=$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')
+        echo "original tz: $ORIG_TZ"
+    fi
+
+    echo 'timedatectl works'
+    assert_in "Local time:" "$(timedatectl --no-pager)"
+
+    echo 'change timezone'
+    assert_eq "$(timedatectl --no-pager set-timezone Europe/Kyiv 2>&1)" ""
+    assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "Europe/Kyiv"
+    if [[ -f /etc/timezone ]]; then
+        assert_eq "$(cat /etc/timezone)" "Europe/Kyiv"
+    fi
+    assert_in "Time zone: Europe/Kyiv \(EES*T, \+0[0-9]00\)" "$(timedatectl)"
+
+    if [[ -n "$ORIG_TZ" ]]; then
+        echo 'reset timezone to original'
+        assert_eq "$(timedatectl set-timezone "$ORIG_TZ" 2>&1)" ""
+        assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "$ORIG_TZ"
+        if [[ -f /etc/timezone ]]; then
+            assert_eq "$(cat /etc/timezone)" "$ORIG_TZ"
+        fi
+    fi
+}
+
+restore_adjtime() {
+    if [[ -e /etc/adjtime.bak ]]; then
+        mv /etc/adjtime.bak /etc/adjtime
+    else
+        rm /etc/adjtime
+    fi
+}
+
+check_adjtime_not_exist() {
+    if [[ -e /etc/adjtime ]]; then
+        echo "/etc/adjtime unexpectedly exists." >&2
+        exit 1
+    fi
+}
+
+testcase_adjtime() {
+    # test setting UTC vs. LOCAL in /etc/adjtime
+    if [[ -e /etc/adjtime ]]; then
+        mv /etc/adjtime /etc/adjtime.bak
+    fi
+
+    trap restore_adjtime RETURN
+
+    echo 'no adjtime file'
+    rm -f /etc/adjtime
+    timedatectl set-local-rtc 0
+    check_adjtime_not_exist
+    timedatectl set-local-rtc 1
+    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+    timedatectl set-local-rtc 0
+    check_adjtime_not_exist
+
+    echo 'UTC set in adjtime file'
+    printf '0.0 0 0\n0\nUTC\n' >/etc/adjtime
+    timedatectl set-local-rtc 0
+    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+UTC"
+    timedatectl set-local-rtc 1
+    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+    echo 'non-zero values in adjtime file'
+    printf '0.1 123 0\n0\nLOCAL\n' >/etc/adjtime
+    timedatectl set-local-rtc 0
+    assert_eq "$(cat /etc/adjtime)" "0.1 123 0
+0
+UTC"
+    timedatectl set-local-rtc 1
+    assert_eq "$(cat /etc/adjtime)" "0.1 123 0
+0
+LOCAL"
+
+    echo 'fourth line adjtime file'
+    printf '0.0 0 0\n0\nLOCAL\nsomethingelse\n' >/etc/adjtime
+    timedatectl set-local-rtc 0
+    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+UTC
+somethingelse"
+    timedatectl set-local-rtc 1
+    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL
+somethingelse"
+
+    echo 'no final newline in adjtime file'
+    printf '0.0 0 0\n0\nUTC' >/etc/adjtime
+    timedatectl set-local-rtc 0
+    check_adjtime_not_exist
+    printf '0.0 0 0\n0\nUTC' >/etc/adjtime
+    timedatectl set-local-rtc 1
+    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+    echo 'only one line in adjtime file'
+    printf '0.0 0 0\n' >/etc/adjtime
+    timedatectl set-local-rtc 0
+    check_adjtime_not_exist
+    printf '0.0 0 0\n' >/etc/adjtime
+    timedatectl set-local-rtc 1
+    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+    echo 'only one line in adjtime file, no final newline'
+    printf '0.0 0 0' >/etc/adjtime
+    timedatectl set-local-rtc 0
+    check_adjtime_not_exist
+    printf '0.0 0 0' >/etc/adjtime
+    timedatectl set-local-rtc 1
+    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+    echo 'only two lines in adjtime file'
+    printf '0.0 0 0\n0\n' >/etc/adjtime
+    timedatectl set-local-rtc 0
+    check_adjtime_not_exist
+    printf '0.0 0 0\n0\n' >/etc/adjtime
+    timedatectl set-local-rtc 1
+    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+    echo 'only two lines in adjtime file, no final newline'
+    printf '0.0 0 0\n0' >/etc/adjtime
+    timedatectl set-local-rtc 0
+    check_adjtime_not_exist
+    printf '0.0 0 0\n0' >/etc/adjtime
+    timedatectl set-local-rtc 1
+    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+    echo 'unknown value in 3rd line of adjtime file'
+    printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime
+    timedatectl set-local-rtc 0
+    check_adjtime_not_exist
+    printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime
+    timedatectl set-local-rtc 1
+    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+}
+
+assert_ntp() {
+    local value="${1:?}"
+
+    for _ in {0..9}; do
+        [[ "$(busctl get-property org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 NTP)" == "b $value" ]] && return 0
+        sleep .5
+    done
+
+    return 1
+}
+
+assert_timedated_signal() {
+    local timestamp="${1:?}"
+    local value="${2:?}"
+    local args=(-q -n 1 --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service")
+
+    journalctl --sync
+
+    for _ in {0..9}; do
+        if journalctl "${args[@]}" --grep .; then
+            [[ "$(journalctl "${args[@]}" -o cat | jq -r '.payload.data[1].NTP.data')" == "$value" ]];
+            return 0
+        fi
+
+        sleep .5
+    done
+
+    return 1
+}
+
+assert_timesyncd_state() {
+    local state="${1:?}"
+
+    for _ in {0..9}; do
+        [[ "$(systemctl show systemd-timesyncd.service -P ActiveState)" == "$state" ]] && return 0
+        sleep .5
+    done
+
+    return 1
+}
+
+testcase_ntp() {
+    # timesyncd has ConditionVirtualization=!container by default; drop/mock that for testing
+    if systemd-detect-virt --container --quiet; then
+        systemctl disable --quiet --now systemd-timesyncd
+        mkdir -p /run/systemd/system/systemd-timesyncd.service.d
+        cat >/run/systemd/system/systemd-timesyncd.service.d/container.conf <<EOF
+[Unit]
+ConditionVirtualization=
+
+[Service]
+Type=simple
+AmbientCapabilities=
+ExecStart=
+ExecStart=sleep infinity
+EOF
+        systemctl daemon-reload
+    fi
+
+    systemd-run --unit busctl-monitor.service --service-type=notify \
+        busctl monitor --json=short --match="type=signal,sender=org.freedesktop.timedate1,member=PropertiesChanged,path=/org/freedesktop/timedate1"
+
+    : 'Disable NTP'
+    ts="$(date +"%F %T.%6N")"
+    timedatectl set-ntp false
+    assert_timedated_signal "$ts" "false"
+    assert_timesyncd_state "inactive"
+    assert_ntp "false"
+    assert_rc 3 systemctl is-active --quiet systemd-timesyncd
+
+    : 'Enable NTP'
+    ts="$(date +"%F %T.%6N")"
+    timedatectl set-ntp true
+    assert_timedated_signal "$ts" "true"
+    assert_ntp "true"
+    assert_timesyncd_state "active"
+    assert_rc 0 systemctl is-active --quiet systemd-timesyncd
+
+    : 'Re-disable NTP'
+    ts="$(date +"%F %T.%6N")"
+    timedatectl set-ntp false
+    assert_timedated_signal "$ts" "false"
+    assert_ntp "false"
+    assert_rc 3 systemctl is-active --quiet systemd-timesyncd
+
+    systemctl stop busctl-monitor.service
+    rm -rf /run/systemd/system/systemd-timesyncd.service.d/
+    systemctl daemon-reload
+}
+
+assert_timesyncd_signal() {
+    local timestamp="${1:?}"
+    local property="${2:?}"
+    local value="${3:?}"
+    local args=(-q --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service")
+
+    journalctl --sync
+
+    for _ in {0..9}; do
+        if journalctl "${args[@]}" --grep .; then
+            [[ "$(journalctl "${args[@]}" -o cat | jq -r ".payload.data[1].$property.data | join(\" \")")" == "$value" ]];
+            return 0
+        fi
+
+        sleep .5
+    done
+
+    return 1
+}
+
+assert_networkd_ntp() {
+    local interface="${1:?}"
+    local value="${2:?}"
+    # Go through the array of NTP servers and for each entry do:
+    #   - if the entry is an IPv4 address, join the Address array into a dot separated string
+    #   - if the entry is a server address, select it unchanged
+    # These steps produce an array of strings, that is then joined into a space-separated string
+    # Note: this doesn't support IPv6 addresses, since converting them to a string is a bit more
+    # involved than a simple join(), but let's leave that to another time
+    local expr='[.NTP[] | (select(.Family == 2).Address | join(".")), select(has("Server")).Server] | join(" ")'
+
+    [[ "$(networkctl status "$interface" --json=short | jq -r "$expr")" == "$value" ]]
+}
+
+testcase_timesyncd() {
+    if systemd-detect-virt -cq; then
+        echo "This test case requires a VM, skipping..."
+        return 0
+    fi
+
+    if ! command -v networkctl >/dev/null; then
+        echo "This test requires systemd-networkd, skipping..."
+        return 0
+    fi
+
+    # Create a dummy interface managed by networkd, so we can configure link NTP servers
+    mkdir -p /run/systemd/network/
+    cat >/etc/systemd/network/10-ntp99.netdev <<EOF
+[NetDev]
+Name=ntp99
+Kind=dummy
+EOF
+    cat >/etc/systemd/network/10-ntp99.network <<EOF
+[Match]
+Name=ntp99
+
+[Network]
+Address=10.0.0.1/24
+EOF
+
+    systemctl unmask systemd-timesyncd systemd-networkd
+    systemctl restart systemd-timesyncd
+    systemctl restart systemd-networkd
+    networkctl status ntp99
+
+    systemd-run --unit busctl-monitor.service --service-type=notify \
+        busctl monitor --json=short --match="type=signal,sender=org.freedesktop.timesync1,member=PropertiesChanged,path=/org/freedesktop/timesync1"
+
+    # LinkNTPServers
+    #
+    # Single IP
+    ts="$(date +"%F %T.%6N")"
+    timedatectl ntp-servers ntp99 10.0.0.1
+    assert_networkd_ntp ntp99 10.0.0.1
+    assert_timesyncd_signal "$ts" LinkNTPServers 10.0.0.1
+    # Setting NTP servers to the same value shouldn't emit a PropertiesChanged signal
+    ts="$(date +"%F %T.%6N")"
+    timedatectl ntp-servers ntp99 10.0.0.1
+    assert_networkd_ntp ntp99 10.0.0.1
+    (! assert_timesyncd_signal "$ts" LinkNTPServers 10.0.0.1)
+    # Multiple IPs
+    ts="$(date +"%F %T.%6N")"
+    timedatectl ntp-servers ntp99 10.0.0.1 192.168.0.99
+    assert_networkd_ntp ntp99 "10.0.0.1 192.168.0.99"
+    assert_timesyncd_signal "$ts" LinkNTPServers "10.0.0.1 192.168.0.99"
+    # Multiple IPs + servers
+    ts="$(date +"%F %T.%6N")"
+    timedatectl ntp-servers ntp99 10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13
+    assert_networkd_ntp ntp99 "10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13"
+    assert_timesyncd_signal "$ts" LinkNTPServers "10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13"
+
+    # RuntimeNTPServers
+    #
+    # There's no user-facing API that allows changing this property (afaik), so let's
+    # call SetRuntimeNTPServers() directly to test things out. The inner workings should
+    # be exactly the same as in the previous case, so do just one test to make sure
+    # things work
+    ts="$(date +"%F %T.%6N")"
+    busctl call org.freedesktop.timesync1 /org/freedesktop/timesync1 org.freedesktop.timesync1.Manager \
+        SetRuntimeNTPServers as 4 "10.0.0.1" foo "192.168.99.1" bar
+    servers="$(busctl get-property org.freedesktop.timesync1 /org/freedesktop/timesync1 org.freedesktop.timesync1.Manager RuntimeNTPServers)"
+    [[ "$servers" == 'as 4 "10.0.0.1" "foo" "192.168.99.1" "bar"' ]]
+    assert_timesyncd_signal "$ts" RuntimeNTPServers "10.0.0.1 foo 192.168.99.1 bar"
+
+    # Cleanup
+    systemctl stop systemd-networkd systemd-timesyncd
+    rm -f /run/systemd/network/ntp99.*
+}
+
+run_testcases
+
+touch /testok
diff --git a/test/units/TEST-46-HOMED.service b/test/units/TEST-46-HOMED.service
new file mode 100644 (file)
index 0000000..5efb9cc
--- /dev/null
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-46-HOMED
+Wants=getty-pre.target
+Before=getty-pre.target
+Requires=systemd-homed.service systemd-userdbd.socket
+After=systemd-homed.service systemd-userdbd.socket
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
+NotifyAccess=all
diff --git a/test/units/TEST-46-HOMED.sh b/test/units/TEST-46-HOMED.sh
new file mode 100755 (executable)
index 0000000..5dc00f4
--- /dev/null
@@ -0,0 +1,612 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check if homectl is installed, and if it isn't bail out early instead of failing
+if ! test -x /usr/bin/homectl ; then
+        echo "no homed" >/skipped
+        exit 77
+fi
+
+inspect() {
+    # As updating disk-size-related attributes can take some time on some
+    # filesystems, let's drop these fields before comparing the outputs to
+    # avoid unexpected fails. To see the full outputs of both homectl &
+    # userdbctl (for debugging purposes) drop the fields just before the
+    # comparison.
+    local USERNAME="${1:?}"
+    homectl inspect "$USERNAME" | tee /tmp/a
+    userdbctl user "$USERNAME" | tee /tmp/b
+
+    # diff uses the grep BREs for pattern matching
+    diff -I '^\s*Disk \(Size\|Free\|Floor\|Ceiling\|Usage\):' /tmp/{a,b}
+    rm /tmp/{a,b}
+
+    homectl inspect --json=pretty "$USERNAME"
+}
+
+wait_for_state() {
+    for i in {1..10}; do
+        (( i > 1 )) && sleep 0.5
+        homectl inspect "$1" | grep -qF "State: $2" && break
+    done
+}
+
+FSTYPE="$(stat --file-system --format "%T" /)"
+
+systemd-analyze log-level debug
+systemctl service-log-level systemd-homed debug
+
+# Create a tmpfs to use as backing store for the home dir. That way we can enforce a size limit nicely.
+mkdir -p /home
+mount -t tmpfs tmpfs /home -o size=290M
+
+# we enable --luks-discard= since we run our tests in a tight VM, hence don't
+# needlessly pressure for storage. We also set the cheapest KDF, since we don't
+# want to waste CI CPU cycles on it. We also effectively disable rate-limiting on
+# the user by allowing 1000 logins per second
+NEWPASSWORD=xEhErW0ndafV4s homectl create test-user \
+           --disk-size=min \
+           --luks-discard=yes \
+           --image-path=/home/test-user.home \
+           --luks-pbkdf-type=pbkdf2 \
+           --luks-pbkdf-time-cost=1ms \
+           --rate-limit-interval=1s \
+           --rate-limit-burst=1000
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl authenticate test-user
+
+PASSWORD=xEhErW0ndafV4s homectl activate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inline test"
+inspect test-user
+
+homectl deactivate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s NEWPASSWORD=yPN4N0fYNKUkOq homectl passwd test-user
+inspect test-user
+
+PASSWORD=yPN4N0fYNKUkOq homectl activate test-user
+inspect test-user
+
+SYSTEMD_LOG_LEVEL=debug PASSWORD=yPN4N0fYNKUkOq NEWPASSWORD=xEhErW0ndafV4s homectl passwd test-user
+inspect test-user
+
+homectl deactivate test-user
+inspect test-user
+
+homectl update test-user --real-name "Offline test" --offline
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl activate test-user
+inspect test-user
+
+# Ensure that the offline changes were propagated in
+grep "Offline test" /home/test-user/.identity
+
+homectl deactivate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inactive test"
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl activate test-user
+inspect test-user
+
+homectl deactivate test-user
+inspect test-user
+
+# Do some keyring tests, but only on real kernels, since keyring access inside of containers will fail
+# (See: https://github.com/systemd/systemd/issues/17606)
+if ! systemd-detect-virt -cq ; then
+        PASSWORD=xEhErW0ndafV4s homectl activate test-user
+        inspect test-user
+
+        # Key should now be in the keyring
+        homectl update test-user --real-name "Keyring Test"
+        inspect test-user
+
+        # These commands shouldn't use the keyring
+        (! timeout 5s homectl authenticate test-user )
+        (! NEWPASSWORD="foobar" timeout 5s homectl passwd test-user )
+
+        homectl lock test-user
+        inspect test-user
+
+        # Key should be gone from keyring
+        (! timeout 5s homectl update test-user --real-name "Keyring Test 2" )
+
+        PASSWORD=xEhErW0ndafV4s homectl unlock test-user
+        inspect test-user
+
+        # Key should have been re-instantiated into the keyring
+        homectl update test-user --real-name "Keyring Test 3"
+        inspect test-user
+
+        homectl deactivate test-user
+        inspect test-user
+fi
+
+# Do some resize tests, but only if we run on real kernels and are on btrfs, as quota inside of containers
+# will fail and minimizing while active only works on btrfs.
+if ! systemd-detect-virt -cq && [[ "$FSTYPE" == "btrfs" ]]; then
+    # grow while inactive
+    PASSWORD=xEhErW0ndafV4s homectl resize test-user 300M
+    inspect test-user
+
+    # minimize while inactive
+    PASSWORD=xEhErW0ndafV4s homectl resize test-user min
+    inspect test-user
+
+    PASSWORD=xEhErW0ndafV4s homectl activate test-user
+    inspect test-user
+
+    # grow while active
+    PASSWORD=xEhErW0ndafV4s homectl resize test-user max
+    inspect test-user
+
+    # minimize while active
+    PASSWORD=xEhErW0ndafV4s homectl resize test-user 0
+    inspect test-user
+
+    # grow while active
+    PASSWORD=xEhErW0ndafV4s homectl resize test-user 300M
+    inspect test-user
+
+    # shrink to original size while active
+    PASSWORD=xEhErW0ndafV4s homectl resize test-user 256M
+    inspect test-user
+
+    # minimize again
+    PASSWORD=xEhErW0ndafV4s homectl resize test-user min
+    inspect test-user
+
+    # Increase space, so that we can reasonably rebalance free space between to home dirs
+    mount /home -o remount,size=800M
+
+    # create second user
+    NEWPASSWORD=uuXoo8ei homectl create test-user2 \
+           --disk-size=min \
+           --luks-discard=yes \
+           --image-path=/home/test-user2.home \
+           --luks-pbkdf-type=pbkdf2 \
+           --luks-pbkdf-time-cost=1ms \
+           --rate-limit-interval=1s \
+           --rate-limit-burst=1000
+    inspect test-user2
+
+    # activate second user
+    PASSWORD=uuXoo8ei homectl activate test-user2
+    inspect test-user2
+
+    # set second user's rebalance weight to 100
+    PASSWORD=uuXoo8ei homectl update test-user2 --rebalance-weight=100
+    inspect test-user2
+
+    # set first user's rebalance weight to quarter of that of the second
+    PASSWORD=xEhErW0ndafV4s homectl update test-user --rebalance-weight=25
+    inspect test-user
+
+    # synchronously rebalance
+    homectl rebalance
+    inspect test-user
+    inspect test-user2
+
+    wait_for_state test-user2 active
+    homectl deactivate test-user2
+    wait_for_state test-user2 inactive
+    homectl remove test-user2
+fi
+
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
+(! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
+(! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
+
+# Regression tests
+wait_for_state test-user inactive
+/usr/lib/systemd/tests/unit-tests/manual/test-homed-regression-31896 test-user
+
+wait_for_state test-user inactive
+homectl remove test-user
+
+# blob directory tests
+# See docs/USER_RECORD_BLOB_DIRS.md
+checkblob() {
+        test -f "/var/cache/systemd/home/blob-user/$1"
+        stat -c "%u %#a" "/var/cache/systemd/home/blob-user/$1" | grep "^0 0644"
+        test -f "/home/blob-user/.identity-blob/$1"
+        stat -c "%u %#a" "/home/blob-user/.identity-blob/$1" | grep "^12345 0644"
+
+        diff "/var/cache/systemd/home/blob-user/$1" "$2"
+        diff "/var/cache/systemd/home/blob-user/$1" "/home/blob-user/.identity-blob/$1"
+}
+
+mkdir /tmp/blob1 /tmp/blob2
+echo data1 blob1 >/tmp/blob1/test1
+echo data1 blob2 >/tmp/blob2/test1
+echo data2 blob1 >/tmp/blob1/test2
+echo data2 blob2 >/tmp/blob2/test2
+echo invalid filename >/tmp/blob1/файл
+echo data3 >/tmp/external-test3
+echo avatardata >/tmp/external-avatar
+ln -s /tmp/external-avatar /tmp/external-avatar-lnk
+dd if=/dev/urandom of=/tmp/external-barely-fits bs=1M count=64
+dd if=/dev/urandom of=/tmp/external-toobig bs=1M count=65
+
+# create w/ prepopulated blob dir
+NEWPASSWORD=EMJuc3zQaMibJo homectl create blob-user \
+           --disk-size=min --luks-discard=yes \
+           --luks-pbkdf-type=pbkdf2 --luks-pbkdf-time-cost=1ms \
+           --rate-limit-interval=1s --rate-limit-burst=1000 \
+           --uid=12345 \
+           --blob=/tmp/blob1
+inspect blob-user
+PASSWORD=EMJuc3zQaMibJo homectl activate blob-user
+inspect blob-user
+
+test -d /var/cache/systemd/home/blob-user
+stat -c "%u %#a" /var/cache/systemd/home/blob-user | grep "^0 0755"
+test -d /home/blob-user/.identity-blob
+stat -c "%u %#a" /home/blob-user/.identity-blob | grep "^12345 0700"
+
+checkblob test1 /tmp/blob1/test1
+(! checkblob test1 /tmp/blob2/test1 )
+checkblob test2 /tmp/blob1/test2
+(! checkblob test2 /tmp/blob2/test2 )
+(! checkblob фаил /tmp/blob1/фаил )
+(! checkblob test3 /tmp/external-test3 )
+(! checkblob avatar /tmp/external-avatar )
+
+# append files to existing blob, both well-known and other
+PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
+        -b test3=/tmp/external-test3 --avatar=/tmp/external-avatar
+inspect blob-user
+checkblob test1 /tmp/blob1/test1
+(! checkblob test1 /tmp/blob2/test1 )
+checkblob test2 /tmp/blob1/test2
+(! checkblob test2 /tmp/blob2/test2 )
+(! checkblob фаил /tmp/blob1/фаил )
+checkblob test3 /tmp/external-test3
+checkblob avatar /tmp/external-avatar
+
+# delete files from existing blob, both well-known and other
+PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
+        -b test3= --avatar=
+inspect blob-user
+checkblob test1 /tmp/blob1/test1
+(! checkblob test1 /tmp/blob2/test1 )
+checkblob test2 /tmp/blob1/test2
+(! checkblob test2 /tmp/blob2/test2 )
+(! checkblob фаил /tmp/blob1/фаил )
+(! checkblob test3 /tmp/external-test3 )
+(! checkblob avatar /tmp/external-avatar )
+
+# swap entire blob directory
+PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
+        -b /tmp/blob2
+inspect blob-user
+(! checkblob test1 /tmp/blob1/test1 )
+checkblob test1 /tmp/blob2/test1
+(! checkblob test2 /tmp/blob1/test2 )
+checkblob test2 /tmp/blob2/test2
+(! checkblob фаил /tmp/blob1/фаил )
+(! checkblob test3 /tmp/external-test3 )
+(! checkblob avatar /tmp/external-avatar )
+
+# create and delete files while swapping blob directory. Also symlinks.
+PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
+        -b /tmp/blob1 -b test2= -b test3=/tmp/external-test3 --avatar=/tmp/external-avatar-lnk
+inspect blob-user
+checkblob test1 /tmp/blob1/test1
+(! checkblob test1 /tmp/blob2/test1 )
+(! checkblob test2 /tmp/blob1/test2 )
+(! checkblob test2 /tmp/blob2/test2 )
+(! checkblob фаил /tmp/blob1/фаил )
+checkblob test3 /tmp/external-test3
+checkblob avatar /tmp/external-avatar # target of the link
+
+# clear the blob directory
+PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
+        -b /tmp/blob2 -b test3=/tmp/external-test3 --blob=
+inspect blob-user
+(! checkblob test1 /tmp/blob1/test1 )
+(! checkblob test1 /tmp/blob2/test1 )
+(! checkblob test2 /tmp/blob1/test2 )
+(! checkblob test2 /tmp/blob2/test2 )
+(! checkblob фаил /tmp/blob1/фаил )
+(! checkblob test3 /tmp/external-test3 )
+(! checkblob avatar /tmp/external-avatar )
+
+# file that's exactly 64M still fits
+# FIXME: Figure out why this fails on ext4.
+if [[ "$FSTYPE" != "ext2/ext3" ]]; then
+    PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
+            -b barely-fits=/tmp/external-barely-fits
+    (! checkblob test1 /tmp/blob1/test1 )
+    (! checkblob test1 /tmp/blob2/test1 )
+    (! checkblob test2 /tmp/blob1/test2 )
+    (! checkblob test2 /tmp/blob2/test2 )
+    (! checkblob фаил /tmp/blob1/фаил )
+    (! checkblob test3 /tmp/external-test3 )
+    (! checkblob avatar /tmp/external-avatar )
+    checkblob barely-fits /tmp/external-barely-fits
+fi
+
+# error out if the file is too big
+(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b huge=/tmp/external-toobig )
+
+# error out if filenames are invalid
+(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b .hidden=/tmp/external-test3 )
+(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b "with spaces=/tmp/external-test3" )
+(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b with=equals=/tmp/external-test3 )
+(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b файл=/tmp/external-test3 )
+(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b special@chars=/tmp/external-test3 )
+
+# Make sure offline updates to blobs get propagated in
+homectl deactivate blob-user
+inspect blob-user
+homectl update blob-user --offline -b barely-fits= -b propagated=/tmp/external-test3
+inspect blob-user
+PASSWORD=EMJuc3zQaMibJo homectl activate blob-user
+inspect blob-user
+(! checkblob barely-fits /tmp/external-barely-fits )
+checkblob propagated /tmp/external-test3
+
+homectl deactivate blob-user
+wait_for_state blob-user inactive
+homectl remove blob-user
+
+# userdbctl tests
+export PAGER=
+
+# Create a couple of user/group records to test io.systemd.DropIn
+# See docs/USER_RECORD.md and docs/GROUP_RECORD.md
+mkdir -p /run/userdb/
+cat >"/run/userdb/dropingroup.group" <<\EOF
+{
+    "groupName" : "dropingroup",
+    "gid"       : 1000000
+}
+EOF
+cat >"/run/userdb/dropinuser.user" <<\EOF
+{
+    "userName" : "dropinuser",
+    "uid"      : 2000000,
+    "realName" : "🐱",
+    "memberOf" : [
+        "dropingroup"
+    ]
+}
+EOF
+cat >"/run/userdb/dropinuser.user-privileged" <<\EOF
+{
+    "privileged" : {
+        "hashedPassword" : [
+            "$6$WHBKvAFFT9jKPA4k$OPY4D4TczKN/jOnJzy54DDuOOagCcvxxybrwMbe1SVdm.Bbr.zOmBdATp.QrwZmvqyr8/SafbbQu.QZ2rRvDs/"
+        ],
+        "sshAuthorizedKeys" : [
+            "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA//dxI2xLg4MgxIKKZv1nqwTEIlE/fdakii2Fb75pG+ foo@bar.tld",
+            "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMlaqG2rTMje5CQnfjXJKmoSpEVJ2gWtx4jBvsQbmee2XbU/Qdq5+SRisssR9zVuxgg5NA5fv08MgjwJQMm+csc= hello@world.tld"
+        ]
+    }
+}
+EOF
+# Set permissions and create necessary symlinks as described in nss-systemd(8)
+chmod 0600 "/run/userdb/dropinuser.user-privileged"
+ln -svrf "/run/userdb/dropingroup.group" "/run/userdb/1000000.group"
+ln -svrf "/run/userdb/dropinuser.user" "/run/userdb/2000000.user"
+ln -svrf "/run/userdb/dropinuser.user-privileged" "/run/userdb/2000000.user-privileged"
+
+userdbctl
+userdbctl --version
+userdbctl --help --no-pager
+userdbctl --no-legend
+userdbctl --output=classic
+userdbctl --output=friendly
+userdbctl --output=table
+userdbctl --output=json | jq
+userdbctl -j --json=pretty | jq
+userdbctl -j --json=short | jq
+userdbctl --with-varlink=no
+
+userdbctl user
+userdbctl user testuser
+userdbctl user root
+userdbctl user testuser root
+userdbctl user -j testuser root | jq
+# Check only UID for the nobody user, since the name is build-configurable
+userdbctl user --with-nss=no --synthesize=yes
+userdbctl user --with-nss=no --synthesize=yes 0 root 65534
+userdbctl user dropinuser
+userdbctl user 2000000
+userdbctl user --with-nss=no --with-varlink=no --synthesize=no --multiplexer=no dropinuser
+userdbctl user --with-nss=no 2000000
+(! userdbctl user '')
+(! userdbctl user 🐱)
+(! userdbctl user 🐱 '' bar)
+(! userdbctl user i-do-not-exist)
+(! userdbctl user root i-do-not-exist testuser)
+(! userdbctl user --with-nss=no --synthesize=no 0 root 65534)
+(! userdbctl user -N root nobody)
+(! userdbctl user --with-dropin=no dropinuser)
+(! userdbctl user --with-dropin=no 2000000)
+
+userdbctl group
+userdbctl group testuser
+userdbctl group root
+userdbctl group testuser root
+userdbctl group -j testuser root | jq
+# Check only GID for the nobody group, since the name is build-configurable
+userdbctl group --with-nss=no --synthesize=yes
+userdbctl group --with-nss=no --synthesize=yes 0 root 65534
+userdbctl group dropingroup
+userdbctl group 1000000
+userdbctl group --with-nss=no --with-varlink=no --synthesize=no --multiplexer=no dropingroup
+userdbctl group --with-nss=no 1000000
+(! userdbctl group '')
+(! userdbctl group 🐱)
+(! userdbctl group 🐱 '' bar)
+(! userdbctl group i-do-not-exist)
+(! userdbctl group root i-do-not-exist testuser)
+(! userdbctl group --with-nss=no --synthesize=no 0 root 65534)
+(! userdbctl group --with-dropin=no dropingroup)
+(! userdbctl group --with-dropin=no 1000000)
+
+userdbctl users-in-group
+userdbctl users-in-group testuser
+userdbctl users-in-group testuser root
+userdbctl users-in-group -j testuser root | jq
+userdbctl users-in-group 🐱
+(! userdbctl users-in-group '')
+(! userdbctl users-in-group foo '' bar)
+
+userdbctl groups-of-user
+userdbctl groups-of-user testuser
+userdbctl groups-of-user testuser root
+userdbctl groups-of-user -j testuser root | jq
+userdbctl groups-of-user 🐱
+(! userdbctl groups-of-user '')
+(! userdbctl groups-of-user foo '' bar)
+
+userdbctl services
+userdbctl services -j | jq
+
+varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"testuser","service":"io.systemd.Multiplexer"}'
+varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"root","service":"io.systemd.Multiplexer"}'
+varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"dropinuser","service":"io.systemd.Multiplexer"}'
+varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"uid":2000000,"service":"io.systemd.Multiplexer"}'
+(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"","service":"io.systemd.Multiplexer"}')
+(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"🐱","service":"io.systemd.Multiplexer"}')
+(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"i-do-not-exist","service":"io.systemd.Multiplexer"}')
+
+userdbctl ssh-authorized-keys dropinuser | tee /tmp/authorized-keys
+grep "ssh-ed25519" /tmp/authorized-keys
+grep "ecdsa-sha2-nistp256" /tmp/authorized-keys
+echo "my-top-secret-key 🐱" >/tmp/my-top-secret-key
+userdbctl ssh-authorized-keys dropinuser --chain /bin/cat /tmp/my-top-secret-key | tee /tmp/authorized-keys
+grep "ssh-ed25519" /tmp/authorized-keys
+grep "ecdsa-sha2-nistp256" /tmp/authorized-keys
+grep "my-top-secret-key 🐱" /tmp/authorized-keys
+(! userdbctl ssh-authorized-keys 🐱)
+(! userdbctl ssh-authorized-keys dropin-user --chain)
+(! userdbctl ssh-authorized-keys dropin-user --chain '')
+(! SYSTEMD_LOG_LEVEL=debug userdbctl ssh-authorized-keys dropin-user --chain /bin/false)
+
+(! userdbctl '')
+for opt in json multiplexer output synthesize with-dropin with-nss with-varlink; do
+    (! userdbctl "--$opt=''")
+    (! userdbctl "--$opt='🐱'")
+    (! userdbctl "--$opt=foo")
+    (! userdbctl "--$opt=foo" "--$opt=''" "--$opt=🐱")
+done
+
+# FIXME: sshd seems to crash inside asan currently, skip the actual ssh test hence
+if command -v ssh &>/dev/null && command -v sshd &>/dev/null && ! [[ -v ASAN_OPTIONS ]]; then
+    at_exit() {
+        set +e
+
+        systemctl is-active -q mysshserver.socket && systemctl stop mysshserver.socket
+        rm -f /tmp/homed.id_ecdsa /run/systemd/system/mysshserver{@.service,.socket}
+        systemctl daemon-reload
+        homectl remove homedsshtest
+        for dir in /etc /usr/lib; do
+            if [[ -f "$dir/pam.d/sshd.bak" ]]; then
+                mv "$dir/pam.d/sshd.bak" "$dir/pam.d/sshd"
+            fi
+        done
+    }
+
+    trap at_exit EXIT
+
+    # Test that SSH logins work with delayed unlocking
+    ssh-keygen -N '' -C '' -t ecdsa -f /tmp/homed.id_ecdsa
+    NEWPASSWORD=hunter4711 homectl create \
+                       --disk-size=min \
+                       --luks-discard=yes \
+                       --luks-pbkdf-type=pbkdf2 \
+                       --luks-pbkdf-time-cost=1ms \
+                       --rate-limit-interval=1s \
+                       --rate-limit-burst=1000 \
+                       --enforce-password-policy=no \
+                       --ssh-authorized-keys=@/tmp/homed.id_ecdsa.pub \
+                       --stop-delay=0 \
+                       homedsshtest
+    homectl inspect homedsshtest
+
+    mkdir -p /etc/ssh
+    test -f /etc/ssh/ssh_host_ecdsa_key || ssh-keygen -t ecdsa -C '' -N '' -f /etc/ssh/ssh_host_ecdsa_key
+
+    # ssh wants this dir around, but distros cannot agree on a common name for it, let's just create all that
+    # are aware of distros use
+    mkdir -p /usr/share/empty.sshd /var/empty /var/empty/sshd /run/sshd
+
+    for dir in /etc /usr/lib; do
+        if [[ -f "$dir/pam.d/sshd" ]]; then
+            mv "$dir/pam.d/sshd" "$dir/pam.d/sshd.bak"
+            cat >"$dir/pam.d/sshd" <<EOF
+auth    sufficient pam_unix.so nullok
+auth    sufficient pam_systemd_home.so debug
+auth    required   pam_deny.so
+account sufficient pam_systemd_home.so debug
+account sufficient pam_unix.so
+account required   pam_permit.so
+session optional   pam_systemd_home.so debug
+session optional   pam_systemd.so
+session required   pam_unix.so
+EOF
+            break
+        fi
+    done
+
+    mkdir -p /etc/sshd/
+    cat >/etc/ssh/sshd_config <<EOF
+AuthorizedKeysCommand /usr/bin/userdbctl ssh-authorized-keys %u
+AuthorizedKeysCommandUser root
+UsePAM yes
+AcceptEnv PASSWORD
+LogLevel DEBUG3
+EOF
+
+    cat >/run/systemd/system/mysshserver.socket <<EOF
+[Socket]
+ListenStream=4711
+Accept=yes
+EOF
+
+    cat >/run/systemd/system/mysshserver@.service <<EOF
+[Service]
+ExecStart=-/usr/sbin/sshd -i -d -e
+StandardInput=socket
+StandardOutput=socket
+StandardError=journal
+EOF
+
+    systemctl daemon-reload
+    systemctl start mysshserver.socket
+
+    userdbctl user -j homedsshtest
+
+    ssh -t -t -4 -p 4711 -i /tmp/homed.id_ecdsa \
+        -o "SetEnv PASSWORD=hunter4711" -o "StrictHostKeyChecking no" \
+        homedsshtest@localhost echo zzz | tr -d '\r' | tee /tmp/homedsshtest.out
+    grep -E "^zzz$" /tmp/homedsshtest.out
+    rm /tmp/homedsshtest.out
+
+    ssh -t -t -4 -p 4711 -i /tmp/homed.id_ecdsa \
+        -o "SetEnv PASSWORD=hunter4711" -o "StrictHostKeyChecking no" \
+        homedsshtest@localhost env
+
+    wait_for_state homedsshtest inactive
+fi
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-50-DISSECT.DDI.sh b/test/units/TEST-50-DISSECT.DDI.sh
new file mode 100755 (executable)
index 0000000..42c9a43
--- /dev/null
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Check that the /sbin/mount.ddi helper works
+dir="/tmp/mounthelper.$RANDOM"
+mount -t ddi "$MINIMAL_IMAGE.gpt" "$dir" -o ro,X-mount.mkdir,discard
+umount -R "$dir"
+
+# Test systemd-repart --make-ddi=:
+if [[ -z "${OPENSSL_CONFIG:?}" ]] || ! command -v mksquashfs &>/dev/null; then
+    echo "Skipping --make-ddi= tests"
+    exit 0
+fi
+
+openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \
+            -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
+            -keyout /tmp/test-50-privkey.key -out /tmp/test-50-cert.crt
+mkdir -p /tmp/test-50-confext/etc/extension-release.d/
+echo "foobar50" >/tmp/test-50-confext/etc/waldo
+{
+    grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release
+    echo IMAGE_ID=waldo
+    echo IMAGE_VERSION=7
+} >/tmp/test-50-confext/etc/extension-release.d/extension-release.waldo
+mkdir -p /run/confexts
+
+SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
+    systemd-repart -C \
+                   -s /tmp/test-50-confext \
+                   --certificate=/tmp/test-50-cert.crt \
+                   --private-key=/tmp/test-50-privkey.key \
+                   /run/confexts/waldo.confext.raw
+rm -rf /tmp/test-50-confext
+
+mkdir -p /run/verity.d
+cp /tmp/test-50-cert.crt /run/verity.d/
+systemd-dissect --mtree /run/confexts/waldo.confext.raw
+
+systemd-confext refresh
+test "$(</etc/waldo)" = foobar50
+rm /run/confexts/waldo.confext.raw
+systemd-confext refresh
+test ! -f /etc/waldo
+
+mkdir -p /tmp/test-50-sysext/usr/lib/extension-release.d/
+# Make sure the sysext is big enough to not fit in the minimum partition size of repart so we know the
+# Minimize= logic is working.
+truncate --size=50M /tmp/test-50-sysext/usr/waldo
+{
+    grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release
+    echo IMAGE_ID=waldo
+    echo IMAGE_VERSION=7
+} >/tmp/test-50-sysext/usr/lib/extension-release.d/extension-release.waldo
+mkdir -p /run/extensions
+
+SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
+    systemd-repart -S \
+                   -s /tmp/test-50-sysext \
+                   --certificate=/tmp/test-50-cert.crt \
+                   --private-key=/tmp/test-50-privkey.key \
+                   /run/extensions/waldo.sysext.raw
+
+systemd-dissect --mtree /run/extensions/waldo.sysext.raw
+systemd-sysext refresh
+test -f /usr/waldo
+rm /run/verity.d/test-50-cert.crt /run/extensions/waldo.sysext.raw /tmp/test-50-cert.crt /tmp/test-50-privkey.key
+systemd-sysext refresh
+test ! -f /usr/waldo
diff --git a/test/units/TEST-50-DISSECT.dissect.sh b/test/units/TEST-50-DISSECT.dissect.sh
new file mode 100755 (executable)
index 0000000..991346f
--- /dev/null
@@ -0,0 +1,740 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+# shellcheck disable=SC2233,SC2235
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+BIND_LOG_SOCKETS=(
+    --property BindReadOnlyPaths=/dev/log
+    --property BindReadOnlyPaths=/run/systemd/journal/socket
+    --property BindReadOnlyPaths=/run/systemd/journal/stdout
+)
+
+systemd-dissect --json=short "$MINIMAL_IMAGE.raw" | \
+    grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
+systemd-dissect "$MINIMAL_IMAGE.raw" | grep -q -F "MARKER=1"
+# shellcheck disable=SC2153
+systemd-dissect "$MINIMAL_IMAGE.raw" | grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
+
+systemd-dissect --list "$MINIMAL_IMAGE.raw" | grep -q '^etc/os-release$'
+systemd-dissect --mtree "$MINIMAL_IMAGE.raw" --mtree-hash yes | \
+    grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]* sha256sum=[a-z0-9]*$"
+systemd-dissect --mtree "$MINIMAL_IMAGE.raw" --mtree-hash no  | \
+    grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]*$"
+
+read -r SHA256SUM1 _ < <(systemd-dissect --copy-from "$MINIMAL_IMAGE.raw" etc/os-release | sha256sum)
+test "$SHA256SUM1" != ""
+read -r SHA256SUM2 _ < <(systemd-dissect --read-only --with "$MINIMAL_IMAGE.raw" sha256sum etc/os-release)
+test "$SHA256SUM2" != ""
+test "$SHA256SUM1" = "$SHA256SUM2"
+
+if systemctl --version | grep -qF -- "+LIBARCHIVE" ; then
+    # Make sure tarballs are reproducible
+    read -r SHA256SUM1 _ < <(systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | sha256sum)
+    test "$SHA256SUM1" != ""
+    read -r SHA256SUM2 _ < <(systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | sha256sum)
+    test "$SHA256SUM2" != ""
+    test "$SHA256SUM1" = "$SHA256SUM2"
+    # Also check that a file we expect to be there is there
+    systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | tar t | grep etc/os-release
+fi
+
+mv "$MINIMAL_IMAGE.verity" "$MINIMAL_IMAGE.fooverity"
+mv "$MINIMAL_IMAGE.roothash" "$MINIMAL_IMAGE.foohash"
+systemd-dissect "$MINIMAL_IMAGE.raw" \
+                --json=short \
+                --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
+                --verity-data="$MINIMAL_IMAGE.fooverity" | \
+                grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
+systemd-dissect "$MINIMAL_IMAGE.raw" \
+                --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
+                --verity-data="$MINIMAL_IMAGE.fooverity" | \
+                grep -q -F "MARKER=1"
+systemd-dissect "$MINIMAL_IMAGE.raw" \
+                --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
+                --verity-data="$MINIMAL_IMAGE.fooverity" | \
+                grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
+mv "$MINIMAL_IMAGE.fooverity" "$MINIMAL_IMAGE.verity"
+mv "$MINIMAL_IMAGE.foohash" "$MINIMAL_IMAGE.roothash"
+
+mkdir -p "$IMAGE_DIR/mount" "$IMAGE_DIR/mount2"
+systemd-dissect --mount "$MINIMAL_IMAGE.raw" "$IMAGE_DIR/mount"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
+grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
+# Verity volume should be shared (opened only once)
+systemd-dissect --mount "$MINIMAL_IMAGE.raw" "$IMAGE_DIR/mount2"
+verity_count=$(find /dev/mapper/ -name "*verity*" | wc -l)
+# In theory we should check that count is exactly one. In practice, libdevmapper
+# randomly and unpredictably fails with an unhelpful EINVAL when a device is open
+# (and even mounted and in use), so best-effort is the most we can do for now
+if [[ "$verity_count" -lt 1 ]]; then
+    echo "Verity device $MINIMAL_IMAGE.raw not found in /dev/mapper/"
+    exit 1
+fi
+systemd-dissect --umount "$IMAGE_DIR/mount"
+systemd-dissect --umount "$IMAGE_DIR/mount2"
+
+systemd-run -P -p RootImage="$MINIMAL_IMAGE.raw" "${BIND_LOG_SOCKETS[@]}" cat /usr/lib/os-release | grep -q -F "MARKER=1"
+mv "$MINIMAL_IMAGE.verity" "$MINIMAL_IMAGE.fooverity"
+mv "$MINIMAL_IMAGE.roothash" "$MINIMAL_IMAGE.foohash"
+systemd-run -P \
+            -p RootImage="$MINIMAL_IMAGE.raw" \
+            -p RootHash="$MINIMAL_IMAGE.foohash" \
+            -p RootVerity="$MINIMAL_IMAGE.fooverity" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+# Let's use the long option name just here as a test
+systemd-run -P \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            --property RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            --property RootVerity="$MINIMAL_IMAGE.fooverity" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+mv "$MINIMAL_IMAGE.fooverity" "$MINIMAL_IMAGE.verity"
+mv "$MINIMAL_IMAGE.foohash" "$MINIMAL_IMAGE.roothash"
+
+# Derive partition UUIDs from root hash, in UUID syntax
+ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "$MINIMAL_IMAGE.roothash")" -u | tail -n 1 | cut -b 6-)"
+VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "$MINIMAL_IMAGE.roothash")" -u | tail -n 1 | cut -b 6-)"
+
+systemd-dissect --json=short \
+                --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
+                "$MINIMAL_IMAGE.gpt" | \
+                grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$ARCHITECTURE"'","verity":"signed",'
+systemd-dissect --json=short \
+                --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
+                "$MINIMAL_IMAGE.gpt" | \
+                grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$ARCHITECTURE"'","verity":null,'
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+    systemd-dissect --json=short \
+                    --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
+                    "$MINIMAL_IMAGE.gpt" | \
+                    grep -qE '{"rw":"ro","designator":"root-verity-sig","partition_uuid":"'".*"'","partition_label":"Signature Partition","fstype":"verity_hash_signature","architecture":"'"$ARCHITECTURE"'","verity":null,'
+fi
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" "$MINIMAL_IMAGE.gpt" | grep -q -F "MARKER=1"
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" "$MINIMAL_IMAGE.gpt" | grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
+
+# Test image policies
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt"
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='*'
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='~')
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='-')
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=absent)
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=swap=unprotected+encrypted+verity)
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=unprotected
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:root-verity-sig=unused+absent
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:swap=absent
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:swap=absent+unprotected
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:root-verity=unused+absent)
+systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed:root-verity-sig=unused+absent)
+(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed:root-verity=unused+absent)
+
+# Test RootImagePolicy= unit file setting
+systemd-run --wait -P \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            -p MountAPIVFS=yes \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run --wait -P \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            -p RootImagePolicy='*' \
+            -p MountAPIVFS=yes \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+(! systemd-run --wait -P \
+               -p RootImage="$MINIMAL_IMAGE.gpt" \
+               -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+               -p RootImagePolicy='~' \
+               -p MountAPIVFS=yes \
+               "${BIND_LOG_SOCKETS[@]}" \
+               cat /usr/lib/os-release | grep -q -F "MARKER=1")
+(! systemd-run --wait -P \
+               -p RootImage="$MINIMAL_IMAGE.gpt" \
+               -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+               -p RootImagePolicy='-' \
+               -p MountAPIVFS=yes \
+               "${BIND_LOG_SOCKETS[@]}" \
+               cat /usr/lib/os-release | grep -q -F "MARKER=1")
+(! systemd-run --wait -P \
+               -p RootImage="$MINIMAL_IMAGE.gpt" \
+               -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+               -p RootImagePolicy='root=absent' \
+               -p MountAPIVFS=yes \
+               "${BIND_LOG_SOCKETS[@]}" \
+               cat /usr/lib/os-release | grep -q -F "MARKER=1")
+systemd-run --wait -P \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            -p RootImagePolicy='root=verity' \
+            -p MountAPIVFS=yes \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run --wait -P \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            -p RootImagePolicy='root=signed' \
+            -p MountAPIVFS=yes \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+(! systemd-run --wait -P \
+               -p RootImage="$MINIMAL_IMAGE.gpt" \
+               -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+               -p RootImagePolicy='root=encrypted' \
+               -p MountAPIVFS=yes \
+               "${BIND_LOG_SOCKETS[@]}" \
+               cat /usr/lib/os-release | grep -q -F "MARKER=1")
+
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" --mount "$MINIMAL_IMAGE.gpt" "$IMAGE_DIR/mount"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
+grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
+systemd-dissect --umount "$IMAGE_DIR/mount"
+
+systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" --mount "$MINIMAL_IMAGE.gpt" --in-memory "$IMAGE_DIR/mount"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
+grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
+grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
+systemd-dissect --umount "$IMAGE_DIR/mount"
+
+# add explicit -p MountAPIVFS=yes once to test the parser
+systemd-run -P \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            -p MountAPIVFS=yes \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+            -p RootImage="$MINIMAL_IMAGE.raw" \
+            -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootImageOptions="root:ro,noatime root:ro,dev" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            mount | grep -F "squashfs" | grep -q -F "noatime"
+
+mkdir -p "$IMAGE_DIR/result"
+cat >/run/systemd/system/testservice-50a.service <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "mount >/run/result/a"
+BindPaths=$IMAGE_DIR/result:/run/result
+TemporaryFileSystem=/run
+RootImage=$MINIMAL_IMAGE.raw
+RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
+RootImageOptions=nosuid,dev
+BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
+EOF
+systemctl start testservice-50a.service
+grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F "noatime"
+grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F -v "nosuid"
+
+cat >/run/systemd/system/testservice-50b.service <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "mount >/run/result/b"
+BindPaths=$IMAGE_DIR/result:/run/result
+TemporaryFileSystem=/run
+RootImage=$MINIMAL_IMAGE.gpt
+RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev
+RootImageOptions=home:ro,dev nosuid,dev,%%foo
+# this is the default, but let's specify once to test the parser
+MountAPIVFS=yes
+BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
+EOF
+systemctl start testservice-50b.service
+grep -F "squashfs" "$IMAGE_DIR/result/b" | grep -q -F "noatime"
+
+# Check that specifier escape is applied %%foo → %foo
+busctl get-property org.freedesktop.systemd1 \
+                    /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice \
+                    org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
+
+# Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image
+systemd-run -P \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+            cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+            cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2:nosuid,dev" \
+            mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1:root:nosuid $MINIMAL_IMAGE.raw:/run/img2:home:suid" \
+            mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+            -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3" \
+            cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+            -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3:nosuid" \
+            mount | grep -F "squashfs" | grep -q -F "nosuid"
+systemd-run -P \
+            -p TemporaryFileSystem=/run \
+            -p RootImage="$MINIMAL_IMAGE.raw" \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+            -p TemporaryFileSystem=/run \
+            -p RootImage="$MINIMAL_IMAGE.raw" \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -P \
+            -p TemporaryFileSystem=/run \
+            -p RootImage="$MINIMAL_IMAGE.gpt" \
+            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
+            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
+cat >/run/systemd/system/testservice-50c.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run
+RootImage=$MINIMAL_IMAGE.raw
+BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
+MountImages=$MINIMAL_IMAGE.gpt:/run/img1:root:noatime:home:relatime
+MountImages=$MINIMAL_IMAGE.raw:/run/img2\:3:nosuid
+ExecStart=bash -c "cat /run/img1/usr/lib/os-release >/run/result/c"
+ExecStart=bash -c "cat /run/img2:3/usr/lib/os-release >>/run/result/c"
+ExecStart=bash -c "mount >>/run/result/c"
+BindPaths=$IMAGE_DIR/result:/run/result
+Type=oneshot
+EOF
+systemctl start testservice-50c.service
+grep -q -F "MARKER=1" "$IMAGE_DIR/result/c"
+grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F "noatime"
+grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F -v "nosuid"
+
+# Adding a new mounts at runtime works if the unit is in the active state,
+# so use Type=notify to make sure there's no race condition in the test
+cat >/run/systemd/system/testservice-50d.service <<EOF
+[Service]
+RuntimeMaxSec=300
+Type=notify
+RemainAfterExit=yes
+MountAPIVFS=yes
+PrivateTmp=yes
+ExecStart=sh -c ' \\
+    systemd-notify --ready; \\
+    while [ ! -f /tmp/img/usr/lib/os-release ] || ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do \\
+        sleep 0.1; \\
+    done; \\
+    mount; \\
+    mount | grep -F "on /tmp/img type squashfs" | grep -q -F "nosuid"; \\
+'
+EOF
+systemctl start testservice-50d.service
+
+# Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount)
+mkdir -p /tmp/wrong/foo
+mksquashfs /tmp/wrong/foo /tmp/wrong.raw
+systemctl mount-image --mkdir testservice-50d.service /tmp/wrong.raw /tmp/img
+test "$(systemctl show -P SubState testservice-50d.service)" = "running"
+systemctl mount-image --mkdir testservice-50d.service "$MINIMAL_IMAGE.raw" /tmp/img root:nosuid
+# shellcheck disable=SC2016
+timeout 30s bash -xec 'while [[ $(systemctl show -P SubState testservice-50d.service) == running ]]; do sleep .2; done'
+systemctl is-active testservice-50d.service
+
+# ExtensionImages will set up an overlay
+systemd-run -P \
+            --property ExtensionImages=/tmp/app0.raw \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+            --property ExtensionImages=/tmp/app0.raw \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+            --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /opt/script1.sh | grep -q -F "extension-release.app2"
+systemd-run -P \
+            --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionImages=/tmp/app-nodistro.raw \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionImages=/etc/service-scoped-test.raw \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+# Check that using a symlink to NAME-VERSION.raw works as long as the symlink has the correct name NAME.raw
+mkdir -p /tmp/symlink-test/
+cp /tmp/app-nodistro.raw /tmp/symlink-test/app-nodistro-v1.raw
+ln -fs /tmp/symlink-test/app-nodistro-v1.raw /tmp/symlink-test/app-nodistro.raw
+systemd-run -P \
+            --property ExtensionImages=/tmp/symlink-test/app-nodistro.raw \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+
+# Symlink check again but for confext
+mkdir -p /etc/symlink-test/
+cp /etc/service-scoped-test.raw /etc/symlink-test/service-scoped-test-v1.raw
+ln -fs /etc/symlink-test/service-scoped-test-v1.raw /etc/symlink-test/service-scoped-test.raw
+systemd-run -P \
+            --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+# And again mixing sysext and confext
+systemd-run -P \
+    --property ExtensionImages=/tmp/symlink-test/app-nodistro.raw \
+    --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
+    --property RootImage="$MINIMAL_IMAGE.raw" \
+    "${BIND_LOG_SOCKETS[@]}" \
+    cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+systemd-run -P \
+    --property ExtensionImages=/tmp/symlink-test/app-nodistro.raw \
+    --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
+    --property RootImage="$MINIMAL_IMAGE.raw" \
+    "${BIND_LOG_SOCKETS[@]}" \
+    cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+
+cat >/run/systemd/system/testservice-50e.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
+RootImage=$MINIMAL_IMAGE.raw
+ExtensionImages=/tmp/app0.raw /tmp/app1.raw:nosuid
+BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
+# Relevant only for sanitizer runs
+UnsetEnvironment=LD_PRELOAD
+ExecStart=bash -c '/opt/script0.sh | grep ID'
+ExecStart=bash -c '/opt/script1.sh | grep ID'
+Type=oneshot
+RemainAfterExit=yes
+EOF
+systemctl start testservice-50e.service
+systemctl is-active testservice-50e.service
+
+# Check vpick support in ExtensionImages=
+VBASE="vtest$RANDOM"
+VDIR="/tmp/$VBASE.v"
+mkdir "$VDIR"
+
+ln -s /tmp/app0.raw "$VDIR/${VBASE}_0.raw"
+ln -s /tmp/app1.raw "$VDIR/${VBASE}_1.raw"
+
+systemd-run -P -p ExtensionImages="$VDIR" bash -c '/opt/script1.sh | grep ID'
+
+rm -rf "$VDIR"
+
+# ExtensionDirectories will set up an overlay
+mkdir -p "$IMAGE_DIR/app0" "$IMAGE_DIR/app1" "$IMAGE_DIR/app-nodistro" "$IMAGE_DIR/service-scoped-test"
+(! systemd-run -P \
+               --property ExtensionDirectories="$IMAGE_DIR/nonexistent" \
+               --property RootImage="$MINIMAL_IMAGE.raw" \
+               "${BIND_LOG_SOCKETS[@]}" \
+               cat /opt/script0.sh)
+(! systemd-run -P \
+               --property ExtensionDirectories="$IMAGE_DIR/app0" \
+               --property RootImage="$MINIMAL_IMAGE.raw" \
+               "${BIND_LOG_SOCKETS[@]}" \
+               cat /opt/script0.sh)
+systemd-dissect --mount /tmp/app0.raw "$IMAGE_DIR/app0"
+systemd-dissect --mount /tmp/app1.raw "$IMAGE_DIR/app1"
+systemd-dissect --mount /tmp/app-nodistro.raw "$IMAGE_DIR/app-nodistro"
+systemd-dissect --mount /etc/service-scoped-test.raw "$IMAGE_DIR/service-scoped-test"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app0" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app0" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /opt/script1.sh | grep -q -F "extension-release.app2"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/app-nodistro" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P \
+            --property ExtensionDirectories="$IMAGE_DIR/service-scoped-test" \
+            --property RootImage="$MINIMAL_IMAGE.raw" \
+            "${BIND_LOG_SOCKETS[@]}" \
+            cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+cat >/run/systemd/system/testservice-50f.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
+RootImage=$MINIMAL_IMAGE.raw
+BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
+ExtensionDirectories=$IMAGE_DIR/app0 $IMAGE_DIR/app1
+# Relevant only for sanitizer runs
+UnsetEnvironment=LD_PRELOAD
+ExecStart=bash -c '/opt/script0.sh | grep ID'
+ExecStart=bash -c '/opt/script1.sh | grep ID'
+Type=oneshot
+RemainAfterExit=yes
+EOF
+systemctl start testservice-50f.service
+systemctl is-active testservice-50f.service
+
+# Check vpick support in ExtensionDirectories=
+VBASE="vtest$RANDOM"
+VDIR="/tmp/$VBASE.v"
+mkdir "$VDIR"
+
+ln -s "$IMAGE_DIR/app0" "$VDIR/${VBASE}_0"
+ln -s "$IMAGE_DIR/app1" "$VDIR/${VBASE}_1"
+
+systemd-run -P --property ExtensionDirectories="$VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
+
+rm -rf "$VDIR"
+
+systemd-dissect --umount "$IMAGE_DIR/app0"
+systemd-dissect --umount "$IMAGE_DIR/app1"
+
+# Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence
+mkdir -p /var/lib/extensions/
+ln -s /tmp/app-nodistro.raw /var/lib/extensions/app-nodistro.raw
+systemd-sysext merge
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+systemd-sysext unmerge
+mkdir -p /etc/extensions/app-nodistro
+systemd-sysext merge
+test ! -e /usr/lib/systemd/system/some_file
+systemd-sysext unmerge
+rmdir /etc/extensions/app-nodistro
+
+# Similar, but go via varlink
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.List '{}'
+(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Merge '{}'
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Refresh '{}'
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Unmerge '{}'
+(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
+
+# Check that extensions cannot contain os-release
+mkdir -p /run/extensions/app-reject/usr/lib/{extension-release.d/,systemd/system}
+echo "ID=_any" >/run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject
+echo "ID=_any" >/run/extensions/app-reject/usr/lib/os-release
+touch /run/extensions/app-reject/usr/lib/systemd/system/other_file
+(! systemd-sysext merge)
+test ! -e /usr/lib/systemd/system/some_file
+test ! -e /usr/lib/systemd/system/other_file
+systemd-sysext unmerge
+rm -rf /run/extensions/app-reject
+rm /var/lib/extensions/app-nodistro.raw
+
+# Some super basic test that RootImage= works with .v/ dirs
+VBASE="vtest$RANDOM"
+VDIR="/tmp/$VBASE.v"
+mkdir "$VDIR"
+
+ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_33.raw"
+ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_34.raw"
+ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_35.raw"
+
+systemd-run -P -p RootImage="$VDIR" "${BIND_LOG_SOCKETS[@]}" cat /usr/lib/os-release | grep -q -F "MARKER=1"
+
+rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw"
+rmdir "$VDIR"
+
+mkdir -p /run/machines /run/portables /run/extensions
+touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
+
+systemd-dissect --discover --json=short >/tmp/discover.json
+grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json
+grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json
+grep -q -F '{"name":"c","type":"raw","class":"sysext","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json
+rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
+
+LOOP="$(systemd-dissect --attach --loop-ref=waldo "$MINIMAL_IMAGE.raw")"
+
+# Wait until the symlinks we want to test are established
+udevadm trigger -w "$LOOP"
+
+# Check if the /dev/loop/* symlinks really reference the right device
+test /dev/disk/by-loop-ref/waldo -ef "$LOOP"
+
+if [ "$(stat -c '%Hd:%Ld' "$MINIMAL_IMAGE.raw")" != '?d:?d' ] ; then
+   # Old stat didn't know the %Hd and %Ld specifiers and turned them into ?d
+   # instead. Let's simply skip the test on such old systems.
+   test "$(stat -c '/dev/disk/by-loop-inode/%Hd:%Ld-%i' "$MINIMAL_IMAGE.raw")" -ef "$LOOP"
+fi
+
+# Detach by loopback device
+systemd-dissect --detach "$LOOP"
+
+# Test long reference name.
+# Note, sizeof_field(struct loop_info64, lo_file_name) == 64,
+# and --loop-ref accepts upto 63 characters, and udev creates symlink
+# based on the name when it has upto _62_ characters.
+name="$(for _ in {1..62}; do echo -n 'x'; done)"
+LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")"
+udevadm trigger -w "$LOOP"
+
+# Check if the /dev/disk/by-loop-ref/$name symlink really references the right device
+test "/dev/disk/by-loop-ref/$name" -ef "$LOOP"
+
+# Detach by the /dev/disk/by-loop-ref symlink
+systemd-dissect --detach "/dev/disk/by-loop-ref/$name"
+
+name="$(for _ in {1..63}; do echo -n 'x'; done)"
+LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")"
+udevadm trigger -w "$LOOP"
+
+# Check if the /dev/disk/by-loop-ref/$name symlink does not exist
+test ! -e "/dev/disk/by-loop-ref/$name"
+
+# Detach by backing inode
+systemd-dissect --detach "$MINIMAL_IMAGE.raw"
+(! systemd-dissect --detach "$MINIMAL_IMAGE.raw")
+
+# check for confext functionality
+mkdir -p /run/confexts/test/etc/extension-release.d
+echo "ID=_any" >/run/confexts/test/etc/extension-release.d/extension-release.test
+echo "ARCHITECTURE=_any" >>/run/confexts/test/etc/extension-release.d/extension-release.test
+echo "MARKER_CONFEXT_123" >/run/confexts/test/etc/testfile
+cat <<EOF >/run/confexts/test/etc/testscript
+#!/bin/bash
+echo "This should not happen"
+EOF
+chmod +x /run/confexts/test/etc/testscript
+systemd-confext merge
+grep -q -F "MARKER_CONFEXT_123" /etc/testfile
+(! /etc/testscript)
+systemd-confext status
+systemd-confext unmerge
+rm -rf /run/confexts/
+
+unsquashfs -no-xattrs -d /tmp/img "$MINIMAL_IMAGE.raw"
+systemd-run --unit=test-root-ephemeral \
+    -p RootDirectory=/tmp/img \
+    -p RootEphemeral=yes \
+    -p Type=exec \
+    "${BIND_LOG_SOCKETS[@]}" \
+    bash -c "touch /abc && sleep infinity"
+test -n "$(ls -A /var/lib/systemd/ephemeral-trees)"
+systemctl stop test-root-ephemeral
+# shellcheck disable=SC2016
+timeout 10 bash -c 'until test -z "$(ls -A /var/lib/systemd/ephemeral-trees)"; do sleep .5; done'
+test ! -f /tmp/img/abc
+
+systemd-dissect --mtree /tmp/img >/dev/null
+systemd-dissect --list /tmp/img >/dev/null
+
+read -r SHA256SUM1 _ < <(systemd-dissect --copy-from /tmp/img etc/os-release | sha256sum)
+test "$SHA256SUM1" != ""
+
+echo abc > abc
+systemd-dissect --copy-to /tmp/img abc /abc
+test -f /tmp/img/abc
+
+# Test for dissect tool support with systemd-sysext
+mkdir -p /run/extensions/ testkit/usr/lib/extension-release.d/
+echo "ID=_any" >testkit/usr/lib/extension-release.d/extension-release.testkit
+echo "ARCHITECTURE=_any" >>testkit/usr/lib/extension-release.d/extension-release.testkit
+echo "MARKER_SYSEXT_123" >testkit/usr/lib/testfile
+mksquashfs testkit/ testkit.raw
+cp testkit.raw /run/extensions/
+unsquashfs -l /run/extensions/testkit.raw
+systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for portable service'
+systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for system'
+systemd-sysext merge
+systemd-sysext status
+grep -q -F "MARKER_SYSEXT_123" /usr/lib/testfile
+systemd-sysext unmerge
+rm -rf /run/extensions/ testkit/
+
+# Test for dissect tool support with systemd-confext
+mkdir -p /run/confexts/ testjob/etc/extension-release.d/
+echo "ID=_any" >testjob/etc/extension-release.d/extension-release.testjob
+echo "ARCHITECTURE=_any" >>testjob/etc/extension-release.d/extension-release.testjob
+echo "MARKER_CONFEXT_123" >testjob/etc/testfile
+mksquashfs testjob/ testjob.raw
+cp testjob.raw /run/confexts/
+unsquashfs -l /run/confexts/testjob.raw
+systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for system'
+systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for portable service'
+systemd-confext merge
+systemd-confext status
+grep -q -F "MARKER_CONFEXT_123" /etc/testfile
+systemd-confext unmerge
+rm -rf /run/confexts/ testjob/
+
+systemd-run -P -p RootImage="$MINIMAL_IMAGE.raw" "${BIND_LOG_SOCKETS[@]}" cat /run/host/os-release | cmp "$OS_RELEASE"
+
+# Test that systemd-sysext reloads the daemon.
+mkdir -p /var/lib/extensions/
+ln -s /tmp/app-reload.raw /var/lib/extensions/app-reload.raw
+systemd-sysext merge --no-reload
+# the service should not be running
+(! systemctl --quiet is-active foo.service)
+systemd-sysext unmerge --no-reload
+systemd-sysext merge
+# shellcheck disable=SC2016
+timeout 30s bash -xec 'until [[ $(journalctl -b -u foo.service _TRANSPORT=stdout -o cat) == foo ]]; do sleep .5; done'
+systemd-sysext unmerge --no-reload
+# Grep on the Warning to find the warning helper mentioning the daemon reload.
+systemctl status foo.service 2>&1 | grep -q -F "Warning"
+systemd-sysext merge
+systemd-sysext unmerge
+systemctl status foo.service 2>&1 | grep -v -q -F "Warning"
+rm /var/lib/extensions/app-reload.raw
+
+# Sneak in a couple of expected-to-fail invocations to cover
+# https://github.com/systemd/systemd/issues/29610
+(! systemd-run -P -p MountImages="/this/should/definitely/not/exist.img:/run/img2\:3:nosuid" false)
+(! systemd-run -P -p ExtensionImages="/this/should/definitely/not/exist.img" false)
+(! systemd-run -P -p RootImage="/this/should/definitely/not/exist.img" false)
+(! systemd-run -P -p ExtensionDirectories="/foo/bar /foo/baz" false)
diff --git a/test/units/TEST-50-DISSECT.mountfsd.sh b/test/units/TEST-50-DISSECT.mountfsd.sh
new file mode 100755 (executable)
index 0000000..604a9db
--- /dev/null
@@ -0,0 +1,92 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if [[ ! -f /usr/lib/systemd/system/systemd-mountfsd.socket ]] || \
+   [[ ! -f /usr/lib/systemd/system/systemd-nsresourced.socket ]] || \
+   ! command -v mksquashfs || \
+   ! grep -q bpf /sys/kernel/security/lsm ||
+   ! find /usr/lib* -name libbpf.so.1 2>/dev/null | grep . || \
+   systemd-analyze compare-versions "$(uname -r)" lt 6.5 || \
+   systemd-analyze compare-versions "$(pkcheck --version | awk '{print $3}')" lt 124; then
+    echo "Skipping mountfsd/nsresourced tests"
+    exit 0
+fi
+
+at_exit() {
+    set +e
+
+    umount -R /tmp/unpriv/mount
+    rmdir /tmp/unpriv
+    rm -f /tmp/test-50-unpriv-privkey.key /tmp/test-50-unpriv-cert.crt /run/verity.d/test-50-unpriv-cert.crt
+    rm -f /var/tmp/unpriv.raw /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree
+    rm -f /tmp/unpriv.out /tmp/unpriv.out2 /tmp/unpriv.out3
+}
+
+trap at_exit EXIT
+
+systemctl start systemd-mountfsd.socket systemd-nsresourced.socket
+
+openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \
+            -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
+            -keyout /tmp/test-50-unpriv-privkey.key -out /tmp/test-50-unpriv-cert.crt
+
+systemd-dissect --mkdir --mount "$MINIMAL_IMAGE.raw" /tmp/unpriv/mount
+SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
+    systemd-repart -P \
+                   -s /tmp/unpriv/mount \
+                   --certificate=/tmp/test-50-unpriv-cert.crt \
+                   --private-key=/tmp/test-50-unpriv-privkey.key \
+                   /var/tmp/unpriv.raw
+systemd-dissect --rmdir --umount /tmp/unpriv/mount
+
+systemd-dissect --image-policy='root=unprotected:=absent+unused' /var/tmp/unpriv.raw
+systemd-dissect --image-policy='root=unprotected:=absent+unused' --mtree /var/tmp/unpriv.raw >/tmp/unpriv.raw.mtree
+
+# Run unpriv, should fail due to lack of privs
+(! runas testuser systemd-dissect /var/tmp/unpriv.raw)
+(! runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw)
+
+if (SYSTEMD_LOG_TARGET=console varlinkctl call \
+        /run/systemd/userdb/io.systemd.NamespaceResource \
+        io.systemd.NamespaceResource.AllocateUserRange \
+        '{"name":"test-supported","size":65536,"userNamespaceFileDescriptor":0}' 2>&1 || true) |
+            grep -q "io.systemd.NamespaceResource.UserNamespaceInterfaceNotSupported"; then
+    echo "User namespace interface not supported, skipping mountfsd/nsresourced tests"
+    exit 0
+fi
+
+# Install key in keychain
+cp /tmp/test-50-unpriv-cert.crt /run/verity.d
+
+# Now run unpriv again, should be OK now.
+runas testuser systemd-dissect /var/tmp/unpriv.raw
+runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw >/tmp/unpriv2.raw.mtree
+
+# Check that unpriv and priv run yielded same results
+cmp /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree
+
+# Make sure nspawn works unpriv, too (for now do not nest)
+if ! systemd-detect-virt -c; then
+    systemd-nspawn --pipe -i /var/tmp/unpriv.raw --read-only echo thisisatest > /tmp/unpriv.out
+    echo thisisatest | cmp /tmp/unpriv.out -
+
+    # The unpriv user has no rights to lock the image or write to it. Let's
+    # turn off both for this test, so that we don't have to copy the image
+    # around.
+    systemd-run -M testuser@ --user --pipe \
+                -p Environment=SYSTEMD_NSPAWN_LOCK=0 \
+                -p Delegate=1 \
+                -p DelegateSubgroup=supervisor \
+                -p Environment=SYSTEMD_LOG_LEVEL=debug \
+                --wait -- \
+                systemd-nspawn --keep-unit -i /var/tmp/unpriv.raw --read-only --pipe echo thisisatest >/tmp/unpriv.out2
+    echo thisisatest | cmp /tmp/unpriv.out2 -
+fi
+
+systemd-run -M testuser@ --user --pipe -p RootImage=/var/tmp/unpriv.raw -p PrivateUsers=1 --wait echo thisisatest >/tmp/unpriv.out3
+echo thisisatest | cmp /tmp/unpriv.out3 -
diff --git a/test/units/TEST-50-DISSECT.service b/test/units/TEST-50-DISSECT.service
new file mode 100644 (file)
index 0000000..bcafe6e
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-50-DISSECT
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-50-DISSECT.sh b/test/units/TEST-50-DISSECT.sh
new file mode 100755 (executable)
index 0000000..b1286d7
--- /dev/null
@@ -0,0 +1,214 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Setup shared stuff & run all subtests
+
+at_exit() {
+    set +e
+
+    if [[ -z "${IMAGE_DIR:-}" ]]; then
+        return
+    fi
+
+    while read -r dir; do
+        if mountpoint -q "$dir"; then
+            umount -Rv "$dir"
+        fi
+    done < <(find "${IMAGE_DIR}" -mindepth 1 -maxdepth 1 -type d)
+
+    rm -rf "$IMAGE_DIR"
+}
+
+trap at_exit EXIT
+
+: "Setup base images"
+
+export SYSTEMD_LOG_LEVEL=debug
+export ARCHITECTURE
+export IMAGE_DIR
+export MACHINE
+export MINIMAL_IMAGE
+export MINIMAL_IMAGE_ROOTHASH
+export OPENSSL_CONFIG
+export OS_RELEASE
+export ROOT_GUID
+export SIGNATURE_GUID
+export VERITY_GUID
+
+machine="$(uname -m)"
+if [[ "$machine" == "x86_64" ]]; then
+    ROOT_GUID=4f68bce3-e8cd-4db1-96e7-fbcaf984b709
+    VERITY_GUID=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5
+    SIGNATURE_GUID=41092b05-9fc8-4523-994f-2def0408b176
+    ARCHITECTURE="x86-64"
+elif [[ "$machine" =~ ^(i386|i686|x86)$ ]]; then
+    ROOT_GUID=44479540-f297-41b2-9af7-d131d5f0458a
+    VERITY_GUID=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
+    SIGNATURE_GUID=5996fc05-109c-48de-808b-23fa0830b676
+    ARCHITECTURE="x86"
+elif [[ "$machine" =~ ^(aarch64|aarch64_be|armv8b|armv8l)$ ]]; then
+    ROOT_GUID=b921b045-1df0-41c3-af44-4c6f280d3fae
+    VERITY_GUID=df3300ce-d69f-4c92-978c-9bfb0f38d820
+    SIGNATURE_GUID=6db69de6-29f4-4758-a7a5-962190f00ce3
+    ARCHITECTURE="arm64"
+elif [[ "$machine" == "arm" ]]; then
+    ROOT_GUID=69dad710-2ce4-4e3c-b16c-21a1d49abed3
+    VERITY_GUID=7386cdf2-203c-47a9-a498-f2ecce45a2d6
+    SIGNATURE_GUID=42b0455f-eb11-491d-98d3-56145ba9d037
+    ARCHITECTURE="arm"
+elif [[ "$machine" == "ia64" ]]; then
+    ROOT_GUID=993d8d3d-f80e-4225-855a-9daf8ed7ea97
+    VERITY_GUID=86ed10d5-b607-45bb-8957-d350f23d0571
+    SIGNATURE_GUID=e98b36ee-32ba-4882-9b12-0ce14655f46a
+    ARCHITECTURE="ia64"
+elif [[ "$machine" == "loongarch64" ]]; then
+    ROOT_GUID=77055800-792c-4f94-b39a-98c91b762bb6
+    VERITY_GUID=f3393b22-e9af-4613-a948-9d3bfbd0c535
+    SIGNATURE_GUID=5afb67eb-ecc8-4f85-ae8e-ac1e7c50e7d0
+    ARCHITECTURE="loongarch64"
+elif [[ "$machine" == "s390x" ]]; then
+    ROOT_GUID=5eead9a9-fe09-4a1e-a1d7-520d00531306
+    VERITY_GUID=b325bfbe-c7be-4ab8-8357-139e652d2f6b
+    SIGNATURE_GUID=c80187a5-73a3-491a-901a-017c3fa953e9
+    ARCHITECTURE="s390x"
+elif [[ "$machine" == "ppc64le" ]]; then
+    ROOT_GUID=c31c45e6-3f39-412e-80fb-4809c4980599
+    VERITY_GUID=906bd944-4589-4aae-a4e4-dd983917446a
+    SIGNATURE_GUID=d4a236e7-e873-4c07-bf1d-bf6cf7f1c3c6
+    ARCHITECTURE="ppc64-le"
+elif [[ "$machine" == "riscv64" ]]; then
+    ROOT_GUID=72ec70a6-cf74-40e6-bd49-4bda08e8f224
+    VERITY_GUID=b6ed5582-440b-4209-b8da-5ff7c419ea3d
+    SIGNATURE_GUID=efe0f087-ea8d-4469-821a-4c2a96a8386a
+    ARCHITECTURE="riscv64"
+elif [[ "$machine" == "riscv32" ]]; then
+    ROOT_GUID=60d5a7fe-8e7d-435c-b714-3dd8162144e1
+    VERITY_GUID=ae0253be-1167-4007-ac68-43926c14c5de
+    SIGNATURE_GUID=3a112a75-8729-4380-b4cf-764d79934448
+    ARCHITECTURE="riscv32"
+else
+    echo "Unexpected uname -m: $machine in TEST-50-DISSECT.sh, please fix me"
+    exit 1
+fi
+
+udevadm control --log-level=debug
+
+IMAGE_DIR="$(mktemp -d --tmpdir="" TEST-50-IMAGES.XXX)"
+cp -v /usr/share/minimal* "$IMAGE_DIR/"
+MINIMAL_IMAGE="$IMAGE_DIR/minimal_0"
+MINIMAL_IMAGE_ROOTHASH="$(<"$MINIMAL_IMAGE.roothash")"
+
+install_extension_images
+
+OS_RELEASE="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)"
+
+if systemctl --version | grep -q -- +OPENSSL ; then
+    # The openssl binary is installed conditionally. If we have OpenSSL support enabled and openssl is
+    # missing, fail early with a proper error message.
+    if ! command -v openssl &>/dev/null; then
+        echo "openssl binary is missing" >/failed
+        exit 1
+    fi
+
+    OPENSSL_CONFIG="$(mktemp)"
+    # Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
+    cat >"${OPENSSL_CONFIG:?}" <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = DE
+ST = Test State
+L = Test Locality
+O = Org Name
+OU = Org Unit Name
+CN = Common Name
+emailAddress = test@email.com
+EOF
+fi
+
+# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2
+#
+# du rounds up to block size, which is more helpful for partitioning
+root_size="$(du --apparent-size -k "$MINIMAL_IMAGE.raw" | cut -f1)"
+verity_size="$(du --apparent-size -k "$MINIMAL_IMAGE.verity" | cut -f1)"
+signature_size=4
+# 4MB seems to be the minimum size blkid will accept, below that probing fails
+dd if=/dev/zero of="$MINIMAL_IMAGE.gpt" bs=512 count=$((8192+root_size*2+verity_size*2+signature_size*2))
+# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB
+# so do some basic rounding up if the minimal image is more than 1 MB
+if [[ "$root_size" -ge 1024 ]]; then
+    root_size="$((root_size/1024 + 1))MiB"
+else
+    root_size="${root_size}KiB"
+fi
+verity_size="$((verity_size * 2))KiB"
+signature_size="$((signature_size * 2))KiB"
+
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+    # Create key pair
+    openssl req -config "$OPENSSL_CONFIG" -new -x509 -newkey rsa:1024 \
+                -keyout "$MINIMAL_IMAGE.key" -out "$MINIMAL_IMAGE.crt" -days 365 -nodes
+    # Sign Verity root hash with it
+    openssl smime -sign -nocerts -noattr -binary \
+                  -in "$MINIMAL_IMAGE.roothash" \
+                  -inkey "$MINIMAL_IMAGE.key" \
+                  -signer "$MINIMAL_IMAGE.crt" \
+                  -outform der \
+                  -out "$MINIMAL_IMAGE.roothash.p7s"
+    # Generate signature partition JSON data
+    echo '{"rootHash":"'"$MINIMAL_IMAGE_ROOTHASH"'","signature":"'"$(base64 -w 0 <"$MINIMAL_IMAGE.roothash.p7s")"'"}' >"$MINIMAL_IMAGE.verity-sig"
+    # Pad it
+    truncate -s "$signature_size" "$MINIMAL_IMAGE.verity-sig"
+    # Register certificate in the (userspace) verity key ring
+    mkdir -p /run/verity.d
+    ln -s "$MINIMAL_IMAGE.crt" /run/verity.d/ok.crt
+fi
+
+# Construct a UUID from hash
+# input:  11111111222233334444555566667777
+# output: 11111111-2222-3333-4444-555566667777
+uuid="$(head -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
+echo -e "label: gpt\nsize=$root_size, type=$ROOT_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt"
+uuid="$(tail -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
+echo -e "size=$verity_size, type=$VERITY_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt" --append
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+    echo -e "size=$signature_size, type=$SIGNATURE_GUID" | sfdisk "$MINIMAL_IMAGE.gpt" --append
+fi
+sfdisk --part-label "$MINIMAL_IMAGE.gpt" 1 "Root Partition"
+sfdisk --part-label "$MINIMAL_IMAGE.gpt" 2 "Verity Partition"
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+    sfdisk --part-label "$MINIMAL_IMAGE.gpt" 3 "Signature Partition"
+fi
+loop="$(losetup --show -P -f "$MINIMAL_IMAGE.gpt")"
+partitions=(
+    "${loop:?}p1"
+    "${loop:?}p2"
+)
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+    partitions+=("${loop:?}p3")
+fi
+# The kernel sometimes(?) does not emit "add" uevent for loop block partition devices.
+# Let's not expect the devices to be initialized.
+udevadm wait --timeout 60 --settle --initialized=no "${partitions[@]}"
+udevadm lock --device="${loop}p1" dd if="$MINIMAL_IMAGE.raw" of="${loop}p1"
+udevadm lock --device="${loop}p2" dd if="$MINIMAL_IMAGE.verity" of="${loop}p2"
+if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
+    udevadm lock --device="${loop}p3" dd if="$MINIMAL_IMAGE.verity-sig" of="${loop}p3"
+fi
+losetup -d "$loop"
+
+: "Run subtests"
+
+run_subtests
+
+touch /testok
diff --git a/test/units/TEST-50-DISSECT.sysext.sh b/test/units/TEST-50-DISSECT.sysext.sh
new file mode 100755 (executable)
index 0000000..3bc8899
--- /dev/null
@@ -0,0 +1,1012 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+FAKE_ROOTS_DIR="$(mktemp -d --tmpdir="" fake-roots-XXX)"
+FSTYPE=$(stat --file-system --format "%T" /usr)
+
+shopt -s nullglob
+
+# shellcheck disable=SC2317
+at_exit() {
+    set +ex
+
+    local target
+
+    # Note: `cat` here is used intentionally, so we iterate over our own copy of /proc/mounts. Otherwise
+    #       things get very confusing once we start unmounting things, due to changing file offsets.
+    # shellcheck disable=SC2002
+    cat /proc/mounts | while read -r _ target _ _ _ _; do
+        if [[ "$target" =~ ^"$FAKE_ROOTS_DIR" ]]; then
+            umount -Rv "$target"
+        fi
+    done
+
+    rm -rf "${FAKE_ROOTS_DIR}"
+}
+
+trap at_exit EXIT
+
+# Clears the trap command - it needs to be invoked for every test-case subshell
+# so prepending commands with prepend_trap inside the subshell won't preserve
+# the trap commands from outer shell.
+init_trap() {
+    trap - EXIT
+}
+
+prepend_trap() {
+    set +x
+
+    local command=${1}; shift
+    local previous_commands
+
+    previous_commands=$(trap -p EXIT)
+    if [[ -z $previous_commands ]]; then
+        previous_commands=':'
+    else
+        previous_commands=${previous_commands#'trap -- '}
+        previous_commands=${previous_commands%' EXIT'}
+        previous_commands=$(xargs <<<"$previous_commands")
+    fi
+
+    # shellcheck disable=SC2064 # We use double quotes on purpose here.
+    trap "${command}; ${previous_commands}" EXIT
+
+    set -x
+}
+
+prepare_root() {
+    local root=${1:-}
+    local hierarchy=${2:?}
+    local dir
+
+    if [[ -n $root ]] && [[ -d $root ]]; then
+        echo >&2 "Directory $root already exists, possible copy-paste error?"
+        exit 1
+    fi
+
+    local -a leftovers=( "$root/var/lib/extensions/"* "$root/var/lib/extensions.mutable/"* )
+    if [[ ${#leftovers[@]} -gt 0 ]]; then
+        echo >&2 "Leftovers remained, make sure to clean them up in the test case: ${leftovers[*]}"
+        exit 1
+    fi
+
+    for dir in "$hierarchy" "/usr/lib" "/var/lib/extensions/" "/var/lib/extensions.mutable"; do
+        mkdir -p "$root$dir"
+    done
+
+    if [[ -e $root/usr/lib/os-release ]]; then
+        mv "$root/usr/lib/os-release" "$root/usr/lib/os-release.orig"
+    fi
+
+    {
+        echo "ID=testtest"
+        echo "VERSION=1.2.3"
+    } >"$root/usr/lib/os-release"
+
+    prepend_trap "cleanup_os_release ${root@Q}"
+}
+
+cleanup_os_release() {
+    # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
+    local root=${1:-}
+
+    # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
+    rm -f "$root/usr/lib/os-release"
+    # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
+    if [[ -e $root/usr/lib/os-release.orig ]]; then
+        # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
+        mv "$root/usr/lib/os-release.orig" "$root/usr/lib/os-release"
+    fi
+}
+
+prepare_extension_image() {
+    local root=${1:-}
+    local hierarchy=${2:?}
+    local ext_dir ext_release name
+
+    name="test-extension"
+    ext_dir="$root/var/lib/extensions/$name"
+    ext_release="$ext_dir/usr/lib/extension-release.d/extension-release.$name"
+    mkdir -p "${ext_release%/*}"
+    echo "ID=_any" >"$ext_release"
+    mkdir -p "$ext_dir/$hierarchy"
+    touch "$ext_dir$hierarchy/preexisting-file-in-extension-image"
+
+    prepend_trap "rm -rf ${ext_dir@Q}"
+}
+
+prepare_extension_mutable_dir() {
+    local dir=${1:?}
+
+    mkdir -p "$dir"
+    touch "$dir/preexisting-file-in-extensions-mutable"
+    prepend_trap "rm -rf ${dir@Q}"
+}
+
+make_read_only() {
+    local root=${1:-}
+    local hierarchy=${2:?}
+
+    mount -o bind,ro "$root$hierarchy" "$root$hierarchy"
+    prepend_trap "umount ${root@Q}${hierarchy@Q}"
+}
+
+prepare_hierarchy() {
+    local root=${1:-}
+    local hierarchy=${2:?}
+    local file
+
+    file="$root$hierarchy/preexisting-file-in-hierarchy"
+    touch "$file"
+    prepend_trap "rm -f ${file@Q}"
+}
+
+prepare_read_only_hierarchy() {
+    local root=${1:-}
+    local hierarchy=${2:?}
+
+    prepare_hierarchy "$root" "$hierarchy"
+    make_read_only "$root" "$hierarchy"
+}
+
+move_existing_hierarchy_aside() {
+    local root=${1:-}
+    local hierarchy=${2:?}
+
+    if [[ -z $root ]] && [[ $hierarchy = /usr ]]; then
+        echo >&2 "Hell no, not moving /usr aside"
+        exit 1
+    fi
+
+    local path=$root$hierarchy
+
+    if [[ -e $path ]]; then
+        mv "$path" "$path.orig"
+        prepend_trap "mv ${path@Q}.orig ${path@Q}"
+    fi
+}
+
+# Extra arguments:
+#   -e: check for a preexisting file in extension
+#   -h: check for a preexisting file in hierarchy
+#   -u: check for a preexisting file in upperdir
+extension_verify() {
+    local root=${1:-}
+    local hierarchy=${2:?}
+    local message=${3:?}
+    shift 3
+    # Map each option to a pre-defined file name
+    local -A option_files_map=(
+        [e]="preexisting-file-in-extension-image"
+        [h]="preexisting-file-in-hierarchy"
+        [u]="preexisting-file-in-extensions-mutable"
+    )
+    local -A args=(
+        [e]=0
+        [h]=0
+        [u]=0
+    )
+    local file full_path opt option
+
+    while getopts "ehu" opt; do
+        case "$opt" in
+            e|h|u)
+                args["$opt"]=1
+                ;;
+            *)
+                echo >&2 "Unxexpected option: $opt"
+                exit 1
+        esac
+    done
+
+    for option in "${!option_files_map[@]}"; do
+        file=${option_files_map["$option"]}
+        full_path="$root$hierarchy/$file"
+
+        if [[ ${args["$option"]} -ne 0 ]]; then
+            if [[ ! -f $full_path ]]; then
+                ls -la "$root$hierarchy"
+                echo >&2 "Expected file '$file' to exist under $root$hierarchy $message"
+                exit 1
+            fi
+        else
+            if [[ -f $full_path ]]; then
+                ls -la "$root$hierarchy"
+                echo >&2 "Expected file '$file' to not exist under $root$hierarchy $message"
+                exit 1
+            fi
+        fi
+    done
+}
+
+extension_verify_after_merge() (
+    set +x
+
+    local root=${1:-}
+    local hierarchy=${2:?}
+    shift 2
+
+    extension_verify "$root" "$hierarchy" "after merge" "$@"
+)
+
+extension_verify_after_unmerge() (
+    set +x
+
+    local root=${1:-}
+    local hierarchy=${2:?}
+    shift 2
+
+    extension_verify "$root" "$hierarchy" "after unmerge" "$@"
+)
+
+run_systemd_sysext() {
+    local root=${1:-}
+    shift
+
+    local -a sysext_args
+    sysext_args=()
+
+    if [[ -n $root ]]; then
+        sysext_args+=( "--root=$root" )
+    fi
+    sysext_args+=( "$@" )
+    systemd-sysext "${sysext_args[@]}"
+}
+
+# General systemd-sysext tests
+
+run_sysext_tests() {
+    # The roots_dir variable may be empty - in such case all the tests will run
+    # on /, otherwise they will run on $roots_dir/<SEPARATE_DIR_FOR_TEST>.
+    local roots_dir=${1}; shift
+
+    # Each test runs in a subshell, so we can use traps for cleanups without
+    # clobbering toplevel traps, and we can do skips by invoking "exit 0".
+
+( init_trap
+: "No extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-read-only-hierarchy"}
+hierarchy=/opt
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+)
+
+
+( init_trap
+: "No extension data in /var/lib/extensions.mutable/…, mutable hierarchy, mutability disabled by default, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-mutable-hierarchy"}
+hierarchy=/opt
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_hierarchy "$fake_root" "$hierarchy"
+touch "$fake_root$hierarchy/should-succeed-on-mutable-fs"
+
+run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+touch "$fake_root$hierarchy/should-succeed-on-mutable-fs-again"
+)
+
+
+( init_trap
+: "No extension data in /var/lib/extensions.mutable/…, no hierarchy either, mutability disabled by default, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-missing-hierarchy"}
+hierarchy=/opt
+
+move_existing_hierarchy_aside "$fake_root" "$hierarchy"
+prepare_root "$fake_root" "$hierarchy"
+rmdir "$fake_root/$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+
+run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+)
+
+
+( init_trap
+: "No extension data in /var/lib/extensions.mutable/…, empty hierarchy, mutability disabled by default, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-empty-hierarchy"}
+hierarchy=/opt
+
+move_existing_hierarchy_aside "$fake_root" "$hierarchy"
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+make_read_only "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-read-only-hierarchy-disabled"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-be-read-only")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-read-only-hierarchy"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, missing hierarchy, auto-mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-missing-hierarchy"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+move_existing_hierarchy_aside "$fake_root" "$hierarchy"
+prepare_root "$fake_root" "$hierarchy"
+rmdir "$fake_root/$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
+test -f "$extension_data_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, empty hierarchy, auto-mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-empty-hierarchy"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+move_existing_hierarchy_aside "$fake_root" "$hierarchy"
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+make_read_only "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
+test -f "$extension_data_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy"
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… is a symlink to other dir, R/O hierarchy, auto-mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/mutable-symlink-with-read-only-hierarchy"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root/upperdir"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, mutable hierarchy, auto-mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/mutable-self-upper"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
+touch "$fake_root$hierarchy/preexisting-file-in-hierarchy"
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h -u
+test -f "$extension_data_dir/now-is-mutable"
+test -f "$extension_real_dir/now-is-mutable"
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, R/O hierarchy, auto-mutability, expected fail"
+fake_root=${roots_dir:+"$roots_dir/failure-self-upper-ro"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+
+(! run_systemd_sysext "$fake_root" --mutable=auto merge)
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… is a dangling symlink, auto-mutability, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/read-only-mutable-dangling-symlink"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+ln -sfTr "/should/not/exist/" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… exists but ignored, mutability disabled explicitly, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/disabled"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=no merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… exists but is imported instead, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/imported"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=import merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… does not exist, mutability enabled, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/enabled"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+test ! -d "$extension_data_dir"
+
+run_systemd_sysext "$fake_root" --mutable=yes merge
+# systemd-sysext with --mutable=yes creates extensions.mutable directory for
+# the hierarchy, so delete it after the test
+prepend_trap "rm -rf ${extension_data_dir@Q}"
+# systemd-sysext with --mutable=yes creates extensions.mutable directory also
+# for the /usr hierarchy, because the image needs to have
+# /usr/lib/extension-release.d/extension-release.<NAME> file - this causes the
+# /usr hierarchy to also become mutable
+prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
+test -d "$extension_data_dir"
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test -f "$extension_data_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… does not exist, auto-mutability, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/simple-read-only-explicit"}
+hierarchy=/opt
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=auto merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… does not exist, mutability enabled through env var, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/enabled-env-var"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+test ! -d "$extension_data_dir"
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" merge
+# systemd-sysext with --mutable=yes creates extensions.mutable directory for
+# the hierarchy, so delete it after the test
+prepend_trap "rm -rf ${extension_data_dir@Q}"
+# systemd-sysext with --mutable=yes creates extensions.mutable directory also
+# for the /usr hierarchy, because the image needs to have
+# /usr/lib/extension-release.d/extension-release.<NAME> file - this causes the
+# /usr hierarchy to also become mutable
+prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
+test -d "$extension_data_dir"
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… does not exist, auto-mutability enabled through env var, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/read-only-auto-env-var"}
+hierarchy=/opt
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" --mutable=auto merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability enabled through env var, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/auto-mutable-env-var"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled through env var, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/env-var-disabled"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=no run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-be-read-only")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=no run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "/var/lib/extensions.mutable/… exists but is imported through env var, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/imported-env-var"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=import run_systemd_sysext "$fake_root" merge
+(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=import run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability enabled through env var but overridden via CLI option, read-only merged"
+fake_root=${roots_dir:+"$roots_dir/env-var-overridden"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" --mutable=no merge
+(! touch "$fake_root$hierarchy/should-be-read-only")
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/ephemeral"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=ephemeral merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test ! -f "$extension_data_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability through env var, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/ephemeral-env-var"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral run_systemd_sysext "$fake_root" merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
+test ! -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/ephemeral-import"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+run_systemd_sysext "$fake_root" --mutable=ephemeral-import merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test ! -f "$extension_data_dir/now-is-mutable"
+
+run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability through env var, mutable merged"
+fake_root=${roots_dir:+"$roots_dir/ephemeral-import-env-var"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
+(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import run_systemd_sysext "$fake_root" merge
+touch "$fake_root$hierarchy/now-is-mutable"
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
+test ! -f "$extension_data_dir/now-is-mutable"
+
+SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import run_systemd_sysext "$fake_root" unmerge
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
+test ! -f "$extension_data_dir/now-is-mutable"
+test ! -f "$fake_root$hierarchy/now-is-mutable"
+)
+
+
+( init_trap
+: "Extension data pointing to mutable hierarchy, ephemeral import mutability, expected fail"
+fake_root=${roots_dir:+"$roots_dir/ephemeral-import-self"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
+prepare_hierarchy "$fake_root" "$hierarchy"
+touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
+
+(! run_systemd_sysext "$fake_root" --mutable=ephemeral-import merge)
+)
+
+
+( init_trap
+: "Extension data pointing to mutable hierarchy, import mutability, expected fail"
+fake_root=${roots_dir:+"$roots_dir/import-self"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+extension_real_dir="$fake_root$hierarchy"
+
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_real_dir"
+ln -sfTr "$extension_real_dir" "$extension_data_dir"
+prepend_trap "rm -f ${extension_data_dir@Q}"
+prepare_hierarchy "$fake_root" "$hierarchy"
+touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
+
+(! run_systemd_sysext "$fake_root" --mutable=import merge)
+)
+
+
+for mutable_mode in no yes ephemeral; do
+    ( init_trap
+    : "Check if merging the hierarchy does not change its permissions, checking with --mutable=${mutable_mode}"
+
+    fake_root=${roots_dir:+"$roots_dir/perm-checks-mutable-$mutable_mode"}
+    hierarchy=/opt
+    extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+    [[ "$FSTYPE" == "fuseblk" ]] && exit 0
+
+    prepare_root "$fake_root" "$hierarchy"
+    prepare_extension_image "$fake_root" "$hierarchy"
+    prepare_extension_mutable_dir "$extension_data_dir"
+    prepare_read_only_hierarchy "${fake_root}" "${hierarchy}"
+
+    full_path="$fake_root$hierarchy"
+    permissions_before_merge=$(stat --format=%A "$full_path")
+
+    run_systemd_sysext "$fake_root" "--mutable=$mutable_mode" merge
+    if [[ $mutable_mode = yes ]]; then
+        # systemd-sysext with --mutable=yes creates extensions.mutable
+        # directory also for the /usr hierarchy, because the image needs to
+        # have /usr/lib/extension-release.d/extension-release.<NAME> file -
+        # this causes the /usr hierarchy to also become mutable
+        extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
+        prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
+    fi
+
+    permissions_after_merge=$(stat --format=%A "$full_path")
+
+    run_systemd_sysext "$fake_root" unmerge
+
+    permissions_after_unmerge=$(stat --format=%A "$full_path")
+
+    if [[ "$permissions_before_merge" != "$permissions_after_merge" ]]; then
+        echo >&2 "Broken hierarchy permissions after merging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_merge@Q}"
+        exit 1
+    fi
+
+    if [[ "$permissions_before_merge" != "$permissions_after_unmerge" ]]; then
+        echo >&2 "Broken hierarchy permissions after unmerging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_unmerge@Q}"
+        exit 1
+    fi
+    )
+done
+
+
+( init_trap
+: "Check if merging fails in case of invalid mutable directory permissions"
+
+fake_root=${roots_dir:+"$roots_dir/mutable-directory-with-invalid-permissions"}
+hierarchy=/opt
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
+
+prepare_root "$fake_root" "$hierarchy"
+prepare_extension_image "$fake_root" "$hierarchy"
+prepare_extension_mutable_dir "$extension_data_dir"
+prepare_hierarchy "$fake_root" "$hierarchy"
+
+old_mode=$(stat --format '%#a' "$fake_root$hierarchy")
+chmod 0755 "$fake_root$hierarchy"
+prepend_trap "chmod ${old_mode@Q} ${fake_root@Q}${hierarchy@Q}"
+chmod 0700 "$extension_data_dir"
+
+(! run_systemd_sysext "$fake_root" --mutable=yes merge)
+)
+
+} # End of run_sysext_tests
+
+
+# For preparing /, we need mutable /usr/. If it is read only, skip running the
+# sysext tests on /.
+if [[ -w /usr ]]; then
+    run_sysext_tests ''
+fi
+run_sysext_tests "$FAKE_ROOTS_DIR"
+
+
+exit 0
diff --git a/test/units/TEST-52-HONORFIRSTSHUTDOWN.service b/test/units/TEST-52-HONORFIRSTSHUTDOWN.service
new file mode 100644 (file)
index 0000000..966b9b6
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-52-HONORFIRSTSHUTDOWN
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-52-HONORFIRSTSHUTDOWN.sh b/test/units/TEST-52-HONORFIRSTSHUTDOWN.sh
new file mode 100755 (executable)
index 0000000..16ff507
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+
+systemctl enable test-honor-first-shutdown.service
+systemctl start test-honor-first-shutdown.service
+
+touch /testok
diff --git a/test/units/TEST-53-ISSUE-16347.service b/test/units/TEST-53-ISSUE-16347.service
new file mode 100644 (file)
index 0000000..cf3adbb
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-53-ISSUE-16347
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-53-ISSUE-16347.sh b/test/units/TEST-53-ISSUE-16347.sh
new file mode 100755 (executable)
index 0000000..84cd661
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+: >/failed
+
+# Reset host date to current time, 3 days in the past.
+date -s "-3 days"
+
+# Run a timer for every 15 minutes.
+systemd-run --unit test-timer --on-calendar "*:0/15:0" true
+
+next_elapsed=$(systemctl show test-timer.timer -p NextElapseUSecRealtime --value)
+next_elapsed=$(date -d "${next_elapsed}" +%s)
+now=$(date +%s)
+time_delta=$((next_elapsed - now))
+
+# Check that the timer will elapse in less than 20 minutes.
+((0 < time_delta && time_delta < 1200)) || {
+    echo 'Timer elapse outside of the expected 20 minute window.'
+    echo "  next_elapsed=${next_elapsed}"
+    echo "  now=${now}"
+    echo "  time_delta=${time_delta}"
+    echo ''
+} >>/failed
+
+if test ! -s /failed ; then
+    rm -f /failed
+    touch /testok
+fi
diff --git a/test/units/TEST-54-CREDS.service b/test/units/TEST-54-CREDS.service
new file mode 100644 (file)
index 0000000..94bf2b7
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-54-CREDS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-54-CREDS.sh b/test/units/TEST-54-CREDS.sh
new file mode 100755 (executable)
index 0000000..fe410d5
--- /dev/null
@@ -0,0 +1,402 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemd-analyze log-level debug
+
+run_with_cred_compare() (
+    local cred="${1:?}"
+    local exp="${2?}"
+    local log_file
+    shift 2
+
+    log_file="$(mktemp)"
+    # shellcheck disable=SC2064
+    trap "rm -f '$log_file'" RETURN
+
+    set -o pipefail
+    systemd-run -p SetCredential="$cred" --wait --pipe -- systemd-creds "$@" | tee "$log_file"
+    diff "$log_file" <(echo -ne "$exp")
+)
+
+# Sanity checks
+#
+# Create a dummy "full" disk (similar to /dev/full) to check out-of-space
+# scenarios
+mkdir /tmp/full
+mount -t tmpfs -o size=1,nr_inodes=1 tmpfs /tmp/full
+
+# verb: setup
+# Run this first, otherwise any encrypted credentials wouldn't be decryptable
+# as we regenerate the host key
+rm -fv /var/lib/systemd/credential.secret
+systemd-creds setup
+test -e /var/lib/systemd/credential.secret
+rm -fv /var/lib/systemd/credential.secret
+
+# Prepare a couple of dummy credentials for the cat/list verbs
+CRED_DIR="$(mktemp -d)"
+ENC_CRED_DIR="$(mktemp -d)"
+echo foo >"$CRED_DIR/secure-or-weak"
+echo foo >"$CRED_DIR/insecure"
+echo foo | systemd-creds --name="encrypted" encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted"
+echo foo | systemd-creds encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted-unnamed"
+chmod -R 0400 "$CRED_DIR" "$ENC_CRED_DIR"
+chmod -R 0444 "$CRED_DIR/insecure"
+mkdir /tmp/empty/
+
+systemd-creds --system
+systemd-creds --no-pager --help
+systemd-creds --version
+systemd-creds has-tpm2 || :
+systemd-creds has-tpm2 -q || :
+
+# verb: list
+systemd-creds list --system
+ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --no-legend
+ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=pretty | jq
+ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=short | jq
+ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=off
+ENCRYPTED_CREDENTIALS_DIRECTORY="/tmp/empty/" CREDENTIALS_DIRECTORY="/tmp/empty/" systemd-creds list
+
+# verb: cat
+for cred in secure-or-weak insecure encrypted encrypted-unnamed; do
+    ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds cat "$cred"
+done
+run_with_cred_compare "mycred:" "" cat mycred
+run_with_cred_compare "mycred:\n" "\n" cat mycred
+run_with_cred_compare "mycred:foo" "foo" cat mycred
+run_with_cred_compare "mycred:foo" "foofoofoo" cat mycred mycred mycred
+# Note: --newline= does nothing when stdout is not a tty, which is the case here
+run_with_cred_compare "mycred:foo" "foo" --newline=yes cat mycred
+run_with_cred_compare "mycred:foo" "foo" --newline=no cat mycred
+run_with_cred_compare "mycred:foo" "foo" --newline=auto cat mycred
+run_with_cred_compare "mycred:foo" "foo" --transcode=no cat mycred
+run_with_cred_compare "mycred:foo" "foo" --transcode=0 cat mycred
+run_with_cred_compare "mycred:foo" "foo" --transcode=false cat mycred
+run_with_cred_compare "mycred:foo" "Zm9v" --transcode=base64 cat mycred
+run_with_cred_compare "mycred:Zm9v" "foo" --transcode=unbase64 cat mycred
+run_with_cred_compare "mycred:Zm9v" "foofoofoo" --transcode=unbase64 cat mycred mycred mycred
+run_with_cred_compare "mycred:Zm9vCg==" "foo\n" --transcode=unbase64 cat mycred
+run_with_cred_compare "mycred:hello world" "68656c6c6f20776f726c64" --transcode=hex cat mycred
+run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello world" --transcode=unhex cat mycred
+run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello worldhello world" --transcode=unhex cat mycred mycred
+run_with_cred_compare "mycred:68656c6c6f0a776f726c64" "hello\nworld" --transcode=unhex cat mycred
+run_with_cred_compare 'mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' '{"foo":"bar","baz":[3,4]}\n' --json=short cat mycred
+systemd-run -p SetCredential='mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' --wait --pipe -- systemd-creds --json=pretty cat mycred | jq
+
+# verb: encrypt/decrypt
+echo "According to all known laws of aviation..." >/tmp/cred.orig
+systemd-creds --with-key=host encrypt /tmp/cred.orig /tmp/cred.enc
+systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec
+diff /tmp/cred.orig /tmp/cred.dec
+rm -f /tmp/cred.{enc,dec}
+# --pretty
+cred_name="fo'''o''bar"
+cred_option="$(systemd-creds --pretty --name="$cred_name" encrypt /tmp/cred.orig -)"
+mkdir -p /run/systemd/system
+cat >/run/systemd/system/test-54-pretty-cred.service <<EOF
+[Service]
+Type=oneshot
+${cred_option:?}
+ExecStart=bash -c "diff <(systemd-creds cat \"$cred_name\") /tmp/cred.orig"
+EOF
+systemctl daemon-reload
+systemctl start test-54-pretty-cred
+rm /run/systemd/system/test-54-pretty-cred.service
+# Credential validation: name
+systemd-creds --name="foo" -H encrypt /tmp/cred.orig /tmp/cred.enc
+(! systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec)
+(! systemd-creds --name="bar" decrypt /tmp/cred.enc /tmp/cred.dec)
+systemd-creds --name="" decrypt /tmp/cred.enc /tmp/cred.dec
+diff /tmp/cred.orig /tmp/cred.dec
+rm -f /tmp/cred.dec
+systemd-creds --name="foo" decrypt /tmp/cred.enc /tmp/cred.dec
+diff /tmp/cred.orig /tmp/cred.dec
+rm -f /tmp/cred.{enc,dec}
+# Credential validation: time
+systemd-creds --not-after="+1d" encrypt /tmp/cred.orig /tmp/cred.enc
+(! systemd-creds --timestamp="+2d" decrypt /tmp/cred.enc /tmp/cred.dec)
+systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec
+diff /tmp/cred.orig /tmp/cred.dec
+rm -f /tmp/cred.{enc,dec}
+
+(! unshare -m bash -exc "mount -t tmpfs tmpfs /run/credentials && systemd-creds list")
+(! unshare -m bash -exc "mount -t tmpfs tmpfs /run/credentials && systemd-creds --system list")
+(! CREDENTIALS_DIRECTORY="" systemd-creds list)
+(! systemd-creds --system --foo)
+(! systemd-creds --system -@)
+(! systemd-creds --system --json=)
+(! systemd-creds --system --json="")
+(! systemd-creds --system --json=foo)
+(! systemd-creds --system cat)
+(! systemd-creds --system cat "")
+(! systemd-creds --system cat this-should-not-exist)
+(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode= cat mycred)
+(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode="" cat mycred)
+(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode=foo cat mycred)
+(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --newline=foo cat mycred)
+(! systemd-run -p SetCredential=mycred:notbase64 --wait --pipe -- systemd-creds --transcode=unbase64 cat mycred)
+(! systemd-run -p SetCredential=mycred:nothex --wait --pipe -- systemd-creds --transcode=unhex cat mycred)
+(! systemd-run -p SetCredential=mycred:a --wait --pipe -- systemd-creds --transcode=unhex cat mycred)
+(! systemd-run -p SetCredential=mycred:notjson --wait --pipe -- systemd-creds --json=short cat mycred)
+(! systemd-run -p SetCredential=mycred:notjson --wait --pipe -- systemd-creds --json=pretty cat mycred)
+(! systemd-creds encrypt /foo/bar/baz -)
+(! systemd-creds decrypt /foo/bar/baz -)
+(! systemd-creds decrypt / -)
+(! systemd-creds encrypt / -)
+(! echo foo | systemd-creds --with-key=foo encrypt - -)
+(! echo {0..20} | systemd-creds decrypt - -)
+(! systemd-creds --not-after= encrypt /tmp/cred.orig /tmp/cred.enc)
+(! systemd-creds --not-after="" encrypt /tmp/cred.orig /tmp/cred.enc)
+(! systemd-creds --not-after="-1d" encrypt /tmp/cred.orig /tmp/cred.enc)
+(! systemd-creds --timestamp= encrypt /tmp/cred.orig /tmp/cred.enc)
+(! systemd-creds --timestamp="" encrypt /tmp/cred.orig /tmp/cred.enc)
+(! dd if=/dev/zero count=2M | systemd-creds --with-key=tpm2-absent encrypt - /dev/null)
+(! dd if=/dev/zero count=2M | systemd-creds --with-key=tpm2-absent decrypt - /dev/null)
+(! echo foo | systemd-creds encrypt - /tmp/full/foo)
+(! echo foo | systemd-creds encrypt - - | systemd-creds decrypt - /tmp/full/foo)
+
+# Verify that the creds are properly loaded and we can read them from the service's unpriv user
+systemd-run -p LoadCredential=passwd:/etc/passwd \
+    -p LoadCredential=shadow:/etc/shadow \
+    -p SetCredential=dog:wuff \
+    -p DynamicUser=1 \
+    --unit=test-54-unpriv.service \
+    --wait \
+    --pipe \
+    cat '${CREDENTIALS_DIRECTORY}/passwd' '${CREDENTIALS_DIRECTORY}/shadow' '${CREDENTIALS_DIRECTORY}/dog' \
+    >/tmp/ts54-concat
+(cat /etc/passwd /etc/shadow && echo -n wuff) | cmp /tmp/ts54-concat
+rm /tmp/ts54-concat
+
+# Test that SetCredential= acts as fallback for LoadCredential=
+echo piff >/tmp/ts54-fallback
+[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "piff" ]
+rm /tmp/ts54-fallback
+[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "poff" ]
+
+if systemd-detect-virt -q -c ; then
+    expected_credential=mynspawncredential
+    expected_value=strangevalue
+elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then
+    # Verify that passing creds through kernel cmdline works
+    [ "$(systemd-creds --system cat kernelcmdlinecred)" = "uff" ]
+    [ "$(systemd-creds --system cat waldi)" = "woooofffwufffwuff" ]
+
+    # And that it also works via SMBIOS
+    [ "$(systemd-creds --system cat smbioscredential)" = "magicdata" ]
+    [ "$(systemd-creds --system cat binarysmbioscredential)" = "magicbinarydata" ]
+
+    # If we aren't run in nspawn, we are run in qemu
+    systemd-detect-virt -q -v
+    expected_credential=myqemucredential
+    expected_value=othervalue
+
+    # Verify that writing a sysctl via the kernel cmdline worked
+    [ "$(cat /proc/sys/kernel/domainname)" = "sysctltest" ]
+
+    # Verify that creating a user via sysusers via the kernel cmdline worked
+    grep -q ^credtestuser: /etc/passwd
+
+    # Verify that writing a file via tmpfiles worked
+    [ "$(cat /tmp/sourcedfromcredential)" = "tmpfilessecret" ]
+    [ "$(cat /etc/motd.d/50-provision.conf)" = "hello" ]
+    [ "$(cat /etc/issue.d/50-provision.conf)" = "welcome" ]
+
+    # Verify that adding a unit and drop-in via credentials worked
+    systemctl start my-service
+    test -f /tmp/unit-cred
+    test -f /tmp/unit-dropin
+    test -f /tmp/unit-named-dropin
+else
+    echo "qemu_fw_cfg support missing in kernel. Sniff!"
+    expected_credential=""
+    expected_value=""
+fi
+
+if [ "$expected_credential" != "" ] ; then
+    # If this test is run in nspawn a credential should have been passed to us. See test/TEST-54-CREDS/test.sh
+    [ "$(systemd-creds --system cat "$expected_credential")" = "$expected_value" ]
+
+    # Test that propagation from system credential to service credential works
+    [ "$(systemd-run -p LoadCredential="$expected_credential" --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ]
+
+    # Check it also works, if we rename it while propagating it
+    [ "$(systemd-run -p LoadCredential=miau:"$expected_credential" --pipe --wait systemd-creds cat miau)" = "$expected_value" ]
+
+    # Combine it with a fallback (which should have no effect, given the cred should be passed down)
+    [ "$(systemd-run -p LoadCredential="$expected_credential" -p SetCredential="$expected_credential":zzz --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ]
+
+    # This should succeed
+    systemd-run -p AssertCredential="$expected_credential" -p Type=oneshot true
+
+    # And this should fail
+    (! systemd-run -p AssertCredential="undefinedcredential" -p Type=oneshot true)
+fi
+
+# Verify that the creds are immutable
+(! systemd-run -p LoadCredential=passwd:/etc/passwd \
+    -p DynamicUser=1 \
+    --unit=test-54-immutable-touch.service \
+    --wait \
+    touch '${CREDENTIALS_DIRECTORY}/passwd')
+(! systemd-run -p LoadCredential=passwd:/etc/passwd \
+    -p DynamicUser=1 \
+    --unit=test-54-immutable-rm.service \
+    --wait \
+    rm '${CREDENTIALS_DIRECTORY}/passwd')
+
+# Check directory-based loading
+mkdir -p /tmp/ts54-creds/sub
+echo -n a >/tmp/ts54-creds/foo
+echo -n b >/tmp/ts54-creds/bar
+echo -n c >/tmp/ts54-creds/baz
+echo -n d >/tmp/ts54-creds/sub/qux
+systemd-run -p LoadCredential=cred:/tmp/ts54-creds \
+    -p DynamicUser=1 \
+    --unit=test-54-dir.service \
+    --wait \
+    --pipe \
+    cat '${CREDENTIALS_DIRECTORY}/cred_foo' \
+        '${CREDENTIALS_DIRECTORY}/cred_bar' \
+        '${CREDENTIALS_DIRECTORY}/cred_baz' \
+        '${CREDENTIALS_DIRECTORY}/cred_sub_qux' >/tmp/ts54-concat
+cmp /tmp/ts54-concat <(echo -n abcd)
+rm /tmp/ts54-concat
+rm -rf /tmp/ts54-creds
+
+# Check that globs work as expected
+mkdir -p /run/credstore
+echo -n a >/run/credstore/test.creds.first
+echo -n b >/run/credstore/test.creds.second
+mkdir -p /etc/credstore
+echo -n c >/etc/credstore/test.creds.third
+systemd-run -p "ImportCredential=test.creds.*" \
+            --unit=test-54-ImportCredential.service \
+            -p DynamicUser=1 \
+            --wait \
+            --pipe \
+            cat '${CREDENTIALS_DIRECTORY}/test.creds.first' \
+                '${CREDENTIALS_DIRECTORY}/test.creds.second' \
+                '${CREDENTIALS_DIRECTORY}/test.creds.third' >/tmp/ts54-concat
+cmp /tmp/ts54-concat <(echo -n abc)
+
+# Now test encrypted credentials (only supported when built with OpenSSL though)
+if systemctl --version | grep -q -- +OPENSSL ; then
+    echo -n $RANDOM >/tmp/test-54-plaintext
+    systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext
+    systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext
+
+    systemd-run -p LoadCredentialEncrypted=test-54:/tmp/test-54-ciphertext \
+        --wait \
+        --pipe \
+        cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext
+
+    echo -n $RANDOM >/tmp/test-54-plaintext
+    systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext
+    systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext
+
+    systemd-run -p SetCredentialEncrypted=test-54:"$(cat /tmp/test-54-ciphertext)" \
+        --wait \
+        --pipe \
+        cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext
+
+    rm /tmp/test-54-plaintext /tmp/test-54-ciphertext
+fi
+
+# https://github.com/systemd/systemd/issues/27275
+systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \
+            -p 'ExecStartPre=true' \
+            -p 'ExecStartPre=systemd-creds cat os' \
+            --unit=test-54-exec-start-pre.service \
+            --wait \
+            --pipe \
+            true | cmp /etc/os-release
+
+# https://github.com/systemd/systemd/issues/31194
+systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \
+            -p 'ExecStartPost=systemd-creds cat os' \
+            --unit=test-54-exec-start-post.service \
+            --service-type=oneshot --wait --pipe \
+            true | cmp /etc/os-release
+
+# https://github.com/systemd/systemd/pull/24734#issuecomment-1925440546
+# Also ExecStartPre= should be able to update creds
+dd if=/dev/urandom of=/tmp/cred-huge bs=600K count=1
+chmod 777 /tmp/cred-huge
+systemd-run -p ProtectSystem=full \
+            -p 'LoadCredential=huge:/tmp/cred-huge' \
+            -p 'ExecStartPre=true' \
+            -p 'ExecStartPre=bash -c "echo fresh >/tmp/cred-huge"' \
+            --unit=test-54-huge-cred.service \
+            --wait --pipe \
+            systemd-creds cat huge | cmp - <(echo "fresh")
+rm /tmp/cred-huge
+
+echo stable >/tmp/cred-stable
+systemd-run -p 'LoadCredential=stable:/tmp/cred-stable' \
+            -p 'ExecStartPost=systemd-creds cat stable' \
+            --unit=test-54-stable.service \
+            --service-type=oneshot --wait --pipe \
+            bash -c "echo bogus >/tmp/cred-stable" | cmp - <(echo "stable")
+assert_eq "$(cat /tmp/cred-stable)" "bogus"
+rm /tmp/cred-stable
+
+if ! systemd-detect-virt -q -c ; then
+    # Validate that the credential we inserted via the initrd logic arrived
+    test "$(systemd-creds cat --system myinitrdcred)" = "guatemala"
+
+    # Check that the fstab credential logic worked
+    test -d /injected
+    grep -q /injected /proc/self/mountinfo
+
+    # Make sure the getty generator processed the credentials properly
+    systemctl -P Wants show getty.target | grep -q container-getty@idontexist.service
+fi
+
+# Decrypt/encrypt via varlink
+
+echo '{"data":"Zm9vYmFyCg=="}' > /tmp/vlcredsdata
+
+varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Encrypt "$(cat /tmp/vlcredsdata)" | \
+    varlinkctl call --json=short /run/systemd/io.systemd.Credentials io.systemd.Credentials.Decrypt > /tmp/vlcredsdata2
+
+cmp /tmp/vlcredsdata /tmp/vlcredsdata2
+rm /tmp/vlcredsdata /tmp/vlcredsdata2
+
+clean_usertest() {
+    rm -f /tmp/usertest.data /tmp/usertest.data
+}
+
+trap clean_usertest EXIT
+dd if=/dev/urandom of=/tmp/usertest.data bs=4096 count=1
+
+systemd-creds encrypt --user /tmp/usertest.data /tmp/usertest.cred
+
+systemd-creds decrypt --user /tmp/usertest.cred - | cmp /tmp/usertest.data
+
+# Decryption must fail if it's not done in user context
+(! systemd-creds decrypt /tmp/usertest.cred - )
+
+# Decryption must also fail if a different user is used
+(! systemd-creds decrypt --user --uid=65534 /tmp/usertest.cred - )
+
+# Try the reverse
+systemd-creds encrypt --user --uid=65534 /tmp/usertest.data /tmp/usertest.cred
+(! systemd-creds decrypt --user /tmp/usertest.cred - )
+systemd-creds decrypt --user --uid=65534 /tmp/usertest.cred - | cmp /tmp/usertest.data
+
+systemd-creds encrypt --user /tmp/usertest.data /tmp/usertest.creds --name=mytest
+
+# Make sure we actually can decode this in user context
+systemctl start user@0.service
+XDG_RUNTIME_DIR=/run/user/0 systemd-run --pipe --user --unit=waldi.service -p LoadCredentialEncrypted=mytest:/tmp/usertest.creds cat /run/user/0/credentials/waldi.service/mytest | cmp /tmp/usertest.data
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-55-OOMD-testbloat.service b/test/units/TEST-55-OOMD-testbloat.service
new file mode 100644 (file)
index 0000000..ba4f2bc
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Create a lot of memory pressure
+
+[Service]
+# A VERY small memory.high will cause the 'stress' (trying to use a lot of memory)
+# to throttle and be put under heavy pressure.
+MemoryHigh=3M
+Slice=TEST-55-OOMD-workload.slice
+ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1
diff --git a/test/units/TEST-55-OOMD-testchill.service b/test/units/TEST-55-OOMD-testchill.service
new file mode 100644 (file)
index 0000000..1f708dd
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=No memory pressure
+
+[Service]
+MemoryHigh=3M
+Slice=TEST-55-OOMD-workload.slice
+ExecStart=sleep infinity
diff --git a/test/units/TEST-55-OOMD-testmunch.service b/test/units/TEST-55-OOMD-testmunch.service
new file mode 100644 (file)
index 0000000..5659906
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Create some memory pressure
+
+[Service]
+MemoryHigh=12M
+Slice=TEST-55-OOMD-workload.slice
+ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1
diff --git a/test/units/TEST-55-OOMD-workload.slice b/test/units/TEST-55-OOMD-workload.slice
new file mode 100644 (file)
index 0000000..d117b75
--- /dev/null
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test slice for memory pressure kills
+
+[Slice]
+CPUAccounting=true
+MemoryAccounting=true
+IOAccounting=true
+TasksAccounting=true
+ManagedOOMMemoryPressure=kill
+ManagedOOMMemoryPressureLimit=20%
diff --git a/test/units/TEST-55-OOMD.service b/test/units/TEST-55-OOMD.service
new file mode 100644 (file)
index 0000000..1b6b8b8
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-55-OOMD
+After=user@4711.service
+Wants=user@4711.service
+
+[Service]
+ExecStartPre=rm -f /failed /skipped /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-55-OOMD.sh b/test/units/TEST-55-OOMD.sh
new file mode 100755 (executable)
index 0000000..b04ebca
--- /dev/null
@@ -0,0 +1,190 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+ . "$(dirname "$0")"/util.sh
+
+. /etc/os-release
+# OpenSUSE does not have the stress tool packaged. It does have stress-ng but the stress-ng does not support
+# --vm-stride which this test uses.
+if [[ "$ID" =~ "opensuse" ]]; then
+    echo "Skipping due to missing stress package in OpenSUSE" >>/skipped
+    exit 77
+fi
+
+systemd-analyze log-level debug
+
+# Ensure that the init.scope.d drop-in is applied on boot
+test "$(cat /sys/fs/cgroup/init.scope/memory.high)" != "max"
+
+# Loose checks to ensure the environment has the necessary features for systemd-oomd
+[[ -e /proc/pressure ]] || echo "no PSI" >>/skipped
+[[ "$(get_cgroup_hierarchy)" == "unified" ]] || echo "no cgroupsv2" >>/skipped
+[[ -x /usr/lib/systemd/systemd-oomd ]] || echo "no oomd" >>/skipped
+if [[ -s /skipped ]]; then
+    exit 77
+fi
+
+rm -rf /run/systemd/system/TEST-55-OOMD-testbloat.service.d
+
+# Activate swap file if we are in a VM
+if systemd-detect-virt --vm --quiet; then
+    swapoff --all
+    if [[ "$(findmnt -n -o FSTYPE /)" == btrfs ]]; then
+        btrfs filesystem mkswapfile -s 64M /swapfile
+    else
+        dd if=/dev/zero of=/swapfile bs=1M count=64
+        chmod 0600 /swapfile
+        mkswap /swapfile
+    fi
+
+    swapon /swapfile
+    swapon --show
+fi
+
+# Configure oomd explicitly to avoid conflicts with distro dropins
+mkdir -p /run/systemd/oomd.conf.d/
+cat >/run/systemd/oomd.conf.d/99-oomd-test.conf <<EOF
+[OOM]
+DefaultMemoryPressureDurationSec=2s
+EOF
+
+mkdir -p /run/systemd/system/-.slice.d/
+cat >/run/systemd/system/-.slice.d/99-oomd-test.conf <<EOF
+[Slice]
+ManagedOOMSwap=auto
+EOF
+
+mkdir -p /run/systemd/system/user@.service.d/
+cat >/run/systemd/system/user@.service.d/99-oomd-test.conf <<EOF
+[Service]
+ManagedOOMMemoryPressure=auto
+ManagedOOMMemoryPressureLimit=0%
+EOF
+
+mkdir -p /run/systemd/system/systemd-oomd.service.d/
+cat >/run/systemd/system/systemd-oomd.service.d/debug.conf <<EOF
+[Service]
+Environment=SYSTEMD_LOG_LEVEL=debug
+EOF
+
+systemctl daemon-reload
+
+# enable the service to ensure dbus-org.freedesktop.oom1.service exists
+# and D-Bus activation works
+systemctl enable systemd-oomd.service
+
+# if oomd is already running for some reasons, then restart it to make sure the above settings to be applied
+if systemctl is-active systemd-oomd.service; then
+    systemctl restart systemd-oomd.service
+fi
+
+if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
+    # If we're running with sanitizers, sd-executor might pull in quite a significant chunk of shared
+    # libraries, which in turn causes a lot of pressure that can put us in the front when sd-oomd decides to
+    # go on a killing spree. This fact is exacerbated further on Arch Linux which ships unstripped gcc-libs,
+    # so sd-executor pulls in over 30M of libs on startup. Let's make the MemoryHigh= limit a bit more
+    # generous when running with sanitizers to make the test happy.
+    systemctl edit --runtime --stdin --drop-in=99-MemoryHigh.conf TEST-55-OOMD-testchill.service <<EOF
+[Service]
+MemoryHigh=60M
+EOF
+    # Do the same for the user instance as well
+    mkdir -p /run/systemd/user/
+    cp -rfv /run/systemd/system/TEST-55-OOMD-testchill.service.d/ /run/systemd/user/
+else
+    # Ensure that we can start services even with a very low hard memory cap without oom-kills, but skip
+    # under sanitizers as they balloon memory usage.
+    systemd-run -t -p MemoryMax=10M -p MemorySwapMax=0 -p MemoryZSwapMax=0 /bin/true
+fi
+
+systemctl start TEST-55-OOMD-testchill.service
+systemctl start TEST-55-OOMD-testbloat.service
+
+# Verify systemd-oomd is monitoring the expected units
+timeout 1m bash -xec 'until oomctl | grep "/TEST-55-OOMD-workload.slice"; do sleep 1; done'
+oomctl | grep "/TEST-55-OOMD-workload.slice"
+oomctl | grep "20.00%"
+oomctl | grep "Default Memory Pressure Duration: 2s"
+
+systemctl status TEST-55-OOMD-testchill.service
+
+# systemd-oomd watches for elevated pressure for 2 seconds before acting.
+# It can take time to build up pressure so either wait 2 minutes or for the service to fail.
+for _ in {0..59}; do
+    if ! systemctl status TEST-55-OOMD-testbloat.service; then
+        break
+    fi
+    oomctl
+    sleep 2
+done
+
+# testbloat should be killed and testchill should be fine
+if systemctl status TEST-55-OOMD-testbloat.service; then exit 42; fi
+if ! systemctl status TEST-55-OOMD-testchill.service; then exit 24; fi
+
+# Make sure we also work correctly on user units.
+loginctl enable-linger testuser
+
+systemctl start --machine "testuser@.host" --user TEST-55-OOMD-testchill.service
+systemctl start --machine "testuser@.host" --user TEST-55-OOMD-testbloat.service
+
+# Verify systemd-oomd is monitoring the expected units
+# Try to avoid racing the oomctl output check by checking in a loop with a timeout
+timeout 1m bash -xec 'until oomctl | grep "/TEST-55-OOMD-workload.slice"; do sleep 1; done'
+oomctl | grep -E "/user.slice.*/TEST-55-OOMD-workload.slice"
+oomctl | grep "20.00%"
+oomctl | grep "Default Memory Pressure Duration: 2s"
+
+systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testchill.service
+
+# systemd-oomd watches for elevated pressure for 2 seconds before acting.
+# It can take time to build up pressure so either wait 2 minutes or for the service to fail.
+for _ in {0..59}; do
+    if ! systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testbloat.service; then
+        break
+    fi
+    oomctl
+    sleep 2
+done
+
+# testbloat should be killed and testchill should be fine
+if systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testbloat.service; then exit 42; fi
+if ! systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testchill.service; then exit 24; fi
+
+loginctl disable-linger testuser
+
+# only run this portion of the test if we can set xattrs
+if cgroupfs_supports_user_xattrs; then
+    sleep 120 # wait for systemd-oomd kill cool down and elevated memory pressure to come down
+
+    mkdir -p /run/systemd/system/TEST-55-OOMD-testbloat.service.d/
+    cat >/run/systemd/system/TEST-55-OOMD-testbloat.service.d/override.conf <<EOF
+[Service]
+ManagedOOMPreference=avoid
+EOF
+
+    systemctl daemon-reload
+    systemctl start TEST-55-OOMD-testchill.service
+    systemctl start TEST-55-OOMD-testmunch.service
+    systemctl start TEST-55-OOMD-testbloat.service
+
+    for _ in {0..59}; do
+        if ! systemctl status TEST-55-OOMD-testmunch.service; then
+            break
+        fi
+        oomctl
+        sleep 2
+    done
+
+    # testmunch should be killed since testbloat had the avoid xattr on it
+    if ! systemctl status TEST-55-OOMD-testbloat.service; then exit 25; fi
+    if systemctl status TEST-55-OOMD-testmunch.service; then exit 43; fi
+    if ! systemctl status TEST-55-OOMD-testchill.service; then exit 24; fi
+fi
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-58-REPART.service b/test/units/TEST-58-REPART.service
new file mode 100644 (file)
index 0000000..f843527
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-58-REPART
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-58-REPART.sh b/test/units/TEST-58-REPART.sh
new file mode 100755 (executable)
index 0000000..d3cb2f8
--- /dev/null
@@ -0,0 +1,1307 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2317
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if ! command -v systemd-repart >/dev/null; then
+    echo "no systemd-repart" >/skipped
+    exit 77
+fi
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+export PAGER=cat
+
+# Disable use of special glyphs such as →
+export SYSTEMD_UTF8=0
+
+seed=750b6cd5c4ae4012a15e7be3c29e6a47
+
+if ! systemd-detect-virt --quiet --container; then
+    udevadm control --log-level debug
+fi
+
+machine="$(uname -m)"
+if [ "${machine}" = "x86_64" ]; then
+    root_guid=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
+    root_uuid=60F33797-1D71-4DCB-AA6F-20564F036CD0
+    root_uuid2=73A4CCD2-EAF5-44DA-A366-F99188210FDC
+    usr_guid=8484680C-9521-48C6-9C11-B0720656F69E
+    usr_uuid=7E3369DD-D653-4513-ADF5-B993A9F20C16
+    architecture="x86-64"
+elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then
+    root_guid=44479540-F297-41B2-9AF7-D131D5F0458A
+    root_uuid=02B4253F-29A4-404E-8972-1669D3B03C87
+    root_uuid2=268E0FD3-B468-4806-A823-E533FE9BB9CC
+    usr_guid=75250D76-8CC6-458E-BD66-BD47CC81A812
+    usr_uuid=7B42FFB0-B0E1-4395-B20B-C78F4A571648
+    architecture="x86"
+elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then
+    root_guid=B921B045-1DF0-41C3-AF44-4C6F280D3FAE
+    root_uuid=055D0227-53A6-4033-85C3-9A5973EFF483
+    root_uuid2=F7DBBE48-8FD0-4833-8411-AA34E7C8E60A
+    usr_guid=B0E01050-EE5F-4390-949A-9101B17104E9
+    usr_uuid=FCE3C75E-D6A4-44C0-87F0-4C105183FB1F
+    architecture="arm64"
+elif [ "${machine}" = "arm" ]; then
+    root_guid=69DAD710-2CE4-4E3C-B16C-21A1D49ABED3
+    root_uuid=567DA89E-8DE2-4499-8D10-18F212DFF034
+    root_uuid2=813ECFE5-4C89-4193-8A52-437493F2F96E
+    usr_guid=7D0359A3-02B3-4F0A-865C-654403E70625
+    usr_uuid=71E93DC2-5073-42CB-8A84-A354E64D8966
+    architecture="arm"
+elif [ "${machine}" = "loongarch64" ]; then
+    root_guid=77055800-792C-4F94-B39A-98C91B762BB6
+    root_uuid=D8EFC2D2-0133-41E4-BDCB-3B9F4CFDDDE8
+    root_uuid2=36499F9E-0688-40C1-A746-EA8FD9543C56
+    usr_guid=E611C702-575C-4CBE-9A46-434FA0BF7E3F
+    usr_uuid=031FFA75-00BB-49B6-A70D-911D2D82A5B7
+    architecture="loongarch64"
+elif [ "${machine}" = "ia64" ]; then
+    root_guid=993D8D3D-F80E-4225-855A-9DAF8ED7EA97
+    root_uuid=DCF33449-0896-4EA9-BC24-7D58AEEF522D
+    root_uuid2=C2A6CAB7-ABEA-4FBA-8C48-CB4C52E6CA38
+    usr_guid=4301D2A6-4E3B-4B2A-BB94-9E0B2C4225EA
+    usr_uuid=BC2BCCE7-80D6-449A-85CC-637424CE5241
+    architecture="ia64"
+elif [ "${machine}" = "s390x" ]; then
+    root_guid=5EEAD9A9-FE09-4A1E-A1D7-520D00531306
+    root_uuid=7EBE0C85-E27E-48EC-B164-F4807606232E
+    root_uuid2=2A074E1C-2A19-4094-A0C2-24B1A5D52FCB
+    usr_guid=8A4F5770-50AA-4ED3-874A-99B710DB6FEA
+    usr_uuid=51171D30-35CF-4A49-B8B5-9478B9B796A5
+    architecture="s390x"
+elif [ "${machine}" = "ppc64le" ]; then
+    root_guid=C31C45E6-3F39-412E-80FB-4809C4980599
+    root_uuid=061E67A1-092F-482F-8150-B525D50D6654
+    root_uuid2=A6687CEF-4E4F-44E7-90B3-CDA52EA81739
+    usr_guid=15BB03AF-77E7-4D4A-B12B-C0D084F7491C
+    usr_uuid=C0D0823B-8040-4C7C-A629-026248E297FB
+    architecture="ppc64-le"
+else
+    echo "Unexpected uname -m: ${machine} in TEST-58-REPART.sh, please fix me"
+    exit 1
+fi
+
+testcase_basic() {
+    local defs imgs output
+    local loop volume
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+    chmod 0755 "$defs"
+
+    echo "*** 1. create an empty image ***"
+
+    systemd-repart --offline="$OFFLINE" \
+                   --empty=create \
+                   --size=1G \
+                   --seed="$seed" \
+                   "$imgs/zzz"
+
+    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+    assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118"
+
+    echo "*** 2. Testing with root, root2, home, and swap ***"
+
+    tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root
+EOF
+
+    ln -s root.conf "$defs/root2.conf"
+
+    tee "$defs/home.conf" <<EOF
+[Partition]
+Type=home
+Label=home-first
+Label=home-always-too-long-xxxxxxxxxxxxxx-%v
+EOF
+
+    tee "$defs/swap.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=64M
+PaddingMinBytes=92M
+EOF
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --dry-run=no \
+                   --seed="$seed" \
+                   --include-partitions=home,swap \
+                   --offline="$OFFLINE" \
+                   "$imgs/zzz"
+
+    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+    assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$imgs/zzz1 : start=        2048, size=     1775576, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz2 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
+
+    systemd-repart --offline="$OFFLINE" \
+                    --definitions="$defs" \
+                   --empty=create \
+                   --size=50M \
+                   --seed="$seed" \
+                   --include-partitions=root,home \
+                   "$imgs/qqq"
+
+    sfdisk -d "$imgs/qqq" | grep -v -e 'sector-size' -e '^$'
+
+    systemd-repart --offline="$OFFLINE" \
+                   --empty=create \
+                   --size=1G \
+                   --dry-run=no \
+                   --seed="$seed" \
+                   --definitions "" \
+                   --copy-from="$imgs/qqq" \
+                   --copy-from="$imgs/qqq" \
+                   "$imgs/copy"
+
+    output=$(sfdisk -d "$imgs/copy" | grep -v -e 'sector-size' -e '^$')
+
+    assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/copy
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$imgs/copy1 : start=        2048, size=       33432, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/copy2 : start=       35480, size=       33440, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/copy3 : start=       68920, size=       33440, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
+$imgs/copy4 : start=      102360, size=       33432, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/copy5 : start=      135792, size=       33440, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/copy6 : start=      169232, size=       33440, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\""
+
+    rm "$imgs/qqq" "$imgs/copy" # Save disk space
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --dry-run=no \
+                   --seed="$seed" \
+                   --empty=force \
+                   --defer-partitions=home,root \
+                   "$imgs/zzz"
+
+    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+    assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --dry-run=no \
+                   --seed="$seed" \
+                   "$imgs/zzz"
+
+    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+    assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$imgs/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz2 : start=      593904, size=      591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/zzz3 : start=     1185760, size=      591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
+$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
+
+    echo "*** 3. Testing with root, root2, home, swap, and another partition ***"
+
+    tee "$defs/swap.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=64M
+EOF
+
+    tee "$defs/extra.conf" <<EOF
+[Partition]
+Type=linux-generic
+Label=custom_label
+UUID=a0a1a2a3a4a5a6a7a8a9aaabacadaeaf
+EOF
+
+    echo "Label=ignored_label" >>"$defs/home.conf"
+    echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>"$defs/home.conf"
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --dry-run=no \
+                   --seed="$seed" \
+                   "$imgs/zzz"
+
+    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+    assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$imgs/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz2 : start=      593904, size=      591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/zzz3 : start=     1185760, size=      591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
+$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
+$imgs/zzz5 : start=     1908696, size=      188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\""
+
+    echo "*** 4. Resizing to 2G ***"
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --size=2G \
+                   --dry-run=no \
+                   --seed="$seed" \
+                   "$imgs/zzz"
+
+    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+    assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 4194270
+$imgs/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz2 : start=      593904, size=      591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/zzz3 : start=     1185760, size=      591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
+$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
+$imgs/zzz5 : start=     1908696, size=     2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\""
+
+    echo "*** 5. Testing with root, root2, home, swap, another partition, and partition copy ***"
+
+    dd if=/dev/urandom of="$imgs/block-copy" bs=4096 count=10240
+
+    tee "$defs/extra2.conf" <<EOF
+[Partition]
+Type=linux-generic
+Label=block-copy
+UUID=2a1d97e1d0a346cca26eadc643926617
+CopyBlocks=$imgs/block-copy
+EOF
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --size=3G \
+                   --dry-run=no \
+                   --seed="$seed" \
+                   "$imgs/zzz"
+
+    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+    assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 6291422
+$imgs/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz2 : start=      593904, size=      591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/zzz3 : start=     1185760, size=      591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
+$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
+$imgs/zzz5 : start=     1908696, size=     2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"
+$imgs/zzz6 : start=     4194264, size=     2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name=\"block-copy\""
+
+    cmp --bytes=$((4096*10240)) --ignore-initial=0:$((512*4194264)) "$imgs/block-copy" "$imgs/zzz"
+
+    echo "*** 6. Testing Format=/Encrypt=/CopyFiles= ***"
+
+    tee "$defs/extra3.conf" <<EOF
+[Partition]
+Type=linux-generic
+Label=luks-format-copy
+UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0
+Format=ext4
+Encrypt=yes
+CopyFiles=$defs:/def
+SizeMinBytes=48M
+EOF
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --size=auto \
+                   --dry-run=no \
+                   --seed="$seed" \
+                   "$imgs/zzz"
+
+    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
+
+    assert_eq "$output" "label: gpt
+label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
+device: $imgs/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 6389726
+$imgs/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
+$imgs/zzz2 : start=      593904, size=      591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
+$imgs/zzz3 : start=     1185760, size=      591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
+$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
+$imgs/zzz5 : start=     1908696, size=     2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"
+$imgs/zzz6 : start=     4194264, size=     2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name=\"block-copy\"
+$imgs/zzz7 : start=     6291416, size=       98304, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=7B93D1F2-595D-4CE3-B0B9-837FBD9E63B0, name=\"luks-format-copy\""
+
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping encrypt mount tests in container."
+        return
+    fi
+
+    loop="$(losetup -P --show --find "$imgs/zzz")"
+    udevadm wait --timeout 60 --settle "${loop:?}"
+
+    volume="test-repart-$RANDOM"
+
+    touch "$imgs/empty-password"
+    cryptsetup open --type=luks2 --key-file="$imgs/empty-password" "${loop}p7" "$volume"
+    mkdir -p "$imgs/mount"
+    mount -t ext4 "/dev/mapper/$volume" "$imgs/mount"
+    # Use deferred closing on the mapper and autoclear on the loop, so they are cleaned up on umount
+    cryptsetup close --deferred "$volume"
+    losetup -d "$loop"
+    diff -r "$imgs/mount/def" "$defs" >/dev/null
+    umount "$imgs/mount"
+}
+
+testcase_dropin() {
+    local defs imgs output
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+    chmod 0755 "$defs"
+
+    tee "$defs/root.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=64M
+UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3
+EOF
+
+    mkdir -p "$defs/root.conf.d"
+    tee "$defs/root.conf.d/override1.conf" <<EOF
+[Partition]
+Label=label1
+SizeMaxBytes=32M
+EOF
+
+    tee "$defs/root.conf.d/override2.conf" <<EOF
+[Partition]
+Label=label2
+EOF
+
+    output=$(systemd-repart --offline="$OFFLINE" \
+                            --definitions="$defs" \
+                            --empty=create \
+                            --size=100M \
+                            --json=pretty \
+                            "$imgs/zzz")
+
+    diff -u <(echo "$output") - <<EOF
+[
+       {
+               "type" : "swap",
+               "label" : "label2",
+               "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
+               "partno" : 0,
+               "file" : "$defs/root.conf",
+               "node" : "$imgs/zzz1",
+               "offset" : 1048576,
+               "old_size" : 0,
+               "raw_size" : 33554432,
+               "size" : "-> 32.0M",
+               "old_padding" : 0,
+               "raw_padding" : 0,
+               "padding" : "-> 0B",
+               "activity" : "create",
+               "drop-in_files" : [
+                       "$defs/root.conf.d/override1.conf",
+                       "$defs/root.conf.d/override2.conf"
+               ]
+       }
+]
+EOF
+}
+
+testcase_multiple_definitions() {
+    local defs imgs output
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+    chmod 0755 "$defs"
+
+    mkdir -p "$defs/1"
+    tee "$defs/1/root1.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=32M
+UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0
+Label=label1
+EOF
+
+    mkdir -p "$defs/2"
+    tee "$defs/2/root2.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=32M
+UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3
+Label=label2
+EOF
+
+    output=$(systemd-repart --offline="$OFFLINE" \
+                            --definitions="$defs/1" \
+                            --definitions="$defs/2" \
+                            --empty=create \
+                            --size=100M \
+                            --json=pretty \
+                            "$imgs/zzz")
+
+    diff -u <(echo "$output") - <<EOF
+[
+       {
+               "type" : "swap",
+               "label" : "label1",
+               "uuid" : "7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0",
+               "partno" : 0,
+               "file" : "$defs/1/root1.conf",
+               "node" : "$imgs/zzz1",
+               "offset" : 1048576,
+               "old_size" : 0,
+               "raw_size" : 33554432,
+               "size" : "-> 32.0M",
+               "old_padding" : 0,
+               "raw_padding" : 0,
+               "padding" : "-> 0B",
+               "activity" : "create"
+       },
+       {
+               "type" : "swap",
+               "label" : "label2",
+               "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
+               "partno" : 1,
+               "file" : "$defs/2/root2.conf",
+               "node" : "$imgs/zzz2",
+               "offset" : 34603008,
+               "old_size" : 0,
+               "raw_size" : 33554432,
+               "size" : "-> 32.0M",
+               "old_padding" : 0,
+               "raw_padding" : 0,
+               "padding" : "-> 0B",
+               "activity" : "create"
+       }
+]
+EOF
+}
+
+testcase_copy_blocks() {
+    local defs imgs output
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+    chmod 0755 "$defs"
+
+    echo "*** First, create a disk image and verify its in order ***"
+
+    tee "$defs/esp.conf" <<EOF
+[Partition]
+Type=esp
+SizeMinBytes=10M
+Format=vfat
+EOF
+
+    tee "$defs/usr.conf" <<EOF
+[Partition]
+Type=usr-${architecture}
+SizeMinBytes=10M
+Format=ext4
+ReadOnly=yes
+EOF
+
+    tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+SizeMinBytes=10M
+Format=ext4
+MakeDirectories=/usr /efi
+EOF
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --empty=create \
+                   --size=auto \
+                   --seed="$seed" \
+                   "$imgs/zzz"
+
+    output=$(sfdisk --dump "$imgs/zzz")
+
+    assert_in "$imgs/zzz1 : start=        2048, size=       20480, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=39107B09-615D-48FB-BA37-C663885FCE67, name=\"esp\"" "$output"
+    assert_in "$imgs/zzz2 : start=       22528, size=       20480, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output"
+    assert_in "$imgs/zzz3 : start=       43008, size=       20480, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:60\"" "$output"
+
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping second part of copy blocks tests in container."
+        return
+    fi
+
+    echo "*** Second, create another image with CopyBlocks=auto ***"
+
+    tee "$defs/esp.conf" <<EOF
+[Partition]
+Type=esp
+CopyBlocks=auto
+EOF
+
+    tee "$defs/usr.conf" <<EOF
+[Partition]
+Type=usr-${architecture}
+ReadOnly=yes
+CopyBlocks=auto
+EOF
+
+    tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+CopyBlocks=auto
+EOF
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --empty=create \
+                   --size=auto \
+                   --seed="$seed" \
+                   --image="$imgs/zzz" \
+                   "$imgs/yyy"
+
+    cmp "$imgs/zzz" "$imgs/yyy"
+}
+
+testcase_unaligned_partition() {
+    local defs imgs output
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+    chmod 0755 "$defs"
+
+    echo "*** Operate on an image with unaligned partition ***"
+
+    tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+EOF
+
+    truncate -s 10g "$imgs/unaligned"
+    sfdisk "$imgs/unaligned" <<EOF
+label: gpt
+
+start=2048, size=69044
+start=71092, size=3591848
+EOF
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --seed="$seed" \
+                   --dry-run=no \
+                   "$imgs/unaligned"
+
+    output=$(sfdisk --dump "$imgs/unaligned")
+
+    assert_in "$imgs/unaligned1 : start=        2048, size=       69044," "$output"
+    assert_in "$imgs/unaligned2 : start=       71092, size=     3591848," "$output"
+    assert_in "$imgs/unaligned3 : start=     3662944, size=    17308536, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output"
+}
+
+testcase_issue_21817() {
+    local defs imgs output
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+    chmod 0755 "$defs"
+
+    echo "*** testcase for #21817 ***"
+
+    tee "$defs/test.conf" <<EOF
+[Partition]
+Type=root
+EOF
+
+    truncate -s 100m "$imgs/21817.img"
+    sfdisk "$imgs/21817.img" <<EOF
+label: gpt
+
+size=50M, type=${root_guid}
+,
+EOF
+
+    systemd-repart --offline="$OFFLINE" \
+                   --pretty=yes \
+                   --definitions "$imgs" \
+                   --seed="$seed" \
+                   --dry-run=no \
+                   "$imgs/21817.img"
+
+    output=$(sfdisk --dump "$imgs/21817.img")
+
+    assert_in "$imgs/21817.img1 : start=        2048, size=      102400, type=${root_guid}," "$output"
+    # Accept both unpadded (pre-v2.38 util-linux) and padded (v2.38+ util-linux) sizes
+    assert_in "$imgs/21817.img2 : start=      104448, size=      (100319| 98304)," "$output"
+}
+
+testcase_issue_24553() {
+    local defs imgs output
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+    chmod 0755 "$defs"
+
+    echo "*** testcase for #24553 ***"
+
+    tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root
+SizeMinBytes=10G
+SizeMaxBytes=120G
+EOF
+
+    tee "$imgs/partscript" <<EOF
+label: gpt
+label-id: C9FFE979-A415-C449-B729-78C7AA664B10
+unit: sectors
+first-lba: 40
+
+start=40, size=524288, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=F2E89C8A-DC5D-4C4C-A29C-6CFB643B74FD, name="ESP System Partition"
+start=524328, size=14848000, type=${root_guid}, uuid=${root_uuid}, name="root-${architecture}"
+EOF
+
+    echo "*** 1. Operate on a small image compared with SizeMinBytes= ***"
+    truncate -s 8g "$imgs/zzz"
+    sfdisk "$imgs/zzz" <"$imgs/partscript"
+
+    # This should fail, but not trigger assertions.
+    assert_rc 1 systemd-repart --offline="$OFFLINE" \
+                               --definitions="$defs" \
+                               --seed="$seed" \
+                               --dry-run=no \
+                               "$imgs/zzz"
+
+    output=$(sfdisk --dump "$imgs/zzz")
+    assert_in "$imgs/zzz2 : start=      524328, size=    14848000, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
+
+    echo "*** 2. Operate on an larger image compared with SizeMinBytes= ***"
+    rm -f "$imgs/zzz"
+    truncate -s 12g "$imgs/zzz"
+    sfdisk "$imgs/zzz" <"$imgs/partscript"
+
+    # This should succeed.
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --seed="$seed" \
+                   --dry-run=no \
+                   "$imgs/zzz"
+
+    output=$(sfdisk --dump "$imgs/zzz")
+    assert_in "$imgs/zzz2 : start=      524328, size=    24641456, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
+
+    echo "*** 3. Multiple partitions with Priority= (small disk) ***"
+    tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root
+SizeMinBytes=10G
+SizeMaxBytes=120G
+Priority=100
+EOF
+
+    tee "$defs/usr.conf" <<EOF
+[Partition]
+Type=usr
+SizeMinBytes=10M
+Priority=10
+EOF
+
+    rm -f "$imgs/zzz"
+    truncate -s 8g "$imgs/zzz"
+    sfdisk "$imgs/zzz" <"$imgs/partscript"
+
+    # This should also succeed, but root is not extended.
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --seed="$seed" \
+                   --dry-run=no \
+                   "$imgs/zzz"
+
+    output=$(sfdisk --dump "$imgs/zzz")
+    assert_in "$imgs/zzz2 : start=      524328, size=    14848000, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
+    assert_in "$imgs/zzz3 : start=    15372328, size=     1404848, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:59\"" "$output"
+
+    echo "*** 4. Multiple partitions with Priority= (large disk) ***"
+    rm -f "$imgs/zzz"
+    truncate -s 12g "$imgs/zzz"
+    sfdisk "$imgs/zzz" <"$imgs/partscript"
+
+    # This should also succeed, and root is extended.
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --seed="$seed" \
+                   --dry-run=no \
+                   "$imgs/zzz"
+
+    output=$(sfdisk --dump "$imgs/zzz")
+    assert_in "$imgs/zzz2 : start=      524328, size=    20971520, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
+    assert_in "$imgs/zzz3 : start=    21495848, size=     3669936, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:59\"" "$output"
+}
+
+testcase_zero_uuid() {
+    local defs imgs output
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+    chmod 0755 "$defs"
+
+    echo "*** Test image with zero UUID ***"
+
+    tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+UUID=null
+EOF
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --seed="$seed" \
+                   --dry-run=no \
+                   --empty=create \
+                   --size=auto \
+                   "$imgs/zero"
+
+    output=$(sfdisk --dump "$imgs/zero")
+
+    assert_in "$imgs/zero1 : start=        2048, size=       20480, type=${root_guid}, uuid=00000000-0000-0000-0000-000000000000" "$output"
+}
+
+testcase_verity() {
+    local defs imgs output
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+    chmod 0755 "$defs"
+
+    echo "*** dm-verity ***"
+
+    tee "$defs/verity-data.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+CopyFiles=${defs}
+Verity=data
+VerityMatchKey=root
+Minimize=guess
+EOF
+
+    tee "$defs/verity-hash.conf" <<EOF
+[Partition]
+Type=root-${architecture}-verity
+Verity=hash
+VerityMatchKey=root
+Minimize=yes
+EOF
+
+    tee "$defs/verity-sig.conf" <<EOF
+[Partition]
+Type=root-${architecture}-verity-sig
+Verity=signature
+VerityMatchKey=root
+EOF
+
+    # Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
+    tee >"$defs/verity.openssl.cnf" <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = DE
+ST = Test State
+L = Test Locality
+O = Org Name
+OU = Org Unit Name
+CN = Common Name
+emailAddress = test@email.com
+EOF
+
+    openssl req \
+            -config "$defs/verity.openssl.cnf" \
+            -new -x509 \
+            -newkey rsa:1024 \
+            -keyout "$defs/verity.key" \
+            -out "$defs/verity.crt" \
+            -days 365 \
+            -nodes
+
+    mkdir -p /run/verity.d
+    ln -sf "$defs/verity.crt" /run/verity.d/ok.crt
+
+    output=$(systemd-repart --offline="$OFFLINE" \
+                            --definitions="$defs" \
+                            --seed="$seed" \
+                            --dry-run=no \
+                            --empty=create \
+                            --size=auto \
+                            --json=pretty \
+                            --private-key="$defs/verity.key" \
+                            --certificate="$defs/verity.crt" \
+                            "$imgs/verity")
+
+    drh=$(jq -r ".[] | select(.type == \"root-${architecture}\") | .roothash" <<<"$output")
+    hrh=$(jq -r ".[] | select(.type == \"root-${architecture}-verity\") | .roothash" <<<"$output")
+    srh=$(jq -r ".[] | select(.type == \"root-${architecture}-verity-sig\") | .roothash" <<<"$output")
+
+    assert_eq "$drh" "$hrh"
+    assert_eq "$hrh" "$srh"
+
+    # Check that we can dissect, mount and unmount a repart verity image. (and that the image UUID is deterministic)
+
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping verity test dissect part in container."
+        return
+    fi
+
+    systemd-dissect "$imgs/verity" --root-hash "$drh"
+    systemd-dissect "$imgs/verity" --root-hash "$drh" --json=short | grep -q '"imageUuid":"1d2ce291-7cce-4f7d-bc83-fdb49ad74ebd"'
+    systemd-dissect "$imgs/verity" --root-hash "$drh" -M "$imgs/mnt"
+    systemd-dissect -U "$imgs/mnt"
+}
+
+testcase_verity_explicit_block_size() {
+    local defs imgs loop
+
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping verity block size tests in container."
+        return
+    fi
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+    chmod 0755 "$defs"
+
+    echo "*** varying-dm-verity-block-sizes ***"
+
+    tee "$defs/verity-data.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+CopyFiles=${defs}
+Verity=data
+VerityMatchKey=root
+Minimize=guess
+EOF
+
+    tee "$defs/verity-hash.conf" <<EOF
+[Partition]
+Type=root-${architecture}-verity
+Verity=hash
+VerityMatchKey=root
+VerityHashBlockSizeBytes=1024
+VerityDataBlockSizeBytes=4096
+Minimize=yes
+EOF
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --seed="$seed" \
+                   --dry-run=no \
+                   --empty=create \
+                   --size=auto \
+                   --json=pretty \
+                   "$imgs/verity"
+
+    loop="$(losetup --partscan --show --find "$imgs/verity")"
+
+    # Make sure the loopback device gets cleaned up
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs' ; losetup -d '$loop'" RETURN ERR
+
+    udevadm wait --timeout 60 --settle "${loop:?}"
+
+    # Check that the verity block sizes are as expected
+    veritysetup dump "${loop}p2" | grep 'Data block size:' | grep -q '4096'
+    veritysetup dump "${loop}p2" | grep 'Hash block size:' | grep -q '1024'
+}
+
+testcase_exclude_files() {
+    local defs imgs root output
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    root="$(mktemp --directory "/var/tmp/test-repart.root.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs' '$root'" RETURN
+    chmod 0755 "$defs"
+
+    echo "*** file exclusion ***"
+
+    touch "$root/abc"
+    mkdir "$root/usr"
+    touch "$root/usr/def"
+    touch "$root/usr/qed"
+    mkdir "$root/tmp"
+    touch "$root/tmp/prs"
+    mkdir "$root/proc"
+    touch "$root/proc/prs"
+    mkdir "$root/zzz"
+    mkdir "$root/zzz/usr"
+    touch "$root/zzz/usr/prs"
+    mkdir "$root/zzz/proc"
+    touch "$root/zzz/proc/prs"
+
+    tee "$defs/00-root.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+CopyFiles=/
+CopyFiles=/zzz:/
+CopyFiles=/:/oiu
+ExcludeFilesTarget=/oiu/usr
+EOF
+
+    tee "$defs/10-usr.conf" <<EOF
+[Partition]
+Type=usr-${architecture}
+CopyFiles=/usr:/
+ExcludeFiles=/usr/qed
+EOF
+
+    output=$(systemd-repart --offline="$OFFLINE" \
+                            --definitions="$defs" \
+                            --seed="$seed" \
+                            --dry-run=no \
+                            --empty=create \
+                            --size=auto \
+                            --json=pretty \
+                            --root="$root" \
+                            "$imgs/zzz")
+
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping issue 24786 test loop/mount parts in container."
+        return
+    fi
+
+    loop=$(losetup -P --show -f "$imgs/zzz")
+    udevadm wait --timeout 60 --settle "${loop:?}"
+
+    # Test that /usr/def did not end up in the root partition but other files did.
+    mkdir "$imgs/mnt"
+    mount -t ext4 "${loop}p1" "$imgs/mnt"
+    assert_rc 0 ls "$imgs/mnt/abc"
+    assert_rc 0 ls "$imgs/mnt/usr"
+    assert_rc 2 ls "$imgs/mnt/usr/def"
+
+    # Test that /zzz/usr/prs did not end up in the root partition under /usr but did end up in /zzz/usr/prs
+    assert_rc 2 ls "$imgs/mnt/usr/prs"
+    assert_rc 0 ls "$imgs/mnt/zzz/usr/prs"
+
+    # Test that /tmp/prs did not end up in the root partition but /tmp did.
+    assert_rc 0 ls "$imgs/mnt/tmp"
+    assert_rc 2 ls "$imgs/mnt/tmp/prs"
+
+    # Test that /usr/qed did not end up in the usr partition but /usr/def did.
+    mount -t ext4 "${loop}p2" "$imgs/mnt/usr"
+    assert_rc 0 ls "$imgs/mnt/usr/def"
+    assert_rc 2 ls "$imgs/mnt/usr/qed"
+
+    # Test that /zzz/proc/prs did not end up in the root partition but /proc did.
+    assert_rc 0 ls "$imgs/mnt/proc"
+    assert_rc 2 ls "$imgs/mnt/proc/prs"
+
+    # Test that /zzz/usr/prs did not end up in the usr partition.
+    assert_rc 2 ls "$imgs/mnt/usr/prs"
+
+    # Test that /oiu/ and /oiu/zzz ended up in the root partition but /oiu/usr did not.
+    assert_rc 0 ls "$imgs/mnt/oiu"
+    assert_rc 0 ls "$imgs/mnt/oiu/zzz"
+    assert_rc 2 ls "$imgs/mnt/oiu/usr"
+
+    umount -R "$imgs/mnt"
+    losetup -d "$loop"
+}
+
+testcase_minimize() {
+    local defs imgs output
+
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping minimize test in container."
+        return
+    fi
+
+    echo "*** minimization ***"
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+
+    for format in ext4 vfat erofs; do
+        if ! command -v "mkfs.$format" >/dev/null; then
+            continue
+        fi
+
+        tee "$defs/root-$format.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+Format=${format}
+CopyFiles=${defs}
+Minimize=guess
+EOF
+    done
+
+    if command -v mksquashfs >/dev/null; then
+        tee "$defs/root-squashfs.conf" <<EOF
+[Partition]
+Type=root-${architecture}
+Format=squashfs
+CopyFiles=${defs}
+Minimize=best
+EOF
+    fi
+
+    output=$(systemd-repart --offline="$OFFLINE" \
+                            --definitions="$defs" \
+                            --seed="$seed" \
+                            --dry-run=no \
+                            --empty=create \
+                            --size=auto \
+                            --json=pretty \
+                            "$imgs/zzz")
+
+    # Check that we can dissect, mount and unmount a minimized image.
+
+    systemd-dissect "$imgs/zzz"
+    systemd-dissect "$imgs/zzz" -M "$imgs/mnt"
+    systemd-dissect -U "$imgs/mnt"
+}
+
+testcase_free_area_calculation() {
+    local defs imgs output
+
+    if ! command -v mksquashfs >/dev/null; then
+        echo "Skipping free area calculation test without squashfs."
+        return
+    fi
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+    chmod 0755 "$defs"
+
+    # https://github.com/systemd/systemd/issues/28225
+    echo "*** free area calculation ***"
+
+    tee "$defs/00-ESP.conf" <<EOF
+[Partition]
+Type         = esp
+Label        = ESP
+Format       = vfat
+
+SizeMinBytes = 128M
+SizeMaxBytes = 128M
+
+# Sufficient for testing
+CopyFiles    = /etc:/
+EOF
+
+    tee "$defs/10-os.conf" <<EOF
+[Partition]
+Type           = root-${architecture}
+Label          = test
+Format         = squashfs
+
+Minimize       = best
+# Sufficient for testing
+CopyFiles      = /etc/:/
+
+VerityMatchKey = os
+Verity         = data
+EOF
+
+    tee "$defs/11-os-verity.conf" <<EOF
+[Partition]
+Type           = root-${architecture}-verity
+Label          = test
+
+Minimize       = best
+
+VerityMatchKey = os
+Verity         = hash
+EOF
+
+    # Set sector size for VFAT to 512 bytes because there will not be enough FAT clusters otherwise
+    output1=$(SYSTEMD_REPART_MKFS_OPTIONS_VFAT="-S 512" systemd-repart \
+                                              --definitions="$defs" \
+                                              --seed="$seed" \
+                                              --dry-run=no \
+                                              --empty=create \
+                                              --size=auto \
+                                              --sector-size=4096 \
+                                              --defer-partitions=esp \
+                                              --json=pretty \
+                                              "$imgs/zzz")
+
+    # The second invocation
+    output2=$(SYSTEMD_REPART_MKFS_OPTIONS_VFAT="-S 512" systemd-repart \
+                                              --definitions="$defs" \
+                                              --seed="$seed" \
+                                              --dry-run=no \
+                                              --empty=allow \
+                                              --size=auto \
+                                              --sector-size=4096 \
+                                              --defer-partitions=esp \
+                                              --json=pretty \
+                                              "$imgs/zzz")
+
+    diff -u <(echo "$output1" | grep -E "(offset|raw_size|raw_padding)") <(echo "$output2" | grep -E "(offset|raw_size|raw_padding)")
+}
+
+test_sector() {
+    local defs imgs output loop
+    local start size ratio
+    local sector="${1?}"
+
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping sector size tests in container."
+        return
+    fi
+
+    echo "*** sector sizes ***"
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+
+    tee "$defs/a.conf" <<EOF
+[Partition]
+Type=root
+SizeMaxBytes=15M
+SizeMinBytes=15M
+EOF
+    tee "$defs/b.conf" <<EOF
+[Partition]
+Type=linux-generic
+Weight=250
+EOF
+
+    tee "$defs/c.conf" <<EOF
+[Partition]
+Type=linux-generic
+Weight=750
+EOF
+
+    truncate -s 100m "$imgs/$sector.img"
+    loop=$(losetup -b "$sector" -P --show -f "$imgs/$sector.img" )
+    udevadm wait --timeout 60 --settle "${loop:?}"
+
+    systemd-repart --offline="$OFFLINE" \
+                   --pretty=yes \
+                   --definitions="$defs" \
+                   --seed="$seed" \
+                   --empty=require \
+                   --dry-run=no \
+                   "$loop"
+
+    sfdisk --verify "$loop"
+    output=$(sfdisk --dump "$loop")
+    losetup -d "$loop"
+
+    ratio=$(( sector / 512 ))
+    start=$(( 2048 / ratio ))
+    size=$(( 30720 / ratio ))
+    assert_in "${loop}p1 : start= *${start}, size= *${size}, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output"
+    start=$(( start + size ))
+    size=$(( 42992 / ratio ))
+    assert_in "${loop}p2 : start= *${start}, size= *${size}, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=DF71F5E3-080A-4D16-824B-18591B881380, name=\"linux-generic\"" "$output"
+    start=$(( start + size ))
+    size=$(( 129000 / ratio ))
+    assert_in "${loop}p3 : start= *${start}, size= *${size}, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=DB081670-07AE-48CA-9F5E-813D5E40B976, name=\"linux-generic-2\"" "$output"
+}
+
+testcase_dropped_partitions() {
+    local workdir image defs
+
+    workdir="$(mktemp --directory "/tmp/test-repart.dropped-partitions.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '${workdir:?}'" RETURN
+
+    image="$workdir/image.img"
+    truncate -s 32M "$image"
+
+    defs="$workdir/defs"
+    mkdir "$defs"
+    echo -ne "[Partition]\nType=root\n" >"$defs/10-part1.conf"
+    echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=1\n" >"$defs/11-dropped-first.conf"
+    echo -ne "[Partition]\nType=root\n" >"$defs/12-part2.conf"
+    echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=2\n" >"$defs/13-dropped-second.conf"
+
+    systemd-repart --empty=allow --pretty=yes --dry-run=no --definitions="$defs" "$image"
+
+    sfdisk -q -l "$image"
+    [[ "$(sfdisk -q -l "$image" | grep -c "$image")" -eq 2 ]]
+}
+
+OFFLINE="yes"
+run_testcases
+
+# Online image builds need loop devices so we can't run them in nspawn.
+if ! systemd-detect-virt --container; then
+    OFFLINE="no"
+    run_testcases
+fi
+
+# Valid block sizes on the Linux block layer are >= 512 and <= PAGE_SIZE, and
+# must be powers of 2. Which leaves exactly four different ones to test on
+# typical hardware
+test_sector 512
+test_sector 1024
+test_sector 2048
+test_sector 4096
+
+touch /testok
diff --git a/test/units/TEST-59-RELOADING-RESTART.service b/test/units/TEST-59-RELOADING-RESTART.service
new file mode 100644 (file)
index 0000000..f85cfab
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-59-RELOADING-RESTART
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
diff --git a/test/units/TEST-59-RELOADING-RESTART.sh b/test/units/TEST-59-RELOADING-RESTART.sh
new file mode 100755 (executable)
index 0000000..0e04403
--- /dev/null
@@ -0,0 +1,168 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+fail() {
+    systemd-analyze log-level info
+    exit 1
+}
+
+# Wait for a service to enter a state within a timeout period, if it doesn't
+# enter the desired state within the timeout period then this function will
+# exit the test case with a non zero exit code.
+wait_on_state_or_fail() {
+    service=$1
+    expected_state=$2
+    timeout=$3
+
+    state=$(systemctl show "$service" --property=ActiveState --value)
+    while [ "$state" != "$expected_state" ]; do
+        if [ "$timeout" = "0" ]; then
+            fail
+        fi
+        timeout=$((timeout - 1))
+        sleep 1
+        state=$(systemctl show "$service" --property=ActiveState --value)
+    done
+}
+
+systemd-analyze log-level debug
+
+
+cat >/run/systemd/system/testservice-fail-59.service <<EOF
+[Unit]
+Description=TEST-59-RELOADING-RESTART Normal exit
+
+[Service]
+Type=notify
+ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
+EOF
+
+cat >/run/systemd/system/testservice-fail-restart-59.service <<EOF
+[Unit]
+Description=TEST-59-RELOADING-RESTART Restart=on-failure
+
+[Service]
+Type=notify
+ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
+Restart=on-failure
+StartLimitBurst=1
+EOF
+
+
+cat >/run/systemd/system/testservice-abort-restart-59.service <<EOF
+[Unit]
+Description=TEST-59-RELOADING-RESTART Restart=on-abort
+
+[Service]
+Type=notify
+ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 5; exit 1"
+Restart=on-abort
+EOF
+
+systemctl daemon-reload
+
+# This service sends a RELOADING=1 message then exits before it sends a
+# READY=1. Ensure it enters failed state and does not linger in reloading
+# state.
+systemctl start testservice-fail-59.service
+wait_on_state_or_fail "testservice-fail-59.service" "failed" "30"
+
+# This service sends a RELOADING=1 message then exits before it sends a
+# READY=1. It should automatically restart on failure. Ensure it enters failed
+# state and does not linger in reloading state.
+systemctl start testservice-fail-restart-59.service
+wait_on_state_or_fail "testservice-fail-restart-59.service" "failed" "30"
+
+# This service sends a RELOADING=1 message then exits before it sends a
+# READY=1. It should automatically restart on abort. It will sleep for 5s
+# to allow us to send it a SIGABRT. Ensure the service enters the failed state
+# and does not linger in reloading state.
+systemctl start testservice-abort-restart-59.service
+systemctl --signal=SIGABRT kill testservice-abort-restart-59.service
+wait_on_state_or_fail "testservice-abort-restart-59.service" "failed" "30"
+
+systemd-analyze log-level info
+
+# Test that rate-limiting daemon-reload works
+mkdir -p /run/systemd/system.conf.d/
+cat >/run/systemd/system.conf.d/50-test-59-reload.conf <<EOF
+[Manager]
+ReloadLimitIntervalSec=9
+ReloadLimitBurst=3
+EOF
+
+# Pick up the new config
+systemctl daemon-reload
+
+# The timeout will hit (and the test will fail) if the reloads are not rate-limited
+timeout 15 bash -c 'while systemctl daemon-reload --no-block; do true; done'
+
+# Rate limit should reset after 9s
+sleep 10
+
+systemctl daemon-reload
+
+# Same test for reexec, but we wait here
+timeout 15 bash -c 'while systemctl daemon-reexec; do true; done'
+
+# Rate limit should reset after 9s
+sleep 10
+
+systemctl daemon-reexec
+
+# Let's now test the notify-reload logic
+
+cat >/run/notify-reload-test.sh <<EOF
+#!/usr/bin/env bash
+set -eux
+set -o pipefail
+
+EXIT_STATUS=88
+LEAVE=0
+
+function reload() {
+    systemd-notify --reloading --status="Adding 11 to exit status"
+    EXIT_STATUS=\$((EXIT_STATUS + 11))
+    systemd-notify --ready --status="Back running"
+}
+
+function leave() {
+    systemd-notify --stopping --status="Adding 7 to exit status"
+    EXIT_STATUS=\$((EXIT_STATUS + 7))
+    LEAVE=1
+    return 0
+}
+
+trap reload SIGHUP
+trap leave SIGTERM
+
+systemd-notify --ready
+systemd-notify --status="Running now"
+
+while [ \$LEAVE = 0 ] ; do
+    sleep 1
+done
+
+systemd-notify --status="Adding 3 to exit status"
+EXIT_STATUS=\$((EXIT_STATUS + 3))
+exit \$EXIT_STATUS
+EOF
+
+chmod +x /run/notify-reload-test.sh
+
+systemd-analyze log-level debug
+
+systemd-run --unit notify-reload-test -p Type=notify-reload -p KillMode=process /run/notify-reload-test.sh
+systemctl reload notify-reload-test
+systemctl stop notify-reload-test
+
+test "$(systemctl show -p ExecMainStatus --value notify-reload-test)" = 109
+
+systemctl reset-failed notify-reload-test
+rm /run/notify-reload-test.sh
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-60-MOUNT-RATELIMIT.service b/test/units/TEST-60-MOUNT-RATELIMIT.service
new file mode 100644 (file)
index 0000000..1a929e4
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-60-MOUNT-RATELIMIT
+
+[Service]
+Type=oneshot
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
diff --git a/test/units/TEST-60-MOUNT-RATELIMIT.sh b/test/units/TEST-60-MOUNT-RATELIMIT.sh
new file mode 100755 (executable)
index 0000000..d205a93
--- /dev/null
@@ -0,0 +1,308 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+teardown_test_dependencies() (
+    set +eux
+
+    if mountpoint /tmp/deptest; then
+        umount /tmp/deptest
+    fi
+
+    if [[ -n "${LOOP}" ]]; then
+        losetup -d "${LOOP}" || :
+    fi
+    if [[ -n "${LOOP_0}" ]]; then
+        losetup -d "${LOOP_0}" || :
+    fi
+    if [[ -n "${LOOP_1}" ]]; then
+        losetup -d "${LOOP_1}" || :
+    fi
+
+    rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-0.img
+    rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-1.img
+
+    rm -f /run/systemd/system/tmp-deptest.mount
+    systemctl daemon-reload
+
+    return 0
+)
+
+setup_loop() {
+    truncate -s 30m "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img"
+    sfdisk --wipe=always "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img" <<EOF
+label:gpt
+
+name="loop${1?}-part1"
+EOF
+    LOOP=$(losetup -P --show -f "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img")
+    udevadm wait --settle --timeout=10 "${LOOP}"
+    udevadm lock --device="${LOOP}" mkfs.ext4 -L "partname${1?}-1" "${LOOP}p1"
+}
+
+check_dependencies() {
+    local escaped_0 escaped_1 after
+
+    escaped_0=$(systemd-escape -p "${LOOP_0}p1")
+    escaped_1=$(systemd-escape -p "${LOOP_1}p1")
+
+    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+        after=$(systemctl show --property=After --value tmp-deptest.mount)
+        assert_not_in "local-fs-pre.target" "$after"
+        assert_in "remote-fs-pre.target" "$after"
+        assert_in "network.target" "$after"
+    fi
+
+    # mount LOOP_0
+    mount -t ext4 "${LOOP_0}p1" /tmp/deptest
+    sleep 1
+    after=$(systemctl show --property=After --value tmp-deptest.mount)
+    assert_in "local-fs-pre.target" "$after"
+    assert_not_in "remote-fs-pre.target" "$after"
+    assert_not_in "network.target" "$after"
+    assert_in "${escaped_0}.device" "$after"
+    assert_in "blockdev@${escaped_0}.target" "$after"
+    assert_not_in "${escaped_1}.device" "$after"
+    assert_not_in "blockdev@${escaped_1}.target" "$after"
+    umount /tmp/deptest
+
+    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+        after=$(systemctl show --property=After --value tmp-deptest.mount)
+        assert_not_in "local-fs-pre.target" "$after"
+        assert_in "remote-fs-pre.target" "$after"
+        assert_in "network.target" "$after"
+    fi
+
+    # mount LOOP_1 (using fake _netdev option)
+    mount -t ext4 -o _netdev "${LOOP_1}p1" /tmp/deptest
+    sleep 1
+    after=$(systemctl show --property=After --value tmp-deptest.mount)
+    assert_not_in "local-fs-pre.target" "$after"
+    assert_in "remote-fs-pre.target" "$after"
+    assert_in "network.target" "$after"
+    assert_not_in "${escaped_0}.device" "$after"
+    assert_not_in "blockdev@${escaped_0}.target" "$after"
+    assert_in "${escaped_1}.device" "$after"
+    assert_in "blockdev@${escaped_1}.target" "$after"
+    umount /tmp/deptest
+
+    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+        after=$(systemctl show --property=After --value tmp-deptest.mount)
+        assert_not_in "local-fs-pre.target" "$after"
+        assert_in "remote-fs-pre.target" "$after"
+        assert_in "network.target" "$after"
+    fi
+
+    # mount tmpfs
+    mount -t tmpfs tmpfs /tmp/deptest
+    sleep 1
+    after=$(systemctl show --property=After --value tmp-deptest.mount)
+    assert_in "local-fs-pre.target" "$after"
+    assert_not_in "remote-fs-pre.target" "$after"
+    assert_not_in "network.target" "$after"
+    assert_not_in "${escaped_0}.device" "$after"
+    assert_not_in "blockdev@${escaped_0}.target" "$after"
+    assert_not_in "${escaped_1}.device" "$after"
+    assert_not_in "blockdev@${escaped_1}.target" "$after"
+    umount /tmp/deptest
+
+    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+        after=$(systemctl show --property=After --value tmp-deptest.mount)
+        assert_not_in "local-fs-pre.target" "$after"
+        assert_in "remote-fs-pre.target" "$after"
+        assert_in "network.target" "$after"
+    fi
+}
+
+test_dependencies() {
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping test_dependencies in container"
+        return
+    fi
+
+    trap teardown_test_dependencies RETURN
+
+    setup_loop 0
+    LOOP_0="${LOOP}"
+    LOOP=
+    setup_loop 1
+    LOOP_1="${LOOP}"
+    LOOP=
+
+    mkdir -p /tmp/deptest
+
+    # without .mount file
+    check_dependencies
+
+    # create .mount file
+    mkdir -p /run/systemd/system
+    cat >/run/systemd/system/tmp-deptest.mount <<EOF
+[Mount]
+Where=/tmp/deptest
+What=192.168.0.1:/tmp/mnt
+Type=nfs
+EOF
+    systemctl daemon-reload
+
+    # with .mount file
+    check_dependencies
+}
+
+test_issue_20329() {
+    local tmpdir unit
+    tmpdir="$(mktemp -d)"
+    unit=$(systemd-escape --suffix mount --path "$tmpdir")
+
+    # Set up test mount unit
+    cat >/run/systemd/system/"$unit" <<EOF
+[Mount]
+What=tmpfs
+Where=$tmpdir
+Type=tmpfs
+Options=defaults,nofail
+EOF
+
+    # Start the unit
+    systemctl daemon-reload
+    systemctl start "$unit"
+
+    [[ "$(systemctl show --property SubState --value "$unit")" = "mounted" ]] || {
+        echo >&2 "Test mount \"$unit\" unit isn't mounted"
+        return 1
+    }
+    mountpoint -q "$tmpdir"
+
+    trap 'systemctl stop $unit' RETURN
+
+    # Trigger the mount ratelimiting
+    cd "$(mktemp -d)"
+    mkdir foo
+    for _ in {1..50}; do
+        mount --bind foo foo
+        umount foo
+    done
+
+    # Unmount the test mount and start it immediately again via systemd
+    umount "$tmpdir"
+    systemctl start "$unit"
+
+    # Make sure it is seen as mounted by systemd and it actually is mounted
+    [[ "$(systemctl show --property SubState --value "$unit")" = "mounted" ]] || {
+        echo >&2 "Test mount \"$unit\" unit isn't in \"mounted\" state"
+        return 1
+    }
+
+    mountpoint -q "$tmpdir" || {
+        echo >&2 "Test mount \"$unit\" is in \"mounted\" state, actually is not mounted"
+        return 1
+    }
+}
+
+test_issue_23796() {
+    local mount_path mount_mytmpfs
+
+    mount_path="$(command -v mount 2>/dev/null)"
+    mount_mytmpfs="${mount_path/\/bin/\/sbin}.mytmpfs"
+    cat >"$mount_mytmpfs" <<EOF
+#!/bin/bash
+sleep ".\$RANDOM"
+exec -- $mount_path -t tmpfs tmpfs "\$2"
+EOF
+    chmod +x "$mount_mytmpfs"
+
+    mkdir -p /run/systemd/system
+    cat >/run/systemd/system/tmp-hoge.mount <<EOF
+[Mount]
+What=mytmpfs
+Where=/tmp/hoge
+Type=mytmpfs
+EOF
+
+    # shellcheck disable=SC2064
+    trap "rm -f /run/systemd/system/tmp-hoge.mount '$mount_mytmpfs'" RETURN
+
+    for _ in {1..10}; do
+        systemctl --no-block start tmp-hoge.mount
+        sleep ".$RANDOM"
+        systemctl daemon-reexec
+
+        sleep 1
+
+        if [[ "$(systemctl is-failed tmp-hoge.mount)" == "failed" ]] || \
+           journalctl -u tmp-hoge.mount -q --grep "but there is no mount"; then
+                exit 1
+        fi
+
+        systemctl stop tmp-hoge.mount
+    done
+}
+
+systemd-analyze log-level debug
+systemd-analyze log-target journal
+
+NUM_DIRS=20
+
+# make sure we can handle mounts at very long paths such that mount unit name must be hashed to fall within our unit name limit
+LONGPATH="$(printf "/$(printf "x%0.s" {1..255})%0.s" {1..7})"
+LONGMNT="$(systemd-escape --suffix=mount --path "$LONGPATH")"
+TS="$(date '+%H:%M:%S')"
+
+mkdir -p "$LONGPATH"
+mount -t tmpfs tmpfs "$LONGPATH"
+systemctl daemon-reload
+
+# check that unit is active(mounted)
+systemctl --no-pager show -p SubState --value "$LONGPATH" | grep -q mounted
+
+# check that relevant part of journal doesn't contain any errors related to unit
+[ "$(journalctl -b --since="$TS" --priority=err | grep -c "$LONGMNT")" = "0" ]
+
+# check that we can successfully stop the mount unit
+systemctl stop "$LONGPATH"
+rm -rf "$LONGPATH"
+
+# mount/unmount enough times to trigger the /proc/self/mountinfo parsing rate limiting
+
+for ((i = 0; i < NUM_DIRS; i++)); do
+    mkdir "/tmp/meow${i}"
+done
+
+TS="$(date '+%H:%M:%S')"
+
+for ((i = 0; i < NUM_DIRS; i++)); do
+    mount -t tmpfs tmpfs "/tmp/meow${i}"
+done
+
+systemctl daemon-reload
+systemctl list-units -t mount tmp-meow* | grep -q tmp-meow
+
+for ((i = 0; i < NUM_DIRS; i++)); do
+    umount "/tmp/meow${i}"
+done
+
+# Figure out if we have entered the rate limit state.
+# If the infra is slow we might not enter the rate limit state; in that case skip the exit check.
+if timeout 2m bash -c "until journalctl -u init.scope --since=$TS | grep -q '(mount-monitor-dispatch) entered rate limit'; do sleep 1; done"; then
+    timeout 2m bash -c "until journalctl -u init.scope --since=$TS | grep -q '(mount-monitor-dispatch) left rate limit'; do sleep 1; done"
+fi
+
+# Verify that the mount units are always cleaned up at the end.
+# Give some time for units to settle so we don't race between exiting the rate limit state and cleaning up the units.
+timeout 2m bash -c 'while systemctl list-units -t mount tmp-meow* | grep -q tmp-meow; do systemctl daemon-reload; sleep 10; done'
+
+# test for issue #19983 and #23552.
+test_dependencies
+
+# test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited
+test_issue_20329
+
+# test for reexecuting with background mount job
+test_issue_23796
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-62-RESTRICT-IFACES-1.service b/test/units/TEST-62-RESTRICT-IFACES-1.service
new file mode 100644 (file)
index 0000000..16695c1
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-all-pings-work
+[Service]
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=
+Type=oneshot
diff --git a/test/units/TEST-62-RESTRICT-IFACES-2.service b/test/units/TEST-62-RESTRICT-IFACES-2.service
new file mode 100644 (file)
index 0000000..bce7e8e
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-allow-list
+[Service]
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0
+RestrictNetworkInterfaces=veth1
+Type=oneshot
diff --git a/test/units/TEST-62-RESTRICT-IFACES-3.service b/test/units/TEST-62-RESTRICT-IFACES-3.service
new file mode 100644 (file)
index 0000000..116530b
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-deny-list
+[Service]
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=~veth0
+RestrictNetworkInterfaces=~veth1
+Type=oneshot
diff --git a/test/units/TEST-62-RESTRICT-IFACES-4.service b/test/units/TEST-62-RESTRICT-IFACES-4.service
new file mode 100644 (file)
index 0000000..200a383
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-empty-assignment
+[Service]
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0
+RestrictNetworkInterfaces=
+Type=oneshot
diff --git a/test/units/TEST-62-RESTRICT-IFACES-5.service b/test/units/TEST-62-RESTRICT-IFACES-5.service
new file mode 100644 (file)
index 0000000..51761ba
--- /dev/null
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-invert-assignment
+[Service]
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0
+RestrictNetworkInterfaces=veth0 veth1
+RestrictNetworkInterfaces=~veth0
+Type=oneshot
diff --git a/test/units/TEST-62-RESTRICT-IFACES-6.service b/test/units/TEST-62-RESTRICT-IFACES-6.service
new file mode 100644 (file)
index 0000000..876d8f3
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-altname
+[Service]
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0-altname-with-more-than-15-chars
+RestrictNetworkInterfaces=veth1-altname-with-more-than-15-chars
+Type=oneshot
diff --git a/test/units/TEST-62-RESTRICT-IFACES.service b/test/units/TEST-62-RESTRICT-IFACES.service
new file mode 100644 (file)
index 0000000..5c3f94d
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-62-RESTRICT-IFACES.sh b/test/units/TEST-62-RESTRICT-IFACES.sh
new file mode 100755 (executable)
index 0000000..35ab862
--- /dev/null
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+setup() {
+    systemd-analyze log-level debug
+
+    for i in {0..3};
+    do
+        ip netns del "ns${i}" || true
+        ip link del "veth${i}" || true
+        ip netns add "ns${i}"
+        ip link add "veth${i}" type veth peer name "veth${i}_"
+        ip link set "veth${i}_" netns "ns${i}"
+        ip -n "ns${i}" link set dev "veth${i}_" up
+        ip -n "ns${i}" link set dev lo up
+        ip -n "ns${i}" addr add "192.168.113."$((4*i+1))/30 dev "veth${i}_"
+        ip link set dev "veth${i}" up
+        ip link property add dev "veth${i}" altname "veth${i}-altname-with-more-than-15-chars"
+        ip addr add "192.168.113."$((4*i+2))/30 dev "veth${i}"
+    done
+}
+
+# shellcheck disable=SC2317
+teardown() {
+    set +e
+
+    for i in {0..3}; do
+        ip netns del "ns${i}"
+        ip link del "veth${i}"
+    done
+
+    systemd-analyze log-level info
+}
+
+if systemd-analyze compare-versions "$(uname -r)" lt 5.7; then
+    echo "kernel is not 5.7+" >>/skipped
+    exit 77
+fi
+
+if systemctl --version | grep -q -F -- "-BPF_FRAMEWORK"; then
+    echo "bpf-framework is disabled" >>/skipped
+    exit 77
+fi
+
+trap teardown EXIT
+setup
+
+systemctl start --wait TEST-62-RESTRICT-IFACES-1.service
+systemctl start --wait TEST-62-RESTRICT-IFACES-2.service
+systemctl start --wait TEST-62-RESTRICT-IFACES-3.service
+systemctl start --wait TEST-62-RESTRICT-IFACES-4.service
+systemctl start --wait TEST-62-RESTRICT-IFACES-5.service
+
+touch /testok
diff --git a/test/units/TEST-63-PATH.service b/test/units/TEST-63-PATH.service
new file mode 100644 (file)
index 0000000..483c6a8
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-63-PATH
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-63-PATH.sh b/test/units/TEST-63-PATH.sh
new file mode 100755 (executable)
index 0000000..ea8cd94
--- /dev/null
@@ -0,0 +1,125 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemctl log-level debug
+
+# Test that a path unit continuously triggering a service that fails condition checks eventually fails with
+# the trigger-limit-hit error.
+rm -f /tmp/nonexistent
+systemctl start test63.path
+touch /tmp/test63
+
+# Make sure systemd has sufficient time to hit the trigger limit for test63.path.
+# shellcheck disable=SC2016
+timeout 30 bash -c 'until test "$(systemctl show test63.path -P ActiveState)" = failed; do sleep .2; done'
+test "$(systemctl show test63.service -P ActiveState)" = inactive
+test "$(systemctl show test63.service -P Result)" = success
+test "$(systemctl show test63.path -P Result)" = trigger-limit-hit
+
+# Test that starting the service manually doesn't affect the path unit.
+rm -f /tmp/test63
+systemctl reset-failed
+systemctl start test63.path
+systemctl start test63.service
+test "$(systemctl show test63.service -P ActiveState)" = inactive
+test "$(systemctl show test63.service -P Result)" = success
+test "$(systemctl show test63.path -P ActiveState)" = active
+test "$(systemctl show test63.path -P Result)" = success
+
+# Test that glob matching works too, with $TRIGGER_PATH
+systemctl start test63-glob.path
+touch /tmp/test63-glob-foo
+timeout 60 bash -c 'until systemctl -q is-active test63-glob.service; do sleep .2; done'
+test "$(systemctl show test63-glob.service -P ActiveState)" = active
+test "$(systemctl show test63-glob.service -P Result)" = success
+
+test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[["trigger_unit","test63-glob.path"],["trigger_path","/tmp/test63-glob-foo"]]}'
+
+systemctl stop test63-glob.path test63-glob.service
+
+test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[]}'
+
+# tests for issue https://github.com/systemd/systemd/issues/24577#issuecomment-1522628906
+rm -f /tmp/hoge
+systemctl start test63-issue-24577.path
+systemctl status -n 0 test63-issue-24577.path
+systemctl status -n 0 test63-issue-24577.service || :
+systemctl list-jobs
+output=$(systemctl list-jobs --no-legend)
+assert_not_in "test63-issue-24577.service" "$output"
+assert_not_in "test63-issue-24577-dep.service" "$output"
+
+touch /tmp/hoge
+systemctl status -n 0 test63-issue-24577.path
+systemctl status -n 0 test63-issue-24577.service || :
+systemctl list-jobs
+output=$(systemctl list-jobs --no-legend)
+assert_in "test63-issue-24577.service" "$output"
+assert_in "test63-issue-24577-dep.service" "$output"
+
+# even if the service is stopped, it will be soon retriggered.
+systemctl stop test63-issue-24577.service
+systemctl status -n 0 test63-issue-24577.path
+systemctl status -n 0 test63-issue-24577.service || :
+systemctl list-jobs
+output=$(systemctl list-jobs --no-legend)
+assert_in "test63-issue-24577.service" "$output"
+assert_in "test63-issue-24577-dep.service" "$output"
+
+rm -f /tmp/hoge
+systemctl stop test63-issue-24577.service
+systemctl status -n 0 test63-issue-24577.path
+systemctl status -n 0 test63-issue-24577.service || :
+systemctl list-jobs
+output=$(systemctl list-jobs --no-legend)
+assert_not_in "test63-issue-24577.service" "$output"
+assert_in "test63-issue-24577-dep.service" "$output"
+
+# Test for race condition fixed by https://github.com/systemd/systemd/pull/30768
+# Here's the schedule of events that we to happen during this test:
+#       (This test)                     (The service)
+#                                       .path unit monitors /tmp/copyme for changes
+#       Take lock on /tmp/noexeit       ↓
+#       Write to /tmp/copyme            ↓
+#       Wait for deactivating           Started
+#       ↓                               Copies /tmp/copyme to /tmp/copied
+#       ↓                               Tells manager it's shutting down
+#       Ensure service did the copy     Tries to lock /tmp/noexit and blocks
+#       Write to /tmp/copyme            ↓
+#
+# Now at this point the test can diverge. If we regress, this second write is
+# missed and we'll see:
+#       ... (second write)              ... (blocked)
+#       Drop lock on /tmp/noexit        ↓
+#       Wait for service to do copy     Unblocks and exits
+#       ↓                               (dead)
+#       ↓
+#       (timeout)
+#       Test fails
+#
+# Otherwise, we'll see:
+#       ... (second write)              ... (blocked)
+#       Drop lock on /tmp/noexit        ↓ and .path unit queues a new start job
+#       Wait for service to do copy     Unblocks and exits
+#       ↓                               Starts again b/c of queued job
+#       ↓                               Copies again
+#       Test Passes
+systemctl start test63-pr-30768.path
+exec {lock}<>/tmp/noexit
+flock -e $lock
+echo test1 > /tmp/copyme
+# shellcheck disable=SC2016
+timeout 30 bash -c 'until test "$(systemctl show test63-pr-30768.service -P ActiveState)" = deactivating; do sleep .2; done'
+diff /tmp/copyme /tmp/copied
+echo test2 > /tmp/copyme
+exec {lock}<&-
+timeout 30 bash -c 'until diff /tmp/copyme /tmp/copied; do sleep .2; done'
+
+systemctl log-level info
+
+touch /testok
diff --git a/test/units/TEST-64-UDEV-STORAGE.service b/test/units/TEST-64-UDEV-STORAGE.service
new file mode 100644 (file)
index 0000000..b7626bb
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-64-UDEV-STORAGE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-64-UDEV-STORAGE.sh b/test/units/TEST-64-UDEV-STORAGE.sh
new file mode 100755 (executable)
index 0000000..120b5f3
--- /dev/null
@@ -0,0 +1,1223 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# vi: ts=4 sw=4 tw=0 et:
+
+set -eux
+set -o pipefail
+
+# Check if all symlinks under /dev/disk/ are valid
+# shellcheck disable=SC2120
+helper_check_device_symlinks() {(
+    set +x
+
+    local dev link path paths target
+
+    [[ $# -gt 0 ]] && paths=("$@") || paths=("/dev/disk" "/dev/mapper")
+
+    # Check if all given paths are valid
+    for path in "${paths[@]}"; do
+        if ! test -e "$path"; then
+            echo >&2 "Path '$path' doesn't exist"
+            return 1
+        fi
+    done
+
+    while read -r link; do
+        target="$(readlink -f "$link")"
+        # Both checks should do virtually the same thing, but check both to be
+        # on the safe side
+        if [[ ! -e "$link" || ! -e "$target" ]]; then
+            echo >&2 "ERROR: symlink '$link' points to '$target' which doesn't exist"
+            return 1
+        fi
+
+        # Check if the symlink points to the correct device in /dev
+        dev="/dev/$(udevadm info -q name "$link")"
+        if [[ "$target" != "$dev" ]]; then
+            echo >&2 "ERROR: symlink '$link' points to '$target' but '$dev' was expected"
+            return 1
+        fi
+    done < <(find "${paths[@]}" -type l)
+)}
+
+helper_check_udev_watch() {(
+    set +x
+
+    local link target id dev
+
+    while read -r link; do
+        target="$(readlink "$link")"
+        if [[ ! -L "/run/udev/watch/$target" ]]; then
+            echo >&2 "ERROR: symlink /run/udev/watch/$target does not exist"
+            return 1
+        fi
+        if [[ "$(readlink "/run/udev/watch/$target")" != "$(basename "$link")" ]]; then
+            echo >&2 "ERROR: symlink target of /run/udev/watch/$target is inconsistent with $link"
+            return 1
+        fi
+
+        if [[ "$target" =~ ^[0-9]+$ ]]; then
+            # $link is ID -> wd
+            id="$(basename "$link")"
+        else
+            # $link is wd -> ID
+            id="$target"
+        fi
+
+        if [[ "${id:0:1}" == "b" ]]; then
+            dev="/dev/block/${id:1}"
+        elif [[ "${id:0:1}" == "c" ]]; then
+            dev="/dev/char/${id:1}"
+        else
+            echo >&2 "ERROR: unexpected device ID '$id'"
+            return 1
+        fi
+
+        if [[ ! -e "$dev" ]]; then
+            echo >&2 "ERROR: device '$dev' corresponding to symlink '$link' does not exist"
+            return 1
+        fi
+    done < <(find /run/udev/watch -type l)
+)}
+
+check_device_unit() {(
+    set +x
+
+    local log_level link links path syspath unit
+
+    log_level="${1?}"
+    path="${2?}"
+    unit=$(systemd-escape --path --suffix=device "$path")
+
+    [[ "$log_level" == 1 ]] && echo "INFO: check_device_unit($unit)"
+
+    syspath=$(systemctl show --value --property SysFSPath "$unit" 2>/dev/null)
+    if [[ -z "$syspath" ]]; then
+        [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit not found."
+        return 1
+    fi
+
+    if [[ ! -L "$path" ]]; then
+        if [[ ! -d "$syspath" ]]; then
+            [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit exists for $syspath but it does not exist."
+            return 1
+        fi
+        return 0
+    fi
+
+    if [[ ! -b "$path" && ! -c "$path" ]]; then
+        [[ "$log_level" == 1 ]] && echo >&2 "ERROR: invalid file type $path"
+        return 1
+    fi
+
+    read -r -a links < <(udevadm info -q symlink "$syspath" 2>/dev/null)
+    for link in "${links[@]}"; do
+        if [[ "/dev/$link" == "$path" ]]; then # DEVLINKS= given by -q symlink are relative to /dev
+            return 0
+        fi
+    done
+
+    read -r -a links < <(udevadm info "$syspath" | sed -ne '/SYSTEMD_ALIAS=/ { s/^E: SYSTEMD_ALIAS=//; p }' 2>/dev/null)
+    for link in "${links[@]}"; do
+        if [[ "$link" == "$path" ]]; then # SYSTEMD_ALIAS= are absolute
+            return 0
+        fi
+    done
+
+    [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit exists for $syspath but it does not have the corresponding DEVLINKS or SYSTEMD_ALIAS."
+    return 1
+)}
+
+check_device_units() {(
+    set +x
+
+    local log_level path paths
+
+    log_level="${1?}"
+    shift
+    paths=("$@")
+
+    for path in "${paths[@]}"; do
+        if ! check_device_unit "$log_level" "$path"; then
+           return 1
+        fi
+    done
+
+    while read -r unit _; do
+        path=$(systemd-escape --path --unescape "$unit")
+        if ! check_device_unit "$log_level" "$path"; then
+           return 1
+        fi
+    done < <(systemctl list-units --all --type=device --no-legend dev-* | awk '$1 !~ /dev-tty.+/ && $4 == "plugged" { print $1 }' | sed -e 's/\.device$//')
+
+    return 0
+)}
+
+helper_check_device_units() {(
+    set +x
+
+    local i
+
+    for i in {1..20}; do
+        (( i > 1 )) && sleep 0.5
+        if check_device_units 0 "$@"; then
+            return 0
+        fi
+    done
+
+    check_device_units 1 "$@"
+)}
+
+testcase_megasas2_basic() {
+    lsblk -S
+    [[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]]
+}
+
+testcase_nvme_basic() {
+    local expected_symlinks=()
+    local i
+
+    for (( i = 0; i < 5; i++ )); do
+        expected_symlinks+=(
+            # both replace mode provides the same devlink
+            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef"$i"
+            # with nsid
+            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef"$i"_1
+        )
+    done
+    for (( i = 5; i < 10; i++ )); do
+        expected_symlinks+=(
+            # old replace mode
+            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl__deadbeef_"$i"
+            # newer replace mode
+            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____deadbeef__"$i"
+            # with nsid
+            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____deadbeef__"$i"_1
+        )
+    done
+    for (( i = 10; i < 15; i++ )); do
+        expected_symlinks+=(
+            # old replace mode does not provide devlink, as serial contains "/"
+            # newer replace mode
+            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____dead_beef_"$i"
+            # with nsid
+            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____dead_beef_"$i"_1
+        )
+    done
+    for (( i = 15; i < 20; i++ )); do
+        expected_symlinks+=(
+            # old replace mode does not provide devlink, as serial contains "/"
+            # newer replace mode
+            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_dead_.._.._beef_"$i"
+            # with nsid
+            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_dead_.._.._beef_"$i"_1
+        )
+    done
+
+    udevadm settle
+    ls /dev/disk/by-id
+    for i in "${expected_symlinks[@]}"; do
+        udevadm wait --settle --timeout=30 "$i"
+    done
+
+    lsblk --noheadings | grep "^nvme"
+    [[ "$(lsblk --noheadings | grep -c "^nvme")" -ge 20 ]]
+}
+
+testcase_nvme_subsystem() {
+    local expected_symlinks=(
+        # Controller(s)
+        /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef
+        /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_16
+        /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_17
+        # Shared namespaces
+        /dev/disk/by-path/pci-*-nvme-16
+        /dev/disk/by-path/pci-*-nvme-17
+    )
+
+    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+}
+
+testcase_virtio_scsi_identically_named_partitions() {
+    local num_part num_disk i j
+    local alphabet='abcdefghijklmnopqrstuvwxyz'
+
+    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
+        num_part=4
+        num_disk=4
+    else
+        num_part=8
+        num_disk=16
+    fi
+
+    for ((i = 0; i < num_disk; i++)); do
+        sfdisk "/dev/sd${alphabet:$i:1}" <<EOF
+label: gpt
+
+$(for ((j = 1; j <= num_part; j++)); do echo 'name="Hello world", size=2M'; done)
+EOF
+    done
+
+    lsblk --noheadings -a -o NAME,PARTLABEL
+    [[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq "$((num_part * num_disk))" ]]
+}
+
+testcase_multipath_basic_failover() {
+    local dmpath i path wwid
+
+    # Configure multipath
+    cat >/etc/multipath.conf <<\EOF
+defaults {
+    # Use /dev/mapper/$WWN paths instead of /dev/mapper/mpathX
+    user_friendly_names no
+    find_multipaths yes
+    enable_foreign "^$"
+}
+
+blacklist_exceptions {
+    property "(SCSI_IDENT_|ID_WWN)"
+}
+
+blacklist {
+}
+EOF
+
+    sfdisk /dev/sda <<EOF
+label: gpt
+
+name="first_partition", size=5M
+uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M
+EOF
+    udevadm settle
+    mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" "/dev/sda2"
+
+    modprobe -v dm_multipath
+    systemctl start multipathd.service
+    systemctl status multipathd.service
+    multipath -ll
+    udevadm settle
+    ls -l /dev/disk/by-id/
+
+    for i in {0..15}; do
+        wwid="deaddeadbeef$(printf "%.4d" "$i")"
+        path="/dev/disk/by-id/wwn-0x$wwid"
+        dmpath="$(readlink -f "$path")"
+
+        lsblk "$path"
+        multipath -C "$dmpath"
+        # We should have 4 active paths for each multipath device
+        [[ "$(multipath -l "$path" | grep -c running)" -eq 4 ]]
+    done
+
+    # Test failover (with the first multipath device that has a partitioned disk)
+    echo "${FUNCNAME[0]}: test failover"
+    local device expected link mpoint part
+    local -a devices
+    mkdir -p /mnt
+    mpoint="$(mktemp -d /mnt/mpathXXX)"
+    wwid="deaddeadbeef0000"
+    path="/dev/disk/by-id/wwn-0x$wwid"
+
+    # All following symlinks should exists and should be valid
+    local -a part_links=(
+        "/dev/disk/by-id/wwn-0x$wwid-part2"
+        "/dev/disk/by-partlabel/failover_part"
+        "/dev/disk/by-partuuid/deadbeef-dead-dead-beef-000000000000"
+        "/dev/disk/by-label/failover_vol"
+        "/dev/disk/by-uuid/deadbeef-dead-dead-beef-111111111111"
+    )
+    udevadm wait --settle --timeout=30 "${part_links[@]}"
+    helper_check_device_units "${part_links[@]}"
+
+    # Choose a random symlink to the failover data partition each time, for
+    # a better coverage
+    part="${part_links[$RANDOM % ${#part_links[@]}]}"
+
+    # Get all devices attached to a specific multipath device (in H:C:T:L format)
+    # and sort them in a random order, so we cut off different paths each time
+    mapfile -t devices < <(multipath -l "$path" | grep -Eo '[0-9]+:[0-9]+:[0-9]+:[0-9]+' | sort -R)
+    if [[ "${#devices[@]}" -ne 4 ]]; then
+        echo "Expected 4 devices attached to WWID=$wwid, got ${#devices[@]} instead"
+        return 1
+    fi
+    # Drop the last path from the array, since we want to leave at least one path active
+    unset "devices[3]"
+    # Mount the first multipath partition, write some data we can check later,
+    # and then disconnect the remaining paths one by one while checking if we
+    # can still read/write from the mount
+    mount -t ext4 "$part" "$mpoint"
+    expected=0
+    echo -n "$expected" >"$mpoint/test"
+    # Sanity check we actually wrote what we wanted
+    [[ "$(<"$mpoint/test")" == "$expected" ]]
+
+    for device in "${devices[@]}"; do
+        echo offline >"/sys/class/scsi_device/$device/device/state"
+        [[ "$(<"$mpoint/test")" == "$expected" ]]
+        expected="$((expected + 1))"
+        echo -n "$expected" >"$mpoint/test"
+
+        # Make sure all symlinks are still valid
+        udevadm wait --settle --timeout=30 "${part_links[@]}"
+        helper_check_device_units "${part_links[@]}"
+    done
+
+    multipath -l "$path"
+    # Three paths should be now marked as 'offline' and one as 'running'
+    [[ "$(multipath -l "$path" | grep -c offline)" -eq 3 ]]
+    [[ "$(multipath -l "$path" | grep -c running)" -eq 1 ]]
+
+    umount "$mpoint"
+    rm -fr "$mpoint"
+}
+
+testcase_simultaneous_events_1() {
+    local disk expected i iterations key link num_part part partscript rule target timeout
+    local -a devices symlinks
+    local -A running
+
+    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
+        num_part=2
+        iterations=10
+        timeout=240
+    else
+        num_part=10
+        iterations=100
+        timeout=30
+    fi
+
+    for disk in {0..9}; do
+        link="/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_deadbeeftest${disk}"
+        target="$(readlink -f "$link")"
+        if [[ ! -b "$target" ]]; then
+            echo "ERROR: failed to find the test SCSI block device $link"
+            return 1
+        fi
+
+        devices+=("$target")
+    done
+
+    for ((part = 1; part <= num_part; part++)); do
+        symlinks+=(
+            "/dev/disk/by-partlabel/test${part}"
+        )
+    done
+
+    partscript="$(mktemp)"
+
+    cat >"$partscript" <<EOF
+$(for ((part = 1; part <= num_part; part++)); do printf 'name="test%d", size=2M\n' "$part"; done)
+EOF
+
+    rule=/run/udev/rules.d/50-test.rules
+    mkdir -p "${rule%/*}"
+    cat >"$rule" <<EOF
+SUBSYSTEM=="block", KERNEL=="${devices[4]##*/}*|${devices[5]##*/}*", OPTIONS="link_priority=10"
+EOF
+
+    udevadm control --reload
+
+    # initialize partition table
+    for disk in {0..9}; do
+        echo 'label: gpt' | udevadm lock --device="${devices[$disk]}" sfdisk -q "${devices[$disk]}"
+    done
+
+    # Delete the partitions, immediately recreate them, wait for udev to settle
+    # down, and then check if we have any dangling symlinks in /dev/disk/. Rinse
+    # and repeat.
+    #
+    # On unpatched udev versions the delete-recreate cycle may trigger a race
+    # leading to dead symlinks in /dev/disk/
+    for ((i = 1; i <= iterations; i++)); do
+        for disk in {0..9}; do
+            if ((disk % 2 == i % 2)); then
+                udevadm lock --device="${devices[$disk]}" sfdisk -q --delete "${devices[$disk]}" &
+            else
+                udevadm lock --device="${devices[$disk]}" sfdisk -q -X gpt "${devices[$disk]}" <"$partscript" &
+            fi
+            running[$disk]=$!
+        done
+
+        for key in "${!running[@]}"; do
+            wait "${running[$key]}"
+            unset "running[$key]"
+        done
+
+        if ((i % 10 <= 1)); then
+            udevadm wait --settle --timeout="$timeout" "${devices[@]}" "${symlinks[@]}"
+            helper_check_device_symlinks
+            helper_check_udev_watch
+            for ((part = 1; part <= num_part; part++)); do
+                link="/dev/disk/by-partlabel/test${part}"
+                target="$(readlink -f "$link")"
+                if ((i % 2 == 0)); then
+                    expected="${devices[5]}$part"
+                else
+                    expected="${devices[4]}$part"
+                fi
+                if [[ "$target" != "$expected" ]]; then
+                    echo >&2 "ERROR: symlink '/dev/disk/by-partlabel/test${part}' points to '$target' but '$expected' was expected"
+                    return 1
+                fi
+            done
+        fi
+    done
+
+    helper_check_device_units
+    rm -f "$rule" "$partscript"
+
+    udevadm control --reload
+}
+
+testcase_simultaneous_events_2() {
+    local disk expected i iterations key link num_part part script_dir target timeout
+    local -a devices symlinks
+    local -A running
+
+    script_dir="$(mktemp --directory "/tmp/test-udev-storage.script.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$script_dir'" RETURN
+
+    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
+        num_part=20
+        iterations=1
+        timeout=2400
+    else
+        num_part=100
+        iterations=3
+        timeout=300
+    fi
+
+    for disk in {0..9}; do
+        link="/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_deadbeeftest${disk}"
+        target="$(readlink -f "$link")"
+        if [[ ! -b "$target" ]]; then
+            echo "ERROR: failed to find the test SCSI block device $link"
+            return 1
+        fi
+
+        devices+=("$target")
+    done
+
+    for ((i = 1; i <= iterations; i++)); do
+        cat >"$script_dir/partscript-$i" <<EOF
+$(for ((part = 1; part <= num_part; part++)); do printf 'name="testlabel-%d", size=1M\n' "$i"; done)
+EOF
+    done
+
+    echo "## $iterations iterations start: $(date '+%H:%M:%S.%N')"
+    for ((i = 1; i <= iterations; i++)); do
+
+        for disk in {0..9}; do
+            udevadm lock --device="${devices[$disk]}" sfdisk -q --delete "${devices[$disk]}" &
+            running[$disk]=$!
+        done
+
+        for key in "${!running[@]}"; do
+            wait "${running[$key]}"
+            unset "running[$key]"
+        done
+
+        for disk in {0..9}; do
+            udevadm lock --device="${devices[$disk]}" sfdisk -q -X gpt "${devices[$disk]}" <"$script_dir/partscript-$i" &
+            running[$disk]=$!
+        done
+
+        for key in "${!running[@]}"; do
+            wait "${running[$key]}"
+            unset "running[$key]"
+        done
+
+        udevadm wait --settle --timeout="$timeout" "${devices[@]}" "/dev/disk/by-partlabel/testlabel-$i"
+    done
+    echo "## $iterations iterations end: $(date '+%H:%M:%S.%N')"
+}
+
+testcase_simultaneous_events() {
+    testcase_simultaneous_events_1
+    testcase_simultaneous_events_2
+}
+
+testcase_lvm_basic() {
+    local i iterations partitions part timeout
+    local vgroup="MyTestGroup$RANDOM"
+    local devices=(
+        /dev/disk/by-id/ata-foobar_deadbeeflvm{0..3}
+    )
+
+    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
+        timeout=180
+    else
+        timeout=30
+    fi
+    # Make sure all the necessary soon-to-be-LVM devices exist
+    ls -l "${devices[@]}"
+
+    # Add all test devices into a volume group, create two logical volumes,
+    # and check if necessary symlinks exist (and are valid)
+    lvm pvcreate -y "${devices[@]}"
+    lvm pvs
+    lvm vgcreate "$vgroup" -y "${devices[@]}"
+    lvm vgs
+    lvm vgchange -ay "$vgroup"
+    lvm lvcreate -y -L 4M "$vgroup" -n mypart1
+    lvm lvcreate -y -L 32M "$vgroup" -n mypart2
+    lvm lvs
+    udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
+    mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1"
+    udevadm wait --settle --timeout="$timeout" "/dev/disk/by-label/mylvpart1"
+    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+    helper_check_device_units
+
+    # Mount mypart1 through by-label devlink
+    mkdir -p /tmp/mypart1-mount-point
+    mount /dev/disk/by-label/mylvpart1 /tmp/mypart1-mount-point
+    timeout 30 bash -c "until systemctl -q is-active /tmp/mypart1-mount-point; do sleep .2; done"
+    # Extend the partition and check if the device and mount units are still active.
+    # See https://bugzilla.redhat.com/show_bug.cgi?id=2158628
+    # Note, the test below may be unstable with LVM2 without the following patch:
+    # https://github.com/lvmteam/lvm2/pull/105
+    # But, to reproduce the issue, udevd must start to process the first 'change' uevent
+    # earlier than extending the volume has been finished, and in most case, the extension
+    # is hopefully fast.
+    lvm lvextend -y --size 8M "/dev/$vgroup/mypart1"
+    udevadm wait --settle --timeout="$timeout" "/dev/disk/by-label/mylvpart1"
+    timeout 30 bash -c "until systemctl -q is-active '/dev/$vgroup/mypart1'; do sleep .2; done"
+    timeout 30 bash -c "until systemctl -q is-active /tmp/mypart1-mount-point; do sleep .2; done"
+    # Umount the partition, otherwise the underlying device unit will stay in
+    # the inactive state and not be collected, and helper_check_device_units() will fail.
+    systemctl show /tmp/mypart1-mount-point
+    umount /tmp/mypart1-mount-point
+
+    # Rename partitions (see issue #24518)
+    lvm lvrename "/dev/$vgroup/mypart1" renamed1
+    lvm lvrename "/dev/$vgroup/mypart2" renamed2
+    udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
+    udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/renamed1" "/dev/$vgroup/renamed2"
+    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+    helper_check_device_units
+
+    # Rename them back
+    lvm lvrename "/dev/$vgroup/renamed1" mypart1
+    lvm lvrename "/dev/$vgroup/renamed2" mypart2
+    udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/renamed1" "/dev/$vgroup/renamed2"
+    udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
+    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+    helper_check_device_units
+
+    # Do not "unready" suspended encrypted devices w/o superblock info
+    # See:
+    #   - https://github.com/systemd/systemd/pull/24177
+    #   - https://bugzilla.redhat.com/show_bug.cgi?id=1985288
+    dd if=/dev/urandom of=/etc/lvm_keyfile bs=64 count=1 iflag=fullblock
+    chmod 0600 /etc/lvm_keyfile
+    # Intentionally use weaker cipher-related settings, since we don't care
+    # about security here as it's a throwaway LUKS partition
+    cryptsetup luksFormat -q --use-urandom --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \
+                          "/dev/$vgroup/mypart2" /etc/lvm_keyfile
+    # Mount the LUKS partition & create a filesystem on it
+    mkdir -p /tmp/lvmluksmnt
+    cryptsetup open --key-file=/etc/lvm_keyfile "/dev/$vgroup/mypart2" "lvmluksmap"
+    udevadm wait --settle --timeout="$timeout" "/dev/mapper/lvmluksmap"
+    mkfs.ext4 -L lvmluksfs "/dev/mapper/lvmluksmap"
+    udevadm wait --settle --timeout="$timeout" "/dev/disk/by-label/lvmluksfs"
+    # Make systemd "interested" in the mount by adding it to /etc/fstab
+    echo "/dev/disk/by-label/lvmluksfs /tmp/lvmluksmnt ext4 defaults 0 2" >>/etc/fstab
+    systemctl daemon-reload
+    mount "/tmp/lvmluksmnt"
+    mountpoint "/tmp/lvmluksmnt"
+    # Temporarily suspend the LUKS device and trigger udev - basically what `cryptsetup resize`
+    # does but in a more deterministic way suitable for a test/reproducer
+    for _ in {0..5}; do
+        dmsetup suspend "/dev/mapper/lvmluksmap"
+        udevadm trigger -v --settle "/dev/mapper/lvmluksmap"
+        dmsetup resume "/dev/mapper/lvmluksmap"
+        # The mount should survive this sequence of events
+        mountpoint "/tmp/lvmluksmnt"
+    done
+    # Cleanup
+    umount "/tmp/lvmluksmnt"
+    cryptsetup close "/dev/mapper/lvmluksmap"
+    sed -i "/lvmluksfs/d" "/etc/fstab"
+    systemctl daemon-reload
+
+    # Disable the VG and check symlinks...
+    lvm vgchange -an "$vgroup"
+    udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup" "/dev/disk/by-label/mylvpart1"
+    helper_check_device_symlinks "/dev/disk"
+    helper_check_device_units
+
+    # reenable the VG and check the symlinks again if all LVs are properly activated
+    lvm vgchange -ay "$vgroup"
+    udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1"
+    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+    helper_check_device_units
+
+    # Same as above, but now with more "stress"
+    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
+        iterations=10
+    else
+        iterations=50
+    fi
+
+    for ((i = 1; i <= iterations; i++)); do
+        lvm vgchange -an "$vgroup"
+        lvm vgchange -ay "$vgroup"
+
+        if ((i % 5 == 0)); then
+            udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1"
+            helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+            helper_check_device_units
+        fi
+    done
+
+    # Remove the first LV
+    lvm lvremove -y "$vgroup/mypart1"
+    udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/mypart1"
+    udevadm wait --timeout=0 "/dev/$vgroup/mypart2"
+    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+    helper_check_device_units
+
+    # Create & remove LVs in a loop, i.e. with more "stress"
+    if [[ -v ASAN_OPTIONS ]]; then
+        iterations=8
+        partitions=16
+    elif [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
+        iterations=8
+        partitions=8
+    else
+        iterations=16
+        partitions=16
+    fi
+
+    for ((i = 1; i <= iterations; i++)); do
+        # 1) Create some logical volumes
+        for ((part = 0; part < partitions; part++)); do
+            lvm lvcreate -y -L 4M "$vgroup" -n "looppart$part"
+        done
+
+        # 2) Immediately remove them
+        lvm lvremove -y $(seq -f "$vgroup/looppart%g" 0 "$((partitions - 1))")
+
+        # 3) On every 4th iteration settle udev and check if all partitions are
+        #    indeed gone, and if all symlinks are still valid
+        if ((i % 4 == 0)); then
+            for ((part = 0; part < partitions; part++)); do
+                udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/looppart$part"
+            done
+            helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+            helper_check_device_units
+        fi
+    done
+}
+
+testcase_btrfs_basic() {
+    local dev_stub i label mpoint uuid
+    local devices=(
+        /dev/disk/by-id/ata-foobar_deadbeefbtrfs{0..3}
+    )
+
+    ls -l "${devices[@]}"
+
+    echo "Single device: default settings"
+    uuid="deadbeef-dead-dead-beef-000000000000"
+    label="btrfs_root"
+    udevadm lock --device="${devices[0]}" mkfs.btrfs -f -L "$label" -U "$uuid" "${devices[0]}"
+    udevadm wait --settle --timeout=30 "${devices[0]}" "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
+    btrfs filesystem show
+    helper_check_device_symlinks
+    helper_check_device_units
+
+    echo "Multiple devices: using partitions, data: single, metadata: raid1"
+    uuid="deadbeef-dead-dead-beef-000000000001"
+    label="btrfs_mpart"
+    udevadm lock --device="${devices[0]}" sfdisk --wipe=always "${devices[0]}" <<EOF
+label: gpt
+
+name="diskpart1", size=85M
+name="diskpart2", size=85M
+name="diskpart3", size=85M
+name="diskpart4", size=85M
+EOF
+    udevadm wait --settle --timeout=30 /dev/disk/by-partlabel/diskpart{1..4}
+    udevadm lock --device="${devices[0]}" mkfs.btrfs -f -d single -m raid1 -L "$label" -U "$uuid" /dev/disk/by-partlabel/diskpart{1..4}
+    udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
+    btrfs filesystem show
+    helper_check_device_symlinks
+    helper_check_device_units
+    wipefs -a -f "${devices[0]}"
+    udevadm wait --settle --timeout=30 --removed /dev/disk/by-partlabel/diskpart{1..4}
+
+    echo "Multiple devices: using disks, data: raid10, metadata: raid10, mixed mode"
+    uuid="deadbeef-dead-dead-beef-000000000002"
+    label="btrfs_mdisk"
+    udevadm lock \
+            --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs0 \
+            --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs1 \
+            --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs2 \
+            --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs3 \
+            mkfs.btrfs -f -M -d raid10 -m raid10 -L "$label" -U "$uuid" "${devices[@]}"
+    udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
+    btrfs filesystem show
+    helper_check_device_symlinks
+    helper_check_device_units
+
+    echo "Multiple devices: using LUKS encrypted disks, data: raid1, metadata: raid1, mixed mode"
+    uuid="deadbeef-dead-dead-beef-000000000003"
+    label="btrfs_mencdisk"
+    mpoint="/btrfs_enc$RANDOM"
+    mkdir "$mpoint"
+    # Create a key-file
+    dd if=/dev/urandom of=/etc/btrfs_keyfile bs=64 count=1 iflag=fullblock
+    chmod 0600 /etc/btrfs_keyfile
+    # Encrypt each device and add it to /etc/crypttab, so it can be mounted
+    # automagically later
+    : >/etc/crypttab
+    for ((i = 0; i < ${#devices[@]}; i++)); do
+        # Intentionally use weaker cipher-related settings, since we don't care
+        # about security here as it's a throwaway LUKS partition
+        cryptsetup luksFormat -q \
+            --use-urandom --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \
+            --uuid "deadbeef-dead-dead-beef-11111111111$i" --label "encdisk$i" "${devices[$i]}" /etc/btrfs_keyfile
+        udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/deadbeef-dead-dead-beef-11111111111$i" "/dev/disk/by-label/encdisk$i"
+        # Add the device into /etc/crypttab, reload systemd, and then activate
+        # the device so we can create a filesystem on it later
+        echo "encbtrfs$i UUID=deadbeef-dead-dead-beef-11111111111$i /etc/btrfs_keyfile luks" >>/etc/crypttab
+        systemctl daemon-reload
+        systemctl start "systemd-cryptsetup@encbtrfs$i"
+    done
+    helper_check_device_symlinks
+    helper_check_device_units
+    # Check if we have all necessary DM devices
+    ls -l /dev/mapper/encbtrfs{0..3}
+    # Create a multi-device btrfs filesystem on the LUKS devices
+    udevadm lock \
+            --device=/dev/mapper/encbtrfs0 \
+            --device=/dev/mapper/encbtrfs1 \
+            --device=/dev/mapper/encbtrfs2 \
+            --device=/dev/mapper/encbtrfs3 \
+            mkfs.btrfs -f -M -d raid1 -m raid1 -L "$label" -U "$uuid" /dev/mapper/encbtrfs{0..3}
+    udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
+    btrfs filesystem show
+    helper_check_device_symlinks
+    helper_check_device_units
+    # Mount it and write some data to it we can compare later
+    mount -t btrfs /dev/mapper/encbtrfs0 "$mpoint"
+    echo "hello there" >"$mpoint/test"
+    # "Deconstruct" the btrfs device and check if we're in a sane state (symlink-wise)
+    umount "$mpoint"
+    systemctl stop systemd-cryptsetup@encbtrfs{0..3}
+    udevadm wait --settle --timeout=30 --removed "/dev/disk/by-uuid/$uuid"
+    helper_check_device_symlinks
+    helper_check_device_units
+    # Add the mount point to /etc/fstab and check if the device can be put together
+    # automagically. The source device is the DM name of the first LUKS device
+    # (from /etc/crypttab). We have to specify all LUKS devices manually, as
+    # registering the necessary devices is usually initrd's job (via btrfs device scan)
+    dev_stub="/dev/mapper/encbtrfs"
+    echo "/dev/mapper/encbtrfs0 $mpoint btrfs device=${dev_stub}0,device=${dev_stub}1,device=${dev_stub}2,device=${dev_stub}3 0 2" >>/etc/fstab
+    # Tell systemd about the new mount
+    systemctl daemon-reload
+    # Restart cryptsetup.target to trigger autounlock of partitions in /etc/crypttab
+    systemctl restart cryptsetup.target
+    # Start the corresponding mount unit and check if the btrfs device was reconstructed
+    # correctly
+    systemctl start "${mpoint##*/}.mount"
+    udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
+    btrfs filesystem show
+    helper_check_device_symlinks
+    helper_check_device_units
+    grep "hello there" "$mpoint/test"
+    # Cleanup
+    systemctl stop "${mpoint##*/}.mount"
+    systemctl stop systemd-cryptsetup@encbtrfs{0..3}
+    sed -i "/${mpoint##*/}/d" /etc/fstab
+    : >/etc/crypttab
+    rm -fr "$mpoint"
+    systemctl daemon-reload
+    udevadm settle
+}
+
+testcase_iscsi_lvm() {
+    local dev i label link lun_id mpoint target_name uuid
+    local target_ip="127.0.0.1"
+    local target_port="3260"
+    local vgroup="iscsi_lvm$RANDOM"
+    local expected_symlinks=()
+    local devices=(
+        /dev/disk/by-id/ata-foobar_deadbeefiscsi{0..3}
+    )
+
+    ls -l "${devices[@]}"
+
+    # Start the target daemon
+    systemctl start tgtd
+    systemctl status tgtd
+
+    echo "iSCSI LUNs backed by devices"
+    # See RFC3721 and RFC7143
+    target_name="iqn.2021-09.com.example:iscsi.test"
+    # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each
+    # backed by a device
+    tgtadm --lld iscsi --op new --mode target --tid=1 --targetname "$target_name"
+    for ((i = 0; i < ${#devices[@]}; i++)); do
+        # lun-0 is reserved by iSCSI
+        lun_id="$((i + 1))"
+        tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun "$lun_id" -b "${devices[$i]}"
+        tgtadm --lld iscsi --op update --mode logicalunit --tid 1 --lun "$lun_id"
+        expected_symlinks+=(
+            "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$lun_id"
+        )
+    done
+    tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL
+    # Configure the iSCSI initiator
+    iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
+    iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
+    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+    helper_check_device_symlinks
+    helper_check_device_units
+    # Cleanup
+    iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
+    tgtadm --lld iscsi --op delete --mode target --tid=1
+
+    echo "iSCSI LUNs backed by files + LVM"
+    # Note: we use files here to "trick" LVM the disks are indeed on a different
+    #       host, so it doesn't automagically detect another path to the backing
+    #       device once we disconnect the iSCSI devices
+    target_name="iqn.2021-09.com.example:iscsi.lvm.test"
+    mpoint="$(mktemp -d /iscsi_storeXXX)"
+    expected_symlinks=()
+    # Use the first device as it's configured with larger capacity
+    mkfs.ext4 -L iscsi_store "${devices[0]}"
+    udevadm wait --settle --timeout=30 "${devices[0]}"
+    mount "${devices[0]}" "$mpoint"
+    for i in {1..4}; do
+        dd if=/dev/zero of="$mpoint/lun$i.img" bs=1M count=32
+    done
+    # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each
+    # backed by a file
+    tgtadm --lld iscsi --op new --mode target --tid=2 --targetname "$target_name"
+    # lun-0 is reserved by iSCSI
+    for i in {1..4}; do
+        tgtadm --lld iscsi --op new --mode logicalunit --tid 2 --lun "$i" -b "$mpoint/lun$i.img"
+        tgtadm --lld iscsi --op update --mode logicalunit --tid 2 --lun "$i"
+        expected_symlinks+=(
+            "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$i"
+        )
+    done
+    tgtadm --lld iscsi --op bind --mode target --tid 2 -I ALL
+    # Configure the iSCSI initiator
+    iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
+    iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
+    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+    helper_check_device_symlinks
+    helper_check_device_units
+    # Add all iSCSI devices into a LVM volume group, create two logical volumes,
+    # and check if necessary symlinks exist (and are valid)
+    lvm pvcreate -y "${expected_symlinks[@]}"
+    lvm pvs
+    lvm vgcreate "$vgroup" -y "${expected_symlinks[@]}"
+    lvm vgs
+    lvm vgchange -ay "$vgroup"
+    lvm lvcreate -y -L 4M "$vgroup" -n mypart1
+    lvm lvcreate -y -L 8M "$vgroup" -n mypart2
+    lvm lvs
+    udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
+    mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1"
+    udevadm wait --settle --timeout=30 "/dev/disk/by-label/mylvpart1"
+    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+    helper_check_device_units
+    # Disconnect the iSCSI devices and check all the symlinks
+    iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
+    # "Reset" the DM state, since we yanked the backing storage from under the LVM,
+    # so the currently active VGs/LVs are invalid
+    dmsetup remove_all --deferred
+    # The LVM and iSCSI related symlinks should be gone
+    udevadm wait --settle --timeout=30 --removed "/dev/$vgroup" "/dev/disk/by-label/mylvpart1" "${expected_symlinks[@]}"
+    helper_check_device_symlinks "/dev/disk"
+    helper_check_device_units
+    # Reconnect the iSCSI devices and check if everything get detected correctly
+    iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
+    iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
+    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1"
+    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
+    helper_check_device_units
+    # Cleanup
+    iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
+    tgtadm --lld iscsi --op delete --mode target --tid=2
+    umount "$mpoint"
+    rm -rf "$mpoint"
+}
+
+testcase_long_sysfs_path() {
+    local cursor link logfile mpoint
+    local expected_symlinks=(
+        "/dev/disk/by-label/data_vol"
+        "/dev/disk/by-label/swap_vol"
+        "/dev/disk/by-partlabel/test_swap"
+        "/dev/disk/by-partlabel/test_part"
+        "/dev/disk/by-partuuid/deadbeef-dead-dead-beef-000000000000"
+        "/dev/disk/by-uuid/deadbeef-dead-dead-beef-111111111111"
+        "/dev/disk/by-uuid/deadbeef-dead-dead-beef-222222222222"
+    )
+
+    # Create a cursor file to skip messages generated by udevd in initrd, as it
+    # might not be the same up-to-date version as we currently run (hence generating
+    # messages we check for later and making the test fail)
+    cursor="$(mktemp)"
+    journalctl --cursor-file="${cursor:?}" -n0 -q
+
+    # Make sure the test device is connected and show its "wonderful" path
+    stat /sys/block/vda
+    readlink -f /sys/block/vda/dev
+
+    dev="/dev/vda"
+    sfdisk "${dev:?}" <<EOF
+label: gpt
+
+name="test_swap", size=32M
+uuid="deadbeef-dead-dead-beef-000000000000", name="test_part", size=5M
+EOF
+    udevadm settle
+    mkswap -U "deadbeef-dead-dead-beef-111111111111" -L "swap_vol" "${dev}1"
+    mkfs.ext4 -U "deadbeef-dead-dead-beef-222222222222" -L "data_vol" "${dev}2"
+    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+
+    # Try to mount the data partition manually (using its label)
+    mpoint="$(mktemp -d /logsysfsXXX)"
+    mount LABEL=data_vol "$mpoint"
+    touch "$mpoint/test"
+    umount "$mpoint"
+    # Do the same, but with UUID and using fstab
+    echo "UUID=deadbeef-dead-dead-beef-222222222222 $mpoint ext4 defaults 0 0" >>/etc/fstab
+    systemctl daemon-reload
+    mount "$mpoint"
+    timeout 30 bash -c "until systemctl -q is-active '$mpoint'; do sleep .2; done"
+    test -e "$mpoint/test"
+    umount "$mpoint"
+
+    # Test out the swap partition
+    swapon -v -L swap_vol
+    swapoff -v -L swap_vol
+
+    udevadm settle
+
+    logfile="$(mktemp)"
+    # Check state of affairs after https://github.com/systemd/systemd/pull/22759
+    # Note: can't use `--cursor-file` here, since we don't want to update the cursor
+    #       after using it
+    [[ "$(journalctl --after-cursor="$(<"$cursor")" -q --no-pager -o short-monotonic -p info --grep "Device path.*vda.?' too long to fit into unit name" | wc -l)" -eq 0 ]]
+    [[ "$(journalctl --after-cursor="$(<"$cursor")" -q --no-pager -o short-monotonic --grep "Unit name .*vda.?\.device\" too long, falling back to hashed unit name" | wc -l)" -gt 0 ]]
+    # Check if the respective "hashed" units exist and are active (plugged)
+    systemctl status --no-pager "$(readlink -f /sys/block/vda/vda1)"
+    systemctl status --no-pager "$(readlink -f /sys/block/vda/vda2)"
+    # Make sure we don't unnecessarily spam the log
+    { journalctl -b -q --no-pager -o short-monotonic -p info --grep "/sys/devices/.+/vda[0-9]?" _PID=1 + UNIT=systemd-udevd.service || :;} | tee "$logfile"
+    [[ "$(wc -l <"$logfile")" -lt 10 ]]
+
+    : >/etc/fstab
+    rm -fr "${cursor:?}" "${logfile:?}" "${mpoint:?}"
+}
+
+testcase_mdadm_basic() {
+    local i part_name raid_name raid_dev uuid
+    local expected_symlinks=()
+    local devices=(
+        /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..4}
+    )
+
+    ls -l "${devices[@]}"
+
+    echo "Mirror raid (RAID 1)"
+    raid_name="mdmirror"
+    raid_dev="/dev/md/$raid_name"
+    part_name="${raid_name}_part"
+    uuid="aaaaaaaa:bbbbbbbb:cccccccc:00000001"
+    expected_symlinks=(
+        "$raid_dev"
+        "/dev/disk/by-id/md-name-H:$raid_name"
+        "/dev/disk/by-id/md-uuid-$uuid"
+        "/dev/disk/by-label/$part_name" # ext4 partition
+    )
+    # Create a simple RAID 1 with an ext4 filesystem
+    echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..1} -v -f --level=1 --raid-devices=2
+    udevadm wait --settle --timeout=30 "$raid_dev"
+    mkfs.ext4 -L "$part_name" "$raid_dev"
+    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+    for i in {0..9}; do
+        echo "Disassemble - reassemble loop, iteration #$i"
+        mdadm -v --stop "$raid_dev"
+        udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+        mdadm --assemble "$raid_dev" --name "$raid_name" -v
+        udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+    done
+    helper_check_device_symlinks
+    helper_check_device_units
+    # Cleanup
+    mdadm -v --stop "$raid_dev"
+    udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+
+    echo "Parity raid (RAID 5)"
+    raid_name="mdparity"
+    raid_dev="/dev/md/$raid_name"
+    part_name="${raid_name}_part"
+    uuid="aaaaaaaa:bbbbbbbb:cccccccc:00000101"
+    expected_symlinks=(
+        "$raid_dev"
+        "/dev/disk/by-id/md-name-H:$raid_name"
+        "/dev/disk/by-id/md-uuid-$uuid"
+        "/dev/disk/by-label/$part_name" # ext4 partition
+    )
+    # Create a simple RAID 5 with an ext4 filesystem
+    echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..2} -v -f --level=5 --raid-devices=3
+    udevadm wait --settle --timeout=30 "$raid_dev"
+    mkfs.ext4 -L "$part_name" "$raid_dev"
+    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+    for i in {0..9}; do
+        echo "Disassemble - reassemble loop, iteration #$i"
+        mdadm -v --stop "$raid_dev"
+        udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+        mdadm --assemble "$raid_dev" --name "$raid_name" -v
+        udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+    done
+    helper_check_device_symlinks
+    helper_check_device_units
+    # Cleanup
+    mdadm -v --stop "$raid_dev"
+    udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+    helper_check_device_units
+
+    echo "Mirror + parity raid (RAID 10) + multiple partitions"
+    raid_name="mdmirpar"
+    raid_dev="/dev/md/$raid_name"
+    part_name="${raid_name}_part"
+    uuid="aaaaaaaa:bbbbbbbb:cccccccc:00001010"
+    expected_symlinks=(
+        "$raid_dev"
+        "/dev/disk/by-id/md-name-H:$raid_name"
+        "/dev/disk/by-id/md-uuid-$uuid"
+        "/dev/disk/by-label/$part_name" # ext4 partition
+        # Partitions
+        "${raid_dev}1"
+        "${raid_dev}2"
+        "${raid_dev}3"
+        "/dev/disk/by-id/md-name-H:$raid_name-part1"
+        "/dev/disk/by-id/md-name-H:$raid_name-part2"
+        "/dev/disk/by-id/md-name-H:$raid_name-part3"
+        "/dev/disk/by-id/md-uuid-$uuid-part1"
+        "/dev/disk/by-id/md-uuid-$uuid-part2"
+        "/dev/disk/by-id/md-uuid-$uuid-part3"
+    )
+    # Create a simple RAID 10 with an ext4 filesystem
+    echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..3} -v -f --level=10 --raid-devices=4
+    udevadm wait --settle --timeout=30 "$raid_dev"
+    # Partition the raid device
+    # Here, 'udevadm lock' is meaningless, as udevd does not lock MD devices.
+    sfdisk --wipe=always "$raid_dev" <<EOF
+label: gpt
+
+uuid="deadbeef-dead-dead-beef-111111111111", name="mdpart1", size=8M
+uuid="deadbeef-dead-dead-beef-222222222222", name="mdpart2", size=32M
+uuid="deadbeef-dead-dead-beef-333333333333", name="mdpart3", size=16M
+EOF
+    udevadm wait --settle --timeout=30 "/dev/disk/by-id/md-uuid-$uuid-part2"
+    mkfs.ext4 -L "$part_name" "/dev/disk/by-id/md-uuid-$uuid-part2"
+    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+    for i in {0..9}; do
+        echo "Disassemble - reassemble loop, iteration #$i"
+        mdadm -v --stop "$raid_dev"
+        udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+        mdadm --assemble "$raid_dev" --name "$raid_name" -v
+        udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+    done
+    helper_check_device_symlinks
+    helper_check_device_units
+    # Cleanup
+    mdadm -v --stop "$raid_dev"
+    # Check if all expected symlinks were removed after the cleanup
+    udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+    helper_check_device_units
+}
+
+testcase_mdadm_lvm() {
+    local part_name raid_name raid_dev uuid vgroup
+    local expected_symlinks=()
+    local devices=(
+        /dev/disk/by-id/ata-foobar_deadbeefmdadmlvm{0..4}
+    )
+
+    ls -l "${devices[@]}"
+
+    raid_name="mdlvm"
+    raid_dev="/dev/md/$raid_name"
+    part_name="${raid_name}_part"
+    vgroup="${raid_name}_vg"
+    uuid="aaaaaaaa:bbbbbbbb:ffffffff:00001010"
+    expected_symlinks=(
+        "$raid_dev"
+        "/dev/$vgroup/mypart1"          # LVM partition
+        "/dev/$vgroup/mypart2"          # LVM partition
+        "/dev/disk/by-id/md-name-H:$raid_name"
+        "/dev/disk/by-id/md-uuid-$uuid"
+        "/dev/disk/by-label/$part_name" # ext4 partition
+    )
+    # Create a RAID 10 with LVM + ext4
+    echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadmlvm{0..3} -v -f --level=10 --raid-devices=4
+    udevadm wait --settle --timeout=30 "$raid_dev"
+    # Create an LVM on the MD
+    lvm pvcreate -y "$raid_dev"
+    lvm pvs
+    lvm vgcreate "$vgroup" -y "$raid_dev"
+    lvm vgs
+    lvm vgchange -ay "$vgroup"
+    lvm lvcreate -y -L 4M "$vgroup" -n mypart1
+    lvm lvcreate -y -L 8M "$vgroup" -n mypart2
+    lvm lvs
+    udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
+    mkfs.ext4 -L "$part_name" "/dev/$vgroup/mypart2"
+    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+    # Disassemble the array
+    lvm vgchange -an "$vgroup"
+    mdadm -v --stop "$raid_dev"
+    udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+    helper_check_device_symlinks
+    helper_check_device_units
+    # Reassemble it and check if all required symlinks exist
+    mdadm --assemble "$raid_dev" --name "$raid_name" -v
+    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
+    helper_check_device_symlinks
+    helper_check_device_units
+    # Cleanup
+    lvm vgchange -an "$vgroup"
+    mdadm -v --stop "$raid_dev"
+    # Check if all expected symlinks were removed after the cleanup
+    udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
+    helper_check_device_units
+}
+
+udevadm settle
+udevadm control --log-level debug
+lsblk -a
+
+echo "Check if all symlinks under /dev/disk/ are valid (pre-test)"
+helper_check_device_symlinks
+
+# TEST_FUNCTION_NAME is passed on the kernel command line via systemd.setenv=
+# in the respective test.sh file
+if ! command -v "${TEST_FUNCTION_NAME:?}"; then
+    echo >&2 "Missing verification handler for test case '$TEST_FUNCTION_NAME'"
+    exit 1
+fi
+
+echo "TEST_FUNCTION_NAME=$TEST_FUNCTION_NAME"
+"$TEST_FUNCTION_NAME"
+udevadm settle
+
+echo "Check if all symlinks under /dev/disk/ are valid (post-test)"
+helper_check_device_symlinks
+
+udevadm control --log-level info
+
+systemctl status systemd-udevd
+
+touch /testok
diff --git a/test/units/TEST-65-ANALYZE.service b/test/units/TEST-65-ANALYZE.service
new file mode 100644 (file)
index 0000000..3610baf
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-65-ANALYZE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-65-ANALYZE.sh b/test/units/TEST-65-ANALYZE.sh
new file mode 100755 (executable)
index 0000000..59dfd4f
--- /dev/null
@@ -0,0 +1,947 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemctl log-level debug
+export SYSTEMD_LOG_LEVEL=debug
+
+# Sanity checks
+#
+# We can't really test time, critical-chain and plot verbs here, as
+# the testsuite service is a part of the boot transaction, so let's assume
+# they fail
+systemd-analyze || :
+systemd-analyze time || :
+systemd-analyze critical-chain || :
+# blame
+systemd-analyze blame
+systemd-run --wait --user --pipe -M testuser@.host systemd-analyze blame
+(! systemd-analyze blame --global)
+# plot
+systemd-analyze plot >/dev/null || :
+systemd-analyze plot --json=pretty >/dev/null || :
+systemd-analyze plot --json=short >/dev/null || :
+systemd-analyze plot --json=off >/dev/null || :
+systemd-analyze plot --json=pretty --no-legend >/dev/null || :
+systemd-analyze plot --json=short --no-legend >/dev/null || :
+systemd-analyze plot --json=off --no-legend >/dev/null || :
+systemd-analyze plot --table >/dev/null || :
+systemd-analyze plot --table --no-legend >/dev/null || :
+(! systemd-analyze plot --global)
+# legacy/deprecated options (moved to systemctl, but still usable from analyze)
+systemd-analyze log-level
+systemd-analyze log-level "$(systemctl log-level)"
+systemd-analyze get-log-level
+systemd-analyze set-log-level "$(systemctl log-level)"
+systemd-analyze log-target
+systemd-analyze log-target "$(systemctl log-target)"
+systemd-analyze get-log-target
+systemd-analyze set-log-target "$(systemctl log-target)"
+systemd-analyze service-watchdogs
+systemd-analyze service-watchdogs "$(systemctl service-watchdogs)"
+# dot
+systemd-analyze dot >/dev/null
+systemd-analyze dot systemd-journald.service >/dev/null
+systemd-analyze dot systemd-journald.service systemd-logind.service >/dev/null
+systemd-analyze dot --from-pattern="*" --from-pattern="*.service" systemd-journald.service >/dev/null
+systemd-analyze dot --to-pattern="*" --to-pattern="*.service" systemd-journald.service >/dev/null
+systemd-analyze dot --from-pattern="*.service" --to-pattern="*.service" systemd-journald.service >/dev/null
+systemd-analyze dot --order systemd-journald.service systemd-logind.service >/dev/null
+systemd-analyze dot --require systemd-journald.service systemd-logind.service >/dev/null
+systemd-analyze dot "systemd-*.service" >/dev/null
+(! systemd-analyze dot systemd-journald.service systemd-logind.service "*" bbb ccc)
+(! systemd-analyze dot --global systemd-journald.service)
+# dump
+# this should be rate limited to 10 calls in 10 minutes for unprivileged callers
+for _ in {1..10}; do
+    runas testuser systemd-analyze dump systemd-journald.service >/dev/null
+done
+(! runas testuser systemd-analyze dump >/dev/null)
+# still limited after a reload
+systemctl daemon-reload
+(! runas testuser systemd-analyze dump >/dev/null)
+# and a re-exec
+systemctl daemon-reexec
+(! runas testuser systemd-analyze dump >/dev/null)
+# privileged call, so should not be rate limited
+for _ in {1..10}; do
+    systemd-analyze dump systemd-journald.service >/dev/null
+done
+systemd-analyze dump >/dev/null
+systemd-analyze dump "*" >/dev/null
+systemd-analyze dump "*.socket" >/dev/null
+systemd-analyze dump "*.socket" "*.service" aaaaaaa ... >/dev/null
+systemd-analyze dump systemd-journald.service >/dev/null
+(! systemd-analyze dump "")
+(! systemd-analyze dump --global systemd-journald.service)
+# malloc
+systemd-analyze malloc >/dev/null
+(! systemd-analyze malloc --global)
+# unit-files
+systemd-analyze unit-files >/dev/null
+systemd-analyze unit-files systemd-journald.service >/dev/null
+systemd-analyze unit-files "*" >/dev/null
+systemd-analyze unit-files "*" aaaaaa "*.service" "*.target" >/dev/null
+systemd-analyze unit-files --user >/dev/null
+systemd-analyze unit-files --user "*" aaaaaa "*.service" "*.target" >/dev/null
+(! systemd-analyze unit-files --global)
+# unit-paths
+systemd-analyze unit-paths
+systemd-analyze unit-paths --user
+systemd-analyze unit-paths --global
+# exist-status
+systemd-analyze exit-status
+systemd-analyze exit-status STDOUT BPF
+systemd-analyze exit-status 0 1 {63..65}
+(! systemd-analyze exit-status STDOUT BPF "hello*")
+(! systemd-analyze exit-status --global)
+# capability
+systemd-analyze capability
+systemd-analyze capability cap_chown CAP_KILL
+systemd-analyze capability 0 1 {30..32}
+(! systemd-analyze capability cap_chown CAP_KILL "hello*")
+(! systemd-analyze capability --global)
+# condition
+mkdir -p /run/systemd/system
+UNIT_NAME="analyze-condition-$RANDOM.service"
+cat >"/run/systemd/system/$UNIT_NAME" <<EOF
+[Unit]
+AssertPathExists=/etc/os-release
+AssertEnvironment=!FOOBAR
+ConditionKernelVersion=>1.0
+ConditionPathExists=/etc/os-release
+
+[Service]
+ExecStart=true
+EOF
+systemctl daemon-reload
+systemd-analyze condition --unit="$UNIT_NAME"
+systemd-analyze condition 'ConditionKernelVersion = ! <4.0' \
+                          'ConditionKernelVersion = >=3.1' \
+                          'ConditionACPower=|false' \
+                          'ConditionArchitecture=|!arm' \
+                          'AssertPathExists=/etc/os-release'
+(! systemd-analyze condition 'ConditionArchitecture=|!arm' 'AssertXYZ=foo')
+(! systemd-analyze condition 'ConditionKernelVersion=<1.0')
+(! systemd-analyze condition 'AssertKernelVersion=<1.0')
+(! systemd-analyze condition --global 'ConditionKernelVersion = ! <4.0')
+# syscall-filter
+systemd-analyze syscall-filter >/dev/null
+systemd-analyze syscall-filter @chown @sync
+systemd-analyze syscall-filter @sync @sync @sync
+(! systemd-analyze syscall-filter @chown @sync @foobar)
+(! systemd-analyze syscall-filter --global)
+# filesystems (requires libbpf support)
+if systemctl --version | grep "+BPF_FRAMEWORK"; then
+    systemd-analyze filesystems >/dev/null
+    systemd-analyze filesystems @basic-api
+    systemd-analyze filesystems @basic-api @basic-api @basic-api
+    (! systemd-analyze filesystems @basic-api @basic-api @foobar @basic-api)
+    (! systemd-analyze filesystems --global @basic-api)
+fi
+# calendar
+systemd-analyze calendar '*-2-29 0:0:0'
+systemd-analyze calendar --iterations=5 '*-2-29 0:0:0'
+systemd-analyze calendar '*-* *:*:*'
+systemd-analyze calendar --iterations=5 '*-* *:*:*'
+systemd-analyze calendar --iterations=50 '*-* *:*:*'
+systemd-analyze calendar --iterations=0 '*-* *:*:*'
+systemd-analyze calendar --iterations=5 '01-01-22 01:00:00'
+systemd-analyze calendar --base-time=yesterday --iterations=5 '*-* *:*:*'
+(! systemd-analyze calendar --iterations=0 '*-* 99:*:*')
+(! systemd-analyze calendar --base-time=never '*-* *:*:*')
+(! systemd-analyze calendar 1)
+(! systemd-analyze calendar "")
+(! systemd-analyze calendar --global '*-2-29 0:0:0')
+# timestamp
+systemd-analyze timestamp now
+systemd-analyze timestamp -- -1
+systemd-analyze timestamp yesterday now tomorrow
+(! systemd-analyze timestamp yesterday never tomorrow)
+(! systemd-analyze timestamp 1)
+(! systemd-analyze timestamp '*-2-29 0:0:0')
+(! systemd-analyze timestamp "")
+(! systemd-analyze timestamp --global now)
+# timespan
+systemd-analyze timespan 1
+systemd-analyze timespan 1s 300s '1year 0.000001s'
+(! systemd-analyze timespan 1s 300s aaaaaa '1year 0.000001s')
+(! systemd-analyze timespan -- -1)
+(! systemd-analyze timespan '*-2-29 0:0:0')
+(! systemd-analyze timespan "")
+(! systemd-analyze timespan --global 1)
+# cat-config
+systemd-analyze cat-config systemd/system.conf >/dev/null
+systemd-analyze cat-config /etc/systemd/system.conf >/dev/null
+systemd-analyze cat-config systemd/system.conf systemd/journald.conf >/dev/null
+systemd-analyze cat-config systemd/system.conf foo/bar systemd/journald.conf >/dev/null
+systemd-analyze cat-config foo/bar
+systemd-analyze cat-config --tldr systemd/system.conf >/dev/null
+systemd-analyze cat-config --tldr /etc/systemd/system.conf >/dev/null
+systemd-analyze cat-config --tldr systemd/system.conf systemd/journald.conf >/dev/null
+systemd-analyze cat-config --tldr systemd/system.conf foo/bar systemd/journald.conf >/dev/null
+systemd-analyze cat-config --tldr foo/bar
+(! systemd-analyze cat-config --global systemd/system.conf)
+# security
+systemd-analyze security
+systemd-analyze security --json=off
+systemd-analyze security --json=pretty | jq
+systemd-analyze security --json=short | jq
+(! systemd-analyze security --global)
+
+if [[ ! -v ASAN_OPTIONS ]]; then
+    # check that systemd-analyze cat-config paths work in a chroot
+    mkdir -p /tmp/root
+    mount --bind / /tmp/root
+    if mountpoint -q /usr; then
+        mount --bind /usr /tmp/root/usr
+    fi
+    systemd-analyze cat-config systemd/system-preset >/tmp/out1
+    chroot /tmp/root systemd-analyze cat-config systemd/system-preset >/tmp/out2
+    diff /tmp/out{1,2}
+fi
+
+# verify
+mkdir -p /tmp/img/usr/lib/systemd/system/
+mkdir -p /tmp/img/opt/
+
+touch /tmp/img/opt/script0.sh
+chmod +x /tmp/img/opt/script0.sh
+
+cat <<EOF >/tmp/img/usr/lib/systemd/system/testfile.service
+[Service]
+ExecStart = /opt/script0.sh
+EOF
+
+set +e
+# Default behaviour is to recurse through all dependencies when unit is loaded
+(! systemd-analyze verify --root=/tmp/img/ testfile.service)
+
+# As above, recurses through all dependencies when unit is loaded
+(! systemd-analyze verify --recursive-errors=yes --root=/tmp/img/ testfile.service)
+
+# Recurses through unit file and its direct dependencies when unit is loaded
+(! systemd-analyze verify --recursive-errors=one --root=/tmp/img/ testfile.service)
+
+set -e
+
+# zero exit status since dependencies are ignored when unit is loaded
+systemd-analyze verify --recursive-errors=no --root=/tmp/img/ testfile.service
+
+rm /tmp/img/usr/lib/systemd/system/testfile.service
+
+cat <<EOF >/tmp/testfile.service
+[Unit]
+foo = bar
+
+[Service]
+ExecStart = echo hello
+EOF
+
+cat <<EOF >/tmp/testfile2.service
+[Unit]
+Requires = testfile.service
+
+[Service]
+ExecStart = echo hello
+EOF
+
+# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded
+systemd-analyze verify --recursive-errors=no /tmp/testfile2.service
+
+set +e
+# Non-zero exit status since all associated dependencies are recursively loaded when the unit file is loaded
+(! systemd-analyze verify --recursive-errors=yes /tmp/testfile2.service)
+set -e
+
+rm /tmp/testfile.service
+rm /tmp/testfile2.service
+
+cat <<EOF >/tmp/sample.service
+[Unit]
+Description = A Sample Service
+
+[Service]
+ExecStart = echo hello
+Slice=support.slice
+EOF
+
+# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded
+systemd-analyze verify --recursive-errors=no /tmp/sample.service
+
+cat <<EOF >/tmp/testfile.service
+[Service]
+ExecStart = echo hello
+DeviceAllow=/dev/sda
+EOF
+
+# Prevent regression from #13380 and #20859 where we can't verify hidden files
+cp /tmp/testfile.service /tmp/.testfile.service
+
+systemd-analyze verify /tmp/.testfile.service
+
+rm /tmp/.testfile.service
+
+# Alias a unit file's name on disk (see #20061)
+cp /tmp/testfile.service /tmp/testsrvc
+
+(! systemd-analyze verify /tmp/testsrvc)
+
+systemd-analyze verify /tmp/testsrvc:alias.service
+
+# Zero exit status since the value used for comparison determine exposure to security threats is by default 100
+systemd-analyze security --offline=true /tmp/testfile.service
+
+#The overall exposure level assigned to the unit is greater than the set threshold
+(! systemd-analyze security --threshold=90 --offline=true /tmp/testfile.service)
+
+# Ensure we print the list of ACLs, see https://github.com/systemd/systemd/issues/23185
+systemd-analyze security --offline=true /tmp/testfile.service | grep -q -F "/dev/sda"
+
+rm /tmp/testfile.service
+
+cat <<EOF >/tmp/img/usr/lib/systemd/system/testfile.service
+[Service]
+ExecStart = echo hello
+PrivateNetwork = yes
+PrivateDevices = yes
+PrivateUsers = yes
+EOF
+
+# The new overall exposure level assigned to the unit is less than the set thresholds
+# Verifies that the --offline= option works with --root=
+systemd-analyze security --threshold=90 --offline=true --root=/tmp/img/ testfile.service
+
+cat <<EOF >/tmp/foo@.service
+[Service]
+ExecStart=ls
+EOF
+
+cat <<EOF >/tmp/hoge@test.service
+[Service]
+ExecStart=ls
+EOF
+
+# issue #30357
+pushd /tmp
+systemd-analyze verify foo@bar.service
+systemd-analyze verify foo@.service
+systemd-analyze verify hoge@test.service
+(! systemd-analyze verify hoge@nonexist.service)
+(! systemd-analyze verify hoge@.service)
+popd
+pushd /
+systemd-analyze verify tmp/foo@bar.service
+systemd-analyze verify tmp/foo@.service
+systemd-analyze verify tmp/hoge@test.service
+(! systemd-analyze verify tmp/hoge@nonexist.service)
+(! systemd-analyze verify tmp/hoge@.service)
+popd
+pushd /usr
+systemd-analyze verify ../tmp/foo@bar.service
+systemd-analyze verify ../tmp/foo@.service
+systemd-analyze verify ../tmp/hoge@test.service
+(! systemd-analyze verify ../tmp/hoge@nonexist.service)
+(! systemd-analyze verify ../tmp/hoge@.service)
+popd
+systemd-analyze verify /tmp/foo@bar.service
+systemd-analyze verify /tmp/foo@.service
+systemd-analyze verify /tmp/hoge@test.service
+(! systemd-analyze verify /tmp/hoge@nonexist.service)
+(! systemd-analyze verify /tmp/hoge@.service)
+
+# test that all commands are verified.
+cat <<EOF >/tmp/multi-exec-start.service
+[Service]
+Type=oneshot
+ExecStart=true
+ExecStart=ls
+EOF
+systemd-analyze verify /tmp/multi-exec-start.service
+echo 'ExecStart=command-should-not-exist' >>/tmp/multi-exec-start.service
+(! systemd-analyze verify /tmp/multi-exec-start.service)
+
+# Added an additional "INVALID_ID" id to the .json to verify that nothing breaks when input is malformed
+# The PrivateNetwork id description and weight was changed to verify that 'security' is actually reading in
+# values from the .json file when required. The default weight for "PrivateNetwork" is 2500, and the new weight
+# assigned to that id in the .json file is 6000. This increased weight means that when the "PrivateNetwork" key is
+# set to 'yes' (as above in the case of testfile.service) in the content of the unit file, the overall exposure
+# level for the unit file should decrease to account for that increased weight.
+cat <<EOF >/tmp/testfile.json
+{"UserOrDynamicUser":
+    {"description_bad": "Service runs as root user",
+    "weight": 0,
+    "range": 10
+    },
+"SupplementaryGroups":
+    {"description_good": "Service has no supplementary groups",
+    "description_bad": "Service runs with supplementary groups",
+    "description_na": "Service runs as root, option does not matter",
+    "weight": 200,
+    "range": 1
+    },
+"PrivateDevices":
+    {"description_good": "Service has no access to hardware devices",
+    "description_bad": "Service potentially has access to hardware devices",
+    "weight": 1000,
+    "range": 1
+    },
+"PrivateMounts":
+    {"description_good": "Service cannot install system mounts",
+    "description_bad": "Service may install system mounts",
+    "weight": 1000,
+    "range": 1
+    },
+"PrivateNetwork":
+    {"description_good": "Service doesn't have access to the host's network",
+    "description_bad": "Service has access to the host's network",
+    "weight": 6000,
+    "range": 1
+    },
+"PrivateTmp":
+    {"description_good": "Service has no access to other software's temporary files",
+    "description_bad": "Service has access to other software's temporary files",
+    "weight": 1000,
+    "range": 1
+    },
+"PrivateUsers":
+    {"description_good": "Service does not have access to other users",
+    "description_bad": "Service has access to other users",
+    "weight": 1000,
+    "range": 1
+    },
+"ProtectControlGroups":
+    {"description_good": "Service cannot modify the control group file system",
+    "description_bad": "Service may modify the control group file system",
+    "weight": 1000,
+    "range": 1
+    },
+"ProtectKernelModules":
+    {"description_good": "Service cannot load or read kernel modules",
+    "description_bad": "Service may load or read kernel modules",
+    "weight": 1000,
+    "range": 1
+    },
+"ProtectKernelTunables":
+    {"description_good": "Service cannot alter kernel tunables (/proc/sys, …)",
+    "description_bad": "Service may alter kernel tunables",
+    "weight": 1000,
+    "range": 1
+    },
+"ProtectKernelLogs":
+    {"description_good": "Service cannot read from or write to the kernel log ring buffer",
+    "description_bad": "Service may read from or write to the kernel log ring buffer",
+    "weight": 1000,
+    "range": 1
+    },
+"ProtectClock":
+    {"description_good": "Service cannot write to the hardware clock or system clock",
+    "description_bad": "Service may write to the hardware clock or system clock",
+    "weight": 1000,
+    "range": 1
+    },
+"ProtectHome":
+    {"weight": 1000,
+    "range": 10
+    },
+"ProtectHostname":
+    {"description_good": "Service cannot change system host/domainname",
+    "description_bad": "Service may change system host/domainname",
+    "weight": 50,
+    "range": 1
+    },
+"ProtectSystem":
+    {"weight": 1000,
+    "range": 10
+    },
+"RootDirectoryOrRootImage":
+    {"description_good": "Service has its own root directory/image",
+    "description_bad": "Service runs within the host's root directory",
+    "weight": 200,
+    "range": 1
+    },
+"LockPersonality":
+    {"description_good": "Service cannot change ABI personality",
+    "description_bad": "Service may change ABI personality",
+    "weight": 100,
+    "range": 1
+    },
+"MemoryDenyWriteExecute":
+    {"description_good": "Service cannot create writable executable memory mappings",
+    "description_bad": "Service may create writable executable memory mappings",
+    "weight": 100,
+    "range": 1
+    },
+"NoNewPrivileges":
+    {"description_good": "Service processes cannot acquire new privileges",
+    "description_bad": "Service processes may acquire new privileges",
+    "weight": 1000,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_SYS_ADMIN":
+    {"description_good": "Service has no administrator privileges",
+    "description_bad": "Service has administrator privileges",
+    "weight": 1500,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_SET_UID_GID_PCAP":
+    {"description_good": "Service cannot change UID/GID identities/capabilities",
+    "description_bad": "Service may change UID/GID identities/capabilities",
+    "weight": 1500,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_SYS_PTRACE":
+    {"description_good": "Service has no ptrace() debugging abilities",
+    "description_bad": "Service has ptrace() debugging abilities",
+    "weight": 1500,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_SYS_TIME":
+    {"description_good": "Service processes cannot change the system clock",
+    "description_bad": "Service processes may change the system clock",
+    "weight": 1000,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_NET_ADMIN":
+    {"description_good": "Service has no network configuration privileges",
+    "description_bad": "Service has network configuration privileges",
+    "weight": 1000,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_SYS_RAWIO":
+    {"description_good": "Service has no raw I/O access",
+    "description_bad": "Service has raw I/O access",
+    "weight": 1000,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_SYS_MODULE":
+    {"description_good": "Service cannot load kernel modules",
+    "description_bad": "Service may load kernel modules",
+    "weight": 1000,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_AUDIT":
+    {"description_good": "Service has no audit subsystem access",
+    "description_bad": "Service has audit subsystem access",
+    "weight": 500,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_SYSLOG":
+    {"description_good": "Service has no access to kernel logging",
+    "description_bad": "Service has access to kernel logging",
+    "weight": 500,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE":
+    {"description_good": "Service has no privileges to change resource use parameters",
+    "description_bad": "Service has privileges to change resource use parameters",
+    "weight": 500,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_MKNOD":
+    {"description_good": "Service cannot create device nodes",
+    "description_bad": "Service may create device nodes",
+    "weight": 500,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP":
+    {"description_good": "Service cannot change file ownership/access mode/capabilities",
+    "description_bad": "Service may change file ownership/access mode/capabilities unrestricted",
+    "weight": 1000,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER":
+    {"description_good": "Service cannot override UNIX file/IPC permission checks",
+    "description_bad": "Service may override UNIX file/IPC permission checks",
+    "weight": 1000,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_KILL":
+    {"description_good": "Service cannot send UNIX signals to arbitrary processes",
+    "description_bad": "Service may send UNIX signals to arbitrary processes",
+    "weight": 500,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW":
+    {"description_good": "Service has no elevated networking privileges",
+    "description_bad": "Service has elevated networking privileges",
+    "weight": 500,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_SYS_BOOT":
+    {"description_good": "Service cannot issue reboot()",
+    "description_bad": "Service may issue reboot()",
+    "weight": 100,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_MAC":
+    {"description_good": "Service cannot adjust SMACK MAC",
+    "description_bad": "Service may adjust SMACK MAC",
+    "weight": 100,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_LINUX_IMMUTABLE":
+    {"description_good": "Service cannot mark files immutable",
+    "description_bad": "Service may mark files immutable",
+    "weight": 75,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_IPC_LOCK":
+    {"description_good": "Service cannot lock memory into RAM",
+    "description_bad": "Service may lock memory into RAM",
+    "weight": 50,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_SYS_CHROOT":
+    {"description_good": "Service cannot issue chroot()",
+    "description_bad": "Service may issue chroot()",
+    "weight": 50,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_BLOCK_SUSPEND":
+    {"description_good": "Service cannot establish wake locks",
+    "description_bad": "Service may establish wake locks",
+    "weight": 25,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_WAKE_ALARM":
+    {"description_good": "Service cannot program timers that wake up the system",
+    "description_bad": "Service may program timers that wake up the system",
+    "weight": 25,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_LEASE":
+    {"description_good": "Service cannot create file leases",
+    "description_bad": "Service may create file leases",
+    "weight": 25,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_SYS_TTY_CONFIG":
+    {"description_good": "Service cannot issue vhangup()",
+    "description_bad": "Service may issue vhangup()",
+    "weight": 25,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_SYS_PACCT":
+    {"description_good": "Service cannot use acct()",
+    "description_bad": "Service may use acct()",
+    "weight": 25,
+    "range": 1
+    },
+"CapabilityBoundingSet_CAP_BPF":
+    {"description_good": "Service may load BPF programs",
+    "description_bad": "Service may not load BPF programs",
+    "weight": 25,
+    "range": 1
+    },
+"UMask":
+    {"weight": 100,
+    "range": 10
+    },
+"KeyringMode":
+    {"description_good": "Service doesn't share key material with other services",
+    "description_bad": "Service shares key material with other service",
+    "weight": 1000,
+    "range": 1
+    },
+"ProtectProc":
+    {"description_good": "Service has restricted access to process tree(/proc hidepid=)",
+    "description_bad": "Service has full access to process tree(/proc hidepid=)",
+    "weight": 1000,
+    "range": 3
+    },
+"ProcSubset":
+    {"description_good": "Service has no access to non-process/proc files(/proc subset=)",
+    "description_bad": "Service has full access to non-process/proc files(/proc subset=)",
+    "weight": 10,
+    "range": 1
+    },
+"NotifyAccess":
+    {"description_good": "Service child processes cannot alter service state",
+    "description_bad": "Service child processes may alter service state",
+    "weight": 1000,
+    "range": 1
+    },
+"RemoveIPC":
+    {"description_good": "Service user cannot leave SysV IPC objects around",
+    "description_bad": "Service user may leave SysV IPC objects around",
+    "description_na": "Service runs as root, option does not apply",
+    "weight": 100,
+    "range": 1
+    },
+"Delegate":
+    {"description_good": "Service does not maintain its own delegated control group subtree",
+    "description_bad": "Service maintains its own delegated control group subtree",
+    "weight": 100,
+    "range": 1
+    },
+"RestrictRealtime":
+    {"description_good": "Service realtime scheduling access is restricted",
+    "description_bad": "Service may acquire realtime scheduling",
+    "weight": 500,
+    "range": 1
+    },
+"RestrictSUIDSGID":
+    {"description_good": "SUID/SGIDfilecreationbyserviceisrestricted",
+    "description_bad": "ServicemaycreateSUID/SGIDfiles",
+    "weight": 1000,
+    "range": 1
+    },
+"RestrictNamespaces_user":
+    {"description_good": "Servicecannotcreateusernamespaces",
+    "description_bad": "Servicemaycreateusernamespaces",
+    "weight": 1500,
+    "range": 1
+    },
+"RestrictNamespaces_mnt":
+    {"description_good": "Service cannot create file system namespaces",
+    "description_bad": "Service may create file system namespaces",
+    "weight": 500,
+    "range": 1
+    },
+"RestrictNamespaces_ipc":
+    {"description_good": "Service cannot create IPC namespaces",
+    "description_bad": "Service may create IPC namespaces",
+    "weight": 500,
+    "range": 1
+    },
+"RestrictNamespaces_pid":
+    {"description_good": "Service cannot create process namespaces",
+    "description_bad": "Service may create process namespaces",
+    "weight": 500,
+    "range": 1
+    },
+"RestrictNamespaces_cgroup":
+    {"description_good": "Service cannot create cgroup namespaces",
+    "description_bad": "Service may create cgroup namespaces",
+    "weight": 500,
+    "range": 1
+    },
+"RestrictNamespaces_net":
+    {"description_good": "Service cannot create network namespaces",
+    "description_bad": "Service may create network namespaces",
+    "weight": 500,
+    "range": 1
+    },
+"RestrictNamespaces_uts":
+    {"description_good": "Service cannot create hostname namespaces",
+    "description_bad": "Service may create hostname namespaces",
+    "weight": 100,
+    "range": 1
+    },
+"RestrictAddressFamilies_AF_INET_INET6":
+    {"description_good": "Service cannot allocate Internet sockets",
+    "description_bad": "Service may allocate Internet sockets",
+    "weight": 1500,
+    "range": 1
+    },
+"RestrictAddressFamilies_AF_UNIX":
+    {"description_good": "Service cannot allocate local sockets",
+    "description_bad": "Service may allocate local sockets",
+    "weight": 25,
+    "range": 1
+    },
+"RestrictAddressFamilies_AF_NETLINK":
+    {"description_good": "Service cannot allocate netlink sockets",
+    "description_bad": "Service may allocate netlink sockets",
+    "weight": 200,
+    "range": 1
+    },
+"RestrictAddressFamilies_AF_PACKET":
+    {"description_good": "Service cannot allocate packet sockets",
+    "description_bad": "Service may allocate packet sockets",
+    "weight": 1000,
+    "range": 1
+    },
+"RestrictAddressFamilies_OTHER":
+    {"description_good": "Service cannot allocate exotic sockets",
+    "description_bad": "Service may allocate exotic sockets",
+    "weight": 1250,
+    "range": 1
+    },
+"SystemCallArchitectures":
+    {"weight": 1000,
+    "range": 10
+    },
+"SystemCallFilter_swap":
+    {"weight": 1000,
+    "range": 10
+    },
+"SystemCallFilter_obsolete":
+    {"weight": 250,
+    "range": 10
+    },
+"SystemCallFilter_clock":
+    {"weight": 1000,
+    "range": 10
+    },
+"SystemCallFilter_cpu_emulation":
+    {"weight": 250,
+    "range": 10
+    },
+"SystemCallFilter_debug":
+    {"weight": 1000,
+    "range": 10
+    },
+"SystemCallFilter_mount":
+    {"weight": 1000,
+    "range": 10
+    },
+"SystemCallFilter_module":
+    {"weight": 1000,
+    "range": 10
+    },
+"SystemCallFilter_raw_io":
+    {"weight": 1000,
+    "range": 10
+    },
+"SystemCallFilter_reboot":
+    {"weight": 1000,
+    "range": 10
+    },
+"SystemCallFilter_privileged":
+    {"weight": 700,
+    "range": 10
+    },
+"SystemCallFilter_resources":
+    {"weight": 700,
+    "range": 10
+    },
+"IPAddressDeny":
+    {"weight": 1000,
+    "range": 10
+    },
+"DeviceAllow":
+    {"weight": 1000,
+    "range": 10
+    },
+"AmbientCapabilities":
+    {"description_good": "Service process does not receive ambient capabilities",
+    "description_bad": "Service process receives ambient capabilities",
+    "weight": 500,
+    "range": 1
+    },
+"INVALID_ID":
+    {"weight": 1000,
+    "range": 10
+    }
+}
+EOF
+
+# Reads in custom security requirements from the parsed .json file and uses these for comparison
+systemd-analyze security --threshold=90 --offline=true \
+                           --security-policy=/tmp/testfile.json \
+                           --root=/tmp/img/ testfile.service
+
+# The strict profile adds a lot of sanboxing options
+systemd-analyze security --threshold=25 --offline=true \
+                           --security-policy=/tmp/testfile.json \
+                           --profile=strict \
+                           --root=/tmp/img/ testfile.service
+
+# The trusted profile doesn't add any sanboxing options
+(! systemd-analyze security --threshold=25 --offline=true \
+                           --security-policy=/tmp/testfile.json \
+                           --profile=/usr/lib/systemd/portable/profile/trusted/service.conf \
+                           --root=/tmp/img/ testfile.service)
+
+(! systemd-analyze security --threshold=50 --offline=true \
+                           --security-policy=/tmp/testfile.json \
+                           --root=/tmp/img/ testfile.service)
+
+rm /tmp/img/usr/lib/systemd/system/testfile.service
+
+if systemd-analyze --version | grep -q -F "+ELFUTILS"; then
+    systemd-analyze inspect-elf --json=short /lib/systemd/systemd | grep -q -F '"elfType":"executable"'
+fi
+
+systemd-analyze --threshold=90 security systemd-journald.service
+
+# issue 23663
+check() {(
+    set +x
+    output=$(systemd-analyze security --offline="${2?}" "${3?}" | grep -F 'SystemCallFilter=')
+    assert_in "System call ${1?} list" "$output"
+    assert_in "[+✓] SystemCallFilter=~@swap" "$output"
+    assert_in "[+✓] SystemCallFilter=~@resources" "$output"
+    assert_in "[+✓] SystemCallFilter=~@reboot" "$output"
+    assert_in "[+✓] SystemCallFilter=~@raw-io" "$output"
+    assert_in "[-✗] SystemCallFilter=~@privileged" "$output"
+    assert_in "[+✓] SystemCallFilter=~@obsolete" "$output"
+    assert_in "[+✓] SystemCallFilter=~@mount" "$output"
+    assert_in "[+✓] SystemCallFilter=~@module" "$output"
+    assert_in "[+✓] SystemCallFilter=~@debug" "$output"
+    assert_in "[+✓] SystemCallFilter=~@cpu-emulation" "$output"
+    assert_in "[-✗] SystemCallFilter=~@clock" "$output"
+)}
+
+export -n SYSTEMD_LOG_LEVEL
+
+mkdir -p /run/systemd/system
+cat >/run/systemd/system/allow-list.service <<EOF
+[Service]
+ExecStart=false
+SystemCallFilter=@system-service
+SystemCallFilter=~@resources:ENOANO @privileged
+SystemCallFilter=@clock
+EOF
+
+cat >/run/systemd/system/deny-list.service <<EOF
+[Service]
+ExecStart=false
+SystemCallFilter=~@known
+SystemCallFilter=@system-service
+SystemCallFilter=~@resources:ENOANO @privileged
+SystemCallFilter=@clock
+EOF
+
+systemctl daemon-reload
+
+check allow yes /run/systemd/system/allow-list.service
+check allow no allow-list.service
+check deny yes /run/systemd/system/deny-list.service
+check deny no deny-list.service
+
+output=$(systemd-run -p "SystemCallFilter=@system-service" -p "SystemCallFilter=~@resources:ENOANO @privileged" -p "SystemCallFilter=@clock" sleep 60 2>&1)
+name=$(echo "$output" | awk '{ print $4 }' | cut -d';' -f1)
+
+check allow yes /run/systemd/transient/"$name"
+check allow no "$name"
+
+output=$(systemd-run -p "SystemCallFilter=~@known" -p "SystemCallFilter=@system-service" -p "SystemCallFilter=~@resources:ENOANO @privileged" -p "SystemCallFilter=@clock" sleep 60 2>&1)
+name=$(echo "$output" | awk '{ print $4 }' | cut -d';' -f1)
+
+check deny yes /run/systemd/transient/"$name"
+check deny no "$name"
+
+# Let's also test the "image-policy" verb
+
+systemd-analyze image-policy '*' 2>&1 | grep -q -F "Long form: =verity+signed+encrypted+unprotected+unused+absent"
+systemd-analyze image-policy '-' 2>&1 | grep -q -F "Long form: =unused+absent"
+systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -F "Long form: usr=verity:home=encrypted:=unused+absent"
+systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^home \+encrypted \+'
+systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^usr \+verity \+'
+systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^root \+ignore \+'
+systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^usr-verity \+unprotected \+'
+
+(! systemd-analyze image-policy 'doedel')
+
+# Output is very hard to predict, but let's run it for coverage anyway
+systemd-analyze pcrs
+systemd-analyze pcrs --json=pretty
+systemd-analyze pcrs 14 7 0 ima
+
+systemd-analyze architectures
+systemd-analyze architectures --json=pretty
+systemd-analyze architectures x86
+systemd-analyze architectures x86-64
+systemd-analyze architectures native
+systemd-analyze architectures uname
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-66-DEVICE-ISOLATION-device-isolation.service b/test/units/TEST-66-DEVICE-ISOLATION-device-isolation.service
new file mode 100644 (file)
index 0000000..85b3d0d
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Service that uses device isolation
+
+[Service]
+DevicePolicy=strict
+DeviceAllow=/dev/null r
+StandardOutput=file:/tmp/TEST-66-DEVICE-ISOLATION.serviceresults
+ExecStartPre=rm -f /tmp/TEST-66-DEVICE-ISOLATION.serviceresults
+ExecStart=bash -c "while true; do sleep 0.01 && echo meow >/dev/null && echo thisshouldnotbehere; done"
diff --git a/test/units/TEST-66-DEVICE-ISOLATION.sh b/test/units/TEST-66-DEVICE-ISOLATION.sh
new file mode 100755 (executable)
index 0000000..ccdfcb2
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+RESULTS_FILE=/tmp/TEST-66-DEVICE-ISOLATION.serviceresults
+
+systemd-analyze log-level debug
+
+systemctl start TEST-66-DEVICE-ISOLATION-device-isolation.service
+
+sleep 5
+grep -q "Operation not permitted" "$RESULTS_FILE"
+
+systemctl daemon-reload
+systemctl daemon-reexec
+
+systemctl stop TEST-66-DEVICE-ISOLATION-device-isolation.service
+
+grep -q "thisshouldnotbehere" "$RESULTS_FILE" && exit 42
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-66-DEVICEISOLATION.service b/test/units/TEST-66-DEVICEISOLATION.service
new file mode 100644 (file)
index 0000000..61cd0ab
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-66-DEVICEISOLATION
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-67-INTEGRITY.service b/test/units/TEST-67-INTEGRITY.service
new file mode 100644 (file)
index 0000000..82f998e
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-67-INTEGRITY
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-67-INTEGRITY.sh b/test/units/TEST-67-INTEGRITY.sh
new file mode 100755 (executable)
index 0000000..a42fd66
--- /dev/null
@@ -0,0 +1,121 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -euxo pipefail
+
+export DM_NAME="integrity_test"
+export FULL_DM_DEV_NAME="/dev/mapper/${DM_NAME}"
+export FS_UUID="01234567-ffff-eeee-eeee-0123456789ab"
+export GEN="/var/run/systemd/generator"
+
+image_dir=""
+
+cleanup()
+{
+    if [ -z "${image_dir}" ]; then
+        return
+    fi
+
+    if [ -f "${image_dir}/image" ]; then
+        if [ -e "${FULL_DM_DEV_NAME}" ]; then
+            integritysetup close "${DM_NAME}"
+        fi
+        losetup -d "${loop}"
+    fi
+
+    rm -rf "${image_dir}"
+}
+
+trap cleanup EXIT
+
+build_integrity_tab()
+{
+cat <<EOF >"/etc/integritytab"
+${DM_NAME} ${loop} - integrity-algorithm=$1
+EOF
+}
+
+image_dir="$(mktemp -d -t -p / integrity.tmp.XXXXXX)"
+if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then
+    echo "mktemp under / failed"
+    exit 1
+fi
+
+dd if=/dev/zero of="${image_dir}/image" bs=1048576 count=64 || exit 1
+dd if=/dev/zero of="${image_dir}/data" bs=1048576 count=64 || exit 1
+loop="$(losetup --show -f "${image_dir}/image")"
+
+if [[ ! -e ${loop} ]]; then
+    echo "Loopback device created not found!"
+    exit 1
+fi
+
+# Do one iteration with a separate data device, to test those branches
+separate_data=1
+
+for algorithm in crc32c crc32 sha1 sha256
+do
+    if [ "${separate_data}" -eq 1 ]; then
+        data_option="--data-device=${image_dir}/data"
+    else
+        data_option=""
+    fi
+    integritysetup format "${loop}" --batch-mode -I "${algorithm}" "${data_option}" || exit 1
+    integritysetup open -I "${algorithm}" "${loop}" "${DM_NAME}" "${data_option}" || exit 1
+    mkfs.ext4 -U "${FS_UUID}" "${FULL_DM_DEV_NAME}" || exit 1
+
+    # Give userspace time to handle udev events for new FS showing up ...
+    udevadm settle
+
+    integritysetup close "${DM_NAME}" || exit 1
+
+    # create integritytab, generate units, start service
+    if [ "${separate_data}" -eq 1 ]; then
+        data_option=",data-device=${image_dir}/data"
+    else
+        data_option=""
+    fi
+    build_integrity_tab "${algorithm}${data_option}"
+
+    # Cause the generator to re-run
+    systemctl daemon-reload || exit 1
+
+    # Check for existence of unit files...
+    if [[ ! -e "/run/systemd/generator/systemd-integritysetup@${DM_NAME}.service" ]]; then
+        echo "Service file does not exist!"
+        exit 1
+    fi
+
+    # Make sure we are in a consistent state, e.g. not already active before we start
+    systemctl stop systemd-integritysetup@"${DM_NAME}".service || exit 1
+    systemctl start systemd-integritysetup@"${DM_NAME}".service || exit 1
+    # Reset the start-limit counters, as we're going to restart the service a couple of times
+    systemctl reset-failed systemd-integritysetup@"${DM_NAME}".service
+
+    # Check the signature on the FS to ensure we can retrieve it and that is matches
+    if [ -e "${FULL_DM_DEV_NAME}" ]; then
+        # If a separate device is used for the metadata storage, then blkid will return one of the loop devices
+        if [ "${separate_data}" -eq 1 ]; then
+            dev_name="$(integritysetup status ${DM_NAME} | grep '^\s*device:' | awk '{print $2}')"
+        else
+            dev_name="${FULL_DM_DEV_NAME}"
+        fi
+        if [ "${dev_name}" != "$(blkid -U "${FS_UUID}")" ]; then
+            echo "Failed to locate FS with matching UUID!"
+            exit 1
+        fi
+    else
+        echo "Failed to bring up integrity device!"
+        exit 1
+    fi
+
+    systemctl stop systemd-integritysetup@"${DM_NAME}".service || exit 1
+
+    if [ -e "${FULL_DM_DEV_NAME}" ]; then
+        echo "Expecting ${FULL_DM_DEV_NAME} to not exist after stopping unit!"
+        exit 1
+    fi
+
+    separate_data=0
+done
+
+touch /testok
diff --git a/test/units/TEST-68-PROPAGATE-EXIT-STATUS.service b/test/units/TEST-68-PROPAGATE-EXIT-STATUS.service
new file mode 100644 (file)
index 0000000..2d86e1f
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-68-PROPAGATE-EXIT-STATUS
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
diff --git a/test/units/TEST-68-PROPAGATE-EXIT-STATUS.sh b/test/units/TEST-68-PROPAGATE-EXIT-STATUS.sh
new file mode 100755 (executable)
index 0000000..11da48a
--- /dev/null
@@ -0,0 +1,216 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# Wait for a service to enter a state within a timeout period, if it doesn't
+# enter the desired state within the timeout period then this function will
+# exit the test case with a non zero exit code.
+wait_on_state_or_fail() {
+    service=$1
+    expected_state=$2
+    timeout=$3
+
+    state=$(systemctl show "$service" --property=ActiveState --value)
+    while [ "$state" != "$expected_state" ]; do
+        if [ "$timeout" = "0" ]; then
+            systemd-analyze log-level info
+            exit 1
+        fi
+        timeout=$((timeout - 1))
+        sleep 1
+        state=$(systemctl show "$service" --property=ActiveState --value)
+    done
+}
+
+systemd-analyze log-level debug
+
+cat >/run/systemd/system/testservice-failure-68.service <<EOF
+[Unit]
+OnFailure=testservice-failure-exit-handler-68.service
+
+[Service]
+ExecStart=sh -c "exit 1"
+EOF
+
+cat >/run/systemd/system/testservice-failure-68-template.service <<EOF
+[Unit]
+OnFailure=testservice-failure-exit-handler-68-template@%n.service
+
+[Service]
+ExecStart=sh -c "exit 1"
+EOF
+
+cat >/run/systemd/system/testservice-success-68.service <<EOF
+[Unit]
+OnSuccess=testservice-success-exit-handler-68.service
+
+[Service]
+ExecStart=sh -c "exit 0"
+EOF
+
+cat >/run/systemd/system/testservice-success-68-template.service <<EOF
+[Unit]
+OnSuccess=testservice-success-exit-handler-68-template@%n.service
+
+[Service]
+ExecStart=sh -c "exit 0"
+EOF
+
+# Script to check that when an OnSuccess= dependency fires, the correct
+# MONITOR* env variables are passed.
+cat >/tmp/check_on_success.sh <<"EOF"
+#!/bin/sh
+
+set -ex
+env | sort
+if [ "$MONITOR_SERVICE_RESULT" != "success" ]; then
+    echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'success'"
+    exit 1
+fi
+
+if [ "$MONITOR_EXIT_CODE" != "exited" ]; then
+    echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'"
+    exit 1
+fi
+
+if [ "$MONITOR_EXIT_STATUS" != "0" ]; then
+    echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '0'"
+    exit 1
+fi
+
+if [ -z "$MONITOR_INVOCATION_ID" ]; then
+    echo "MONITOR_INVOCATION_ID unset"
+    exit 1
+fi
+
+if [ "$MONITOR_UNIT" != "testservice-success-68.service" ] &&
+   [ "$MONITOR_UNIT" != "testservice-success-68-template.service" ] &&
+   [ "$MONITOR_UNIT" != "testservice-transient-success-68.service" ]; then
+
+    echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-success-68[-template].service'"
+    exit 1
+fi
+
+exit 0
+EOF
+chmod +x /tmp/check_on_success.sh
+
+cat >/run/systemd/system/testservice-success-exit-handler-68.service <<EOF
+[Service]
+ExecStartPre=/tmp/check_on_success.sh
+ExecStart=/tmp/check_on_success.sh
+EOF
+
+cp /run/systemd/system/testservice-success-exit-handler-68.service \
+   /run/systemd/system/testservice-transient-success-exit-handler-68.service
+
+# Template version.
+cat >/run/systemd/system/testservice-success-exit-handler-68-template@.service <<EOF
+[Service]
+ExecStartPre=echo "triggered by %i"
+ExecStartPre=/tmp/check_on_success.sh
+ExecStart=/tmp/check_on_success.sh
+EOF
+
+# Script to check that when an OnFailure= dependency fires, the correct
+# MONITOR* env variables are passed.
+cat >/tmp/check_on_failure.sh <<"EOF"
+#!/bin/sh
+
+set -ex
+env | sort
+if [ "$MONITOR_SERVICE_RESULT" != "exit-code" ]; then
+    echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'exit-code'"
+    exit 1
+fi
+
+if [ "$MONITOR_EXIT_CODE" != "exited" ]; then
+    echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'"
+    exit 1
+fi
+
+if [ "$MONITOR_EXIT_STATUS" != "1" ]; then
+    echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '1'"
+    exit 1
+fi
+
+if [ -z "$MONITOR_INVOCATION_ID" ]; then
+    echo "MONITOR_INVOCATION_ID unset"
+    exit 1
+fi
+
+if [ "$MONITOR_UNIT" != "testservice-failure-68.service" ] &&
+   [ "$MONITOR_UNIT" != "testservice-failure-68-template.service" ] &&
+   [ "$MONITOR_UNIT" != "testservice-transient-failure-68.service" ]; then
+
+    echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-failure-68[-template].service'"
+    exit 1
+fi
+
+exit 0
+EOF
+chmod +x /tmp/check_on_failure.sh
+
+
+cat >/run/systemd/system/testservice-failure-exit-handler-68.service <<EOF
+[Service]
+# repeat the check to make sure that values are set correctly on repeated invocations
+Type=oneshot
+ExecStartPre=/tmp/check_on_failure.sh
+ExecStartPre=/tmp/check_on_failure.sh
+ExecStart=/tmp/check_on_failure.sh
+ExecStart=/tmp/check_on_failure.sh
+ExecStartPost=test -z '$MONITOR_SERVICE_RESULT'
+EOF
+
+cp /run/systemd/system/testservice-failure-exit-handler-68.service \
+   /run/systemd/system/testservice-transient-failure-exit-handler-68.service
+
+# Template version.
+cat >/run/systemd/system/testservice-failure-exit-handler-68-template@.service <<EOF
+[Service]
+Type=oneshot
+ExecStartPre=echo "triggered by %i"
+ExecStartPre=/tmp/check_on_failure.sh
+ExecStartPre=/tmp/check_on_failure.sh
+ExecStart=/tmp/check_on_failure.sh
+ExecStart=/tmp/check_on_failure.sh
+ExecStartPost=test -z '$MONITOR_SERVICE_RESULT'
+EOF
+
+systemctl daemon-reload
+
+: "-------I----------------------------------------------------"
+systemctl start testservice-failure-68.service
+wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
+
+: "-------II---------------------------------------------------"
+systemctl start testservice-success-68.service
+wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
+
+# Test some transient units since these exit very quickly.
+: "-------III--------------------------------------------------"
+systemd-run --unit=testservice-transient-success-68 \
+            --property=OnSuccess=testservice-transient-success-exit-handler-68.service \
+            sh -c "exit 0"
+wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
+
+: "-------IIII-------------------------------------------------"
+systemd-run --unit=testservice-transient-failure-68 \
+            --property=OnFailure=testservice-transient-failure-exit-handler-68.service \
+            sh -c "exit 1"
+wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
+
+# Test template handlers too
+: "-------V---------------------------------------------------"
+systemctl start testservice-success-68-template.service
+wait_on_state_or_fail "testservice-success-exit-handler-68-template@testservice-success-68-template.service.service" "inactive" "10"
+
+: "-------VI----------------------------------------------------"
+systemctl start testservice-failure-68-template.service
+wait_on_state_or_fail "testservice-failure-exit-handler-68-template@testservice-failure-68-template.service.service" "inactive" "10"
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-69-SHUTDOWN.service b/test/units/TEST-69-SHUTDOWN.service
new file mode 100644 (file)
index 0000000..3910369
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-69-SHUTDOWN
+
+[Service]
+Type=oneshot
+ExecStart=true
diff --git a/test/units/TEST-70-TPM2.creds.sh b/test/units/TEST-70-TPM2.creds.sh
new file mode 100755 (executable)
index 0000000..e66bfd1
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+export SYSTEMD_LOG_LEVEL=debug
+
+# Ensure that sandboxing doesn't stop creds from being accessible
+echo "test" > /tmp/testdata
+systemd-creds encrypt /tmp/testdata /tmp/testdata.encrypted --with-key=tpm2
+# LoadCredentialEncrypted
+systemd-run -p PrivateDevices=yes -p LoadCredentialEncrypted=testdata.encrypted:/tmp/testdata.encrypted --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata
+# SetCredentialEncrypted
+systemd-run -p PrivateDevices=yes -p SetCredentialEncrypted=testdata.encrypted:"$(cat /tmp/testdata.encrypted)" --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata
+
+rm -f /tmp/testdata
diff --git a/test/units/TEST-70-TPM2.cryptenroll.sh b/test/units/TEST-70-TPM2.cryptenroll.sh
new file mode 100755 (executable)
index 0000000..f18ef02
--- /dev/null
@@ -0,0 +1,96 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+cryptenroll_wipe_and_check() {(
+    set +o pipefail
+
+    : >/tmp/cryptenroll.out
+    systemd-cryptenroll "$@" |& tee /tmp/cryptenroll.out
+    grep -qE "Wiped slot [[:digit:]]+" /tmp/cryptenroll.out
+)}
+
+# There is an external issue with libcryptsetup on ppc64 that hits 95% of Ubuntu ppc64 test runs, so skip it
+if [[ "$(uname -m)" == "ppc64le" ]]; then
+    echo "Skipping systemd-cryptenroll tests on ppc64le, see https://github.com/systemd/systemd/issues/27716"
+    exit 0
+fi
+
+export SYSTEMD_LOG_LEVEL=debug
+IMAGE="$(mktemp /tmp/systemd-cryptenroll-XXX.image)"
+
+truncate -s 20M "$IMAGE"
+echo -n password >/tmp/password
+# Change file mode to avoid "/tmp/password has 0644 mode that is too permissive" messages
+chmod 0600 /tmp/password
+cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/password
+
+# Enroll additional tokens, keys, and passwords to exercise the list and wipe stuff
+systemd-cryptenroll --unlock-key-file=/tmp/password --tpm2-device=auto "$IMAGE"
+NEWPASSWORD="" systemd-cryptenroll --unlock-key-file=/tmp/password  --password "$IMAGE"
+NEWPASSWORD=foo systemd-cryptenroll --unlock-key-file=/tmp/password  --password "$IMAGE"
+for _ in {0..9}; do
+    systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE"
+done
+PASSWORD="" NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE"
+# Do some basic checks before we start wiping stuff
+systemd-cryptenroll "$IMAGE"
+systemd-cryptenroll "$IMAGE" | grep password
+systemd-cryptenroll "$IMAGE" | grep recovery
+# Let's start wiping
+cryptenroll_wipe_and_check "$IMAGE" --wipe=empty
+(! cryptenroll_wipe_and_check "$IMAGE" --wipe=empty)
+cryptenroll_wipe_and_check "$IMAGE" --wipe=empty,0
+PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --wipe=0,0,empty,0,pkcs11,fido2,000,recovery,password --password
+systemd-cryptenroll "$IMAGE" | grep password
+(! systemd-cryptenroll "$IMAGE" | grep recovery)
+# We shouldn't be able to wipe all keyslots without enrolling a new key first
+(! systemd-cryptenroll "$IMAGE" --wipe=all)
+PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --password --wipe=all
+# Check if the newly (and only) enrolled password works
+(! systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE")
+(! PASSWORD="" systemd-cryptenroll --recovery-key "$IMAGE")
+PASSWORD=foo systemd-cryptenroll --recovery-key "$IMAGE"
+
+systemd-cryptenroll --fido2-with-client-pin=false "$IMAGE"
+systemd-cryptenroll --fido2-with-user-presence=false "$IMAGE"
+systemd-cryptenroll --fido2-with-user-verification=false "$IMAGE"
+systemd-cryptenroll --tpm2-pcrs=8 "$IMAGE"
+systemd-cryptenroll --tpm2-pcrs=boot-loader-code+boot-loader-config "$IMAGE"
+
+# Unlocking using TPM2
+PASSWORD=foo systemd-cryptenroll --tpm2-device=auto "$IMAGE"
+systemd-cryptenroll --unlock-tpm2-device=auto --recovery-key "$IMAGE"
+systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --wipe-slot=tpm2 "$IMAGE"
+
+# Add PIN to TPM2 enrollment
+NEWPIN=1234 systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --tpm2-with-pin=yes "$IMAGE"
+
+# Change PIN on TPM2 enrollment
+PIN=1234 NEWPIN=4321 systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --tpm2-with-pin=yes "$IMAGE"
+PIN=4321 systemd-cryptenroll --unlock-tpm2-device=auto --recovery-key "$IMAGE"
+
+(! systemd-cryptenroll --fido2-with-client-pin=false)
+(! systemd-cryptenroll --fido2-with-user-presence=f "$IMAGE" /tmp/foo)
+(! systemd-cryptenroll --fido2-with-client-pin=1234 "$IMAGE")
+(! systemd-cryptenroll --fido2-with-user-presence=1234 "$IMAGE")
+(! systemd-cryptenroll --fido2-with-user-verification=1234 "$IMAGE")
+(! systemd-cryptenroll --tpm2-with-pin=1234 "$IMAGE")
+(! systemd-cryptenroll --recovery-key --password "$IMAGE")
+(! systemd-cryptenroll --password --recovery-key "$IMAGE")
+(! systemd-cryptenroll --password --fido2-device=auto "$IMAGE")
+(! systemd-cryptenroll --password --pkcs11-token-uri=auto "$IMAGE")
+(! systemd-cryptenroll --password --tpm2-device=auto "$IMAGE")
+(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-fido2-device=auto "$IMAGE")
+(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-key-file=/tmp/unlock "$IMAGE")
+(! systemd-cryptenroll --fido2-credential-algorithm=es512 "$IMAGE")
+(! systemd-cryptenroll --tpm2-public-key-pcrs=key "$IMAGE")
+(! systemd-cryptenroll --tpm2-pcrs=key "$IMAGE")
+(! systemd-cryptenroll --tpm2-pcrs=44+8 "$IMAGE")
+(! systemd-cryptenroll --tpm2-pcrs=hello "$IMAGE")
+(! systemd-cryptenroll --wipe-slot "$IMAGE")
+(! systemd-cryptenroll --wipe-slot=10240000 "$IMAGE")
+(! systemd-cryptenroll --fido2-device=auto --unlock-fido2-device=auto "$IMAGE")
+
+rm -f "$IMAGE"
diff --git a/test/units/TEST-70-TPM2.cryptsetup.sh b/test/units/TEST-70-TPM2.cryptsetup.sh
new file mode 100755 (executable)
index 0000000..0ecf34b
--- /dev/null
@@ -0,0 +1,227 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+
+cryptsetup_has_token_plugin_support() {
+    local plugin_path
+
+    plugin_path="$(cryptsetup --help | sed -nr 's/.*LUKS2 external token plugin path: (.*)\./\1/p')/libcryptsetup-token-systemd-tpm2.so)"
+    cryptsetup --help | grep -q 'LUKS2 external token plugin support is compiled-in' && [[ -f "$plugin_path" ]]
+}
+
+tpm_check_failure_with_wrong_pin() {
+    local testIMAGE="${1:?}"
+    local badpin="${2:?}"
+    local goodpin="${3:?}"
+
+    # We need to be careful not to trigger DA lockout; allow 2 failures
+    tpm2_dictionarylockout -s -n 2
+    (! PIN=$badpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1)
+    # Verify the correct PIN works, to be sure the failure wasn't a DA lockout
+    PIN=$goodpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1
+    systemd-cryptsetup detach test-volume
+    # Clear/reset the DA lockout counter
+    tpm2_dictionarylockout -c
+}
+
+at_exit() {
+    # Evict the TPM primary key that we persisted
+    if [[ -n "${PERSISTENT_HANDLE:-}" ]]; then
+        tpm2_evictcontrol -c "$PERSISTENT_HANDLE"
+    fi
+}
+
+trap at_exit EXIT
+
+# Prepare a fresh disk image
+IMAGE="$(mktemp /tmp/systemd-cryptsetup-XXX.IMAGE)"
+
+truncate -s 20M "$IMAGE"
+echo -n passphrase >/tmp/passphrase
+# Change file mode to avoid "/tmp/passphrase has 0644 mode that is too permissive" messages
+chmod 0600 /tmp/passphrase
+cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase
+
+# Unlocking via keyfile
+systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto "$IMAGE"
+
+# Enroll unlock with default PCR policy
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# Check with wrong PCR
+tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
+(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
+
+# Enroll unlock with PCR+PIN policy
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE"
+PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# Check failure with wrong PIN; try a few times to make sure we avoid DA lockout
+for _ in {0..3}; do
+    tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456
+done
+
+# Check LUKS2 token plugin unlock (i.e. without specifying tpm2-device=auto)
+if cryptsetup_has_token_plugin_support; then
+    PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - headless=1
+    systemd-cryptsetup detach test-volume
+
+    # Check failure with wrong PIN
+    for _ in {0..3}; do
+        tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456
+    done
+else
+    echo 'cryptsetup has no LUKS2 token plugin support, skipping'
+fi
+
+# Check failure with wrong PCR (and correct PIN)
+tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
+(! PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
+
+# Enroll unlock with PCR 0+7
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# Check with wrong PCR 0
+tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000
+(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
+
+if tpm_has_pcr sha256 12; then
+    # Enroll using an explicit PCR value (that does match current PCR value)
+    systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+    EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+    PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE"
+    systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+    systemd-cryptsetup detach test-volume
+
+    # Same as above plus more PCRs without the value or alg specified
+    systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+    EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+    PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE"
+    systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+    systemd-cryptsetup detach test-volume
+
+    # Same as above plus more PCRs with hash alg specified but hash value not specified
+    systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+    EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+    PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1:sha256,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE"
+    systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+    systemd-cryptsetup detach test-volume
+
+    # Now the interesting part, enrolling using a hash value that doesn't match the current PCR value
+    systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+    tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12
+    CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+    EXPECTED_PCR_VALUE=$(cat /tmp/pcr.dat /tmp/pcr.dat | openssl dgst -sha256 -r | cut -d ' ' -f 1)
+    PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE"
+    (! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
+    tpm2_pcrextend "12:sha256=$CURRENT_PCR_VALUE"
+    systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+    systemd-cryptsetup detach test-volume
+
+    # enroll TPM using device key instead of direct access, then verify unlock using TPM
+    tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12
+    CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
+    tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub
+    systemd-analyze srk > /tmp/srk2.pub
+    cmp /tmp/srk.pub /tmp/srk2.pub
+    if [ -f /run/systemd/tpm2-srk-public-key.tpm2b_public ] ; then
+        cmp /tmp/srk.pub /run/systemd/tpm2-srk-public-key.tpm2b_public
+    fi
+
+    # --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS
+    if openssl_supports_kdf SSKDF; then
+        PASSWORD=passphrase systemd-cryptenroll --tpm2-device-key=/tmp/srk.pub --tpm2-pcrs="12:sha256=$CURRENT_PCR_VALUE" "$IMAGE"
+        systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+        systemd-cryptsetup detach test-volume
+    fi
+
+    rm -f /tmp/pcr.dat /tmp/srk.pub
+fi
+
+# Use default (0) seal key handle
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0 "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x0 "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# Use SRK seal key handle
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=81000001 "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x81000001 "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# Test invalid ranges: pcr, nv, session, permanent
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=7 "$IMAGE")          # PCR
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x01000001 "$IMAGE") # NV index
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x02000001 "$IMAGE") # HMAC/loaded session
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x03000001 "$IMAGE") # Policy/saved session
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x40000001 "$IMAGE") # Permanent
+
+# Use non-SRK persistent seal key handle (by creating/persisting new key)
+PRIMARY=/tmp/primary.ctx
+tpm2_createprimary -c "$PRIMARY"
+PERSISTENT_LINE=$(tpm2_evictcontrol -c "$PRIMARY" | grep persistent-handle)
+PERSISTENT_HANDLE="0x${PERSISTENT_LINE##*0x}"
+tpm2_flushcontext -t
+
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="${PERSISTENT_HANDLE#0x}" "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="$PERSISTENT_HANDLE" "$IMAGE"
+systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS
+if openssl_supports_kdf SSKDF; then
+    # Make sure that --tpm2-device-key= also works with systemd-repart
+    tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub
+    mkdir /tmp/dditest
+    cat > /tmp/dditest/50-root.conf <<EOF
+[Partition]
+Type=root
+Format=ext4
+CopyFiles=/tmp/dditest:/
+Encrypt=tpm2
+EOF
+    PASSWORD=passphrase systemd-repart --tpm2-device-key=/tmp/srk.pub --definitions=/tmp/dditest --empty=create --size=50M /tmp/dditest.raw --tpm2-pcrs=
+    DEVICE="$(systemd-dissect --attach /tmp/dditest.raw)"
+    udevadm wait --settle --timeout=10 "$DEVICE"
+    systemd-cryptsetup attach dditest "$DEVICE"p1 - tpm2-device=auto,headless=yes
+    mkdir /tmp/dditest.mnt
+    mount -t ext4 /dev/mapper/dditest /tmp/dditest.mnt
+    cmp /tmp/dditest.mnt/50-root.conf /tmp/dditest/50-root.conf
+    umount /tmp/dditest.mnt
+    rmdir /tmp/dditest.mnt
+    rm /tmp/dditest.raw
+    rm /tmp/dditest/50-root.conf
+    rmdir /tmp/dditest
+fi
+
+rm -f "$IMAGE" "$PRIMARY"
diff --git a/test/units/TEST-70-TPM2.measure.sh b/test/units/TEST-70-TPM2.measure.sh
new file mode 100755 (executable)
index 0000000..3336f38
--- /dev/null
@@ -0,0 +1,130 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+SD_MEASURE="/usr/lib/systemd/systemd-measure"
+
+if [[ ! -x "${SD_MEASURE:?}" ]]; then
+    echo "$SD_MEASURE not found, skipping the test"
+    exit 0
+fi
+
+IMAGE="$(mktemp /tmp/systemd-measure-XXX.image)"
+
+echo HALLO >/tmp/tpmdata1
+echo foobar >/tmp/tpmdata2
+
+cat >/tmp/result <<EOF
+11:sha1=5177e4ad69db92192c10e5f80402bf81bfec8a81
+11:sha256=37b48bd0b222394dbe3cceff2fca4660c4b0a90ae9369ec90b42f14489989c13
+11:sha384=5573f9b2caf55b1d0a6a701f890662d682af961899f0419cf1e2d5ea4a6a68c1f25bd4f5b8a0865eeee82af90f5cb087
+11:sha512=961305d7e9981d6606d1ce97b3a9a1f92610cac033e9c39064895f0e306abc1680463d55767bd98e751eae115bdef3675a9ee1d29ed37da7885b1db45bb2555b
+EOF
+"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=: | cmp - /tmp/result
+
+cat >/tmp/result.json <<EOF
+{"sha1":[{"pcr":11,"hash":"5177e4ad69db92192c10e5f80402bf81bfec8a81"}],"sha256":[{"pcr":11,"hash":"37b48bd0b222394dbe3cceff2fca4660c4b0a90ae9369ec90b42f14489989c13"}],"sha384":[{"pcr":11,"hash":"5573f9b2caf55b1d0a6a701f890662d682af961899f0419cf1e2d5ea4a6a68c1f25bd4f5b8a0865eeee82af90f5cb087"}],"sha512":[{"pcr":11,"hash":"961305d7e9981d6606d1ce97b3a9a1f92610cac033e9c39064895f0e306abc1680463d55767bd98e751eae115bdef3675a9ee1d29ed37da7885b1db45bb2555b"}]}
+EOF
+"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=: -j | diff -u - /tmp/result.json
+
+cat >/tmp/result <<EOF
+11:sha1=6765ee305db063040c454d32697d922b3d4f232b
+11:sha256=21c49c1242042649e09c156546fd7d425ccc3c67359f840507b30be4e0f6f699
+11:sha384=08d0b003a134878eee552070d51d58abe942f457ca85704131dd36f73728e7327ca837594bc9d5ac7de818d02a3d5dd2
+11:sha512=65120f6ebc04b156421c6f3d543b2fad545363d9ca61c514205459e9c0e0b22e09c23605eae5853e38458ef3ca54e087168af8d8a882a98d220d9391e48be6d0
+EOF
+"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=foo | cmp - /tmp/result
+
+cat >/tmp/result.json <<EOF
+{"sha1":[{"phase":"foo","pcr":11,"hash":"6765ee305db063040c454d32697d922b3d4f232b"}],"sha256":[{"phase":"foo","pcr":11,"hash":"21c49c1242042649e09c156546fd7d425ccc3c67359f840507b30be4e0f6f699"}],"sha384":[{"phase":"foo","pcr":11,"hash":"08d0b003a134878eee552070d51d58abe942f457ca85704131dd36f73728e7327ca837594bc9d5ac7de818d02a3d5dd2"}],"sha512":[{"phase":"foo","pcr":11,"hash":"65120f6ebc04b156421c6f3d543b2fad545363d9ca61c514205459e9c0e0b22e09c23605eae5853e38458ef3ca54e087168af8d8a882a98d220d9391e48be6d0"}]}
+EOF
+"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=foo -j | diff -u - /tmp/result.json
+
+rm /tmp/result /tmp/result.json
+
+if ! tpm_has_pcr sha1 11 || ! tpm_has_pcr sha256 11; then
+    echo "PCR sysfs files not found, skipping signed PCR policy tests"
+    exit 0
+fi
+
+# Generate key pair
+openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "/tmp/pcrsign-private.pem"
+openssl rsa -pubout -in "/tmp/pcrsign-private.pem" -out "/tmp/pcrsign-public.pem"
+
+MEASURE_BANKS=("--bank=sha256")
+# Check if SHA1 signatures are supported
+#
+# Some distros have started phasing out SHA1, so make sure the SHA1
+# signatures are supported before trying to use them.
+if echo hello | openssl dgst -sign /tmp/pcrsign-private.pem -sha1 >/dev/null; then
+    MEASURE_BANKS+=("--bank=sha1")
+fi
+
+# Sign current PCR state with it
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: | tee "/tmp/pcrsign.sig"
+dd if=/dev/urandom of=/tmp/pcrtestdata bs=1024 count=64
+systemd-creds encrypt /tmp/pcrtestdata /tmp/pcrtestdata.encrypted --with-key=host+tpm2-with-public-key --tpm2-public-key="/tmp/pcrsign-public.pem"
+systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" | cmp - /tmp/pcrtestdata
+
+# Invalidate PCR, decrypting should fail now
+tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000
+(! systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" >/dev/null)
+
+# Sign new PCR state, decrypting should work now.
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig2"
+systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig2" | cmp - /tmp/pcrtestdata
+
+# Now, do the same, but with a cryptsetup binding
+truncate -s 20M "$IMAGE"
+cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase
+# Ensure that an unrelated signature, when not requested, is not used
+touch /run/systemd/tpm2-pcr-signature.json
+systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" "$IMAGE"
+# Reset and use the signature now
+rm -f /run/systemd/tpm2-pcr-signature.json
+systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
+systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" --tpm2-signature="/tmp/pcrsign.sig2" "$IMAGE"
+
+# Check if we can activate that (without the token module stuff)
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup detach test-volume2
+
+# Check if we can activate that (and a second time with the token module stuff enabled)
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup detach test-volume2
+
+# After extending the PCR things should fail
+tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000
+(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1)
+(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1)
+
+# But once we sign the current PCRs, we should be able to unlock again
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig3"
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
+systemd-cryptsetup detach test-volume2
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
+systemd-cryptsetup detach test-volume2
+
+# Test --append mode and de-duplication. With the same parameters signing should not add a new entry
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig3" >"/tmp/pcrsign.sig4"
+cmp "/tmp/pcrsign.sig3" "/tmp/pcrsign.sig4"
+
+# Sign one more phase, this should
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig4" >"/tmp/pcrsign.sig5"
+(! cmp "/tmp/pcrsign.sig4" "/tmp/pcrsign.sig5")
+
+# Should still be good to unlock, given the old entry still exists
+SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig5",headless=1
+systemd-cryptsetup detach test-volume2
+
+# Adding both signatures once more should not change anything, due to the deduplication
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig5" >"/tmp/pcrsign.sig6"
+"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig6" >"/tmp/pcrsign.sig7"
+cmp "/tmp/pcrsign.sig5" "/tmp/pcrsign.sig7"
+
+rm -f "$IMAGE"
diff --git a/test/units/TEST-70-TPM2.pcrextend.sh b/test/units/TEST-70-TPM2.pcrextend.sh
new file mode 100755 (executable)
index 0000000..318fce0
--- /dev/null
@@ -0,0 +1,124 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
+
+if [[ ! -x "${SD_PCREXTEND:?}" ]] || ! tpm_has_pcr sha256 11 || ! tpm_has_pcr sha256 15; then
+    echo "$SD_PCREXTEND or PCR sysfs files not found, skipping PCR extension tests"
+    exit 0
+fi
+
+at_exit() {
+    if [[ $? -ne 0 ]]; then
+        # Dump the event log on fail, to make debugging a bit easier
+        jq --seq --slurp </run/log/systemd/tpm2-measure.log
+    fi
+}
+
+trap at_exit EXIT
+
+# Temporarily override sd-pcrextend's sanity checks
+export SYSTEMD_FORCE_MEASURE=1
+
+"$SD_PCREXTEND" --help
+"$SD_PCREXTEND" --version
+"$SD_PCREXTEND" foo
+"$SD_PCREXTEND" --machine-id
+"$SD_PCREXTEND" --tpm2-device=list
+"$SD_PCREXTEND" --tpm2-device=auto foo
+"$SD_PCREXTEND" --tpm2-device=/dev/tpm0 foo
+"$SD_PCREXTEND" --bank=sha256 foo
+"$SD_PCREXTEND" --bank=sha256 --bank=sha256 foo
+"$SD_PCREXTEND" --graceful foo
+"$SD_PCREXTEND" --pcr=15 foo
+"$SD_PCREXTEND" --file-system=/
+"$SD_PCREXTEND" --file-system=/tmp --file-system=/
+"$SD_PCREXTEND" --file-system=/tmp --file-system=/ --pcr=15 --pcr=11
+
+if tpm_has_pcr sha1 11; then
+    "$SD_PCREXTEND" --bank=sha1 --pcr=11 foo
+fi
+
+(! "$SD_PCREXTEND")
+(! "$SD_PCREXTEND" "")
+(! "$SD_PCREXTEND" foo bar)
+(! "$SD_PCREXTEND" --bank= foo)
+(! "$SD_PCREXTEND" --tpm2-device= foo)
+(! "$SD_PCREXTEND" --tpm2-device=/dev/null foo)
+(! "$SD_PCREXTEND" --pcr= foo)
+(! "$SD_PCREXTEND" --pcr=-1 foo)
+(! "$SD_PCREXTEND" --pcr=1024 foo)
+(! "$SD_PCREXTEND" --foo=bar)
+
+unset SYSTEMD_FORCE_MEASURE
+
+# Note: since we're reading the TPM event log as json-seq, the same rules apply to the output
+#       as well, i.e. each record is prefixed by RS (0x1E, 036) and suffixed by LF (0x0A, 012).
+#       LF is usually eaten by bash, but RS needs special handling.
+
+# Save the number of events in the current event log, so we can skip them when
+# checking changes caused by following tests
+RECORD_COUNT="$(jq --seq --slurp '. | length' </run/log/systemd/tpm2-measure.log | tr -d '\036')"
+
+# Let's measure the machine ID
+tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15
+mv /etc/machine-id /etc/machine-id.save
+echo 994013bf23864ee7992eab39a96dd3bb >/etc/machine-id
+SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --machine-id
+mv /etc/machine-id.save /etc/machine-id
+tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15
+
+# And check it matches expectations
+diff /tmp/newpcr15 \
+     <(cat /tmp/oldpcr15 <(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
+
+# Check that the event log record was properly written
+test "$(jq --seq --slurp ".[$RECORD_COUNT].pcr" </run/log/systemd/tpm2-measure.log)" == "$(printf '\x1e15')"
+DIGEST_EXPECTED="$(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -hex -sha256 -r)"
+DIGEST_CURRENT="$(jq --seq --slurp --raw-output ".[$RECORD_COUNT].digests[] | select(.hashAlg == \"sha256\").digest" </run/log/systemd/tpm2-measure.log) *stdin"
+test "$DIGEST_EXPECTED" == "$DIGEST_CURRENT"
+
+RECORD_COUNT=$((RECORD_COUNT + 1))
+# And similar for the boot phase measurement into PCR 11
+tpm2_pcrread sha256:11 -Q -o /tmp/oldpcr11
+# Do the equivalent of 'SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" foobar' via Varlink, just to test the Varlink logic (but first we need to patch out the conditionalization...)
+mkdir -p /run/systemd/system/systemd-pcrextend.socket.d
+cat > /run/systemd/system/systemd-pcrextend.socket.d/50-no-condition.conf <<EOF
+[Unit]
+# Turn off all conditions */
+ConditionSecurity=
+EOF
+systemctl daemon-reload
+systemctl restart systemd-pcrextend.socket
+varlinkctl call /run/systemd/io.systemd.PCRExtend io.systemd.PCRExtend.Extend '{"pcr":11,"text":"foobar"}'
+tpm2_pcrread sha256:11 -Q -o /tmp/newpcr11
+
+diff /tmp/newpcr11 \
+    <(cat /tmp/oldpcr11 <(echo -n "foobar" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
+
+# Check the event log for the 2nd new record since $RECORD_COUNT
+test "$(jq --seq --slurp ".[$RECORD_COUNT].pcr" </run/log/systemd/tpm2-measure.log)" == "$(printf '\x1e11')"
+DIGEST_EXPECTED="$(echo -n "foobar" | openssl dgst -hex -sha256 -r)"
+DIGEST_CURRENT="$(jq --seq --slurp --raw-output ".[$RECORD_COUNT].digests[] | select(.hashAlg == \"sha256\").digest" </run/log/systemd/tpm2-measure.log) *stdin"
+test "$DIGEST_EXPECTED" == "$DIGEST_CURRENT"
+
+# Measure a file system into PCR 15
+tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15
+SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --file-system=/
+# Put together the "file system word" we just sent to the TPM
+#   file-system:MOUNTPOINT:TYPE:UUID:LABEL:PART_ENTRY_UUID:PART_ENTRY_TYPE:PART_ENTRY_NAME
+ROOT_DEVICE="$(findmnt -n -o SOURCE /)"
+FS_WORD="$(lsblk -n -o MOUNTPOINT,FSTYPE,UUID,LABEL,PARTUUID,PARTTYPE,PARTLABEL "$ROOT_DEVICE" | sed -r 's/[ ]+/:/g')"
+tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15
+
+# And check if it matches with the current PCR 15 state
+diff /tmp/newpcr15 \
+     <(cat /tmp/oldpcr15 <(echo -n "file-system:$FS_WORD" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
+
+rm -f /tmp/oldpcr{11,15} /tmp/newpcr{11,15}
diff --git a/test/units/TEST-70-TPM2.pcrlock.sh b/test/units/TEST-70-TPM2.pcrlock.sh
new file mode 100755 (executable)
index 0000000..fd51161
--- /dev/null
@@ -0,0 +1,179 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+export PAGER=
+SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
+SD_PCRLOCK="/usr/lib/systemd/systemd-pcrlock"
+
+if [[ ! -x "${SD_PCREXTEND:?}" ]] || [[ ! -x "${SD_PCRLOCK:?}" ]] ; then
+    echo "$SD_PCREXTEND or $SD_PCRLOCK not found, skipping pcrlock tests"
+    exit 0
+fi
+
+at_exit() {
+    if [[ $? -ne 0 ]]; then
+        # Dump the event log on fail, to make debugging a bit easier
+        [[ -e /run/log/systemd/tpm2-measure.log ]] && jq --seq --slurp </run/log/systemd/tpm2-measure.log
+    fi
+
+    return 0
+}
+
+trap at_exit EXIT
+
+# Temporarily override sd-pcrextend's sanity checks
+export SYSTEMD_FORCE_MEASURE=1
+
+# The PCRs we are going to lock to. We exclude the various PCRs we touched
+# above where no event log record was written, because we cannot analyze
+# things without event log. We include debug PCR 16, see below.
+PCRS="1+2+3+4+5+16"
+
+# Remove the old measurement log, as it contains all kinds of nonsense from the
+# previous test, which will fail our consistency checks. Removing the file also
+# means we'll fail consistency check, but at least we'll fail them consistently
+# (as the PCR values simply won't match the log).
+rm -f /run/log/systemd/tpm2-measure.log
+
+# Ensure a truncated log doesn't crash pcrlock
+echo -n -e \\x1e >/tmp/borked
+set +e
+SYSTEMD_MEASURE_LOG_USERSPACE=/tmp/borked "$SD_PCRLOCK" cel --no-pager --json=pretty
+ret=$?
+set -e
+# If it crashes the exit code will be 149
+test $ret -eq 1
+
+SYSTEMD_COLORS=256 "$SD_PCRLOCK"
+"$SD_PCRLOCK" cel --no-pager --json=pretty
+"$SD_PCRLOCK" log --pcr="$PCRS"
+"$SD_PCRLOCK" log --json=pretty --pcr="$PCRS"
+"$SD_PCRLOCK" list-components
+"$SD_PCRLOCK" list-components --location=250-
+"$SD_PCRLOCK" list-components --location=250-:350-
+"$SD_PCRLOCK" lock-firmware-config
+"$SD_PCRLOCK" lock-gpt
+"$SD_PCRLOCK" lock-machine-id
+"$SD_PCRLOCK" lock-file-system
+"$SD_PCRLOCK" lock-file-system /
+"$SD_PCRLOCK" predict --pcr="$PCRS"
+"$SD_PCRLOCK" predict --pcr="0x1+0x3+4"
+"$SD_PCRLOCK" predict --json=pretty --pcr="$PCRS"
+
+SD_STUB="$(find /usr/lib/systemd/boot/efi/ -name "systemd-boot*.efi" | head -n1)"
+if [[ -n "$SD_STUB" ]]; then
+    "$SD_PCRLOCK" lock-pe "$SD_STUB"
+    "$SD_PCRLOCK" lock-pe <"$SD_STUB"
+    "$SD_PCRLOCK" lock-uki "$SD_STUB"
+    "$SD_PCRLOCK" lock-uki <"$SD_STUB"
+fi
+
+PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
+# Repeat immediately (this call will have to reuse the nvindex, rather than create it)
+"$SD_PCRLOCK" make-policy --pcr="$PCRS"
+"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force
+
+img="/tmp/pcrlock.img"
+truncate -s 20M "$img"
+echo -n hoho >/tmp/pcrlockpwd
+chmod 0600 /tmp/pcrlockpwd
+cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$img" /tmp/pcrlockpwd
+
+systemd-cryptenroll --unlock-key-file=/tmp/pcrlockpwd --tpm2-device=auto --tpm2-pcrlock=/var/lib/systemd/pcrlock.json --wipe-slot=tpm2 "$img"
+systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
+systemd-cryptsetup detach pcrlock
+
+# Measure something into PCR 16 (the "debug" PCR), which should make the activation fail
+"$SD_PCREXTEND" --pcr=16 test70
+
+"$SD_PCRLOCK" cel --json=pretty
+
+(! systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless )
+
+# Now add a component for it, rebuild policy and it should work (we'll rebuild
+# once like that, but don't provide the recovery pin. This should fail, since
+# the PCR is hosed after all. But then we'll use recovery pin, and it should
+# work.
+echo -n test70 | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock --pcr=16
+(! "$SD_PCRLOCK" make-policy --pcr="$PCRS")
+PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
+
+systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
+systemd-cryptsetup detach pcrlock
+
+# And now let's do it the clean way, and generate the right policy ahead of time.
+echo -n test70-take-two | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock --pcr=16
+"$SD_PCRLOCK" make-policy --pcr="$PCRS"
+# the next one should be skipped because redundant
+"$SD_PCRLOCK" make-policy --pcr="$PCRS"
+# but this one should not be skipped, even if redundant, because we force it
+"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force --recovery-pin=show
+
+"$SD_PCREXTEND" --pcr=16 test70-take-two
+
+"$SD_PCRLOCK" cel --json=pretty
+
+systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
+systemd-cryptsetup detach pcrlock
+
+# Now use the root fs support, i.e. make the tool write a copy of the pcrlock
+# file as service credential to some temporary dir and remove the local copy, so that
+# it has to use the credential version.
+mkdir /tmp/fakexbootldr
+SYSTEMD_XBOOTLDR_PATH=/tmp/fakexbootldr SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 "$SD_PCRLOCK" make-policy --pcr="$PCRS" --force
+mv /var/lib/systemd/pcrlock.json /var/lib/systemd/pcrlock.json.gone
+
+systemd-creds decrypt /tmp/fakexbootldr/loader/credentials/pcrlock.*.cred
+
+SYSTEMD_ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY=/tmp/fakexbootldr/loader/credentials systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,headless
+systemd-cryptsetup detach pcrlock
+
+mv /var/lib/systemd/pcrlock.json.gone /var/lib/systemd/pcrlock.json
+SYSTEMD_XBOOTLDR_PATH=/tmp/fakexbootldr SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 "$SD_PCRLOCK" remove-policy
+
+"$SD_PCRLOCK" unlock-firmware-config
+"$SD_PCRLOCK" unlock-gpt
+"$SD_PCRLOCK" unlock-machine-id
+"$SD_PCRLOCK" unlock-file-system
+"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock
+"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock
+
+(! "$SD_PCRLOCK" "")
+(! "$SD_PCRLOCK" predict --pcr=-1)
+(! "$SD_PCRLOCK" predict --pcr=foo)
+(! "$SD_PCRLOCK" predict --pcr=1+1)
+(! "$SD_PCRLOCK" predict --pcr=1+++++1)
+(! "$SD_PCRLOCK" make-policy --nv-index=0)
+(! "$SD_PCRLOCK" make-policy --nv-index=foo)
+(! "$SD_PCRLOCK" list-components --location=:)
+(! "$SD_PCRLOCK" lock-gpt "")
+(! "$SD_PCRLOCK" lock-gpt /dev/sr0)
+(! "$SD_PCRLOCK" lock-pe /dev/full)
+(! "$SD_PCRLOCK" lock-pe /bin/true)
+(! "$SD_PCRLOCK" lock-uki /dev/full)
+(! "$SD_PCRLOCK" lock-uki /bin/true)
+(! "$SD_PCRLOCK" lock-file-system "")
+
+# Exercise Varlink API a bit (but first turn off condition)
+
+mkdir -p /run/systemd/system/systemd-pcrlock.socket.d
+cat > /run/systemd/system/systemd-pcrlock.socket.d/50-no-condition.conf <<EOF
+[Unit]
+# Turn off all conditions
+ConditionSecurity=
+EOF
+
+systemctl daemon-reload
+systemctl restart systemd-pcrlock.socket
+
+varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.RemovePolicy '{}'
+varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.MakePolicy '{}'
+varlinkctl call --collect --json=pretty /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.ReadEventLog '{}'
+
+rm "$img" /tmp/pcrlockpwd
diff --git a/test/units/TEST-70-TPM2.service b/test/units/TEST-70-TPM2.service
new file mode 100644 (file)
index 0000000..a9c1542
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-70-TPM2
+Wants=tpm2.target
+After=tpm2.target
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
diff --git a/test/units/TEST-70-TPM2.sh b/test/units/TEST-70-TPM2.sh
new file mode 100755 (executable)
index 0000000..9c2a033
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
diff --git a/test/units/TEST-70-TPM2.tpm2-setup.sh b/test/units/TEST-70-TPM2.tpm2-setup.sh
new file mode 100755 (executable)
index 0000000..faf6fe7
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+export SYSTEMD_LOG_LEVEL=debug
+SD_TPM2SETUP="/usr/lib/systemd/systemd-tpm2-setup"
+
+if [[ ! -x "${SD_TPM2SETUP:?}" ]]; then
+    echo "$SD_TPM2SETUP not found, skipping the test"
+    exit 0
+fi
+
+"$SD_TPM2SETUP" --help
+"$SD_TPM2SETUP" --version
+"$SD_TPM2SETUP" --tpm2-device=list
+"$SD_TPM2SETUP" --tpm2-device=auto
+"$SD_TPM2SETUP" --tpm2-device=/dev/tpm0
+"$SD_TPM2SETUP" --early=yes
+"$SD_TPM2SETUP" --early=yes
+"$SD_TPM2SETUP" --early=no
+"$SD_TPM2SETUP" --early=no
+
+(! "$SD_TPM2SETUP" "")
+(! "$SD_TPM2SETUP" --tpm2-device=)
+(! "$SD_TPM2SETUP" --tpm2-device=/dev/null)
+(! "$SD_TPM2SETUP" --foo=bar)
diff --git a/test/units/TEST-71-HOSTNAME.service b/test/units/TEST-71-HOSTNAME.service
new file mode 100644 (file)
index 0000000..1718629
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-71-HOSTNAME
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-71-HOSTNAME.sh b/test/units/TEST-71-HOSTNAME.sh
new file mode 100755 (executable)
index 0000000..dc3f587
--- /dev/null
@@ -0,0 +1,242 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+restore_hostname() {
+    if [[ -e /tmp/hostname.bak ]]; then
+        mv /tmp/hostname.bak /etc/hostname
+    else
+        rm -f /etc/hostname
+    fi
+}
+
+testcase_hostname() {
+    local orig=
+
+    if [[ -f /etc/hostname ]]; then
+        cp /etc/hostname /tmp/hostname.bak
+        orig=$(cat /etc/hostname)
+    fi
+
+    trap restore_hostname RETURN
+
+    # should activate daemon and work
+    if [[ -n "$orig" ]]; then
+        assert_in "Static hostname: $orig" "$(hostnamectl)"
+    fi
+    assert_in "Kernel: $(uname -s) $(uname -r)" "$(hostnamectl)"
+
+    # change hostname
+    assert_rc 0 hostnamectl set-hostname testhost
+    assert_eq "$(cat /etc/hostname)" "testhost"
+    assert_in "Static hostname: testhost" "$(hostnamectl)"
+
+    if [[ -n "$orig" ]]; then
+        # reset to original
+        assert_rc 0 hostnamectl set-hostname "$orig"
+        assert_eq "$(cat /etc/hostname)" "$orig"
+        assert_in "Static hostname: $orig" "$(hostnamectl)"
+    fi
+}
+
+restore_machine_info() {
+    if [[ -e /tmp/machine-info.bak ]]; then
+        mv /tmp/machine-info.bak /etc/machine-info
+    else
+        rm -f /etc/machine-info
+    fi
+}
+
+get_chassis() (
+    # shellcheck source=/dev/null
+    . /etc/machine-info
+
+    echo "$CHASSIS"
+)
+
+stop_hostnamed() {
+    systemctl stop systemd-hostnamed.service
+    # Reset trigger limit. This might fail if the unit was unloaded already, so ignore any errors.
+    systemctl reset-failed systemd-hostnamed || :
+}
+
+testcase_chassis() {
+    local i
+
+    if [[ -f /etc/machine-info ]]; then
+        cp /etc/machine-info /tmp/machine-info.bak
+    fi
+
+    trap restore_machine_info RETURN
+
+    # Invalid chassis type is refused
+    assert_rc 1 hostnamectl chassis hoge
+
+    # Valid chassis types
+    for i in vm container desktop laptop convertible server tablet handset watch embedded; do
+        hostnamectl chassis "$i"
+        assert_eq "$(hostnamectl chassis)" "$i"
+        assert_eq "$(get_chassis)" "$i"
+    done
+
+    stop_hostnamed
+    rm -f /etc/machine-info
+
+    # fallback chassis type
+    if systemd-detect-virt --quiet --container; then
+        assert_eq "$(hostnamectl chassis)" container
+    elif systemd-detect-virt --quiet --vm; then
+        assert_eq "$(hostnamectl chassis)" vm
+    fi
+}
+
+restore_sysfs_dmi() {
+    umount /sys/class/dmi/id
+    rm -rf /run/systemd/system/systemd-hostnamed.service.d
+    systemctl daemon-reload
+    stop_hostnamed
+}
+
+testcase_firmware_date() {
+    # No DMI on s390x or ppc
+    if [[ ! -d /sys/class/dmi/id ]]; then
+        echo "/sys/class/dmi/id not found, skipping firmware date tests."
+        return 0
+    fi
+
+    trap restore_sysfs_dmi RETURN
+
+    # Ignore /sys being mounted as tmpfs
+    mkdir -p /run/systemd/system/systemd-hostnamed.service.d/
+    cat >/run/systemd/system/systemd-hostnamed.service.d/override.conf <<EOF
+[Service]
+Environment="SYSTEMD_DEVICE_VERIFY_SYSFS=0"
+Environment="SYSTEMD_HOSTNAME_FORCE_DMI=1"
+EOF
+    systemctl daemon-reload
+
+    mount -t tmpfs none /sys/class/dmi/id
+    echo '1' >/sys/class/dmi/id/uevent
+
+    echo '09/08/2000' >/sys/class/dmi/id/bios_date
+    stop_hostnamed
+    assert_in '2000-09-08' "$(hostnamectl)"
+
+    echo '2022' >/sys/class/dmi/id/bios_date
+    stop_hostnamed
+    assert_not_in 'Firmware Date' "$(hostnamectl)"
+
+    echo 'garbage' >/sys/class/dmi/id/bios_date
+    stop_hostnamed
+    assert_not_in 'Firmware Date' "$(hostnamectl)"
+}
+
+testcase_nss-myhostname() {
+    local database host i
+
+    HOSTNAME="$(hostnamectl hostname)"
+
+    # Set up a dummy network for _gateway and _outbound labels
+    ip link add foo type dummy
+    ip link set up dev foo
+    ip addr add 10.0.0.2/24 dev foo
+    for i in {128..150}; do
+        ip addr add "10.0.0.$i/24" dev foo
+    done
+    ip route add 10.0.0.1 dev foo
+    ip route add default via 10.0.0.1 dev foo
+
+    # Note: `getent hosts` probes gethostbyname2(), whereas `getent ahosts` probes gethostbyname3()
+    #       and gethostbyname4() (through getaddrinfo() -> gaih_inet() -> get_nss_addresses())
+    getent hosts -s myhostname
+    getent ahosts -s myhostname
+
+    # With IPv6 disabled
+    sysctl -w net.ipv6.conf.all.disable_ipv6=1
+    # Everything under .localhost and .localhost.localdomain should resolve to localhost
+    for host in {foo.,foo.bar.baz.,.,}localhost{,.} {foo.,foo.bar.baz.,.,}localhost.localdomain{,.}; do
+        run_and_grep "^127\.0\.0\.1\s+localhost$" getent hosts -s myhostname "$host"
+        run_and_grep "^127\.0\.0\.1\s+STREAM\s+localhost" getent ahosts -s myhostname "$host"
+        run_and_grep "^127\.0\.0\.1\s+STREAM\s+localhost" getent ahostsv4 -s myhostname "$host"
+        (! getent ahostsv6 -s myhostname localhost)
+    done
+    for i in 2 {128..150}; do
+        run_and_grep "^10\.0\.0\.$i\s+$HOSTNAME$" getent hosts -s myhostname "$HOSTNAME"
+        run_and_grep "^10\.0\.0\.$i\s+" getent ahosts -s myhostname "$HOSTNAME"
+        run_and_grep "^10\.0\.0\.$i\s+" getent ahostsv4 -s myhostname "$HOSTNAME"
+        run_and_grep "^10\.0\.0\.$i\s+$HOSTNAME$" getent hosts -s myhostname "10.0.0.$i"
+        run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahosts -s myhostname "10.0.0.$i"
+        run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahostsv4 -s myhostname "10.0.0.$i"
+    done
+    for database in hosts ahosts ahostsv4 ahostsv6; do
+        (! getent "$database" -s myhostname ::1)
+    done
+    (! getent ahostsv6 -s myhostname "$HOSTNAME")
+    run_and_grep -n "^fe80:[^ ]+\s+STREAM$" getent ahosts -s myhostname "$HOSTNAME"
+
+    # With IPv6 enabled
+    sysctl -w net.ipv6.conf.all.disable_ipv6=0
+    # Everything under .localhost and .localhost.localdomain should resolve to localhost
+    for host in {foo.,foo.bar.baz.,.,}localhost{,.} {foo.,foo.bar.baz.,.,}localhost.localdomain{,.}; do
+        run_and_grep "^::1\s+localhost$" getent hosts -s myhostname "$host"
+        run_and_grep "^::1\s+STREAM" getent ahosts -s myhostname "$host"
+        run_and_grep "^127\.0\.0\.1\s+STREAM" getent ahosts -s myhostname "$host"
+        run_and_grep "^127\.0\.0\.1\s+STREAM" getent ahostsv4 -s myhostname "$host"
+        run_and_grep -n "^::1\s+STREAM" getent ahostsv4 -s myhostname "$host"
+        run_and_grep "^::1\s+STREAM" getent ahostsv6 -s myhostname "$host"
+        run_and_grep -n "^127\.0\.0\.1\s+STREAM" getent ahostsv6 -s myhostname "$host"
+    done
+    for i in 2 {128..150}; do
+        run_and_grep "^10\.0\.0\.$i\s+" getent ahosts -s myhostname "$HOSTNAME"
+        run_and_grep "^10\.0\.0\.$i\s+" getent ahostsv4 -s myhostname "$HOSTNAME"
+        run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahosts -s myhostname "10.0.0.$i"
+        run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahostsv4 -s myhostname "10.0.0.$i"
+    done
+    run_and_grep "^fe80:[^ ]+\s+$HOSTNAME$" getent hosts -s myhostname "$HOSTNAME"
+    run_and_grep "^fe80:[^ ]+\s+STREAM" getent ahosts -s myhostname "$HOSTNAME"
+    run_and_grep "^127\.0\.0\.1\s+localhost$" getent hosts -s myhostname 127.0.0.1
+    run_and_grep "^127\.0\.0\.1\s+STREAM\s+127\.0\.0\.1$" getent ahosts -s myhostname 127.0.0.1
+    run_and_grep "^::ffff:127\.0\.0\.1\s+STREAM\s+127\.0\.0\.1$" getent ahostsv6 -s myhostname 127.0.0.1
+    run_and_grep "^127\.0\.0\.2\s+$HOSTNAME$" getent hosts -s myhostname 127.0.0.2
+    run_and_grep "^::1\s+localhost $HOSTNAME$" getent hosts -s myhostname ::1
+    run_and_grep "^::1\s+STREAM\s+::1$" getent ahosts -s myhostname ::1
+    (! getent ahostsv4 -s myhostname ::1)
+
+    # _gateway
+    for host in _gateway{,.} 10.0.0.1; do
+        run_and_grep "^10\.0\.0\.1\s+_gateway$" getent hosts -s myhostname "$host"
+        run_and_grep "^10\.0\.0\.1\s+STREAM" getent ahosts -s myhostname "$host"
+    done
+
+    # _outbound
+    for host in _outbound{,.} 10.0.0.2; do
+        run_and_grep "^10\.0\.0\.2\s+" getent hosts -s myhostname "$host"
+        run_and_grep "^10\.0\.0\.2\s+STREAM" getent ahosts -s myhostname "$host"
+    done
+
+    # Non-existent records
+    for database in hosts ahosts ahostsv4 ahostsv6; do
+        (! getent "$database" -s myhostname this.should.not.exist)
+    done
+    (! getent hosts -s myhostname 10.254.254.1)
+    (! getent hosts -s myhostname fd00:dead:beef:cafe::1)
+}
+
+test_varlink() {
+    A="$(mktemp -u)"
+    B="$(mktemp -u)"
+    varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}' --json=short > "$A"
+    hostnamectl --json=short > "$B"
+    cmp "$A" "$B"
+}
+
+run_testcases
+
+touch /testok
diff --git a/test/units/TEST-72-SYSUPDATE.service b/test/units/TEST-72-SYSUPDATE.service
new file mode 100644 (file)
index 0000000..1640350
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-72-SYSUPDATE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-72-SYSUPDATE.sh b/test/units/TEST-72-SYSUPDATE.sh
new file mode 100755 (executable)
index 0000000..5e658e0
--- /dev/null
@@ -0,0 +1,273 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -eux
+set -o pipefail
+
+SYSUPDATE=/lib/systemd/systemd-sysupdate
+SECTOR_SIZES=(512 4096)
+WORKDIR="$(mktemp -d /var/tmp/test-72-XXXXXX)"
+BACKING_FILE="$WORKDIR/joined.raw"
+export SYSTEMD_ESP_PATH="$WORKDIR/esp"
+export SYSTEMD_XBOOTLDR_PATH="$WORKDIR/xbootldr"
+export SYSTEMD_PAGER=cat
+export SYSTEMD_LOG_LEVEL=debug
+
+if [[ ! -x "$SYSUPDATE" ]]; then
+    echo "no systemd-sysupdate" >/skipped
+    exit 77
+fi
+
+# Loopback devices may not be supported. They are used because sfdisk cannot
+# change the sector size of a file, and we want to test both 512 and 4096 byte
+# sectors. If loopback devices are not supported, we can only test one sector
+# size, and the underlying device is likely to have a sector size of 512 bytes.
+if [[ ! -e /dev/loop-control ]]; then
+    echo "No loopback device support"
+    SECTOR_SIZES=(512)
+fi
+
+at_exit() {
+    set +e
+
+    losetup -n --output NAME --associated "$BACKING_FILE" | while read -r loop_dev; do
+        losetup --detach "$loop_dev"
+    done
+
+    rm -rf "$WORKDIR"
+}
+
+trap at_exit EXIT
+
+new_version() {
+    local sector_size="${1:?}"
+    local version="${2:?}"
+
+    # Create a pair of random partition payloads, and compress one
+    dd if=/dev/urandom of="$WORKDIR/source/part1-$version.raw" bs="$sector_size" count=2048
+    dd if=/dev/urandom of="$WORKDIR/source/part2-$version.raw" bs="$sector_size" count=2048
+    gzip -k -f "$WORKDIR/source/part2-$version.raw"
+
+    # Create a random "UKI" payload
+    echo $RANDOM >"$WORKDIR/source/uki-$version.efi"
+
+    # Create a random extra payload
+    echo $RANDOM >"$WORKDIR/source/uki-extra-$version.efi"
+
+    # Create tarball of a directory
+    mkdir -p "$WORKDIR/source/dir-$version"
+    echo $RANDOM >"$WORKDIR/source/dir-$version/foo.txt"
+    echo $RANDOM >"$WORKDIR/source/dir-$version/bar.txt"
+    tar --numeric-owner -C "$WORKDIR/source/dir-$version/" -czf "$WORKDIR/source/dir-$version.tar.gz" .
+
+    (cd "$WORKDIR/source" && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS)
+}
+
+update_now() {
+    # Update to newest version. First there should be an update ready, then we
+    # do the update, and then there should not be any ready anymore
+
+    "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new
+    "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no update
+    (! "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new)
+}
+
+verify_version() {
+    local block_device="${1:?}"
+    local sector_size="${2:?}"
+    local version="${3:?}"
+    local part1_number="${4:?}"
+    local part2_number="${5:?}"
+    local gpt_reserved_sectors part1_offset part2_offset
+
+    gpt_reserved_sectors=$((1024 * 1024 / sector_size))
+    part1_offset=$(((part1_number - 1) * 2048 + gpt_reserved_sectors))
+    part2_offset=$(((part2_number - 1) * 2048 + gpt_reserved_sectors))
+
+    # Check the partitions
+    dd if="$block_device" bs="$sector_size" skip="$part1_offset" count=2048 | cmp "$WORKDIR/source/part1-$version.raw"
+    dd if="$block_device" bs="$sector_size" skip="$part2_offset" count=2048 | cmp "$WORKDIR/source/part2-$version.raw"
+
+    # Check the UKI
+    cmp "$WORKDIR/source/uki-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version+3-0.efi"
+    test -z "$(ls -A "$WORKDIR/esp/EFI/Linux")"
+
+    # Check the extra efi
+    cmp "$WORKDIR/source/uki-extra-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version.efi.extra.d/extra.addon.efi"
+
+    # Check the directories
+    cmp "$WORKDIR/source/dir-$version/foo.txt" "$WORKDIR/dirs/current/foo.txt"
+    cmp "$WORKDIR/source/dir-$version/bar.txt" "$WORKDIR/dirs/current/bar.txt"
+}
+
+for sector_size in "${SECTOR_SIZES[@]}"; do
+    # Disk size of:
+    # - 1MB for GPT
+    # - 4 partitions of 2048 sectors each
+    # - 1MB for backup GPT
+    disk_size=$((sector_size * 2048 * 4 + 1024 * 1024 * 2))
+    rm -f "$BACKING_FILE"
+    truncate -s "$disk_size" "$BACKING_FILE"
+
+    if [[ -e /dev/loop-control ]]; then
+        blockdev="$(losetup --find --show --sector-size "$sector_size" "$BACKING_FILE")"
+    else
+        blockdev="$BACKING_FILE"
+    fi
+
+    sfdisk "$blockdev" <<EOF
+label: gpt
+unit: sectors
+sector-size: $sector_size
+
+size=2048, type=4f68bce3-e8cd-4db1-96e7-fbcaf984b709, name=_empty
+size=2048, type=4f68bce3-e8cd-4db1-96e7-fbcaf984b709, name=_empty
+size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
+size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
+EOF
+
+    for d in "dirs" "defs"; do
+        rm -rf "${WORKDIR:?}/$d"
+        mkdir -p "$WORKDIR/$d"
+    done
+
+    cat >"$WORKDIR/defs/01-first.conf" <<EOF
+[Source]
+Type=regular-file
+Path=$WORKDIR/source
+MatchPattern=part1-@v.raw
+
+[Target]
+Type=partition
+Path=$blockdev
+MatchPattern=part1-@v
+MatchPartitionType=root-x86-64
+EOF
+
+    cat >"$WORKDIR/defs/02-second.conf" <<EOF
+[Source]
+Type=regular-file
+Path=$WORKDIR/source
+MatchPattern=part2-@v.raw.gz
+
+[Target]
+Type=partition
+Path=$blockdev
+MatchPattern=part2-@v
+MatchPartitionType=root-x86-64-verity
+EOF
+
+    cat >"$WORKDIR/defs/03-third.conf" <<EOF
+[Source]
+Type=directory
+Path=$WORKDIR/source
+MatchPattern=dir-@v
+
+[Target]
+Type=directory
+Path=$WORKDIR/dirs
+CurrentSymlink=$WORKDIR/dirs/current
+MatchPattern=dir-@v
+InstancesMax=3
+EOF
+
+    cat >"$WORKDIR/defs/04-fourth.conf" <<EOF
+[Source]
+Type=regular-file
+Path=$WORKDIR/source
+MatchPattern=uki-@v.efi
+
+[Target]
+Type=regular-file
+Path=/EFI/Linux
+PathRelativeTo=boot
+MatchPattern=uki_@v+@l-@d.efi \
+            uki_@v+@l.efi \
+            uki_@v.efi
+Mode=0444
+TriesLeft=3
+TriesDone=0
+InstancesMax=2
+EOF
+
+    cat >"$WORKDIR/defs/05-fifth.conf" <<EOF
+[Source]
+Type=regular-file
+Path=$WORKDIR/source
+MatchPattern=uki-extra-@v.efi
+
+[Target]
+Type=regular-file
+Path=/EFI/Linux
+PathRelativeTo=boot
+MatchPattern=uki_@v.efi.extra.d/extra.addon.efi
+Mode=0444
+InstancesMax=2
+EOF
+
+    rm -rf "${WORKDIR:?}"/{esp,xbootldr,source}
+    mkdir -p "$WORKDIR"/{source,esp/EFI/Linux,xbootldr/EFI/Linux}
+
+    # Install initial version and verify
+    new_version "$sector_size" v1
+    update_now
+    verify_version "$blockdev" "$sector_size" v1 1 3
+
+    # Create second version, update and verify that it is added
+    new_version "$sector_size" v2
+    update_now
+    verify_version "$blockdev" "$sector_size" v2 2 4
+
+    # Create third version, update and verify it replaced the first version
+    new_version "$sector_size" v3
+    update_now
+    verify_version "$blockdev" "$sector_size" v3 1 3
+    test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v1+3-0.efi"
+    test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v1.efi.extra.d/extra.addon.efi"
+    test ! -d "$WORKDIR/xbootldr/EFI/Linux/uki_v1.efi.extra.d"
+
+    # Create fourth version, and update through a file:// URL. This should be
+    # almost as good as testing HTTP, but is simpler for us to set up. file:// is
+    # abstracted in curl for us, and since our main goal is to test our own code
+    # (and not curl) this test should be quite good even if not comprehensive. This
+    # will test the SHA256SUMS logic at least (we turn off GPG validation though,
+    # see above)
+    new_version "$sector_size" v4
+
+    cat >"$WORKDIR/defs/02-second.conf" <<EOF
+[Source]
+Type=url-file
+Path=file://$WORKDIR/source
+MatchPattern=part2-@v.raw.gz
+
+[Target]
+Type=partition
+Path=$blockdev
+MatchPattern=part2-@v
+MatchPartitionType=root-x86-64-verity
+EOF
+
+    cat >"$WORKDIR/defs/03-third.conf" <<EOF
+[Source]
+Type=url-tar
+Path=file://$WORKDIR/source
+MatchPattern=dir-@v.tar.gz
+
+[Target]
+Type=directory
+Path=$WORKDIR/dirs
+CurrentSymlink=$WORKDIR/dirs/current
+MatchPattern=dir-@v
+InstancesMax=3
+EOF
+
+    update_now
+    verify_version "$blockdev" "$sector_size" v4 2 4
+
+    # Cleanup
+    [[ -b "$blockdev" ]] && losetup --detach "$blockdev"
+    rm "$BACKING_FILE"
+done
+
+touch /testok
diff --git a/test/units/TEST-73-LOCALE.service b/test/units/TEST-73-LOCALE.service
new file mode 100644 (file)
index 0000000..3ebd24d
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-73-LOCALE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-73-LOCALE.sh b/test/units/TEST-73-LOCALE.sh
new file mode 100755 (executable)
index 0000000..18539b8
--- /dev/null
@@ -0,0 +1,663 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+enable_debug() {
+    mkdir -p /run/systemd/system/systemd-localed.service.d
+    cat >>/run/systemd/system/systemd-localed.service.d/override.conf <<EOF
+[Service]
+Environment=SYSTEMD_LOG_LEVEL=debug
+EOF
+
+    mkdir -p /run/systemd/system/systemd-vconsole-setup.service.d
+    cat >>/run/systemd/system/systemd-vconsole-setup.service.d/override.conf <<EOF
+[Unit]
+StartLimitIntervalSec=0
+
+[Service]
+Environment=SYSTEMD_LOG_LEVEL=debug
+EOF
+
+    systemctl daemon-reload
+}
+
+testcase_locale() {
+    local i output
+
+    if [[ -f /etc/locale.conf ]]; then
+        cp /etc/locale.conf /tmp/locale.conf.bak
+    fi
+
+    # Debian/Ubuntu specific file
+    if [[ -f /etc/default/locale ]]; then
+        cp /etc/default/locale /tmp/default-locale.bak
+    fi
+
+    if [[ -f /etc/locale.gen ]]; then
+        cp /etc/locale.gen /tmp/locale.gen.bak
+    fi
+
+    # remove locale.conf to make /etc/default/locale used by Debian/Ubuntu
+    rm -f /etc/locale.conf
+    # also remove /etc/default/locale
+    rm -f /etc/default/locale
+    # and create /etc/default to make /etc/default/locale created by localed
+    mkdir -p /etc/default
+
+    trap restore_locale RETURN
+    # Ensure at least one UTF-8 locale exists.
+    generate_locale en_US.UTF-8
+
+    # create invalid locale
+    mkdir -p /usr/lib/locale/xx_XX.UTF-8
+    assert_not_in "xx_XX.UTF-8" "$(localectl list-locales)"
+
+    if [[ -z "$(localectl list-locales)" ]]; then
+        echo "No locale installed, skipping test."
+        return
+    fi
+
+    # start with a known default environment and make sure to also give a
+    # default value to LC_CTYPE= since we're about to also set/unset it. We
+    # also reload PID1 configuration to make sure that PID1 environment itself
+    # is updated as it's not always been the case.
+    assert_rc 0 localectl set-locale "LANG=en_US.UTF-8" "LC_CTYPE=C"
+    systemctl daemon-reload
+    output=$(localectl)
+    assert_in "System Locale: LANG=en_US.UTF-8" "$output"
+    assert_in "LC_CTYPE=C" "$output"
+    output=$(systemctl show-environment)
+    assert_in "LANG=en_US.UTF-8" "$output"
+    assert_in "LC_CTYPE=C" "$output"
+
+    # warn when kernel command line has locale settings
+    output=$(SYSTEMD_PROC_CMDLINE="locale.LANG=C.UTF-8 locale.LC_CTYPE=ja_JP.UTF-8" localectl 2>&1)
+    assert_in "Warning:" "$output"
+    assert_in "Command Line: LANG=C.UTF-8" "$output"
+    assert_in "LC_CTYPE=ja_JP.UTF-8" "$output"
+    assert_in "System Locale:" "$output"
+
+    # change locale
+    for i in $(localectl list-locales); do
+        assert_rc 0 localectl set-locale "LANG=C" "LC_CTYPE=$i"
+        if [[ -f /etc/default/locale ]]; then
+            assert_eq "$(cat /etc/default/locale)" "LANG=C
+LC_CTYPE=$i"
+        else
+            assert_eq "$(cat /etc/locale.conf)" "LANG=C
+LC_CTYPE=$i"
+        fi
+        output=$(localectl)
+        assert_in "System Locale: LANG=C" "$output"
+        assert_in "LC_CTYPE=$i" "$output"
+        output=$(systemctl show-environment)
+        assert_in "LANG=C" "$output"
+        assert_in "LC_CTYPE=$i" "$output"
+
+        assert_rc 0 localectl set-locale "$i"
+        if [[ -f /etc/default/locale ]]; then
+            assert_eq "$(cat /etc/default/locale)" "LANG=$i"
+        else
+            assert_eq "$(cat /etc/locale.conf)" "LANG=$i"
+        fi
+        output=$(localectl)
+        assert_in "System Locale: LANG=$i" "$output"
+        assert_not_in "LC_CTYPE=" "$output"
+        output=$(systemctl show-environment)
+        assert_in "LANG=$i" "$output"
+        assert_not_in "LC_CTYPE=" "$output"
+    done
+
+    # test if localed auto-runs locale-gen
+    if command -v locale-gen >/dev/null 2>&1 &&
+           ! localectl list-locales | grep -F "de_DE.UTF-8"; then
+
+        # clear previous locale
+        systemctl stop systemd-localed.service
+        rm -f /etc/locale.conf /etc/default/locale
+
+        # change locale
+        assert_rc 0 localectl set-locale de_DE.UTF-8
+        if [[ -f /etc/default/locale ]]; then
+            assert_eq "$(cat /etc/default/locale)" "LANG=de_DE.UTF-8"
+        else
+            assert_eq "$(cat /etc/locale.conf)" "LANG=de_DE.UTF-8"
+        fi
+        assert_in "System Locale: LANG=de_DE.UTF-8" "$(localectl)"
+        assert_in "LANG=de_DE.UTF-8" "$(systemctl show-environment)"
+
+        # ensure tested locale exists and works now
+        assert_in "de_DE.UTF-8" "$(localectl list-locales)"
+    fi
+}
+
+backup_keymap() {
+    if [[ -f /etc/vconsole.conf ]]; then
+        cp /etc/vconsole.conf /tmp/vconsole.conf.bak
+    fi
+
+    if [[ -f /etc/X11/xorg.conf.d/00-keyboard.conf ]]; then
+        cp /etc/X11/xorg.conf.d/00-keyboard.conf /tmp/00-keyboard.conf.bak
+    fi
+
+    # Debian/Ubuntu specific file
+    if [[ -f /etc/default/keyboard ]]; then
+        cp /etc/default/keyboard /tmp/default-keyboard.bak
+    fi
+
+    mkdir -p /etc/default
+}
+
+restore_keymap() {
+    if [[ -f /tmp/vconsole.conf.bak ]]; then
+        mv /tmp/vconsole.conf.bak /etc/vconsole.conf
+    else
+        rm -f /etc/vconsole.conf
+    fi
+
+    if [[ -f /tmp/00-keyboard.conf.bak ]]; then
+        mv /tmp/00-keyboard.conf.bak /etc/X11/xorg.conf.d/00-keyboard.conf
+    else
+        rm -f /etc/X11/xorg.conf.d/00-keyboard.conf
+    fi
+
+    if [[ -f /tmp/default-keyboard.bak ]]; then
+        mv /tmp/default-keyboard.bak /etc/default/keyboard
+    else
+        rm -f /etc/default/keyboard
+        rmdir --ignore-fail-on-non-empty /etc/default
+    fi
+}
+
+wait_vconsole_setup() {
+    local i ss
+    for i in {1..20}; do
+        (( i > 1 )) && sleep 0.5
+        ss="$(systemctl --property SubState --value show systemd-vconsole-setup.service)"
+        if [[ "$ss" == "exited" || "$ss" == "dead" || "$ss" == "condition" ]]; then
+            return 0
+        elif [[ "$ss" == "failed" ]]; then
+            echo "WARNING: systemd-vconsole-setup.service failed, ignoring." >&2
+            systemctl reset-failed systemd-vconsole-setup.service
+            return 0
+        fi
+    done
+
+    systemctl status systemd-vconsole-setup.service
+    return 1
+}
+
+testcase_vc_keymap() {
+    local i output vc
+
+    if [[ -z "$(localectl list-keymaps)" ]]; then
+        echo "No vconsole keymap installed, skipping test."
+        return
+    fi
+
+    backup_keymap
+    trap restore_keymap RETURN
+
+    # should activate daemon and work
+    assert_in "VC Keymap:" "$(localectl)"
+
+    for i in $(localectl list-keymaps); do
+        # clear previous conversion from VC -> X11 keymap
+        systemctl stop systemd-localed.service
+        wait_vconsole_setup
+        rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+
+        # set VC keymap
+        assert_rc 0 localectl set-keymap "$i"
+        output=$(localectl)
+
+        # check VC keymap
+        vc=$(cat /etc/vconsole.conf)
+        assert_in "KEYMAP=$i" "$vc"
+        assert_in "VC Keymap: $i" "$output"
+
+        # check VC -> X11 keymap conversion
+        if [[ "$i" == "us" ]]; then
+            assert_in "X11 Layout: us" "$output"
+            assert_in "X11 Model: pc105\+inet" "$output"
+            assert_not_in "X11 Variant:" "$output"
+            assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
+
+            assert_in "XKBLAYOUT=us" "$vc"
+            assert_in "XKBMODEL=pc105\+inet" "$vc"
+            assert_not_in "XKBVARIANT" "$vc"
+            assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
+        elif [[ "$i" == "us-acentos" ]]; then
+            assert_in "X11 Layout: us" "$output"
+            assert_in "X11 Model: pc105" "$output"
+            assert_in "X11 Variant: intl" "$output"
+            assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
+
+            assert_in "XKBLAYOUT=us" "$vc"
+            assert_in "XKBMODEL=pc105" "$vc"
+            assert_in "XKBVARIANT=intl" "$vc"
+            assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
+        elif [[ "$i" =~ ^us-.* ]]; then
+            assert_in "X11 Layout: us" "$output"
+            assert_in "X11 Model: microsoftpro" "$output"
+            assert_in "X11 Variant:" "$output"
+            assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
+
+            assert_in "XKBLAYOUT=us" "$vc"
+            assert_in "XKBMODEL=microsoftpro" "$vc"
+            assert_in "XKBVARIANT=" "$vc"
+            assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
+        fi
+    done
+
+    # gets along without config file
+    systemctl stop systemd-localed.service
+    wait_vconsole_setup
+    rm -f /etc/vconsole.conf
+    assert_in "VC Keymap: .unset." "$(localectl)"
+}
+
+testcase_x11_keymap() {
+    local output
+
+    if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then
+        echo "No x11 keymap installed, skipping test."
+        return
+    fi
+
+    backup_keymap
+    trap restore_keymap RETURN
+
+    # should activate daemon and work
+    assert_in "X11 Layout:" "$(localectl)"
+
+    # set x11 keymap (layout, model, variant, options)
+    assert_rc 0 localectl set-x11-keymap us pc105+inet intl terminate:ctrl_alt_bksp
+
+    if [[ -f /etc/default/keyboard ]]; then
+        assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us
+XKBMODEL=pc105+inet
+XKBVARIANT=intl
+XKBOPTIONS=terminate:ctrl_alt_bksp"
+    else
+        output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
+        assert_in 'Option "XkbLayout" "us"' "$output"
+        assert_in 'Option "XkbModel" "pc105\+inet"' "$output"
+        assert_in 'Option "XkbVariant" "intl"' "$output"
+        assert_in 'Option "XkbOptions" "terminate:ctrl_alt_bksp"' "$output"
+
+        output=$(cat /etc/vconsole.conf)
+        assert_in 'XKBLAYOUT=us' "$output"
+        assert_in 'XKBMODEL=pc105\+inet' "$output"
+        assert_in 'XKBVARIANT=intl' "$output"
+        assert_in 'XKBOPTIONS=terminate:ctrl_alt_bksp' "$output"
+    fi
+
+    output=$(localectl)
+    assert_in "X11 Layout: us" "$output"
+    assert_in "X11 Model: pc105\+inet" "$output"
+    assert_in "X11 Variant: intl" "$output"
+    assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
+
+    # Debian/Ubuntu patch is buggy, unspecified settings are not cleared
+    rm -f /etc/default/keyboard
+
+    # set x11 keymap (layout, model, variant)
+    assert_rc 0 localectl set-x11-keymap us pc105+inet intl
+
+    if [[ -f /etc/default/keyboard ]]; then
+        assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us
+XKBMODEL=pc105+inet
+XKBVARIANT=intl"
+    else
+        output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
+        assert_in 'Option "XkbLayout" "us"' "$output"
+        assert_in 'Option "XkbModel" "pc105\+inet"' "$output"
+        assert_in 'Option "XkbVariant" "intl"' "$output"
+        assert_not_in 'Option "XkbOptions"' "$output"
+
+        output=$(cat /etc/vconsole.conf)
+        assert_in 'XKBLAYOUT=us' "$output"
+        assert_in 'XKBMODEL=pc105\+inet' "$output"
+        assert_in 'XKBVARIANT=intl' "$output"
+        assert_not_in 'XKBOPTIONS' "$output"
+    fi
+
+    output=$(localectl)
+    assert_in "X11 Layout: us" "$output"
+    assert_in "X11 Model: pc105\+inet" "$output"
+    assert_in "X11 Variant: intl" "$output"
+    assert_not_in "X11 Options:" "$output"
+
+    # Debian/Ubuntu patch is buggy, unspecified settings are not cleared
+    rm -f /etc/default/keyboard
+
+    # set x11 keymap (layout, model)
+    assert_rc 0 localectl set-x11-keymap us pc105+inet
+
+    if [[ -f /etc/default/keyboard ]]; then
+        assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us
+XKBMODEL=pc105+inet"
+    else
+        output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
+        assert_in 'Option "XkbLayout" "us"' "$output"
+        assert_in 'Option "XkbModel" "pc105\+inet"' "$output"
+        assert_not_in 'Option "XkbVariant"' "$output"
+        assert_not_in 'Option "XkbOptions"' "$output"
+
+        output=$(cat /etc/vconsole.conf)
+        assert_in 'XKBLAYOUT=us' "$output"
+        assert_in 'XKBMODEL=pc105\+inet' "$output"
+        assert_not_in 'XKBVARIANT' "$output"
+        assert_not_in 'XKBOPTIONS' "$output"
+    fi
+
+    output=$(localectl)
+    assert_in "X11 Layout: us" "$output"
+    assert_in "X11 Model: pc105\+inet" "$output"
+    assert_not_in "X11 Variant:" "$output"
+    assert_not_in "X11 Options:" "$output"
+
+    # Debian/Ubuntu patch is buggy, unspecified settings are not cleared
+    rm -f /etc/default/keyboard
+
+    # set x11 keymap (layout)
+    assert_rc 0 localectl set-x11-keymap us
+
+    if [[ -f /etc/default/keyboard ]]; then
+        assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us"
+    else
+        output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
+        assert_in 'Option "XkbLayout" "us"' "$output"
+        assert_not_in 'Option "XkbModel"' "$output"
+        assert_not_in 'Option "XkbVariant"' "$output"
+        assert_not_in 'Option "XkbOptions"' "$output"
+
+        output=$(cat /etc/vconsole.conf)
+        assert_in 'XKBLAYOUT=us' "$output"
+        assert_not_in 'XKBMODEL' "$output"
+        assert_not_in 'XKBVARIANT' "$output"
+        assert_not_in 'XKBOPTIONS' "$output"
+    fi
+
+    output=$(localectl)
+    assert_in "X11 Layout: us" "$output"
+    assert_not_in "X11 Model:" "$output"
+    assert_not_in "X11 Variant:" "$output"
+    assert_not_in "X11 Options:" "$output"
+
+    # gets along without config file
+    systemctl stop systemd-localed.service
+    rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+    output=$(localectl)
+    assert_in "X11 Layout: .unset." "$output"
+    assert_not_in "X11 Model:" "$output"
+    assert_not_in "X11 Variant:" "$output"
+    assert_not_in "X11 Options:" "$output"
+}
+
+testcase_convert() {
+    if [[ -z "$(localectl list-keymaps)" ]]; then
+        echo "No vconsole keymap installed, skipping test."
+        return
+    fi
+
+    if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then
+        echo "No x11 keymap installed, skipping test."
+        return
+    fi
+
+    backup_keymap
+    trap restore_keymap RETURN
+
+    # clear previous settings
+    systemctl stop systemd-localed.service
+    wait_vconsole_setup
+    rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+
+    # set VC keymap without conversion
+    assert_rc 0 localectl --no-convert set-keymap us
+    output=$(localectl)
+
+    # check VC keymap
+    vc=$(cat /etc/vconsole.conf)
+    assert_in "KEYMAP=us" "$vc"
+    assert_in "VC Keymap: us" "$output"
+
+    # check VC -> X11 keymap conversion (nothing set)
+    assert_in     "X11 Layout: .unset."  "$output"
+    assert_not_in "X11 Model:"           "$output"
+    assert_not_in "X11 Variant:"         "$output"
+    assert_not_in "X11 Options:"         "$output"
+
+    assert_not_in "XKBLAYOUT="  "$vc"
+    assert_not_in "XKBMODEL="   "$vc"
+    assert_not_in "XKBVARIANT=" "$vc"
+    assert_not_in "XKBOPTIONS=" "$vc"
+
+    # set VC keymap with conversion
+    assert_rc 0 localectl set-keymap us
+    output=$(localectl)
+
+    # check VC keymap
+    vc=$(cat /etc/vconsole.conf)
+    assert_in "KEYMAP=us" "$vc"
+    assert_in "VC Keymap: us" "$output"
+
+    # check VC -> X11 keymap conversion
+    assert_in     "X11 Layout: us"                       "$output"
+    assert_in     "X11 Model: pc105\+inet"               "$output"
+    assert_not_in "X11 Variant:"                         "$output"
+    assert_in     "X11 Options: terminate:ctrl_alt_bksp" "$output"
+
+    assert_in     "XKBLAYOUT=us"                       "$vc"
+    assert_in     "XKBMODEL=pc105\+inet"               "$vc"
+    assert_not_in "XKBVARIANT"                         "$vc"
+    assert_in     "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
+
+    # clear previous settings
+    systemctl stop systemd-localed.service
+    wait_vconsole_setup
+    rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+
+    # set x11 keymap (layout) without conversion
+    assert_rc 0 localectl --no-convert set-x11-keymap us
+
+    assert_not_in "KEYMAP=" "$(cat /etc/vconsole.conf)"
+    assert_in "VC Keymap: .unset." "$(localectl)"
+
+    if [[ -f /etc/default/keyboard ]]; then
+        assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us"
+    else
+        output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
+        assert_in     'Option "XkbLayout" "us"' "$output"
+        assert_not_in 'Option "XkbModel"'       "$output"
+        assert_not_in 'Option "XkbVariant"'     "$output"
+        assert_not_in 'Option "XkbOptions"'     "$output"
+
+        output=$(cat /etc/vconsole.conf)
+        assert_in     'XKBLAYOUT=us' "$output"
+        assert_not_in 'XKBMODEL='    "$output"
+        assert_not_in 'XKBVARIANT='  "$output"
+        assert_not_in 'XKBOPTIONS='  "$output"
+    fi
+
+    output=$(localectl)
+    assert_in     "X11 Layout: us" "$output"
+    assert_not_in "X11 Model:"     "$output"
+    assert_not_in "X11 Variant:"   "$output"
+    assert_not_in "X11 Options:"   "$output"
+
+    # set x11 keymap (layout, model) with conversion
+    assert_rc 0 localectl set-x11-keymap us
+
+    assert_in "KEYMAP=us" "$(cat /etc/vconsole.conf)"
+    assert_in "VC Keymap: us" "$(localectl)"
+
+    if [[ -f /etc/default/keyboard ]]; then
+        assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us"
+    else
+        output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
+        assert_in     'Option "XkbLayout" "us"' "$output"
+        assert_not_in 'Option "XkbModel"'       "$output"
+        assert_not_in 'Option "XkbVariant"'     "$output"
+        assert_not_in 'Option "XkbOptions"'     "$output"
+
+        output=$(cat /etc/vconsole.conf)
+        assert_in     'XKBLAYOUT=us' "$output"
+        assert_not_in 'XKBMODEL='    "$output"
+        assert_not_in 'XKBVARIANT='  "$output"
+        assert_not_in 'XKBOPTIONS='  "$output"
+    fi
+
+    output=$(localectl)
+    assert_in     "X11 Layout: us" "$output"
+    assert_not_in "X11 Model:"     "$output"
+    assert_not_in "X11 Variant:"   "$output"
+    assert_not_in "X11 Options:"   "$output"
+}
+
+testcase_validate() {
+    if [[ -z "$(localectl list-keymaps)" ]]; then
+        echo "No vconsole keymap installed, skipping test."
+        return
+    fi
+
+    if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then
+        echo "No x11 keymap installed, skipping test."
+        return
+    fi
+
+    backup_keymap
+    trap restore_keymap RETURN
+
+    # clear previous settings
+    systemctl stop systemd-localed.service
+    wait_vconsole_setup
+    rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+
+    # create invalid configs
+    cat >/etc/vconsole.conf <<EOF
+KEYMAP=foobar
+XKBLAYOUT=hogehoge
+EOF
+
+    # confirm that the invalid settings are not shown
+    output=$(localectl)
+    assert_in "VC Keymap: .unset."  "$output"
+    if [[ "$output" =~ "X11 Layout: hogehoge" ]]; then
+        # Debian/Ubuntu build systemd without xkbcommon.
+        echo "systemd built without xkbcommon, skipping test."
+        return
+    fi
+    assert_in "X11 Layout: .unset." "$output"
+
+    # only update the virtual console keymap
+    assert_rc 0 localectl --no-convert set-keymap us
+
+    output=$(localectl)
+    assert_in "VC Keymap: us"       "$output"
+    assert_in "X11 Layout: .unset." "$output"
+
+    output=$(cat /etc/vconsole.conf)
+    assert_in     "KEYMAP=us"  "$output"
+    assert_not_in "XKBLAYOUT=" "$output"
+
+    # clear previous settings
+    systemctl stop systemd-localed.service
+    wait_vconsole_setup
+    rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+
+    # create invalid configs
+    cat >/etc/vconsole.conf <<EOF
+KEYMAP=foobar
+XKBLAYOUT=hogehoge
+EOF
+
+    # confirm that the invalid settings are not shown
+    output=$(localectl)
+    assert_in "VC Keymap: .unset."  "$output"
+    assert_in "X11 Layout: .unset." "$output"
+
+    # only update the X11 keyboard layout
+    assert_rc 0 localectl --no-convert set-x11-keymap us
+
+    output=$(localectl)
+    assert_in "VC Keymap: .unset."  "$output"
+    assert_in "X11 Layout: us"      "$output"
+
+    output=$(cat /etc/vconsole.conf)
+    assert_not_in "KEYMAP="      "$output"
+    assert_in     "XKBLAYOUT=us" "$output"
+
+    # clear previous settings
+    systemctl stop systemd-localed.service
+    wait_vconsole_setup
+    rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
+
+    # create invalid configs
+    cat >/etc/vconsole.conf <<EOF
+KEYMAP=foobar
+XKBLAYOUT=hogehoge
+EOF
+
+    # update the virtual console keymap with conversion
+    assert_rc 0 localectl set-keymap us
+
+    output=$(localectl)
+    assert_in "VC Keymap: us"  "$output"
+    assert_in "X11 Layout: us" "$output"
+
+    output=$(cat /etc/vconsole.conf)
+    assert_in "KEYMAP=us"    "$output"
+    assert_in "XKBLAYOUT=us" "$output"
+}
+
+locale_gen_cleanup() {
+    # Some running apps might keep the mount point busy, hence the lazy unmount
+    mountpoint -q /usr/lib/locale && umount --lazy /usr/lib/locale
+    [[ -e /tmp/locale.gen.bak ]] && mv -f /tmp/locale.gen.bak /etc/locale.gen
+
+    return 0
+}
+
+# Issue: https://github.com/systemd/systemd/pull/27179
+testcase_locale_gen_leading_space() {
+    if ! command -v locale-gen >/dev/null; then
+        echo "No locale-gen support, skipping test."
+        return 0
+    fi
+
+    [[ -e /etc/locale.gen ]] && cp -f /etc/locale.gen /tmp/locale.gen.bak
+    trap locale_gen_cleanup RETURN
+    # Overmount the existing locale-gen database with an empty directory
+    # to force it to regenerate locales
+    mount -t tmpfs tmpfs /usr/lib/locale
+
+    {
+        echo -e "en_US.UTF-8 UTF-8"
+        echo -e " en_US.UTF-8 UTF-8"
+        echo -e "\ten_US.UTF-8 UTF-8"
+        echo -e " \t en_US.UTF-8 UTF-8 \t"
+    } >/etc/locale.gen
+
+    localectl set-locale de_DE.UTF-8
+    localectl set-locale en_US.UTF-8
+}
+
+# Make sure the content of kbd-model-map is the one that the tests expect
+# regardless of the version installed on the distro where the testsuite is
+# running on.
+export SYSTEMD_KBD_MODEL_MAP=/usr/lib/systemd/tests/testdata/test-keymap-util/kbd-model-map
+
+enable_debug
+run_testcases
+
+touch /testok
diff --git a/test/units/TEST-74-AUX-UTILS.battery-check.sh b/test/units/TEST-74-AUX-UTILS.battery-check.sh
new file mode 100755 (executable)
index 0000000..52a92b8
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+/usr/lib/systemd/systemd-battery-check --help
+/usr/lib/systemd/systemd-battery-check --version
+
+/usr/lib/systemd/systemd-battery-check || :
diff --git a/test/units/TEST-74-AUX-UTILS.bootctl.sh b/test/units/TEST-74-AUX-UTILS.bootctl.sh
new file mode 100755 (executable)
index 0000000..78c0e6e
--- /dev/null
@@ -0,0 +1,279 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if systemd-detect-virt --quiet --container; then
+    echo "running on container, skipping."
+    exit 0
+fi
+
+if ! command -v bootctl >/dev/null; then
+    echo "bootctl not found, skipping."
+    exit 0
+fi
+
+if [[ ! -d /usr/lib/systemd/boot/efi ]]; then
+    echo "sd-boot is not installed, skipping."
+    exit 0
+fi
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+basic_tests() {
+    bootctl "$@" --help
+    bootctl "$@" --version
+
+    bootctl "$@" install --make-entry-directory=yes
+    bootctl "$@" remove  --make-entry-directory=yes
+
+    bootctl "$@" install --all-architectures
+    bootctl "$@" remove  --all-architectures
+
+    bootctl "$@" install --make-entry-directory=yes --all-architectures
+    bootctl "$@" remove  --make-entry-directory=yes --all-architectures
+
+    bootctl "$@" install
+    (! bootctl "$@" update)
+    bootctl "$@" update --graceful
+
+    bootctl "$@" is-installed
+    bootctl "$@" is-installed --graceful
+    bootctl "$@" random-seed
+
+    bootctl "$@"
+    bootctl "$@" status
+    bootctl "$@" status --quiet
+    bootctl "$@" list
+    bootctl "$@" list --quiet
+    bootctl "$@" list --json=short
+    bootctl "$@" list --json=pretty
+
+    bootctl "$@" remove
+    (! bootctl "$@" is-installed)
+    (! bootctl "$@" is-installed --graceful)
+}
+
+testcase_bootctl_basic() {
+    assert_in "$(bootctl --print-esp-path)" "^(/boot/|/efi)$"
+    assert_in "$(bootctl --print-boot-path)" "^(/boot/|/efi)$"
+    bootctl --print-root-device
+
+    basic_tests
+}
+
+cleanup_image() (
+    set +e
+
+    if [[ -z "${IMAGE_DIR:-}" ]]; then
+        return 0
+    fi
+
+    umount "${IMAGE_DIR}/root"
+
+    if [[ -n "${LOOPDEV:-}" ]]; then
+        losetup -d "${LOOPDEV}"
+        unset LOOPDEV
+    fi
+
+    udevadm settle
+
+    rm -rf "${IMAGE_DIR}"
+    unset IMAGE_DIR
+
+    return 0
+)
+
+testcase_bootctl_image() {
+    IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)"
+    trap cleanup_image RETURN
+
+    truncate -s 256m "${IMAGE_DIR}/image"
+
+    cat >"${IMAGE_DIR}/partscript" <<EOF
+label: gpt
+type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp  size=64M
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=64M bootable
+type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot
+EOF
+
+    LOOPDEV="$(losetup --show -P -f "${IMAGE_DIR}/image")"
+    sfdisk "$LOOPDEV" <"${IMAGE_DIR}/partscript"
+
+    udevadm settle
+
+    mkfs.vfat -n esp  "${LOOPDEV}p1"
+    mkfs.ext4 -L root "${LOOPDEV}p2"
+    mkfs.ext4 -L boot "${LOOPDEV}p3"
+
+    mkdir -p "${IMAGE_DIR}/root"
+    mount -t ext4 "${LOOPDEV}p2" "${IMAGE_DIR}/root"
+
+    mkdir -p "${IMAGE_DIR}/root/efi"
+    mkdir -p "${IMAGE_DIR}/root/boot"
+    mkdir -p "${IMAGE_DIR}/root/etc"
+    mkdir -p "${IMAGE_DIR}/root/usr/lib"
+    if [[ -f /usr/lib/os-release ]]; then
+        cp /usr/lib/os-release "${IMAGE_DIR}/root/usr/lib/."
+        ln -s ../usr/lib/os-release "${IMAGE_DIR}/root/etc/os-release"
+    else
+        cp -a /etc/os-release "${IMAGE_DIR}/root/etc/."
+    fi
+
+    umount "${IMAGE_DIR}/root"
+
+    assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-esp-path)" "/run/systemd/mount-rootfs/efi"
+    assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-esp-path --esp-path=/efi)" "/run/systemd/mount-rootfs/efi"
+    assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-boot-path)" "/run/systemd/mount-rootfs/boot"
+    assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-boot-path --boot-path=/boot)" "/run/systemd/mount-rootfs/boot"
+
+    # FIXME: This provides spurious result.
+    bootctl --image "${IMAGE_DIR}/image" --print-root-device || :
+
+    basic_tests --image "${IMAGE_DIR}/image"
+}
+
+cleanup_raid() (
+    set +e
+
+    if [[ -z "${IMAGE_DIR:-}" ]]; then
+        return 0
+    fi
+
+    systemd-umount "${IMAGE_DIR}/root/efi"
+    systemd-umount "${IMAGE_DIR}/root/boot"
+    systemd-umount "${IMAGE_DIR}/root"
+
+    mdadm --misc --stop /dev/md/raid-esp
+    mdadm --misc --stop /dev/md/raid-root
+
+    if [[ -n "${LOOPDEV1:-}" ]]; then
+        mdadm --misc --force --zero-superblock "${LOOPDEV1}p1"
+        mdadm --misc --force --zero-superblock "${LOOPDEV1}p2"
+    fi
+
+    if [[ -n "${LOOPDEV2:-}" ]]; then
+        mdadm --misc --force --zero-superblock "${LOOPDEV2}p1"
+        mdadm --misc --force --zero-superblock "${LOOPDEV2}p2"
+    fi
+
+    udevadm settle
+
+    if [[ -n "${LOOPDEV1:-}" ]]; then
+        mdadm --misc --force --zero-superblock "${LOOPDEV1}p1"
+        mdadm --misc --force --zero-superblock "${LOOPDEV1}p2"
+        losetup -d "${LOOPDEV1}"
+        unset LOOPDEV1
+    fi
+
+    if [[ -n "${LOOPDEV2:-}" ]]; then
+        mdadm --misc --force --zero-superblock "${LOOPDEV2}p1"
+        mdadm --misc --force --zero-superblock "${LOOPDEV2}p2"
+        losetup -d "${LOOPDEV2}"
+        unset LOOPDEV2
+    fi
+
+    udevadm settle
+
+    rm -rf "${IMAGE_DIR}"
+
+    return 0
+)
+
+testcase_bootctl_raid() {
+    if ! command -v mdadm >/dev/null; then
+        echo "mdadm not found, skipping."
+        return 0
+    fi
+
+    if ! command -v mkfs.btrfs >/dev/null; then
+        echo "mkfs.btrfs not found, skipping."
+        return 0
+    fi
+
+    IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)"
+    trap cleanup_raid RETURN
+
+    truncate -s 256m "${IMAGE_DIR}/image1"
+    truncate -s 256m "${IMAGE_DIR}/image2"
+
+    cat >"${IMAGE_DIR}/partscript" <<EOF
+label: gpt
+type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp  size=64M
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=64M bootable
+type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot
+EOF
+
+    LOOPDEV1="$(losetup --show -P -f "${IMAGE_DIR}/image1")"
+    LOOPDEV2="$(losetup --show -P -f "${IMAGE_DIR}/image2")"
+    sfdisk "$LOOPDEV1" <"${IMAGE_DIR}/partscript"
+    sfdisk "$LOOPDEV2" <"${IMAGE_DIR}/partscript"
+
+    udevadm settle
+
+    echo y | mdadm --create /dev/md/raid-esp --name "raid-esp" "${LOOPDEV1}p1" "${LOOPDEV2}p1" -v -f --level=1 --raid-devices=2
+    mkfs.vfat /dev/md/raid-esp
+    echo y | mdadm --create /dev/md/raid-root --name "raid-root" "${LOOPDEV1}p2" "${LOOPDEV2}p2" -v -f --level=1 --raid-devices=2
+    mkfs.ext4 /dev/md/raid-root
+    mkfs.btrfs -f -M -d raid1 -m raid1 -L "raid-boot" "${LOOPDEV1}p3" "${LOOPDEV2}p3"
+
+    mkdir -p "${IMAGE_DIR}/root"
+    mount -t ext4 /dev/md/raid-root "${IMAGE_DIR}/root"
+    mkdir -p "${IMAGE_DIR}/root/efi"
+    mount -t vfat /dev/md/raid-esp "${IMAGE_DIR}/root/efi"
+    mkdir -p "${IMAGE_DIR}/root/boot"
+    mount -t btrfs "${LOOPDEV1}p3" "${IMAGE_DIR}/root/boot"
+
+    mkdir -p "${IMAGE_DIR}/root/etc"
+    mkdir -p "${IMAGE_DIR}/root/usr/lib"
+    if [[ -f /usr/lib/os-release ]]; then
+        cp /usr/lib/os-release "${IMAGE_DIR}/root/usr/lib/."
+        ln -s ../usr/lib/os-release "${IMAGE_DIR}/root/etc/os-release"
+    else
+        cp -a /etc/os-release "${IMAGE_DIR}/root/etc/."
+    fi
+
+    # find_esp() does not support md RAID partition.
+    (! bootctl --root "${IMAGE_DIR}/root" --print-esp-path)
+    (! bootctl --root "${IMAGE_DIR}/root" --print-esp-path --esp-path=/efi)
+
+    # If the verification is relaxed, it accepts md RAID partition.
+    assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-esp-path)" "${IMAGE_DIR}/root/efi"
+    assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-esp-path --esp-path=/efi)" "${IMAGE_DIR}/root/efi"
+
+    # find_xbootldr() does not support btrfs RAID, and bootctl tries to fall back to use ESP.
+    # (but as in the above, the ESP verification is also failed in this case).
+    (! bootctl --root "${IMAGE_DIR}/root" --print-boot-path)
+    (! bootctl --root "${IMAGE_DIR}/root" --print-boot-path --boot-path=/boot)
+
+    # If the verification for ESP is relaxed, bootctl falls back to use ESP.
+    assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path)" "${IMAGE_DIR}/root/efi"
+
+    # If the verification is relaxed, it accepts the xbootldr partition.
+    assert_eq "$(SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path)" "${IMAGE_DIR}/root/boot"
+    assert_eq "$(SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path --boot-path=/boot)" "${IMAGE_DIR}/root/boot"
+
+    # FIXME: This provides spurious result.
+    bootctl --root "${IMAGE_DIR}/root" --print-root-device || :
+
+    SYSTEMD_RELAX_ESP_CHECKS=yes SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes basic_tests --root "${IMAGE_DIR}/root"
+}
+
+testcase_bootctl_varlink() {
+    varlinkctl call --collect /run/systemd/io.systemd.BootControl io.systemd.BootControl.ListBootEntries '{}'
+
+    # We may have UEFI in the test environment.
+    # If we don't have UEFI then we can test whether bootctl's varlink API fails cleanly.
+    # If we do have UEFI then the rest of the clean fail tests should be skipped.
+    if ! (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.GetRebootToFirmware '{}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported; then
+        return 0
+    fi
+    (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":true}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported
+    (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":false}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported
+}
+
+run_testcases
diff --git a/test/units/TEST-74-AUX-UTILS.busctl.sh b/test/units/TEST-74-AUX-UTILS.busctl.sh
new file mode 100755 (executable)
index 0000000..aaf96d0
--- /dev/null
@@ -0,0 +1,110 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Unset $PAGER so we don't have to use --no-pager everywhere
+export PAGER=
+
+busctl --help
+busctl help
+busctl --version
+busctl
+busctl list --no-pager --allow-interactive-authorization=no
+busctl list
+busctl list --unique --show-machine --full
+# Pass the JSON output (-j) through jq to check if it's valid
+busctl list --acquired --activatable --no-legend -j | jq
+busctl status
+busctl status --machine=.host --augment-creds=no
+busctl status --user --machine=testuser@.host
+busctl status org.freedesktop.systemd1
+# Ignore the exit code here, since this runs during machine bootup, so busctl
+# might attempt to introspect a job that already finished and fail, i.e.:
+# Failed to introspect object /org/freedesktop/systemd1/job/335 of service org.freedesktop.systemd1: Unknown object '/org/freedesktop/systemd1/job/335'.
+busctl tree || :
+busctl tree org.freedesktop.login1
+busctl tree --list org.freedesktop.login1
+busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1
+busctl introspect --watch-bind=yes --xml-interface org.freedesktop.systemd1 /org/freedesktop/LogControl1
+busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager
+
+busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+            GetDefaultTarget
+# Pass both JSON outputs through jq to check if the response JSON is valid
+busctl call --json=pretty \
+            org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+            ListUnitsByNames as 2 "systemd-journald.service" "systemd-logind.service" | jq
+busctl call --json=short \
+            org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+            ListUnitsByNames as 2 "systemd-journald.service" "systemd-logind.service" | jq
+# Get all properties on the org.freedesktop.systemd1.Manager interface and dump
+# them as JSON to exercise the internal JSON transformations
+busctl call -j \
+            org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.DBus.Properties \
+            GetAll s "org.freedesktop.systemd1.Manager" | jq -c
+busctl call --verbose --timeout=60 --expect-reply=yes \
+            org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+            ListUnitsByPatterns asas 1 "active" 2 "systemd-*.socket" "*.mount"
+
+busctl emit /org/freedesktop/login1 org.freedesktop.login1.Manager \
+            PrepareForSleep b false
+busctl emit --auto-start=no --destination=systemd-logind.service \
+            /org/freedesktop/login1 org.freedesktop.login1.Manager \
+            PrepareForShutdown b false
+
+busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+                    Version
+busctl get-property --verbose \
+                    org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+                    LogLevel LogTarget SystemState Version
+# Pass both JSON outputs through jq to check if the response JSON is valid
+busctl get-property --json=pretty \
+                    org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+                    LogLevel LogTarget SystemState Version | jq
+busctl get-property --json=short \
+                    org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+                    LogLevel LogTarget SystemState Version | jq
+
+# Set a property and check if it was indeed set
+busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+                    KExecWatchdogUSec t 666
+busctl get-property -j \
+                    org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+                    KExecWatchdogUSec | jq -e '.data == 666'
+
+(! busctl status org.freedesktop.systemd2)
+(! busctl tree org.freedesktop.systemd2)
+(! busctl introspect org.freedesktop.systemd1)
+(! busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd2)
+(! busctl introspect org.freedesktop.systemd2 /org/freedesktop/systemd1)
+
+# Invalid method
+(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+               ThisMethodDoesntExist)
+# Invalid signature
+(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+               ListUnitsByNames ab 1 false)
+# Invalid arguments
+(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+               GetUnitByPID u "hello")
+(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+               -- ListUnitsByNames as -1 "systemd-journald.service")
+# Not enough arguments
+(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+               ListUnitsByNames as 99 "systemd-journald.service")
+
+(! busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+                       NonexistentProperty)
+(! busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+                       Version NonexistentProperty Version)
+
+# Invalid property
+(! busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+                       NonexistentProperty t 666)
+# Invalid signature
+(! busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+                       KExecWatchdogUSec s 666)
+# Invalid argument
+(! busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+                       KExecWatchdogUSec t "foo")
diff --git a/test/units/TEST-74-AUX-UTILS.capsule.sh b/test/units/TEST-74-AUX-UTILS.capsule.sh
new file mode 100755 (executable)
index 0000000..e7b5c87
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+set -eux
+set -o pipefail
+
+at_exit() {
+    set +e
+    systemctl --no-block stop capsule@foobar.service
+    rm -rf /run/capsules/foobar
+    rm -rf /var/lib/capsules/foobar
+    rm -f /run/systemd/system/capsule@.service.d/99-asan.conf
+}
+
+trap at_exit EXIT
+
+# Appease ASan, since the capsule@.service uses DynamicUser=yes
+systemctl edit --runtime --stdin capsule@.service --drop-in=99-asan.conf <<EOF
+[Service]
+EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
+EOF
+
+(! test -f /run/capsules/foobar )
+(! test -f /var/lib/capsules/foobar )
+(! id -u c-foobar )
+
+systemctl start capsule@foobar.service
+
+test -d /run/capsules/foobar
+test -d /var/lib/capsules/foobar
+id -u c-foobar
+
+systemctl status capsule@foobar.service
+
+busctl -C foobar
+
+systemctl -C foobar
+
+systemd-run -C foobar -u sleepinfinity /bin/sleep infinity
+
+systemctl -C foobar status sleepinfinity
+
+systemctl -C foobar stop sleepinfinity
+
+(! systemctl clean capsule@foobar.service )
+
+systemctl stop capsule@foobar.service
+
+systemctl clean capsule@foobar.service --what=all
+
+(! test -f /run/capsules/foobar )
+(! test -f /var/lib/capsules/foobar )
+(! id -u c-foobar )
diff --git a/test/units/TEST-74-AUX-UTILS.cgls.sh b/test/units/TEST-74-AUX-UTILS.cgls.sh
new file mode 100755 (executable)
index 0000000..9268f42
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemd-cgls
+systemd-cgls --all --full
+systemd-cgls -k
+systemd-cgls --xattr=yes
+systemd-cgls --xattr=no
+systemd-cgls --cgroup-id=yes
+systemd-cgls --cgroup-id=no
+
+systemd-cgls /system.slice/systemd-journald.service
+systemd-cgls /system.slice/systemd-journald.service /init.scope
+systemd-cgls /sys/fs/cgroup/system.slice/systemd-journald.service /init.scope
+[[ -d /sys/fs/cgroup/init.scope ]] && init_scope="init.scope" || init_scope="systemd/init.scope"
+(cd "/sys/fs/cgroup/$init_scope" && systemd-cgls)
+systemd-cgls --unit=systemd-journald.service
+# There's most likely no user session running, so we need to create one
+systemd-run --user --wait --pipe -M testuser@.host systemd-cgls --user-unit=app.slice
+
+(! systemd-cgls /foo/bar)
+(! systemd-cgls --unit=hello.world)
+(! systemd-cgls --user-unit=hello.world)
+(! systemd-cgls --xattr=foo)
+(! systemd-cgls --cgroup-id=foo)
diff --git a/test/units/TEST-74-AUX-UTILS.cgtop.sh b/test/units/TEST-74-AUX-UTILS.cgtop.sh
new file mode 100755 (executable)
index 0000000..cf98279
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Without tty attached cgtop should default to --iterations=1
+systemd-cgtop
+systemd-cgtop --iterations=1
+# Same as --iterations=1
+systemd-cgtop -1
+systemd-cgtop --delay=1ms
+systemd-cgtop --raw
+systemd-cgtop --batch
+systemd-cgtop --cpu=percentage
+systemd-cgtop --cpu=time
+systemd-cgtop -P
+systemd-cgtop -k
+systemd-cgtop --recursive=no -P
+systemd-cgtop --recursive=no -k
+systemd-cgtop --depth=0
+systemd-cgtop --depth=100
+
+for order in path tasks cpu memory io; do
+    systemd-cgtop --order="$order"
+done
+systemd-cgtop -p -t -c -m -i
+
+(! systemd-cgtop --cpu=foo)
+(! systemd-cgtop --order=foo)
+(! systemd-cgtop --depth=-1)
+(! systemd-cgtop --recursive=foo)
+(! systemd-cgtop --delay=1foo)
diff --git a/test/units/TEST-74-AUX-UTILS.coredump.sh b/test/units/TEST-74-AUX-UTILS.coredump.sh
new file mode 100755 (executable)
index 0000000..3f35716
--- /dev/null
@@ -0,0 +1,225 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+ . "$(dirname "$0")"/util.sh
+
+# Make sure the binary name fits into 15 characters
+CORE_TEST_BIN="/tmp/test-dump"
+CORE_TEST_UNPRIV_BIN="/tmp/test-usr-dump"
+MAKE_DUMP_SCRIPT="/tmp/make-dump"
+# Unset $PAGER so we don't have to use --no-pager everywhere
+export PAGER=
+
+at_exit() {
+    rm -fv -- "$CORE_TEST_BIN" "$CORE_TEST_UNPRIV_BIN" "$MAKE_DUMP_SCRIPT"
+}
+
+trap at_exit EXIT
+
+if systemd-detect-virt -cq; then
+    echo "Running in a container, skipping the systemd-coredump test..."
+    exit 0
+fi
+
+# To make all coredump entries stored in system.journal.
+journalctl --rotate
+
+# Check that we're the ones to receive coredumps
+sysctl kernel.core_pattern | grep systemd-coredump
+
+# Prepare "fake" binaries for coredumps, so we can properly exercise
+# the matching stuff too
+cp -vf /bin/sleep "${CORE_TEST_BIN:?}"
+cp -vf /bin/sleep "${CORE_TEST_UNPRIV_BIN:?}"
+# Simple script that spawns given "fake" binary and then kills it with
+# given signal
+cat >"${MAKE_DUMP_SCRIPT:?}" <<\EOF
+#!/bin/bash -ex
+
+bin="${1:?}"
+sig="${2:?}"
+
+ulimit -c unlimited
+"$bin" infinity &
+pid=$!
+# Sync with the "fake" binary, so we kill it once it's fully forked off,
+# otherwise we might kill it during fork and kernel would then report
+# "wrong" binary name (i.e. $MAKE_DUMP_SCRIPT instead of $CORE_TEST_BIN).
+# In this case, wait until the "fake" binary (sleep in this case) enters
+# the "interruptible sleep" state, at which point it should be ready
+# to be sacrificed.
+for _ in {0..9}; do
+    read -ra self_stat <"/proc/$pid/stat"
+    [[ "${self_stat[2]}" == S ]] && break
+    sleep .5
+done
+kill -s "$sig" "$pid"
+# This should always fail
+! wait "$pid"
+EOF
+chmod +x "$MAKE_DUMP_SCRIPT"
+
+# Privileged stuff
+[[ "$(id -u)" -eq 0 ]]
+# Trigger a couple of coredumps
+"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP"
+"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT"
+# In the tests we store the coredumps in journals, so let's generate a couple
+# with Storage=external as well
+mkdir -p /run/systemd/coredump.conf.d/
+printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf
+"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP"
+"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT"
+rm -fv /run/systemd/coredump.conf.d/99-external.conf
+# Wait a bit for the coredumps to get processed
+timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
+
+if cgroupfs_supports_user_xattrs; then
+    # Make sure we can forward crashes back to containers
+    CONTAINER="TEST-74-AUX-UTILS-container"
+
+    mkdir -p "/var/lib/machines/$CONTAINER"
+    mkdir -p "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d"
+    # Bind-mounting /etc into the container kinda defeats the purpose of --volatile=,
+    # but we need the ASan-related overrides scattered across /etc
+    cat > "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d/override.conf" <<EOF
+[Service]
+ExecStart=
+ExecStart=systemd-nspawn --quiet --link-journal=try-guest --keep-unit --machine=%i --boot \
+                         --volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id
+EOF
+    systemctl daemon-reload
+
+    [[ "$(systemd-detect-virt)" == "qemu" ]] && TIMEOUT=120 || TIMEOUT=60
+
+    machinectl start "$CONTAINER"
+    timeout "$TIMEOUT" bash -xec "until systemd-run -M '$CONTAINER' -q --wait --pipe true; do sleep .5; done"
+
+    [[ "$(systemd-run -M "$CONTAINER" -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l)" -eq 0 ]]
+    machinectl copy-to "$CONTAINER" "$MAKE_DUMP_SCRIPT"
+    systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGABRT"
+    systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGTRAP"
+    # Wait a bit for the coredumps to get processed
+    timeout 30 bash -c "while [[ \$(systemd-run -M $CONTAINER -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l) -lt 2 ]]; do sleep 1; done"
+
+    rm -rf "/var/lib/machines/$CONTAINER"
+fi
+
+coredumpctl
+SYSTEMD_LOG_LEVEL=debug coredumpctl
+coredumpctl --help
+coredumpctl --version
+coredumpctl --no-pager --no-legend
+coredumpctl --all
+coredumpctl -1
+coredumpctl -n 1
+coredumpctl --reverse
+coredumpctl -F COREDUMP_EXE
+coredumpctl --json=short | jq
+coredumpctl --json=pretty | jq
+coredumpctl --json=off
+coredumpctl --root=/
+coredumpctl --directory=/var/log/journal
+coredumpctl --file="/var/log/journal/$(</etc/machine-id)"/*.journal
+coredumpctl --since=@0
+coredumpctl --since=yesterday --until=tomorrow
+# We should have a couple of externally stored coredumps
+coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out
+grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out
+rm -f /tmp/coredumpctl.out
+
+coredumpctl info
+coredumpctl info "$CORE_TEST_BIN"
+coredumpctl info /foo /bar/ /baz "$CORE_TEST_BIN"
+coredumpctl info "${CORE_TEST_BIN##*/}"
+coredumpctl info foo bar baz "${CORE_TEST_BIN##*/}"
+coredumpctl info COREDUMP_EXE="$CORE_TEST_BIN"
+coredumpctl info COREDUMP_EXE=aaaaa COREDUMP_EXE= COREDUMP_EXE="$CORE_TEST_BIN"
+
+coredumpctl debug --debugger=/bin/true "$CORE_TEST_BIN"
+SYSTEMD_DEBUGGER=/bin/true coredumpctl debug "$CORE_TEST_BIN"
+coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_BIN##*/}"
+
+coredumpctl dump "$CORE_TEST_BIN" >/tmp/core.redirected
+test -s /tmp/core.redirected
+coredumpctl dump -o /tmp/core.output "${CORE_TEST_BIN##*/}"
+test -s /tmp/core.output
+rm -f /tmp/core.{output,redirected}
+
+# Unprivileged stuff
+# Related issue: https://github.com/systemd/systemd/issues/26912
+UNPRIV_CMD=(systemd-run --user --wait --pipe -M "testuser@.host" --)
+# Trigger a couple of coredumps as an unprivileged user
+"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
+"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
+# In the tests we store the coredumps in journals, so let's generate a couple
+# with Storage=external as well
+mkdir -p /run/systemd/coredump.conf.d/
+printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf
+"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
+"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
+rm -fv /run/systemd/coredump.conf.d/99-external.conf
+# Wait a bit for the coredumps to get processed
+timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_UNPRIV_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
+
+# root should see coredumps from both binaries
+coredumpctl info "$CORE_TEST_UNPRIV_BIN"
+coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}"
+# The test user should see only their own coredumps
+"${UNPRIV_CMD[@]}" coredumpctl
+"${UNPRIV_CMD[@]}" coredumpctl info "$CORE_TEST_UNPRIV_BIN"
+"${UNPRIV_CMD[@]}" coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}"
+(! "${UNPRIV_CMD[@]}" coredumpctl info --all "$CORE_TEST_BIN")
+(! "${UNPRIV_CMD[@]}" coredumpctl info --all "${CORE_TEST_BIN##*/}")
+# We should have a couple of externally stored coredumps
+"${UNPRIV_CMD[@]}" coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out
+grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out
+rm -f /tmp/coredumpctl.out
+
+"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true "$CORE_TEST_UNPRIV_BIN"
+"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_UNPRIV_BIN##*/}"
+
+"${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_UNPRIV_BIN" >/tmp/core.redirected
+test -s /tmp/core.redirected
+"${UNPRIV_CMD[@]}" coredumpctl dump -o /tmp/core.output "${CORE_TEST_UNPRIV_BIN##*/}"
+test -s /tmp/core.output
+rm -f /tmp/core.{output,redirected}
+(! "${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_BIN" >/dev/null)
+
+# --backtrace mode
+# Pass one of the existing journal coredump records to systemd-coredump and
+# use our PID as the source to make matching the coredump later easier
+# systemd-coredump args: PID UID GID SIGNUM TIMESTAMP CORE_SOFT_RLIMIT HOSTNAME
+journalctl -b -n 1 --output=export --output-fields=MESSAGE,COREDUMP COREDUMP_EXE="/usr/bin/test-dump" |
+    /usr/lib/systemd/systemd-coredump --backtrace $$ 0 0 6 1679509994 12345 mymachine
+# Wait a bit for the coredump to get processed
+timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $$ | wc -l) -eq 0 ]]; do sleep 1; done"
+coredumpctl info "$$"
+coredumpctl info COREDUMP_HOSTNAME="mymachine"
+
+# This used to cause a stack overflow
+systemd-run -t --property CoredumpFilter=all ls /tmp
+systemd-run -t --property CoredumpFilter=default ls /tmp
+
+(! coredumpctl --hello-world)
+(! coredumpctl -n 0)
+(! coredumpctl -n -1)
+(! coredumpctl --file=/dev/null)
+(! coredumpctl --since=0)
+(! coredumpctl --until='')
+(! coredumpctl --since=today --until=yesterday)
+(! coredumpctl --directory=/ --root=/)
+(! coredumpctl --json=foo)
+(! coredumpctl -F foo -F bar)
+(! coredumpctl list 0)
+(! coredumpctl list -- -1)
+(! coredumpctl list '')
+(! coredumpctl info /../.~=)
+(! coredumpctl info '')
+(! coredumpctl dump --output=/dev/full "$CORE_TEST_BIN")
+(! coredumpctl dump --output=/dev/null --output=/dev/null "$CORE_TEST_BIN")
+(! coredumpctl debug --debugger=/bin/false)
+(! coredumpctl debug --debugger=/bin/true --debugger-arguments='"')
diff --git a/test/units/TEST-74-AUX-UTILS.delta.sh b/test/units/TEST-74-AUX-UTILS.delta.sh
new file mode 100755 (executable)
index 0000000..dabe234
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+at_exit() {
+    rm -rfv /{run,etc}/systemd/system/delta-test*
+}
+
+trap at_exit EXIT
+
+# Create a couple of supporting units with overrides
+#
+# Extended unit
+cat >"/run/systemd/system/delta-test-unit-extended.service" <<EOF
+[Service]
+ExecStart=true
+EOF
+mkdir -p "/run/systemd/system/delta-test-unit-extended.service.d"
+cat >"/run/systemd/system/delta-test-unit-extended.service.d/override.conf" <<EOF
+[Unit]
+Description=Foo Bar
+[Service]
+ExecStartPre=true
+EOF
+# Masked unit
+cp -fv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-masked.service
+systemctl mask delta-test-unit-masked.service
+# Overridden unit
+cp -fv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-overridden.service
+cp -fv /run/systemd/system/delta-test-unit-overridden.service /etc/systemd/system/delta-test-unit-overridden.service
+echo "ExecStartPost=/bin/true" >>/etc/systemd/system/delta-test-unit-overridden.service
+# Overridden but equivalent unit
+ln -srfv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-equivalent.service
+ln -sfv /run/systemd/system/delta-test-unit-extended.service /etc/systemd/system/delta-test-unit-equivalent.service
+# Redirected unit
+ln -srfv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-redirected.service
+ln -sfv /run/systemd/system/delta-test-unit-overidden.service /etc/systemd/system/delta-test-unit-extended.service
+
+systemctl daemon-reload
+
+systemd-delta
+systemd-delta /run
+systemd-delta systemd/system
+systemd-delta /run systemd/system /run
+systemd-delta /run foo/bar hello/world systemd/system /run
+systemd-delta foo/bar
+systemd-delta --diff=true
+systemd-delta --diff=false
+
+for type in masked equivalent redirected overridden extended unchanged; do
+    systemd-delta --type="$type"
+    systemd-delta --type="$type" /run
+done
+systemd-delta --type=equivalent,redirected
+
+(! systemd-delta --diff=foo)
+(! systemd-delta --type=foo)
+(! systemd-delta --type=equivalent,redirected,foo)
diff --git a/test/units/TEST-74-AUX-UTILS.escape.sh b/test/units/TEST-74-AUX-UTILS.escape.sh
new file mode 100755 (executable)
index 0000000..e398d40
--- /dev/null
@@ -0,0 +1,108 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# Simple wrapper to check both escaping and unescaping of given strings
+# Arguments:
+#   $1 - expected unescaped string
+#   $2 - expected escaped string
+#   $3 - optional arguments for systemd-escape
+check_escape() {
+    unescaped="${1?}"
+    escaped="${2?}"
+    shift 2
+
+    assert_eq "$(systemd-escape "$@" -- "$unescaped")" "$escaped"
+    assert_eq "$(systemd-escape "$@" --unescape -- "$escaped")" "$unescaped"
+}
+
+systemd-escape --help
+systemd-escape --version
+
+check_escape '' ''
+check_escape 'hello' 'hello'
+check_escape 'hello-world' 'hello\x2dworld'
+check_escape '-+ěščřž---🤔' '\x2d\x2b\xc4\x9b\xc5\xa1\xc4\x8d\xc5\x99\xc5\xbe\x2d\x2d\x2d\xf0\x9f\xa4\x94'
+check_escape '/this/is/a/path/a b c' '-this-is-a-path-a\x20b\x20c'
+
+# Multiple strings to escape/unescape
+assert_eq "$(systemd-escape 'hello-world' '/dev/loop1' 'template@🐍')" \
+          'hello\x2dworld -dev-loop1 template\x40\xf0\x9f\x90\x8d'
+assert_eq "$(systemd-escape --unescape -- 'hello\x2dworld' '-dev-loop1' 'template\x40\xf0\x9f\x90\x8d')" \
+          'hello-world /dev/loop1 template@🐍'
+
+# --suffix= is not compatible with --unescape
+assert_eq "$(systemd-escape --suffix=mount -- '-+ěščřž---🤔')" \
+          '\x2d\x2b\xc4\x9b\xc5\xa1\xc4\x8d\xc5\x99\xc5\xbe\x2d\x2d\x2d\xf0\x9f\xa4\x94.mount'
+assert_eq "$(systemd-escape --suffix=timer 'this has spaces')" \
+          'this\x20has\x20spaces.timer'
+assert_eq "$(systemd-escape --suffix=service 'trailing-spaces  ')" \
+          'trailing\x2dspaces\x20\x20.service'
+assert_eq "$(systemd-escape --suffix=automount '   leading-spaces')" \
+          '\x20\x20\x20leading\x2dspaces.automount'
+
+# --template=
+check_escape 'hello' 'hello@hello.service' --template=hello@.service
+check_escape '  what - is _ love? 🤔 ¯\_(ツ)_/¯' \
+             'hello@\x20\x20what\x20\x2d\x20is\x20_\x20love\x3f\x20\xf0\x9f\xa4\x94\x20\xc2\xaf\x5c_\x28\xe3\x83\x84\x29_-\xc2\xaf.service' \
+             --template=hello@.service
+check_escape '/this/is/where/my/stuff/is/ with spaces though ' \
+             'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service' \
+             --template=mount-my-stuff@.service
+check_escape '/this/is/where/my/stuff/is/ with spaces though ' \
+             'mount-my-stuff@this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service' \
+             --template=mount-my-stuff@.service --path
+
+# --instance (must be used with --unescape)
+assert_eq "$(systemd-escape --unescape --instance 'hello@\x20\x20what\x20\x2d\x20is\x20_\x20love\x3f\x20\xf0\x9f\xa4\x94\x20\xc2\xaf\x5c_\x28\xe3\x83\x84\x29_-\xc2\xaf.service')" \
+          '  what - is _ love? 🤔 ¯\_(ツ)_/¯'
+assert_eq "$(systemd-escape --unescape --instance 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')" \
+          '/this/is/where/my/stuff/is/ with spaces though '
+assert_eq "$(systemd-escape --unescape --instance --path 'mount-my-stuff@this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')" \
+          '/this/is/where/my/stuff/is/ with spaces though '
+
+# --path, reversible cases
+check_escape / '-' --path
+check_escape '/hello/world' 'hello-world' --path
+check_escape '/mnt/smb/おにぎり' \
+             'mnt-smb-\xe3\x81\x8a\xe3\x81\xab\xe3\x81\x8e\xe3\x82\x8a' \
+             --path
+
+# --path, non-reversible cases
+assert_eq "$(systemd-escape --path ///////////////)" '-'
+assert_eq "$(systemd-escape --path /..)" '-'
+assert_eq "$(systemd-escape --path /../.././../.././)" '-'
+assert_eq "$(systemd-escape --path /../.././../.././foo)" 'foo'
+
+# --mangle
+assert_eq "$(systemd-escape --mangle 'hello-world')" 'hello-world.service'
+assert_eq "$(systemd-escape --mangle '/mount/this')" 'mount-this.mount'
+assert_eq "$(systemd-escape --mangle 'my-service@ 🐱 ')" 'my-service@\x20\xf0\x9f\x90\xb1\x20.service'
+assert_eq "$(systemd-escape --mangle '/dev/disk/by-emoji/🍎')" 'dev-disk-by\x2demoji-\xf0\x9f\x8d\x8e.device'
+assert_eq "$(systemd-escape --mangle 'daily-existential-crisis .timer')" 'daily-existential-crisis\x20.timer'
+assert_eq "$(systemd-escape --mangle 'trailing-whitespace.mount ')" 'trailing-whitespace.mount\x20.service'
+
+(! systemd-escape)
+(! systemd-escape --suffix='' hello)
+(! systemd-escape --suffix=invalid hello)
+(! systemd-escape --suffix=mount --template=hello@.service hello)
+(! systemd-escape --suffix=mount --mangle)
+(! systemd-escape --template='')
+(! systemd-escape --template=@)
+(! systemd-escape --template='hello@.service' '')
+(! systemd-escape --unescape --template='hello@.service' '@hello.service')
+(! systemd-escape --unescape --template='hello@.service' 'hello@.service')
+(! systemd-escape --mangle --template=hello@.service hello)
+(! systemd-escape --instance 'hello@hello.service')
+(! systemd-escape --instance --template=hello@.service 'hello@hello.service')
+(! systemd-escape --unescape --instance --path 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')
+(! systemd-escape --path '/../hello/..')
+(! systemd-escape --path '.')
+(! systemd-escape --path '..')
+(! systemd-escape --path "$(set +x; printf '%0.sa' {0..256})")
+(! systemd-escape --unescape --path '')
+(! systemd-escape --mangle '')
diff --git a/test/units/TEST-74-AUX-UTILS.firstboot.sh b/test/units/TEST-74-AUX-UTILS.firstboot.sh
new file mode 100755 (executable)
index 0000000..7bab009
--- /dev/null
@@ -0,0 +1,219 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+if ! command -v systemd-firstboot >/dev/null; then
+    echo "systemd-firstboot not found, skipping the test"
+    exit 0
+fi
+
+at_exit() {
+    if [[ -n "${ROOT:-}" ]]; then
+        ls -lR "$ROOT"
+        rm -fr "$ROOT"
+    fi
+
+    restore_locale
+}
+
+trap at_exit EXIT
+
+# Generated via `mkpasswd -m sha-512 -S foobarsalt password1`
+# shellcheck disable=SC2016
+ROOT_HASHED_PASSWORD1='$6$foobarsalt$YbwdaATX6IsFxvWbY3QcZj2gB31R/LFRFrjlFrJtTTqFtSfn4dfOAg/km2k4Sl.a2g7LOYDo31wMTaEsCo9j41'
+# Generated via `mkpasswd -m sha-512 -S foobarsalt password2`
+# shellcheck disable=SC2016
+ROOT_HASHED_PASSWORD2='$6$foobarsalt$q.P2932zYMLbKnjFwIxPI8y3iuxeuJ2BgE372LcZMMnj3Gcg/9mJg2LPKUl.ha0TG/.fRNNnRQcLfzM0SNot3.'
+
+if [[ -f /etc/locale.conf ]]; then
+    cp /etc/locale.conf /tmp/locale.conf.bak
+fi
+
+# Debian/Ubuntu specific file
+if [[ -f /etc/default/locale ]]; then
+    cp /etc/default/locale /tmp/default-locale.bak
+fi
+
+if [[ -f /etc/locale.gen ]]; then
+    cp /etc/locale.gen /tmp/locale.gen.bak
+fi
+
+# Make sure at least two locales exist (C.UTF-8 and en_US.UTF-8) as systemd-firstboot --prompt-locale will
+# skip writing the locale if it detects only one is installed.
+generate_locale en_US.UTF-8
+
+# Debian and Ubuntu use /etc/default/locale instead of /etc/locale.conf. Make
+# sure we use the appropriate path for locale configuration.
+LOCALE_PATH="/etc/locale.conf"
+[ -e "$LOCALE_PATH" ] || LOCALE_PATH="/etc/default/locale"
+[ -e "$LOCALE_PATH" ] || systemd-firstboot --locale=C.UTF-8
+
+# Create a minimal root so we don't modify the testbed
+ROOT=test-root
+mkdir -p "$ROOT/bin"
+# Dummy shell for --root-shell=
+touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
+
+systemd-firstboot --root="$ROOT" --locale=foo
+grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
+rm -fv "$ROOT$LOCALE_PATH"
+systemd-firstboot --root="$ROOT" --locale-messages=foo
+grep -q "LC_MESSAGES=foo" "$ROOT$LOCALE_PATH"
+rm -fv "$ROOT$LOCALE_PATH"
+systemd-firstboot --root="$ROOT" --locale=foo --locale-messages=bar
+grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
+grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
+
+systemd-firstboot --root="$ROOT" --keymap=foo
+grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
+
+systemd-firstboot --root="$ROOT" --timezone=Europe/Berlin
+readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin"
+
+systemd-firstboot --root="$ROOT" --hostname "foobar"
+grep -q "foobar" "$ROOT/etc/hostname"
+
+systemd-firstboot --root="$ROOT" --machine-id=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id"
+
+rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
+systemd-firstboot --root="$ROOT" --root-password=foo
+grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
+grep -q "^root:" "$ROOT/etc/shadow"
+rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
+echo "foo" >root.passwd
+systemd-firstboot --root="$ROOT" --root-password-file=root.passwd
+grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
+grep -q "^root:" "$ROOT/etc/shadow"
+rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" root.passwd
+# Set the shell together with the password, as firstboot won't touch
+# /etc/passwd if it already exists
+systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1" --root-shell=/bin/fooshell
+grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
+grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
+
+systemd-firstboot --root="$ROOT" --kernel-command-line="foo.bar=42"
+grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline"
+
+# Configs should not get overwritten if they exist unless --force is used
+systemd-firstboot --root="$ROOT" \
+                  --locale=locale-overwrite \
+                  --locale-messages=messages-overwrite \
+                  --keymap=keymap-overwrite \
+                  --timezone=CET \
+                  --hostname=hostname-overwrite \
+                  --machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \
+                  --root-password-hashed="$ROOT_HASHED_PASSWORD2" \
+                  --root-shell=/bin/barshell \
+                  --kernel-command-line="hello.world=0"
+grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
+grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
+grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
+readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
+grep -q "foobar" "$ROOT/etc/hostname"
+grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id"
+grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
+grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
+grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline"
+
+# The same thing, but now with --force
+systemd-firstboot --root="$ROOT" --force \
+                  --locale=locale-overwrite \
+                  --locale-messages=messages-overwrite \
+                  --keymap=keymap-overwrite \
+                  --timezone=CET \
+                  --hostname=hostname-overwrite \
+                  --machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \
+                  --root-password-hashed="$ROOT_HASHED_PASSWORD2" \
+                  --root-shell=/bin/barshell \
+                  --kernel-command-line="hello.world=0"
+grep -q "LANG=locale-overwrite" "$ROOT$LOCALE_PATH"
+grep -q "LC_MESSAGES=messages-overwrite" "$ROOT$LOCALE_PATH"
+grep -q "KEYMAP=keymap-overwrite" "$ROOT/etc/vconsole.conf"
+readlink "$ROOT/etc/localtime" | grep -q "/CET$"
+grep -q "hostname-overwrite" "$ROOT/etc/hostname"
+grep -q "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" "$ROOT/etc/machine-id"
+grep -q "^root:x:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd"
+grep -q "^root:$ROOT_HASHED_PASSWORD2:" "$ROOT/etc/shadow"
+grep -q "hello.world=0" "$ROOT/etc/kernel/cmdline"
+
+# Test that --reset removes all files configured by firstboot.
+systemd-firstboot --root="$ROOT" --reset
+[[ ! -e "$ROOT/etc/locale.conf" ]]
+[[ ! -e "$ROOT/etc/vconsole.conf" ]]
+[[ ! -e "$ROOT/etc/localtime" ]]
+[[ ! -e "$ROOT/etc/hostname" ]]
+[[ ! -e "$ROOT/etc/machine-id" ]]
+[[ ! -e "$ROOT/etc/kernel/cmdline" ]]
+
+# --copy-* options
+rm -fr "$ROOT"
+mkdir "$ROOT"
+# Copy everything at once (--copy)
+systemd-firstboot --root="$ROOT" --copy
+diff $LOCALE_PATH "$ROOT$LOCALE_PATH"
+diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd")
+diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
+[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf"
+[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime")
+rm -fr "$ROOT"
+mkdir "$ROOT"
+# Copy everything at once, but now by using separate switches
+systemd-firstboot --root="$ROOT" --copy-locale --copy-keymap --copy-timezone --copy-root-password --copy-root-shell
+diff $LOCALE_PATH "$ROOT$LOCALE_PATH"
+diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd")
+diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
+[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf"
+[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime")
+
+# --prompt-* options
+rm -fr "$ROOT"
+mkdir -p "$ROOT/bin"
+touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
+# Temporarily disable pipefail to avoid `echo: write error: Broken pipe
+set +o pipefail
+# We can do only limited testing here, since it's all an interactive stuff,
+# so --prompt and --prompt-root-password are skipped on purpose
+echo -ne "\nfoo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale
+grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
+grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
+# systemd-firstboot in prompt-keymap mode requires keymaps to be installed so
+# it can present them as a list to the user. As Debian does not ship/provide
+# compatible keymaps (from the kbd package), skip this test if the keymaps are
+# missing.
+if [ -d "/usr/share/keymaps/" ] || [ -d "/usr/share/kbd/keymaps/" ] || [ -d "/usr/lib/kbd/keymaps/" ] ; then
+   echo -ne "\nfoo\n" | systemd-firstboot --root="$ROOT" --prompt-keymap
+   grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
+fi
+echo -ne "\nEurope/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezone
+readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
+echo -ne "\nfoobar\n" | systemd-firstboot --root="$ROOT" --prompt-hostname
+grep -q "foobar" "$ROOT/etc/hostname"
+echo -ne "\n/bin/fooshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
+grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
+# Existing files should not get overwritten
+echo -ne "\n/bin/barshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
+grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
+# Now without the welcome screen but with force
+echo -ne "/bin/barshell\n" | systemd-firstboot --root="$ROOT" --force --prompt-root-shell --welcome=no
+grep -q "^root:.*:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd"
+# Re-enable pipefail
+set -o pipefail
+
+# Assorted tests
+rm -fr "$ROOT"
+mkdir "$ROOT"
+
+systemd-firstboot --root="$ROOT" --setup-machine-id
+grep -E "[a-z0-9]{32}" "$ROOT/etc/machine-id"
+
+systemd-firstboot --root="$ROOT" --delete-root-password
+diff <(echo) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
+
+(! systemd-firstboot --root="$ROOT" --root-shell=/bin/nonexistentshell)
+(! systemd-firstboot --root="$ROOT" --machine-id=invalidmachineid)
+(! systemd-firstboot --root="$ROOT" --timezone=Foo/Bar)
diff --git a/test/units/TEST-74-AUX-UTILS.id128.sh b/test/units/TEST-74-AUX-UTILS.id128.sh
new file mode 100755 (executable)
index 0000000..f91cd5f
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemd-id128 --help
+systemd-id128 help
+systemd-id128 show
+systemd-id128 show --pretty | tail
+systemd-id128 show --value | tail
+systemd-id128 show 4f68bce3e8cd4db196e7fbcaf984b709 # root-x86-64
+systemd-id128 show --pretty 4f68bce3e8cd4db196e7fbcaf984b709
+systemd-id128 show root-x86-64
+systemd-id128 show --pretty root-x86-64
+[[ "$(systemd-id128 show 4f68bce3e8cd4db196e7fbcaf984b709)" = "$(systemd-id128 show root-x86-64)" ]]
+[[ "$(systemd-id128 show 4f68bce3-e8cd-4db1-96e7-fbcaf984b709)" = "$(systemd-id128 show root-x86-64)" ]]
+
+systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
+systemd-id128 show --pretty root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
+[[ "$(systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709 -P)" = "8ee5535e7cb14c249e1d28b8dfbb939c" ]]
+
+systemd-id128 show -j
+systemd-id128 show --no-pager
+systemd-id128 show --json=short
+systemd-id128 show --no-legend
+systemd-id128 show --no-pager --no-legend
+systemd-id128 show root -P -u
+
+[[ "$(systemd-id128 new | wc -c)" -eq 33 ]]
+systemd-id128 new -p
+systemd-id128 new -u
+systemd-id128 new -a 4f68bce3e8cd4db196e7fbcaf984b709
+
+systemd-id128 machine-id
+systemd-id128 machine-id --pretty
+systemd-id128 machine-id --uuid
+systemd-id128 machine-id --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
+assert_eq "$(systemd-id128 machine-id)" "$(</etc/machine-id)"
+
+systemd-id128 boot-id
+systemd-id128 boot-id --pretty
+systemd-id128 boot-id --uuid
+systemd-id128 boot-id --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
+assert_eq "$(systemd-id128 boot-id --uuid)" "$(</proc/sys/kernel/random/boot_id)"
+
+# shellcheck disable=SC2016
+systemd-run --wait --pipe bash -euxc '[[ $INVOCATION_ID == "$(systemd-id128 invocation-id)" ]]'
+
+(! systemd-id128)
+(! systemd-id128 new -a '')
+(! systemd-id128 new -a '0')
+(! systemd-id128 invocation-id -a 4f68bce3e8cd4db196e7fbcaf984b709)
+(! systemd-id128 show '')
+(! systemd-id128 show "$(set +x; printf '%0.s0' {0..64})")
diff --git a/test/units/TEST-74-AUX-UTILS.machine-id-setup.sh b/test/units/TEST-74-AUX-UTILS.machine-id-setup.sh
new file mode 100755 (executable)
index 0000000..c2b9db5
--- /dev/null
@@ -0,0 +1,77 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2064
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+root_mock() {
+    local root="${1:?}"
+
+    mkdir -p "$root"
+    # Put a tmpfs over the "root", so we're able to remount it as read-only
+    # when needed
+    mount -t tmpfs tmpfs "$root"
+    mkdir "$root/etc" "$root/run"
+}
+
+root_cleanup() {
+    local root="${1:?}"
+
+    umount --recursive "$root"
+    rm -fr "$root"
+}
+
+testcase_sanity() {
+    systemd-machine-id-setup
+    systemd-machine-id-setup --help
+    systemd-machine-id-setup --version
+    systemd-machine-id-setup --print
+    systemd-machine-id-setup --root= --print
+    systemd-machine-id-setup --root=/ --print
+
+    (! systemd-machine-id-setup "")
+    (! systemd-machine-id-setup --foo)
+}
+
+testcase_invalid() {
+    local root machine_id
+
+    root="$(mktemp -d)"
+    trap "root_cleanup $root" RETURN
+    root_mock "$root"
+
+    systemd-machine-id-setup --print --root "$root"
+    echo abc >>"$root/etc/machine-id"
+    machine_id="$(systemd-machine-id-setup --print --root "$root")"
+    diff <(echo "$machine_id") "$root/etc/machine-id"
+}
+
+testcase_transient() {
+    local root transient_id committed_id
+
+    root="$(mktemp -d)"
+    trap "root_cleanup $root" RETURN
+    root_mock "$root"
+
+    systemd-machine-id-setup --print --root "$root"
+    echo abc >>"$root/etc/machine-id"
+    mount -o remount,ro "$root"
+    mount -t tmpfs tmpfs "$root/run"
+    transient_id="$(systemd-machine-id-setup --print --root "$root")"
+    mount -o remount,rw "$root"
+    committed_id="$(systemd-machine-id-setup --print --commit --root "$root")"
+    [[ "$transient_id" == "$committed_id" ]]
+    diff "$root/etc/machine-id" "$root/run/machine-id"
+}
+
+# Check if we correctly processed the invalid machine ID we set up in the respective
+# test.sh file
+systemctl --state=failed --no-legend --no-pager >/failed
+test ! -s /failed
+
+run_testcases
diff --git a/test/units/TEST-74-AUX-UTILS.modules-load.sh b/test/units/TEST-74-AUX-UTILS.modules-load.sh
new file mode 100755 (executable)
index 0000000..ceac826
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+MODULES_LOAD_BIN="/usr/lib/systemd/systemd-modules-load"
+CONFIG_FILE="/run/modules-load.d/99-test.conf"
+
+at_exit() {
+    rm -rfv "${CONFIG_FILE:?}"
+}
+
+trap at_exit EXIT
+
+if systemd-detect-virt -cq; then
+    echo "Running in a container, skipping the systemd-modules-load test..."
+    exit 0
+fi
+
+ORIG_MODULES_LOAD_CONFIG="$(systemd-analyze cat-config modules-load.d)"
+
+# Check if we have required kernel modules
+modprobe --all --resolve-alias dummy
+
+mkdir -p /run/modules-load.d/
+
+"$MODULES_LOAD_BIN"
+"$MODULES_LOAD_BIN" --help
+"$MODULES_LOAD_BIN" --version
+
+# Explicit config file
+modprobe -v --all --remove dummy
+printf "dummy" >"$CONFIG_FILE"
+"$MODULES_LOAD_BIN" "$CONFIG_FILE" |& tee /tmp/out.log
+grep -E "Inserted module .*dummy" /tmp/out.log
+
+# Implicit config file
+modprobe -v --all --remove dummy
+printf "dummy" >"$CONFIG_FILE"
+"$MODULES_LOAD_BIN" |& tee /tmp/out.log
+grep -E "Inserted module .*dummy" /tmp/out.log
+
+# Valid & invalid data mixed together
+modprobe -v --all --remove dummy
+cat >"$CONFIG_FILE" <<EOF
+
+dummy
+dummy
+dummy
+    dummy
+dummy
+    \\n\n\n\\\\\\
+
+dumm!@@123##2455
+# This is a comment
+$(printf "%.0sx" {0..4096})
+dummy
+dummy
+foo-bar-baz
+1
+"
+'
+EOF
+"$MODULES_LOAD_BIN" |& tee /tmp/out.log
+grep -E "^Inserted module .*dummy" /tmp/out.log
+grep -E "^Failed to find module .*foo-bar-baz" /tmp/out.log
+(! grep -E "This is a comment" /tmp/out.log)
+# Each module should be loaded only once, even if specified multiple times
+[[ "$(grep -Ec "^Inserted module" /tmp/out.log)" -eq 1 ]]
+[[ "$(grep -Ec "^Failed to find module" /tmp/out.log)" -eq 7 ]]
+
+# Command line arguments
+modprobe -v --all --remove dummy
+# Make sure we have no config files left over that might interfere with
+# following tests
+rm -fv "$CONFIG_FILE"
+[[ "$ORIG_MODULES_LOAD_CONFIG" == "$(systemd-analyze cat-config modules-load.d)" ]]
+CMDLINE="ro root= modules_load= modules_load=, / = modules_load=foo-bar-baz,dummy modules_load=dummy,dummy,dummy"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" "$MODULES_LOAD_BIN" |& tee /tmp/out.log
+grep -E "^Inserted module .*dummy" /tmp/out.log
+grep -E "^Failed to find module .*foo-bar-baz" /tmp/out.log
+# Each module should be loaded only once, even if specified multiple times
+[[ "$(grep -Ec "^Inserted module" /tmp/out.log)" -eq 1 ]]
+
+(! "$MODULES_LOAD_BIN" --nope)
+(! "$MODULES_LOAD_BIN" /foo/bar/baz)
diff --git a/test/units/TEST-74-AUX-UTILS.mount.sh b/test/units/TEST-74-AUX-UTILS.mount.sh
new file mode 100755 (executable)
index 0000000..57b06bb
--- /dev/null
@@ -0,0 +1,159 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# We're going to play around with block/loop devices, so bail out early
+# if we're running in nspawn
+if systemd-detect-virt --container >/dev/null; then
+    echo "Container detected, skipping the test"
+    exit 0
+fi
+
+at_exit() {
+    set +e
+
+    [[ -n "${LOOP:-}" ]] && losetup -d "$LOOP"
+    [[ -n "${WORK_DIR:-}" ]] && rm -fr "$WORK_DIR"
+}
+
+trap at_exit EXIT
+
+WORK_DIR="$(mktemp -d)"
+
+systemd-mount --list
+systemd-mount --list --full
+systemd-mount --list --no-legend
+systemd-mount --list --no-pager
+systemd-mount --list --quiet
+
+# Set up a simple block device for further tests
+dd if=/dev/zero of="$WORK_DIR/simple.img" bs=1M count=16
+mkfs.ext4 -L sd-mount-test "$WORK_DIR/simple.img"
+LOOP="$(losetup --show --find "$WORK_DIR/simple.img")"
+udevadm wait --timeout 60 --settle "$LOOP"
+mkdir "$WORK_DIR/mnt"
+mount "$LOOP" "$WORK_DIR/mnt"
+touch "$WORK_DIR/mnt/foo.bar"
+umount "$LOOP"
+(! mountpoint "$WORK_DIR/mnt")
+
+# Mount with both source and destination set
+systemd-mount "$LOOP" "$WORK_DIR/mnt"
+systemctl status "$WORK_DIR/mnt"
+systemd-mount --list --full
+test -e "$WORK_DIR/mnt/foo.bar"
+systemd-umount "$WORK_DIR/mnt"
+# Same thing, but with explicitly specified filesystem and disabled filesystem check
+systemd-mount --type=ext4 --fsck=no --collect "$LOOP" "$WORK_DIR/mnt"
+systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").mount"
+test -e "$WORK_DIR/mnt/foo.bar"
+systemd-mount --umount "$LOOP"
+# Discover additional metadata (unit description should now contain filesystem label)
+systemd-mount --no-ask-password --discover "$LOOP" "$WORK_DIR/mnt"
+test -e "$WORK_DIR/mnt/foo.bar"
+systemctl show -P Description "$WORK_DIR/mnt" | grep -q sd-mount-test
+systemd-umount "$WORK_DIR/mnt"
+# Set a unit description
+systemd-mount --description="Very Important Unit" "$LOOP" "$WORK_DIR/mnt"
+test -e "$WORK_DIR/mnt/foo.bar"
+systemctl show -P Description "$WORK_DIR/mnt" | grep -q "Very Important Unit"
+systemd-umount "$WORK_DIR/mnt"
+# Set a property
+systemd-mount --property="Description=Foo Bar" "$LOOP" "$WORK_DIR/mnt"
+test -e "$WORK_DIR/mnt/foo.bar"
+systemctl show -P Description "$WORK_DIR/mnt" | grep -q "Foo Bar"
+systemd-umount "$WORK_DIR/mnt"
+# Set mount options
+systemd-mount --options=ro,x-foo-bar "$LOOP" "$WORK_DIR/mnt"
+test -e "$WORK_DIR/mnt/foo.bar"
+systemctl show -P Options "$WORK_DIR/mnt" | grep -Eq "(^ro|,ro)"
+systemctl show -P Options "$WORK_DIR/mnt" | grep -q "x-foo-bar"
+systemd-umount "$WORK_DIR/mnt"
+
+# Mount with only source set
+systemd-mount "$LOOP"
+systemctl status /run/media/system/sd-mount-test
+systemd-mount --list --full
+test -e /run/media/system/sd-mount-test/foo.bar
+systemd-umount LABEL=sd-mount-test
+
+# Automount
+systemd-mount --automount=yes "$LOOP" "$WORK_DIR/mnt"
+systemd-mount --list --full
+systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount"
+[[ "$(systemctl show -P ActiveState "$WORK_DIR/mnt")" == inactive ]]
+test -e "$WORK_DIR/mnt/foo.bar"
+systemctl status "$WORK_DIR/mnt"
+systemd-umount "$WORK_DIR/mnt"
+# Automount + automount-specific property
+systemd-mount -A --automount-property="Description=Bar Baz" "$LOOP" "$WORK_DIR/mnt"
+systemctl show -P Description "$(systemd-escape --path "$WORK_DIR/mnt").automount" | grep -q "Bar Baz"
+test -e "$WORK_DIR/mnt/foo.bar"
+# Call --umount via --machine=, first with a relative path (bad) and then with
+# an absolute one (good)
+(! systemd-umount --machine=.host "$(realpath --relative-to=. "$WORK_DIR/mnt")")
+systemd-umount --machine=.host "$WORK_DIR/mnt"
+
+# ext4 doesn't support uid=/gid=
+(! systemd-mount -t ext4 --owner=testuser "$LOOP" "$WORK_DIR/mnt")
+
+# Automount + --bind-device
+systemd-mount --automount=yes --bind-device --timeout-idle-sec=1 "$LOOP" "$WORK_DIR/mnt"
+systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount"
+# Trigger the automount
+test -e "$WORK_DIR/mnt/foo.bar"
+# Wait until it's idle again
+sleep 1.5
+# Safety net for slower/overloaded systems
+timeout 10s bash -c "while systemctl is-active -q $WORK_DIR/mnt; do sleep .2; done"
+systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount"
+# Disassemble the underlying block device
+losetup -d "$LOOP"
+unset LOOP
+# The automount unit should disappear once the underlying blockdev is gone
+timeout 10s bash -c "while systemctl status '$(systemd-escape --path "$WORK_DIR/mnt".automount)'; do sleep .2; done"
+
+# Mount a disk image
+systemd-mount --discover "$WORK_DIR/simple.img"
+# We can access files in the image even if the loopback block device is not initialized by udevd.
+test -e /run/media/system/simple.img/foo.bar
+# systemd-mount --list and systemd-umount require the loopback block device is initialized by udevd.
+udevadm settle --timeout 30
+assert_in "/dev/loop.* ext4 +sd-mount-test" "$(systemd-mount --list --full)"
+systemd-umount "$WORK_DIR/simple.img"
+
+# --owner + vfat
+#
+# Create a vfat image, as ext4 doesn't support uid=/gid= fixating for all
+# files/directories
+dd if=/dev/zero of="$WORK_DIR/owner-vfat.img" bs=1M count=16
+mkfs.vfat -n owner-vfat "$WORK_DIR/owner-vfat.img"
+LOOP="$(losetup --show --find "$WORK_DIR/owner-vfat.img")"
+udevadm wait --timeout 60 --settle "$LOOP"
+# Mount it and check the UID/GID
+[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt")" == "root:root" ]]
+systemd-mount --owner=testuser "$LOOP" "$WORK_DIR/mnt"
+systemctl status "$WORK_DIR/mnt"
+[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt")" == "testuser:testuser" ]]
+touch "$WORK_DIR/mnt/hello"
+[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt/hello")" == "testuser:testuser" ]]
+systemd-umount LABEL=owner-vfat
+
+# tmpfs
+mkdir -p "$WORK_DIR/mnt/foo/bar"
+systemd-mount --tmpfs "$WORK_DIR/mnt/foo"
+test ! -d "$WORK_DIR/mnt/foo/bar"
+touch "$WORK_DIR/mnt/foo/baz"
+systemd-umount "$WORK_DIR/mnt/foo"
+test -d "$WORK_DIR/mnt/foo/bar"
+test ! -e "$WORK_DIR/mnt/foo/baz"
+
+# overlay
+systemd-mount --type=overlay --options="lowerdir=/etc,upperdir=$WORK_DIR/upper,workdir=$WORK_DIR/work" /etc "$WORK_DIR/overlay"
+touch "$WORK_DIR/overlay/foo"
+test -e "$WORK_DIR/upper/foo"
+systemd-umount "$WORK_DIR/overlay"
diff --git a/test/units/TEST-74-AUX-UTILS.network-generator.sh b/test/units/TEST-74-AUX-UTILS.network-generator.sh
new file mode 100755 (executable)
index 0000000..5b5b0a1
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+at_exit() {
+    rm -f /run/credstore/network.conf.50-testme
+    rm -f /run/credstore/network.network.50-testme
+    rm -f /run/systemd/networkd.conf.d/50-testme.conf
+    rm -f /run/systemd/network/50-testme.network
+    rm -f /run/systemd/system/systemd-network-generator.service.d/50-testme.conf
+}
+
+trap at_exit EXIT
+
+mkdir -p /run/credstore
+cat > /run/credstore/network.conf.50-testme <<EOF
+[Network]
+SpeedMeter=yes
+EOF
+
+cat > /run/credstore/network.network.50-testme <<EOF
+[Match]
+Property=IDONTEXIST
+EOF
+
+systemctl edit systemd-network-generator.service --stdin --drop-in=50-testme.conf <<EOF
+[Service]
+LoadCredential=network.conf.50-testme
+LoadCredential=network.network.50-testme
+EOF
+
+systemctl restart systemd-network-generator
+
+diff /run/credstore/network.conf.50-testme /run/systemd/networkd.conf.d/50-testme.conf
+diff /run/credstore/network.network.50-testme /run/systemd/network/50-testme.network
diff --git a/test/units/TEST-74-AUX-UTILS.networkctl.sh b/test/units/TEST-74-AUX-UTILS.networkctl.sh
new file mode 100755 (executable)
index 0000000..3e333a2
--- /dev/null
@@ -0,0 +1,124 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+    systemctl stop systemd-networkd
+
+    if [[ -v NETWORK_NAME && -v NETDEV_NAME && -v LINK_NAME ]]; then
+        rm -fvr {/usr/lib,/etc,/run}/systemd/network/"$NETWORK_NAME" "/usr/lib/systemd/network/$NETDEV_NAME" \
+            {/usr/lib,/etc}/systemd/network/"$LINK_NAME" "/etc/systemd/network/${NETWORK_NAME}.d" \
+            "new" "+4"
+    fi
+
+    rm -f /run/systemd/networkd.conf.d/10-hoge.conf
+}
+
+trap at_exit EXIT
+
+export NETWORK_NAME="10-networkctl-test-$RANDOM.network"
+export NETDEV_NAME="10-networkctl-test-$RANDOM.netdev"
+export LINK_NAME="10-networkctl-test-$RANDOM.link"
+cat >"/usr/lib/systemd/network/$NETWORK_NAME" <<EOF
+[Match]
+Name=test
+EOF
+
+# Test files
+
+networkctl mask --runtime "donotexist.network"
+assert_eq "$(readlink /run/systemd/network/donotexist.network)" "/dev/null"
+networkctl unmask "donotexist.network" # unmask should work even without --runtime
+[[ ! -e /run/systemd/network/donotexist.network ]]
+
+touch /usr/lib/systemd/network/donotexist.network
+(! networkctl unmask "donotexist.network")
+rm /usr/lib/systemd/network/donotexist.network
+
+networkctl cat "$NETWORK_NAME" | tail -n +2 | cmp - "/usr/lib/systemd/network/$NETWORK_NAME"
+
+cat >new <<EOF
+[Match]
+Name=test2
+EOF
+
+EDITOR='mv new' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null
+(! networkctl mask --runtime "$NETWORK_NAME")
+printf '%s\n' '[Match]' 'Name=test2' | cmp - "/run/systemd/network/$NETWORK_NAME"
+
+networkctl mask "$NETWORK_NAME"
+assert_eq "$(readlink "/etc/systemd/network/$NETWORK_NAME")" "/dev/null"
+(! networkctl edit "$NETWORK_NAME")
+(! networkctl edit --runtime "$NETWORK_NAME")
+(! networkctl cat "$NETWORK_NAME")
+networkctl unmask "$NETWORK_NAME"
+
+EDITOR='true' script -ec 'networkctl edit "$NETWORK_NAME"' /dev/null
+printf '%s\n' '[Match]' 'Name=test2' | cmp - "/etc/systemd/network/$NETWORK_NAME"
+
+(! networkctl mask "$NETWORK_NAME")
+(! EDITOR='true' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null)
+
+cat >"+4" <<EOF
+[Network]
+IPv6AcceptRA=no
+EOF
+
+EDITOR='cp' script -ec 'networkctl edit "$NETWORK_NAME" --drop-in test' /dev/null
+cmp "+4" "/etc/systemd/network/${NETWORK_NAME}.d/test.conf"
+
+networkctl cat "$NETWORK_NAME" | grep '^# ' |
+    cmp - <(printf '%s\n' "# /etc/systemd/network/$NETWORK_NAME" "# /etc/systemd/network/${NETWORK_NAME}.d/test.conf")
+
+cat >"/usr/lib/systemd/network/$NETDEV_NAME" <<EOF
+[NetDev]
+Name=test2
+Kind=dummy
+EOF
+
+networkctl cat "$NETDEV_NAME"
+
+cat >"/usr/lib/systemd/network/$LINK_NAME" <<EOF
+[Match]
+OriginalName=test2
+
+[Link]
+Alias=test_alias
+EOF
+
+SYSTEMD_LOG_LEVEL=debug EDITOR='true' script -ec 'networkctl edit "$LINK_NAME"' /dev/null
+cmp "/usr/lib/systemd/network/$LINK_NAME" "/etc/systemd/network/$LINK_NAME"
+
+# Test links
+systemctl unmask systemd-networkd
+systemctl stop systemd-networkd
+(! networkctl cat @test2)
+
+systemctl start systemd-networkd
+SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-networkd-wait-online -i test2:carrier --timeout 20
+networkctl cat @test2:network | cmp - <(networkctl cat "$NETWORK_NAME")
+
+EDITOR='cp' script -ec 'networkctl edit @test2 --drop-in test2.conf' /dev/null
+cmp "+4" "/etc/systemd/network/${NETWORK_NAME}.d/test2.conf"
+
+SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-networkd-wait-online -i test2:carrier --timeout 20
+(! EDITOR='true' script -ec 'networkctl edit @test2 --runtime --drop-in test2.conf' /dev/null)
+
+ip_link="$(ip link show test2)"
+if systemctl --quiet is-active systemd-udevd; then
+    assert_in 'alias test_alias' "$ip_link"
+fi
+
+mkdir -p /run/systemd/networkd.conf.d
+cat >/run/systemd/networkd.conf.d/10-hoge.conf <<EOF
+# TEST DROP-IN FILE
+[Network]
+SpeedMeter=yes
+EOF
+
+assert_in '# TEST DROP-IN FILE' "$(networkctl cat)"
diff --git a/test/units/TEST-74-AUX-UTILS.path.sh b/test/units/TEST-74-AUX-UTILS.path.sh
new file mode 100755 (executable)
index 0000000..79056a5
--- /dev/null
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+USER_DIRS_CONF="/root/.config/user-dirs.dirs"
+
+at_exit() {
+    set +e
+
+    rm -fv "${USER_DIRS_CONF:?}"
+}
+
+trap at_exit EXIT
+
+# Check that we indeed run under root to make the rest of the test work
+[[ "$(id -u)" -eq 0 ]]
+
+# Create a custom user-dirs.dir file to exercise the xdg-user-dirs part
+# of sd-path/from_user_dir()
+mkdir -p "/root/.config"
+cat >"${USER_DIRS_CONF:?}" <<\EOF
+XDG_DESKTOP_DIR="$HOME/my-fancy-desktop"
+XDG_INVALID
+
+XDG_DOWNLOAD_DIR   = "$HOME"
+XDG_TEMPLATES_DIR="/templates"
+# Invalid records
+XDG_TEMPLATES_DIR=/not-templates"
+XDG_TEMPLATES_DIR="/also-not-teplates
+XDG_TEMPLATES_DIR=""
+XDG_TEMPLATES_DIR="../"
+
+XDG_PUBLICSHARE_DIR="$HOME/cat-pictures"
+XDG_DOCUMENTS_DIR="$HOME/top/secret/documents"
+XDG_MUSIC_DIR="/tmp/vaporwave"
+XDG_PICTURES_DIR="$HOME/Pictures"
+XDG_VIDEOS_DIR="$HOME/🤔"
+EOF
+
+systemd-path --help
+systemd-path --version
+systemd-path
+systemd-path temporary system-binaries user binfmt
+
+assert_eq "$(systemd-path system-runtime)" "/run"
+assert_eq "$(systemd-path --suffix='' system-runtime)" "/run"
+assert_eq "$(systemd-path --suffix='🤔' system-runtime)" "/run/🤔"
+assert_eq "$(systemd-path --suffix=hello system-runtime)" "/run/hello"
+
+# Note for the stuff below: everything defaults to $HOME, only the desktop
+# directory defaults to $HOME/Desktop.
+#
+# Check the user-dirs.dir stuff from above
+assert_eq "$(systemd-path user)" "/root"
+assert_eq "$(systemd-path user-desktop)" "/root/my-fancy-desktop"
+assert_eq "$(systemd-path user-documents)" "/root/top/secret/documents"
+assert_eq "$(systemd-path user-download)" "/root"
+assert_eq "$(systemd-path user-music)" "/tmp/vaporwave"
+assert_eq "$(systemd-path user-pictures)" "/root/Pictures"
+assert_eq "$(systemd-path user-public)" "/root/cat-pictures"
+assert_eq "$(systemd-path user-templates)" "/templates"
+assert_eq "$(systemd-path user-videos)" "/root/🤔"
+
+# Remove the user-dirs.dir file and check the defaults
+rm -fv "$USER_DIRS_CONF"
+[[ ! -e "$USER_DIRS_CONF" ]]
+assert_eq "$(systemd-path user-desktop)" "/root/Desktop"
+for dir in "" documents download music pictures public templates videos; do
+    assert_eq "$(systemd-path "user${dir:+-$dir}")" "/root"
+done
+
+# sd-path should consider only absolute $HOME
+assert_eq "$(HOME=/hello-world systemd-path user)" "/hello-world"
+assert_eq "$(HOME=hello-world systemd-path user)" "/root"
+assert_eq "$(HOME=/hello systemd-path --suffix=world user)" "/hello/world"
+assert_eq "$(HOME=hello systemd-path --suffix=world user)" "/root/world"
+# Same with some other env variables
+assert_in "/my-config" "$(HOME='' XDG_CONFIG_HOME=/my-config systemd-path search-configuration)"
+assert_in "/my-config/foo" "$(HOME='' XDG_CONFIG_HOME=/my-config systemd-path --suffix=foo search-configuration)"
+assert_in "/my-home/.config/foo" "$(HOME=/my-home XDG_CONFIG_HOME=my-config systemd-path --suffix=foo search-configuration)"
+assert_not_in "my-config" "$(HOME=my-config XDG_CONFIG_HOME=my-config systemd-path search-configuration)"
+
+(! systemd-path '')
+(! systemd-path system-binaries 🤔 user)
+(! systemd-path --xyz)
diff --git a/test/units/TEST-74-AUX-UTILS.pstore.sh b/test/units/TEST-74-AUX-UTILS.pstore.sh
new file mode 100755 (executable)
index 0000000..9be8066
--- /dev/null
@@ -0,0 +1,258 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+systemctl log-level info
+
+if systemd-detect-virt -cq; then
+    echo "Running in a container, skipping the systemd-pstore test..."
+    exit 0
+fi
+
+DUMMY_DMESG_0="$(mktemp)"
+cat >"$DUMMY_DMESG_0" <<\EOF
+6,17159,5340096332127,-;usb 1-4: USB disconnect, device number 124
+6,17160,5340109662397,-;input: WH-1000XM3 (AVRCP) as /devices/virtual/input/input293
+6,17161,5343126458360,-;loop0: detected capacity change from 0 to 3145728
+6,17162,5343126766065,-; loop0: p1 p2
+6,17163,5343126815038,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none.
+6,17164,5343158037334,-;EXT4-fs (loop0p1): unmounting filesystem.
+6,17165,5343158072598,-;loop0: detected capacity change from 0 to 3145728
+6,17166,5343158073563,-; loop0: p1 p2
+6,17167,5343158074325,-; loop0: p1 p2
+6,17168,5343158140859,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none.
+6,17169,5343158182977,-;EXT4-fs (loop0p1): unmounting filesystem.
+6,17170,5343158700241,-;loop0: detected capacity change from 0 to 3145728
+6,17171,5343158700439,-; loop0: p1 p2
+6,17172,5343158701120,-; loop0: p1 p2
+EOF
+
+DUMMY_DMESG_1="$(mktemp)"
+cat >"$DUMMY_DMESG_1" <<\EOF
+Nechť již hříšné saxofony ďáblů rozezvučí síň úděsnými tóny waltzu, tanga a quickstepu.
+Příliš žluťoučký kůň úpěl ďábelské ódy.
+Zvlášť zákeřný učeň s ďolíčky běží podél zóny úlů.
+Vyciď křišťálový nůž, ó učiň úděsné líbivým!
+Loď čeří kýlem tůň obzvlášť v Grónské úžině
+Ó, náhlý déšť již zvířil prach a čilá laň teď běží s houfcem gazel k úkrytům.
+Vypätá dcéra grófa Maxwella s IQ nižším ako kôň núti čeľaď hrýzť hŕbu jabĺk.
+Kŕdeľ šťastných ďatľov učí pri ústí Váhu mĺkveho koňa obhrýzať kôru a žrať čerstvé mäso.
+Stróż pchnął kość w quiz gędźb vel fax myjń.
+Portez ce vieux whisky au juge blond qui fume!
+EOF
+
+file_count() { find "${1:?}" -type f | wc -l; }
+file_size() { wc -l <"${1:?}"; }
+random_efi_timestamp() { printf "%0.10d" "$((1000000000 + RANDOM))"; }
+
+# The dmesg- filename contains the backend-type and the Common Platform Error Record, CPER,
+# record id, a 64-bit number.
+#
+# Files are processed in reverse lexigraphical order so as to properly reconstruct original dmesg.
+
+prepare_efi_logs() {
+    local file="${1:?}"
+    local timestamp="${2:?}"
+    local chunk count filename
+
+    # For the EFI backend, the 3 least significant digits of record id encodes a
+    # "count" number, the next 2 least significant digits for the dmesg part
+    # (chunk) number, and the remaining digits as the timestamp.  See
+    # linux/drivers/firmware/efi/efi-pstore.c in efi_pstore_write().
+    count="$(file_size "$file")"
+    chunk=0
+    # The sed in the process substitution below just reverses the file
+    while read -r line; do
+        filename="$(printf "dmesg-efi-%0.10d%0.2d%0.3d" "$timestamp" "$chunk" "$count")"
+        echo "$line" >"/sys/fs/pstore/$filename"
+        chunk=$((chunk + 1))
+    done < <(sed '1!G;h;$!d' "$file")
+
+    if [[ "$chunk" -eq 0 ]]; then
+        echo >&2 "No dmesg-efi files were created"
+        exit 1
+    fi
+}
+
+prepare_erst_logs() {
+    local file="${1:?}"
+    local start_id="${2:?}"
+    local id filename
+
+    # For the ERST backend, the record is a monotonically increasing number, seeded as
+    # a timestamp. See linux/drivers/acpi/apei/erst.c in erst_writer().
+    id="$start_id"
+    # The sed in the process substitution below just reverses the file
+    while read -r line; do
+        filename="$(printf "dmesg-erst-%0.16d" "$id")"
+        echo "$line" >"/sys/fs/pstore/$filename"
+        id=$((id + 1))
+    done < <(sed '1!G;h;$!d' "$file")
+
+    if [[ "$id" -eq "$start_id" ]]; then
+        echo >&2 "No dmesg-erst files were created"
+        exit 1
+    fi
+
+    # ID of the last dmesg file will be the ID of the erst subfolder
+    echo "$((id - 1))"
+}
+
+prepare_pstore_config() {
+    local storage="${1:?}"
+    local unlink="${2:?}"
+
+    systemctl stop systemd-pstore
+
+    rm -fr /sys/fs/pstore/* /var/lib/systemd/pstore/*
+
+    mkdir -p /run/systemd/pstore.conf.d
+    cat >/run/systemd/pstore.conf.d/99-test.conf <<EOF
+[PStore]
+Storage=$storage
+Unlink=$unlink
+EOF
+
+    systemd-analyze cat-config systemd/pstore.conf | grep "$storage"
+    systemd-analyze cat-config systemd/pstore.conf | grep "$unlink"
+}
+
+start_pstore() {
+    rm -f /tmp/journal.cursor
+    journalctl -q -n 0 --cursor-file=/tmp/journal.cursor
+    systemctl restart systemd-pstore
+    journalctl --sync
+}
+
+at_exit() {
+    set +e
+
+    mountpoint -q /sys/fs/pstore && umount /sys/fs/pstore
+    rm -fr /var/lib/systemd/pstore/*
+    rm -f /run/systemd/system/systemd-pstore.service.d/99-StartLimitInterval.conf
+    rm -f /run/systemd/pstore.conf.d/99-test.conf
+}
+
+trap at_exit EXIT
+
+# To avoid having to depend on the VM providing the pstore, let's simulate
+# it using a simple bind mount
+PSTORE_DIR="$(mktemp -d)"
+mount --bind "${PSTORE_DIR:?}" "/sys/fs/pstore"
+
+# Disable the start limit since we're going to restart the systemd-pstore
+# service quite a lot in a short time span
+mkdir -p /run/systemd/system/systemd-pstore.service.d
+cat >/run/systemd/system/systemd-pstore.service.d/99-StartLimitInterval.conf <<EOF
+[Unit]
+StartLimitInterval=0
+EOF
+systemctl daemon-reload
+
+# systemd-pstore is a no-op with Storage=none
+for unlink in yes no; do
+    : "Backend: N/A; Storage: none; Unlink: $unlink"
+    timestamp="$(random_efi_timestamp)"
+    prepare_pstore_config "none" "$unlink"
+    prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp"
+    old_count="$(file_count /sys/fs/pstore/)"
+    start_pstore
+    [[ "$(file_count /sys/fs/pstore)" -ge "$old_count" ]]
+    [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
+
+    : "Backend: EFI; Storage: external; Unlink: $unlink"
+    timestamp="$(random_efi_timestamp)"
+    prepare_pstore_config "external" "$unlink"
+    prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp"
+    [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
+    start_pstore
+    [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
+    [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
+    # We always log to journal
+    diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
+    filename="$(printf "/var/lib/systemd/pstore/%s/%0.3d/dmesg.txt" "$timestamp" "$(file_size "$DUMMY_DMESG_0")")"
+    diff "$DUMMY_DMESG_0" "$filename"
+
+    : "Backend: EFI; Storage: external; Unlink: $unlink; multiple dmesg files"
+    timestamps=()
+    timestamp="$(random_efi_timestamp)"
+    prepare_pstore_config "external" "$unlink"
+    for i in {0..6}; do
+        timestamp="$((timestamp + (i * 10)))"
+        timestamps+=("$timestamp")
+        # Create a name reference to one of the $DUMMY_DMESG_X variables
+        dmesg="DUMMY_DMESG_$((i % 2))"
+        prepare_efi_logs "${!dmesg}" "$timestamp"
+        # Add one "random" (non-dmesg) file as well
+        echo "hello world" >/sys/fs/pstore/foo.bar
+        [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
+        start_pstore
+        [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
+        [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
+        filename="$(printf "/var/lib/systemd/pstore/%s/%0.3d/dmesg.txt" "$timestamp" "$(file_size "${!dmesg}")")"
+        diff "${!dmesg}" "$filename"
+        grep "hello world" "/var/lib/systemd/pstore/foo.bar"
+    done
+    # Check that we kept all previous records as well
+    for timestamp in "${timestamps[@]}"; do
+        [[ -d "/var/lib/systemd/pstore/$timestamp" ]]
+        [[ "$(file_count "/var/lib/systemd/pstore/$timestamp/")" -gt 0 ]]
+    done
+
+    : "Backend: EFI; Storage: journal; Unlink: $unlink"
+    timestamp="$(random_efi_timestamp)"
+    prepare_pstore_config "journal" "$unlink"
+    prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp"
+    [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
+    start_pstore
+    [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
+    [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
+    diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
+
+    : "Backend: ERST; Storage: external; Unlink: $unlink"
+    prepare_pstore_config "external" "$unlink"
+    last_id="$(prepare_erst_logs "$DUMMY_DMESG_0" 0)"
+    [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
+    start_pstore
+    [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
+    [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
+    # We always log to journal
+    diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
+    filename="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id")"
+    diff "$DUMMY_DMESG_0" "$filename"
+
+    : "Backend: ERST; Storage: external; Unlink: $unlink; multiple dmesg files"
+    last_ids=()
+    prepare_pstore_config "external" "$unlink"
+    for i in {0..9}; do
+        # Create a name reference to one of the $DUMMY_DMESG_X variables
+        dmesg="DUMMY_DMESG_$((i % 2))"
+        last_id="$(prepare_erst_logs "${!dmesg}" "$((i * 100))")"
+        last_ids+=("$last_id")
+        # Add one "random" (non-dmesg) file as well
+        echo "hello world" >/sys/fs/pstore/foo.bar
+        [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
+        start_pstore
+        [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
+        [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
+        filename="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id")"
+        diff "${!dmesg}" "$filename"
+        grep "hello world" "/var/lib/systemd/pstore/foo.bar"
+    done
+    # Check that we kept all previous records as well
+    for last_id in "${last_ids[@]}"; do
+        directory="$(printf "/var/lib/systemd/pstore/%0.16d" "$last_id")"
+        [[ -d "$directory" ]]
+        [[ "$(file_count "$directory")" -gt 0 ]]
+    done
+
+    : "Backend: ERST; Storage: journal; Unlink: $unlink"
+    prepare_pstore_config "journal" "$unlink"
+    last_id="$(prepare_erst_logs "$DUMMY_DMESG_0" 0)"
+    [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
+    start_pstore
+    [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
+    [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
+    diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
+done
diff --git a/test/units/TEST-74-AUX-UTILS.run.sh b/test/units/TEST-74-AUX-UTILS.run.sh
new file mode 100755 (executable)
index 0000000..b4ff72e
--- /dev/null
@@ -0,0 +1,247 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+systemd-run --help --no-pager
+systemd-run --version
+systemd-run --no-ask-password true
+systemd-run --no-block --collect true
+
+export PARENT_FOO=bar
+touch /tmp/public-marker
+
+: "Transient service (system daemon)"
+systemd-run --wait --pipe \
+            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.service$ ]]'
+systemd-run --wait --pipe --system \
+            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.service$ ]]'
+systemd-run --wait --pipe --slice=foo \
+            bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.service$ ]]'
+systemd-run --wait --pipe --slice=foo.slice \
+            bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.service$ ]]'
+systemd-run --wait --pipe --slice-inherit \
+            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.service$ ]]'
+systemd-run --wait --pipe --slice-inherit --slice=foo \
+            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/system-foo\.slice/run-.+\.service$ ]]'
+# We should not inherit caller's environment
+systemd-run --wait --pipe bash -xec '[[ -z "$PARENT_FOO" ]]'
+systemd-run --wait --pipe bash -xec '[[ "$PWD" == / && -n "$INVOCATION_ID" ]]'
+systemd-run --wait --pipe \
+            --send-sighup \
+            --working-directory="" \
+            --working-directory=/tmp \
+            bash -xec '[[ "$PWD" == /tmp ]]'
+systemd-run --wait --pipe --same-dir bash -xec "[[ \"\$PWD\" == $PWD ]]"
+systemd-run --wait --pipe \
+            --property=LimitCORE=1M:2M \
+            --property=LimitCORE=16M:32M \
+            --property=PrivateTmp=yes \
+            bash -xec '[[ "$(ulimit -c -S)" -eq 16384 && "$(ulimit -c -H)" -eq 32768 && ! -e /tmp/public-marker ]]'
+systemd-run --wait --pipe \
+            --uid=testuser \
+            bash -xec '[[ "$(id -nu)" == testuser && "$(id -ng)" == testuser ]]'
+systemd-run --wait --pipe \
+            --gid=testuser \
+            bash -xec '[[ "$(id -nu)" == root && "$(id -ng)" == testuser ]]'
+systemd-run --wait --pipe \
+            --uid=testuser \
+            --gid=root \
+            bash -xec '[[ "$(id -nu)" == testuser && "$(id -ng)" == root ]]'
+systemd-run --wait --pipe --expand-environment=no \
+            --nice=10 \
+            bash -xec 'read -r -a SELF_STAT </proc/self/stat && [[ "${SELF_STAT[18]}" -eq 10 ]]'
+systemd-run --wait --pipe \
+            --setenv=ENV_HELLO="nope" \
+            --setenv=ENV_HELLO="env world" \
+            --setenv=EMPTY= \
+            --setenv=PARENT_FOO \
+            --property=Environment="ALSO_HELLO='also world'" \
+            bash -xec '[[ "$ENV_HELLO" == "env world" && -z "$EMPTY" && "$PARENT_FOO" == bar && "$ALSO_HELLO" == "also world" ]]'
+
+UNIT="service-0-$RANDOM"
+systemd-run --remain-after-exit --unit="$UNIT" \
+            --service-type=simple \
+            --service-type=oneshot \
+            true
+systemctl cat "$UNIT"
+grep -q "^Type=oneshot" "/run/systemd/transient/$UNIT.service"
+systemctl stop "$UNIT"
+(! systemctl cat "$UNIT")
+
+: "Transient service (user daemon)"
+systemd-run --wait --pipe --user --machine=testuser@ \
+            bash -xec '[[ "$(</proc/self/cgroup)" =~ /user\.slice/.+/run-.+\.service$ ]]'
+systemd-run --wait --pipe --user --machine=testuser@ \
+            bash -xec '[[ "$(id -nu)" == testuser && "$(id -ng)" == testuser ]]'
+systemd-run --wait --pipe --user --machine=testuser@ \
+            bash -xec '[[ "$PWD" == /home/testuser && -n "$INVOCATION_ID" ]]'
+
+# PrivateTmp=yes implies PrivateUsers=yes for user manager, so skip this if we
+# don't have unprivileged user namespaces.
+if [[ "$(sysctl -ne kernel.apparmor_restrict_unprivileged_userns)" -ne 1 ]]; then
+    systemd-run --wait --pipe --user --machine=testuser@ \
+                --property=LimitCORE=1M:2M \
+                --property=LimitCORE=16M:32M \
+                --property=PrivateTmp=yes \
+                bash -xec '[[ "$(ulimit -c -S)" -eq 16384 && "$(ulimit -c -H)" -eq 32768 && ! -e /tmp/public-marker ]]'
+fi
+
+: "Transient scope (system daemon)"
+systemd-run --scope \
+            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.scope$ ]]'
+systemd-run --scope --system \
+            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.scope$ ]]'
+systemd-run --scope --slice=foo \
+            bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.scope$ ]]'
+systemd-run --scope --slice=foo.slice \
+            bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.scope$ ]]'
+systemd-run --scope --slice-inherit \
+            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.scope$ ]]'
+systemd-run --scope --slice-inherit --slice=foo \
+            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/system-foo\.slice/run-.+\.scope$ ]]'
+# We should inherit caller's environment
+systemd-run --scope bash -xec '[[ "$PARENT_FOO" == bar ]]'
+systemd-run --scope \
+            --property=RuntimeMaxSec=10 \
+            --property=RuntimeMaxSec=infinity \
+            true
+
+: "Transient scope (user daemon)"
+# FIXME: https://github.com/systemd/systemd/issues/27883
+#systemd-run --scope --user --machine=testuser@ \
+#            bash -xec '[[ "$(</proc/self/cgroup)" =~ /user\.slice/run-.+\.scope$ ]]'
+# We should inherit caller's environment
+#systemd-run --scope --user --machine=testuser@ bash -xec '[[ "$PARENT_FOO" == bar ]]'
+
+: "Transient timer unit"
+UNIT="timer-0-$RANDOM"
+systemd-run --remain-after-exit \
+            --unit="$UNIT" \
+            --timer-property=OnUnitInactiveSec=16h \
+            true
+systemctl cat "$UNIT.service" "$UNIT.timer"
+grep -q "^OnUnitInactiveSec=16h$" "/run/systemd/transient/$UNIT.timer"
+grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
+systemctl stop "$UNIT.timer" "$UNIT.service" || :
+
+UNIT="timer-1-$RANDOM"
+systemd-run --remain-after-exit \
+            --unit="$UNIT" \
+            --on-active=10 \
+            --on-active=30s \
+            --on-boot=1s \
+            --on-startup=2m \
+            --on-unit-active=3h20m \
+            --on-unit-inactive="5d 4m 32s" \
+            --on-calendar="mon,fri *-1/2-1,3 *:30:45" \
+            --on-clock-change \
+            --on-clock-change \
+            --on-timezone-change \
+            --timer-property=After=systemd-journald.service \
+            --description="Hello world" \
+            --description="My Fancy Timer" \
+            true
+systemctl cat "$UNIT.service" "$UNIT.timer"
+systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.service"
+systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.timer"
+grep -q "^Description=My Fancy Timer$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnActiveSec=10s$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnActiveSec=30s$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnBootSec=1s$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnStartupSec=2min$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnUnitActiveSec=3h 20min$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnUnitInactiveSec=5d 4min 32s$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnCalendar=mon,fri \*\-1/2\-1,3 \*:30:45$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnClockChange=yes$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^OnTimezoneChange=yes$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^After=systemd-journald.service$" "/run/systemd/transient/$UNIT.timer"
+grep -q "^Description=My Fancy Timer$" "/run/systemd/transient/$UNIT.service"
+grep -q "^RemainAfterExit=yes$" "/run/systemd/transient/$UNIT.service"
+grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
+(! grep -q "^After=systemd-journald.service$" "/run/systemd/transient/$UNIT.service")
+systemctl stop "$UNIT.timer" "$UNIT.service" || :
+
+: "Transient path unit"
+UNIT="path-0-$RANDOM"
+systemd-run --remain-after-exit \
+            --unit="$UNIT" \
+            --path-property=PathExists=/tmp \
+            --path-property=PathExists=/tmp/foo \
+            --path-property=PathChanged=/root/bar \
+            true
+systemctl cat "$UNIT.service" "$UNIT.path"
+systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.service"
+systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.path"
+grep -q "^PathExists=/tmp$" "/run/systemd/transient/$UNIT.path"
+grep -q "^PathExists=/tmp/foo$" "/run/systemd/transient/$UNIT.path"
+grep -q "^PathChanged=/root/bar$" "/run/systemd/transient/$UNIT.path"
+grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
+systemctl stop "$UNIT.path" "$UNIT.service" || :
+
+: "Transient socket unit"
+UNIT="socket-0-$RANDOM"
+systemd-run --remain-after-exit \
+            --unit="$UNIT" \
+            --socket-property=ListenFIFO=/tmp/socket.fifo \
+            --socket-property=SocketMode=0666 \
+            --socket-property=SocketMode=0644 \
+            true
+systemctl cat "$UNIT.service" "$UNIT.socket"
+systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.service"
+systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.socket"
+grep -q "^ListenFIFO=/tmp/socket.fifo$" "/run/systemd/transient/$UNIT.socket"
+grep -q "^SocketMode=0666$" "/run/systemd/transient/$UNIT.socket"
+grep -q "^SocketMode=0644$" "/run/systemd/transient/$UNIT.socket"
+grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
+systemctl stop "$UNIT.socket" "$UNIT.service" || :
+
+: "Interactive options"
+SHELL=/bin/true systemd-run --shell
+SHELL=/bin/true systemd-run --scope --shell
+systemd-run --wait --pty true
+systemd-run --wait --machine=.host --pty true
+(! SHELL=/bin/false systemd-run --quiet --shell)
+
+(! systemd-run)
+(! systemd-run "")
+(! systemd-run --foo=bar)
+(! systemd-run --wait --pipe --slice=foo.service true)
+
+for opt in nice on-{active,boot,calendar,startup,unit-active,unit-inactive} property service-type setenv; do
+    (! systemd-run "--$opt=" true)
+    (! systemd-run "--$opt=''" true)
+done
+
+# Let's make sure that ProtectProc= properly moves submounts of the original /proc over to the new proc
+BOOT_ID="$(</proc/sys/kernel/random/boot_id)"
+UNIT_BOOT_ID="$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/sys/kernel/random/boot_id)"
+assert_eq "$BOOT_ID" "$UNIT_BOOT_ID"
+
+TMP_KVER="/tmp/version.$RANDOM"
+KVER="$(</proc/version).piff"
+echo "$KVER" >"$TMP_KVER"
+mount --bind "$TMP_KVER" /proc/version
+UNIT_KVER="$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/version)"
+assert_eq "$KVER" "$UNIT_KVER"
+umount /proc/version
+rm -f "$TMP_KVER"
+
+if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; then
+    # Check that invoking the tool under the run0 alias name works
+    run0 ls /
+    assert_eq "$(run0 echo foo)" "foo"
+    # Check if we set some expected environment variables
+    for arg in "" "--user=root" "--user=testuser"; do
+        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER"
+        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
+        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
+    done
+    # Let's chain a couple of run0 calls together, for fun
+    readarray -t cmdline < <(printf "%.0srun0\n" {0..31})
+    assert_eq "$("${cmdline[@]}" bash -c 'echo $SUDO_USER')" "$USER"
+fi
diff --git a/test/units/TEST-74-AUX-UTILS.service b/test/units/TEST-74-AUX-UTILS.service
new file mode 100644 (file)
index 0000000..f782132
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-74-AUX-UTILS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-74-AUX-UTILS.sh b/test/units/TEST-74-AUX-UTILS.sh
new file mode 100755 (executable)
index 0000000..9c2a033
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
diff --git a/test/units/TEST-74-AUX-UTILS.socket.sh b/test/units/TEST-74-AUX-UTILS.socket.sh
new file mode 100755 (executable)
index 0000000..7ef85fa
--- /dev/null
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+    systemctl stop per-source-limit.socket
+    rm -f /run/systemd/system/per-source-limit{@.service,.socket} /run/foo.conn{1..4}
+    systemctl daemon-reload
+}
+
+trap at_exit EXIT
+
+cat >/run/systemd/system/per-source-limit.socket <<EOF
+[Socket]
+ListenStream=/run/per-source-limit.sk
+MaxConnectionsPerSource=2
+Accept=yes
+EOF
+
+cat >/run/systemd/system/per-source-limit@.service <<EOF
+[Unit]
+BindsTo=per-source-limit.socket
+After=per-source-limit.socket
+
+[Service]
+ExecStartPre=echo waldo
+ExecStart=sleep infinity
+StandardOutput=socket
+EOF
+
+systemctl daemon-reload
+systemctl start per-source-limit.socket
+systemctl status per-source-limit.socket
+
+# So these two should take up the first two connection slots
+socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn1 &
+J1="$!"
+socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn2 &
+J2="$!"
+
+waitfor() {
+    local file="${1:?}"
+
+    for _ in {0..20}; do
+        if grep -q waldo "$file"; then
+            return 0
+        fi
+
+        sleep .5
+    done
+
+    echo >&2 "Timeout while waiting for the expected output"
+    return 1
+}
+
+# Wait until the word "waldo" shows in the output files
+waitfor /tmp/foo.conn1
+waitfor /tmp/foo.conn2
+
+# The next connection should fail, because the limit is hit
+socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn3 &
+J3="$!"
+
+# But this one should work, because done under a different UID
+setpriv --reuid=1 socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn4 &
+J4="$!"
+
+waitfor /tmp/foo.conn4
+
+# The third job should fail quickly, wait for it
+wait "$J3"
+
+# The other jobs will hang forever, since we run "sleep infinity" on the server side. Let's kill the jobs now.
+kill "$J1"
+kill "$J2"
+kill "$J4"
+
+# The 3rd connection should not have seen "waldo", since it should have been refused too early
+(! grep -q "waldo" /tmp/foo.conn3 )
diff --git a/test/units/TEST-74-AUX-UTILS.ssh.sh b/test/units/TEST-74-AUX-UTILS.ssh.sh
new file mode 100755 (executable)
index 0000000..5d87d9f
--- /dev/null
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if ! command -v ssh &> /dev/null || ! command -v sshd &> /dev/null ; then
+    echo "ssh/sshd not found, skipping test." >&2
+    exit 0
+fi
+
+systemctl -q is-active sshd-unix-local.socket
+
+if test -e /dev/vsock ; then
+    systemctl -q is-active sshd-vsock.socket
+fi
+
+if test -d /run/host/unix-export ; then
+    systemctl -q is-active sshd-unix-export.socket
+fi
+
+# FIXME: sshd seems to crash inside asan currently, skip the actual ssh test hence
+if [[ -v ASAN_OPTIONS ]] ; then
+    exit 0
+fi
+
+ROOTID=$(mktemp -u)
+
+removesshid() {
+    rm -f "$ROOTID" "$ROOTID".pub
+}
+
+ssh-keygen -N '' -C '' -t rsa -f "$ROOTID"
+
+mkdir -p 0700 /root/.ssh
+# Add a newline in case authorized_keys wasn't terminated correctly.
+echo >>/root/.ssh/authorized_keys
+cat "$ROOTID".pub >>/root/.ssh/authorized_keys
+
+# set root pw to "foo", just to set it to something valid
+# shellcheck disable=SC2016
+usermod -p '$5$AAy6BYJ6rzz.QELv$6LpVEU3/RQmVz.svHu/33qoJWWWzZuJ3DM2fo9JgcUD' root
+usermod -U root
+
+mkdir -p /etc/ssh
+test -f /etc/ssh/ssh_host_rsa_key || ssh-keygen -t rsa -C '' -N '' -f /etc/ssh/ssh_host_rsa_key
+echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
+echo "LogLevel DEBUG3" >> /etc/ssh/sshd_config
+
+test -f /etc/ssh/ssh_config || echo 'Include /etc/ssh/ssh_config.d/*.conf' > /etc/ssh/ssh_config
+
+# ssh wants this dir around, but distros cannot agree on a common name for it, let's just create all that are aware of distros use
+mkdir -p /usr/share/empty.sshd /var/empty /var/empty/sshd /run/sshd
+
+ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" .host cat /etc/machine-id | cmp - /etc/machine-id
+ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" unix/run/ssh-unix-local/socket cat /etc/machine-id | cmp - /etc/machine-id
+
+modprobe vsock_loopback ||:
+if test -e /dev/vsock -a -d /sys/module/vsock_loopback ; then
+    ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" vsock/1 cat /etc/machine-id | cmp - /etc/machine-id
+fi
diff --git a/test/units/TEST-74-AUX-UTILS.varlinkctl.sh b/test/units/TEST-74-AUX-UTILS.varlinkctl.sh
new file mode 100755 (executable)
index 0000000..7912360
--- /dev/null
@@ -0,0 +1,119 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Unset $PAGER so we don't have to use --no-pager everywhere
+export PAGER=
+
+varlinkctl --help
+varlinkctl help --no-pager
+varlinkctl --version
+varlinkctl --json=help
+
+# TODO: abstract namespace sockets (@...)
+# Path to a socket
+varlinkctl info /run/systemd/journal/io.systemd.journal
+varlinkctl info /run/systemd/../systemd/../../run/systemd/journal/io.systemd.journal
+varlinkctl info "./$(realpath --relative-to="$PWD" /run/systemd/journal/io.systemd.journal)"
+varlinkctl info unix:/run/systemd/journal/io.systemd.journal
+varlinkctl info --json=off /run/systemd/journal/io.systemd.journal
+varlinkctl info --json=pretty /run/systemd/journal/io.systemd.journal | jq .
+varlinkctl info --json=short /run/systemd/journal/io.systemd.journal | jq .
+varlinkctl info -j /run/systemd/journal/io.systemd.journal | jq .
+
+varlinkctl list-interfaces /run/systemd/journal/io.systemd.journal
+varlinkctl list-interfaces -j /run/systemd/journal/io.systemd.journal | jq .
+
+varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal
+varlinkctl introspect -j /run/systemd/journal/io.systemd.journal io.systemd.Journal | jq .
+
+if command -v userdbctl >/dev/null; then
+    systemctl start systemd-userdbd
+    varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }'
+    varlinkctl call -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }' | jq .
+    varlinkctl call --more /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }'
+    varlinkctl call --more -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' | jq --seq .
+    varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }'
+    (! varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' | grep .)
+fi
+
+IDL_FILE="$(mktemp)"
+varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal | tee "${IDL_FILE:?}"
+varlinkctl validate-idl "$IDL_FILE"
+varlinkctl validate-idl "$IDL_FILE"
+cat /bin/sh >"$IDL_FILE"
+(! varlinkctl validate-idl "$IDL_FILE")
+
+if [[ -x /usr/lib/systemd/systemd-pcrextend ]]; then
+    # Path to an executable
+    varlinkctl info /usr/lib/systemd/systemd-pcrextend
+    varlinkctl info exec:/usr/lib/systemd/systemd-pcrextend
+    varlinkctl list-interfaces /usr/lib/systemd/systemd-pcrextend
+    varlinkctl introspect /usr/lib/systemd/systemd-pcrextend io.systemd.PCRExtend
+fi
+
+# SSH transport
+SSHBINDIR="$(mktemp -d)"
+
+rm_rf_sshbindir() {
+    rm -rf "$SSHBINDIR"
+}
+
+trap rm_rf_sshbindir EXIT
+
+# Create a fake "ssh" binary that validates everything works as expected
+cat > "$SSHBINDIR"/ssh <<'EOF'
+#!/bin/sh
+
+set -xe
+
+test "$1" = "-W"
+test "$2" = "/run/systemd/journal/io.systemd.journal"
+test "$3" = "foobar"
+
+exec socat - UNIX-CONNECT:/run/systemd/journal/io.systemd.journal
+EOF
+
+chmod +x "$SSHBINDIR"/ssh
+
+SYSTEMD_SSH="$SSHBINDIR/ssh" varlinkctl info ssh:foobar:/run/systemd/journal/io.systemd.journal
+
+# Go through all varlink sockets we can find under /run/systemd/ for some extra coverage
+find /run/systemd/ -name "io.systemd*" -type s | while read -r socket; do
+    varlinkctl info "$socket"
+
+    varlinkctl list-interfaces "$socket" | while read -r interface; do
+        varlinkctl introspect "$socket" "$interface"
+    done
+done
+
+(! varlinkctl)
+(! varlinkctl "")
+(! varlinkctl info)
+(! varlinkctl info "")
+(! varlinkctl info /run/systemd/notify)
+(! varlinkctl info /run/systemd/private)
+# Relative paths must begin with ./
+(! varlinkctl info "$(realpath --relative-to="$PWD" /run/systemd/journal/io.systemd.journal)")
+(! varlinkctl info unix:)
+(! varlinkctl info unix:"")
+(! varlinkctl info exec:)
+(! varlinkctl info exec:"")
+(! varlinkctl list-interfaces)
+(! varlinkctl list-interfaces "")
+(! varlinkctl introspect)
+(! varlinkctl introspect /run/systemd/journal/io.systemd.journal)
+(! varlinkctl introspect /run/systemd/journal/io.systemd.journal "")
+(! varlinkctl introspect "" "")
+(! varlinkctl call)
+(! varlinkctl call "")
+(! varlinkctl call "" "")
+(! varlinkctl call "" "" "")
+(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord </dev/null)
+(! varlinkctl validate-idl "")
+(! varlinkctl validate-idl </dev/null)
+
+varlinkctl info /run/systemd/io.systemd.Hostname
+varlinkctl introspect /run/systemd/io.systemd.Hostname io.systemd.Hostname
+varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}'
diff --git a/test/units/TEST-74-AUX-UTILS.vpick.sh b/test/units/TEST-74-AUX-UTILS.vpick.sh
new file mode 100755 (executable)
index 0000000..400097f
--- /dev/null
@@ -0,0 +1,116 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+at_exit() {
+    set +e
+    rm -rf /var/lib/machines/mymachine.raw.v
+    rm -rf /var/lib/machines/mytree.v
+    rm -rf /var/lib/machines/testroot.v
+    umount -l /tmp/dotvroot
+    rmdir /tmp/dotvroot
+}
+
+trap at_exit EXIT
+
+mkdir -p /var/lib/machines/mymachine.raw.v
+
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw
+
+mkdir -p /var/lib/machines/mytree.v
+
+mkdir /var/lib/machines/mytree.v/mytree_33.4
+mkdir /var/lib/machines/mytree.v/mytree_33.5
+mkdir /var/lib/machines/mytree.v/mytree_36.0+0-5
+mkdir /var/lib/machines/mytree.v/mytree_37.0_arm64+2-3
+mkdir /var/lib/machines/mytree.v/mytree_38.0_arm64+0-5
+
+ARCH="$(busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Architecture | cut -d\" -f 2)"
+
+export SYSTEMD_LOG_LEVEL=debug
+
+if [ "$ARCH" = "x86-64" ] ; then
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw"
+
+    systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+elif [ "$ARCH" = "arm64" ] ; then
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
+
+    systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+else
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
+
+    systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+fi
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A x86-64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A ia64)
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p version)" = "7.6.0"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p type)" = "reg"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p filename)" = "mymachine_7.6.0_arm64.raw"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p arch)" = "arm64"
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t reg)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t dir)
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t fifo)
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t sock)
+
+if [ "$ARCH" != "arm64" ] ; then
+    test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_33.5/"
+    test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_33.5/"
+else
+    test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
+    test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
+fi
+
+(! systemd-vpick /var/lib/machines/mytree.v --type=reg)
+
+mkdir /tmp/dotvroot
+mount --bind / /tmp/dotvroot
+
+mkdir /var/lib/machines/testroot.v
+mkdir /var/lib/machines/testroot.v/testroot_32
+ln -s /tmp/dotvroot /var/lib/machines/testroot.v/testroot_33
+mkdir /var/lib/machines/testroot.v/testroot_34
+
+ls -l /var/lib/machines/testroot.v
+
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
+
+find /var/lib/machines/testroot.v/testroot_34
+rm -rf /var/lib/machines/testroot.v/testroot_34
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_33/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /tmp/dotvroot/
+systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true
+
+rm /var/lib/machines/testroot.v/testroot_33
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
+
+rm -rf /var/lib/machines/testroot.v/testroot_32
+(! systemd-vpick /var/lib/machines/testroot.v)
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
diff --git a/test/units/TEST-75-RESOLVED.service b/test/units/TEST-75-RESOLVED.service
new file mode 100644 (file)
index 0000000..3368e04
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-75-RESOLVED
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-75-RESOLVED.sh b/test/units/TEST-75-RESOLVED.sh
new file mode 100755 (executable)
index 0000000..a4417ce
--- /dev/null
@@ -0,0 +1,902 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# vi: ts=4 sw=4 tw=0 et:
+
+# TODO:
+#   - IPv6-only stack
+#   - mDNS
+#   - LLMNR
+#   - DoT/DoH
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# We need at least Knot 3.0 which support (among others) the ds-push directive
+if ! knotc -c /usr/lib/systemd/tests/testdata/knot-data/knot.conf conf-check; then
+    echo "This test requires at least Knot 3.0. skipping..." | tee --append /skipped
+    exit 77
+fi
+
+RUN_OUT="$(mktemp)"
+
+run() {
+    "$@" |& tee "$RUN_OUT"
+}
+
+run_delv() {
+    # Since [0] delv no longer loads /etc/(bind/)bind.keys by default, so we
+    # have to do that explicitly for each invocation
+    run delv -a /etc/bind.keys "$@"
+}
+
+disable_ipv6() {
+    sysctl -w net.ipv6.conf.all.disable_ipv6=1
+}
+
+enable_ipv6() {
+    sysctl -w net.ipv6.conf.all.disable_ipv6=0
+    networkctl reconfigure dns0
+    /usr/lib/systemd/systemd-networkd-wait-online --ipv4 --ipv6 --interface=dns0:routable --timeout=30
+}
+
+monitor_check_rr() (
+    set +x
+    set +o pipefail
+    local since="${1:?}"
+    local match="${2:?}"
+
+    # Wait until the first mention of the specified log message is
+    # displayed. We turn off pipefail for this, since we don't care about the
+    # lhs of this pipe expression, we only care about the rhs' result to be
+    # clean
+    timeout -v 30s journalctl -u resolvectl-monitor.service --since "$since" -f --full | grep -m1 "$match"
+)
+
+restart_resolved() {
+    systemctl stop systemd-resolved.service
+    (! systemctl is-failed systemd-resolved.service)
+    # Reset the restart counter since we call this method a bunch of times
+    # and can occasionally hit the default rate limit
+    systemctl reset-failed systemd-resolved.service
+    systemctl start systemd-resolved.service
+    systemctl service-log-level systemd-resolved.service debug
+}
+
+# Test for resolvectl, resolvconf
+systemctl unmask systemd-resolved.service
+systemctl enable --now systemd-resolved.service
+systemctl service-log-level systemd-resolved.service debug
+ip link add hoge type dummy
+ip link add hoge.foo type dummy
+resolvectl dns hoge 10.0.0.1 10.0.0.2
+resolvectl dns hoge.foo 10.0.0.3 10.0.0.4
+assert_in '10.0.0.1 10.0.0.2' "$(resolvectl dns hoge)"
+assert_in '10.0.0.3 10.0.0.4' "$(resolvectl dns hoge.foo)"
+resolvectl dns hoge 10.0.1.1 10.0.1.2
+resolvectl dns hoge.foo 10.0.1.3 10.0.1.4
+assert_in '10.0.1.1 10.0.1.2' "$(resolvectl dns hoge)"
+assert_in '10.0.1.3 10.0.1.4' "$(resolvectl dns hoge.foo)"
+if ! RESOLVCONF=$(command -v resolvconf 2>/dev/null); then
+    TMPDIR=$(mktemp -d -p /tmp resolvconf-tests.XXXXXX)
+    RESOLVCONF="$TMPDIR"/resolvconf
+    ln -s "$(command -v resolvectl 2>/dev/null)" "$RESOLVCONF"
+fi
+echo nameserver 10.0.2.1 10.0.2.2 | "$RESOLVCONF" -a hoge
+echo nameserver 10.0.2.3 10.0.2.4 | "$RESOLVCONF" -a hoge.foo
+assert_in '10.0.2.1 10.0.2.2' "$(resolvectl dns hoge)"
+assert_in '10.0.2.3 10.0.2.4' "$(resolvectl dns hoge.foo)"
+echo nameserver 10.0.3.1 10.0.3.2 | "$RESOLVCONF" -a hoge.inet.ipsec.192.168.35
+echo nameserver 10.0.3.3 10.0.3.4 | "$RESOLVCONF" -a hoge.foo.dhcp
+assert_in '10.0.3.1 10.0.3.2' "$(resolvectl dns hoge)"
+assert_in '10.0.3.3 10.0.3.4' "$(resolvectl dns hoge.foo)"
+
+# Tests for _localdnsstub and _localdnsproxy
+assert_in '127.0.0.53' "$(resolvectl query _localdnsstub)"
+assert_in '_localdnsstub' "$(resolvectl query 127.0.0.53)"
+assert_in '127.0.0.54' "$(resolvectl query _localdnsproxy)"
+assert_in '_localdnsproxy' "$(resolvectl query 127.0.0.54)"
+
+assert_in '127.0.0.53' "$(dig @127.0.0.53 _localdnsstub)"
+assert_in '_localdnsstub' "$(dig @127.0.0.53 -x 127.0.0.53)"
+assert_in '127.0.0.54' "$(dig @127.0.0.53 _localdnsproxy)"
+assert_in '_localdnsproxy' "$(dig @127.0.0.53 -x 127.0.0.54)"
+
+# Tests for mDNS and LLMNR settings
+mkdir -p /run/systemd/resolved.conf.d
+{
+    echo "[Resolve]"
+    echo "MulticastDNS=no"
+    echo "LLMNR=no"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+restart_resolved
+# make sure networkd is not running.
+systemctl stop systemd-networkd.service
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+# Tests that reloading works
+{
+    echo "[Resolve]"
+    echo "MulticastDNS=yes"
+    echo "LLMNR=yes"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+systemctl reload systemd-resolved.service
+# defaults to yes (both the global and per-link settings are yes)
+assert_in 'yes' "$(resolvectl mdns hoge)"
+assert_in 'yes' "$(resolvectl llmnr hoge)"
+# set per-link setting
+resolvectl mdns hoge yes
+resolvectl llmnr hoge yes
+assert_in 'yes' "$(resolvectl mdns hoge)"
+assert_in 'yes' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge resolve
+resolvectl llmnr hoge resolve
+assert_in 'resolve' "$(resolvectl mdns hoge)"
+assert_in 'resolve' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge no
+resolvectl llmnr hoge no
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+# downgrade global setting to resolve
+{
+    echo "[Resolve]"
+    echo "MulticastDNS=resolve"
+    echo "LLMNR=resolve"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+systemctl reload systemd-resolved.service
+# set per-link setting
+resolvectl mdns hoge yes
+resolvectl llmnr hoge yes
+assert_in 'resolve' "$(resolvectl mdns hoge)"
+assert_in 'resolve' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge resolve
+resolvectl llmnr hoge resolve
+assert_in 'resolve' "$(resolvectl mdns hoge)"
+assert_in 'resolve' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge no
+resolvectl llmnr hoge no
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+# downgrade global setting to no
+{
+    echo "[Resolve]"
+    echo "MulticastDNS=no"
+    echo "LLMNR=no"
+} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
+systemctl reload systemd-resolved.service
+# set per-link setting
+resolvectl mdns hoge yes
+resolvectl llmnr hoge yes
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge resolve
+resolvectl llmnr hoge resolve
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+resolvectl mdns hoge no
+resolvectl llmnr hoge no
+assert_in 'no' "$(resolvectl mdns hoge)"
+assert_in 'no' "$(resolvectl llmnr hoge)"
+
+# Cleanup
+rm -f /run/systemd/resolved.conf.d/mdns-llmnr.conf
+ip link del hoge
+ip link del hoge.foo
+
+### SETUP ###
+# Configure network
+hostnamectl hostname ns1.unsigned.test
+cat >>/etc/hosts <<EOF
+10.0.0.1               ns1.unsigned.test
+fd00:dead:beef:cafe::1 ns1.unsigned.test
+
+127.128.0.5     localhost5 localhost5.localdomain localhost5.localdomain4 localhost.localdomain5 localhost5.localdomain5
+EOF
+
+mkdir -p /run/systemd/network
+cat >/run/systemd/network/10-dns0.netdev <<EOF
+[NetDev]
+Name=dns0
+Kind=dummy
+EOF
+cat >/run/systemd/network/10-dns0.network <<EOF
+[Match]
+Name=dns0
+
+[Network]
+IPv6AcceptRA=no
+Address=10.0.0.1/24
+Address=fd00:dead:beef:cafe::1/64
+DNSSEC=allow-downgrade
+DNS=10.0.0.1
+DNS=fd00:dead:beef:cafe::1
+EOF
+cat >/run/systemd/network/10-dns1.netdev <<EOF
+[NetDev]
+Name=dns1
+Kind=dummy
+EOF
+cat >/run/systemd/network/10-dns1.network <<EOF
+[Match]
+Name=dns1
+
+[Network]
+IPv6AcceptRA=no
+Address=10.99.0.1/24
+DNSSEC=no
+EOF
+systemctl edit --stdin --full --runtime --force "resolved-dummy-server.service" <<EOF
+[Service]
+Type=notify
+Environment=SYSTEMD_LOG_LEVEL=debug
+ExecStart=/usr/lib/systemd/tests/unit-tests/manual/test-resolved-dummy-server 10.99.0.1:53
+EOF
+
+DNS_ADDRESSES=(
+    "10.0.0.1"
+    "fd00:dead:beef:cafe::1"
+)
+
+mkdir -p /run/systemd/resolved.conf.d
+{
+    echo "[Resolve]"
+    echo "FallbackDNS="
+    echo "DNSSEC=allow-downgrade"
+    echo "DNSOverTLS=opportunistic"
+} >/run/systemd/resolved.conf.d/test.conf
+ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
+# Override the default NTA list, which turns off DNSSEC validation for (among
+# others) the test. domain
+mkdir -p "/etc/dnssec-trust-anchors.d/"
+echo local >/etc/dnssec-trust-anchors.d/local.negative
+
+# Copy over our knot configuration
+mkdir -p /var/lib/knot/zones/ /etc/knot/
+cp -rfv /usr/lib/systemd/tests/testdata/knot-data/zones/* /var/lib/knot/zones/
+cp -fv /usr/lib/systemd/tests/testdata/knot-data/knot.conf /etc/knot/knot.conf
+chgrp -R knot /etc/knot/ /var/lib/knot/
+chmod -R ug+rwX /var/lib/knot/
+chmod -R g+r /etc/knot/
+
+# Sign the root zone
+keymgr . generate algorithm=ECDSAP256SHA256 ksk=yes zsk=yes
+# Create a trust anchor for resolved with our root zone
+keymgr . ds | sed 's/ DS/ IN DS/g' >/etc/dnssec-trust-anchors.d/root.positive
+# Create a bind-compatible trust anchor (for delv)
+# Note: the trust-anchors directive is relatively new, so use the original
+#       managed-keys one until it's widespread enough
+{
+    echo 'managed-keys {'
+    keymgr . dnskey | sed -r 's/^\. DNSKEY ([0-9]+ [0-9]+ [0-9]+) (.+)$/. static-key \1 "\2";/g'
+    echo '};'
+} >/etc/bind.keys
+# Create an /etc/bind/bind.keys symlink, which is used by delv on Ubuntu
+mkdir -p /etc/bind
+ln -svf /etc/bind.keys /etc/bind/bind.keys
+
+# Start the services
+systemctl unmask systemd-networkd
+systemctl restart systemd-networkd
+/usr/lib/systemd/systemd-networkd-wait-online --interface=dns1:routable --timeout=60
+systemctl reload systemd-resolved
+systemctl start resolved-dummy-server
+
+# Create knot's runtime dir, since from certain version it's provided only by
+# the package and not created by tmpfiles/systemd
+if [[ ! -d /run/knot ]]; then
+    mkdir -p /run/knot
+    chown -R knot:knot /run/knot
+fi
+systemctl start knot
+# Wait a bit for the keys to propagate
+sleep 4
+
+systemctl status resolved-dummy-server
+networkctl status
+resolvectl status
+resolvectl log-level debug
+
+# Start monitoring queries
+systemd-run -u resolvectl-monitor.service -p Type=notify resolvectl monitor
+systemd-run -u resolvectl-monitor-json.service -p Type=notify resolvectl monitor --json=short
+
+# FIXME: knot, unfortunately, incorrectly complains about missing zone files for zones
+#        that are forwarded using the `dnsproxy` module. Until the issue is resolved,
+#        let's fall back to pre-processing the `zone-check` output a bit before checking it
+#
+# See: https://gitlab.nic.cz/knot/knot-dns/-/issues/913
+run knotc zone-check || :
+sed -i '/forwarded.test./d' "$RUN_OUT"
+[[ ! -s "$RUN_OUT" ]]
+# We need to manually propagate the DS records of onlinesign.test. to the parent
+# zone, since they're generated online
+knotc zone-begin test.
+if knotc zone-get test. onlinesign.test. ds | grep .; then
+    # Drop any old DS records, if present (e.g. on test re-run)
+    knotc zone-unset test. onlinesign.test. ds
+fi
+# Propagate the new DS records
+while read -ra line; do
+    knotc zone-set test. "${line[0]}" 600 "${line[@]:1}"
+done < <(keymgr onlinesign.test. ds)
+knotc zone-commit test.
+
+knotc reload
+sleep 2
+
+### SETUP END ###
+
+: "--- nss-resolve/nss-myhostname tests"
+# Sanity check
+TIMESTAMP=$(date '+%F %T')
+# Issue: https://github.com/systemd/systemd/issues/23951
+# With IPv6 enabled
+run getent -s resolve ahosts ns1.unsigned.test
+grep -qE "^fd00:dead:beef:cafe::1\s+STREAM\s+ns1\.unsigned\.test" "$RUN_OUT"
+monitor_check_rr "$TIMESTAMP" "ns1.unsigned.test IN AAAA fd00:dead:beef:cafe::1"
+# With IPv6 disabled
+# Issue: https://github.com/systemd/systemd/issues/23951
+disable_ipv6
+run getent -s resolve ahosts ns1.unsigned.test
+grep -qE "^10\.0\.0\.1\s+STREAM\s+ns1\.unsigned\.test" "$RUN_OUT"
+(! grep -qE "fd00:dead:beef:cafe::1" "$RUN_OUT")
+monitor_check_rr "$TIMESTAMP" "ns1.unsigned.test IN A 10.0.0.1"
+enable_ipv6
+
+# Issue: https://github.com/systemd/systemd/issues/18812
+# PR: https://github.com/systemd/systemd/pull/18896
+# Follow-up issue: https://github.com/systemd/systemd/issues/23152
+# Follow-up PR: https://github.com/systemd/systemd/pull/23161
+# With IPv6 enabled
+run getent -s resolve ahosts localhost
+grep -qE "^::1\s+STREAM\s+localhost" "$RUN_OUT"
+run getent -s myhostname ahosts localhost
+grep -qE "^::1\s+STREAM\s+localhost" "$RUN_OUT"
+# With IPv6 disabled
+disable_ipv6
+run getent -s resolve ahosts localhost
+grep -qE "^127\.0\.0\.1\s+STREAM\s+localhost" "$RUN_OUT"
+(! grep -qE "::1" "$RUN_OUT")
+run getent -s myhostname ahosts localhost
+grep -qE "^127\.0\.0\.1\s+STREAM\s+localhost" "$RUN_OUT"
+enable_ipv6
+
+# Issue: https://github.com/systemd/systemd/issues/25088
+run getent -s resolve hosts 127.128.0.5
+grep -qEx '127\.128\.0\.5\s+localhost5(\s+localhost5?\.localdomain[45]?){4}' "$RUN_OUT"
+[ "$(wc -l <"$RUN_OUT")" -eq 1 ]
+
+# Issue: https://github.com/systemd/systemd/issues/20158
+run dig +noall +answer +additional localhost5.
+grep -qEx 'localhost5\.\s+0\s+IN\s+A\s+127\.128\.0\.5' "$RUN_OUT"
+[ "$(wc -l <"$RUN_OUT")" -eq 1 ]
+run dig +noall +answer +additional localhost5.localdomain4.
+grep -qEx 'localhost5\.localdomain4\.\s+0\s+IN\s+CNAME\s+localhost5\.' "$RUN_OUT"
+grep -qEx 'localhost5\.\s+0\s+IN\s+A\s+127\.128\.0\.5' "$RUN_OUT"
+[ "$(wc -l <"$RUN_OUT")" -eq 2 ]
+
+: "--- Basic resolved tests ---"
+# Issue: https://github.com/systemd/systemd/issues/22229
+# PR: https://github.com/systemd/systemd/pull/22231
+FILTERED_NAMES=(
+    "0.in-addr.arpa"
+    "255.255.255.255.in-addr.arpa"
+    "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa"
+    "hello.invalid"
+    "hello.alt"
+)
+
+for name in "${FILTERED_NAMES[@]}"; do
+    (! run host "$name")
+    grep -qF "NXDOMAIN" "$RUN_OUT"
+done
+
+# Follow-up
+# Issue: https://github.com/systemd/systemd/issues/22401
+# PR: https://github.com/systemd/systemd/pull/22414
+run dig +noall +authority +comments SRV .
+grep -qF "status: NOERROR" "$RUN_OUT"
+grep -qE "IN\s+SOA\s+ns1\.unsigned\.test\." "$RUN_OUT"
+
+run resolvectl query -t SVCB svcb.test
+grep -qF 'alpn="dot"' "$RUN_OUT"
+grep -qF "ipv4hint=10.0.0.1" "$RUN_OUT"
+
+run resolvectl query -t HTTPS https.test
+grep -qF 'alpn="h2,h3"' "$RUN_OUT"
+
+: "--- ZONE: unsigned.test. ---"
+run dig @ns1.unsigned.test +short unsigned.test A unsigned.test AAAA
+grep -qF "10.0.0.101" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::101" "$RUN_OUT"
+run resolvectl query unsigned.test
+grep -qF "10.0.0.10" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::101" "$RUN_OUT"
+grep -qF "authenticated: no" "$RUN_OUT"
+run dig @ns1.unsigned.test +short MX unsigned.test
+grep -qF "15 mail.unsigned.test." "$RUN_OUT"
+run resolvectl query --legend=no -t MX unsigned.test
+grep -qF "unsigned.test IN MX 15 mail.unsigned.test" "$RUN_OUT"
+
+
+: "--- ZONE: signed.test (static DNSSEC) ---"
+# Check the trust chain (with and without systemd-resolved in between
+# Issue: https://github.com/systemd/systemd/issues/22002
+# PR: https://github.com/systemd/systemd/pull/23289
+run_delv @ns1.unsigned.test signed.test
+grep -qF "; fully validated" "$RUN_OUT"
+run_delv signed.test
+grep -qF "; fully validated" "$RUN_OUT"
+
+for addr in "${DNS_ADDRESSES[@]}"; do
+    run_delv "@$addr" -t A mail.signed.test
+    grep -qF "; fully validated" "$RUN_OUT"
+    run_delv "@$addr" -t AAAA mail.signed.test
+    grep -qF "; fully validated" "$RUN_OUT"
+done
+run resolvectl query mail.signed.test
+grep -qF "10.0.0.11" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::11" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+
+run dig +short signed.test
+grep -qF "10.0.0.10" "$RUN_OUT"
+run resolvectl query signed.test
+grep -qF "signed.test: 10.0.0.10" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+run dig @ns1.unsigned.test +short MX signed.test
+grep -qF "10 mail.signed.test." "$RUN_OUT"
+run resolvectl query --legend=no -t MX signed.test
+grep -qF "signed.test IN MX 10 mail.signed.test" "$RUN_OUT"
+# Check a non-existent domain
+run dig +dnssec this.does.not.exist.signed.test
+grep -qF "status: NXDOMAIN" "$RUN_OUT"
+# Check a wildcard record
+run resolvectl query -t TXT this.should.be.authenticated.wild.signed.test
+grep -qF 'this.should.be.authenticated.wild.signed.test IN TXT "this is a wildcard"' "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+# Check SRV support
+run resolvectl service _mysvc._tcp signed.test
+grep -qF "myservice.signed.test:1234" "$RUN_OUT"
+grep -qF "10.0.0.20" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::17" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+
+# Test service resolve over Varlink
+run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"name":"","type":"_mysvc._tcp","domain":"signed.test"}'
+grep -qF '"services":[{"priority":10,"weight":5,"port":1234,"hostname":"myservice.signed.test","canonicalName":"myservice.signed.test","addresses":[{"ifindex":' "$RUN_OUT"
+grep -qF '"family":10,"address":[253,0,222,173,190,239,202,254,0,0,0,0,0,0,0,23]' "$RUN_OUT"
+grep -qF '"family":2,"address":[10,0,0,20]' "$RUN_OUT"
+grep -qF '}]}],"txt":["This is TXT for myservice"],"canonical":{"name":null,"type":"_mysvc._tcp","domain":"signed.test"},"flags":' "$RUN_OUT"
+
+# without name
+run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test"}'
+# without txt (SD_RESOLVE_NO_TXT)
+run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":64}'
+(! grep -qF '"txt"' "$RUN_OUT")
+# without address (SD_RESOLVE_NO_ADDRESS)
+run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":128}'
+(! grep -qF '"addresses"' "$RUN_OUT")
+# without txt and address
+run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":192}'
+(! grep -qF '"txt"' "$RUN_OUT")
+(! grep -qF '"addresses"' "$RUN_OUT")
+
+(! run resolvectl service _invalidsvc._udp signed.test)
+grep -qE "invalidservice\.signed\.test' not found" "$RUN_OUT"
+run resolvectl service _untrustedsvc._udp signed.test
+grep -qF "myservice.untrusted.test:1111" "$RUN_OUT"
+grep -qF "10.0.0.123" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+# Check OPENPGPKEY support
+run_delv -t OPENPGPKEY 5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test
+grep -qF "; fully validated" "$RUN_OUT"
+run resolvectl openpgp mr.smith@signed.test
+grep -qF "5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+# Check zone transfers (AXFR/IXFR)
+# Note: since resolved doesn't support zone transfers, let's just make sure it
+#       simply refuses such requests without choking on them
+# See: https://github.com/systemd/systemd/pull/30809#issuecomment-1880102804
+run dig @ns1.unsigned.test AXFR signed.test
+grep -qE "SOA\s+ns1.unsigned.test. root.unsigned.test." "$RUN_OUT"
+run dig AXFR signed.test
+grep -qF "; Transfer failed" "$RUN_OUT"
+run dig @ns1.unsigned.test IXFR=43 signed.test
+grep -qE "SOA\s+ns1.unsigned.test. root.unsigned.test." "$RUN_OUT"
+run dig IXFR=43 signed.test
+grep -qF "; Transfer failed" "$RUN_OUT"
+
+# DNSSEC validation with multiple records of the same type for the same name
+# Issue: https://github.com/systemd/systemd/issues/22002
+# PR: https://github.com/systemd/systemd/pull/23289
+check_domain() {
+    local domain="${1:?}"
+    local record="${2:?}"
+    local message="${3:?}"
+    local addr
+
+    for addr in "${DNS_ADDRESSES[@]}"; do
+        run_delv "@$addr" -t "$record" "$domain"
+        grep -qF "$message" "$RUN_OUT"
+    done
+
+    run_delv -t "$record" "$domain"
+    grep -qF "$message" "$RUN_OUT"
+
+    run resolvectl query "$domain"
+    grep -qF "authenticated: yes" "$RUN_OUT"
+}
+
+check_domain "dupe.signed.test"       "A"    "; fully validated"
+check_domain "dupe.signed.test"       "AAAA" "; negative response, fully validated"
+check_domain "dupe-ipv6.signed.test"  "AAAA" "; fully validated"
+check_domain "dupe-ipv6.signed.test"  "A"    "; negative response, fully validated"
+check_domain "dupe-mixed.signed.test" "A"    "; fully validated"
+check_domain "dupe-mixed.signed.test" "AAAA" "; fully validated"
+
+# Test resolution of CNAME chains
+TIMESTAMP=$(date '+%F %T')
+run resolvectl query -t A cname-chain.signed.test
+grep -qF "follow14.final.signed.test IN A 10.0.0.14" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+
+monitor_check_rr "$TIMESTAMP" "follow10.so.close.signed.test IN CNAME follow11.yet.so.far.signed.test"
+monitor_check_rr "$TIMESTAMP" "follow11.yet.so.far.signed.test IN CNAME follow12.getting.hot.signed.test"
+monitor_check_rr "$TIMESTAMP" "follow12.getting.hot.signed.test IN CNAME follow13.almost.final.signed.test"
+monitor_check_rr "$TIMESTAMP" "follow13.almost.final.signed.test IN CNAME follow14.final.signed.test"
+monitor_check_rr "$TIMESTAMP" "follow14.final.signed.test IN A 10.0.0.14"
+
+# Non-existing RR + CNAME chain
+#run dig +dnssec AAAA cname-chain.signed.test
+#grep -qF "status: NOERROR" "$RUN_OUT"
+#grep -qE "^follow14\.final\.signed\.test\..+IN\s+NSEC\s+" "$RUN_OUT"
+
+
+: "--- ZONE: onlinesign.test (dynamic DNSSEC) ---"
+# Check the trust chain (with and without systemd-resolved in between
+# Issue: https://github.com/systemd/systemd/issues/22002
+# PR: https://github.com/systemd/systemd/pull/23289
+run_delv @ns1.unsigned.test sub.onlinesign.test
+grep -qF "; fully validated" "$RUN_OUT"
+run_delv sub.onlinesign.test
+grep -qF "; fully validated" "$RUN_OUT"
+
+run dig +short sub.onlinesign.test
+grep -qF "10.0.0.133" "$RUN_OUT"
+run resolvectl query sub.onlinesign.test
+grep -qF "sub.onlinesign.test: 10.0.0.133" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+run dig @ns1.unsigned.test +short TXT onlinesign.test
+grep -qF '"hello from onlinesign"' "$RUN_OUT"
+run resolvectl query --legend=no -t TXT onlinesign.test
+grep -qF 'onlinesign.test IN TXT "hello from onlinesign"' "$RUN_OUT"
+
+for addr in "${DNS_ADDRESSES[@]}"; do
+    run_delv "@$addr" -t A dual.onlinesign.test
+    grep -qF "10.0.0.135" "$RUN_OUT"
+    run_delv "@$addr" -t AAAA dual.onlinesign.test
+    grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT"
+    run_delv "@$addr" -t ANY ipv6.onlinesign.test
+    grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT"
+done
+run resolvectl query dual.onlinesign.test
+grep -qF "10.0.0.135" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+run resolvectl query ipv6.onlinesign.test
+grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+
+# Check a non-existent domain
+# Note: mod-onlinesign utilizes Minimally Covering NSEC Records, hence the
+#       different response than with "standard" DNSSEC
+run dig +dnssec this.does.not.exist.onlinesign.test
+grep -qF "status: NOERROR" "$RUN_OUT"
+grep -qF "NSEC \\000.this.does.not.exist.onlinesign.test." "$RUN_OUT"
+# Check a wildcard record
+run resolvectl query -t TXT this.should.be.authenticated.wild.onlinesign.test
+grep -qF 'this.should.be.authenticated.wild.onlinesign.test IN TXT "this is an onlinesign wildcard"' "$RUN_OUT"
+grep -qF "authenticated: yes" "$RUN_OUT"
+
+# Resolve via dbus method
+TIMESTAMP=$(date '+%F %T')
+run busctl call org.freedesktop.resolve1 /org/freedesktop/resolve1 org.freedesktop.resolve1.Manager ResolveHostname 'isit' 0 secondsub.onlinesign.test 0 0
+grep -qF '10 0 0 134 "secondsub.onlinesign.test"' "$RUN_OUT"
+monitor_check_rr "$TIMESTAMP" "secondsub.onlinesign.test IN A 10.0.0.134"
+
+
+: "--- ZONE: untrusted.test (DNSSEC without propagated DS records) ---"
+# Issue: https://github.com/systemd/systemd/issues/23955
+# FIXME
+resolvectl flush-caches
+#run dig +short untrusted.test A untrusted.test AAAA
+#grep -qF "10.0.0.121" "$RUN_OUT"
+#grep -qF "fd00:dead:beef:cafe::121" "$RUN_OUT"
+run resolvectl query untrusted.test
+grep -qF "untrusted.test:" "$RUN_OUT"
+grep -qF "10.0.0.121" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::121" "$RUN_OUT"
+grep -qF "authenticated: no" "$RUN_OUT"
+run resolvectl service _mysvc._tcp untrusted.test
+grep -qF "myservice.untrusted.test:1234" "$RUN_OUT"
+grep -qF "10.0.0.123" "$RUN_OUT"
+grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT"
+
+# Issue: https://github.com/systemd/systemd/issues/19472
+# 1) Query for a non-existing RR should return NOERROR + NSEC (?), not NXDOMAIN
+# FIXME: re-enable once the issue is resolved
+#run dig +dnssec AAAA untrusted.test
+#grep -qF "status: NOERROR" "$RUN_OUT"
+#grep -qE "^untrusted\.test\..+IN\s+NSEC\s+" "$RUN_OUT"
+## 2) Query for a non-existing name should return NXDOMAIN, not SERVFAIL
+#run dig +dnssec this.does.not.exist.untrusted.test
+#grep -qF "status: NXDOMAIN" "$RUN_OUT"
+
+: "--- ZONE: forwarded.test (queries forwarded to our dummy test server) ---"
+JOURNAL_CURSOR="$(mktemp)"
+journalctl -n0 -q --cursor-file="$JOURNAL_CURSOR"
+
+# See "test-resolved-dummy-server.c" for the server part
+(! run resolvectl query nope.forwarded.test)
+grep -qF "nope.forwarded.test" "$RUN_OUT"
+grep -qF "not found" "$RUN_OUT"
+
+# SERVFAIL + EDE code 6: DNSSEC Bogus
+(! run resolvectl query edns-bogus-dnssec.forwarded.test)
+grep -qE "^edns-bogus-dnssec.forwarded.test:.+: upstream-failure \(DNSSEC Bogus\)" "$RUN_OUT"
+# Same thing, but over Varlink
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-bogus-dnssec.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSSECValidationFailed" "$RUN_OUT"
+grep -qF '{"result":"upstream-failure","extendedDNSErrorCode":6}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(DNSSEC Bogus\). Lookup failed."
+
+# SERVFAIL + EDE code 16: Censored + extra text
+(! run resolvectl query edns-extra-text.forwarded.test)
+grep -qE "^edns-extra-text.forwarded.test.+: SERVFAIL \(Censored: Nothing to see here!\)" "$RUN_OUT"
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-extra-text.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
+grep -qF '{"rcode":2,"extendedDNSErrorCode":16,"extendedDNSErrorMessage":"Nothing to see here!"}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Censored: Nothing to see here!\)"
+
+# SERVFAIL + EDE code 0: Other + extra text
+(! run resolvectl query edns-code-zero.forwarded.test)
+grep -qE "^edns-code-zero.forwarded.test:.+: SERVFAIL \(Other: 🐱\)" "$RUN_OUT"
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-code-zero.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
+grep -qF '{"rcode":2,"extendedDNSErrorCode":0,"extendedDNSErrorMessage":"🐱"}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Other: 🐱\)"
+
+# SERVFAIL + invalid EDE code
+(! run resolvectl query edns-invalid-code.forwarded.test)
+grep -qE "^edns-invalid-code.forwarded.test:.+: SERVFAIL \([0-9]+\)" "$RUN_OUT"
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
+grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+\)"
+
+# SERVFAIL + invalid EDE code + extra text
+(! run resolvectl query edns-invalid-code-with-extra-text.forwarded.test)
+grep -qE '^edns-invalid-code-with-extra-text.forwarded.test:.+: SERVFAIL \([0-9]+: Hello \[#\]\$%~ World\)' "$RUN_OUT"
+(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code-with-extra-text.forwarded.test"}')
+grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
+grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+,"extendedDNSErrorMessage":"Hello \[#\]\$%~ World"}' "$RUN_OUT"
+journalctl --sync
+journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+: Hello \[\#\]\\$%~ World\)"
+
+### Test resolvectl show-cache
+run resolvectl show-cache
+run resolvectl show-cache --json=short
+run resolvectl show-cache --json=pretty
+
+# Issue: https://github.com/systemd/systemd/issues/29580 (part #1)
+dig @127.0.0.54 signed.test
+
+systemctl stop resolvectl-monitor.service
+systemctl stop resolvectl-monitor-json.service
+
+# Issue: https://github.com/systemd/systemd/issues/29580 (part #2)
+#
+# Check for any warnings regarding malformed messages
+(! journalctl -u resolvectl-monitor.service -u reseolvectl-monitor-json.service -p warning --grep malformed)
+# Verify that all queries recorded by `resolvectl monitor --json` produced a valid JSON
+# with expected fields
+journalctl -p info -o cat _SYSTEMD_UNIT="resolvectl-monitor-json.service" | while read -r line; do
+    # Check that both "question" and "answer" fields are arrays
+    #
+    # The expression is slightly more complicated due to the fact that the "answer" field is optional,
+    # so we need to select it only if it's present, otherwise the type == "array" check would fail
+    echo "$line" | jq -e '[. | .question, (select(has("answer")) | .answer) | type == "array"] | all'
+done
+
+# Test serve stale feature and NFTSet= if nftables is installed
+if command -v nft >/dev/null; then
+    ### Test without serve stale feature ###
+    NFT_FILTER_NAME=dns_port_filter
+
+    drop_dns_outbound_traffic() {
+        nft add table inet $NFT_FILTER_NAME
+        nft add chain inet $NFT_FILTER_NAME output \{ type filter hook output priority 0 \; \}
+        nft add rule inet $NFT_FILTER_NAME output ip daddr 10.0.0.1 udp dport 53 drop
+        nft add rule inet $NFT_FILTER_NAME output ip daddr 10.0.0.1 tcp dport 53 drop
+        nft add rule inet $NFT_FILTER_NAME output ip6 daddr fd00:dead:beef:cafe::1 udp dport 53 drop
+        nft add rule inet $NFT_FILTER_NAME output ip6 daddr fd00:dead:beef:cafe::1 tcp dport 53 drop
+    }
+
+    run dig stale1.unsigned.test -t A
+    grep -qE "NOERROR" "$RUN_OUT"
+    sleep 2
+    drop_dns_outbound_traffic
+    set +e
+    # Make sure we give sd-resolved enough time to timeout (5-10s) before giving up
+    # See: https://github.com/systemd/systemd/issues/31639#issuecomment-2009152617
+    run dig +tries=1 +timeout=15 stale1.unsigned.test -t A
+    set -eux
+    grep -qE "no servers could be reached" "$RUN_OUT"
+    nft flush ruleset
+
+    ### Test TIMEOUT with serve stale feature ###
+
+    mkdir -p /run/systemd/resolved.conf.d
+    {
+        echo "[Resolve]"
+        echo "StaleRetentionSec=1d"
+    } >/run/systemd/resolved.conf.d/test.conf
+    ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
+    systemctl reload systemd-resolved.service
+
+    run dig stale1.unsigned.test -t A
+    grep -qE "NOERROR" "$RUN_OUT"
+    sleep 2
+    drop_dns_outbound_traffic
+    # Make sure we give sd-resolved enough time to timeout (5-10s) and serve the stale data (see above)
+    run dig +tries=1 +timeout=15 stale1.unsigned.test -t A
+    grep -qE "NOERROR" "$RUN_OUT"
+    grep -qE "10.0.0.112" "$RUN_OUT"
+
+    nft flush ruleset
+
+    ### Test NXDOMAIN with serve stale feature ###
+    # NXDOMAIN response should replace the cache with NXDOMAIN response
+    run dig stale1.unsigned.test -t A
+    grep -qE "NOERROR" "$RUN_OUT"
+    # Delete stale1 record from zone
+    knotc zone-begin unsigned.test
+    knotc zone-unset unsigned.test stale1 A
+    knotc zone-commit unsigned.test
+    knotc reload
+    sleep 2
+    run dig stale1.unsigned.test -t A
+    grep -qE "NXDOMAIN" "$RUN_OUT"
+
+    nft flush ruleset
+
+    ### NFTSet= test
+    nft add table inet sd_test
+    nft add set inet sd_test c '{ type cgroupsv2; }'
+    nft add set inet sd_test u '{ typeof meta skuid; }'
+    nft add set inet sd_test g '{ typeof meta skgid; }'
+
+    # service
+    systemd-run --unit test-nft.service --service-type=exec -p DynamicUser=yes \
+                -p 'NFTSet=cgroup:inet:sd_test:c user:inet:sd_test:u group:inet:sd_test:g' sleep 10000
+    run nft list set inet sd_test c
+    grep -qF "test-nft.service" "$RUN_OUT"
+    uid=$(getent passwd test-nft | cut -d':' -f3)
+    run nft list set inet sd_test u
+    grep -qF "$uid" "$RUN_OUT"
+    gid=$(getent passwd test-nft | cut -d':' -f4)
+    run nft list set inet sd_test g
+    grep -qF "$gid" "$RUN_OUT"
+    systemctl stop test-nft.service
+
+    # scope
+    run systemd-run --scope -u test-nft.scope -p 'NFTSet=cgroup:inet:sd_test:c' nft list set inet sd_test c
+    grep -qF "test-nft.scope" "$RUN_OUT"
+
+    mkdir -p /run/systemd/system
+    # socket
+    {
+        echo "[Socket]"
+        echo "ListenStream=12345"
+        echo "BindToDevice=lo"
+        echo "NFTSet=cgroup:inet:sd_test:c"
+    } >/run/systemd/system/test-nft.socket
+    {
+        echo "[Service]"
+        echo "ExecStart=/usr/bin/sleep 10000"
+    } >/run/systemd/system/test-nft.service
+    systemctl daemon-reload
+    systemctl start test-nft.socket
+    systemctl status test-nft.socket
+    run nft list set inet sd_test c
+    grep -qF "test-nft.socket" "$RUN_OUT"
+    systemctl stop test-nft.socket
+    rm -f /run/systemd/system/test-nft.{socket,service}
+
+    # slice
+    mkdir /run/systemd/system/system.slice.d
+    {
+        echo "[Slice]"
+        echo "NFTSet=cgroup:inet:sd_test:c"
+    } >/run/systemd/system/system.slice.d/00-test-nft.conf
+    systemctl daemon-reload
+    run nft list set inet sd_test c
+    grep -qF "system.slice" "$RUN_OUT"
+    rm -rf /run/systemd/system/system.slice.d
+
+    nft flush ruleset
+else
+    echo "nftables is not installed. Skipped serve stale feature and NFTSet= tests."
+fi
+
+### Test resolvectl show-server-state ###
+run resolvectl show-server-state
+grep -qF "10.0.0.1" "$RUN_OUT"
+grep -qF "Interface" "$RUN_OUT"
+
+run resolvectl show-server-state --json=short
+grep -qF "10.0.0.1" "$RUN_OUT"
+grep -qF "Interface" "$RUN_OUT"
+
+run resolvectl show-server-state --json=pretty
+grep -qF "10.0.0.1" "$RUN_OUT"
+grep -qF "Interface" "$RUN_OUT"
+
+### Test resolvectl statistics ###
+run resolvectl statistics
+grep -qF "Transactions" "$RUN_OUT"
+grep -qF "Cache" "$RUN_OUT"
+grep -qF "Failure Transactions" "$RUN_OUT"
+grep -qF "DNSSEC Verdicts" "$RUN_OUT"
+
+run resolvectl statistics --json=short
+grep -qF "transactions" "$RUN_OUT"
+grep -qF "cache" "$RUN_OUT"
+grep -qF "dnssec" "$RUN_OUT"
+
+run resolvectl statistics --json=pretty
+grep -qF "transactions" "$RUN_OUT"
+grep -qF "cache" "$RUN_OUT"
+grep -qF "dnssec" "$RUN_OUT"
+
+### Test resolvectl reset-statistics ###
+run resolvectl reset-statistics
+
+run resolvectl reset-statistics --json=pretty
+
+run resolvectl reset-statistics --json=short
+
+test "$(resolvectl --json=short query -t AAAA localhost)" == '{"key":{"class":1,"type":28,"name":"localhost"},"address":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]}'
+test "$(resolvectl --json=short query -t A localhost)" == '{"key":{"class":1,"type":1,"name":"localhost"},"address":[127,0,0,1]}'
+
+# Test ResolveRecord RR resolving via Varlink
+test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":1}' --json=short | jq -rc 'del(.rrs | .[] | .ifindex)')" == '{"rrs":[{"rr":{"key":{"class":1,"type":1,"name":"localhost"},"address":[127,0,0,1]},"raw":"CWxvY2FsaG9zdAAAAQABAAAAAAAEfwAAAQ=="}],"flags":786945}'
+test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":28}' --json=short | jq -rc 'del(.rrs | .[] | .ifindex)')" == '{"rrs":[{"rr":{"key":{"class":1,"type":28,"name":"localhost"},"address":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]},"raw":"CWxvY2FsaG9zdAAAHAABAAAAAAAQAAAAAAAAAAAAAAAAAAAAAQ=="}],"flags":786945}'
+
+# Ensure that reloading keeps the manually configured address
+{
+    echo "[Resolve]"
+    echo "DNS=8.8.8.8"
+} >/run/systemd/resolved.conf.d/reload.conf
+resolvectl dns dns0 1.1.1.1
+systemctl reload systemd-resolved.service
+resolvectl status
+resolvectl dns dns0 | grep -qF "1.1.1.1"
+# For some reason piping this last command to grep fails with:
+# 'resolvectl[1378]: Failed to print table: Broken pipe'
+# so use an intermediate file in /tmp/
+resolvectl >/tmp/output
+grep -qF "DNS Servers: 8.8.8.8" /tmp/output
+
+# Check if resolved exits cleanly.
+restart_resolved
+
+touch /testok
diff --git a/test/units/TEST-76-SYSCTL.service b/test/units/TEST-76-SYSCTL.service
new file mode 100644 (file)
index 0000000..3c8a9e8
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-76-SYSCTL
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-76-SYSCTL.sh b/test/units/TEST-76-SYSCTL.sh
new file mode 100755 (executable)
index 0000000..855d0ef
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+
+echo "foo.bar=42" >/tmp/foo.conf
+assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
+assert_rc 1 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
+
+echo "-foo.foo=42" >/tmp/foo.conf
+assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
+assert_rc 0 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
+
+if ! systemd-detect-virt --quiet --container; then
+    ip link add hoge type dummy
+    udevadm wait /sys/class/net/hoge
+
+    cat >/tmp/foo.conf <<EOF
+net.ipv4.conf.*.drop_gratuitous_arp=1
+net.ipv4.*.*.bootp_relay=1
+net.ipv4.aaa.*.disable_policy=1
+EOF
+
+    echo 0 >/proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp
+    echo 0 >/proc/sys/net/ipv4/conf/hoge/bootp_relay
+    echo 0 >/proc/sys/net/ipv4/conf/hoge/disable_policy
+
+    assert_rc 0 /usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/hoge /tmp/foo.conf
+    assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp)" "1"
+    assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/bootp_relay)" "1"
+    assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/disable_policy)" "0"
+fi
+
+touch /testok
diff --git a/test/units/TEST-78-SIGQUEUE.service b/test/units/TEST-78-SIGQUEUE.service
new file mode 100644 (file)
index 0000000..05f3eff
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-78-SIGQUEUE
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
diff --git a/test/units/TEST-78-SIGQUEUE.sh b/test/units/TEST-78-SIGQUEUE.sh
new file mode 100755 (executable)
index 0000000..46afd3c
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+if ! env --block-signal=SIGUSR1 true 2> /dev/null ; then
+    echo "env tool too old, can't block signals, skipping test." >&2
+    echo OK >/testok
+    exit 0
+fi
+
+systemd-analyze log-level debug
+
+UNIT="test-sigqueue-$RANDOM.service"
+
+systemd-run -u "$UNIT" -p Type=notify -p DynamicUser=1 -- env --block-signal=SIGRTMIN+7 systemd-notify --exec --ready \; sleep infinity
+
+systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT"
+systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT"
+systemctl kill --kill-whom=main --kill-value=7 --signal=SIGRTMIN+7 "$UNIT"
+systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT"
+systemctl kill --kill-whom=main --kill-value=32 --signal=SIGRTMIN+7 "$UNIT"
+systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT"
+
+# We simply check that six signals are queued now. There's no easy way to check
+# from shell which ones those are, hence we don't check that.
+P=$(systemctl show -P MainPID "$UNIT")
+
+test "$(grep SigQ: /proc/"$P"/status | cut -d: -f2 | cut -d/ -f1)" -eq 6
+
+systemctl stop $UNIT
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-79-MEMPRESS.service b/test/units/TEST-79-MEMPRESS.service
new file mode 100644 (file)
index 0000000..f2d24df
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-79-MEMPRESS
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+MemoryAccounting=1
diff --git a/test/units/TEST-79-MEMPRESS.sh b/test/units/TEST-79-MEMPRESS.sh
new file mode 100755 (executable)
index 0000000..4ce73d4
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# We not just test if the file exists, but try to read from it, since if
+# CONFIG_PSI_DEFAULT_DISABLED is set in the kernel the file will exist and can
+# be opened, but any read()s will fail with EOPNOTSUPP, which we want to
+# detect.
+if ! cat /proc/pressure/memory >/dev/null ; then
+    echo "kernel too old, has no PSI." >&2
+    echo OK >/testok
+    exit 0
+fi
+
+systemd-analyze log-level debug
+
+CGROUP=/sys/fs/cgroup/"$(systemctl show TEST-79-MEMPRESS.service -P ControlGroup)"
+test -d "$CGROUP"
+
+if ! test -f "$CGROUP"/memory.pressure ; then
+    echo "No memory accounting/PSI delegated via cgroup, can't test." >&2
+    echo OK >/testok
+    exit 0
+fi
+
+UNIT="test-mempress-$RANDOM.service"
+SCRIPT="/tmp/mempress-$RANDOM.sh"
+
+cat >"$SCRIPT" <<'EOF'
+#!/bin/bash
+
+set -ex
+
+export
+id
+
+test -n "$MEMORY_PRESSURE_WATCH"
+test "$MEMORY_PRESSURE_WATCH" != /dev/null
+test -w "$MEMORY_PRESSURE_WATCH"
+
+ls -al "$MEMORY_PRESSURE_WATCH"
+
+EXPECTED="$(echo -n -e "some 123000 2000000\x00" | base64)"
+
+test "$EXPECTED" = "$MEMORY_PRESSURE_WRITE"
+
+EOF
+
+chmod +x "$SCRIPT"
+
+systemd-run -u "$UNIT" -p Type=exec -p ProtectControlGroups=1 -p DynamicUser=1 -p MemoryPressureWatch=on -p MemoryPressureThresholdSec=123ms -p BindPaths=$SCRIPT --wait "$SCRIPT"
+
+rm "$SCRIPT"
+
+systemd-analyze log-level info
+
+touch /testok
diff --git a/test/units/TEST-80-NOTIFYACCESS.service b/test/units/TEST-80-NOTIFYACCESS.service
new file mode 100644 (file)
index 0000000..4c7f5d5
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-80-NOTIFYACCESS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-80-NOTIFYACCESS.sh b/test/units/TEST-80-NOTIFYACCESS.sh
new file mode 100755 (executable)
index 0000000..97b222a
--- /dev/null
@@ -0,0 +1,126 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+mkfifo /tmp/syncfifo1 /tmp/syncfifo2
+
+sync_in() {
+    read -r x < /tmp/syncfifo1
+    test "$x" = "$1"
+}
+
+sync_out() {
+    echo "$1" > /tmp/syncfifo2
+}
+
+export SYSTEMD_LOG_LEVEL=debug
+
+systemctl --no-block start notify.service
+
+sync_in a
+
+assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all"
+assert_eq "$(systemctl show notify.service -p StatusText --value)" "Test starts"
+
+sync_out b
+sync_in c
+
+assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "main"
+assert_eq "$(systemctl show notify.service -p StatusText --value)" "Sending READY=1 in an unprivileged process"
+assert_rc 3 systemctl --quiet is-active notify.service
+
+sync_out d
+sync_in e
+
+systemctl --quiet is-active notify.service
+assert_eq "$(systemctl show notify.service -p StatusText --value)" "OK"
+assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "none"
+
+systemctl stop notify.service
+assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all"
+
+rm /tmp/syncfifo1 /tmp/syncfifo2
+
+# Now test basic fdstore behaviour
+
+MYSCRIPT="/tmp/myscript$RANDOM.sh"
+cat >> "$MYSCRIPT" <<'EOF'
+#!/usr/bin/env bash
+set -eux
+set -o pipefail
+test "$FDSTORE" -eq 7
+N="/tmp/$RANDOM"
+echo $RANDOM > "$N"
+systemd-notify --fd=4 --fdname=quux --pid=parent 4< "$N"
+rm "$N"
+systemd-notify --ready
+exec sleep infinity
+EOF
+
+chmod +x "$MYSCRIPT"
+
+MYUNIT="myunit$RANDOM.service"
+systemd-run -u "$MYUNIT" -p Type=notify -p FileDescriptorStoreMax=7 "$MYSCRIPT"
+
+test "$(systemd-analyze fdstore "$MYUNIT" | wc -l)" -eq 2
+systemd-analyze fdstore "$MYUNIT" --json=short
+systemd-analyze fdstore "$MYUNIT" --json=short | grep -P -q '\[{"fdname":"quux","type":.*,"devno":\[.*\],"inode":.*,"rdevno":null,"path":"/tmp/.*","flags":"ro"}\]'
+
+systemctl stop "$MYUNIT"
+rm "$MYSCRIPT"
+
+systemd-analyze log-level debug
+
+# Test fdstore pinning (this will pull in fdstore-pin.service fdstore-nopin.service)
+systemctl start fdstore-pin.target
+
+assert_eq "$(systemctl show fdstore-pin.service -P FileDescriptorStorePreserve)" yes
+assert_eq "$(systemctl show fdstore-nopin.service -P FileDescriptorStorePreserve)" restart
+assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
+assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
+assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
+assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1
+
+# The file descriptor store should survive service restarts
+systemctl restart fdstore-pin.service fdstore-nopin.service
+
+assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
+assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1
+assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
+assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
+
+# It should not survive the service stop plus a later start (unless pinned)
+systemctl stop fdstore-pin.service fdstore-nopin.service
+
+assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
+assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
+assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned
+assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
+
+systemctl start fdstore-pin.service fdstore-nopin.service
+
+assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
+assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
+assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
+assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
+
+systemctl stop fdstore-pin.service fdstore-nopin.service
+
+assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
+assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
+assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned
+assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
+
+systemctl clean fdstore-pin.service --what=fdstore
+
+assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 0
+assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
+assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead
+assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
+
+touch /testok
diff --git a/test/units/TEST-81-GENERATORS.debug-generator.sh b/test/units/TEST-81-GENERATORS.debug-generator.sh
new file mode 100755 (executable)
index 0000000..ef1e205
--- /dev/null
@@ -0,0 +1,112 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/generator-utils.sh
+. "$(dirname "$0")/generator-utils.sh"
+
+GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-debug-generator"
+OUT_DIR="$(mktemp -d /tmp/debug-generator.XXX)"
+
+at_exit() {
+    rm -frv "${OUT_DIR:?}"
+}
+
+trap at_exit EXIT
+
+test -x "${GENERATOR_BIN:?}"
+
+# Potential FIXME:
+#   - debug-generator should gracefully handle duplicated mask/wants
+#   - also, handle gracefully empty mask/wants
+ARGS=(
+    "systemd.mask=masked-no-suffix"
+    "systemd.mask=masked.service"
+    "systemd.mask=masked.socket"
+    "systemd.wants=wanted-no-suffix"
+    "systemd.wants=wanted.service"
+    "systemd.wants=wanted.mount"
+    "rd.systemd.mask=masked-initrd.service"
+    "rd.systemd.wants=wanted-initrd.service"
+)
+
+# Regular (non-initrd) scenario
+#
+: "debug-shell: regular"
+CMDLINE="ro root=/ ${ARGS[*]} rd.systemd.debug_shell"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null
+link_eq "$OUT_DIR/early/masked.service" /dev/null
+link_eq "$OUT_DIR/early/masked.socket" /dev/null
+link_endswith "$OUT_DIR/early/default.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service
+link_endswith "$OUT_DIR/early/default.target.wants/wanted.service" /lib/systemd/system/wanted.service
+link_endswith "$OUT_DIR/early/default.target.wants/wanted.mount" /lib/systemd/system/wanted.mount
+# Following stuff should be ignored, as it's prefixed with rd.
+test ! -h "$OUT_DIR/early/masked-initrd.service"
+test ! -h "$OUT_DIR/early/default.target.wants/wants-initrd.service"
+test ! -h "$OUT_DIR/early/default.target.wants/debug-shell.service"
+test ! -d "$OUT_DIR/early/initrd.target.wants"
+
+# Let's re-run the generator with systemd.debug_shell that should be honored
+: "debug-shell: regular + systemd.debug_shell"
+CMDLINE="$CMDLINE systemd.debug_shell"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
+
+# Same thing, but with custom tty
+: "debug-shell: regular + systemd.debug_shell=/dev/tty666"
+CMDLINE="$CMDLINE systemd.debug_shell=/dev/tty666"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
+grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf"
+
+# Same thing, but with custom tty using systemd.default_debug_tty
+: "debug-shell: regular + systemd.default_debug_tty=/dev/tty666 systemd.debug_shell=yes"
+CMDLINE="$CMDLINE systemd.default_debug_tty=/dev/tty666 systemd.debug_shell=yes"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
+grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf"
+
+# Now override the default target via systemd.unit=
+: "debug-shell: regular + systemd.unit="
+CMDLINE="$CMDLINE systemd.unit=my-fancy.target"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null
+link_eq "$OUT_DIR/early/masked.service" /dev/null
+link_eq "$OUT_DIR/early/masked.socket" /dev/null
+link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service
+link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.service" /lib/systemd/system/wanted.service
+link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.mount" /lib/systemd/system/wanted.mount
+link_endswith "$OUT_DIR/early/my-fancy.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
+test ! -d "$OUT_DIR/early/default.target.wants"
+
+
+# Initrd scenario
+: "debug-shell: initrd"
+CMDLINE="ro root=/ ${ARGS[*]} systemd.debug_shell"
+SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null
+link_endswith "$OUT_DIR/early/initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service
+# The non-initrd stuff (i.e. without the rd. suffix) should be ignored in
+# this case
+test ! -h "$OUT_DIR/early/masked-no-suffix.service"
+test ! -h "$OUT_DIR/early/masked.service"
+test ! -h "$OUT_DIR/early/masked.socket"
+test ! -h "$OUT_DIR/early/initrd.target.wants/debug-shell.service"
+test ! -d "$OUT_DIR/early/default.target.wants"
+
+# Again, but with rd.systemd.debug_shell
+: "debug-shell: initrd + rd.systemd.debug_shell"
+CMDLINE="$CMDLINE rd.systemd.debug_shell"
+SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_endswith "$OUT_DIR/early/initrd.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
+
+# Override the default target
+: "debug-shell: initrd + rd.systemd.unit"
+CMDLINE="$CMDLINE rd.systemd.unit=my-fancy-initrd.target"
+SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null
+link_endswith "$OUT_DIR/early/my-fancy-initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service
+test ! -d "$OUT_DIR/early/initrd.target.wants"
diff --git a/test/units/TEST-81-GENERATORS.environment-d-generator.sh b/test/units/TEST-81-GENERATORS.environment-d-generator.sh
new file mode 100755 (executable)
index 0000000..5bc3978
--- /dev/null
@@ -0,0 +1,80 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/generator-utils.sh
+. "$(dirname "$0")/generator-utils.sh"
+
+GENERATOR_BIN="/usr/lib/systemd/user-environment-generators/30-systemd-environment-d-generator"
+CONFIG_FILE="/run/environment.d/99-test.conf"
+OUT_FILE="$(mktemp)"
+
+at_exit() {
+    set +e
+    rm -frv "${CONFIG_FILE:?}" "${OUT_FILE:?}"
+    systemctl -M testuser@.host --user daemon-reload
+}
+
+trap at_exit EXIT
+
+test -x "${GENERATOR_BIN:?}"
+mkdir -p /run/environment.d/
+
+cat >"$CONFIG_FILE" <<EOF
+
+\t\n\t
+3
+=
+    =
+INVALID
+ALSO_INVALID=
+EMPTY_INVALID=""
+3_INVALID=foo
+xxxx xx xxxxxx
+# This is a comment
+$(printf "%.0sx" {0..4096})=
+SIMPLE=foo
+REF=\$SIMPLE
+ALSO_REF=\${SIMPLE}
+DEFAULT="\${NONEXISTENT:-default value}"
+ALTERNATE="\${SIMPLE:+alternate value}"
+LIST=foo,bar,baz
+SIMPLE=redefined
+UNASSIGNED=\$FOO_BAR_BAZ
+VERY_LONG="very $(printf "%.0sx" {0..4096})= long string"
+EOF
+
+# Source env assignments from a file and check them - do this in a subshell
+# to not pollute the test environment
+check_environment() {(
+    # shellcheck source=/dev/null
+    source "${1:?}"
+
+    [[ "$SIMPLE" == "redefined" ]]
+    [[ "$REF" == "foo" ]]
+    [[ "$ALSO_REF" == "foo" ]]
+    [[ "$DEFAULT" == "default value" ]]
+    [[ "$ALTERNATE" == "alternate value" ]]
+    [[ "$LIST" == "foo,bar,baz" ]]
+    [[ "$VERY_LONG" =~ ^very\  ]]
+    [[ "$VERY_LONG" =~ \ long\ string$ ]]
+    [[ -z "$UNASSIGNED" ]]
+    [[ ! -v INVALID ]]
+    [[ ! -v ALSO_INVALID ]]
+    [[ ! -v EMPTY_INVALID ]]
+    [[ ! -v 3_INVALID ]]
+)}
+
+# Check the output by directly calling the generator
+"$GENERATOR_BIN" | tee "$OUT_FILE"
+check_environment "$OUT_FILE"
+: >"$OUT_FILE"
+
+# Check if the generator is correctly called in a user session
+systemctl -M testuser@.host --user daemon-reload
+systemctl -M testuser@.host --user show-environment | tee "$OUT_FILE"
+check_environment "$OUT_FILE"
+
+(! "$GENERATOR_BIN" foo)
diff --git a/test/units/TEST-81-GENERATORS.fstab-generator.sh b/test/units/TEST-81-GENERATORS.fstab-generator.sh
new file mode 100755 (executable)
index 0000000..0c1b27e
--- /dev/null
@@ -0,0 +1,407 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235,SC2233
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/generator-utils.sh
+. "$(dirname "$0")/generator-utils.sh"
+
+GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-fstab-generator"
+NETWORK_FS_RX="^(afs|ceph|cifs|gfs|gfs2|ncp|ncpfs|nfs|nfs4|ocfs2|orangefs|pvfs2|smb3|smbfs|davfs|glusterfs|lustre|sshfs)$"
+OUT_DIR="$(mktemp -d /tmp/fstab-generator.XXX)"
+FSTAB="$(mktemp)"
+
+at_exit() {
+    mountpoint -q /proc/cmdline && umount /proc/cmdline
+    rm -fr "${OUT_DIR:?}" "${FSTAB:?}"
+}
+
+trap at_exit EXIT
+
+test -x "${GENERATOR_BIN:?}"
+
+FSTAB_GENERAL=(
+    # Valid entries
+    "/dev/test2     /nofail                             ext4        nofail 0 0"
+    "/dev/test3     /regular                            btrfs       defaults 0 0"
+    "/dev/test4     /x-systemd.requires                 xfs         x-systemd.requires=foo.service 0 0"
+    "/dev/test5     /x-systemd.before-after             xfs         x-systemd.before=foo.service,x-systemd.after=bar.mount 0 0"
+    "/dev/test6     /x-systemd.wanted-required-by       xfs         x-systemd.wanted-by=foo.service,x-systemd.required-by=bar.device 0 0"
+    "/dev/test7     /x-systemd.requires-mounts-for      xfs         x-systemd.requires-mounts-for=/foo/bar/baz 0 0"
+    "/dev/test8     /x-systemd.automount-idle-timeout   vfat        x-systemd.automount,x-systemd.idle-timeout=50s 0 0"
+    "/dev/test9     /x-systemd.makefs                   xfs         x-systemd.makefs 0 0"
+    "/dev/test10    /x-systemd.growfs                   xfs         x-systemd.growfs 0 0"
+    "/dev/test11    /_netdev                            ext4        defaults,_netdev 0 0"
+    "/dev/test12    /_rwonly                            ext4        x-systemd.rw-only 0 0"
+    "/dev/test13    /chaos1                             zfs         x-systemd.rw-only,x-systemd.requires=hello.service,x-systemd.after=my.device 0 0"
+    "/dev/test14    /chaos2                             zfs         x.systemd.wanted-by=foo.service,x-systemd.growfs,x-systemd.makefs 0 0"
+    "/dev/test15    /fstype/auto                        auto        defaults 0 0"
+    "/dev/test16    /fsck/me                            ext4        defaults 0 1"
+    "/dev/test17    /also/fsck/me                       ext4        defaults,x-systemd.requires-mounts-for=/var/lib/foo 0 99"
+    "/dev/test18    /swap                               swap        defaults 0 0"
+    "/dev/test19    /swap/makefs                        swap        defaults,x-systemd.makefs 0 0"
+    "/dev/test20    /var                                xfs         defaults,x-systemd.device-timeout=1h 0 0"
+    "/dev/test21    /usr                                ext4        defaults 0 1"
+    "/dev/test22    /initrd/mount                       ext2        defaults,x-systemd.rw-only,x-initrd.mount 0 1"
+    "/dev/test23    /initrd/mount/nofail                ext3        defaults,nofail,x-initrd.mount 0 1"
+    "/dev/test24    /initrd/mount/deps                  ext4        x-initrd.mount,x-systemd.before=early.service,x-systemd.after=late.service 0 1"
+
+    # Incomplete, but valid entries
+    "/dev/incomplete1 /incomplete1"
+    "/dev/incomplete2 /incomplete2                      ext4"
+    "/dev/incomplete3 /incomplete3                      ext4        defaults"
+    "/dev/incomplete4 /incomplete4                      ext4        defaults 0"
+
+    # Remote filesystems
+    "/dev/remote1   /nfs                                nfs         bg 0 0"
+    "/dev/remote2   /nfs4                               nfs4        bg 0 0"
+    "bar.tld:/store /remote/storage                     nfs         ro,x-systemd.wanted-by=store.service 0 0"
+    "user@host.tld:/remote/dir /remote/top-secret       sshfs       rw,x-systemd.before=naughty.service 0 0"
+    "foo.tld:/hello /hello/world                        ceph        defaults 0 0"
+    "//192.168.0.1/storage /cifs-storage                cifs        automount,nofail 0 0"
+)
+
+FSTAB_GENERAL_ROOT=(
+    # rootfs with bunch of options we should ignore and fsck enabled
+    "/dev/test1     /                                   ext4        noauto,nofail,x-systemd.automount,x-systemd.wanted-by=foo,x-systemd.required-by=bar 0 1"
+    "${FSTAB_GENERAL[@]}"
+)
+
+FSTAB_MINIMAL=(
+    "/dev/loop1     /foo/bar                            ext3        defaults 0 0"
+)
+
+FSTAB_DUPLICATE=(
+    "/dev/dup1     /       ext4 defaults 0 1"
+    "/dev/dup2     /       ext4 defaults,x-systemd.requires=foo.mount 0 2"
+)
+
+FSTAB_INVALID=(
+    # Ignored entries
+    "/dev/ignored1  /sys/fs/cgroup/foo                  ext4        defaults    0 0"
+    "/dev/ignored2  /sys/fs/selinux                     ext4        defaults    0 0"
+    "/dev/ignored3  /dev/console                        ext4        defaults    0 0"
+    "/dev/ignored4  /proc/kmsg                          ext4        defaults    0 0"
+    "/dev/ignored5  /proc/sys                           ext4        defaults    0 0"
+    "/dev/ignored6  /proc/sys/kernel/random/boot_id     ext4        defaults    0 0"
+    "/dev/ignored7  /run/host                           ext4        defaults    0 0"
+    "/dev/ignored8  /run/host/foo                       ext4        defaults    0 0"
+    "/dev/ignored9  /autofs                             autofs      defaults    0 0"
+    "/dev/invalid1  not-a-path                          ext4        defaults    0 0"
+    ""
+    "/dev/invalid1"
+    "                  "
+    "\\"
+    "$"
+)
+
+check_fstab_mount_units() {
+    local what where fstype opts passno unit
+    local item opt split_options filtered_options supp service device arg
+    local array_name="${1:?}"
+    local out_dir="${2:?}/normal"
+    # Get a reference to the array from its name
+    local -n fstab_entries="$array_name"
+
+    # Running the checks in a container is pretty much useless, since we don't
+    # generate any mounts, but don't skip the whole test to test the "skip"
+    # paths as well
+    in_container && return 0
+
+    for item in "${fstab_entries[@]}"; do
+        # Don't use a pipe here, as it would make the variables out of scope
+        read -r what where fstype opts _ passno <<< "$item"
+
+        # Skip non-initrd mounts in initrd
+        if in_initrd_host && ! [[ "$opts" =~ x-initrd.mount ]]; then
+            continue
+        fi
+
+        if [[ "$fstype" == swap ]]; then
+            unit="$(systemd-escape --suffix=swap --path "${what:?}")"
+            cat "$out_dir/$unit"
+
+            grep -qE "^What=$what$" "$out_dir/$unit"
+            if [[ "$opts" != defaults ]]; then
+                grep -qE "^Options=$opts$" "$out_dir/$unit"
+            fi
+
+            if [[ "$opts" =~ x-systemd.makefs ]]; then
+                service="$(systemd-escape --template=systemd-mkswap@.service --path "$what")"
+                test -e "$out_dir/$service"
+            fi
+
+            continue
+        fi
+
+        # If we're parsing host's fstab in initrd, prefix all mount targets
+        # with /sysroot
+        in_initrd_host && where="/sysroot${where:?}"
+        unit="$(systemd-escape --suffix=mount --path "${where:?}")"
+        cat "$out_dir/$unit"
+
+        # Check the general stuff
+        grep -qE "^What=$what$" "$out_dir/$unit"
+        grep -qE "^Where=$where$" "$out_dir/$unit"
+        if [[ -n "$fstype" ]] && [[ "$fstype" != auto ]]; then
+            grep -qE "^Type=$fstype$" "$out_dir/$unit"
+        fi
+        if [[ -n "$opts" ]] && [[ "$opts" != defaults ]]; then
+            # Some options are not propagated to the generated unit
+            if [[ "$where" == / || "$where" == /usr ]]; then
+                filtered_options="$(opt_filter "$opts" "(noauto|nofail|x-systemd.(wanted-by=|required-by=|automount|device-timeout=))")"
+            else
+                filtered_options="$(opt_filter "$opts" "^x-systemd.device-timeout=")"
+            fi
+
+            if [[ "${filtered_options[*]}" != defaults ]]; then
+                grep -qE "^Options=.*$filtered_options.*$" "$out_dir/$unit"
+            fi
+        fi
+
+        if ! [[ "$opts" =~ (noauto|x-systemd.(wanted-by=|required-by=|automount)) ]]; then
+            # We don't create the Requires=/Wants= symlinks for noauto/automount mounts
+            # and for mounts that use x-systemd.wanted-by=/required-by=
+            if in_initrd_host; then
+                if [[ "$where" == / ]] || ! [[ "$opts" =~ nofail ]]; then
+                    link_eq "$out_dir/initrd-fs.target.requires/$unit" "../$unit"
+                else
+                    link_eq "$out_dir/initrd-fs.target.wants/$unit" "../$unit"
+                fi
+            elif [[ "$fstype" =~ $NETWORK_FS_RX || "$opts" =~ _netdev ]]; then
+                # Units with network filesystems should have a Requires= dependency
+                # on the remote-fs.target, unless they use nofail or are an nfs "bg"
+                # mounts, in which case the dependency is downgraded to Wants=
+                if [[ "$opts" =~ nofail ]] || [[ "$fstype" =~ ^(nfs|nfs4) && "$opts" =~ bg ]]; then
+                    link_eq "$out_dir/remote-fs.target.wants/$unit" "../$unit"
+                else
+                    link_eq "$out_dir/remote-fs.target.requires/$unit" "../$unit"
+                fi
+            else
+                # Similarly, local filesystems should have a Requires= dependency on
+                # the local-fs.target, unless they use nofail, in which case the
+                # dependency is downgraded to Wants=. Rootfs is a special case,
+                # since we always ignore nofail there
+                if [[ "$where" == / ]] || ! [[ "$opts" =~ nofail ]]; then
+                    link_eq "$out_dir/local-fs.target.requires/$unit" "../$unit"
+                else
+                    link_eq "$out_dir/local-fs.target.wants/$unit" "../$unit"
+                fi
+            fi
+        fi
+
+        if [[ "${passno:=0}" -ne 0 ]]; then
+            # Generate systemd-fsck@.service dependencies, if applicable
+            if in_initrd && [[ "$where" == / || "$where" == /usr ]]; then
+                continue
+            fi
+
+            if [[ "$where" == / ]]; then
+                link_endswith "$out_dir/local-fs.target.wants/systemd-fsck-root.service" "/lib/systemd/system/systemd-fsck-root.service"
+            else
+                service="$(systemd-escape --template=systemd-fsck@.service --path "$what")"
+                grep -qE "^After=$service$" "$out_dir/$unit"
+                if [[ "$where" == /usr ]]; then
+                    grep -qE "^Wants=$service$" "$out_dir/$unit"
+                else
+                    grep -qE "^Requires=$service$" "$out_dir/$unit"
+                fi
+            fi
+        fi
+
+        # Check various x-systemd options
+        #
+        # First, split them into an array to make splitting them even further
+        # easier
+        IFS="," read -ra split_options <<< "$opts"
+        # and process them one by one.
+        #
+        # Note: the "machinery" below might (and probably does) miss some
+        #       combinations of supported options, so tread carefully
+        for opt in "${split_options[@]}"; do
+            if [[ "$opt" =~ ^x-systemd.requires= ]]; then
+                service="$(opt_get_arg "$opt")"
+                grep -qE "^Requires=$service$" "$out_dir/$unit"
+                grep -qE "^After=$service$" "$out_dir/$unit"
+            elif [[ "$opt" =~ ^x-systemd.before= ]]; then
+                service="$(opt_get_arg "$opt")"
+                grep -qE "^Before=$service$" "$out_dir/$unit"
+            elif [[ "$opt" =~ ^x-systemd.after= ]]; then
+                service="$(opt_get_arg "$opt")"
+                grep -qE "^After=$service$" "$out_dir/$unit"
+            elif [[ "$opt" =~ ^x-systemd.wanted-by= ]]; then
+                service="$(opt_get_arg "$opt")"
+                if [[ "$where" == / ]]; then
+                    # This option is ignored for rootfs mounts
+                    (! link_eq "$out_dir/$service.wants/$unit" "../$unit")
+                else
+                    link_eq "$out_dir/$service.wants/$unit" "../$unit"
+                fi
+            elif [[ "$opt" =~ ^x-systemd.required-by= ]]; then
+                service="$(opt_get_arg "$opt")"
+                if [[ "$where" == / ]]; then
+                    # This option is ignored for rootfs mounts
+                    (! link_eq "$out_dir/$service.requires/$unit" "../$unit")
+                else
+                    link_eq "$out_dir/$service.requires/$unit" "../$unit"
+                fi
+            elif [[ "$opt" =~ ^x-systemd.requires-mounts-for= ]]; then
+                arg="$(opt_get_arg "$opt")"
+                grep -qE "^RequiresMountsFor=$arg$" "$out_dir/$unit"
+            elif [[ "$opt" == x-systemd.device-bound ]]; then
+                # This is implied for fstab mounts
+                :
+            elif [[ "$opt" == x-systemd.automount ]]; then
+                # The $unit should have an accompanying automount unit
+                supp="$(systemd-escape --suffix=automount --path "$where")"
+                if [[ "$where" == / ]]; then
+                    # This option is ignored for rootfs mounts
+                    test ! -e "$out_dir/$supp"
+                    (! link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp")
+                else
+                    test -e "$out_dir/$supp"
+                    link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp"
+                fi
+            elif [[ "$opt" =~ ^x-systemd.idle-timeout= ]]; then
+                # The timeout applies to the automount unit, not the original
+                # mount one
+                arg="$(opt_get_arg "$opt")"
+                supp="$(systemd-escape --suffix=automount --path "$where")"
+                grep -qE "^TimeoutIdleSec=$arg$" "$out_dir/$supp"
+            elif [[ "$opt" =~ ^x-systemd.device-timeout= ]]; then
+                arg="$(opt_get_arg "$opt")"
+                device="$(systemd-escape --suffix=device --path "$what")"
+                grep -qE "^JobRunningTimeoutSec=$arg$" "$out_dir/${device}.d/50-device-timeout.conf"
+            elif [[ "$opt" == x-systemd.makefs ]]; then
+                service="$(systemd-escape --template=systemd-makefs@.service --path "$what")"
+                test -e "$out_dir/$service"
+                link_eq "$out_dir/${unit}.requires/$service" "../$service"
+            elif [[ "$opt" == x-systemd.rw-only ]]; then
+                grep -qE "^ReadWriteOnly=yes$" "$out_dir/$unit"
+            elif [[ "$opt" == x-systemd.growfs ]]; then
+                service="$(systemd-escape --template=systemd-growfs@.service --path "$where")"
+                link_endswith "$out_dir/${unit}.wants/$service" "/lib/systemd/system/systemd-growfs@.service"
+            elif [[ "$opt" == bg ]] && [[ "$fstype" =~ ^(nfs|nfs4)$ ]]; then
+                # We "convert" nfs bg mounts to fg, so we can do the job-control
+                # ourselves
+                grep -qE "^Options=.*\bx-systemd.mount-timeout=infinity\b" "$out_dir/$unit"
+                grep -qE "^Options=.*\bfg\b.*" "$out_dir/$unit"
+            elif [[ "$opt" =~ ^x-systemd\. ]]; then
+                echo >&2 "Unhandled mount option: $opt"
+                exit 1
+            fi
+        done
+    done
+}
+
+: "fstab-generator: regular"
+printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB"
+cat "$FSTAB"
+SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+check_fstab_mount_units FSTAB_GENERAL_ROOT "$OUT_DIR"
+
+# Skip the rest when running in a container, as it makes little sense to check
+# initrd-related stuff there and fstab-generator might have a bit strange
+# behavior during certain tests, like https://github.com/systemd/systemd/issues/27156
+if in_container; then
+    echo "Running in a container, skipping the rest of the fstab-generator tests..."
+    exit 0
+fi
+
+# In this mode we treat the entries as "regular" ones
+: "fstab-generator: initrd - initrd fstab"
+printf "%s\n" "${FSTAB_GENERAL[@]}" >"$FSTAB"
+cat "$FSTAB"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_SYSROOT_FSTAB=/dev/null check_fstab_mount_units FSTAB_GENERAL "$OUT_DIR"
+
+# In this mode we prefix the mount target with /sysroot and ignore all mounts
+# that don't have the x-initrd.mount flag
+: "fstab-generator: initrd - host fstab"
+printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB"
+cat "$FSTAB"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_GENERAL_ROOT "$OUT_DIR"
+
+# Check the default stuff that we (almost) always create in initrd
+: "fstab-generator: initrd default"
+SYSTEMD_PROC_CMDLINE="root=/dev/sda2" SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+test -e "$OUT_DIR/normal/sysroot.mount"
+test -e "$OUT_DIR/normal/systemd-fsck-root.service"
+link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount"
+link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount"
+
+: "fstab-generator: run as systemd-sysroot-fstab-check in initrd"
+ln -svf "$GENERATOR_BIN" /tmp/systemd-sysroot-fstab-check
+(! /tmp/systemd-sysroot-fstab-check foo)
+(! SYSTEMD_IN_INITRD=0 /tmp/systemd-sysroot-fstab-check)
+printf "%s\n" "${FSTAB_GENERAL[@]}" >"$FSTAB"
+SYSTEMD_IN_INITRD=1 SYSTEMD_SYSROOT_FSTAB="$FSTAB" /tmp/systemd-sysroot-fstab-check
+
+: "fstab-generator: duplicate"
+printf "%s\n" "${FSTAB_DUPLICATE[@]}" >"$FSTAB"
+cat "$FSTAB"
+(! SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR")
+
+: "fstab-generator: invalid"
+printf "%s\n" "${FSTAB_INVALID[@]}" >"$FSTAB"
+cat "$FSTAB"
+# Don't care about the exit code here
+SYSTEMD_PROC_CMDLINE="" SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR" || :
+# No mounts should get created here
+[[ "$(find "$OUT_DIR" -name "*.mount" | wc -l)" -eq 0 ]]
+
+: "fstab-generator: kernel args - fstab=0"
+printf "%s\n" "${FSTAB_MINIMAL[@]}" >"$FSTAB"
+SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+(! SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR")
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+(! SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR")
+
+: "fstab-generator: kernel args - rd.fstab=0"
+printf "%s\n" "${FSTAB_MINIMAL[@]}" >"$FSTAB"
+SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+(! SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR")
+
+: "fstab-generator: kernel args - systemd.swap=0"
+printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB"
+cat "$FSTAB"
+SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="systemd.swap=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+# No swap units should get created here
+[[ "$(find "$OUT_DIR" -name "*.swap" | wc -l)" -eq 0 ]]
+
+# Possible TODO
+#   - combine the rootfs & usrfs arguments and mix them with fstab entries
+#   - systemd.volatile=
+: "fstab-generator: kernel args - root= + rootfstype= + rootflags="
+# shellcheck disable=SC2034
+EXPECTED_FSTAB=(
+    "/dev/disk/by-label/rootfs  /    ext4    noexec,ro   0 1"
+)
+CMDLINE="root=LABEL=rootfs rootfstype=ext4 rootflags=noexec"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+# The /proc/cmdline here is a dummy value to tell the in_initrd_host() function
+# we're parsing host's fstab, but it's all on the kernel cmdline instead
+SYSTEMD_IN_INITRD=1 SYSTEMD_SYSROOT_FSTAB=/proc/cmdline check_fstab_mount_units EXPECTED_FSTAB "$OUT_DIR"
+
+# This is a very basic sanity test that involves manual checks, since adding it
+# to the check_fstab_mount_units() function would make it way too complex
+# (yet another possible TODO)
+: "fstab-generator: kernel args - mount.usr= + mount.usrfstype= + mount.usrflags="
+CMDLINE="mount.usr=UUID=be780f43-8803-4a76-9732-02ceda6e9808 mount.usrfstype=ext4 mount.usrflags=noexec,nodev"
+SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+cat "$OUT_DIR/normal/sysroot-usr.mount" "$OUT_DIR/normal/sysusr-usr.mount"
+# The general idea here is to mount the device to /sysusr/usr and then
+# bind-mount /sysusr/usr to /sysroot/usr
+grep -qE "^What=/dev/disk/by-uuid/be780f43-8803-4a76-9732-02ceda6e9808$" "$OUT_DIR/normal/sysusr-usr.mount"
+grep -qE "^Where=/sysusr/usr$" "$OUT_DIR/normal/sysusr-usr.mount"
+grep -qE "^Type=ext4$" "$OUT_DIR/normal/sysusr-usr.mount"
+grep -qE "^Options=noexec,nodev,ro$" "$OUT_DIR/normal/sysusr-usr.mount"
+link_eq "$OUT_DIR/normal/initrd-usr-fs.target.requires/sysusr-usr.mount" "../sysusr-usr.mount"
+grep -qE "^What=/sysusr/usr$" "$OUT_DIR/normal/sysroot-usr.mount"
+grep -qE "^Where=/sysroot/usr$" "$OUT_DIR/normal/sysroot-usr.mount"
+grep -qE "^Options=bind$" "$OUT_DIR/normal/sysroot-usr.mount"
+link_eq "$OUT_DIR/normal/initrd-fs.target.requires/sysroot-usr.mount" "../sysroot-usr.mount"
diff --git a/test/units/TEST-81-GENERATORS.getty-generator.sh b/test/units/TEST-81-GENERATORS.getty-generator.sh
new file mode 100755 (executable)
index 0000000..d1dd22c
--- /dev/null
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+set -eux
+set -o pipefail
+# Disable history expansion so we don't have to escape ! in strings below
+set +o histexpand
+
+# shellcheck source=test/units/generator-utils.sh
+. "$(dirname "$0")/generator-utils.sh"
+
+GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-getty-generator"
+OUT_DIR="$(mktemp -d /tmp/getty-generator.XXX)"
+
+at_exit() {
+    rm -frv "${OUT_DIR:?}"
+}
+
+trap at_exit EXIT
+
+test -x "${GENERATOR_BIN:?}"
+
+if in_container; then
+    # Do a limited test in a container, as writing to /dev is usually restrited
+    : "getty-generator: \$container_ttys env (container)"
+    # In a container we allow only /dev/pts/* ptys
+    PID1_ENVIRON="container_ttys=tty0 pts/0 /dev/tty0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+
+    # console-getty.service is always pulled in in containers
+    link_endswith "$OUT_DIR/normal/getty.target.wants/console-getty.service" "/lib/systemd/system/console-getty.service"
+    link_endswith "$OUT_DIR/normal/getty.target.wants/container-getty@0.service" "/lib/systemd/system/container-getty@.service"
+    test ! -e "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service"
+    test ! -h "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service"
+
+    exit 0
+fi
+
+DUMMY_ACTIVE_CONSOLES=(
+    "hvc99"
+    "xvc99"
+    "hvsi99"
+    "sclp_line99"
+    "ttysclp99"
+    "3270!tty99"
+    "dummy99"
+)
+DUMMY_INACTIVE_CONSOLES=(
+    "inactive99"
+    "xvc199"
+)
+DUMMY_CONSOLES=(
+    "${DUMMY_ACTIVE_CONSOLES[@]}"
+    "${DUMMY_INACTIVE_CONSOLES[@]}"
+)
+# Create a bunch of dummy consoles
+for console in "${DUMMY_CONSOLES[@]}"; do
+    mknod "/dev/$console" c 4 0
+done
+# Sneak in one "not-a-tty" console
+touch /dev/notatty99
+# Temporarily replace /sys/class/tty/console/active with our list of dummy
+# consoles so getty-generator can process them
+echo -ne "${DUMMY_ACTIVE_CONSOLES[@]}" /dev/notatty99  >/tmp/dummy-active-consoles
+mount -v --bind /tmp/dummy-active-consoles /sys/class/tty/console/active
+
+: "getty-generator: no arguments"
+# Sneak in an invalid value for $SYSTEMD_GETTY_AUTO to test things out
+PID1_ENVIRON="SYSTEMD_GETTY_AUTO=foo" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+for console in "${DUMMY_ACTIVE_CONSOLES[@]}"; do
+    unit="$(systemd-escape --template serial-getty@.service "$console")"
+    link_endswith "$OUT_DIR/normal/getty.target.wants/$unit" "/lib/systemd/system/serial-getty@.service"
+done
+for console in "${DUMMY_INACTIVE_CONSOLES[@]}" /dev/notatty99; do
+    unit="$(systemd-escape --template serial-getty@.service "$console")"
+    test ! -e "$OUT_DIR/normal/getty.target.wants/$unit"
+    test ! -h "$OUT_DIR/normal/getty.target.wants/$unit"
+done
+
+: "getty-generator: systemd.getty_auto=0 on kernel cmdline"
+SYSTEMD_PROC_CMDLINE="systemd.getty_auto=foo systemd.getty_auto=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
+
+: "getty-generator: SYSTEMD_GETTY_AUTO=0 in PID1's environment"
+PID1_ENVIRON="SYSTEMD_GETTY_AUTO=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
+
+# Cleanup
+umount /sys/class/tty/console/active --lazy
+rm -f "${DUMMY_CONSOLES[@]/#//dev/}" /dev/notatty99
diff --git a/test/units/TEST-81-GENERATORS.run-generator.sh b/test/units/TEST-81-GENERATORS.run-generator.sh
new file mode 100755 (executable)
index 0000000..9bd74ef
--- /dev/null
@@ -0,0 +1,76 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/generator-utils.sh
+. "$(dirname "$0")/generator-utils.sh"
+
+GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-run-generator"
+OUT_DIR="$(mktemp -d /tmp/run-generator.XXX)"
+
+at_exit() {
+    rm -frv "${OUT_DIR:?}"
+}
+
+trap at_exit EXIT
+
+test -x "${GENERATOR_BIN:?}"
+
+check_kernel_cmdline_target() {
+    local out_dir="${1:?}/normal"
+
+    cat "$out_dir/kernel-command-line.target"
+    grep -qE "^Requires=kernel-command-line.service$" "$out_dir/kernel-command-line.target"
+    grep -qE "^After=kernel-command-line.service$" "$out_dir/kernel-command-line.target"
+
+    link_eq "$out_dir/default.target" "kernel-command-line.target"
+}
+
+: "run-generator: empty cmdline"
+SYSTEMD_PROC_CMDLINE="" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
+
+: "run-generator: single command"
+CMDLINE="systemd.run='echo hello world'"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+check_kernel_cmdline_target "$OUT_DIR"
+UNIT="$OUT_DIR/normal/kernel-command-line.service"
+cat "$UNIT"
+systemd-analyze verify --man=no --recursive-errors=no "$UNIT"
+grep -qE "^SuccessAction=exit$" "$UNIT"
+grep -qE "^FailureAction=exit$" "$UNIT"
+grep -qE "^ExecStart=echo hello world$" "$UNIT"
+
+: "run-generator: multiple commands + success/failure actions"
+ARGS=(
+    # These should be ignored
+    "systemd.run"
+    "systemd.run_success_action"
+    "systemd.run_failure_action"
+
+    # Set actions which we will overwrite later
+    "systemd.run_success_action="
+    "systemd.run_failure_action="
+
+    "systemd.run=/bin/false"
+    "systemd.run="
+    "systemd.run=/bin/true"
+    "systemd.run='echo this is a long string'"
+
+    "systemd.run_success_action=reboot"
+    "systemd.run_failure_action=poweroff-force"
+)
+CMDLINE="${ARGS[*]}"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+check_kernel_cmdline_target "$OUT_DIR"
+UNIT="$OUT_DIR/normal/kernel-command-line.service"
+cat "$UNIT"
+systemd-analyze verify --man=no --recursive-errors=no "$UNIT"
+grep -qE "^SuccessAction=reboot$" "$UNIT"
+grep -qE "^FailureAction=poweroff-force$" "$UNIT"
+grep -qE "^ExecStart=/bin/false$" "$UNIT"
+grep -qE "^ExecStart=$" "$UNIT"
+grep -qE "^ExecStart=/bin/true$" "$UNIT"
+grep -qE "^ExecStart=echo this is a long string$" "$UNIT"
diff --git a/test/units/TEST-81-GENERATORS.service b/test/units/TEST-81-GENERATORS.service
new file mode 100644 (file)
index 0000000..3b697b3
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-81-GENERATORS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-81-GENERATORS.sh b/test/units/TEST-81-GENERATORS.sh
new file mode 100755 (executable)
index 0000000..9c2a033
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+run_subtests
+
+touch /testok
diff --git a/test/units/TEST-81-GENERATORS.system-update-generator.sh b/test/units/TEST-81-GENERATORS.system-update-generator.sh
new file mode 100755 (executable)
index 0000000..8ee1fee
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2235
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/generator-utils.sh
+. "$(dirname "$0")/generator-utils.sh"
+
+GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-system-update-generator"
+OUT_DIR="$(mktemp -d /tmp/system-update-generator-generator.XXX)"
+
+at_exit() {
+    rm -frv "${OUT_DIR:?}" /system-update
+}
+
+trap at_exit EXIT
+
+test -x "${GENERATOR_BIN:?}"
+
+rm -f /system-update
+
+: "system-update-generator: no /system-update flag"
+run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
+
+: "system-update-generator: with /system-update flag"
+touch /system-update
+run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target"
+
+: "system-update-generator: kernel cmdline warnings"
+# We should warn if the default target is overridden on the kernel cmdline
+# by a runlevel or systemd.unit=, but still generate the symlink
+SYSTEMD_PROC_CMDLINE="systemd.unit=foo.bar 3" run_and_list "$GENERATOR_BIN" "$OUT_DIR" |& tee /tmp/system-update-generator.log
+link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target"
+grep -qE "Offline system update overridden .* systemd.unit=" /tmp/system-update-generator.log
+grep -qE "Offline system update overridden .* runlevel" /tmp/system-update-generator.log
diff --git a/test/units/TEST-82-SOFTREBOOT.service b/test/units/TEST-82-SOFTREBOOT.service
new file mode 100644 (file)
index 0000000..a8fc4f9
--- /dev/null
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-82-SOFTREBOOT
+DefaultDependencies=no
+After=basic.target
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+FileDescriptorStoreMax=3
+NotifyAccess=all
diff --git a/test/units/TEST-82-SOFTREBOOT.sh b/test/units/TEST-82-SOFTREBOOT.sh
new file mode 100755 (executable)
index 0000000..7a1d8a9
--- /dev/null
@@ -0,0 +1,257 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+at_exit() {
+    # Since the soft-reboot drops the enqueued end.service, we won't shutdown
+    # the test VM if the test fails and have to wait for the watchdog to kill
+    # us (which may take quite a long time). Let's just forcibly kill the machine
+    # instead to save CI resources.
+    if [[ $? -ne 0 ]]; then
+        echo >&2 "Test failed, shutting down the machine..."
+        systemctl poweroff -ff
+    fi
+}
+
+trap at_exit EXIT
+
+systemd-analyze log-level debug
+
+export SYSTEMD_LOG_LEVEL=debug
+
+if [ -f /run/TEST-82-SOFTREBOOT.touch3 ]; then
+    echo "This is the fourth boot!"
+    systemd-notify --status="Fourth Boot"
+
+    test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 3
+
+    rm /run/TEST-82-SOFTREBOOT.touch3
+    mount
+    rmdir /original-root /run/nextroot
+
+    # Check that the fdstore entry still exists
+    test "$LISTEN_FDS" -eq 3
+    read -r x <&5
+    test "$x" = "oinkoink"
+
+    # Check that the surviving services are still around
+    test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active"
+    test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active"
+    test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active"
+    test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active"
+
+    # Check journals
+    journalctl -o short-monotonic --no-hostname --grep '(will soft-reboot|KILL|corrupt)'
+    assert_eq "$(journalctl -q -o short-monotonic -u systemd-journald.service --grep 'corrupt')" ""
+
+    # All succeeded, exit cleanly now
+
+elif [ -f /run/TEST-82-SOFTREBOOT.touch2 ]; then
+    echo "This is the third boot!"
+    systemd-notify --status="Third Boot"
+
+    test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 2
+
+    rm /run/TEST-82-SOFTREBOOT.touch2
+
+    # Check that the fdstore entry still exists
+    test "$LISTEN_FDS" -eq 2
+    read -r x <&4
+    test "$x" = "miaumiau"
+
+    # Upload another entry
+    T="/dev/shm/fdstore.$RANDOM"
+    echo "oinkoink" >"$T"
+    systemd-notify --fd=3 --pid=parent 3<"$T"
+    rm "$T"
+
+    # Check that the surviving services are still around
+    test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active"
+    test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active"
+    test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active"
+    test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active"
+
+    # Test that we really are in the new overlayfs root fs
+    read -r x </lower
+    test "$x" = "miep"
+    cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release
+    grep -q MARKER=1 /etc/os-release
+
+    # Switch back to the original root, away from the overlayfs
+    mount --bind /original-root /run/nextroot
+    mount
+
+    # Restart the unit that is not supposed to survive
+    systemd-run --collect --service-type=exec --unit=TEST-82-SOFTREBOOT-nosurvive.service sleep infinity
+
+    # Now issue the soft reboot. We should be right back soon.
+    touch /run/TEST-82-SOFTREBOOT.touch3
+    systemctl --no-block soft-reboot
+
+    # Now block until the soft-boot killing spree kills us
+    exec sleep infinity
+
+elif [ -f /run/TEST-82-SOFTREBOOT.touch ]; then
+    echo "This is the second boot!"
+    systemd-notify --status="Second Boot"
+
+    test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 1
+
+    # Clean up what we created earlier
+    rm /run/TEST-82-SOFTREBOOT.touch
+
+    # Check that the fdstore entry still exists
+    test "$LISTEN_FDS" -eq 1
+    read -r x <&3
+    test "$x" = "wuffwuff"
+
+    # Check that we got a PrepareForShutdownWithMetadata signal with the right type
+    cat /run/TEST-82-SOFTREBOOT.signal
+    test "$(jq -r '.payload.data[1].type.data' </run/TEST-82-SOFTREBOOT.signal)" = "soft-reboot"
+
+    # Check that the system credentials survived the soft reboot.
+    test "$(systemd-creds cat --system kernelcmdlinecred)" = "uff"
+
+    # Upload another entry
+    T="/dev/shm/fdstore.$RANDOM"
+    echo "miaumiau" >"$T"
+    systemd-notify --fd=3 --pid=parent 3<"$T"
+    rm "$T"
+
+    # Check that the surviving services are still around
+    test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active"
+    test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active"
+    test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active"
+    test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active"
+
+    # This time we test the /run/nextroot/ root switching logic. (We synthesize a new rootfs from the old via overlayfs)
+    mkdir -p /run/nextroot /tmp/nextroot-lower /original-root
+    mount -t tmpfs tmpfs /tmp/nextroot-lower
+    echo miep >/tmp/nextroot-lower/lower
+
+    # Copy os-release away, so that we can manipulate it and check that it is updated in the propagate
+    # directory across soft reboots. Try to cover corner cases by truncating it.
+    mkdir -p /tmp/nextroot-lower/usr/lib
+    grep ID /etc/os-release >/tmp/nextroot-lower/usr/lib/os-release
+    echo MARKER=1 >>/tmp/nextroot-lower/usr/lib/os-release
+    cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release
+    (! grep -q MARKER=1 /etc/os-release)
+
+    mount -t overlay nextroot /run/nextroot -o lowerdir=/tmp/nextroot-lower:/,ro
+
+    # Bind our current root into the target so that we later can return to it
+    mount --bind / /run/nextroot/original-root
+
+    # Restart the unit that is not supposed to survive
+    systemd-run --collect --service-type=exec --unit=TEST-82-SOFTREBOOT-nosurvive.service sleep infinity
+
+    # Now ensure there are no naming clashes and a bunch of transient units all succeed
+    for _ in $(seq 1 25); do
+        systemd-run --wait true
+    done
+
+    # Now issue the soft reboot. We should be right back soon. Given /run/nextroot exists, we should
+    # automatically do a softreboot instead of normal reboot.
+    touch /run/TEST-82-SOFTREBOOT.touch2
+    systemctl --no-block reboot
+
+    # Now block until the soft-boot killing spree kills us
+    exec sleep infinity
+else
+    # This is the first boot
+    systemd-notify --status="First Boot"
+
+    test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 0
+
+    # Let's upload an fd to the fdstore, so that we can verify fdstore passing works correctly
+    T="/dev/shm/fdstore.$RANDOM"
+    echo "wuffwuff" >"$T"
+    systemd-notify --fd=3 --pid=parent 3<"$T"
+    rm "$T"
+
+    survive_sigterm="/dev/shm/survive-sigterm-$RANDOM.sh"
+    cat >"$survive_sigterm" <<EOF
+#!/bin/bash
+trap "" TERM
+systemd-notify --ready
+rm "$survive_sigterm"
+exec sleep infinity
+EOF
+    chmod +x "$survive_sigterm"
+
+    survive_argv="/dev/shm/survive-argv-$RANDOM.sh"
+    cat >"$survive_argv" <<EOF
+#!/bin/bash
+systemd-notify --ready
+rm "$survive_argv"
+exec -a @sleep sleep infinity
+EOF
+    chmod +x "$survive_argv"
+    # This sets DefaultDependencies=no so that they remain running until the very end, and
+    # IgnoreOnIsolate=yes so that they aren't stopped via the "testsuite.target" isolation we do on next boot,
+    # and will be killed by the final sigterm/sigkill spree.
+    systemd-run --collect --service-type=notify -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=TEST-82-SOFTREBOOT-nosurvive-sigterm.service "$survive_sigterm"
+    systemd-run --collect --service-type=exec -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=TEST-82-SOFTREBOOT-nosurvive.service sleep infinity
+
+    # Ensure that the unit doesn't get deactivated by dependencies on the source file. Given it's a verity
+    # image that is already open, even if the tmpfs with the image goes away, the file will be pinned by the
+    # kernel and will keep working.
+    cp /usr/share/minimal_0.* /tmp/
+
+    # Configure these transient units to survive the soft reboot - they will not conflict with shutdown.target
+    # and it will be ignored on the isolate that happens in the next boot. The first will use argv[0][0] =
+    # '@', and the second will use SurviveFinalKillSignal=yes. Both should survive.
+    systemd-run --service-type=notify --unit=TEST-82-SOFTREBOOT-survive-argv.service \
+        --property SurviveFinalKillSignal=no \
+        --property IgnoreOnIsolate=yes \
+        --property DefaultDependencies=no \
+        --property After=basic.target \
+        --property "Conflicts=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
+        --property "Before=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
+         "$survive_argv"
+    systemd-run --service-type=exec --unit=TEST-82-SOFTREBOOT-survive.service \
+        --property TemporaryFileSystem="/run /tmp /var" \
+        --property RootImage=/tmp/minimal_0.raw \
+        --property BindReadOnlyPaths=/dev/log \
+        --property BindReadOnlyPaths=/run/systemd/journal/socket \
+        --property BindReadOnlyPaths=/run/systemd/journal/stdout \
+        --property SurviveFinalKillSignal=yes \
+        --property IgnoreOnIsolate=yes \
+        --property DefaultDependencies=no \
+        --property After=basic.target \
+        --property "Conflicts=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
+        --property "Before=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
+        sleep infinity
+
+    # Check that we can set up an inhibitor, and that busctl monitor sees the
+    # PrepareForShutdownWithMetadata signal and that it says 'soft-reboot'.
+    systemd-run --unit busctl.service --service-type=exec --property StandardOutput=file:/run/TEST-82-SOFTREBOOT.signal \
+        busctl monitor --json=pretty --match 'sender=org.freedesktop.login1,path=/org/freedesktop/login1,interface=org.freedesktop.login1.Manager,member=PrepareForShutdownWithMetadata,type=signal'
+    systemd-run --unit inhibit.service --service-type=exec \
+        systemd-inhibit --what=shutdown --who=test --why=test --mode=delay \
+            sleep infinity
+
+    # Enqueue a bunch of failing units to try and trigger the transient name clash that happens due to D-Bus
+    # being restarted and the "unique" bus IDs not being unique across restarts
+    for _ in $(seq 1 25); do
+        # Use --wait to ensure we connect to the system bus instead of the private bus (otherwise a UUID is
+        # used instead of the bus ID)
+        systemd-run --wait false || true
+    done
+
+    # Now issue the soft reboot. We should be right back soon.
+    touch /run/TEST-82-SOFTREBOOT.touch
+    systemctl --no-block --check-inhibitors=yes soft-reboot
+
+    # Now block until the soft-boot killing spree kills us
+    exec sleep infinity
+fi
+
+systemd-analyze log-level info
+
+touch /testok
+systemctl --no-block exit 123
diff --git a/test/units/TEST-83-BTRFS.service b/test/units/TEST-83-BTRFS.service
new file mode 100644 (file)
index 0000000..55ebb45
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-83-BTRFS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-83-BTRFS.sh b/test/units/TEST-83-BTRFS.sh
new file mode 100755 (executable)
index 0000000..4f10ae7
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+FSTYPE="$(stat --file-system --format "%T" /)"
+
+if [[ "$FSTYPE" != "btrfs" ]]; then
+    echo "Root filesystem is $FSTYPE instead of btrfs, skipping"
+    exit 77
+fi
+
+TEST_BTRFS_OFFSET=/usr/lib/systemd/tests/unit-tests/manual/test-btrfs-physical-offset
+
+SWAPFILE=/var/tmp/swapfile
+
+btrfs filesystem mkswapfile -s 10m "$SWAPFILE"
+sync -f "$SWAPFILE"
+
+offset_btrfs_progs="$(btrfs inspect-internal map-swapfile -r "$SWAPFILE")"
+echo "btrfs-progs: $offset_btrfs_progs"
+
+offset_btrfs_util="$("$TEST_BTRFS_OFFSET" "$SWAPFILE")"
+echo "btrfs-util: $offset_btrfs_util"
+
+(( offset_btrfs_progs == offset_btrfs_util ))
+
+rm -f "$SWAPFILE"
+
+/usr/lib/systemd/tests/unit-tests/manual/test-btrfs
+
+touch /testok
diff --git a/test/units/TEST-84-STORAGETM.service b/test/units/TEST-84-STORAGETM.service
new file mode 100644 (file)
index 0000000..2c25770
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-84-STORAGETM
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/TEST-84-STORAGETM.sh b/test/units/TEST-84-STORAGETM.sh
new file mode 100755 (executable)
index 0000000..eae87d5
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+modprobe -v nvmet-tcp
+modprobe -v nvme-tcp
+
+systemctl start sys-kernel-config.mount
+
+dd if=/dev/urandom of=/var/tmp/storagetm.test bs=1024 count=10240
+
+NVME_UUID="$(cat /proc/sys/kernel/random/uuid)"
+systemd-run -u teststoragetm.service -p Type=notify -p "Environment=SYSTEMD_NVME_UUID=${NVME_UUID:?}" /usr/lib/systemd/systemd-storagetm /var/tmp/storagetm.test --nqn=quux
+NVME_DEVICE="/dev/disk/by-id/nvme-uuid.${NVME_UUID:?}"
+
+nvme connect-all -t tcp -a 127.0.0.1 -s 16858 --hostid="$(cat /proc/sys/kernel/random/uuid)"
+udevadm wait --settle "$NVME_DEVICE"
+
+dd if="$NVME_DEVICE" bs=1024 | cmp /var/tmp/storagetm.test -
+
+nvme disconnect-all
+systemctl stop teststoragetm.service
+rm /var/tmp/storagetm.test
+
+touch /testok
index 0a1611b80aee2e0565cdbadce566a54dbd8c1c55..4cede74d50e3cf2719ff0d6e10967b816d5ac84b 100644 (file)
@@ -107,7 +107,7 @@ run_subtests_with_signals() {
     _show_summary
 }
 
-# Run all subtests (i.e. files named as testsuite-<testid>.<subtest_name>.sh)
+# Run all subtests (i.e. files named as $TESTNAME.<subtest_name>.sh)
 run_subtests() {
     local subtests=("${0%.sh}".*.sh)
     local subtest
diff --git a/test/units/testsuite-01.service b/test/units/testsuite-01.service
deleted file mode 100644 (file)
index 9074e09..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-01-BASIC
-After=multi-user.target
-Wants=systemd-resolved.service systemd-networkd.service
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-01.sh b/test/units/testsuite-01.sh
deleted file mode 100755 (executable)
index bb3ff2f..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Check if the colored --version output behaves correctly
-SYSTEMD_COLORS=256 systemctl --version
-
-# Check if we properly differentiate between a full systemd setup and a "light"
-# version of it that's done during daemon-reexec
-#
-# See: https://github.com/systemd/systemd/issues/27106
-if systemd-detect-virt -q --container; then
-    # We initialize /run/systemd/container only during a full setup
-    test -e /run/systemd/container
-    cp -afv /run/systemd/container /tmp/container
-    rm -fv /run/systemd/container
-    systemctl daemon-reexec
-    test ! -e /run/systemd/container
-    cp -afv /tmp/container /run/systemd/container
-else
-    # We bring the loopback netdev up only during a full setup, so it should
-    # not get brought back up during reexec if we disable it beforehand
-    [[ "$(ip -o link show lo)" =~ LOOPBACK,UP ]]
-    ip link set lo down
-    [[ "$(ip -o link show lo)" =~ state\ DOWN ]]
-    systemctl daemon-reexec
-    [[ "$(ip -o link show lo)" =~ state\ DOWN ]]
-    ip link set lo up
-
-    # We also disable coredumps only during a full setup
-    sysctl -w kernel.core_pattern=dont-overwrite-me
-    systemctl daemon-reexec
-    diff <(echo dont-overwrite-me) <(sysctl --values kernel.core_pattern)
-fi
-
-# Collect failed units & do one daemon-reload to a basic sanity check
-systemctl --state=failed --no-legend --no-pager | tee /failed
-test ! -s /failed
-systemctl daemon-reload
-
-# Check that the early setup is actually skipped on reexec.
-# If the early setup is done more than once, then several timestamps,
-# e.g. SecurityStartTimestamp, are re-initialized, and causes an ABRT
-# of systemd-analyze blame. See issue #27187.
-systemd-analyze blame
-
-# Test for 'systemd-update-utmp runlevel' vs 'systemctl daemon-reexec'.
-# See issue #27163.
-# shellcheck disable=SC2034
-if [[ -x /usr/lib/systemd/systemd-update-utmp ]]; then
-    for _ in {0..10}; do
-        systemctl daemon-reexec &
-        pid_reexec=$!
-        # shellcheck disable=SC2034
-        for _ in {0..10}; do
-            SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-update-utmp runlevel
-        done
-        wait "$pid_reexec"
-    done
-fi
-
-touch /testok
diff --git a/test/units/testsuite-02.service b/test/units/testsuite-02.service
deleted file mode 100644 (file)
index dea2c4f..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-02-UNITTESTS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-02.sh b/test/units/testsuite-02.sh
deleted file mode 100755 (executable)
index 7d44f97..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if ! systemd-detect-virt -qc && [[ "${TEST_CMDLINE_NEWLINE:-}" != bar ]]; then
-    cat /proc/cmdline
-    echo >&2 "Expected TEST_CMDLINE_NEWLINE=bar from the kernel command line"
-    exit 1
-fi
-
-# If we're running with TEST_PREFER_NSPAWN=1 limit the set of tests we run
-# in QEMU to only those that can't run in a container to avoid running
-# the same tests again in a, most likely, very slow environment
-if ! systemd-detect-virt -qc && [[ "${TEST_PREFER_NSPAWN:-0}" -ne 0 ]]; then
-    TESTS_GLOB="test-loop-block"
-else
-    TESTS_GLOB=${TESTS_GLOB:-test-*}
-fi
-
-NPROC=$(nproc)
-MAX_QUEUE_SIZE=${NPROC:-2}
-
-# Reset state
-rm -fv /failed /skipped /testok
-touch /lock
-
-if ! systemd-detect-virt -qc; then
-    # Make sure ping works for unprivileged users (for test-bpf-firewall)
-    sysctl net.ipv4.ping_group_range="0 2147483647"
-fi
-
-# Check & report test results
-# Arguments:
-#   $1: test path
-#   $2: test exit code
-run_test() {
-    if [[ $# -ne 1 ]]; then
-        echo >&2 "run_test: missing arguments"
-        exit 1
-    fi
-
-    local test="$1"
-    local name="${test##*/}"
-
-    echo "Executing test $name as unit $name.service"
-
-    systemd-run --quiet --property Delegate=1 --unit="$name" --wait "$test" && ret=0 || ret=$?
-
-    exec {LOCK_FD}> /lock
-    flock --exclusive ${LOCK_FD}
-
-    if [[ $ret -ne 0 && $ret != 77 && $ret != 127 ]]; then
-        echo "$name failed with $ret"
-        echo "$name" >>/failed-tests
-        {
-            echo "--- $name begin ---"
-            journalctl --unit="$name" --no-hostname -o short-monotonic
-            echo "--- $name end ---"
-        } >>/failed
-    elif [[ $ret == 77 || $ret == 127 ]]; then
-        echo "$name skipped"
-        echo "$name" >>/skipped-tests
-        {
-            echo "--- $name begin ---"
-            journalctl --unit="$name" --no-hostname -o short-monotonic
-            echo "--- $name end ---"
-        } >>/skipped
-    else
-        echo "$name OK"
-        echo "$name" >>/testok
-    fi
-
-    exec {LOCK_FD}<&-
-}
-
-export -f run_test
-
-find /usr/lib/systemd/tests/unit-tests/ -maxdepth 1 -type f -name "${TESTS_GLOB}" -print0 |
-    xargs -0 -I {} --max-procs="$MAX_QUEUE_SIZE" bash -ec "run_test {}"
-
-# Test logs are sometimes lost, as the system shuts down immediately after
-journalctl --sync
-
-test ! -s /failed
-touch /testok
diff --git a/test/units/testsuite-03.service b/test/units/testsuite-03.service
deleted file mode 100644 (file)
index 836f962..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-03-JOBS
-After=multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-03.sh b/test/units/testsuite-03.sh
deleted file mode 100755 (executable)
index 115b941..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Simple test for that daemon-reexec works in container.
-# See: https://github.com/systemd/systemd/pull/23883
-systemctl daemon-reexec
-
-# Test merging of a --job-mode=ignore-dependencies job into a previously
-# installed job.
-
-systemctl start --no-block hello-after-sleep.target
-
-systemctl list-jobs >/root/list-jobs.txt
-until grep 'sleep\.service.*running' /root/list-jobs.txt; do
-    systemctl list-jobs >/root/list-jobs.txt
-done
-
-grep 'hello\.service.*waiting' /root/list-jobs.txt
-
-# This is supposed to finish quickly, not wait for sleep to finish.
-START_SEC=$(date -u '+%s')
-systemctl start --job-mode=ignore-dependencies hello
-END_SEC=$(date -u '+%s')
-ELAPSED=$((END_SEC-START_SEC))
-
-test "$ELAPSED" -lt 3
-
-# sleep should still be running, hello not.
-systemctl list-jobs >/root/list-jobs.txt
-grep 'sleep\.service.*running' /root/list-jobs.txt
-grep 'hello\.service' /root/list-jobs.txt && exit 1
-systemctl stop sleep.service hello-after-sleep.target
-
-# Some basic testing that --show-transaction does something useful
-(! systemctl is-active systemd-importd)
-systemctl -T start systemd-importd
-systemctl is-active systemd-importd
-systemctl --show-transaction stop systemd-importd
-(! systemctl is-active systemd-importd)
-
-# Test for a crash when enqueuing a JOB_NOP when other job already exists
-systemctl start --no-block hello-after-sleep.target
-# hello.service should still be waiting, so these try-restarts will collapse
-# into NOPs.
-systemctl try-restart --job-mode=fail hello.service
-systemctl try-restart hello.service
-systemctl stop hello.service sleep.service hello-after-sleep.target
-
-# TODO: add more job queueing/merging tests here.
-
-# Test that restart propagates to activating units
-systemctl -T --no-block start always-activating.service
-systemctl list-jobs | grep 'always-activating.service'
-ACTIVATING_ID_PRE=$(systemctl show -P InvocationID always-activating.service)
-systemctl -T start always-activating.socket # Wait for the socket to come up
-systemctl -T restart always-activating.socket
-ACTIVATING_ID_POST=$(systemctl show -P InvocationID always-activating.service)
-[ "$ACTIVATING_ID_PRE" != "$ACTIVATING_ID_POST" ] || exit 1
-
-# Test for irreversible jobs
-systemctl start unstoppable.service
-
-# This is expected to fail with 'job cancelled'
-systemctl stop unstoppable.service && exit 1
-# But this should succeed
-systemctl stop --job-mode=replace-irreversibly unstoppable.service
-
-# We're going to shutdown soon. Let's see if it succeeds when
-# there's an active service that tries to be unstoppable.
-# Shutdown of the container/VM will hang if not.
-systemctl start unstoppable.service
-
-# Test waiting for a started units to terminate again
-cat <<EOF >/run/systemd/system/wait2.service
-[Unit]
-Description=Wait for 2 seconds
-[Service]
-ExecStart=sh -ec 'sleep 2'
-EOF
-cat <<EOF >/run/systemd/system/wait5fail.service
-[Unit]
-Description=Wait for 5 seconds and fail
-[Service]
-ExecStart=sh -ec 'sleep 5; false'
-EOF
-
-# wait2 succeeds
-START_SEC=$(date -u '+%s')
-systemctl start --wait wait2.service
-END_SEC=$(date -u '+%s')
-ELAPSED=$((END_SEC-START_SEC))
-[[ "$ELAPSED" -ge 2 ]] && [[ "$ELAPSED" -le 4 ]] || exit 1
-
-# wait5fail fails, so systemctl should fail
-START_SEC=$(date -u '+%s')
-(! systemctl start --wait wait2.service wait5fail.service)
-END_SEC=$(date -u '+%s')
-ELAPSED=$((END_SEC-START_SEC))
-[[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1
-
-# Test time-limited scopes
-START_SEC=$(date -u '+%s')
-set +e
-systemd-run --scope --property=RuntimeMaxSec=3s sleep 10
-RESULT=$?
-END_SEC=$(date -u '+%s')
-ELAPSED=$((END_SEC-START_SEC))
-[[ "$ELAPSED" -ge 3 ]] && [[ "$ELAPSED" -le 5 ]] || exit 1
-[[ "$RESULT" -ne 0 ]] || exit 1
-
-# Test transactions with cycles
-# Provides coverage for issues like https://github.com/systemd/systemd/issues/26872
-for i in {0..19}; do
-    cat >"/run/systemd/system/transaction-cycle$i.service" <<EOF
-[Unit]
-After=transaction-cycle$(((i + 1) % 20)).service
-Requires=transaction-cycle$(((i + 1) % 20)).service
-
-[Service]
-ExecStart=true
-EOF
-done
-systemctl daemon-reload
-for i in {0..19}; do
-    systemctl start "transaction-cycle$i.service"
-done
-
-# Test PropagatesStopTo= when restart (issue #26839)
-systemctl start propagatestopto-and-pullin.target
-systemctl --quiet is-active propagatestopto-and-pullin.target
-
-systemctl restart propagatestopto-and-pullin.target
-systemctl --quiet is-active propagatestopto-and-pullin.target
-systemctl --quiet is-active sleep-infinity-simple.service
-
-systemctl start propagatestopto-only.target
-systemctl --quiet is-active propagatestopto-only.target
-systemctl --quiet is-active sleep-infinity-simple.service
-
-systemctl restart propagatestopto-only.target
-assert_rc 3 systemctl --quiet is-active sleep-infinity-simple.service
-
-systemctl start propagatesstopto-indirect.target propagatestopto-and-pullin.target
-systemctl --quiet is-active propagatestopto-indirect.target
-systemctl --quiet is-active propagatestopto-and-pullin.target
-
-systemctl restart propagatestopto-indirect.target
-assert_rc 3 systemctl --quiet is-active propagatestopto-and-pullin.target
-assert_rc 3 systemctl --quiet is-active sleep-infinity-simple.service
-
-# Test restart mode direct
-systemctl start succeeds-on-restart-restartdirect.target
-assert_rc 0 systemctl --quiet is-active succeeds-on-restart-restartdirect.target
-
-systemctl start fails-on-restart-restartdirect.target || :
-assert_rc 3 systemctl --quiet is-active fails-on-restart-restartdirect.target
-
-systemctl start succeeds-on-restart.target || :
-assert_rc 3 systemctl --quiet is-active succeeds-on-restart.target
-
-systemctl start fails-on-restart.target || :
-assert_rc 3 systemctl --quiet is-active fails-on-restart.target
-
-touch /testok
diff --git a/test/units/testsuite-04.LogFilterPatterns.sh b/test/units/testsuite-04.LogFilterPatterns.sh
deleted file mode 100755 (executable)
index dfa9652..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
- . "$(dirname "$0")"/util.sh
-
-add_logs_filtering_override() {
-    local unit="${1:?}"
-    local override_name="${2:?}"
-    local log_filter="${3:-}"
-
-    mkdir -p "/run/systemd/system/$unit.d/"
-    echo -ne "[Service]\nLogFilterPatterns=$log_filter" >"/run/systemd/system/$unit.d/$override_name.conf"
-    systemctl daemon-reload
-}
-
-run_service_and_fetch_logs() {
-    local unit="${1:?}"
-    local start
-
-    start="$(date '+%Y-%m-%d %T.%6N')"
-    systemctl start "$unit"
-    journalctl --sync
-    journalctl -q -u "$unit" -S "$start" -p notice
-}
-
-if cgroupfs_supports_user_xattrs; then
-    # Accept all log messages
-    add_logs_filtering_override "logs-filtering.service" "00-reset" ""
-    [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
-    add_logs_filtering_override "logs-filtering.service" "01-allow-all" ".*"
-    [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
-    # Discard all log messages
-    add_logs_filtering_override "logs-filtering.service" "02-discard-all" "~.*"
-    [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
-    # Accept all test messages
-    add_logs_filtering_override "logs-filtering.service" "03-reset" ""
-    [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
-    # Discard all test messages
-    add_logs_filtering_override "logs-filtering.service" "04-discard-gg" "~.*gg.*"
-    [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
-    # Deny filter takes precedence
-    add_logs_filtering_override "logs-filtering.service" "05-allow-all-but-too-late" ".*"
-    [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
-    # Use tilde in a deny pattern
-    add_logs_filtering_override "logs-filtering.service" "06-reset" ""
-    add_logs_filtering_override "logs-filtering.service" "07-prevent-tilde" "~~more~"
-    [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
-    # Only allow a pattern that won't be matched
-    add_logs_filtering_override "logs-filtering.service" "08-reset" ""
-    add_logs_filtering_override "logs-filtering.service" "09-allow-only-non-existing" "non-existing string"
-    [[ -z $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
-    # Allow a pattern starting with a tilde
-    add_logs_filtering_override "logs-filtering.service" "10-allow-with-escape-char" "\\\\x7emore~"
-    [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
-    add_logs_filtering_override "logs-filtering.service" "11-reset" ""
-    add_logs_filtering_override "logs-filtering.service" "12-allow-with-spaces" "foo bar"
-    [[ -n $(run_service_and_fetch_logs "logs-filtering.service") ]]
-
-    add_logs_filtering_override "delegated-cgroup-filtering.service" "00-allow-all" ".*"
-    [[ -n $(run_service_and_fetch_logs "delegated-cgroup-filtering.service") ]]
-
-    add_logs_filtering_override "delegated-cgroup-filtering.service" "01-discard-hello" "~hello"
-    [[ -z $(run_service_and_fetch_logs "delegated-cgroup-filtering.service") ]]
-
-    rm -rf /run/systemd/system/{logs-filtering,delegated-cgroup-filtering}.service.d
-fi
diff --git a/test/units/testsuite-04.SYSTEMD_JOURNAL_COMPRESS.sh b/test/units/testsuite-04.SYSTEMD_JOURNAL_COMPRESS.sh
deleted file mode 100755 (executable)
index 96d096d..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# https://bugzilla.redhat.com/show_bug.cgi?id=2183546
-mkdir /run/systemd/system/systemd-journald.service.d
-MACHINE_ID="$(</etc/machine-id)"
-
-# Reset the start-limit counters, as we're going to restart journald a couple of times
-systemctl reset-failed systemd-journald.service
-
-for c in NONE XZ LZ4 ZSTD; do
-    cat >/run/systemd/system/systemd-journald.service.d/compress.conf <<EOF
-[Service]
-Environment=SYSTEMD_JOURNAL_COMPRESS=${c}
-EOF
-    systemctl daemon-reload
-    systemctl restart systemd-journald.service
-    journalctl --rotate
-
-    ID="$(systemd-id128 new)"
-    systemd-cat -t "$ID" /bin/bash -c "for ((i=0;i<100;i++)); do echo -n hoge with ${c}; done; echo"
-    journalctl --sync
-    timeout 10 bash -c "until SYSTEMD_LOG_LEVEL=debug journalctl --verify --quiet --file /var/log/journal/$MACHINE_ID/system.journal 2>&1 | grep -q -F 'compress=${c}'; do sleep .5; done"
-
-    # $SYSTEMD_JOURNAL_COMPRESS= also works for journal-remote
-    if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then
-        for cc in NONE XZ LZ4 ZSTD; do
-            rm -f /tmp/foo.journal
-            SYSTEMD_JOURNAL_COMPRESS="${cc}" /usr/lib/systemd/systemd-journal-remote --split-mode=none -o /tmp/foo.journal --getter="journalctl -b -o export -t $ID"
-            SYSTEMD_LOG_LEVEL=debug journalctl --verify --quiet --file /tmp/foo.journal 2>&1 | grep -q -F "compress=${cc}"
-            journalctl -t "$ID" -o cat --file /tmp/foo.journal | grep -q -F "hoge with ${c}"
-        done
-    fi
-done
-
-rm /run/systemd/system/systemd-journald.service.d/compress.conf
-systemctl daemon-reload
-systemctl restart systemd-journald.service
-systemctl reset-failed systemd-journald.service
-journalctl --rotate
diff --git a/test/units/testsuite-04.bsod.sh b/test/units/testsuite-04.bsod.sh
deleted file mode 100755 (executable)
index 83feb89..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if systemd-detect-virt -cq; then
-    echo "This test requires a VM, skipping the test" | tee --append /skipped
-    exit 0
-fi
-
-if [[ ! -x /usr/lib/systemd/systemd-bsod ]]; then
-    echo "systemd-bsod is not installed, skipping the test" | tee --append /skipped
-    exit 0
-fi
-
-# shellcheck disable=SC2317
-at_exit() {
-    local EC=$?
-
-    if [[ $EC -ne 0 ]] && [[ -e /tmp/console.dump ]]; then
-        cat /tmp/console.dump
-    fi
-
-    if mountpoint -q /var/log/journal; then
-        # In order to preserve the journal from the just run test we need to do a little dance, as
-        # --relinquish-var is not a "true" opposite of --flush, meaning that it won't move the existing
-        # journal(s) from /var/log/ to /run/log/. To do that, let's rotate the journal first, so all
-        # important bits are in the archived journal(s)...
-        journalctl --rotate
-        # ...then instruct sd-journald to write further entries to the runtime journal...
-        journalctl --relinquish-var
-        # ...make sure there are no outstanding writes to the persistent journal that might block us from
-        # unmounting the tmpfs...
-        journalctl --sync
-        # ...move the archived journals to the runtime storage...
-        mv -v "/var/log/journal/$(</etc/machine-id)"/system@*.journal "/run/log/journal/$(</etc/machine-id)/"
-        # ...get rid of the tmpfs on /var/log/journal/...
-        umount /var/log/journal
-        # ...and finally flush everything to the "real" persistent journal, so we can collect it after the
-        # test finishes.
-        journalctl --flush
-    fi
-
-    return 0
-}
-
-vcs_dump_and_check() {
-    local expected_message="${1:?}"
-
-    # It might take a while before the systemd-bsod stuff appears on the VCS,
-    # so try it a couple of times
-    for _ in {0..9}; do
-        setterm --term linux --dump --file /tmp/console.dump
-        if grep -aq "Press any key to exit" /tmp/console.dump &&
-           grep -aq "$expected_message" /tmp/console.dump &&
-           grep -aq "The current boot has failed" /tmp/console.dump; then
-
-            return 0
-        fi
-
-        sleep .5
-    done
-
-    return 1
-}
-
-# Since systemd-bsod always fetches only the first emergency message from the
-# current boot, let's temporarily overmount /var/log/journal with a tmpfs,
-# as we're going to wipe it multiple times, but we need to keep the original
-# journal intact for the other tests to work correctly.
-trap at_exit EXIT
-mount -t tmpfs tmpfs /var/log/journal
-systemctl restart systemd-journald
-
-systemctl stop systemd-bsod
-
-# Since we just wiped the journal, there should be no emergency messages and
-# systemd-bsod should be just a no-op
-timeout 10s /usr/lib/systemd/systemd-bsod
-setterm --term linux --dump --file /tmp/console.dump
-(! grep "The current boot has failed" /tmp/console.dump)
-
-# systemd-bsod should pick up emergency messages only with UID=0, so let's check
-# that as well
-systemd-run --user --machine testuser@ --wait --pipe systemd-cat -p emerg echo "User emergency message"
-systemd-cat -p emerg echo "Root emergency message"
-journalctl --sync
-# Set $SYSTEMD_COLORS so systemd-bsod also prints out the QR code
-SYSTEMD_COLORS=256 /usr/lib/systemd/systemd-bsod &
-PID=$!
-vcs_dump_and_check "Root emergency message"
-grep -aq "Scan the QR code" /tmp/console.dump
-# TODO: check if systemd-bsod exits on a key press (didn't figure this one out yet)
-kill $PID
-timeout 10 bash -c "while kill -0 $PID; do sleep .5; done"
-
-# Wipe the journal
-journalctl --vacuum-size=1 --rotate
-(! journalctl -q -b -p emerg --grep .)
-
-# Check the systemd-bsod.service as well
-# Note: the systemd-bsod.service unit has ConditionVirtualization=no, so let's
-# temporarily override it just for the test
-mkdir /run/systemd/system/systemd-bsod.service.d
-printf '[Unit]\nConditionVirtualization=\n' >/run/systemd/system/systemd-bsod.service.d/99-override.conf
-systemctl daemon-reload
-systemctl start systemd-bsod
-systemd-cat -p emerg echo "Service emergency message"
-vcs_dump_and_check "Service emergency message"
-systemctl status systemd-bsod
-systemctl stop systemd-bsod
-
-# Wipe the journal
-journalctl --vacuum-size=1 --rotate
-(! journalctl -q -b -p emerg --grep .)
-
-# Same as above, but make sure the service responds to signals even when there are
-# no "emerg" messages, see systemd/systemd#30084
-(! systemctl is-active systemd-bsod)
-systemctl start systemd-bsod
-timeout 5s bash -xec 'until systemctl is-active systemd-bsod; do sleep .5; done'
-timeout 5s systemctl stop systemd-bsod
-timeout 5s bash -xec 'while systemctl is-active systemd-bsod; do sleep .5; done'
diff --git a/test/units/testsuite-04.cat.sh b/test/units/testsuite-04.cat.sh
deleted file mode 100755 (executable)
index bef3e18..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemctl enable --now systemd-journald@cat-test.socket
-
-systemd-cat --namespace cat-test env CAT_TEST_RESULT=1
-
-timeout 30 bash -c "until systemctl -q is-active systemd-journald@cat-test.service; do sleep .5; done"
-
-journalctl --namespace cat-test --grep "JOURNAL_STREAM="
-journalctl --namespace cat-test --grep "CAT_TEST_RESULT=1"
-
-systemctl disable --now systemd-journald@cat-test.socket
diff --git a/test/units/testsuite-04.corrupted-journals.sh b/test/units/testsuite-04.corrupted-journals.sh
deleted file mode 100755 (executable)
index 479011e..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-JOURNAL_DIR="$(mktemp -d)"
-REMOTE_OUT="$(mktemp -d)"
-# tar on C8S doesn't support the --zstd option
-unzstd --stdout "/usr/lib/systemd/tests/testdata/test-journals/afl-corrupted-journals.tar.zst" | tar -xC "$JOURNAL_DIR/"
-while read -r file; do
-    filename="${file##*/}"
-    unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}"
-done < <(find /usr/lib/systemd/tests/testdata/test-journals/corrupted/ -name "*.zst")
-# First, try each of them sequentially. Skip this part when running with plain
-# QEMU, as it is excruciatingly slow
-# Note: we care only about exit code 124 (timeout) and special bash exit codes
-# >124 (like signals)
-if [[ "$(systemd-detect-virt -v)" != "qemu" ]]; then
-    while read -r file; do
-        timeout 10 journalctl --file="$file" --boot >/dev/null || [[ $? -lt 124 ]]
-        timeout 10 journalctl --file="$file" --verify >/dev/null || [[ $? -lt 124 ]]
-        timeout 10 journalctl --file="$file" --output=export >/dev/null || [[ $? -lt 124 ]]
-        timeout 10 journalctl --file="$file" --fields >/dev/null || [[ $? -lt 124 ]]
-        timeout 10 journalctl --file="$file" --list-boots >/dev/null || [[ $? -lt 124 ]]
-        if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then
-            timeout 10 /usr/lib/systemd/systemd-journal-remote \
-                            --getter="journalctl --file=$file --output=export" \
-                            --split-mode=none \
-                            --output="$REMOTE_OUT/system.journal" || [[ $? -lt 124 ]]
-            timeout 10 journalctl --directory="$REMOTE_OUT" >/dev/null || [[ $? -lt 124 ]]
-            rm -f "$REMOTE_OUT"/*
-        fi
-    done < <(find "$JOURNAL_DIR" -type f)
-fi
-# And now all at once
-timeout 30 journalctl --directory="$JOURNAL_DIR" --boot >/dev/null || [[ $? -lt 124 ]]
-timeout 30 journalctl --directory="$JOURNAL_DIR" --verify >/dev/null || [[ $? -lt 124 ]]
-timeout 30 journalctl --directory="$JOURNAL_DIR" --output=export >/dev/null || [[ $? -lt 124 ]]
-timeout 30 journalctl --directory="$JOURNAL_DIR" --fields >/dev/null || [[ $? -lt 124 ]]
-timeout 30 journalctl --directory="$JOURNAL_DIR" --list-boots >/dev/null || [[ $? -lt 124 ]]
-if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then
-    timeout 30 /usr/lib/systemd/systemd-journal-remote \
-                    --getter="journalctl --directory=$JOURNAL_DIR --output=export" \
-                    --split-mode=none \
-                    --output="$REMOTE_OUT/system.journal" || [[ $? -lt 124 ]]
-    timeout 30 journalctl --directory="$REMOTE_OUT" >/dev/null || [[ $? -lt 124 ]]
-    rm -f "$REMOTE_OUT"/*
-fi
diff --git a/test/units/testsuite-04.fss.sh b/test/units/testsuite-04.fss.sh
deleted file mode 100755 (executable)
index 03351b8..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Forward Secure Sealing
-
-if ! journalctl --version | grep -qF +GCRYPT; then
-    echo "Built without gcrypt, skipping the FSS tests"
-    exit 0
-fi
-
-journalctl --force --setup-keys --interval=2 |& tee /tmp/fss
-FSS_VKEY="$(sed -rn '/([a-f0-9]{6}\-){3}[a-f0-9]{6}\/[a-f0-9]+\-[a-f0-9]+/p' /tmp/fss)"
-[[ -n "$FSS_VKEY" ]]
-
-# Generate some buzz in the journal and wait until the FSS key is changed
-# at least once
-systemd-cat cat /etc/os-release
-sleep 4
-# Seal the journal
-journalctl --rotate
-# Verification should fail without a valid FSS key
-(! journalctl --verify)
-(! journalctl --verify --verify-key="")
-(! journalctl --verify --verify-key="000000-000000-000000-000000/00000000-00000")
-# FIXME: ignore --verify result until #27532 is resolved
-journalctl --verify --verify-key="$FSS_VKEY" || :
-
-# Sealing + systemd-journal-remote
-/usr/lib/systemd/systemd-journal-remote --getter="journalctl -n 5 -o export" \
-                                        --split-mode=none \
-                                        --seal=yes \
-                                        --output=/tmp/sealed.journal
-(! journalctl --file=/tmp/sealed.journal --verify)
-(! journalctl --file=/tmp/sealed.journal --verify --verify-key="")
-(! journalctl --file=/tmp/sealed.journal --verify --verify-key="000000-000000-000000-000000/00000000-00000")
-# FIXME: ignore --verify result until #27532 is resolved
-journalctl --file=/tmp/sealed.journal --verify --verify-key="$FSS_VKEY" || :
-rm -f /tmp/sealed.journal
-
-# Return back to a journal without FSS
-rm -fv "/var/log/journal/$(</etc/machine-id)/fss"
-journalctl --rotate --vacuum-size=1
-# FIXME: ignore --verify result until #27532 is resolved
-journalctl --verify || :
diff --git a/test/units/testsuite-04.journal-append.sh b/test/units/testsuite-04.journal-append.sh
deleted file mode 100755 (executable)
index 35f9433..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# test-journal-append corrupts the journal file by flipping a bit at a given offset and
-# following it by a write to check if we handle appending messages to corrupted journals
-# gracefully
-
-TEST_JOURNAL_APPEND=/usr/lib/systemd/tests/unit-tests/manual/test-journal-append
-
-[[ -x "$TEST_JOURNAL_APPEND" ]]
-
-# Corrupt the first ~1024 bytes, this should be pretty quick
-"$TEST_JOURNAL_APPEND" --sequential --start-offset=0 --iterations=350 --iteration-step=3
-
-# Skip most of the test when running without acceleration, as it's excruciatingly slow
-# (this shouldn't be an issue, as it should run in nspawn as well)
-if ! [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
-    # Corrupt the beginning of every 1K block between 1K - 32K
-    for ((i = 1024; i <= (32 * 1024); i += 1024)); do
-        "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
-    done
-
-    # Corrupt the beginning of every 16K block between 32K - 128K
-    for ((i = (32 * 1024); i <= (256 * 1024); i += (16 * 1024))); do
-        "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
-    done
-
-    # Corrupt the beginning of every 128K block between 128K - 1M
-    for ((i = (128 * 1024); i <= (1 * 1024 * 1024); i += (128 * 1024))); do
-        "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
-    done
-
-    # And finally the beginning of every 1M block between 1M and 8M
-    for ((i = (1 * 1024 * 1024); i < (8 * 1024 * 1024); i += (1 * 1024 * 1024))); do
-        "$TEST_JOURNAL_APPEND" --sequential --start-offset="$i" --iterations=5 --iteration-step=13
-    done
-
-    if [[ "$(nproc)" -ge 2 ]]; then
-        # Try to corrupt random bytes throughout the journal
-        "$TEST_JOURNAL_APPEND" --iterations=25
-    fi
-else
-    "$TEST_JOURNAL_APPEND" --iterations=10
-fi
diff --git a/test/units/testsuite-04.journal-corrupt.sh b/test/units/testsuite-04.journal-corrupt.sh
deleted file mode 100755 (executable)
index c25cf54..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-journalctl --rotate --vacuum-files=1
-# Nuke all archived journals, so we start with a clean slate
-rm -f "/var/log/journal/$(</etc/machine-id)"/system@*.journal
-rm -f "/var/log/journal/$(</etc/machine-id)"/user-*@*.journal
-journalctl --header | grep path
-
-# Make sure the user instance is active when we rotate journals
-loginctl enable-linger testuser
-systemd-run --unit user-sleep.service --user -M testuser@ sleep infinity
-
-for _ in {0..9}; do
-    journalctl --rotate
-    journalctl --sync
-    SYSTEMD_LOG_LEVEL=debug journalctl -n1 -q
-    (! journalctl -n0 -q |& grep corrupted)
-done
-
-systemctl stop --user -M testuser@ user-sleep.service
-loginctl disable-linger testuser
-
-journalctl --sync
-journalctl --rotate --vacuum-files=1
-# Nuke all archived journals, so we start with a clean slate
-rm -f "/var/log/journal/$(</etc/machine-id)"/system@*.journal
-rm -f "/var/log/journal/$(</etc/machine-id)/"user-*@*.journal
-journalctl --header | grep path
-
-for _ in {0..9}; do
-    journalctl --rotate --vacuum-files=1
-    journalctl --sync
-    SYSTEMD_LOG_LEVEL=debug journalctl -n1 -q
-    (! journalctl -n0 -q |& grep corrupted)
-done
diff --git a/test/units/testsuite-04.journal-gatewayd.sh b/test/units/testsuite-04.journal-gatewayd.sh
deleted file mode 100755 (executable)
index 9c7a3d0..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if [[ ! -x /usr/lib/systemd/systemd-journal-gatewayd ]]; then
-    echo "Built without systemd-journal-gatewayd support, skipping the test"
-    exit 0
-fi
-
-LOG_FILE="$(mktemp)"
-
-at_exit() {
-    if [[ $? -ne 0 ]]; then
-        # The $LOG_FILE is potentially huge (as it might be a full copy of the current journal), so let's
-        # dump it at debug level under a specific syslog tag, so it's clearly separated from the actual test
-        # journal; things get very confusing otherwise.
-        systemd-cat -t log-file-dump -p debug cat "$LOG_FILE"
-    fi
-
-    rm -f "$LOG_FILE"
-}
-
-trap at_exit EXIT
-
-TEST_MESSAGE="-= This is a test message $RANDOM =-"
-TEST_TAG="$(systemd-id128 new)"
-
-BEFORE_TIMESTAMP="$(date +%s)"
-echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
-sleep 1
-journalctl --sync
-TEST_CURSOR="$(journalctl -q -t "$TEST_TAG" -n 0 --show-cursor | awk '{ print $3; }')"
-BOOT_CURSOR="$(journalctl -q -b -n 0 --show-cursor | awk '{ print $3; }')"
-AFTER_TIMESTAMP="$(date +%s)"
-
-/usr/lib/systemd/systemd-journal-gatewayd --version
-/usr/lib/systemd/systemd-journal-gatewayd --help
-
-# Default configuration (HTTP, socket activated)
-systemctl start systemd-journal-gatewayd.socket
-
-# /browse
-# We should get redirected to /browse by default
-curl -LSfs http://localhost:19531 >"$LOG_FILE"
-grep -qF "<title>Journal</title>" "$LOG_FILE"
-curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
-grep -qF "<title>Journal</title>" "$LOG_FILE"
-(! curl -LSfs http://localhost:19531/foo/bar/baz)
-(! curl -LSfs http://localhost:19531/foo/../../../bar/../baz)
-
-# /entries
-# Accept: text/plain should be the default
-curl -LSfs http://localhost:19531/entries >"$LOG_FILE"
-grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
-curl -LSfs --header "Accept: text/plain" http://localhost:19531/entries >"$LOG_FILE"
-grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
-curl -LSfs --header "Accept: application/json" http://localhost:19531/entries >"$LOG_FILE"
-jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
-curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?boot >"$LOG_FILE"
-jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
-curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
-# Show 10 entries starting from $BOOT_CURSOR, skip the first 5
-curl -LSfs \
-     --header "Accept: application/json" \
-     --header "Range: entries=$BOOT_CURSOR:5:10" \
-     http://localhost:19531/entries >"$LOG_FILE"
-jq -se "length == 10" "$LOG_FILE"
-# Check if the specified cursor refers to an existing entry and return just that entry
-curl -LSfs \
-     --header "Accept: application/json" \
-     --header "Range: entries=$TEST_CURSOR" \
-     http://localhost:19531/entries?discrete >"$LOG_FILE"
-jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
-# Check entry is present (resp. absent) when filtering by timestamp
-curl -LSfs \
-     --header "Range: realtime=$BEFORE_TIMESTAMP:" \
-     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
-curl -LSfs \
-     --header "Range: realtime=:$AFTER_TIMESTAMP" \
-     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
-curl -LSfs \
-     --header "Accept: application/json" \
-     --header "Range: realtime=:$BEFORE_TIMESTAMP" \
-     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-jq -se "length == 0" "$LOG_FILE"
-curl -LSfs \
-     --header "Accept: application/json" \
-     --header "Range: realtime=$AFTER_TIMESTAMP:" \
-     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-jq -se "length == 0" "$LOG_FILE"
-# Check positive and negative skip when filtering by timestamp
-echo "-= This is a second test message =-" | systemd-cat -t "$TEST_TAG"
-journalctl --sync
-TEST2_CURSOR="$(journalctl -q -t "$TEST_TAG" -n 0 --show-cursor | awk '{ print $3; }')"
-echo "-= This is a third test message =-" | systemd-cat -t "$TEST_TAG"
-journalctl --sync
-sleep 1
-END_TIMESTAMP="$(date +%s)"
-curl -LSfs \
-     --header "Accept: application/json" \
-     --header "Range: realtime=$BEFORE_TIMESTAMP::1:1" \
-     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
-curl -LSfs \
-     --header "Accept: application/json" \
-     --header "Range: realtime=$END_TIMESTAMP::-1:1" \
-     http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" >"$LOG_FILE"
-jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" "$LOG_FILE"
-
-# No idea how to properly parse this (jq won't cut it), so let's at least do some sanity checks that every
-# line is either empty or begins with data:
-curl -LSfs --header "Accept: text/event-stream" http://localhost:19531/entries >"$LOG_FILE"
-awk '!/^(data: \{.+\}|)$/ { exit 1; }' "$LOG_FILE"
-# Same thing as journalctl --output=export
-mkdir /tmp/remote-journal
-curl -LSfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries >"$LOG_FILE"
-/usr/lib/systemd/systemd-journal-remote --output=/tmp/remote-journal/system.journal --split-mode=none "$LOG_FILE"
-journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE"
-rm -rf /tmp/remote-journal/*
-# Let's do the same thing again, but let systemd-journal-remote spawn curl itself
-/usr/lib/systemd/systemd-journal-remote --url=http://localhost:19531/entries \
-                                        --output=/tmp/remote-journal/system.journal \
-                                        --split-mode=none
-journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE"
-rm -rf /tmp/remote-journal
-
-# /machine
-curl -LSfs http://localhost:19531/machine >"$LOG_FILE"
-jq . "$LOG_FILE"
-
-# /fields
-curl -LSfs http://localhost:19531/fields/MESSAGE >"$LOG_FILE"
-grep -qE -- "$TEST_MESSAGE" "$LOG_FILE"
-curl -LSfs http://localhost:19531/fields/_TRANSPORT
-(! curl -LSfs http://localhost:19531/fields)
-(! curl -LSfs http://localhost:19531/fields/foo-bar-baz)
-
-systemctl stop systemd-journal-gatewayd.{socket,service}
-
-if ! command -v openssl >/dev/null; then
-    echo "openssl command not available, skipping the HTTPS tests"
-    exit 0
-fi
-
-# Generate a self-signed certificate for systemd-journal-gatewayd
-#
-# Note: older OpenSSL requires a config file with some extra options, unfortunately
-cat >/tmp/openssl.conf <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = CZ
-L = Brno
-O = Foo
-OU = Bar
-CN = localhost
-EOF
-openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 7 \
-            -config /tmp/openssl.conf \
-            -keyout /tmp/key.pem -out /tmp/cert.pem
-# Start HTTPS version of gatewayd via the systemd-socket-activate tool to give it some coverage as well
-systemd-socket-activate --listen=19531 -- \
-    /usr/lib/systemd/systemd-journal-gatewayd \
-        --cert=/tmp/cert.pem \
-        --key=/tmp/key.pem \
-        --file="/var/log/journal/*/*.journal" &
-GATEWAYD_PID=$!
-sleep 1
-
-# Do a limited set of tests, since the underlying code should be the same past the HTTPS transport
-curl -LSfsk https://localhost:19531 >"$LOG_FILE"
-grep -qF "<title>Journal</title>" "$LOG_FILE"
-curl -LSfsk https://localhost:19531/entries >"$LOG_FILE"
-grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" "$LOG_FILE"
-curl -LSfsk --header "Accept: application/json" https://localhost:19531/entries >"$LOG_FILE"
-jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" "$LOG_FILE"
-curl -LSfsk https://localhost:19531/machine >"$LOG_FILE"
-jq . "$LOG_FILE"
-curl -LSfsk https://localhost:19531/fields/_TRANSPORT
-
-kill "$GATEWAYD_PID"
-
-# Test a couple of error scenarios
-GATEWAYD_FILE="$(mktemp /tmp/test-gatewayd-XXX.journal)"
-
-/usr/lib/systemd/systemd-journal-remote --output="$GATEWAYD_FILE" --getter="journalctl -n5 -o export"
-systemd-run --unit="test-gatewayd.service" --socket-property="ListenStream=19531" \
-            /usr/lib/systemd/systemd-journal-gatewayd --file="$GATEWAYD_FILE"
-
-# Call an unsupported endpoint together with some garbage data - gatewayd should not send garbage in return
-# See: https://github.com/systemd/systemd/issues/9858
-OUT="$(mktemp)"
-for _ in {0..4}; do
-    (! curl --fail-with-body -d "please process this🐱 $RANDOM" -L http://localhost:19531/upload | tee "$OUT")
-    (! grep '[^[:print:]]' "$OUT")
-done
-(! curl --fail-with-body --upload-file "$GATEWAYD_FILE" -L http://localhost:19531/upload | tee "$OUT")
-(! grep '[^[:print:]]' "$OUT")
-rm -rf "$OUT"
-
-curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
-grep -qF "<title>Journal</title>" "$LOG_FILE"
-# Nuke the file behind the /browse endpoint
-mv /usr/share/systemd/gatewayd/browse.html /usr/share/systemd/gatewayd/browse.html.bak
-(! curl --fail-with-body -L http://localhost:19531/browse)
-mv /usr/share/systemd/gatewayd/browse.html.bak /usr/share/systemd/gatewayd/browse.html
-curl -LSfs http://localhost:19531/browse >"$LOG_FILE"
-grep -qF "<title>Journal</title>" "$LOG_FILE"
-
-# Nuke the journal file
-mv "$GATEWAYD_FILE" "$GATEWAYD_FILE.bak"
-(! curl --fail-with-body -L http://localhost:19531/fields/_PID)
-mv "$GATEWAYD_FILE.bak" "$GATEWAYD_FILE"
-curl -LSfs http://localhost:19531/fields/_PID
-
-systemctl stop test-gatewayd.{socket,service}
-rm -f "$GATEWAYD_FILE"
diff --git a/test/units/testsuite-04.journal-remote.sh b/test/units/testsuite-04.journal-remote.sh
deleted file mode 100755 (executable)
index c7b99b1..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-if [[ ! -x /usr/lib/systemd/systemd-journal-remote || ! -x /usr/lib/systemd/systemd-journal-upload ]]; then
-    echo "Built without systemd-journal-remote/upload support, skipping the test"
-    exit 0
-fi
-
-if ! command -v openssl >/dev/null; then
-    echo "openssl command not available, skipping the tests"
-    exit 0
-fi
-
-at_exit() {
-    set +e
-
-    systemctl stop systemd-journal-upload
-    systemctl stop systemd-journal-remote.{socket,service}
-    # Remove any remote journals on exit, so we don't try to export them together
-    # with the local journals, causing a mess
-    rm -rf /var/log/journal/remote
-}
-
-trap at_exit EXIT
-
-TEST_MESSAGE="-= This is a test message $RANDOM =-"
-TEST_TAG="$(systemd-id128 new)"
-
-echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
-journalctl --sync
-
-/usr/lib/systemd/systemd-journal-remote --version
-/usr/lib/systemd/systemd-journal-remote --help
-/usr/lib/systemd/systemd-journal-upload --version
-/usr/lib/systemd/systemd-journal-upload --help
-
-# Generate a self-signed certificate for systemd-journal-remote
-#
-# Note: older OpenSSL requires a config file with some extra options, unfortunately
-# Note2: /run here is used on purpose, since the systemd-journal-remote service uses PrivateTmp=yes
-mkdir -p /run/systemd/journal-remote-tls
-cat >/tmp/openssl.conf <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = CZ
-L = Brno
-O = Foo
-OU = Bar
-CN = localhost
-EOF
-openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 7 \
-            -config /tmp/openssl.conf \
-            -keyout /run/systemd/journal-remote-tls/key.pem \
-            -out /run/systemd/journal-remote-tls/cert.pem
-chown -R systemd-journal-remote /run/systemd/journal-remote-tls
-
-# Configure journal-upload to upload journals to journal-remote without client certificates
-mkdir -p /run/systemd/journal-{remote,upload}.conf.d
-cat >/run/systemd/journal-remote.conf.d/99-test.conf <<EOF
-[Remote]
-SplitMode=host
-ServerKeyFile=/run/systemd/journal-remote-tls/key.pem
-ServerCertificateFile=/run/systemd/journal-remote-tls/cert.pem
-TrustedCertificateFile=-
-EOF
-cat >/run/systemd/journal-upload.conf.d/99-test.conf <<EOF
-[Upload]
-URL=https://localhost:19532
-ServerKeyFile=-
-ServerCertificateFile=-
-TrustedCertificateFile=-
-EOF
-systemd-analyze cat-config systemd/journal-remote.conf
-systemd-analyze cat-config systemd/journal-upload.conf
-
-systemctl restart systemd-journal-remote.socket
-systemctl restart systemd-journal-upload
-timeout 15 bash -xec 'until systemctl -q is-active systemd-journal-remote.service; do sleep 1; done'
-systemctl status systemd-journal-{remote,upload}
-
-# It may take a bit until the whole journal is transferred
-timeout 30 bash -xec "until journalctl --directory=/var/log/journal/remote --identifier='$TEST_TAG' --grep='$TEST_MESSAGE'; do sleep 1; done"
-
-systemctl stop systemd-journal-upload
-systemctl stop systemd-journal-remote.{socket,service}
-rm -rf /var/log/journal/remote/*
-
-# Now let's do the same, but with a full PKI setup
-#
-# journal-upload keeps the cursor of the last uploaded message, so let's send a fresh one
-echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
-journalctl --sync
-
-mkdir /run/systemd/remote-pki
-cat >/run/systemd/remote-pki/ca.conf <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = CZ
-L = Brno
-O = Foo
-OU = Bar
-CN = Test CA
-
-[ v3_ca ]
-subjectKeyIdentifier = hash
-authorityKeyIdentifier = keyid:always,issuer:always
-basicConstraints = CA:true
-EOF
-cat >/run/systemd/remote-pki/client.conf <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = CZ
-L = Brno
-O = Foo
-OU = Bar
-CN = Test Client
-EOF
-cat >/run/systemd/remote-pki/server.conf <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = CZ
-L = Brno
-O = Foo
-OU = Bar
-CN = localhost
-EOF
-# Generate a dummy CA
-openssl req -x509 -nodes -newkey rsa:2048 -sha256 -days 7 \
-            -extensions v3_ca \
-            -config /run/systemd/remote-pki/ca.conf \
-            -keyout /run/systemd/remote-pki/ca.key \
-            -out /run/systemd/remote-pki/ca.crt
-openssl x509 -in /run/systemd/remote-pki/ca.crt -noout -text
-echo 01 >/run/systemd/remote-pki/ca.srl
-# Generate a client key and signing request
-openssl req -nodes -newkey rsa:2048 -sha256 \
-            -config /run/systemd/remote-pki/client.conf \
-            -keyout /run/systemd/remote-pki/client.key \
-            -out /run/systemd/remote-pki/client.csr
-# Sign the request with the CA key
-openssl x509 -req -days 7 \
-             -in /run/systemd/remote-pki/client.csr \
-             -CA /run/systemd/remote-pki/ca.crt \
-             -CAkey /run/systemd/remote-pki/ca.key \
-             -out /run/systemd/remote-pki/client.crt
-# And do the same for the server
-openssl req -nodes -newkey rsa:2048 -sha256 \
-            -config /run/systemd/remote-pki/server.conf \
-            -keyout /run/systemd/remote-pki/server.key \
-            -out /run/systemd/remote-pki/server.csr
-openssl x509 -req -days 7 \
-             -in /run/systemd/remote-pki/server.csr \
-             -CA /run/systemd/remote-pki/ca.crt \
-             -CAkey /run/systemd/remote-pki/ca.key \
-             -out /run/systemd/remote-pki/server.crt
-chown -R systemd-journal-remote:systemd-journal /run/systemd/remote-pki
-chmod -R g+rwX /run/systemd/remote-pki
-
-# Reconfigure journal-upload/journal remote with the new keys
-cat >/run/systemd/journal-remote.conf.d/99-test.conf <<EOF
-[Remote]
-SplitMode=host
-ServerKeyFile=/run/systemd/remote-pki/server.key
-ServerCertificateFile=/run/systemd/remote-pki/server.crt
-TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
-EOF
-cat >/run/systemd/journal-upload.conf.d/99-test.conf <<EOF
-[Upload]
-URL=https://localhost:19532
-ServerKeyFile=/run/systemd/remote-pki/client.key
-ServerCertificateFile=/run/systemd/remote-pki/client.crt
-TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
-EOF
-systemd-analyze cat-config systemd/journal-remote.conf
-systemd-analyze cat-config systemd/journal-upload.conf
-
-systemctl restart systemd-journal-remote.socket
-systemctl restart systemd-journal-upload
-timeout 15 bash -xec 'until systemctl -q is-active systemd-journal-remote.service; do sleep 1; done'
-systemctl status systemd-journal-{remote,upload}
-
-# It may take a bit until the whole journal is transferred
-timeout 30 bash -xec "until journalctl --directory=/var/log/journal/remote --identifier='$TEST_TAG' --grep='$TEST_MESSAGE'; do sleep 1; done"
-
-systemctl stop systemd-journal-upload
-systemctl stop systemd-journal-remote.{socket,service}
-
-# Let's test if journal-remote refuses connection from journal-upload with invalid client certs
-#
-# We should end up with something like this:
-#    systemd-journal-remote[726]: Client is not authorized
-#    systemd-journal-upload[738]: Upload to https://localhost:19532/upload failed with code 401:
-#    systemd[1]: systemd-journal-upload.service: Main process exited, code=exited, status=1/FAILURE
-#    systemd[1]: systemd-journal-upload.service: Failed with result 'exit-code'.
-#
-cat >/run/systemd/journal-upload.conf.d/99-test.conf <<EOF
-[Upload]
-URL=https://localhost:19532
-ServerKeyFile=/run/systemd/journal-remote-tls/key.pem
-ServerCertificateFile=/run/systemd/journal-remote-tls/cert.pem
-TrustedCertificateFile=/run/systemd/remote-pki/ca.crt
-EOF
-systemd-analyze cat-config systemd/journal-upload.conf
-mkdir -p /run/systemd/system/systemd-journal-upload.service.d
-cat >/run/systemd/system/systemd-journal-upload.service.d/99-test.conf <<EOF
-[Service]
-Restart=no
-EOF
-systemctl daemon-reload
-chgrp -R systemd-journal /run/systemd/journal-remote-tls
-chmod -R g+rwX /run/systemd/journal-remote-tls
-
-systemctl restart systemd-journal-upload
-timeout 10 bash -xec 'while [[ "$(systemctl show -P ActiveState systemd-journal-upload)" != failed ]]; do sleep 1; done'
-(! systemctl status systemd-journal-upload)
diff --git a/test/units/testsuite-04.journal.sh b/test/units/testsuite-04.journal.sh
deleted file mode 100755 (executable)
index bb4f66d..0000000
+++ /dev/null
@@ -1,289 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Rotation/flush test, see https://github.com/systemd/systemd/issues/19895
-journalctl --relinquish-var
-[[ "$(systemd-detect-virt -v)" == "qemu" ]] && ITERATIONS=10 || ITERATIONS=50
-for ((i = 0; i < ITERATIONS; i++)); do
-    dd if=/dev/urandom bs=1M count=1 | base64 | systemd-cat
-done
-journalctl --rotate
-# Let's test varlinkctl a bit, i.e. implement the equivalent of 'journalctl --rotate' via varlinkctl
-varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Rotate '{}'
-journalctl --flush
-varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.FlushToVar '{}'
-journalctl --sync
-varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Synchronize '{}'
-journalctl --rotate --vacuum-size=8M
-
-# Reset the ratelimit buckets for the subsequent tests below.
-systemctl restart systemd-journald
-
-# Test stdout stream
-write_and_match() {
-    local input="${1:?}"
-    local expected="${2?}"
-    local id
-    shift 2
-
-    id="$(systemd-id128 new)"
-    echo -ne "$input" | systemd-cat -t "$id" "$@"
-    journalctl --sync
-    diff <(echo -ne "$expected") <(journalctl -b -o cat -t "$id")
-}
-# Skip empty lines
-write_and_match "\n\n\n" "" --level-prefix false
-write_and_match "<5>\n<6>\n<7>\n" "" --level-prefix true
-# Remove trailing spaces
-write_and_match "Trailing spaces \t \n" "Trailing spaces\n" --level-prefix false
-write_and_match "<5>Trailing spaces \t \n" "Trailing spaces\n" --level-prefix true
-# Don't remove leading spaces
-write_and_match " \t Leading spaces\n" " \t Leading spaces\n" --level-prefix false
-write_and_match "<5> \t Leading spaces\n" " \t Leading spaces\n" --level-prefix true
-
-# --output-fields restricts output
-ID="$(systemd-id128 new)"
-echo -ne "foo" | systemd-cat -t "$ID" --level-prefix false
-# Let's test varlinkctl a bit, i.e. implement the equivalent of 'journalctl --sync' via varlinkctl
-varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Synchronize '{}'
-journalctl -b -o export --output-fields=MESSAGE,FOO --output-fields=PRIORITY,MESSAGE -t "$ID" >/tmp/output
-[[ $(wc -l </tmp/output) -eq 9 ]]
-grep -q '^__CURSOR=' /tmp/output
-grep -q '^MESSAGE=foo$' /tmp/output
-grep -q '^PRIORITY=6$' /tmp/output
-(! grep '^FOO=' /tmp/output)
-(! grep '^SYSLOG_FACILITY=' /tmp/output)
-
-# --truncate shows only first line, skip under asan due to logger
-ID="$(systemd-id128 new)"
-echo -e 'HEAD\nTAIL\nTAIL' | systemd-cat -t "$ID"
-journalctl --sync
-journalctl -b -t "$ID" | grep -q HEAD
-journalctl -b -t "$ID" | grep -q TAIL
-journalctl -b -t "$ID" --truncate-newline | grep -q HEAD
-journalctl -b -t "$ID" --truncate-newline | grep -q -v TAIL
-
-# '-b all' negates earlier use of -b (-b and -m are otherwise exclusive)
-journalctl -b -1 -b all -m >/dev/null
-
-# -b always behaves like -b0
-journalctl -q -b-1 -b0 | head -1 >/tmp/expected
-journalctl -q -b-1 -b | head -1 >/tmp/output
-diff /tmp/expected /tmp/output
-# ... even when another option follows (both of these should fail due to -m)
-{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 >/tmp/expected
-{ journalctl -ball -b  -m 2>&1 || :; } | head -1 >/tmp/output
-diff /tmp/expected /tmp/output
-
-# https://github.com/systemd/systemd/issues/13708
-ID=$(systemd-id128 new)
-systemd-cat -t "$ID" bash -c 'echo parent; (echo child) & wait' &
-PID=$!
-wait $PID
-journalctl --sync
-# We can drop this grep when https://github.com/systemd/systemd/issues/13937
-# has a fix.
-journalctl -b -o export -t "$ID" --output-fields=_PID | grep '^_PID=' >/tmp/output
-[[ $(wc -l </tmp/output) -eq 2 ]]
-grep -q "^_PID=$PID" /tmp/output
-grep -vq "^_PID=$PID" /tmp/output
-
-# https://github.com/systemd/systemd/issues/15654
-ID=$(systemd-id128 new)
-printf "This will\nusually fail\nand be truncated\n" >/tmp/expected
-systemd-cat -t "$ID" /bin/sh -c 'env echo -n "This will";echo;env echo -n "usually fail";echo;env echo -n "and be truncated";echo;'
-journalctl --sync
-journalctl -b -o cat -t "$ID" >/tmp/output
-diff /tmp/expected /tmp/output
-[[ $(journalctl -b -o cat -t "$ID" --output-fields=_TRANSPORT | grep -Pc "^stdout$") -eq 3 ]]
-[[ $(journalctl -b -o cat -t "$ID" --output-fields=_LINE_BREAK | grep -Pc "^pid-change$") -eq 3 ]]
-[[ $(journalctl -b -o cat -t "$ID" --output-fields=_PID | sort -u | grep -c "^.*$") -eq 3 ]]
-[[ $(journalctl -b -o cat -t "$ID" --output-fields=MESSAGE | grep -Pc "^(This will|usually fail|and be truncated)$") -eq 3 ]]
-
-# test that LogLevelMax can also suppress logging about services, not only by services
-systemctl start silent-success
-journalctl --sync
-[[ -z "$(journalctl -b -q -u silent-success.service)" ]]
-
-# Test syslog identifiers exclusion
-systemctl start verbose-success.service
-journalctl --sync
-[[ -n "$(journalctl -b -q -u verbose-success.service -t systemd)" ]]
-[[ -n "$(journalctl -b -q -u verbose-success.service -t echo)" ]]
-[[ -n "$(journalctl -b -q -u verbose-success.service -T systemd)" ]]
-[[ -n "$(journalctl -b -q -u verbose-success.service -T echo)" ]]
-[[ -z "$(journalctl -b -q -u verbose-success.service -T echo -T '(echo)' -T sleep -T '(sleep)' -T systemd -T '(systemd)' -T systemd-executor)" ]]
-
-# Exercise the matching machinery
-SYSTEMD_LOG_LEVEL=debug journalctl -b -n 1 /dev/null /dev/zero /dev/null /dev/null /dev/null
-journalctl -b -n 1 /bin/true /bin/false
-journalctl -b -n 1 /bin/true + /bin/false
-journalctl -b -n 1 -r --unit "systemd*"
-
-systemd-run --user -M "testuser@.host" /bin/echo hello
-journalctl --sync
-journalctl -b -n 1 -r --user-unit "*"
-
-(! journalctl -b /dev/lets-hope-this-doesnt-exist)
-(! journalctl -b /dev/null /dev/zero /dev/this-also-shouldnt-exist)
-(! journalctl -b --unit "this-unit-should-not-exist*")
-
-# Facilities & priorities
-journalctl --facility help
-journalctl --facility help | grep -F 'kern'
-journalctl --facility help | grep -F 'mail'
-journalctl --facility kern -n 1
-journalctl --facility syslog --priority 0..3 -n 1
-journalctl --facility syslog --priority 3..0 -n 1
-journalctl --facility user --priority 0..0 -n 1
-journalctl --facility daemon --priority warning -n 1
-journalctl --facility daemon --priority warning..info -n 1
-journalctl --facility daemon --priority notice..crit -n 1
-journalctl --facility daemon --priority 5..crit -n 1
-
-# Assorted combinations
-journalctl -o help
-journalctl -o help | grep -F 'short'
-journalctl -o help | grep -F 'export'
-journalctl -q -n all -a | grep . >/dev/null
-journalctl -q --no-full | grep . >/dev/null
-journalctl -q --user --system | grep . >/dev/null
-journalctl --namespace "*" | grep . >/dev/null
-journalctl --namespace "" | grep . >/dev/null
-journalctl -q --namespace "+foo-bar-baz-$RANDOM" | grep . >/dev/null
-(! journalctl -q --namespace "foo-bar-baz-$RANDOM" | grep .)
-journalctl --root / | grep . >/dev/null
-journalctl --cursor "t=0;t=-1;t=0;t=0x0" | grep . >/dev/null
-journalctl --header | grep system.journal
-journalctl --field _EXE | grep . >/dev/null
-journalctl --no-hostname --utc --catalog | grep . >/dev/null
-# Exercise executable_is_script() and the related code, e.g. `journalctl -b /path/to/a/script.sh` should turn
-# into ((_EXE=/bin/bash AND _COMM=script.sh) AND _BOOT_ID=c002e3683ba14fa8b6c1e12878386514)
-journalctl -b "$(readlink -f "$0")" | grep . >/dev/null
-journalctl -b "$(systemd-id128 boot-id)" | grep . >/dev/null
-journalctl --since yesterday --reverse | grep . >/dev/null
-journalctl --machine .host | grep . >/dev/null
-# Log something that journald will forward to wall
-echo "Oh no!" | systemd-cat -t "emerg$RANDOM" -p emerg --stderr-priority emerg
-
-TAG="$(systemd-id128 new)"
-echo "Foo Bar Baz" | systemd-cat -t "$TAG"
-journalctl --sync
-# Relevant excerpt from journalctl(1):
-#   If the pattern is all lowercase, matching is case insensitive. Otherwise, matching is case sensitive.
-#   This can be overridden with the --case-sensitive option
-journalctl -e -t "$TAG" --grep "Foo Bar Baz"
-journalctl -e -t "$TAG" --grep "foo bar baz"
-(! journalctl -e -t "$TAG" --grep "foo Bar baz")
-journalctl -e -t "$TAG" --case-sensitive=false --grep "foo Bar baz"
-
-(! journalctl --facility hopefully-an-unknown-facility)
-(! journalctl --priority hello-world)
-(! journalctl --priority 0..128)
-(! journalctl --priority 0..systemd)
-
-# Other options
-journalctl --disk-usage
-journalctl --dmesg -n 1
-journalctl --fields
-journalctl --list-boots
-journalctl --update-catalog
-journalctl --list-catalog
-
-# Add new tests before here, the journald restarts below
-# may make tests flappy.
-
-# Don't lose streams on restart
-systemctl start forever-print-hola
-sleep 3
-systemctl restart systemd-journald
-sleep 3
-systemctl stop forever-print-hola
-[[ ! -f "/tmp/i-lose-my-logs" ]]
-
-# https://github.com/systemd/systemd/issues/4408
-rm -f /tmp/i-lose-my-logs
-systemctl start forever-print-hola
-sleep 3
-systemctl kill --signal=SIGKILL systemd-journald
-sleep 3
-[[ ! -f "/tmp/i-lose-my-logs" ]]
-systemctl stop forever-print-hola
-
-set +o pipefail
-# https://github.com/systemd/systemd/issues/15528
-journalctl --follow --file=/var/log/journal/*/* | head -n1 | grep .
-# https://github.com/systemd/systemd/issues/24565
-journalctl --follow --merge | head -n1 | grep .
-set -o pipefail
-
-# https://github.com/systemd/systemd/issues/26746
-rm -f /tmp/issue-26746-log /tmp/issue-26746-cursor
-ID="$(systemd-id128 new)"
-journalctl -t "$ID" --follow --cursor-file=/tmp/issue-26746-cursor | tee /tmp/issue-26746-log &
-systemd-cat -t "$ID" /bin/sh -c 'echo hogehoge'
-# shellcheck disable=SC2016
-timeout 10 bash -c 'until [[ -f /tmp/issue-26746-log && "$(cat /tmp/issue-26746-log)" =~ hogehoge ]]; do sleep .5; done'
-pkill -TERM journalctl
-timeout 10 bash -c 'until test -f /tmp/issue-26746-cursor; do sleep .5; done'
-CURSOR_FROM_FILE="$(cat /tmp/issue-26746-cursor)"
-CURSOR_FROM_JOURNAL="$(journalctl -t "$ID" --output=export MESSAGE=hogehoge | sed -n -e '/__CURSOR=/ { s/__CURSOR=//; p }')"
-test "$CURSOR_FROM_FILE" = "$CURSOR_FROM_JOURNAL"
-
-# Check that the seqnum field at least superficially works
-systemd-cat echo "ya"
-journalctl --sync
-SEQNUM1=$(journalctl -o export -n 1 | grep -Ea "^__SEQNUM=" | cut -d= -f2)
-systemd-cat echo "yo"
-journalctl --sync
-SEQNUM2=$(journalctl -o export -n 1 | grep -Ea "^__SEQNUM=" | cut -d= -f2)
-test "$SEQNUM2" -gt "$SEQNUM1"
-
-# Test for journals without RTC
-# See: https://github.com/systemd/systemd/issues/662
-JOURNAL_DIR="$(mktemp -d)"
-while read -r file; do
-    filename="${file##*/}"
-    unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}"
-done < <(find /usr/lib/systemd/tests/testdata/test-journals/no-rtc -name "*.zst")
-
-journalctl --directory="$JOURNAL_DIR" --list-boots --output=json >/tmp/lb1
-diff -u /tmp/lb1 - <<'EOF'
-[{"index":-3,"boot_id":"5ea5fc4f82a14186b5332a788ef9435e","first_entry":1666569600994371,"last_entry":1666584266223608},{"index":-2,"boot_id":"bea6864f21ad4c9594c04a99d89948b0","first_entry":1666569601005945,"last_entry":1666584347230411},{"index":-1,"boot_id":"4c708e1fd0744336be16f3931aa861fb","first_entry":1666569601017222,"last_entry":1666584354649355},{"index":0,"boot_id":"35e8501129134edd9df5267c49f744a4","first_entry":1666569601009823,"last_entry":1666584438086856}]
-EOF
-rm -rf "$JOURNAL_DIR" /tmp/lb1
-
-# Check that using --after-cursor/--cursor-file= together with journal filters doesn't
-# skip over entries matched by the filter
-# See: https://github.com/systemd/systemd/issues/30288
-UNIT_NAME="test-cursor-$RANDOM.service"
-CURSOR_FILE="$(mktemp)"
-# Generate some messages we can match against
-journalctl --cursor-file="$CURSOR_FILE" -n1
-systemd-run --unit="$UNIT_NAME" --wait --service-type=exec bash -ec "sleep 2; set -x; echo hello; echo world; set +x; sleep 2"
-journalctl --sync
-# --after-cursor= + --unit=
-# The format of the "Starting ..." message depends on StatusUnitFormat=, so match only the beginning
-# which should be enough in this case
-[[ "$(journalctl -n 1 -p info -o cat --unit="$UNIT_NAME" --after-cursor="$(<"$CURSOR_FILE")" _PID=1 )" =~ ^Starting\  ]]
-# There should be no such messages before the cursor
-[[ -z "$(journalctl -n 1 -p info -o cat --unit="$UNIT_NAME" --after-cursor="$(<"$CURSOR_FILE")" --reverse)" ]]
-# --cursor-file= + a journal filter
-diff <(journalctl --cursor-file="$CURSOR_FILE" -p info -o cat _SYSTEMD_UNIT="$UNIT_NAME") - <<EOF
-+ echo hello
-hello
-+ echo world
-world
-+ set +x
-EOF
-rm -f "$CURSOR_FILE"
-
-# Check that --until works with --after-cursor and --lines/-n
-# See: https://github.com/systemd/systemd/issues/31776
-CURSOR_FILE="$(mktemp)"
-journalctl -q -n 0 --cursor-file="$CURSOR_FILE"
-TIMESTAMP="$(journalctl -q -n 1 --cursor="$(<"$CURSOR_FILE")" --output=short-unix | cut -d ' ' -f 1 | cut -d '.' -f 1)"
-[[ -z "$(journalctl -q -n 10 --after-cursor="$(<"$CURSOR_FILE")" --until "@$((TIMESTAMP - 3))")" ]]
-rm -f "$CURSOR_FILE"
diff --git a/test/units/testsuite-04.service b/test/units/testsuite-04.service
deleted file mode 100644 (file)
index 63a0104..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-04-JOURNAL
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-04.sh b/test/units/testsuite-04.sh
deleted file mode 100755 (executable)
index 9c2a033..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
diff --git a/test/units/testsuite-05.effective-limit.sh b/test/units/testsuite-05.effective-limit.sh
deleted file mode 100755 (executable)
index 3ff8e83..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-pre=test05
-cat >/run/systemd/system/"$pre"alpha.slice <<EOF
-[Slice]
-MemoryMax=40M
-MemoryHigh=40M
-TasksMax=400
-EOF
-
-cat >/run/systemd/system/"$pre"alpha-beta.slice <<EOF
-[Slice]
-MemoryMax=10M
-MemoryHigh=10M
-TasksMax=100
-EOF
-
-cat >/run/systemd/system/"$pre"alpha-beta-gamma.slice <<EOF
-[Slice]
-MemoryMax=20M
-MemoryHigh=20M
-TasksMax=200
-EOF
-
-systemctl daemon-reload
-
-srv=probe.service
-slc0="$pre"alpha.slice
-slc="$pre"alpha-beta-gamma.slice
-
-systemd-run --unit "$srv" --slice "$slc"  \
-    -p MemoryMax=5M \
-    -p MemoryHigh=5M \
-    -p TasksMax=50 \
-    sleep inf
-
-# Compare with inequality because test can run in a constrained container
-assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
-assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
-assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
-
-systemctl stop "$srv"
-
-systemd-run --unit "$srv" --slice "$slc"  \
-    sleep inf
-
-assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "10485760"
-assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "10485760"
-assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "100"
-
-systemctl set-property "$slc0" \
-    MemoryMax=5M \
-    MemoryHigh=5M \
-    TasksMax=50
-
-assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
-assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
-assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
-
-systemctl stop "$srv"
-
-rm -f /run/systemd/system/"$pre"* || :
diff --git a/test/units/testsuite-05.rlimit.sh b/test/units/testsuite-05.rlimit.sh
deleted file mode 100755 (executable)
index bbf3adb..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-P=/run/systemd/system.conf.d
-mkdir $P
-
-cat >$P/rlimits.conf <<EOF
-[Manager]
-DefaultLimitNOFILE=10000:16384
-EOF
-
-systemctl daemon-reload
-
-[[ "$(systemctl show -P DefaultLimitNOFILESoft)" = "10000" ]]
-[[ "$(systemctl show -P DefaultLimitNOFILE)" = "16384" ]]
-
-[[ "$(systemctl show -P LimitNOFILESoft testsuite-05.service)" = "10000" ]]
-[[ "$(systemctl show -P LimitNOFILE testsuite-05.service)" = "16384" ]]
-
-# shellcheck disable=SC2016
-systemd-run --wait -t bash -c '[[ "$(ulimit -n -S)" = "10000" ]]'
-# shellcheck disable=SC2016
-systemd-run --wait -t bash -c '[[ "$(ulimit -n -H)" = "16384" ]]'
diff --git a/test/units/testsuite-05.service b/test/units/testsuite-05.service
deleted file mode 100644 (file)
index cf32acc..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-05-LIMITS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-05.sh b/test/units/testsuite-05.sh
deleted file mode 100755 (executable)
index 9c2a033..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
diff --git a/test/units/testsuite-06.service b/test/units/testsuite-06.service
deleted file mode 100644 (file)
index c4c1d87..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-06-SELINUX
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-06.sh b/test/units/testsuite-06.sh
deleted file mode 100755 (executable)
index 937a040..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-. /etc/os-release
-if ! [[ "$ID" =~ centos|fedora ]]; then
-    echo "Skipping because only CentOS and Fedora support SELinux tests" >>/skipped
-    exit 77
-fi
-
-# Note: ATTOW the following checks should work with both Fedora and upstream reference policy
-#       (with or without MCS/MLS)
-
-sestatus
-
-# We should end up in permissive mode
-[[ "$(getenforce)" == "Permissive" ]]
-
-# Check PID 1's context
-PID1_CONTEXT="$(ps -h -o label 1)"
-[[ "$PID1_CONTEXT" =~ ^system_u:system_r:init_t(:s0)?$ ]]
-# The same label should be attached to all PID 1's journal messages
-journalctl -q -b -p info -n 5 --grep . _SELINUX_CONTEXT="$PID1_CONTEXT"
-
-# Check context on a couple of arbitrarily-selected files/directories
-[[ "$(stat --printf %C /run/systemd/journal/)" =~ ^system_u:object_r:(syslogd_runtime_t|syslogd_var_run_t)(:s0)?$ ]]
-[[ "$(stat --printf %C /run/systemd/notify)" =~ ^system_u:object_r:(init_runtime_t|init_var_run_t)(:s0)?$ ]]
-[[ "$(stat --printf %C /run/systemd/sessions/)" =~ ^system_u:object_r:(systemd_sessions_runtime_t|systemd_logind_sessions_t)(:s0)?$ ]]
-
-# Check if our SELinux-related functionality works
-#
-# Since the SELinux policies vary wildly, use a context from some existing file
-# as our test context
-CONTEXT="$(stat -c %C /proc/sys/kernel/core_pattern)"
-
-[[ "$(systemd-run --wait --pipe -p SELinuxContext="$CONTEXT" cat /proc/self/attr/current | tr -d '\0')" == "$CONTEXT" ]]
-(! systemd-run --wait --pipe -p SELinuxContext="foo:bar:baz" cat /proc/self/attr/current)
-(! systemd-run --wait --pipe -p ConditionSecurity='selinux' false)
-systemd-run --wait --pipe -p ConditionSecurity='!selinux' false
-
-NSPAWN_ARGS=(systemd-nspawn -q --volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id)
-[[ "$("${NSPAWN_ARGS[@]}" cat /proc/self/attr/current | tr -d '\0')" != "$CONTEXT" ]]
-[[ "$("${NSPAWN_ARGS[@]}" --selinux-context="$CONTEXT" cat /proc/self/attr/current | tr -d '\0')" == "$CONTEXT" ]]
-[[ "$("${NSPAWN_ARGS[@]}" stat --printf %C /run)" != "$CONTEXT" ]]
-[[ "$("${NSPAWN_ARGS[@]}" --selinux-apifs-context="$CONTEXT" stat --printf %C /run)" == "$CONTEXT" ]]
-[[ "$("${NSPAWN_ARGS[@]}" --selinux-apifs-context="$CONTEXT" --tmpfs=/tmp stat --printf %C /tmp)" == "$CONTEXT" ]]
-
-touch /testok
diff --git a/test/units/testsuite-07.aux-scope.sh b/test/units/testsuite-07.aux-scope.sh
deleted file mode 100755 (executable)
index 8e2a99c..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-export SYSTEMD_PAGER=cat
-
-if ! grep -q pidfd_open /proc/kallsyms; then
-    echo "pidfds not available, skipping the test..."
-    exit 0
-fi
-
-systemd-run --unit test-aux-scope.service \
-            --service-type notify -p Slice=aux.slice -p TasksMax=99 -p CPUWeight=199 -p IPAccounting=yes \
-            /usr/lib/systemd/tests/unit-tests/manual/test-aux-scope
-kill -s USR1 "$(systemctl show --value --property MainPID test-aux-scope.service)"
-
-timeout 30s bash -xec 'until systemctl is-active test-aux-scope.scope; do sleep 1; done'
-
-systemctl status test-aux-scope.service
-# shellcheck disable=SC2009
-test "$(ps -eo pid,unit | grep -c test-aux-scope.service)" = 1
-
-systemctl status test-aux-scope.scope
-# shellcheck disable=SC2009
-test "$(ps -eo pid,unit | grep -c test-aux-scope.scope)" = 10
-
-test "$(systemctl show -p Slice --value test-aux-scope.scope)" = aux.slice
-test "$(systemctl show -p TasksMax --value test-aux-scope.scope)" = 99
-test "$(systemctl show -p CPUWeight --value test-aux-scope.scope)" = 199
-test "$(systemctl show -p IPAccounting --value test-aux-scope.scope)" = yes
-
-systemctl stop test-aux-scope.scope
-systemctl stop test-aux-scope.service
diff --git a/test/units/testsuite-07.exec-context.sh b/test/units/testsuite-07.exec-context.sh
deleted file mode 100755 (executable)
index a3379ef..0000000
+++ /dev/null
@@ -1,386 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Make sure the unit's exec context matches its configuration
-# See: https://github.com/systemd/systemd/pull/29552
-
-# Even though hidepid= was introduced in kernel 3.3, we support only
-# the post 5.8 implementation that allows us to apply the option per-instance,
-# instead of the whole namespace. To distinguish between these two implementations
-# lets check if we can mount procfs with a named value (e.g. hidepid=off), since
-# support for this was introduced in the same commit as the per-instance stuff
-proc_supports_option() {
-    local option="${1:?}"
-    local proc_tmp ec
-
-    proc_tmp="$(mktemp -d)"
-    mount -t proc -o "$option" proc "$proc_tmp" && ec=0 || ec=$?
-    mountpoint -q "$proc_tmp" && umount -q "$proc_tmp"
-    rm -rf "$proc_tmp"
-
-    return $ec
-}
-
-# In coverage builds we disable ProtectSystem= and ProtectHome= via a service.d
-# dropin in /etc. This dropin has, unfortunately, higher priority than
-# the transient stuff from systemd-run. Let's just skip the following tests
-# in that case instead of complicating the test setup even more */
-if [[ -z "${COVERAGE_BUILD_DIR:-}" ]]; then
-    if ! systemd-detect-virt -cq && command -v bootctl >/dev/null; then
-        boot_path="$(bootctl --print-boot-path)"
-        esp_path="$(bootctl --print-esp-path)"
-
-        # If the mount points are handled by automount units, make sure we trigger
-        # them before proceeding further
-        ls -l "$boot_path" "$esp_path"
-    fi
-
-    systemd-run --wait --pipe -p ProtectSystem=yes \
-        bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test -w /etc; test -w /var"
-    systemd-run --wait --pipe -p ProtectSystem=full \
-        bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test ! -w /etc; test -w /var"
-    systemd-run --wait --pipe -p ProtectSystem=strict \
-        bash -xec "test ! -w /; test ! -w /etc; test ! -w /var; test -w /dev; test -w /proc"
-    systemd-run --wait --pipe -p ProtectSystem=no \
-        bash -xec "test -w /; test -w /etc; test -w /var; test -w /dev; test -w /proc"
-
-    MARK="$(mktemp /root/.exec-context.XXX)"
-    systemd-run --wait --pipe -p ProtectHome=yes \
-        bash -xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test ! -e $MARK"
-    systemd-run --wait --pipe -p ProtectHome=read-only \
-        bash -xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test -e $MARK"
-    systemd-run --wait --pipe -p ProtectHome=tmpfs \
-        bash -xec "test -w /home; test -w /root; test -w /run/user; test ! -e $MARK"
-    systemd-run --wait --pipe -p ProtectHome=no \
-        bash -xec "test -w /home; test -w /root; test -w /run/user; test -e $MARK"
-    rm -f "$MARK"
-fi
-
-if proc_supports_option "hidepid=off"; then
-    systemd-run --wait --pipe -p ProtectProc=noaccess -p User=testuser \
-        bash -xec 'test -e /proc/1; test ! -r /proc/1; test -r /proc/$$$$/comm'
-    systemd-run --wait --pipe -p ProtectProc=invisible -p User=testuser \
-        bash -xec 'test ! -e /proc/1; test -r /proc/$$$$/comm'
-    systemd-run --wait --pipe -p ProtectProc=ptraceable -p User=testuser \
-        bash -xec 'test ! -e /proc/1; test -r /proc/$$$$/comm'
-    systemd-run --wait --pipe -p ProtectProc=ptraceable -p User=testuser -p AmbientCapabilities=CAP_SYS_PTRACE \
-        bash -xec 'test -r /proc/1; test -r /proc/$$$$/comm'
-    systemd-run --wait --pipe -p ProtectProc=default -p User=testuser \
-        bash -xec 'test -r /proc/1; test -r /proc/$$$$/comm'
-fi
-
-if proc_supports_option "subset=pid"; then
-    systemd-run --wait --pipe -p ProcSubset=pid -p User=testuser \
-        bash -xec "test -r /proc/1/comm; test ! -e /proc/cpuinfo"
-    systemd-run --wait --pipe -p ProcSubset=all -p User=testuser \
-        bash -xec "test -r /proc/1/comm; test -r /proc/cpuinfo"
-fi
-
-if ! systemd-detect-virt -cq; then
-    systemd-run --wait --pipe -p ProtectKernelLogs=yes -p User=testuser \
-        bash -xec "test ! -r /dev/kmsg"
-    systemd-run --wait --pipe -p ProtectKernelLogs=no -p User=testuser \
-        bash -xec "test -r /dev/kmsg"
-fi
-
-systemd-run --wait --pipe -p BindPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
-    bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; ! mountpoint /usr"
-systemd-run --wait --pipe -p BindReadOnlyPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
-    bash -xec "test ! -w /etc; test ! -w /mnt; ! mountpoint /usr"
-# Make sure we properly serialize/deserialize paths with spaces
-# See: https://github.com/systemd/systemd/issues/30747
-touch "/tmp/test file with spaces"
-systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces" \
-    bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/test file with spaces'"
-systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces:/tmp/destination\ wi\:th\ spaces" \
-    bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/destination wi:th spaces'"
-
-# Check if we correctly serialize, deserialize, and set directives that
-# have more complex internal handling
-if ! systemd-detect-virt -cq; then
-    # Funny detail: this originally used the underlying rootfs device, but that,
-    # for some reason, caused "divide error" in kernel, followed by a kernel panic
-    TEMPFILE="$(mktemp)"
-    LODEV="$(losetup --show -f "$TEMPFILE")"
-    ROOT_DEV_MAJ_MIN="$(lsblk -nro MAJ:MIN "$LODEV")"
-    EXPECTED_IO_MAX="$ROOT_DEV_MAJ_MIN rbps=1000 wbps=1000000000000 riops=2000000000 wiops=4000"
-    EXPECTED_IO_LATENCY="$ROOT_DEV_MAJ_MIN target=69000"
-    SERVICE_NAME="test-io-directives-$RANDOM.service"
-    CGROUP_PATH="/sys/fs/cgroup/system.slice/$SERVICE_NAME"
-
-    # IO*=
-    ARGUMENTS=(
-        # Throw in a couple of invalid entries just to test things out
-        -p IOReadBandwidthMax="/foo/bar 1M"
-        -p IOReadBandwidthMax="/foo/baz 1M"
-        -p IOReadBandwidthMax="$LODEV 1M"
-        -p IOReadBandwidthMax="$LODEV 1K"
-        -p IOWriteBandwidthMax="$LODEV 1G"
-        -p IOWriteBandwidthMax="$LODEV 1T"
-        -p IOReadIOPSMax="$LODEV 2G"
-        -p IOWriteIOPSMax="$LODEV 4K"
-        -p IODeviceLatencyTargetSec="$LODEV 666ms"
-        -p IODeviceLatencyTargetSec="/foo/bar 69ms"
-        -p IODeviceLatencyTargetSec="$LODEV 69ms"
-        -p IOReadBandwidthMax="/foo/bar 1M"
-        -p IOReadBandwidthMax="/foo/baz 1M"
-        # TODO: IODeviceWeight= doesn't work on loop devices and virtual disks
-        -p IODeviceWeight="$LODEV 999"
-        -p IODeviceWeight="/foo/bar 999"
-    )
-
-    systemctl set-property system.slice IOAccounting=yes
-    # io.latency not available by default on Debian stable
-    if [[ -e /sys/fs/cgroup/system.slice/io.latency ]]; then
-        systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
-            bash -xec "diff <(echo $EXPECTED_IO_MAX) $CGROUP_PATH/io.max; diff <(echo $EXPECTED_IO_LATENCY) $CGROUP_PATH/io.latency"
-    fi
-
-    # CPUScheduling=
-    ARGUMENTS=(
-        -p CPUSchedulingPolicy=rr   # ID: 2
-        -p CPUSchedulingPolicy=fifo # ID: 1
-        -p CPUSchedulingPriority=5  # Actual prio: 94 (99 - prio)
-        -p CPUSchedulingPriority=10 # Actual prio: 89 (99 - prio)
-    )
-
-    systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
-        bash -xec 'grep -E "^policy\s*:\s*1$" /proc/self/sched; grep -E "^prio\s*:\s*89$" /proc/self/sched'
-
-    # Device*=
-    ARGUMENTS=(
-        -p DevicePolicy=closed
-        -p DevicePolicy=strict
-        -p DeviceAllow="char-mem rm"  # Allow read & mknod for /dev/{null,zero,...}
-        -p DeviceAllow="$LODEV rw"
-        -p DeviceAllow="$LODEV w" # Allow write for the loop
-        # Everything else should be disallowed per the strict policy
-    )
-
-    systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
-        bash -xec "test -r /dev/null; test ! -w /dev/null; test ! -r $LODEV; test -w $LODEV; test ! -r /dev/tty; test ! -w /dev/tty"
-
-    if ! systemctl --version | grep -qF -- "-BPF_FRAMEWORK"; then
-        # SocketBind*=
-        ARGUMENTS=(
-            -p SocketBindAllow=
-            -p SocketBindAllow=1234
-            -p SocketBindAllow=ipv4:udp:any
-            -p SocketBindAllow=ipv6:6666
-            # Everything but the last assignment is superfluous, but it still exercises
-            # the parsing machinery
-            -p SocketBindDeny=
-            -p SocketBindDeny=1111
-            -p SocketBindDeny=ipv4:1111
-            -p SocketBindDeny=ipv4:any
-            -p SocketBindDeny=ipv4:tcp:any
-            -p SocketBindDeny=ipv4:udp:10000-11000
-            -p SocketBindDeny=ipv6:1111
-            -p SocketBindDeny=any
-        )
-
-        # We should fail with EPERM when trying to bind to a socket not on the allow list
-        # (nc exits with 2 in that case)
-        systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
-            bash -xec 'timeout 1s nc -l 127.0.0.1 9999; exit 42'
-        systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
-            bash -xec 'timeout 1s nc -l ::1 9999; exit 42'
-        systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
-            bash -xec 'timeout 1s nc -6 -u -l ::1 9999; exit 42'
-        systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
-            bash -xec 'timeout 1s nc -4 -l 127.0.0.1 6666; exit 42'
-        systemd-run --wait -p SuccessExitStatus="1 2" --pipe -p SocketBindDeny=any \
-            bash -xec 'timeout 1s nc -l 127.0.0.1 9999; exit 42'
-        # Consequently, we should succeed when binding to a socket on the allow list
-        # and keep listening on it until we're killed by `timeout` (EC 124)
-        systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
-            bash -xec 'timeout 1s nc -4 -l 127.0.0.1 1234; exit 1'
-        systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
-            bash -xec 'timeout 1s nc -4 -u -l 127.0.0.1 5678; exit 1'
-        systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
-            bash -xec 'timeout 1s nc -6 -l ::1 1234; exit 1'
-        systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
-            bash -xec 'timeout 1s nc -6 -l ::1 6666; exit 1'
-    fi
-
-    losetup -d "$LODEV"
-    rm -f "$TEMPFILE"
-fi
-
-# {Cache,Configuration,Logs,Runtime,State}Directory=
-ARGUMENTS=(
-    -p CacheDirectory="foo/bar/baz also\ with\ spaces"
-    -p CacheDirectory="foo"
-    -p CacheDirectory="context"
-    -p CacheDirectoryMode="0123"
-    -p CacheDirectoryMode="0666"
-    -p ConfigurationDirectory="context/foo also_context/bar context/nested/baz context/semi\:colon"
-    -p ConfigurationDirectoryMode="0400"
-    -p LogsDirectory="context/foo"
-    -p LogsDirectory=""
-    -p LogsDirectory="context/a/very/nested/logs/dir"
-    -p RuntimeDirectory="context/with\ spaces"
-    # Note: {Runtime,State,Cache,Logs}Directory= directives support the directory:symlink syntax, which
-    #       requires an additional level of escaping for the colon character
-    -p RuntimeDirectory="also_context:a\ symlink\ with\ \\\:\ col\\\:ons\ and\ \ spaces"
-    -p RuntimeDirectoryPreserve=yes
-    -p StateDirectory="context"
-    -p StateDirectory="./././././././context context context"
-    -p StateDirectoryMode="0000"
-)
-
-rm -rf /run/context
-systemd-run --wait --pipe "${ARGUMENTS[@]}" \
-    bash -xec '[[ $CACHE_DIRECTORY == "/var/cache/also with spaces:/var/cache/context:/var/cache/foo:/var/cache/foo/bar/baz" ]];
-               [[ $(stat -c "%a" "${CACHE_DIRECTORY##*:}") == 666 ]]'
-systemd-run --wait --pipe "${ARGUMENTS[@]}" \
-    bash -xec '[[ $CONFIGURATION_DIRECTORY == /etc/also_context/bar:/etc/context/foo:/etc/context/nested/baz:/etc/context/semi:colon ]];
-               [[ $(stat -c "%a" "${CONFIGURATION_DIRECTORY%%:*}") == 400 ]]'
-systemd-run --wait --pipe "${ARGUMENTS[@]}" \
-    bash -xec '[[ $LOGS_DIRECTORY == /var/log/context/a/very/nested/logs/dir:/var/log/context/foo ]];
-               [[ $(stat -c "%a" "${LOGS_DIRECTORY##*:}") == 755 ]]'
-systemd-run --wait --pipe "${ARGUMENTS[@]}" \
-    bash -xec '[[ $RUNTIME_DIRECTORY == "/run/also_context:/run/context/with spaces" ]];
-               [[ $(stat -c "%a" "${RUNTIME_DIRECTORY##*:}") == 755 ]];
-               [[ $(stat -c "%a" "${RUNTIME_DIRECTORY%%:*}") == 755 ]]'
-systemd-run --wait --pipe "${ARGUMENTS[@]}" \
-    bash -xec '[[ $STATE_DIRECTORY == /var/lib/context ]]; [[ $(stat -c "%a" $STATE_DIRECTORY) == 0 ]]'
-test -d "/run/context/with spaces"
-test -s "/run/a symlink with : col:ons and  spaces"
-rm -rf /var/{cache,lib,log}/context /etc/{also_,}context
-
-# Limit*=
-#
-# Note: keep limits of LimitDATA= and LimitAS= unlimited, otherwise ASan (LSan)
-# won't be able to mmap the shadow maps
-ARGUMENTS=(
-    -p LimitCPU=15
-    -p LimitCPU=10:15         # ulimit -t
-    -p LimitFSIZE=96G         # ulimit -f
-    -p LimitDATA=8T:infinity
-    -p LimitDATA=infinity     # ulimit -d
-    -p LimitSTACK=8M          # ulimit -s
-    -p LimitCORE=infinity
-    -p LimitCORE=17M          # ulimit -c
-    -p LimitRSS=27G           # ulimit -m
-    -p LimitNOFILE=7:127      # ulimit -n
-    -p LimitAS=infinity       # ulimit -v
-    -p LimitNPROC=1
-    -p LimitNPROC=64:infinity # ulimit -u
-    -p LimitMEMLOCK=37M       # ulimit -l
-    -p LimitLOCKS=19:1021     # ulimit -x
-    -p LimitSIGPENDING=21     # ulimit -i
-    -p LimitMSGQUEUE=666      # ulimit -q
-    -p LimitNICE=4            # ulimit -e
-    -p LimitRTPRIO=8          # ulimit -r
-    -p LimitRTTIME=666666     # ulimit -R
-)
-# Do all the checks in one giant inline shell blob to avoid the overhead of spawning
-# a new service for each check
-#
-# Note: ulimit shows storage-related values in 1024-byte increments*
-# Note2: ulimit -R requires bash >= 5.1
-#
-# * in POSIX mode -c a -f options show values in 512-byte increments; let's hope
-#   we never run in the POSIX mode
-systemd-run --wait --pipe "${ARGUMENTS[@]}" \
-    bash -xec 'KB=1; MB=$((KB * 1024)); GB=$((MB * 1024)); TB=$((GB * 1024));
-               : CPU;        [[ $(ulimit -St) -eq 10 ]];           [[ $(ulimit -Ht) -eq 15 ]];
-               : FSIZE;      [[ $(ulimit -Sf) -eq $((96 * GB)) ]]; [[ $(ulimit -Hf) -eq $((96 * GB)) ]];
-               : DATA;       [[ $(ulimit -Sd) == unlimited  ]];    [[ $(ulimit -Hd) == unlimited ]];
-               : STACK;      [[ $(ulimit -Ss) -eq $((8 * MB)) ]];  [[ $(ulimit -Hs) -eq $((8 * MB)) ]];
-               : CORE;       [[ $(ulimit -Sc) -eq $((17 * MB)) ]]; [[ $(ulimit -Hc) -eq $((17 * MB)) ]];
-               : RSS;        [[ $(ulimit -Sm) -eq $((27 * GB)) ]]; [[ $(ulimit -Hm) -eq $((27 * GB)) ]];
-               : NOFILE;     [[ $(ulimit -Sn) -eq 7 ]];            [[ $(ulimit -Hn) -eq 127 ]];
-               : AS;         [[ $(ulimit -Sv) == unlimited ]];     [[ $(ulimit -Hv) == unlimited ]];
-               : NPROC;      [[ $(ulimit -Su) -eq 64 ]];           [[ $(ulimit -Hu) == unlimited ]];
-               : MEMLOCK;    [[ $(ulimit -Sl) -eq $((37 * MB)) ]]; [[ $(ulimit -Hl) -eq $((37 * MB)) ]];
-               : LOCKS;      [[ $(ulimit -Sx) -eq 19 ]];           [[ $(ulimit -Hx) -eq 1021 ]];
-               : SIGPENDING; [[ $(ulimit -Si) -eq 21 ]];           [[ $(ulimit -Hi) -eq 21 ]];
-               : MSGQUEUE;   [[ $(ulimit -Sq) -eq 666 ]];          [[ $(ulimit -Hq) -eq 666 ]];
-               : NICE;       [[ $(ulimit -Se) -eq 4 ]];            [[ $(ulimit -He) -eq 4 ]];
-               : RTPRIO;     [[ $(ulimit -Sr) -eq 8 ]];            [[ $(ulimit -Hr) -eq 8 ]];
-               ulimit -R || exit 0;
-               : RTTIME;     [[ $(ulimit -SR) -eq 666666 ]];       [[ $(ulimit -HR) -eq 666666 ]];'
-
-# RestrictFileSystems=
-#
-# Note: running instrumented binaries requires at least /proc to be accessible, so let's
-#       skip the test when we're running under sanitizers
-#
-# Note: $GCOV_ERROR_LOG is used during coverage runs to suppress errors when creating *.gcda files,
-#       since gcov can't access the restricted filesystem (as expected)
-if [[ ! -v ASAN_OPTIONS ]] && systemctl --version | grep "+BPF_FRAMEWORK" && kernel_supports_lsm bpf; then
-    ROOTFS="$(df --output=fstype /usr/bin | sed --quiet 2p)"
-    systemd-run --wait --pipe -p RestrictFileSystems="" ls /
-    systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar" ls /
-    (! systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS" ls /proc)
-    (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="foo" ls /)
-    systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar baz proc" ls /proc
-    systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /proc
-    systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /sys/fs/cgroup
-
-    systemd-run --wait --pipe -p RestrictFileSystems="~" ls /
-    systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls /
-    systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls /
-    (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="~$ROOTFS" ls /)
-    (! systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls /proc)
-    (! systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls /proc)
-    (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /proc)
-    (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /sys)
-    systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /
-    (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /proc)
-    (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /dev)
-    (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /sys)
-fi
-
-# Make sure we properly (de)serialize various string arrays, including whitespaces
-# See: https://github.com/systemd/systemd/issues/31214
-systemd-run --wait --pipe -p Environment="FOO='bar4    '" \
-            bash -xec '[[ $FOO == "bar4    " ]]'
-systemd-run --wait --pipe -p Environment="FOO='bar4    ' BAR='\n\n'" \
-            bash -xec "[[ \$FOO == 'bar4    ' && \$BAR == $'\n\n' ]]"
-systemd-run --wait --pipe -p Environment='FOO="bar4  \\  "' -p Environment="BAR='\n\t'" \
-            bash -xec "[[ \$FOO == 'bar4  \\  ' && \$BAR == $'\n\t' ]]"
-TEST_ENV_FILE="/tmp/test-env-file-$RANDOM-    "
-cat >"$TEST_ENV_FILE" <<EOF
-FOO="env file    "
-BAR="
-    "
-EOF
-systemd-run --wait --pipe cat "$TEST_ENV_FILE"
-systemd-run --wait --pipe -p ReadOnlyPaths="'$TEST_ENV_FILE'" \
-            bash -xec '[[ ! -w "$TEST_ENV_FILE" ]]'
-systemd-run --wait --pipe -p PrivateTmp=yes -p BindReadOnlyPaths="'$TEST_ENV_FILE':'/tmp/bar-    '" \
-            bash -xec '[[ -e "/tmp/bar-    " && ! -w "/tmp/bar-    " ]]'
-systemd-run --wait --pipe -p EnvironmentFile="$TEST_ENV_FILE" \
-            bash -xec "[[ \$FOO == 'env file    ' && \$BAR == $'\n    ' ]]"
-rm -f "$TEST_ENV_FILE"
-# manager_serialize()/manager_deserialize() uses similar machinery
-systemctl unset-environment FOO_WITH_SPACES
-systemctl set-environment FOO_WITH_SPACES="foo   " FOO_WITH_TABS="foo\t\t\t"
-systemctl show-environment
-systemctl show-environment | grep -F "FOO_WITH_SPACES=$'foo   '"
-systemctl show-environment | grep -F "FOO_WITH_TABS=$'foo\\\\t\\\\t\\\\t'"
-systemctl daemon-reexec
-systemctl show-environment
-systemctl show-environment | grep -F "FOO_WITH_SPACES=$'foo   '"
-systemctl show-environment | grep -F "FOO_WITH_TABS=$'foo\\\\t\\\\t\\\\t'"
-
-# Ensure that clean-up codepaths work correctly if activation ultimately fails
-touch /run/not-a-directory
-mkdir /tmp/root
-touch /tmp/root/foo
-chmod +x /tmp/root/foo
-(! systemd-run --wait --pipe false)
-(! systemd-run --wait --pipe --unit "test-dynamicuser-fail" -p DynamicUser=yes -p WorkingDirectory=/nonexistent true)
-(! systemd-run --wait --pipe -p RuntimeDirectory=not-a-directory true)
-(! systemd-run --wait --pipe -p RootDirectory=/tmp/root this-shouldnt-exist)
-(! systemd-run --wait --pipe -p RootDirectory=/tmp/root /foo)
-(! systemd-run --wait --pipe --service-type=oneshot -p ExecStartPre=-/foo/bar/baz -p ExecStart=-/foo/bar/baz -p RootDirectory=/tmp/root -- "- foo")
diff --git a/test/units/testsuite-07.exec-deserialization.sh b/test/units/testsuite-07.exec-deserialization.sh
deleted file mode 100755 (executable)
index 04f17c8..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-setup_base_unit() {
-    local unit_path="${1:?}"
-    local log_file="${2:?}"
-    local unit_name="${unit_path##*/}"
-
-    cat >"$unit_path" <<EOF
-[Service]
-Type=oneshot
-ExecStart=sleep 3
-ExecStart=bash -c "echo foo >>$log_file"
-EOF
-    systemctl daemon-reload
-
-    systemctl --job-mode=replace --no-block start "$unit_name"
-    # Wait until the unit leaves the "inactive" state
-    timeout 5s bash -xec "while [[ \"\$(systemctl show -P ActiveState $unit_name)\" == inactive ]]; do sleep .1; done"
-    # Sleep for 1 second from the unit start to get well "into" the first (or second) ExecStart= directive
-    sleep 1
-}
-
-check_output() {
-    local unit_name="${1:?}"
-    local log_file="${2:?}"
-    local expected="${3?}"
-    local unit_name="${unit_path##*/}"
-
-    # Wait until the unit becomes inactive before checking the log
-    timeout 10s bash -xec "while [[ \"\$(systemctl show -P ActiveState $unit_name)\" != inactive ]]; do sleep .5; done"
-
-    diff "$log_file" <(echo -ne "$expected")
-}
-
-testcase_no_change() {
-    local unit_path log_file
-
-    unit_path="$(mktemp /run/systemd/system/test-deserialization-no-change-XXX.service)"
-    log_file="$(mktemp)"
-
-    setup_base_unit "$unit_path" "$log_file"
-
-    # Simple sanity test without any reordering shenanignans, to check if the base unit works as expected.
-    check_output "$unit_path" "$log_file" "foo\n"
-
-    rm -f "$unit_path" "$log_file"
-}
-
-testcase_swapped() {
-    local unit_path log_file
-
-    unit_path="$(mktemp /run/systemd/system/test-deserialization-swapped-XXX.service)"
-    log_file="$(mktemp)"
-
-    setup_base_unit "$unit_path" "$log_file"
-
-    # Swap the two ExecStart= lines.
-    #
-    # Since we should be in the first "sleep" of the base unit, after replacing the unit with the following
-    # one we should continue running from the respective "ExecStart=sleep 3" line, which is now the last
-    # one, resulting no output in the final log file.
-    cat >"$unit_path" <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "echo foo >>$log_file"
-ExecStart=sleep 3
-EOF
-    systemctl daemon-reload
-
-    check_output "$unit_path" "$log_file" ""
-
-    rm -f "$unit_path" "$log_file"
-}
-
-testcase_added_before() {
-    local unit_path log_file
-
-    unit_path="$(mktemp /run/systemd/system/test-deserialization-added-before-XXX.service)"
-    log_file="$(mktemp)"
-
-    setup_base_unit "$unit_path" "$log_file"
-
-    # Add one new ExecStart= before the existing ones.
-    #
-    # Since, after reload, we should continue running from the "sleep 3" statement, the newly added "echo
-    # bar" one will have no effect and we should end up with the same output as in the previous case.
-    cat >"$unit_path" <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "echo bar >>$log_file"
-ExecStart=sleep 3
-ExecStart=bash -c "echo foo >>$log_file"
-EOF
-    systemctl daemon-reload
-
-    check_output "$unit_path" "$log_file" "foo\n"
-
-    rm -f "$unit_path" "$log_file"
-}
-
-testcase_added_after() {
-    local unit_path log_file
-
-    unit_path="$(mktemp /run/systemd/system/test-deserialization-added-after-XXX.service)"
-    log_file="$(mktemp)"
-
-    setup_base_unit "$unit_path" "$log_file"
-
-    # Add an ExecStart= line after the existing ones.
-    #
-    # Same case as above, except the newly added ExecStart= should get executed, as it was added after the
-    # "sleep 3" statement.
-    cat >"$unit_path" <<EOF
-[Service]
-Type=oneshot
-ExecStart=sleep 3
-ExecStart=bash -c "echo foo >>$log_file"
-ExecStart=bash -c "echo bar >>$log_file"
-EOF
-    systemctl daemon-reload
-
-    check_output "$unit_path" "$log_file" "foo\nbar\n"
-
-    rm -f "$unit_path" "$log_file"
-}
-
-testcase_interleaved() {
-    local unit_path log_file
-
-    unit_path="$(mktemp /run/systemd/system/test-deserialization-interleaved-XXX.service)"
-    log_file="$(mktemp)"
-
-    setup_base_unit "$unit_path" "$log_file"
-
-    # Combination of the two previous cases.
-    cat >"$unit_path" <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "echo baz >>$log_file"
-ExecStart=sleep 3
-ExecStart=bash -c "echo foo >>$log_file"
-ExecStart=bash -c "echo bar >>$log_file"
-EOF
-    systemctl daemon-reload
-
-    check_output "$unit_path" "$log_file" "foo\nbar\n"
-
-    rm -f "$unit_path" "$log_file"
-}
-
-testcase_removal() {
-    local unit_path log_file
-
-    unit_path="$(mktemp /run/systemd/system/test-deserialization-removal-XXX.service)"
-    log_file="$(mktemp)"
-
-    setup_base_unit "$unit_path" "$log_file"
-
-    # Remove the currently executed ExecStart= line.
-    #
-    # In this case we completely drop the currently executed "sleep 3" statement, so after reload systemd
-    # should complain that the currently executed command vanished and simply finish executing the unit,
-    # resulting in an empty log.
-    cat >"$unit_path" <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "echo bar >>$log_file"
-ExecStart=bash -c "echo baz >>$log_file"
-EOF
-    systemctl daemon-reload
-
-    check_output "$unit_path" "$log_file" ""
-
-    rm -f "$unit_path" "$log_file"
-}
-
-testcase_issue_6533() {
-    local unit_path unit_name log_file
-
-    unit_path="$(mktemp /run/systemd/system/test-deserialization-issue-6533-XXX.service)"
-    unit_name="${unit_path##*/}"
-    log_file="$(mktemp)"
-
-    cat >"$unit_path" <<EOF
-[Service]
-Type=simple
-ExecStart=/bin/sleep 5
-EOF
-    systemctl daemon-reload
-
-    systemctl --job-mode=replace --no-block start "$unit_name"
-    sleep 2
-
-    # Make sure we try to execute the next command only for oneshot services, as for other types we allow
-    # only one ExecStart= directive.
-    #
-    # See: https://github.com/systemd/systemd/issues/6533
-    cat >"$unit_path" <<EOF
-[Service]
-Type=simple
-ExecStart=/bin/sleep 5
-ExecStart=bash -c "echo foo >>$log_file"
-EOF
-    systemctl daemon-reload
-
-    check_output "$unit_path" "$log_file" ""
-    (! journalctl -b --grep "Freezing execution" _PID=1)
-}
-
-mkdir -p /run/systemd/system/
-run_testcases
-systemctl daemon-reload
diff --git a/test/units/testsuite-07.exec-timestamps.sh b/test/units/testsuite-07.exec-timestamps.sh
deleted file mode 100755 (executable)
index 0211166..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Check that timestamps of a Type=notify service are consistent
-
-systemd-run --service-type notify --property NotifyAccess=all --unit notify.service --wait sh -c 'systemd-notify --ready; exit 1' || :
-
-start=$(systemctl show --property=ExecMainStartTimestampMonotonic --value notify.service)
-handoff=$(systemctl show --property=ExecMainHandoffTimestampMonotonic --value notify.service)
-active=$(systemctl show --property=ActiveEnterTimestampMonotonic --value notify.service)
-exit=$(systemctl show --property=ExecMainExitTimestampMonotonic --value notify.service)
-
-[[ $start -le $handoff ]]
-[[ $handoff -le $active ]]
-[[ $active -le $exit ]]
diff --git a/test/units/testsuite-07.issue-14566.sh b/test/units/testsuite-07.issue-14566.sh
deleted file mode 100755 (executable)
index d4be5b5..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test that KillMode=mixed does not leave left over processes with ExecStopPost=
-# Issue: https://github.com/systemd/systemd/issues/14566
-
-if [[ -n "${ASAN_OPTIONS:-}" ]]; then
-    # Temporarily skip this test when running with sanitizers due to a deadlock
-    # See: https://bugzilla.redhat.com/show_bug.cgi?id=2098125
-    echo "Sanitizers detected, skipping the test..."
-    exit 0
-fi
-
-systemctl start issue14566-repro
-sleep 4
-systemctl status issue14566-repro
-
-leaked_pid=$(cat /leakedtestpid)
-
-systemctl stop issue14566-repro
-sleep 4
-
-# Leaked PID will still be around if we're buggy.
-# I personally prefer to see 42.
-ps -p "$leaked_pid" && exit 42
-
-exit 0
diff --git a/test/units/testsuite-07.issue-16115.sh b/test/units/testsuite-07.issue-16115.sh
deleted file mode 100755 (executable)
index 8f63826..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test ExecCondition= does not restart on abnormal or failure
-# Issue: https://github.com/systemd/systemd/issues/16115
-
-systemctl start issue16115-repro-1
-systemctl start issue16115-repro-2
-systemctl start issue16115-repro-3
-sleep 5 # wait a bit in case there are restarts so we can count them below
-
-[[ "$(systemctl show issue16115-repro-1 -P NRestarts)" == "0" ]]
-[[ "$(systemctl show issue16115-repro-2 -P NRestarts)" == "0" ]]
-[[ "$(systemctl show issue16115-repro-3 -P NRestarts)" == "0" ]]
diff --git a/test/units/testsuite-07.issue-1981.sh b/test/units/testsuite-07.issue-1981.sh
deleted file mode 100755 (executable)
index dcfa9b1..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Segmentation fault in timer_enter_waiting while masking a unit
-# Issue: https://github.com/systemd/systemd/issues/1981
-
-at_exit() {
-    set +e
-
-    systemctl stop my.timer my.service
-    rm -f /run/systemd/system/my.{service,timer}
-    systemctl daemon-reload
-}
-
-trap at_exit EXIT
-
-mkdir -p /run/systemd/system
-
-cat >/run/systemd/system/my.service <<\EOF
-[Service]
-Type=oneshot
-ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = my.timer'
-ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_REALTIME_USEC"'
-ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_MONOTONIC_USEC"'
-ExecStart=echo Timer runs me
-EOF
-
-cat >/run/systemd/system/my.timer <<EOF
-[Timer]
-OnBootSec=10s
-OnUnitInactiveSec=1h
-EOF
-
-systemctl unmask my.timer
-systemctl start my.timer
-
-mkdir -p /run/systemd/system/my.timer.d/
-cat >/run/systemd/system/my.timer.d/override.conf <<EOF
-[Timer]
-OnBootSec=10s
-OnUnitInactiveSec=1h
-EOF
-
-systemctl daemon-reload
-systemctl mask my.timer
diff --git a/test/units/testsuite-07.issue-2467.sh b/test/units/testsuite-07.issue-2467.sh
deleted file mode 100755 (executable)
index de0577b..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Don't start services every few ms if condition fails
-# Issue: https://github.com/systemd/systemd/issues/2467
-
-rm -f /tmp/nonexistent
-systemctl start issue2467.socket
-nc -i20 -w20 -U /run/test.ctl || :
-
-# TriggerLimitIntervalSec= by default is set to 2s. A "sleep 10" should give
-# systemd enough time even on slower machines, to reach the trigger limit.
-# shellcheck disable=SC2016
-timeout 10 bash -c 'until [[ "$(systemctl show issue2467.socket -P ActiveState)" == failed ]]; do sleep .5; done'
-[[ "$(systemctl show issue2467.socket -P Result)" == trigger-limit-hit ]]
diff --git a/test/units/testsuite-07.issue-27953.sh b/test/units/testsuite-07.issue-27953.sh
deleted file mode 100755 (executable)
index 8659970..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Check if the unit doesn't remain in active state after the main PID exits
-# Issue: https://github.com/systemd/systemd/issues/27953
-
-systemctl start issue27953.service
-timeout 10 sh -c 'while systemctl is-active issue27953.service; do sleep .5; done'
-[[ "$(systemctl show -P ExitType issue27953.service)" == main ]]
diff --git a/test/units/testsuite-07.issue-30412.sh b/test/units/testsuite-07.issue-30412.sh
deleted file mode 100755 (executable)
index c1cb00e..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Check that socket FDs are not double closed on error: https://github.com/systemd/systemd/issues/30412
-
-mkdir -p /run/systemd/system
-
-rm -f /tmp/badbin
-touch /tmp/badbin
-chmod 744 /tmp/badbin
-
-cat >/run/systemd/system/badbin_assert.service <<EOF
-[Service]
-ExecStart=/tmp/badbin
-Restart=no
-EOF
-
-cat >/run/systemd/system/badbin_assert.socket <<EOF
-[Socket]
-ListenStream=@badbin_assert.socket
-FlushPending=yes
-EOF
-
-systemctl daemon-reload
-systemctl start badbin_assert.socket
-
-socat - ABSTRACT-CONNECT:badbin_assert.socket
-
-timeout 10 sh -c 'while systemctl is-active badbin_assert.service; do sleep .5; done'
-[[ "$(systemctl show -P ExecMainStatus badbin_assert.service)" == 203 ]]
diff --git a/test/units/testsuite-07.issue-3166.sh b/test/units/testsuite-07.issue-3166.sh
deleted file mode 100755 (executable)
index 6677901..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Service doesn't enter the "failed" state
-# Issue: https://github.com/systemd/systemd/issues/3166
-
-systemctl --no-block start issue3166-fail-on-restart.service
-active_state="$(systemctl show --value --property ActiveState issue3166-fail-on-restart.service)"
-while [[ "$active_state" == "activating" || "$active_state" =~ ^(in)?active$ ]]; do
-    sleep .5
-    active_state="$(systemctl show --value --property ActiveState issue3166-fail-on-restart.service)"
-done
-systemctl is-failed issue3166-fail-on-restart.service || exit 1
-[[ "$(systemctl show --value --property NRestarts issue3166-fail-on-restart.service)" -le 3 ]] || exit 1
diff --git a/test/units/testsuite-07.issue-3171.sh b/test/units/testsuite-07.issue-3171.sh
deleted file mode 100755 (executable)
index 374df54..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# SocketGroup lost on daemon-reload with unit moving away temporarily
-# Issue: https://github.com/systemd/systemd/issues/3171
-
-echo "g adm - - -" | systemd-sysusers -
-
-U=/run/systemd/system/issue-3171.socket
-cat >$U <<EOF
-[Unit]
-Description=Test 12 socket
-[Socket]
-Accept=yes
-ListenStream=/run/issue-3171.socket
-SocketGroup=adm
-SocketMode=0660
-EOF
-
-cat >/run/systemd/system/issue-3171@.service <<EOF
-[Unit]
-Description=Test service
-[Service]
-StandardInput=socket
-ExecStart=sh -x -c cat
-EOF
-
-systemctl start issue-3171.socket
-systemctl is-active issue-3171.socket
-[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
-echo A | nc -w1 -U /run/issue-3171.socket
-
-mv $U ${U}.disabled
-systemctl daemon-reload
-systemctl is-active issue-3171.socket
-[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
-echo B | nc -w1 -U /run/issue-3171.socket && exit 1
-
-mv ${U}.disabled $U
-systemctl daemon-reload
-systemctl is-active issue-3171.socket
-echo C | nc -w1 -U /run/issue-3171.socket && exit 1
-[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
-
-systemctl restart issue-3171.socket
-systemctl is-active issue-3171.socket
-echo D | nc -w1 -U /run/issue-3171.socket
-[[ "$(stat --format='%G' /run/issue-3171.socket)" == adm ]]
diff --git a/test/units/testsuite-07.main-PID-change.sh b/test/units/testsuite-07.main-PID-change.sh
deleted file mode 100755 (executable)
index bd1144c..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test changing the main PID
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# The main service PID should be the parent bash process
-MAINPID="${PPID:?}"
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-# Start a test process inside of our own cgroup
-sleep infinity &
-INTERNALPID=$!
-disown
-
-# Start a test process outside of our own cgroup
-systemd-run -p DynamicUser=1 --unit=test-sleep.service /bin/sleep infinity
-EXTERNALPID="$(systemctl show -P MainPID test-sleep.service)"
-
-# Update our own main PID to the external test PID, this should work
-systemd-notify MAINPID="$EXTERNALPID"
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$EXTERNALPID"
-
-# Update our own main PID to the internal test PID, this should work, too
-systemd-notify MAINPID=$INTERNALPID
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$INTERNALPID"
-
-# Update it back to our own PID, this should also work
-systemd-notify MAINPID="$MAINPID"
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-# Try to set it to PID 1, which it should ignore, because that's the manager
-systemd-notify MAINPID=1
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-# Try to set it to PID 0, which is invalid and should be ignored
-systemd-notify MAINPID=0
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-# Try to set it to a valid but non-existing PID, which should be ignored. (Note
-# that we set the PID to a value well above any known /proc/sys/kernel/pid_max,
-# which means we can be pretty sure it doesn't exist by coincidence)
-systemd-notify MAINPID=1073741824
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-# Change it again to the external PID, without privileges this time. This should be ignored, because the PID is from outside of our cgroup and we lack privileges.
-systemd-notify --uid=1000 MAINPID="$EXTERNALPID"
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-# Change it again to the internal PID, without privileges this time. This should work, as the process is on our cgroup, and that's enough even if we lack privileges.
-systemd-notify --uid=1000 MAINPID="$INTERNALPID"
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$INTERNALPID"
-
-# Update it back to our own PID, this should also work
-systemd-notify --uid=1000 MAINPID="$MAINPID"
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
-
-cat >/tmp/test-mainpid.sh <<\EOF
-#!/usr/bin/env bash
-
-set -eux
-set -o pipefail
-
-# Create a number of children, and make one the main one
-sleep infinity &
-disown
-
-sleep infinity &
-MAINPID=$!
-disown
-
-sleep infinity &
-disown
-
-echo $MAINPID >/run/mainpidsh/pid
-EOF
-chmod +x /tmp/test-mainpid.sh
-
-systemd-run --unit=test-mainpidsh.service \
-            -p StandardOutput=tty \
-            -p StandardError=tty \
-            -p Type=forking \
-            -p RuntimeDirectory=mainpidsh \
-            -p PIDFile=/run/mainpidsh/pid \
-            /tmp/test-mainpid.sh
-test "$(systemctl show -P MainPID test-mainpidsh.service)" -eq "$(cat /run/mainpidsh/pid)"
-
-cat >/tmp/test-mainpid2.sh <<\EOF
-#!/usr/bin/env bash
-
-set -eux
-set -o pipefail
-
-# Create a number of children, and make one the main one
-sleep infinity &
-disown
-
-sleep infinity &
-MAINPID=$!
-disown
-
-sleep infinity &
-disown
-
-echo $MAINPID >/run/mainpidsh2/pid
-chown 1001:1001 /run/mainpidsh2/pid
-EOF
-chmod +x /tmp/test-mainpid2.sh
-
-systemd-run --unit=test-mainpidsh2.service \
-            -p StandardOutput=tty \
-            -p StandardError=tty \
-            -p Type=forking \
-            -p RuntimeDirectory=mainpidsh2 \
-            -p PIDFile=/run/mainpidsh2/pid \
-            /tmp/test-mainpid2.sh
-test "$(systemctl show -P MainPID test-mainpidsh2.service)" -eq "$(cat /run/mainpidsh2/pid)"
-
-cat >/dev/shm/test-mainpid3.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-set -o pipefail
-
-sleep infinity &
-disown
-
-sleep infinity &
-disown
-
-sleep infinity &
-disown
-
-# Let's try to play games, and link up a privileged PID file
-ln -s ../mainpidsh/pid /run/mainpidsh3/pid
-
-# Quick assertion that the link isn't dead
-test -f /run/mainpidsh3/pid
-EOF
-chmod 755 /dev/shm/test-mainpid3.sh
-
-# This has to fail, as we shouldn't accept the dangerous PID file, and then
-# inotify-wait on it to be corrected which we never do.
-(! systemd-run \
-    --unit=test-mainpidsh3.service \
-    -p StandardOutput=tty \
-    -p StandardError=tty \
-    -p Type=forking \
-    -p RuntimeDirectory=mainpidsh3 \
-    -p PIDFile=/run/mainpidsh3/pid \
-    -p DynamicUser=1 \
-    `# Make sanitizers happy when DynamicUser=1 pulls in instrumented systemd NSS modules` \
-    -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
-    -p TimeoutStartSec=2s \
-    /dev/shm/test-mainpid3.sh)
-
-# Test that this failed due to timeout, and not some other error
-test "$(systemctl show -P Result test-mainpidsh3.service)" = timeout
-
-# Test that scope units work
-systemd-run --scope --unit test-true.scope /bin/true
-test "$(systemctl show -P Result test-true.scope)" = success
-
-# Test that user scope units work as well
-
-systemctl start user@4711.service
-runas testuser systemd-run --scope --user --unit test-true.scope /bin/true
-test "$(systemctl show -P Result test-true.scope)" = success
diff --git a/test/units/testsuite-07.mount-invalid-chars.sh b/test/units/testsuite-07.mount-invalid-chars.sh
deleted file mode 100755 (executable)
index a879334..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Don't send invalid characters over dbus if a mount contains them
-
-at_exit() {
-    mountpoint -q /proc/1/mountinfo && umount /proc/1/mountinfo
-    [[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab.bak /etc/fstab
-    rm -f /run/systemd/system/foo-*.mount
-    systemctl daemon-reload
-}
-
-trap at_exit EXIT
-
-# Check invalid characters directly in /proc/mountinfo
-#
-# This is a bit tricky (and hacky), since we have to temporarily replace
-# PID 1's /proc/mountinfo, but we have to keep the original mounts intact,
-# otherwise systemd would unmount them on reload
-TMP_MOUNTINFO="$(mktemp)"
-
-cp /proc/1/mountinfo "$TMP_MOUNTINFO"
-# Add a mount entry with a "Unicode non-character" in it
-LANG="C.UTF-8" printf '69 1 252:2 / /foo/mountinfo rw,relatime shared:1 - cifs //foo\ufffebar rw,seclabel\n' >>"$TMP_MOUNTINFO"
-mount --bind "$TMP_MOUNTINFO" /proc/1/mountinfo
-systemctl daemon-reload
-# On affected versions this would throw an error:
-#   Failed to get properties: Bad message
-systemctl status foo-mountinfo.mount
-
-umount /proc/1/mountinfo
-systemctl daemon-reload
-rm -f "$TMP_MOUNTINFO"
-
-# Check invalid characters in a mount unit
-#
-# systemd already handles this and refuses to load the invalid string, e.g.:
-#   foo-fstab.mount:9: String is not UTF-8 clean, ignoring assignment: What=//localhost/foo���bar
-#
-# a) Unit generated from /etc/fstab
-[[ -e /etc/fstab ]] && cp -f /etc/fstab /tmp/fstab.bak
-
-LANG="C.UTF-8" printf '//localhost/foo\ufffebar  /foo/fstab  cifs defaults 0 0\n' >/etc/fstab
-systemctl daemon-reload
-[[ "$(systemctl show -P UnitFileState foo-fstab.mount)" == bad ]]
-
-# b) Unit generated from /etc/fstab (but the invalid character is in options)
-LANG="C.UTF-8" printf '//localhost/foobar  /foo/fstab/opt  cifs nosuid,a\ufffeb,noexec 0 0\n' >/etc/fstab
-systemctl daemon-reload
-[[ "$(systemctl show -P UnitFileState foo-fstab-opt.mount)" == bad ]]
-rm -f /etc/fstab
-
-[[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab.bak /etc/fstab
-systemctl daemon-reload
-
-# c) Mount unit
-mkdir -p /run/systemd/system
-LANG="C.UTF-8" printf '[Mount]\nWhat=//localhost/foo\ufffebar\nWhere=/foo/unit\nType=cifs\nOptions=noexec\n' >/run/systemd/system/foo-unit.mount
-systemctl daemon-reload
-[[ "$(systemctl show -P UnitFileState foo-unit.mount)" == bad ]]
-rm -f /run/systemd/system/foo-unit.mount
-
-# d) Mount unit (but the invalid character is in Options=)
-mkdir -p /run/systemd/system
-LANG="C.UTF-8" printf '[Mount]\nWhat=//localhost/foobar\nWhere=/foo/unit/opt\nType=cifs\nOptions=noexec,a\ufffeb,nosuid\n' >/run/systemd/system/foo-unit-opt.mount
-systemctl daemon-reload
-[[ "$(systemctl show -P UnitFileState foo-unit-opt.mount)" == bad ]]
-rm -f /run/systemd/system/foo-unit-opt.mount
diff --git a/test/units/testsuite-07.poll-limit.sh b/test/units/testsuite-07.poll-limit.sh
deleted file mode 100755 (executable)
index ca988b2..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemd-analyze log-level debug
-
-cat >/run/systemd/system/floodme@.service <<EOF
-[Service]
-ExecStart=true
-EOF
-
-cat >/run/systemd/system/floodme.socket <<EOF
-[Socket]
-ListenStream=/tmp/floodme
-PollLimitIntervalSec=10s
-Accept=yes
-PollLimitBurst=3
-EOF
-
-systemctl daemon-reload
-systemctl start floodme.socket
-
-START=$(date +%s%N)
-
-# Trigger this 100 times in a flood
-for _ in {1..100}; do
-    logger -u /tmp/floodme foo &
-done
-
-# Let some time pass
-sleep 5
-
-END=$(date +%s%N)
-
-PASSED=$((END-START))
-
-# Calculate (round up) how many trigger events could have happened in the passed time
-MAXCOUNT=$(((PASSED+10000000000)*3/10000000000))
-
-# We started 100 connection attempts, but only 3 should have gone through, as per limit
-test "$(systemctl show -P NAccepted floodme.socket)" -le "$MAXCOUNT"
-
-systemctl stop floodme.socket floodme@*.service
-
-rm /run/systemd/system/floodme@.service /run/systemd/system/floodme.socket /tmp/floodme
-
-systemctl daemon-reload
diff --git a/test/units/testsuite-07.pr-31351.sh b/test/units/testsuite-07.pr-31351.sh
deleted file mode 100755 (executable)
index f6911f2..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-cat >/run/systemd/system/nonexistent-execstart-exit-status.service <<EOF
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStart=-/foo/bar/not-exist
-EOF
-
-systemctl start nonexistent-execstart-exit-status.service
-systemctl is-active nonexistent-execstart-exit-status.service
-assert_eq "$(systemctl show nonexistent-execstart-exit-status.service -P Result)" "success"
-(( $(systemctl show nonexistent-execstart-exit-status.service -P ExecMainStatus) > 0 ))
-
-systemctl stop nonexistent-execstart-exit-status.service
-rm /run/systemd/system/nonexistent-execstart-exit-status.service
diff --git a/test/units/testsuite-07.private-network.sh b/test/units/testsuite-07.private-network.sh
deleted file mode 100755 (executable)
index 37658f7..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# For issue https://github.com/systemd/systemd/issues/29526
-systemd-run -p PrivateNetwork=yes --wait /bin/true
diff --git a/test/units/testsuite-07.service b/test/units/testsuite-07.service
deleted file mode 100644 (file)
index 92302bf..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-07-PID1
-
-[Service]
-Type=oneshot
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-NotifyAccess=all
-# Issue: https://github.com/systemd/systemd/issues/2691
-ExecStop=sh -c 'kill -SEGV $$$$'
-RemainAfterExit=yes
-TimeoutStopSec=270s
diff --git a/test/units/testsuite-07.sh b/test/units/testsuite-07.sh
deleted file mode 100755 (executable)
index 873d638..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-# Issue: https://github.com/systemd/systemd/issues/2730
-# See TEST-07-PID1/test.sh for the first "half" of the test
-mountpoint /issue2730
-
-run_subtests
-
-touch /testok
-systemctl --no-block exit 123
diff --git a/test/units/testsuite-07.socket-pass-fds.sh b/test/units/testsuite-07.socket-pass-fds.sh
deleted file mode 100755 (executable)
index a61b1c0..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test PassFileDescriptorsToExec= option in socket units
-
-for u in pass-fds-to-exec-{no,yes}.socket; do
-    systemctl start "$u"
-    systemctl stop "$u"
-done
diff --git a/test/units/testsuite-07.type-exec-parallel.sh b/test/units/testsuite-07.type-exec-parallel.sh
deleted file mode 100755 (executable)
index 30f80c5..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Make sure that we never mistake a process starting but failing quickly for a process failing to start, with Type=exec.
-# See https://github.com/systemd/systemd/pull/30799
-
-seq 25 | xargs -n 1 -P 0 systemd-run -p Type=exec /bin/false
diff --git a/test/units/testsuite-08.service b/test/units/testsuite-08.service
deleted file mode 100644 (file)
index 2db35cf..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-08-INITRD
-After=multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-08.sh b/test/units/testsuite-08.sh
deleted file mode 100755 (executable)
index 58d9bea..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if systemd-detect-virt -qc; then
-    echo >&2 "This test can't run in a container"
-    exit 1
-fi
-
-# This test requires systemd to run in the initrd as well, which is not the case
-# for mkinitrd-based initrd (Ubuntu/Debian)
-if [[ "$(systemctl show -P InitRDTimestampMonotonic)" -eq 0 ]]; then
-    echo "systemd didn't run in the initrd, skipping the test"
-    touch /skipped
-    exit 77
-fi
-
-# We should've created a mount under /run in initrd (see the other half of the test)
-# that should've survived the transition from initrd to the real system
-test -d /run/initrd-mount-target
-mountpoint /run/initrd-mount-target
-[[ -e /run/initrd-mount-target/hello-world ]]
-
-# Copy the prepared shutdown initrd to its intended location. Check the respective
-# test.sh file for details
-mkdir -p /run/initramfs
-cp -r /shutdown-initrd/* /run/initramfs/
-
-touch /testok
diff --git a/test/units/testsuite-09.journal.sh b/test/units/testsuite-09.journal.sh
deleted file mode 100755 (executable)
index 5248d70..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-get_first_boot_id() {
-    journalctl -b "${1:?}" -o json -n +1 | jq -r '._BOOT_ID'
-}
-
-get_last_boot_id() {
-    journalctl -b "${1:?}" -o json -n 1 | jq -r '._BOOT_ID'
-}
-
-get_first_timestamp() {
-    journalctl -b "${1:?}" -o json -n +1 | jq -r '.__REALTIME_TIMESTAMP'
-}
-
-get_last_timestamp() {
-    journalctl -b "${1:?}" -o json -n 1 | jq -r '.__REALTIME_TIMESTAMP'
-}
-
-# Issue: #29275, second part
-# Now let's check if the boot entries are in the correct/expected order
-index=0
-SYSTEMD_LOG_LEVEL=debug journalctl --list-boots
-journalctl --list-boots -o json | jq -r '.[] | [.index, .boot_id, .first_entry, .last_entry] | @tsv' |
-    while read -r offset boot_id first_ts last_ts; do
-        : "Boot #$((++index)) ($offset) with ID $boot_id"
-
-        # Try the "regular" (non-json) variants first, as they provide a helpful
-        # error message if something is not right
-        SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$index"
-        SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$offset"
-        SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$boot_id"
-
-        # Check the boot ID of the first entry
-        entry_boot_id="$(get_first_boot_id "$index")"
-        assert_eq "$entry_boot_id" "$boot_id"
-        entry_boot_id="$(get_first_boot_id "$offset")"
-        assert_eq "$entry_boot_id" "$boot_id"
-        entry_boot_id="$(get_first_boot_id "$boot_id")"
-        assert_eq "$entry_boot_id" "$boot_id"
-
-        # Check the timestamp of the first entry
-        entry_ts="$(get_first_timestamp "$index")"
-        assert_eq "$entry_ts" "$first_ts"
-        entry_ts="$(get_first_timestamp "$offset")"
-        assert_eq "$entry_ts" "$first_ts"
-        entry_ts="$(get_first_timestamp "$boot_id")"
-        assert_eq "$entry_ts" "$first_ts"
-
-        # Check the boot ID of the last entry
-        entry_boot_id="$(get_last_boot_id "$index")"
-        assert_eq "$entry_boot_id" "$boot_id"
-        entry_boot_id="$(get_last_boot_id "$offset")"
-        assert_eq "$entry_boot_id" "$boot_id"
-        entry_boot_id="$(get_last_boot_id "$boot_id")"
-        assert_eq "$entry_boot_id" "$boot_id"
-
-        # Check the timestamp of the last entry
-        if [[ "$offset" != "0" ]]; then
-            entry_ts="$(get_last_timestamp "$index")"
-            assert_eq "$entry_ts" "$last_ts"
-            entry_ts="$(get_last_timestamp "$offset")"
-            assert_eq "$entry_ts" "$last_ts"
-            entry_ts="$(get_last_timestamp "$boot_id")"
-            assert_eq "$entry_ts" "$last_ts"
-        fi
-    done
-
-verify_seqnum() {
-    if [[ "$REBOOT_COUNT" -ne "$NUM_REBOOT" ]]; then
-        return 0
-    fi
-
-    journalctl --flush
-    journalctl --sync
-
-    ls -lR /var/log/journal/
-    ls -lR /run/log/journal/
-
-    journalctl --system --header
-
-    (! journalctl --system -q -o short-monotonic -u systemd-journald.service --grep 'Journal file uses a different sequence number ID, rotating')
-
-    set +x
-    previous_seqnum=0
-    previous_seqnum_id=
-    previous_boot_id=
-    journalctl --system -q -o json | jq -r '[.__SEQNUM, .__SEQNUM_ID, ._BOOT_ID] | @tsv' |
-        while read -r seqnum seqnum_id boot_id; do
-
-            if [[ -n "$previous_seqnum_id" ]]; then
-                if ! test "$seqnum" -gt "$previous_seqnum"; then
-                    echo "seqnum=$seqnum is not greater than previous_seqnum=$previous_seqnum"
-                    echo "seqnum_id=$seqnum_id, previous_seqnum_id=$previous_seqnum_id"
-                    echo "boot_id=$boot_id, previous_boot_id=$previous_boot_id"
-                    return 1
-                fi
-
-                assert_eq "$seqnum_id" "$previous_seqnum_id"
-            fi
-
-            previous_seqnum="$seqnum"
-            previous_seqnum_id="$seqnum_id"
-            previous_boot_id="$boot_id"
-        done
-    set -x
-
-    return 0
-}
-
-verify_seqnum
diff --git a/test/units/testsuite-09.service b/test/units/testsuite-09.service
deleted file mode 100644 (file)
index 6c957ec..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-09-REBOOT
-After=multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-09.sh b/test/units/testsuite-09.sh
deleted file mode 100755 (executable)
index 85630b6..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-export NUM_REBOOT=4
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-systemd-cat echo "Reboot count: $REBOOT_COUNT"
-systemd-cat journalctl --list-boots
-
-run_subtests
-
-if [[ "$REBOOT_COUNT" -lt "$NUM_REBOOT" ]]; then
-    systemctl_final reboot
-elif [[ "$REBOOT_COUNT" -gt "$NUM_REBOOT" ]]; then
-    assert_not_reached
-fi
-
-touch /testok
diff --git a/test/units/testsuite-13.importctl.sh b/test/units/testsuite-13.importctl.sh
deleted file mode 100755 (executable)
index a13e3fd..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export PAGER=
-
-at_exit() {
-    set +e
-    umount -l -R /var/lib/confexts
-    rm -f /var/tmp/importtest /var/tmp/importtest2 /var/tmp/importtest.tar.gz /var/tmp/importtest2.tar.gz
-}
-
-trap at_exit EXIT
-
-systemctl service-log-level systemd-importd debug
-
-# Mount tmpfs over /var/lib/confexts to not pollute the image
-mkdir -p /var/lib/confexts
-mount -t tmpfs tmpfs /var/lib/confexts -o mode=755
-
-importctl
-importctl --no-pager --help
-importctl --version
-importctl list-transfers
-importctl list-transfers --no-legend --no-ask-password
-importctl list-transfers -j
-importctl list-images
-importctl list-images --no-legend --no-ask-password
-importctl list-images -j
-
-(! importctl cancel-transfer 4711)
-
-dd if=/dev/urandom of=/var/tmp/importtest bs=4096 count=10
-
-importctl import-raw --class=confext /var/tmp/importtest
-cmp /var/tmp/importtest /var/lib/confexts/importtest.raw
-importctl export-raw --class=confext importtest /var/tmp/importtest2
-cmp /var/tmp/importtest /var/tmp/importtest2
-
-(! importctl pull-raw --class=confext file:///var/tmp/importtest)
-importctl pull-raw --verify=no --class=confext file:///var/tmp/importtest importtest3
-cmp /var/tmp/importtest /var/lib/confexts/importtest3.raw
-
-tar czf /var/tmp/importtest.tar.gz -C /var/tmp importtest
-
-importctl import-tar --class=confext /var/tmp/importtest.tar.gz importtest4
-cmp /var/tmp/importtest /var/lib/confexts/importtest4/importtest
-
-importctl export-tar --class=confext importtest4 /var/tmp/importtest2.tar.gz
-importctl import-tar --class=confext /var/tmp/importtest2.tar.gz importtest5
-cmp /var/tmp/importtest /var/lib/confexts/importtest5/importtest
-
-importctl import-fs --class=confext /var/lib/confexts/importtest5 importtest6
-cmp /var/tmp/importtest /var/lib/confexts/importtest6/importtest
-
-(! importctl pull-tar --class=confext file:///var/tmp/importtest.tar.gz importtest7)
-importctl pull-tar --class=confext --verify=no file:///var/tmp/importtest.tar.gz importtest7
-cmp /var/tmp/importtest /var/lib/confexts/importtest7/importtest
-
-importctl list-images
-importctl list-images -j
diff --git a/test/units/testsuite-13.machinectl.sh b/test/units/testsuite-13.machinectl.sh
deleted file mode 100755 (executable)
index 6e2ad16..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export PAGER=
-
-at_exit() {
-    set +e
-
-    machinectl status long-running >/dev/null && machinectl kill --signal=KILL long-running
-    mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done"
-    [[ -n "${NSPAWN_FRAGMENT:-}" ]] && rm -f "/etc/systemd/nspawn/$NSPAWN_FRAGMENT" "/var/lib/machines/$NSPAWN_FRAGMENT"
-    rm -f /run/systemd/nspawn/*.nspawn
-}
-
-trap at_exit EXIT
-
-systemctl service-log-level systemd-machined debug
-systemctl service-log-level systemd-importd debug
-
-# Mount temporary directory over /var/lib/machines to not pollute the image
-mkdir -p /var/lib/machines
-mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
-
-# Create a couple of containers we can refer to in tests
-for i in {0..4}; do
-    create_dummy_container "/var/lib/machines/container$i"
-    machinectl start "container$i"
-done
-# Create one "long running" container with some basic signal handling
-create_dummy_container /var/lib/machines/long-running
-cat >/var/lib/machines/long-running/sbin/init <<\EOF
-#!/usr/bin/bash -x
-
-PID=0
-
-trap "touch /poweroff" RTMIN+4
-trap "touch /reboot" INT
-trap "touch /trap" TRAP
-trap 'kill $PID' EXIT
-
-# We need to wait for the sleep process asynchronously in order to allow
-# bash to process signals
-sleep infinity &
-PID=$!
-while :; do
-    wait || :
-done
-EOF
-machinectl start long-running
-
-machinectl
-machinectl --no-pager --help
-machinectl --version
-machinectl list
-machinectl list --no-legend --no-ask-password
-
-machinectl status long-running long-running long-running
-machinectl status --full long-running
-machinectl status --quiet --lines=1 long-running
-machinectl status --lines=0 --max-addresses=0 long-running
-machinectl status --machine=testuser@.host long-running
-machinectl status --output=help long-running
-while read -r output; do
-    machinectl status --output="$output" long-running
-done < <(machinectl --output=help)
-
-machinectl show
-machinectl show --all
-machinectl show --all --machine=root@
-machinectl show --all --machine=testuser@
-[[ "$(machinectl show --property=PoolPath --value)" == "/var/lib/machines" ]]
-machinectl show long-running
-machinectl show long-running long-running long-running --all
-[[ "$(machinectl show --property=RootDirectory --value long-running)" == "/var/lib/machines/long-running" ]]
-
-machinectl enable long-running
-test -L /etc/systemd/system/machines.target.wants/systemd-nspawn@long-running.service
-machinectl enable long-running long-running long-running container1
-machinectl disable long-running
-test ! -L /etc/systemd/system/machines.target.wants/systemd-nspawn@long-running.service
-machinectl disable long-running long-running long-running container1
-
-[[ "$(machinectl shell testuser@ /usr/bin/bash -c 'echo -ne $FOO')" == "" ]]
-[[ "$(machinectl shell --setenv=FOO=bar testuser@ /usr/bin/bash -c 'echo -ne $FOO')" == "bar" ]]
-
-[[ "$(machinectl show --property=State --value long-running)" == "running" ]]
-# Equivalent to machinectl kill --signal=SIGRTMIN+4 --kill-whom=leader
-rm -f /var/lib/machines/long-running/poweroff
-machinectl poweroff long-running
-timeout 10 bash -c "until test -e /var/lib/machines/long-running/poweroff; do sleep .5; done"
-# Equivalent to machinectl kill --signal=SIGINT --kill-whom=leader
-rm -f /var/lib/machines/long-running/reboot
-machinectl reboot long-running
-timeout 10 bash -c "until test -e /var/lib/machines/long-running/reboot; do sleep .5; done"
-# Skip machinectl terminate for now, as it doesn't play well with our "init"
-rm -f /var/lib/machines/long-running/trap
-machinectl kill --signal=SIGTRAP --kill-whom=leader long-running
-timeout 10 bash -c "until test -e /var/lib/machines/long-running/trap; do sleep .5; done"
-# Multiple machines at once
-machinectl poweroff long-running long-running long-running
-machinectl reboot long-running long-running long-running
-machinectl kill --signal=SIGTRAP --kill-whom=leader long-running long-running long-running
-# All used signals should've been caught by a handler
-[[ "$(machinectl show --property=State --value long-running)" == "running" ]]
-
-cp /etc/machine-id /tmp/foo
-machinectl copy-to long-running /tmp/foo /root/foo
-test -f /var/lib/machines/long-running/root/foo
-machinectl copy-from long-running /root/foo /tmp/bar
-diff /tmp/foo /tmp/bar
-rm -f /tmp/{foo,bar}
-
-# machinectl bind is covered by testcase_check_machinectl_bind() in nspawn tests
-
-machinectl list-images
-machinectl list-images --no-legend
-machinectl image-status
-machinectl image-status container1
-machinectl image-status container1 container1 container{0..4}
-machinectl show-image
-machinectl show-image container1
-machinectl show-image container1 container1 container{0..4}
-
-machinectl clone container1 clone1
-machinectl show-image clone1
-machinectl rename clone1 clone2
-(! machinectl show-image clone1)
-machinectl show-image clone2
-if lsattr -d /var/lib/machines >/dev/null; then
-    [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]]
-    machinectl read-only clone2 yes
-    [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == yes ]]
-    machinectl read-only clone2 no
-    [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]]
-fi
-machinectl remove clone2
-for i in {0..4}; do
-    machinectl clone container1 "clone$i"
-done
-machinectl remove clone{0..4}
-for i in {0..4}; do
-    machinectl clone container1 ".hidden$i"
-done
-machinectl list-images --all
-test -d /var/lib/machines/.hidden1
-machinectl clean
-test ! -d /var/lib/machines/.hidden1
-
-# Prepare a simple raw container
-mkdir -p /tmp/mnt
-dd if=/dev/zero of=/var/tmp/container.raw bs=1M count=256
-mkfs.ext4 /var/tmp/container.raw
-mount -o loop /var/tmp/container.raw /tmp/mnt
-cp -r /var/lib/machines/container1/* /tmp/mnt
-umount /tmp/mnt
-# Try to import it, run it, export it, and re-import it
-machinectl import-raw /var/tmp/container.raw container-raw
-[[ "$(machinectl show-image --property=Type --value container-raw)" == "raw" ]]
-machinectl start container-raw
-machinectl export-raw container-raw /var/tmp/container-export.raw
-machinectl import-raw /var/tmp/container-export.raw container-raw-reimport
-[[ "$(machinectl show-image --property=Type --value container-raw-reimport)" == "raw" ]]
-rm -f /var/tmp/container{,-export}.raw
-
-# Prepare a simple tar.gz container
-tar -pczf /var/tmp/container.tar.gz -C /var/lib/machines/container1 .
-# Try to import it, run it, export it, and re-import it
-machinectl import-tar /var/tmp/container.tar.gz container-tar
-[[ "$(machinectl show-image --property=Type --value container-tar)" =~ directory|subvolume ]]
-machinectl start container-tar
-machinectl export-tar container-tar /var/tmp/container-export.tar.gz
-machinectl import-tar /var/tmp/container-export.tar.gz container-tar-reimport
-[[ "$(machinectl show-image --property=Type --value container-tar-reimport)" =~ directory|subvolume ]]
-rm -f /var/tmp/container{,-export}.tar.gz
-
-# Try to import a container directory & run it
-cp -r /var/lib/machines/container1 /var/tmp/container.dir
-machinectl import-fs /var/tmp/container.dir container-dir
-[[ "$(machinectl show-image --property=Type --value container-dir)" =~ directory|subvolume ]]
-machinectl start container-dir
-rm -fr /var/tmp/container.dir
-
-timeout 10 bash -c "until machinectl clean --all; do sleep .5; done"
-
-NSPAWN_FRAGMENT="machinectl-test-$RANDOM.nspawn"
-cat >"/var/lib/machines/$NSPAWN_FRAGMENT" <<EOF
-[Exec]
-Boot=true
-EOF
-machinectl cat "$NSPAWN_FRAGMENT"
-EDITOR=true script -qec "machinectl edit $NSPAWN_FRAGMENT" /dev/null
-test -f "/etc/systemd/nspawn/$NSPAWN_FRAGMENT"
-diff "/var/lib/machines/$NSPAWN_FRAGMENT" "/etc/systemd/nspawn/$NSPAWN_FRAGMENT"
-
-cat >/tmp/fragment.nspawn <<EOF
-[Exec]
-Boot=false
-EOF
-machinectl cat /tmp/fragment.nspawn
-EDITOR="cp /tmp/fragment.nspawn" script -qec "machinectl edit $NSPAWN_FRAGMENT" /dev/null
-diff /tmp/fragment.nspawn "/etc/systemd/nspawn/$NSPAWN_FRAGMENT"
-
-for opt in format lines machine max-addresses output setenv verify; do
-    (! machinectl status "--$opt=" long-running)
-    (! machinectl status "--$opt=-1" long-running)
-    (! machinectl status "--$opt=''" long-running)
-done
-(! machinectl show "")
-(! machinectl enable)
-(! machinectl enable "")
-(! machinectl disable)
-(! machinectl disable "")
-(! machinectl read-only container1 "")
-(! machinectl read-only container1 foo)
-(! machinectl read-only container1 -- -1)
diff --git a/test/units/testsuite-13.nspawn-oci.sh b/test/units/testsuite-13.nspawn-oci.sh
deleted file mode 100755 (executable)
index b8a76fa..0000000
+++ /dev/null
@@ -1,467 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-export SYSTEMD_LOG_TARGET=journal
-
-# shellcheck disable=SC2317
-at_exit() {
-    set +e
-
-    mountpoint -q /var/lib/machines && umount /var/lib/machines
-    [[ -n "${DEV:-}" ]] && rm -f "$DEV"
-    [[ -n "${NETNS:-}" ]] && umount "$NETNS" && rm -f "$NETNS"
-    [[ -n "${TMPDIR:-}" ]] && rm -fr "$TMPDIR"
-    rm -f /run/systemd/nspawn/*.nspawn
-}
-
-trap at_exit EXIT
-
-# Mount temporary directory over /var/lib/machines to not pollute the image
-mkdir -p /var/lib/machines
-mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
-
-# Setup a couple of dirs/devices for the OCI containers
-DEV="$(mktemp -u /dev/oci-dev-XXX)"
-mknod -m 666 "$DEV" b 42 42
-NETNS="$(mktemp /var/tmp/netns.XXX)"
-mount --bind /proc/self/ns/net "$NETNS"
-TMPDIR="$(mktemp -d)"
-touch "$TMPDIR/hello"
-OCI="$(mktemp -d /var/lib/machines/testsuite-13.oci-bundle.XXX)"
-create_dummy_container "$OCI/rootfs"
-mkdir -p "$OCI/rootfs/opt/var"
-mkdir -p "$OCI/rootfs/opt/readonly"
-
-# Let's start with a simple config
-cat >"$OCI/config.json" <<EOF
-{
-    "ociVersion" : "1.0.0",
-    "root" : {
-            "path" : "rootfs"
-    },
-    "mounts" : [
-        {
-            "destination" : "/root",
-            "type" : "tmpfs",
-            "source" : "tmpfs"
-        }
-    ]
-}
-EOF
-systemd-nspawn --oci-bundle="$OCI" bash -xec 'mountpoint /root'
-
-# And now for something a bit more involved
-# Notes:
-#   - the hooks are parsed & processed, but never executed
-#   - set sysctl's are parsed but never used?
-#       - same goes for arg_sysctl in nspawn.c
-cat >"$OCI/config.json" <<EOF
-{
-    "ociVersion" : "1.0.0",
-    "hostname" : "my-oci-container",
-    "root" : {
-            "path" : "rootfs",
-            "readonly" : false
-    },
-    "mounts" : [
-        {
-            "destination" : "/root",
-            "type" : "tmpfs",
-            "source" : "tmpfs"
-        },
-        ${COVERAGE_BUILD_DIR:+"{ \"destination\" : \"$COVERAGE_BUILD_DIR\" },"}
-        {
-            "destination" : "/var",
-            "type" : "none",
-            "source" : "$TMPDIR",
-            "options" : ["rbind", "rw"]
-        }
-    ],
-    "process" : {
-        "terminal" : false,
-        "consoleSize" : {
-            "height" : 25,
-            "width" : 80
-        },
-        "user" : {
-            "uid" : 0,
-            "gid" : 0,
-            "additionalGids" : [5, 6]
-        },
-        "env" : [
-            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
-            "FOO=bar"
-        ],
-        "cwd" : "/root",
-        "args" : [
-            "bash",
-            "-xe",
-            "/entrypoint.sh"
-        ],
-        "noNewPrivileges" : true,
-        "oomScoreAdj" : 20,
-        "capabilities" : {
-            "bounding" : [
-                "CAP_AUDIT_WRITE",
-                "CAP_KILL",
-                "CAP_NET_BIND_SERVICE"
-            ],
-            "permitted" : [
-                "CAP_AUDIT_WRITE",
-                "CAP_KILL",
-                "CAP_NET_BIND_SERVICE"
-            ],
-            "inheritable" : [
-                "CAP_AUDIT_WRITE",
-                "CAP_KILL",
-                "CAP_NET_BIND_SERVICE"
-            ],
-            "effective" : [
-                "CAP_AUDIT_WRITE",
-                "CAP_KILL"
-            ],
-            "ambient" : [
-                "CAP_NET_BIND_SERVICE"
-            ]
-        },
-        "rlimits" : [
-            {
-                "type" : "RLIMIT_NOFILE",
-                "soft" : 1024,
-                "hard" : 1024
-            },
-            {
-                "type" : "RLIMIT_RTPRIO",
-                "soft" : 5,
-                "hard" : 10
-            }
-        ]
-    },
-    "linux" : {
-        "namespaces" : [
-            {
-                "type" : "mount"
-            },
-            {
-                "type" : "network",
-                "path" : "$NETNS"
-            },
-            {
-                "type" : "pid"
-            },
-            {
-                "type" : "uts"
-            }
-        ],
-        "uidMappings" : [
-            {
-                "containerID" : 0,
-                "hostID" : 1000,
-                "size" : 100
-            }
-        ],
-        "gidMappings" : [
-            {
-                "containerID" : 0,
-                "hostID" : 1000,
-                "size" : 100
-            }
-        ],
-        "devices" : [
-            {
-                "type" : "c",
-                "path" : "/dev/zero",
-                "major" : 1,
-                "minor" : 5,
-                "fileMode" : 444
-            },
-            {
-                "type" : "b",
-                "path" : "$DEV",
-                "major" : 4,
-                "minor" : 2,
-                "fileMode" : 666,
-                "uid" : 0,
-                "gid" : 0
-            }
-        ],
-        "resources" : {
-            "devices" : [
-                {
-                    "allow" : false,
-                    "access" : "m"
-                },
-                {
-                    "allow" : true,
-                    "type" : "b",
-                    "major" : 4,
-                    "minor" : 2,
-                    "access" : "rwm"
-                }
-            ],
-            "memory" : {
-                "limit" : 134217728,
-                "reservation" : 33554432,
-                "swap" : 268435456
-            },
-            "cpu" : {
-                "shares" : 1024,
-                "quota" : 1000000,
-                "period" : 500000,
-                "cpus" : "0-7"
-            },
-            "blockIO" : {
-                "weight" : 10,
-                "weightDevice" : [
-                    {
-                        "major" : 4,
-                        "minor" : 2,
-                        "weight" : 500
-                    }
-                ],
-                "throttleReadBpsDevice" : [
-                    {
-                        "major" : 4,
-                        "minor" : 2,
-                        "rate" : 500
-                    }
-                ],
-                "throttleWriteBpsDevice" : [
-                    {
-                        "major" : 4,
-                        "minor" : 2,
-                        "rate" : 500
-                    }
-                ],
-                "throttleReadIOPSDevice" : [
-                    {
-                        "major" : 4,
-                        "minor" : 2,
-                        "rate" : 500
-                    }
-                ],
-                "throttleWriteIOPSDevice" : [
-                    {
-                        "major" : 4,
-                        "minor" : 2,
-                        "rate" : 500
-                    }
-                ]
-            },
-            "pids" : {
-                "limit" : 1024
-            }
-        },
-        "sysctl" : {
-            "kernel.domainname" : "foo.bar",
-            "vm.swappiness" : "60"
-        },
-        "seccomp" : {
-            "defaultAction" : "SCMP_ACT_ALLOW",
-            "architectures" : [
-                "SCMP_ARCH_ARM",
-                "SCMP_ARCH_X86_64"
-            ],
-            "syscalls" : [
-                {
-                    "names" : [
-                        "lchown",
-                        "chmod"
-                    ],
-                    "action" : "SCMP_ACT_ERRNO",
-                    "args" : [
-                        {
-                            "index" : 0,
-                            "value" : 1,
-                            "op" : "SCMP_CMP_NE"
-                        },
-                        {
-                            "index" : 1,
-                            "value" : 2,
-                            "valueTwo" : 3,
-                            "op" : "SCMP_CMP_MASKED_EQ"
-                        }
-                    ]
-                }
-            ]
-        },
-        "rootfsPropagation" : "shared",
-        "maskedPaths" : [
-            "/proc/kcore",
-            "/root/nonexistent"
-        ],
-        "readonlyPaths" : [
-            "/proc/sys",
-            "/opt/readonly"
-        ]
-    },
-    "hooks" : {
-        "prestart" : [
-            {
-                "path" : "/bin/sh",
-                "args" : [
-                    "-xec",
-                    "echo \$PRESTART_FOO >/prestart"
-                ],
-                "env" : [
-                    "PRESTART_FOO=prestart_bar",
-                    "ALSO_FOO=also_bar"
-                ],
-                "timeout" : 666
-            },
-            {
-                "path" : "/bin/touch",
-                "args" : [
-                    "/tmp/also-prestart"
-                ]
-            }
-        ],
-        "poststart" : [
-            {
-                "path" : "/bin/sh",
-                "args" : [
-                    "touch",
-                    "/poststart"
-                ]
-            }
-        ],
-        "poststop" : [
-            {
-                "path" : "/bin/sh",
-                "args" : [
-                    "touch",
-                    "/poststop"
-                ]
-            }
-        ]
-    },
-    "annotations" : {
-        "hello.world" : "1",
-        "foo" : "bar"
-    }
-}
-EOF
-# Create a simple "entrypoint" script that validates that the container
-# is created correctly according to the OCI config
-cat >"$OCI/rootfs/entrypoint.sh" <<EOF
-#!/usr/bin/bash -e
-
-# Mounts
-mountpoint /root
-mountpoint /var
-test -e /var/hello
-
-# Process
-[[ "\$PWD" == /root ]]
-[[ "\$FOO" == bar ]]
-
-# Process - rlimits
-[[ "\$(ulimit -S -n)" -eq 1024 ]]
-[[ "\$(ulimit -H -n)" -eq 1024 ]]
-[[ "\$(ulimit -S -r)" -eq 5 ]]
-[[ "\$(ulimit -H -r)" -eq 10 ]]
-[[ "\$(hostname)" == my-oci-container ]]
-
-# Linux - devices
-test -c /dev/zero
-test -b "$DEV"
-[[ "\$(stat -c '%t:%T' "$DEV")" == 4:2 ]]
-
-# Linux - maskedPaths
-test -e /proc/kcore
-cat /proc/kcore && exit 1
-test ! -e /root/nonexistent
-
-# Linux - readonlyPaths
-touch /opt/readonly/foo && exit 1
-
-exit 0
-EOF
-timeout 30 systemd-nspawn --oci-bundle="$OCI"
-
-# Test a couple of invalid configs
-INVALID_SNIPPETS=(
-    # Invalid object
-    '"foo" : { }'
-    '"process" : { "foo" : [ ] }'
-    # Non-absolute mount
-    '"mounts" : [ { "destination" : "foo", "type" : "tmpfs", "source" : "tmpfs" } ]'
-    # Invalid rlimit
-    '"process" : { "rlimits" : [ { "type" : "RLIMIT_FOO", "soft" : 0, "hard" : 0 } ] }'
-    # rlimit without RLIMIT_ prefix
-    '"process" : { "rlimits" : [ { "type" : "CORE", "soft" : 0, "hard" : 0 } ] }'
-    # Invalid env assignment
-    '"process" : { "env" : [ "foo" ] }'
-    '"process" : { "env" : [ "foo=bar", 1 ] }'
-    # Invalid process args
-    '"process" : { "args" : [ ] }'
-    '"process" : { "args" : [ "" ] }'
-    '"process" : { "args" : [ "foo", 1 ] }'
-    # Invalid capabilities
-    '"process" : { "capabilities" : { "bounding" : [ 1 ] } }'
-    '"process" : { "capabilities" : { "bounding" : [ "FOO_BAR" ] } }'
-    # Unsupported option (without JSON_PERMISSIVE)
-    '"linux" : { "resources" : { "cpu" : { "realtimeRuntime" : 1 } } }'
-    # Invalid namespace
-    '"linux" : { "namespaces" : [ { "type" : "foo" } ] }'
-    # Namespace path for a non-network namespace
-    '"linux" : { "namespaces" : [ { "type" : "user", "path" : "/foo/bar" } ] }'
-    # Duplicate namespace
-    '"linux" : { "namespaces" : [ { "type" : "ipc" }, { "type" : "ipc" } ] }'
-    # Invalid device type
-    '"linux" : { "devices" : [ { "type" : "foo", "path" : "/dev/foo" } ] }'
-    # Invalid cgroups path
-    '"linux" : { "cgroupsPath" : "/foo/bar/baz" }'
-    '"linux" : { "cgroupsPath" : "foo/bar/baz" }'
-    # Invalid sysctl assignments
-    '"linux" : { "sysctl" : { "vm.swappiness" : 60 } }'
-    '"linux" : { "sysctl" : { "foo..bar" : "baz" } }'
-    # Invalid seccomp assignments
-    '"linux" : { "seccomp" : { } }'
-    '"linux" : { "seccomp" : { "defaultAction" : 1 } }'
-    '"linux" : { "seccomp" : { "defaultAction" : "foo" } }'
-    '"linux" : { "seccomp" : { "defaultAction" : "SCMP_ACT_ALLOW", "syscalls" : [ { "action" : "SCMP_ACT_ERRNO", "names" : [ ] } ] } }'
-    # Invalid masked paths
-    '"linux" : { "maskedPaths" : [ "/foo", 1 ] }'
-    '"linux" : { "maskedPaths" : [ "/foo", "bar" ] }'
-    # Invalid read-only paths
-    '"linux" : { "readonlyPaths" : [ "/foo", 1 ] }'
-    '"linux" : { "readonlyPaths" : [ "/foo", "bar" ] }'
-    # Invalid hooks
-    '"hooks" : { "prestart" : [ { "path" : "/bin/sh", "timeout" : 0 } ] }'
-    # Invalid annotations
-    '"annotations" : { "" : "bar" }'
-    '"annotations" : { "foo" : 1 }'
-)
-
-for snippet in "${INVALID_SNIPPETS[@]}"; do
-    : "Snippet: $snippet"
-    cat >"$OCI/config.json" <<EOF
-{
-    "ociVersion" : "1.0.0",
-    "root" : {
-            "path" : "rootfs"
-    },
-    $snippet
-}
-EOF
-    (! systemd-nspawn --oci-bundle="$OCI" sh -c 'echo hello')
-done
-
-# Invalid OCI bundle version
-cat >"$OCI/config.json" <<EOF
-{
-    "ociVersion" : "6.6.6",
-    "root" : {
-            "path" : "rootfs"
-    }
-}
-EOF
-(! systemd-nspawn --oci-bundle="$OCI" sh -c 'echo hello')
diff --git a/test/units/testsuite-13.nspawn.sh b/test/units/testsuite-13.nspawn.sh
deleted file mode 100755 (executable)
index 026ed23..0000000
+++ /dev/null
@@ -1,977 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-#
-# Notes on coverage: when collecting coverage we need the $BUILD_DIR present
-# and writable in the container as well. To do this in the least intrusive way,
-# two things are going on in the background (only when built with -Db_coverage=true):
-#   1) the systemd-nspawn@.service is copied to /etc/systemd/system/ with
-#      --bind=$BUILD_DIR appended to the ExecStart= line
-#   2) each create_dummy_container() call also creates an .nspawn file in /run/systemd/nspawn/
-#      with the last fragment from the path used as a name
-#
-# The first change is quite self-contained and applies only to containers run
-# with machinectl. The second one might cause some unexpected side-effects, namely:
-#   - nspawn config (setting) files don't support dropins, so tests that test
-#     the config files might need some tweaking (as seen below with
-#     the $COVERAGE_BUILD_DIR shenanigans) since they overwrite the .nspawn file
-#   - also a note - if /etc/systemd/nspawn/cont-name.nspawn exists, it takes
-#     precedence and /run/systemd/nspawn/cont-name.nspawn won't be read even
-#     if it exists
-#   - also a note 2 - --bind= overrides any Bind= from a config file
-#   - in some cases we don't create a test container using create_dummy_container(),
-#     so in that case an explicit call to coverage_create_nspawn_dropin() is needed
-#
-# However, even after jumping through all these hooks, there still might (and is)
-# some "incorrectly" missing coverage, especially in the window between spawning
-# the inner child process and bind-mounting the coverage $BUILD_DIR
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-
-export SYSTEMD_LOG_LEVEL=debug
-export SYSTEMD_LOG_TARGET=journal
-
-at_exit() {
-    set +e
-
-    mountpoint -q /var/lib/machines && umount --recursive /var/lib/machines
-    rm -f /run/systemd/nspawn/*.nspawn
-}
-
-trap at_exit EXIT
-
-# check cgroup-v2
-IS_CGROUPSV2_SUPPORTED=no
-mkdir -p /tmp/cgroup2
-if mount -t cgroup2 cgroup2 /tmp/cgroup2; then
-    IS_CGROUPSV2_SUPPORTED=yes
-    umount /tmp/cgroup2
-fi
-rmdir /tmp/cgroup2
-
-# check cgroup namespaces
-IS_CGNS_SUPPORTED=no
-if [[ -f /proc/1/ns/cgroup ]]; then
-    IS_CGNS_SUPPORTED=yes
-fi
-
-IS_USERNS_SUPPORTED=no
-# On some systems (e.g. CentOS 7) the default limit for user namespaces
-# is set to 0, which causes the following unshare syscall to fail, even
-# with enabled user namespaces support. By setting this value explicitly
-# we can ensure the user namespaces support to be detected correctly.
-sysctl -w user.max_user_namespaces=10000
-if unshare -U bash -c :; then
-    IS_USERNS_SUPPORTED=yes
-fi
-
-# Mount temporary directory over /var/lib/machines to not pollute the image
-mkdir -p /var/lib/machines
-mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
-
-testcase_sanity() {
-    local template root image uuid tmpdir
-
-    tmpdir="$(mktemp -d)"
-    template="$(mktemp -d /tmp/nspawn-template.XXX)"
-    create_dummy_container "$template"
-    # Create a simple image from the just created container template
-    image="$(mktemp /var/lib/machines/testsuite-13.image-XXX.img)"
-    dd if=/dev/zero of="$image" bs=1M count=256
-    mkfs.ext4 "$image"
-    mkdir -p /mnt
-    mount -o loop "$image" /mnt
-    cp -r "$template"/* /mnt/
-    umount /mnt
-
-    systemd-nspawn --help --no-pager
-    systemd-nspawn --version
-
-    # --template=
-    root="$(mktemp -u -d /var/lib/machines/testsuite-13.sanity.XXX)"
-    coverage_create_nspawn_dropin "$root"
-    (! systemd-nspawn --directory="$root" bash -xec 'echo hello')
-    # Initialize $root from $template (the $root directory must not exist, hence
-    # the `mktemp -u` above)
-    systemd-nspawn --directory="$root" --template="$template" bash -xec 'echo hello'
-    systemd-nspawn --directory="$root" bash -xec 'echo hello; touch /initialized'
-    test -e "$root/initialized"
-    # Check if the $root doesn't get re-initialized once it's not empty
-    systemd-nspawn --directory="$root" --template="$template" bash -xec 'echo hello'
-    test -e "$root/initialized"
-
-    systemd-nspawn --directory="$root" --ephemeral bash -xec 'touch /ephemeral'
-    test ! -e "$root/ephemeral"
-    (! systemd-nspawn --directory="$root" \
-                      --read-only \
-                      bash -xec 'touch /nope')
-    test ! -e "$root/nope"
-    systemd-nspawn --image="$image" bash -xec 'echo hello'
-
-    # --volatile=
-    touch "$root/usr/has-usr"
-    # volatile(=yes): rootfs is tmpfs, /usr/ from the OS tree is mounted read only
-    systemd-nspawn --directory="$root"\
-                   --volatile \
-                   bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
-    test ! -e "$root/nope"
-    test ! -e "$root/usr/read-only"
-    systemd-nspawn --directory="$root"\
-                   --volatile=yes \
-                   bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
-    test ! -e "$root/nope"
-    test ! -e "$root/usr/read-only"
-    # volatile=state: rootfs is read-only, /var/ is tmpfs
-    systemd-nspawn --directory="$root" \
-                   --volatile=state \
-                   bash -xec 'test -e /usr/has-usr; mountpoint /var; touch /read-only && exit 1; touch /var/nope'
-    test ! -e "$root/read-only"
-    test ! -e "$root/var/nope"
-    # volatile=state: tmpfs overlay is mounted over rootfs
-    systemd-nspawn --directory="$root" \
-                   --volatile=overlay \
-                   bash -xec 'test -e /usr/has-usr; touch /nope; touch /var/also-nope; touch /usr/nope-too'
-    test ! -e "$root/nope"
-    test ! -e "$root/var/also-nope"
-    test ! -e "$root/usr/nope-too"
-
-    # --machine=, --hostname=
-    systemd-nspawn --directory="$root" \
-                   --machine="foo-bar.baz" \
-                   bash -xec '[[ $(hostname) == foo-bar.baz ]]'
-    systemd-nspawn --directory="$root" \
-                   --hostname="hello.world.tld" \
-                   bash -xec '[[ $(hostname) == hello.world.tld ]]'
-    systemd-nspawn --directory="$root" \
-                   --machine="foo-bar.baz" \
-                   --hostname="hello.world.tld" \
-                   bash -xec '[[ $(hostname) == hello.world.tld ]]'
-
-    # --uuid=
-    rm -f "$root/etc/machine-id"
-    uuid="deadbeef-dead-dead-beef-000000000000"
-    systemd-nspawn --directory="$root" \
-                   --uuid="$uuid" \
-                   bash -xec "[[ \$container_uuid == $uuid ]]"
-
-    # --as-pid2
-    systemd-nspawn --directory="$root" bash -xec '[[ $$ -eq 1 ]]'
-    systemd-nspawn --directory="$root" --as-pid2 bash -xec '[[ $$ -eq 2 ]]'
-
-    # --user=
-    # "Fake" getent passwd's bare minimum, so we don't have to pull it in
-    # with all the DSO shenanigans
-    cat >"$root/bin/getent" <<\EOF
-#!/bin/bash
-
-if [[ $# -eq 0 ]]; then
-    :
-elif [[ $1 == passwd ]]; then
-    echo "testuser:x:1000:1000:testuser:/:/bin/sh"
-elif [[ $1 == initgroups ]]; then
-    echo "testuser"
-fi
-EOF
-    chmod +x "$root/bin/getent"
-    systemd-nspawn --directory="$root" bash -xec '[[ $USER == root ]]'
-    systemd-nspawn --directory="$root" --user=testuser bash -xec '[[ $USER == testuser ]]'
-
-    # --settings= + .nspawn files
-    mkdir -p /run/systemd/nspawn/
-    uuid="deadbeef-dead-dead-beef-000000000000"
-    echo -ne "[Exec]\nMachineID=deadbeef-dead-dead-beef-111111111111" >/run/systemd/nspawn/foo-bar.nspawn
-    systemd-nspawn --directory="$root" \
-                   --machine=foo-bar \
-                   --settings=yes \
-                   bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]'
-    systemd-nspawn --directory="$root" \
-                   --machine=foo-bar \
-                   --uuid="$uuid" \
-                   --settings=yes \
-                   bash -xec "[[ \$container_uuid == $uuid ]]"
-    systemd-nspawn --directory="$root" \
-                   --machine=foo-bar \
-                   --uuid="$uuid" \
-                   --settings=override \
-                   bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]'
-    systemd-nspawn --directory="$root" \
-                   --machine=foo-bar \
-                   --uuid="$uuid" \
-                   --settings=trusted \
-                   bash -xec "[[ \$container_uuid == $uuid ]]"
-
-    # Mounts
-    mkdir "$tmpdir"/{1,2,3}
-    touch "$tmpdir/1/one" "$tmpdir/2/two" "$tmpdir/3/three"
-    touch "$tmpdir/foo"
-    # --bind=
-    systemd-nspawn --directory="$root" \
-                   ${COVERAGE_BUILD_DIR:+--bind="$COVERAGE_BUILD_DIR"} \
-                   --bind="$tmpdir:/foo" \
-                   --bind="$tmpdir:/also-foo:noidmap,norbind" \
-                   bash -xec 'test -e /foo/foo; touch /foo/bar; test -e /also-foo/bar'
-    test -e "$tmpdir/bar"
-    # --bind-ro=
-    systemd-nspawn --directory="$root" \
-                   --bind-ro="$tmpdir:/foo" \
-                   --bind-ro="$tmpdir:/bar:noidmap,norbind" \
-                   bash -xec 'test -e /foo/foo; touch /foo/baz && exit 1; touch /bar && exit 1; true'
-    # --inaccessible=
-    systemd-nspawn --directory="$root" \
-                   --inaccessible=/var \
-                   bash -xec 'touch /var/foo && exit 1; true'
-    # --tmpfs=
-    systemd-nspawn --directory="$root" \
-                   --tmpfs=/var:rw,nosuid,noexec \
-                   bash -xec 'touch /var/nope'
-    test ! -e "$root/var/nope"
-    # --overlay=
-    systemd-nspawn --directory="$root" \
-                   --overlay="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \
-                   bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/foo'
-    test -e "$tmpdir/3/foo"
-    # --overlay-ro=
-    systemd-nspawn --directory="$root" \
-                   --overlay-ro="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \
-                   bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/nope && exit 1; true'
-    test ! -e "$tmpdir/3/nope"
-    rm -fr "$tmpdir"
-
-    # --port (sanity only)
-    systemd-nspawn --network-veth --directory="$root" --port=80 --port=90 true
-    systemd-nspawn --network-veth --directory="$root" --port=80:8080 true
-    systemd-nspawn --network-veth --directory="$root" --port=tcp:80 true
-    systemd-nspawn --network-veth --directory="$root" --port=tcp:80:8080 true
-    systemd-nspawn --network-veth --directory="$root" --port=udp:80 true
-    systemd-nspawn --network-veth --directory="$root" --port=udp:80:8080 --port=tcp:80:8080 true
-    (! systemd-nspawn --network-veth --directory="$root" --port= true)
-    (! systemd-nspawn --network-veth --directory="$root" --port=-1 true)
-    (! systemd-nspawn --network-veth --directory="$root" --port=: true)
-    (! systemd-nspawn --network-veth --directory="$root" --port=icmp:80:8080 true)
-    (! systemd-nspawn --network-veth --directory="$root" --port=tcp::8080 true)
-    (! systemd-nspawn --network-veth --directory="$root" --port=8080: true)
-    # Exercise adding/removing ports from an interface
-    systemd-nspawn --directory="$root" \
-                   --network-veth \
-                   --port=6667 \
-                   --port=80:8080 \
-                   --port=udp:53 \
-                   --port=tcp:22:2222 \
-                   bash -xec 'ip addr add dev host0 10.0.0.10/24; ip a; ip addr del dev host0 10.0.0.10/24'
-
-    # --load-credential=, --set-credential=
-    echo "foo bar" >/tmp/cred.path
-    systemd-nspawn --directory="$root" \
-                   --load-credential=cred.path:/tmp/cred.path \
-                   --set-credential="cred.set:hello world" \
-                   bash -xec '[[ "$(</run/host/credentials/cred.path)" == "foo bar" ]]; [[ "$(</run/host/credentials/cred.set)" == "hello world" ]]'
-    # Combine with --user to ensure creds are still readable
-    systemd-nspawn --directory="$root" \
-                   --user=testuser \
-                   --no-new-privileges=yes \
-                   --load-credential=cred.path:/tmp/cred.path \
-                   --set-credential="cred.set:hello world" \
-                   bash -xec '[[ "$(</run/host/credentials/cred.path)" == "foo bar" ]]; [[ "$(</run/host/credentials/cred.set)" == "hello world" ]]'
-    rm -f /tmp/cred.path
-
-    # Assorted tests
-    systemd-nspawn --directory="$root" --suppress-sync=yes bash -xec 'echo hello'
-    systemd-nspawn --capability=help
-    systemd-nspawn --resolv-conf=help
-    systemd-nspawn --timezone=help
-
-    # Handling of invalid arguments
-    opts=(
-        bind
-        bind-ro
-        bind-user
-        chdir
-        console
-        inaccessible
-        kill-signal
-        link-journal
-        load-credential
-        network-{interface,macvlan,ipvlan,veth-extra,bridge,zone}
-        no-new-privileges
-        oom-score-adjust
-        overlay
-        overlay-ro
-        personality
-        pivot-root
-        port
-        private-users
-        private-users-ownership
-        register
-        resolv-conf
-        rlimit
-        root-hash
-        root-hash-sig
-        set-credential
-        settings
-        suppress-sync
-        timezone
-        tmpfs
-        uuid
-    )
-    for opt in "${opts[@]}"; do
-        (! systemd-nspawn "--$opt")
-        [[ "$opt" == network-zone ]] && continue
-        (! systemd-nspawn "--$opt=''")
-        (! systemd-nspawn "--$opt=%\$š")
-    done
-    (! systemd-nspawn --volatile="")
-    (! systemd-nspawn --volatile=-1)
-    (! systemd-nspawn --rlimit==)
-}
-
-nspawn_settings_cleanup() {
-    for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-ipvlan{1,2}; do
-        ip link del "$dev" || :
-    done
-
-    return 0
-}
-
-testcase_nspawn_settings() {
-    local root container dev private_users wlan_names
-
-    mkdir -p /run/systemd/nspawn
-    root="$(mktemp -d /var/lib/machines/testsuite-13.nspawn-settings.XXX)"
-    container="$(basename "$root")"
-    create_dummy_container "$root"
-    rm -f "/etc/systemd/nspawn/$container.nspawn"
-    mkdir -p "$root/tmp" "$root"/opt/{tmp,inaccessible,also-inaccessible}
-
-    # add virtual wlan interfaces
-    if modprobe mac80211_hwsim radios=2; then
-        wlan_names="wlan0 wlan1:wl-renamed1"
-    fi
-
-    for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do
-        ip link add "$dev" type dummy
-    done
-    udevadm settle
-    ip link property add dev sd-shared3 altname sd-altname3 altname sd-altname-tooooooooooooo-long
-    ip link
-    trap nspawn_settings_cleanup RETURN
-
-    # Let's start with one huge config to test as much as we can at once
-    cat >"/run/systemd/nspawn/$container.nspawn" <<EOF
-[Exec]
-Boot=no
-Ephemeral=no
-ProcessTwo=no
-Parameters=bash /entrypoint.sh "foo bar" 'bar baz'
-Environment=FOO=bar
-Environment=BAZ="hello world"
-User=root
-WorkingDirectory=/tmp
-Capability=CAP_BLOCK_SUSPEND CAP_BPF CAP_CHOWN
-DropCapability=CAP_AUDIT_CONTROL CAP_AUDIT_WRITE
-AmbientCapability=CAP_BPF CAP_CHOWN
-NoNewPrivileges=no
-MachineID=f28f129b51874b1280a89421ec4b4ad4
-PrivateUsers=no
-NotifyReady=no
-SystemCallFilter=@basic-io @chown
-SystemCallFilter=~ @clock
-LimitNOFILE=1024:2048
-LimitRTPRIO=8:16
-OOMScoreAdjust=32
-CPUAffinity=0,0-5,1-5
-Hostname=nspawn-settings
-ResolvConf=copy-host
-Timezone=delete
-LinkJournal=no
-SuppressSync=no
-
-[Files]
-ReadOnly=no
-Volatile=no
-TemporaryFileSystem=/tmp
-TemporaryFileSystem=/opt/tmp
-Inaccessible=/opt/inaccessible
-Inaccessible=/opt/also-inaccessible
-PrivateUsersOwnership=auto
-Overlay=+/var::/var
-${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
-
-[Network]
-Private=yes
-VirtualEthernet=yes
-VirtualEthernetExtra=my-fancy-veth1
-VirtualEthernetExtra=fancy-veth2:my-fancy-veth2
-Interface=sd-shared1 sd-shared2:sd-renamed2 sd-shared3:sd-altname3 ${wlan_names:-}
-MACVLAN=sd-macvlan1 sd-macvlan2:my-macvlan2 sd-macvlanloong
-IPVLAN=sd-ipvlan1 sd-ipvlan2:my-ipvlan2 sd-ipvlanlooong
-Zone=sd-zone0
-Port=80
-Port=81:8181
-Port=tcp:60
-Port=udp:60:61
-EOF
-    cat >"$root/entrypoint.sh" <<\EOF
-#!/bin/bash
-set -ex
-
-env
-
-[[ "$1" == "foo bar" ]]
-[[ "$2" == "bar baz" ]]
-
-[[ "$USER" == root ]]
-[[ "$FOO" == bar ]]
-[[ "$BAZ" == "hello world" ]]
-[[ "$PWD" == /tmp ]]
-[[ "$container_uuid" == f28f129b-5187-4b12-80a8-9421ec4b4ad4 ]]
-[[ "$(ulimit -S -n)" -eq 1024 ]]
-[[ "$(ulimit -H -n)" -eq 2048 ]]
-[[ "$(ulimit -S -r)" -eq 8 ]]
-[[ "$(ulimit -H -r)" -eq 16 ]]
-[[ "$(</proc/self/oom_score_adj)" -eq 32 ]]
-[[ "$(hostname)" == nspawn-settings ]]
-[[ -e /etc/resolv.conf ]]
-[[ ! -e /etc/localtime ]]
-
-mountpoint /tmp
-touch /tmp/foo
-mountpoint /opt/tmp
-touch /opt/tmp/foo
-touch /opt/inaccessible/foo && exit 1
-touch /opt/also-inaccessible/foo && exit 1
-mountpoint /var
-
-ip link
-ip link | grep host-only && exit 1
-ip link | grep host0@
-ip link | grep my-fancy-veth1@
-ip link | grep my-fancy-veth2@
-ip link | grep sd-shared1
-ip link | grep sd-renamed2
-ip link | grep sd-shared3
-ip link | grep sd-altname3
-ip link | grep sd-altname-tooooooooooooo-long
-ip link | grep mv-sd-macvlan1@
-ip link | grep my-macvlan2@
-ip link | grep iv-sd-ipvlan1@
-ip link | grep my-ipvlan2@
-EOF
-    if [[ -n "${wlan_names:-}" ]]; then
-        cat >>"$root/entrypoint.sh" <<\EOF
-ip link | grep wlan0
-ip link | grep wl-renamed1
-EOF
-    fi
-
-    timeout 30 systemd-nspawn --directory="$root"
-
-    # And now for stuff that needs to run separately
-    #
-    # Note on the condition below: since our container tree is owned by root,
-    # both "yes" and "identity" private users settings will behave the same
-    # as PrivateUsers=0:65535, which makes BindUser= fail as the UID already
-    # exists there, so skip setting it in such case
-    for private_users in "131072:65536" yes identity pick; do
-        cat >"/run/systemd/nspawn/$container.nspawn" <<EOF
-[Exec]
-Hostname=private-users
-PrivateUsers=$private_users
-
-[Files]
-PrivateUsersOwnership=auto
-BindUser=
-$([[ "$private_users" =~ (yes|identity) ]] || echo "BindUser=testuser")
-${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
-EOF
-        cat "/run/systemd/nspawn/$container.nspawn"
-        chown -R root:root "$root"
-        systemd-nspawn --directory="$root" bash -xec '[[ "$(hostname)" == private-users ]]'
-    done
-
-    rm -fr "$root" "/run/systemd/nspawn/$container.nspawn"
-}
-
-bind_user_cleanup() {
-    userdel --force --remove nspawn-bind-user-1
-    userdel --force --remove nspawn-bind-user-2
-}
-
-testcase_bind_user() {
-    local root
-
-    root="$(mktemp -d /var/lib/machines/testsuite-13.bind-user.XXX)"
-    create_dummy_container "$root"
-    useradd --create-home --user-group nspawn-bind-user-1
-    useradd --create-home --user-group nspawn-bind-user-2
-    trap bind_user_cleanup RETURN
-    touch /home/nspawn-bind-user-1/foo
-    touch /home/nspawn-bind-user-2/bar
-    # Add a couple of POSIX ACLs to test the patch-uid stuff
-    mkdir -p "$root/opt"
-    setfacl -R -m 'd:u:nspawn-bind-user-1:rwX' -m 'u:nspawn-bind-user-1:rwX' "$root/opt"
-    setfacl -R -m 'd:g:nspawn-bind-user-1:rwX' -m 'g:nspawn-bind-user-1:rwX' "$root/opt"
-
-    systemd-nspawn --directory="$root" \
-                   --private-users=pick \
-                   --bind-user=nspawn-bind-user-1 \
-                   bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo'
-
-    systemd-nspawn --directory="$root" \
-                   --private-users=pick \
-                   --private-users-ownership=chown \
-                   --bind-user=nspawn-bind-user-1 \
-                   --bind-user=nspawn-bind-user-2 \
-                   bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo; test -e /run/host/home/nspawn-bind-user-2/bar'
-    chown -R root:root "$root"
-
-    # User/group name collision
-    echo "nspawn-bind-user-2:x:1000:1000:nspawn-bind-user-2:/home/nspawn-bind-user-2:/bin/bash" >"$root/etc/passwd"
-    (! systemd-nspawn --directory="$root" \
-                      --private-users=pick \
-                      --bind-user=nspawn-bind-user-1 \
-                      --bind-user=nspawn-bind-user-2 \
-                      true)
-    rm -f "$root/etc/passwd"
-
-    echo "nspawn-bind-user-2:x:1000:" >"$root/etc/group"
-    (! systemd-nspawn --directory="$root" \
-                      --private-users=pick \
-                      --bind-user=nspawn-bind-user-1 \
-                      --bind-user=nspawn-bind-user-2 \
-                      true)
-    rm -f "$root/etc/group"
-
-    rm -fr "$root"
-}
-
-testcase_bind_tmp_path() {
-    # https://github.com/systemd/systemd/issues/4789
-    local root
-
-    root="$(mktemp -d /var/lib/machines/testsuite-13.bind-tmp-path.XXX)"
-    create_dummy_container "$root"
-    : >/tmp/bind
-    systemd-nspawn --register=no \
-                   --directory="$root" \
-                   --bind=/tmp/bind \
-                   bash -c 'test -e /tmp/bind'
-
-    rm -fr "$root" /tmp/bind
-}
-
-testcase_norbind() {
-    # https://github.com/systemd/systemd/issues/13170
-    local root
-
-    root="$(mktemp -d /var/lib/machines/testsuite-13.norbind-path.XXX)"
-    mkdir -p /tmp/binddir/subdir
-    echo -n "outer" >/tmp/binddir/subdir/file
-    mount -t tmpfs tmpfs /tmp/binddir/subdir
-    echo -n "inner" >/tmp/binddir/subdir/file
-    create_dummy_container "$root"
-
-    systemd-nspawn --register=no \
-                   --directory="$root" \
-                   --bind=/tmp/binddir:/mnt:norbind \
-                   bash -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; exit 1; fi'
-
-    umount /tmp/binddir/subdir
-    rm -fr "$root" /tmp/binddir/
-}
-
-rootidmap_cleanup() {
-    local dir="${1:?}"
-
-    mountpoint -q "$dir/bind" && umount "$dir/bind"
-    rm -fr "$dir"
-}
-
-testcase_rootidmap() {
-    local root cmd permissions
-    local owner=1000
-
-    root="$(mktemp -d /var/lib/machines/testsuite-13.rootidmap-path.XXX)"
-    # Create ext4 image, as ext4 supports idmapped-mounts.
-    mkdir -p /tmp/rootidmap/bind
-    dd if=/dev/zero of=/tmp/rootidmap/ext4.img bs=4k count=2048
-    mkfs.ext4 /tmp/rootidmap/ext4.img
-    mount /tmp/rootidmap/ext4.img /tmp/rootidmap/bind
-    trap "rootidmap_cleanup /tmp/rootidmap/" RETURN
-
-    touch /tmp/rootidmap/bind/file
-    chown -R "$owner:$owner" /tmp/rootidmap/bind
-
-    create_dummy_container "$root"
-    cmd='PERMISSIONS=$(stat -c "%u:%g" /mnt/file); if [[ $PERMISSIONS != "0:0" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /mnt/other_file'
-    if ! SYSTEMD_LOG_TARGET=console \
-            systemd-nspawn --register=no \
-                           --directory="$root" \
-                           --bind=/tmp/rootidmap/bind:/mnt:rootidmap \
-                           bash -c "$cmd" |& tee nspawn.out; then
-        if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then
-            echo "idmapped mounts are not supported, skipping the test..."
-            return 0
-        fi
-
-        return 1
-    fi
-
-    permissions=$(stat -c "%u:%g" /tmp/rootidmap/bind/other_file)
-    if [[ $permissions != "$owner:$owner" ]]; then
-        echo "*** wrong permissions: $permissions"
-        [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1
-    fi
-}
-
-owneridmap_cleanup() {
-    local dir="${1:?}"
-
-    mountpoint -q "$dir/bind" && umount "$dir/bind"
-    rm -fr "$dir"
-}
-
-testcase_owneridmap() {
-    local root cmd permissions
-    local owner=1000
-
-    root="$(mktemp -d /var/lib/machines/testsuite-13.owneridmap-path.XXX)"
-    # Create ext4 image, as ext4 supports idmapped-mounts.
-    mkdir -p /tmp/owneridmap/bind
-    dd if=/dev/zero of=/tmp/owneridmap/ext4.img bs=4k count=2048
-    mkfs.ext4 /tmp/owneridmap/ext4.img
-    mount /tmp/owneridmap/ext4.img /tmp/owneridmap/bind
-    trap "owneridmap_cleanup /tmp/owneridmap/" RETURN
-
-    touch /tmp/owneridmap/bind/file
-    chown -R "$owner:$owner" /tmp/owneridmap/bind
-
-    # Allow users to read and execute / in order to execute binaries
-    chmod o+rx "$root"
-
-    create_dummy_container "$root"
-
-    # --user=
-    # "Fake" getent passwd's bare minimum, so we don't have to pull it in
-    # with all the DSO shenanigans
-    cat >"$root/bin/getent" <<\EOF
-#!/bin/bash
-
-if [[ $# -eq 0 ]]; then
-    :
-elif [[ $1 == passwd ]]; then
-    echo "testuser:x:1010:1010:testuser:/:/bin/sh"
-elif [[ $1 == initgroups ]]; then
-    echo "testuser"
-fi
-EOF
-    chmod +x "$root/bin/getent"
-
-    mkdir -p "$root/home/testuser"
-    chown 1010:1010 "$root/home/testuser"
-
-    cmd='PERMISSIONS=$(stat -c "%u:%g" /home/testuser/file); if [[ $PERMISSIONS != "1010:1010" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /home/testuser/other_file'
-    if ! SYSTEMD_LOG_TARGET=console \
-            systemd-nspawn --register=no \
-                           --directory="$root" \
-                           -U \
-                           --user=testuser \
-                           --bind=/tmp/owneridmap/bind:/home/testuser:owneridmap \
-                           ${COVERAGE_BUILD_DIR:+--bind="$COVERAGE_BUILD_DIR"} \
-                           /usr/bin/bash -c "$cmd" |& tee nspawn.out; then
-        if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then
-            echo "idmapped mounts are not supported, skipping the test..."
-            return 0
-        fi
-
-        return 1
-    fi
-
-    permissions=$(stat -c "%u:%g" /tmp/owneridmap/bind/other_file)
-    if [[ $permissions != "$owner:$owner" ]]; then
-        echo "*** wrong permissions: $permissions"
-        [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1
-    fi
-}
-
-testcase_notification_socket() {
-    # https://github.com/systemd/systemd/issues/4944
-    local root
-    local cmd='echo a | nc -U -u -w 1 /run/host/notify'
-
-    root="$(mktemp -d /var/lib/machines/testsuite-13.check_notification_socket.XXX)"
-    create_dummy_container "$root"
-
-    systemd-nspawn --register=no --directory="$root" bash -x -c "$cmd"
-    systemd-nspawn --register=no --directory="$root" -U bash -x -c "$cmd"
-
-    rm -fr "$root"
-}
-
-testcase_os_release() {
-    local root entrypoint os_release_source
-
-    root="$(mktemp -d /var/lib/machines/testsuite-13.os-release.XXX)"
-    create_dummy_container "$root"
-    entrypoint="$root/entrypoint.sh"
-    cat >"$entrypoint" <<\EOF
-#!/usr/bin/bash -ex
-
-. /tmp/os-release
-[[ -n "${ID:-}" && "$ID" != "$container_host_id" ]] && exit 1
-[[ -n "${VERSION_ID:-}" && "$VERSION_ID" != "$container_host_version_id" ]] && exit 1
-[[ -n "${BUILD_ID:-}" && "$BUILD_ID" != "$container_host_build_id" ]] && exit 1
-[[ -n "${VARIANT_ID:-}" && "$VARIANT_ID" != "$container_host_variant_id" ]] && exit 1
-
-cd /tmp
-(cd /run/host && md5sum os-release) | md5sum -c
-EOF
-    chmod +x "$entrypoint"
-
-    os_release_source="/etc/os-release"
-    if [[ ! -r "$os_release_source" ]]; then
-        os_release_source="/usr/lib/os-release"
-    elif [[ -L "$os_release_source" ]]; then
-        # Ensure that /etc always wins if available
-        cp --remove-destination -fv /usr/lib/os-release /etc/os-release
-        echo MARKER=1 >>/etc/os-release
-    fi
-
-    systemd-nspawn --register=no \
-                   --directory="$root" \
-                   --bind="$os_release_source:/tmp/os-release" \
-                   "${entrypoint##"$root"}"
-
-    if grep -q MARKER /etc/os-release; then
-        ln -svrf /usr/lib/os-release /etc/os-release
-    fi
-
-    rm -fr "$root"
-}
-
-testcase_machinectl_bind() {
-    local service_path service_name root container_name ec
-    local cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep .5; done; exit 1;'
-
-    root="$(mktemp -d /var/lib/machines/testsuite-13.machinectl-bind.XXX)"
-    create_dummy_container "$root"
-    container_name="$(basename "$root")"
-
-    service_path="$(mktemp /run/systemd/system/nspawn-machinectl-bind-XXX.service)"
-    service_name="${service_path##*/}"
-    cat >"$service_path" <<EOF
-[Service]
-Type=notify
-ExecStart=systemd-nspawn --directory="$root" --notify-ready=no /usr/bin/bash -xec "$cmd"
-EOF
-
-    systemctl daemon-reload
-    systemctl start "$service_name"
-    touch /tmp/marker
-    machinectl bind --mkdir "$container_name" /tmp/marker
-
-    timeout 10 bash -c "while [[ '\$(systemctl show -P SubState $service_name)' == running ]]; do sleep .2; done"
-    ec="$(systemctl show -P ExecMainStatus "$service_name")"
-    systemctl stop "$service_name"
-
-    rm -fr "$root" "$service_path"
-
-    return "$ec"
-}
-
-testcase_selinux() {
-    # Basic test coverage to avoid issues like https://github.com/systemd/systemd/issues/19976
-    if ! command -v selinuxenabled >/dev/null || ! selinuxenabled; then
-        echo >&2 "SELinux is not enabled, skipping SELinux-related tests"
-        return 0
-    fi
-
-    local root
-
-    root="$(mktemp -d /var/lib/machines/testsuite-13.selinux.XXX)"
-    create_dummy_container "$root"
-    chcon -R -t container_t "$root"
-
-    systemd-nspawn --register=no \
-                   --boot \
-                   --directory="$root" \
-                   --selinux-apifs-context=system_u:object_r:container_file_t:s0:c0,c1 \
-                   --selinux-context=system_u:system_r:container_t:s0:c0,c1
-
-    rm -fr "$root"
-}
-
-testcase_ephemeral_config() {
-    # https://github.com/systemd/systemd/issues/13297
-    local root container_name
-
-    root="$(mktemp -d /var/lib/machines/testsuite-13.ephemeral-config.XXX)"
-    create_dummy_container "$root"
-    container_name="$(basename "$root")"
-
-    mkdir -p /run/systemd/nspawn/
-    rm -f "/etc/systemd/nspawn/$container_name.nspawn"
-    cat >"/run/systemd/nspawn/$container_name.nspawn" <<EOF
-[Files]
-${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
-BindReadOnly=/tmp/ephemeral-config
-EOF
-    touch /tmp/ephemeral-config
-
-    systemd-nspawn --register=no \
-                   --directory="$root" \
-                   --ephemeral \
-                   bash -x -c "test -f /tmp/ephemeral-config"
-
-    systemd-nspawn --register=no \
-                   --directory="$root" \
-                   --ephemeral \
-                   --machine=foobar \
-                   bash -x -c "! test -f /tmp/ephemeral-config"
-
-    rm -fr "$root" "/run/systemd/nspawn/$container_name.nspawn"
-}
-
-matrix_run_one() {
-    local cgroupsv2="${1:?}"
-    local use_cgns="${2:?}"
-    local api_vfs_writable="${3:?}"
-    local root
-
-    if [[ "$cgroupsv2" == "yes" && "$IS_CGROUPSV2_SUPPORTED" == "no" ]]; then
-        echo >&2 "Unified cgroup hierarchy is not supported, skipping..."
-        return 0
-    fi
-
-    if [[ "$use_cgns" == "yes" && "$IS_CGNS_SUPPORTED" == "no" ]];  then
-        echo >&2 "CGroup namespaces are not supported, skipping..."
-        return 0
-    fi
-
-    root="$(mktemp -d "/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3.XXX")"
-    create_dummy_container "$root"
-
-    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
-        systemd-nspawn --register=no \
-                       --directory="$root" \
-                       --boot
-
-    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
-        systemd-nspawn --register=no \
-                       --directory="$root" \
-                       --private-network \
-                       --boot
-
-    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
-        systemd-nspawn --register=no \
-                       --directory="$root" \
-                       --private-users=pick \
-                       --boot; then
-        [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "network" ]] && return 1
-    else
-        [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "network" ]] && return 1
-    fi
-
-    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
-        systemd-nspawn --register=no \
-                       --directory="$root" \
-                       --private-network \
-                       --private-users=pick \
-                       --boot; then
-        [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "yes" ]] && return 1
-    else
-        [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "yes" ]] && return 1
-    fi
-
-    local netns_opt="--network-namespace-path=/proc/self/ns/net"
-    local net_opt
-    local net_opts=(
-        "--network-bridge=lo"
-        "--network-interface=lo"
-        "--network-ipvlan=lo"
-        "--network-macvlan=lo"
-        "--network-veth"
-        "--network-veth-extra=lo"
-        "--network-zone=zone"
-    )
-
-    # --network-namespace-path and network-related options cannot be used together
-    for net_opt in "${net_opts[@]}"; do
-        echo "$netns_opt in combination with $net_opt should fail"
-        if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
-            systemd-nspawn --register=no \
-                           --directory="$root" \
-                           --boot \
-                           "$netns_opt" \
-                           "$net_opt"; then
-            echo >&2 "unexpected pass"
-            return 1
-        fi
-    done
-
-    # allow combination of --network-namespace-path and --private-network
-    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
-        systemd-nspawn --register=no \
-                       --directory="$root" \
-                       --boot \
-                       --private-network \
-                       "$netns_opt"
-
-    # test --network-namespace-path works with a network namespace created by "ip netns"
-    ip netns add nspawn_test
-    netns_opt="--network-namespace-path=/run/netns/nspawn_test"
-    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
-        systemd-nspawn --register=no \
-                       --directory="$root" \
-                       --network-namespace-path=/run/netns/nspawn_test \
-                       ip a | grep -v -E '^1: lo.*UP'
-    ip netns del nspawn_test
-
-    rm -fr "$root"
-
-    return 0
-}
-
-testcase_check_os_release() {
-    # https://github.com/systemd/systemd/issues/29185
-    local base common_opts root
-
-    base="$(mktemp -d /var/lib/machines/testsuite-13.check_os_release_base.XXX)"
-    root="$(mktemp -d /var/lib/machines/testsuite-13.check_os_release.XXX)"
-    create_dummy_container "$base"
-    cp -d "$base"/{bin,sbin,lib,lib64} "$root/"
-    common_opts=(
-        --boot
-        --register=no
-        --directory="$root"
-        --bind-ro="$base/usr:/usr"
-    )
-
-    # Empty /etc/ & /usr/
-    (! systemd-nspawn "${common_opts[@]}")
-    (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}")
-    (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=foo systemd-nspawn "${common_opts[@]}")
-    SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}"
-
-    # Empty /usr/ + a broken /etc/os-release -> /usr/os-release symlink
-    ln -svrf "$root/etc/os-release" "$root/usr/os-release"
-    (! systemd-nspawn "${common_opts[@]}")
-    (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}")
-    SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}"
-
-    rm -fr "$root" "$base"
-}
-
-run_testcases
-
-for api_vfs_writable in yes no network; do
-    matrix_run_one no  no  $api_vfs_writable
-    matrix_run_one yes no  $api_vfs_writable
-    matrix_run_one no  yes $api_vfs_writable
-    matrix_run_one yes yes $api_vfs_writable
-done
diff --git a/test/units/testsuite-13.nss-mymachines.sh b/test/units/testsuite-13.nss-mymachines.sh
deleted file mode 100755 (executable)
index 817431b..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-at_exit() {
-    set +e
-
-    machinectl kill --signal=KILL nss-mymachines-{noip,singleip,manyips}
-    mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done"
-    rm -f /run/systemd/nspawn/*.nspawn
-}
-
-trap at_exit EXIT
-
-# Mount temporary directory over /var/lib/machines to not pollute the image
-mkdir -p /var/lib/machines
-mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
-
-# Create a bunch of containers that:
-# 1) Have no IP addresses assigned
-create_dummy_container /var/lib/machines/nss-mymachines-noip
-cat >/var/lib/machines/nss-mymachines-noip/sbin/init <<\EOF
-#!/usr/bin/bash -ex
-
-ip addr show dev ve-noip
-touch /initialized
-sleep infinity &
-# Run the sleep command asynchronously, so bash is able to process signals
-while :; do
-    wait || :
-done
-EOF
-# 2) Have one IP address assigned (IPv4 only)
-create_dummy_container /var/lib/machines/nss-mymachines-singleip
-cat >/var/lib/machines/nss-mymachines-singleip/sbin/init <<\EOF
-#!/usr/bin/bash -ex
-
-ip addr add 10.1.0.2/24 dev ve-singleip
-ip addr show dev ve-singleip
-touch /initialized
-sleep infinity &
-while :; do
-    wait || :
-done
-EOF
-# 3) Have bunch of IP addresses assigned (both IPv4 and IPv6)
-create_dummy_container /var/lib/machines/nss-mymachines-manyips
-cat >/var/lib/machines/nss-mymachines-manyips/sbin/init <<\EOF
-#!/usr/bin/bash -ex
-
-ip addr add 10.2.0.2/24 dev ve-manyips
-for i in {100..120}; do
-    ip addr add 10.2.0.$i/24 dev ve-manyips
-done
-ip addr add fd00:dead:beef:cafe::2/64 dev ve-manyips nodad
-ip addr show dev ve-manyips
-touch /initialized
-sleep infinity
-while :; do
-    wait || :
-done
-EOF
-# Create the respective .nspawn config files
-mkdir -p /run/systemd/nspawn
-for container in noip singleip manyips; do
-    cat >"/run/systemd/nspawn/nss-mymachines-$container.nspawn" <<EOF
-[Exec]
-Boot=yes
-
-[Network]
-VirtualEthernetExtra=ve-$container
-EOF
-done
-
-# Start the containers and wait until all of them are initialized
-machinectl start nss-mymachines-{noip,singleip,manyips}
-for container in nss-mymachines-{noip,singleip,manyips}; do
-    timeout 30 bash -xec "while [[ ! -e /var/lib/machines/$container/initialized ]]; do sleep .5; done"
-done
-
-# We need to configure the dummy interfaces on the "outside" as well for `getent {ahosts4,ahosts6}` to work
-# properly. This is caused by getaddrinfo() calling _check_pf() that iterates through all interfaces and
-# notes if any of them has an IPv4/IPv6 - this is then used together with AF_INET/AF_INET6 to determine if we
-# can ever return a valid answer, and if we configured the container interfaces only in the container, we
-# would have no valid IPv4/IPv6 on the "outside" (as we don't set up any other netdev) which would make
-# getaddrinfo() return EAI_NONAME without ever asking nss-mymachines.
-ip addr add 10.1.0.1/24 dev ve-singleip
-ip addr add 10.2.0.1/24 dev ve-manyips
-ip addr add fd00:dead:beef:cafe::1/64 dev ve-manyips nodad
-
-getent hosts -s mymachines
-getent ahosts -s mymachines
-
-# And finally check if we can resolve the containers via nss-mymachines
-for database in hosts ahosts{,v4,v6}; do
-    (! getent "$database" -s mymachines nss-mymachines-noip)
-done
-
-run_and_grep "^10\.1\.0\.2\s+nss-mymachines-singleip$" getent hosts -s mymachines nss-mymachines-singleip
-run_and_grep "^10\.1\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-singleip
-run_and_grep "^10\.1\.0\.2\s+STREAM" getent ahostsv4 -s mymachines nss-mymachines-singleip
-run_and_grep "^::ffff:10\.1\.0\.2\s+STREAM" getent ahostsv6 -s mymachines nss-mymachines-singleip
-
-run_and_grep "^fd00:dead:beef:cafe::2\s+nss-mymachines-manyips$" getent hosts -s mymachines nss-mymachines-manyips
-run_and_grep "^fd00:dead:beef:cafe::2\s+STREAM" getent ahosts -s mymachines nss-mymachines-manyips
-run_and_grep "^10\.2\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-manyips
-for i in {100..120}; do
-    run_and_grep "^10\.2\.0\.$i\s+STREAM" getent ahosts -s mymachines nss-mymachines-manyips
-    run_and_grep "^10\.2\.0\.$i\s+STREAM" getent ahostsv4 -s mymachines nss-mymachines-manyips
-done
-run_and_grep "^fd00:dead:beef:cafe::2\s+STREAM" getent ahostsv6 -s mymachines nss-mymachines-manyips
-(! run_and_grep "^fd00:" getent ahostsv4 -s mymachines nss-mymachines-manyips)
-(! run_and_grep "^10\.2:" getent ahostsv6 -s mymachines nss-mymachines-manyips)
-
-# Multiple machines at once
-run_and_grep "^10\.1\.0\.2\s+nss-mymachines-singleip$" getent hosts -s mymachines nss-mymachines-{singleip,manyips}
-run_and_grep "^fd00:dead:beef:cafe::2\s+nss-mymachines-manyips$" getent hosts -s mymachines nss-mymachines-{singleip,manyips}
-run_and_grep "^10\.1\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-{singleip,manyips}
-run_and_grep "^10\.2\.0\.2\s+STREAM" getent ahosts -s mymachines nss-mymachines-{singleip,manyips}
-run_and_grep "^fd00:dead:beef:cafe::2\s+STREAM" getent ahosts -s mymachines nss-mymachines-{singleip,manyips}
-
-for database in hosts ahosts ahostsv4 ahostsv6; do
-    (! getent "$database" -s mymachines foo-bar-baz)
-done
-
-# getgrid(), getgrnam(), getpwuid(), and getpwnam() are explicitly handled by nss-mymachines, so probe them
-# as well
-(! getent group -s mymachines foo 11)
-(! getent passwd -s mymachines foo 11)
-
-machinectl stop nss-mymachines-{noip,singleip,manyips}
diff --git a/test/units/testsuite-13.service b/test/units/testsuite-13.service
deleted file mode 100644 (file)
index 95c9a02..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-13-NSPAWN
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-13.sh b/test/units/testsuite-13.sh
deleted file mode 100755 (executable)
index dd7f274..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-FSTYPE="$(stat --file-system --format "%T" /)"
-
-if [[ "$FSTYPE" == "fuseblk" ]]; then
-    echo "Root filesystem is virtiofs, skipping"
-    exit 77
-fi
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
diff --git a/test/units/testsuite-15.service b/test/units/testsuite-15.service
deleted file mode 100644 (file)
index 10af93f..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-15-DROPIN
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-15.sh b/test/units/testsuite-15.sh
deleted file mode 100755 (executable)
index 7143a15..0000000
+++ /dev/null
@@ -1,713 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-clear_unit() {
-    local unit_name="${1:?}"
-    local base suffix
-
-    systemctl stop "$unit_name" 2>/dev/null || :
-    rm -f  /{etc,run,usr/lib}/systemd/system/"$unit_name"
-    rm -fr /{etc,run,usr/lib}/systemd/system/"$unit_name".d
-    rm -fr /{etc,run,usr/lib}/systemd/system/"$unit_name".{wants,requires}
-    if [[ $unit_name == *@* ]]; then
-        base="${unit_name%@*}"
-        suffix="${unit_name##*.}"
-        systemctl stop "$base@"*."$suffix" 2>/dev/null || :
-        rm -f  /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix"
-        rm -fr /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix".d
-        rm -fr /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix".{wants,requires}
-    fi
-}
-
-clear_units() {
-    for u in "$@"; do
-        clear_unit "$u"
-    done
-    systemctl daemon-reload
-}
-
-create_service() {
-    local service_name="${1:?}"
-    clear_units "${service_name}".service
-
-    cat >/etc/systemd/system/"$service_name".service <<EOF
-[Unit]
-Description=$service_name unit
-
-[Service]
-ExecStart=sleep 100000
-EOF
-    mkdir -p /{etc,run,usr/lib}/systemd/system/"$service_name".service.{d,wants,requires}
-}
-
-create_services() {
-    for u in "$@"; do
-        create_service "$u"
-    done
-}
-
-check_ok() {
-    x="$(systemctl show --value -p "${2:?}" "${1:?}")"
-    case "$x" in
-        *${3:?}*) return 0 ;;
-        *)        return 1 ;;
-    esac
-}
-
-check_ko() {
-    ! check_ok "$@"
-}
-
-testcase_basic_dropins() {
-    echo "Testing basic dropins..."
-
-    echo "*** test a wants b wants c"
-    create_services test15-a test15-b test15-c
-    ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/
-    ln -s ../test15-c.service /etc/systemd/system/test15-b.service.wants/
-    check_ok test15-a Wants test15-b.service
-    check_ok test15-b Wants test15-c.service
-
-    echo "*** test a wants,requires b"
-    create_services test15-a test15-b test15-c
-    ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/
-    ln -s ../test15-b.service /etc/systemd/system/test15-a.service.requires/
-    check_ok test15-a Wants test15-b.service
-    check_ok test15-a Requires test15-b.service
-
-    echo "*** test a wants nonexistent"
-    create_service test15-a
-    ln -s ../nonexistent.service /etc/systemd/system/test15-a.service.wants/
-    check_ok test15-a Wants nonexistent.service
-    systemctl start test15-a
-    systemctl stop  test15-a
-
-    echo "*** test a requires nonexistent"
-    ln -sf ../nonexistent.service /etc/systemd/system/test15-a.service.requires/
-    systemctl daemon-reload
-    check_ok test15-a Requires nonexistent.service
-
-    # 'b' is already loaded when 'c' pulls it in via a dropin.
-    echo "*** test a,c require b"
-    create_services test15-a test15-b test15-c
-    ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
-    ln -sf ../test15-b.service /etc/systemd/system/test15-c.service.requires/
-    systemctl start test15-a
-    check_ok test15-c Requires test15-b.service
-    systemctl stop test15-a test15-b
-
-    # 'b'  is already loaded when 'c' pulls it in via an alias dropin.
-    echo "*** test a wants alias"
-    create_services test15-a test15-b test15-c
-    ln -sf test15-c.service /etc/systemd/system/test15-c1.service
-    ln -sf ../test15-c.service  /etc/systemd/system/test15-a.service.wants/
-    ln -sf ../test15-c1.service /etc/systemd/system/test15-b.service.wants/
-    systemctl start test15-a
-    check_ok test15-a Wants test15-c.service
-    check_ok test15-b Wants test15-c.service
-    systemctl stop test15-a test15-c
-
-    echo "*** test service.d/ top level drop-in"
-    create_services test15-a test15-b
-    check_ko test15-a ExecCondition "/bin/echo a"
-    check_ko test15-b ExecCondition "/bin/echo b"
-    mkdir -p /run/systemd/system/service.d
-    cat >/run/systemd/system/service.d/override.conf <<EOF
-[Service]
-ExecCondition=/bin/echo %n
-EOF
-    systemctl daemon-reload
-    check_ok test15-a ExecCondition "/bin/echo test15-a"
-    check_ok test15-b ExecCondition "/bin/echo test15-b"
-    rm -rf /run/systemd/system/service.d
-
-    clear_units test15-{a,b,c,c1}.service
-}
-
-testcase_linked_units() {
-    echo "Testing linked units..."
-    echo "*** test linked unit (same basename)"
-
-    create_service test15-a
-    mv /etc/systemd/system/test15-a.service /
-    ln -s /test15-a.service /etc/systemd/system/
-    ln -s test15-a.service /etc/systemd/system/test15-b.service
-
-    check_ok test15-a Names test15-a.service
-    check_ok test15-a Names test15-b.service
-
-    echo "*** test linked unit (cross basename)"
-
-    mv /test15-a.service /test15-a@.scope
-    ln -fs /test15-a@.scope /etc/systemd/system/test15-a.service
-    systemctl daemon-reload
-
-    check_ok test15-a Names test15-a.service
-    check_ok test15-a Names test15-b.service
-    check_ko test15-a Names test15-a@     # test15-a@.scope is the symlink target.
-                                          # Make sure it is completely ignored.
-
-    rm /test15-a@.scope
-    clear_units test15-{a,b}.service
-}
-
-testcase_template_alias() {
-    echo "Testing instance alias..."
-    echo "*** forward"
-
-    create_service test15-a@
-    ln -s test15-a@inst.service /etc/systemd/system/test15-b@inst.service  # alias
-
-    check_ok test15-a@inst Names test15-a@inst.service
-    check_ok test15-a@inst Names test15-b@inst.service
-
-    check_ok test15-a@other Names test15-a@other.service
-    check_ko test15-a@other Names test15-b@other.service
-
-    echo "*** reverse"
-
-    systemctl daemon-reload
-
-    check_ok test15-b@inst Names test15-a@inst.service
-    check_ok test15-b@inst Names test15-b@inst.service
-
-    check_ko test15-b@other Names test15-a@other.service
-    check_ok test15-b@other Names test15-b@other.service
-
-    clear_units test15-{a,b}@.service
-}
-
-testcase_hierarchical_service_dropins() {
-    echo "Testing hierarchical service dropins..."
-    echo "*** test service.d/ top level drop-in"
-    create_services a-b-c
-    check_ko a-b-c ExecCondition "echo service.d"
-    check_ko a-b-c ExecCondition "echo a-.service.d"
-    check_ko a-b-c ExecCondition "echo a-b-.service.d"
-    check_ko a-b-c ExecCondition "echo a-b-c.service.d"
-
-    for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do
-        mkdir -p "/run/systemd/system/$dropin"
-        cat >"/run/systemd/system/$dropin/override.conf" <<EOF
-[Service]
-ExecCondition=echo $dropin
-EOF
-        systemctl daemon-reload
-        check_ok a-b-c ExecCondition "echo $dropin"
-
-        # Check that we can start a transient service in presence of the drop-ins
-        systemd-run -u a-b-c2.service -p Description='sleepy' sleep infinity
-
-        # The transient setting replaces the default
-        check_ok a-b-c2.service Description "sleepy"
-
-        # The override takes precedence for ExecCondition
-        # (except the last iteration when it only applies to the other service)
-        if [ "$dropin" != "a-b-c.service.d" ]; then
-            check_ok a-b-c2.service ExecCondition "echo $dropin"
-        fi
-
-        # Check that things are the same after a reload
-        systemctl daemon-reload
-        check_ok a-b-c2.service Description "sleepy"
-        if [ "$dropin" != "a-b-c.service.d" ]; then
-            check_ok a-b-c2.service ExecCondition "echo $dropin"
-        fi
-
-        systemctl stop a-b-c2.service
-    done
-    for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do
-        rm -rf "/run/systemd/system/$dropin"
-    done
-
-    clear_units a-b-c.service
-}
-
-testcase_hierarchical_slice_dropins() {
-    echo "Testing hierarchical slice dropins..."
-    echo "*** test slice.d/ top level drop-in"
-    # Slice units don't even need a fragment, so we test the defaults here
-    check_ok a-b-c.slice Description "Slice /a/b/c"
-    check_ok a-b-c.slice MemoryMax "infinity"
-
-    # Test drop-ins
-    for dropin in slice.d a-.slice.d a-b-.slice.d a-b-c.slice.d; do
-        mkdir -p "/run/systemd/system/$dropin"
-        cat >"/run/systemd/system/$dropin/override.conf" <<EOF
-[Slice]
-MemoryMax=1000000000
-EOF
-        systemctl daemon-reload
-        check_ok a-b-c.slice MemoryMax "1000000000"
-
-        busctl call \
-               org.freedesktop.systemd1 \
-               /org/freedesktop/systemd1 \
-               org.freedesktop.systemd1.Manager \
-               StartTransientUnit 'ssa(sv)a(sa(sv))' \
-               'a-b-c.slice' 'replace' \
-               2 \
-               'Description' s 'slice too' \
-               'MemoryMax' t 1000000002 \
-               0
-
-        # The override takes precedence for MemoryMax
-        check_ok a-b-c.slice MemoryMax "1000000000"
-        # The transient setting replaces the default
-        check_ok a-b-c.slice Description "slice too"
-
-        # Check that things are the same after a reload
-        systemctl daemon-reload
-        check_ok a-b-c.slice MemoryMax "1000000000"
-        check_ok a-b-c.slice Description "slice too"
-
-        busctl call \
-               org.freedesktop.systemd1 \
-               /org/freedesktop/systemd1 \
-               org.freedesktop.systemd1.Manager \
-               StopUnit 'ss' \
-               'a-b-c.slice' 'replace'
-
-        rm -f "/run/systemd/system/$dropin/override.conf"
-    done
-
-    # Test unit with a fragment
-    cat >/run/systemd/system/a-b-c.slice <<EOF
-[Slice]
-MemoryMax=1000000001
-EOF
-    systemctl daemon-reload
-    check_ok a-b-c.slice MemoryMax "1000000001"
-
-    clear_units a-b-c.slice
-}
-
-testcase_transient_service_dropins() {
-    echo "Testing dropins for a transient service..."
-    echo "*** test transient service drop-ins"
-
-    mkdir -p /etc/systemd/system/service.d
-    mkdir -p /etc/systemd/system/a-.service.d
-    mkdir -p /etc/systemd/system/a-b-.service.d
-    mkdir -p /etc/systemd/system/a-b-c.service.d
-
-    echo -e '[Service]\nStandardInputText=aaa' >/etc/systemd/system/service.d/drop1.conf
-    echo -e '[Service]\nStandardInputText=bbb' >/etc/systemd/system/a-.service.d/drop2.conf
-    echo -e '[Service]\nStandardInputText=ccc' >/etc/systemd/system/a-b-.service.d/drop3.conf
-    echo -e '[Service]\nStandardInputText=ddd' >/etc/systemd/system/a-b-c.service.d/drop4.conf
-
-    # There's no fragment yet, so this fails
-    systemctl cat a-b-c.service && exit 1
-
-    # xxx → eHh4Cg==
-    systemd-run -u a-b-c.service -p StandardInputData=eHh4Cg== sleep infinity
-
-    data=$(systemctl show -P StandardInputData a-b-c.service)
-    # xxx\naaa\n\bbb\nccc\nddd\n → eHh4…
-    test "$data" = "eHh4CmFhYQpiYmIKY2NjCmRkZAo="
-
-    # Do a reload and check again
-    systemctl daemon-reload
-    data=$(systemctl show -P StandardInputData a-b-c.service)
-    test "$data" = "eHh4CmFhYQpiYmIKY2NjCmRkZAo="
-
-    clear_units a-b-c.service
-    rm /etc/systemd/system/service.d/drop1.conf \
-       /etc/systemd/system/a-.service.d/drop2.conf \
-       /etc/systemd/system/a-b-.service.d/drop3.conf
-}
-
-testcase_transient_slice_dropins() {
-    echo "Testing dropins for a transient slice..."
-    echo "*** test transient slice drop-ins"
-
-    # FIXME: implement reloading of individual units.
-    #
-    # The settings here are loaded twice. For most settings it doesn't matter,
-    # but Documentation is not deduplicated, so we current get repeated entried
-    # which is a bug.
-
-    mkdir -p /etc/systemd/system/slice.d
-    mkdir -p /etc/systemd/system/a-.slice.d
-    mkdir -p /etc/systemd/system/a-b-.slice.d
-    mkdir -p /etc/systemd/system/a-b-c.slice.d
-
-    echo -e '[Unit]\nDocumentation=man:drop1' >/etc/systemd/system/slice.d/drop1.conf
-    echo -e '[Unit]\nDocumentation=man:drop2' >/etc/systemd/system/a-.slice.d/drop2.conf
-    echo -e '[Unit]\nDocumentation=man:drop3' >/etc/systemd/system/a-b-.slice.d/drop3.conf
-    echo -e '[Unit]\nDocumentation=man:drop4' >/etc/systemd/system/a-b-c.slice.d/drop4.conf
-
-    # Invoke daemon-reload to make sure that the call below doesn't fail
-    systemctl daemon-reload
-
-    # No fragment is required, so this works
-    systemctl cat a-b-c.slice
-
-    busctl call \
-           org.freedesktop.systemd1 \
-           /org/freedesktop/systemd1 \
-           org.freedesktop.systemd1.Manager \
-           StartTransientUnit 'ssa(sv)a(sa(sv))' \
-           'a-b-c.slice' 'replace' \
-           1 \
-           'Documentation' as 1 'man:drop5' \
-           0
-
-    data=$(systemctl show -P Documentation a-b-c.slice)
-    test "$data" = "man:drop1 man:drop2 man:drop3 man:drop4 man:drop5 man:drop1 man:drop2 man:drop3 man:drop4"
-
-    # Do a reload and check again
-    systemctl daemon-reload
-    data=$(systemctl show -P Documentation a-b-c.slice)
-    test "$data" = "man:drop5 man:drop1 man:drop2 man:drop3 man:drop4"
-
-    clear_units a-b-c.slice
-    rm /etc/systemd/system/slice.d/drop1.conf \
-       /etc/systemd/system/a-.slice.d/drop2.conf \
-       /etc/systemd/system/a-b-.slice.d/drop3.conf
-}
-
-testcase_template_dropins() {
-    echo "Testing template dropins..."
-
-    create_services foo bar@ yup@
-
-    # Declare some deps to check if the body was loaded
-    cat >>/etc/systemd/system/bar@.service <<EOF
-[Unit]
-After=bar-template-after.device
-EOF
-
-    cat >>/etc/systemd/system/yup@.service <<EOF
-[Unit]
-After=yup-template-after.device
-EOF
-
-    ln -s /etc/systemd/system/bar@.service /etc/systemd/system/foo.service.wants/bar@1.service
-    check_ok foo Wants bar@1.service
-
-    echo "*** test bar-alias@.service→bar@.service, but instance symlinks point to yup@.service ***"
-    ln -s bar@.service  /etc/systemd/system/bar-alias@.service
-    ln -s bar@1.service /etc/systemd/system/bar-alias@1.service
-    ln -s yup@.service  /etc/systemd/system/bar-alias@2.service
-    ln -s yup@3.service /etc/systemd/system/bar-alias@3.service
-
-    # create some dropin deps
-    mkdir -p /etc/systemd/system/bar@{,0,1,2,3}.service.requires/
-    mkdir -p /etc/systemd/system/yup@{,0,1,2,3}.service.requires/
-    mkdir -p /etc/systemd/system/bar-alias@{,0,1,2,3}.service.requires/
-
-    ln -s ../bar-template-requires.device /etc/systemd/system/bar@.service.requires/
-    ln -s ../bar-0-requires.device /etc/systemd/system/bar@0.service.requires/
-    ln -s ../bar-1-requires.device /etc/systemd/system/bar@1.service.requires/
-    ln -s ../bar-2-requires.device /etc/systemd/system/bar@2.service.requires/
-    ln -s ../bar-3-requires.device /etc/systemd/system/bar@3.service.requires/
-
-    ln -s ../yup-template-requires.device /etc/systemd/system/yup@.service.requires/
-    ln -s ../yup-0-requires.device /etc/systemd/system/yup@0.service.requires/
-    ln -s ../yup-1-requires.device /etc/systemd/system/yup@1.service.requires/
-    ln -s ../yup-2-requires.device /etc/systemd/system/yup@2.service.requires/
-    ln -s ../yup-3-requires.device /etc/systemd/system/yup@3.service.requires/
-
-    ln -s ../bar-alias-template-requires.device /etc/systemd/system/bar-alias@.service.requires/
-    ln -s ../bar-alias-0-requires.device /etc/systemd/system/bar-alias@0.service.requires/
-    ln -s ../bar-alias-1-requires.device /etc/systemd/system/bar-alias@1.service.requires/
-    ln -s ../bar-alias-2-requires.device /etc/systemd/system/bar-alias@2.service.requires/
-    ln -s ../bar-alias-3-requires.device /etc/systemd/system/bar-alias@3.service.requires/
-
-    systemctl daemon-reload
-
-    echo '*** bar@0 is aliased by bar-alias@0 ***'
-    systemctl show -p Names,Requires bar@0
-    systemctl show -p Names,Requires bar-alias@0
-    check_ok bar@0 Names bar@0
-    check_ok bar@0 Names bar-alias@0
-
-    check_ok bar@0 After bar-template-after.device
-
-    check_ok bar@0 Requires bar-0-requires.device
-    check_ok bar@0 Requires bar-alias-0-requires.device
-    check_ok bar@0 Requires bar-template-requires.device
-    check_ok bar@0 Requires bar-alias-template-requires.device
-    check_ko bar@0 Requires yup-template-requires.device
-
-    check_ok bar-alias@0 After bar-template-after.device
-
-    check_ok bar-alias@0 Requires bar-0-requires.device
-    check_ok bar-alias@0 Requires bar-alias-0-requires.device
-    check_ok bar-alias@0 Requires bar-template-requires.device
-    check_ok bar-alias@0 Requires bar-alias-template-requires.device
-    check_ko bar-alias@0 Requires yup-template-requires.device
-    check_ko bar-alias@0 Requires yup-0-requires.device
-
-    echo '*** bar@1 is aliased by bar-alias@1 ***'
-    systemctl show -p Names,Requires bar@1
-    systemctl show -p Names,Requires bar-alias@1
-    check_ok bar@1 Names bar@1
-    check_ok bar@1 Names bar-alias@1
-
-    check_ok bar@1 After bar-template-after.device
-
-    check_ok bar@1 Requires bar-1-requires.device
-    check_ok bar@1 Requires bar-alias-1-requires.device
-    check_ok bar@1 Requires bar-template-requires.device
-    # See https://github.com/systemd/systemd/pull/13119#discussion_r308145418
-    check_ok bar@1 Requires bar-alias-template-requires.device
-    check_ko bar@1 Requires yup-template-requires.device
-    check_ko bar@1 Requires yup-1-requires.device
-
-    check_ok bar-alias@1 After bar-template-after.device
-
-    check_ok bar-alias@1 Requires bar-1-requires.device
-    check_ok bar-alias@1 Requires bar-alias-1-requires.device
-    check_ok bar-alias@1 Requires bar-template-requires.device
-    check_ok bar-alias@1 Requires bar-alias-template-requires.device
-    check_ko bar-alias@1 Requires yup-template-requires.device
-    check_ko bar-alias@1 Requires yup-1-requires.device
-
-    echo '*** bar-alias@2 aliases yup@2, bar@2 is independent ***'
-    systemctl show -p Names,Requires bar@2
-    systemctl show -p Names,Requires bar-alias@2
-    check_ok bar@2 Names bar@2
-    check_ko bar@2 Names bar-alias@2
-
-    check_ok bar@2 After bar-template-after.device
-
-    check_ok bar@2 Requires bar-2-requires.device
-    check_ko bar@2 Requires bar-alias-2-requires.device
-    check_ok bar@2 Requires bar-template-requires.device
-    check_ko bar@2 Requires bar-alias-template-requires.device
-    check_ko bar@2 Requires yup-template-requires.device
-    check_ko bar@2 Requires yup-2-requires.device
-
-    check_ko bar-alias@2 After bar-template-after.device
-
-    check_ko bar-alias@2 Requires bar-2-requires.device
-    check_ok bar-alias@2 Requires bar-alias-2-requires.device
-    check_ko bar-alias@2 Requires bar-template-requires.device
-    check_ok bar-alias@2 Requires bar-alias-template-requires.device
-    check_ok bar-alias@2 Requires yup-template-requires.device
-    check_ok bar-alias@2 Requires yup-2-requires.device
-
-    echo '*** bar-alias@3 aliases yup@3, bar@3 is independent ***'
-    systemctl show -p Names,Requires bar@3
-    systemctl show -p Names,Requires bar-alias@3
-    check_ok bar@3 Names bar@3
-    check_ko bar@3 Names bar-alias@3
-
-    check_ok bar@3 After bar-template-after.device
-
-    check_ok bar@3 Requires bar-3-requires.device
-    check_ko bar@3 Requires bar-alias-3-requires.device
-    check_ok bar@3 Requires bar-template-requires.device
-    check_ko bar@3 Requires bar-alias-template-requires.device
-    check_ko bar@3 Requires yup-template-requires.device
-    check_ko bar@3 Requires yup-3-requires.device
-
-    check_ko bar-alias@3 After bar-template-after.device
-
-    check_ko bar-alias@3 Requires bar-3-requires.device
-    check_ok bar-alias@3 Requires bar-alias-3-requires.device
-    check_ko bar-alias@3 Requires bar-template-requires.device
-    check_ok bar-alias@3 Requires bar-alias-template-requires.device
-    check_ok bar-alias@3 Requires yup-template-requires.device
-    check_ok bar-alias@3 Requires yup-3-requires.device
-
-    clear_units foo.service {bar,yup,bar-alias}@{,1,2,3}.service
-}
-
-testcase_alias_dropins() {
-    echo "Testing alias dropins..."
-
-    echo "*** test a wants b1 alias of b"
-    create_services test15-a test15-b
-    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
-    ln -sf ../test15-b1.service /etc/systemd/system/test15-a.service.wants/
-    check_ok test15-a Wants test15-b.service
-    systemctl start test15-a
-    systemctl --quiet is-active test15-b
-    systemctl stop test15-a test15-b
-    rm /etc/systemd/system/test15-b1.service
-    clear_units test15-{a,b}.service
-
-    # Check that dependencies don't vary.
-    echo "*** test 2"
-    create_services test15-a test15-x test15-y
-    mkdir -p /etc/systemd/system/test15-a1.service.wants/
-    ln -sf test15-a.service /etc/systemd/system/test15-a1.service
-    ln -sf ../test15-x.service /etc/systemd/system/test15-a.service.wants/
-    ln -sf ../test15-y.service /etc/systemd/system/test15-a1.service.wants/
-    check_ok test15-a1 Wants test15-x.service # see [1]
-    check_ok test15-a1 Wants test15-y.service
-    systemctl start test15-a
-    check_ok test15-a1 Wants test15-x.service # see [2]
-    check_ok test15-a1 Wants test15-y.service
-    systemctl stop test15-a test15-x test15-y
-    rm /etc/systemd/system/test15-a1.service
-
-    clear_units test15-{a,x,y}.service
-}
-
-testcase_masked_dropins() {
-    echo "Testing masked dropins..."
-
-    create_services test15-a test15-b
-
-    # 'b' is masked for both deps
-    echo "*** test a wants,requires b is masked"
-    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
-    ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b.service
-    check_ko test15-a Wants test15-b.service
-    check_ko test15-a Requires test15-b.service
-
-    # 'a' wants 'b' and 'b' is masked at a lower level
-    echo "*** test a wants b, mask override"
-    ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.wants/test15-b.service
-    ln -sf /dev/null /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
-    check_ok test15-a Wants test15-b.service
-
-    # 'a' wants 'b' and 'b' is masked at a higher level
-    echo "*** test a wants b, mask"
-    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
-    ln -sf ../test15-b.service /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
-    check_ko test15-a Wants test15-b.service
-
-    # 'a' is masked but has an override config file
-    echo "*** test a is masked but has an override"
-    create_services test15-a test15-b
-    ln -sf /dev/null /etc/systemd/system/test15-a.service
-    cat >/usr/lib/systemd/system/test15-a.service.d/override.conf <<EOF
-[Unit]
-After=test15-b.service
-EOF
-    check_ok test15-a UnitFileState masked
-
-    # 'b1' is an alias for 'b': masking 'b' dep should not influence 'b1' dep
-    echo "*** test a wants b, b1, and one is masked"
-    create_services test15-a test15-b
-    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
-    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
-    ln -sf ../test15-b1.service /usr/lib/systemd/system/test15-a.service.wants/test15-b1.service
-    systemctl cat test15-a
-    systemctl show -p Wants,Requires test15-a
-    systemctl cat test15-b1
-    systemctl show -p Wants,Requires test15-b1
-    check_ok test15-a Wants test15-b.service
-    check_ko test15-a Wants test15-b1.service # the alias does not show up in the list of units
-    rm /etc/systemd/system/test15-b1.service
-
-    # 'b1' is an alias for 'b': masking 'b1' should not influence 'b' dep
-    echo "*** test a wants b, alias dep is masked"
-    create_services test15-a test15-b
-    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
-    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b1.service
-    ln -sf ../test15-b.service /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
-    check_ok test15-a Wants test15-b.service
-    check_ko test15-a Wants test15-b1.service # the alias does not show up in the list of units
-    rm /etc/systemd/system/test15-b1.service
-
-    # 'a' has Wants=b.service but also has a masking
-    # dropin 'b': 'b' should still be pulled in.
-    echo "*** test a wants b both ways"
-    create_services test15-a test15-b
-    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
-    cat >/usr/lib/systemd/system/test15-a.service.d/wants-b.conf <<EOF
-[Unit]
-Wants=test15-b.service
-EOF
-    check_ok test15-a Wants test15-b.service
-
-    # mask a dropin that points to an nonexistent unit.
-    echo "*** test a wants nonexistent is masked"
-    create_services test15-a
-    ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/nonexistent.service
-    ln -sf ../nonexistent.service /usr/lib/systemd/system/test15-a.service.requires/
-    check_ko test15-a Requires nonexistent.service
-
-    # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is
-    # masked at a higher level.
-    echo "*** test a wants b is masked"
-    create_services test15-a test15-b test15-c
-    ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
-    ln -sf ../test15-b.service /run/systemd/system/test15-c.service.requires/
-    ln -sf /dev/null /etc/systemd/system/test15-c.service.requires/test15-b.service
-    systemctl start test15-a
-    check_ko test15-c Requires test15-b.service
-    systemctl stop test15-a test15-b
-
-    # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is
-    # masked at a lower level.
-    echo "*** test a requires b is masked"
-    create_services test15-a test15-b test15-c
-    ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
-    ln -sf ../test15-b.service /etc/systemd/system/test15-c.service.requires/
-    ln -sf /dev/null /run/systemd/system/test15-c.service.requires/test15-b.service
-    systemctl start test15-a
-    check_ok test15-c Requires test15-b.service
-    systemctl stop test15-a test15-b
-
-    # 'a' requires 2 aliases of 'b' and one of them is a mask.
-    echo "*** test a requires alias of b, other alias masked"
-    create_services test15-a test15-b
-    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
-    ln -sf test15-b.service /etc/systemd/system/test15-b2.service
-    ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b1.service
-    ln -sf ../test15-b1.service /run/systemd/system/test15-a.service.requires/
-    ln -sf ../test15-b2.service /usr/lib/systemd/system/test15-a.service.requires/
-    check_ok test15-a Requires test15-b
-
-    # Same as above but now 'b' is masked.
-    echo "*** test a requires alias of b, b dep masked"
-    create_services test15-a test15-b
-    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
-    ln -sf test15-b.service /etc/systemd/system/test15-b2.service
-    ln -sf ../test15-b1.service /run/systemd/system/test15-a.service.requires/
-    ln -sf ../test15-b2.service /usr/lib/systemd/system/test15-a.service.requires/
-    ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b.service
-    check_ok test15-a Requires test15-b
-
-    clear_units test15-{a,b}.service
-}
-
-testcase_invalid_dropins() {
-    echo "Testing invalid dropins..."
-    # Assertion failed on earlier versions, command exits unsuccessfully on later versions
-    systemctl cat nonexistent@.service || true
-    create_services a
-    systemctl daemon-reload
-    # Assertion failed on earlier versions, command exits unsuccessfully on later versions
-    systemctl cat a@.service || true
-    systemctl stop a
-    clear_units a.service
-    return 0
-}
-
-testcase_symlink_dropin_directory() {
-    # For issue #21920.
-    echo "Testing symlink drop-in directory..."
-    create_services test15-a
-    rmdir /{etc,run,usr/lib}/systemd/system/test15-a.service.d
-    mkdir -p /tmp/testsuite-15-test15-a-dropin-directory
-    ln -s /tmp/testsuite-15-test15-a-dropin-directory /etc/systemd/system/test15-a.service.d
-    cat >/tmp/testsuite-15-test15-a-dropin-directory/override.conf <<EOF
-[Unit]
-Description=hogehoge
-EOF
-    ln -s /tmp/testsuite-15-test15-a-dropin-directory-nonexistent /run/systemd/system/test15-a.service.d
-    touch /tmp/testsuite-15-test15-a-dropin-directory-regular
-    ln -s /tmp/testsuite-15-test15-a-dropin-directory-regular /usr/lib/systemd/system/test15-a.service.d
-    check_ok test15-a Description hogehoge
-
-    clear_units test15-a.service
-}
-
-run_testcases
-
-touch /testok
diff --git a/test/units/testsuite-16.service b/test/units/testsuite-16.service
deleted file mode 100644 (file)
index d5494ae..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-16-EXTEND-TIMEOUT
-# Testsuite: Assess all other testsuite-*.services worked as expected
-
-Wants=success-all.service
-Wants=success-start.service
-Wants=success-runtime.service
-Wants=success-stop.service
-Wants=fail-start.service
-Wants=fail-stop.service
-Wants=fail-runtime.service
-StopWhenUnneeded=yes
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-Type=exec
-TimeoutStartSec=infinity
-ExecStartPre=/usr/lib/systemd/tests/testdata/units/%N.sh
-ExecStart=true
diff --git a/test/units/testsuite-16.sh b/test/units/testsuite-16.sh
deleted file mode 100755 (executable)
index c60995a..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-rm -f /test.log
-
-TESTLOG=/test.log.XXXXXXXX
-
-wait_for()
-{
-    local service="${1:-wait_for: missing service argument}"
-    local result="${2:-success}"
-    local time="${3:-45}"
-
-    while [[ ! -f /${service}.terminated && ! -f /${service}.success && $time -gt 0 ]]; do
-        sleep 1
-        time=$((time - 1))
-    done
-
-    if [[ ! -f /${service}.${result} ]]; then
-        journalctl -u "${service/_/-}.service" >>"$TESTLOG"
-    fi
-}
-
-wait_for_timeout()
-{
-    local unit="$1"
-    local time="$2"
-
-    while [[ $time -gt 0 ]]; do
-        if [[ "$(systemctl show --property=Result "$unit")" == "Result=timeout" ]]; then
-            return 0
-        fi
-
-        sleep 1
-        time=$((time - 1))
-    done
-
-    journalctl -u "$unit" >>"$TESTLOG"
-
-    return 1
-}
-
-# This checks all stages, start, runtime and stop, can be extended by
-# EXTEND_TIMEOUT_USEC
-
-wait_for success_all
-
-# These check that EXTEND_TIMEOUT_USEC that occurs at greater than the
-# extend timeout interval but less then the stage limit (TimeoutStartSec,
-# RuntimeMaxSec, TimeoutStopSec) still succeed.
-
-wait_for success_start
-wait_for success_runtime
-wait_for success_stop
-
-# These ensure that EXTEND_TIMEOUT_USEC will still timeout in the
-# appropriate stage, after the stage limit, when the EXTEND_TIMEOUT_USEC
-# message isn't sent within the extend timeout interval.
-
-wait_for fail_start startfail
-wait_for fail_stop stopfail
-wait_for fail_runtime runtimefail
-
-# These ensure that RuntimeMaxSec is honored for scope and service units
-# when they are created.
-runtime_max_sec=5
-
-systemd-run \
-    --property=RuntimeMaxSec=${runtime_max_sec}s \
-    -u runtime-max-sec-test-1.service \
-    /usr/bin/sh -c "while true; do sleep 1; done"
-wait_for_timeout runtime-max-sec-test-1.service $((runtime_max_sec + 2))
-
-systemd-run \
-    --property=RuntimeMaxSec=${runtime_max_sec}s \
-    --scope \
-    -u runtime-max-sec-test-2.scope \
-    /usr/bin/sh -c "while true; do sleep 1; done" &
-wait_for_timeout runtime-max-sec-test-2.scope $((runtime_max_sec + 2))
-
-# These ensure that RuntimeMaxSec is honored for scope and service
-# units if the value is changed and then the manager is reloaded.
-systemd-run \
-    -u runtime-max-sec-test-3.service \
-    /usr/bin/sh -c "while true; do sleep 1; done"
-mkdir -p /etc/systemd/system/runtime-max-sec-test-3.service.d/
-cat > /etc/systemd/system/runtime-max-sec-test-3.service.d/override.conf << EOF
-[Service]
-RuntimeMaxSec=${runtime_max_sec}s
-EOF
-systemctl daemon-reload
-wait_for_timeout runtime-max-sec-test-3.service $((runtime_max_sec + 2))
-
-systemd-run \
-    --scope \
-    -u runtime-max-sec-test-4.scope \
-    /usr/bin/sh -c "while true; do sleep 1; done" &
-
-# Wait until the unit is running to avoid race with creating the override.
-until systemctl is-active runtime-max-sec-test-4.scope; do
-    sleep 1
-done
-mkdir -p /etc/systemd/system/runtime-max-sec-test-4.scope.d/
-cat > /etc/systemd/system/runtime-max-sec-test-4.scope.d/override.conf << EOF
-[Scope]
-RuntimeMaxSec=${runtime_max_sec}s
-EOF
-systemctl daemon-reload
-wait_for_timeout runtime-max-sec-test-4.scope $((runtime_max_sec + 2))
-
-if [[ -f "$TESTLOG" ]]; then
-    # no mv
-    cp "$TESTLOG" /test.log
-    exit 1
-fi
-
-touch /testok
diff --git a/test/units/testsuite-17.00.sh b/test/units/testsuite-17.00.sh
deleted file mode 100755 (executable)
index d2aec60..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Tests for issue #28588 and #28653.
-
-# On boot, services need to be started in the following order:
-# 1. systemd-tmpfiles-setup-dev-early.service
-# 2. systemd-sysusers.service
-# 3. systemd-tmpfiles-setup-dev.service
-# 4. systemd-udevd.service
-
-output="$(systemctl show --property After --value systemd-udevd.service)"
-assert_in "systemd-tmpfiles-setup-dev-early.service" "$output"
-assert_in "systemd-sysusers.service" "$output"
-assert_in "systemd-tmpfiles-setup-dev.service" "$output"
-
-output="$(systemctl show --property After --value systemd-tmpfiles-setup-dev.service)"
-assert_in "systemd-tmpfiles-setup-dev-early.service" "$output"
-assert_in "systemd-sysusers.service" "$output"
-
-output="$(systemctl show --property After --value systemd-sysusers.service)"
-assert_in "systemd-tmpfiles-setup-dev-early.service" "$output"
-
-check_owner_and_mode() {
-    local dev=${1?}
-    local user=${2?}
-    local group=${3?}
-    local mode=${4:-}
-
-    if [[ -e "$dev" ]]; then
-        assert_in "$user" "$(stat --format=%U "$dev")"
-        assert_in "$group" "$(stat --format=%G "$dev")"
-        if [[ -n "$mode" ]]; then
-            assert_in "$mode" "$(stat --format=%#0a "$dev")"
-        fi
-    fi
-
-    return 0
-}
-
-# Check owner and access mode specified in static-nodes-permissions.conf
-check_owner_and_mode /dev/snd/seq      root audio 0660
-check_owner_and_mode /dev/snd/timer    root audio 0660
-check_owner_and_mode /dev/loop-control root disk  0660
-check_owner_and_mode /dev/net/tun      root root  0666
-check_owner_and_mode /dev/fuse         root root  0666
-check_owner_and_mode /dev/vfio/vfio    root root  0666
-check_owner_and_mode /dev/kvm          root kvm
-check_owner_and_mode /dev/vhost-net    root kvm
-check_owner_and_mode /dev/vhost-vsock  root kvm
-
-exit 0
diff --git a/test/units/testsuite-17.01.sh b/test/units/testsuite-17.01.sh
deleted file mode 100755 (executable)
index 44f36f5..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-mkdir -p /run/udev/rules.d/
-
-rm -f /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload
-udevadm trigger --settle /dev/sda
-
-while : ; do
-    (
-        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
-        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
-        systemctl show -p WantedBy foobar.service | grep -q -v sda
-        systemctl show -p WantedBy waldo.service | grep -q -v sda
-    ) && break
-
-    sleep .5
-done
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM=="block", KERNEL=="sda", OPTIONS="log_level=debug"
-ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="foobar.service"
-EOF
-udevadm control --reload
-udevadm trigger --settle /dev/sda
-
-while : ; do
-    (
-        udevadm info /dev/sda | grep -q SYSTEMD_WANTS=foobar.service
-        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
-        systemctl show -p WantedBy foobar.service | grep -q sda
-        systemctl show -p WantedBy waldo.service | grep -q -v sda
-    ) && break
-
-    sleep .5
-done
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM=="block", KERNEL=="sda", OPTIONS="log_level=debug"
-ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="waldo.service"
-EOF
-udevadm control --reload
-udevadm trigger --settle /dev/sda
-
-while : ; do
-    (
-        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
-        udevadm info /dev/sda | grep -q SYSTEMD_WANTS=waldo.service
-        systemctl show -p WantedBy foobar.service | grep -q -v sda
-        systemctl show -p WantedBy waldo.service | grep -q sda
-    ) && break
-
-    sleep .5
-done
-
-rm /run/udev/rules.d/50-testsuite.rules
-
-udevadm control --reload
-udevadm trigger --settle /dev/sda
-
-while : ; do
-    (
-        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
-        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
-        systemctl show -p WantedBy foobar.service | grep -q -v sda
-        systemctl show -p WantedBy waldo.service | grep -q -v sda
-    ) && break
-
-    sleep .5
-done
-
-exit 0
diff --git a/test/units/testsuite-17.02.sh b/test/units/testsuite-17.02.sh
deleted file mode 100755 (executable)
index b232fca..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-# disable shellcheck warning about '"aaa"' type quotation
-# shellcheck disable=SC2016
-
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-mkdir -p /run/udev/rules.d/
-
-# test for ID_RENAMING= udev property and device unit state
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-ACTION=="remove", GOTO="hoge_end"
-SUBSYSTEM!="net", GOTO="hoge_end"
-KERNEL!="hoge", GOTO="hoge_end"
-
-OPTIONS="log_level=debug"
-
-# emulate renaming
-ACTION=="online", ENV{ID_RENAMING}="1"
-
-LABEL="hoge_end"
-EOF
-
-udevadm control --log-priority=debug --reload --timeout=30
-
-ip link add hoge type dummy
-udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
-assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
-
-udevadm trigger --action=online --settle /sys/devices/virtual/net/hoge
-assert_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "inactive" ]]; do sleep .5; done'
-
-udevadm trigger --action=move --settle /sys/devices/virtual/net/hoge
-assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
-
-# test for renaming interface with NAME= (issue #25106)
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-ACTION!="add", GOTO="hoge_end"
-SUBSYSTEM!="net", GOTO="hoge_end"
-
-OPTIONS="log_level=debug"
-
-KERNEL=="hoge",  NAME="foobar"
-KERNEL=="foobar", NAME="hoge"
-
-LABEL="hoge_end"
-EOF
-
-udevadm control --log-priority=debug --reload --timeout=30
-
-udevadm trigger --action=add --settle /sys/devices/virtual/net/hoge
-udevadm wait --timeout=30 --settle /sys/devices/virtual/net/foobar
-assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/foobar)"
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "active" ]]; do sleep .5; done'
-
-udevadm trigger --action=add --settle /sys/devices/virtual/net/foobar
-udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
-assert_not_in "ID_RENAMING=" "$(udevadm info /sys/devices/virtual/net/hoge)"
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "inactive" ]]; do sleep .5; done'
-
-# cleanup
-rm -f /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload --timeout=30
-
-# test for renaming interface with an external tool (issue #16967)
-
-ip link set hoge name foobar
-udevadm wait --timeout=30 --settle /sys/devices/virtual/net/foobar
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "active" ]]; do sleep .5; done'
-
-ip link set foobar name hoge
-udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/hoge)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/hoge)" != "active" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/devices/virtual/net/foobar)" != "inactive" ]]; do sleep .5; done'
-timeout 30 bash -c 'while [[ "$(systemctl show --property=ActiveState --value /sys/subsystem/net/devices/foobar)" != "inactive" ]]; do sleep .5; done'
-
-# cleanup
-ip link del hoge
-
-# shellcheck disable=SC2317
-teardown_netif_renaming_conflict() {
-    set +ex
-
-    if [[ -n "$KILL_PID" ]]; then
-        kill "$KILL_PID"
-    fi
-
-    rm -rf "$TMPDIR"
-
-    rm -f /run/udev/rules.d/50-testsuite.rules
-    udevadm control --reload --timeout=30
-
-    ip link del hoge
-    ip link del foobar
-}
-
-test_netif_renaming_conflict() {
-    local since found=
-
-    trap teardown_netif_renaming_conflict RETURN
-
-    cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-ACTION!="add", GOTO="hoge_end"
-SUBSYSTEM!="net", GOTO="hoge_end"
-
-OPTIONS="log_level=debug"
-
-KERNEL=="foobar", NAME="hoge"
-
-LABEL="hoge_end"
-EOF
-
-    udevadm control --log-priority=debug --reload --timeout=30
-
-    ip link add hoge type dummy
-    udevadm wait --timeout=30 --settle /sys/devices/virtual/net/hoge
-
-    TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
-    udevadm monitor --udev --property --subsystem-match=net >"$TMPDIR"/monitor.txt &
-    KILL_PID="$!"
-
-    # make sure that 'udevadm monitor' actually monitor uevents
-    sleep 1
-
-    since="$(date '+%H:%M:%S')"
-
-    # add another interface which will conflict with an existing interface
-    ip link add foobar type dummy
-
-    for _ in {1..40}; do
-        if (
-            grep -q 'ACTION=add' "$TMPDIR"/monitor.txt
-            grep -q 'DEVPATH=/devices/virtual/net/foobar' "$TMPDIR"/monitor.txt
-            grep -q 'SUBSYSTEM=net' "$TMPDIR"/monitor.txt
-            grep -q 'INTERFACE=foobar' "$TMPDIR"/monitor.txt
-            grep -q 'ID_NET_DRIVER=dummy' "$TMPDIR"/monitor.txt
-            grep -q 'ID_NET_NAME=foobar' "$TMPDIR"/monitor.txt
-            # Even when network interface renaming is failed, SYSTEMD_ALIAS with the conflicting name will be broadcast.
-            grep -q 'SYSTEMD_ALIAS=/sys/subsystem/net/devices/hoge' "$TMPDIR"/monitor.txt
-            grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt
-            grep -q 'UDEV_WORKER_ERRNO=17' "$TMPDIR"/monitor.txt
-            grep -q 'UDEV_WORKER_ERRNO_NAME=EEXIST' "$TMPDIR"/monitor.txt
-        ); then
-            cat "$TMPDIR"/monitor.txt
-            found=1
-            break
-        fi
-        sleep .5
-    done
-    test -n "$found"
-
-    timeout 30 bash -c "until journalctl _PID=1 _COMM=systemd --since $since | grep -q 'foobar: systemd-udevd failed to process the device, ignoring: File exists'; do sleep 1; done"
-    # check if the invalid SYSTEMD_ALIAS property for the interface foobar is ignored by PID1
-    assert_eq "$(systemctl show --property=SysFSPath --value /sys/subsystem/net/devices/hoge)" "/sys/devices/virtual/net/hoge"
-}
-
-test_netif_renaming_conflict
-
-exit 0
diff --git a/test/units/testsuite-17.03.sh b/test/units/testsuite-17.03.sh
deleted file mode 100755 (executable)
index d6b3162..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-
-TMPDIR=
-TEST_RULE="/run/udev/rules.d/49-test.rules"
-TEST_CONF="/run/udev/udev.conf.d/test-17.conf"
-KILL_PID=
-
-setup() {
-    mkdir -p "${TEST_RULE%/*}"
-    mkdir -p /run/udev/udev.conf.d
-
-    cat >"${TEST_RULE}" <<EOF
-ACTION!="add", GOTO="test_end"
-SUBSYSTEM!="mem", GOTO="test_end"
-KERNEL!="null", GOTO="test_end"
-
-OPTIONS="log_level=debug"
-PROGRAM=="/bin/touch /tmp/test-udev-marker"
-PROGRAM!="/bin/sleep 60", ENV{PROGRAM_RESULT}="KILLED"
-
-LABEL="test_end"
-EOF
-    cat >"$TEST_CONF" <<EOF
-event_timeout=10
-timeout_signal=SIGABRT
-EOF
-
-    systemctl restart systemd-udevd.service
-}
-
-# shellcheck disable=SC2317
-teardown() {
-    set +e
-
-    if [[ -n "$KILL_PID" ]]; then
-        kill "$KILL_PID"
-    fi
-
-    rm -rf "$TMPDIR"
-    rm -f "$TEST_RULE" "$TEST_CONF"
-    systemctl restart systemd-udevd.service
-}
-
-run_test_timeout() {
-    TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
-    udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
-    KILL_PID="$!"
-
-    SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
-
-    for _ in {1..40}; do
-        if grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt; then
-            sleep .5
-            kill "$KILL_PID"
-            KILL_PID=
-
-            cat "$TMPDIR"/monitor.txt
-            (! grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt)
-            (! grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt)
-            (! grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt)
-            grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt
-            rm -rf "$TMPDIR"
-            return 0
-        fi
-        sleep .5
-    done
-
-    return 1
-}
-
-run_test_killed() {
-    local killed=
-
-    TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
-    udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
-    KILL_PID="$!"
-
-    rm -f /tmp/test-udev-marker
-    SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
-
-    for _ in {1..40}; do
-        if [[ -z "$killed" ]]; then
-            if [[ -e /tmp/test-udev-marker ]]; then
-                killall --signal ABRT --regexp udev-worker
-                killed=1
-            fi
-        elif grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt; then
-            sleep .5
-            kill "$KILL_PID"
-            KILL_PID=
-
-            cat "$TMPDIR"/monitor.txt
-            grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt
-            grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt
-            grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt
-            (! grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt)
-            rm -rf "$TMPDIR"
-            return 0
-        fi
-        sleep .5
-    done
-
-    return 1
-}
-
-trap teardown EXIT
-
-setup
-run_test_timeout
-run_test_killed
-
-exit 0
diff --git a/test/units/testsuite-17.04.sh b/test/units/testsuite-17.04.sh
deleted file mode 100755 (executable)
index d1c3c85..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-mkdir -p /run/udev/rules.d/
-
-test ! -f /run/udev/tags/added/c1:3
-test ! -f /run/udev/tags/changed/c1:3
-udevadm info /dev/null | grep -E 'E: (TAGS|CURRENT_TAGS)=.*:(added|changed):' && exit 1
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
-ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", TAG+="added"
-ACTION=="change", SUBSYSTEM=="mem", KERNEL=="null", TAG+="changed"
-EOF
-
-udevadm control --reload
-SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
-
-test -f /run/udev/tags/added/c1:3
-test ! -f /run/udev/tags/changed/c1:3
-udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*'
-udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*'
-udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' && { echo 'unexpected TAGS='; exit 1; }
-udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*' && { echo 'unexpected CURRENT_TAGS='; exit 1; }
-
-SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action change /dev/null
-
-test -f /run/udev/tags/added/c1:3
-test -f /run/udev/tags/changed/c1:3
-udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*'
-udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' && { echo 'unexpected CURRENT_TAGS='; exit 1; }
-udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*'
-udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*'
-
-SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
-
-test -f /run/udev/tags/added/c1:3
-test -f /run/udev/tags/changed/c1:3
-udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*'
-udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*'
-udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*'
-udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*' && { echo 'unexpected CURRENT_TAGS='; exit 1; }
-
-rm /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload
-
-exit 0
diff --git a/test/units/testsuite-17.05.sh b/test/units/testsuite-17.05.sh
deleted file mode 100755 (executable)
index 60be31a..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-mkdir -p /run/udev/rules.d/
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
-ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", IMPORT{program}="/bin/echo -e HOGE=aa\\\\x20\\\\x20\\\\x20bb\nFOO=\\\\x20aaa\\\\x20\n\n\n"
-EOF
-
-udevadm control --reload
-SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --settle --action add /dev/null
-
-test -f /run/udev/data/c1:3
-udevadm info /dev/null | grep -q 'E: HOGE=aa\\x20\\x20\\x20bb'
-udevadm info /dev/null | grep -q 'E: FOO=\\x20aaa\\x20'
-
-rm /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload
-
-exit 0
diff --git a/test/units/testsuite-17.06.sh b/test/units/testsuite-17.06.sh
deleted file mode 100755 (executable)
index 6d83645..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# tests for udev watch
-
-function check_validity() {
-    local f ID_OR_HANDLE
-
-    for f in /run/udev/watch/*; do
-        ID_OR_HANDLE="$(readlink "$f")"
-        test -L "/run/udev/watch/${ID_OR_HANDLE}"
-        test "$(readlink "/run/udev/watch/${ID_OR_HANDLE}")" = "$(basename "$f")"
-    done
-}
-
-function check() {
-    for _ in {1..2}; do
-        systemctl restart systemd-udevd.service
-        udevadm control --ping
-        udevadm settle
-        check_validity
-
-        for _ in {1..2}; do
-            udevadm trigger -w --action add --subsystem-match=block
-            check_validity
-        done
-
-        for _ in {1..2}; do
-            udevadm trigger -w --action change --subsystem-match=block
-            check_validity
-        done
-    done
-}
-
-mkdir -p /run/udev/rules.d/
-
-cat >/run/udev/rules.d/00-debug.rules <<EOF
-SUBSYSTEM=="block", KERNEL=="sda*", OPTIONS="log_level=debug"
-EOF
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-ACTION=="add", SUBSYSTEM=="block", KERNEL=="sda", OPTIONS:="watch"
-EOF
-
-check
-
-MAJOR=$(udevadm info /dev/sda | grep -e '^E: MAJOR=' | sed -e 's/^E: MAJOR=//')
-MINOR=$(udevadm info /dev/sda | grep -e '^E: MINOR=' | sed -e 's/^E: MINOR=//')
-test -L "/run/udev/watch/b${MAJOR}:${MINOR}"
-
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-ACTION=="change", SUBSYSTEM=="block", KERNEL=="sda", OPTIONS:="nowatch"
-EOF
-
-check
-
-MAJOR=$(udevadm info /dev/sda | grep -e '^E: MAJOR=' | sed -e 's/^E: MAJOR=//')
-MINOR=$(udevadm info /dev/sda | grep -e '^E: MINOR=' | sed -e 's/^E: MINOR=//')
-test ! -e "/run/udev/watch/b${MAJOR}:${MINOR}"
-
-rm /run/udev/rules.d/00-debug.rules
-rm /run/udev/rules.d/50-testsuite.rules
-
-udevadm control --reload
-systemctl reset-failed systemd-udevd.service
-
-exit 0
diff --git a/test/units/testsuite-17.07.sh b/test/units/testsuite-17.07.sh
deleted file mode 100755 (executable)
index 629393a..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-wait_service_active() {(
-    set +ex
-    for i in {1..20}; do
-        (( i > 1 )) && sleep 0.5
-        if systemctl --quiet is-active "${1?}"; then
-            return 0
-        fi
-    done
-    return 1
-)}
-
-wait_service_inactive() {(
-    set +ex
-    for i in {1..20}; do
-        (( i > 1 )) && sleep 0.5
-        systemctl --quiet is-active "${1?}"
-        if [[ "$?" == "3" ]]; then
-            return 0
-        fi
-    done
-    return 1
-)}
-
-mkdir -p /run/systemd/system
-cat >/run/systemd/system/both.service <<EOF
-[Service]
-ExecStart=sleep 1000
-EOF
-
-cat >/run/systemd/system/on-add.service <<EOF
-[Service]
-ExecStart=sleep 1000
-EOF
-
-cat >/run/systemd/system/on-change.service <<EOF
-[Service]
-ExecStart=sleep 1000
-EOF
-
-systemctl daemon-reload
-
-mkdir -p /run/udev/rules.d/
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM=="net", KERNEL=="dummy9?", OPTIONS="log_level=debug"
-SUBSYSTEM=="net", KERNEL=="dummy9?", ACTION=="add",    TAG+="systemd", ENV{SYSTEMD_WANTS}+="both.service", ENV{SYSTEMD_WANTS}+="on-add.service"
-SUBSYSTEM=="net", KERNEL=="dummy9?", ACTION=="change", TAG+="systemd", ENV{SYSTEMD_WANTS}+="both.service", ENV{SYSTEMD_WANTS}+="on-change.service"
-EOF
-
-udevadm control --reload
-
-# StopWhenUnneeded=no
-ip link add dummy99 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy99
-wait_service_active both.service
-wait_service_active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-systemctl stop both.service on-add.service
-
-udevadm trigger --action=change --settle /sys/class/net/dummy99
-udevadm info /sys/class/net/dummy99
-wait_service_active both.service
-assert_rc 3 systemctl --quiet is-active on-add.service
-wait_service_active on-change.service
-systemctl stop both.service on-change.service
-
-ip link del dummy99
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
-assert_rc 3 systemctl --quiet is-active both.service
-assert_rc 3 systemctl --quiet is-active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-# StopWhenUnneeded=yes
-cat >/run/systemd/system/both.service <<EOF
-[Unit]
-StopWhenUnneeded=yes
-
-[Service]
-ExecStart=sleep 1000
-Type=simple
-EOF
-
-cat >/run/systemd/system/on-add.service <<EOF
-[Unit]
-StopWhenUnneeded=yes
-
-[Service]
-ExecStart=sleep 1000
-Type=simple
-EOF
-
-cat >/run/systemd/system/on-change.service <<EOF
-[Unit]
-StopWhenUnneeded=yes
-
-[Service]
-ExecStart=echo changed
-RemainAfterExit=true
-Type=oneshot
-EOF
-
-systemctl daemon-reload
-
-# StopWhenUnneeded=yes (single device, only add event)
-ip link add dummy99 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy99
-wait_service_active both.service
-wait_service_active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-ip link del dummy99
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
-wait_service_inactive both.service
-wait_service_inactive on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-# StopWhenUnneeded=yes (single device, add and change event)
-ip link add dummy99 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy99
-wait_service_active both.service
-wait_service_active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-udevadm trigger --action=change --settle /sys/class/net/dummy99
-assert_rc 0 systemctl --quiet is-active both.service
-wait_service_inactive on-add.service
-wait_service_active on-change.service
-
-ip link del dummy99
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
-wait_service_inactive both.service
-assert_rc 3 systemctl --quiet is-active on-add.service
-wait_service_inactive on-change.service
-
-# StopWhenUnneeded=yes (multiple devices, only add events)
-ip link add dummy99 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy99
-wait_service_active both.service
-wait_service_active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-ip link add dummy98 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy98
-assert_rc 0 systemctl --quiet is-active both.service
-assert_rc 0 systemctl --quiet is-active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-ip link del dummy99
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
-assert_rc 0 systemctl --quiet is-active both.service
-assert_rc 0 systemctl --quiet is-active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-ip link del dummy98
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy98
-wait_service_inactive both.service
-wait_service_inactive on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-# StopWhenUnneeded=yes (multiple devices, add and change events)
-ip link add dummy99 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy99
-wait_service_active both.service
-wait_service_active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-ip link add dummy98 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/dummy98
-assert_rc 0 systemctl --quiet is-active both.service
-assert_rc 0 systemctl --quiet is-active on-add.service
-assert_rc 3 systemctl --quiet is-active on-change.service
-
-udevadm trigger --action=change --settle /sys/class/net/dummy99
-assert_rc 0 systemctl --quiet is-active both.service
-assert_rc 0 systemctl --quiet is-active on-add.service
-wait_service_active on-change.service
-
-ip link del dummy98
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy98
-assert_rc 0 systemctl --quiet is-active both.service
-wait_service_inactive on-add.service
-assert_rc 0 systemctl --quiet is-active on-change.service
-
-ip link del dummy99
-udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
-wait_service_inactive both.service
-assert_rc 3 systemctl --quiet is-active on-add.service
-wait_service_inactive on-change.service
-
-# cleanup
-rm -f /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload
-
-rm -f /run/systemd/system/on-add.service
-rm -f /run/systemd/system/on-change.service
-systemctl daemon-reload
-
-exit 0
diff --git a/test/units/testsuite-17.08.sh b/test/units/testsuite-17.08.sh
deleted file mode 100755 (executable)
index e570c69..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# This is a test for issue #24518.
-
-mkdir -p /run/udev/rules.d/
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug", TAG+="systemd"
-SUBSYSTEM=="mem", KERNEL=="null", ACTION=="add",    SYMLINK+="test/symlink-to-null-on-add",    ENV{SYSTEMD_ALIAS}+="/sys/test/alias-to-null-on-add"
-SUBSYSTEM=="mem", KERNEL=="null", ACTION=="change", SYMLINK+="test/symlink-to-null-on-change", ENV{SYSTEMD_ALIAS}+="/sys/test/alias-to-null-on-change"
-EOF
-
-udevadm control --reload
-
-udevadm trigger --settle --action add /dev/null
-for i in {1..20}; do
-    ((i > 1)) && sleep .5
-
-    (
-        systemctl -q is-active /dev/test/symlink-to-null-on-add
-        ! systemctl -q is-active /dev/test/symlink-to-null-on-change
-        systemctl -q is-active /sys/test/alias-to-null-on-add
-        ! systemctl -q is-active /sys/test/alias-to-null-on-change
-    ) && break
-done
-assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-add
-assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-change
-assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-add
-assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-change
-
-udevadm trigger --settle --action change /dev/null
-for i in {1..20}; do
-    ((i > 1)) && sleep .5
-
-    (
-        ! systemctl -q is-active /dev/test/symlink-to-null-on-add
-        systemctl -q is-active /dev/test/symlink-to-null-on-change
-        ! systemctl -q is-active /sys/test/alias-to-null-on-add
-        systemctl -q is-active /sys/test/alias-to-null-on-change
-    ) && break
-done
-assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-add
-assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-change
-assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-add
-assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-change
-
-udevadm trigger --settle --action add /dev/null
-for i in {1..20}; do
-    ((i > 1)) && sleep .5
-
-    (
-        systemctl -q is-active /dev/test/symlink-to-null-on-add
-        ! systemctl -q is-active /dev/test/symlink-to-null-on-change
-        systemctl -q is-active /sys/test/alias-to-null-on-add
-        ! systemctl -q is-active /sys/test/alias-to-null-on-change
-    ) && break
-done
-assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-add
-assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-change
-assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-add
-assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-change
-
-# cleanup
-rm -f /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload
-
-exit 0
diff --git a/test/units/testsuite-17.09.sh b/test/units/testsuite-17.09.sh
deleted file mode 100755 (executable)
index 9993196..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# This is a test for issue #24987.
-
-mkdir -p /run/udev/rules.d/
-cat >/run/udev/rules.d/50-testsuite.rules <<EOF
-SUBSYSTEM!="mem", GOTO="test-end"
-KERNEL!="null", GOTO="test-end"
-ACTION=="remove", GOTO="test-end"
-
-# add 100 * 100byte of properties
-$(for i in {1..100}; do printf 'ENV{XXX%03i}="0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"\n' "$i"; done)
-
-LABEL="test-end"
-EOF
-
-udevadm control --reload
-
-TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
-SYSTEMD_LOG_LEVEL=debug udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt 2>&1 &
-KILL_PID="$!"
-
-FOUND=
-for _ in {1..40}; do
-    if grep -F 'UDEV - the event which udev sends out after rule processing' "$TMPDIR"/monitor.txt; then
-        FOUND=1
-        break
-    fi
-    sleep .5
-done
-[[ -n "$FOUND" ]]
-
-udevadm trigger --verbose --settle --action add /dev/null
-
-FOUND=
-for _ in {1..40}; do
-    if ! grep -e 'UDEV *\[[0-9.]*\] *add *\/devices\/virtual\/mem\/null (mem)' "$TMPDIR"/monitor.txt; then
-        sleep .5
-        continue
-    fi
-
-    FOUND=1
-    for i in {1..100}; do
-        if ! grep -F "$(printf 'XXX%03i=0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' "$i")" "$TMPDIR"/monitor.txt; then
-            FOUND=
-            break
-        fi
-    done
-    if [[ -n "$FOUND" ]]; then
-        break;
-    fi
-
-    sleep .5
-done
-[[ -n "$FOUND" ]]
-
-# cleanup
-rm -f /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload
-
-kill "$KILL_PID"
-rm -rf "$TMPDIR"
-
-exit 0
diff --git a/test/units/testsuite-17.10.sh b/test/units/testsuite-17.10.sh
deleted file mode 100755 (executable)
index a2b8b14..0000000
+++ /dev/null
@@ -1,254 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Coverage test for udevadm
-
-# shellcheck disable=SC2317
-cleanup_17_10() {
-    set +e
-
-    losetup -d "$loopdev"
-    rm -f "$blk"
-
-    ip link delete "$netdev"
-}
-
-# Set up some test devices
-trap cleanup_17_10 EXIT
-
-netdev=dummy17.10
-ip link add $netdev type dummy
-
-blk="$(mktemp)"
-dd if=/dev/zero of="$blk" bs=1M count=1
-loopdev="$(losetup --show -f "$blk")"
-
-udevadm -h
-
-udevadm control -e
-udevadm control -l emerg
-udevadm control -l alert
-udevadm control -l crit
-udevadm control -l err
-udevadm control -l warning
-udevadm control -l notice
-udevadm control --log-level info
-udevadm control --log-level debug
-(! udevadm control -l hello)
-udevadm control -s
-udevadm control -S
-udevadm control -R
-udevadm control -p HELLO=world
-udevadm control -m 42
-udevadm control --ping -t 5
-udevadm control --load-credentials
-udevadm control -h
-
-udevadm info /dev/null
-udevadm info /sys/class/net/$netdev
-udevadm info "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
-udevadm info --property DEVNAME /sys/class/net/$netdev
-udevadm info --property DEVNAME --value /sys/class/net/$netdev
-udevadm info --property HELLO /sys/class/net/$netdev
-udevadm info -p class/net/$netdev
-udevadm info -p /class/net/$netdev
-udevadm info --json=off -p class/net/$netdev
-udevadm info --json=pretty -p class/net/$netdev | jq .
-udevadm info --json=short -p class/net/$netdev | jq .
-udevadm info -n null
-udevadm info -q all /sys/class/net/$netdev
-udevadm info -q name /dev/null
-udevadm info -q path /sys/class/net/$netdev
-udevadm info -q property /sys/class/net/$netdev
-udevadm info -q symlink /sys/class/net/$netdev
-udevadm info -q name -r /dev/null
-udevadm info --query symlink --root /sys/class/net/$netdev
-(! udevadm info -q hello -r /sys/class/net/$netdev)
-udevadm info -a /sys/class/net/$netdev
-udevadm info -t >/dev/null
-udevadm info --tree /sys/class/net/$netdev
-udevadm info -x /sys/class/net/$netdev
-udevadm info -x -q path /sys/class/net/$netdev
-udevadm info -P TEST_ /sys/class/net/$netdev
-udevadm info -d /dev/null
-udevadm info -e >/dev/null
-udevadm info -e --json=off >/dev/null
-udevadm info -e --json=pretty | jq . >/dev/null
-udevadm info -e --json=short | jq . >/dev/null
-udevadm info -e --subsystem-match acpi >/dev/null
-udevadm info -e --subsystem-nomatch acpi >/dev/null
-udevadm info -e --attr-match ifindex=2 >/dev/null
-udevadm info -e --attr-nomatch ifindex=2 >/dev/null
-udevadm info -e --property-match SUBSYSTEM=acpi >/dev/null
-udevadm info -e --tag-match systemd >/dev/null
-udevadm info -e --sysname-match lo >/dev/null
-udevadm info -e --name-match /sys/class/net/$netdev >/dev/null
-udevadm info -e --parent-match /sys/class/net/$netdev >/dev/null
-udevadm info -e --initialized-match >/dev/null
-udevadm info -e --initialized-nomatch >/dev/null
-# udevadm info -c
-udevadm info -w /sys/class/net/$netdev
-udevadm info --wait-for-initialization=5 /sys/class/net/$netdev
-udevadm info -h
-
-assert_rc 124 timeout 1 udevadm monitor
-assert_rc 124 timeout 1 udevadm monitor -k
-assert_rc 124 timeout 1 udevadm monitor -u
-assert_rc 124 timeout 1 udevadm monitor -s net
-assert_rc 124 timeout 1 udevadm monitor --subsystem-match net/$netdev
-assert_rc 124 timeout 1 udevadm monitor -t systemd
-assert_rc 124 timeout 1 udevadm monitor --tag-match hello
-udevadm monitor -h
-
-udevadm settle
-udevadm settle -t 5
-udevadm settle -E /sys/class/net/$netdev
-udevadm settle -h
-
-udevadm test /dev/null
-udevadm info /sys/class/net/$netdev
-udevadm test "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
-udevadm test -a add /sys/class/net/$netdev
-udevadm test -a change /sys/class/net/$netdev
-udevadm test -a move /sys/class/net/$netdev
-udevadm test -a online /sys/class/net/$netdev
-udevadm test -a offline /sys/class/net/$netdev
-udevadm test -a bind /sys/class/net/$netdev
-udevadm test -a unbind /sys/class/net/$netdev
-udevadm test -a help /sys/class/net/$netdev
-udevadm test --action help
-(! udevadm test -a hello /sys/class/net/$netdev)
-udevadm test -N early /sys/class/net/$netdev
-udevadm test -N late /sys/class/net/$netdev
-udevadm test --resolve-names never /sys/class/net/$netdev
-(! udevadm test -N hello /sys/class/net/$netdev)
-udevadm test -h
-
-# udevadm test-builtin path_id "$loopdev"
-udevadm test-builtin net_id /sys/class/net/$netdev
-udevadm test-builtin net_id "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
-udevadm test-builtin -a add net_id /sys/class/net/$netdev
-udevadm test-builtin -a remove net_id /sys/class/net/$netdev
-udevadm test-builtin -a change net_id /sys/class/net/$netdev
-udevadm test-builtin -a move net_id /sys/class/net/$netdev
-udevadm test-builtin -a online net_id /sys/class/net/$netdev
-udevadm test-builtin -a offline net_id /sys/class/net/$netdev
-udevadm test-builtin -a bind net_id /sys/class/net/$netdev
-udevadm test-builtin -a unbind net_id /sys/class/net/$netdev
-udevadm test-builtin -a help net_id /sys/class/net/$netdev
-udevadm test-builtin net_setup_link /sys/class/net/$netdev
-udevadm test-builtin blkid "$loopdev"
-udevadm test-builtin input_id /sys/class/net/$netdev
-udevadm test-builtin keyboard /dev/null
-# udevadm test-builtin kmod /sys/class/net/$netdev
-udevadm test-builtin uaccess /dev/null
-# udevadm test-builtin usb_id dev/null
-(! udevadm test-builtin hello /sys/class/net/$netdev)
-# systemd-hwdb update is extremely slow when combined with sanitizers and run
-# in a VM without acceleration, so let's just skip the one particular test
-# if we detect this combination
-if ! [[ -v ASAN_OPTIONS && "$(systemd-detect-virt -v)" == "qemu" ]]; then
-    modprobe scsi_debug
-    scsidev=$(readlink -f /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/[0-9]*)
-    mkdir -p /etc/udev/hwdb.d
-    cat >/etc/udev/hwdb.d/99-test.hwdb <<EOF
-scsi:*
-  ID_TEST=test
-EOF
-    systemd-hwdb update
-
-    udevadm test-builtin hwdb "$scsidev"
-
-    rmmod scsi_debug || :
-    rm -fv /etc/udev/hwdb.d/99-test.hwdb
-    systemd-hwdb update
-fi
-
-
-udevadm trigger
-udevadm trigger /dev/null
-udevadm trigger /sys/class/net/$netdev
-udevadm trigger "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)"
-udevadm trigger -v /sys/class/net/$netdev
-udevadm trigger -n /sys/class/net/$netdev
-udevadm trigger -q /sys/class/net/$netdev
-udevadm trigger -t all /sys/class/net/$netdev
-udevadm trigger -t devices /sys/class/net/$netdev
-udevadm trigger --type subsystems /sys/class/net/$netdev
-(! udevadm trigger -t hello /sys/class/net/$netdev)
-udevadm trigger -c add /sys/class/net/$netdev
-udevadm trigger -c remove /sys/class/net/$netdev
-udevadm trigger -c change /sys/class/net/$netdev
-udevadm trigger -c move /sys/class/net/$netdev
-udevadm trigger -c online /sys/class/net/$netdev
-udevadm trigger -c offline /sys/class/net/$netdev
-udevadm trigger -c bind /sys/class/net/$netdev
-udevadm trigger -c unbind /sys/class/net/$netdev
-udevadm trigger -c help /sys/class/net/$netdev
-udevadm trigger --action help /sys/class/net/$netdev
-(! udevadm trigger -c hello /sys/class/net/$netdev)
-udevadm trigger --prioritized-subsystem block
-udevadm trigger --prioritized-subsystem block,net
-udevadm trigger --prioritized-subsystem hello
-udevadm trigger -s net
-udevadm trigger -S net
-udevadm trigger -a subsystem=net
-udevadm trigger --attr-match hello=world
-udevadm trigger -p DEVNAME=null
-udevadm trigger --property-match HELLO=world
-udevadm trigger -g systemd
-udevadm trigger --tag-match hello
-udevadm trigger -y net
-udevadm trigger --sysname-match hello
-udevadm trigger --name-match /sys/class/net/$netdev
-udevadm trigger --name-match /sys/class/net/$netdev --name-match /dev/null
-udevadm trigger -b /sys/class/net/$netdev
-udevadm trigger --parent-match /sys/class/net/$netdev --name-match /dev/null
-udevadm trigger --initialized-match
-udevadm trigger --initialized-nomatch
-udevadm trigger -w
-udevadm trigger --uuid /sys/class/net/$netdev
-udevadm settle -t 300
-udevadm trigger --wait-daemon
-udevadm settle -t 300
-udevadm trigger --wait-daemon=5
-udevadm trigger -h
-
-# https://github.com/systemd/systemd/issues/29863
-if [[ "$(systemd-detect-virt -v)" != "qemu" ]]; then
-    udevadm control --log-level=0
-    for _ in {0..9}; do
-        timeout 30 udevadm trigger --settle
-    done
-    udevadm control --log-level=debug
-fi
-
-udevadm wait /dev/null
-udevadm wait /sys/class/net/$netdev
-udevadm wait -t 5 /sys/class/net/$netdev
-udevadm wait --initialized true /sys/class/net/$netdev
-udevadm wait --initialized false /sys/class/net/$netdev
-(! udevadm wait --initialized hello /sys/class/net/$netdev)
-assert_rc 124 timeout 5 udevadm wait --removed /sys/class/net/$netdev
-udevadm wait --settle /sys/class/net/$netdev
-udevadm wait -h
-
-udevadm lock --help
-udevadm lock --version
-for i in /dev/block/*; do
-    udevadm lock --device "$i" --print
-    udevadm lock --device "$i" true
-    (! udevadm lock --device "$i" false)
-done
-for i in / /usr; do
-    udevadm lock --backing "$i" --print
-    udevadm lock --backing "$i" true
-    (! udevadm lock --backing "$i" false)
-done
-
-exit 0
diff --git a/test/units/testsuite-17.11.sh b/test/units/testsuite-17.11.sh
deleted file mode 100755 (executable)
index 42b925f..0000000
+++ /dev/null
@@ -1,447 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# Test for udevadm verify.
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# shellcheck disable=SC2317
-cleanup() {
-    cd /
-    rm -rf "${workdir}"
-    workdir=
-}
-
-workdir="$(mktemp -d)"
-trap cleanup EXIT
-cd "${workdir}"
-
-cat >"${workdir}/default_output_1_success" <<EOF
-
-1 udev rules files have been checked.
-  Success: 1
-  Fail:    0
-EOF
-cat >"${workdir}/default_output_1_fail" <<EOF
-
-1 udev rules files have been checked.
-  Success: 0
-  Fail:    1
-EOF
-cat >"${workdir}/output_0_files" <<EOF
-
-0 udev rules files have been checked.
-  Success: 0
-  Fail:    0
-EOF
-
-test_number=0
-rules=
-exp=
-err=
-out=
-next_test_number() {
-    : $((++test_number))
-
-    local num_str
-    num_str=$(printf %05d "${test_number}")
-
-    rules="sample-${num_str}.rules"
-    exp="sample-${num_str}.exp"
-    err="sample-${num_str}.err"
-    exo="sample-${num_str}.exo"
-    out="sample-${num_str}.out"
-}
-
-assert_0_impl() {
-    udevadm verify "$@" >"${out}"
-    if [ -f "${exo}" ]; then
-        diff -u "${exo}" "${out}"
-    elif [ -f "${rules}" ]; then
-        diff -u "${workdir}/default_output_1_success" "${out}"
-    fi
-}
-
-assert_0() {
-    assert_0_impl "$@"
-    next_test_number
-}
-
-assert_1_impl() {
-    local rc
-    set +e
-    udevadm verify "$@" >"${out}" 2>"${err}"
-    rc=$?
-    set -e
-    assert_eq "$rc" 1
-
-    if [ -f "${exp}" ]; then
-        diff -u "${exp}" "${err}"
-    fi
-
-    if [ -f "${exo}" ]; then
-        diff -u "${exo}" "${out}"
-    elif [ -f "${rules}" ]; then
-        diff -u "${workdir}/default_output_1_fail" "${out}"
-    fi
-}
-
-assert_1() {
-    assert_1_impl "$@"
-    next_test_number
-}
-
-# initialize variables
-next_test_number
-
-assert_0 -h
-assert_0 --help
-assert_0 -V
-assert_0 --version
-assert_0 /dev/null
-
-# unrecognized option '--unknown'
-assert_1 --unknown
-# option requires an argument -- 'N'
-assert_1 -N
-# --resolve-names= takes "early" or "never"
-assert_1 -N now
-# option '--resolve-names' requires an argument
-assert_1 --resolve-names
-# --resolve-names= takes "early" or "never"
-assert_1 --resolve-names=now
-# Failed to parse rules file ./nosuchfile: No such file or directory
-assert_1 ./nosuchfile
-# Failed to parse rules file ./nosuchfile: No such file or directory
-cat >"${exo}" <<EOF
-
-3 udev rules files have been checked.
-  Success: 2
-  Fail:    1
-EOF
-assert_1 /dev/null ./nosuchfile /dev/null
-
-rules_dir='etc/udev/rules.d'
-mkdir -p "${rules_dir}"
-# No rules files found in $PWD
-assert_1 --root="${workdir}"
-
-# Directory without rules.
-cp "${workdir}/output_0_files" "${exo}"
-assert_0 "${rules_dir}"
-
-# Directory with a loop.
-ln -s . "${rules_dir}/loop.rules"
-assert_1 "${rules_dir}"
-rm "${rules_dir}/loop.rules"
-
-# Empty rules.
-touch "${rules_dir}/empty.rules"
-assert_0 --root="${workdir}"
-: >"${exo}"
-assert_0 --root="${workdir}" --no-summary
-
-# Directory with a single *.rules file.
-cp "${workdir}/default_output_1_success" "${exo}"
-assert_0 "${rules_dir}"
-
-# Combination of --root= and FILEs is not supported.
-assert_1 --root="${workdir}" /dev/null
-# No rules files found in nosuchdir
-assert_1 --root=nosuchdir
-
-cd "${rules_dir}"
-
-# UDEV_LINE_SIZE 16384
-printf '%16383s\n' ' ' >"${rules}"
-assert_0 "${rules}"
-
-# Failed to parse rules file ${rules}: No buffer space available
-printf '%16384s\n' ' ' >"${rules}"
-echo "Failed to parse rules file ${rules}: No buffer space available" >"${exp}"
-assert_1 "${rules}"
-
-{
-    printf 'RUN+="/bin/true",%8174s\\\n' ' '
-    printf 'RUN+="/bin/false"%8174s\\\n' ' '
-    echo
-} >"${rules}"
-assert_0 "${rules}"
-
-printf 'RUN+="/bin/true"%8176s\\\n #\n' ' ' ' ' >"${rules}"
-echo >>"${rules}"
-cat >"${exp}" <<EOF
-${rules}:5 Line is too long, ignored.
-${rules}: udev rules check failed.
-EOF
-assert_1 "${rules}"
-
-printf '\\\n' >"${rules}"
-cat >"${exp}" <<EOF
-${rules}:1 Unexpected EOF after line continuation, line ignored.
-${rules}: udev rules check failed.
-EOF
-assert_1 "${rules}"
-
-test_syntax_error() {
-    local rule msg
-
-    rule="$1"; shift
-    msg="$1"; shift
-
-    printf '%s\n' "${rule}" >"${rules}"
-    cat >"${exp}" <<EOF
-${rules}:1 ${msg}
-${rules}: udev rules check failed.
-EOF
-    assert_1 "${rules}"
-}
-
-test_style_error() {
-    local rule msg
-
-    rule="$1"; shift
-    msg="$1"; shift
-
-    printf '%s\n' "${rule}" >"${rules}"
-    cat >"${exp}" <<EOF
-${rules}:1 ${msg}
-${rules}: udev rules have style issues.
-EOF
-    assert_0_impl --no-style "${rules}"
-    assert_1_impl "${rules}"
-    next_test_number
-}
-
-test_syntax_error '=' 'Invalid key/value pair, ignoring.'
-test_syntax_error 'ACTION{a}=="b"' 'Invalid attribute for ACTION.'
-test_syntax_error 'ACTION:="b"' 'Invalid operator for ACTION.'
-test_syntax_error 'ACTION=="b"' 'The line has no effect, ignoring.'
-test_syntax_error 'DEVPATH{a}=="b"' 'Invalid attribute for DEVPATH.'
-test_syntax_error 'DEVPATH:="b"' 'Invalid operator for DEVPATH.'
-test_syntax_error 'KERNEL{a}=="b"' 'Invalid attribute for KERNEL.'
-test_syntax_error 'KERNEL:="b"' 'Invalid operator for KERNEL.'
-test_syntax_error 'KERNELS{a}=="b"' 'Invalid attribute for KERNELS.'
-test_syntax_error 'KERNELS:="b"' 'Invalid operator for KERNELS.'
-test_syntax_error 'SYMLINK{a}=="b"' 'Invalid attribute for SYMLINK.'
-test_syntax_error 'SYMLINK:="%?"' 'Invalid value "%?" for SYMLINK (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'NAME{a}=="b"' 'Invalid attribute for NAME.'
-test_syntax_error 'NAME-="b"' 'Invalid operator for NAME.'
-test_syntax_error 'NAME+="a"' "NAME key takes '==', '!=', '=', or ':=' operator, assuming '='."
-test_syntax_error 'NAME:=""' 'Ignoring NAME="", as udev will not delete any network interfaces.'
-test_syntax_error 'NAME="%k"' 'Ignoring NAME="%k", as it will take no effect.'
-test_syntax_error 'ENV=="b"' 'Invalid attribute for ENV.'
-test_syntax_error 'ENV{a}-="b"' 'Invalid operator for ENV.'
-test_syntax_error 'ENV{a}:="b"' "ENV key takes '==', '!=', '=', or '+=' operator, assuming '='."
-test_syntax_error 'ENV{ACTION}="b"' "Invalid ENV attribute. 'ACTION' cannot be set."
-test_syntax_error 'CONST=="b"' 'Invalid attribute for CONST.'
-test_syntax_error 'CONST{a}=="b"' 'Invalid attribute for CONST.'
-test_syntax_error 'CONST{arch}="b"' 'Invalid operator for CONST.'
-test_syntax_error 'TAG{a}=="b"' 'Invalid attribute for TAG.'
-test_syntax_error 'TAG:="a"' "TAG key takes '==', '!=', '=', or '+=' operator, assuming '='."
-test_syntax_error 'TAG="%?"' 'Invalid value "%?" for TAG (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'TAGS{a}=="b"' 'Invalid attribute for TAGS.'
-test_syntax_error 'TAGS:="a"' 'Invalid operator for TAGS.'
-test_syntax_error 'SUBSYSTEM{a}=="b"' 'Invalid attribute for SUBSYSTEM.'
-test_syntax_error 'SUBSYSTEM:="b"' 'Invalid operator for SUBSYSTEM.'
-test_syntax_error 'SUBSYSTEM=="bus", NAME="b"' '"bus" must be specified as "subsystem".'
-test_syntax_error 'SUBSYSTEMS{a}=="b"' 'Invalid attribute for SUBSYSTEMS.'
-test_syntax_error 'SUBSYSTEMS:="b"' 'Invalid operator for SUBSYSTEMS.'
-test_syntax_error 'DRIVER{a}=="b"' 'Invalid attribute for DRIVER.'
-test_syntax_error 'DRIVER:="b"' 'Invalid operator for DRIVER.'
-test_syntax_error 'DRIVERS{a}=="b"' 'Invalid attribute for DRIVERS.'
-test_syntax_error 'DRIVERS:="b"' 'Invalid operator for DRIVERS.'
-test_syntax_error 'ATTR="b"' 'Invalid attribute for ATTR.'
-test_syntax_error 'ATTR{%}="b"' 'Invalid attribute "%" for ATTR (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'ATTR{a}-="b"' 'Invalid operator for ATTR.'
-test_syntax_error 'ATTR{a}+="b"' "ATTR key takes '==', '!=', or '=' operator, assuming '='."
-test_syntax_error 'ATTR{a}="%?"' 'Invalid value "%?" for ATTR (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'SYSCTL=""' 'Invalid attribute for SYSCTL.'
-test_syntax_error 'SYSCTL{%}="b"' 'Invalid attribute "%" for SYSCTL (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'SYSCTL{a}-="b"' 'Invalid operator for SYSCTL.'
-test_syntax_error 'SYSCTL{a}+="b"' "SYSCTL key takes '==', '!=', or '=' operator, assuming '='."
-test_syntax_error 'SYSCTL{a}="%?"' 'Invalid value "%?" for SYSCTL (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'ATTRS=""' 'Invalid attribute for ATTRS.'
-test_syntax_error 'ATTRS{%}=="b", NAME="b"' 'Invalid attribute "%" for ATTRS (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'ATTRS{a}-="b"' 'Invalid operator for ATTRS.'
-test_syntax_error 'ATTRS{device/}!="a", NAME="b"' "'device' link may not be available in future kernels."
-test_syntax_error 'ATTRS{../}!="a", NAME="b"' 'Direct reference to parent sysfs directory, may break in future kernels.'
-test_syntax_error 'TEST{a}=="b"' "Failed to parse mode 'a': Invalid argument"
-test_syntax_error 'TEST{0}=="%", NAME="b"' 'Invalid value "%" for TEST (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'TEST{0644}="b"' 'Invalid operator for TEST.'
-test_syntax_error 'PROGRAM{a}=="b"' 'Invalid attribute for PROGRAM.'
-test_syntax_error 'PROGRAM-="b"' 'Invalid operator for PROGRAM.'
-test_syntax_error 'PROGRAM=="%", NAME="b"' 'Invalid value "%" for PROGRAM (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'IMPORT="b"' 'Invalid attribute for IMPORT.'
-test_syntax_error 'IMPORT{a}="b"' 'Invalid attribute for IMPORT.'
-test_syntax_error 'IMPORT{a}-="b"' 'Invalid operator for IMPORT.'
-test_syntax_error 'IMPORT{file}=="%", NAME="b"' 'Invalid value "%" for IMPORT (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'IMPORT{builtin}!="foo"' 'Unknown builtin command: foo'
-test_syntax_error 'RESULT{a}=="b"' 'Invalid attribute for RESULT.'
-test_syntax_error 'RESULT:="b"' 'Invalid operator for RESULT.'
-test_syntax_error 'OPTIONS{a}="b"' 'Invalid attribute for OPTIONS.'
-test_syntax_error 'OPTIONS-="b"' 'Invalid operator for OPTIONS.'
-test_syntax_error 'OPTIONS!="b"' 'Invalid operator for OPTIONS.'
-test_syntax_error 'OPTIONS+="link_priority=a"' "Failed to parse link priority 'a': Invalid argument"
-test_syntax_error 'OPTIONS:="log_level=a"' "Failed to parse log level 'a': Invalid argument"
-test_syntax_error 'OPTIONS="a", NAME="b"' "Invalid value for OPTIONS key, ignoring: 'a'"
-test_syntax_error 'OWNER{a}="b"' 'Invalid attribute for OWNER.'
-test_syntax_error 'OWNER-="b"' 'Invalid operator for OWNER.'
-test_syntax_error 'OWNER!="b"' 'Invalid operator for OWNER.'
-test_syntax_error 'OWNER+="0"' "OWNER key takes '=' or ':=' operator, assuming '='."
-test_syntax_error 'OWNER=":nosuchuser:"' "Unknown user ':nosuchuser:', ignoring."
-test_syntax_error 'GROUP{a}="b"' 'Invalid attribute for GROUP.'
-test_syntax_error 'GROUP-="b"' 'Invalid operator for GROUP.'
-test_syntax_error 'GROUP!="b"' 'Invalid operator for GROUP.'
-test_syntax_error 'GROUP+="0"' "GROUP key takes '=' or ':=' operator, assuming '='."
-test_syntax_error 'GROUP=":nosuchgroup:"' "Unknown group ':nosuchgroup:', ignoring."
-test_syntax_error 'MODE{a}="b"' 'Invalid attribute for MODE.'
-test_syntax_error 'MODE-="b"' 'Invalid operator for MODE.'
-test_syntax_error 'MODE!="b"' 'Invalid operator for MODE.'
-test_syntax_error 'MODE+="0"' "MODE key takes '=' or ':=' operator, assuming '='."
-test_syntax_error 'MODE="%"' 'Invalid value "%" for MODE (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'SECLABEL="b"' 'Invalid attribute for SECLABEL.'
-test_syntax_error 'SECLABEL{a}="%"' 'Invalid value "%" for SECLABEL (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'SECLABEL{a}!="b"' 'Invalid operator for SECLABEL.'
-test_syntax_error 'SECLABEL{a}-="b"' 'Invalid operator for SECLABEL.'
-test_syntax_error 'SECLABEL{a}:="b"' "SECLABEL key takes '=' or '+=' operator, assuming '='."
-test_syntax_error 'RUN=="b"' 'Invalid operator for RUN.'
-test_syntax_error 'RUN-="b"' 'Invalid operator for RUN.'
-test_syntax_error 'RUN="%"' 'Invalid value "%" for RUN (char 1: invalid substitution type), ignoring.'
-test_syntax_error 'RUN{builtin}+="foo"' "Unknown builtin command 'foo', ignoring."
-test_syntax_error 'GOTO{a}="b"' 'Invalid attribute for GOTO.'
-test_syntax_error 'GOTO=="b"' 'Invalid operator for GOTO.'
-test_syntax_error 'NAME="a", GOTO="b"' 'GOTO="b" has no matching label, ignoring.'
-test_syntax_error 'GOTO="a", GOTO="b"
-LABEL="a"' 'Contains multiple GOTO keys, ignoring GOTO="b".'
-test_syntax_error 'LABEL{a}="b"' 'Invalid attribute for LABEL.'
-test_syntax_error 'LABEL=="b"' 'Invalid operator for LABEL.'
-test_style_error 'LABEL="b"' 'style: LABEL="b" is unused.'
-test_syntax_error 'a="b"' "Invalid key 'a'."
-test_syntax_error 'KERNEL=="", KERNEL=="?*", NAME="a"' 'conflicting match expressions, the line has no effect.'
-test_syntax_error 'KERNEL=="abc", KERNEL!="abc", NAME="b"' 'conflicting match expressions, the line has no effect.'
-test_syntax_error 'KERNEL=="|a|b", KERNEL!="b|a|", NAME="c"' 'conflicting match expressions, the line has no effect.'
-test_syntax_error 'KERNEL=="a|b", KERNEL=="c|d|e", NAME="f"' 'conflicting match expressions, the line has no effect.'
-# shellcheck disable=SC2016
-test_syntax_error 'ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{DISKSEQ}!="?*", ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"' \
-                  'conflicting match expressions, the line has no effect.'
-test_syntax_error 'ACTION=="a*", ACTION=="bc*", NAME="d"' 'conflicting match expressions, the line has no effect.'
-test_syntax_error 'ACTION=="a*|bc*", ACTION=="d*|ef*", NAME="g"' 'conflicting match expressions, the line has no effect.'
-test_syntax_error 'KERNEL!="", KERNEL=="?*", NAME="a"' 'duplicate expressions.'
-test_syntax_error 'KERNEL=="|a|b", KERNEL=="b|a|", NAME="c"' 'duplicate expressions.'
-# shellcheck disable=SC2016
-test_syntax_error 'ENV{DISKSEQ}=="?*", ENV{DEVTYPE}!="partition", ENV{DISKSEQ}=="?*", ENV{ID_IGNORE_DISKSEQ}!="1", SYMLINK+="disk/by-diskseq/$env{DISKSEQ}"' \
-                  'duplicate expressions.'
-test_style_error ',ACTION=="a", NAME="b"' 'style: stray leading comma.'
-test_style_error ' ,ACTION=="a", NAME="b"' 'style: stray leading comma.'
-test_style_error ', ACTION=="a", NAME="b"' 'style: stray leading comma.'
-test_style_error 'ACTION=="a", NAME="b",' 'style: stray trailing comma.'
-test_style_error 'ACTION=="a", NAME="b", ' 'style: stray trailing comma.'
-test_style_error 'ACTION=="a" NAME="b"' 'style: a comma between tokens is expected.'
-test_style_error 'ACTION=="a",, NAME="b"' 'style: more than one comma between tokens.'
-test_style_error 'ACTION=="a" , NAME="b"' 'style: stray whitespace before comma.'
-test_style_error 'ACTION=="a",NAME="b"' 'style: whitespace after comma is expected.'
-test_syntax_error 'RESULT=="a", PROGRAM="b"' 'Reordering RESULT check after PROGRAM assignment.'
-test_syntax_error 'RESULT=="a*", PROGRAM="b", RESULT=="*c", PROGRAM="d"' \
-                  'Reordering RESULT check after PROGRAM assignment.'
-
-cat >"${rules}" <<'EOF'
-KERNEL=="a|b", KERNEL=="a|c", NAME="d"
-KERNEL=="a|b", KERNEL!="a|c", NAME="d"
-KERNEL!="a", KERNEL!="b", NAME="c"
-KERNEL=="|a", KERNEL=="|b", NAME="c"
-KERNEL=="*", KERNEL=="a*", NAME="b"
-KERNEL=="a*", KERNEL=="c*|ab*", NAME="d"
-PROGRAM="a", RESULT=="b"
-EOF
-assert_0 "${rules}"
-
-echo 'GOTO="a"' >"${rules}"
-cat >"${exp}" <<EOF
-${rules}:1 GOTO="a" has no matching label, ignoring.
-${rules}:1 The line has no effect any more, dropping.
-${rules}: udev rules check failed.
-EOF
-assert_1 "${rules}"
-
-cat >"${rules}" <<'EOF'
-GOTO="a"
-LABEL="a"
-EOF
-assert_0 "${rules}"
-
-cat >"${rules}" <<'EOF'
-GOTO="b"
-LABEL="b"
-LABEL="b"
-EOF
-cat >"${exp}" <<EOF
-${rules}:3 style: LABEL="b" is unused.
-${rules}: udev rules have style issues.
-EOF
-assert_0_impl --no-style "${rules}"
-assert_1_impl "${rules}"
-
-cat >"${rules}" <<'EOF'
-GOTO="a"
-LABEL="a", LABEL="b"
-EOF
-cat >"${exp}" <<EOF
-${rules}:2 Contains multiple LABEL keys, ignoring LABEL="a".
-${rules}:1 GOTO="a" has no matching label, ignoring.
-${rules}:1 The line has no effect any more, dropping.
-${rules}:2 style: LABEL="b" is unused.
-${rules}: udev rules check failed.
-EOF
-assert_1 "${rules}"
-
-cat >"${rules}" <<'EOF'
-KERNEL!="", KERNEL=="?*", KERNEL=="", NAME="a"
-EOF
-cat >"${exp}" <<EOF
-${rules}:1 duplicate expressions.
-${rules}:1 conflicting match expressions, the line has no effect.
-${rules}: udev rules check failed.
-EOF
-assert_1 "${rules}"
-
-cat >"${rules}" <<'EOF'
-ACTION=="a"NAME="b"
-EOF
-cat >"${exp}" <<EOF
-${rules}:1 style: a comma between tokens is expected.
-${rules}:1 style: whitespace between tokens is expected.
-${rules}: udev rules have style issues.
-EOF
-assert_0_impl --no-style "${rules}"
-assert_1_impl "${rules}"
-next_test_number
-
-cat >"${rules}" <<'EOF'
-ACTION=="a" ,NAME="b"
-EOF
-cat >"${exp}" <<EOF
-${rules}:1 style: stray whitespace before comma.
-${rules}:1 style: whitespace after comma is expected.
-${rules}: udev rules have style issues.
-EOF
-assert_0_impl --no-style "${rules}"
-assert_1_impl "${rules}"
-next_test_number
-
-# udevadm verify --root
-sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"
-cd -
-assert_1 --root="${workdir}"
-cd -
-
-# udevadm verify path/
-sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}"
-cd -
-assert_1 "${rules_dir}"
-cd -
-
-exit 0
diff --git a/test/units/testsuite-17.12.sh b/test/units/testsuite-17.12.sh
deleted file mode 100755 (executable)
index ccc91bf..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-create_link_file() {
-    name=${1?}
-
-    mkdir -p /run/systemd/network/
-    cat >/run/systemd/network/10-test.link <<EOF
-[Match]
-Kind=dummy
-MACAddress=00:50:56:c0:00:18
-
-[Link]
-Name=$name
-AlternativeName=test1 test2 test3 test4
-EOF
-    udevadm control --reload
-}
-
-udevadm control --log-level=debug
-
-create_link_file test1
-ip link add address 00:50:56:c0:00:18 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/test1
-output=$(ip link show dev test1)
-if ! [[ "$output" =~ altname ]]; then
-    echo "alternative name for network interface not supported, skipping test."
-    exit 0
-fi
-assert_not_in "altname test1" "$output"
-assert_in "altname test2" "$output"
-assert_in "altname test3" "$output"
-assert_in "altname test4" "$output"
-
-# By triggering add event, Name= and AlternativeNames= are re-applied
-create_link_file test2
-udevadm trigger --action add --settle /sys/class/net/test1
-udevadm wait --settle --timeout=30 /sys/class/net/test2
-output=$(ip link show dev test2)
-assert_in "altname test1" "$output"
-assert_not_in "altname test2" "$output"
-assert_in "altname test3" "$output"
-assert_in "altname test4" "$output"
-
-# Name= and AlternativeNames= are not applied on move event
-create_link_file test3
-udevadm trigger --action move --settle /sys/class/net/test2
-udevadm wait --settle --timeout=30 /sys/class/net/test2
-output=$(ip link show dev test2)
-assert_in "altname test1" "$output"
-assert_not_in "altname test2" "$output"
-assert_in "altname test3" "$output"
-assert_in "altname test4" "$output"
-
-# Test move event triggered by manual renaming
-ip link set dev test2 name hoge
-udevadm wait --settle --timeout=30 /sys/class/net/hoge
-output=$(ip link show dev hoge)
-assert_in "altname test1" "$output"
-assert_not_in "altname test2" "$output"
-assert_in "altname test3" "$output"
-assert_in "altname test4" "$output"
-assert_not_in "altname hoge" "$output"
-
-# Re-test add event
-udevadm trigger --action add --settle /sys/class/net/hoge
-udevadm wait --settle --timeout=30 /sys/class/net/test3
-output=$(ip link show dev test3)
-assert_in "altname test1" "$output"
-assert_in "altname test2" "$output"
-assert_not_in "altname test3" "$output"
-assert_in "altname test4" "$output"
-assert_not_in "altname hoge" "$output"
-
-# cleanup
-ip link del dev test3
-
-rm -f /run/systemd/network/10-test.link
-udevadm control --reload --log-level=info
-
-exit 0
diff --git a/test/units/testsuite-17.13.sh b/test/units/testsuite-17.13.sh
deleted file mode 100755 (executable)
index d9dfdd7..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Test for `udevadm control -p`
-
-test_not_property() {
-    assert_eq "$(udevadm info --query property --property "$2" --value "$1")" ""
-}
-
-test_property() {
-    assert_eq "$(udevadm info --query property --property "$2" --value "$1")" "$3"
-}
-
-# shellcheck disable=SC2317
-cleanup() {
-    set +e
-
-    udevadm control -p FOO= -p BAR=
-
-    rm -f "$rules"
-}
-
-# Set up a test device
-trap cleanup EXIT
-
-rules="/run/udev/rules.d/99-test-17.13.rules"
-
-mkdir -p "${rules%/*}"
-cat > "$rules" <<'EOF'
-ENV{FOO}=="?*", ENV{PROP_FOO}="$env{FOO}"
-ENV{BAR}=="?*", ENV{PROP_BAR}="$env{BAR}"
-EOF
-
-udevadm control --reload
-
-test_not_property /dev/null PROP_FOO
-test_not_property /dev/null PROP_BAR
-
-: Setting of a property works
-
-udevadm control --property FOO=foo
-udevadm trigger --action change --settle /dev/null
-test_property /dev/null PROP_FOO foo
-test_not_property /dev/null PROP_BAR
-
-: Change of a property works
-
-udevadm control --property FOO=goo
-udevadm trigger --action change --settle /dev/null
-test_property /dev/null PROP_FOO goo
-
-: Removal of a property works
-
-udevadm control --property FOO=
-udevadm trigger --action change --settle /dev/null
-test_not_property /dev/null PROP_FOO
-
-: Repeated removal of a property does nothing
-
-udevadm control --property FOO=
-udevadm trigger --action change --settle /dev/null
-test_not_property /dev/null PROP_FOO
-
-: Multiple properties can be set at once
-
-udevadm control --property FOO=foo --property BAR=bar
-udevadm trigger --action change --settle /dev/null
-test_property /dev/null PROP_FOO foo
-test_property /dev/null PROP_BAR bar
-
-: Multiple setting of the same property is handled correctly
-
-udevadm control --property FOO=foo --property FOO=42
-udevadm trigger --action change --settle /dev/null
-test_property /dev/null PROP_FOO 42
-
-: Mix of settings and removals of the same property is handled correctly
-
-udevadm control -p FOO= -p FOO=foo -p BAR=car -p BAR=
-udevadm trigger --action change --settle /dev/null
-test_property /dev/null PROP_FOO foo
-test_not_property /dev/null PROP_BAR
-
-exit 0
diff --git a/test/units/testsuite-17.credentials.sh b/test/units/testsuite-17.credentials.sh
deleted file mode 100755 (executable)
index 42d3883..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-if [[ $(systemctl is-enabled systemd-udev-load-credentials.service) == not-found ]]; then
-    echo "Missing systemd-udev-load-credentials.service" >>/skipped
-    exit 0
-fi
-
-at_exit() {
-    rm -f /run/credstore/udev.*
-    rm -f /run/udev/udev.conf.d/*
-    rm -f /run/udev/rules.d/*
-    rm -rf /run/systemd/system/systemd-udev-load-credentials.service.d
-}
-
-trap at_exit EXIT
-
-mkdir -p /run/credstore
-cat > /run/credstore/udev.conf.50-testme <<EOF
-udev_log=debug
-EOF
-cat > /run/credstore/udev.rules.50-testme <<EOF
-SUBSYSTEM=="net", OPTIONS="log_level=debug"
-EOF
-
-systemctl edit systemd-udev-load-credentials.service --stdin --drop-in=50-testme.conf <<EOF
-[Service]
-LoadCredential=udev.conf.50-testme
-LoadCredential=udev.rules.50-testme
-EOF
-
-systemctl restart systemd-udev-load-credentials.service
-
-diff /run/credstore/udev.conf.50-testme /run/udev/udev.conf.d/50-testme.conf
-diff /run/credstore/udev.rules.50-testme /run/udev/rules.d/50-testme.rules
diff --git a/test/units/testsuite-17.link-property.sh b/test/units/testsuite-17.link-property.sh
deleted file mode 100755 (executable)
index 517cc3f..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-udevadm control --log-level=debug
-
-mkdir -p /run/systemd/network/
-cat >/run/systemd/network/10-test.link <<EOF
-[Match]
-Kind=dummy
-MACAddress=00:50:56:c0:00:19
-
-[Link]
-Name=test1
-EOF
-
-mkdir /run/systemd/network/10-test.link.d
-cat >/run/systemd/network/10-test.link.d/10-override.conf <<EOF
-[Link]
-Property=HOGE=foo BAR=baz SHOULD_BE_UNSET=unset
-UnsetProperty=SHOULD_BE_UNSET
-EOF
-
-udevadm control --reload
-
-ip link add address 00:50:56:c0:00:19 type dummy
-udevadm wait --settle --timeout=30 /sys/class/net/test1
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_in "HOGE=foo" "$output"
-assert_in "BAR=baz" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-cat >/run/systemd/network/10-test.link.d/11-override.conf <<EOF
-[Link]
-Property=
-Property=HOGE2=foo2 BAR2=baz2 SHOULD_BE_UNSET=unset
-ImportProperty=HOGE
-EOF
-
-udevadm control --reload
-
-udevadm trigger --settle --action add /sys/class/net/test1
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_in "HOGE=foo" "$output"
-assert_in "HOGE2=foo2" "$output"
-assert_not_in "BAR=" "$output"
-assert_in "BAR2=baz2" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-# On change event, .link file will not be applied.
-udevadm trigger --settle --action change /sys/class/net/test1
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-### testing with udevadm test-builtin
-output=$(udevadm test-builtin --action add net_setup_link /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_in "HOGE2=foo2" "$output"
-assert_not_in "BAR=" "$output"
-assert_in "BAR2=baz2" "$output"
-assert_in "SHOULD_BE_UNSET=" "$output"  # this is expected, as an empty assignment is also logged.
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-# check that test-builtin command does not update udev database.
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-output=$(udevadm test-builtin --action change net_setup_link /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-### testing with udevadm test
-output=$(udevadm test --action add /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_in "HOGE2=foo2" "$output"
-assert_not_in "BAR=" "$output"
-assert_in "BAR2=baz2" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-# check that test command does not update udev database.
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-output=$(udevadm test --action change /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_not_in "HOGE=" "$output"
-assert_not_in "HOGE2=" "$output"
-assert_not_in "BAR=" "$output"
-assert_not_in "BAR2=" "$output"
-assert_not_in "SHOULD_BE_UNSET=" "$output"
-assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
-assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
-assert_in "ID_NET_NAME=test1" "$output"
-
-# test for specifiers
-cat >/run/systemd/network/10-test.link.d/12-override.conf <<EOF
-[Link]
-Property=
-Property=LINK_VERSION=%v
-EOF
-
-udevadm control --reload
-
-output=$(udevadm test --action add /sys/class/net/test1)
-assert_in "LINK_VERSION=$(uname -r)" "$output"
-
-udevadm trigger --settle --action add /sys/class/net/test1
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_in "LINK_VERSION=$(uname -r)" "$output"
-
-# test for constant properties
-cat >/run/systemd/network/10-test.link.d/13-override.conf <<EOF
-[Link]
-Property=
-Property=ACTION=foo IFINDEX=bar
-UnsetProperty=DEVPATH
-EOF
-
-udevadm control --reload
-
-output=$(udevadm test --action add /sys/class/net/test1)
-assert_in "ACTION=add" "$output"
-assert_not_in "ACTION=foo" "$output"
-assert_in "IFINDEX=" "$output"
-assert_not_in "IFINDEX=bar" "$output"
-assert_in "DEVPATH=" "$output"
-
-udevadm trigger --settle --action add /sys/class/net/test1
-output=$(udevadm info --query property /sys/class/net/test1)
-assert_not_in "ACTION=foo" "$output"
-assert_in "IFINDEX=" "$output"
-assert_not_in "IFINDEX=bar" "$output"
-assert_in "DEVPATH=" "$output"
-
-# cleanup
-ip link del dev test1
-
-rm -f /run/systemd/network/10-test.link
-rm -rf /run/systemd/network/10-test.link.d
-udevadm control --reload --log-level=info
-
-exit 0
diff --git a/test/units/testsuite-17.service b/test/units/testsuite-17.service
deleted file mode 100644 (file)
index d218d72..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-17-UDEV
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-17.sh b/test/units/testsuite-17.sh
deleted file mode 100755 (executable)
index 14ceeba..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-udevadm settle
-
-run_subtests
-
-touch /testok
diff --git a/test/units/testsuite-18.service b/test/units/testsuite-18.service
deleted file mode 100644 (file)
index 16d90a1..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-18-FAILUREACTION
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-18.sh b/test/units/testsuite-18.sh
deleted file mode 100755 (executable)
index 364c20d..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemd-run --wait -p FailureAction=poweroff true
-(! systemd-run --wait -p SuccessAction=poweroff false)
-
-if ! test -f /firstphase ; then
-    echo OK >/firstphase
-    systemd-run --wait -p SuccessAction=reboot true
-else
-    echo OK >/testok
-    systemd-run --wait -p FailureAction=exit -p FailureActionExitStatus=123 false
-fi
-
-sleep infinity
diff --git a/test/units/testsuite-19.ExitType-cgroup.sh b/test/units/testsuite-19.ExitType-cgroup.sh
deleted file mode 100755 (executable)
index cd221d7..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/env bash
-set -eux
-
-# Test ExitType=cgroup
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-if [[ "$(get_cgroup_hierarchy)" != unified ]]; then
-    echo "Skipping $0 as we're not running with the unified cgroup hierarchy"
-    exit 0
-fi
-
-systemd-analyze log-level debug
-
-# Multiple level process tree, parent process stays up
-cat >/tmp/test19-exit-cgroup.sh <<EOF
-#!/usr/bin/env bash
-set -eux
-
-# process tree: systemd -> sleep
-sleep infinity &
-disown
-
-# process tree: systemd -> bash -> bash -> sleep
-((sleep infinity); true) &
-
-systemd-notify --ready
-
-# Run the stop/kill command
-\$1 &
-
-# process tree: systemd -> bash -> sleep
-sleep infinity
-EOF
-chmod +x /tmp/test19-exit-cgroup.sh
-
-# service should be stopped cleanly
-systemd-run --wait \
-           --unit=one \
-           --property="Type=notify" \
-           --property="ExitType=cgroup" \
-           /tmp/test19-exit-cgroup.sh 'systemctl stop one'
-
-# same thing with a truthy exec condition
-systemd-run --wait \
-            --unit=two \
-            --property="Type=notify" \
-            --property="ExitType=cgroup" \
-            --property="ExecCondition=true" \
-            /tmp/test19-exit-cgroup.sh 'systemctl stop two'
-
-# false exec condition: systemd-run should exit immediately with status code: 1
-(! systemd-run --wait \
-               --unit=three \
-               --property="Type=notify" \
-               --property="ExitType=cgroup" \
-               --property="ExecCondition=false" \
-               /tmp/test19-exit-cgroup.sh)
-
-# service should exit uncleanly (main process exits with SIGKILL)
-(! systemd-run --wait \
-               --unit=four \
-               --property="Type=notify" \
-               --property="ExitType=cgroup" \
-               /tmp/test19-exit-cgroup.sh 'systemctl kill --signal 9 four')
-
-
-# Multiple level process tree, parent process exits quickly
-cat >/tmp/test19-exit-cgroup-parentless.sh <<EOF
-#!/usr/bin/env bash
-set -eux
-
-# process tree: systemd -> sleep
-sleep infinity &
-
-# process tree: systemd -> bash -> sleep
-((sleep infinity); true) &
-
-systemd-notify --ready
-
-# Run the stop/kill command after this bash process exits
-(sleep 1; \$1) &
-EOF
-chmod +x /tmp/test19-exit-cgroup-parentless.sh
-
-# service should be stopped cleanly
-systemd-run --wait \
-            --unit=five \
-            --property="Type=notify" \
-            --property="ExitType=cgroup" \
-            /tmp/test19-exit-cgroup-parentless.sh 'systemctl stop five'
-
-# service should still exit cleanly despite SIGKILL (the main process already exited cleanly)
-systemd-run --wait \
-            --unit=six \
-            --property="Type=notify" \
-            --property="ExitType=cgroup" \
-            /tmp/test19-exit-cgroup-parentless.sh 'systemctl kill --signal 9 six'
-
-
-systemd-analyze log-level info
diff --git a/test/units/testsuite-19.cleanup-slice.sh b/test/units/testsuite-19.cleanup-slice.sh
deleted file mode 100755 (executable)
index 5d63160..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-
-# Create service with KillMode=none inside a slice
-cat <<EOF >/run/systemd/system/test19cleanup.service
-[Unit]
-Description=Test 19 cleanup Service
-[Service]
-Slice=test19cleanup.slice
-Type=exec
-ExecStart=sleep infinity
-KillMode=none
-EOF
-cat <<EOF >/run/systemd/system/test19cleanup.slice
-[Unit]
-Description=Test 19 cleanup Slice
-EOF
-
-# Start service
-systemctl start test19cleanup.service
-assert_rc 0 systemd-cgls /test19cleanup.slice
-
-pid=$(systemctl show --property MainPID --value test19cleanup)
-ps "$pid"
-
-# Stop slice
-# The sleep process will not be killed because of KillMode=none
-# Since there is still a process running under it, the /test19cleanup.slice cgroup won't be removed
-systemctl stop test19cleanup.slice
-
-ps "$pid"
-
-# Kill sleep process manually
-kill -s TERM "$pid"
-while kill -0 "$pid" 2>/dev/null; do sleep 0.1; done
-
-timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice/test19cleanup.service >& /dev/null; do sleep .5; done'
-assert_rc 1 systemd-cgls /test19cleanup.slice/test19cleanup.service
-
-# Check that empty cgroup /test19cleanup.slice has been removed
-timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice >& /dev/null; do sleep .5; done'
-assert_rc 1 systemd-cgls /test19cleanup.slice
diff --git a/test/units/testsuite-19.delegate.sh b/test/units/testsuite-19.delegate.sh
deleted file mode 100755 (executable)
index 74d36c4..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test cgroup delegation in the unified hierarchy
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-if [[ "$(get_cgroup_hierarchy)" != unified ]]; then
-    echo "Skipping $0 as we're not running with the unified cgroup hierarchy"
-    exit 0
-fi
-
-at_exit() {
-    set +e
-    userdel -r test
-}
-
-systemd-run --wait \
-            --unit=test-0.service \
-            --property="DynamicUser=1" \
-            --property="Delegate=" \
-            test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \
-                 -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.procs -a \
-                 -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.subtree_control
-
-# Test if this also works for some of the more recent attrs the kernel might or might not support
-for attr in cgroup.threads memory.oom.group memory.reclaim ; do
-
-    if grep -q "$attr" /sys/kernel/cgroup/delegate ; then
-        systemd-run --wait \
-                    --unit=test-0.service \
-                    --property="DynamicUser=1" \
-                    --property="Delegate=" \
-                    test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \
-                    -w /sys/fs/cgroup/system.slice/test-0.service/"$attr"
-    fi
-done
-
-systemd-run --wait \
-            --unit=test-1.service \
-            --property="DynamicUser=1" \
-            --property="Delegate=memory pids" \
-            grep -q memory /sys/fs/cgroup/system.slice/test-1.service/cgroup.controllers
-
-systemd-run --wait \
-            --unit=test-2.service \
-            --property="DynamicUser=1" \
-            --property="Delegate=memory pids" \
-            grep -q pids /sys/fs/cgroup/system.slice/test-2.service/cgroup.controllers
-
-# "io" is not among the controllers enabled by default for all units, verify that
-grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
-
-# Run a service with "io" enabled, and verify it works
-systemd-run --wait \
-            --unit=test-3.service \
-            --property="IOAccounting=yes" \
-            --property="Slice=system-foo-bar-baz.slice"  \
-            grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test-3.service/cgroup.controllers
-
-# We want to check if "io" is removed again from the controllers
-# list. However, PID 1 (rightfully) does this asynchronously. In order
-# to force synchronization on this, let's start a short-lived service
-# which requires PID 1 to refresh the cgroup tree, so that we can
-# verify that this all works.
-systemd-run --wait --unit=test-4.service true
-
-# And now check again, "io" should have vanished
-grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
-
-# Check that unprivileged delegation works for scopes
-useradd test ||:
-systemd-run --uid=test \
-            --property="User=test" \
-            --property="Delegate=yes" \
-            --slice workload.slice \
-            --unit test-workload0.scope\
-            --scope \
-            test -w /sys/fs/cgroup/workload.slice/test-workload0.scope -a \
-                 -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.procs -a \
-                 -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.subtree_control
-
-# Verify that DelegateSubgroup= affects ownership correctly
-unit="test-subgroup-$RANDOM.service"
-systemd-run --wait \
-            --unit="$unit" \
-            --property="DynamicUser=1" \
-            --property="Delegate=pids" \
-            --property="DelegateSubgroup=foo" \
-            test -w "/sys/fs/cgroup/system.slice/$unit" -a \
-                 -w "/sys/fs/cgroup/system.slice/$unit/foo"
-
-# Check that for the subgroup also attributes that aren't covered by
-# regular (i.e. main cgroup) delegation ownership rules are delegated properly
-if test -f /sys/fs/cgroup/cgroup.max.depth; then
-    unit="test-subgroup-$RANDOM.service"
-    systemd-run --wait \
-                --unit="$unit" \
-                --property="DynamicUser=1" \
-                --property="Delegate=pids" \
-                --property="DelegateSubgroup=zzz" \
-                test -w "/sys/fs/cgroup/system.slice/$unit/zzz/cgroup.max.depth"
-fi
-
-# Check that the invoked process itself is also in the subgroup
-unit="test-subgroup-$RANDOM.service"
-systemd-run --wait \
-            --unit="$unit" \
-            --property="DynamicUser=1" \
-            --property="Delegate=pids" \
-            --property="DelegateSubgroup=bar" \
-            grep -q -x -F "0::/system.slice/$unit/bar" /proc/self/cgroup
diff --git a/test/units/testsuite-19.service b/test/units/testsuite-19.service
deleted file mode 100644 (file)
index 9ee5fc9..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-19-DELEGATE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-19.sh b/test/units/testsuite-19.sh
deleted file mode 100755 (executable)
index 60c53cd..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-. /etc/os-release
-# FIXME: This test fails on opensuse with the following error and others:
-# Apr 25 10:24:04 H (cat)[910]: device-mapper: create ioctl on ... failed: Device or resource busy
-if [[ "$ID" =~ "opensuse" ]]; then
-    echo "Skipping due to known unexpected behaviour in OpenSUSE kernels" >>/skipped
-    exit 77
-fi
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
diff --git a/test/units/testsuite-21.service b/test/units/testsuite-21.service
deleted file mode 100644 (file)
index a5f77d0..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Fuzz our D-Bus interfaces with dfuzzer
-After=dbus.service multi-user.target
-Wants=dbus.service multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /skipped /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-21.sh b/test/units/testsuite-21.sh
deleted file mode 100755 (executable)
index 08ebfd9..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# check dfuzzer is present before testing
-if ! command -v dfuzzer &>/dev/null; then
-    echo "dfuzzer is not installed, skipping" | tee --append /skipped
-    exit 77
-fi
-
-# Save the end.service state before we start fuzzing, as it might get changed
-# on the fly by one of the fuzzers
-systemctl list-jobs | grep -F 'end.service' && SHUTDOWN_AT_EXIT=1 || SHUTDOWN_AT_EXIT=0
-
-# shellcheck disable=SC2317
-at_exit() {
-    set +e
-    # We have to call the end.service/poweroff explicitly even if it's specified on
-    # the kernel cmdline via systemd.wants=end.service, since dfuzzer calls
-    # org.freedesktop.systemd1.Manager.ClearJobs() which drops the service
-    # from the queue
-    if [[ $SHUTDOWN_AT_EXIT -ne 0 ]] && ! systemctl poweroff; then
-        # PID1 is down let's try to save the journal
-        journalctl --sync      # journal can be down as well so let's ignore exit codes here
-        systemctl -ff poweroff # sync() and reboot(RB_POWER_OFF)
-    fi
-}
-
-add_suppression() {
-    local interface="${1:?}"
-    local suppression="${2:?}"
-
-    sed -i "\%\[$interface\]%a$suppression" /etc/dfuzzer.conf
-}
-
-trap at_exit EXIT
-
-systemctl log-level info
-
-# FIXME: systemd-run doesn't play well with daemon-reexec
-# See: https://github.com/systemd/systemd/issues/27204
-add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:Reexecute FIXME"
-
-add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:SoftReboot destructive"
-add_suppression "org.freedesktop.login1" "Sleep destructive"
-
-# Skip calling start and stop methods on unit objects, as doing that is not only time consuming, but it also
-# starts/stops units that interfere with the machine state. The actual code paths should be covered (to some
-# degree) by the respective method counterparts on the manager object.
-for method in Start Stop Restart ReloadOrRestart ReloadOrTryRestart Kill; do
-    add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Unit:$method"
-done
-
-cat /etc/dfuzzer.conf
-
-# TODO
-#   * check for possibly newly introduced buses?
-BUS_LIST=(
-    org.freedesktop.home1
-    org.freedesktop.hostname1
-    org.freedesktop.import1
-    org.freedesktop.locale1
-    org.freedesktop.login1
-    org.freedesktop.machine1
-    org.freedesktop.portable1
-    org.freedesktop.resolve1
-    org.freedesktop.systemd1
-    org.freedesktop.timedate1
-)
-
-# systemd-oomd requires PSI
-if tail -n +1 /proc/pressure/{cpu,io,memory}; then
-    BUS_LIST+=(
-        org.freedesktop.oom1
-    )
-fi
-
-# Some services require specific conditions:
-#   - systemd-timesyncd can't run in a container
-#   - systemd-networkd can run in a container if it has CAP_NET_ADMIN capability
-if ! systemd-detect-virt --container; then
-    BUS_LIST+=(
-        org.freedesktop.network1
-        org.freedesktop.timesync1
-    )
-elif busctl introspect org.freedesktop.network1 / &>/dev/null; then
-    BUS_LIST+=(
-        org.freedesktop.network1
-    )
-fi
-
-SESSION_BUS_LIST=(
-    org.freedesktop.systemd1
-)
-
-# Maximum payload size generated by dfuzzer (in bytes) - default: 50K
-PAYLOAD_MAX=50000
-# Tweak the maximum payload size if we're running under sanitizers, since
-# with larger payloads we start hitting reply timeouts
-if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
-    PAYLOAD_MAX=10000 # 10K
-fi
-
-# Overmount /var/lib/machines with a size-limited tmpfs, as fuzzing
-# the org.freedesktop.machine1 stuff makes quite a mess
-mount -t tmpfs -o size=50M tmpfs /var/lib/machines
-
-# Fuzz both the system and the session buses (where applicable)
-for bus in "${BUS_LIST[@]}"; do
-    echo "Bus: $bus (system)"
-    systemd-run --pipe --wait \
-                -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus"
-
-    # Let's reload the systemd daemon to test (de)serialization as well
-    systemctl daemon-reload
-    # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved
-    systemctl daemon-reexec
-done
-
-umount /var/lib/machines
-
-for bus in "${SESSION_BUS_LIST[@]}"; do
-    echo "Bus: $bus (session)"
-    systemd-run --machine 'testuser@.host' --user --pipe --wait \
-                -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus"
-
-    # Let's reload the systemd user daemon to test (de)serialization as well
-    systemctl --machine 'testuser@.host' --user daemon-reload
-    # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved
-    systemctl --machine 'testuser@.host' --user daemon-reexec
-done
-
-touch /testok
diff --git a/test/units/testsuite-22.01.sh b/test/units/testsuite-22.01.sh
deleted file mode 100755 (executable)
index 2276b75..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# With "e" don't attempt to set permissions when file doesn't exist, see
-# https://github.com/systemd/systemd/pull/6682.
-set -eux
-set -o pipefail
-
-rm -fr /tmp/test
-
-echo "e /tmp/test - root root 1d" | systemd-tmpfiles --create -
-
-test ! -e /tmp/test
diff --git a/test/units/testsuite-22.02.sh b/test/units/testsuite-22.02.sh
deleted file mode 100755 (executable)
index f191ae3..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Basic tests for types creating directories
-set -eux
-set -o pipefail
-
-rm -fr /tmp/{C,d,D,e}
-mkdir  /tmp/{C,d,D,e}
-
-#
-# 'd'
-#
-mkdir /tmp/d/2
-chmod 777 /tmp/d/2
-
-systemd-tmpfiles --dry-run --create - <<EOF
-d     /tmp/d/1    0755 daemon daemon - -
-d     /tmp/d/2    0755 daemon daemon - -
-EOF
-
-test ! -d /tmp/d/1
-test -d /tmp/d/2
-
-systemd-tmpfiles --create - <<EOF
-d     /tmp/d/1    0755 daemon daemon - -
-d     /tmp/d/2    0755 daemon daemon - -
-EOF
-
-test -d /tmp/d/1
-test "$(stat -c %U:%G:%a /tmp/d/1)" = "daemon:daemon:755"
-
-test -d /tmp/d/2
-test "$(stat -c %U:%G:%a /tmp/d/2)" = "daemon:daemon:755"
-
-#
-# 'D'
-#
-mkdir /tmp/D/2
-chmod 777 /tmp/D/2
-touch /tmp/D/2/foo
-
-systemd-tmpfiles --create - <<EOF
-D     /tmp/D/1    0755 daemon daemon - -
-D     /tmp/D/2    0755 daemon daemon - -
-EOF
-
-test -d /tmp/D/1
-test "$(stat -c %U:%G:%a /tmp/D/1)" = "daemon:daemon:755"
-
-test -d /tmp/D/2
-test "$(stat -c %U:%G:%a /tmp/D/2)" = "daemon:daemon:755"
-
-systemd-tmpfiles --remove - <<EOF
-D     /tmp/D/2    0755 daemon daemon - -
-EOF
-
-# the content of '2' should be removed
-test "$(echo /tmp/D/2/*)" = "/tmp/D/2/*"
-
-#
-# 'e'
-#
-mkdir -p /tmp/e/2/{d1,d2}
-chmod 777 /tmp/e/2
-chmod 777 /tmp/e/2/d*
-
-systemd-tmpfiles --create - <<EOF
-e     /tmp/e/1     0755 daemon daemon - -
-e     /tmp/e/2/*   0755 daemon daemon - -
-EOF
-
-test ! -d /tmp/e/1
-
-test -d /tmp/e/2
-test "$(stat -c %U:%G:%a /tmp/e/2)" = "root:root:777"
-
-test -d /tmp/e/2/d1
-test "$(stat -c %U:%G:%a /tmp/e/2/d1)" = "daemon:daemon:755"
-test -d /tmp/e/2/d2
-test "$(stat -c %U:%G:%a /tmp/e/2/d2)" = "daemon:daemon:755"
-
-# 'e' operates on directories only
-mkdir -p /tmp/e/3/{d1,d2}
-chmod 777 /tmp/e/3
-chmod 777 /tmp/e/3/d*
-touch /tmp/e/3/f1
-chmod 644 /tmp/e/3/f1
-
-systemd-tmpfiles --create - <<EOF
-e     /tmp/e/3/*   0755 daemon daemon - -
-EOF
-
-# the directories should have been processed although systemd-tmpfiles failed
-# previously due to the presence of a file.
-test -d /tmp/e/3/d1
-test "$(stat -c %U:%G:%a /tmp/e/3/d1)" = "daemon:daemon:755"
-test -d /tmp/e/3/d2
-test "$(stat -c %U:%G:%a /tmp/e/3/d2)" = "daemon:daemon:755"
-
-test -f /tmp/e/3/f1
-test "$(stat -c %U:%G:%a /tmp/e/3/f1)" = "root:root:644"
-
-#
-# 'C'
-#
-
-mkdir /tmp/C/{0,1,2,3}-origin
-touch /tmp/C/{1,2,3}-origin/f1
-chmod 755 /tmp/C/{1,2,3}-origin/f1
-
-mkdir /tmp/C/{2,3}
-touch /tmp/C/3/f1
-
-systemd-tmpfiles --dry-run --create - <<EOF
-C     /tmp/C/1    0755 daemon daemon - /tmp/C/1-origin
-C     /tmp/C/2    0755 daemon daemon - /tmp/C/2-origin
-EOF
-
-test ! -d /tmp/C/1
-test -d /tmp/C/2
-
-systemd-tmpfiles --create - <<EOF
-C     /tmp/C/1    0755 daemon daemon - /tmp/C/1-origin
-C     /tmp/C/2    0755 daemon daemon - /tmp/C/2-origin
-EOF
-
-test -d /tmp/C/1
-test "$(stat -c %U:%G:%a /tmp/C/1/f1)" = "daemon:daemon:755"
-test -d /tmp/C/2
-test "$(stat -c %U:%G:%a /tmp/C/2/f1)" = "daemon:daemon:755"
-
-systemd-tmpfiles --create - <<EOF
-C     /tmp/C/3    0755 daemon daemon - /tmp/C/3-origin
-C     /tmp/C/4    0755 daemon daemon - /tmp/C/definitely-missing
-EOF
-
-test "$(stat -c %U:%G:%a /tmp/C/3/f1)" = "root:root:644"
-test ! -e /tmp/C/4
-
-touch /tmp/C/3-origin/f{2,3,4}
-echo -n ABC > /tmp/C/3/f1
-
-systemd-tmpfiles --create - <<EOF
-C+     /tmp/C/3    0755 daemon daemon - /tmp/C/3-origin
-EOF
-
-# Test that the trees got merged, even though /tmp/C/3 already exists.
-test -e /tmp/C/3/f1
-test -e /tmp/C/3/f2
-test -e /tmp/C/3/f3
-test -e /tmp/C/3/f4
-
-# Test that /tmp/C/3/f1 did not get overwritten.
-test "$(cat /tmp/C/3/f1)" = "ABC"
-
-# Check that %U expands to 0, both in the path and in the argument.
-home='/tmp/C'
-systemd-tmpfiles --create - <<EOF
-C     $home/%U    - - - - $home/%U-origin
-EOF
-
-test -d "$home/0"
-
-# Check that %h expands to $home, both in the path and in the argument.
-HOME="$home" \
-systemd-tmpfiles --create - <<EOF
-C     %h/5    - - - - %h/3-origin
-EOF
-
-test -f "$home/5/f1"
-
-# Check that %h in the path is expanded, but
-# the result of this expansion is not expanded once again.
-root='/tmp/C/6'
-home='/%U'
-mkdir -p "$root/usr/share/factory$home"
-HOME="$home" \
-systemd-tmpfiles --create --root="$root" - <<EOF
-C     %h    - - - -
-EOF
-
-test -d "$root$home"
diff --git a/test/units/testsuite-22.03.sh b/test/units/testsuite-22.03.sh
deleted file mode 100755 (executable)
index d158498..0000000
+++ /dev/null
@@ -1,275 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Basic tests for types creating/writing files
-set -eux
-set -o pipefail
-
-rm -fr /tmp/{f,F,w}
-mkdir  /tmp/{f,F,w}
-touch /tmp/file-owned-by-root
-
-#
-# 'f'
-#
-systemd-tmpfiles --dry-run --create - <<EOF
-f     /tmp/f/1    0644 - - - -
-f     /tmp/f/2    0644 - - - This string should be written
-EOF
-
-test ! -e /tmp/f/1
-test ! -e /tmp/f/2
-
-systemd-tmpfiles --create - <<EOF
-f     /tmp/f/1    0644 - - - -
-f     /tmp/f/2    0644 - - - This string should be written
-EOF
-
-### '1' should exist and be empty
-test -f /tmp/f/1; test ! -s /tmp/f/1
-test "$(stat -c %U:%G:%a /tmp/f/1)" = "root:root:644"
-
-test "$(stat -c %U:%G:%a /tmp/f/2)" = "root:root:644"
-test "$(< /tmp/f/2)" = "This string should be written"
-
-### The perms are supposed to be updated even if the file already exists.
-systemd-tmpfiles --create - <<EOF
-f     /tmp/f/1    0666 daemon daemon - This string should not be written
-EOF
-
-# file should be empty
-test ! -s /tmp/f/1
-test "$(stat -c %U:%G:%a /tmp/f/1)" = "daemon:daemon:666"
-
-### But we shouldn't try to set perms on an existing file which is not a
-### regular one.
-mkfifo /tmp/f/fifo
-chmod 644 /tmp/f/fifo
-
-(! systemd-tmpfiles --create -) <<EOF
-f     /tmp/f/fifo    0666 daemon daemon - This string should not be written
-EOF
-
-test -p /tmp/f/fifo
-test "$(stat -c %U:%G:%a /tmp/f/fifo)" = "root:root:644"
-
-### 'f' should not follow symlinks.
-ln -s missing /tmp/f/dangling
-ln -s /tmp/file-owned-by-root /tmp/f/symlink
-
-(! systemd-tmpfiles --create -) <<EOF
-f     /tmp/f/dangling    0644 daemon daemon - -
-f     /tmp/f/symlink     0644 daemon daemon - -
-EOF
-test ! -e /tmp/f/missing
-test "$(stat -c %U:%G:%a /tmp/file-owned-by-root)" = "root:root:644"
-
-### Handle read-only filesystem gracefully: we shouldn't fail if the target
-### already exists and have the correct perms.
-mkdir /tmp/f/rw-fs
-mkdir /tmp/f/ro-fs
-
-touch /tmp/f/rw-fs/foo
-chmod 644 /tmp/f/rw-fs/foo
-
-mount -o bind,ro /tmp/f/rw-fs /tmp/f/ro-fs
-
-systemd-tmpfiles --create - <<EOF
-f     /tmp/f/ro-fs/foo    0644 - - - - This string should not be written
-EOF
-test -f /tmp/f/ro-fs/foo; test ! -s /tmp/f/ro-fs/foo
-
-(! systemd-tmpfiles --create -) <<EOF
-f     /tmp/f/ro-fs/foo    0666 - - - -
-EOF
-test "$(stat -c %U:%G:%a /tmp/f/fifo)" = "root:root:644"
-
-(! systemd-tmpfiles --create -) <<EOF
-f     /tmp/f/ro-fs/bar    0644 - - - -
-EOF
-test ! -e /tmp/f/ro-fs/bar
-
-### 'f' shouldn't follow unsafe paths.
-mkdir /tmp/f/daemon
-ln -s /root /tmp/f/daemon/unsafe-symlink
-chown -R --no-dereference daemon:daemon /tmp/f/daemon
-
-(! systemd-tmpfiles --create -) <<EOF
-f     /tmp/f/daemon/unsafe-symlink/exploit    0644 daemon daemon - -
-EOF
-test ! -e /tmp/f/daemon/unsafe-symlink/exploit
-
-#
-# 'F'
-#
-echo "This should be truncated" >/tmp/F/truncated
-echo "This should be truncated" >/tmp/F/truncated-with-content
-
-systemd-tmpfiles --create - <<EOF
-F     /tmp/F/created                0644 - - - -
-F     /tmp/F/created-with-content   0644 - - - new content
-F     /tmp/F/truncated              0666 daemon daemon - -
-F     /tmp/F/truncated-with-content 0666 daemon daemon - new content
-EOF
-
-test -f /tmp/F/created; test ! -s /tmp/F/created
-test -f /tmp/F/created-with-content
-test "$(< /tmp/F/created-with-content)" = "new content"
-test -f /tmp/F/truncated; test ! -s /tmp/F/truncated
-test "$(stat -c %U:%G:%a /tmp/F/truncated)" = "daemon:daemon:666"
-test -s /tmp/F/truncated-with-content
-test "$(stat -c %U:%G:%a /tmp/F/truncated-with-content)" = "daemon:daemon:666"
-
-### We shouldn't try to truncate anything but regular files since the behavior is
-### unspecified in the other cases.
-mkfifo /tmp/F/fifo
-
-(! systemd-tmpfiles --create -) <<EOF
-F     /tmp/F/fifo                0644 - - - -
-EOF
-
-test -p /tmp/F/fifo
-
-### 'F' should not follow symlinks.
-ln -s missing /tmp/F/dangling
-ln -s /tmp/file-owned-by-root /tmp/F/symlink
-
-(! systemd-tmpfiles --create -) <<EOF
-f     /tmp/F/dangling    0644 daemon daemon - -
-f     /tmp/F/symlink     0644 daemon daemon - -
-EOF
-test ! -e /tmp/F/missing
-test "$(stat -c %U:%G:%a /tmp/file-owned-by-root)" = "root:root:644"
-
-### Handle read-only filesystem gracefully: we shouldn't fail if the target
-### already exists and is empty.
-mkdir /tmp/F/rw-fs
-mkdir /tmp/F/ro-fs
-
-touch /tmp/F/rw-fs/foo
-chmod 644 /tmp/F/rw-fs/foo
-
-mount -o bind,ro /tmp/F/rw-fs /tmp/F/ro-fs
-
-systemd-tmpfiles --create - <<EOF
-F     /tmp/F/ro-fs/foo    0644 - - - -
-EOF
-test -f /tmp/F/ro-fs/foo; test ! -s /tmp/F/ro-fs/foo
-
-echo "truncating is not allowed anymore" >/tmp/F/rw-fs/foo
-(! systemd-tmpfiles --create -) <<EOF
-F     /tmp/F/ro-fs/foo    0644 - - - -
-EOF
-
-(! systemd-tmpfiles --create -) <<EOF
-F     /tmp/F/ro-fs/foo    0644 - - - - This string should not be written
-EOF
-test -f /tmp/F/ro-fs/foo
-grep -q 'truncating is not allowed' /tmp/F/ro-fs/foo
-
-# Trying to change the perms should fail.
-: >/tmp/F/rw-fs/foo
-(! systemd-tmpfiles --create -) <<EOF
-F     /tmp/F/ro-fs/foo    0666 - - - -
-EOF
-test "$(stat -c %U:%G:%a /tmp/F/ro-fs/foo)" = "root:root:644"
-
-### Try to create a new file.
-(! systemd-tmpfiles --create -) <<EOF
-F     /tmp/F/ro-fs/bar    0644 - - - -
-EOF
-test ! -e /tmp/F/ro-fs/bar
-
-### 'F' shouldn't follow unsafe paths.
-mkdir /tmp/F/daemon
-ln -s /root /tmp/F/daemon/unsafe-symlink
-chown -R --no-dereference daemon:daemon /tmp/F/daemon
-
-(! systemd-tmpfiles --create -) <<EOF
-F     /tmp/F/daemon/unsafe-symlink/exploit    0644 daemon daemon - -
-EOF
-test ! -e /tmp/F/daemon/unsafe-symlink/exploit
-
-#
-# 'w'
-#
-touch /tmp/w/overwritten
-touch /tmp/w/appended
-
-### nop if the target does not exist.
-systemd-tmpfiles --dry-run --create - <<EOF
-w     /tmp/w/unexistent    0644 - - - new content
-EOF
-test ! -e /tmp/w/unexistent
-
-systemd-tmpfiles --create - <<EOF
-w     /tmp/w/unexistent    0644 - - - new content
-EOF
-test ! -e /tmp/w/unexistent
-
-### no argument given -> fails.
-(! systemd-tmpfiles --create -) <<EOF
-w     /tmp/w/unexistent    0644 - - - -
-EOF
-
-### write into an empty file.
-systemd-tmpfiles --dry-run --create - <<EOF
-w     /tmp/w/overwritten    0644 - - - old content
-EOF
-test -f /tmp/w/overwritten
-test -z "$(< /tmp/w/overwritten)"
-
-systemd-tmpfiles --create - <<EOF
-w     /tmp/w/overwritten    0644 - - - old content
-EOF
-test -f /tmp/w/overwritten
-test "$(< /tmp/w/overwritten)" = "old content"
-
-### old content is overwritten
-systemd-tmpfiles --dry-run --create - <<EOF
-w     /tmp/w/overwritten    0644 - - - new content
-EOF
-test -f /tmp/w/overwritten
-test "$(< /tmp/w/overwritten)" = "old content"
-
-systemd-tmpfiles --create - <<EOF
-w     /tmp/w/overwritten    0644 - - - new content
-EOF
-test -f /tmp/w/overwritten
-test "$(< /tmp/w/overwritten)" = "new content"
-
-### append lines
-systemd-tmpfiles --create - <<EOF
-w+    /tmp/w/appended    0644 - - - 1
-w+    /tmp/w/appended    0644 - - - 2\n
-w+    /tmp/w/appended    0644 - - - 3
-EOF
-test -f /tmp/w/appended
-test "$(< /tmp/w/appended)" = "$(echo -ne '12\n3')"
-
-### writing into an 'exotic' file should be allowed.
-systemd-tmpfiles --dry-run --create - <<EOF
-w     /dev/null    - - - - new content
-EOF
-
-systemd-tmpfiles --create - <<EOF
-w     /dev/null    - - - - new content
-EOF
-
-### 'w' follows symlinks
-ln -s ./overwritten /tmp/w/symlink
-systemd-tmpfiles --create - <<EOF
-w     /tmp/w/symlink    - - - - $(readlink -e /tmp/w/symlink)
-EOF
-readlink -e /tmp/w/symlink
-test "$(< /tmp/w/overwritten)" = "/tmp/w/overwritten"
-
-### 'w' shouldn't follow unsafe paths.
-mkdir /tmp/w/daemon
-ln -s /root /tmp/w/daemon/unsafe-symlink
-chown -R --no-dereference daemon:daemon /tmp/w/daemon
-
-(! systemd-tmpfiles --create -) <<EOF
-f     /tmp/w/daemon/unsafe-symlink/exploit    0644 daemon daemon - -
-EOF
-test ! -e /tmp/w/daemon/unsafe-symlink/exploit
diff --git a/test/units/testsuite-22.04.sh b/test/units/testsuite-22.04.sh
deleted file mode 100755 (executable)
index 2c0ffe3..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Basic tests for types creating fifos
-set -eux
-set -o pipefail
-
-rm -fr /tmp/p
-mkdir  /tmp/p
-touch  /tmp/p/f1
-
-systemd-tmpfiles --dry-run --create - <<EOF
-p     /tmp/p/fifo1    0666 - - - -
-EOF
-
-test ! -p /tmp/p/fifo1
-
-systemd-tmpfiles --create - <<EOF
-p     /tmp/p/fifo1    0666 - - - -
-EOF
-
-test -p /tmp/p/fifo1
-test "$(stat -c %U:%G:%a /tmp/p/fifo1)" = "root:root:666"
-
-# Refuse to overwrite an existing file. Error is not propagated.
-systemd-tmpfiles --create - <<EOF
-p     /tmp/p/f1    0666 - - - -
-EOF
-
-test -f /tmp/p/f1
-
-# unless '+' prefix is used
-systemd-tmpfiles --create - <<EOF
-p+     /tmp/p/f1    0666 - - - -
-EOF
-
-test -p /tmp/p/f1
-test "$(stat -c %U:%G:%a /tmp/p/f1)" = "root:root:666"
-
-#
-# Must be fixed
-#
-# mkdir /tmp/p/daemon
-# #ln -s /root /tmp/F/daemon/unsafe-symlink
-# chown -R --no-dereference daemon:daemon /tmp/p/daemon
-#
-# systemd-tmpfiles --create - <<EOF
-# p      /tmp/p/daemon/fifo2    0666 daemon daemon - -
-# EOF
diff --git a/test/units/testsuite-22.05.sh b/test/units/testsuite-22.05.sh
deleted file mode 100755 (executable)
index d78e7ee..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-#! /bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-rm -fr /tmp/{z,Z}
-mkdir  /tmp/{z,Z}
-
-#
-# 'z'
-#
-mkdir /tmp/z/d{1,2}
-touch /tmp/z/f1 /tmp/z/d1/f11 /tmp/z/d2/f21
-
-systemd-tmpfiles --dry-run --create - <<EOF
-z     /tmp/z/f1    0755 daemon daemon - -
-z     /tmp/z/d1    0755 daemon daemon - -
-EOF
-
-test "$(stat -c %U /tmp/z/f1)" = "$USER"
-test "$(stat -c %U /tmp/z/d1)" = "$USER"
-test "$(stat -c %U /tmp/z/d1/f11)" = "$USER"
-
-systemd-tmpfiles --create - <<EOF
-z     /tmp/z/f1    0755 daemon daemon - -
-z     /tmp/z/d1    0755 daemon daemon - -
-EOF
-
-test "$(stat -c %U:%G /tmp/z/f1)" = "daemon:daemon"
-test "$(stat -c %U:%G /tmp/z/d1)" = "daemon:daemon"
-test "$(stat -c %U:%G /tmp/z/d1/f11)" = "root:root"
-
-systemd-tmpfiles --create - <<EOF
-z     /tmp/z/d2/*    0755 daemon daemon - -
-EOF
-
-test "$(stat -c %U:%G /tmp/z/d2/f21)" = "daemon:daemon"
-
-#
-# 'Z'
-#
-mkdir /tmp/Z/d1 /tmp/Z/d1/d11
-touch /tmp/Z/f1 /tmp/Z/d1/f11 /tmp/Z/d1/d11/f111
-
-systemd-tmpfiles --create - <<EOF
-Z     /tmp/Z/f1    0755 daemon daemon - -
-Z     /tmp/Z/d1    0755 daemon daemon - -
-EOF
-
-test "$(stat -c %U:%G /tmp/Z/f1)" = "daemon:daemon"
-test "$(stat -c %U:%G /tmp/Z/d1)" = "daemon:daemon"
-test "$(stat -c %U:%G /tmp/Z/d1/d11)" = "daemon:daemon"
-test "$(stat -c %U:%G /tmp/Z/d1/f11)" = "daemon:daemon"
-test "$(stat -c %U:%G /tmp/Z/d1/d11/f111)" = "daemon:daemon"
diff --git a/test/units/testsuite-22.06.sh b/test/units/testsuite-22.06.sh
deleted file mode 100755 (executable)
index 45aea9c..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Inspired by https://github.com/systemd/systemd/issues/9508
-set -eux
-set -o pipefail
-
-test_snippet() {
-    # First call with --dry-run to test the code paths
-    systemd-tmpfiles --dry-run "$@" - <<EOF
-d /var/tmp/foobar-test-06
-d /var/tmp/foobar-test-06/important
-R /var/tmp/foobar-test-06
-EOF
-
-    systemd-tmpfiles "$@" - <<EOF
-d /var/tmp/foobar-test-06
-d /var/tmp/foobar-test-06/important
-R /var/tmp/foobar-test-06
-EOF
-}
-
-test_snippet --create --remove
-test -d /var/tmp/foobar-test-06
-test -d /var/tmp/foobar-test-06/important
-
-test_snippet --remove
-test ! -f /var/tmp/foobar-test-06
-test ! -f /var/tmp/foobar-test-06/important
-
-test_snippet --create
-test -d /var/tmp/foobar-test-06
-test -d /var/tmp/foobar-test-06/important
-
-touch /var/tmp/foobar-test-06/something-else
-
-test_snippet --create
-test -d /var/tmp/foobar-test-06
-test -d /var/tmp/foobar-test-06/important
-test -f /var/tmp/foobar-test-06/something-else
-
-test_snippet --create --remove
-test -d /var/tmp/foobar-test-06
-test -d /var/tmp/foobar-test-06/important
-test ! -f /var/tmp/foobar-test-06/something-else
diff --git a/test/units/testsuite-22.07.sh b/test/units/testsuite-22.07.sh
deleted file mode 100755 (executable)
index de20d5e..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Verifies the issues described by https://github.com/systemd/systemd/issues/10191
-set -eux
-set -o pipefail
-
-rm -rf /tmp/test-prefix
-
-mkdir /tmp/test-prefix
-touch /tmp/test-prefix/file
-
-systemd-tmpfiles --remove - <<EOF
-r /tmp/test-prefix
-r /tmp/test-prefix/file
-EOF
-
-test ! -f /tmp/test-prefix/file
-test ! -f /tmp/test-prefix
-
-mkdir /tmp/test-prefix
-touch /tmp/test-prefix/file
-
-systemd-tmpfiles --remove - <<EOF
-r /tmp/test-prefix/file
-r /tmp/test-prefix
-EOF
-
-test ! -f /tmp/test-prefix/file
-test ! -f /tmp/test-prefix
diff --git a/test/units/testsuite-22.08.sh b/test/units/testsuite-22.08.sh
deleted file mode 100755 (executable)
index 40fafd3..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Verify tmpfiles can run in a root directory under a path prefix that contains
-# directories owned by unprivileged users, for example when a root file system
-# is mounted in a regular user's home directory.
-#
-# https://github.com/systemd/systemd/pull/11820
-set -eux
-set -o pipefail
-
-rm -fr /tmp/root /tmp/user
-mkdir -p /tmp/root /tmp/user/root
-chown daemon:daemon /tmp/user
-
-# Verify the command works as expected with no prefix or a root-owned prefix.
-echo 'd /tmp/root/test1' | systemd-tmpfiles --create -
-test -d /tmp/root/test1
-echo 'd /test2' | systemd-tmpfiles --root=/tmp/root --create -
-test -d /tmp/root/test2
-
-# Verify the command fails to write to a root-owned subdirectory under an
-# unprivileged user's directory when it's not part of the prefix, as expected
-# by the unsafe_transition function.
-echo 'd /tmp/user/root/test' | (! systemd-tmpfiles --create -)
-test ! -e /tmp/user/root/test
-echo 'd /user/root/test' | (! systemd-tmpfiles --root=/tmp --create -)
-test ! -e /tmp/user/root/test
-
-# Verify the above works when all user-owned directories are in the prefix.
-echo 'd /test' | systemd-tmpfiles --root=/tmp/user/root --create -
-test -d /tmp/user/root/test
diff --git a/test/units/testsuite-22.09.sh b/test/units/testsuite-22.09.sh
deleted file mode 100755 (executable)
index 0857773..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Make sure that the "stat" output is not locale dependent.
-export LANG=C LC_ALL=C
-
-# first, create file without suid/sgid
-systemd-tmpfiles --create - <<EOF
-f     /tmp/xxx    0755 1 1 - -
-f     /tmp/yyy    0755 1 1 - -
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
-
-# then, add suid/sgid
-systemd-tmpfiles --create - <<EOF
-f     /tmp/xxx    04755
-f     /tmp/yyy    02755
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:4755"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:2755"
-
-# then, chown the files to somebody else
-systemd-tmpfiles --create - <<EOF
-f     /tmp/xxx    - 2 2
-f     /tmp/yyy    - 2 2
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:2:2:4755"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:2:2:2755"
-
-# then, chown the files to a third user/group but also drop to a mask that has
-# both more and fewer bits set
-systemd-tmpfiles --create - <<EOF
-f     /tmp/xxx    0770 3 3
-f     /tmp/yyy    0770 3 3
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:3:3:770"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:3:3:770"
-
-# return to the beginning
-systemd-tmpfiles --create - <<EOF
-f     /tmp/xxx    0755 1 1 - -
-f     /tmp/yyy    0755 1 1 - -
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
-
-# remove everything
-systemd-tmpfiles --remove - <<EOF
-r /tmp/xxx
-r /tmp/yyy
-EOF
diff --git a/test/units/testsuite-22.10.sh b/test/units/testsuite-22.10.sh
deleted file mode 100755 (executable)
index 99052c8..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemd-tmpfiles --create - <<EOF
-f /tmp/xxx1 0644 - - - foo
-f /tmp/xxx2 0644 - - - foo bar
-f /tmp/xxx3 0644 - - - foo\x20bar
-f /tmp/xxx4 0644 - - - \x20foobar
-f /tmp/xxx5 0644 - - - foobar\x20
-f /tmp/xxx6 0644 - - -  foo bar
-f /tmp/xxx7 0644 - - -  foo bar \n
-f /tmp/xxx8 0644 - - - " foo bar "
-f /tmp/xxx9 0644 - - - ' foo bar '
-EOF
-
-echo -n "foo" | cmp /tmp/xxx1 -
-echo -n "foo bar" | cmp /tmp/xxx2 -
-echo -n "foo bar" | cmp /tmp/xxx3 -
-echo -n " foobar" | cmp /tmp/xxx4 -
-echo -n "foobar " | cmp /tmp/xxx5 -
-echo -n "foo bar" | cmp /tmp/xxx6 -
-echo "foo bar " | cmp /tmp/xxx7 -
-echo -n "\" foo bar \"" | cmp /tmp/xxx8 -
-echo -n "' foo bar '" | cmp /tmp/xxx9 -
-
-rm /tmp/xxx{1,2,3,4,5,6,7,8,9}
diff --git a/test/units/testsuite-22.11.sh b/test/units/testsuite-22.11.sh
deleted file mode 100755 (executable)
index 6f75888..0000000
+++ /dev/null
@@ -1,249 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-set -x
-
-rm -fr /tmp/x
-mkdir  /tmp/x
-
-#
-# 'x'
-#
-mkdir -p /tmp/x/{1,2}
-touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
-
-systemd-tmpfiles --clean --dry-run - <<EOF
-d /tmp/x - - - 0
-x /tmp/x/1
-EOF
-
-find /tmp/x | sort
-test -f /tmp/x/1/x1
-test -f /tmp/x/1/x2
-test -f /tmp/x/2/y1
-test -f /tmp/x/2/y2
-test -f /tmp/x/z1
-test -f /tmp/x/z2
-
-systemd-tmpfiles --clean - <<EOF
-d /tmp/x - - - 0
-x /tmp/x/1
-EOF
-
-find /tmp/x | sort
-test -d /tmp/x/1
-test -f /tmp/x/1/x1
-test -f /tmp/x/1/x2
-test ! -d /tmp/x/2
-test ! -f /tmp/x/2/x1
-test ! -f /tmp/x/2/x2
-test ! -f /tmp/x/z1
-test ! -f /tmp/x/z2
-
-#
-# 'X'
-#
-
-mkdir -p /tmp/x/{1,2}
-touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
-
-systemd-tmpfiles --dry-run --clean - <<EOF
-d /tmp/x - - - 0
-X /tmp/x/1
-EOF
-
-find /tmp/x | sort
-test -f /tmp/x/1/x1
-test -f /tmp/x/1/x2
-test -f /tmp/x/2/y1
-test -f /tmp/x/2/y2
-test -f /tmp/x/z1
-test -f /tmp/x/z2
-
-systemd-tmpfiles --clean - <<EOF
-d /tmp/x - - - 0
-X /tmp/x/1
-EOF
-
-find /tmp/x | sort
-test -d /tmp/x/1
-test ! -f /tmp/x/1/x1
-test ! -f /tmp/x/1/x2
-test ! -d /tmp/x/2
-test ! -f /tmp/x/2/x1
-test ! -f /tmp/x/2/x2
-test ! -f /tmp/x/z1
-test ! -f /tmp/x/z2
-
-#
-# 'x' with glob
-#
-
-mkdir -p /tmp/x/{1,2}
-touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
-
-systemd-tmpfiles --dry-run --clean - <<EOF
-d /tmp/x - - - 0
-x /tmp/x/[1345]
-x /tmp/x/z*
-EOF
-
-find /tmp/x | sort
-test -f /tmp/x/1/x1
-test -f /tmp/x/1/x2
-test -f /tmp/x/2/y1
-test -f /tmp/x/2/y2
-test -f /tmp/x/z1
-test -f /tmp/x/z2
-
-systemd-tmpfiles --clean - <<EOF
-d /tmp/x - - - 0
-x /tmp/x/[1345]
-x /tmp/x/z*
-EOF
-
-find /tmp/x | sort
-test -d /tmp/x/1
-test -f /tmp/x/1/x1
-test -f /tmp/x/1/x2
-test ! -d /tmp/x/2
-test ! -f /tmp/x/2/x1
-test ! -f /tmp/x/2/x2
-test -f /tmp/x/z1
-test -f /tmp/x/z2
-
-#
-# 'X' with glob
-#
-
-mkdir -p /tmp/x/{1,2}
-touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
-
-systemd-tmpfiles --dry-run --clean - <<EOF
-d /tmp/x - - - 0
-X /tmp/x/[1345]
-X /tmp/x/?[12]
-EOF
-
-find /tmp/x | sort
-test -f /tmp/x/1/x1
-test -f /tmp/x/1/x2
-test -f /tmp/x/2/y1
-test -f /tmp/x/2/y2
-test -f /tmp/x/z1
-test -f /tmp/x/z2
-
-systemd-tmpfiles --clean - <<EOF
-d /tmp/x - - - 0
-X /tmp/x/[1345]
-X /tmp/x/?[12]
-EOF
-
-find /tmp/x | sort
-test -d /tmp/x/1
-test ! -f /tmp/x/1/x1
-test ! -f /tmp/x/1/x2
-test ! -d /tmp/x/2
-test ! -f /tmp/x/2/x1
-test ! -f /tmp/x/2/x2
-test -f /tmp/x/z1
-test -f /tmp/x/z2
-
-#
-# 'x' with 'r'
-#
-
-mkdir -p /tmp/x/{1,2}/a
-touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
-
-systemd-tmpfiles --dry-run --clean - <<EOF
-# x/X is not supposed to influence r
-x /tmp/x/1/a
-X /tmp/x/2/a
-r /tmp/x/1
-r /tmp/x/2
-EOF
-
-test -f /tmp/x/1/a/x1
-test -f /tmp/x/1/a/x2
-test -f /tmp/x/2/a/y1
-test -f /tmp/x/2/a/y2
-
-systemd-tmpfiles --clean - <<EOF
-# x/X is not supposed to influence r
-x /tmp/x/1/a
-X /tmp/x/2/a
-r /tmp/x/1
-r /tmp/x/2
-EOF
-
-find /tmp/x | sort
-test -d /tmp/x/1
-test -d /tmp/x/1/a
-test -f /tmp/x/1/a/x1
-test -f /tmp/x/1/a/x2
-test -f /tmp/x/2/a/y1
-test -f /tmp/x/2/a/y2
-
-#
-# 'x' with 'R'
-#
-
-mkdir -p /tmp/x/{1,2}/a
-touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
-
-systemd-tmpfiles --dry-run --remove - <<EOF
-# Check that X is honoured below R
-X /tmp/x/1/a
-X /tmp/x/2/a
-R /tmp/x/1
-EOF
-
-test -f /tmp/x/1/a/x1
-test -f /tmp/x/1/a/x2
-test -f /tmp/x/2/a/y1
-test -f /tmp/x/2/a/y2
-
-systemd-tmpfiles --remove - <<EOF
-# Check that X is honoured below R
-X /tmp/x/1/a
-X /tmp/x/2/a
-R /tmp/x/1
-EOF
-
-find /tmp/x | sort
-test -d /tmp/x/1
-test -d /tmp/x/1/a
-test -f /tmp/x/1/a/x1
-test -f /tmp/x/1/a/x2
-test -f /tmp/x/2/a/y1
-test -f /tmp/x/2/a/y2
-
-#
-# 'r/R/D' and non-directories
-#
-
-touch /tmp/x/{11,22,33}
-
-systemd-tmpfiles --dry-run --remove - <<EOF
-# Check that X is honoured below R
-r /tmp/x/11
-R /tmp/x/22
-D /tmp/x/33
-EOF
-
-test -f /tmp/x/11
-test -f /tmp/x/22
-test -f /tmp/x/33
-
-systemd-tmpfiles --remove - <<EOF
-# Check that X is honoured below R
-r /tmp/x/11
-R /tmp/x/22
-D /tmp/x/33
-EOF
-
-find /tmp/x | sort
-test ! -f /tmp/x/11
-test ! -f /tmp/x/22
-test -f /tmp/x/33
diff --git a/test/units/testsuite-22.12.sh b/test/units/testsuite-22.12.sh
deleted file mode 100755 (executable)
index 57788da..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Test the "Age" parameter (with age-by) for systemd-tmpfiles.
-set -e
-set -x
-
-# Test directory structure looks like this:
-#   /tmp/ageby/
-#   ├── d1
-#   │   ├── f1
-#   │   ├── f2
-#   │   ├── f3
-#   │   └── f4
-#   ├── d2
-#   │   ├── f1
-#   │   ├── f2
-#   ...
-
-export SYSTEMD_LOG_LEVEL="debug"
-
-rm -rf /tmp/ageby
-mkdir -p /tmp/ageby/d{1..4}
-
-# TODO: There is probably a better way to figure this out.
-# Test for [bB] age-by arguments only on filesystems that expose
-# the creation time. Note that this is _not_ an accurate way to
-# check if the filesystem or kernel version don't provide the
-# timestamp. But, if the timestamp is visible in "stat" it is a
-# good indicator that the test can be run.
-TEST_TMPFILES_AGEBY_BTIME=${TEST_TMPFILES_AGEBY_BTIME:-0}
-if stat --format "%w" /tmp/ageby 2>/dev/null | grep -qv '^[\?\-]$'; then
-    TEST_TMPFILES_AGEBY_BTIME=1
-fi
-
-touch -a --date "2 minutes ago" /tmp/ageby/d1/f1
-touch -m --date "4 minutes ago" /tmp/ageby/d2/f1
-
-# Create a bunch of other files.
-touch /tmp/ageby/d{1,2}/f{2..4}
-
-# For "ctime".
-touch /tmp/ageby/d3/f1
-chmod +x /tmp/ageby/d3/f1
-sleep 1
-
-# For "btime".
-touch /tmp/ageby/d4/f1
-sleep 1
-
-# More files with recent "{a,b}time" values.
-touch /tmp/ageby/d{3,4}/f{2..4}
-
-# Check for cleanup of "f1" in each of "/tmp/d{1..4}".
-systemd-tmpfiles --dry-run --clean - <<-EOF
-d /tmp/ageby/d1 - - - a:1m -
-e /tmp/ageby/d2 - - - m:3m -
-D /tmp/ageby/d3 - - - c:2s -
-EOF
-
-for d in d{1..3}; do
-    test -f "/tmp/ageby/${d}/f1"
-done
-
-systemd-tmpfiles --clean - <<-EOF
-d /tmp/ageby/d1 - - - a:1m -
-e /tmp/ageby/d2 - - - m:3m -
-D /tmp/ageby/d3 - - - c:2s -
-EOF
-
-for d in d{1..3}; do
-    test ! -f "/tmp/ageby/${d}/f1"
-done
-
-if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
-    systemd-tmpfiles --clean - <<-EOF
-d /tmp/ageby/d4 - - - b:1s -
-EOF
-
-    test ! -f "/tmp/ageby/d4/f1"
-else
-    # Remove the file manually.
-    rm "/tmp/ageby/d4/f1"
-fi
-
-# Check for an invalid "age" and "age-by" arguments.
-for a in ':' ':1s' '2:1h' 'nope:42h' '"  :7m"' 'm:' '::' '"+r^w-x:2/h"' 'b ar::64'; do
-    systemd-tmpfiles --clean - <<EOF 2>&1 | grep -q -F 'Invalid age'
-d /tmp/ageby - - - ${a} -
-EOF
-done
-
-for d in d{1..4}; do
-    for f in f{2..4}; do
-        test -f "/tmp/ageby/${d}/${f}"
-    done
-done
-
-# Check for parsing with whitespace, repeated values
-# for "age-by" (valid arguments).
-for a in '"  a:24h"' 'cccaab:2h' '" aa : 4h"' '" a A B C c:1h"'; do
-    systemd-tmpfiles --clean - <<EOF
-d /tmp/ageby - - - ${a} -
-EOF
-done
-
-for d in d{1..4}; do
-    for f in f{2..4}; do
-        test -f "/tmp/ageby/${d}/${f}"
-    done
-done
-
-# Check that all files are removed if the "Age" is
-# set to "0" (regardless of "age-by" argument).
-systemd-tmpfiles --clean - <<-EOF
-d /tmp/ageby/d1 - - - abc:0 -
-e /tmp/ageby/d2 - - - cmb:0 -
-EOF
-
-for d in d{1,2}; do
-    for f in f{2..4}; do
-        test ! -f "/tmp/ageby/${d}/${f}"
-    done
-done
-
-# Check for combinations:
-#   - "/tmp/ageby/d3/f2" has file timestamps that
-#     are older than the specified age, it will be
-#     removed
-#   - "/tmp/ageby/d4/f2", has not aged for the given
-#     timestamp combination, it will not be removed
-touch -a -m --date "4 minutes ago" /tmp/ageby/d3/f2
-touch -a -m --date "8 minutes ago" /tmp/ageby/d4/f2
-systemd-tmpfiles --clean - <<-EOF
-e /tmp/ageby/d3 - - - am:3m -
-D /tmp/ageby/d4 - - - mc:7m -
-EOF
-
-test ! -f "/tmp/ageby/d3/f2"
-test -f "/tmp/ageby/d4/f2"
-
-# Check that all files are removed if only "Age" is set to 0.
-systemd-tmpfiles --clean - <<-EOF
-e /tmp/ageby/d3 - - - 0s
-d /tmp/ageby/d4 - - - 0s
-EOF
-
-for d in d{3,4}; do
-    for f in f{2..4}; do
-        test ! -f "/tmp/ageby/$d/${f}"
-    done
-done
-
-# Check "age-by" argument for sub-directories in "/tmp/ageby".
-systemd-tmpfiles --clean - <<-EOF
-d /tmp/ageby/ - - - A:1m -
-EOF
-
-for d in d{1..4}; do
-    test -d "/tmp/ageby/${d}"
-done
-
-# Check for combinations.
-touch -a -m --date "5 seconds ago" /tmp/ageby/d{1,2}
-systemd-tmpfiles --clean - <<-EOF
-e /tmp/ageby/ - - - AM:4s -
-EOF
-
-for d in d{1,2}; do
-    test ! -d "/tmp/ageby/${d}"
-done
-
-for d in d{3,4}; do
-    test -d "/tmp/ageby/${d}"
-done
-
-# Check "btime" for directories.
-if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
-    systemd-tmpfiles --clean - <<-EOF
-d /tmp/ageby/ - - - B:8s -
-EOF
-
-    for d in d{3,4}; do
-        test -d "/tmp/ageby/${d}"
-    done
-fi
-
-# To bump "atime".
-touch -a --date "1 second ago" /tmp/ageby/d3
-systemd-tmpfiles --clean - <<-EOF
-d /tmp/ageby/ - - - A:2s -
-EOF
-
-test -d /tmp/ageby/d3
-test ! -d /tmp/ageby/d4
-
-# Check if sub-directories are removed regardless
-# of "age-by", when "Age" is set to "0".
-systemd-tmpfiles --clean - <<-EOF
-D /tmp/ageby/ - - - AM:0 -
-EOF
-
-test ! -d /tmp/ageby/d3
-
-# Cleanup the test directory (fail if not empty).
-rmdir /tmp/ageby
diff --git a/test/units/testsuite-22.13.sh b/test/units/testsuite-22.13.sh
deleted file mode 100755 (executable)
index 33ef451..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Tests for configuration directory and file precedences
-#
-set -eux
-
-rm -f  /{usr/lib,etc}/tmpfiles.d/{L,w}-*.conf
-rm -fr /tmp/precedence/{L,w}
-
-mkdir -p /{usr/lib,etc}/tmpfiles.d
-mkdir -p /tmp/precedence/{L,w}
-
-#
-# 'L'
-#
-ln -s /dev/null /tmp/precedence/L
-
-# Overwrite the existing symlink
-cat >/usr/lib/tmpfiles.d/L-z.conf<<EOF
-L+ /tmp/precedence/L - - - - /usr/lib/tmpfiles.d/L-z.conf
-EOF
-
-systemd-tmpfiles --create
-test "$(readlink /tmp/precedence/L)" = "/usr/lib/tmpfiles.d/L-z.conf"
-
-# Files in /etc should override those in /usr
-cat >/etc/tmpfiles.d/L-z.conf<<EOF
-L+ /tmp/precedence/L - - - - /etc/tmpfiles.d/L-z.conf
-EOF
-
-systemd-tmpfiles --create
-test "$(readlink /tmp/precedence/L)" = "/etc/tmpfiles.d/L-z.conf"
-
-# /usr/…/L-a.conf has higher prio than /etc/…/L-z.conf
-cat >/usr/lib/tmpfiles.d/L-a.conf<<EOF
-L+ /tmp/precedence/L - - - - /usr/lib/tmpfiles.d/L-a.conf
-EOF
-
-systemd-tmpfiles --create
-test "$(readlink /tmp/precedence/L)" = "/usr/lib/tmpfiles.d/L-a.conf"
-
-# Files in /etc should override those in /usr
-cat >/etc/tmpfiles.d/L-a.conf<<EOF
-L+ /tmp/precedence/L - - - - /etc/tmpfiles.d/L-a.conf
-EOF
-
-systemd-tmpfiles --create
-test "$(readlink /tmp/precedence/L)" = "/etc/tmpfiles.d/L-a.conf"
-
-#
-# 'w'
-#
-touch /tmp/precedence/w/f
-
-# Multiple configuration files specifying 'w+' for the same path is allowed.
-for i in a c; do
-    cat >/usr/lib/tmpfiles.d/w-$i.conf<<EOF
-w+ /tmp/precedence/w/f - - - - /usr/lib/tmpfiles.d/w-$i.conf\n
-EOF
-    cat >/etc/tmpfiles.d/w-$i.conf<<EOF
-w+ /tmp/precedence/w/f - - - - /etc/tmpfiles.d/w-$i.conf\n
-EOF
-done
-
-cat >/usr/lib/tmpfiles.d/w-b.conf<<EOF
-w+ /tmp/precedence/w/f - - - - /usr/lib/tmpfiles.d/w-b.conf\n
-EOF
-
-systemd-tmpfiles --create
-cmp /tmp/precedence/w/f <<EOF
-/etc/tmpfiles.d/w-a.conf
-/usr/lib/tmpfiles.d/w-b.conf
-/etc/tmpfiles.d/w-c.conf
-EOF
diff --git a/test/units/testsuite-22.14.sh b/test/units/testsuite-22.14.sh
deleted file mode 100755 (executable)
index 2132de7..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Tests for the ":" uid/gid/mode modifier
-#
-set -eux
-
-rm -rf /tmp/someinode
-
-systemd-tmpfiles --create - <<EOF
-d /tmp/someinode :0123 :1 :1
-EOF
-test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:1:1:123"
-
-systemd-tmpfiles --create - <<EOF
-d /tmp/someinode :0321 :2 :2
-EOF
-test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:1:1:123"
-
-systemd-tmpfiles --create - <<EOF
-d /tmp/someinode 0321 2 2
-EOF
-test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:2:2:321"
-
-systemd-tmpfiles --create - <<EOF
-d /tmp/someinode :0123 :1 :1
-EOF
-test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:2:2:321"
-
-rm -rf /tmp/someinode
-
-systemd-tmpfiles --create - <<EOF
-d /tmp/someinode :0123 :1 :1
-EOF
-test "$(stat -c %F:%u:%g:%a /tmp/someinode)" = "directory:1:1:123"
-
-rm -rf /tmp/someinode
diff --git a/test/units/testsuite-22.15.sh b/test/units/testsuite-22.15.sh
deleted file mode 100755 (executable)
index 6ee0e63..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Check specifier expansion in L lines.
-#
-set -eux
-
-rm -fr /tmp/L
-mkdir  /tmp/L
-
-# Check that %h expands to $home.
-home='/somewhere'
-dst='/tmp/L/1'
-src="$home"
-HOME="$home" \
-systemd-tmpfiles --dry-run --create - <<EOF
-L     $dst    - - - - %h
-EOF
-test ! -h "$dst"
-
-HOME="$home" \
-systemd-tmpfiles --create - <<EOF
-L     $dst    - - - - %h
-EOF
-test "$(readlink "$dst")" = "$src"
-
-# Check that %h in the path is expanded, but
-# the result of this expansion is not expanded once again.
-root='/tmp/L/2'
-home='/%U'
-src="/usr/share/factory$home"
-mkdir -p "$root$src"
-dst="$root$home"
-HOME="$home" \
-systemd-tmpfiles --create --dry-run --root="$root" - <<EOF
-L     %h    - - - -
-EOF
-test ! -h "$dst"
-
-HOME="$home" \
-systemd-tmpfiles --create --root="$root" - <<EOF
-L     %h    - - - -
-EOF
-test "$(readlink "$dst")" = "$src"
diff --git a/test/units/testsuite-22.16.sh b/test/units/testsuite-22.16.sh
deleted file mode 100755 (executable)
index 3d3d0c8..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Test for conditionalized execute bit ('X' bit)
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-rm -f /tmp/acl_exec
-touch /tmp/acl_exec
-
-# No ACL set yet
-systemd-tmpfiles --dry-run --create - <<EOF
-a /tmp/acl_exec - - - - u:root:rwX
-EOF
-assert_not_in 'user:root:rw-' "$(getfacl -Ec /tmp/acl_exec)"
-
-systemd-tmpfiles --create - <<EOF
-a /tmp/acl_exec - - - - u:root:rwX
-EOF
-assert_in 'user:root:rw-' "$(getfacl -Ec /tmp/acl_exec)"
-
-# Set another ACL and append
-setfacl -m g:root:x /tmp/acl_exec
-
-systemd-tmpfiles --create - <<EOF
-a+ /tmp/acl_exec - - - - u:root:rwX
-EOF
-acl="$(getfacl -Ec /tmp/acl_exec)"
-assert_in 'user:root:rwx' "$acl"
-assert_in 'group:root:--x' "$acl"
-
-# Reset ACL (no append)
-systemd-tmpfiles --create - <<EOF
-a /tmp/acl_exec - - - - u:root:rwX
-EOF
-assert_in 'user:root:rw-' "$(getfacl -Ec /tmp/acl_exec)"
-
-rm -f /tmp/acl_exec
diff --git a/test/units/testsuite-22.17.sh b/test/units/testsuite-22.17.sh
deleted file mode 100755 (executable)
index f43aba5..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Test for C-style escapes in file names and contents
-set -eux
-set -o pipefail
-
-data="\x20foo\nbar"
-dst="/tmp/x/\x20a\nb"
-
-systemd-tmpfiles --create - <<EOF
-f     "$dst" 0644 0 0 - $data
-EOF
-
-diff "$(printf "/tmp/x/\x20a\nb")" <(printf "\x20foo\nbar")
diff --git a/test/units/testsuite-22.18.sh b/test/units/testsuite-22.18.sh
deleted file mode 100755 (executable)
index 5d24197..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Tests for the --purge switch
-#
-set -eux
-set -o pipefail
-
-export SYSTEMD_LOG_LEVEL=debug
-
-c='
-d /tmp/somedir
-f /tmp/somedir/somefile - - - - baz
-'
-
-systemd-tmpfiles --create - <<<"$c"
-test -f /tmp/somedir/somefile
-grep -q baz /tmp/somedir/somefile
-
-systemd-tmpfiles --purge --dry-run - <<<"$c"
-test -f /tmp/somedir/somefile
-grep -q baz /tmp/somedir/somefile
-
-systemd-tmpfiles --purge - <<<"$c"
-test ! -f /tmp/somedir/somefile
-test ! -d /tmp/somedir/
-
-systemd-tmpfiles --create --purge --dry-run - <<<"$c"
-test ! -f /tmp/somedir/somefile
-test ! -d /tmp/somedir/
-
-systemd-tmpfiles --create --purge - <<<"$c"
-test -f /tmp/somedir/somefile
-grep -q baz /tmp/somedir/somefile
diff --git a/test/units/testsuite-22.19.sh b/test/units/testsuite-22.19.sh
deleted file mode 100755 (executable)
index c5a0966..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Tests for character and block device creation
-#
-set -eux
-set -o pipefail
-
-rm -rf /tmp/dev
-mkdir /tmp/dev
-
-# We are running tests in /tmp, which would normally be mounted nodev,
-# so we only try with --dry-run.
-
-systemd-tmpfiles --dry-run --create - <<EOF
-c /tmp/dev/char  - - - - 11:12
-b /tmp/dev/block - - - - 11:14
-EOF
-
-test ! -e /tmp/dev/char
-test ! -e /tmp/dev/block
-
-systemd-tmpfiles --dry-run --create - <<EOF
-c+ /tmp/dev/char  - - - - 11:12
-b+ /tmp/dev/block - - - - 11:14
-EOF
-
-test ! -e /tmp/dev/char
-test ! -e /tmp/dev/block
diff --git a/test/units/testsuite-22.service b/test/units/testsuite-22.service
deleted file mode 100644 (file)
index a5ed660..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-22-TMPFILES
-After=systemd-tmpfiles-setup.service
-Before=getty-pre.target
-Wants=getty-pre.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-22.sh b/test/units/testsuite-22.sh
deleted file mode 100755 (executable)
index 9c2a033..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
diff --git a/test/units/testsuite-23-openfile-child.sh b/test/units/testsuite-23-openfile-child.sh
deleted file mode 100755 (executable)
index 4828b9d..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-assert_eq "$LISTEN_FDS" "$1"
-assert_eq "$LISTEN_FDNAMES" "$2"
-
-for ((i = 3; i < 3 + LISTEN_FDS; i++)); do
-    read -r -u "$i" text
-    assert_eq "$text" "${!i}" # Dereference $i to get i'th arg
-done
diff --git a/test/units/testsuite-23-short-lived.sh b/test/units/testsuite-23-short-lived.sh
deleted file mode 100755 (executable)
index 4a12c7f..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-
-if [ -f /tmp/testsuite-23.counter ] ; then
-    read -r counter < /tmp/testsuite-23.counter
-    counter=$((counter + 1))
-else
-    counter=0
-fi
-
-echo "$counter" >/tmp/testsuite-23.counter
-
-if [ "$counter" -eq 5 ] ; then
-    systemctl kill --kill-whom=main -sUSR1 testsuite-23.service
-fi
-
-exec sleep 1.5
diff --git a/test/units/testsuite-23.ExecReload.sh b/test/units/testsuite-23.ExecReload.sh
deleted file mode 100755 (executable)
index d544ce6..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test ExecReload= (PR #13098)
-
-systemd-analyze log-level debug
-
-export SYSTEMD_PAGER=
-SERVICE_PATH="$(mktemp /etc/systemd/system/execreloadXXX.service)"
-SERVICE_NAME="${SERVICE_PATH##*/}"
-
-echo "[#1] Failing ExecReload= should not kill the service"
-cat >"$SERVICE_PATH" <<EOF
-[Service]
-ExecStart=sleep infinity
-ExecReload=/bin/false
-EOF
-
-systemctl daemon-reload
-systemctl start "$SERVICE_NAME"
-systemctl status "$SERVICE_NAME"
-# The reload SHOULD fail but SHOULD NOT affect the service state
-(! systemctl reload "$SERVICE_NAME")
-systemctl status "$SERVICE_NAME"
-systemctl stop "$SERVICE_NAME"
-
-
-echo "[#2] Failing ExecReload= should not kill the service (multiple ExecReload=)"
-cat >"$SERVICE_PATH" <<EOF
-[Service]
-ExecStart=sleep infinity
-ExecReload=/bin/true
-ExecReload=/bin/false
-ExecReload=/bin/true
-EOF
-
-systemctl daemon-reload
-systemctl start "$SERVICE_NAME"
-systemctl status "$SERVICE_NAME"
-# The reload SHOULD fail but SHOULD NOT affect the service state
-(! systemctl reload "$SERVICE_NAME")
-systemctl status "$SERVICE_NAME"
-systemctl stop "$SERVICE_NAME"
-
-echo "[#3] Failing ExecReload=- should not affect reload's exit code"
-cat >"$SERVICE_PATH" <<EOF
-[Service]
-ExecStart=sleep infinity
-ExecReload=-/bin/false
-EOF
-
-systemctl daemon-reload
-systemctl start "$SERVICE_NAME"
-systemctl status "$SERVICE_NAME"
-systemctl reload "$SERVICE_NAME"
-systemctl status "$SERVICE_NAME"
-systemctl stop "$SERVICE_NAME"
-
-systemd-analyze log-level info
diff --git a/test/units/testsuite-23.ExecStopPost.sh b/test/units/testsuite-23.ExecStopPost.sh
deleted file mode 100755 (executable)
index aeaf3aa..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-
-# Test that ExecStopPost= is always run
-
-systemd-analyze log-level debug
-
-systemd-run --unit=simple1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple \
-    -p ExecStopPost='/bin/touch /run/simple1' true
-test -f /run/simple1
-
-(! systemd-run --unit=simple2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple \
-    -p ExecStopPost='/bin/touch /run/simple2' false)
-test -f /run/simple2
-
-systemd-run --unit=exec1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec \
-    -p ExecStopPost='/bin/touch /run/exec1' sleep 1
-test -f /run/exec1
-
-(! systemd-run --unit=exec2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec \
-   -p ExecStopPost='/bin/touch /run/exec2' sh -c 'sleep 1; false')
-test -f /run/exec2
-
-cat >/tmp/forking1.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-
-sleep 4 &
-MAINPID=\$!
-disown
-
-systemd-notify MAINPID=\$MAINPID
-EOF
-chmod +x /tmp/forking1.sh
-
-systemd-run --unit=forking1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec \
-        -p ExecStopPost='/bin/touch /run/forking1' /tmp/forking1.sh
-test -f /run/forking1
-
-cat >/tmp/forking2.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-
-(sleep 4; exit 1) &
-MAINPID=\$!
-disown
-
-systemd-notify MAINPID=\$MAINPID
-EOF
-chmod +x /tmp/forking2.sh
-
-(! systemd-run --unit=forking2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec \
-    -p ExecStopPost='/bin/touch /run/forking2' /tmp/forking2.sh)
-test -f /run/forking2
-
-systemd-run --unit=oneshot1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot \
-    -p ExecStopPost='/bin/touch /run/oneshot1' true
-test -f /run/oneshot1
-
-(! systemd-run --unit=oneshot2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot \
-    -p ExecStopPost='/bin/touch /run/oneshot2' false)
-test -f /run/oneshot2
-
-systemd-run --unit=dbus1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost \
-    -p ExecStopPost='/bin/touch /run/dbus1' \
-    busctl call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus RequestName su systemd.test.ExecStopPost 4 || :
-test -f /run/dbus1
-
-systemd-run --unit=dbus2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost \
-     -p ExecStopPost='/bin/touch /run/dbus2' true
-test -f /run/dbus2
-
-# https://github.com/systemd/systemd/issues/19920
-(! systemd-run --unit=dbus3.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus \
-    -p ExecStopPost='/bin/touch /run/dbus3' true)
-
-cat >/tmp/notify1.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-
-systemd-notify --ready
-EOF
-chmod +x /tmp/notify1.sh
-
-systemd-run --unit=notify1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify \
-    -p ExecStopPost='/bin/touch /run/notify1' /tmp/notify1.sh
-test -f /run/notify1
-
-(! systemd-run --unit=notify2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify \
-    -p ExecStopPost='/bin/touch /run/notify2' true)
-test -f /run/notify2
-
-systemd-run --unit=idle1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle1' true
-test -f /run/idle1
-
-(! systemd-run --unit=idle2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle \
-     -p ExecStopPost='/bin/touch /run/idle2' false)
-test -f /run/idle2
-
-systemd-analyze log-level info
diff --git a/test/units/testsuite-23.JoinsNamespaceOf.sh b/test/units/testsuite-23.JoinsNamespaceOf.sh
deleted file mode 100755 (executable)
index 68ba465..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-set -eux
-set -o pipefail
-
-# Test JoinsNamespaceOf= with PrivateTmp=yes
-
-systemd-analyze log-level debug
-systemd-analyze log-target journal
-
-# simple case
-systemctl start testsuite-23-joins-namespace-of-1.service
-systemctl start testsuite-23-joins-namespace-of-2.service
-systemctl start testsuite-23-joins-namespace-of-3.service
-systemctl stop testsuite-23-joins-namespace-of-1.service
-
-# inverse dependency
-systemctl start testsuite-23-joins-namespace-of-4.service
-systemctl start testsuite-23-joins-namespace-of-5.service
-systemctl stop testsuite-23-joins-namespace-of-4.service
-
-# transitive dependency
-systemctl start testsuite-23-joins-namespace-of-6.service
-systemctl start testsuite-23-joins-namespace-of-7.service
-systemctl start testsuite-23-joins-namespace-of-8.service
-systemctl start testsuite-23-joins-namespace-of-9.service
-systemctl stop testsuite-23-joins-namespace-of-6.service
-systemctl stop testsuite-23-joins-namespace-of-8.service
-
-systemd-analyze log-level info
diff --git a/test/units/testsuite-23.RuntimeDirectoryPreserve.sh b/test/units/testsuite-23.RuntimeDirectoryPreserve.sh
deleted file mode 100755 (executable)
index ca57702..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-# Test RuntimeDirectoryPreserve=yes
-
-at_exit() {
-    set +e
-
-    rm -fr /run/hoge /tmp/aaa
-}
-
-trap at_exit EXIT
-
-systemd-mount -p RuntimeDirectory=hoge -p RuntimeDirectoryPreserve=yes -t tmpfs tmpfs /tmp/aaa
-
-touch /run/hoge/foo
-touch /tmp/aaa/bbb
-
-systemctl restart tmp-aaa.mount
-
-test -e /run/hoge/foo
-test ! -e /tmp/aaa/bbb
diff --git a/test/units/testsuite-23.StandardOutput.sh b/test/units/testsuite-23.StandardOutput.sh
deleted file mode 100755 (executable)
index 50b9ac2..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test StandardOutput=file:
-
-systemd-analyze log-level debug
-
-systemd-run --wait --unit=testsuite-23-standard-output-one \
-            -p StandardOutput=file:/tmp/stdout \
-            -p StandardError=file:/tmp/stderr \
-            -p Type=exec \
-            sh -c 'echo x ; echo y >&2'
-cmp /tmp/stdout <<EOF
-x
-EOF
-cmp /tmp/stderr <<EOF
-y
-EOF
-
-systemd-run --wait --unit=testsuite-23-standard-output-two \
-            -p StandardOutput=file:/tmp/stdout \
-            -p StandardError=file:/tmp/stderr \
-            -p Type=exec \
-            sh -c 'echo z ; echo a >&2'
-cmp /tmp/stdout <<EOF
-z
-EOF
-cmp /tmp/stderr <<EOF
-a
-EOF
-
-systemd-run --wait --unit=testsuite-23-standard-output-three \
-            -p StandardOutput=append:/tmp/stdout \
-            -p StandardError=append:/tmp/stderr \
-            -p Type=exec \
-            sh -c 'echo b ; echo c >&2'
-cmp /tmp/stdout <<EOF
-z
-b
-EOF
-cmp /tmp/stderr <<EOF
-a
-c
-EOF
-
-systemd-run --wait --unit=testsuite-23-standard-output-four \
-            -p StandardOutput=truncate:/tmp/stdout \
-            -p StandardError=truncate:/tmp/stderr \
-            -p Type=exec \
-            sh -c 'echo a ; echo b >&2'
-cmp /tmp/stdout <<EOF
-a
-EOF
-cmp /tmp/stderr <<EOF
-b
-EOF
-
-systemd-analyze log-level info
diff --git a/test/units/testsuite-23.Upholds.sh b/test/units/testsuite-23.Upholds.sh
deleted file mode 100755 (executable)
index e62f9c6..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-set -eux
-set -o pipefail
-
-# Test OnSuccess= + Uphold= + PropagatesStopTo= + BindsTo=
-
-systemd-analyze log-level debug
-systemd-analyze log-target journal
-
-# Idea is this:
-#    1. we start testsuite-23-success.service
-#    2. which through OnSuccess= starts testsuite-23-fail.service,
-#    3. which through OnFailure= starts testsuite-23-uphold.service,
-#    4. which through Uphold= starts/keeps testsuite-23-short-lived.service running,
-#    5. which will sleep 1s when invoked, and on the 5th invocation send us a SIGUSR1
-#    6. once we got that we finish cleanly
-
-sigusr1=0
-trap sigusr1=1 SIGUSR1
-
-trap -p SIGUSR1
-
-systemctl start testsuite-23-success.service
-
-while [ "$sigusr1" -eq 0 ] ; do
-    sleep .5
-done
-
-systemctl stop testsuite-23-uphold.service
-
-systemctl enable testsuite-23-upheldby-install.service
-
-# Idea is this:
-#    1. we start testsuite-23-retry-uphold.service
-#    2. which through Uphold= starts testsuite-23-retry-upheld.service
-#    3. which through Requires= starts testsuite-23-retry-fail.service
-#    4. which fails as /tmp/testsuite-23-retry-fail does not exist, so testsuite-23-retry-upheld.service
-#       is no longer restarted
-#    5. we create /tmp/testsuite-23-retry-fail
-#    6. now testsuite-23-retry-upheld.service will be restarted since upheld, and its dependency will
-#       be satisfied
-
-rm -f /tmp/testsuite-23-retry-fail
-systemctl start testsuite-23-retry-uphold.service
-systemctl is-active testsuite-23-upheldby-install.service
-
-until systemctl is-failed testsuite-23-retry-fail.service ; do
-    sleep .5
-done
-
-(! systemctl is-active testsuite-23-retry-upheld.service)
-
-touch /tmp/testsuite-23-retry-fail
-
-until systemctl is-active testsuite-23-retry-upheld.service ; do
-    sleep .5
-done
-
-systemctl stop testsuite-23-retry-uphold.service testsuite-23-retry-fail.service testsuite-23-retry-upheld.service
-
-# Idea is this:
-#    1. we start testsuite-23-prop-stop-one.service
-#    2. which through Wants=/After= pulls in testsuite-23-prop-stop-two.service as well
-#    3. testsuite-23-prop-stop-one.service then sleeps indefinitely
-#    4. testsuite-23-prop-stop-two.service sleeps a short time and exits
-#    5. the StopPropagatedFrom= dependency between the two should ensure *both* will exit as result
-#    6. an ExecStopPost= line on testsuite-23-prop-stop-one.service will send us a SIGUSR2
-#    7. once we got that we finish cleanly
-
-sigusr2=0
-trap sigusr2=1 SIGUSR2
-
-systemctl start testsuite-23-prop-stop-one.service
-
-while [ "$sigusr2" -eq 0 ] ; do
-    sleep .5
-done
-
-
-# Idea is this:
-#    1. we start testsuite-23-binds-to.service
-#    2. which through BindsTo=/After= pulls in testsuite-23-bound-by.service as well
-#    3. testsuite-23-bound-by.service suddenly dies
-#    4. testsuite-23-binds-to.service should then also be pulled down (it otherwise just hangs)
-#    6. an ExecStopPost= line on testsuite-23-binds-to.service will send us a SIGRTMIN1+1
-#    7. once we got that we finish cleanly
-
-sigrtmin1=0
-trap sigrtmin1=1 SIGRTMIN+1
-
-systemctl start testsuite-23-binds-to.service
-
-while [ "$sigrtmin1" -eq 0 ] ; do
-    sleep .5
-done
-
-systemd-analyze log-level info
diff --git a/test/units/testsuite-23.clean-unit.sh b/test/units/testsuite-23.clean-unit.sh
deleted file mode 100755 (executable)
index a883243..0000000
+++ /dev/null
@@ -1,329 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-# Test unit configuration/state/cache/log/runtime data cleanup
-
-at_exit() {
-    set +e
-
-    rm -fr /{etc,run,var/lib,var/cache,var/log}/test-service
-    rm -fr /{etc,run,var/lib,var/cache,var/log}/private/test-service
-    rm -fr /{etc,run,var/lib,var/cache,var/log}/hoge
-    rm -fr /{etc,run,var/lib,var/cache,var/log}/test-socket
-}
-
-trap at_exit EXIT
-
-cat >/run/systemd/system/test-service.service <<EOF
-[Service]
-ConfigurationDirectory=test-service
-RuntimeDirectory=test-service
-StateDirectory=test-service
-CacheDirectory=test-service
-LogsDirectory=test-service
-RuntimeDirectoryPreserve=yes
-ExecStart=sleep infinity
-Type=exec
-EOF
-
-systemctl daemon-reload
-
-test ! -e /etc/test-service
-test ! -e /run/test-service
-test ! -e /var/lib/test-service
-test ! -e /var/cache/test-service
-test ! -e /var/log/test-service
-
-systemctl start test-service
-
-test -d /etc/test-service
-test -d /run/test-service
-test -d /var/lib/test-service
-test -d /var/cache/test-service
-test -d /var/log/test-service
-
-(! systemctl clean test-service)
-
-systemctl stop test-service
-
-test -d /etc/test-service
-test -d /run/test-service
-test -d /var/lib/test-service
-test -d /var/cache/test-service
-test -d /var/log/test-service
-
-systemctl clean test-service --what=configuration
-
-test ! -e /etc/test-service
-test -d /run/test-service
-test -d /var/lib/test-service
-test -d /var/cache/test-service
-test -d /var/log/test-service
-
-systemctl clean test-service
-
-test ! -e /etc/test-service
-test ! -e /run/test-service
-test -d /var/lib/test-service
-test ! -e /var/cache/test-service
-test -d /var/log/test-service
-
-systemctl clean test-service --what=logs
-
-test ! -e /etc/test-service
-test ! -e /run/test-service
-test -d /var/lib/test-service
-test ! -e /var/cache/test-service
-test ! -e /var/log/test-service
-
-systemctl clean test-service --what=all
-
-test ! -e /etc/test-service
-test ! -e /run/test-service
-test ! -e /var/lib/test-service
-test ! -e /var/cache/test-service
-test ! -e /var/log/test-service
-
-cat >/run/systemd/system/test-service.service <<EOF
-[Service]
-DynamicUser=yes
-ConfigurationDirectory=test-service
-RuntimeDirectory=test-service
-StateDirectory=test-service
-CacheDirectory=test-service
-LogsDirectory=test-service
-RuntimeDirectoryPreserve=yes
-ExecStart=sleep infinity
-Type=exec
-EOF
-
-systemctl daemon-reload
-
-test ! -e /etc/test-service
-test ! -e /run/test-service
-test ! -e /var/lib/test-service
-test ! -e /var/cache/test-service
-test ! -e /var/log/test-service
-
-systemctl restart test-service
-
-test -d /etc/test-service
-test -d /run/private/test-service
-test -d /var/lib/private/test-service
-test -d /var/cache/private/test-service
-test -d /var/log/private/test-service
-test -L /run/test-service
-test -L /var/lib/test-service
-test -L /var/cache/test-service
-test -L /var/log/test-service
-
-(! systemctl clean test-service)
-
-systemctl stop test-service
-
-test -d /etc/test-service
-test -d /run/private/test-service
-test -d /var/lib/private/test-service
-test -d /var/cache/private/test-service
-test -d /var/log/private/test-service
-test -L /run/test-service
-test -L /var/lib/test-service
-test -L /var/cache/test-service
-test -L /var/log/test-service
-
-systemctl clean test-service --what=configuration
-
-test ! -d /etc/test-service
-test -d /run/private/test-service
-test -d /var/lib/private/test-service
-test -d /var/cache/private/test-service
-test -d /var/log/private/test-service
-test -L /run/test-service
-test -L /var/lib/test-service
-test -L /var/cache/test-service
-test -L /var/log/test-service
-
-systemctl clean test-service
-
-test ! -d /etc/test-service
-test ! -d /run/private/test-service
-test -d /var/lib/private/test-service
-test ! -d /var/cache/private/test-service
-test -d /var/log/private/test-service
-test ! -L /run/test-service
-test -L /var/lib/test-service
-test ! -L /var/cache/test-service
-test -L /var/log/test-service
-
-systemctl clean test-service --what=logs
-
-test ! -d /etc/test-service
-test ! -d /run/private/test-service
-test -d /var/lib/private/test-service
-test ! -d /var/cache/private/test-service
-test ! -d /var/log/private/test-service
-test ! -L /run/test-service
-test -L /var/lib/test-service
-test ! -L /var/cache/test-service
-test ! -L /var/log/test-service
-
-systemctl clean test-service --what=all
-
-test ! -d /etc/test-service
-test ! -d /run/private/test-service
-test ! -d /var/lib/private/test-service
-test ! -d /var/cache/private/test-service
-test ! -d /var/log/private/test-service
-test ! -L /run/test-service
-test ! -L /var/lib/test-service
-test ! -L /var/cache/test-service
-test ! -L /var/log/test-service
-
-cat >/run/systemd/system/tmp-hoge.mount <<EOF
-[Mount]
-What=tmpfs
-Type=tmpfs
-ConfigurationDirectory=hoge
-RuntimeDirectory=hoge
-StateDirectory=hoge
-CacheDirectory=hoge
-LogsDirectory=hoge
-EOF
-
-systemctl daemon-reload
-
-test ! -e /etc/hoge
-test ! -e /run/hoge
-test ! -e /var/lib/hoge
-test ! -e /var/cache/hoge
-test ! -e /var/log/hoge
-
-systemctl start tmp-hoge.mount
-
-test -d /etc/hoge
-test -d /run/hoge
-test -d /var/lib/hoge
-test -d /var/cache/hoge
-test -d /var/log/hoge
-
-(! systemctl clean tmp-hoge.mount)
-
-test -d /etc/hoge
-test -d /run/hoge
-test -d /var/lib/hoge
-test -d /var/cache/hoge
-test -d /var/log/hoge
-
-systemctl stop tmp-hoge.mount
-
-test -d /etc/hoge
-test ! -d /run/hoge
-test -d /var/lib/hoge
-test -d /var/cache/hoge
-test -d /var/log/hoge
-
-systemctl clean tmp-hoge.mount --what=configuration
-
-test ! -d /etc/hoge
-test ! -d /run/hoge
-test -d /var/lib/hoge
-test -d /var/cache/hoge
-test -d /var/log/hoge
-
-systemctl clean tmp-hoge.mount
-
-test ! -d /etc/hoge
-test ! -d /run/hoge
-test -d /var/lib/hoge
-test ! -d /var/cache/hoge
-test -d /var/log/hoge
-
-systemctl clean tmp-hoge.mount --what=logs
-
-test ! -d /etc/hoge
-test ! -d /run/hoge
-test -d /var/lib/hoge
-test ! -d /var/cache/hoge
-test ! -d /var/log/hoge
-
-systemctl clean tmp-hoge.mount --what=all
-
-test ! -d /etc/hoge
-test ! -d /run/hoge
-test ! -d /var/lib/hoge
-test ! -d /var/cache/hoge
-test ! -d /var/log/hoge
-
-cat >/run/systemd/system/test-service.socket <<EOF
-[Socket]
-ListenSequentialPacket=/run/test-service.socket
-RemoveOnStop=yes
-ExecStartPre=true
-ConfigurationDirectory=test-socket
-RuntimeDirectory=test-socket
-StateDirectory=test-socket
-CacheDirectory=test-socket
-LogsDirectory=test-socket
-EOF
-
-systemctl daemon-reload
-
-test ! -e /etc/test-socket
-test ! -e /run/test-socket
-test ! -e /var/lib/test-socket
-test ! -e /var/cache/test-socket
-test ! -e /var/log/test-socket
-
-systemctl start test-service.socket
-
-test -d /etc/test-socket
-test -d /run/test-socket
-test -d /var/lib/test-socket
-test -d /var/cache/test-socket
-test -d /var/log/test-socket
-
-(! systemctl clean test-service.socket)
-
-systemctl stop test-service.socket
-
-test -d /etc/test-socket
-test ! -d /run/test-socket
-test -d /var/lib/test-socket
-test -d /var/cache/test-socket
-test -d /var/log/test-socket
-
-systemctl clean test-service.socket --what=configuration
-
-test ! -e /etc/test-socket
-test ! -d /run/test-socket
-test -d /var/lib/test-socket
-test -d /var/cache/test-socket
-test -d /var/log/test-socket
-
-systemctl clean test-service.socket
-
-test ! -e /etc/test-socket
-test ! -e /run/test-socket
-test -d /var/lib/test-socket
-test ! -e /var/cache/test-socket
-test -d /var/log/test-socket
-
-systemctl clean test-service.socket --what=logs
-
-test ! -e /etc/test-socket
-test ! -e /run/test-socket
-test -d /var/lib/test-socket
-test ! -e /var/cache/test-socket
-test ! -e /var/log/test-socket
-
-systemctl clean test-service.socket --what=all
-
-test ! -e /etc/test-socket
-test ! -e /run/test-socket
-test ! -e /var/lib/test-socket
-test ! -e /var/cache/test-socket
-test ! -e /var/log/test-socket
diff --git a/test/units/testsuite-23.exec-command-ex.sh b/test/units/testsuite-23.exec-command-ex.sh
deleted file mode 100755 (executable)
index f926e7d..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test ExecXYZEx= service unit dbus hookups
-
-systemd-analyze log-level debug
-
-declare -A property
-
-property[1_one]=ExecCondition
-property[2_two]=ExecStartPre
-property[3_three]=ExecStart
-property[4_four]=ExecStartPost
-property[5_five]=ExecReload
-property[6_six]=ExecStop
-property[7_seven]=ExecStopPost
-
-# These should all get upgraded to the corresponding Ex property as the non-Ex variant
-# does not support the ":" prefix (no-env-expand).
-for c in "${!property[@]}"; do
-    systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property[$c]}=:/bin/echo \${$c}" /bin/true
-    systemctl show -p "${property[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
-    systemctl show -p "${property[$c]}Ex" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
-done
-
-declare -A property_ex
-
-property_ex[1_one_ex]=ExecConditionEx
-property_ex[2_two_ex]=ExecStartPreEx
-property_ex[3_three_ex]=ExecStartEx
-property_ex[4_four_ex]=ExecStartPostEx
-property_ex[5_five_ex]=ExecReloadEx
-property_ex[6_six_ex]=ExecStopEx
-property_ex[7_seven_ex]=ExecStopPostEx
-
-for c in "${!property_ex[@]}"; do
-    systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property_ex[$c]}=:/bin/echo \${$c}" /bin/true
-    systemctl show -p "${property_ex[$c]%??}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
-    systemctl show -p "${property_ex[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
-done
-
-systemd-analyze log-level info
diff --git a/test/units/testsuite-23.oneshot-restart.sh b/test/units/testsuite-23.oneshot-restart.sh
deleted file mode 100755 (executable)
index bb4d664..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Test oneshot unit restart on failure
-
-# wait this many secs for each test service to succeed in what is being tested
-MAX_SECS=60
-
-systemctl log-level debug
-
-# test one: Restart=on-failure should restart the service
-(! systemd-run --unit=oneshot-restart-one -p Type=oneshot -p Restart=on-failure /bin/bash -c "exit 1")
-
-for ((secs = 0; secs < MAX_SECS; secs++)); do
-    [[ "$(systemctl show oneshot-restart-one.service -P NRestarts)" -le 0 ]] || break
-    sleep 1
-done
-if [[ "$(systemctl show oneshot-restart-one.service -P NRestarts)" -le 0 ]]; then
-    exit 1
-fi
-
-TMP_FILE="/tmp/test-23-oneshot-restart-test$RANDOM"
-
-: >$TMP_FILE
-
-# test two: make sure StartLimitBurst correctly limits the number of restarts
-# and restarts execution of the unit from the first ExecStart=
-(! systemd-run --unit=oneshot-restart-two \
-        -p StartLimitIntervalSec=120 \
-        -p StartLimitBurst=3 \
-        -p Type=oneshot \
-        -p Restart=on-failure \
-        -p ExecStart="/bin/bash -c 'printf a >>$TMP_FILE'" /bin/bash -c "exit 1")
-
-# wait for at least 3 restarts
-for ((secs = 0; secs < MAX_SECS; secs++)); do
-    [[ $(cat $TMP_FILE) != "aaa" ]] || break
-    sleep 1
-done
-if [[ $(cat $TMP_FILE) != "aaa" ]]; then
-    exit 1
-fi
-
-# wait for 5 more seconds to make sure there aren't excess restarts
-sleep 5
-if [[ $(cat $TMP_FILE) != "aaa" ]]; then
-    exit 1
-fi
-rm "$TMP_FILE"
-
-# Test RestartForceExitStatus=. Note that success exit statuses are meant to be skipped
-
-TMP_FILE="/tmp/test-23-oneshot-restart-test$RANDOM"
-UNIT_NAME="testsuite-23-oneshot-restartforce.service"
-ONSUCCESS_UNIT_NAME="testsuite-23-oneshot-restartforce-onsuccess.service"
-FIFO_FILE="/tmp/test-23-oneshot-restart-test-fifo"
-
-cat >"/run/systemd/system/$UNIT_NAME" <<EOF
-[Unit]
-OnSuccess=$ONSUCCESS_UNIT_NAME
-
-[Service]
-Type=oneshot
-RestartForceExitStatus=0 2
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-23.units/testsuite-23-oneshot-restartforce.sh "$TMP_FILE"
-
-[Install]
-WantedBy=multi-user.target
-EOF
-
-cat >"/run/systemd/system/$ONSUCCESS_UNIT_NAME" <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c 'echo finished >$FIFO_FILE'
-EOF
-
-mkfifo "$FIFO_FILE"
-
-# Pin the unit in memory
-systemctl enable "$UNIT_NAME"
-# Initial run should fail
-(! systemctl start "$UNIT_NAME")
-# Wait for OnSuccess=
-read -r x <"$FIFO_FILE"
-assert_eq "$x" "finished"
-
-cmp -b <(systemctl show "$UNIT_NAME" -p Result -p NRestarts -p SubState) <<EOF
-Result=success
-NRestarts=1
-SubState=dead
-EOF
-
-systemctl disable "$UNIT_NAME"
-rm "$TMP_FILE" /run/systemd/system/{"$UNIT_NAME","$ONSUCCESS_UNIT_NAME"} "$FIFO_FILE"
-
-systemctl log-level info
diff --git a/test/units/testsuite-23.openfile.sh b/test/units/testsuite-23.openfile.sh
deleted file mode 100755 (executable)
index 6108161..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-at_exit() {
-    set +e
-
-    rm -rf /tmp/test-open-file/
-}
-
-trap at_exit EXIT
-
-systemctl log-level debug
-
-# Existing files
-
-mkdir /tmp/test-open-file
-echo "Open" >'/tmp/test-open-file/open.txt'
-echo "File" >'/tmp/test-open-file/file:colon.txt'
-
-systemd-run -p DynamicUser=yes -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
-            -p OpenFile='/tmp/test-open-file/open.txt::read-only' \
-            -p OpenFile='/tmp/test-open-file/file\x3Acolon.txt:colon' \
-            -p RemainAfterExit=yes \
-            --unit=test-23-openfile-existing.service \
-            --service-type=oneshot \
-            /usr/lib/systemd/tests/testdata/units/testsuite-23-openfile-child.sh 2 "open.txt:colon" "Open" "File"
-
-cmp <(systemctl show -p OpenFile test-23-openfile-existing.service) <<EOF
-OpenFile=/tmp/test-open-file/open.txt::read-only
-OpenFile=/tmp/test-open-file/file\\x3acolon.txt:colon
-EOF
-
-systemctl stop test-23-openfile-existing.service
-
-# Sockets
-
-systemctl start testsuite-23-openfile-server.socket
-
-systemd-run -p OpenFile=/tmp/test.sock:socket:read-only \
-            --wait \
-            /usr/lib/systemd/tests/testdata/units/testsuite-23-openfile-child.sh 1 "socket" "Socket"
-
-systemctl stop testsuite-23-openfile-server.socket
-
-# Ignore when missing
-
-assert_rc 202 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only --wait true
-assert_rc 0 systemd-run -p OpenFile=/run/missing/foo:missing-file:read-only,graceful --wait true
-
-systemctl log-level info
diff --git a/test/units/testsuite-23.percentj-wantedby.sh b/test/units/testsuite-23.percentj-wantedby.sh
deleted file mode 100755 (executable)
index e9ffaba..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-# Ensure %j Wants directives work
-systemd-run --wait \
-            --property="Type=oneshot" \
-            --property="Wants=testsuite-23-specifier-j-wants.service" \
-            --property="After=testsuite-23-specifier-j-wants.service" \
-            true
-
-test -f /tmp/tetsuite-23-specifier-j-done
diff --git a/test/units/testsuite-23.runtime-bind-paths.sh b/test/units/testsuite-23.runtime-bind-paths.sh
deleted file mode 100755 (executable)
index 65c2dbf..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# Test adding new BindPaths while unit is already running
-
-at_exit() {
-    set +e
-
-    rm -f /run/testsuite-23-marker-{fixed,runtime}
-    rm -fr /run/inaccessible
-}
-
-trap at_exit EXIT
-
-echo "MARKER_FIXED" >/run/testsuite-23-marker-fixed
-mkdir /run/inaccessible
-
-systemctl start testsuite-23-namespaced.service
-
-# Ensure that inaccessible paths aren't bypassed by the runtime setup,
-(! systemctl bind --mkdir testsuite-23-namespaced.service /run/testsuite-23-marker-fixed /run/inaccessible/testfile-marker-fixed)
-
-echo "MARKER_WRONG" >/run/testsuite-23-marker-wrong
-echo "MARKER_RUNTIME" >/run/testsuite-23-marker-runtime
-
-# Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount)
-systemctl bind --mkdir testsuite-23-namespaced.service /run/testsuite-23-marker-wrong /tmp/testfile-marker-runtime
-test "$(systemctl show -P SubState testsuite-23-namespaced.service)" = "running"
-systemctl bind --mkdir testsuite-23-namespaced.service /run/testsuite-23-marker-runtime /tmp/testfile-marker-runtime
-
-timeout 10 bash -xec 'while [[ "$(systemctl show -P SubState testsuite-23-namespaced.service)" == running ]]; do sleep .5; done'
-systemctl is-active testsuite-23-namespaced.service
-
-# Now test that systemctl bind fails when attempted on a non-namespaced unit
-systemctl start testsuite-23-non-namespaced.service
-
-(! systemctl bind --mkdir testsuite-49-non-namespaced.service /run/testsuite-23-marker-runtime /tmp/testfile-marker-runtime)
-
-timeout 10 bash -xec 'while [[ "$(systemctl show -P SubState testsuite-23-non-namespaced.service)" == running ]]; do sleep .5; done'
-(! systemctl is-active testsuite-23-non-namespaced.service)
diff --git a/test/units/testsuite-23.service b/test/units/testsuite-23.service
deleted file mode 100644 (file)
index 26f5226..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-23-TYPE-EXEC
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-23.sh b/test/units/testsuite-23.sh
deleted file mode 100755 (executable)
index a929c8b..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-# Note: the signal shenanigans are necessary for the Upholds= tests
-run_subtests_with_signals SIGUSR1 SIGUSR2 SIGRTMIN+1
-
-touch /testok
diff --git a/test/units/testsuite-23.start-stop-no-reload.sh b/test/units/testsuite-23.start-stop-no-reload.sh
deleted file mode 100755 (executable)
index e40990b..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-# Test start & stop operations without daemon-reload
-
-at_exit() {
-    set +e
-
-    rm -f /run/systemd/system/testsuite-23-no-reload.{service,target}
-}
-
-trap at_exit EXIT
-
-cat >/run/systemd/system/testsuite-23-no-reload.target <<EOF
-[Unit]
-Wants=testsuite-23-no-reload.service
-EOF
-
-systemctl daemon-reload
-
-systemctl start testsuite-23-no-reload.target
-
-# The filesystem on the test image, despite being ext4, seems to have a mtime
-# granularity of one second, which means the manager's unit cache won't be
-# marked as dirty when writing the unit file, unless we wait at least a full
-# second after the previous daemon-reload.
-# May 07 23:12:20 H testsuite-48.sh[30]: + cat
-# May 07 23:12:20 H testsuite-48.sh[30]: + ls -l --full-time /etc/systemd/system/testsuite-23-no-reload.service
-# May 07 23:12:20 H testsuite-48.sh[52]: -rw-r--r-- 1 root root 50 2020-05-07 23:12:20.000000000 +0100 /
-# May 07 23:12:20 H testsuite-48.sh[30]: + stat -f --format=%t /etc/systemd/system/testsuite-23-no-reload.servic
-# May 07 23:12:20 H testsuite-48.sh[53]: ef53
-sleep 3.1
-
-cat >/run/systemd/system/testsuite-23-no-reload.service <<EOF
-[Service]
-ExecStart=sleep infinity
-EOF
-
-systemctl start testsuite-23-no-reload.service
-
-systemctl is-active testsuite-23-no-reload.service
-
-# Stop and remove, and try again to exercise https://github.com/systemd/systemd/issues/15992
-systemctl stop testsuite-23-no-reload.service
-rm -f /run/systemd/system/testsuite-23-no-reload.service
-systemctl daemon-reload
-
-sleep 3.1
-
-cat >/run/systemd/system/testsuite-23-no-reload.service <<EOF
-[Service]
-ExecStart=sleep infinity
-EOF
-
-# Start a non-existing unit first, so that the cache is reloaded for an unrelated
-# reason. Starting the existing unit later should still work thanks to the check
-# for the last load attempt vs cache timestamp.
-systemctl start testsuite-23-no-reload-nonexistent.service || true
-
-systemctl start testsuite-23-no-reload.service
-
-systemctl is-active testsuite-23-no-reload.service
-
-# Stop and remove, and try again to exercise the transaction setup code path by
-# having the target pull in the unloaded but available unit
-systemctl stop testsuite-23-no-reload.service testsuite-23-no-reload.target
-rm -f /run/systemd/system/testsuite-23-no-reload.service /run/systemd/system/testsuite-23-no-reload.target
-systemctl daemon-reload
-
-sleep 3.1
-
-cat >/run/systemd/system/testsuite-23-no-reload.target <<EOF
-[Unit]
-Conflicts=shutdown.target
-Wants=testsuite-23-no-reload.service
-EOF
-
-systemctl daemon-reload
-
-systemctl start testsuite-23-no-reload.target
-
-cat >/run/systemd/system/testsuite-23-no-reload.service <<EOF
-[Service]
-ExecStart=sleep infinity
-EOF
-
-systemctl restart testsuite-23-no-reload.target
-
-systemctl is-active testsuite-23-no-reload.service
diff --git a/test/units/testsuite-23.statedir.sh b/test/units/testsuite-23.statedir.sh
deleted file mode 100755 (executable)
index b592314..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-# Test unit configuration/state/cache/log/runtime data cleanup
-
-export HOME=/root
-export XDG_RUNTIME_DIR=/run/user/0
-
-systemctl start user@0.service
-
-( ! test -d "$HOME"/.local/state/foo)
-( ! test -d "$HOME"/.config/foo)
-
-systemd-run --user -p StateDirectory=foo --wait /bin/true
-
-test -d "$HOME"/.local/state/foo
-( ! test -L "$HOME"/.local/state/foo)
-( ! test -d "$HOME"/.config/foo)
-
-systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
-
-test -d "$HOME"/.local/state/foo
-( ! test -L "$HOME"/.local/state/foo)
-test -d "$HOME"/.config/foo
-
-rmdir "$HOME"/.local/state/foo "$HOME"/.config/foo
-
-systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
-
-test -d "$HOME"/.local/state/foo
-( ! test -L "$HOME"/.local/state/foo)
-test -d "$HOME"/.config/foo
-
-rmdir "$HOME"/.local/state/foo "$HOME"/.config/foo
-
-# Now trigger an update scenario by creating a config dir first
-systemd-run --user -p ConfigurationDirectory=foo --wait /bin/true
-
-( ! test -d "$HOME"/.local/state/foo)
-test -d "$HOME"/.config/foo
-
-# This will look like an update and result in a symlink
-systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
-
-test -d "$HOME"/.local/state/foo
-test -L "$HOME"/.local/state/foo
-test -d "$HOME"/.config/foo
-
-test "$(readlink "$HOME"/.local/state/foo)" = ../../.config/foo
-
-# Check that this will work safely a second time
-systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
-
-rm "$HOME"/.local/state/foo
-rmdir "$HOME"/.config/foo
diff --git a/test/units/testsuite-23.success-failure.sh b/test/units/testsuite-23.success-failure.sh
deleted file mode 100755 (executable)
index 8fc9596..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test OnSuccess=/OnFailure= in combination
-
-systemd-analyze log-level debug
-
-# Start-up should fail, but the automatic restart should fix it
-(! systemctl start success-failure-test )
-
-# Wait until the first invocation finished & failed
-while test ! -f /tmp/success-failure-test-ran ; do
-    sleep .5
-done
-
-# Wait until the second invocation finished & succeeded
-while test ! -f /tmp/success-failure-test-ran2 ; do
-    sleep .5
-done
-
-# Verify it is indeed running
-systemctl is-active -q success-failure-test
-
-# The above should have caused the failure service to start (asynchronously)
-while test "$(systemctl is-active success-failure-test-failure)" != "active" ; do
-    sleep .5
-done
-
-# But the success service should not have started
-test "$(systemctl is-active success-failure-test-success)" = "inactive"
-
-systemctl stop success-failure-test-failure
-
-# Do a clean kill of the service now
-systemctl kill success-failure-test
-
-# This should result in the success service to start
-while test "$(systemctl is-active success-failure-test-success)" != "active" ; do
-    sleep .5
-done
-
-# But the failure service should not have started again
-test "$(systemctl is-active success-failure-test-failure)" = "inactive"
-
-systemctl stop success-failure-test success-failure-test-success
-
-systemd-analyze log-level info
diff --git a/test/units/testsuite-23.type-exec.sh b/test/units/testsuite-23.type-exec.sh
deleted file mode 100755 (executable)
index 87f32cc..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test Type=exec
-
-systemd-analyze log-level debug
-
-# Create a binary for which execve() will fail
-touch /tmp/brokenbinary
-chmod +x /tmp/brokenbinary
-
-# These three commands should succeed.
-systemd-run --unit=exec-one -p Type=simple /bin/sleep infinity
-systemd-run --unit=exec-two -p Type=simple -p User=idontexist /bin/sleep infinity
-systemd-run --unit=exec-three -p Type=simple /tmp/brokenbinary
-
-# And now, do the same with Type=exec, where the latter two should fail
-systemd-run --unit=exec-four -p Type=exec /bin/sleep infinity
-(! systemd-run --unit=exec-five -p Type=exec -p User=idontexist /bin/sleep infinity)
-(! systemd-run --unit=exec-six -p Type=exec /tmp/brokenbinary)
-
-systemd-run --unit=exec-seven -p KillSignal=SIGTERM -p RestartKillSignal=SIGINT -p Type=exec /bin/sleep infinity
-# Both TERM and SIGINT happen to have the same number on all architectures
-test "$(systemctl show --value -p KillSignal exec-seven.service)" -eq 15
-test "$(systemctl show --value -p RestartKillSignal exec-seven.service)" -eq 2
-
-systemctl restart exec-seven.service
-systemctl stop exec-seven.service
-
-# For issue #20933
-
-# Should work normally
-busctl call \
-    org.freedesktop.systemd1 /org/freedesktop/systemd1 \
-    org.freedesktop.systemd1.Manager StartTransientUnit \
-    "ssa(sv)a(sa(sv))" test-20933-ok.service replace 1 \
-      ExecStart "a(sasb)" 1 \
-        /usr/bin/sleep 2 /usr/bin/sleep 1 true \
-    0
-
-# DBus call should fail but not crash systemd
-(! busctl call \
-    org.freedesktop.systemd1 /org/freedesktop/systemd1 \
-    org.freedesktop.systemd1.Manager StartTransientUnit \
-    "ssa(sv)a(sa(sv))" test-20933-bad.service replace 1 \
-      ExecStart "a(sasb)" 1 \
-        /usr/bin/sleep 0 true \
-    0)
-
-# Same but with the empty argv in the middle
-(! busctl call \
-    org.freedesktop.systemd1 /org/freedesktop/systemd1 \
-    org.freedesktop.systemd1.Manager StartTransientUnit \
-    "ssa(sv)a(sa(sv))" test-20933-bad-middle.service replace 1 \
-      ExecStart "a(sasb)" 3 \
-        /usr/bin/sleep 2 /usr/bin/sleep 1 true \
-        /usr/bin/sleep 0                  true \
-        /usr/bin/sleep 2 /usr/bin/sleep 1 true \
-    0)
-
-systemd-analyze log-level info
diff --git a/test/units/testsuite-23.utmp.sh b/test/units/testsuite-23.utmp.sh
deleted file mode 100755 (executable)
index 4f84315..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-USER="test-23-utmp"
-
-cleanup() {
-    userdel "$USER"
-}
-
-trap cleanup EXIT
-useradd "$USER"
-
-assert_eq "$(systemd-run -qP -p UtmpIdentifier=test -p UtmpMode=user -p User=$USER whoami)" "$USER"
-assert_eq "$(systemd-run -qP -p UtmpIdentifier=test -p UtmpMode=user whoami)" "$(whoami)"
diff --git a/test/units/testsuite-23.verify-unit-files.sh b/test/units/testsuite-23.verify-unit-files.sh
deleted file mode 100755 (executable)
index 3e83d44..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# Verify our own unit files (where applicable)
-
-# This is generated by meson during build
-UNIT_FILE_LIST="/usr/lib/systemd/tests/testdata/installed-unit-files.txt"
-
-if [[ ! -f "$UNIT_FILE_LIST" ]]; then
-    echo "Couldn't find the list of installed units, skipping the test"
-    exit 0
-fi
-
-if ! command -v systemd-analyze >/dev/null; then
-    echo "Built without systemd-analyze, skipping the test"
-    exit 0
-fi
-
-mapfile -t UNIT_FILES <"$UNIT_FILE_LIST"
-
-if [[ "${#UNIT_FILES[@]}" -le 0 ]]; then
-    echo >&2 "The unit file list is empty, this is most likely a bug"
-    exit 1
-fi
-
-for unit_file in "${UNIT_FILES[@]}"; do
-    # Skip the check for a couple of units, namely:
-    #   - syslog.socket: the corresponding syslog.service might not be installed
-    #   - rc-local.service: compat API, /etc/rc.d/rc.local most likely won't be present
-    if [[ "$unit_file" =~ /(syslog.socket|rc-local.service)$ ]]; then
-        continue
-    fi
-
-    # Skip missing unit files - this is useful for $NO_BUILD runs, where certain unit files might be dropped
-    # in distro packaging
-    if [[ ! -e "$unit_file" ]]; then
-        echo "$unit_file not found, skipping"
-        continue
-    fi
-
-    systemd-analyze --recursive-errors=no --man=no verify "$unit_file"
-done
diff --git a/test/units/testsuite-23.whoami.sh b/test/units/testsuite-23.whoami.sh
deleted file mode 100755 (executable)
index a0c73b8..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-test "$(systemctl whoami)" = testsuite-23.service
-test "$(systemctl whoami $$)" = testsuite-23.service
-
-systemctl whoami 1 $$ 1 | cmp - /dev/fd/3 3<<'EOF'
-init.scope
-testsuite-23.service
-init.scope
-EOF
diff --git a/test/units/testsuite-24.service b/test/units/testsuite-24.service
deleted file mode 100644 (file)
index e192d1c..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-24-CRYPTSETUP
-After=multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-24.sh b/test/units/testsuite-24.sh
deleted file mode 100755 (executable)
index 0822b86..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# TODO:
-#   - /proc/cmdline parsing
-#   - expect + interactive auth?
-
-# We set up an encrypted /var partition which should get mounted automatically
-# on boot
-mountpoint /var
-
-systemctl --state=failed --no-legend --no-pager | tee /failed
-if [[ -s /failed ]]; then
-    echo >&2 "Found units in failed state"
-    exit 1
-fi
-
-at_exit() {
-    set +e
-
-    mountpoint -q /proc/cmdline && umount /proc/cmdline
-    rm -f /etc/crypttab
-    [[ -e /tmp/crypttab.bak ]] && cp -fv /tmp/crypttab.bak /etc/crypttab
-    [[ -n "${STORE_LOOP:-}" ]] && losetup -d "$STORE_LOOP"
-    [[ -n "${WORKDIR:-}" ]] && rm -rf "$WORKDIR"
-
-    systemctl daemon-reload
-}
-
-trap at_exit EXIT
-
-cryptsetup_start_and_check() {
-    local expect_fail=0
-    local umount_header_and_key=0
-    local ec volume unit
-
-    if [[ "${1:?}" == "-f" ]]; then
-        expect_fail=1
-        shift
-    fi
-
-    if [[ "${1:?}" == "-u" ]]; then
-        umount_header_and_key=1
-        shift
-    fi
-
-    for volume in "$@"; do
-        unit="systemd-cryptsetup@$volume.service"
-
-        # The unit existence check should always pass
-        [[ "$(systemctl show -P LoadState "$unit")" == loaded ]]
-        systemctl list-unit-files "$unit"
-
-        systemctl start "$unit" && ec=0 || ec=$?
-        if [[ "$expect_fail" -ne 0 ]]; then
-            if [[ "$ec" -eq 0 ]]; then
-                echo >&2 "Unexpected pass when starting $unit"
-                return 1
-            fi
-
-            return 0
-        fi
-
-        if [[ "$ec" -ne 0 ]]; then
-            echo >&2 "Unexpected fail when starting $unit"
-            return 1
-        fi
-
-        if [[ "$umount_header_and_key" -ne 0 ]]; then
-            umount "$TMPFS_DETACHED_KEYFILE"
-            umount "$TMPFS_DETACHED_HEADER"
-            udevadm settle --timeout=30
-        fi
-
-        systemctl status "$unit"
-        test -e "/dev/mapper/$volume"
-        systemctl stop "$unit"
-        test ! -e "/dev/mapper/$volume"
-    done
-
-    return 0
-}
-
-# Note: some stuff (especially TPM-related) is already tested by TEST-70-TPM2,
-#       so focus more on other areas instead
-
-# Use a common workdir to make the cleanup easier
-WORKDIR="$(mktemp -d)"
-
-# Prepare a couple of LUKS2-encrypted disk images
-#
-# 1) Image with an empty password
-IMAGE_EMPTY="$WORKDIR/empty.img)"
-IMAGE_EMPTY_KEYFILE="$WORKDIR/empty.keyfile"
-IMAGE_EMPTY_KEYFILE_ERASE="$WORKDIR/empty-erase.keyfile"
-IMAGE_EMPTY_KEYFILE_ERASE_FAIL="$WORKDIR/empty-erase-fail.keyfile)"
-truncate -s 32M "$IMAGE_EMPTY"
-echo -n passphrase >"$IMAGE_EMPTY_KEYFILE"
-chmod 0600 "$IMAGE_EMPTY_KEYFILE"
-cryptsetup luksFormat --batch-mode \
-                      --pbkdf pbkdf2 \
-                      --pbkdf-force-iterations 1000 \
-                      --use-urandom \
-                      "$IMAGE_EMPTY" "$IMAGE_EMPTY_KEYFILE"
-PASSWORD=passphrase NEWPASSWORD="" systemd-cryptenroll --password "$IMAGE_EMPTY"
-# Duplicate the key file to test keyfile-erase as well
-cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE"
-# The key should get erased even on a failed attempt, so test that too
-cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
-
-# 2) Image with a detached header and a key file offset + size
-IMAGE_DETACHED="$WORKDIR/detached.img"
-IMAGE_DETACHED_KEYFILE="$WORKDIR/detached.keyfile"
-IMAGE_DETACHED_KEYFILE2="$WORKDIR/detached.keyfile2"
-IMAGE_DETACHED_HEADER="$WORKDIR/detached.header"
-truncate -s 32M "$IMAGE_DETACHED"
-dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE" count=64 bs=1
-dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE2" count=32 bs=1
-chmod 0600 "$IMAGE_DETACHED_KEYFILE" "$IMAGE_DETACHED_KEYFILE2"
-cryptsetup luksFormat --batch-mode \
-                      --pbkdf pbkdf2 \
-                      --pbkdf-force-iterations 1000 \
-                      --use-urandom \
-                      --header "$IMAGE_DETACHED_HEADER" \
-                      --keyfile-offset 32 \
-                      --keyfile-size 16 \
-                      "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE"
-# Also, add a second key file to key slot 8
-# Note: --key-slot= behaves as --new-key-slot= when used alone for backwards compatibility
-cryptsetup luksAddKey --batch-mode \
-                      --header "$IMAGE_DETACHED_HEADER" \
-                      --key-file "$IMAGE_DETACHED_KEYFILE" \
-                      --keyfile-offset 32 \
-                      --keyfile-size 16 \
-                      --key-slot 8 \
-                      "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE2"
-
-# Prepare a couple of dummy devices we'll store a copy of the detached header
-# and one of the keys on to test if systemd-cryptsetup correctly mounts them
-# when necessary
-STORE_IMAGE="$WORKDIR/store.img"
-truncate -s 64M "$STORE_IMAGE"
-STORE_LOOP="$(losetup --show --find --partscan "$STORE_IMAGE")"
-sfdisk "$STORE_LOOP" <<EOF
-label: gpt
-type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=header_store size=32M
-type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=keyfile_store
-EOF
-udevadm settle --timeout=30
-mkdir -p /mnt
-mkfs.ext4 -L header_store "/dev/disk/by-partlabel/header_store"
-mount "/dev/disk/by-partlabel/header_store" /mnt
-cp "$IMAGE_DETACHED_HEADER" /mnt/header
-umount /mnt
-mkfs.ext4 -L keyfile_store "/dev/disk/by-partlabel/keyfile_store"
-mount "/dev/disk/by-partlabel/keyfile_store" /mnt
-cp "$IMAGE_DETACHED_KEYFILE2" /mnt/keyfile
-umount /mnt
-
-# Also copy the key and header on a tmpfs that we will umount after unlocking
-TMPFS_DETACHED_KEYFILE="$(mktemp -d)"
-TMPFS_DETACHED_HEADER="$(mktemp -d)"
-mount -t tmpfs -o size=32M tmpfs "$TMPFS_DETACHED_KEYFILE"
-mount -t tmpfs -o size=32M tmpfs "$TMPFS_DETACHED_HEADER"
-cp "$IMAGE_DETACHED_KEYFILE" "$TMPFS_DETACHED_KEYFILE/keyfile"
-cp "$IMAGE_DETACHED_HEADER" "$TMPFS_DETACHED_HEADER/header"
-
-udevadm settle --timeout=30
-
-# Prepare our test crypttab
-[[ -e /etc/crypttab ]] && cp -fv /etc/crypttab /tmp/crypttab.bak
-cat >/etc/crypttab <<EOF
-# headless should translate to headless=1
-empty_key            $IMAGE_EMPTY    $IMAGE_EMPTY_KEYFILE            headless,x-systemd.device-timeout=1m
-empty_key_erase      $IMAGE_EMPTY    $IMAGE_EMPTY_KEYFILE_ERASE      headless=1,keyfile-erase=1
-empty_key_erase_fail $IMAGE_EMPTY    $IMAGE_EMPTY_KEYFILE_ERASE_FAIL headless=1,keyfile-erase=1,keyfile-offset=4
-# Empty passphrase without try-empty-password(=yes) shouldn't work
-empty_fail0          $IMAGE_EMPTY    -                               headless=1
-empty_fail1          $IMAGE_EMPTY    -                               headless=1,try-empty-password=0
-empty0               $IMAGE_EMPTY    -                               headless=1,try-empty-password
-empty1               $IMAGE_EMPTY    -                               headless=1,try-empty-password=1
-# This one expects the key to be under /{etc,run}/cryptsetup-keys.d/empty_nokey.key
-empty_nokey          $IMAGE_EMPTY    -                               headless=1
-empty_pkcs11_auto    $IMAGE_EMPTY    -                               headless=1,pkcs11-uri=auto
-
-detached             $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32,keyfile-size=16
-detached_store0      $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=/header:LABEL=header_store,keyfile-offset=32,keyfile-size=16
-detached_store1      $IMAGE_DETACHED /keyfile:LABEL=keyfile_store    headless=1,header=$IMAGE_DETACHED_HEADER
-detached_store2      $IMAGE_DETACHED /keyfile:LABEL=keyfile_store    headless=1,header=/header:LABEL=header_store
-detached_fail0       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32
-detached_fail1       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=$IMAGE_DETACHED_HEADER
-detached_fail2       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1
-detached_fail3       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=16,keyfile-size=16
-detached_fail4       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE         headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32,keyfile-size=8
-detached_slot0       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2        headless=1,header=$IMAGE_DETACHED_HEADER
-detached_slot1       $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2        headless=1,header=$IMAGE_DETACHED_HEADER,key-slot=8
-detached_slot_fail   $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE2        headless=1,header=$IMAGE_DETACHED_HEADER,key-slot=0
-detached_nofail      $IMAGE_DETACHED $TMPFS_DETACHED_KEYFILE/keyfile headless=1,header=$TMPFS_DETACHED_HEADER/header,keyfile-offset=32,keyfile-size=16,nofail
-EOF
-
-# Temporarily drop luks.name=/luks.uuid= from the kernel command line, as it makes
-# systemd-cryptsetup-generator ignore mounts from /etc/crypttab that are not also
-# specified on the kernel command line
-sed -r 's/luks.(name|uuid)=[^[:space:]+]//' /proc/cmdline >/tmp/cmdline.tmp
-mount --bind /tmp/cmdline.tmp /proc/cmdline
-# Run the systemd-cryptsetup-generator once explicitly, to collect coverage,
-# as during daemon-reload we run generators in a sandbox
-mkdir -p /tmp/systemd-cryptsetup-generator.out
-/usr/lib/systemd/system-generators/systemd-cryptsetup-generator /tmp/systemd-cryptsetup-generator.out/
-systemctl daemon-reload
-systemctl list-unit-files "systemd-cryptsetup@*"
-
-cryptsetup_start_and_check empty_key
-test -e "$IMAGE_EMPTY_KEYFILE_ERASE"
-cryptsetup_start_and_check empty_key_erase
-test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE"
-test -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
-cryptsetup_start_and_check -f empty_key_erase_fail
-test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL"
-cryptsetup_start_and_check -f empty_fail{0..1}
-cryptsetup_start_and_check empty{0..1}
-# First, check if we correctly fail without any key
-cryptsetup_start_and_check -f empty_nokey
-# And now provide the key via /{etc,run}/cryptsetup-keys.d/
-mkdir -p /run/cryptsetup-keys.d
-cp "$IMAGE_EMPTY_KEYFILE" /run/cryptsetup-keys.d/empty_nokey.key
-cryptsetup_start_and_check empty_nokey
-
-if [[ -r /etc/softhsm2.conf ]]; then
-    # Test unlocking with a PKCS#11 token
-    export SOFTHSM2_CONF="/etc/softhsm2.conf"
-
-    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
-    cryptsetup_start_and_check empty_pkcs11_auto
-    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
-    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
-
-    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey;type=cert" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
-    cryptsetup_start_and_check empty_pkcs11_auto
-    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
-    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
-
-    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey;type=public" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
-    cryptsetup_start_and_check empty_pkcs11_auto
-    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
-    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
-
-    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
-    cryptsetup_start_and_check empty_pkcs11_auto
-    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
-    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
-
-    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey;type=cert" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
-    cryptsetup_start_and_check empty_pkcs11_auto
-    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
-    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
-
-    PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey;type=public" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
-    cryptsetup_start_and_check empty_pkcs11_auto
-    cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
-    cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
-fi
-
-cryptsetup_start_and_check detached
-cryptsetup_start_and_check detached_store{0..2}
-cryptsetup_start_and_check -f detached_fail{0..4}
-cryptsetup_start_and_check detached_slot{0..1}
-cryptsetup_start_and_check -f detached_slot_fail
-cryptsetup_start_and_check -u detached_nofail
-
-touch /testok
diff --git a/test/units/testsuite-25.service b/test/units/testsuite-25.service
deleted file mode 100644 (file)
index 503eabb..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-25-IMPORT
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-25.sh b/test/units/testsuite-25.sh
deleted file mode 100755 (executable)
index b298c50..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-export SYSTEMD_PAGER=cat
-
-dd if=/dev/urandom of=/var/tmp/testimage.raw bs=$((1024*1024+7)) count=5
-
-# Test import
-machinectl import-raw /var/tmp/testimage.raw
-machinectl image-status testimage
-test -f /var/lib/machines/testimage.raw
-cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
-
-# Test export
-machinectl export-raw testimage /var/tmp/testimage2.raw
-cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
-rm /var/tmp/testimage2.raw
-
-# Test compressed export (gzip)
-machinectl export-raw testimage /var/tmp/testimage2.raw.gz
-gunzip /var/tmp/testimage2.raw.gz
-cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
-rm /var/tmp/testimage2.raw
-
-# Test clone
-machinectl clone testimage testimage3
-test -f /var/lib/machines/testimage3.raw
-machinectl image-status testimage3
-test -f /var/lib/machines/testimage.raw
-machinectl image-status testimage
-cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
-cmp /var/tmp/testimage.raw /var/lib/machines/testimage3.raw
-
-# Test removal
-machinectl remove testimage
-test ! -f /var/lib/machines/testimage.raw
-(! machinectl image-status testimage)
-
-# Test export of clone
-machinectl export-raw testimage3 /var/tmp/testimage3.raw
-cmp /var/tmp/testimage.raw /var/tmp/testimage3.raw
-rm /var/tmp/testimage3.raw
-
-# Test rename
-machinectl rename testimage3 testimage4
-test -f /var/lib/machines/testimage4.raw
-machinectl image-status testimage4
-test ! -f /var/lib/machines/testimage3.raw
-(! machinectl image-status testimage3)
-cmp /var/tmp/testimage.raw /var/lib/machines/testimage4.raw
-
-# Test export of rename
-machinectl export-raw testimage4 /var/tmp/testimage4.raw
-cmp /var/tmp/testimage.raw /var/tmp/testimage4.raw
-rm /var/tmp/testimage4.raw
-
-# Test removal
-machinectl remove testimage4
-test ! -f /var/lib/machines/testimage4.raw
-(! machinectl image-status testimage4)
-
-# → And now, let's test directory trees ← #
-
-# Set up a directory we can import
-mkdir /var/tmp/scratch
-mv /var/tmp/testimage.raw /var/tmp/scratch/
-touch /var/tmp/scratch/anotherfile
-mkdir /var/tmp/scratch/adirectory
-echo "piep" >/var/tmp/scratch/adirectory/athirdfile
-
-# Test import-fs
-machinectl import-fs /var/tmp/scratch/
-test -d /var/lib/machines/scratch
-machinectl image-status scratch
-
-# Test export-tar
-machinectl export-tar scratch /var/tmp/scratch.tar.gz
-test -f /var/tmp/scratch.tar.gz
-mkdir /var/tmp/extract
-(cd /var/tmp/extract ; tar xzf /var/tmp/scratch.tar.gz)
-diff -r /var/tmp/scratch/ /var/tmp/extract/
-rm -rf /var/tmp/extract
-
-# Test import-tar
-machinectl import-tar /var/tmp/scratch.tar.gz scratch2
-test -d /var/lib/machines/scratch2
-machinectl image-status scratch2
-diff -r /var/tmp/scratch/ /var/lib/machines/scratch2
-
-# Test removal
-machinectl remove scratch
-test ! -f /var/lib/machines/scratch
-(! machinectl image-status scratch)
-
-# Test clone
-machinectl clone scratch2 scratch3
-test -d /var/lib/machines/scratch2
-machinectl image-status scratch2
-test -d /var/lib/machines/scratch3
-machinectl image-status scratch3
-diff -r /var/tmp/scratch/ /var/lib/machines/scratch3
-
-# Test removal
-machinectl remove scratch2
-test ! -f /var/lib/machines/scratch2
-(! machinectl image-status scratch2)
-
-# Test rename
-machinectl rename scratch3 scratch4
-test -d /var/lib/machines/scratch4
-machinectl image-status scratch4
-test ! -f /var/lib/machines/scratch3
-(! machinectl image-status scratch3)
-diff -r /var/tmp/scratch/ /var/lib/machines/scratch4
-
-# Test removal
-machinectl remove scratch4
-test ! -f /var/lib/machines/scratch4
-(! machinectl image-status scratch4)
-
-# Test import-tar hyphen/stdin pipe behavior
-# shellcheck disable=SC2002
-cat /var/tmp/scratch.tar.gz | machinectl import-tar - scratch5
-test -d /var/lib/machines/scratch5
-machinectl image-status scratch5
-diff -r /var/tmp/scratch/ /var/lib/machines/scratch5
-
-# Test export-tar hyphen/stdout pipe behavior
-mkdir -p /var/tmp/extract
-machinectl export-tar scratch5 - | tar xvf - -C /var/tmp/extract/
-diff -r /var/tmp/scratch/ /var/tmp/extract/
-rm -rf /var/tmp/extract
-
-rm -rf /var/tmp/scratch
-
-# Test removal
-machinectl remove scratch5
-test ! -f /var/lib/machines/scratch5
-(! machinectl image-status scratch5)
-
-touch /testok
diff --git a/test/units/testsuite-26.service b/test/units/testsuite-26.service
deleted file mode 100644 (file)
index d8fdaff..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-26-SYSTEMCTL
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-26.sh b/test/units/testsuite-26.sh
deleted file mode 100755 (executable)
index 117ffb8..0000000
+++ /dev/null
@@ -1,501 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-at_exit() {
-    if [[ -v UNIT_NAME && -e "/usr/lib/systemd/system/$UNIT_NAME" ]]; then
-        rm -fvr "/usr/lib/systemd/system/$UNIT_NAME" "/etc/systemd/system/$UNIT_NAME.d" "+4"
-    fi
-
-    rm -f /etc/init.d/issue-24990
-    return 0
-}
-
-# Create a simple unit file for testing
-# Note: the service file is created under /usr on purpose to test
-#       the 'revert' verb as well
-export UNIT_NAME="systemctl-test-$RANDOM.service"
-export UNIT_NAME2="systemctl-test-$RANDOM.service"
-
-cat >"/usr/lib/systemd/system/$UNIT_NAME" <<\EOF
-[Unit]
-Description=systemctl test
-
-[Service]
-ExecStart=sleep infinity
-ExecReload=true
-
-# For systemctl clean
-CacheDirectory=%n
-ConfigurationDirectory=%n
-LogsDirectory=%n
-RuntimeDirectory=%n
-StateDirectory=%n
-
-[Install]
-WantedBy=multi-user.target
-EOF
-
-# Configure the preset setting for the unit file
-mkdir /run/systemd/system-preset/
-echo "disable $UNIT_NAME" >/run/systemd/system-preset/99-systemd-test.preset
-
-EDITOR='true' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
-[ ! -e "/etc/systemd/system/$UNIT_NAME.d/override.conf" ]
-
-printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' >"+4"
-EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
-printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf"
-
-printf '%b'   '[Service]\n' 'ExecStart=\n' 'ExecStart=sleep 10d' >"+4"
-EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null
-printf '%s\n' '[Service]'   'ExecStart='   'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf"
-
-systemctl edit "$UNIT_NAME" --stdin --drop-in=override2.conf <<EOF
-[Unit]
-Description=spectacular
-# this comment should remain
-
-EOF
-printf '%s\n' '[Unit]'   'Description=spectacular' '# this comment should remain' | \
-    cmp - "/etc/systemd/system/$UNIT_NAME.d/override2.conf"
-
-# Test simultaneous editing of two units and creation of drop-in for a nonexistent unit
-systemctl edit "$UNIT_NAME" "$UNIT_NAME2" --stdin --force --drop-in=override2.conf <<<'[X-Section]'
-printf '%s\n' '[X-Section]' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override2.conf"
-printf '%s\n' '[X-Section]' | cmp - "/etc/systemd/system/$UNIT_NAME2.d/override2.conf"
-
-# Double free when editing a template unit (#26483)
-EDITOR='true' script -ec 'systemctl edit user@0' /dev/null
-
-# Argument help
-systemctl --state help
-systemctl --signal help
-systemctl --type help
-
-# list-dependencies
-systemctl list-dependencies systemd-journald
-systemctl list-dependencies --after systemd-journald
-systemctl list-dependencies --before systemd-journald
-systemctl list-dependencies --after --reverse systemd-journald
-systemctl list-dependencies --before --reverse systemd-journald
-systemctl list-dependencies --plain systemd-journald
-
-# list-* verbs
-systemctl list-units
-systemctl list-units --recursive
-systemctl list-units --type=socket
-systemctl list-units --type=service,timer
-# Compat: --type= allows load states for compatibility reasons
-systemctl list-units --type=loaded
-systemctl list-units --type=loaded,socket
-systemctl list-units --legend=yes -a "systemd-*"
-systemctl list-units --state=active
-systemctl list-units --with-dependencies systemd-journald.service
-systemctl list-units --with-dependencies --after systemd-journald.service
-systemctl list-units --with-dependencies --before --reverse systemd-journald.service
-systemctl list-sockets
-systemctl list-sockets --legend=no -a "*journal*"
-systemctl list-sockets --show-types
-systemctl list-sockets --state=listening
-systemctl list-timers -a -l
-systemctl list-jobs
-systemctl list-jobs --after
-systemctl list-jobs --before
-systemctl list-jobs --after --before
-systemctl list-jobs "*"
-systemctl list-dependencies sysinit.target --type=socket,mount
-systemctl list-dependencies multi-user.target --state=active
-systemctl list-dependencies sysinit.target --state=mounted --all
-systemctl list-paths
-systemctl list-paths --legend=no -a "systemd*"
-
-test_list_unit_files() {
-    systemctl list-unit-files "$@"
-    systemctl list-unit-files "$@" "*journal*"
-}
-
-test_list_unit_files
-test_list_unit_files --root=/
-
-# is-* verbs
-# Should return 4 for a missing unit file
-assert_rc 4 systemctl --quiet is-active not-found.service
-assert_rc 4 systemctl --quiet is-failed not-found.service
-assert_rc 4 systemctl --quiet is-enabled not-found.service
-# is-active: return 3 when the unit exists but inactive
-assert_rc 3 systemctl --quiet is-active "$UNIT_NAME"
-# is-enabled: return 1 when the unit exists but disabled
-assert_rc 1 systemctl --quiet is-enabled "$UNIT_NAME"
-
-# Basic service management
-systemctl start --show-transaction "$UNIT_NAME"
-systemctl status -n 5 "$UNIT_NAME"
-systemctl is-active "$UNIT_NAME"
-systemctl reload -T "$UNIT_NAME"
-systemctl restart -T "$UNIT_NAME"
-systemctl try-restart --show-transaction "$UNIT_NAME"
-systemctl try-reload-or-restart --show-transaction "$UNIT_NAME"
-timeout 10 systemctl kill --wait "$UNIT_NAME"
-(! systemctl is-active "$UNIT_NAME")
-systemctl restart "$UNIT_NAME"
-systemctl is-active "$UNIT_NAME"
-systemctl restart "$UNIT_NAME"
-systemctl stop "$UNIT_NAME"
-(! systemctl is-active "$UNIT_NAME")
-
-assert_eq "$(systemctl is-system-running)" "$(systemctl is-failed)"
-
-# enable/disable/preset
-test_enable_disable_preset() {
-    (! systemctl is-enabled "$@" "$UNIT_NAME")
-    systemctl enable "$@" "$UNIT_NAME"
-    systemctl is-enabled "$@" -l "$UNIT_NAME"
-    # We created a preset file for this unit above with a "disable" policy
-    systemctl preset "$@" "$UNIT_NAME"
-    (! systemctl is-enabled "$@" "$UNIT_NAME")
-    systemctl reenable "$@" "$UNIT_NAME"
-    systemctl is-enabled "$@" "$UNIT_NAME"
-    systemctl preset "$@" --preset-mode=enable-only "$UNIT_NAME"
-    systemctl is-enabled "$@" "$UNIT_NAME"
-    systemctl preset "$@" --preset-mode=disable-only "$UNIT_NAME"
-    (! systemctl is-enabled "$@" "$UNIT_NAME")
-    systemctl enable "$@" --runtime "$UNIT_NAME"
-    [[ -e "/run/systemd/system/multi-user.target.wants/$UNIT_NAME" ]]
-    systemctl is-enabled "$@" "$UNIT_NAME"
-    systemctl disable "$@" "$UNIT_NAME"
-    # The unit should be still enabled, as we didn't use the --runtime switch
-    systemctl is-enabled "$@" "$UNIT_NAME"
-    systemctl disable "$@" --runtime "$UNIT_NAME"
-    (! systemctl is-enabled "$@" "$UNIT_NAME")
-}
-
-test_enable_disable_preset
-test_enable_disable_preset --root=/
-
-# mask/unmask/revert
-test_mask_unmask_revert() {
-    systemctl disable "$@" "$UNIT_NAME"
-    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
-    systemctl mask "$@" "$UNIT_NAME"
-    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]]
-    systemctl unmask "$@" "$UNIT_NAME"
-    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
-    systemctl mask "$@" "$UNIT_NAME"
-    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]]
-    systemctl revert "$@" "$UNIT_NAME"
-    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
-    systemctl mask "$@" --runtime "$UNIT_NAME"
-    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]]
-    # This should be a no-op without the --runtime switch
-    systemctl unmask "$@" "$UNIT_NAME"
-    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]]
-    systemctl unmask "$@" --runtime "$UNIT_NAME"
-    [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]]
-}
-
-test_mask_unmask_revert
-test_mask_unmask_revert --root=/
-
-# disable --now with template unit
-cat >/run/systemd/system/test-disable@.service <<EOF
-[Service]
-ExecStart=sleep infinity
-
-[Install]
-WantedBy=multi-user.target
-EOF
-systemctl enable --now test-disable@1.service test-disable@2.service
-systemctl is-active test-disable@1.service
-systemctl is-active test-disable@2.service
-systemctl disable --now test-disable@.service
-for u in test-disable@{1,2}.service; do
-    (! systemctl is-active "$u")
-    (! systemctl is-enabled "$u")
-done
-rm /run/systemd/system/test-disable@.service
-
-# add-wants/add-requires
-(! systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service")
-systemctl add-wants "$UNIT_NAME" "systemd-journald.service"
-systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service"
-(! systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service")
-systemctl add-requires "$UNIT_NAME" "systemd-journald.service"
-systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service"
-
-# set-property
-systemctl set-property "$UNIT_NAME" IPAccounting=yes MemoryMax=1234567
-systemctl cat "$UNIT_NAME"
-# These properties should be saved to a persistent storage
-grep -r "IPAccounting=yes" "/etc/systemd/system.control/${UNIT_NAME}.d/"
-grep -r "MemoryMax=1234567" "/etc/systemd/system.control/${UNIT_NAME}.d"
-systemctl revert "$UNIT_NAME"
-(! grep -r "IPAccounting=" "/etc/systemd/system.control/${UNIT_NAME}.d/")
-(! grep -r "MemoryMax=" "/etc/systemd/system.control/${UNIT_NAME}.d/")
-# Same stuff, but with --runtime, which should use /run
-systemctl set-property --runtime "$UNIT_NAME" CPUAccounting=no CPUQuota=10%
-systemctl cat "$UNIT_NAME"
-grep -r "CPUAccounting=no" "/run/systemd/system.control/${UNIT_NAME}.d/"
-grep -r "CPUQuota=10%" "/run/systemd/system.control/${UNIT_NAME}.d/"
-systemctl revert "$UNIT_NAME"
-(! grep -r "CPUAccounting=" "/run/systemd/system.control/${UNIT_NAME}.d/")
-(! grep -r "CPUQuota=" "/run/systemd/system.control/${UNIT_NAME}.d/")
-
-# Failed-unit related tests
-(! systemd-run --wait --unit "failed.service" /bin/false)
-systemctl is-failed failed.service
-systemctl --state=failed | grep failed.service
-systemctl --failed | grep failed.service
-systemctl reset-failed "fail*.service"
-(! systemctl is-failed failed.service)
-
-# clean
-systemctl restart "$UNIT_NAME"
-systemctl stop "$UNIT_NAME"
-# Check if the directories from *Directory= directives exist
-# (except RuntimeDirectory= in /run, which is removed when the unit is stopped)
-for path in /var/lib /var/cache /var/log /etc; do
-    [[ -e "$path/$UNIT_NAME" ]]
-done
-# Run the cleanup
-for what in "" configuration state cache logs runtime all; do
-    systemctl clean ${what:+--what="$what"} "$UNIT_NAME"
-done
-# All respective directories should be removed
-for path in /run /var/lib /var/cache /var/log /etc; do
-    [[ ! -e "$path/$UNIT_NAME" ]]
-done
-
-# --timestamp
-for value in pretty us µs utc us+utc µs+utc; do
-    systemctl show -P KernelTimestamp --timestamp="$value"
-done
-
-# set-default/get-default
-test_get_set_default() {
-    target="$(systemctl get-default "$@")"
-    systemctl set-default "$@" emergency.target
-    [[ "$(systemctl get-default "$@")" == emergency.target ]]
-    systemctl set-default "$@" "$target"
-    [[ "$(systemctl get-default "$@")" == "$target" ]]
-}
-
-test_get_set_default
-test_get_set_default --root=/
-
-# show/status
-systemctl show --property ""
-# Pick a heavily sandboxed unit for the best effect on coverage
-systemctl show systemd-logind.service
-systemctl status
-# Ignore the exit code in this case, as it might try to load non-existing units
-systemctl status -a >/dev/null || :
-systemctl status -a --state active,running,plugged >/dev/null
-systemctl status "systemd-*.timer"
-systemctl status "systemd-journald*.socket"
-systemctl status "sys-devices-*-ttyS0.device"
-systemctl status -- -.mount
-systemctl status 1
-
-# --marked
-systemctl restart "$UNIT_NAME"
-systemctl set-property "$UNIT_NAME" Markers=needs-restart
-systemctl show -P Markers "$UNIT_NAME" | grep needs-restart
-systemctl reload-or-restart --marked
-(! systemctl show -P Markers "$UNIT_NAME" | grep needs-restart)
-
-# --dry-run with destructive verbs
-# kexec is skipped intentionally, as it requires a bit more involved setup
-VERBS=(
-    default
-    emergency
-    exit
-    halt
-    hibernate
-    hybrid-sleep
-    poweroff
-    reboot
-    rescue
-    suspend
-    suspend-then-hibernate
-)
-
-for verb in "${VERBS[@]}"; do
-    systemctl --dry-run "$verb"
-
-    if [[ "$verb" =~ (halt|poweroff|reboot) ]]; then
-        systemctl --dry-run --message "Hello world" "$verb"
-        systemctl --dry-run --no-wall "$verb"
-        systemctl --dry-run -f "$verb"
-        systemctl --dry-run -ff "$verb"
-    fi
-done
-
-# Aux verbs & assorted checks
-systemctl is-active "*-journald.service"
-systemctl cat "*udevd*"
-systemctl cat "$UNIT_NAME"
-systemctl help "$UNIT_NAME"
-systemctl service-watchdogs
-systemctl service-watchdogs "$(systemctl service-watchdogs)"
-
-# show/set-environment
-# Make sure PATH is set
-systemctl show-environment | grep -q '^PATH='
-# Let's add an entry and override a built-in one
-systemctl set-environment PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/testaddition FOO=BAR
-# Check that both are set
-systemctl show-environment | grep -q '^PATH=.*testaddition$'
-systemctl show-environment | grep -q '^FOO=BAR$'
-systemctl daemon-reload
-# Check again after the reload
-systemctl show-environment | grep -q '^PATH=.*testaddition$'
-systemctl show-environment | grep -q '^FOO=BAR$'
-# Check that JSON output is supported
-systemctl show-environment --output=json | grep -q '^{.*"FOO":"BAR".*}$'
-# Drop both
-systemctl unset-environment FOO PATH
-# Check that one is gone and the other reverted to the built-in
-systemctl show-environment | grep '^FOO=$' && exit 1
-systemctl show-environment | grep '^PATH=.*testaddition$' && exit 1
-systemctl show-environment | grep -q '^PATH='
-# Check import-environment
-export IMPORT_THIS=hello
-export IMPORT_THIS_TOO=world
-systemctl import-environment IMPORT_THIS IMPORT_THIS_TOO
-systemctl show-environment | grep "^IMPORT_THIS=$IMPORT_THIS"
-systemctl show-environment | grep "^IMPORT_THIS_TOO=$IMPORT_THIS_TOO"
-systemctl unset-environment IMPORT_THIS IMPORT_THIS_TOO
-(! systemctl show-environment | grep "^IMPORT_THIS=")
-(! systemctl show-environment | grep "^IMPORT_THIS_TOO=")
-
-# test for sysv-generator (issue #24990)
-if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then
-    # This is configurable via -Dsysvinit-path=, but we can't get the value
-    # at runtime, so let's just support the two most common paths for now.
-    [[ -d /etc/rc.d/init.d ]] && SYSVINIT_PATH="/etc/rc.d/init.d" || SYSVINIT_PATH="/etc/init.d"
-
-    # OpenSUSE leaves sysvinit-path enabled, which means systemd-sysv-generator is built
-    # but may not create the directory if there's no services that use it.
-    mkdir -p "$SYSVINIT_PATH"
-
-    # invalid dependency
-    cat >"${SYSVINIT_PATH:?}/issue-24990" <<\EOF
-#!/bin/bash
-
-### BEGIN INIT INFO
-# Provides:test1 test2
-# Required-Start:test1 $remote_fs $network
-# Required-Stop:test1 $remote_fs $network
-# Description:Test
-# Short-Description: Test
-### END INIT INFO
-
-case "$1" in
-    start)
-        echo "Starting issue-24990.service"
-        sleep 1000 &
-        ;;
-    stop)
-        echo "Stopping issue-24990.service"
-        sleep 10 &
-        ;;
-    *)
-        echo "Usage: service test {start|stop|restart|status}"
-        ;;
-esac
-EOF
-
-    chmod +x "$SYSVINIT_PATH/issue-24990"
-    systemctl daemon-reload
-    [[ -L /run/systemd/generator.late/test1.service ]]
-    [[ -L /run/systemd/generator.late/test2.service ]]
-    assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
-    assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
-    output=$(systemctl cat issue-24990)
-    assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
-    assert_in "Description=LSB: Test" "$output"
-    assert_in "After=test1.service" "$output"
-    assert_in "After=remote-fs.target" "$output"
-    assert_in "After=network-online.target" "$output"
-    assert_in "Wants=network-online.target" "$output"
-    assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
-    assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
-    systemctl status issue-24990 || :
-    systemctl show issue-24990
-    assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
-    assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
-
-    if ! systemctl is-active network-online.target; then
-        systemctl start network-online.target
-    fi
-
-    systemctl restart issue-24990
-    systemctl stop issue-24990
-
-    # valid dependency
-    cat >"$SYSVINIT_PATH/issue-24990" <<\EOF
-#!/bin/bash
-
-### BEGIN INIT INFO
-# Provides:test1 test2
-# Required-Start:$remote_fs
-# Required-Stop:$remote_fs
-# Description:Test
-# Short-Description: Test
-### END INIT INFO
-
-case "$1" in
-    start)
-        echo "Starting issue-24990.service"
-        sleep 1000 &
-        ;;
-    stop)
-        echo "Stopping issue-24990.service"
-        sleep 10 &
-        ;;
-    *)
-        echo "Usage: service test {start|stop|restart|status}"
-        ;;
-esac
-EOF
-
-    chmod +x "$SYSVINIT_PATH/issue-24990"
-    systemctl daemon-reload
-    [[ -L /run/systemd/generator.late/test1.service ]]
-    [[ -L /run/systemd/generator.late/test2.service ]]
-    assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
-    assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
-    output=$(systemctl cat issue-24990)
-    assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
-    assert_in "Description=LSB: Test" "$output"
-    assert_in "After=remote-fs.target" "$output"
-    assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
-    assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
-    systemctl status issue-24990 || :
-    systemctl show issue-24990
-    assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
-    assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
-
-    systemctl restart issue-24990
-    systemctl stop issue-24990
-fi
-
-# %J in WantedBy= causes ABRT (#26467)
-cat >/run/systemd/system/test-WantedBy.service <<EOF
-[Service]
-ExecStart=true
-
-[Install]
-WantedBy=user-%i@%J.service
-EOF
-systemctl daemon-reload
-systemctl enable --now test-WantedBy.service || :
-systemctl daemon-reload
-
-touch /testok
diff --git a/test/units/testsuite-29.service b/test/units/testsuite-29.service
deleted file mode 100644 (file)
index 035c6bf..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-29-PORTABLE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-29.sh b/test/units/testsuite-29.sh
deleted file mode 100755 (executable)
index 27c24a0..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-install_extension_images
-
-# Set longer timeout for slower machines, e.g. non-KVM vm.
-mkdir -p /run/systemd/system.conf.d
-cat >/run/systemd/system.conf.d/10-timeout.conf <<EOF
-[Manager]
-DefaultEnvironment=SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
-ManagerEnvironment=SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
-EOF
-
-systemctl daemon-reexec
-
-export SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
-
-udevadm control --log-level debug
-
-ARGS=()
-STATE_DIRECTORY=/var/lib/private/
-if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
-    # If we're running under sanitizers, we need to use a less restrictive
-    # profile, otherwise LSan syscall would get blocked by seccomp
-    ARGS+=(--profile=trusted)
-    # With the trusted profile DynamicUser is disabled, so the storage is not in private/
-    STATE_DIRECTORY=/var/lib/
-fi
-
-systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service'
-systemd-dissect --no-pager /usr/share/minimal_1.raw | grep -q '✓ portable service'
-systemd-dissect --no-pager /tmp/app0.raw | grep -q '✓ sysext for portable service'
-systemd-dissect --no-pager /tmp/app1.raw | grep -q '✓ sysext for portable service'
-systemd-dissect --no-pager /tmp/conf0.raw | grep -q '✓ confext for portable service'
-
-export SYSTEMD_LOG_LEVEL=debug
-mkdir -p /run/systemd/system/systemd-portabled.service.d/
-cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf
-[Service]
-Environment=SYSTEMD_LOG_LEVEL=debug
-EOF
-
-portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0
-
-portablectl is-attached minimal-app0
-portablectl inspect /usr/share/minimal_0.raw minimal-app0.service
-systemctl is-active minimal-app0.service
-systemctl is-active minimal-app0-foo.service
-systemctl is-active minimal-app0-bar.service && exit 1
-
-portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0
-
-portablectl is-attached minimal-app0
-portablectl inspect /usr/share/minimal_0.raw minimal-app0.service
-systemctl is-active minimal-app0.service
-systemctl is-active minimal-app0-bar.service
-systemctl is-active minimal-app0-foo.service && exit 1
-
-portablectl list | grep -q -F "minimal_1"
-busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
-
-portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0
-
-portablectl list | grep -q -F "No images."
-busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
-
-# Ensure we don't regress (again) when using --force
-
-portablectl "${ARGS[@]}" attach --force --now --runtime /usr/share/minimal_0.raw minimal-app0
-
-portablectl is-attached --force minimal-app0
-portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service
-systemctl is-active minimal-app0.service
-systemctl is-active minimal-app0-foo.service
-systemctl is-active minimal-app0-bar.service && exit 1
-
-portablectl "${ARGS[@]}" reattach --force --now --runtime /usr/share/minimal_1.raw minimal-app0
-
-portablectl is-attached --force minimal-app0
-portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service
-systemctl is-active minimal-app0.service
-systemctl is-active minimal-app0-bar.service
-systemctl is-active minimal-app0-foo.service && exit 1
-
-portablectl list | grep -q -F "minimal_1"
-busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
-
-portablectl detach --force --now --runtime /usr/share/minimal_1.raw minimal-app0
-
-portablectl list | grep -q -F "No images."
-busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
-
-# portablectl also works with directory paths rather than images
-
-unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw
-unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw
-
-portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0
-
-systemctl is-active minimal-app0.service
-systemctl is-active minimal-app0-foo.service
-systemctl is-active minimal-app0-bar.service && exit 1
-
-portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0
-
-systemctl is-active minimal-app0.service
-systemctl is-active minimal-app0-bar.service
-systemctl is-active minimal-app0-foo.service && exit 1
-
-portablectl list | grep -q -F "minimal_1"
-busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
-
-portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0
-
-portablectl list | grep -q -F "No images."
-busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
-
-portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
-
-systemctl is-active app0.service
-status="$(portablectl is-attached --extension app0 minimal_0)"
-[[ "${status}" == "running-runtime" ]]
-
-grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
-
-portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0
-
-systemctl is-active app0.service
-status="$(portablectl is-attached --extension app0 minimal_1)"
-[[ "${status}" == "running-runtime" ]]
-
-grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_1.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
-
-portablectl detach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0
-
-# Ensure versioned images are accepted without needing to use --force to override the extension-release
-# matching
-
-cp /tmp/app0.raw /tmp/app0_1.0.raw
-portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_0.raw app0
-
-systemctl is-active app0.service
-status="$(portablectl is-attached --extension app0_1 minimal_0)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl detach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_1.raw app0
-rm -f /tmp/app0_1.0.raw
-
-portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
-
-systemctl is-active app1.service
-status="$(portablectl is-attached --extension app1 minimal_0)"
-[[ "${status}" == "running-runtime" ]]
-
-# Ensure that adding or removing a version to the image doesn't break reattaching
-cp /tmp/app1.raw /tmp/app1_2.raw
-portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1_2.raw /usr/share/minimal_1.raw app1
-
-systemctl is-active app1.service
-status="$(portablectl is-attached --extension app1_2 minimal_1)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1
-
-systemctl is-active app1.service
-status="$(portablectl is-attached --extension app1 minimal_1)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl detach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1
-portablectl "${ARGS[@]}" attach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
-systemctl daemon-reload
-systemctl restart app1.service
-
-systemctl is-active app1.service
-status="$(portablectl is-attached --extension app1 minimal_0)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl detach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
-
-# Ensure vpick works, including reattaching to a new image
-mkdir -p /tmp/app1.v/
-cp /tmp/app1.raw /tmp/app1.v/app1_1.0.raw
-cp /tmp/app1_2.raw /tmp/app1.v/app1_2.0.raw
-portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
-
-systemctl is-active app1.service
-status="$(portablectl is-attached --extension app1_2.0.raw minimal_1)"
-[[ "${status}" == "running-runtime" ]]
-
-rm -f /tmp/app1.v/app1_2.0.raw
-portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
-
-systemctl is-active app1.service
-status="$(portablectl is-attached --extension app1_1.0.raw minimal_1)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl detach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_0.raw app1
-rm -f /tmp/app1.v/app1_1.0.raw
-
-# Ensure that the combination of read-only images, state directory and dynamic user works, and that
-# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while
-# after the service is attached before the file appears.
-grep -q -F bar "${STATE_DIRECTORY}/app0/foo"
-grep -q -F baz "${STATE_DIRECTORY}/app1/foo"
-
-# Ensure that we can override the check on extension-release.NAME
-cp /tmp/app0.raw /tmp/app10.raw
-portablectl "${ARGS[@]}" attach --force --now --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0
-
-systemctl is-active app0.service
-status="$(portablectl is-attached --extension /tmp/app10.raw /usr/share/minimal_0.raw)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/app10.raw"
-
-# Ensure that we can detach even when an image has been deleted already (stop the unit manually as
-# portablectl won't find it)
-rm -f /tmp/app10.raw
-systemctl stop app0.service
-portablectl detach --force --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0
-
-# portablectl also accepts confexts
-portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0
-
-systemctl is-active app0.service
-status="$(portablectl is-attached --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw)"
-[[ "${status}" == "running-runtime" ]]
-
-portablectl inspect --force --cat --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/conf0.raw"
-
-portablectl detach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0
-
-# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
-portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
-test -f /run/portables/app0.raw
-test -f /run/portables/minimal_0.raw
-test -f /run/systemd/system.attached/app0.service
-test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
-portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
-
-# Ensure that when two portables share the same base image, removing one doesn't remove the other too
-
-portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
-portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
-
-status="$(portablectl is-attached --extension app0 minimal_0)"
-[[ "${status}" == "attached-runtime" ]]
-status="$(portablectl is-attached --extension app1 minimal_0)"
-[[ "${status}" == "attached-runtime" ]]
-
-(! portablectl detach --runtime /usr/share/minimal_0.raw app)
-
-status="$(portablectl is-attached --extension app0 minimal_0)"
-[[ "${status}" == "attached-runtime" ]]
-status="$(portablectl is-attached --extension app1 minimal_0)"
-[[ "${status}" == "attached-runtime" ]]
-
-# Ensure 'portablectl list' shows the correct status for both images
-portablectl list
-portablectl list | grep -F "minimal_0" | grep -q -F "attached-runtime"
-portablectl list | grep -F "app0" | grep -q -F "attached-runtime"
-portablectl list | grep -F "app1" | grep -q -F "attached-runtime"
-
-portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app
-
-status="$(portablectl is-attached --extension app1 minimal_0)"
-[[ "${status}" == "attached-runtime" ]]
-
-portablectl detach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app
-
-# portablectl also works with directory paths rather than images
-
-mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
-mount /tmp/app0.raw /tmp/app0
-mount /tmp/app1.raw /tmp/app1
-mount /usr/share/minimal_0.raw /tmp/rootdir
-
-# Fix up os-release to drop the valid PORTABLE_SERVICES field (because we are
-# bypassing the sysext logic in portabled here it will otherwise not see the
-# extensions additional valid prefix)
-grep -v "^PORTABLE_PREFIXES=" /tmp/rootdir/etc/os-release >/tmp/os-release-fix/etc/os-release
-
-mount -t overlay overlay -o lowerdir=/tmp/os-release-fix:/tmp/app1:/tmp/rootdir /tmp/overlay
-
-grep . /tmp/overlay/usr/lib/extension-release.d/*
-grep . /tmp/overlay/etc/os-release
-
-portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/overlay app1
-
-systemctl is-active app1.service
-
-portablectl detach --now --runtime overlay app1
-
-umount /tmp/overlay
-
-portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
-
-systemctl is-active app0.service
-systemctl is-active app1.service
-
-portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release
-portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0
-portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2
-portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service
-portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service
-
-grep -q -F "LogExtraFields=PORTABLE=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app0.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app0.service.d/20-portable.conf
-
-grep -q -F "LogExtraFields=PORTABLE=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app1.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app1.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app1.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
-grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app1.service.d/20-portable.conf
-
-portablectl detach --clean --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
-
-# Ensure --clean remove state and other directories belonging to the portable image being detached
-test ! -d /var/lib/app0
-test ! -d /run/app0
-
-# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
-portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
-test -d /run/portables/app0
-test -d /run/portables/app1
-test -d /run/portables/rootdir
-test -f /run/systemd/system.attached/app0.service
-test -f /run/systemd/system.attached/app1.service
-test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
-test -L /run/systemd/system.attached/app1.service.d/10-profile.conf
-portablectl detach --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
-
-# Attempt to disable the app unit during detaching. Requires --copy=symlink to reproduce.
-# Provides coverage for https://github.com/systemd/systemd/issues/23481
-portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
-portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
-# attach and detach again to check if all drop-in configs are removed even if the main unit files are removed
-portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
-portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
-
-umount /tmp/rootdir
-umount /tmp/app0
-umount /tmp/app1
-
-# Lack of ID field in os-release should be rejected, but it caused a crash in the past instead
-mkdir -p /tmp/emptyroot/usr/lib
-mkdir -p /tmp/emptyext/usr/lib/extension-release.d
-touch /tmp/emptyroot/usr/lib/os-release
-touch /tmp/emptyext/usr/lib/extension-release.d/extension-release.emptyext
-
-# Remote peer disconnected -> portabled crashed
-res="$(! portablectl attach --extension /tmp/emptyext /tmp/emptyroot 2> >(grep "Remote peer disconnected"))"
-test -z "${res}"
-
-touch /testok
diff --git a/test/units/testsuite-30.service b/test/units/testsuite-30.service
deleted file mode 100644 (file)
index 253f7b5..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-30-ONCLOCKCHANGE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-30.sh b/test/units/testsuite-30.sh
deleted file mode 100755 (executable)
index 83698b8..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemd-analyze log-level debug
-
-systemctl disable --now systemd-timesyncd.service
-
-timedatectl set-timezone Europe/Berlin
-timedatectl set-time 1980-10-15
-
-systemd-run --on-timezone-change touch /tmp/timezone-changed
-systemd-run --on-clock-change touch /tmp/clock-changed
-
-test ! -f /tmp/timezone-changed
-test ! -f /tmp/clock-changed
-
-timedatectl set-timezone Europe/Kyiv
-
-while test ! -f /tmp/timezone-changed ; do sleep .5 ; done
-
-timedatectl set-time 2018-1-1
-
-while test ! -f /tmp/clock-changed ; do sleep .5 ; done
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-31.service b/test/units/testsuite-31.service
deleted file mode 100644 (file)
index f0e78a9..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-31-DEVICE-ENUMERATION
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-31.sh b/test/units/testsuite-31.sh
deleted file mode 100755 (executable)
index 5e99302..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if journalctl -b -t systemd --grep '(?<!loop.|diskseq-.)\.device: Changed plugged -> dead'; then
-    exit 1
-fi
-
-touch /testok
diff --git a/test/units/testsuite-32.service b/test/units/testsuite-32.service
deleted file mode 100644 (file)
index 50f5823..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-32-OOMPOLICY
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
-MemoryAccounting=yes
diff --git a/test/units/testsuite-32.sh b/test/units/testsuite-32.sh
deleted file mode 100755 (executable)
index 83b548a..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Let's run this test only if the "memory.oom.group" cgroupfs attribute
-# exists. This test is a bit too strict, since the "memory.events"/"oom_kill"
-# logic has been around since a longer time than "memory.oom.group", but it's
-# an easier thing to test for, and also: let's not get confused by older
-# kernels where the concept was still new.
-
-if test -f /sys/fs/cgroup/system.slice/testsuite-32.service/memory.oom.group; then
-    systemd-analyze log-level debug
-
-    # Run a service that is guaranteed to be the first candidate for OOM killing
-    systemd-run --unit=oomtest.service \
-                -p Type=exec -p OOMScoreAdjust=1000 -p OOMPolicy=stop -p MemoryAccounting=yes \
-                sleep infinity
-
-    # Trigger an OOM killer run
-    echo 1 >/proc/sys/kernel/sysrq
-    echo f >/proc/sysrq-trigger
-
-    while : ; do
-        STATE="$(systemctl show -P ActiveState oomtest.service)"
-        [ "$STATE" = "failed" ] && break
-        sleep .5
-    done
-
-    RESULT="$(systemctl show -P Result oomtest.service)"
-    test "$RESULT" = "oom-kill"
-
-    systemd-analyze log-level info
-fi
-
-touch /testok
diff --git a/test/units/testsuite-34.service b/test/units/testsuite-34.service
deleted file mode 100644 (file)
index 6917afe..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-34-DYNAMICUSERMIGRATE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-34.sh b/test/units/testsuite-34.sh
deleted file mode 100755 (executable)
index d15b675..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemd-analyze log-level debug
-
-test_directory() {
-    local directory="$1"
-    local path="$2"
-
-    # cleanup for previous invocation
-    for i in xxx xxx2 yyy zzz x:yz x:yz2; do
-        rm -rf "${path:?}/${i}" "${path:?}/private/${i}"
-    done
-
-    # Set everything up without DynamicUser=1
-
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz touch "${path}"/zzz/test
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}=zzz:xxx zzz:xxx2" -p TemporaryFileSystem="${path}" bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test"
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
-    (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing)
-
-    test -d "${path}"/zzz
-    test ! -L "${path}"/zzz
-    test ! -e "${path}"/private/zzz
-
-    test ! -e "${path}"/xxx
-    test ! -e "${path}"/private/xxx
-    test ! -e "${path}"/xxx2
-    test ! -e "${path}"/private/xxx2
-    test -L "${path}"/yyy
-    test ! -e "${path}"/private/yyy
-
-    test -f "${path}"/zzz/test
-    test ! -e "${path}"/zzz/test-missing
-
-    # Convert to DynamicUser=1
-
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}=zzz:xxx zzz:xxx2" \
-                -p TemporaryFileSystem="${path}" -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test"
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
-    (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing)
-
-    test -L "${path}"/zzz
-    test -d "${path}"/private/zzz
-
-    test ! -e "${path}"/xxx
-    test ! -e "${path}"/private/xxx
-    test ! -e "${path}"/xxx2
-    test ! -e "${path}"/private/xxx2
-    test -L "${path}"/yyy # previous symlink is not removed
-    test ! -e "${path}"/private/yyy
-
-    test -f "${path}"/zzz/test
-    test ! -e "${path}"/zzz/test-missing
-
-    # Convert back
-
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}" test -f "${path}"/xxx/test
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}=zzz:xxx zzz:xxx2" -p TemporaryFileSystem="${path}" bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test"
-    systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test
-    (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing)
-
-    test -d "${path}"/zzz
-    test ! -L "${path}"/zzz
-    test ! -e "${path}"/private/zzz
-
-    test ! -e "${path}"/xxx
-    test ! -e "${path}"/private/xxx
-    test ! -e "${path}"/xxx2
-    test ! -e "${path}"/private/xxx2
-    test -L "${path}"/yyy
-    test ! -e "${path}"/private/yyy
-
-    test -f "${path}"/zzz/test
-    test ! -e "${path}"/zzz/test-missing
-
-    # Exercise the unit parsing paths too
-    cat >/run/systemd/system/testservice-34.service <<EOF
-[Service]
-Type=oneshot
-TemporaryFileSystem=${path}
-RuntimeDirectoryPreserve=yes
-${directory}=zzz:x\:yz zzz:x\:yz2
-ExecStart=test -f ${path}/x:yz2/test
-ExecStart=test -f ${path}/x:yz/test
-ExecStart=test -f ${path}/zzz/test
-EOF
-    systemctl daemon-reload
-    systemctl start --wait testservice-34.service
-
-    test -d "${path}"/zzz
-    test ! -L "${path}"/zzz
-    test ! -e "${path}"/private/zzz
-
-    test ! -L "${path}"/x:yz
-    test ! -L "${path}"/x:yz2
-}
-
-test_check_writable() {
-    # cleanup for previous invocation
-    for i in aaa quux waldo xxx; do
-        rm -rf "/var/lib/$i" "/var/lib/private/$i"
-    done
-
-    cat >/run/systemd/system/testservice-34-check-writable.service <<\EOF
-[Unit]
-Description=Check writable directories when DynamicUser= with StateDirectory=
-
-[Service]
-# Relevant only for sanitizer runs
-EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
-
-Type=oneshot
-DynamicUser=yes
-StateDirectory=waldo quux/pief aaa/bbb aaa aaa/ccc xxx/yyy:aaa/111 xxx:aaa/222 xxx/zzz:aaa/333
-
-# Make sure that the state directories are really the only writable directory besides the obvious candidates
-ExecStart=bash -c ' \
-    set -eux; \
-    set -o pipefail; \
-    declare -a writable_dirs; \
-    readarray -t writable_dirs < <(find / \( -path /var/tmp -o -path /tmp -o -path /proc -o -path /dev/mqueue -o -path /dev/shm -o \
-                                              -path /sys/fs/bpf -o -path /dev/.lxc -o -path /sys/devices/system/cpu \) \
-                                   -prune -o -type d -writable -print 2>/dev/null | sort -u); \
-    [[ "$${#writable_dirs[@]}" == "8" ]]; \
-    [[ "$${writable_dirs[0]}" == "/var/lib/private/aaa" ]]; \
-    [[ "$${writable_dirs[1]}" == "/var/lib/private/aaa/bbb" ]]; \
-    [[ "$${writable_dirs[2]}" == "/var/lib/private/aaa/ccc" ]]; \
-    [[ "$${writable_dirs[3]}" == "/var/lib/private/quux/pief" ]]; \
-    [[ "$${writable_dirs[4]}" == "/var/lib/private/waldo" ]]; \
-    [[ "$${writable_dirs[5]}" == "/var/lib/private/xxx" ]]; \
-    [[ "$${writable_dirs[6]}" == "/var/lib/private/xxx/yyy" ]]; \
-    [[ "$${writable_dirs[7]}" == "/var/lib/private/xxx/zzz" ]]; \
-'
-EOF
-    systemctl daemon-reload
-    systemctl start testservice-34-check-writable.service
-}
-
-test_directory "StateDirectory" "/var/lib"
-test_directory "RuntimeDirectory" "/run"
-test_directory "CacheDirectory" "/var/cache"
-test_directory "LogsDirectory" "/var/log"
-
-test_check_writable
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-35.service b/test/units/testsuite-35.service
deleted file mode 100644 (file)
index 0599f61..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-35-LOGIN
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-35.sh b/test/units/testsuite-35.sh
deleted file mode 100755 (executable)
index 0f4b770..0000000
+++ /dev/null
@@ -1,738 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-cleanup_test_user() (
-    set +ex
-
-    pkill -u "$(id -u logind-test-user)"
-    sleep 1
-    pkill -KILL -u "$(id -u logind-test-user)"
-    userdel -r logind-test-user
-
-    return 0
-)
-
-setup_test_user() {
-    mkdir -p /var/spool/cron /var/spool/mail
-    useradd -m -s /bin/bash logind-test-user
-    trap cleanup_test_user EXIT
-}
-
-test_write_dropin() {
-    systemctl edit --runtime --stdin systemd-logind.service --drop-in=debug.conf <<EOF
-[Service]
-Environment=SYSTEMD_LOG_LEVEL=debug
-EOF
-
-    # We test "coldplug" (completely stop and start logind) here. So we need to preserve
-    # the fdstore, which might contain session leader pidfds. This is extremely rare use case
-    # and shall not be considered fully supported.
-    # See also: https://github.com/systemd/systemd/pull/30610#discussion_r1440507850
-    systemctl edit --runtime --stdin systemd-logind.service --drop-in=fdstore-preserve.conf <<EOF
-[Service]
-FileDescriptorStorePreserve=yes
-EOF
-
-    systemctl restart systemd-logind.service
-}
-
-testcase_properties() {
-    mkdir -p /run/systemd/logind.conf.d
-
-    cat >/run/systemd/logind.conf.d/kill-user-processes.conf <<EOF
-[Login]
-KillUserProcesses=no
-EOF
-
-    systemctl restart systemd-logind.service
-    assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager KillUserProcesses)" "b false"
-
-    cat >/run/systemd/logind.conf.d/kill-user-processes.conf <<EOF
-[Login]
-KillUserProcesses=yes
-EOF
-
-    systemctl restart systemd-logind.service
-    assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager KillUserProcesses)" "b true"
-
-    rm -rf /run/systemd/logind.conf.d
-}
-
-testcase_sleep_automated() {
-    assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager SleepOperation)" 'as 3 "suspend-then-hibernate" "suspend" "hibernate"'
-
-    mkdir -p /run/systemd/logind.conf.d
-
-    cat >/run/systemd/logind.conf.d/sleep-operations.conf <<EOF
-[Login]
-SleepOperation=suspend hybrid-sleep
-EOF
-
-    systemctl restart systemd-logind.service
-
-    assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager SleepOperation)" 'as 2 "hybrid-sleep" "suspend"'
-
-    busctl call org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager CanSleep
-
-    rm -rf /run/systemd/logind.conf.d
-}
-
-testcase_started() {
-    local pid
-
-    systemctl restart systemd-logind.service
-
-    # should start at boot, not with D-BUS activation
-    pid=$(systemctl show systemd-logind.service -p ExecMainPID --value)
-
-    # loginctl should succeed
-    loginctl
-
-    # logind should still be running
-    assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
-}
-
-wait_suspend() {
-    timeout "${1?}" bash -c "while [[ ! -e /run/suspend.flag ]]; do sleep 1; done"
-    rm /run/suspend.flag
-}
-
-teardown_suspend() (
-    set +eux
-
-    pkill evemu-device
-
-    rm -rf /run/systemd/system/systemd-suspend.service.d
-    systemctl daemon-reload
-
-    rm -f /run/udev/rules.d/70-logindtest-lid.rules
-    udevadm control --reload
-
-    return 0
-)
-
-testcase_suspend_on_lid() {
-    local pid input_name lid_dev
-
-    if systemd-detect-virt --quiet --container; then
-        echo "Skipping suspend test in container"
-        return
-    fi
-    if ! grep -s -q mem /sys/power/state; then
-        echo "suspend not supported on this testbed, skipping"
-        return
-    fi
-    if ! command -v evemu-device >/dev/null; then
-        echo "command evemu-device not found, skipping"
-        return
-    fi
-    if ! command -v evemu-event >/dev/null; then
-        echo "command evemu-event not found, skipping"
-        return
-    fi
-
-    trap teardown_suspend RETURN
-
-    # save pid
-    pid=$(systemctl show systemd-logind.service -p ExecMainPID --value)
-
-    # create fake suspend
-    mkdir -p /run/systemd/system/systemd-suspend.service.d
-    cat >/run/systemd/system/systemd-suspend.service.d/override.conf <<EOF
-[Service]
-ExecStart=
-ExecStart=touch /run/suspend.flag
-EOF
-    systemctl daemon-reload
-
-    # create fake lid switch
-    mkdir -p /run/udev/rules.d
-    cat >/run/udev/rules.d/70-logindtest-lid.rules <<EOF
-SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="Fake Lid Switch", TAG+="power-switch"
-EOF
-    udevadm control --reload
-
-    cat >/run/lidswitch.evemu <<EOF
-# EVEMU 1.2
-# Input device name: "Lid Switch"
-# Input device ID: bus 0x19 vendor 0000 product 0x05 version 0000
-# Supported events:
-#   Event type 0 (EV_SYN)
-#     Event code 0 (SYN_REPORT)
-#     Event code 5 (FF_STATUS_MAX)
-#   Event type 5 (EV_SW)
-#     Event code 0 (SW_LID)
-# Properties:
-N: Fake Lid Switch
-I: 0019 0000 0005 0000
-P: 00 00 00 00 00 00 00 00
-B: 00 21 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 01 00 00 00 00 00 00 00 00
-B: 02 00 00 00 00 00 00 00 00
-B: 03 00 00 00 00 00 00 00 00
-B: 04 00 00 00 00 00 00 00 00
-B: 05 01 00 00 00 00 00 00 00
-B: 11 00 00 00 00 00 00 00 00
-B: 12 00 00 00 00 00 00 00 00
-B: 15 00 00 00 00 00 00 00 00
-B: 15 00 00 00 00 00 00 00 00
-EOF
-
-    evemu-device /run/lidswitch.evemu &
-
-    timeout 20 bash -c 'until grep "^Fake Lid Switch" /sys/class/input/*/device/name; do sleep .5; done'
-    input_name=$(grep -l '^Fake Lid Switch' /sys/class/input/*/device/name || :)
-    if [[ -z "$input_name" ]]; then
-        echo "cannot find fake lid switch." >&2
-        exit 1
-    fi
-    input_name=${input_name%/device/name}
-    lid_dev=/dev/${input_name#/sys/class/}
-    udevadm info --wait-for-initialization=10s "$lid_dev"
-    udevadm settle
-
-    # close lid
-    evemu-event "$lid_dev" --sync --type 5 --code 0 --value 1
-    # need to wait for 30s suspend inhibition after boot
-    wait_suspend 31
-    # open lid again
-    evemu-event "$lid_dev" --sync --type 5 --code 0 --value 0
-
-    # waiting for 30s inhibition time between suspends
-    sleep 30
-
-    # now closing lid should cause instant suspend
-    evemu-event "$lid_dev" --sync --type 5 --code 0 --value 1
-    wait_suspend 2
-    evemu-event "$lid_dev" --sync --type 5 --code 0 --value 0
-
-    assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
-}
-
-testcase_shutdown() {
-    local pid
-
-    # save pid
-    pid=$(systemctl show systemd-logind.service -p ExecMainPID --value)
-
-    # scheduled shutdown with wall message
-    shutdown 2>&1
-    sleep 5
-    shutdown -c || :
-    # logind should still be running
-    assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
-
-    # scheduled shutdown without wall message
-    shutdown --no-wall 2>&1
-    sleep 5
-    shutdown -c --no-wall || true
-    assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid"
-}
-
-cleanup_session() (
-    set +ex
-
-    local uid s
-
-    uid=$(id -u logind-test-user)
-
-    loginctl disable-linger logind-test-user
-
-    systemctl stop getty@tty2.service
-
-    for s in $(loginctl --no-legend list-sessions | grep -v manager | awk '$3 == "logind-test-user" { print $1 }'); do
-        echo "INFO: stopping session $s"
-        loginctl terminate-session "$s"
-    done
-
-    loginctl terminate-user logind-test-user
-
-    if ! timeout 30 bash -c "while loginctl --no-legend | grep -q logind-test-user; do sleep 1; done"; then
-        echo "WARNING: session for logind-test-user still active, ignoring."
-    fi
-
-    pkill -u "$uid"
-    sleep 1
-    pkill -KILL -u "$uid"
-
-    if ! timeout 30 bash -c "while systemctl is-active --quiet user@${uid}.service; do sleep 1; done"; then
-        echo "WARNING: user@${uid}.service is still active, ignoring."
-    fi
-
-    if ! timeout 30 bash -c "while systemctl is-active --quiet user-runtime-dir@${uid}.service; do sleep 1; done"; then
-        echo "WARNING: user-runtime-dir@${uid}.service is still active, ignoring."
-    fi
-
-    if ! timeout 30 bash -c "while systemctl is-active --quiet user-${uid}.slice; do sleep 1; done"; then
-        echo "WARNING: user-${uid}.slice is still active, ignoring."
-    fi
-
-    rm -rf /run/systemd/system/getty@tty2.service.d
-    systemctl daemon-reload
-
-    return 0
-)
-
-teardown_session() (
-    set +ex
-
-    cleanup_session
-
-    rm -f /run/udev/rules.d/70-logindtest-scsi_debug-user.rules
-    udevadm control --reload
-    rmmod scsi_debug
-
-    return 0
-)
-
-check_session() (
-    set +ex
-
-    local seat session leader_pid
-
-    if [[ $(loginctl --no-legend | grep -v manager | grep -c "logind-test-user") != 1 ]]; then
-        echo "no session or multiple sessions for logind-test-user." >&2
-        return 1
-    fi
-
-    seat=$(loginctl --no-legend | grep -v manager | grep 'logind-test-user *seat' | awk '{ print $4 }')
-    if [[ -z "$seat" ]]; then
-        echo "no seat found for user logind-test-user" >&2
-        return 1
-    fi
-
-    session=$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
-    if [[ -z "$session" ]]; then
-        echo "no session found for user logind-test-user" >&2
-        return 1
-    fi
-
-    if ! loginctl session-status "$session" | grep -q "Unit: session-${session}\.scope"; then
-        echo "cannot find scope unit for session $session" >&2
-        return 1
-    fi
-
-    leader_pid=$(loginctl session-status "$session" | awk '$1 == "Leader:" { print $2 }')
-    if [[ -z "$leader_pid" ]]; then
-        echo "cannot found leader process for session $session" >&2
-        return 1
-    fi
-
-    # cgroup v1: "1:name=systemd:/user.slice/..."; unified hierarchy: "0::/user.slice"
-    if ! grep -q -E '(name=systemd|^0:):.*session.*scope' /proc/"$leader_pid"/cgroup; then
-        echo "FAIL: process $leader_pid is not in the session cgroup" >&2
-        cat /proc/self/cgroup
-        return 1
-    fi
-)
-
-create_session() {
-    # login with the test user to start a session
-    mkdir -p /run/systemd/system/getty@tty2.service.d
-    cat >/run/systemd/system/getty@tty2.service.d/override.conf <<EOF
-[Service]
-Type=simple
-ExecStart=
-ExecStart=-/sbin/agetty --autologin logind-test-user --noclear %I $TERM
-Restart=no
-EOF
-    systemctl daemon-reload
-
-    systemctl restart getty@tty2.service
-
-    # check session
-    for i in {1..30}; do
-        (( i > 1 )) && sleep 1
-        check_session && break
-    done
-    check_session
-    assert_eq "$(loginctl --no-legend | grep -v manager | awk '$3=="logind-test-user" { print $7 }')" "tty2"
-}
-
-testcase_sanity_check() {
-    # Exercise basic loginctl options
-
-    if [[ ! -c /dev/tty2 ]]; then
-        echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
-        return
-    fi
-
-    trap cleanup_session RETURN
-    create_session
-
-    # Run most of the loginctl commands from a user session to make
-    # the seat/session autodetection work-ish
-    systemd-run --user --pipe --wait -M "logind-test-user@.host" bash -eux <<\EOF
-    loginctl list-sessions
-    loginctl list-sessions -j
-    loginctl list-sessions --json=short
-    loginctl session-status
-    loginctl show-session
-    loginctl show-session -P DelayInhibited
-
-    # We're not in the same session scope, so in this case we need to specify
-    # the session ID explicitly
-    session=$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1; exit; }')
-    loginctl kill-session --signal=SIGCONT "$session"
-    # FIXME(?)
-    #loginctl kill-session --signal=SIGCONT --kill-whom=leader "$session"
-
-    loginctl list-users
-    loginctl user-status
-    loginctl show-user -a
-    loginctl show-user -P IdleAction
-    loginctl kill-user --signal=SIGCONT ""
-
-    loginctl list-seats
-    loginctl seat-status
-    loginctl show-seat
-    loginctl show-seat -P IdleActionUSec
-EOF
-
-    # Requires root privileges
-    loginctl lock-sessions
-    loginctl unlock-sessions
-    loginctl flush-devices
-}
-
-testcase_session() {
-    local dev
-
-    if systemd-detect-virt --quiet --container; then
-        echo "Skipping ACL tests in container"
-        return
-    fi
-
-    if [[ ! -c /dev/tty2 ]]; then
-        echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
-        return
-    fi
-
-    trap teardown_session RETURN
-
-    create_session
-
-    # scsi_debug should not be loaded yet
-    if [[ -d /sys/bus/pseudo/drivers/scsi_debug ]]; then
-        echo "scsi_debug module is already loaded." >&2
-        exit 1
-    fi
-
-    # we use scsi_debug to create new devices which we can put ACLs on
-    # tell udev about the tagging, so that logind can pick it up
-    mkdir -p /run/udev/rules.d
-    cat >/run/udev/rules.d/70-logindtest-scsi_debug-user.rules <<EOF
-SUBSYSTEM=="block", ATTRS{model}=="scsi_debug*", TAG+="uaccess"
-EOF
-    udevadm control --reload
-
-    # coldplug: logind started with existing device
-    systemctl stop systemd-logind.service
-    modprobe scsi_debug
-    timeout 30 bash -c 'until ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null; do sleep 1; done'
-    dev=/dev/$(ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null)
-    if [[ ! -b "$dev" ]]; then
-        echo "cannot find suitable scsi block device" >&2
-        exit 1
-    fi
-    udevadm settle
-    udevadm info "$dev"
-
-    # trigger logind and activate session
-    loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')"
-
-    # check ACL
-    sleep 1
-    assert_in "user:logind-test-user:rw-" "$(getfacl -p "$dev")"
-
-    # hotplug: new device appears while logind is running
-    rmmod scsi_debug
-    modprobe scsi_debug
-    timeout 30 bash -c 'until ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null; do sleep 1; done'
-    dev=/dev/$(ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null)
-    if [[ ! -b "$dev" ]]; then
-        echo "cannot find suitable scsi block device" >&2
-        exit 1
-    fi
-    udevadm settle
-
-    # check ACL
-    sleep 1
-    assert_in "user:logind-test-user:rw-" "$(getfacl -p "$dev")"
-}
-
-teardown_lock_idle_action() (
-    set +eux
-
-    rm -f /run/systemd/logind.conf.d/idle-action-lock.conf
-    systemctl restart systemd-logind.service
-
-    cleanup_session
-
-    return 0
-)
-
-testcase_lock_idle_action() {
-    local ts
-
-    if [[ ! -c /dev/tty2 ]]; then
-        echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
-        return
-    fi
-
-    if loginctl --no-legend | grep -v manager | grep -q logind-test-user; then
-        echo >&2 "Session of the 'logind-test-user' is already present."
-        exit 1
-    fi
-
-    trap teardown_lock_idle_action RETURN
-
-    create_session
-
-    ts="$(date '+%H:%M:%S')"
-
-    mkdir -p /run/systemd/logind.conf.d
-    cat >/run/systemd/logind.conf.d/idle-action-lock.conf <<EOF
-[Login]
-IdleAction=lock
-IdleActionSec=1s
-EOF
-    systemctl restart systemd-logind.service
-
-    # Wait for 35s, in that interval all sessions should have become idle
-    # and "Lock" signal should have been sent out. Then we wrote to tty to make
-    # session active again and next we slept for another 35s so sessions have
-    # become idle again. 'Lock' signal is sent out for each session, we have at
-    # least one session, so minimum of 2 "Lock" signals must have been sent.
-    timeout 35 bash -c "while [[ \"\$(journalctl -b -u systemd-logind.service --since=$ts | grep -c 'Sent message type=signal .* member=Lock')\" -lt 1 ]]; do sleep 1; done"
-
-    # We need to know that a new message was sent after waking up,
-    # so we must track how many happened before sleeping to check we have extra.
-    locks="$(journalctl -b -u systemd-logind.service --since="$ts" | grep -c 'Sent message type=signal .* member=Lock')"
-
-    # Wakeup
-    touch /dev/tty2
-
-    # Wait again
-    timeout 35 bash -c "while [[ \"\$(journalctl -b -u systemd-logind.service --since=$ts | grep -c 'Sent message type=signal .* member=Lock')\" -lt $((locks + 1)) ]]; do sleep 1; done"
-
-    if [[ "$(journalctl -b -u systemd-logind.service --since="$ts" | grep -c 'System idle. Will be locked now.')" -lt 2 ]]; then
-        echo >&2 "System haven't entered idle state at least 2 times."
-        exit 1
-    fi
-}
-
-testcase_session_properties() {
-    local s
-
-    if [[ ! -c /dev/tty2 ]]; then
-        echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
-        return
-    fi
-
-    trap cleanup_session RETURN
-    create_session
-
-    s=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
-    /usr/lib/systemd/tests/unit-tests/manual/test-session-properties "/org/freedesktop/login1/session/_3${s?}" /dev/tty2
-}
-
-testcase_list_users_sessions_seats() {
-    local session seat
-
-    if [[ ! -c /dev/tty2 ]]; then
-        echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
-        return
-    fi
-
-    trap cleanup_session RETURN
-    create_session
-
-    # Activate the session
-    loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')"
-
-    session=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')
-    : check that we got a valid session id
-    busctl get-property org.freedesktop.login1 "/org/freedesktop/login1/session/_3${session?}" org.freedesktop.login1.Session Id
-    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $2 }')" "$(id -ru logind-test-user)"
-    seat=$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $4 }')
-    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $6 }')" user
-    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $7 }')" tty2
-    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $8 }')" no
-    assert_eq "$(loginctl list-sessions --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $9 }')" '-'
-
-    loginctl list-seats --no-legend | grep -Fwq "${seat?}"
-
-    assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $1 }')" "$(id -ru logind-test-user)"
-    assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" no
-    assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $4 }')" active
-
-    loginctl enable-linger logind-test-user
-    assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" yes
-
-    for s in $(loginctl list-sessions --no-legend | grep tty | awk '$3 == "logind-test-user" { print $1 }'); do
-        loginctl terminate-session "$s"
-    done
-    if ! timeout 30 bash -c "while loginctl --no-legend | grep tty | grep -q logind-test-user; do sleep 1; done"; then
-        echo "WARNING: session for logind-test-user still active, ignoring."
-        return
-    fi
-
-    assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $4 }')" lingering
-}
-
-teardown_stop_idle_session() (
-    set +eux
-
-    rm -f /run/systemd/logind.conf.d/stop-idle-session.conf
-    systemctl restart systemd-logind.service
-
-    cleanup_session
-)
-
-testcase_stop_idle_session() {
-    local id ts
-
-    if [[ ! -c /dev/tty2 ]]; then
-        echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
-        return
-    fi
-
-    create_session
-    trap teardown_stop_idle_session RETURN
-
-    id="$(loginctl --no-legend | grep tty | awk '$3 == "logind-test-user" { print $1; }')"
-    ts="$(date '+%H:%M:%S')"
-
-    mkdir -p /run/systemd/logind.conf.d
-    cat >/run/systemd/logind.conf.d/stop-idle-session.conf <<EOF
-[Login]
-StopIdleSessionSec=2s
-EOF
-    systemctl restart systemd-logind.service
-    sleep 5
-
-    assert_eq "$(journalctl -b -u systemd-logind.service --since="$ts" --grep "Session \"$id\" of user \"logind-test-user\" is idle, stopping." | wc -l)" 1
-    assert_eq "$(loginctl --no-legend | grep -v manager | grep -c "logind-test-user")" 0
-}
-
-testcase_ambient_caps() {
-    local PAMSERVICE TRANSIENTUNIT SCRIPT
-
-    # Verify that pam_systemd works and assigns ambient caps as it should
-
-    if ! grep -q 'CapAmb:' /proc/self/status ; then
-        echo "ambient caps not available, skipping test." >&2
-        return
-    fi
-
-    typeset -i BND MASK
-
-    # Get PID 1's bounding set
-    BND="0x$(grep 'CapBnd:' /proc/1/status | cut -d: -f2 | tr -d '[:space:]')"
-
-    # CAP_CHOWN | CAP_KILL
-    MASK=$(((1 << 0) | (1 << 5)))
-
-    if [ $((BND & MASK)) -ne "$MASK" ] ; then
-        echo "CAP_CHOWN or CAP_KILL not available in bounding set, skipping test." >&2
-        return
-    fi
-
-    PAMSERVICE="pamserv$RANDOM"
-    TRANSIENTUNIT="capwakealarm$RANDOM.service"
-    SCRIPT="/tmp/capwakealarm$RANDOM.sh"
-
-    cat > /etc/pam.d/"$PAMSERVICE" <<EOF
-auth sufficient    pam_unix.so
-auth required      pam_deny.so
-account sufficient pam_unix.so
-account required   pam_permit.so
-session optional   pam_systemd.so default-capability-ambient-set=CAP_CHOWN,CAP_KILL debug
-session required   pam_unix.so
-EOF
-
-    cat > "$SCRIPT" <<'EOF'
-#!/bin/bash
-set -ex
-typeset -i AMB MASK
-AMB="0x$(grep 'CapAmb:' /proc/self/status | cut -d: -f2 | tr -d '[:space:]')"
-MASK=$(((1 << 0) | (1 << 5)))
-test "$AMB" -eq "$MASK"
-EOF
-
-    chmod +x "$SCRIPT"
-
-    systemd-run -u "$TRANSIENTUNIT" -p PAMName="$PAMSERVICE" -p Type=oneshot -p User=logind-test-user -p StandardError=tty "$SCRIPT"
-
-    rm -f "$SCRIPT" "$PAMSERVICE"
-}
-
-background_at_return() {
-    rm -f /etc/pam.d/"$PAMSERVICE"
-    unset PAMSERVICE
-}
-
-testcase_background() {
-
-    local uid TRANSIENTUNIT1 TRANSIENTUNIT2
-
-    uid=$(id -u logind-test-user)
-
-    systemctl stop user@"$uid".service
-
-    PAMSERVICE="pamserv$RANDOM"
-    TRANSIENTUNIT1="bg$RANDOM.service"
-    TRANSIENTUNIT2="bgg$RANDOM.service"
-
-    trap background_at_return RETURN
-
-    cat > /etc/pam.d/"$PAMSERVICE" <<EOF
-auth sufficient    pam_unix.so
-auth required      pam_deny.so
-account sufficient pam_unix.so
-account required   pam_permit.so
-session optional   pam_systemd.so debug
-session required   pam_unix.so
-EOF
-
-    systemd-run -u "$TRANSIENTUNIT1" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_CLASS=background-light" -p Type=exec -p User=logind-test-user sleep infinity
-
-    # This was a 'light' background service, hence the service manager should not be running
-    (! systemctl is-active user@"$uid".service )
-
-    systemctl stop "$TRANSIENTUNIT1"
-
-    systemd-run -u "$TRANSIENTUNIT2" -p PAMName="$PAMSERVICE" -p "Environment=XDG_SESSION_CLASS=background" -p Type=exec -p User=logind-test-user sleep infinity
-
-    # This was a regular background service, hence the service manager should be running
-    systemctl is-active user@"$uid".service
-
-    systemctl stop "$TRANSIENTUNIT2"
-
-    systemctl stop user@"$uid".service
-}
-
-setup_test_user
-test_write_dropin
-run_testcases
-
-touch /testok
diff --git a/test/units/testsuite-36.service b/test/units/testsuite-36.service
deleted file mode 100644 (file)
index 5746dc1..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-36-NUMAPOLICY
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-36.sh b/test/units/testsuite-36.sh
deleted file mode 100755 (executable)
index 02ca88f..0000000
+++ /dev/null
@@ -1,352 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck disable=SC2317
-at_exit() {
-    # shellcheck disable=SC2181
-    if [[ $? -ne 0 ]]; then
-        # We're exiting with a non-zero EC, let's dump test artifacts
-        # for easier debugging
-        [[ -v straceLog && -f "$straceLog" ]] && cat "$straceLog"
-        [[ -v journalLog && -f "$journalLog" ]] && cat "$journalLog"
-    fi
-}
-
-trap at_exit EXIT
-
-systemd-analyze log-level debug
-systemd-analyze log-target journal
-
-# Log files
-straceLog='strace.log'
-journalLog='journal.log'
-
-# Systemd config files
-testUnit='numa-test.service'
-testUnitFile="/run/systemd/system/$testUnit"
-testUnitNUMAConf="$testUnitFile.d/numa.conf"
-
-# Sleep constants (we should probably figure out something better but nothing comes to mind)
-sleepAfterStart=3
-
-# Journal cursor for easier navigation
-journalCursorFile="jounalCursorFile"
-
-startStrace() {
-    coproc strace -qq -p 1 -o "$straceLog" -e set_mempolicy -s 1024 ${1:+"$1"}
-    # Wait for strace to properly "initialize", i.e. until PID 1 has the TracerPid
-    # field set to the current strace's PID
-    until awk -v spid="$COPROC_PID" '/^TracerPid:/ {exit !($2 == spid);}' /proc/1/status; do sleep 0.1; done
-}
-
-stopStrace() {
-    [[ -v COPROC_PID ]] || return
-
-    local PID=$COPROC_PID
-    kill -s TERM "$PID"
-    # Make sure the strace process is indeed dead
-    while kill -0 "$PID" 2>/dev/null; do sleep 0.1; done
-}
-
-startJournalctl() {
-    : >"$journalCursorFile"
-    # Save journal's cursor for later navigation
-    journalctl --no-pager --cursor-file="$journalCursorFile" -n0 -ocat
-}
-
-stopJournalctl() {
-    local unit="${1:-init.scope}"
-    # Using journalctl --sync should be better than using SIGRTMIN+1, as
-    # the --sync wait until the synchronization is complete
-    echo "Force journald to write all queued messages"
-    journalctl --sync
-    journalctl -u "$unit" --cursor-file="$journalCursorFile" >"$journalLog"
-}
-
-checkNUMA() {
-    # NUMA enabled system should have at least NUMA node0
-    test -e /sys/devices/system/node/node0
-}
-
-writePID1NUMAPolicy() {
-    cat >"$confDir/numa.conf" <<EOF
-[Manager]
-NUMAPolicy=${1:?}
-NUMAMask=${2:-""}
-EOF
-}
-
-writeTestUnit() {
-    mkdir -p "$testUnitFile.d/"
-    printf "[Service]\nExecStart=sleep 3600\n" >"$testUnitFile"
-}
-
-writeTestUnitNUMAPolicy() {
-    cat >"$testUnitNUMAConf" <<EOF
-[Service]
-NUMAPolicy=${1:?}
-NUMAMask=${2:-""}
-EOF
-    systemctl daemon-reload
-}
-
-pid1ReloadWithStrace() {
-    startStrace
-    systemctl daemon-reload
-    sleep $sleepAfterStart
-    stopStrace
-}
-
-pid1ReloadWithJournal() {
-    startJournalctl
-    systemctl daemon-reload
-    stopJournalctl
-}
-
-pid1StartUnitWithStrace() {
-    startStrace '-f'
-    systemctl start "${1:?}"
-    sleep $sleepAfterStart
-    stopStrace
-}
-
-pid1StartUnitWithJournal() {
-    startJournalctl
-    systemctl start "${1:?}"
-    sleep $sleepAfterStart
-    stopJournalctl
-}
-
-pid1StopUnit() {
-    systemctl stop "${1:?}"
-}
-
-systemctlCheckNUMAProperties() {
-    local UNIT_NAME="${1:?}"
-    local NUMA_POLICY="${2:?}"
-    local NUMA_MASK="${3:-""}"
-    local LOGFILE
-
-    LOGFILE="$(mktemp)"
-
-    systemctl show -p NUMAPolicy "$UNIT_NAME" >"$LOGFILE"
-    grep "NUMAPolicy=$NUMA_POLICY" "$LOGFILE"
-
-    : >"$LOGFILE"
-
-    if [ -n "$NUMA_MASK" ]; then
-        systemctl show -p NUMAMask "$UNIT_NAME" >"$LOGFILE"
-        grep "NUMAMask=$NUMA_MASK" "$LOGFILE"
-    fi
-}
-
-writeTestUnit
-
-# Create systemd config drop-in directory
-confDir="/run/systemd/system.conf.d/"
-mkdir -p "$confDir"
-
-if ! checkNUMA; then
-    echo >&2 "NUMA is not supported on this machine, switching to a simple sanity check"
-
-    echo "PID1 NUMAPolicy=default && NUMAMask=0 check without NUMA support"
-    writePID1NUMAPolicy "default" "0"
-    startJournalctl
-    systemctl daemon-reload
-    stopJournalctl
-    grep "NUMA support not available, ignoring" "$journalLog"
-
-    echo "systemd-run NUMAPolicy=default && NUMAMask=0 check without NUMA support"
-    runUnit='numa-systemd-run-test.service'
-    startJournalctl
-    systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit "$runUnit" sleep 1000
-    sleep $sleepAfterStart
-    pid1StopUnit "$runUnit"
-    stopJournalctl "$runUnit"
-    grep "NUMA support not available, ignoring" "$journalLog"
-
-else
-    echo "PID1 NUMAPolicy support - Default policy w/o mask"
-    writePID1NUMAPolicy "default"
-    pid1ReloadWithStrace
-    # Kernel requires that nodemask argument is set to NULL when setting default policy
-    grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
-
-    echo "PID1 NUMAPolicy support - Default policy w/ mask"
-    writePID1NUMAPolicy "default" "0"
-    pid1ReloadWithStrace
-    grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
-
-    echo "PID1 NUMAPolicy support - Bind policy w/o mask"
-    writePID1NUMAPolicy "bind"
-    pid1ReloadWithJournal
-    grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog"
-
-    echo "PID1 NUMAPolicy support - Bind policy w/ mask"
-    writePID1NUMAPolicy "bind" "0"
-    pid1ReloadWithStrace
-    grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" "$straceLog"
-
-    echo "PID1 NUMAPolicy support - Interleave policy w/o mask"
-    writePID1NUMAPolicy "interleave"
-    pid1ReloadWithJournal
-    grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog"
-
-    echo "PID1 NUMAPolicy support - Interleave policy w/ mask"
-    writePID1NUMAPolicy "interleave" "0"
-    pid1ReloadWithStrace
-    grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" "$straceLog"
-
-    echo "PID1 NUMAPolicy support - Preferred policy w/o mask"
-    writePID1NUMAPolicy "preferred"
-    pid1ReloadWithJournal
-    # Preferred policy with empty node mask is actually allowed and should reset allocation policy to default
-    grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog" && { echo >&2 "unexpected pass"; exit 1; }
-
-    echo "PID1 NUMAPolicy support - Preferred policy w/ mask"
-    writePID1NUMAPolicy "preferred" "0"
-    pid1ReloadWithStrace
-    grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" "$straceLog"
-
-    echo "PID1 NUMAPolicy support - Local policy w/o mask"
-    writePID1NUMAPolicy "local"
-    pid1ReloadWithStrace
-    # Kernel requires that nodemask argument is set to NULL when setting default policy
-    # The unpatched versions of strace don't recognize the MPOL_LOCAL constant and
-    # return a numerical constant instead (with a comment):
-    #   set_mempolicy(0x4 /* MPOL_??? */, NULL, 0) = 0
-    # Let's cover this scenario as well
-    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
-
-    echo "PID1 NUMAPolicy support - Local policy w/ mask"
-    writePID1NUMAPolicy "local" "0"
-    pid1ReloadWithStrace
-    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
-
-    echo "Unit file NUMAPolicy support - Default policy w/o mask"
-    writeTestUnitNUMAPolicy "default"
-    pid1StartUnitWithStrace "$testUnit"
-    systemctlCheckNUMAProperties "$testUnit" "default"
-    pid1StopUnit "$testUnit"
-    grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
-
-    echo "Unit file NUMAPolicy support - Default policy w/ mask"
-    writeTestUnitNUMAPolicy "default" "0"
-    pid1StartUnitWithStrace "$testUnit"
-    systemctlCheckNUMAProperties "$testUnit" "default" "0"
-    pid1StopUnit $testUnit
-    # Mask must be ignored
-    grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog"
-
-    echo "Unit file NUMAPolicy support - Bind policy w/o mask"
-    writeTestUnitNUMAPolicy "bind"
-    pid1StartUnitWithJournal "$testUnit"
-    pid1StopUnit "$testUnit"
-    [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]]
-
-    echo "Unit file NUMAPolicy support - Bind policy w/ mask"
-    writeTestUnitNUMAPolicy "bind" "0"
-    pid1StartUnitWithStrace "$testUnit"
-    systemctlCheckNUMAProperties "$testUnit" "bind" "0"
-    pid1StopUnit "$testUnit"
-    grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" "$straceLog"
-
-    echo "Unit file NUMAPolicy support - Interleave policy w/o mask"
-    writeTestUnitNUMAPolicy "interleave"
-    pid1StartUnitWithStrace "$testUnit"
-    pid1StopUnit "$testUnit"
-    [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]]
-
-    echo "Unit file NUMAPolicy support - Interleave policy w/ mask"
-    writeTestUnitNUMAPolicy "interleave" "0"
-    pid1StartUnitWithStrace "$testUnit"
-    systemctlCheckNUMAProperties "$testUnit" "interleave" "0"
-    pid1StopUnit "$testUnit"
-    grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" "$straceLog"
-
-    echo "Unit file NUMAPolicy support - Preferred policy w/o mask"
-    writeTestUnitNUMAPolicy "preferred"
-    pid1StartUnitWithJournal "$testUnit"
-    systemctlCheckNUMAProperties "$testUnit" "preferred"
-    pid1StopUnit "$testUnit"
-    [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]] && { echo >&2 "unexpected pass"; exit 1; }
-
-    echo "Unit file NUMAPolicy support - Preferred policy w/ mask"
-    writeTestUnitNUMAPolicy "preferred" "0"
-    pid1StartUnitWithStrace "$testUnit"
-    systemctlCheckNUMAProperties "$testUnit" "preferred" "0"
-    pid1StopUnit "$testUnit"
-    grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" "$straceLog"
-
-    echo "Unit file NUMAPolicy support - Local policy w/o mask"
-    writeTestUnitNUMAPolicy "local"
-    pid1StartUnitWithStrace "$testUnit"
-    systemctlCheckNUMAProperties "$testUnit" "local"
-    pid1StopUnit "$testUnit"
-    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
-
-    echo "Unit file NUMAPolicy support - Local policy w/ mask"
-    writeTestUnitNUMAPolicy "local" "0"
-    pid1StartUnitWithStrace "$testUnit"
-    systemctlCheckNUMAProperties "$testUnit" "local" "0"
-    pid1StopUnit "$testUnit"
-    # Mask must be ignored
-    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog"
-
-    echo "Unit file CPUAffinity=NUMA support"
-    writeTestUnitNUMAPolicy "bind" "0"
-    echo "CPUAffinity=numa" >>"$testUnitNUMAConf"
-    systemctl daemon-reload
-    systemctl start "$testUnit"
-    systemctlCheckNUMAProperties "$testUnit" "bind" "0"
-    cpulist="$(cat /sys/devices/system/node/node0/cpulist)"
-    affinity_systemd="$(systemctl show --value -p CPUAffinity "$testUnit")"
-    [ "$cpulist" = "$affinity_systemd" ]
-    pid1StopUnit "$testUnit"
-
-    echo "systemd-run NUMAPolicy support"
-    runUnit='numa-systemd-run-test.service'
-
-    systemd-run -p NUMAPolicy=default --unit "$runUnit" sleep 1000
-    systemctlCheckNUMAProperties "$runUnit" "default"
-    pid1StopUnit "$runUnit"
-
-    systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit "$runUnit" sleep 1000
-    systemctlCheckNUMAProperties "$runUnit" "default" ""
-    pid1StopUnit "$runUnit"
-
-    systemd-run -p NUMAPolicy=bind -p NUMAMask=0 --unit "$runUnit" sleep 1000
-    systemctlCheckNUMAProperties "$runUnit" "bind" "0"
-    pid1StopUnit "$runUnit"
-
-    systemd-run -p NUMAPolicy=interleave -p NUMAMask=0 --unit "$runUnit" sleep 1000
-    systemctlCheckNUMAProperties "$runUnit" "interleave" "0"
-    pid1StopUnit "$runUnit"
-
-    systemd-run -p NUMAPolicy=preferred -p NUMAMask=0 --unit "$runUnit" sleep 1000
-    systemctlCheckNUMAProperties "$runUnit" "preferred" "0"
-    pid1StopUnit "$runUnit"
-
-    systemd-run -p NUMAPolicy=local --unit "$runUnit" sleep 1000
-    systemctlCheckNUMAProperties "$runUnit" "local"
-    pid1StopUnit "$runUnit"
-
-    systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit "$runUnit" sleep 1000
-    systemctlCheckNUMAProperties "$runUnit" "local" ""
-    pid1StopUnit "$runUnit"
-
-    systemd-run -p NUMAPolicy=local -p NUMAMask=0 -p CPUAffinity=numa --unit "$runUnit" sleep 1000
-    systemctlCheckNUMAProperties "$runUnit" "local" ""
-    systemctl cat "$runUnit" | grep -q 'CPUAffinity=numa'
-    pid1StopUnit "$runUnit"
-fi
-
-# Cleanup
-rm -rf "$confDir"
-systemctl daemon-reload
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-38-sleep.service b/test/units/testsuite-38-sleep.service
deleted file mode 100644 (file)
index 1bb9ddf..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-ExecStart=sleep 3600
diff --git a/test/units/testsuite-38.service b/test/units/testsuite-38.service
deleted file mode 100644 (file)
index ac77836..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-38-FREEZER
-
-[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-38.sh b/test/units/testsuite-38.sh
deleted file mode 100755 (executable)
index 1cb8915..0000000
+++ /dev/null
@@ -1,363 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2317
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-systemd-analyze log-level debug
-
-unit=testsuite-38-sleep.service
-
-start_test_service() {
-    systemctl daemon-reload
-    systemctl start "${unit}"
-}
-
-dbus_freeze() {
-    local name object_path suffix
-
-    suffix="${1##*.}"
-    name="${1%".$suffix"}"
-    object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
-
-    busctl call \
-           org.freedesktop.systemd1 \
-           "${object_path}" \
-           org.freedesktop.systemd1.Unit \
-           Freeze
-}
-
-dbus_thaw() {
-    local name object_path suffix
-
-    suffix="${1##*.}"
-    name="${1%".$suffix"}"
-    object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
-
-    busctl call \
-           org.freedesktop.systemd1 \
-           "${object_path}" \
-           org.freedesktop.systemd1.Unit \
-           Thaw
-}
-
-dbus_freeze_unit() {
-    busctl call \
-           org.freedesktop.systemd1 \
-           /org/freedesktop/systemd1 \
-           org.freedesktop.systemd1.Manager \
-           FreezeUnit \
-           s \
-           "$1"
-}
-
-dbus_thaw_unit() {
-    busctl call \
-           org.freedesktop.systemd1 \
-           /org/freedesktop/systemd1 \
-           org.freedesktop.systemd1.Manager \
-           ThawUnit \
-           s \
-           "$1"
-}
-
-dbus_can_freeze() {
-    local name object_path suffix
-
-    suffix="${1##*.}"
-    name="${1%".$suffix"}"
-    object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
-
-    busctl get-property \
-           org.freedesktop.systemd1 \
-           "${object_path}" \
-           org.freedesktop.systemd1.Unit \
-           CanFreeze
-}
-
-check_freezer_state() {
-    local name object_path suffix
-
-    suffix="${1##*.}"
-    name="${1%".$suffix"}"
-    object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}"
-
-    for _ in {0..10}; do
-        state=$(busctl get-property \
-                       org.freedesktop.systemd1 \
-                       "${object_path}" \
-                       org.freedesktop.systemd1.Unit \
-                       FreezerState | cut -d " " -f2 | tr -d '"')
-
-        # Ignore the intermediate freezing & thawing states in case we check
-        # the unit state too quickly
-        [[ "$state" =~ ^(freezing|thawing) ]] || break
-        sleep .5
-    done
-
-    [ "$state" = "$2" ] || {
-        echo "error: unexpected freezer state, expected: $2, actual: $state" >&2
-        exit 1
-    }
-}
-
-check_cgroup_state() {
-    # foo.unit -> /system.slice/foo.unit/
-    # foo.slice/ -> /foo.slice/./
-    # foo.slice/foo.unit -> /foo.slice/foo.unit/
-    local slice unit
-    unit="${1##*/}"
-    slice="${1%"$unit"}"
-    slice="${slice%/}"
-    grep -q "frozen $2" /sys/fs/cgroup/"${slice:-system.slice}"/"${unit:-.}"/cgroup.events
-}
-
-testcase_dbus_api() {
-    echo "Test that DBus API works:"
-    echo -n "  - Freeze(): "
-    dbus_freeze "${unit}"
-    check_freezer_state "${unit}" "frozen"
-    check_cgroup_state "$unit" 1
-    echo "[ OK ]"
-
-    echo -n "  - Thaw(): "
-    dbus_thaw "${unit}"
-    check_freezer_state "${unit}" "running"
-    check_cgroup_state "$unit" 0
-    echo "[ OK ]"
-
-    echo -n "  - FreezeUnit(): "
-    dbus_freeze_unit "${unit}"
-    check_freezer_state "${unit}" "frozen"
-    check_cgroup_state "$unit" 1
-    echo "[ OK ]"
-
-    echo -n "  - ThawUnit(): "
-    dbus_thaw_unit "${unit}"
-    check_freezer_state "${unit}" "running"
-    check_cgroup_state "$unit" 0
-    echo "[ OK ]"
-
-    echo -n "  - CanFreeze(): "
-    output=$(dbus_can_freeze "${unit}")
-    [ "$output" = "b true" ]
-    echo "[ OK ]"
-
-    echo
-}
-
-testcase_systemctl() {
-    echo "Test that systemctl freeze/thaw verbs:"
-
-    systemctl start "$unit"
-
-    echo -n "  - freeze: "
-    systemctl freeze "$unit"
-    check_freezer_state "${unit}" "frozen"
-    check_cgroup_state "$unit" 1
-    # Freezing already frozen unit should be NOP and return quickly
-    timeout 3s systemctl freeze "$unit"
-    echo "[ OK ]"
-
-    echo -n "  - thaw: "
-    systemctl thaw "$unit"
-    check_freezer_state "${unit}" "running"
-    check_cgroup_state "$unit" 0
-    # Likewise thawing already running unit shouldn't block
-    timeout 3s systemctl thaw "$unit"
-    echo "[ OK ]"
-
-    systemctl stop "$unit"
-
-    echo
-}
-
-testcase_systemctl_show() {
-    echo "Test systemctl show integration:"
-
-    systemctl start "$unit"
-
-    echo -n "  - FreezerState property: "
-    state=$(systemctl show -p FreezerState --value "$unit")
-    [ "$state" = "running" ]
-    systemctl freeze "$unit"
-    state=$(systemctl show -p FreezerState --value "$unit")
-    [ "$state" = "frozen" ]
-    systemctl thaw "$unit"
-    echo "[ OK ]"
-
-    echo -n "  - CanFreeze property: "
-    state=$(systemctl show -p CanFreeze --value "$unit")
-    [ "$state" = "yes" ]
-    echo "[ OK ]"
-
-    systemctl stop "$unit"
-    echo
-}
-
-testcase_recursive() {
-    local slice="bar.slice"
-    local unit="baz.service"
-
-    systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
-
-    echo "Test recursive freezing:"
-
-    echo -n "  - freeze/thaw parent: "
-    systemctl freeze "$slice"
-    check_freezer_state "$slice" "frozen"
-    check_freezer_state "$unit" "frozen-by-parent"
-    check_cgroup_state "$slice/" 1
-    check_cgroup_state "$slice/$unit" 1
-    systemctl thaw "$slice"
-    check_freezer_state "$slice" "running"
-    check_freezer_state "$unit" "running"
-    check_cgroup_state "$slice/" 0
-    check_cgroup_state "$slice/$unit" 0
-    echo "[ OK ]"
-
-    echo -n "  - child freeze/thaw during frozen parent: "
-    systemctl freeze "$slice"
-    check_freezer_state "$slice" "frozen"
-    check_freezer_state "$unit" "frozen-by-parent"
-    check_cgroup_state "$slice/" 1
-    check_cgroup_state "$slice/$unit" 1
-    systemctl freeze "$unit"
-    check_freezer_state "$slice" "frozen"
-    check_freezer_state "$unit" "frozen"
-    check_cgroup_state "$slice/" 1
-    check_cgroup_state "$slice/$unit" 1
-    systemctl thaw "$unit"
-    check_freezer_state "$slice" "frozen"
-    check_freezer_state "$unit" "frozen-by-parent"
-    check_cgroup_state "$slice/" 1
-    check_cgroup_state "$slice/$unit" 1
-    systemctl thaw "$slice"
-    check_freezer_state "$slice" "running"
-    check_freezer_state "$unit" "running"
-    check_cgroup_state "$slice/" 0
-    check_cgroup_state "$slice/$unit" 0
-    echo "[ OK ]"
-
-    echo -n "  - pre-frozen child not thawed by parent: "
-    systemctl freeze "$unit"
-    check_freezer_state "$slice" "running"
-    check_freezer_state "$unit" "frozen"
-    check_cgroup_state "$slice/" 0
-    check_cgroup_state "$slice/$unit" 1
-    systemctl freeze "$slice"
-    check_freezer_state "$slice" "frozen"
-    check_freezer_state "$unit" "frozen"
-    check_cgroup_state "$slice/" 1
-    check_cgroup_state "$slice/$unit" 1
-    systemctl thaw "$slice"
-    check_freezer_state "$slice" "running"
-    check_freezer_state "$unit" "frozen"
-    check_cgroup_state "$slice/" 0
-    check_cgroup_state "$slice/$unit" 1
-    echo "[ OK ]"
-
-    echo -n "  - pre-frozen child demoted and thawed by parent: "
-    systemctl freeze "$slice"
-    check_freezer_state "$slice" "frozen"
-    check_freezer_state "$unit" "frozen"
-    check_cgroup_state "$slice/" 1
-    check_cgroup_state "$slice/$unit" 1
-    systemctl thaw "$unit"
-    check_freezer_state "$slice" "frozen"
-    check_freezer_state "$unit" "frozen-by-parent"
-    check_cgroup_state "$slice/" 1
-    check_cgroup_state "$slice/$unit" 1
-    systemctl thaw "$slice"
-    check_freezer_state "$slice" "running"
-    check_freezer_state "$unit" "running"
-    check_cgroup_state "$slice/" 0
-    check_cgroup_state "$slice/$unit" 0
-    echo "[ OK ]"
-
-    echo -n "  - child promoted and not thawed by parent: "
-    systemctl freeze "$slice"
-    check_freezer_state "$slice" "frozen"
-    check_freezer_state "$unit" "frozen-by-parent"
-    check_cgroup_state "$slice/" 1
-    check_cgroup_state "$slice/$unit" 1
-    systemctl freeze "$unit"
-    check_freezer_state "$slice" "frozen"
-    check_freezer_state "$unit" "frozen"
-    check_cgroup_state "$slice/" 1
-    check_cgroup_state "$slice/$unit" 1
-    systemctl thaw "$slice"
-    check_freezer_state "$slice" "running"
-    check_freezer_state "$unit" "frozen"
-    check_cgroup_state "$slice/" 0
-    check_cgroup_state "$slice/$unit" 1
-    echo "[ OK ]"
-
-    echo -n "  - can't stop a frozen unit: "
-    (! systemctl -q stop "$unit" )
-    echo "[ OK ]"
-    systemctl thaw "$unit"
-
-    systemctl stop "$unit"
-    systemctl stop "$slice"
-
-    echo
-}
-
-testcase_preserve_state() {
-    local slice="bar.slice"
-    local unit="baz.service"
-
-    systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
-
-    echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):"
-
-    echo -n "  - freeze from outside: "
-    echo 1 >/sys/fs/cgroup/"$slice"/cgroup.freeze
-    # Give kernel some time to freeze the slice
-    sleep 1
-
-    # Our state should not be affected
-    check_freezer_state "$slice" "running"
-    check_freezer_state "$unit" "running"
-
-    # However actual kernel state should be frozen
-    check_cgroup_state "$slice/" 1
-    check_cgroup_state "$slice/$unit" 1
-    echo "[ OK ]"
-
-    echo -n "  - thaw from outside: "
-    echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze
-    sleep 1
-
-    check_freezer_state "$unit" "running"
-    check_freezer_state "$slice" "running"
-    check_cgroup_state "$slice/" 0
-    check_cgroup_state "$slice/$unit" 0
-    echo "[ OK ]"
-
-    echo -n "  - thaw from outside while inner service is frozen: "
-    systemctl freeze "$unit"
-    check_freezer_state "$unit" "frozen"
-    echo 1 >/sys/fs/cgroup/"$slice"/cgroup.freeze
-    echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze
-    check_freezer_state "$slice" "running"
-    check_freezer_state "$unit" "frozen"
-    echo "[ OK ]"
-
-    systemctl thaw "$unit"
-    systemctl stop "$unit"
-    systemctl stop "$slice"
-
-    echo
-}
-
-if [[ -e /sys/fs/cgroup/system.slice/cgroup.freeze ]]; then
-    start_test_service
-    run_testcases
-fi
-
-touch /testok
diff --git a/test/units/testsuite-43.service b/test/units/testsuite-43.service
deleted file mode 100644 (file)
index e36afea..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-43-PRIVATEUSER-UNPRIV
-After=systemd-logind.service user@4711.service
-Wants=user@4711.service
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-43.sh b/test/units/testsuite-43.sh
deleted file mode 100755 (executable)
index 165af47..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-install_extension_images
-
-if [[ "$(sysctl -ne kernel.apparmor_restrict_unprivileged_userns)" -eq 1 ]]; then
-    echo "Cannot create unprivileged user namespaces" >/skipped
-    exit 77
-fi
-
-systemd-analyze log-level debug
-
-runas testuser systemd-run --wait --user --unit=test-private-users \
-    -p PrivateUsers=yes -P echo hello
-
-runas testuser systemctl --user log-level debug
-
-runas testuser systemd-run --wait --user --unit=test-private-tmp-innerfile \
-    -p PrivateTmp=yes \
-    -P touch /tmp/innerfile.txt
-# File should not exist outside the job's tmp directory.
-test ! -e /tmp/innerfile.txt
-
-touch /tmp/outerfile.txt
-# File should not appear in unit's private tmp.
-runas testuser systemd-run --wait --user --unit=test-private-tmp-outerfile \
-    -p PrivateTmp=yes \
-    -P test ! -e /tmp/outerfile.txt
-
-# Confirm that creating a file in home works
-runas testuser systemd-run --wait --user --unit=test-unprotected-home \
-    -P touch /home/testuser/works.txt
-test -e /home/testuser/works.txt
-
-# Confirm that creating a file in home is blocked under read-only
-(! runas testuser systemd-run --wait --user --unit=test-protect-home-read-only \
-    -p ProtectHome=read-only \
-    -P bash -c '
-        test -e /home/testuser/works.txt || exit 10
-        touch /home/testuser/blocked.txt && exit 11
-    ')
-test ! -e /home/testuser/blocked.txt
-
-# Check that tmpfs hides the whole directory
-runas testuser systemd-run --wait --user --unit=test-protect-home-tmpfs \
-    -p ProtectHome=tmpfs \
-    -P test ! -e /home/testuser
-
-# Confirm that home, /root, and /run/user are inaccessible under "yes"
-# shellcheck disable=SC2016
-runas testuser systemd-run --wait --user --unit=test-protect-home-yes \
-    -p ProtectHome=yes \
-    -P bash -c '
-        test "$(stat -c %a /home)" = "0"
-        test "$(stat -c %a /root)" = "0"
-        test "$(stat -c %a /run/user)" = "0"
-    '
-
-# Confirm we cannot change groups because we only have one mapping in the user
-# namespace (no CAP_SETGID in the parent namespace to write the additional
-# mapping of the user supplied group and thus cannot change groups to an
-# unmapped group ID)
-(! runas testuser systemd-run --wait --user --unit=test-group-fail \
-    -p PrivateUsers=yes -p Group=daemon \
-    -P true)
-
-# Check that with a new user namespace we can bind mount
-# files and use a different root directory
-runas testuser systemd-run --wait --user --unit=test-bind-mount \
-    -p BindPaths=/dev/null:/etc/os-release \
-    test ! -s /etc/os-release
-
-runas testuser systemd-run --wait --user --unit=test-read-write \
-    -p ReadOnlyPaths=/ \
-    -p ReadWritePaths="/var /run /tmp" \
-    -p NoExecPaths=/ -p ExecPaths=/usr \
-    test ! -w /etc/os-release
-
-runas testuser systemd-run --wait --user --unit=test-caps \
-    -p PrivateUsers=yes -p AmbientCapabilities=CAP_SYS_ADMIN \
-    -p CapabilityBoundingSet=CAP_SYS_ADMIN \
-    test -s /etc/os-release
-
-runas testuser systemd-run --wait --user --unit=test-devices \
-    -p PrivateDevices=yes -p PrivateIPC=yes \
-    sh -c "ls -1 /dev/ | wc -l | grep -q -F 18"
-
-# Same check as test/test-execute/exec-privatenetwork-yes.service
-runas testuser systemd-run --wait --user --unit=test-network \
-    -p PrivateNetwork=yes \
-    /bin/sh -x -c '! ip link | grep -E "^[0-9]+: " | grep -Ev ": (lo|(erspan|gre|gretap|ip_vti|ip6_vti|ip6gre|ip6tnl|sit|tunl)0@.*):"'
-
-(! runas testuser systemd-run --wait --user --unit=test-hostname \
-    -p ProtectHostname=yes \
-    hostnamectl hostname foo)
-
-(! runas testuser systemd-run --wait --user --unit=test-clock \
-    -p ProtectClock=yes \
-    timedatectl set-time "2012-10-30 18:17:16")
-
-(! runas testuser systemd-run --wait --user --unit=test-kernel-tunable \
-    -p ProtectKernelTunables=yes \
-    sh -c "echo 0 >/proc/sys/user/max_user_namespaces")
-
-(! runas testuser systemd-run --wait --user --unit=test-kernel-mod \
-    -p ProtectKernelModules=yes \
-    sh -c "modprobe -r overlay && modprobe overlay")
-
-if sysctl kernel.dmesg_restrict=0; then
-    (! runas testuser systemd-run --wait --user --unit=test-kernel-log \
-        -p ProtectKernelLogs=yes -p LogNamespace=yes \
-        dmesg)
-fi
-
-unsquashfs -no-xattrs -d /tmp/img /usr/share/minimal_0.raw
-runas testuser systemd-run --wait --user --unit=test-root-dir \
-    -p RootDirectory=/tmp/img \
-    grep MARKER=1 /etc/os-release
-
-mkdir /tmp/img_bind
-mount --bind /tmp/img /tmp/img_bind
-runas testuser systemd-run --wait --user --unit=test-root-dir-bind \
-    -p RootDirectory=/tmp/img_bind -p MountFlags=private \
-    grep MARKER=1 /etc/os-release
-umount /tmp/img_bind
-
-# Unprivileged overlayfs was added to Linux 5.11, so try to detect it first
-mkdir -p /tmp/a /tmp/b /tmp/c
-if unshare --mount --user --map-root-user mount -t overlay overlay /tmp/c -o lowerdir=/tmp/a:/tmp/b; then
-    unsquashfs -no-xattrs -d /tmp/app2 /tmp/app1.raw
-    runas testuser systemd-run --wait --user --unit=test-extension-dir \
-        -p ExtensionDirectories=/tmp/app2 \
-        -p TemporaryFileSystem=/run -p RootDirectory=/tmp/img \
-        -p MountAPIVFS=yes \
-        grep PORTABLE_PREFIXES=app1 /usr/lib/extension-release.d/extension-release.app2
-fi
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-44.service b/test/units/testsuite-44.service
deleted file mode 100644 (file)
index 4dffdea..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TESTSUITE-44-LOG-NAMESPACE
-Before=getty-pre.target
-Wants=getty-pre.target
-Wants=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
-After=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-44.sh b/test/units/testsuite-44.sh
deleted file mode 100755 (executable)
index 0819a4b..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-
-systemd-analyze log-level debug
-
-journalctl --list-namespaces -o json | jq .
-
-systemd-run --wait -p LogNamespace=foobar echo "hello world"
-systemd-run --wait -p LogNamespace=foobaz echo "hello world"
-
-journalctl --namespace=foobar --sync
-journalctl --namespace=foobaz --sync
-ls -l /var/log/journal/
-journalctl --list-namespaces
-
-journalctl -o cat --namespace=foobar >/tmp/hello-world
-journalctl -o cat >/tmp/no-hello-world
-
-journalctl --list-namespaces | grep foobar
-journalctl --list-namespaces | grep foobaz
-journalctl --list-namespaces -o json | jq .
-[[ "$(journalctl --root=/tmp --list-namespaces --quiet)" == "" ]]
-
-grep "^hello world$" /tmp/hello-world
-(! grep "^hello world$" /tmp/no-hello-world)
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-45.service b/test/units/testsuite-45.service
deleted file mode 100644 (file)
index b16ce99..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-45-TIMEDATE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-45.sh b/test/units/testsuite-45.sh
deleted file mode 100755 (executable)
index dff3ed0..0000000
+++ /dev/null
@@ -1,408 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-testcase_timedatectl() {
-    timedatectl --no-pager --help
-    timedatectl --version
-
-    timedatectl
-    timedatectl --no-ask-password
-    timedatectl status --machine=testuser@.host
-    timedatectl status
-    timedatectl show
-    timedatectl show --all
-    timedatectl show -p NTP
-    timedatectl show -p NTP --value
-    timedatectl list-timezones
-
-    if ! systemd-detect-virt -qc; then
-        systemctl enable --runtime --now systemd-timesyncd
-        timedatectl timesync-status
-        timedatectl show-timesync
-    fi
-}
-
-restore_timezone() {
-    if [[ -f /tmp/timezone.bak ]]; then
-        mv /tmp/timezone.bak /etc/timezone
-    else
-        rm -f /etc/timezone
-    fi
-}
-
-testcase_timezone() {
-    local ORIG_TZ=
-
-    # Debian/Ubuntu specific file
-    if [[ -f /etc/timezone ]]; then
-        mv /etc/timezone /tmp/timezone.bak
-    fi
-
-    trap restore_timezone RETURN
-
-    if [[ -L /etc/localtime ]]; then
-        ORIG_TZ=$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')
-        echo "original tz: $ORIG_TZ"
-    fi
-
-    echo 'timedatectl works'
-    assert_in "Local time:" "$(timedatectl --no-pager)"
-
-    echo 'change timezone'
-    assert_eq "$(timedatectl --no-pager set-timezone Europe/Kyiv 2>&1)" ""
-    assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "Europe/Kyiv"
-    if [[ -f /etc/timezone ]]; then
-        assert_eq "$(cat /etc/timezone)" "Europe/Kyiv"
-    fi
-    assert_in "Time zone: Europe/Kyiv \(EES*T, \+0[0-9]00\)" "$(timedatectl)"
-
-    if [[ -n "$ORIG_TZ" ]]; then
-        echo 'reset timezone to original'
-        assert_eq "$(timedatectl set-timezone "$ORIG_TZ" 2>&1)" ""
-        assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "$ORIG_TZ"
-        if [[ -f /etc/timezone ]]; then
-            assert_eq "$(cat /etc/timezone)" "$ORIG_TZ"
-        fi
-    fi
-}
-
-restore_adjtime() {
-    if [[ -e /etc/adjtime.bak ]]; then
-        mv /etc/adjtime.bak /etc/adjtime
-    else
-        rm /etc/adjtime
-    fi
-}
-
-check_adjtime_not_exist() {
-    if [[ -e /etc/adjtime ]]; then
-        echo "/etc/adjtime unexpectedly exists." >&2
-        exit 1
-    fi
-}
-
-testcase_adjtime() {
-    # test setting UTC vs. LOCAL in /etc/adjtime
-    if [[ -e /etc/adjtime ]]; then
-        mv /etc/adjtime /etc/adjtime.bak
-    fi
-
-    trap restore_adjtime RETURN
-
-    echo 'no adjtime file'
-    rm -f /etc/adjtime
-    timedatectl set-local-rtc 0
-    check_adjtime_not_exist
-    timedatectl set-local-rtc 1
-    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-    timedatectl set-local-rtc 0
-    check_adjtime_not_exist
-
-    echo 'UTC set in adjtime file'
-    printf '0.0 0 0\n0\nUTC\n' >/etc/adjtime
-    timedatectl set-local-rtc 0
-    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-UTC"
-    timedatectl set-local-rtc 1
-    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-
-    echo 'non-zero values in adjtime file'
-    printf '0.1 123 0\n0\nLOCAL\n' >/etc/adjtime
-    timedatectl set-local-rtc 0
-    assert_eq "$(cat /etc/adjtime)" "0.1 123 0
-0
-UTC"
-    timedatectl set-local-rtc 1
-    assert_eq "$(cat /etc/adjtime)" "0.1 123 0
-0
-LOCAL"
-
-    echo 'fourth line adjtime file'
-    printf '0.0 0 0\n0\nLOCAL\nsomethingelse\n' >/etc/adjtime
-    timedatectl set-local-rtc 0
-    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-UTC
-somethingelse"
-    timedatectl set-local-rtc 1
-    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL
-somethingelse"
-
-    echo 'no final newline in adjtime file'
-    printf '0.0 0 0\n0\nUTC' >/etc/adjtime
-    timedatectl set-local-rtc 0
-    check_adjtime_not_exist
-    printf '0.0 0 0\n0\nUTC' >/etc/adjtime
-    timedatectl set-local-rtc 1
-    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-
-    echo 'only one line in adjtime file'
-    printf '0.0 0 0\n' >/etc/adjtime
-    timedatectl set-local-rtc 0
-    check_adjtime_not_exist
-    printf '0.0 0 0\n' >/etc/adjtime
-    timedatectl set-local-rtc 1
-    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-
-    echo 'only one line in adjtime file, no final newline'
-    printf '0.0 0 0' >/etc/adjtime
-    timedatectl set-local-rtc 0
-    check_adjtime_not_exist
-    printf '0.0 0 0' >/etc/adjtime
-    timedatectl set-local-rtc 1
-    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-
-    echo 'only two lines in adjtime file'
-    printf '0.0 0 0\n0\n' >/etc/adjtime
-    timedatectl set-local-rtc 0
-    check_adjtime_not_exist
-    printf '0.0 0 0\n0\n' >/etc/adjtime
-    timedatectl set-local-rtc 1
-    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-
-    echo 'only two lines in adjtime file, no final newline'
-    printf '0.0 0 0\n0' >/etc/adjtime
-    timedatectl set-local-rtc 0
-    check_adjtime_not_exist
-    printf '0.0 0 0\n0' >/etc/adjtime
-    timedatectl set-local-rtc 1
-    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-
-    echo 'unknown value in 3rd line of adjtime file'
-    printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime
-    timedatectl set-local-rtc 0
-    check_adjtime_not_exist
-    printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime
-    timedatectl set-local-rtc 1
-    assert_eq "$(cat /etc/adjtime)" "0.0 0 0
-0
-LOCAL"
-}
-
-assert_ntp() {
-    local value="${1:?}"
-
-    for _ in {0..9}; do
-        [[ "$(busctl get-property org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 NTP)" == "b $value" ]] && return 0
-        sleep .5
-    done
-
-    return 1
-}
-
-assert_timedated_signal() {
-    local timestamp="${1:?}"
-    local value="${2:?}"
-    local args=(-q -n 1 --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service")
-
-    journalctl --sync
-
-    for _ in {0..9}; do
-        if journalctl "${args[@]}" --grep .; then
-            [[ "$(journalctl "${args[@]}" -o cat | jq -r '.payload.data[1].NTP.data')" == "$value" ]];
-            return 0
-        fi
-
-        sleep .5
-    done
-
-    return 1
-}
-
-assert_timesyncd_state() {
-    local state="${1:?}"
-
-    for _ in {0..9}; do
-        [[ "$(systemctl show systemd-timesyncd.service -P ActiveState)" == "$state" ]] && return 0
-        sleep .5
-    done
-
-    return 1
-}
-
-testcase_ntp() {
-    # timesyncd has ConditionVirtualization=!container by default; drop/mock that for testing
-    if systemd-detect-virt --container --quiet; then
-        systemctl disable --quiet --now systemd-timesyncd
-        mkdir -p /run/systemd/system/systemd-timesyncd.service.d
-        cat >/run/systemd/system/systemd-timesyncd.service.d/container.conf <<EOF
-[Unit]
-ConditionVirtualization=
-
-[Service]
-Type=simple
-AmbientCapabilities=
-ExecStart=
-ExecStart=sleep infinity
-EOF
-        systemctl daemon-reload
-    fi
-
-    systemd-run --unit busctl-monitor.service --service-type=notify \
-        busctl monitor --json=short --match="type=signal,sender=org.freedesktop.timedate1,member=PropertiesChanged,path=/org/freedesktop/timedate1"
-
-    : 'Disable NTP'
-    ts="$(date +"%F %T.%6N")"
-    timedatectl set-ntp false
-    assert_timedated_signal "$ts" "false"
-    assert_timesyncd_state "inactive"
-    assert_ntp "false"
-    assert_rc 3 systemctl is-active --quiet systemd-timesyncd
-
-    : 'Enable NTP'
-    ts="$(date +"%F %T.%6N")"
-    timedatectl set-ntp true
-    assert_timedated_signal "$ts" "true"
-    assert_ntp "true"
-    assert_timesyncd_state "active"
-    assert_rc 0 systemctl is-active --quiet systemd-timesyncd
-
-    : 'Re-disable NTP'
-    ts="$(date +"%F %T.%6N")"
-    timedatectl set-ntp false
-    assert_timedated_signal "$ts" "false"
-    assert_ntp "false"
-    assert_rc 3 systemctl is-active --quiet systemd-timesyncd
-
-    systemctl stop busctl-monitor.service
-    rm -rf /run/systemd/system/systemd-timesyncd.service.d/
-    systemctl daemon-reload
-}
-
-assert_timesyncd_signal() {
-    local timestamp="${1:?}"
-    local property="${2:?}"
-    local value="${3:?}"
-    local args=(-q --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service")
-
-    journalctl --sync
-
-    for _ in {0..9}; do
-        if journalctl "${args[@]}" --grep .; then
-            [[ "$(journalctl "${args[@]}" -o cat | jq -r ".payload.data[1].$property.data | join(\" \")")" == "$value" ]];
-            return 0
-        fi
-
-        sleep .5
-    done
-
-    return 1
-}
-
-assert_networkd_ntp() {
-    local interface="${1:?}"
-    local value="${2:?}"
-    # Go through the array of NTP servers and for each entry do:
-    #   - if the entry is an IPv4 address, join the Address array into a dot separated string
-    #   - if the entry is a server address, select it unchanged
-    # These steps produce an array of strings, that is then joined into a space-separated string
-    # Note: this doesn't support IPv6 addresses, since converting them to a string is a bit more
-    # involved than a simple join(), but let's leave that to another time
-    local expr='[.NTP[] | (select(.Family == 2).Address | join(".")), select(has("Server")).Server] | join(" ")'
-
-    [[ "$(networkctl status "$interface" --json=short | jq -r "$expr")" == "$value" ]]
-}
-
-testcase_timesyncd() {
-    if systemd-detect-virt -cq; then
-        echo "This test case requires a VM, skipping..."
-        return 0
-    fi
-
-    if ! command -v networkctl >/dev/null; then
-        echo "This test requires systemd-networkd, skipping..."
-        return 0
-    fi
-
-    # Create a dummy interface managed by networkd, so we can configure link NTP servers
-    mkdir -p /run/systemd/network/
-    cat >/etc/systemd/network/10-ntp99.netdev <<EOF
-[NetDev]
-Name=ntp99
-Kind=dummy
-EOF
-    cat >/etc/systemd/network/10-ntp99.network <<EOF
-[Match]
-Name=ntp99
-
-[Network]
-Address=10.0.0.1/24
-EOF
-
-    systemctl unmask systemd-timesyncd systemd-networkd
-    systemctl restart systemd-timesyncd
-    systemctl restart systemd-networkd
-    networkctl status ntp99
-
-    systemd-run --unit busctl-monitor.service --service-type=notify \
-        busctl monitor --json=short --match="type=signal,sender=org.freedesktop.timesync1,member=PropertiesChanged,path=/org/freedesktop/timesync1"
-
-    # LinkNTPServers
-    #
-    # Single IP
-    ts="$(date +"%F %T.%6N")"
-    timedatectl ntp-servers ntp99 10.0.0.1
-    assert_networkd_ntp ntp99 10.0.0.1
-    assert_timesyncd_signal "$ts" LinkNTPServers 10.0.0.1
-    # Setting NTP servers to the same value shouldn't emit a PropertiesChanged signal
-    ts="$(date +"%F %T.%6N")"
-    timedatectl ntp-servers ntp99 10.0.0.1
-    assert_networkd_ntp ntp99 10.0.0.1
-    (! assert_timesyncd_signal "$ts" LinkNTPServers 10.0.0.1)
-    # Multiple IPs
-    ts="$(date +"%F %T.%6N")"
-    timedatectl ntp-servers ntp99 10.0.0.1 192.168.0.99
-    assert_networkd_ntp ntp99 "10.0.0.1 192.168.0.99"
-    assert_timesyncd_signal "$ts" LinkNTPServers "10.0.0.1 192.168.0.99"
-    # Multiple IPs + servers
-    ts="$(date +"%F %T.%6N")"
-    timedatectl ntp-servers ntp99 10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13
-    assert_networkd_ntp ntp99 "10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13"
-    assert_timesyncd_signal "$ts" LinkNTPServers "10.0.0.1 192.168.0.99 foo.localhost foo 10.11.12.13"
-
-    # RuntimeNTPServers
-    #
-    # There's no user-facing API that allows changing this property (afaik), so let's
-    # call SetRuntimeNTPServers() directly to test things out. The inner workings should
-    # be exactly the same as in the previous case, so do just one test to make sure
-    # things work
-    ts="$(date +"%F %T.%6N")"
-    busctl call org.freedesktop.timesync1 /org/freedesktop/timesync1 org.freedesktop.timesync1.Manager \
-        SetRuntimeNTPServers as 4 "10.0.0.1" foo "192.168.99.1" bar
-    servers="$(busctl get-property org.freedesktop.timesync1 /org/freedesktop/timesync1 org.freedesktop.timesync1.Manager RuntimeNTPServers)"
-    [[ "$servers" == 'as 4 "10.0.0.1" "foo" "192.168.99.1" "bar"' ]]
-    assert_timesyncd_signal "$ts" RuntimeNTPServers "10.0.0.1 foo 192.168.99.1 bar"
-
-    # Cleanup
-    systemctl stop systemd-networkd systemd-timesyncd
-    rm -f /run/systemd/network/ntp99.*
-}
-
-run_testcases
-
-touch /testok
diff --git a/test/units/testsuite-46.service b/test/units/testsuite-46.service
deleted file mode 100644 (file)
index 5efb9cc..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-46-HOMED
-Wants=getty-pre.target
-Before=getty-pre.target
-Requires=systemd-homed.service systemd-userdbd.socket
-After=systemd-homed.service systemd-userdbd.socket
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
-NotifyAccess=all
diff --git a/test/units/testsuite-46.sh b/test/units/testsuite-46.sh
deleted file mode 100755 (executable)
index 5dc00f4..0000000
+++ /dev/null
@@ -1,612 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Check if homectl is installed, and if it isn't bail out early instead of failing
-if ! test -x /usr/bin/homectl ; then
-        echo "no homed" >/skipped
-        exit 77
-fi
-
-inspect() {
-    # As updating disk-size-related attributes can take some time on some
-    # filesystems, let's drop these fields before comparing the outputs to
-    # avoid unexpected fails. To see the full outputs of both homectl &
-    # userdbctl (for debugging purposes) drop the fields just before the
-    # comparison.
-    local USERNAME="${1:?}"
-    homectl inspect "$USERNAME" | tee /tmp/a
-    userdbctl user "$USERNAME" | tee /tmp/b
-
-    # diff uses the grep BREs for pattern matching
-    diff -I '^\s*Disk \(Size\|Free\|Floor\|Ceiling\|Usage\):' /tmp/{a,b}
-    rm /tmp/{a,b}
-
-    homectl inspect --json=pretty "$USERNAME"
-}
-
-wait_for_state() {
-    for i in {1..10}; do
-        (( i > 1 )) && sleep 0.5
-        homectl inspect "$1" | grep -qF "State: $2" && break
-    done
-}
-
-FSTYPE="$(stat --file-system --format "%T" /)"
-
-systemd-analyze log-level debug
-systemctl service-log-level systemd-homed debug
-
-# Create a tmpfs to use as backing store for the home dir. That way we can enforce a size limit nicely.
-mkdir -p /home
-mount -t tmpfs tmpfs /home -o size=290M
-
-# we enable --luks-discard= since we run our tests in a tight VM, hence don't
-# needlessly pressure for storage. We also set the cheapest KDF, since we don't
-# want to waste CI CPU cycles on it. We also effectively disable rate-limiting on
-# the user by allowing 1000 logins per second
-NEWPASSWORD=xEhErW0ndafV4s homectl create test-user \
-           --disk-size=min \
-           --luks-discard=yes \
-           --image-path=/home/test-user.home \
-           --luks-pbkdf-type=pbkdf2 \
-           --luks-pbkdf-time-cost=1ms \
-           --rate-limit-interval=1s \
-           --rate-limit-burst=1000
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl authenticate test-user
-
-PASSWORD=xEhErW0ndafV4s homectl activate test-user
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inline test"
-inspect test-user
-
-homectl deactivate test-user
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s NEWPASSWORD=yPN4N0fYNKUkOq homectl passwd test-user
-inspect test-user
-
-PASSWORD=yPN4N0fYNKUkOq homectl activate test-user
-inspect test-user
-
-SYSTEMD_LOG_LEVEL=debug PASSWORD=yPN4N0fYNKUkOq NEWPASSWORD=xEhErW0ndafV4s homectl passwd test-user
-inspect test-user
-
-homectl deactivate test-user
-inspect test-user
-
-homectl update test-user --real-name "Offline test" --offline
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl activate test-user
-inspect test-user
-
-# Ensure that the offline changes were propagated in
-grep "Offline test" /home/test-user/.identity
-
-homectl deactivate test-user
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inactive test"
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl activate test-user
-inspect test-user
-
-homectl deactivate test-user
-inspect test-user
-
-# Do some keyring tests, but only on real kernels, since keyring access inside of containers will fail
-# (See: https://github.com/systemd/systemd/issues/17606)
-if ! systemd-detect-virt -cq ; then
-        PASSWORD=xEhErW0ndafV4s homectl activate test-user
-        inspect test-user
-
-        # Key should now be in the keyring
-        homectl update test-user --real-name "Keyring Test"
-        inspect test-user
-
-        # These commands shouldn't use the keyring
-        (! timeout 5s homectl authenticate test-user )
-        (! NEWPASSWORD="foobar" timeout 5s homectl passwd test-user )
-
-        homectl lock test-user
-        inspect test-user
-
-        # Key should be gone from keyring
-        (! timeout 5s homectl update test-user --real-name "Keyring Test 2" )
-
-        PASSWORD=xEhErW0ndafV4s homectl unlock test-user
-        inspect test-user
-
-        # Key should have been re-instantiated into the keyring
-        homectl update test-user --real-name "Keyring Test 3"
-        inspect test-user
-
-        homectl deactivate test-user
-        inspect test-user
-fi
-
-# Do some resize tests, but only if we run on real kernels and are on btrfs, as quota inside of containers
-# will fail and minimizing while active only works on btrfs.
-if ! systemd-detect-virt -cq && [[ "$FSTYPE" == "btrfs" ]]; then
-    # grow while inactive
-    PASSWORD=xEhErW0ndafV4s homectl resize test-user 300M
-    inspect test-user
-
-    # minimize while inactive
-    PASSWORD=xEhErW0ndafV4s homectl resize test-user min
-    inspect test-user
-
-    PASSWORD=xEhErW0ndafV4s homectl activate test-user
-    inspect test-user
-
-    # grow while active
-    PASSWORD=xEhErW0ndafV4s homectl resize test-user max
-    inspect test-user
-
-    # minimize while active
-    PASSWORD=xEhErW0ndafV4s homectl resize test-user 0
-    inspect test-user
-
-    # grow while active
-    PASSWORD=xEhErW0ndafV4s homectl resize test-user 300M
-    inspect test-user
-
-    # shrink to original size while active
-    PASSWORD=xEhErW0ndafV4s homectl resize test-user 256M
-    inspect test-user
-
-    # minimize again
-    PASSWORD=xEhErW0ndafV4s homectl resize test-user min
-    inspect test-user
-
-    # Increase space, so that we can reasonably rebalance free space between to home dirs
-    mount /home -o remount,size=800M
-
-    # create second user
-    NEWPASSWORD=uuXoo8ei homectl create test-user2 \
-           --disk-size=min \
-           --luks-discard=yes \
-           --image-path=/home/test-user2.home \
-           --luks-pbkdf-type=pbkdf2 \
-           --luks-pbkdf-time-cost=1ms \
-           --rate-limit-interval=1s \
-           --rate-limit-burst=1000
-    inspect test-user2
-
-    # activate second user
-    PASSWORD=uuXoo8ei homectl activate test-user2
-    inspect test-user2
-
-    # set second user's rebalance weight to 100
-    PASSWORD=uuXoo8ei homectl update test-user2 --rebalance-weight=100
-    inspect test-user2
-
-    # set first user's rebalance weight to quarter of that of the second
-    PASSWORD=xEhErW0ndafV4s homectl update test-user --rebalance-weight=25
-    inspect test-user
-
-    # synchronously rebalance
-    homectl rebalance
-    inspect test-user
-    inspect test-user2
-
-    wait_for_state test-user2 active
-    homectl deactivate test-user2
-    wait_for_state test-user2 inactive
-    homectl remove test-user2
-fi
-
-PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
-(! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
-PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz
-PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
-PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
-PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
-(! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
-
-# Regression tests
-wait_for_state test-user inactive
-/usr/lib/systemd/tests/unit-tests/manual/test-homed-regression-31896 test-user
-
-wait_for_state test-user inactive
-homectl remove test-user
-
-# blob directory tests
-# See docs/USER_RECORD_BLOB_DIRS.md
-checkblob() {
-        test -f "/var/cache/systemd/home/blob-user/$1"
-        stat -c "%u %#a" "/var/cache/systemd/home/blob-user/$1" | grep "^0 0644"
-        test -f "/home/blob-user/.identity-blob/$1"
-        stat -c "%u %#a" "/home/blob-user/.identity-blob/$1" | grep "^12345 0644"
-
-        diff "/var/cache/systemd/home/blob-user/$1" "$2"
-        diff "/var/cache/systemd/home/blob-user/$1" "/home/blob-user/.identity-blob/$1"
-}
-
-mkdir /tmp/blob1 /tmp/blob2
-echo data1 blob1 >/tmp/blob1/test1
-echo data1 blob2 >/tmp/blob2/test1
-echo data2 blob1 >/tmp/blob1/test2
-echo data2 blob2 >/tmp/blob2/test2
-echo invalid filename >/tmp/blob1/файл
-echo data3 >/tmp/external-test3
-echo avatardata >/tmp/external-avatar
-ln -s /tmp/external-avatar /tmp/external-avatar-lnk
-dd if=/dev/urandom of=/tmp/external-barely-fits bs=1M count=64
-dd if=/dev/urandom of=/tmp/external-toobig bs=1M count=65
-
-# create w/ prepopulated blob dir
-NEWPASSWORD=EMJuc3zQaMibJo homectl create blob-user \
-           --disk-size=min --luks-discard=yes \
-           --luks-pbkdf-type=pbkdf2 --luks-pbkdf-time-cost=1ms \
-           --rate-limit-interval=1s --rate-limit-burst=1000 \
-           --uid=12345 \
-           --blob=/tmp/blob1
-inspect blob-user
-PASSWORD=EMJuc3zQaMibJo homectl activate blob-user
-inspect blob-user
-
-test -d /var/cache/systemd/home/blob-user
-stat -c "%u %#a" /var/cache/systemd/home/blob-user | grep "^0 0755"
-test -d /home/blob-user/.identity-blob
-stat -c "%u %#a" /home/blob-user/.identity-blob | grep "^12345 0700"
-
-checkblob test1 /tmp/blob1/test1
-(! checkblob test1 /tmp/blob2/test1 )
-checkblob test2 /tmp/blob1/test2
-(! checkblob test2 /tmp/blob2/test2 )
-(! checkblob фаил /tmp/blob1/фаил )
-(! checkblob test3 /tmp/external-test3 )
-(! checkblob avatar /tmp/external-avatar )
-
-# append files to existing blob, both well-known and other
-PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
-        -b test3=/tmp/external-test3 --avatar=/tmp/external-avatar
-inspect blob-user
-checkblob test1 /tmp/blob1/test1
-(! checkblob test1 /tmp/blob2/test1 )
-checkblob test2 /tmp/blob1/test2
-(! checkblob test2 /tmp/blob2/test2 )
-(! checkblob фаил /tmp/blob1/фаил )
-checkblob test3 /tmp/external-test3
-checkblob avatar /tmp/external-avatar
-
-# delete files from existing blob, both well-known and other
-PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
-        -b test3= --avatar=
-inspect blob-user
-checkblob test1 /tmp/blob1/test1
-(! checkblob test1 /tmp/blob2/test1 )
-checkblob test2 /tmp/blob1/test2
-(! checkblob test2 /tmp/blob2/test2 )
-(! checkblob фаил /tmp/blob1/фаил )
-(! checkblob test3 /tmp/external-test3 )
-(! checkblob avatar /tmp/external-avatar )
-
-# swap entire blob directory
-PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
-        -b /tmp/blob2
-inspect blob-user
-(! checkblob test1 /tmp/blob1/test1 )
-checkblob test1 /tmp/blob2/test1
-(! checkblob test2 /tmp/blob1/test2 )
-checkblob test2 /tmp/blob2/test2
-(! checkblob фаил /tmp/blob1/фаил )
-(! checkblob test3 /tmp/external-test3 )
-(! checkblob avatar /tmp/external-avatar )
-
-# create and delete files while swapping blob directory. Also symlinks.
-PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
-        -b /tmp/blob1 -b test2= -b test3=/tmp/external-test3 --avatar=/tmp/external-avatar-lnk
-inspect blob-user
-checkblob test1 /tmp/blob1/test1
-(! checkblob test1 /tmp/blob2/test1 )
-(! checkblob test2 /tmp/blob1/test2 )
-(! checkblob test2 /tmp/blob2/test2 )
-(! checkblob фаил /tmp/blob1/фаил )
-checkblob test3 /tmp/external-test3
-checkblob avatar /tmp/external-avatar # target of the link
-
-# clear the blob directory
-PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
-        -b /tmp/blob2 -b test3=/tmp/external-test3 --blob=
-inspect blob-user
-(! checkblob test1 /tmp/blob1/test1 )
-(! checkblob test1 /tmp/blob2/test1 )
-(! checkblob test2 /tmp/blob1/test2 )
-(! checkblob test2 /tmp/blob2/test2 )
-(! checkblob фаил /tmp/blob1/фаил )
-(! checkblob test3 /tmp/external-test3 )
-(! checkblob avatar /tmp/external-avatar )
-
-# file that's exactly 64M still fits
-# FIXME: Figure out why this fails on ext4.
-if [[ "$FSTYPE" != "ext2/ext3" ]]; then
-    PASSWORD=EMJuc3zQaMibJo homectl update blob-user \
-            -b barely-fits=/tmp/external-barely-fits
-    (! checkblob test1 /tmp/blob1/test1 )
-    (! checkblob test1 /tmp/blob2/test1 )
-    (! checkblob test2 /tmp/blob1/test2 )
-    (! checkblob test2 /tmp/blob2/test2 )
-    (! checkblob фаил /tmp/blob1/фаил )
-    (! checkblob test3 /tmp/external-test3 )
-    (! checkblob avatar /tmp/external-avatar )
-    checkblob barely-fits /tmp/external-barely-fits
-fi
-
-# error out if the file is too big
-(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b huge=/tmp/external-toobig )
-
-# error out if filenames are invalid
-(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b .hidden=/tmp/external-test3 )
-(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b "with spaces=/tmp/external-test3" )
-(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b with=equals=/tmp/external-test3 )
-(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b файл=/tmp/external-test3 )
-(! PASSWORD=EMJuc3zQaMibJo homectl update blob-user -b special@chars=/tmp/external-test3 )
-
-# Make sure offline updates to blobs get propagated in
-homectl deactivate blob-user
-inspect blob-user
-homectl update blob-user --offline -b barely-fits= -b propagated=/tmp/external-test3
-inspect blob-user
-PASSWORD=EMJuc3zQaMibJo homectl activate blob-user
-inspect blob-user
-(! checkblob barely-fits /tmp/external-barely-fits )
-checkblob propagated /tmp/external-test3
-
-homectl deactivate blob-user
-wait_for_state blob-user inactive
-homectl remove blob-user
-
-# userdbctl tests
-export PAGER=
-
-# Create a couple of user/group records to test io.systemd.DropIn
-# See docs/USER_RECORD.md and docs/GROUP_RECORD.md
-mkdir -p /run/userdb/
-cat >"/run/userdb/dropingroup.group" <<\EOF
-{
-    "groupName" : "dropingroup",
-    "gid"       : 1000000
-}
-EOF
-cat >"/run/userdb/dropinuser.user" <<\EOF
-{
-    "userName" : "dropinuser",
-    "uid"      : 2000000,
-    "realName" : "🐱",
-    "memberOf" : [
-        "dropingroup"
-    ]
-}
-EOF
-cat >"/run/userdb/dropinuser.user-privileged" <<\EOF
-{
-    "privileged" : {
-        "hashedPassword" : [
-            "$6$WHBKvAFFT9jKPA4k$OPY4D4TczKN/jOnJzy54DDuOOagCcvxxybrwMbe1SVdm.Bbr.zOmBdATp.QrwZmvqyr8/SafbbQu.QZ2rRvDs/"
-        ],
-        "sshAuthorizedKeys" : [
-            "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA//dxI2xLg4MgxIKKZv1nqwTEIlE/fdakii2Fb75pG+ foo@bar.tld",
-            "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMlaqG2rTMje5CQnfjXJKmoSpEVJ2gWtx4jBvsQbmee2XbU/Qdq5+SRisssR9zVuxgg5NA5fv08MgjwJQMm+csc= hello@world.tld"
-        ]
-    }
-}
-EOF
-# Set permissions and create necessary symlinks as described in nss-systemd(8)
-chmod 0600 "/run/userdb/dropinuser.user-privileged"
-ln -svrf "/run/userdb/dropingroup.group" "/run/userdb/1000000.group"
-ln -svrf "/run/userdb/dropinuser.user" "/run/userdb/2000000.user"
-ln -svrf "/run/userdb/dropinuser.user-privileged" "/run/userdb/2000000.user-privileged"
-
-userdbctl
-userdbctl --version
-userdbctl --help --no-pager
-userdbctl --no-legend
-userdbctl --output=classic
-userdbctl --output=friendly
-userdbctl --output=table
-userdbctl --output=json | jq
-userdbctl -j --json=pretty | jq
-userdbctl -j --json=short | jq
-userdbctl --with-varlink=no
-
-userdbctl user
-userdbctl user testuser
-userdbctl user root
-userdbctl user testuser root
-userdbctl user -j testuser root | jq
-# Check only UID for the nobody user, since the name is build-configurable
-userdbctl user --with-nss=no --synthesize=yes
-userdbctl user --with-nss=no --synthesize=yes 0 root 65534
-userdbctl user dropinuser
-userdbctl user 2000000
-userdbctl user --with-nss=no --with-varlink=no --synthesize=no --multiplexer=no dropinuser
-userdbctl user --with-nss=no 2000000
-(! userdbctl user '')
-(! userdbctl user 🐱)
-(! userdbctl user 🐱 '' bar)
-(! userdbctl user i-do-not-exist)
-(! userdbctl user root i-do-not-exist testuser)
-(! userdbctl user --with-nss=no --synthesize=no 0 root 65534)
-(! userdbctl user -N root nobody)
-(! userdbctl user --with-dropin=no dropinuser)
-(! userdbctl user --with-dropin=no 2000000)
-
-userdbctl group
-userdbctl group testuser
-userdbctl group root
-userdbctl group testuser root
-userdbctl group -j testuser root | jq
-# Check only GID for the nobody group, since the name is build-configurable
-userdbctl group --with-nss=no --synthesize=yes
-userdbctl group --with-nss=no --synthesize=yes 0 root 65534
-userdbctl group dropingroup
-userdbctl group 1000000
-userdbctl group --with-nss=no --with-varlink=no --synthesize=no --multiplexer=no dropingroup
-userdbctl group --with-nss=no 1000000
-(! userdbctl group '')
-(! userdbctl group 🐱)
-(! userdbctl group 🐱 '' bar)
-(! userdbctl group i-do-not-exist)
-(! userdbctl group root i-do-not-exist testuser)
-(! userdbctl group --with-nss=no --synthesize=no 0 root 65534)
-(! userdbctl group --with-dropin=no dropingroup)
-(! userdbctl group --with-dropin=no 1000000)
-
-userdbctl users-in-group
-userdbctl users-in-group testuser
-userdbctl users-in-group testuser root
-userdbctl users-in-group -j testuser root | jq
-userdbctl users-in-group 🐱
-(! userdbctl users-in-group '')
-(! userdbctl users-in-group foo '' bar)
-
-userdbctl groups-of-user
-userdbctl groups-of-user testuser
-userdbctl groups-of-user testuser root
-userdbctl groups-of-user -j testuser root | jq
-userdbctl groups-of-user 🐱
-(! userdbctl groups-of-user '')
-(! userdbctl groups-of-user foo '' bar)
-
-userdbctl services
-userdbctl services -j | jq
-
-varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"testuser","service":"io.systemd.Multiplexer"}'
-varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"root","service":"io.systemd.Multiplexer"}'
-varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"dropinuser","service":"io.systemd.Multiplexer"}'
-varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"uid":2000000,"service":"io.systemd.Multiplexer"}'
-(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"","service":"io.systemd.Multiplexer"}')
-(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"🐱","service":"io.systemd.Multiplexer"}')
-(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{"userName":"i-do-not-exist","service":"io.systemd.Multiplexer"}')
-
-userdbctl ssh-authorized-keys dropinuser | tee /tmp/authorized-keys
-grep "ssh-ed25519" /tmp/authorized-keys
-grep "ecdsa-sha2-nistp256" /tmp/authorized-keys
-echo "my-top-secret-key 🐱" >/tmp/my-top-secret-key
-userdbctl ssh-authorized-keys dropinuser --chain /bin/cat /tmp/my-top-secret-key | tee /tmp/authorized-keys
-grep "ssh-ed25519" /tmp/authorized-keys
-grep "ecdsa-sha2-nistp256" /tmp/authorized-keys
-grep "my-top-secret-key 🐱" /tmp/authorized-keys
-(! userdbctl ssh-authorized-keys 🐱)
-(! userdbctl ssh-authorized-keys dropin-user --chain)
-(! userdbctl ssh-authorized-keys dropin-user --chain '')
-(! SYSTEMD_LOG_LEVEL=debug userdbctl ssh-authorized-keys dropin-user --chain /bin/false)
-
-(! userdbctl '')
-for opt in json multiplexer output synthesize with-dropin with-nss with-varlink; do
-    (! userdbctl "--$opt=''")
-    (! userdbctl "--$opt='🐱'")
-    (! userdbctl "--$opt=foo")
-    (! userdbctl "--$opt=foo" "--$opt=''" "--$opt=🐱")
-done
-
-# FIXME: sshd seems to crash inside asan currently, skip the actual ssh test hence
-if command -v ssh &>/dev/null && command -v sshd &>/dev/null && ! [[ -v ASAN_OPTIONS ]]; then
-    at_exit() {
-        set +e
-
-        systemctl is-active -q mysshserver.socket && systemctl stop mysshserver.socket
-        rm -f /tmp/homed.id_ecdsa /run/systemd/system/mysshserver{@.service,.socket}
-        systemctl daemon-reload
-        homectl remove homedsshtest
-        for dir in /etc /usr/lib; do
-            if [[ -f "$dir/pam.d/sshd.bak" ]]; then
-                mv "$dir/pam.d/sshd.bak" "$dir/pam.d/sshd"
-            fi
-        done
-    }
-
-    trap at_exit EXIT
-
-    # Test that SSH logins work with delayed unlocking
-    ssh-keygen -N '' -C '' -t ecdsa -f /tmp/homed.id_ecdsa
-    NEWPASSWORD=hunter4711 homectl create \
-                       --disk-size=min \
-                       --luks-discard=yes \
-                       --luks-pbkdf-type=pbkdf2 \
-                       --luks-pbkdf-time-cost=1ms \
-                       --rate-limit-interval=1s \
-                       --rate-limit-burst=1000 \
-                       --enforce-password-policy=no \
-                       --ssh-authorized-keys=@/tmp/homed.id_ecdsa.pub \
-                       --stop-delay=0 \
-                       homedsshtest
-    homectl inspect homedsshtest
-
-    mkdir -p /etc/ssh
-    test -f /etc/ssh/ssh_host_ecdsa_key || ssh-keygen -t ecdsa -C '' -N '' -f /etc/ssh/ssh_host_ecdsa_key
-
-    # ssh wants this dir around, but distros cannot agree on a common name for it, let's just create all that
-    # are aware of distros use
-    mkdir -p /usr/share/empty.sshd /var/empty /var/empty/sshd /run/sshd
-
-    for dir in /etc /usr/lib; do
-        if [[ -f "$dir/pam.d/sshd" ]]; then
-            mv "$dir/pam.d/sshd" "$dir/pam.d/sshd.bak"
-            cat >"$dir/pam.d/sshd" <<EOF
-auth    sufficient pam_unix.so nullok
-auth    sufficient pam_systemd_home.so debug
-auth    required   pam_deny.so
-account sufficient pam_systemd_home.so debug
-account sufficient pam_unix.so
-account required   pam_permit.so
-session optional   pam_systemd_home.so debug
-session optional   pam_systemd.so
-session required   pam_unix.so
-EOF
-            break
-        fi
-    done
-
-    mkdir -p /etc/sshd/
-    cat >/etc/ssh/sshd_config <<EOF
-AuthorizedKeysCommand /usr/bin/userdbctl ssh-authorized-keys %u
-AuthorizedKeysCommandUser root
-UsePAM yes
-AcceptEnv PASSWORD
-LogLevel DEBUG3
-EOF
-
-    cat >/run/systemd/system/mysshserver.socket <<EOF
-[Socket]
-ListenStream=4711
-Accept=yes
-EOF
-
-    cat >/run/systemd/system/mysshserver@.service <<EOF
-[Service]
-ExecStart=-/usr/sbin/sshd -i -d -e
-StandardInput=socket
-StandardOutput=socket
-StandardError=journal
-EOF
-
-    systemctl daemon-reload
-    systemctl start mysshserver.socket
-
-    userdbctl user -j homedsshtest
-
-    ssh -t -t -4 -p 4711 -i /tmp/homed.id_ecdsa \
-        -o "SetEnv PASSWORD=hunter4711" -o "StrictHostKeyChecking no" \
-        homedsshtest@localhost echo zzz | tr -d '\r' | tee /tmp/homedsshtest.out
-    grep -E "^zzz$" /tmp/homedsshtest.out
-    rm /tmp/homedsshtest.out
-
-    ssh -t -t -4 -p 4711 -i /tmp/homed.id_ecdsa \
-        -o "SetEnv PASSWORD=hunter4711" -o "StrictHostKeyChecking no" \
-        homedsshtest@localhost env
-
-    wait_for_state homedsshtest inactive
-fi
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-50.DDI.sh b/test/units/testsuite-50.DDI.sh
deleted file mode 100755 (executable)
index 42c9a43..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Check that the /sbin/mount.ddi helper works
-dir="/tmp/mounthelper.$RANDOM"
-mount -t ddi "$MINIMAL_IMAGE.gpt" "$dir" -o ro,X-mount.mkdir,discard
-umount -R "$dir"
-
-# Test systemd-repart --make-ddi=:
-if [[ -z "${OPENSSL_CONFIG:?}" ]] || ! command -v mksquashfs &>/dev/null; then
-    echo "Skipping --make-ddi= tests"
-    exit 0
-fi
-
-openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \
-            -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
-            -keyout /tmp/test-50-privkey.key -out /tmp/test-50-cert.crt
-mkdir -p /tmp/test-50-confext/etc/extension-release.d/
-echo "foobar50" >/tmp/test-50-confext/etc/waldo
-{
-    grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release
-    echo IMAGE_ID=waldo
-    echo IMAGE_VERSION=7
-} >/tmp/test-50-confext/etc/extension-release.d/extension-release.waldo
-mkdir -p /run/confexts
-
-SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
-    systemd-repart -C \
-                   -s /tmp/test-50-confext \
-                   --certificate=/tmp/test-50-cert.crt \
-                   --private-key=/tmp/test-50-privkey.key \
-                   /run/confexts/waldo.confext.raw
-rm -rf /tmp/test-50-confext
-
-mkdir -p /run/verity.d
-cp /tmp/test-50-cert.crt /run/verity.d/
-systemd-dissect --mtree /run/confexts/waldo.confext.raw
-
-systemd-confext refresh
-test "$(</etc/waldo)" = foobar50
-rm /run/confexts/waldo.confext.raw
-systemd-confext refresh
-test ! -f /etc/waldo
-
-mkdir -p /tmp/test-50-sysext/usr/lib/extension-release.d/
-# Make sure the sysext is big enough to not fit in the minimum partition size of repart so we know the
-# Minimize= logic is working.
-truncate --size=50M /tmp/test-50-sysext/usr/waldo
-{
-    grep -e '^\(ID\|VERSION_ID\)=' /etc/os-release
-    echo IMAGE_ID=waldo
-    echo IMAGE_VERSION=7
-} >/tmp/test-50-sysext/usr/lib/extension-release.d/extension-release.waldo
-mkdir -p /run/extensions
-
-SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
-    systemd-repart -S \
-                   -s /tmp/test-50-sysext \
-                   --certificate=/tmp/test-50-cert.crt \
-                   --private-key=/tmp/test-50-privkey.key \
-                   /run/extensions/waldo.sysext.raw
-
-systemd-dissect --mtree /run/extensions/waldo.sysext.raw
-systemd-sysext refresh
-test -f /usr/waldo
-rm /run/verity.d/test-50-cert.crt /run/extensions/waldo.sysext.raw /tmp/test-50-cert.crt /tmp/test-50-privkey.key
-systemd-sysext refresh
-test ! -f /usr/waldo
diff --git a/test/units/testsuite-50.dissect.sh b/test/units/testsuite-50.dissect.sh
deleted file mode 100755 (executable)
index 991346f..0000000
+++ /dev/null
@@ -1,740 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-# shellcheck disable=SC2233,SC2235
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-BIND_LOG_SOCKETS=(
-    --property BindReadOnlyPaths=/dev/log
-    --property BindReadOnlyPaths=/run/systemd/journal/socket
-    --property BindReadOnlyPaths=/run/systemd/journal/stdout
-)
-
-systemd-dissect --json=short "$MINIMAL_IMAGE.raw" | \
-    grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
-systemd-dissect "$MINIMAL_IMAGE.raw" | grep -q -F "MARKER=1"
-# shellcheck disable=SC2153
-systemd-dissect "$MINIMAL_IMAGE.raw" | grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
-
-systemd-dissect --list "$MINIMAL_IMAGE.raw" | grep -q '^etc/os-release$'
-systemd-dissect --mtree "$MINIMAL_IMAGE.raw" --mtree-hash yes | \
-    grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]* sha256sum=[a-z0-9]*$"
-systemd-dissect --mtree "$MINIMAL_IMAGE.raw" --mtree-hash no  | \
-    grep -qe "^./usr/bin/cat type=file mode=0755 uid=0 gid=0 size=[0-9]*$"
-
-read -r SHA256SUM1 _ < <(systemd-dissect --copy-from "$MINIMAL_IMAGE.raw" etc/os-release | sha256sum)
-test "$SHA256SUM1" != ""
-read -r SHA256SUM2 _ < <(systemd-dissect --read-only --with "$MINIMAL_IMAGE.raw" sha256sum etc/os-release)
-test "$SHA256SUM2" != ""
-test "$SHA256SUM1" = "$SHA256SUM2"
-
-if systemctl --version | grep -qF -- "+LIBARCHIVE" ; then
-    # Make sure tarballs are reproducible
-    read -r SHA256SUM1 _ < <(systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | sha256sum)
-    test "$SHA256SUM1" != ""
-    read -r SHA256SUM2 _ < <(systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | sha256sum)
-    test "$SHA256SUM2" != ""
-    test "$SHA256SUM1" = "$SHA256SUM2"
-    # Also check that a file we expect to be there is there
-    systemd-dissect --make-archive "$MINIMAL_IMAGE.raw" | tar t | grep etc/os-release
-fi
-
-mv "$MINIMAL_IMAGE.verity" "$MINIMAL_IMAGE.fooverity"
-mv "$MINIMAL_IMAGE.roothash" "$MINIMAL_IMAGE.foohash"
-systemd-dissect "$MINIMAL_IMAGE.raw" \
-                --json=short \
-                --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
-                --verity-data="$MINIMAL_IMAGE.fooverity" | \
-                grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
-systemd-dissect "$MINIMAL_IMAGE.raw" \
-                --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
-                --verity-data="$MINIMAL_IMAGE.fooverity" | \
-                grep -q -F "MARKER=1"
-systemd-dissect "$MINIMAL_IMAGE.raw" \
-                --root-hash="$MINIMAL_IMAGE_ROOTHASH" \
-                --verity-data="$MINIMAL_IMAGE.fooverity" | \
-                grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
-mv "$MINIMAL_IMAGE.fooverity" "$MINIMAL_IMAGE.verity"
-mv "$MINIMAL_IMAGE.foohash" "$MINIMAL_IMAGE.roothash"
-
-mkdir -p "$IMAGE_DIR/mount" "$IMAGE_DIR/mount2"
-systemd-dissect --mount "$MINIMAL_IMAGE.raw" "$IMAGE_DIR/mount"
-grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
-grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
-grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
-# Verity volume should be shared (opened only once)
-systemd-dissect --mount "$MINIMAL_IMAGE.raw" "$IMAGE_DIR/mount2"
-verity_count=$(find /dev/mapper/ -name "*verity*" | wc -l)
-# In theory we should check that count is exactly one. In practice, libdevmapper
-# randomly and unpredictably fails with an unhelpful EINVAL when a device is open
-# (and even mounted and in use), so best-effort is the most we can do for now
-if [[ "$verity_count" -lt 1 ]]; then
-    echo "Verity device $MINIMAL_IMAGE.raw not found in /dev/mapper/"
-    exit 1
-fi
-systemd-dissect --umount "$IMAGE_DIR/mount"
-systemd-dissect --umount "$IMAGE_DIR/mount2"
-
-systemd-run -P -p RootImage="$MINIMAL_IMAGE.raw" "${BIND_LOG_SOCKETS[@]}" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-mv "$MINIMAL_IMAGE.verity" "$MINIMAL_IMAGE.fooverity"
-mv "$MINIMAL_IMAGE.roothash" "$MINIMAL_IMAGE.foohash"
-systemd-run -P \
-            -p RootImage="$MINIMAL_IMAGE.raw" \
-            -p RootHash="$MINIMAL_IMAGE.foohash" \
-            -p RootVerity="$MINIMAL_IMAGE.fooverity" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/os-release | grep -q -F "MARKER=1"
-# Let's use the long option name just here as a test
-systemd-run -P \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            --property RootHash="$MINIMAL_IMAGE_ROOTHASH" \
-            --property RootVerity="$MINIMAL_IMAGE.fooverity" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/os-release | grep -q -F "MARKER=1"
-mv "$MINIMAL_IMAGE.fooverity" "$MINIMAL_IMAGE.verity"
-mv "$MINIMAL_IMAGE.foohash" "$MINIMAL_IMAGE.roothash"
-
-# Derive partition UUIDs from root hash, in UUID syntax
-ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "$MINIMAL_IMAGE.roothash")" -u | tail -n 1 | cut -b 6-)"
-VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "$MINIMAL_IMAGE.roothash")" -u | tail -n 1 | cut -b 6-)"
-
-systemd-dissect --json=short \
-                --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
-                "$MINIMAL_IMAGE.gpt" | \
-                grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$ARCHITECTURE"'","verity":"signed",'
-systemd-dissect --json=short \
-                --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
-                "$MINIMAL_IMAGE.gpt" | \
-                grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$ARCHITECTURE"'","verity":null,'
-if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
-    systemd-dissect --json=short \
-                    --root-hash "$MINIMAL_IMAGE_ROOTHASH" \
-                    "$MINIMAL_IMAGE.gpt" | \
-                    grep -qE '{"rw":"ro","designator":"root-verity-sig","partition_uuid":"'".*"'","partition_label":"Signature Partition","fstype":"verity_hash_signature","architecture":"'"$ARCHITECTURE"'","verity":null,'
-fi
-systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" "$MINIMAL_IMAGE.gpt" | grep -q -F "MARKER=1"
-systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" "$MINIMAL_IMAGE.gpt" | grep -q -F -f <(sed 's/"//g' "$OS_RELEASE")
-
-# Test image policies
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt"
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='*'
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='~')
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy='-')
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=absent)
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=swap=unprotected+encrypted+verity)
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=unprotected
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:root-verity-sig=unused+absent
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:swap=absent
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:swap=absent+unprotected
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=verity:root-verity=unused+absent)
-systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed:root-verity-sig=unused+absent)
-(! systemd-dissect --validate "$MINIMAL_IMAGE.gpt" --image-policy=root=signed:root-verity=unused+absent)
-
-# Test RootImagePolicy= unit file setting
-systemd-run --wait -P \
-            -p RootImage="$MINIMAL_IMAGE.gpt" \
-            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
-            -p MountAPIVFS=yes \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run --wait -P \
-            -p RootImage="$MINIMAL_IMAGE.gpt" \
-            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
-            -p RootImagePolicy='*' \
-            -p MountAPIVFS=yes \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/os-release | grep -q -F "MARKER=1"
-(! systemd-run --wait -P \
-               -p RootImage="$MINIMAL_IMAGE.gpt" \
-               -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
-               -p RootImagePolicy='~' \
-               -p MountAPIVFS=yes \
-               "${BIND_LOG_SOCKETS[@]}" \
-               cat /usr/lib/os-release | grep -q -F "MARKER=1")
-(! systemd-run --wait -P \
-               -p RootImage="$MINIMAL_IMAGE.gpt" \
-               -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
-               -p RootImagePolicy='-' \
-               -p MountAPIVFS=yes \
-               "${BIND_LOG_SOCKETS[@]}" \
-               cat /usr/lib/os-release | grep -q -F "MARKER=1")
-(! systemd-run --wait -P \
-               -p RootImage="$MINIMAL_IMAGE.gpt" \
-               -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
-               -p RootImagePolicy='root=absent' \
-               -p MountAPIVFS=yes \
-               "${BIND_LOG_SOCKETS[@]}" \
-               cat /usr/lib/os-release | grep -q -F "MARKER=1")
-systemd-run --wait -P \
-            -p RootImage="$MINIMAL_IMAGE.gpt" \
-            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
-            -p RootImagePolicy='root=verity' \
-            -p MountAPIVFS=yes \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run --wait -P \
-            -p RootImage="$MINIMAL_IMAGE.gpt" \
-            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
-            -p RootImagePolicy='root=signed' \
-            -p MountAPIVFS=yes \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/os-release | grep -q -F "MARKER=1"
-(! systemd-run --wait -P \
-               -p RootImage="$MINIMAL_IMAGE.gpt" \
-               -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
-               -p RootImagePolicy='root=encrypted' \
-               -p MountAPIVFS=yes \
-               "${BIND_LOG_SOCKETS[@]}" \
-               cat /usr/lib/os-release | grep -q -F "MARKER=1")
-
-systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" --mount "$MINIMAL_IMAGE.gpt" "$IMAGE_DIR/mount"
-grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
-grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
-grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
-systemd-dissect --umount "$IMAGE_DIR/mount"
-
-systemd-dissect --root-hash "$MINIMAL_IMAGE_ROOTHASH" --mount "$MINIMAL_IMAGE.gpt" --in-memory "$IMAGE_DIR/mount"
-grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/usr/lib/os-release"
-grep -q -F -f "$OS_RELEASE" "$IMAGE_DIR/mount/etc/os-release"
-grep -q -F "MARKER=1" "$IMAGE_DIR/mount/usr/lib/os-release"
-systemd-dissect --umount "$IMAGE_DIR/mount"
-
-# add explicit -p MountAPIVFS=yes once to test the parser
-systemd-run -P \
-            -p RootImage="$MINIMAL_IMAGE.gpt" \
-            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
-            -p MountAPIVFS=yes \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P \
-            -p RootImage="$MINIMAL_IMAGE.raw" \
-            -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P \
-            -p RootImage="$MINIMAL_IMAGE.gpt" \
-            -p RootImageOptions="root:ro,noatime root:ro,dev" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            mount | grep -F "squashfs" | grep -q -F "noatime"
-
-mkdir -p "$IMAGE_DIR/result"
-cat >/run/systemd/system/testservice-50a.service <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "mount >/run/result/a"
-BindPaths=$IMAGE_DIR/result:/run/result
-TemporaryFileSystem=/run
-RootImage=$MINIMAL_IMAGE.raw
-RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
-RootImageOptions=nosuid,dev
-BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
-EOF
-systemctl start testservice-50a.service
-grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F "noatime"
-grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -q -F -v "nosuid"
-
-cat >/run/systemd/system/testservice-50b.service <<EOF
-[Service]
-Type=oneshot
-ExecStart=bash -c "mount >/run/result/b"
-BindPaths=$IMAGE_DIR/result:/run/result
-TemporaryFileSystem=/run
-RootImage=$MINIMAL_IMAGE.gpt
-RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev
-RootImageOptions=home:ro,dev nosuid,dev,%%foo
-# this is the default, but let's specify once to test the parser
-MountAPIVFS=yes
-BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
-EOF
-systemctl start testservice-50b.service
-grep -F "squashfs" "$IMAGE_DIR/result/b" | grep -q -F "noatime"
-
-# Check that specifier escape is applied %%foo → %foo
-busctl get-property org.freedesktop.systemd1 \
-                    /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice \
-                    org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
-
-# Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image
-systemd-run -P \
-            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
-            cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P \
-            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
-            cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P \
-            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2:nosuid,dev" \
-            mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P \
-            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1:root:nosuid $MINIMAL_IMAGE.raw:/run/img2:home:suid" \
-            mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P \
-            -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3" \
-            cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P \
-            -p MountImages="$MINIMAL_IMAGE.raw:/run/img2\:3:nosuid" \
-            mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P \
-            -p TemporaryFileSystem=/run \
-            -p RootImage="$MINIMAL_IMAGE.raw" \
-            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P \
-            -p TemporaryFileSystem=/run \
-            -p RootImage="$MINIMAL_IMAGE.raw" \
-            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P \
-            -p TemporaryFileSystem=/run \
-            -p RootImage="$MINIMAL_IMAGE.gpt" \
-            -p RootHash="$MINIMAL_IMAGE_ROOTHASH" \
-            -p MountImages="$MINIMAL_IMAGE.gpt:/run/img1 $MINIMAL_IMAGE.raw:/run/img2" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
-cat >/run/systemd/system/testservice-50c.service <<EOF
-[Service]
-MountAPIVFS=yes
-TemporaryFileSystem=/run
-RootImage=$MINIMAL_IMAGE.raw
-BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
-MountImages=$MINIMAL_IMAGE.gpt:/run/img1:root:noatime:home:relatime
-MountImages=$MINIMAL_IMAGE.raw:/run/img2\:3:nosuid
-ExecStart=bash -c "cat /run/img1/usr/lib/os-release >/run/result/c"
-ExecStart=bash -c "cat /run/img2:3/usr/lib/os-release >>/run/result/c"
-ExecStart=bash -c "mount >>/run/result/c"
-BindPaths=$IMAGE_DIR/result:/run/result
-Type=oneshot
-EOF
-systemctl start testservice-50c.service
-grep -q -F "MARKER=1" "$IMAGE_DIR/result/c"
-grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F "noatime"
-grep -F "squashfs" "$IMAGE_DIR/result/c" | grep -q -F -v "nosuid"
-
-# Adding a new mounts at runtime works if the unit is in the active state,
-# so use Type=notify to make sure there's no race condition in the test
-cat >/run/systemd/system/testservice-50d.service <<EOF
-[Service]
-RuntimeMaxSec=300
-Type=notify
-RemainAfterExit=yes
-MountAPIVFS=yes
-PrivateTmp=yes
-ExecStart=sh -c ' \\
-    systemd-notify --ready; \\
-    while [ ! -f /tmp/img/usr/lib/os-release ] || ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do \\
-        sleep 0.1; \\
-    done; \\
-    mount; \\
-    mount | grep -F "on /tmp/img type squashfs" | grep -q -F "nosuid"; \\
-'
-EOF
-systemctl start testservice-50d.service
-
-# Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount)
-mkdir -p /tmp/wrong/foo
-mksquashfs /tmp/wrong/foo /tmp/wrong.raw
-systemctl mount-image --mkdir testservice-50d.service /tmp/wrong.raw /tmp/img
-test "$(systemctl show -P SubState testservice-50d.service)" = "running"
-systemctl mount-image --mkdir testservice-50d.service "$MINIMAL_IMAGE.raw" /tmp/img root:nosuid
-# shellcheck disable=SC2016
-timeout 30s bash -xec 'while [[ $(systemctl show -P SubState testservice-50d.service) == running ]]; do sleep .2; done'
-systemctl is-active testservice-50d.service
-
-# ExtensionImages will set up an overlay
-systemd-run -P \
-            --property ExtensionImages=/tmp/app0.raw \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P \
-            --property ExtensionImages=/tmp/app0.raw \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P \
-            --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P \
-            --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P \
-            --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /opt/script1.sh | grep -q -F "extension-release.app2"
-systemd-run -P \
-            --property ExtensionImages="/tmp/app0.raw /tmp/app1.raw" \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
-systemd-run -P \
-            --property ExtensionImages=/tmp/app-nodistro.raw \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P \
-            --property ExtensionImages=/etc/service-scoped-test.raw \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-# Check that using a symlink to NAME-VERSION.raw works as long as the symlink has the correct name NAME.raw
-mkdir -p /tmp/symlink-test/
-cp /tmp/app-nodistro.raw /tmp/symlink-test/app-nodistro-v1.raw
-ln -fs /tmp/symlink-test/app-nodistro-v1.raw /tmp/symlink-test/app-nodistro.raw
-systemd-run -P \
-            --property ExtensionImages=/tmp/symlink-test/app-nodistro.raw \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-
-# Symlink check again but for confext
-mkdir -p /etc/symlink-test/
-cp /etc/service-scoped-test.raw /etc/symlink-test/service-scoped-test-v1.raw
-ln -fs /etc/symlink-test/service-scoped-test-v1.raw /etc/symlink-test/service-scoped-test.raw
-systemd-run -P \
-            --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-# And again mixing sysext and confext
-systemd-run -P \
-    --property ExtensionImages=/tmp/symlink-test/app-nodistro.raw \
-    --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
-    --property RootImage="$MINIMAL_IMAGE.raw" \
-    "${BIND_LOG_SOCKETS[@]}" \
-    cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-systemd-run -P \
-    --property ExtensionImages=/tmp/symlink-test/app-nodistro.raw \
-    --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
-    --property RootImage="$MINIMAL_IMAGE.raw" \
-    "${BIND_LOG_SOCKETS[@]}" \
-    cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-
-cat >/run/systemd/system/testservice-50e.service <<EOF
-[Service]
-MountAPIVFS=yes
-TemporaryFileSystem=/run /var/lib
-StateDirectory=app0
-RootImage=$MINIMAL_IMAGE.raw
-ExtensionImages=/tmp/app0.raw /tmp/app1.raw:nosuid
-BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
-# Relevant only for sanitizer runs
-UnsetEnvironment=LD_PRELOAD
-ExecStart=bash -c '/opt/script0.sh | grep ID'
-ExecStart=bash -c '/opt/script1.sh | grep ID'
-Type=oneshot
-RemainAfterExit=yes
-EOF
-systemctl start testservice-50e.service
-systemctl is-active testservice-50e.service
-
-# Check vpick support in ExtensionImages=
-VBASE="vtest$RANDOM"
-VDIR="/tmp/$VBASE.v"
-mkdir "$VDIR"
-
-ln -s /tmp/app0.raw "$VDIR/${VBASE}_0.raw"
-ln -s /tmp/app1.raw "$VDIR/${VBASE}_1.raw"
-
-systemd-run -P -p ExtensionImages="$VDIR" bash -c '/opt/script1.sh | grep ID'
-
-rm -rf "$VDIR"
-
-# ExtensionDirectories will set up an overlay
-mkdir -p "$IMAGE_DIR/app0" "$IMAGE_DIR/app1" "$IMAGE_DIR/app-nodistro" "$IMAGE_DIR/service-scoped-test"
-(! systemd-run -P \
-               --property ExtensionDirectories="$IMAGE_DIR/nonexistent" \
-               --property RootImage="$MINIMAL_IMAGE.raw" \
-               "${BIND_LOG_SOCKETS[@]}" \
-               cat /opt/script0.sh)
-(! systemd-run -P \
-               --property ExtensionDirectories="$IMAGE_DIR/app0" \
-               --property RootImage="$MINIMAL_IMAGE.raw" \
-               "${BIND_LOG_SOCKETS[@]}" \
-               cat /opt/script0.sh)
-systemd-dissect --mount /tmp/app0.raw "$IMAGE_DIR/app0"
-systemd-dissect --mount /tmp/app1.raw "$IMAGE_DIR/app1"
-systemd-dissect --mount /tmp/app-nodistro.raw "$IMAGE_DIR/app-nodistro"
-systemd-dissect --mount /etc/service-scoped-test.raw "$IMAGE_DIR/service-scoped-test"
-systemd-run -P \
-            --property ExtensionDirectories="$IMAGE_DIR/app0" \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P \
-            --property ExtensionDirectories="$IMAGE_DIR/app0" \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P \
-            --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P \
-            --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P \
-            --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /opt/script1.sh | grep -q -F "extension-release.app2"
-systemd-run -P \
-            --property ExtensionDirectories="$IMAGE_DIR/app0 $IMAGE_DIR/app1" \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
-systemd-run -P \
-            --property ExtensionDirectories="$IMAGE_DIR/app-nodistro" \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P \
-            --property ExtensionDirectories="$IMAGE_DIR/service-scoped-test" \
-            --property RootImage="$MINIMAL_IMAGE.raw" \
-            "${BIND_LOG_SOCKETS[@]}" \
-            cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-cat >/run/systemd/system/testservice-50f.service <<EOF
-[Service]
-MountAPIVFS=yes
-TemporaryFileSystem=/run /var/lib
-StateDirectory=app0
-RootImage=$MINIMAL_IMAGE.raw
-BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
-ExtensionDirectories=$IMAGE_DIR/app0 $IMAGE_DIR/app1
-# Relevant only for sanitizer runs
-UnsetEnvironment=LD_PRELOAD
-ExecStart=bash -c '/opt/script0.sh | grep ID'
-ExecStart=bash -c '/opt/script1.sh | grep ID'
-Type=oneshot
-RemainAfterExit=yes
-EOF
-systemctl start testservice-50f.service
-systemctl is-active testservice-50f.service
-
-# Check vpick support in ExtensionDirectories=
-VBASE="vtest$RANDOM"
-VDIR="/tmp/$VBASE.v"
-mkdir "$VDIR"
-
-ln -s "$IMAGE_DIR/app0" "$VDIR/${VBASE}_0"
-ln -s "$IMAGE_DIR/app1" "$VDIR/${VBASE}_1"
-
-systemd-run -P --property ExtensionDirectories="$VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
-
-rm -rf "$VDIR"
-
-systemd-dissect --umount "$IMAGE_DIR/app0"
-systemd-dissect --umount "$IMAGE_DIR/app1"
-
-# Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence
-mkdir -p /var/lib/extensions/
-ln -s /tmp/app-nodistro.raw /var/lib/extensions/app-nodistro.raw
-systemd-sysext merge
-grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
-systemd-sysext unmerge
-mkdir -p /etc/extensions/app-nodistro
-systemd-sysext merge
-test ! -e /usr/lib/systemd/system/some_file
-systemd-sysext unmerge
-rmdir /etc/extensions/app-nodistro
-
-# Similar, but go via varlink
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.List '{}'
-(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Merge '{}'
-grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Refresh '{}'
-grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
-varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Unmerge '{}'
-(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
-
-# Check that extensions cannot contain os-release
-mkdir -p /run/extensions/app-reject/usr/lib/{extension-release.d/,systemd/system}
-echo "ID=_any" >/run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject
-echo "ID=_any" >/run/extensions/app-reject/usr/lib/os-release
-touch /run/extensions/app-reject/usr/lib/systemd/system/other_file
-(! systemd-sysext merge)
-test ! -e /usr/lib/systemd/system/some_file
-test ! -e /usr/lib/systemd/system/other_file
-systemd-sysext unmerge
-rm -rf /run/extensions/app-reject
-rm /var/lib/extensions/app-nodistro.raw
-
-# Some super basic test that RootImage= works with .v/ dirs
-VBASE="vtest$RANDOM"
-VDIR="/tmp/$VBASE.v"
-mkdir "$VDIR"
-
-ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_33.raw"
-ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_34.raw"
-ln -s "$MINIMAL_IMAGE.raw" "$VDIR/${VBASE}_35.raw"
-
-systemd-run -P -p RootImage="$VDIR" "${BIND_LOG_SOCKETS[@]}" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-
-rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw"
-rmdir "$VDIR"
-
-mkdir -p /run/machines /run/portables /run/extensions
-touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
-
-systemd-dissect --discover --json=short >/tmp/discover.json
-grep -q -F '{"name":"a","type":"raw","class":"machine","ro":false,"path":"/run/machines/a.raw"' /tmp/discover.json
-grep -q -F '{"name":"b","type":"raw","class":"portable","ro":false,"path":"/run/portables/b.raw"' /tmp/discover.json
-grep -q -F '{"name":"c","type":"raw","class":"sysext","ro":false,"path":"/run/extensions/c.raw"' /tmp/discover.json
-rm /tmp/discover.json /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
-
-LOOP="$(systemd-dissect --attach --loop-ref=waldo "$MINIMAL_IMAGE.raw")"
-
-# Wait until the symlinks we want to test are established
-udevadm trigger -w "$LOOP"
-
-# Check if the /dev/loop/* symlinks really reference the right device
-test /dev/disk/by-loop-ref/waldo -ef "$LOOP"
-
-if [ "$(stat -c '%Hd:%Ld' "$MINIMAL_IMAGE.raw")" != '?d:?d' ] ; then
-   # Old stat didn't know the %Hd and %Ld specifiers and turned them into ?d
-   # instead. Let's simply skip the test on such old systems.
-   test "$(stat -c '/dev/disk/by-loop-inode/%Hd:%Ld-%i' "$MINIMAL_IMAGE.raw")" -ef "$LOOP"
-fi
-
-# Detach by loopback device
-systemd-dissect --detach "$LOOP"
-
-# Test long reference name.
-# Note, sizeof_field(struct loop_info64, lo_file_name) == 64,
-# and --loop-ref accepts upto 63 characters, and udev creates symlink
-# based on the name when it has upto _62_ characters.
-name="$(for _ in {1..62}; do echo -n 'x'; done)"
-LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")"
-udevadm trigger -w "$LOOP"
-
-# Check if the /dev/disk/by-loop-ref/$name symlink really references the right device
-test "/dev/disk/by-loop-ref/$name" -ef "$LOOP"
-
-# Detach by the /dev/disk/by-loop-ref symlink
-systemd-dissect --detach "/dev/disk/by-loop-ref/$name"
-
-name="$(for _ in {1..63}; do echo -n 'x'; done)"
-LOOP="$(systemd-dissect --attach --loop-ref="$name" "$MINIMAL_IMAGE.raw")"
-udevadm trigger -w "$LOOP"
-
-# Check if the /dev/disk/by-loop-ref/$name symlink does not exist
-test ! -e "/dev/disk/by-loop-ref/$name"
-
-# Detach by backing inode
-systemd-dissect --detach "$MINIMAL_IMAGE.raw"
-(! systemd-dissect --detach "$MINIMAL_IMAGE.raw")
-
-# check for confext functionality
-mkdir -p /run/confexts/test/etc/extension-release.d
-echo "ID=_any" >/run/confexts/test/etc/extension-release.d/extension-release.test
-echo "ARCHITECTURE=_any" >>/run/confexts/test/etc/extension-release.d/extension-release.test
-echo "MARKER_CONFEXT_123" >/run/confexts/test/etc/testfile
-cat <<EOF >/run/confexts/test/etc/testscript
-#!/bin/bash
-echo "This should not happen"
-EOF
-chmod +x /run/confexts/test/etc/testscript
-systemd-confext merge
-grep -q -F "MARKER_CONFEXT_123" /etc/testfile
-(! /etc/testscript)
-systemd-confext status
-systemd-confext unmerge
-rm -rf /run/confexts/
-
-unsquashfs -no-xattrs -d /tmp/img "$MINIMAL_IMAGE.raw"
-systemd-run --unit=test-root-ephemeral \
-    -p RootDirectory=/tmp/img \
-    -p RootEphemeral=yes \
-    -p Type=exec \
-    "${BIND_LOG_SOCKETS[@]}" \
-    bash -c "touch /abc && sleep infinity"
-test -n "$(ls -A /var/lib/systemd/ephemeral-trees)"
-systemctl stop test-root-ephemeral
-# shellcheck disable=SC2016
-timeout 10 bash -c 'until test -z "$(ls -A /var/lib/systemd/ephemeral-trees)"; do sleep .5; done'
-test ! -f /tmp/img/abc
-
-systemd-dissect --mtree /tmp/img >/dev/null
-systemd-dissect --list /tmp/img >/dev/null
-
-read -r SHA256SUM1 _ < <(systemd-dissect --copy-from /tmp/img etc/os-release | sha256sum)
-test "$SHA256SUM1" != ""
-
-echo abc > abc
-systemd-dissect --copy-to /tmp/img abc /abc
-test -f /tmp/img/abc
-
-# Test for dissect tool support with systemd-sysext
-mkdir -p /run/extensions/ testkit/usr/lib/extension-release.d/
-echo "ID=_any" >testkit/usr/lib/extension-release.d/extension-release.testkit
-echo "ARCHITECTURE=_any" >>testkit/usr/lib/extension-release.d/extension-release.testkit
-echo "MARKER_SYSEXT_123" >testkit/usr/lib/testfile
-mksquashfs testkit/ testkit.raw
-cp testkit.raw /run/extensions/
-unsquashfs -l /run/extensions/testkit.raw
-systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for portable service'
-systemd-dissect --no-pager /run/extensions/testkit.raw | grep -q '✓ sysext for system'
-systemd-sysext merge
-systemd-sysext status
-grep -q -F "MARKER_SYSEXT_123" /usr/lib/testfile
-systemd-sysext unmerge
-rm -rf /run/extensions/ testkit/
-
-# Test for dissect tool support with systemd-confext
-mkdir -p /run/confexts/ testjob/etc/extension-release.d/
-echo "ID=_any" >testjob/etc/extension-release.d/extension-release.testjob
-echo "ARCHITECTURE=_any" >>testjob/etc/extension-release.d/extension-release.testjob
-echo "MARKER_CONFEXT_123" >testjob/etc/testfile
-mksquashfs testjob/ testjob.raw
-cp testjob.raw /run/confexts/
-unsquashfs -l /run/confexts/testjob.raw
-systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for system'
-systemd-dissect --no-pager /run/confexts/testjob.raw | grep -q '✓ confext for portable service'
-systemd-confext merge
-systemd-confext status
-grep -q -F "MARKER_CONFEXT_123" /etc/testfile
-systemd-confext unmerge
-rm -rf /run/confexts/ testjob/
-
-systemd-run -P -p RootImage="$MINIMAL_IMAGE.raw" "${BIND_LOG_SOCKETS[@]}" cat /run/host/os-release | cmp "$OS_RELEASE"
-
-# Test that systemd-sysext reloads the daemon.
-mkdir -p /var/lib/extensions/
-ln -s /tmp/app-reload.raw /var/lib/extensions/app-reload.raw
-systemd-sysext merge --no-reload
-# the service should not be running
-(! systemctl --quiet is-active foo.service)
-systemd-sysext unmerge --no-reload
-systemd-sysext merge
-# shellcheck disable=SC2016
-timeout 30s bash -xec 'until [[ $(journalctl -b -u foo.service _TRANSPORT=stdout -o cat) == foo ]]; do sleep .5; done'
-systemd-sysext unmerge --no-reload
-# Grep on the Warning to find the warning helper mentioning the daemon reload.
-systemctl status foo.service 2>&1 | grep -q -F "Warning"
-systemd-sysext merge
-systemd-sysext unmerge
-systemctl status foo.service 2>&1 | grep -v -q -F "Warning"
-rm /var/lib/extensions/app-reload.raw
-
-# Sneak in a couple of expected-to-fail invocations to cover
-# https://github.com/systemd/systemd/issues/29610
-(! systemd-run -P -p MountImages="/this/should/definitely/not/exist.img:/run/img2\:3:nosuid" false)
-(! systemd-run -P -p ExtensionImages="/this/should/definitely/not/exist.img" false)
-(! systemd-run -P -p RootImage="/this/should/definitely/not/exist.img" false)
-(! systemd-run -P -p ExtensionDirectories="/foo/bar /foo/baz" false)
diff --git a/test/units/testsuite-50.mountfsd.sh b/test/units/testsuite-50.mountfsd.sh
deleted file mode 100755 (executable)
index 604a9db..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-if [[ ! -f /usr/lib/systemd/system/systemd-mountfsd.socket ]] || \
-   [[ ! -f /usr/lib/systemd/system/systemd-nsresourced.socket ]] || \
-   ! command -v mksquashfs || \
-   ! grep -q bpf /sys/kernel/security/lsm ||
-   ! find /usr/lib* -name libbpf.so.1 2>/dev/null | grep . || \
-   systemd-analyze compare-versions "$(uname -r)" lt 6.5 || \
-   systemd-analyze compare-versions "$(pkcheck --version | awk '{print $3}')" lt 124; then
-    echo "Skipping mountfsd/nsresourced tests"
-    exit 0
-fi
-
-at_exit() {
-    set +e
-
-    umount -R /tmp/unpriv/mount
-    rmdir /tmp/unpriv
-    rm -f /tmp/test-50-unpriv-privkey.key /tmp/test-50-unpriv-cert.crt /run/verity.d/test-50-unpriv-cert.crt
-    rm -f /var/tmp/unpriv.raw /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree
-    rm -f /tmp/unpriv.out /tmp/unpriv.out2 /tmp/unpriv.out3
-}
-
-trap at_exit EXIT
-
-systemctl start systemd-mountfsd.socket systemd-nsresourced.socket
-
-openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \
-            -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
-            -keyout /tmp/test-50-unpriv-privkey.key -out /tmp/test-50-unpriv-cert.crt
-
-systemd-dissect --mkdir --mount "$MINIMAL_IMAGE.raw" /tmp/unpriv/mount
-SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
-    systemd-repart -P \
-                   -s /tmp/unpriv/mount \
-                   --certificate=/tmp/test-50-unpriv-cert.crt \
-                   --private-key=/tmp/test-50-unpriv-privkey.key \
-                   /var/tmp/unpriv.raw
-systemd-dissect --rmdir --umount /tmp/unpriv/mount
-
-systemd-dissect --image-policy='root=unprotected:=absent+unused' /var/tmp/unpriv.raw
-systemd-dissect --image-policy='root=unprotected:=absent+unused' --mtree /var/tmp/unpriv.raw >/tmp/unpriv.raw.mtree
-
-# Run unpriv, should fail due to lack of privs
-(! runas testuser systemd-dissect /var/tmp/unpriv.raw)
-(! runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw)
-
-if (SYSTEMD_LOG_TARGET=console varlinkctl call \
-        /run/systemd/userdb/io.systemd.NamespaceResource \
-        io.systemd.NamespaceResource.AllocateUserRange \
-        '{"name":"test-supported","size":65536,"userNamespaceFileDescriptor":0}' 2>&1 || true) |
-            grep -q "io.systemd.NamespaceResource.UserNamespaceInterfaceNotSupported"; then
-    echo "User namespace interface not supported, skipping mountfsd/nsresourced tests"
-    exit 0
-fi
-
-# Install key in keychain
-cp /tmp/test-50-unpriv-cert.crt /run/verity.d
-
-# Now run unpriv again, should be OK now.
-runas testuser systemd-dissect /var/tmp/unpriv.raw
-runas testuser systemd-dissect --mtree /var/tmp/unpriv.raw >/tmp/unpriv2.raw.mtree
-
-# Check that unpriv and priv run yielded same results
-cmp /tmp/unpriv.raw.mtree /tmp/unpriv2.raw.mtree
-
-# Make sure nspawn works unpriv, too (for now do not nest)
-if ! systemd-detect-virt -c; then
-    systemd-nspawn --pipe -i /var/tmp/unpriv.raw --read-only echo thisisatest > /tmp/unpriv.out
-    echo thisisatest | cmp /tmp/unpriv.out -
-
-    # The unpriv user has no rights to lock the image or write to it. Let's
-    # turn off both for this test, so that we don't have to copy the image
-    # around.
-    systemd-run -M testuser@ --user --pipe \
-                -p Environment=SYSTEMD_NSPAWN_LOCK=0 \
-                -p Delegate=1 \
-                -p DelegateSubgroup=supervisor \
-                -p Environment=SYSTEMD_LOG_LEVEL=debug \
-                --wait -- \
-                systemd-nspawn --keep-unit -i /var/tmp/unpriv.raw --read-only --pipe echo thisisatest >/tmp/unpriv.out2
-    echo thisisatest | cmp /tmp/unpriv.out2 -
-fi
-
-systemd-run -M testuser@ --user --pipe -p RootImage=/var/tmp/unpriv.raw -p PrivateUsers=1 --wait echo thisisatest >/tmp/unpriv.out3
-echo thisisatest | cmp /tmp/unpriv.out3 -
diff --git a/test/units/testsuite-50.service b/test/units/testsuite-50.service
deleted file mode 100644 (file)
index bcafe6e..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-50-DISSECT
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh
deleted file mode 100755 (executable)
index d014e82..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Setup shared stuff & run all subtests
-
-at_exit() {
-    set +e
-
-    if [[ -z "${IMAGE_DIR:-}" ]]; then
-        return
-    fi
-
-    while read -r dir; do
-        if mountpoint -q "$dir"; then
-            umount -Rv "$dir"
-        fi
-    done < <(find "${IMAGE_DIR}" -mindepth 1 -maxdepth 1 -type d)
-
-    rm -rf "$IMAGE_DIR"
-}
-
-trap at_exit EXIT
-
-: "Setup base images"
-
-export SYSTEMD_LOG_LEVEL=debug
-export ARCHITECTURE
-export IMAGE_DIR
-export MACHINE
-export MINIMAL_IMAGE
-export MINIMAL_IMAGE_ROOTHASH
-export OPENSSL_CONFIG
-export OS_RELEASE
-export ROOT_GUID
-export SIGNATURE_GUID
-export VERITY_GUID
-
-machine="$(uname -m)"
-if [[ "$machine" == "x86_64" ]]; then
-    ROOT_GUID=4f68bce3-e8cd-4db1-96e7-fbcaf984b709
-    VERITY_GUID=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5
-    SIGNATURE_GUID=41092b05-9fc8-4523-994f-2def0408b176
-    ARCHITECTURE="x86-64"
-elif [[ "$machine" =~ ^(i386|i686|x86)$ ]]; then
-    ROOT_GUID=44479540-f297-41b2-9af7-d131d5f0458a
-    VERITY_GUID=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
-    SIGNATURE_GUID=5996fc05-109c-48de-808b-23fa0830b676
-    ARCHITECTURE="x86"
-elif [[ "$machine" =~ ^(aarch64|aarch64_be|armv8b|armv8l)$ ]]; then
-    ROOT_GUID=b921b045-1df0-41c3-af44-4c6f280d3fae
-    VERITY_GUID=df3300ce-d69f-4c92-978c-9bfb0f38d820
-    SIGNATURE_GUID=6db69de6-29f4-4758-a7a5-962190f00ce3
-    ARCHITECTURE="arm64"
-elif [[ "$machine" == "arm" ]]; then
-    ROOT_GUID=69dad710-2ce4-4e3c-b16c-21a1d49abed3
-    VERITY_GUID=7386cdf2-203c-47a9-a498-f2ecce45a2d6
-    SIGNATURE_GUID=42b0455f-eb11-491d-98d3-56145ba9d037
-    ARCHITECTURE="arm"
-elif [[ "$machine" == "ia64" ]]; then
-    ROOT_GUID=993d8d3d-f80e-4225-855a-9daf8ed7ea97
-    VERITY_GUID=86ed10d5-b607-45bb-8957-d350f23d0571
-    SIGNATURE_GUID=e98b36ee-32ba-4882-9b12-0ce14655f46a
-    ARCHITECTURE="ia64"
-elif [[ "$machine" == "loongarch64" ]]; then
-    ROOT_GUID=77055800-792c-4f94-b39a-98c91b762bb6
-    VERITY_GUID=f3393b22-e9af-4613-a948-9d3bfbd0c535
-    SIGNATURE_GUID=5afb67eb-ecc8-4f85-ae8e-ac1e7c50e7d0
-    ARCHITECTURE="loongarch64"
-elif [[ "$machine" == "s390x" ]]; then
-    ROOT_GUID=5eead9a9-fe09-4a1e-a1d7-520d00531306
-    VERITY_GUID=b325bfbe-c7be-4ab8-8357-139e652d2f6b
-    SIGNATURE_GUID=c80187a5-73a3-491a-901a-017c3fa953e9
-    ARCHITECTURE="s390x"
-elif [[ "$machine" == "ppc64le" ]]; then
-    ROOT_GUID=c31c45e6-3f39-412e-80fb-4809c4980599
-    VERITY_GUID=906bd944-4589-4aae-a4e4-dd983917446a
-    SIGNATURE_GUID=d4a236e7-e873-4c07-bf1d-bf6cf7f1c3c6
-    ARCHITECTURE="ppc64-le"
-elif [[ "$machine" == "riscv64" ]]; then
-    ROOT_GUID=72ec70a6-cf74-40e6-bd49-4bda08e8f224
-    VERITY_GUID=b6ed5582-440b-4209-b8da-5ff7c419ea3d
-    SIGNATURE_GUID=efe0f087-ea8d-4469-821a-4c2a96a8386a
-    ARCHITECTURE="riscv64"
-elif [[ "$machine" == "riscv32" ]]; then
-    ROOT_GUID=60d5a7fe-8e7d-435c-b714-3dd8162144e1
-    VERITY_GUID=ae0253be-1167-4007-ac68-43926c14c5de
-    SIGNATURE_GUID=3a112a75-8729-4380-b4cf-764d79934448
-    ARCHITECTURE="riscv32"
-else
-    echo "Unexpected uname -m: $machine in testsuite-50.sh, please fix me"
-    exit 1
-fi
-
-udevadm control --log-level=debug
-
-IMAGE_DIR="$(mktemp -d --tmpdir="" TEST-50-IMAGES.XXX)"
-cp -v /usr/share/minimal* "$IMAGE_DIR/"
-MINIMAL_IMAGE="$IMAGE_DIR/minimal_0"
-MINIMAL_IMAGE_ROOTHASH="$(<"$MINIMAL_IMAGE.roothash")"
-
-install_extension_images
-
-OS_RELEASE="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)"
-
-if systemctl --version | grep -q -- +OPENSSL ; then
-    # The openssl binary is installed conditionally. If we have OpenSSL support enabled and openssl is
-    # missing, fail early with a proper error message.
-    if ! command -v openssl &>/dev/null; then
-        echo "openssl binary is missing" >/failed
-        exit 1
-    fi
-
-    OPENSSL_CONFIG="$(mktemp)"
-    # Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
-    cat >"${OPENSSL_CONFIG:?}" <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = DE
-ST = Test State
-L = Test Locality
-O = Org Name
-OU = Org Unit Name
-CN = Common Name
-emailAddress = test@email.com
-EOF
-fi
-
-# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2
-#
-# du rounds up to block size, which is more helpful for partitioning
-root_size="$(du --apparent-size -k "$MINIMAL_IMAGE.raw" | cut -f1)"
-verity_size="$(du --apparent-size -k "$MINIMAL_IMAGE.verity" | cut -f1)"
-signature_size=4
-# 4MB seems to be the minimum size blkid will accept, below that probing fails
-dd if=/dev/zero of="$MINIMAL_IMAGE.gpt" bs=512 count=$((8192+root_size*2+verity_size*2+signature_size*2))
-# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB
-# so do some basic rounding up if the minimal image is more than 1 MB
-if [[ "$root_size" -ge 1024 ]]; then
-    root_size="$((root_size/1024 + 1))MiB"
-else
-    root_size="${root_size}KiB"
-fi
-verity_size="$((verity_size * 2))KiB"
-signature_size="$((signature_size * 2))KiB"
-
-if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
-    # Create key pair
-    openssl req -config "$OPENSSL_CONFIG" -new -x509 -newkey rsa:1024 \
-                -keyout "$MINIMAL_IMAGE.key" -out "$MINIMAL_IMAGE.crt" -days 365 -nodes
-    # Sign Verity root hash with it
-    openssl smime -sign -nocerts -noattr -binary \
-                  -in "$MINIMAL_IMAGE.roothash" \
-                  -inkey "$MINIMAL_IMAGE.key" \
-                  -signer "$MINIMAL_IMAGE.crt" \
-                  -outform der \
-                  -out "$MINIMAL_IMAGE.roothash.p7s"
-    # Generate signature partition JSON data
-    echo '{"rootHash":"'"$MINIMAL_IMAGE_ROOTHASH"'","signature":"'"$(base64 -w 0 <"$MINIMAL_IMAGE.roothash.p7s")"'"}' >"$MINIMAL_IMAGE.verity-sig"
-    # Pad it
-    truncate -s "$signature_size" "$MINIMAL_IMAGE.verity-sig"
-    # Register certificate in the (userspace) verity key ring
-    mkdir -p /run/verity.d
-    ln -s "$MINIMAL_IMAGE.crt" /run/verity.d/ok.crt
-fi
-
-# Construct a UUID from hash
-# input:  11111111222233334444555566667777
-# output: 11111111-2222-3333-4444-555566667777
-uuid="$(head -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
-echo -e "label: gpt\nsize=$root_size, type=$ROOT_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt"
-uuid="$(tail -c 32 "$MINIMAL_IMAGE.roothash" | sed -r 's/(.{8})(.{4})(.{4})(.{4})(.+)/\1-\2-\3-\4-\5/')"
-echo -e "size=$verity_size, type=$VERITY_GUID, uuid=$uuid" | sfdisk "$MINIMAL_IMAGE.gpt" --append
-if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
-    echo -e "size=$signature_size, type=$SIGNATURE_GUID" | sfdisk "$MINIMAL_IMAGE.gpt" --append
-fi
-sfdisk --part-label "$MINIMAL_IMAGE.gpt" 1 "Root Partition"
-sfdisk --part-label "$MINIMAL_IMAGE.gpt" 2 "Verity Partition"
-if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
-    sfdisk --part-label "$MINIMAL_IMAGE.gpt" 3 "Signature Partition"
-fi
-loop="$(losetup --show -P -f "$MINIMAL_IMAGE.gpt")"
-partitions=(
-    "${loop:?}p1"
-    "${loop:?}p2"
-)
-if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
-    partitions+=("${loop:?}p3")
-fi
-# The kernel sometimes(?) does not emit "add" uevent for loop block partition devices.
-# Let's not expect the devices to be initialized.
-udevadm wait --timeout 60 --settle --initialized=no "${partitions[@]}"
-udevadm lock --device="${loop}p1" dd if="$MINIMAL_IMAGE.raw" of="${loop}p1"
-udevadm lock --device="${loop}p2" dd if="$MINIMAL_IMAGE.verity" of="${loop}p2"
-if [[ -n "${OPENSSL_CONFIG:-}" ]]; then
-    udevadm lock --device="${loop}p3" dd if="$MINIMAL_IMAGE.verity-sig" of="${loop}p3"
-fi
-losetup -d "$loop"
-
-: "Run subtests"
-
-run_subtests
-
-touch /testok
diff --git a/test/units/testsuite-50.sysext.sh b/test/units/testsuite-50.sysext.sh
deleted file mode 100755 (executable)
index 3bc8899..0000000
+++ /dev/null
@@ -1,1012 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-FAKE_ROOTS_DIR="$(mktemp -d --tmpdir="" fake-roots-XXX)"
-FSTYPE=$(stat --file-system --format "%T" /usr)
-
-shopt -s nullglob
-
-# shellcheck disable=SC2317
-at_exit() {
-    set +ex
-
-    local target
-
-    # Note: `cat` here is used intentionally, so we iterate over our own copy of /proc/mounts. Otherwise
-    #       things get very confusing once we start unmounting things, due to changing file offsets.
-    # shellcheck disable=SC2002
-    cat /proc/mounts | while read -r _ target _ _ _ _; do
-        if [[ "$target" =~ ^"$FAKE_ROOTS_DIR" ]]; then
-            umount -Rv "$target"
-        fi
-    done
-
-    rm -rf "${FAKE_ROOTS_DIR}"
-}
-
-trap at_exit EXIT
-
-# Clears the trap command - it needs to be invoked for every test-case subshell
-# so prepending commands with prepend_trap inside the subshell won't preserve
-# the trap commands from outer shell.
-init_trap() {
-    trap - EXIT
-}
-
-prepend_trap() {
-    set +x
-
-    local command=${1}; shift
-    local previous_commands
-
-    previous_commands=$(trap -p EXIT)
-    if [[ -z $previous_commands ]]; then
-        previous_commands=':'
-    else
-        previous_commands=${previous_commands#'trap -- '}
-        previous_commands=${previous_commands%' EXIT'}
-        previous_commands=$(xargs <<<"$previous_commands")
-    fi
-
-    # shellcheck disable=SC2064 # We use double quotes on purpose here.
-    trap "${command}; ${previous_commands}" EXIT
-
-    set -x
-}
-
-prepare_root() {
-    local root=${1:-}
-    local hierarchy=${2:?}
-    local dir
-
-    if [[ -n $root ]] && [[ -d $root ]]; then
-        echo >&2 "Directory $root already exists, possible copy-paste error?"
-        exit 1
-    fi
-
-    local -a leftovers=( "$root/var/lib/extensions/"* "$root/var/lib/extensions.mutable/"* )
-    if [[ ${#leftovers[@]} -gt 0 ]]; then
-        echo >&2 "Leftovers remained, make sure to clean them up in the test case: ${leftovers[*]}"
-        exit 1
-    fi
-
-    for dir in "$hierarchy" "/usr/lib" "/var/lib/extensions/" "/var/lib/extensions.mutable"; do
-        mkdir -p "$root$dir"
-    done
-
-    if [[ -e $root/usr/lib/os-release ]]; then
-        mv "$root/usr/lib/os-release" "$root/usr/lib/os-release.orig"
-    fi
-
-    {
-        echo "ID=testtest"
-        echo "VERSION=1.2.3"
-    } >"$root/usr/lib/os-release"
-
-    prepend_trap "cleanup_os_release ${root@Q}"
-}
-
-cleanup_os_release() {
-    # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
-    local root=${1:-}
-
-    # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
-    rm -f "$root/usr/lib/os-release"
-    # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
-    if [[ -e $root/usr/lib/os-release.orig ]]; then
-        # shellcheck disable=SC2317 # It is not unreachable, used in a trap couple lines above.
-        mv "$root/usr/lib/os-release.orig" "$root/usr/lib/os-release"
-    fi
-}
-
-prepare_extension_image() {
-    local root=${1:-}
-    local hierarchy=${2:?}
-    local ext_dir ext_release name
-
-    name="test-extension"
-    ext_dir="$root/var/lib/extensions/$name"
-    ext_release="$ext_dir/usr/lib/extension-release.d/extension-release.$name"
-    mkdir -p "${ext_release%/*}"
-    echo "ID=_any" >"$ext_release"
-    mkdir -p "$ext_dir/$hierarchy"
-    touch "$ext_dir$hierarchy/preexisting-file-in-extension-image"
-
-    prepend_trap "rm -rf ${ext_dir@Q}"
-}
-
-prepare_extension_mutable_dir() {
-    local dir=${1:?}
-
-    mkdir -p "$dir"
-    touch "$dir/preexisting-file-in-extensions-mutable"
-    prepend_trap "rm -rf ${dir@Q}"
-}
-
-make_read_only() {
-    local root=${1:-}
-    local hierarchy=${2:?}
-
-    mount -o bind,ro "$root$hierarchy" "$root$hierarchy"
-    prepend_trap "umount ${root@Q}${hierarchy@Q}"
-}
-
-prepare_hierarchy() {
-    local root=${1:-}
-    local hierarchy=${2:?}
-    local file
-
-    file="$root$hierarchy/preexisting-file-in-hierarchy"
-    touch "$file"
-    prepend_trap "rm -f ${file@Q}"
-}
-
-prepare_read_only_hierarchy() {
-    local root=${1:-}
-    local hierarchy=${2:?}
-
-    prepare_hierarchy "$root" "$hierarchy"
-    make_read_only "$root" "$hierarchy"
-}
-
-move_existing_hierarchy_aside() {
-    local root=${1:-}
-    local hierarchy=${2:?}
-
-    if [[ -z $root ]] && [[ $hierarchy = /usr ]]; then
-        echo >&2 "Hell no, not moving /usr aside"
-        exit 1
-    fi
-
-    local path=$root$hierarchy
-
-    if [[ -e $path ]]; then
-        mv "$path" "$path.orig"
-        prepend_trap "mv ${path@Q}.orig ${path@Q}"
-    fi
-}
-
-# Extra arguments:
-#   -e: check for a preexisting file in extension
-#   -h: check for a preexisting file in hierarchy
-#   -u: check for a preexisting file in upperdir
-extension_verify() {
-    local root=${1:-}
-    local hierarchy=${2:?}
-    local message=${3:?}
-    shift 3
-    # Map each option to a pre-defined file name
-    local -A option_files_map=(
-        [e]="preexisting-file-in-extension-image"
-        [h]="preexisting-file-in-hierarchy"
-        [u]="preexisting-file-in-extensions-mutable"
-    )
-    local -A args=(
-        [e]=0
-        [h]=0
-        [u]=0
-    )
-    local file full_path opt option
-
-    while getopts "ehu" opt; do
-        case "$opt" in
-            e|h|u)
-                args["$opt"]=1
-                ;;
-            *)
-                echo >&2 "Unxexpected option: $opt"
-                exit 1
-        esac
-    done
-
-    for option in "${!option_files_map[@]}"; do
-        file=${option_files_map["$option"]}
-        full_path="$root$hierarchy/$file"
-
-        if [[ ${args["$option"]} -ne 0 ]]; then
-            if [[ ! -f $full_path ]]; then
-                ls -la "$root$hierarchy"
-                echo >&2 "Expected file '$file' to exist under $root$hierarchy $message"
-                exit 1
-            fi
-        else
-            if [[ -f $full_path ]]; then
-                ls -la "$root$hierarchy"
-                echo >&2 "Expected file '$file' to not exist under $root$hierarchy $message"
-                exit 1
-            fi
-        fi
-    done
-}
-
-extension_verify_after_merge() (
-    set +x
-
-    local root=${1:-}
-    local hierarchy=${2:?}
-    shift 2
-
-    extension_verify "$root" "$hierarchy" "after merge" "$@"
-)
-
-extension_verify_after_unmerge() (
-    set +x
-
-    local root=${1:-}
-    local hierarchy=${2:?}
-    shift 2
-
-    extension_verify "$root" "$hierarchy" "after unmerge" "$@"
-)
-
-run_systemd_sysext() {
-    local root=${1:-}
-    shift
-
-    local -a sysext_args
-    sysext_args=()
-
-    if [[ -n $root ]]; then
-        sysext_args+=( "--root=$root" )
-    fi
-    sysext_args+=( "$@" )
-    systemd-sysext "${sysext_args[@]}"
-}
-
-# General systemd-sysext tests
-
-run_sysext_tests() {
-    # The roots_dir variable may be empty - in such case all the tests will run
-    # on /, otherwise they will run on $roots_dir/<SEPARATE_DIR_FOR_TEST>.
-    local roots_dir=${1}; shift
-
-    # Each test runs in a subshell, so we can use traps for cleanups without
-    # clobbering toplevel traps, and we can do skips by invoking "exit 0".
-
-( init_trap
-: "No extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-read-only-hierarchy"}
-hierarchy=/opt
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-)
-
-
-( init_trap
-: "No extension data in /var/lib/extensions.mutable/…, mutable hierarchy, mutability disabled by default, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-mutable-hierarchy"}
-hierarchy=/opt
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_hierarchy "$fake_root" "$hierarchy"
-touch "$fake_root$hierarchy/should-succeed-on-mutable-fs"
-
-run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-touch "$fake_root$hierarchy/should-succeed-on-mutable-fs-again"
-)
-
-
-( init_trap
-: "No extension data in /var/lib/extensions.mutable/…, no hierarchy either, mutability disabled by default, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-missing-hierarchy"}
-hierarchy=/opt
-
-move_existing_hierarchy_aside "$fake_root" "$hierarchy"
-prepare_root "$fake_root" "$hierarchy"
-rmdir "$fake_root/$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-
-run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy"
-)
-
-
-( init_trap
-: "No extension data in /var/lib/extensions.mutable/…, empty hierarchy, mutability disabled by default, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/simple-read-only-with-empty-hierarchy"}
-hierarchy=/opt
-
-move_existing_hierarchy_aside "$fake_root" "$hierarchy"
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-make_read_only "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled by default, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-read-only-hierarchy-disabled"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-be-read-only")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-read-only-hierarchy"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-test -f "$extension_data_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, missing hierarchy, auto-mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-missing-hierarchy"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-move_existing_hierarchy_aside "$fake_root" "$hierarchy"
-prepare_root "$fake_root" "$hierarchy"
-rmdir "$fake_root/$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
-test -f "$extension_data_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy"
-test -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, empty hierarchy, auto-mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/simple-mutable-with-empty-hierarchy"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-move_existing_hierarchy_aside "$fake_root" "$hierarchy"
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-make_read_only "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -u
-test -f "$extension_data_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy"
-test -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… is a symlink to other dir, R/O hierarchy, auto-mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/mutable-symlink-with-read-only-hierarchy"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_real_dir="$fake_root/upperdir"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_real_dir"
-ln -sfTr "$extension_real_dir" "$extension_data_dir"
-prepend_trap "rm -f ${extension_data_dir@Q}"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-test -f "$extension_data_dir/now-is-mutable"
-test -f "$extension_real_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test -f "$extension_data_dir/now-is-mutable"
-test -f "$extension_real_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, mutable hierarchy, auto-mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/mutable-self-upper"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_real_dir="$fake_root$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_real_dir"
-ln -sfTr "$extension_real_dir" "$extension_data_dir"
-prepend_trap "rm -f ${extension_data_dir@Q}"
-touch "$fake_root$hierarchy/preexisting-file-in-hierarchy"
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-test -f "$extension_data_dir/now-is-mutable"
-test -f "$extension_real_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h -u
-test -f "$extension_data_dir/now-is-mutable"
-test -f "$extension_real_dir/now-is-mutable"
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… is a symlink to the hierarchy itself, R/O hierarchy, auto-mutability, expected fail"
-fake_root=${roots_dir:+"$roots_dir/failure-self-upper-ro"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_real_dir="$fake_root$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_real_dir"
-ln -sfTr "$extension_real_dir" "$extension_data_dir"
-prepend_trap "rm -f ${extension_data_dir@Q}"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-
-(! run_systemd_sysext "$fake_root" --mutable=auto merge)
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… is a dangling symlink, auto-mutability, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/read-only-mutable-dangling-symlink"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-ln -sfTr "/should/not/exist/" "$extension_data_dir"
-prepend_trap "rm -f ${extension_data_dir@Q}"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… exists but ignored, mutability disabled explicitly, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/disabled"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=no merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… exists but is imported instead, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/imported"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=import merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… does not exist, mutability enabled, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/enabled"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-test ! -d "$extension_data_dir"
-
-run_systemd_sysext "$fake_root" --mutable=yes merge
-# systemd-sysext with --mutable=yes creates extensions.mutable directory for
-# the hierarchy, so delete it after the test
-prepend_trap "rm -rf ${extension_data_dir@Q}"
-# systemd-sysext with --mutable=yes creates extensions.mutable directory also
-# for the /usr hierarchy, because the image needs to have
-# /usr/lib/extension-release.d/extension-release.<NAME> file - this causes the
-# /usr hierarchy to also become mutable
-prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
-test -d "$extension_data_dir"
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-test -f "$extension_data_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… does not exist, auto-mutability, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/simple-read-only-explicit"}
-hierarchy=/opt
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=auto merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… does not exist, mutability enabled through env var, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/enabled-env-var"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-test ! -d "$extension_data_dir"
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" merge
-# systemd-sysext with --mutable=yes creates extensions.mutable directory for
-# the hierarchy, so delete it after the test
-prepend_trap "rm -rf ${extension_data_dir@Q}"
-# systemd-sysext with --mutable=yes creates extensions.mutable directory also
-# for the /usr hierarchy, because the image needs to have
-# /usr/lib/extension-release.d/extension-release.<NAME> file - this causes the
-# /usr hierarchy to also become mutable
-prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
-test -d "$extension_data_dir"
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-test -f "$extension_data_dir/now-is-mutable"
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… does not exist, auto-mutability enabled through env var, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/read-only-auto-env-var"}
-hierarchy=/opt
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" --mutable=auto merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, auto-mutability enabled through env var, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/auto-mutable-env-var"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-test -f "$extension_data_dir/now-is-mutable"
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=auto run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability disabled through env var, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/env-var-disabled"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=no run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-be-read-only")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=no run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "/var/lib/extensions.mutable/… exists but is imported through env var, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/imported-env-var"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=import run_systemd_sysext "$fake_root" merge
-(! touch "$fake_root$hierarchy/should-still-fail-on-read-only-fs")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=import run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, mutability enabled through env var but overridden via CLI option, read-only merged"
-fake_root=${roots_dir:+"$roots_dir/env-var-overridden"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" --mutable=no merge
-(! touch "$fake_root$hierarchy/should-be-read-only")
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=yes run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/ephemeral"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=ephemeral merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-test ! -f "$extension_data_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test ! -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral mutability through env var, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/ephemeral-env-var"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral run_systemd_sysext "$fake_root" merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
-test ! -f "$extension_data_dir/now-is-mutable"
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test ! -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/ephemeral-import"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-run_systemd_sysext "$fake_root" --mutable=ephemeral-import merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-test ! -f "$extension_data_dir/now-is-mutable"
-
-run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test ! -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data in /var/lib/extensions.mutable/…, R/O hierarchy, ephemeral import mutability through env var, mutable merged"
-fake_root=${roots_dir:+"$roots_dir/ephemeral-import-env-var"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_read_only_hierarchy "$fake_root" "$hierarchy"
-(! touch "$fake_root$hierarchy/should-fail-on-read-only-fs")
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import run_systemd_sysext "$fake_root" merge
-touch "$fake_root$hierarchy/now-is-mutable"
-extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
-test ! -f "$extension_data_dir/now-is-mutable"
-
-SYSTEMD_SYSEXT_MUTABLE_MODE=ephemeral-import run_systemd_sysext "$fake_root" unmerge
-extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
-test ! -f "$extension_data_dir/now-is-mutable"
-test ! -f "$fake_root$hierarchy/now-is-mutable"
-)
-
-
-( init_trap
-: "Extension data pointing to mutable hierarchy, ephemeral import mutability, expected fail"
-fake_root=${roots_dir:+"$roots_dir/ephemeral-import-self"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_real_dir="$fake_root$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_real_dir"
-ln -sfTr "$extension_real_dir" "$extension_data_dir"
-prepend_trap "rm -f ${extension_data_dir@Q}"
-prepare_hierarchy "$fake_root" "$hierarchy"
-touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
-
-(! run_systemd_sysext "$fake_root" --mutable=ephemeral-import merge)
-)
-
-
-( init_trap
-: "Extension data pointing to mutable hierarchy, import mutability, expected fail"
-fake_root=${roots_dir:+"$roots_dir/import-self"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-extension_real_dir="$fake_root$hierarchy"
-
-[[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_real_dir"
-ln -sfTr "$extension_real_dir" "$extension_data_dir"
-prepend_trap "rm -f ${extension_data_dir@Q}"
-prepare_hierarchy "$fake_root" "$hierarchy"
-touch "$fake_root$hierarchy/should-succeed-on-read-only-fs"
-
-(! run_systemd_sysext "$fake_root" --mutable=import merge)
-)
-
-
-for mutable_mode in no yes ephemeral; do
-    ( init_trap
-    : "Check if merging the hierarchy does not change its permissions, checking with --mutable=${mutable_mode}"
-
-    fake_root=${roots_dir:+"$roots_dir/perm-checks-mutable-$mutable_mode"}
-    hierarchy=/opt
-    extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-    [[ "$FSTYPE" == "fuseblk" ]] && exit 0
-
-    prepare_root "$fake_root" "$hierarchy"
-    prepare_extension_image "$fake_root" "$hierarchy"
-    prepare_extension_mutable_dir "$extension_data_dir"
-    prepare_read_only_hierarchy "${fake_root}" "${hierarchy}"
-
-    full_path="$fake_root$hierarchy"
-    permissions_before_merge=$(stat --format=%A "$full_path")
-
-    run_systemd_sysext "$fake_root" "--mutable=$mutable_mode" merge
-    if [[ $mutable_mode = yes ]]; then
-        # systemd-sysext with --mutable=yes creates extensions.mutable
-        # directory also for the /usr hierarchy, because the image needs to
-        # have /usr/lib/extension-release.d/extension-release.<NAME> file -
-        # this causes the /usr hierarchy to also become mutable
-        extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr"
-        prepend_trap "rm -rf ${extension_data_dir_usr@Q}"
-    fi
-
-    permissions_after_merge=$(stat --format=%A "$full_path")
-
-    run_systemd_sysext "$fake_root" unmerge
-
-    permissions_after_unmerge=$(stat --format=%A "$full_path")
-
-    if [[ "$permissions_before_merge" != "$permissions_after_merge" ]]; then
-        echo >&2 "Broken hierarchy permissions after merging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_merge@Q}"
-        exit 1
-    fi
-
-    if [[ "$permissions_before_merge" != "$permissions_after_unmerge" ]]; then
-        echo >&2 "Broken hierarchy permissions after unmerging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_unmerge@Q}"
-        exit 1
-    fi
-    )
-done
-
-
-( init_trap
-: "Check if merging fails in case of invalid mutable directory permissions"
-
-fake_root=${roots_dir:+"$roots_dir/mutable-directory-with-invalid-permissions"}
-hierarchy=/opt
-extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
-
-prepare_root "$fake_root" "$hierarchy"
-prepare_extension_image "$fake_root" "$hierarchy"
-prepare_extension_mutable_dir "$extension_data_dir"
-prepare_hierarchy "$fake_root" "$hierarchy"
-
-old_mode=$(stat --format '%#a' "$fake_root$hierarchy")
-chmod 0755 "$fake_root$hierarchy"
-prepend_trap "chmod ${old_mode@Q} ${fake_root@Q}${hierarchy@Q}"
-chmod 0700 "$extension_data_dir"
-
-(! run_systemd_sysext "$fake_root" --mutable=yes merge)
-)
-
-} # End of run_sysext_tests
-
-
-# For preparing /, we need mutable /usr/. If it is read only, skip running the
-# sysext tests on /.
-if [[ -w /usr ]]; then
-    run_sysext_tests ''
-fi
-run_sysext_tests "$FAKE_ROOTS_DIR"
-
-
-exit 0
diff --git a/test/units/testsuite-52.service b/test/units/testsuite-52.service
deleted file mode 100644 (file)
index b9f2909..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-52.sh b/test/units/testsuite-52.sh
deleted file mode 100755 (executable)
index 16ff507..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-systemd-analyze log-level debug
-
-systemctl enable test-honor-first-shutdown.service
-systemctl start test-honor-first-shutdown.service
-
-touch /testok
diff --git a/test/units/testsuite-53.service b/test/units/testsuite-53.service
deleted file mode 100644 (file)
index cf3adbb..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-53-ISSUE-16347
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-53.sh b/test/units/testsuite-53.sh
deleted file mode 100755 (executable)
index 84cd661..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-: >/failed
-
-# Reset host date to current time, 3 days in the past.
-date -s "-3 days"
-
-# Run a timer for every 15 minutes.
-systemd-run --unit test-timer --on-calendar "*:0/15:0" true
-
-next_elapsed=$(systemctl show test-timer.timer -p NextElapseUSecRealtime --value)
-next_elapsed=$(date -d "${next_elapsed}" +%s)
-now=$(date +%s)
-time_delta=$((next_elapsed - now))
-
-# Check that the timer will elapse in less than 20 minutes.
-((0 < time_delta && time_delta < 1200)) || {
-    echo 'Timer elapse outside of the expected 20 minute window.'
-    echo "  next_elapsed=${next_elapsed}"
-    echo "  now=${now}"
-    echo "  time_delta=${time_delta}"
-    echo ''
-} >>/failed
-
-if test ! -s /failed ; then
-    rm -f /failed
-    touch /testok
-fi
diff --git a/test/units/testsuite-54.service b/test/units/testsuite-54.service
deleted file mode 100644 (file)
index ba8cdad..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TESTSUITE-54-CREDS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-54.sh b/test/units/testsuite-54.sh
deleted file mode 100755 (executable)
index fe410d5..0000000
+++ /dev/null
@@ -1,402 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-systemd-analyze log-level debug
-
-run_with_cred_compare() (
-    local cred="${1:?}"
-    local exp="${2?}"
-    local log_file
-    shift 2
-
-    log_file="$(mktemp)"
-    # shellcheck disable=SC2064
-    trap "rm -f '$log_file'" RETURN
-
-    set -o pipefail
-    systemd-run -p SetCredential="$cred" --wait --pipe -- systemd-creds "$@" | tee "$log_file"
-    diff "$log_file" <(echo -ne "$exp")
-)
-
-# Sanity checks
-#
-# Create a dummy "full" disk (similar to /dev/full) to check out-of-space
-# scenarios
-mkdir /tmp/full
-mount -t tmpfs -o size=1,nr_inodes=1 tmpfs /tmp/full
-
-# verb: setup
-# Run this first, otherwise any encrypted credentials wouldn't be decryptable
-# as we regenerate the host key
-rm -fv /var/lib/systemd/credential.secret
-systemd-creds setup
-test -e /var/lib/systemd/credential.secret
-rm -fv /var/lib/systemd/credential.secret
-
-# Prepare a couple of dummy credentials for the cat/list verbs
-CRED_DIR="$(mktemp -d)"
-ENC_CRED_DIR="$(mktemp -d)"
-echo foo >"$CRED_DIR/secure-or-weak"
-echo foo >"$CRED_DIR/insecure"
-echo foo | systemd-creds --name="encrypted" encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted"
-echo foo | systemd-creds encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted-unnamed"
-chmod -R 0400 "$CRED_DIR" "$ENC_CRED_DIR"
-chmod -R 0444 "$CRED_DIR/insecure"
-mkdir /tmp/empty/
-
-systemd-creds --system
-systemd-creds --no-pager --help
-systemd-creds --version
-systemd-creds has-tpm2 || :
-systemd-creds has-tpm2 -q || :
-
-# verb: list
-systemd-creds list --system
-ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --no-legend
-ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=pretty | jq
-ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=short | jq
-ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=off
-ENCRYPTED_CREDENTIALS_DIRECTORY="/tmp/empty/" CREDENTIALS_DIRECTORY="/tmp/empty/" systemd-creds list
-
-# verb: cat
-for cred in secure-or-weak insecure encrypted encrypted-unnamed; do
-    ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds cat "$cred"
-done
-run_with_cred_compare "mycred:" "" cat mycred
-run_with_cred_compare "mycred:\n" "\n" cat mycred
-run_with_cred_compare "mycred:foo" "foo" cat mycred
-run_with_cred_compare "mycred:foo" "foofoofoo" cat mycred mycred mycred
-# Note: --newline= does nothing when stdout is not a tty, which is the case here
-run_with_cred_compare "mycred:foo" "foo" --newline=yes cat mycred
-run_with_cred_compare "mycred:foo" "foo" --newline=no cat mycred
-run_with_cred_compare "mycred:foo" "foo" --newline=auto cat mycred
-run_with_cred_compare "mycred:foo" "foo" --transcode=no cat mycred
-run_with_cred_compare "mycred:foo" "foo" --transcode=0 cat mycred
-run_with_cred_compare "mycred:foo" "foo" --transcode=false cat mycred
-run_with_cred_compare "mycred:foo" "Zm9v" --transcode=base64 cat mycred
-run_with_cred_compare "mycred:Zm9v" "foo" --transcode=unbase64 cat mycred
-run_with_cred_compare "mycred:Zm9v" "foofoofoo" --transcode=unbase64 cat mycred mycred mycred
-run_with_cred_compare "mycred:Zm9vCg==" "foo\n" --transcode=unbase64 cat mycred
-run_with_cred_compare "mycred:hello world" "68656c6c6f20776f726c64" --transcode=hex cat mycred
-run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello world" --transcode=unhex cat mycred
-run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello worldhello world" --transcode=unhex cat mycred mycred
-run_with_cred_compare "mycred:68656c6c6f0a776f726c64" "hello\nworld" --transcode=unhex cat mycred
-run_with_cred_compare 'mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' '{"foo":"bar","baz":[3,4]}\n' --json=short cat mycred
-systemd-run -p SetCredential='mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' --wait --pipe -- systemd-creds --json=pretty cat mycred | jq
-
-# verb: encrypt/decrypt
-echo "According to all known laws of aviation..." >/tmp/cred.orig
-systemd-creds --with-key=host encrypt /tmp/cred.orig /tmp/cred.enc
-systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec
-diff /tmp/cred.orig /tmp/cred.dec
-rm -f /tmp/cred.{enc,dec}
-# --pretty
-cred_name="fo'''o''bar"
-cred_option="$(systemd-creds --pretty --name="$cred_name" encrypt /tmp/cred.orig -)"
-mkdir -p /run/systemd/system
-cat >/run/systemd/system/test-54-pretty-cred.service <<EOF
-[Service]
-Type=oneshot
-${cred_option:?}
-ExecStart=bash -c "diff <(systemd-creds cat \"$cred_name\") /tmp/cred.orig"
-EOF
-systemctl daemon-reload
-systemctl start test-54-pretty-cred
-rm /run/systemd/system/test-54-pretty-cred.service
-# Credential validation: name
-systemd-creds --name="foo" -H encrypt /tmp/cred.orig /tmp/cred.enc
-(! systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec)
-(! systemd-creds --name="bar" decrypt /tmp/cred.enc /tmp/cred.dec)
-systemd-creds --name="" decrypt /tmp/cred.enc /tmp/cred.dec
-diff /tmp/cred.orig /tmp/cred.dec
-rm -f /tmp/cred.dec
-systemd-creds --name="foo" decrypt /tmp/cred.enc /tmp/cred.dec
-diff /tmp/cred.orig /tmp/cred.dec
-rm -f /tmp/cred.{enc,dec}
-# Credential validation: time
-systemd-creds --not-after="+1d" encrypt /tmp/cred.orig /tmp/cred.enc
-(! systemd-creds --timestamp="+2d" decrypt /tmp/cred.enc /tmp/cred.dec)
-systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec
-diff /tmp/cred.orig /tmp/cred.dec
-rm -f /tmp/cred.{enc,dec}
-
-(! unshare -m bash -exc "mount -t tmpfs tmpfs /run/credentials && systemd-creds list")
-(! unshare -m bash -exc "mount -t tmpfs tmpfs /run/credentials && systemd-creds --system list")
-(! CREDENTIALS_DIRECTORY="" systemd-creds list)
-(! systemd-creds --system --foo)
-(! systemd-creds --system -@)
-(! systemd-creds --system --json=)
-(! systemd-creds --system --json="")
-(! systemd-creds --system --json=foo)
-(! systemd-creds --system cat)
-(! systemd-creds --system cat "")
-(! systemd-creds --system cat this-should-not-exist)
-(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode= cat mycred)
-(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode="" cat mycred)
-(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --transcode=foo cat mycred)
-(! systemd-run -p SetCredential=mycred:foo --wait --pipe -- systemd-creds --newline=foo cat mycred)
-(! systemd-run -p SetCredential=mycred:notbase64 --wait --pipe -- systemd-creds --transcode=unbase64 cat mycred)
-(! systemd-run -p SetCredential=mycred:nothex --wait --pipe -- systemd-creds --transcode=unhex cat mycred)
-(! systemd-run -p SetCredential=mycred:a --wait --pipe -- systemd-creds --transcode=unhex cat mycred)
-(! systemd-run -p SetCredential=mycred:notjson --wait --pipe -- systemd-creds --json=short cat mycred)
-(! systemd-run -p SetCredential=mycred:notjson --wait --pipe -- systemd-creds --json=pretty cat mycred)
-(! systemd-creds encrypt /foo/bar/baz -)
-(! systemd-creds decrypt /foo/bar/baz -)
-(! systemd-creds decrypt / -)
-(! systemd-creds encrypt / -)
-(! echo foo | systemd-creds --with-key=foo encrypt - -)
-(! echo {0..20} | systemd-creds decrypt - -)
-(! systemd-creds --not-after= encrypt /tmp/cred.orig /tmp/cred.enc)
-(! systemd-creds --not-after="" encrypt /tmp/cred.orig /tmp/cred.enc)
-(! systemd-creds --not-after="-1d" encrypt /tmp/cred.orig /tmp/cred.enc)
-(! systemd-creds --timestamp= encrypt /tmp/cred.orig /tmp/cred.enc)
-(! systemd-creds --timestamp="" encrypt /tmp/cred.orig /tmp/cred.enc)
-(! dd if=/dev/zero count=2M | systemd-creds --with-key=tpm2-absent encrypt - /dev/null)
-(! dd if=/dev/zero count=2M | systemd-creds --with-key=tpm2-absent decrypt - /dev/null)
-(! echo foo | systemd-creds encrypt - /tmp/full/foo)
-(! echo foo | systemd-creds encrypt - - | systemd-creds decrypt - /tmp/full/foo)
-
-# Verify that the creds are properly loaded and we can read them from the service's unpriv user
-systemd-run -p LoadCredential=passwd:/etc/passwd \
-    -p LoadCredential=shadow:/etc/shadow \
-    -p SetCredential=dog:wuff \
-    -p DynamicUser=1 \
-    --unit=test-54-unpriv.service \
-    --wait \
-    --pipe \
-    cat '${CREDENTIALS_DIRECTORY}/passwd' '${CREDENTIALS_DIRECTORY}/shadow' '${CREDENTIALS_DIRECTORY}/dog' \
-    >/tmp/ts54-concat
-(cat /etc/passwd /etc/shadow && echo -n wuff) | cmp /tmp/ts54-concat
-rm /tmp/ts54-concat
-
-# Test that SetCredential= acts as fallback for LoadCredential=
-echo piff >/tmp/ts54-fallback
-[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "piff" ]
-rm /tmp/ts54-fallback
-[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "poff" ]
-
-if systemd-detect-virt -q -c ; then
-    expected_credential=mynspawncredential
-    expected_value=strangevalue
-elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then
-    # Verify that passing creds through kernel cmdline works
-    [ "$(systemd-creds --system cat kernelcmdlinecred)" = "uff" ]
-    [ "$(systemd-creds --system cat waldi)" = "woooofffwufffwuff" ]
-
-    # And that it also works via SMBIOS
-    [ "$(systemd-creds --system cat smbioscredential)" = "magicdata" ]
-    [ "$(systemd-creds --system cat binarysmbioscredential)" = "magicbinarydata" ]
-
-    # If we aren't run in nspawn, we are run in qemu
-    systemd-detect-virt -q -v
-    expected_credential=myqemucredential
-    expected_value=othervalue
-
-    # Verify that writing a sysctl via the kernel cmdline worked
-    [ "$(cat /proc/sys/kernel/domainname)" = "sysctltest" ]
-
-    # Verify that creating a user via sysusers via the kernel cmdline worked
-    grep -q ^credtestuser: /etc/passwd
-
-    # Verify that writing a file via tmpfiles worked
-    [ "$(cat /tmp/sourcedfromcredential)" = "tmpfilessecret" ]
-    [ "$(cat /etc/motd.d/50-provision.conf)" = "hello" ]
-    [ "$(cat /etc/issue.d/50-provision.conf)" = "welcome" ]
-
-    # Verify that adding a unit and drop-in via credentials worked
-    systemctl start my-service
-    test -f /tmp/unit-cred
-    test -f /tmp/unit-dropin
-    test -f /tmp/unit-named-dropin
-else
-    echo "qemu_fw_cfg support missing in kernel. Sniff!"
-    expected_credential=""
-    expected_value=""
-fi
-
-if [ "$expected_credential" != "" ] ; then
-    # If this test is run in nspawn a credential should have been passed to us. See test/TEST-54-CREDS/test.sh
-    [ "$(systemd-creds --system cat "$expected_credential")" = "$expected_value" ]
-
-    # Test that propagation from system credential to service credential works
-    [ "$(systemd-run -p LoadCredential="$expected_credential" --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ]
-
-    # Check it also works, if we rename it while propagating it
-    [ "$(systemd-run -p LoadCredential=miau:"$expected_credential" --pipe --wait systemd-creds cat miau)" = "$expected_value" ]
-
-    # Combine it with a fallback (which should have no effect, given the cred should be passed down)
-    [ "$(systemd-run -p LoadCredential="$expected_credential" -p SetCredential="$expected_credential":zzz --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ]
-
-    # This should succeed
-    systemd-run -p AssertCredential="$expected_credential" -p Type=oneshot true
-
-    # And this should fail
-    (! systemd-run -p AssertCredential="undefinedcredential" -p Type=oneshot true)
-fi
-
-# Verify that the creds are immutable
-(! systemd-run -p LoadCredential=passwd:/etc/passwd \
-    -p DynamicUser=1 \
-    --unit=test-54-immutable-touch.service \
-    --wait \
-    touch '${CREDENTIALS_DIRECTORY}/passwd')
-(! systemd-run -p LoadCredential=passwd:/etc/passwd \
-    -p DynamicUser=1 \
-    --unit=test-54-immutable-rm.service \
-    --wait \
-    rm '${CREDENTIALS_DIRECTORY}/passwd')
-
-# Check directory-based loading
-mkdir -p /tmp/ts54-creds/sub
-echo -n a >/tmp/ts54-creds/foo
-echo -n b >/tmp/ts54-creds/bar
-echo -n c >/tmp/ts54-creds/baz
-echo -n d >/tmp/ts54-creds/sub/qux
-systemd-run -p LoadCredential=cred:/tmp/ts54-creds \
-    -p DynamicUser=1 \
-    --unit=test-54-dir.service \
-    --wait \
-    --pipe \
-    cat '${CREDENTIALS_DIRECTORY}/cred_foo' \
-        '${CREDENTIALS_DIRECTORY}/cred_bar' \
-        '${CREDENTIALS_DIRECTORY}/cred_baz' \
-        '${CREDENTIALS_DIRECTORY}/cred_sub_qux' >/tmp/ts54-concat
-cmp /tmp/ts54-concat <(echo -n abcd)
-rm /tmp/ts54-concat
-rm -rf /tmp/ts54-creds
-
-# Check that globs work as expected
-mkdir -p /run/credstore
-echo -n a >/run/credstore/test.creds.first
-echo -n b >/run/credstore/test.creds.second
-mkdir -p /etc/credstore
-echo -n c >/etc/credstore/test.creds.third
-systemd-run -p "ImportCredential=test.creds.*" \
-            --unit=test-54-ImportCredential.service \
-            -p DynamicUser=1 \
-            --wait \
-            --pipe \
-            cat '${CREDENTIALS_DIRECTORY}/test.creds.first' \
-                '${CREDENTIALS_DIRECTORY}/test.creds.second' \
-                '${CREDENTIALS_DIRECTORY}/test.creds.third' >/tmp/ts54-concat
-cmp /tmp/ts54-concat <(echo -n abc)
-
-# Now test encrypted credentials (only supported when built with OpenSSL though)
-if systemctl --version | grep -q -- +OPENSSL ; then
-    echo -n $RANDOM >/tmp/test-54-plaintext
-    systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext
-    systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext
-
-    systemd-run -p LoadCredentialEncrypted=test-54:/tmp/test-54-ciphertext \
-        --wait \
-        --pipe \
-        cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext
-
-    echo -n $RANDOM >/tmp/test-54-plaintext
-    systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext
-    systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext
-
-    systemd-run -p SetCredentialEncrypted=test-54:"$(cat /tmp/test-54-ciphertext)" \
-        --wait \
-        --pipe \
-        cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext
-
-    rm /tmp/test-54-plaintext /tmp/test-54-ciphertext
-fi
-
-# https://github.com/systemd/systemd/issues/27275
-systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \
-            -p 'ExecStartPre=true' \
-            -p 'ExecStartPre=systemd-creds cat os' \
-            --unit=test-54-exec-start-pre.service \
-            --wait \
-            --pipe \
-            true | cmp /etc/os-release
-
-# https://github.com/systemd/systemd/issues/31194
-systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \
-            -p 'ExecStartPost=systemd-creds cat os' \
-            --unit=test-54-exec-start-post.service \
-            --service-type=oneshot --wait --pipe \
-            true | cmp /etc/os-release
-
-# https://github.com/systemd/systemd/pull/24734#issuecomment-1925440546
-# Also ExecStartPre= should be able to update creds
-dd if=/dev/urandom of=/tmp/cred-huge bs=600K count=1
-chmod 777 /tmp/cred-huge
-systemd-run -p ProtectSystem=full \
-            -p 'LoadCredential=huge:/tmp/cred-huge' \
-            -p 'ExecStartPre=true' \
-            -p 'ExecStartPre=bash -c "echo fresh >/tmp/cred-huge"' \
-            --unit=test-54-huge-cred.service \
-            --wait --pipe \
-            systemd-creds cat huge | cmp - <(echo "fresh")
-rm /tmp/cred-huge
-
-echo stable >/tmp/cred-stable
-systemd-run -p 'LoadCredential=stable:/tmp/cred-stable' \
-            -p 'ExecStartPost=systemd-creds cat stable' \
-            --unit=test-54-stable.service \
-            --service-type=oneshot --wait --pipe \
-            bash -c "echo bogus >/tmp/cred-stable" | cmp - <(echo "stable")
-assert_eq "$(cat /tmp/cred-stable)" "bogus"
-rm /tmp/cred-stable
-
-if ! systemd-detect-virt -q -c ; then
-    # Validate that the credential we inserted via the initrd logic arrived
-    test "$(systemd-creds cat --system myinitrdcred)" = "guatemala"
-
-    # Check that the fstab credential logic worked
-    test -d /injected
-    grep -q /injected /proc/self/mountinfo
-
-    # Make sure the getty generator processed the credentials properly
-    systemctl -P Wants show getty.target | grep -q container-getty@idontexist.service
-fi
-
-# Decrypt/encrypt via varlink
-
-echo '{"data":"Zm9vYmFyCg=="}' > /tmp/vlcredsdata
-
-varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Encrypt "$(cat /tmp/vlcredsdata)" | \
-    varlinkctl call --json=short /run/systemd/io.systemd.Credentials io.systemd.Credentials.Decrypt > /tmp/vlcredsdata2
-
-cmp /tmp/vlcredsdata /tmp/vlcredsdata2
-rm /tmp/vlcredsdata /tmp/vlcredsdata2
-
-clean_usertest() {
-    rm -f /tmp/usertest.data /tmp/usertest.data
-}
-
-trap clean_usertest EXIT
-dd if=/dev/urandom of=/tmp/usertest.data bs=4096 count=1
-
-systemd-creds encrypt --user /tmp/usertest.data /tmp/usertest.cred
-
-systemd-creds decrypt --user /tmp/usertest.cred - | cmp /tmp/usertest.data
-
-# Decryption must fail if it's not done in user context
-(! systemd-creds decrypt /tmp/usertest.cred - )
-
-# Decryption must also fail if a different user is used
-(! systemd-creds decrypt --user --uid=65534 /tmp/usertest.cred - )
-
-# Try the reverse
-systemd-creds encrypt --user --uid=65534 /tmp/usertest.data /tmp/usertest.cred
-(! systemd-creds decrypt --user /tmp/usertest.cred - )
-systemd-creds decrypt --user --uid=65534 /tmp/usertest.cred - | cmp /tmp/usertest.data
-
-systemd-creds encrypt --user /tmp/usertest.data /tmp/usertest.creds --name=mytest
-
-# Make sure we actually can decode this in user context
-systemctl start user@0.service
-XDG_RUNTIME_DIR=/run/user/0 systemd-run --pipe --user --unit=waldi.service -p LoadCredentialEncrypted=mytest:/tmp/usertest.creds cat /run/user/0/credentials/waldi.service/mytest | cmp /tmp/usertest.data
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-55-testbloat.service b/test/units/testsuite-55-testbloat.service
deleted file mode 100644 (file)
index 6c8e3c9..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Create a lot of memory pressure
-
-[Service]
-# A VERY small memory.high will cause the 'stress' (trying to use a lot of memory)
-# to throttle and be put under heavy pressure.
-MemoryHigh=3M
-Slice=testsuite-55-workload.slice
-ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1
diff --git a/test/units/testsuite-55-testchill.service b/test/units/testsuite-55-testchill.service
deleted file mode 100644 (file)
index 369b802..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=No memory pressure
-
-[Service]
-MemoryHigh=3M
-Slice=testsuite-55-workload.slice
-ExecStart=sleep infinity
diff --git a/test/units/testsuite-55-testmunch.service b/test/units/testsuite-55-testmunch.service
deleted file mode 100644 (file)
index 3730059..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Create some memory pressure
-
-[Service]
-MemoryHigh=12M
-Slice=testsuite-55-workload.slice
-ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1
diff --git a/test/units/testsuite-55-workload.slice b/test/units/testsuite-55-workload.slice
deleted file mode 100644 (file)
index d117b75..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Test slice for memory pressure kills
-
-[Slice]
-CPUAccounting=true
-MemoryAccounting=true
-IOAccounting=true
-TasksAccounting=true
-ManagedOOMMemoryPressure=kill
-ManagedOOMMemoryPressureLimit=20%
diff --git a/test/units/testsuite-55.service b/test/units/testsuite-55.service
deleted file mode 100644 (file)
index 00fb499..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TESTSUITE-55-OOMD
-After=user@4711.service
-Wants=user@4711.service
-
-[Service]
-ExecStartPre=rm -f /failed /skipped /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-55.sh b/test/units/testsuite-55.sh
deleted file mode 100755 (executable)
index 5bc0eae..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
- . "$(dirname "$0")"/util.sh
-
-. /etc/os-release
-# OpenSUSE does not have the stress tool packaged. It does have stress-ng but the stress-ng does not support
-# --vm-stride which this test uses.
-if [[ "$ID" =~ "opensuse" ]]; then
-    echo "Skipping due to missing stress package in OpenSUSE" >>/skipped
-    exit 77
-fi
-
-systemd-analyze log-level debug
-
-# Ensure that the init.scope.d drop-in is applied on boot
-test "$(cat /sys/fs/cgroup/init.scope/memory.high)" != "max"
-
-# Loose checks to ensure the environment has the necessary features for systemd-oomd
-[[ -e /proc/pressure ]] || echo "no PSI" >>/skipped
-[[ "$(get_cgroup_hierarchy)" == "unified" ]] || echo "no cgroupsv2" >>/skipped
-[[ -x /usr/lib/systemd/systemd-oomd ]] || echo "no oomd" >>/skipped
-if [[ -s /skipped ]]; then
-    exit 77
-fi
-
-rm -rf /run/systemd/system/testsuite-55-testbloat.service.d
-
-# Activate swap file if we are in a VM
-if systemd-detect-virt --vm --quiet; then
-    swapoff --all
-    if [[ "$(findmnt -n -o FSTYPE /)" == btrfs ]]; then
-        btrfs filesystem mkswapfile -s 64M /swapfile
-    else
-        dd if=/dev/zero of=/swapfile bs=1M count=64
-        chmod 0600 /swapfile
-        mkswap /swapfile
-    fi
-
-    swapon /swapfile
-    swapon --show
-fi
-
-# Configure oomd explicitly to avoid conflicts with distro dropins
-mkdir -p /run/systemd/oomd.conf.d/
-cat >/run/systemd/oomd.conf.d/99-oomd-test.conf <<EOF
-[OOM]
-DefaultMemoryPressureDurationSec=2s
-EOF
-
-mkdir -p /run/systemd/system/-.slice.d/
-cat >/run/systemd/system/-.slice.d/99-oomd-test.conf <<EOF
-[Slice]
-ManagedOOMSwap=auto
-EOF
-
-mkdir -p /run/systemd/system/user@.service.d/
-cat >/run/systemd/system/user@.service.d/99-oomd-test.conf <<EOF
-[Service]
-ManagedOOMMemoryPressure=auto
-ManagedOOMMemoryPressureLimit=0%
-EOF
-
-mkdir -p /run/systemd/system/systemd-oomd.service.d/
-cat >/run/systemd/system/systemd-oomd.service.d/debug.conf <<EOF
-[Service]
-Environment=SYSTEMD_LOG_LEVEL=debug
-EOF
-
-systemctl daemon-reload
-
-# enable the service to ensure dbus-org.freedesktop.oom1.service exists
-# and D-Bus activation works
-systemctl enable systemd-oomd.service
-
-# if oomd is already running for some reasons, then restart it to make sure the above settings to be applied
-if systemctl is-active systemd-oomd.service; then
-    systemctl restart systemd-oomd.service
-fi
-
-if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
-    # If we're running with sanitizers, sd-executor might pull in quite a significant chunk of shared
-    # libraries, which in turn causes a lot of pressure that can put us in the front when sd-oomd decides to
-    # go on a killing spree. This fact is exacerbated further on Arch Linux which ships unstripped gcc-libs,
-    # so sd-executor pulls in over 30M of libs on startup. Let's make the MemoryHigh= limit a bit more
-    # generous when running with sanitizers to make the test happy.
-    systemctl edit --runtime --stdin --drop-in=99-MemoryHigh.conf testsuite-55-testchill.service <<EOF
-[Service]
-MemoryHigh=60M
-EOF
-    # Do the same for the user instance as well
-    mkdir -p /run/systemd/user/
-    cp -rfv /run/systemd/system/testsuite-55-testchill.service.d/ /run/systemd/user/
-else
-    # Ensure that we can start services even with a very low hard memory cap without oom-kills, but skip
-    # under sanitizers as they balloon memory usage.
-    systemd-run -t -p MemoryMax=10M -p MemorySwapMax=0 -p MemoryZSwapMax=0 /bin/true
-fi
-
-systemctl start testsuite-55-testchill.service
-systemctl start testsuite-55-testbloat.service
-
-# Verify systemd-oomd is monitoring the expected units
-timeout 1m bash -xec 'until oomctl | grep "/testsuite-55-workload.slice"; do sleep 1; done'
-oomctl | grep "/testsuite-55-workload.slice"
-oomctl | grep "20.00%"
-oomctl | grep "Default Memory Pressure Duration: 2s"
-
-systemctl status testsuite-55-testchill.service
-
-# systemd-oomd watches for elevated pressure for 2 seconds before acting.
-# It can take time to build up pressure so either wait 2 minutes or for the service to fail.
-for _ in {0..59}; do
-    if ! systemctl status testsuite-55-testbloat.service; then
-        break
-    fi
-    oomctl
-    sleep 2
-done
-
-# testbloat should be killed and testchill should be fine
-if systemctl status testsuite-55-testbloat.service; then exit 42; fi
-if ! systemctl status testsuite-55-testchill.service; then exit 24; fi
-
-# Make sure we also work correctly on user units.
-loginctl enable-linger testuser
-
-systemctl start --machine "testuser@.host" --user testsuite-55-testchill.service
-systemctl start --machine "testuser@.host" --user testsuite-55-testbloat.service
-
-# Verify systemd-oomd is monitoring the expected units
-# Try to avoid racing the oomctl output check by checking in a loop with a timeout
-timeout 1m bash -xec 'until oomctl | grep "/testsuite-55-workload.slice"; do sleep 1; done'
-oomctl | grep -E "/user.slice.*/testsuite-55-workload.slice"
-oomctl | grep "20.00%"
-oomctl | grep "Default Memory Pressure Duration: 2s"
-
-systemctl --machine "testuser@.host" --user status testsuite-55-testchill.service
-
-# systemd-oomd watches for elevated pressure for 2 seconds before acting.
-# It can take time to build up pressure so either wait 2 minutes or for the service to fail.
-for _ in {0..59}; do
-    if ! systemctl --machine "testuser@.host" --user status testsuite-55-testbloat.service; then
-        break
-    fi
-    oomctl
-    sleep 2
-done
-
-# testbloat should be killed and testchill should be fine
-if systemctl --machine "testuser@.host" --user status testsuite-55-testbloat.service; then exit 42; fi
-if ! systemctl --machine "testuser@.host" --user status testsuite-55-testchill.service; then exit 24; fi
-
-loginctl disable-linger testuser
-
-# only run this portion of the test if we can set xattrs
-if cgroupfs_supports_user_xattrs; then
-    sleep 120 # wait for systemd-oomd kill cool down and elevated memory pressure to come down
-
-    mkdir -p /run/systemd/system/testsuite-55-testbloat.service.d/
-    cat >/run/systemd/system/testsuite-55-testbloat.service.d/override.conf <<EOF
-[Service]
-ManagedOOMPreference=avoid
-EOF
-
-    systemctl daemon-reload
-    systemctl start testsuite-55-testchill.service
-    systemctl start testsuite-55-testmunch.service
-    systemctl start testsuite-55-testbloat.service
-
-    for _ in {0..59}; do
-        if ! systemctl status testsuite-55-testmunch.service; then
-            break
-        fi
-        oomctl
-        sleep 2
-    done
-
-    # testmunch should be killed since testbloat had the avoid xattr on it
-    if ! systemctl status testsuite-55-testbloat.service; then exit 25; fi
-    if systemctl status testsuite-55-testmunch.service; then exit 43; fi
-    if ! systemctl status testsuite-55-testchill.service; then exit 24; fi
-fi
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-58.service b/test/units/testsuite-58.service
deleted file mode 100644 (file)
index f843527..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-58-REPART
-
-[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh
deleted file mode 100755 (executable)
index bebe7b7..0000000
+++ /dev/null
@@ -1,1307 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2317
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-if ! command -v systemd-repart >/dev/null; then
-    echo "no systemd-repart" >/skipped
-    exit 77
-fi
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-export PAGER=cat
-
-# Disable use of special glyphs such as →
-export SYSTEMD_UTF8=0
-
-seed=750b6cd5c4ae4012a15e7be3c29e6a47
-
-if ! systemd-detect-virt --quiet --container; then
-    udevadm control --log-level debug
-fi
-
-machine="$(uname -m)"
-if [ "${machine}" = "x86_64" ]; then
-    root_guid=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
-    root_uuid=60F33797-1D71-4DCB-AA6F-20564F036CD0
-    root_uuid2=73A4CCD2-EAF5-44DA-A366-F99188210FDC
-    usr_guid=8484680C-9521-48C6-9C11-B0720656F69E
-    usr_uuid=7E3369DD-D653-4513-ADF5-B993A9F20C16
-    architecture="x86-64"
-elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then
-    root_guid=44479540-F297-41B2-9AF7-D131D5F0458A
-    root_uuid=02B4253F-29A4-404E-8972-1669D3B03C87
-    root_uuid2=268E0FD3-B468-4806-A823-E533FE9BB9CC
-    usr_guid=75250D76-8CC6-458E-BD66-BD47CC81A812
-    usr_uuid=7B42FFB0-B0E1-4395-B20B-C78F4A571648
-    architecture="x86"
-elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then
-    root_guid=B921B045-1DF0-41C3-AF44-4C6F280D3FAE
-    root_uuid=055D0227-53A6-4033-85C3-9A5973EFF483
-    root_uuid2=F7DBBE48-8FD0-4833-8411-AA34E7C8E60A
-    usr_guid=B0E01050-EE5F-4390-949A-9101B17104E9
-    usr_uuid=FCE3C75E-D6A4-44C0-87F0-4C105183FB1F
-    architecture="arm64"
-elif [ "${machine}" = "arm" ]; then
-    root_guid=69DAD710-2CE4-4E3C-B16C-21A1D49ABED3
-    root_uuid=567DA89E-8DE2-4499-8D10-18F212DFF034
-    root_uuid2=813ECFE5-4C89-4193-8A52-437493F2F96E
-    usr_guid=7D0359A3-02B3-4F0A-865C-654403E70625
-    usr_uuid=71E93DC2-5073-42CB-8A84-A354E64D8966
-    architecture="arm"
-elif [ "${machine}" = "loongarch64" ]; then
-    root_guid=77055800-792C-4F94-B39A-98C91B762BB6
-    root_uuid=D8EFC2D2-0133-41E4-BDCB-3B9F4CFDDDE8
-    root_uuid2=36499F9E-0688-40C1-A746-EA8FD9543C56
-    usr_guid=E611C702-575C-4CBE-9A46-434FA0BF7E3F
-    usr_uuid=031FFA75-00BB-49B6-A70D-911D2D82A5B7
-    architecture="loongarch64"
-elif [ "${machine}" = "ia64" ]; then
-    root_guid=993D8D3D-F80E-4225-855A-9DAF8ED7EA97
-    root_uuid=DCF33449-0896-4EA9-BC24-7D58AEEF522D
-    root_uuid2=C2A6CAB7-ABEA-4FBA-8C48-CB4C52E6CA38
-    usr_guid=4301D2A6-4E3B-4B2A-BB94-9E0B2C4225EA
-    usr_uuid=BC2BCCE7-80D6-449A-85CC-637424CE5241
-    architecture="ia64"
-elif [ "${machine}" = "s390x" ]; then
-    root_guid=5EEAD9A9-FE09-4A1E-A1D7-520D00531306
-    root_uuid=7EBE0C85-E27E-48EC-B164-F4807606232E
-    root_uuid2=2A074E1C-2A19-4094-A0C2-24B1A5D52FCB
-    usr_guid=8A4F5770-50AA-4ED3-874A-99B710DB6FEA
-    usr_uuid=51171D30-35CF-4A49-B8B5-9478B9B796A5
-    architecture="s390x"
-elif [ "${machine}" = "ppc64le" ]; then
-    root_guid=C31C45E6-3F39-412E-80FB-4809C4980599
-    root_uuid=061E67A1-092F-482F-8150-B525D50D6654
-    root_uuid2=A6687CEF-4E4F-44E7-90B3-CDA52EA81739
-    usr_guid=15BB03AF-77E7-4D4A-B12B-C0D084F7491C
-    usr_uuid=C0D0823B-8040-4C7C-A629-026248E297FB
-    architecture="ppc64-le"
-else
-    echo "Unexpected uname -m: ${machine} in testsuite-58.sh, please fix me"
-    exit 1
-fi
-
-testcase_basic() {
-    local defs imgs output
-    local loop volume
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod 0755 "$defs"
-
-    echo "*** 1. create an empty image ***"
-
-    systemd-repart --offline="$OFFLINE" \
-                   --empty=create \
-                   --size=1G \
-                   --seed="$seed" \
-                   "$imgs/zzz"
-
-    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
-    assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 2097118"
-
-    echo "*** 2. Testing with root, root2, home, and swap ***"
-
-    tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root
-EOF
-
-    ln -s root.conf "$defs/root2.conf"
-
-    tee "$defs/home.conf" <<EOF
-[Partition]
-Type=home
-Label=home-first
-Label=home-always-too-long-xxxxxxxxxxxxxx-%v
-EOF
-
-    tee "$defs/swap.conf" <<EOF
-[Partition]
-Type=swap
-SizeMaxBytes=64M
-PaddingMinBytes=92M
-EOF
-
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --dry-run=no \
-                   --seed="$seed" \
-                   --include-partitions=home,swap \
-                   --offline="$OFFLINE" \
-                   "$imgs/zzz"
-
-    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
-    assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 2097118
-$imgs/zzz1 : start=        2048, size=     1775576, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/zzz2 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
-
-    systemd-repart --offline="$OFFLINE" \
-                    --definitions="$defs" \
-                   --empty=create \
-                   --size=50M \
-                   --seed="$seed" \
-                   --include-partitions=root,home \
-                   "$imgs/qqq"
-
-    sfdisk -d "$imgs/qqq" | grep -v -e 'sector-size' -e '^$'
-
-    systemd-repart --offline="$OFFLINE" \
-                   --empty=create \
-                   --size=1G \
-                   --dry-run=no \
-                   --seed="$seed" \
-                   --definitions "" \
-                   --copy-from="$imgs/qqq" \
-                   --copy-from="$imgs/qqq" \
-                   "$imgs/copy"
-
-    output=$(sfdisk -d "$imgs/copy" | grep -v -e 'sector-size' -e '^$')
-
-    assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/copy
-unit: sectors
-first-lba: 2048
-last-lba: 2097118
-$imgs/copy1 : start=        2048, size=       33432, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/copy2 : start=       35480, size=       33440, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/copy3 : start=       68920, size=       33440, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
-$imgs/copy4 : start=      102360, size=       33432, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/copy5 : start=      135792, size=       33440, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/copy6 : start=      169232, size=       33440, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\""
-
-    rm "$imgs/qqq" "$imgs/copy" # Save disk space
-
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --dry-run=no \
-                   --seed="$seed" \
-                   --empty=force \
-                   --defer-partitions=home,root \
-                   "$imgs/zzz"
-
-    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
-    assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 2097118
-$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
-
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --dry-run=no \
-                   --seed="$seed" \
-                   "$imgs/zzz"
-
-    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
-    assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 2097118
-$imgs/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/zzz2 : start=      593904, size=      591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/zzz3 : start=     1185760, size=      591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
-$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
-
-    echo "*** 3. Testing with root, root2, home, swap, and another partition ***"
-
-    tee "$defs/swap.conf" <<EOF
-[Partition]
-Type=swap
-SizeMaxBytes=64M
-EOF
-
-    tee "$defs/extra.conf" <<EOF
-[Partition]
-Type=linux-generic
-Label=custom_label
-UUID=a0a1a2a3a4a5a6a7a8a9aaabacadaeaf
-EOF
-
-    echo "Label=ignored_label" >>"$defs/home.conf"
-    echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>"$defs/home.conf"
-
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --dry-run=no \
-                   --seed="$seed" \
-                   "$imgs/zzz"
-
-    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
-    assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 2097118
-$imgs/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/zzz2 : start=      593904, size=      591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/zzz3 : start=     1185760, size=      591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
-$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
-$imgs/zzz5 : start=     1908696, size=      188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\""
-
-    echo "*** 4. Resizing to 2G ***"
-
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --size=2G \
-                   --dry-run=no \
-                   --seed="$seed" \
-                   "$imgs/zzz"
-
-    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
-    assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 4194270
-$imgs/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/zzz2 : start=      593904, size=      591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/zzz3 : start=     1185760, size=      591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
-$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
-$imgs/zzz5 : start=     1908696, size=     2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\""
-
-    echo "*** 5. Testing with root, root2, home, swap, another partition, and partition copy ***"
-
-    dd if=/dev/urandom of="$imgs/block-copy" bs=4096 count=10240
-
-    tee "$defs/extra2.conf" <<EOF
-[Partition]
-Type=linux-generic
-Label=block-copy
-UUID=2a1d97e1d0a346cca26eadc643926617
-CopyBlocks=$imgs/block-copy
-EOF
-
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --size=3G \
-                   --dry-run=no \
-                   --seed="$seed" \
-                   "$imgs/zzz"
-
-    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
-    assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 6291422
-$imgs/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/zzz2 : start=      593904, size=      591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/zzz3 : start=     1185760, size=      591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
-$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
-$imgs/zzz5 : start=     1908696, size=     2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"
-$imgs/zzz6 : start=     4194264, size=     2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name=\"block-copy\""
-
-    cmp --bytes=$((4096*10240)) --ignore-initial=0:$((512*4194264)) "$imgs/block-copy" "$imgs/zzz"
-
-    echo "*** 6. Testing Format=/Encrypt=/CopyFiles= ***"
-
-    tee "$defs/extra3.conf" <<EOF
-[Partition]
-Type=linux-generic
-Label=luks-format-copy
-UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0
-Format=ext4
-Encrypt=yes
-CopyFiles=$defs:/def
-SizeMinBytes=48M
-EOF
-
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --size=auto \
-                   --dry-run=no \
-                   --seed="$seed" \
-                   "$imgs/zzz"
-
-    output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
-
-    assert_eq "$output" "label: gpt
-label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
-device: $imgs/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 6389726
-$imgs/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
-$imgs/zzz2 : start=      593904, size=      591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"
-$imgs/zzz3 : start=     1185760, size=      591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\"
-$imgs/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\"
-$imgs/zzz5 : start=     1908696, size=     2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"
-$imgs/zzz6 : start=     4194264, size=     2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name=\"block-copy\"
-$imgs/zzz7 : start=     6291416, size=       98304, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=7B93D1F2-595D-4CE3-B0B9-837FBD9E63B0, name=\"luks-format-copy\""
-
-    if systemd-detect-virt --quiet --container; then
-        echo "Skipping encrypt mount tests in container."
-        return
-    fi
-
-    loop="$(losetup -P --show --find "$imgs/zzz")"
-    udevadm wait --timeout 60 --settle "${loop:?}"
-
-    volume="test-repart-$RANDOM"
-
-    touch "$imgs/empty-password"
-    cryptsetup open --type=luks2 --key-file="$imgs/empty-password" "${loop}p7" "$volume"
-    mkdir -p "$imgs/mount"
-    mount -t ext4 "/dev/mapper/$volume" "$imgs/mount"
-    # Use deferred closing on the mapper and autoclear on the loop, so they are cleaned up on umount
-    cryptsetup close --deferred "$volume"
-    losetup -d "$loop"
-    diff -r "$imgs/mount/def" "$defs" >/dev/null
-    umount "$imgs/mount"
-}
-
-testcase_dropin() {
-    local defs imgs output
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod 0755 "$defs"
-
-    tee "$defs/root.conf" <<EOF
-[Partition]
-Type=swap
-SizeMaxBytes=64M
-UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3
-EOF
-
-    mkdir -p "$defs/root.conf.d"
-    tee "$defs/root.conf.d/override1.conf" <<EOF
-[Partition]
-Label=label1
-SizeMaxBytes=32M
-EOF
-
-    tee "$defs/root.conf.d/override2.conf" <<EOF
-[Partition]
-Label=label2
-EOF
-
-    output=$(systemd-repart --offline="$OFFLINE" \
-                            --definitions="$defs" \
-                            --empty=create \
-                            --size=100M \
-                            --json=pretty \
-                            "$imgs/zzz")
-
-    diff -u <(echo "$output") - <<EOF
-[
-       {
-               "type" : "swap",
-               "label" : "label2",
-               "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
-               "partno" : 0,
-               "file" : "$defs/root.conf",
-               "node" : "$imgs/zzz1",
-               "offset" : 1048576,
-               "old_size" : 0,
-               "raw_size" : 33554432,
-               "size" : "-> 32.0M",
-               "old_padding" : 0,
-               "raw_padding" : 0,
-               "padding" : "-> 0B",
-               "activity" : "create",
-               "drop-in_files" : [
-                       "$defs/root.conf.d/override1.conf",
-                       "$defs/root.conf.d/override2.conf"
-               ]
-       }
-]
-EOF
-}
-
-testcase_multiple_definitions() {
-    local defs imgs output
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod 0755 "$defs"
-
-    mkdir -p "$defs/1"
-    tee "$defs/1/root1.conf" <<EOF
-[Partition]
-Type=swap
-SizeMaxBytes=32M
-UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0
-Label=label1
-EOF
-
-    mkdir -p "$defs/2"
-    tee "$defs/2/root2.conf" <<EOF
-[Partition]
-Type=swap
-SizeMaxBytes=32M
-UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3
-Label=label2
-EOF
-
-    output=$(systemd-repart --offline="$OFFLINE" \
-                            --definitions="$defs/1" \
-                            --definitions="$defs/2" \
-                            --empty=create \
-                            --size=100M \
-                            --json=pretty \
-                            "$imgs/zzz")
-
-    diff -u <(echo "$output") - <<EOF
-[
-       {
-               "type" : "swap",
-               "label" : "label1",
-               "uuid" : "7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0",
-               "partno" : 0,
-               "file" : "$defs/1/root1.conf",
-               "node" : "$imgs/zzz1",
-               "offset" : 1048576,
-               "old_size" : 0,
-               "raw_size" : 33554432,
-               "size" : "-> 32.0M",
-               "old_padding" : 0,
-               "raw_padding" : 0,
-               "padding" : "-> 0B",
-               "activity" : "create"
-       },
-       {
-               "type" : "swap",
-               "label" : "label2",
-               "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
-               "partno" : 1,
-               "file" : "$defs/2/root2.conf",
-               "node" : "$imgs/zzz2",
-               "offset" : 34603008,
-               "old_size" : 0,
-               "raw_size" : 33554432,
-               "size" : "-> 32.0M",
-               "old_padding" : 0,
-               "raw_padding" : 0,
-               "padding" : "-> 0B",
-               "activity" : "create"
-       }
-]
-EOF
-}
-
-testcase_copy_blocks() {
-    local defs imgs output
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod 0755 "$defs"
-
-    echo "*** First, create a disk image and verify its in order ***"
-
-    tee "$defs/esp.conf" <<EOF
-[Partition]
-Type=esp
-SizeMinBytes=10M
-Format=vfat
-EOF
-
-    tee "$defs/usr.conf" <<EOF
-[Partition]
-Type=usr-${architecture}
-SizeMinBytes=10M
-Format=ext4
-ReadOnly=yes
-EOF
-
-    tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-SizeMinBytes=10M
-Format=ext4
-MakeDirectories=/usr /efi
-EOF
-
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --empty=create \
-                   --size=auto \
-                   --seed="$seed" \
-                   "$imgs/zzz"
-
-    output=$(sfdisk --dump "$imgs/zzz")
-
-    assert_in "$imgs/zzz1 : start=        2048, size=       20480, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=39107B09-615D-48FB-BA37-C663885FCE67, name=\"esp\"" "$output"
-    assert_in "$imgs/zzz2 : start=       22528, size=       20480, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output"
-    assert_in "$imgs/zzz3 : start=       43008, size=       20480, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:60\"" "$output"
-
-    if systemd-detect-virt --quiet --container; then
-        echo "Skipping second part of copy blocks tests in container."
-        return
-    fi
-
-    echo "*** Second, create another image with CopyBlocks=auto ***"
-
-    tee "$defs/esp.conf" <<EOF
-[Partition]
-Type=esp
-CopyBlocks=auto
-EOF
-
-    tee "$defs/usr.conf" <<EOF
-[Partition]
-Type=usr-${architecture}
-ReadOnly=yes
-CopyBlocks=auto
-EOF
-
-    tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-CopyBlocks=auto
-EOF
-
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --empty=create \
-                   --size=auto \
-                   --seed="$seed" \
-                   --image="$imgs/zzz" \
-                   "$imgs/yyy"
-
-    cmp "$imgs/zzz" "$imgs/yyy"
-}
-
-testcase_unaligned_partition() {
-    local defs imgs output
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod 0755 "$defs"
-
-    echo "*** Operate on an image with unaligned partition ***"
-
-    tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-EOF
-
-    truncate -s 10g "$imgs/unaligned"
-    sfdisk "$imgs/unaligned" <<EOF
-label: gpt
-
-start=2048, size=69044
-start=71092, size=3591848
-EOF
-
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --seed="$seed" \
-                   --dry-run=no \
-                   "$imgs/unaligned"
-
-    output=$(sfdisk --dump "$imgs/unaligned")
-
-    assert_in "$imgs/unaligned1 : start=        2048, size=       69044," "$output"
-    assert_in "$imgs/unaligned2 : start=       71092, size=     3591848," "$output"
-    assert_in "$imgs/unaligned3 : start=     3662944, size=    17308536, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output"
-}
-
-testcase_issue_21817() {
-    local defs imgs output
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod 0755 "$defs"
-
-    echo "*** testcase for #21817 ***"
-
-    tee "$defs/test.conf" <<EOF
-[Partition]
-Type=root
-EOF
-
-    truncate -s 100m "$imgs/21817.img"
-    sfdisk "$imgs/21817.img" <<EOF
-label: gpt
-
-size=50M, type=${root_guid}
-,
-EOF
-
-    systemd-repart --offline="$OFFLINE" \
-                   --pretty=yes \
-                   --definitions "$imgs" \
-                   --seed="$seed" \
-                   --dry-run=no \
-                   "$imgs/21817.img"
-
-    output=$(sfdisk --dump "$imgs/21817.img")
-
-    assert_in "$imgs/21817.img1 : start=        2048, size=      102400, type=${root_guid}," "$output"
-    # Accept both unpadded (pre-v2.38 util-linux) and padded (v2.38+ util-linux) sizes
-    assert_in "$imgs/21817.img2 : start=      104448, size=      (100319| 98304)," "$output"
-}
-
-testcase_issue_24553() {
-    local defs imgs output
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod 0755 "$defs"
-
-    echo "*** testcase for #24553 ***"
-
-    tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root
-SizeMinBytes=10G
-SizeMaxBytes=120G
-EOF
-
-    tee "$imgs/partscript" <<EOF
-label: gpt
-label-id: C9FFE979-A415-C449-B729-78C7AA664B10
-unit: sectors
-first-lba: 40
-
-start=40, size=524288, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=F2E89C8A-DC5D-4C4C-A29C-6CFB643B74FD, name="ESP System Partition"
-start=524328, size=14848000, type=${root_guid}, uuid=${root_uuid}, name="root-${architecture}"
-EOF
-
-    echo "*** 1. Operate on a small image compared with SizeMinBytes= ***"
-    truncate -s 8g "$imgs/zzz"
-    sfdisk "$imgs/zzz" <"$imgs/partscript"
-
-    # This should fail, but not trigger assertions.
-    assert_rc 1 systemd-repart --offline="$OFFLINE" \
-                               --definitions="$defs" \
-                               --seed="$seed" \
-                               --dry-run=no \
-                               "$imgs/zzz"
-
-    output=$(sfdisk --dump "$imgs/zzz")
-    assert_in "$imgs/zzz2 : start=      524328, size=    14848000, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
-
-    echo "*** 2. Operate on an larger image compared with SizeMinBytes= ***"
-    rm -f "$imgs/zzz"
-    truncate -s 12g "$imgs/zzz"
-    sfdisk "$imgs/zzz" <"$imgs/partscript"
-
-    # This should succeed.
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --seed="$seed" \
-                   --dry-run=no \
-                   "$imgs/zzz"
-
-    output=$(sfdisk --dump "$imgs/zzz")
-    assert_in "$imgs/zzz2 : start=      524328, size=    24641456, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
-
-    echo "*** 3. Multiple partitions with Priority= (small disk) ***"
-    tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root
-SizeMinBytes=10G
-SizeMaxBytes=120G
-Priority=100
-EOF
-
-    tee "$defs/usr.conf" <<EOF
-[Partition]
-Type=usr
-SizeMinBytes=10M
-Priority=10
-EOF
-
-    rm -f "$imgs/zzz"
-    truncate -s 8g "$imgs/zzz"
-    sfdisk "$imgs/zzz" <"$imgs/partscript"
-
-    # This should also succeed, but root is not extended.
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --seed="$seed" \
-                   --dry-run=no \
-                   "$imgs/zzz"
-
-    output=$(sfdisk --dump "$imgs/zzz")
-    assert_in "$imgs/zzz2 : start=      524328, size=    14848000, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
-    assert_in "$imgs/zzz3 : start=    15372328, size=     1404848, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:59\"" "$output"
-
-    echo "*** 4. Multiple partitions with Priority= (large disk) ***"
-    rm -f "$imgs/zzz"
-    truncate -s 12g "$imgs/zzz"
-    sfdisk "$imgs/zzz" <"$imgs/partscript"
-
-    # This should also succeed, and root is extended.
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --seed="$seed" \
-                   --dry-run=no \
-                   "$imgs/zzz"
-
-    output=$(sfdisk --dump "$imgs/zzz")
-    assert_in "$imgs/zzz2 : start=      524328, size=    20971520, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\"" "$output"
-    assert_in "$imgs/zzz3 : start=    21495848, size=     3669936, type=${usr_guid}, uuid=${usr_uuid}, name=\"usr-${architecture}\", attrs=\"GUID:59\"" "$output"
-}
-
-testcase_zero_uuid() {
-    local defs imgs output
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod 0755 "$defs"
-
-    echo "*** Test image with zero UUID ***"
-
-    tee "$defs/root.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-UUID=null
-EOF
-
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --seed="$seed" \
-                   --dry-run=no \
-                   --empty=create \
-                   --size=auto \
-                   "$imgs/zero"
-
-    output=$(sfdisk --dump "$imgs/zero")
-
-    assert_in "$imgs/zero1 : start=        2048, size=       20480, type=${root_guid}, uuid=00000000-0000-0000-0000-000000000000" "$output"
-}
-
-testcase_verity() {
-    local defs imgs output
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod 0755 "$defs"
-
-    echo "*** dm-verity ***"
-
-    tee "$defs/verity-data.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-CopyFiles=${defs}
-Verity=data
-VerityMatchKey=root
-Minimize=guess
-EOF
-
-    tee "$defs/verity-hash.conf" <<EOF
-[Partition]
-Type=root-${architecture}-verity
-Verity=hash
-VerityMatchKey=root
-Minimize=yes
-EOF
-
-    tee "$defs/verity-sig.conf" <<EOF
-[Partition]
-Type=root-${architecture}-verity-sig
-Verity=signature
-VerityMatchKey=root
-EOF
-
-    # Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
-    tee >"$defs/verity.openssl.cnf" <<EOF
-[ req ]
-prompt = no
-distinguished_name = req_distinguished_name
-
-[ req_distinguished_name ]
-C = DE
-ST = Test State
-L = Test Locality
-O = Org Name
-OU = Org Unit Name
-CN = Common Name
-emailAddress = test@email.com
-EOF
-
-    openssl req \
-            -config "$defs/verity.openssl.cnf" \
-            -new -x509 \
-            -newkey rsa:1024 \
-            -keyout "$defs/verity.key" \
-            -out "$defs/verity.crt" \
-            -days 365 \
-            -nodes
-
-    mkdir -p /run/verity.d
-    ln -sf "$defs/verity.crt" /run/verity.d/ok.crt
-
-    output=$(systemd-repart --offline="$OFFLINE" \
-                            --definitions="$defs" \
-                            --seed="$seed" \
-                            --dry-run=no \
-                            --empty=create \
-                            --size=auto \
-                            --json=pretty \
-                            --private-key="$defs/verity.key" \
-                            --certificate="$defs/verity.crt" \
-                            "$imgs/verity")
-
-    drh=$(jq -r ".[] | select(.type == \"root-${architecture}\") | .roothash" <<<"$output")
-    hrh=$(jq -r ".[] | select(.type == \"root-${architecture}-verity\") | .roothash" <<<"$output")
-    srh=$(jq -r ".[] | select(.type == \"root-${architecture}-verity-sig\") | .roothash" <<<"$output")
-
-    assert_eq "$drh" "$hrh"
-    assert_eq "$hrh" "$srh"
-
-    # Check that we can dissect, mount and unmount a repart verity image. (and that the image UUID is deterministic)
-
-    if systemd-detect-virt --quiet --container; then
-        echo "Skipping verity test dissect part in container."
-        return
-    fi
-
-    systemd-dissect "$imgs/verity" --root-hash "$drh"
-    systemd-dissect "$imgs/verity" --root-hash "$drh" --json=short | grep -q '"imageUuid":"1d2ce291-7cce-4f7d-bc83-fdb49ad74ebd"'
-    systemd-dissect "$imgs/verity" --root-hash "$drh" -M "$imgs/mnt"
-    systemd-dissect -U "$imgs/mnt"
-}
-
-testcase_verity_explicit_block_size() {
-    local defs imgs loop
-
-    if systemd-detect-virt --quiet --container; then
-        echo "Skipping verity block size tests in container."
-        return
-    fi
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod 0755 "$defs"
-
-    echo "*** varying-dm-verity-block-sizes ***"
-
-    tee "$defs/verity-data.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-CopyFiles=${defs}
-Verity=data
-VerityMatchKey=root
-Minimize=guess
-EOF
-
-    tee "$defs/verity-hash.conf" <<EOF
-[Partition]
-Type=root-${architecture}-verity
-Verity=hash
-VerityMatchKey=root
-VerityHashBlockSizeBytes=1024
-VerityDataBlockSizeBytes=4096
-Minimize=yes
-EOF
-
-    systemd-repart --offline="$OFFLINE" \
-                   --definitions="$defs" \
-                   --seed="$seed" \
-                   --dry-run=no \
-                   --empty=create \
-                   --size=auto \
-                   --json=pretty \
-                   "$imgs/verity"
-
-    loop="$(losetup --partscan --show --find "$imgs/verity")"
-
-    # Make sure the loopback device gets cleaned up
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs' ; losetup -d '$loop'" RETURN ERR
-
-    udevadm wait --timeout 60 --settle "${loop:?}"
-
-    # Check that the verity block sizes are as expected
-    veritysetup dump "${loop}p2" | grep 'Data block size:' | grep -q '4096'
-    veritysetup dump "${loop}p2" | grep 'Hash block size:' | grep -q '1024'
-}
-
-testcase_exclude_files() {
-    local defs imgs root output
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-    root="$(mktemp --directory "/var/tmp/test-repart.root.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs' '$root'" RETURN
-    chmod 0755 "$defs"
-
-    echo "*** file exclusion ***"
-
-    touch "$root/abc"
-    mkdir "$root/usr"
-    touch "$root/usr/def"
-    touch "$root/usr/qed"
-    mkdir "$root/tmp"
-    touch "$root/tmp/prs"
-    mkdir "$root/proc"
-    touch "$root/proc/prs"
-    mkdir "$root/zzz"
-    mkdir "$root/zzz/usr"
-    touch "$root/zzz/usr/prs"
-    mkdir "$root/zzz/proc"
-    touch "$root/zzz/proc/prs"
-
-    tee "$defs/00-root.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-CopyFiles=/
-CopyFiles=/zzz:/
-CopyFiles=/:/oiu
-ExcludeFilesTarget=/oiu/usr
-EOF
-
-    tee "$defs/10-usr.conf" <<EOF
-[Partition]
-Type=usr-${architecture}
-CopyFiles=/usr:/
-ExcludeFiles=/usr/qed
-EOF
-
-    output=$(systemd-repart --offline="$OFFLINE" \
-                            --definitions="$defs" \
-                            --seed="$seed" \
-                            --dry-run=no \
-                            --empty=create \
-                            --size=auto \
-                            --json=pretty \
-                            --root="$root" \
-                            "$imgs/zzz")
-
-    if systemd-detect-virt --quiet --container; then
-        echo "Skipping issue 24786 test loop/mount parts in container."
-        return
-    fi
-
-    loop=$(losetup -P --show -f "$imgs/zzz")
-    udevadm wait --timeout 60 --settle "${loop:?}"
-
-    # Test that /usr/def did not end up in the root partition but other files did.
-    mkdir "$imgs/mnt"
-    mount -t ext4 "${loop}p1" "$imgs/mnt"
-    assert_rc 0 ls "$imgs/mnt/abc"
-    assert_rc 0 ls "$imgs/mnt/usr"
-    assert_rc 2 ls "$imgs/mnt/usr/def"
-
-    # Test that /zzz/usr/prs did not end up in the root partition under /usr but did end up in /zzz/usr/prs
-    assert_rc 2 ls "$imgs/mnt/usr/prs"
-    assert_rc 0 ls "$imgs/mnt/zzz/usr/prs"
-
-    # Test that /tmp/prs did not end up in the root partition but /tmp did.
-    assert_rc 0 ls "$imgs/mnt/tmp"
-    assert_rc 2 ls "$imgs/mnt/tmp/prs"
-
-    # Test that /usr/qed did not end up in the usr partition but /usr/def did.
-    mount -t ext4 "${loop}p2" "$imgs/mnt/usr"
-    assert_rc 0 ls "$imgs/mnt/usr/def"
-    assert_rc 2 ls "$imgs/mnt/usr/qed"
-
-    # Test that /zzz/proc/prs did not end up in the root partition but /proc did.
-    assert_rc 0 ls "$imgs/mnt/proc"
-    assert_rc 2 ls "$imgs/mnt/proc/prs"
-
-    # Test that /zzz/usr/prs did not end up in the usr partition.
-    assert_rc 2 ls "$imgs/mnt/usr/prs"
-
-    # Test that /oiu/ and /oiu/zzz ended up in the root partition but /oiu/usr did not.
-    assert_rc 0 ls "$imgs/mnt/oiu"
-    assert_rc 0 ls "$imgs/mnt/oiu/zzz"
-    assert_rc 2 ls "$imgs/mnt/oiu/usr"
-
-    umount -R "$imgs/mnt"
-    losetup -d "$loop"
-}
-
-testcase_minimize() {
-    local defs imgs output
-
-    if systemd-detect-virt --quiet --container; then
-        echo "Skipping minimize test in container."
-        return
-    fi
-
-    echo "*** minimization ***"
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs'" RETURN
-
-    for format in ext4 vfat erofs; do
-        if ! command -v "mkfs.$format" >/dev/null; then
-            continue
-        fi
-
-        tee "$defs/root-$format.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-Format=${format}
-CopyFiles=${defs}
-Minimize=guess
-EOF
-    done
-
-    if command -v mksquashfs >/dev/null; then
-        tee "$defs/root-squashfs.conf" <<EOF
-[Partition]
-Type=root-${architecture}
-Format=squashfs
-CopyFiles=${defs}
-Minimize=best
-EOF
-    fi
-
-    output=$(systemd-repart --offline="$OFFLINE" \
-                            --definitions="$defs" \
-                            --seed="$seed" \
-                            --dry-run=no \
-                            --empty=create \
-                            --size=auto \
-                            --json=pretty \
-                            "$imgs/zzz")
-
-    # Check that we can dissect, mount and unmount a minimized image.
-
-    systemd-dissect "$imgs/zzz"
-    systemd-dissect "$imgs/zzz" -M "$imgs/mnt"
-    systemd-dissect -U "$imgs/mnt"
-}
-
-testcase_free_area_calculation() {
-    local defs imgs output
-
-    if ! command -v mksquashfs >/dev/null; then
-        echo "Skipping free area calculation test without squashfs."
-        return
-    fi
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs'" RETURN
-    chmod 0755 "$defs"
-
-    # https://github.com/systemd/systemd/issues/28225
-    echo "*** free area calculation ***"
-
-    tee "$defs/00-ESP.conf" <<EOF
-[Partition]
-Type         = esp
-Label        = ESP
-Format       = vfat
-
-SizeMinBytes = 128M
-SizeMaxBytes = 128M
-
-# Sufficient for testing
-CopyFiles    = /etc:/
-EOF
-
-    tee "$defs/10-os.conf" <<EOF
-[Partition]
-Type           = root-${architecture}
-Label          = test
-Format         = squashfs
-
-Minimize       = best
-# Sufficient for testing
-CopyFiles      = /etc/:/
-
-VerityMatchKey = os
-Verity         = data
-EOF
-
-    tee "$defs/11-os-verity.conf" <<EOF
-[Partition]
-Type           = root-${architecture}-verity
-Label          = test
-
-Minimize       = best
-
-VerityMatchKey = os
-Verity         = hash
-EOF
-
-    # Set sector size for VFAT to 512 bytes because there will not be enough FAT clusters otherwise
-    output1=$(SYSTEMD_REPART_MKFS_OPTIONS_VFAT="-S 512" systemd-repart \
-                                              --definitions="$defs" \
-                                              --seed="$seed" \
-                                              --dry-run=no \
-                                              --empty=create \
-                                              --size=auto \
-                                              --sector-size=4096 \
-                                              --defer-partitions=esp \
-                                              --json=pretty \
-                                              "$imgs/zzz")
-
-    # The second invocation
-    output2=$(SYSTEMD_REPART_MKFS_OPTIONS_VFAT="-S 512" systemd-repart \
-                                              --definitions="$defs" \
-                                              --seed="$seed" \
-                                              --dry-run=no \
-                                              --empty=allow \
-                                              --size=auto \
-                                              --sector-size=4096 \
-                                              --defer-partitions=esp \
-                                              --json=pretty \
-                                              "$imgs/zzz")
-
-    diff -u <(echo "$output1" | grep -E "(offset|raw_size|raw_padding)") <(echo "$output2" | grep -E "(offset|raw_size|raw_padding)")
-}
-
-test_sector() {
-    local defs imgs output loop
-    local start size ratio
-    local sector="${1?}"
-
-    if systemd-detect-virt --quiet --container; then
-        echo "Skipping sector size tests in container."
-        return
-    fi
-
-    echo "*** sector sizes ***"
-
-    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
-    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$defs' '$imgs'" RETURN
-
-    tee "$defs/a.conf" <<EOF
-[Partition]
-Type=root
-SizeMaxBytes=15M
-SizeMinBytes=15M
-EOF
-    tee "$defs/b.conf" <<EOF
-[Partition]
-Type=linux-generic
-Weight=250
-EOF
-
-    tee "$defs/c.conf" <<EOF
-[Partition]
-Type=linux-generic
-Weight=750
-EOF
-
-    truncate -s 100m "$imgs/$sector.img"
-    loop=$(losetup -b "$sector" -P --show -f "$imgs/$sector.img" )
-    udevadm wait --timeout 60 --settle "${loop:?}"
-
-    systemd-repart --offline="$OFFLINE" \
-                   --pretty=yes \
-                   --definitions="$defs" \
-                   --seed="$seed" \
-                   --empty=require \
-                   --dry-run=no \
-                   "$loop"
-
-    sfdisk --verify "$loop"
-    output=$(sfdisk --dump "$loop")
-    losetup -d "$loop"
-
-    ratio=$(( sector / 512 ))
-    start=$(( 2048 / ratio ))
-    size=$(( 30720 / ratio ))
-    assert_in "${loop}p1 : start= *${start}, size= *${size}, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\"" "$output"
-    start=$(( start + size ))
-    size=$(( 42992 / ratio ))
-    assert_in "${loop}p2 : start= *${start}, size= *${size}, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=DF71F5E3-080A-4D16-824B-18591B881380, name=\"linux-generic\"" "$output"
-    start=$(( start + size ))
-    size=$(( 129000 / ratio ))
-    assert_in "${loop}p3 : start= *${start}, size= *${size}, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=DB081670-07AE-48CA-9F5E-813D5E40B976, name=\"linux-generic-2\"" "$output"
-}
-
-testcase_dropped_partitions() {
-    local workdir image defs
-
-    workdir="$(mktemp --directory "/tmp/test-repart.dropped-partitions.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '${workdir:?}'" RETURN
-
-    image="$workdir/image.img"
-    truncate -s 32M "$image"
-
-    defs="$workdir/defs"
-    mkdir "$defs"
-    echo -ne "[Partition]\nType=root\n" >"$defs/10-part1.conf"
-    echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=1\n" >"$defs/11-dropped-first.conf"
-    echo -ne "[Partition]\nType=root\n" >"$defs/12-part2.conf"
-    echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=2\n" >"$defs/13-dropped-second.conf"
-
-    systemd-repart --empty=allow --pretty=yes --dry-run=no --definitions="$defs" "$image"
-
-    sfdisk -q -l "$image"
-    [[ "$(sfdisk -q -l "$image" | grep -c "$image")" -eq 2 ]]
-}
-
-OFFLINE="yes"
-run_testcases
-
-# Online image builds need loop devices so we can't run them in nspawn.
-if ! systemd-detect-virt --container; then
-    OFFLINE="no"
-    run_testcases
-fi
-
-# Valid block sizes on the Linux block layer are >= 512 and <= PAGE_SIZE, and
-# must be powers of 2. Which leaves exactly four different ones to test on
-# typical hardware
-test_sector 512
-test_sector 1024
-test_sector 2048
-test_sector 4096
-
-touch /testok
diff --git a/test/units/testsuite-59.service b/test/units/testsuite-59.service
deleted file mode 100644 (file)
index f85cfab..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-59-RELOADING-RESTART
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
diff --git a/test/units/testsuite-59.sh b/test/units/testsuite-59.sh
deleted file mode 100755 (executable)
index 0e04403..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-fail() {
-    systemd-analyze log-level info
-    exit 1
-}
-
-# Wait for a service to enter a state within a timeout period, if it doesn't
-# enter the desired state within the timeout period then this function will
-# exit the test case with a non zero exit code.
-wait_on_state_or_fail() {
-    service=$1
-    expected_state=$2
-    timeout=$3
-
-    state=$(systemctl show "$service" --property=ActiveState --value)
-    while [ "$state" != "$expected_state" ]; do
-        if [ "$timeout" = "0" ]; then
-            fail
-        fi
-        timeout=$((timeout - 1))
-        sleep 1
-        state=$(systemctl show "$service" --property=ActiveState --value)
-    done
-}
-
-systemd-analyze log-level debug
-
-
-cat >/run/systemd/system/testservice-fail-59.service <<EOF
-[Unit]
-Description=TEST-59-RELOADING-RESTART Normal exit
-
-[Service]
-Type=notify
-ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
-EOF
-
-cat >/run/systemd/system/testservice-fail-restart-59.service <<EOF
-[Unit]
-Description=TEST-59-RELOADING-RESTART Restart=on-failure
-
-[Service]
-Type=notify
-ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
-Restart=on-failure
-StartLimitBurst=1
-EOF
-
-
-cat >/run/systemd/system/testservice-abort-restart-59.service <<EOF
-[Unit]
-Description=TEST-59-RELOADING-RESTART Restart=on-abort
-
-[Service]
-Type=notify
-ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 5; exit 1"
-Restart=on-abort
-EOF
-
-systemctl daemon-reload
-
-# This service sends a RELOADING=1 message then exits before it sends a
-# READY=1. Ensure it enters failed state and does not linger in reloading
-# state.
-systemctl start testservice-fail-59.service
-wait_on_state_or_fail "testservice-fail-59.service" "failed" "30"
-
-# This service sends a RELOADING=1 message then exits before it sends a
-# READY=1. It should automatically restart on failure. Ensure it enters failed
-# state and does not linger in reloading state.
-systemctl start testservice-fail-restart-59.service
-wait_on_state_or_fail "testservice-fail-restart-59.service" "failed" "30"
-
-# This service sends a RELOADING=1 message then exits before it sends a
-# READY=1. It should automatically restart on abort. It will sleep for 5s
-# to allow us to send it a SIGABRT. Ensure the service enters the failed state
-# and does not linger in reloading state.
-systemctl start testservice-abort-restart-59.service
-systemctl --signal=SIGABRT kill testservice-abort-restart-59.service
-wait_on_state_or_fail "testservice-abort-restart-59.service" "failed" "30"
-
-systemd-analyze log-level info
-
-# Test that rate-limiting daemon-reload works
-mkdir -p /run/systemd/system.conf.d/
-cat >/run/systemd/system.conf.d/50-test-59-reload.conf <<EOF
-[Manager]
-ReloadLimitIntervalSec=9
-ReloadLimitBurst=3
-EOF
-
-# Pick up the new config
-systemctl daemon-reload
-
-# The timeout will hit (and the test will fail) if the reloads are not rate-limited
-timeout 15 bash -c 'while systemctl daemon-reload --no-block; do true; done'
-
-# Rate limit should reset after 9s
-sleep 10
-
-systemctl daemon-reload
-
-# Same test for reexec, but we wait here
-timeout 15 bash -c 'while systemctl daemon-reexec; do true; done'
-
-# Rate limit should reset after 9s
-sleep 10
-
-systemctl daemon-reexec
-
-# Let's now test the notify-reload logic
-
-cat >/run/notify-reload-test.sh <<EOF
-#!/usr/bin/env bash
-set -eux
-set -o pipefail
-
-EXIT_STATUS=88
-LEAVE=0
-
-function reload() {
-    systemd-notify --reloading --status="Adding 11 to exit status"
-    EXIT_STATUS=\$((EXIT_STATUS + 11))
-    systemd-notify --ready --status="Back running"
-}
-
-function leave() {
-    systemd-notify --stopping --status="Adding 7 to exit status"
-    EXIT_STATUS=\$((EXIT_STATUS + 7))
-    LEAVE=1
-    return 0
-}
-
-trap reload SIGHUP
-trap leave SIGTERM
-
-systemd-notify --ready
-systemd-notify --status="Running now"
-
-while [ \$LEAVE = 0 ] ; do
-    sleep 1
-done
-
-systemd-notify --status="Adding 3 to exit status"
-EXIT_STATUS=\$((EXIT_STATUS + 3))
-exit \$EXIT_STATUS
-EOF
-
-chmod +x /run/notify-reload-test.sh
-
-systemd-analyze log-level debug
-
-systemd-run --unit notify-reload-test -p Type=notify-reload -p KillMode=process /run/notify-reload-test.sh
-systemctl reload notify-reload-test
-systemctl stop notify-reload-test
-
-test "$(systemctl show -p ExecMainStatus --value notify-reload-test)" = 109
-
-systemctl reset-failed notify-reload-test
-rm /run/notify-reload-test.sh
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-60.service b/test/units/testsuite-60.service
deleted file mode 100644 (file)
index 1a929e4..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-60-MOUNT-RATELIMIT
-
-[Service]
-Type=oneshot
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
diff --git a/test/units/testsuite-60.sh b/test/units/testsuite-60.sh
deleted file mode 100755 (executable)
index e800a7a..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-teardown_test_dependencies() (
-    set +eux
-
-    if mountpoint /tmp/deptest; then
-        umount /tmp/deptest
-    fi
-
-    if [[ -n "${LOOP}" ]]; then
-        losetup -d "${LOOP}" || :
-    fi
-    if [[ -n "${LOOP_0}" ]]; then
-        losetup -d "${LOOP_0}" || :
-    fi
-    if [[ -n "${LOOP_1}" ]]; then
-        losetup -d "${LOOP_1}" || :
-    fi
-
-    rm -f /tmp/testsuite-60-dependencies-0.img
-    rm -f /tmp/testsuite-60-dependencies-1.img
-
-    rm -f /run/systemd/system/tmp-deptest.mount
-    systemctl daemon-reload
-
-    return 0
-)
-
-setup_loop() {
-    truncate -s 30m "/tmp/testsuite-60-dependencies-${1?}.img"
-    sfdisk --wipe=always "/tmp/testsuite-60-dependencies-${1?}.img" <<EOF
-label:gpt
-
-name="loop${1?}-part1"
-EOF
-    LOOP=$(losetup -P --show -f "/tmp/testsuite-60-dependencies-${1?}.img")
-    udevadm wait --settle --timeout=10 "${LOOP}"
-    udevadm lock --device="${LOOP}" mkfs.ext4 -L "partname${1?}-1" "${LOOP}p1"
-}
-
-check_dependencies() {
-    local escaped_0 escaped_1 after
-
-    escaped_0=$(systemd-escape -p "${LOOP_0}p1")
-    escaped_1=$(systemd-escape -p "${LOOP_1}p1")
-
-    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
-        after=$(systemctl show --property=After --value tmp-deptest.mount)
-        assert_not_in "local-fs-pre.target" "$after"
-        assert_in "remote-fs-pre.target" "$after"
-        assert_in "network.target" "$after"
-    fi
-
-    # mount LOOP_0
-    mount -t ext4 "${LOOP_0}p1" /tmp/deptest
-    sleep 1
-    after=$(systemctl show --property=After --value tmp-deptest.mount)
-    assert_in "local-fs-pre.target" "$after"
-    assert_not_in "remote-fs-pre.target" "$after"
-    assert_not_in "network.target" "$after"
-    assert_in "${escaped_0}.device" "$after"
-    assert_in "blockdev@${escaped_0}.target" "$after"
-    assert_not_in "${escaped_1}.device" "$after"
-    assert_not_in "blockdev@${escaped_1}.target" "$after"
-    umount /tmp/deptest
-
-    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
-        after=$(systemctl show --property=After --value tmp-deptest.mount)
-        assert_not_in "local-fs-pre.target" "$after"
-        assert_in "remote-fs-pre.target" "$after"
-        assert_in "network.target" "$after"
-    fi
-
-    # mount LOOP_1 (using fake _netdev option)
-    mount -t ext4 -o _netdev "${LOOP_1}p1" /tmp/deptest
-    sleep 1
-    after=$(systemctl show --property=After --value tmp-deptest.mount)
-    assert_not_in "local-fs-pre.target" "$after"
-    assert_in "remote-fs-pre.target" "$after"
-    assert_in "network.target" "$after"
-    assert_not_in "${escaped_0}.device" "$after"
-    assert_not_in "blockdev@${escaped_0}.target" "$after"
-    assert_in "${escaped_1}.device" "$after"
-    assert_in "blockdev@${escaped_1}.target" "$after"
-    umount /tmp/deptest
-
-    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
-        after=$(systemctl show --property=After --value tmp-deptest.mount)
-        assert_not_in "local-fs-pre.target" "$after"
-        assert_in "remote-fs-pre.target" "$after"
-        assert_in "network.target" "$after"
-    fi
-
-    # mount tmpfs
-    mount -t tmpfs tmpfs /tmp/deptest
-    sleep 1
-    after=$(systemctl show --property=After --value tmp-deptest.mount)
-    assert_in "local-fs-pre.target" "$after"
-    assert_not_in "remote-fs-pre.target" "$after"
-    assert_not_in "network.target" "$after"
-    assert_not_in "${escaped_0}.device" "$after"
-    assert_not_in "blockdev@${escaped_0}.target" "$after"
-    assert_not_in "${escaped_1}.device" "$after"
-    assert_not_in "blockdev@${escaped_1}.target" "$after"
-    umount /tmp/deptest
-
-    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
-        after=$(systemctl show --property=After --value tmp-deptest.mount)
-        assert_not_in "local-fs-pre.target" "$after"
-        assert_in "remote-fs-pre.target" "$after"
-        assert_in "network.target" "$after"
-    fi
-}
-
-test_dependencies() {
-    if systemd-detect-virt --quiet --container; then
-        echo "Skipping test_dependencies in container"
-        return
-    fi
-
-    trap teardown_test_dependencies RETURN
-
-    setup_loop 0
-    LOOP_0="${LOOP}"
-    LOOP=
-    setup_loop 1
-    LOOP_1="${LOOP}"
-    LOOP=
-
-    mkdir -p /tmp/deptest
-
-    # without .mount file
-    check_dependencies
-
-    # create .mount file
-    mkdir -p /run/systemd/system
-    cat >/run/systemd/system/tmp-deptest.mount <<EOF
-[Mount]
-Where=/tmp/deptest
-What=192.168.0.1:/tmp/mnt
-Type=nfs
-EOF
-    systemctl daemon-reload
-
-    # with .mount file
-    check_dependencies
-}
-
-test_issue_20329() {
-    local tmpdir unit
-    tmpdir="$(mktemp -d)"
-    unit=$(systemd-escape --suffix mount --path "$tmpdir")
-
-    # Set up test mount unit
-    cat >/run/systemd/system/"$unit" <<EOF
-[Mount]
-What=tmpfs
-Where=$tmpdir
-Type=tmpfs
-Options=defaults,nofail
-EOF
-
-    # Start the unit
-    systemctl daemon-reload
-    systemctl start "$unit"
-
-    [[ "$(systemctl show --property SubState --value "$unit")" = "mounted" ]] || {
-        echo >&2 "Test mount \"$unit\" unit isn't mounted"
-        return 1
-    }
-    mountpoint -q "$tmpdir"
-
-    trap 'systemctl stop $unit' RETURN
-
-    # Trigger the mount ratelimiting
-    cd "$(mktemp -d)"
-    mkdir foo
-    for _ in {1..50}; do
-        mount --bind foo foo
-        umount foo
-    done
-
-    # Unmount the test mount and start it immediately again via systemd
-    umount "$tmpdir"
-    systemctl start "$unit"
-
-    # Make sure it is seen as mounted by systemd and it actually is mounted
-    [[ "$(systemctl show --property SubState --value "$unit")" = "mounted" ]] || {
-        echo >&2 "Test mount \"$unit\" unit isn't in \"mounted\" state"
-        return 1
-    }
-
-    mountpoint -q "$tmpdir" || {
-        echo >&2 "Test mount \"$unit\" is in \"mounted\" state, actually is not mounted"
-        return 1
-    }
-}
-
-test_issue_23796() {
-    local mount_path mount_mytmpfs
-
-    mount_path="$(command -v mount 2>/dev/null)"
-    mount_mytmpfs="${mount_path/\/bin/\/sbin}.mytmpfs"
-    cat >"$mount_mytmpfs" <<EOF
-#!/bin/bash
-sleep ".\$RANDOM"
-exec -- $mount_path -t tmpfs tmpfs "\$2"
-EOF
-    chmod +x "$mount_mytmpfs"
-
-    mkdir -p /run/systemd/system
-    cat >/run/systemd/system/tmp-hoge.mount <<EOF
-[Mount]
-What=mytmpfs
-Where=/tmp/hoge
-Type=mytmpfs
-EOF
-
-    # shellcheck disable=SC2064
-    trap "rm -f /run/systemd/system/tmp-hoge.mount '$mount_mytmpfs'" RETURN
-
-    for _ in {1..10}; do
-        systemctl --no-block start tmp-hoge.mount
-        sleep ".$RANDOM"
-        systemctl daemon-reexec
-
-        sleep 1
-
-        if [[ "$(systemctl is-failed tmp-hoge.mount)" == "failed" ]] || \
-           journalctl -u tmp-hoge.mount -q --grep "but there is no mount"; then
-                exit 1
-        fi
-
-        systemctl stop tmp-hoge.mount
-    done
-}
-
-systemd-analyze log-level debug
-systemd-analyze log-target journal
-
-NUM_DIRS=20
-
-# make sure we can handle mounts at very long paths such that mount unit name must be hashed to fall within our unit name limit
-LONGPATH="$(printf "/$(printf "x%0.s" {1..255})%0.s" {1..7})"
-LONGMNT="$(systemd-escape --suffix=mount --path "$LONGPATH")"
-TS="$(date '+%H:%M:%S')"
-
-mkdir -p "$LONGPATH"
-mount -t tmpfs tmpfs "$LONGPATH"
-systemctl daemon-reload
-
-# check that unit is active(mounted)
-systemctl --no-pager show -p SubState --value "$LONGPATH" | grep -q mounted
-
-# check that relevant part of journal doesn't contain any errors related to unit
-[ "$(journalctl -b --since="$TS" --priority=err | grep -c "$LONGMNT")" = "0" ]
-
-# check that we can successfully stop the mount unit
-systemctl stop "$LONGPATH"
-rm -rf "$LONGPATH"
-
-# mount/unmount enough times to trigger the /proc/self/mountinfo parsing rate limiting
-
-for ((i = 0; i < NUM_DIRS; i++)); do
-    mkdir "/tmp/meow${i}"
-done
-
-TS="$(date '+%H:%M:%S')"
-
-for ((i = 0; i < NUM_DIRS; i++)); do
-    mount -t tmpfs tmpfs "/tmp/meow${i}"
-done
-
-systemctl daemon-reload
-systemctl list-units -t mount tmp-meow* | grep -q tmp-meow
-
-for ((i = 0; i < NUM_DIRS; i++)); do
-    umount "/tmp/meow${i}"
-done
-
-# Figure out if we have entered the rate limit state.
-# If the infra is slow we might not enter the rate limit state; in that case skip the exit check.
-if timeout 2m bash -c "until journalctl -u init.scope --since=$TS | grep -q '(mount-monitor-dispatch) entered rate limit'; do sleep 1; done"; then
-    timeout 2m bash -c "until journalctl -u init.scope --since=$TS | grep -q '(mount-monitor-dispatch) left rate limit'; do sleep 1; done"
-fi
-
-# Verify that the mount units are always cleaned up at the end.
-# Give some time for units to settle so we don't race between exiting the rate limit state and cleaning up the units.
-timeout 2m bash -c 'while systemctl list-units -t mount tmp-meow* | grep -q tmp-meow; do systemctl daemon-reload; sleep 10; done'
-
-# test for issue #19983 and #23552.
-test_dependencies
-
-# test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited
-test_issue_20329
-
-# test for reexecuting with background mount job
-test_issue_23796
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-62-1.service b/test/units/testsuite-62-1.service
deleted file mode 100644 (file)
index 16695c1..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES-all-pings-work
-[Service]
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
-RestrictNetworkInterfaces=
-Type=oneshot
diff --git a/test/units/testsuite-62-2.service b/test/units/testsuite-62-2.service
deleted file mode 100644 (file)
index bce7e8e..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES-allow-list
-[Service]
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
-ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.9'
-RestrictNetworkInterfaces=veth0
-RestrictNetworkInterfaces=veth1
-Type=oneshot
diff --git a/test/units/testsuite-62-3.service b/test/units/testsuite-62-3.service
deleted file mode 100644 (file)
index 116530b..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES-deny-list
-[Service]
-ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.1'
-ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.5'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
-RestrictNetworkInterfaces=~veth0
-RestrictNetworkInterfaces=~veth1
-Type=oneshot
diff --git a/test/units/testsuite-62-4.service b/test/units/testsuite-62-4.service
deleted file mode 100644 (file)
index 200a383..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES-empty-assignment
-[Service]
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.1'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.9'
-RestrictNetworkInterfaces=veth0
-RestrictNetworkInterfaces=
-Type=oneshot
diff --git a/test/units/testsuite-62-5.service b/test/units/testsuite-62-5.service
deleted file mode 100644 (file)
index 51761ba..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES-invert-assignment
-[Service]
-ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.1'
-ExecStart=sh -c 'ping -c 1 -W 0.2 192.168.113.5'
-ExecStart=sh -c '! ping -c 1 -W 0.2 192.168.113.9'
-RestrictNetworkInterfaces=veth0
-RestrictNetworkInterfaces=veth0 veth1
-RestrictNetworkInterfaces=~veth0
-Type=oneshot
diff --git a/test/units/testsuite-62-6.service b/test/units/testsuite-62-6.service
deleted file mode 100644 (file)
index 876d8f3..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES-altname
-[Service]
-ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
-ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
-ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.9'
-RestrictNetworkInterfaces=veth0-altname-with-more-than-15-chars
-RestrictNetworkInterfaces=veth1-altname-with-more-than-15-chars
-Type=oneshot
diff --git a/test/units/testsuite-62.service b/test/units/testsuite-62.service
deleted file mode 100644 (file)
index 5c3f94d..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-62-RESTRICT-IFACES
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-62.sh b/test/units/testsuite-62.sh
deleted file mode 100755 (executable)
index 1fbb3cb..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-setup() {
-    systemd-analyze log-level debug
-
-    for i in {0..3};
-    do
-        ip netns del "ns${i}" || true
-        ip link del "veth${i}" || true
-        ip netns add "ns${i}"
-        ip link add "veth${i}" type veth peer name "veth${i}_"
-        ip link set "veth${i}_" netns "ns${i}"
-        ip -n "ns${i}" link set dev "veth${i}_" up
-        ip -n "ns${i}" link set dev lo up
-        ip -n "ns${i}" addr add "192.168.113."$((4*i+1))/30 dev "veth${i}_"
-        ip link set dev "veth${i}" up
-        ip link property add dev "veth${i}" altname "veth${i}-altname-with-more-than-15-chars"
-        ip addr add "192.168.113."$((4*i+2))/30 dev "veth${i}"
-    done
-}
-
-# shellcheck disable=SC2317
-teardown() {
-    set +e
-
-    for i in {0..3}; do
-        ip netns del "ns${i}"
-        ip link del "veth${i}"
-    done
-
-    systemd-analyze log-level info
-}
-
-if systemd-analyze compare-versions "$(uname -r)" lt 5.7; then
-    echo "kernel is not 5.7+" >>/skipped
-    exit 77
-fi
-
-if systemctl --version | grep -q -F -- "-BPF_FRAMEWORK"; then
-    echo "bpf-framework is disabled" >>/skipped
-    exit 77
-fi
-
-trap teardown EXIT
-setup
-
-systemctl start --wait testsuite-62-1.service
-systemctl start --wait testsuite-62-2.service
-systemctl start --wait testsuite-62-3.service
-systemctl start --wait testsuite-62-4.service
-systemctl start --wait testsuite-62-5.service
-
-touch /testok
diff --git a/test/units/testsuite-63.service b/test/units/testsuite-63.service
deleted file mode 100644 (file)
index 483c6a8..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-63-PATH
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-63.sh b/test/units/testsuite-63.sh
deleted file mode 100755 (executable)
index ea8cd94..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-systemctl log-level debug
-
-# Test that a path unit continuously triggering a service that fails condition checks eventually fails with
-# the trigger-limit-hit error.
-rm -f /tmp/nonexistent
-systemctl start test63.path
-touch /tmp/test63
-
-# Make sure systemd has sufficient time to hit the trigger limit for test63.path.
-# shellcheck disable=SC2016
-timeout 30 bash -c 'until test "$(systemctl show test63.path -P ActiveState)" = failed; do sleep .2; done'
-test "$(systemctl show test63.service -P ActiveState)" = inactive
-test "$(systemctl show test63.service -P Result)" = success
-test "$(systemctl show test63.path -P Result)" = trigger-limit-hit
-
-# Test that starting the service manually doesn't affect the path unit.
-rm -f /tmp/test63
-systemctl reset-failed
-systemctl start test63.path
-systemctl start test63.service
-test "$(systemctl show test63.service -P ActiveState)" = inactive
-test "$(systemctl show test63.service -P Result)" = success
-test "$(systemctl show test63.path -P ActiveState)" = active
-test "$(systemctl show test63.path -P Result)" = success
-
-# Test that glob matching works too, with $TRIGGER_PATH
-systemctl start test63-glob.path
-touch /tmp/test63-glob-foo
-timeout 60 bash -c 'until systemctl -q is-active test63-glob.service; do sleep .2; done'
-test "$(systemctl show test63-glob.service -P ActiveState)" = active
-test "$(systemctl show test63-glob.service -P Result)" = success
-
-test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[["trigger_unit","test63-glob.path"],["trigger_path","/tmp/test63-glob-foo"]]}'
-
-systemctl stop test63-glob.path test63-glob.service
-
-test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[]}'
-
-# tests for issue https://github.com/systemd/systemd/issues/24577#issuecomment-1522628906
-rm -f /tmp/hoge
-systemctl start test63-issue-24577.path
-systemctl status -n 0 test63-issue-24577.path
-systemctl status -n 0 test63-issue-24577.service || :
-systemctl list-jobs
-output=$(systemctl list-jobs --no-legend)
-assert_not_in "test63-issue-24577.service" "$output"
-assert_not_in "test63-issue-24577-dep.service" "$output"
-
-touch /tmp/hoge
-systemctl status -n 0 test63-issue-24577.path
-systemctl status -n 0 test63-issue-24577.service || :
-systemctl list-jobs
-output=$(systemctl list-jobs --no-legend)
-assert_in "test63-issue-24577.service" "$output"
-assert_in "test63-issue-24577-dep.service" "$output"
-
-# even if the service is stopped, it will be soon retriggered.
-systemctl stop test63-issue-24577.service
-systemctl status -n 0 test63-issue-24577.path
-systemctl status -n 0 test63-issue-24577.service || :
-systemctl list-jobs
-output=$(systemctl list-jobs --no-legend)
-assert_in "test63-issue-24577.service" "$output"
-assert_in "test63-issue-24577-dep.service" "$output"
-
-rm -f /tmp/hoge
-systemctl stop test63-issue-24577.service
-systemctl status -n 0 test63-issue-24577.path
-systemctl status -n 0 test63-issue-24577.service || :
-systemctl list-jobs
-output=$(systemctl list-jobs --no-legend)
-assert_not_in "test63-issue-24577.service" "$output"
-assert_in "test63-issue-24577-dep.service" "$output"
-
-# Test for race condition fixed by https://github.com/systemd/systemd/pull/30768
-# Here's the schedule of events that we to happen during this test:
-#       (This test)                     (The service)
-#                                       .path unit monitors /tmp/copyme for changes
-#       Take lock on /tmp/noexeit       ↓
-#       Write to /tmp/copyme            ↓
-#       Wait for deactivating           Started
-#       ↓                               Copies /tmp/copyme to /tmp/copied
-#       ↓                               Tells manager it's shutting down
-#       Ensure service did the copy     Tries to lock /tmp/noexit and blocks
-#       Write to /tmp/copyme            ↓
-#
-# Now at this point the test can diverge. If we regress, this second write is
-# missed and we'll see:
-#       ... (second write)              ... (blocked)
-#       Drop lock on /tmp/noexit        ↓
-#       Wait for service to do copy     Unblocks and exits
-#       ↓                               (dead)
-#       ↓
-#       (timeout)
-#       Test fails
-#
-# Otherwise, we'll see:
-#       ... (second write)              ... (blocked)
-#       Drop lock on /tmp/noexit        ↓ and .path unit queues a new start job
-#       Wait for service to do copy     Unblocks and exits
-#       ↓                               Starts again b/c of queued job
-#       ↓                               Copies again
-#       Test Passes
-systemctl start test63-pr-30768.path
-exec {lock}<>/tmp/noexit
-flock -e $lock
-echo test1 > /tmp/copyme
-# shellcheck disable=SC2016
-timeout 30 bash -c 'until test "$(systemctl show test63-pr-30768.service -P ActiveState)" = deactivating; do sleep .2; done'
-diff /tmp/copyme /tmp/copied
-echo test2 > /tmp/copyme
-exec {lock}<&-
-timeout 30 bash -c 'until diff /tmp/copyme /tmp/copied; do sleep .2; done'
-
-systemctl log-level info
-
-touch /testok
diff --git a/test/units/testsuite-64.service b/test/units/testsuite-64.service
deleted file mode 100644 (file)
index f75a3d7..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-64-UDEV
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-64.sh b/test/units/testsuite-64.sh
deleted file mode 100755 (executable)
index 120b5f3..0000000
+++ /dev/null
@@ -1,1223 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# vi: ts=4 sw=4 tw=0 et:
-
-set -eux
-set -o pipefail
-
-# Check if all symlinks under /dev/disk/ are valid
-# shellcheck disable=SC2120
-helper_check_device_symlinks() {(
-    set +x
-
-    local dev link path paths target
-
-    [[ $# -gt 0 ]] && paths=("$@") || paths=("/dev/disk" "/dev/mapper")
-
-    # Check if all given paths are valid
-    for path in "${paths[@]}"; do
-        if ! test -e "$path"; then
-            echo >&2 "Path '$path' doesn't exist"
-            return 1
-        fi
-    done
-
-    while read -r link; do
-        target="$(readlink -f "$link")"
-        # Both checks should do virtually the same thing, but check both to be
-        # on the safe side
-        if [[ ! -e "$link" || ! -e "$target" ]]; then
-            echo >&2 "ERROR: symlink '$link' points to '$target' which doesn't exist"
-            return 1
-        fi
-
-        # Check if the symlink points to the correct device in /dev
-        dev="/dev/$(udevadm info -q name "$link")"
-        if [[ "$target" != "$dev" ]]; then
-            echo >&2 "ERROR: symlink '$link' points to '$target' but '$dev' was expected"
-            return 1
-        fi
-    done < <(find "${paths[@]}" -type l)
-)}
-
-helper_check_udev_watch() {(
-    set +x
-
-    local link target id dev
-
-    while read -r link; do
-        target="$(readlink "$link")"
-        if [[ ! -L "/run/udev/watch/$target" ]]; then
-            echo >&2 "ERROR: symlink /run/udev/watch/$target does not exist"
-            return 1
-        fi
-        if [[ "$(readlink "/run/udev/watch/$target")" != "$(basename "$link")" ]]; then
-            echo >&2 "ERROR: symlink target of /run/udev/watch/$target is inconsistent with $link"
-            return 1
-        fi
-
-        if [[ "$target" =~ ^[0-9]+$ ]]; then
-            # $link is ID -> wd
-            id="$(basename "$link")"
-        else
-            # $link is wd -> ID
-            id="$target"
-        fi
-
-        if [[ "${id:0:1}" == "b" ]]; then
-            dev="/dev/block/${id:1}"
-        elif [[ "${id:0:1}" == "c" ]]; then
-            dev="/dev/char/${id:1}"
-        else
-            echo >&2 "ERROR: unexpected device ID '$id'"
-            return 1
-        fi
-
-        if [[ ! -e "$dev" ]]; then
-            echo >&2 "ERROR: device '$dev' corresponding to symlink '$link' does not exist"
-            return 1
-        fi
-    done < <(find /run/udev/watch -type l)
-)}
-
-check_device_unit() {(
-    set +x
-
-    local log_level link links path syspath unit
-
-    log_level="${1?}"
-    path="${2?}"
-    unit=$(systemd-escape --path --suffix=device "$path")
-
-    [[ "$log_level" == 1 ]] && echo "INFO: check_device_unit($unit)"
-
-    syspath=$(systemctl show --value --property SysFSPath "$unit" 2>/dev/null)
-    if [[ -z "$syspath" ]]; then
-        [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit not found."
-        return 1
-    fi
-
-    if [[ ! -L "$path" ]]; then
-        if [[ ! -d "$syspath" ]]; then
-            [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit exists for $syspath but it does not exist."
-            return 1
-        fi
-        return 0
-    fi
-
-    if [[ ! -b "$path" && ! -c "$path" ]]; then
-        [[ "$log_level" == 1 ]] && echo >&2 "ERROR: invalid file type $path"
-        return 1
-    fi
-
-    read -r -a links < <(udevadm info -q symlink "$syspath" 2>/dev/null)
-    for link in "${links[@]}"; do
-        if [[ "/dev/$link" == "$path" ]]; then # DEVLINKS= given by -q symlink are relative to /dev
-            return 0
-        fi
-    done
-
-    read -r -a links < <(udevadm info "$syspath" | sed -ne '/SYSTEMD_ALIAS=/ { s/^E: SYSTEMD_ALIAS=//; p }' 2>/dev/null)
-    for link in "${links[@]}"; do
-        if [[ "$link" == "$path" ]]; then # SYSTEMD_ALIAS= are absolute
-            return 0
-        fi
-    done
-
-    [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit exists for $syspath but it does not have the corresponding DEVLINKS or SYSTEMD_ALIAS."
-    return 1
-)}
-
-check_device_units() {(
-    set +x
-
-    local log_level path paths
-
-    log_level="${1?}"
-    shift
-    paths=("$@")
-
-    for path in "${paths[@]}"; do
-        if ! check_device_unit "$log_level" "$path"; then
-           return 1
-        fi
-    done
-
-    while read -r unit _; do
-        path=$(systemd-escape --path --unescape "$unit")
-        if ! check_device_unit "$log_level" "$path"; then
-           return 1
-        fi
-    done < <(systemctl list-units --all --type=device --no-legend dev-* | awk '$1 !~ /dev-tty.+/ && $4 == "plugged" { print $1 }' | sed -e 's/\.device$//')
-
-    return 0
-)}
-
-helper_check_device_units() {(
-    set +x
-
-    local i
-
-    for i in {1..20}; do
-        (( i > 1 )) && sleep 0.5
-        if check_device_units 0 "$@"; then
-            return 0
-        fi
-    done
-
-    check_device_units 1 "$@"
-)}
-
-testcase_megasas2_basic() {
-    lsblk -S
-    [[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]]
-}
-
-testcase_nvme_basic() {
-    local expected_symlinks=()
-    local i
-
-    for (( i = 0; i < 5; i++ )); do
-        expected_symlinks+=(
-            # both replace mode provides the same devlink
-            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef"$i"
-            # with nsid
-            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef"$i"_1
-        )
-    done
-    for (( i = 5; i < 10; i++ )); do
-        expected_symlinks+=(
-            # old replace mode
-            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl__deadbeef_"$i"
-            # newer replace mode
-            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____deadbeef__"$i"
-            # with nsid
-            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____deadbeef__"$i"_1
-        )
-    done
-    for (( i = 10; i < 15; i++ )); do
-        expected_symlinks+=(
-            # old replace mode does not provide devlink, as serial contains "/"
-            # newer replace mode
-            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____dead_beef_"$i"
-            # with nsid
-            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____dead_beef_"$i"_1
-        )
-    done
-    for (( i = 15; i < 20; i++ )); do
-        expected_symlinks+=(
-            # old replace mode does not provide devlink, as serial contains "/"
-            # newer replace mode
-            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_dead_.._.._beef_"$i"
-            # with nsid
-            /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_dead_.._.._beef_"$i"_1
-        )
-    done
-
-    udevadm settle
-    ls /dev/disk/by-id
-    for i in "${expected_symlinks[@]}"; do
-        udevadm wait --settle --timeout=30 "$i"
-    done
-
-    lsblk --noheadings | grep "^nvme"
-    [[ "$(lsblk --noheadings | grep -c "^nvme")" -ge 20 ]]
-}
-
-testcase_nvme_subsystem() {
-    local expected_symlinks=(
-        # Controller(s)
-        /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef
-        /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_16
-        /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_17
-        # Shared namespaces
-        /dev/disk/by-path/pci-*-nvme-16
-        /dev/disk/by-path/pci-*-nvme-17
-    )
-
-    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-}
-
-testcase_virtio_scsi_identically_named_partitions() {
-    local num_part num_disk i j
-    local alphabet='abcdefghijklmnopqrstuvwxyz'
-
-    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
-        num_part=4
-        num_disk=4
-    else
-        num_part=8
-        num_disk=16
-    fi
-
-    for ((i = 0; i < num_disk; i++)); do
-        sfdisk "/dev/sd${alphabet:$i:1}" <<EOF
-label: gpt
-
-$(for ((j = 1; j <= num_part; j++)); do echo 'name="Hello world", size=2M'; done)
-EOF
-    done
-
-    lsblk --noheadings -a -o NAME,PARTLABEL
-    [[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq "$((num_part * num_disk))" ]]
-}
-
-testcase_multipath_basic_failover() {
-    local dmpath i path wwid
-
-    # Configure multipath
-    cat >/etc/multipath.conf <<\EOF
-defaults {
-    # Use /dev/mapper/$WWN paths instead of /dev/mapper/mpathX
-    user_friendly_names no
-    find_multipaths yes
-    enable_foreign "^$"
-}
-
-blacklist_exceptions {
-    property "(SCSI_IDENT_|ID_WWN)"
-}
-
-blacklist {
-}
-EOF
-
-    sfdisk /dev/sda <<EOF
-label: gpt
-
-name="first_partition", size=5M
-uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M
-EOF
-    udevadm settle
-    mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" "/dev/sda2"
-
-    modprobe -v dm_multipath
-    systemctl start multipathd.service
-    systemctl status multipathd.service
-    multipath -ll
-    udevadm settle
-    ls -l /dev/disk/by-id/
-
-    for i in {0..15}; do
-        wwid="deaddeadbeef$(printf "%.4d" "$i")"
-        path="/dev/disk/by-id/wwn-0x$wwid"
-        dmpath="$(readlink -f "$path")"
-
-        lsblk "$path"
-        multipath -C "$dmpath"
-        # We should have 4 active paths for each multipath device
-        [[ "$(multipath -l "$path" | grep -c running)" -eq 4 ]]
-    done
-
-    # Test failover (with the first multipath device that has a partitioned disk)
-    echo "${FUNCNAME[0]}: test failover"
-    local device expected link mpoint part
-    local -a devices
-    mkdir -p /mnt
-    mpoint="$(mktemp -d /mnt/mpathXXX)"
-    wwid="deaddeadbeef0000"
-    path="/dev/disk/by-id/wwn-0x$wwid"
-
-    # All following symlinks should exists and should be valid
-    local -a part_links=(
-        "/dev/disk/by-id/wwn-0x$wwid-part2"
-        "/dev/disk/by-partlabel/failover_part"
-        "/dev/disk/by-partuuid/deadbeef-dead-dead-beef-000000000000"
-        "/dev/disk/by-label/failover_vol"
-        "/dev/disk/by-uuid/deadbeef-dead-dead-beef-111111111111"
-    )
-    udevadm wait --settle --timeout=30 "${part_links[@]}"
-    helper_check_device_units "${part_links[@]}"
-
-    # Choose a random symlink to the failover data partition each time, for
-    # a better coverage
-    part="${part_links[$RANDOM % ${#part_links[@]}]}"
-
-    # Get all devices attached to a specific multipath device (in H:C:T:L format)
-    # and sort them in a random order, so we cut off different paths each time
-    mapfile -t devices < <(multipath -l "$path" | grep -Eo '[0-9]+:[0-9]+:[0-9]+:[0-9]+' | sort -R)
-    if [[ "${#devices[@]}" -ne 4 ]]; then
-        echo "Expected 4 devices attached to WWID=$wwid, got ${#devices[@]} instead"
-        return 1
-    fi
-    # Drop the last path from the array, since we want to leave at least one path active
-    unset "devices[3]"
-    # Mount the first multipath partition, write some data we can check later,
-    # and then disconnect the remaining paths one by one while checking if we
-    # can still read/write from the mount
-    mount -t ext4 "$part" "$mpoint"
-    expected=0
-    echo -n "$expected" >"$mpoint/test"
-    # Sanity check we actually wrote what we wanted
-    [[ "$(<"$mpoint/test")" == "$expected" ]]
-
-    for device in "${devices[@]}"; do
-        echo offline >"/sys/class/scsi_device/$device/device/state"
-        [[ "$(<"$mpoint/test")" == "$expected" ]]
-        expected="$((expected + 1))"
-        echo -n "$expected" >"$mpoint/test"
-
-        # Make sure all symlinks are still valid
-        udevadm wait --settle --timeout=30 "${part_links[@]}"
-        helper_check_device_units "${part_links[@]}"
-    done
-
-    multipath -l "$path"
-    # Three paths should be now marked as 'offline' and one as 'running'
-    [[ "$(multipath -l "$path" | grep -c offline)" -eq 3 ]]
-    [[ "$(multipath -l "$path" | grep -c running)" -eq 1 ]]
-
-    umount "$mpoint"
-    rm -fr "$mpoint"
-}
-
-testcase_simultaneous_events_1() {
-    local disk expected i iterations key link num_part part partscript rule target timeout
-    local -a devices symlinks
-    local -A running
-
-    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
-        num_part=2
-        iterations=10
-        timeout=240
-    else
-        num_part=10
-        iterations=100
-        timeout=30
-    fi
-
-    for disk in {0..9}; do
-        link="/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_deadbeeftest${disk}"
-        target="$(readlink -f "$link")"
-        if [[ ! -b "$target" ]]; then
-            echo "ERROR: failed to find the test SCSI block device $link"
-            return 1
-        fi
-
-        devices+=("$target")
-    done
-
-    for ((part = 1; part <= num_part; part++)); do
-        symlinks+=(
-            "/dev/disk/by-partlabel/test${part}"
-        )
-    done
-
-    partscript="$(mktemp)"
-
-    cat >"$partscript" <<EOF
-$(for ((part = 1; part <= num_part; part++)); do printf 'name="test%d", size=2M\n' "$part"; done)
-EOF
-
-    rule=/run/udev/rules.d/50-test.rules
-    mkdir -p "${rule%/*}"
-    cat >"$rule" <<EOF
-SUBSYSTEM=="block", KERNEL=="${devices[4]##*/}*|${devices[5]##*/}*", OPTIONS="link_priority=10"
-EOF
-
-    udevadm control --reload
-
-    # initialize partition table
-    for disk in {0..9}; do
-        echo 'label: gpt' | udevadm lock --device="${devices[$disk]}" sfdisk -q "${devices[$disk]}"
-    done
-
-    # Delete the partitions, immediately recreate them, wait for udev to settle
-    # down, and then check if we have any dangling symlinks in /dev/disk/. Rinse
-    # and repeat.
-    #
-    # On unpatched udev versions the delete-recreate cycle may trigger a race
-    # leading to dead symlinks in /dev/disk/
-    for ((i = 1; i <= iterations; i++)); do
-        for disk in {0..9}; do
-            if ((disk % 2 == i % 2)); then
-                udevadm lock --device="${devices[$disk]}" sfdisk -q --delete "${devices[$disk]}" &
-            else
-                udevadm lock --device="${devices[$disk]}" sfdisk -q -X gpt "${devices[$disk]}" <"$partscript" &
-            fi
-            running[$disk]=$!
-        done
-
-        for key in "${!running[@]}"; do
-            wait "${running[$key]}"
-            unset "running[$key]"
-        done
-
-        if ((i % 10 <= 1)); then
-            udevadm wait --settle --timeout="$timeout" "${devices[@]}" "${symlinks[@]}"
-            helper_check_device_symlinks
-            helper_check_udev_watch
-            for ((part = 1; part <= num_part; part++)); do
-                link="/dev/disk/by-partlabel/test${part}"
-                target="$(readlink -f "$link")"
-                if ((i % 2 == 0)); then
-                    expected="${devices[5]}$part"
-                else
-                    expected="${devices[4]}$part"
-                fi
-                if [[ "$target" != "$expected" ]]; then
-                    echo >&2 "ERROR: symlink '/dev/disk/by-partlabel/test${part}' points to '$target' but '$expected' was expected"
-                    return 1
-                fi
-            done
-        fi
-    done
-
-    helper_check_device_units
-    rm -f "$rule" "$partscript"
-
-    udevadm control --reload
-}
-
-testcase_simultaneous_events_2() {
-    local disk expected i iterations key link num_part part script_dir target timeout
-    local -a devices symlinks
-    local -A running
-
-    script_dir="$(mktemp --directory "/tmp/test-udev-storage.script.XXXXXXXXXX")"
-    # shellcheck disable=SC2064
-    trap "rm -rf '$script_dir'" RETURN
-
-    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
-        num_part=20
-        iterations=1
-        timeout=2400
-    else
-        num_part=100
-        iterations=3
-        timeout=300
-    fi
-
-    for disk in {0..9}; do
-        link="/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_deadbeeftest${disk}"
-        target="$(readlink -f "$link")"
-        if [[ ! -b "$target" ]]; then
-            echo "ERROR: failed to find the test SCSI block device $link"
-            return 1
-        fi
-
-        devices+=("$target")
-    done
-
-    for ((i = 1; i <= iterations; i++)); do
-        cat >"$script_dir/partscript-$i" <<EOF
-$(for ((part = 1; part <= num_part; part++)); do printf 'name="testlabel-%d", size=1M\n' "$i"; done)
-EOF
-    done
-
-    echo "## $iterations iterations start: $(date '+%H:%M:%S.%N')"
-    for ((i = 1; i <= iterations; i++)); do
-
-        for disk in {0..9}; do
-            udevadm lock --device="${devices[$disk]}" sfdisk -q --delete "${devices[$disk]}" &
-            running[$disk]=$!
-        done
-
-        for key in "${!running[@]}"; do
-            wait "${running[$key]}"
-            unset "running[$key]"
-        done
-
-        for disk in {0..9}; do
-            udevadm lock --device="${devices[$disk]}" sfdisk -q -X gpt "${devices[$disk]}" <"$script_dir/partscript-$i" &
-            running[$disk]=$!
-        done
-
-        for key in "${!running[@]}"; do
-            wait "${running[$key]}"
-            unset "running[$key]"
-        done
-
-        udevadm wait --settle --timeout="$timeout" "${devices[@]}" "/dev/disk/by-partlabel/testlabel-$i"
-    done
-    echo "## $iterations iterations end: $(date '+%H:%M:%S.%N')"
-}
-
-testcase_simultaneous_events() {
-    testcase_simultaneous_events_1
-    testcase_simultaneous_events_2
-}
-
-testcase_lvm_basic() {
-    local i iterations partitions part timeout
-    local vgroup="MyTestGroup$RANDOM"
-    local devices=(
-        /dev/disk/by-id/ata-foobar_deadbeeflvm{0..3}
-    )
-
-    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
-        timeout=180
-    else
-        timeout=30
-    fi
-    # Make sure all the necessary soon-to-be-LVM devices exist
-    ls -l "${devices[@]}"
-
-    # Add all test devices into a volume group, create two logical volumes,
-    # and check if necessary symlinks exist (and are valid)
-    lvm pvcreate -y "${devices[@]}"
-    lvm pvs
-    lvm vgcreate "$vgroup" -y "${devices[@]}"
-    lvm vgs
-    lvm vgchange -ay "$vgroup"
-    lvm lvcreate -y -L 4M "$vgroup" -n mypart1
-    lvm lvcreate -y -L 32M "$vgroup" -n mypart2
-    lvm lvs
-    udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
-    mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1"
-    udevadm wait --settle --timeout="$timeout" "/dev/disk/by-label/mylvpart1"
-    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
-    helper_check_device_units
-
-    # Mount mypart1 through by-label devlink
-    mkdir -p /tmp/mypart1-mount-point
-    mount /dev/disk/by-label/mylvpart1 /tmp/mypart1-mount-point
-    timeout 30 bash -c "until systemctl -q is-active /tmp/mypart1-mount-point; do sleep .2; done"
-    # Extend the partition and check if the device and mount units are still active.
-    # See https://bugzilla.redhat.com/show_bug.cgi?id=2158628
-    # Note, the test below may be unstable with LVM2 without the following patch:
-    # https://github.com/lvmteam/lvm2/pull/105
-    # But, to reproduce the issue, udevd must start to process the first 'change' uevent
-    # earlier than extending the volume has been finished, and in most case, the extension
-    # is hopefully fast.
-    lvm lvextend -y --size 8M "/dev/$vgroup/mypart1"
-    udevadm wait --settle --timeout="$timeout" "/dev/disk/by-label/mylvpart1"
-    timeout 30 bash -c "until systemctl -q is-active '/dev/$vgroup/mypart1'; do sleep .2; done"
-    timeout 30 bash -c "until systemctl -q is-active /tmp/mypart1-mount-point; do sleep .2; done"
-    # Umount the partition, otherwise the underlying device unit will stay in
-    # the inactive state and not be collected, and helper_check_device_units() will fail.
-    systemctl show /tmp/mypart1-mount-point
-    umount /tmp/mypart1-mount-point
-
-    # Rename partitions (see issue #24518)
-    lvm lvrename "/dev/$vgroup/mypart1" renamed1
-    lvm lvrename "/dev/$vgroup/mypart2" renamed2
-    udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
-    udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/renamed1" "/dev/$vgroup/renamed2"
-    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
-    helper_check_device_units
-
-    # Rename them back
-    lvm lvrename "/dev/$vgroup/renamed1" mypart1
-    lvm lvrename "/dev/$vgroup/renamed2" mypart2
-    udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/renamed1" "/dev/$vgroup/renamed2"
-    udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
-    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
-    helper_check_device_units
-
-    # Do not "unready" suspended encrypted devices w/o superblock info
-    # See:
-    #   - https://github.com/systemd/systemd/pull/24177
-    #   - https://bugzilla.redhat.com/show_bug.cgi?id=1985288
-    dd if=/dev/urandom of=/etc/lvm_keyfile bs=64 count=1 iflag=fullblock
-    chmod 0600 /etc/lvm_keyfile
-    # Intentionally use weaker cipher-related settings, since we don't care
-    # about security here as it's a throwaway LUKS partition
-    cryptsetup luksFormat -q --use-urandom --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \
-                          "/dev/$vgroup/mypart2" /etc/lvm_keyfile
-    # Mount the LUKS partition & create a filesystem on it
-    mkdir -p /tmp/lvmluksmnt
-    cryptsetup open --key-file=/etc/lvm_keyfile "/dev/$vgroup/mypart2" "lvmluksmap"
-    udevadm wait --settle --timeout="$timeout" "/dev/mapper/lvmluksmap"
-    mkfs.ext4 -L lvmluksfs "/dev/mapper/lvmluksmap"
-    udevadm wait --settle --timeout="$timeout" "/dev/disk/by-label/lvmluksfs"
-    # Make systemd "interested" in the mount by adding it to /etc/fstab
-    echo "/dev/disk/by-label/lvmluksfs /tmp/lvmluksmnt ext4 defaults 0 2" >>/etc/fstab
-    systemctl daemon-reload
-    mount "/tmp/lvmluksmnt"
-    mountpoint "/tmp/lvmluksmnt"
-    # Temporarily suspend the LUKS device and trigger udev - basically what `cryptsetup resize`
-    # does but in a more deterministic way suitable for a test/reproducer
-    for _ in {0..5}; do
-        dmsetup suspend "/dev/mapper/lvmluksmap"
-        udevadm trigger -v --settle "/dev/mapper/lvmluksmap"
-        dmsetup resume "/dev/mapper/lvmluksmap"
-        # The mount should survive this sequence of events
-        mountpoint "/tmp/lvmluksmnt"
-    done
-    # Cleanup
-    umount "/tmp/lvmluksmnt"
-    cryptsetup close "/dev/mapper/lvmluksmap"
-    sed -i "/lvmluksfs/d" "/etc/fstab"
-    systemctl daemon-reload
-
-    # Disable the VG and check symlinks...
-    lvm vgchange -an "$vgroup"
-    udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup" "/dev/disk/by-label/mylvpart1"
-    helper_check_device_symlinks "/dev/disk"
-    helper_check_device_units
-
-    # reenable the VG and check the symlinks again if all LVs are properly activated
-    lvm vgchange -ay "$vgroup"
-    udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1"
-    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
-    helper_check_device_units
-
-    # Same as above, but now with more "stress"
-    if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
-        iterations=10
-    else
-        iterations=50
-    fi
-
-    for ((i = 1; i <= iterations; i++)); do
-        lvm vgchange -an "$vgroup"
-        lvm vgchange -ay "$vgroup"
-
-        if ((i % 5 == 0)); then
-            udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1"
-            helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
-            helper_check_device_units
-        fi
-    done
-
-    # Remove the first LV
-    lvm lvremove -y "$vgroup/mypart1"
-    udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/mypart1"
-    udevadm wait --timeout=0 "/dev/$vgroup/mypart2"
-    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
-    helper_check_device_units
-
-    # Create & remove LVs in a loop, i.e. with more "stress"
-    if [[ -v ASAN_OPTIONS ]]; then
-        iterations=8
-        partitions=16
-    elif [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then
-        iterations=8
-        partitions=8
-    else
-        iterations=16
-        partitions=16
-    fi
-
-    for ((i = 1; i <= iterations; i++)); do
-        # 1) Create some logical volumes
-        for ((part = 0; part < partitions; part++)); do
-            lvm lvcreate -y -L 4M "$vgroup" -n "looppart$part"
-        done
-
-        # 2) Immediately remove them
-        lvm lvremove -y $(seq -f "$vgroup/looppart%g" 0 "$((partitions - 1))")
-
-        # 3) On every 4th iteration settle udev and check if all partitions are
-        #    indeed gone, and if all symlinks are still valid
-        if ((i % 4 == 0)); then
-            for ((part = 0; part < partitions; part++)); do
-                udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/looppart$part"
-            done
-            helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
-            helper_check_device_units
-        fi
-    done
-}
-
-testcase_btrfs_basic() {
-    local dev_stub i label mpoint uuid
-    local devices=(
-        /dev/disk/by-id/ata-foobar_deadbeefbtrfs{0..3}
-    )
-
-    ls -l "${devices[@]}"
-
-    echo "Single device: default settings"
-    uuid="deadbeef-dead-dead-beef-000000000000"
-    label="btrfs_root"
-    udevadm lock --device="${devices[0]}" mkfs.btrfs -f -L "$label" -U "$uuid" "${devices[0]}"
-    udevadm wait --settle --timeout=30 "${devices[0]}" "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
-    btrfs filesystem show
-    helper_check_device_symlinks
-    helper_check_device_units
-
-    echo "Multiple devices: using partitions, data: single, metadata: raid1"
-    uuid="deadbeef-dead-dead-beef-000000000001"
-    label="btrfs_mpart"
-    udevadm lock --device="${devices[0]}" sfdisk --wipe=always "${devices[0]}" <<EOF
-label: gpt
-
-name="diskpart1", size=85M
-name="diskpart2", size=85M
-name="diskpart3", size=85M
-name="diskpart4", size=85M
-EOF
-    udevadm wait --settle --timeout=30 /dev/disk/by-partlabel/diskpart{1..4}
-    udevadm lock --device="${devices[0]}" mkfs.btrfs -f -d single -m raid1 -L "$label" -U "$uuid" /dev/disk/by-partlabel/diskpart{1..4}
-    udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
-    btrfs filesystem show
-    helper_check_device_symlinks
-    helper_check_device_units
-    wipefs -a -f "${devices[0]}"
-    udevadm wait --settle --timeout=30 --removed /dev/disk/by-partlabel/diskpart{1..4}
-
-    echo "Multiple devices: using disks, data: raid10, metadata: raid10, mixed mode"
-    uuid="deadbeef-dead-dead-beef-000000000002"
-    label="btrfs_mdisk"
-    udevadm lock \
-            --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs0 \
-            --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs1 \
-            --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs2 \
-            --device=/dev/disk/by-id/ata-foobar_deadbeefbtrfs3 \
-            mkfs.btrfs -f -M -d raid10 -m raid10 -L "$label" -U "$uuid" "${devices[@]}"
-    udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
-    btrfs filesystem show
-    helper_check_device_symlinks
-    helper_check_device_units
-
-    echo "Multiple devices: using LUKS encrypted disks, data: raid1, metadata: raid1, mixed mode"
-    uuid="deadbeef-dead-dead-beef-000000000003"
-    label="btrfs_mencdisk"
-    mpoint="/btrfs_enc$RANDOM"
-    mkdir "$mpoint"
-    # Create a key-file
-    dd if=/dev/urandom of=/etc/btrfs_keyfile bs=64 count=1 iflag=fullblock
-    chmod 0600 /etc/btrfs_keyfile
-    # Encrypt each device and add it to /etc/crypttab, so it can be mounted
-    # automagically later
-    : >/etc/crypttab
-    for ((i = 0; i < ${#devices[@]}; i++)); do
-        # Intentionally use weaker cipher-related settings, since we don't care
-        # about security here as it's a throwaway LUKS partition
-        cryptsetup luksFormat -q \
-            --use-urandom --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \
-            --uuid "deadbeef-dead-dead-beef-11111111111$i" --label "encdisk$i" "${devices[$i]}" /etc/btrfs_keyfile
-        udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/deadbeef-dead-dead-beef-11111111111$i" "/dev/disk/by-label/encdisk$i"
-        # Add the device into /etc/crypttab, reload systemd, and then activate
-        # the device so we can create a filesystem on it later
-        echo "encbtrfs$i UUID=deadbeef-dead-dead-beef-11111111111$i /etc/btrfs_keyfile luks" >>/etc/crypttab
-        systemctl daemon-reload
-        systemctl start "systemd-cryptsetup@encbtrfs$i"
-    done
-    helper_check_device_symlinks
-    helper_check_device_units
-    # Check if we have all necessary DM devices
-    ls -l /dev/mapper/encbtrfs{0..3}
-    # Create a multi-device btrfs filesystem on the LUKS devices
-    udevadm lock \
-            --device=/dev/mapper/encbtrfs0 \
-            --device=/dev/mapper/encbtrfs1 \
-            --device=/dev/mapper/encbtrfs2 \
-            --device=/dev/mapper/encbtrfs3 \
-            mkfs.btrfs -f -M -d raid1 -m raid1 -L "$label" -U "$uuid" /dev/mapper/encbtrfs{0..3}
-    udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
-    btrfs filesystem show
-    helper_check_device_symlinks
-    helper_check_device_units
-    # Mount it and write some data to it we can compare later
-    mount -t btrfs /dev/mapper/encbtrfs0 "$mpoint"
-    echo "hello there" >"$mpoint/test"
-    # "Deconstruct" the btrfs device and check if we're in a sane state (symlink-wise)
-    umount "$mpoint"
-    systemctl stop systemd-cryptsetup@encbtrfs{0..3}
-    udevadm wait --settle --timeout=30 --removed "/dev/disk/by-uuid/$uuid"
-    helper_check_device_symlinks
-    helper_check_device_units
-    # Add the mount point to /etc/fstab and check if the device can be put together
-    # automagically. The source device is the DM name of the first LUKS device
-    # (from /etc/crypttab). We have to specify all LUKS devices manually, as
-    # registering the necessary devices is usually initrd's job (via btrfs device scan)
-    dev_stub="/dev/mapper/encbtrfs"
-    echo "/dev/mapper/encbtrfs0 $mpoint btrfs device=${dev_stub}0,device=${dev_stub}1,device=${dev_stub}2,device=${dev_stub}3 0 2" >>/etc/fstab
-    # Tell systemd about the new mount
-    systemctl daemon-reload
-    # Restart cryptsetup.target to trigger autounlock of partitions in /etc/crypttab
-    systemctl restart cryptsetup.target
-    # Start the corresponding mount unit and check if the btrfs device was reconstructed
-    # correctly
-    systemctl start "${mpoint##*/}.mount"
-    udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label"
-    btrfs filesystem show
-    helper_check_device_symlinks
-    helper_check_device_units
-    grep "hello there" "$mpoint/test"
-    # Cleanup
-    systemctl stop "${mpoint##*/}.mount"
-    systemctl stop systemd-cryptsetup@encbtrfs{0..3}
-    sed -i "/${mpoint##*/}/d" /etc/fstab
-    : >/etc/crypttab
-    rm -fr "$mpoint"
-    systemctl daemon-reload
-    udevadm settle
-}
-
-testcase_iscsi_lvm() {
-    local dev i label link lun_id mpoint target_name uuid
-    local target_ip="127.0.0.1"
-    local target_port="3260"
-    local vgroup="iscsi_lvm$RANDOM"
-    local expected_symlinks=()
-    local devices=(
-        /dev/disk/by-id/ata-foobar_deadbeefiscsi{0..3}
-    )
-
-    ls -l "${devices[@]}"
-
-    # Start the target daemon
-    systemctl start tgtd
-    systemctl status tgtd
-
-    echo "iSCSI LUNs backed by devices"
-    # See RFC3721 and RFC7143
-    target_name="iqn.2021-09.com.example:iscsi.test"
-    # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each
-    # backed by a device
-    tgtadm --lld iscsi --op new --mode target --tid=1 --targetname "$target_name"
-    for ((i = 0; i < ${#devices[@]}; i++)); do
-        # lun-0 is reserved by iSCSI
-        lun_id="$((i + 1))"
-        tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun "$lun_id" -b "${devices[$i]}"
-        tgtadm --lld iscsi --op update --mode logicalunit --tid 1 --lun "$lun_id"
-        expected_symlinks+=(
-            "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$lun_id"
-        )
-    done
-    tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL
-    # Configure the iSCSI initiator
-    iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
-    iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
-    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-    helper_check_device_symlinks
-    helper_check_device_units
-    # Cleanup
-    iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
-    tgtadm --lld iscsi --op delete --mode target --tid=1
-
-    echo "iSCSI LUNs backed by files + LVM"
-    # Note: we use files here to "trick" LVM the disks are indeed on a different
-    #       host, so it doesn't automagically detect another path to the backing
-    #       device once we disconnect the iSCSI devices
-    target_name="iqn.2021-09.com.example:iscsi.lvm.test"
-    mpoint="$(mktemp -d /iscsi_storeXXX)"
-    expected_symlinks=()
-    # Use the first device as it's configured with larger capacity
-    mkfs.ext4 -L iscsi_store "${devices[0]}"
-    udevadm wait --settle --timeout=30 "${devices[0]}"
-    mount "${devices[0]}" "$mpoint"
-    for i in {1..4}; do
-        dd if=/dev/zero of="$mpoint/lun$i.img" bs=1M count=32
-    done
-    # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each
-    # backed by a file
-    tgtadm --lld iscsi --op new --mode target --tid=2 --targetname "$target_name"
-    # lun-0 is reserved by iSCSI
-    for i in {1..4}; do
-        tgtadm --lld iscsi --op new --mode logicalunit --tid 2 --lun "$i" -b "$mpoint/lun$i.img"
-        tgtadm --lld iscsi --op update --mode logicalunit --tid 2 --lun "$i"
-        expected_symlinks+=(
-            "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$i"
-        )
-    done
-    tgtadm --lld iscsi --op bind --mode target --tid 2 -I ALL
-    # Configure the iSCSI initiator
-    iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
-    iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
-    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-    helper_check_device_symlinks
-    helper_check_device_units
-    # Add all iSCSI devices into a LVM volume group, create two logical volumes,
-    # and check if necessary symlinks exist (and are valid)
-    lvm pvcreate -y "${expected_symlinks[@]}"
-    lvm pvs
-    lvm vgcreate "$vgroup" -y "${expected_symlinks[@]}"
-    lvm vgs
-    lvm vgchange -ay "$vgroup"
-    lvm lvcreate -y -L 4M "$vgroup" -n mypart1
-    lvm lvcreate -y -L 8M "$vgroup" -n mypart2
-    lvm lvs
-    udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
-    mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1"
-    udevadm wait --settle --timeout=30 "/dev/disk/by-label/mylvpart1"
-    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
-    helper_check_device_units
-    # Disconnect the iSCSI devices and check all the symlinks
-    iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
-    # "Reset" the DM state, since we yanked the backing storage from under the LVM,
-    # so the currently active VGs/LVs are invalid
-    dmsetup remove_all --deferred
-    # The LVM and iSCSI related symlinks should be gone
-    udevadm wait --settle --timeout=30 --removed "/dev/$vgroup" "/dev/disk/by-label/mylvpart1" "${expected_symlinks[@]}"
-    helper_check_device_symlinks "/dev/disk"
-    helper_check_device_units
-    # Reconnect the iSCSI devices and check if everything get detected correctly
-    iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
-    iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
-    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1"
-    helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
-    helper_check_device_units
-    # Cleanup
-    iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
-    tgtadm --lld iscsi --op delete --mode target --tid=2
-    umount "$mpoint"
-    rm -rf "$mpoint"
-}
-
-testcase_long_sysfs_path() {
-    local cursor link logfile mpoint
-    local expected_symlinks=(
-        "/dev/disk/by-label/data_vol"
-        "/dev/disk/by-label/swap_vol"
-        "/dev/disk/by-partlabel/test_swap"
-        "/dev/disk/by-partlabel/test_part"
-        "/dev/disk/by-partuuid/deadbeef-dead-dead-beef-000000000000"
-        "/dev/disk/by-uuid/deadbeef-dead-dead-beef-111111111111"
-        "/dev/disk/by-uuid/deadbeef-dead-dead-beef-222222222222"
-    )
-
-    # Create a cursor file to skip messages generated by udevd in initrd, as it
-    # might not be the same up-to-date version as we currently run (hence generating
-    # messages we check for later and making the test fail)
-    cursor="$(mktemp)"
-    journalctl --cursor-file="${cursor:?}" -n0 -q
-
-    # Make sure the test device is connected and show its "wonderful" path
-    stat /sys/block/vda
-    readlink -f /sys/block/vda/dev
-
-    dev="/dev/vda"
-    sfdisk "${dev:?}" <<EOF
-label: gpt
-
-name="test_swap", size=32M
-uuid="deadbeef-dead-dead-beef-000000000000", name="test_part", size=5M
-EOF
-    udevadm settle
-    mkswap -U "deadbeef-dead-dead-beef-111111111111" -L "swap_vol" "${dev}1"
-    mkfs.ext4 -U "deadbeef-dead-dead-beef-222222222222" -L "data_vol" "${dev}2"
-    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-
-    # Try to mount the data partition manually (using its label)
-    mpoint="$(mktemp -d /logsysfsXXX)"
-    mount LABEL=data_vol "$mpoint"
-    touch "$mpoint/test"
-    umount "$mpoint"
-    # Do the same, but with UUID and using fstab
-    echo "UUID=deadbeef-dead-dead-beef-222222222222 $mpoint ext4 defaults 0 0" >>/etc/fstab
-    systemctl daemon-reload
-    mount "$mpoint"
-    timeout 30 bash -c "until systemctl -q is-active '$mpoint'; do sleep .2; done"
-    test -e "$mpoint/test"
-    umount "$mpoint"
-
-    # Test out the swap partition
-    swapon -v -L swap_vol
-    swapoff -v -L swap_vol
-
-    udevadm settle
-
-    logfile="$(mktemp)"
-    # Check state of affairs after https://github.com/systemd/systemd/pull/22759
-    # Note: can't use `--cursor-file` here, since we don't want to update the cursor
-    #       after using it
-    [[ "$(journalctl --after-cursor="$(<"$cursor")" -q --no-pager -o short-monotonic -p info --grep "Device path.*vda.?' too long to fit into unit name" | wc -l)" -eq 0 ]]
-    [[ "$(journalctl --after-cursor="$(<"$cursor")" -q --no-pager -o short-monotonic --grep "Unit name .*vda.?\.device\" too long, falling back to hashed unit name" | wc -l)" -gt 0 ]]
-    # Check if the respective "hashed" units exist and are active (plugged)
-    systemctl status --no-pager "$(readlink -f /sys/block/vda/vda1)"
-    systemctl status --no-pager "$(readlink -f /sys/block/vda/vda2)"
-    # Make sure we don't unnecessarily spam the log
-    { journalctl -b -q --no-pager -o short-monotonic -p info --grep "/sys/devices/.+/vda[0-9]?" _PID=1 + UNIT=systemd-udevd.service || :;} | tee "$logfile"
-    [[ "$(wc -l <"$logfile")" -lt 10 ]]
-
-    : >/etc/fstab
-    rm -fr "${cursor:?}" "${logfile:?}" "${mpoint:?}"
-}
-
-testcase_mdadm_basic() {
-    local i part_name raid_name raid_dev uuid
-    local expected_symlinks=()
-    local devices=(
-        /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..4}
-    )
-
-    ls -l "${devices[@]}"
-
-    echo "Mirror raid (RAID 1)"
-    raid_name="mdmirror"
-    raid_dev="/dev/md/$raid_name"
-    part_name="${raid_name}_part"
-    uuid="aaaaaaaa:bbbbbbbb:cccccccc:00000001"
-    expected_symlinks=(
-        "$raid_dev"
-        "/dev/disk/by-id/md-name-H:$raid_name"
-        "/dev/disk/by-id/md-uuid-$uuid"
-        "/dev/disk/by-label/$part_name" # ext4 partition
-    )
-    # Create a simple RAID 1 with an ext4 filesystem
-    echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..1} -v -f --level=1 --raid-devices=2
-    udevadm wait --settle --timeout=30 "$raid_dev"
-    mkfs.ext4 -L "$part_name" "$raid_dev"
-    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-    for i in {0..9}; do
-        echo "Disassemble - reassemble loop, iteration #$i"
-        mdadm -v --stop "$raid_dev"
-        udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
-        mdadm --assemble "$raid_dev" --name "$raid_name" -v
-        udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-    done
-    helper_check_device_symlinks
-    helper_check_device_units
-    # Cleanup
-    mdadm -v --stop "$raid_dev"
-    udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
-
-    echo "Parity raid (RAID 5)"
-    raid_name="mdparity"
-    raid_dev="/dev/md/$raid_name"
-    part_name="${raid_name}_part"
-    uuid="aaaaaaaa:bbbbbbbb:cccccccc:00000101"
-    expected_symlinks=(
-        "$raid_dev"
-        "/dev/disk/by-id/md-name-H:$raid_name"
-        "/dev/disk/by-id/md-uuid-$uuid"
-        "/dev/disk/by-label/$part_name" # ext4 partition
-    )
-    # Create a simple RAID 5 with an ext4 filesystem
-    echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..2} -v -f --level=5 --raid-devices=3
-    udevadm wait --settle --timeout=30 "$raid_dev"
-    mkfs.ext4 -L "$part_name" "$raid_dev"
-    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-    for i in {0..9}; do
-        echo "Disassemble - reassemble loop, iteration #$i"
-        mdadm -v --stop "$raid_dev"
-        udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
-        mdadm --assemble "$raid_dev" --name "$raid_name" -v
-        udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-    done
-    helper_check_device_symlinks
-    helper_check_device_units
-    # Cleanup
-    mdadm -v --stop "$raid_dev"
-    udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
-    helper_check_device_units
-
-    echo "Mirror + parity raid (RAID 10) + multiple partitions"
-    raid_name="mdmirpar"
-    raid_dev="/dev/md/$raid_name"
-    part_name="${raid_name}_part"
-    uuid="aaaaaaaa:bbbbbbbb:cccccccc:00001010"
-    expected_symlinks=(
-        "$raid_dev"
-        "/dev/disk/by-id/md-name-H:$raid_name"
-        "/dev/disk/by-id/md-uuid-$uuid"
-        "/dev/disk/by-label/$part_name" # ext4 partition
-        # Partitions
-        "${raid_dev}1"
-        "${raid_dev}2"
-        "${raid_dev}3"
-        "/dev/disk/by-id/md-name-H:$raid_name-part1"
-        "/dev/disk/by-id/md-name-H:$raid_name-part2"
-        "/dev/disk/by-id/md-name-H:$raid_name-part3"
-        "/dev/disk/by-id/md-uuid-$uuid-part1"
-        "/dev/disk/by-id/md-uuid-$uuid-part2"
-        "/dev/disk/by-id/md-uuid-$uuid-part3"
-    )
-    # Create a simple RAID 10 with an ext4 filesystem
-    echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..3} -v -f --level=10 --raid-devices=4
-    udevadm wait --settle --timeout=30 "$raid_dev"
-    # Partition the raid device
-    # Here, 'udevadm lock' is meaningless, as udevd does not lock MD devices.
-    sfdisk --wipe=always "$raid_dev" <<EOF
-label: gpt
-
-uuid="deadbeef-dead-dead-beef-111111111111", name="mdpart1", size=8M
-uuid="deadbeef-dead-dead-beef-222222222222", name="mdpart2", size=32M
-uuid="deadbeef-dead-dead-beef-333333333333", name="mdpart3", size=16M
-EOF
-    udevadm wait --settle --timeout=30 "/dev/disk/by-id/md-uuid-$uuid-part2"
-    mkfs.ext4 -L "$part_name" "/dev/disk/by-id/md-uuid-$uuid-part2"
-    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-    for i in {0..9}; do
-        echo "Disassemble - reassemble loop, iteration #$i"
-        mdadm -v --stop "$raid_dev"
-        udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
-        mdadm --assemble "$raid_dev" --name "$raid_name" -v
-        udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-    done
-    helper_check_device_symlinks
-    helper_check_device_units
-    # Cleanup
-    mdadm -v --stop "$raid_dev"
-    # Check if all expected symlinks were removed after the cleanup
-    udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
-    helper_check_device_units
-}
-
-testcase_mdadm_lvm() {
-    local part_name raid_name raid_dev uuid vgroup
-    local expected_symlinks=()
-    local devices=(
-        /dev/disk/by-id/ata-foobar_deadbeefmdadmlvm{0..4}
-    )
-
-    ls -l "${devices[@]}"
-
-    raid_name="mdlvm"
-    raid_dev="/dev/md/$raid_name"
-    part_name="${raid_name}_part"
-    vgroup="${raid_name}_vg"
-    uuid="aaaaaaaa:bbbbbbbb:ffffffff:00001010"
-    expected_symlinks=(
-        "$raid_dev"
-        "/dev/$vgroup/mypart1"          # LVM partition
-        "/dev/$vgroup/mypart2"          # LVM partition
-        "/dev/disk/by-id/md-name-H:$raid_name"
-        "/dev/disk/by-id/md-uuid-$uuid"
-        "/dev/disk/by-label/$part_name" # ext4 partition
-    )
-    # Create a RAID 10 with LVM + ext4
-    echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadmlvm{0..3} -v -f --level=10 --raid-devices=4
-    udevadm wait --settle --timeout=30 "$raid_dev"
-    # Create an LVM on the MD
-    lvm pvcreate -y "$raid_dev"
-    lvm pvs
-    lvm vgcreate "$vgroup" -y "$raid_dev"
-    lvm vgs
-    lvm vgchange -ay "$vgroup"
-    lvm lvcreate -y -L 4M "$vgroup" -n mypart1
-    lvm lvcreate -y -L 8M "$vgroup" -n mypart2
-    lvm lvs
-    udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
-    mkfs.ext4 -L "$part_name" "/dev/$vgroup/mypart2"
-    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-    # Disassemble the array
-    lvm vgchange -an "$vgroup"
-    mdadm -v --stop "$raid_dev"
-    udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
-    helper_check_device_symlinks
-    helper_check_device_units
-    # Reassemble it and check if all required symlinks exist
-    mdadm --assemble "$raid_dev" --name "$raid_name" -v
-    udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
-    helper_check_device_symlinks
-    helper_check_device_units
-    # Cleanup
-    lvm vgchange -an "$vgroup"
-    mdadm -v --stop "$raid_dev"
-    # Check if all expected symlinks were removed after the cleanup
-    udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}"
-    helper_check_device_units
-}
-
-udevadm settle
-udevadm control --log-level debug
-lsblk -a
-
-echo "Check if all symlinks under /dev/disk/ are valid (pre-test)"
-helper_check_device_symlinks
-
-# TEST_FUNCTION_NAME is passed on the kernel command line via systemd.setenv=
-# in the respective test.sh file
-if ! command -v "${TEST_FUNCTION_NAME:?}"; then
-    echo >&2 "Missing verification handler for test case '$TEST_FUNCTION_NAME'"
-    exit 1
-fi
-
-echo "TEST_FUNCTION_NAME=$TEST_FUNCTION_NAME"
-"$TEST_FUNCTION_NAME"
-udevadm settle
-
-echo "Check if all symlinks under /dev/disk/ are valid (post-test)"
-helper_check_device_symlinks
-
-udevadm control --log-level info
-
-systemctl status systemd-udevd
-
-touch /testok
diff --git a/test/units/testsuite-65.service b/test/units/testsuite-65.service
deleted file mode 100644 (file)
index 3610baf..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-65-ANALYZE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-65.sh b/test/units/testsuite-65.sh
deleted file mode 100755 (executable)
index 59dfd4f..0000000
+++ /dev/null
@@ -1,947 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-systemctl log-level debug
-export SYSTEMD_LOG_LEVEL=debug
-
-# Sanity checks
-#
-# We can't really test time, critical-chain and plot verbs here, as
-# the testsuite service is a part of the boot transaction, so let's assume
-# they fail
-systemd-analyze || :
-systemd-analyze time || :
-systemd-analyze critical-chain || :
-# blame
-systemd-analyze blame
-systemd-run --wait --user --pipe -M testuser@.host systemd-analyze blame
-(! systemd-analyze blame --global)
-# plot
-systemd-analyze plot >/dev/null || :
-systemd-analyze plot --json=pretty >/dev/null || :
-systemd-analyze plot --json=short >/dev/null || :
-systemd-analyze plot --json=off >/dev/null || :
-systemd-analyze plot --json=pretty --no-legend >/dev/null || :
-systemd-analyze plot --json=short --no-legend >/dev/null || :
-systemd-analyze plot --json=off --no-legend >/dev/null || :
-systemd-analyze plot --table >/dev/null || :
-systemd-analyze plot --table --no-legend >/dev/null || :
-(! systemd-analyze plot --global)
-# legacy/deprecated options (moved to systemctl, but still usable from analyze)
-systemd-analyze log-level
-systemd-analyze log-level "$(systemctl log-level)"
-systemd-analyze get-log-level
-systemd-analyze set-log-level "$(systemctl log-level)"
-systemd-analyze log-target
-systemd-analyze log-target "$(systemctl log-target)"
-systemd-analyze get-log-target
-systemd-analyze set-log-target "$(systemctl log-target)"
-systemd-analyze service-watchdogs
-systemd-analyze service-watchdogs "$(systemctl service-watchdogs)"
-# dot
-systemd-analyze dot >/dev/null
-systemd-analyze dot systemd-journald.service >/dev/null
-systemd-analyze dot systemd-journald.service systemd-logind.service >/dev/null
-systemd-analyze dot --from-pattern="*" --from-pattern="*.service" systemd-journald.service >/dev/null
-systemd-analyze dot --to-pattern="*" --to-pattern="*.service" systemd-journald.service >/dev/null
-systemd-analyze dot --from-pattern="*.service" --to-pattern="*.service" systemd-journald.service >/dev/null
-systemd-analyze dot --order systemd-journald.service systemd-logind.service >/dev/null
-systemd-analyze dot --require systemd-journald.service systemd-logind.service >/dev/null
-systemd-analyze dot "systemd-*.service" >/dev/null
-(! systemd-analyze dot systemd-journald.service systemd-logind.service "*" bbb ccc)
-(! systemd-analyze dot --global systemd-journald.service)
-# dump
-# this should be rate limited to 10 calls in 10 minutes for unprivileged callers
-for _ in {1..10}; do
-    runas testuser systemd-analyze dump systemd-journald.service >/dev/null
-done
-(! runas testuser systemd-analyze dump >/dev/null)
-# still limited after a reload
-systemctl daemon-reload
-(! runas testuser systemd-analyze dump >/dev/null)
-# and a re-exec
-systemctl daemon-reexec
-(! runas testuser systemd-analyze dump >/dev/null)
-# privileged call, so should not be rate limited
-for _ in {1..10}; do
-    systemd-analyze dump systemd-journald.service >/dev/null
-done
-systemd-analyze dump >/dev/null
-systemd-analyze dump "*" >/dev/null
-systemd-analyze dump "*.socket" >/dev/null
-systemd-analyze dump "*.socket" "*.service" aaaaaaa ... >/dev/null
-systemd-analyze dump systemd-journald.service >/dev/null
-(! systemd-analyze dump "")
-(! systemd-analyze dump --global systemd-journald.service)
-# malloc
-systemd-analyze malloc >/dev/null
-(! systemd-analyze malloc --global)
-# unit-files
-systemd-analyze unit-files >/dev/null
-systemd-analyze unit-files systemd-journald.service >/dev/null
-systemd-analyze unit-files "*" >/dev/null
-systemd-analyze unit-files "*" aaaaaa "*.service" "*.target" >/dev/null
-systemd-analyze unit-files --user >/dev/null
-systemd-analyze unit-files --user "*" aaaaaa "*.service" "*.target" >/dev/null
-(! systemd-analyze unit-files --global)
-# unit-paths
-systemd-analyze unit-paths
-systemd-analyze unit-paths --user
-systemd-analyze unit-paths --global
-# exist-status
-systemd-analyze exit-status
-systemd-analyze exit-status STDOUT BPF
-systemd-analyze exit-status 0 1 {63..65}
-(! systemd-analyze exit-status STDOUT BPF "hello*")
-(! systemd-analyze exit-status --global)
-# capability
-systemd-analyze capability
-systemd-analyze capability cap_chown CAP_KILL
-systemd-analyze capability 0 1 {30..32}
-(! systemd-analyze capability cap_chown CAP_KILL "hello*")
-(! systemd-analyze capability --global)
-# condition
-mkdir -p /run/systemd/system
-UNIT_NAME="analyze-condition-$RANDOM.service"
-cat >"/run/systemd/system/$UNIT_NAME" <<EOF
-[Unit]
-AssertPathExists=/etc/os-release
-AssertEnvironment=!FOOBAR
-ConditionKernelVersion=>1.0
-ConditionPathExists=/etc/os-release
-
-[Service]
-ExecStart=true
-EOF
-systemctl daemon-reload
-systemd-analyze condition --unit="$UNIT_NAME"
-systemd-analyze condition 'ConditionKernelVersion = ! <4.0' \
-                          'ConditionKernelVersion = >=3.1' \
-                          'ConditionACPower=|false' \
-                          'ConditionArchitecture=|!arm' \
-                          'AssertPathExists=/etc/os-release'
-(! systemd-analyze condition 'ConditionArchitecture=|!arm' 'AssertXYZ=foo')
-(! systemd-analyze condition 'ConditionKernelVersion=<1.0')
-(! systemd-analyze condition 'AssertKernelVersion=<1.0')
-(! systemd-analyze condition --global 'ConditionKernelVersion = ! <4.0')
-# syscall-filter
-systemd-analyze syscall-filter >/dev/null
-systemd-analyze syscall-filter @chown @sync
-systemd-analyze syscall-filter @sync @sync @sync
-(! systemd-analyze syscall-filter @chown @sync @foobar)
-(! systemd-analyze syscall-filter --global)
-# filesystems (requires libbpf support)
-if systemctl --version | grep "+BPF_FRAMEWORK"; then
-    systemd-analyze filesystems >/dev/null
-    systemd-analyze filesystems @basic-api
-    systemd-analyze filesystems @basic-api @basic-api @basic-api
-    (! systemd-analyze filesystems @basic-api @basic-api @foobar @basic-api)
-    (! systemd-analyze filesystems --global @basic-api)
-fi
-# calendar
-systemd-analyze calendar '*-2-29 0:0:0'
-systemd-analyze calendar --iterations=5 '*-2-29 0:0:0'
-systemd-analyze calendar '*-* *:*:*'
-systemd-analyze calendar --iterations=5 '*-* *:*:*'
-systemd-analyze calendar --iterations=50 '*-* *:*:*'
-systemd-analyze calendar --iterations=0 '*-* *:*:*'
-systemd-analyze calendar --iterations=5 '01-01-22 01:00:00'
-systemd-analyze calendar --base-time=yesterday --iterations=5 '*-* *:*:*'
-(! systemd-analyze calendar --iterations=0 '*-* 99:*:*')
-(! systemd-analyze calendar --base-time=never '*-* *:*:*')
-(! systemd-analyze calendar 1)
-(! systemd-analyze calendar "")
-(! systemd-analyze calendar --global '*-2-29 0:0:0')
-# timestamp
-systemd-analyze timestamp now
-systemd-analyze timestamp -- -1
-systemd-analyze timestamp yesterday now tomorrow
-(! systemd-analyze timestamp yesterday never tomorrow)
-(! systemd-analyze timestamp 1)
-(! systemd-analyze timestamp '*-2-29 0:0:0')
-(! systemd-analyze timestamp "")
-(! systemd-analyze timestamp --global now)
-# timespan
-systemd-analyze timespan 1
-systemd-analyze timespan 1s 300s '1year 0.000001s'
-(! systemd-analyze timespan 1s 300s aaaaaa '1year 0.000001s')
-(! systemd-analyze timespan -- -1)
-(! systemd-analyze timespan '*-2-29 0:0:0')
-(! systemd-analyze timespan "")
-(! systemd-analyze timespan --global 1)
-# cat-config
-systemd-analyze cat-config systemd/system.conf >/dev/null
-systemd-analyze cat-config /etc/systemd/system.conf >/dev/null
-systemd-analyze cat-config systemd/system.conf systemd/journald.conf >/dev/null
-systemd-analyze cat-config systemd/system.conf foo/bar systemd/journald.conf >/dev/null
-systemd-analyze cat-config foo/bar
-systemd-analyze cat-config --tldr systemd/system.conf >/dev/null
-systemd-analyze cat-config --tldr /etc/systemd/system.conf >/dev/null
-systemd-analyze cat-config --tldr systemd/system.conf systemd/journald.conf >/dev/null
-systemd-analyze cat-config --tldr systemd/system.conf foo/bar systemd/journald.conf >/dev/null
-systemd-analyze cat-config --tldr foo/bar
-(! systemd-analyze cat-config --global systemd/system.conf)
-# security
-systemd-analyze security
-systemd-analyze security --json=off
-systemd-analyze security --json=pretty | jq
-systemd-analyze security --json=short | jq
-(! systemd-analyze security --global)
-
-if [[ ! -v ASAN_OPTIONS ]]; then
-    # check that systemd-analyze cat-config paths work in a chroot
-    mkdir -p /tmp/root
-    mount --bind / /tmp/root
-    if mountpoint -q /usr; then
-        mount --bind /usr /tmp/root/usr
-    fi
-    systemd-analyze cat-config systemd/system-preset >/tmp/out1
-    chroot /tmp/root systemd-analyze cat-config systemd/system-preset >/tmp/out2
-    diff /tmp/out{1,2}
-fi
-
-# verify
-mkdir -p /tmp/img/usr/lib/systemd/system/
-mkdir -p /tmp/img/opt/
-
-touch /tmp/img/opt/script0.sh
-chmod +x /tmp/img/opt/script0.sh
-
-cat <<EOF >/tmp/img/usr/lib/systemd/system/testfile.service
-[Service]
-ExecStart = /opt/script0.sh
-EOF
-
-set +e
-# Default behaviour is to recurse through all dependencies when unit is loaded
-(! systemd-analyze verify --root=/tmp/img/ testfile.service)
-
-# As above, recurses through all dependencies when unit is loaded
-(! systemd-analyze verify --recursive-errors=yes --root=/tmp/img/ testfile.service)
-
-# Recurses through unit file and its direct dependencies when unit is loaded
-(! systemd-analyze verify --recursive-errors=one --root=/tmp/img/ testfile.service)
-
-set -e
-
-# zero exit status since dependencies are ignored when unit is loaded
-systemd-analyze verify --recursive-errors=no --root=/tmp/img/ testfile.service
-
-rm /tmp/img/usr/lib/systemd/system/testfile.service
-
-cat <<EOF >/tmp/testfile.service
-[Unit]
-foo = bar
-
-[Service]
-ExecStart = echo hello
-EOF
-
-cat <<EOF >/tmp/testfile2.service
-[Unit]
-Requires = testfile.service
-
-[Service]
-ExecStart = echo hello
-EOF
-
-# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded
-systemd-analyze verify --recursive-errors=no /tmp/testfile2.service
-
-set +e
-# Non-zero exit status since all associated dependencies are recursively loaded when the unit file is loaded
-(! systemd-analyze verify --recursive-errors=yes /tmp/testfile2.service)
-set -e
-
-rm /tmp/testfile.service
-rm /tmp/testfile2.service
-
-cat <<EOF >/tmp/sample.service
-[Unit]
-Description = A Sample Service
-
-[Service]
-ExecStart = echo hello
-Slice=support.slice
-EOF
-
-# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded
-systemd-analyze verify --recursive-errors=no /tmp/sample.service
-
-cat <<EOF >/tmp/testfile.service
-[Service]
-ExecStart = echo hello
-DeviceAllow=/dev/sda
-EOF
-
-# Prevent regression from #13380 and #20859 where we can't verify hidden files
-cp /tmp/testfile.service /tmp/.testfile.service
-
-systemd-analyze verify /tmp/.testfile.service
-
-rm /tmp/.testfile.service
-
-# Alias a unit file's name on disk (see #20061)
-cp /tmp/testfile.service /tmp/testsrvc
-
-(! systemd-analyze verify /tmp/testsrvc)
-
-systemd-analyze verify /tmp/testsrvc:alias.service
-
-# Zero exit status since the value used for comparison determine exposure to security threats is by default 100
-systemd-analyze security --offline=true /tmp/testfile.service
-
-#The overall exposure level assigned to the unit is greater than the set threshold
-(! systemd-analyze security --threshold=90 --offline=true /tmp/testfile.service)
-
-# Ensure we print the list of ACLs, see https://github.com/systemd/systemd/issues/23185
-systemd-analyze security --offline=true /tmp/testfile.service | grep -q -F "/dev/sda"
-
-rm /tmp/testfile.service
-
-cat <<EOF >/tmp/img/usr/lib/systemd/system/testfile.service
-[Service]
-ExecStart = echo hello
-PrivateNetwork = yes
-PrivateDevices = yes
-PrivateUsers = yes
-EOF
-
-# The new overall exposure level assigned to the unit is less than the set thresholds
-# Verifies that the --offline= option works with --root=
-systemd-analyze security --threshold=90 --offline=true --root=/tmp/img/ testfile.service
-
-cat <<EOF >/tmp/foo@.service
-[Service]
-ExecStart=ls
-EOF
-
-cat <<EOF >/tmp/hoge@test.service
-[Service]
-ExecStart=ls
-EOF
-
-# issue #30357
-pushd /tmp
-systemd-analyze verify foo@bar.service
-systemd-analyze verify foo@.service
-systemd-analyze verify hoge@test.service
-(! systemd-analyze verify hoge@nonexist.service)
-(! systemd-analyze verify hoge@.service)
-popd
-pushd /
-systemd-analyze verify tmp/foo@bar.service
-systemd-analyze verify tmp/foo@.service
-systemd-analyze verify tmp/hoge@test.service
-(! systemd-analyze verify tmp/hoge@nonexist.service)
-(! systemd-analyze verify tmp/hoge@.service)
-popd
-pushd /usr
-systemd-analyze verify ../tmp/foo@bar.service
-systemd-analyze verify ../tmp/foo@.service
-systemd-analyze verify ../tmp/hoge@test.service
-(! systemd-analyze verify ../tmp/hoge@nonexist.service)
-(! systemd-analyze verify ../tmp/hoge@.service)
-popd
-systemd-analyze verify /tmp/foo@bar.service
-systemd-analyze verify /tmp/foo@.service
-systemd-analyze verify /tmp/hoge@test.service
-(! systemd-analyze verify /tmp/hoge@nonexist.service)
-(! systemd-analyze verify /tmp/hoge@.service)
-
-# test that all commands are verified.
-cat <<EOF >/tmp/multi-exec-start.service
-[Service]
-Type=oneshot
-ExecStart=true
-ExecStart=ls
-EOF
-systemd-analyze verify /tmp/multi-exec-start.service
-echo 'ExecStart=command-should-not-exist' >>/tmp/multi-exec-start.service
-(! systemd-analyze verify /tmp/multi-exec-start.service)
-
-# Added an additional "INVALID_ID" id to the .json to verify that nothing breaks when input is malformed
-# The PrivateNetwork id description and weight was changed to verify that 'security' is actually reading in
-# values from the .json file when required. The default weight for "PrivateNetwork" is 2500, and the new weight
-# assigned to that id in the .json file is 6000. This increased weight means that when the "PrivateNetwork" key is
-# set to 'yes' (as above in the case of testfile.service) in the content of the unit file, the overall exposure
-# level for the unit file should decrease to account for that increased weight.
-cat <<EOF >/tmp/testfile.json
-{"UserOrDynamicUser":
-    {"description_bad": "Service runs as root user",
-    "weight": 0,
-    "range": 10
-    },
-"SupplementaryGroups":
-    {"description_good": "Service has no supplementary groups",
-    "description_bad": "Service runs with supplementary groups",
-    "description_na": "Service runs as root, option does not matter",
-    "weight": 200,
-    "range": 1
-    },
-"PrivateDevices":
-    {"description_good": "Service has no access to hardware devices",
-    "description_bad": "Service potentially has access to hardware devices",
-    "weight": 1000,
-    "range": 1
-    },
-"PrivateMounts":
-    {"description_good": "Service cannot install system mounts",
-    "description_bad": "Service may install system mounts",
-    "weight": 1000,
-    "range": 1
-    },
-"PrivateNetwork":
-    {"description_good": "Service doesn't have access to the host's network",
-    "description_bad": "Service has access to the host's network",
-    "weight": 6000,
-    "range": 1
-    },
-"PrivateTmp":
-    {"description_good": "Service has no access to other software's temporary files",
-    "description_bad": "Service has access to other software's temporary files",
-    "weight": 1000,
-    "range": 1
-    },
-"PrivateUsers":
-    {"description_good": "Service does not have access to other users",
-    "description_bad": "Service has access to other users",
-    "weight": 1000,
-    "range": 1
-    },
-"ProtectControlGroups":
-    {"description_good": "Service cannot modify the control group file system",
-    "description_bad": "Service may modify the control group file system",
-    "weight": 1000,
-    "range": 1
-    },
-"ProtectKernelModules":
-    {"description_good": "Service cannot load or read kernel modules",
-    "description_bad": "Service may load or read kernel modules",
-    "weight": 1000,
-    "range": 1
-    },
-"ProtectKernelTunables":
-    {"description_good": "Service cannot alter kernel tunables (/proc/sys, …)",
-    "description_bad": "Service may alter kernel tunables",
-    "weight": 1000,
-    "range": 1
-    },
-"ProtectKernelLogs":
-    {"description_good": "Service cannot read from or write to the kernel log ring buffer",
-    "description_bad": "Service may read from or write to the kernel log ring buffer",
-    "weight": 1000,
-    "range": 1
-    },
-"ProtectClock":
-    {"description_good": "Service cannot write to the hardware clock or system clock",
-    "description_bad": "Service may write to the hardware clock or system clock",
-    "weight": 1000,
-    "range": 1
-    },
-"ProtectHome":
-    {"weight": 1000,
-    "range": 10
-    },
-"ProtectHostname":
-    {"description_good": "Service cannot change system host/domainname",
-    "description_bad": "Service may change system host/domainname",
-    "weight": 50,
-    "range": 1
-    },
-"ProtectSystem":
-    {"weight": 1000,
-    "range": 10
-    },
-"RootDirectoryOrRootImage":
-    {"description_good": "Service has its own root directory/image",
-    "description_bad": "Service runs within the host's root directory",
-    "weight": 200,
-    "range": 1
-    },
-"LockPersonality":
-    {"description_good": "Service cannot change ABI personality",
-    "description_bad": "Service may change ABI personality",
-    "weight": 100,
-    "range": 1
-    },
-"MemoryDenyWriteExecute":
-    {"description_good": "Service cannot create writable executable memory mappings",
-    "description_bad": "Service may create writable executable memory mappings",
-    "weight": 100,
-    "range": 1
-    },
-"NoNewPrivileges":
-    {"description_good": "Service processes cannot acquire new privileges",
-    "description_bad": "Service processes may acquire new privileges",
-    "weight": 1000,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_SYS_ADMIN":
-    {"description_good": "Service has no administrator privileges",
-    "description_bad": "Service has administrator privileges",
-    "weight": 1500,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_SET_UID_GID_PCAP":
-    {"description_good": "Service cannot change UID/GID identities/capabilities",
-    "description_bad": "Service may change UID/GID identities/capabilities",
-    "weight": 1500,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_SYS_PTRACE":
-    {"description_good": "Service has no ptrace() debugging abilities",
-    "description_bad": "Service has ptrace() debugging abilities",
-    "weight": 1500,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_SYS_TIME":
-    {"description_good": "Service processes cannot change the system clock",
-    "description_bad": "Service processes may change the system clock",
-    "weight": 1000,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_NET_ADMIN":
-    {"description_good": "Service has no network configuration privileges",
-    "description_bad": "Service has network configuration privileges",
-    "weight": 1000,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_SYS_RAWIO":
-    {"description_good": "Service has no raw I/O access",
-    "description_bad": "Service has raw I/O access",
-    "weight": 1000,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_SYS_MODULE":
-    {"description_good": "Service cannot load kernel modules",
-    "description_bad": "Service may load kernel modules",
-    "weight": 1000,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_AUDIT":
-    {"description_good": "Service has no audit subsystem access",
-    "description_bad": "Service has audit subsystem access",
-    "weight": 500,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_SYSLOG":
-    {"description_good": "Service has no access to kernel logging",
-    "description_bad": "Service has access to kernel logging",
-    "weight": 500,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE":
-    {"description_good": "Service has no privileges to change resource use parameters",
-    "description_bad": "Service has privileges to change resource use parameters",
-    "weight": 500,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_MKNOD":
-    {"description_good": "Service cannot create device nodes",
-    "description_bad": "Service may create device nodes",
-    "weight": 500,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP":
-    {"description_good": "Service cannot change file ownership/access mode/capabilities",
-    "description_bad": "Service may change file ownership/access mode/capabilities unrestricted",
-    "weight": 1000,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER":
-    {"description_good": "Service cannot override UNIX file/IPC permission checks",
-    "description_bad": "Service may override UNIX file/IPC permission checks",
-    "weight": 1000,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_KILL":
-    {"description_good": "Service cannot send UNIX signals to arbitrary processes",
-    "description_bad": "Service may send UNIX signals to arbitrary processes",
-    "weight": 500,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW":
-    {"description_good": "Service has no elevated networking privileges",
-    "description_bad": "Service has elevated networking privileges",
-    "weight": 500,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_SYS_BOOT":
-    {"description_good": "Service cannot issue reboot()",
-    "description_bad": "Service may issue reboot()",
-    "weight": 100,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_MAC":
-    {"description_good": "Service cannot adjust SMACK MAC",
-    "description_bad": "Service may adjust SMACK MAC",
-    "weight": 100,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_LINUX_IMMUTABLE":
-    {"description_good": "Service cannot mark files immutable",
-    "description_bad": "Service may mark files immutable",
-    "weight": 75,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_IPC_LOCK":
-    {"description_good": "Service cannot lock memory into RAM",
-    "description_bad": "Service may lock memory into RAM",
-    "weight": 50,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_SYS_CHROOT":
-    {"description_good": "Service cannot issue chroot()",
-    "description_bad": "Service may issue chroot()",
-    "weight": 50,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_BLOCK_SUSPEND":
-    {"description_good": "Service cannot establish wake locks",
-    "description_bad": "Service may establish wake locks",
-    "weight": 25,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_WAKE_ALARM":
-    {"description_good": "Service cannot program timers that wake up the system",
-    "description_bad": "Service may program timers that wake up the system",
-    "weight": 25,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_LEASE":
-    {"description_good": "Service cannot create file leases",
-    "description_bad": "Service may create file leases",
-    "weight": 25,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_SYS_TTY_CONFIG":
-    {"description_good": "Service cannot issue vhangup()",
-    "description_bad": "Service may issue vhangup()",
-    "weight": 25,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_SYS_PACCT":
-    {"description_good": "Service cannot use acct()",
-    "description_bad": "Service may use acct()",
-    "weight": 25,
-    "range": 1
-    },
-"CapabilityBoundingSet_CAP_BPF":
-    {"description_good": "Service may load BPF programs",
-    "description_bad": "Service may not load BPF programs",
-    "weight": 25,
-    "range": 1
-    },
-"UMask":
-    {"weight": 100,
-    "range": 10
-    },
-"KeyringMode":
-    {"description_good": "Service doesn't share key material with other services",
-    "description_bad": "Service shares key material with other service",
-    "weight": 1000,
-    "range": 1
-    },
-"ProtectProc":
-    {"description_good": "Service has restricted access to process tree(/proc hidepid=)",
-    "description_bad": "Service has full access to process tree(/proc hidepid=)",
-    "weight": 1000,
-    "range": 3
-    },
-"ProcSubset":
-    {"description_good": "Service has no access to non-process/proc files(/proc subset=)",
-    "description_bad": "Service has full access to non-process/proc files(/proc subset=)",
-    "weight": 10,
-    "range": 1
-    },
-"NotifyAccess":
-    {"description_good": "Service child processes cannot alter service state",
-    "description_bad": "Service child processes may alter service state",
-    "weight": 1000,
-    "range": 1
-    },
-"RemoveIPC":
-    {"description_good": "Service user cannot leave SysV IPC objects around",
-    "description_bad": "Service user may leave SysV IPC objects around",
-    "description_na": "Service runs as root, option does not apply",
-    "weight": 100,
-    "range": 1
-    },
-"Delegate":
-    {"description_good": "Service does not maintain its own delegated control group subtree",
-    "description_bad": "Service maintains its own delegated control group subtree",
-    "weight": 100,
-    "range": 1
-    },
-"RestrictRealtime":
-    {"description_good": "Service realtime scheduling access is restricted",
-    "description_bad": "Service may acquire realtime scheduling",
-    "weight": 500,
-    "range": 1
-    },
-"RestrictSUIDSGID":
-    {"description_good": "SUID/SGIDfilecreationbyserviceisrestricted",
-    "description_bad": "ServicemaycreateSUID/SGIDfiles",
-    "weight": 1000,
-    "range": 1
-    },
-"RestrictNamespaces_user":
-    {"description_good": "Servicecannotcreateusernamespaces",
-    "description_bad": "Servicemaycreateusernamespaces",
-    "weight": 1500,
-    "range": 1
-    },
-"RestrictNamespaces_mnt":
-    {"description_good": "Service cannot create file system namespaces",
-    "description_bad": "Service may create file system namespaces",
-    "weight": 500,
-    "range": 1
-    },
-"RestrictNamespaces_ipc":
-    {"description_good": "Service cannot create IPC namespaces",
-    "description_bad": "Service may create IPC namespaces",
-    "weight": 500,
-    "range": 1
-    },
-"RestrictNamespaces_pid":
-    {"description_good": "Service cannot create process namespaces",
-    "description_bad": "Service may create process namespaces",
-    "weight": 500,
-    "range": 1
-    },
-"RestrictNamespaces_cgroup":
-    {"description_good": "Service cannot create cgroup namespaces",
-    "description_bad": "Service may create cgroup namespaces",
-    "weight": 500,
-    "range": 1
-    },
-"RestrictNamespaces_net":
-    {"description_good": "Service cannot create network namespaces",
-    "description_bad": "Service may create network namespaces",
-    "weight": 500,
-    "range": 1
-    },
-"RestrictNamespaces_uts":
-    {"description_good": "Service cannot create hostname namespaces",
-    "description_bad": "Service may create hostname namespaces",
-    "weight": 100,
-    "range": 1
-    },
-"RestrictAddressFamilies_AF_INET_INET6":
-    {"description_good": "Service cannot allocate Internet sockets",
-    "description_bad": "Service may allocate Internet sockets",
-    "weight": 1500,
-    "range": 1
-    },
-"RestrictAddressFamilies_AF_UNIX":
-    {"description_good": "Service cannot allocate local sockets",
-    "description_bad": "Service may allocate local sockets",
-    "weight": 25,
-    "range": 1
-    },
-"RestrictAddressFamilies_AF_NETLINK":
-    {"description_good": "Service cannot allocate netlink sockets",
-    "description_bad": "Service may allocate netlink sockets",
-    "weight": 200,
-    "range": 1
-    },
-"RestrictAddressFamilies_AF_PACKET":
-    {"description_good": "Service cannot allocate packet sockets",
-    "description_bad": "Service may allocate packet sockets",
-    "weight": 1000,
-    "range": 1
-    },
-"RestrictAddressFamilies_OTHER":
-    {"description_good": "Service cannot allocate exotic sockets",
-    "description_bad": "Service may allocate exotic sockets",
-    "weight": 1250,
-    "range": 1
-    },
-"SystemCallArchitectures":
-    {"weight": 1000,
-    "range": 10
-    },
-"SystemCallFilter_swap":
-    {"weight": 1000,
-    "range": 10
-    },
-"SystemCallFilter_obsolete":
-    {"weight": 250,
-    "range": 10
-    },
-"SystemCallFilter_clock":
-    {"weight": 1000,
-    "range": 10
-    },
-"SystemCallFilter_cpu_emulation":
-    {"weight": 250,
-    "range": 10
-    },
-"SystemCallFilter_debug":
-    {"weight": 1000,
-    "range": 10
-    },
-"SystemCallFilter_mount":
-    {"weight": 1000,
-    "range": 10
-    },
-"SystemCallFilter_module":
-    {"weight": 1000,
-    "range": 10
-    },
-"SystemCallFilter_raw_io":
-    {"weight": 1000,
-    "range": 10
-    },
-"SystemCallFilter_reboot":
-    {"weight": 1000,
-    "range": 10
-    },
-"SystemCallFilter_privileged":
-    {"weight": 700,
-    "range": 10
-    },
-"SystemCallFilter_resources":
-    {"weight": 700,
-    "range": 10
-    },
-"IPAddressDeny":
-    {"weight": 1000,
-    "range": 10
-    },
-"DeviceAllow":
-    {"weight": 1000,
-    "range": 10
-    },
-"AmbientCapabilities":
-    {"description_good": "Service process does not receive ambient capabilities",
-    "description_bad": "Service process receives ambient capabilities",
-    "weight": 500,
-    "range": 1
-    },
-"INVALID_ID":
-    {"weight": 1000,
-    "range": 10
-    }
-}
-EOF
-
-# Reads in custom security requirements from the parsed .json file and uses these for comparison
-systemd-analyze security --threshold=90 --offline=true \
-                           --security-policy=/tmp/testfile.json \
-                           --root=/tmp/img/ testfile.service
-
-# The strict profile adds a lot of sanboxing options
-systemd-analyze security --threshold=25 --offline=true \
-                           --security-policy=/tmp/testfile.json \
-                           --profile=strict \
-                           --root=/tmp/img/ testfile.service
-
-# The trusted profile doesn't add any sanboxing options
-(! systemd-analyze security --threshold=25 --offline=true \
-                           --security-policy=/tmp/testfile.json \
-                           --profile=/usr/lib/systemd/portable/profile/trusted/service.conf \
-                           --root=/tmp/img/ testfile.service)
-
-(! systemd-analyze security --threshold=50 --offline=true \
-                           --security-policy=/tmp/testfile.json \
-                           --root=/tmp/img/ testfile.service)
-
-rm /tmp/img/usr/lib/systemd/system/testfile.service
-
-if systemd-analyze --version | grep -q -F "+ELFUTILS"; then
-    systemd-analyze inspect-elf --json=short /lib/systemd/systemd | grep -q -F '"elfType":"executable"'
-fi
-
-systemd-analyze --threshold=90 security systemd-journald.service
-
-# issue 23663
-check() {(
-    set +x
-    output=$(systemd-analyze security --offline="${2?}" "${3?}" | grep -F 'SystemCallFilter=')
-    assert_in "System call ${1?} list" "$output"
-    assert_in "[+✓] SystemCallFilter=~@swap" "$output"
-    assert_in "[+✓] SystemCallFilter=~@resources" "$output"
-    assert_in "[+✓] SystemCallFilter=~@reboot" "$output"
-    assert_in "[+✓] SystemCallFilter=~@raw-io" "$output"
-    assert_in "[-✗] SystemCallFilter=~@privileged" "$output"
-    assert_in "[+✓] SystemCallFilter=~@obsolete" "$output"
-    assert_in "[+✓] SystemCallFilter=~@mount" "$output"
-    assert_in "[+✓] SystemCallFilter=~@module" "$output"
-    assert_in "[+✓] SystemCallFilter=~@debug" "$output"
-    assert_in "[+✓] SystemCallFilter=~@cpu-emulation" "$output"
-    assert_in "[-✗] SystemCallFilter=~@clock" "$output"
-)}
-
-export -n SYSTEMD_LOG_LEVEL
-
-mkdir -p /run/systemd/system
-cat >/run/systemd/system/allow-list.service <<EOF
-[Service]
-ExecStart=false
-SystemCallFilter=@system-service
-SystemCallFilter=~@resources:ENOANO @privileged
-SystemCallFilter=@clock
-EOF
-
-cat >/run/systemd/system/deny-list.service <<EOF
-[Service]
-ExecStart=false
-SystemCallFilter=~@known
-SystemCallFilter=@system-service
-SystemCallFilter=~@resources:ENOANO @privileged
-SystemCallFilter=@clock
-EOF
-
-systemctl daemon-reload
-
-check allow yes /run/systemd/system/allow-list.service
-check allow no allow-list.service
-check deny yes /run/systemd/system/deny-list.service
-check deny no deny-list.service
-
-output=$(systemd-run -p "SystemCallFilter=@system-service" -p "SystemCallFilter=~@resources:ENOANO @privileged" -p "SystemCallFilter=@clock" sleep 60 2>&1)
-name=$(echo "$output" | awk '{ print $4 }' | cut -d';' -f1)
-
-check allow yes /run/systemd/transient/"$name"
-check allow no "$name"
-
-output=$(systemd-run -p "SystemCallFilter=~@known" -p "SystemCallFilter=@system-service" -p "SystemCallFilter=~@resources:ENOANO @privileged" -p "SystemCallFilter=@clock" sleep 60 2>&1)
-name=$(echo "$output" | awk '{ print $4 }' | cut -d';' -f1)
-
-check deny yes /run/systemd/transient/"$name"
-check deny no "$name"
-
-# Let's also test the "image-policy" verb
-
-systemd-analyze image-policy '*' 2>&1 | grep -q -F "Long form: =verity+signed+encrypted+unprotected+unused+absent"
-systemd-analyze image-policy '-' 2>&1 | grep -q -F "Long form: =unused+absent"
-systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -F "Long form: usr=verity:home=encrypted:=unused+absent"
-systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^home \+encrypted \+'
-systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^usr \+verity \+'
-systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^root \+ignore \+'
-systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^usr-verity \+unprotected \+'
-
-(! systemd-analyze image-policy 'doedel')
-
-# Output is very hard to predict, but let's run it for coverage anyway
-systemd-analyze pcrs
-systemd-analyze pcrs --json=pretty
-systemd-analyze pcrs 14 7 0 ima
-
-systemd-analyze architectures
-systemd-analyze architectures --json=pretty
-systemd-analyze architectures x86
-systemd-analyze architectures x86-64
-systemd-analyze architectures native
-systemd-analyze architectures uname
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-66-deviceisolation.service b/test/units/testsuite-66-deviceisolation.service
deleted file mode 100644 (file)
index 14b0d89..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Service that uses device isolation
-
-[Service]
-DevicePolicy=strict
-DeviceAllow=/dev/null r
-StandardOutput=file:/tmp/testsuite66serviceresults
-ExecStartPre=rm -f /tmp/testsuite66serviceresults
-ExecStart=bash -c "while true; do sleep 0.01 && echo meow >/dev/null && echo thisshouldnotbehere; done"
diff --git a/test/units/testsuite-66.service b/test/units/testsuite-66.service
deleted file mode 100644 (file)
index 7e9dc3b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TESTSUITE-66-DEVICEISOLATION
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-66.sh b/test/units/testsuite-66.sh
deleted file mode 100755 (executable)
index 147335a..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-RESULTS_FILE=/tmp/testsuite66serviceresults
-
-systemd-analyze log-level debug
-
-systemctl start testsuite-66-deviceisolation.service
-
-sleep 5
-grep -q "Operation not permitted" "$RESULTS_FILE"
-
-systemctl daemon-reload
-systemctl daemon-reexec
-
-systemctl stop testsuite-66-deviceisolation.service
-
-grep -q "thisshouldnotbehere" "$RESULTS_FILE" && exit 42
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-67.service b/test/units/testsuite-67.service
deleted file mode 100644 (file)
index 82f998e..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-67-INTEGRITY
-After=multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-67.sh b/test/units/testsuite-67.sh
deleted file mode 100755 (executable)
index a42fd66..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -euxo pipefail
-
-export DM_NAME="integrity_test"
-export FULL_DM_DEV_NAME="/dev/mapper/${DM_NAME}"
-export FS_UUID="01234567-ffff-eeee-eeee-0123456789ab"
-export GEN="/var/run/systemd/generator"
-
-image_dir=""
-
-cleanup()
-{
-    if [ -z "${image_dir}" ]; then
-        return
-    fi
-
-    if [ -f "${image_dir}/image" ]; then
-        if [ -e "${FULL_DM_DEV_NAME}" ]; then
-            integritysetup close "${DM_NAME}"
-        fi
-        losetup -d "${loop}"
-    fi
-
-    rm -rf "${image_dir}"
-}
-
-trap cleanup EXIT
-
-build_integrity_tab()
-{
-cat <<EOF >"/etc/integritytab"
-${DM_NAME} ${loop} - integrity-algorithm=$1
-EOF
-}
-
-image_dir="$(mktemp -d -t -p / integrity.tmp.XXXXXX)"
-if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then
-    echo "mktemp under / failed"
-    exit 1
-fi
-
-dd if=/dev/zero of="${image_dir}/image" bs=1048576 count=64 || exit 1
-dd if=/dev/zero of="${image_dir}/data" bs=1048576 count=64 || exit 1
-loop="$(losetup --show -f "${image_dir}/image")"
-
-if [[ ! -e ${loop} ]]; then
-    echo "Loopback device created not found!"
-    exit 1
-fi
-
-# Do one iteration with a separate data device, to test those branches
-separate_data=1
-
-for algorithm in crc32c crc32 sha1 sha256
-do
-    if [ "${separate_data}" -eq 1 ]; then
-        data_option="--data-device=${image_dir}/data"
-    else
-        data_option=""
-    fi
-    integritysetup format "${loop}" --batch-mode -I "${algorithm}" "${data_option}" || exit 1
-    integritysetup open -I "${algorithm}" "${loop}" "${DM_NAME}" "${data_option}" || exit 1
-    mkfs.ext4 -U "${FS_UUID}" "${FULL_DM_DEV_NAME}" || exit 1
-
-    # Give userspace time to handle udev events for new FS showing up ...
-    udevadm settle
-
-    integritysetup close "${DM_NAME}" || exit 1
-
-    # create integritytab, generate units, start service
-    if [ "${separate_data}" -eq 1 ]; then
-        data_option=",data-device=${image_dir}/data"
-    else
-        data_option=""
-    fi
-    build_integrity_tab "${algorithm}${data_option}"
-
-    # Cause the generator to re-run
-    systemctl daemon-reload || exit 1
-
-    # Check for existence of unit files...
-    if [[ ! -e "/run/systemd/generator/systemd-integritysetup@${DM_NAME}.service" ]]; then
-        echo "Service file does not exist!"
-        exit 1
-    fi
-
-    # Make sure we are in a consistent state, e.g. not already active before we start
-    systemctl stop systemd-integritysetup@"${DM_NAME}".service || exit 1
-    systemctl start systemd-integritysetup@"${DM_NAME}".service || exit 1
-    # Reset the start-limit counters, as we're going to restart the service a couple of times
-    systemctl reset-failed systemd-integritysetup@"${DM_NAME}".service
-
-    # Check the signature on the FS to ensure we can retrieve it and that is matches
-    if [ -e "${FULL_DM_DEV_NAME}" ]; then
-        # If a separate device is used for the metadata storage, then blkid will return one of the loop devices
-        if [ "${separate_data}" -eq 1 ]; then
-            dev_name="$(integritysetup status ${DM_NAME} | grep '^\s*device:' | awk '{print $2}')"
-        else
-            dev_name="${FULL_DM_DEV_NAME}"
-        fi
-        if [ "${dev_name}" != "$(blkid -U "${FS_UUID}")" ]; then
-            echo "Failed to locate FS with matching UUID!"
-            exit 1
-        fi
-    else
-        echo "Failed to bring up integrity device!"
-        exit 1
-    fi
-
-    systemctl stop systemd-integritysetup@"${DM_NAME}".service || exit 1
-
-    if [ -e "${FULL_DM_DEV_NAME}" ]; then
-        echo "Expecting ${FULL_DM_DEV_NAME} to not exist after stopping unit!"
-        exit 1
-    fi
-
-    separate_data=0
-done
-
-touch /testok
diff --git a/test/units/testsuite-68.service b/test/units/testsuite-68.service
deleted file mode 100644 (file)
index 2d86e1f..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-68-PROPAGATE-EXIT-STATUS
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
diff --git a/test/units/testsuite-68.sh b/test/units/testsuite-68.sh
deleted file mode 100755 (executable)
index 11da48a..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# Wait for a service to enter a state within a timeout period, if it doesn't
-# enter the desired state within the timeout period then this function will
-# exit the test case with a non zero exit code.
-wait_on_state_or_fail() {
-    service=$1
-    expected_state=$2
-    timeout=$3
-
-    state=$(systemctl show "$service" --property=ActiveState --value)
-    while [ "$state" != "$expected_state" ]; do
-        if [ "$timeout" = "0" ]; then
-            systemd-analyze log-level info
-            exit 1
-        fi
-        timeout=$((timeout - 1))
-        sleep 1
-        state=$(systemctl show "$service" --property=ActiveState --value)
-    done
-}
-
-systemd-analyze log-level debug
-
-cat >/run/systemd/system/testservice-failure-68.service <<EOF
-[Unit]
-OnFailure=testservice-failure-exit-handler-68.service
-
-[Service]
-ExecStart=sh -c "exit 1"
-EOF
-
-cat >/run/systemd/system/testservice-failure-68-template.service <<EOF
-[Unit]
-OnFailure=testservice-failure-exit-handler-68-template@%n.service
-
-[Service]
-ExecStart=sh -c "exit 1"
-EOF
-
-cat >/run/systemd/system/testservice-success-68.service <<EOF
-[Unit]
-OnSuccess=testservice-success-exit-handler-68.service
-
-[Service]
-ExecStart=sh -c "exit 0"
-EOF
-
-cat >/run/systemd/system/testservice-success-68-template.service <<EOF
-[Unit]
-OnSuccess=testservice-success-exit-handler-68-template@%n.service
-
-[Service]
-ExecStart=sh -c "exit 0"
-EOF
-
-# Script to check that when an OnSuccess= dependency fires, the correct
-# MONITOR* env variables are passed.
-cat >/tmp/check_on_success.sh <<"EOF"
-#!/bin/sh
-
-set -ex
-env | sort
-if [ "$MONITOR_SERVICE_RESULT" != "success" ]; then
-    echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'success'"
-    exit 1
-fi
-
-if [ "$MONITOR_EXIT_CODE" != "exited" ]; then
-    echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'"
-    exit 1
-fi
-
-if [ "$MONITOR_EXIT_STATUS" != "0" ]; then
-    echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '0'"
-    exit 1
-fi
-
-if [ -z "$MONITOR_INVOCATION_ID" ]; then
-    echo "MONITOR_INVOCATION_ID unset"
-    exit 1
-fi
-
-if [ "$MONITOR_UNIT" != "testservice-success-68.service" ] &&
-   [ "$MONITOR_UNIT" != "testservice-success-68-template.service" ] &&
-   [ "$MONITOR_UNIT" != "testservice-transient-success-68.service" ]; then
-
-    echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-success-68[-template].service'"
-    exit 1
-fi
-
-exit 0
-EOF
-chmod +x /tmp/check_on_success.sh
-
-cat >/run/systemd/system/testservice-success-exit-handler-68.service <<EOF
-[Service]
-ExecStartPre=/tmp/check_on_success.sh
-ExecStart=/tmp/check_on_success.sh
-EOF
-
-cp /run/systemd/system/testservice-success-exit-handler-68.service \
-   /run/systemd/system/testservice-transient-success-exit-handler-68.service
-
-# Template version.
-cat >/run/systemd/system/testservice-success-exit-handler-68-template@.service <<EOF
-[Service]
-ExecStartPre=echo "triggered by %i"
-ExecStartPre=/tmp/check_on_success.sh
-ExecStart=/tmp/check_on_success.sh
-EOF
-
-# Script to check that when an OnFailure= dependency fires, the correct
-# MONITOR* env variables are passed.
-cat >/tmp/check_on_failure.sh <<"EOF"
-#!/bin/sh
-
-set -ex
-env | sort
-if [ "$MONITOR_SERVICE_RESULT" != "exit-code" ]; then
-    echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'exit-code'"
-    exit 1
-fi
-
-if [ "$MONITOR_EXIT_CODE" != "exited" ]; then
-    echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'"
-    exit 1
-fi
-
-if [ "$MONITOR_EXIT_STATUS" != "1" ]; then
-    echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '1'"
-    exit 1
-fi
-
-if [ -z "$MONITOR_INVOCATION_ID" ]; then
-    echo "MONITOR_INVOCATION_ID unset"
-    exit 1
-fi
-
-if [ "$MONITOR_UNIT" != "testservice-failure-68.service" ] &&
-   [ "$MONITOR_UNIT" != "testservice-failure-68-template.service" ] &&
-   [ "$MONITOR_UNIT" != "testservice-transient-failure-68.service" ]; then
-
-    echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-failure-68[-template].service'"
-    exit 1
-fi
-
-exit 0
-EOF
-chmod +x /tmp/check_on_failure.sh
-
-
-cat >/run/systemd/system/testservice-failure-exit-handler-68.service <<EOF
-[Service]
-# repeat the check to make sure that values are set correctly on repeated invocations
-Type=oneshot
-ExecStartPre=/tmp/check_on_failure.sh
-ExecStartPre=/tmp/check_on_failure.sh
-ExecStart=/tmp/check_on_failure.sh
-ExecStart=/tmp/check_on_failure.sh
-ExecStartPost=test -z '$MONITOR_SERVICE_RESULT'
-EOF
-
-cp /run/systemd/system/testservice-failure-exit-handler-68.service \
-   /run/systemd/system/testservice-transient-failure-exit-handler-68.service
-
-# Template version.
-cat >/run/systemd/system/testservice-failure-exit-handler-68-template@.service <<EOF
-[Service]
-Type=oneshot
-ExecStartPre=echo "triggered by %i"
-ExecStartPre=/tmp/check_on_failure.sh
-ExecStartPre=/tmp/check_on_failure.sh
-ExecStart=/tmp/check_on_failure.sh
-ExecStart=/tmp/check_on_failure.sh
-ExecStartPost=test -z '$MONITOR_SERVICE_RESULT'
-EOF
-
-systemctl daemon-reload
-
-: "-------I----------------------------------------------------"
-systemctl start testservice-failure-68.service
-wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
-
-: "-------II---------------------------------------------------"
-systemctl start testservice-success-68.service
-wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
-
-# Test some transient units since these exit very quickly.
-: "-------III--------------------------------------------------"
-systemd-run --unit=testservice-transient-success-68 \
-            --property=OnSuccess=testservice-transient-success-exit-handler-68.service \
-            sh -c "exit 0"
-wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
-
-: "-------IIII-------------------------------------------------"
-systemd-run --unit=testservice-transient-failure-68 \
-            --property=OnFailure=testservice-transient-failure-exit-handler-68.service \
-            sh -c "exit 1"
-wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
-
-# Test template handlers too
-: "-------V---------------------------------------------------"
-systemctl start testservice-success-68-template.service
-wait_on_state_or_fail "testservice-success-exit-handler-68-template@testservice-success-68-template.service.service" "inactive" "10"
-
-: "-------VI----------------------------------------------------"
-systemctl start testservice-failure-68-template.service
-wait_on_state_or_fail "testservice-failure-exit-handler-68-template@testservice-failure-68-template.service.service" "inactive" "10"
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-69.service b/test/units/testsuite-69.service
deleted file mode 100644 (file)
index 3910369..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-69-SHUTDOWN
-
-[Service]
-Type=oneshot
-ExecStart=true
diff --git a/test/units/testsuite-70.creds.sh b/test/units/testsuite-70.creds.sh
deleted file mode 100755 (executable)
index e66bfd1..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-export SYSTEMD_LOG_LEVEL=debug
-
-# Ensure that sandboxing doesn't stop creds from being accessible
-echo "test" > /tmp/testdata
-systemd-creds encrypt /tmp/testdata /tmp/testdata.encrypted --with-key=tpm2
-# LoadCredentialEncrypted
-systemd-run -p PrivateDevices=yes -p LoadCredentialEncrypted=testdata.encrypted:/tmp/testdata.encrypted --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata
-# SetCredentialEncrypted
-systemd-run -p PrivateDevices=yes -p SetCredentialEncrypted=testdata.encrypted:"$(cat /tmp/testdata.encrypted)" --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata
-
-rm -f /tmp/testdata
diff --git a/test/units/testsuite-70.cryptenroll.sh b/test/units/testsuite-70.cryptenroll.sh
deleted file mode 100755 (executable)
index f18ef02..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-cryptenroll_wipe_and_check() {(
-    set +o pipefail
-
-    : >/tmp/cryptenroll.out
-    systemd-cryptenroll "$@" |& tee /tmp/cryptenroll.out
-    grep -qE "Wiped slot [[:digit:]]+" /tmp/cryptenroll.out
-)}
-
-# There is an external issue with libcryptsetup on ppc64 that hits 95% of Ubuntu ppc64 test runs, so skip it
-if [[ "$(uname -m)" == "ppc64le" ]]; then
-    echo "Skipping systemd-cryptenroll tests on ppc64le, see https://github.com/systemd/systemd/issues/27716"
-    exit 0
-fi
-
-export SYSTEMD_LOG_LEVEL=debug
-IMAGE="$(mktemp /tmp/systemd-cryptenroll-XXX.image)"
-
-truncate -s 20M "$IMAGE"
-echo -n password >/tmp/password
-# Change file mode to avoid "/tmp/password has 0644 mode that is too permissive" messages
-chmod 0600 /tmp/password
-cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/password
-
-# Enroll additional tokens, keys, and passwords to exercise the list and wipe stuff
-systemd-cryptenroll --unlock-key-file=/tmp/password --tpm2-device=auto "$IMAGE"
-NEWPASSWORD="" systemd-cryptenroll --unlock-key-file=/tmp/password  --password "$IMAGE"
-NEWPASSWORD=foo systemd-cryptenroll --unlock-key-file=/tmp/password  --password "$IMAGE"
-for _ in {0..9}; do
-    systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE"
-done
-PASSWORD="" NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE"
-# Do some basic checks before we start wiping stuff
-systemd-cryptenroll "$IMAGE"
-systemd-cryptenroll "$IMAGE" | grep password
-systemd-cryptenroll "$IMAGE" | grep recovery
-# Let's start wiping
-cryptenroll_wipe_and_check "$IMAGE" --wipe=empty
-(! cryptenroll_wipe_and_check "$IMAGE" --wipe=empty)
-cryptenroll_wipe_and_check "$IMAGE" --wipe=empty,0
-PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --wipe=0,0,empty,0,pkcs11,fido2,000,recovery,password --password
-systemd-cryptenroll "$IMAGE" | grep password
-(! systemd-cryptenroll "$IMAGE" | grep recovery)
-# We shouldn't be able to wipe all keyslots without enrolling a new key first
-(! systemd-cryptenroll "$IMAGE" --wipe=all)
-PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --password --wipe=all
-# Check if the newly (and only) enrolled password works
-(! systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE")
-(! PASSWORD="" systemd-cryptenroll --recovery-key "$IMAGE")
-PASSWORD=foo systemd-cryptenroll --recovery-key "$IMAGE"
-
-systemd-cryptenroll --fido2-with-client-pin=false "$IMAGE"
-systemd-cryptenroll --fido2-with-user-presence=false "$IMAGE"
-systemd-cryptenroll --fido2-with-user-verification=false "$IMAGE"
-systemd-cryptenroll --tpm2-pcrs=8 "$IMAGE"
-systemd-cryptenroll --tpm2-pcrs=boot-loader-code+boot-loader-config "$IMAGE"
-
-# Unlocking using TPM2
-PASSWORD=foo systemd-cryptenroll --tpm2-device=auto "$IMAGE"
-systemd-cryptenroll --unlock-tpm2-device=auto --recovery-key "$IMAGE"
-systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --wipe-slot=tpm2 "$IMAGE"
-
-# Add PIN to TPM2 enrollment
-NEWPIN=1234 systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --tpm2-with-pin=yes "$IMAGE"
-
-# Change PIN on TPM2 enrollment
-PIN=1234 NEWPIN=4321 systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --tpm2-with-pin=yes "$IMAGE"
-PIN=4321 systemd-cryptenroll --unlock-tpm2-device=auto --recovery-key "$IMAGE"
-
-(! systemd-cryptenroll --fido2-with-client-pin=false)
-(! systemd-cryptenroll --fido2-with-user-presence=f "$IMAGE" /tmp/foo)
-(! systemd-cryptenroll --fido2-with-client-pin=1234 "$IMAGE")
-(! systemd-cryptenroll --fido2-with-user-presence=1234 "$IMAGE")
-(! systemd-cryptenroll --fido2-with-user-verification=1234 "$IMAGE")
-(! systemd-cryptenroll --tpm2-with-pin=1234 "$IMAGE")
-(! systemd-cryptenroll --recovery-key --password "$IMAGE")
-(! systemd-cryptenroll --password --recovery-key "$IMAGE")
-(! systemd-cryptenroll --password --fido2-device=auto "$IMAGE")
-(! systemd-cryptenroll --password --pkcs11-token-uri=auto "$IMAGE")
-(! systemd-cryptenroll --password --tpm2-device=auto "$IMAGE")
-(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-fido2-device=auto "$IMAGE")
-(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-key-file=/tmp/unlock "$IMAGE")
-(! systemd-cryptenroll --fido2-credential-algorithm=es512 "$IMAGE")
-(! systemd-cryptenroll --tpm2-public-key-pcrs=key "$IMAGE")
-(! systemd-cryptenroll --tpm2-pcrs=key "$IMAGE")
-(! systemd-cryptenroll --tpm2-pcrs=44+8 "$IMAGE")
-(! systemd-cryptenroll --tpm2-pcrs=hello "$IMAGE")
-(! systemd-cryptenroll --wipe-slot "$IMAGE")
-(! systemd-cryptenroll --wipe-slot=10240000 "$IMAGE")
-(! systemd-cryptenroll --fido2-device=auto --unlock-fido2-device=auto "$IMAGE")
-
-rm -f "$IMAGE"
diff --git a/test/units/testsuite-70.cryptsetup.sh b/test/units/testsuite-70.cryptsetup.sh
deleted file mode 100755 (executable)
index 0ecf34b..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-
-cryptsetup_has_token_plugin_support() {
-    local plugin_path
-
-    plugin_path="$(cryptsetup --help | sed -nr 's/.*LUKS2 external token plugin path: (.*)\./\1/p')/libcryptsetup-token-systemd-tpm2.so)"
-    cryptsetup --help | grep -q 'LUKS2 external token plugin support is compiled-in' && [[ -f "$plugin_path" ]]
-}
-
-tpm_check_failure_with_wrong_pin() {
-    local testIMAGE="${1:?}"
-    local badpin="${2:?}"
-    local goodpin="${3:?}"
-
-    # We need to be careful not to trigger DA lockout; allow 2 failures
-    tpm2_dictionarylockout -s -n 2
-    (! PIN=$badpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1)
-    # Verify the correct PIN works, to be sure the failure wasn't a DA lockout
-    PIN=$goodpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1
-    systemd-cryptsetup detach test-volume
-    # Clear/reset the DA lockout counter
-    tpm2_dictionarylockout -c
-}
-
-at_exit() {
-    # Evict the TPM primary key that we persisted
-    if [[ -n "${PERSISTENT_HANDLE:-}" ]]; then
-        tpm2_evictcontrol -c "$PERSISTENT_HANDLE"
-    fi
-}
-
-trap at_exit EXIT
-
-# Prepare a fresh disk image
-IMAGE="$(mktemp /tmp/systemd-cryptsetup-XXX.IMAGE)"
-
-truncate -s 20M "$IMAGE"
-echo -n passphrase >/tmp/passphrase
-# Change file mode to avoid "/tmp/passphrase has 0644 mode that is too permissive" messages
-chmod 0600 /tmp/passphrase
-cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase
-
-# Unlocking via keyfile
-systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto "$IMAGE"
-
-# Enroll unlock with default PCR policy
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-# Check with wrong PCR
-tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
-(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
-
-# Enroll unlock with PCR+PIN policy
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE"
-PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-# Check failure with wrong PIN; try a few times to make sure we avoid DA lockout
-for _ in {0..3}; do
-    tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456
-done
-
-# Check LUKS2 token plugin unlock (i.e. without specifying tpm2-device=auto)
-if cryptsetup_has_token_plugin_support; then
-    PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - headless=1
-    systemd-cryptsetup detach test-volume
-
-    # Check failure with wrong PIN
-    for _ in {0..3}; do
-        tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456
-    done
-else
-    echo 'cryptsetup has no LUKS2 token plugin support, skipping'
-fi
-
-# Check failure with wrong PCR (and correct PIN)
-tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
-(! PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
-
-# Enroll unlock with PCR 0+7
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-# Check with wrong PCR 0
-tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000
-(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
-
-if tpm_has_pcr sha256 12; then
-    # Enroll using an explicit PCR value (that does match current PCR value)
-    systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-    EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
-    PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE"
-    systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-    systemd-cryptsetup detach test-volume
-
-    # Same as above plus more PCRs without the value or alg specified
-    systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-    EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
-    PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE"
-    systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-    systemd-cryptsetup detach test-volume
-
-    # Same as above plus more PCRs with hash alg specified but hash value not specified
-    systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-    EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
-    PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1:sha256,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE"
-    systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-    systemd-cryptsetup detach test-volume
-
-    # Now the interesting part, enrolling using a hash value that doesn't match the current PCR value
-    systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-    tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12
-    CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
-    EXPECTED_PCR_VALUE=$(cat /tmp/pcr.dat /tmp/pcr.dat | openssl dgst -sha256 -r | cut -d ' ' -f 1)
-    PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE"
-    (! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1)
-    tpm2_pcrextend "12:sha256=$CURRENT_PCR_VALUE"
-    systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-    systemd-cryptsetup detach test-volume
-
-    # enroll TPM using device key instead of direct access, then verify unlock using TPM
-    tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12
-    CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12)
-    tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub
-    systemd-analyze srk > /tmp/srk2.pub
-    cmp /tmp/srk.pub /tmp/srk2.pub
-    if [ -f /run/systemd/tpm2-srk-public-key.tpm2b_public ] ; then
-        cmp /tmp/srk.pub /run/systemd/tpm2-srk-public-key.tpm2b_public
-    fi
-
-    # --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS
-    if openssl_supports_kdf SSKDF; then
-        PASSWORD=passphrase systemd-cryptenroll --tpm2-device-key=/tmp/srk.pub --tpm2-pcrs="12:sha256=$CURRENT_PCR_VALUE" "$IMAGE"
-        systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-        systemd-cryptsetup detach test-volume
-    fi
-
-    rm -f /tmp/pcr.dat /tmp/srk.pub
-fi
-
-# Use default (0) seal key handle
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0 "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x0 "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-# Use SRK seal key handle
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=81000001 "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x81000001 "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-# Test invalid ranges: pcr, nv, session, permanent
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=7 "$IMAGE")          # PCR
-(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x01000001 "$IMAGE") # NV index
-(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x02000001 "$IMAGE") # HMAC/loaded session
-(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x03000001 "$IMAGE") # Policy/saved session
-(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x40000001 "$IMAGE") # Permanent
-
-# Use non-SRK persistent seal key handle (by creating/persisting new key)
-PRIMARY=/tmp/primary.ctx
-tpm2_createprimary -c "$PRIMARY"
-PERSISTENT_LINE=$(tpm2_evictcontrol -c "$PRIMARY" | grep persistent-handle)
-PERSISTENT_HANDLE="0x${PERSISTENT_LINE##*0x}"
-tpm2_flushcontext -t
-
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="${PERSISTENT_HANDLE#0x}" "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="$PERSISTENT_HANDLE" "$IMAGE"
-systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1
-systemd-cryptsetup detach test-volume
-
-# --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS
-if openssl_supports_kdf SSKDF; then
-    # Make sure that --tpm2-device-key= also works with systemd-repart
-    tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub
-    mkdir /tmp/dditest
-    cat > /tmp/dditest/50-root.conf <<EOF
-[Partition]
-Type=root
-Format=ext4
-CopyFiles=/tmp/dditest:/
-Encrypt=tpm2
-EOF
-    PASSWORD=passphrase systemd-repart --tpm2-device-key=/tmp/srk.pub --definitions=/tmp/dditest --empty=create --size=50M /tmp/dditest.raw --tpm2-pcrs=
-    DEVICE="$(systemd-dissect --attach /tmp/dditest.raw)"
-    udevadm wait --settle --timeout=10 "$DEVICE"
-    systemd-cryptsetup attach dditest "$DEVICE"p1 - tpm2-device=auto,headless=yes
-    mkdir /tmp/dditest.mnt
-    mount -t ext4 /dev/mapper/dditest /tmp/dditest.mnt
-    cmp /tmp/dditest.mnt/50-root.conf /tmp/dditest/50-root.conf
-    umount /tmp/dditest.mnt
-    rmdir /tmp/dditest.mnt
-    rm /tmp/dditest.raw
-    rm /tmp/dditest/50-root.conf
-    rmdir /tmp/dditest
-fi
-
-rm -f "$IMAGE" "$PRIMARY"
diff --git a/test/units/testsuite-70.measure.sh b/test/units/testsuite-70.measure.sh
deleted file mode 100755 (executable)
index 3336f38..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-SD_MEASURE="/usr/lib/systemd/systemd-measure"
-
-if [[ ! -x "${SD_MEASURE:?}" ]]; then
-    echo "$SD_MEASURE not found, skipping the test"
-    exit 0
-fi
-
-IMAGE="$(mktemp /tmp/systemd-measure-XXX.image)"
-
-echo HALLO >/tmp/tpmdata1
-echo foobar >/tmp/tpmdata2
-
-cat >/tmp/result <<EOF
-11:sha1=5177e4ad69db92192c10e5f80402bf81bfec8a81
-11:sha256=37b48bd0b222394dbe3cceff2fca4660c4b0a90ae9369ec90b42f14489989c13
-11:sha384=5573f9b2caf55b1d0a6a701f890662d682af961899f0419cf1e2d5ea4a6a68c1f25bd4f5b8a0865eeee82af90f5cb087
-11:sha512=961305d7e9981d6606d1ce97b3a9a1f92610cac033e9c39064895f0e306abc1680463d55767bd98e751eae115bdef3675a9ee1d29ed37da7885b1db45bb2555b
-EOF
-"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=: | cmp - /tmp/result
-
-cat >/tmp/result.json <<EOF
-{"sha1":[{"pcr":11,"hash":"5177e4ad69db92192c10e5f80402bf81bfec8a81"}],"sha256":[{"pcr":11,"hash":"37b48bd0b222394dbe3cceff2fca4660c4b0a90ae9369ec90b42f14489989c13"}],"sha384":[{"pcr":11,"hash":"5573f9b2caf55b1d0a6a701f890662d682af961899f0419cf1e2d5ea4a6a68c1f25bd4f5b8a0865eeee82af90f5cb087"}],"sha512":[{"pcr":11,"hash":"961305d7e9981d6606d1ce97b3a9a1f92610cac033e9c39064895f0e306abc1680463d55767bd98e751eae115bdef3675a9ee1d29ed37da7885b1db45bb2555b"}]}
-EOF
-"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=: -j | diff -u - /tmp/result.json
-
-cat >/tmp/result <<EOF
-11:sha1=6765ee305db063040c454d32697d922b3d4f232b
-11:sha256=21c49c1242042649e09c156546fd7d425ccc3c67359f840507b30be4e0f6f699
-11:sha384=08d0b003a134878eee552070d51d58abe942f457ca85704131dd36f73728e7327ca837594bc9d5ac7de818d02a3d5dd2
-11:sha512=65120f6ebc04b156421c6f3d543b2fad545363d9ca61c514205459e9c0e0b22e09c23605eae5853e38458ef3ca54e087168af8d8a882a98d220d9391e48be6d0
-EOF
-"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=foo | cmp - /tmp/result
-
-cat >/tmp/result.json <<EOF
-{"sha1":[{"phase":"foo","pcr":11,"hash":"6765ee305db063040c454d32697d922b3d4f232b"}],"sha256":[{"phase":"foo","pcr":11,"hash":"21c49c1242042649e09c156546fd7d425ccc3c67359f840507b30be4e0f6f699"}],"sha384":[{"phase":"foo","pcr":11,"hash":"08d0b003a134878eee552070d51d58abe942f457ca85704131dd36f73728e7327ca837594bc9d5ac7de818d02a3d5dd2"}],"sha512":[{"phase":"foo","pcr":11,"hash":"65120f6ebc04b156421c6f3d543b2fad545363d9ca61c514205459e9c0e0b22e09c23605eae5853e38458ef3ca54e087168af8d8a882a98d220d9391e48be6d0"}]}
-EOF
-"$SD_MEASURE" calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=foo -j | diff -u - /tmp/result.json
-
-rm /tmp/result /tmp/result.json
-
-if ! tpm_has_pcr sha1 11 || ! tpm_has_pcr sha256 11; then
-    echo "PCR sysfs files not found, skipping signed PCR policy tests"
-    exit 0
-fi
-
-# Generate key pair
-openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "/tmp/pcrsign-private.pem"
-openssl rsa -pubout -in "/tmp/pcrsign-private.pem" -out "/tmp/pcrsign-public.pem"
-
-MEASURE_BANKS=("--bank=sha256")
-# Check if SHA1 signatures are supported
-#
-# Some distros have started phasing out SHA1, so make sure the SHA1
-# signatures are supported before trying to use them.
-if echo hello | openssl dgst -sign /tmp/pcrsign-private.pem -sha1 >/dev/null; then
-    MEASURE_BANKS+=("--bank=sha1")
-fi
-
-# Sign current PCR state with it
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: | tee "/tmp/pcrsign.sig"
-dd if=/dev/urandom of=/tmp/pcrtestdata bs=1024 count=64
-systemd-creds encrypt /tmp/pcrtestdata /tmp/pcrtestdata.encrypted --with-key=host+tpm2-with-public-key --tpm2-public-key="/tmp/pcrsign-public.pem"
-systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" | cmp - /tmp/pcrtestdata
-
-# Invalidate PCR, decrypting should fail now
-tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000
-(! systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" >/dev/null)
-
-# Sign new PCR state, decrypting should work now.
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig2"
-systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig2" | cmp - /tmp/pcrtestdata
-
-# Now, do the same, but with a cryptsetup binding
-truncate -s 20M "$IMAGE"
-cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase
-# Ensure that an unrelated signature, when not requested, is not used
-touch /run/systemd/tpm2-pcr-signature.json
-systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" "$IMAGE"
-# Reset and use the signature now
-rm -f /run/systemd/tpm2-pcr-signature.json
-systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE"
-systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" --tpm2-signature="/tmp/pcrsign.sig2" "$IMAGE"
-
-# Check if we can activate that (without the token module stuff)
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup detach test-volume2
-
-# Check if we can activate that (and a second time with the token module stuff enabled)
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup detach test-volume2
-
-# After extending the PCR things should fail
-tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000
-(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1)
-(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1)
-
-# But once we sign the current PCRs, we should be able to unlock again
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig3"
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
-systemd-cryptsetup detach test-volume2
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
-systemd-cryptsetup detach test-volume2
-
-# Test --append mode and de-duplication. With the same parameters signing should not add a new entry
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig3" >"/tmp/pcrsign.sig4"
-cmp "/tmp/pcrsign.sig3" "/tmp/pcrsign.sig4"
-
-# Sign one more phase, this should
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig4" >"/tmp/pcrsign.sig5"
-(! cmp "/tmp/pcrsign.sig4" "/tmp/pcrsign.sig5")
-
-# Should still be good to unlock, given the old entry still exists
-SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig5",headless=1
-systemd-cryptsetup detach test-volume2
-
-# Adding both signatures once more should not change anything, due to the deduplication
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig5" >"/tmp/pcrsign.sig6"
-"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig6" >"/tmp/pcrsign.sig7"
-cmp "/tmp/pcrsign.sig5" "/tmp/pcrsign.sig7"
-
-rm -f "$IMAGE"
diff --git a/test/units/testsuite-70.pcrextend.sh b/test/units/testsuite-70.pcrextend.sh
deleted file mode 100755 (executable)
index 318fce0..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
-
-if [[ ! -x "${SD_PCREXTEND:?}" ]] || ! tpm_has_pcr sha256 11 || ! tpm_has_pcr sha256 15; then
-    echo "$SD_PCREXTEND or PCR sysfs files not found, skipping PCR extension tests"
-    exit 0
-fi
-
-at_exit() {
-    if [[ $? -ne 0 ]]; then
-        # Dump the event log on fail, to make debugging a bit easier
-        jq --seq --slurp </run/log/systemd/tpm2-measure.log
-    fi
-}
-
-trap at_exit EXIT
-
-# Temporarily override sd-pcrextend's sanity checks
-export SYSTEMD_FORCE_MEASURE=1
-
-"$SD_PCREXTEND" --help
-"$SD_PCREXTEND" --version
-"$SD_PCREXTEND" foo
-"$SD_PCREXTEND" --machine-id
-"$SD_PCREXTEND" --tpm2-device=list
-"$SD_PCREXTEND" --tpm2-device=auto foo
-"$SD_PCREXTEND" --tpm2-device=/dev/tpm0 foo
-"$SD_PCREXTEND" --bank=sha256 foo
-"$SD_PCREXTEND" --bank=sha256 --bank=sha256 foo
-"$SD_PCREXTEND" --graceful foo
-"$SD_PCREXTEND" --pcr=15 foo
-"$SD_PCREXTEND" --file-system=/
-"$SD_PCREXTEND" --file-system=/tmp --file-system=/
-"$SD_PCREXTEND" --file-system=/tmp --file-system=/ --pcr=15 --pcr=11
-
-if tpm_has_pcr sha1 11; then
-    "$SD_PCREXTEND" --bank=sha1 --pcr=11 foo
-fi
-
-(! "$SD_PCREXTEND")
-(! "$SD_PCREXTEND" "")
-(! "$SD_PCREXTEND" foo bar)
-(! "$SD_PCREXTEND" --bank= foo)
-(! "$SD_PCREXTEND" --tpm2-device= foo)
-(! "$SD_PCREXTEND" --tpm2-device=/dev/null foo)
-(! "$SD_PCREXTEND" --pcr= foo)
-(! "$SD_PCREXTEND" --pcr=-1 foo)
-(! "$SD_PCREXTEND" --pcr=1024 foo)
-(! "$SD_PCREXTEND" --foo=bar)
-
-unset SYSTEMD_FORCE_MEASURE
-
-# Note: since we're reading the TPM event log as json-seq, the same rules apply to the output
-#       as well, i.e. each record is prefixed by RS (0x1E, 036) and suffixed by LF (0x0A, 012).
-#       LF is usually eaten by bash, but RS needs special handling.
-
-# Save the number of events in the current event log, so we can skip them when
-# checking changes caused by following tests
-RECORD_COUNT="$(jq --seq --slurp '. | length' </run/log/systemd/tpm2-measure.log | tr -d '\036')"
-
-# Let's measure the machine ID
-tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15
-mv /etc/machine-id /etc/machine-id.save
-echo 994013bf23864ee7992eab39a96dd3bb >/etc/machine-id
-SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --machine-id
-mv /etc/machine-id.save /etc/machine-id
-tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15
-
-# And check it matches expectations
-diff /tmp/newpcr15 \
-     <(cat /tmp/oldpcr15 <(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
-
-# Check that the event log record was properly written
-test "$(jq --seq --slurp ".[$RECORD_COUNT].pcr" </run/log/systemd/tpm2-measure.log)" == "$(printf '\x1e15')"
-DIGEST_EXPECTED="$(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -hex -sha256 -r)"
-DIGEST_CURRENT="$(jq --seq --slurp --raw-output ".[$RECORD_COUNT].digests[] | select(.hashAlg == \"sha256\").digest" </run/log/systemd/tpm2-measure.log) *stdin"
-test "$DIGEST_EXPECTED" == "$DIGEST_CURRENT"
-
-RECORD_COUNT=$((RECORD_COUNT + 1))
-# And similar for the boot phase measurement into PCR 11
-tpm2_pcrread sha256:11 -Q -o /tmp/oldpcr11
-# Do the equivalent of 'SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" foobar' via Varlink, just to test the Varlink logic (but first we need to patch out the conditionalization...)
-mkdir -p /run/systemd/system/systemd-pcrextend.socket.d
-cat > /run/systemd/system/systemd-pcrextend.socket.d/50-no-condition.conf <<EOF
-[Unit]
-# Turn off all conditions */
-ConditionSecurity=
-EOF
-systemctl daemon-reload
-systemctl restart systemd-pcrextend.socket
-varlinkctl call /run/systemd/io.systemd.PCRExtend io.systemd.PCRExtend.Extend '{"pcr":11,"text":"foobar"}'
-tpm2_pcrread sha256:11 -Q -o /tmp/newpcr11
-
-diff /tmp/newpcr11 \
-    <(cat /tmp/oldpcr11 <(echo -n "foobar" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
-
-# Check the event log for the 2nd new record since $RECORD_COUNT
-test "$(jq --seq --slurp ".[$RECORD_COUNT].pcr" </run/log/systemd/tpm2-measure.log)" == "$(printf '\x1e11')"
-DIGEST_EXPECTED="$(echo -n "foobar" | openssl dgst -hex -sha256 -r)"
-DIGEST_CURRENT="$(jq --seq --slurp --raw-output ".[$RECORD_COUNT].digests[] | select(.hashAlg == \"sha256\").digest" </run/log/systemd/tpm2-measure.log) *stdin"
-test "$DIGEST_EXPECTED" == "$DIGEST_CURRENT"
-
-# Measure a file system into PCR 15
-tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15
-SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --file-system=/
-# Put together the "file system word" we just sent to the TPM
-#   file-system:MOUNTPOINT:TYPE:UUID:LABEL:PART_ENTRY_UUID:PART_ENTRY_TYPE:PART_ENTRY_NAME
-ROOT_DEVICE="$(findmnt -n -o SOURCE /)"
-FS_WORD="$(lsblk -n -o MOUNTPOINT,FSTYPE,UUID,LABEL,PARTUUID,PARTTYPE,PARTLABEL "$ROOT_DEVICE" | sed -r 's/[ ]+/:/g')"
-tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15
-
-# And check if it matches with the current PCR 15 state
-diff /tmp/newpcr15 \
-     <(cat /tmp/oldpcr15 <(echo -n "file-system:$FS_WORD" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
-
-rm -f /tmp/oldpcr{11,15} /tmp/newpcr{11,15}
diff --git a/test/units/testsuite-70.pcrlock.sh b/test/units/testsuite-70.pcrlock.sh
deleted file mode 100755 (executable)
index fd51161..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-export PAGER=
-SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
-SD_PCRLOCK="/usr/lib/systemd/systemd-pcrlock"
-
-if [[ ! -x "${SD_PCREXTEND:?}" ]] || [[ ! -x "${SD_PCRLOCK:?}" ]] ; then
-    echo "$SD_PCREXTEND or $SD_PCRLOCK not found, skipping pcrlock tests"
-    exit 0
-fi
-
-at_exit() {
-    if [[ $? -ne 0 ]]; then
-        # Dump the event log on fail, to make debugging a bit easier
-        [[ -e /run/log/systemd/tpm2-measure.log ]] && jq --seq --slurp </run/log/systemd/tpm2-measure.log
-    fi
-
-    return 0
-}
-
-trap at_exit EXIT
-
-# Temporarily override sd-pcrextend's sanity checks
-export SYSTEMD_FORCE_MEASURE=1
-
-# The PCRs we are going to lock to. We exclude the various PCRs we touched
-# above where no event log record was written, because we cannot analyze
-# things without event log. We include debug PCR 16, see below.
-PCRS="1+2+3+4+5+16"
-
-# Remove the old measurement log, as it contains all kinds of nonsense from the
-# previous test, which will fail our consistency checks. Removing the file also
-# means we'll fail consistency check, but at least we'll fail them consistently
-# (as the PCR values simply won't match the log).
-rm -f /run/log/systemd/tpm2-measure.log
-
-# Ensure a truncated log doesn't crash pcrlock
-echo -n -e \\x1e >/tmp/borked
-set +e
-SYSTEMD_MEASURE_LOG_USERSPACE=/tmp/borked "$SD_PCRLOCK" cel --no-pager --json=pretty
-ret=$?
-set -e
-# If it crashes the exit code will be 149
-test $ret -eq 1
-
-SYSTEMD_COLORS=256 "$SD_PCRLOCK"
-"$SD_PCRLOCK" cel --no-pager --json=pretty
-"$SD_PCRLOCK" log --pcr="$PCRS"
-"$SD_PCRLOCK" log --json=pretty --pcr="$PCRS"
-"$SD_PCRLOCK" list-components
-"$SD_PCRLOCK" list-components --location=250-
-"$SD_PCRLOCK" list-components --location=250-:350-
-"$SD_PCRLOCK" lock-firmware-config
-"$SD_PCRLOCK" lock-gpt
-"$SD_PCRLOCK" lock-machine-id
-"$SD_PCRLOCK" lock-file-system
-"$SD_PCRLOCK" lock-file-system /
-"$SD_PCRLOCK" predict --pcr="$PCRS"
-"$SD_PCRLOCK" predict --pcr="0x1+0x3+4"
-"$SD_PCRLOCK" predict --json=pretty --pcr="$PCRS"
-
-SD_STUB="$(find /usr/lib/systemd/boot/efi/ -name "systemd-boot*.efi" | head -n1)"
-if [[ -n "$SD_STUB" ]]; then
-    "$SD_PCRLOCK" lock-pe "$SD_STUB"
-    "$SD_PCRLOCK" lock-pe <"$SD_STUB"
-    "$SD_PCRLOCK" lock-uki "$SD_STUB"
-    "$SD_PCRLOCK" lock-uki <"$SD_STUB"
-fi
-
-PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
-# Repeat immediately (this call will have to reuse the nvindex, rather than create it)
-"$SD_PCRLOCK" make-policy --pcr="$PCRS"
-"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force
-
-img="/tmp/pcrlock.img"
-truncate -s 20M "$img"
-echo -n hoho >/tmp/pcrlockpwd
-chmod 0600 /tmp/pcrlockpwd
-cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$img" /tmp/pcrlockpwd
-
-systemd-cryptenroll --unlock-key-file=/tmp/pcrlockpwd --tpm2-device=auto --tpm2-pcrlock=/var/lib/systemd/pcrlock.json --wipe-slot=tpm2 "$img"
-systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
-systemd-cryptsetup detach pcrlock
-
-# Measure something into PCR 16 (the "debug" PCR), which should make the activation fail
-"$SD_PCREXTEND" --pcr=16 test70
-
-"$SD_PCRLOCK" cel --json=pretty
-
-(! systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless )
-
-# Now add a component for it, rebuild policy and it should work (we'll rebuild
-# once like that, but don't provide the recovery pin. This should fail, since
-# the PCR is hosed after all. But then we'll use recovery pin, and it should
-# work.
-echo -n test70 | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock --pcr=16
-(! "$SD_PCRLOCK" make-policy --pcr="$PCRS")
-PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
-
-systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
-systemd-cryptsetup detach pcrlock
-
-# And now let's do it the clean way, and generate the right policy ahead of time.
-echo -n test70-take-two | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock --pcr=16
-"$SD_PCRLOCK" make-policy --pcr="$PCRS"
-# the next one should be skipped because redundant
-"$SD_PCRLOCK" make-policy --pcr="$PCRS"
-# but this one should not be skipped, even if redundant, because we force it
-"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force --recovery-pin=show
-
-"$SD_PCREXTEND" --pcr=16 test70-take-two
-
-"$SD_PCRLOCK" cel --json=pretty
-
-systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
-systemd-cryptsetup detach pcrlock
-
-# Now use the root fs support, i.e. make the tool write a copy of the pcrlock
-# file as service credential to some temporary dir and remove the local copy, so that
-# it has to use the credential version.
-mkdir /tmp/fakexbootldr
-SYSTEMD_XBOOTLDR_PATH=/tmp/fakexbootldr SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 "$SD_PCRLOCK" make-policy --pcr="$PCRS" --force
-mv /var/lib/systemd/pcrlock.json /var/lib/systemd/pcrlock.json.gone
-
-systemd-creds decrypt /tmp/fakexbootldr/loader/credentials/pcrlock.*.cred
-
-SYSTEMD_ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY=/tmp/fakexbootldr/loader/credentials systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,headless
-systemd-cryptsetup detach pcrlock
-
-mv /var/lib/systemd/pcrlock.json.gone /var/lib/systemd/pcrlock.json
-SYSTEMD_XBOOTLDR_PATH=/tmp/fakexbootldr SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 "$SD_PCRLOCK" remove-policy
-
-"$SD_PCRLOCK" unlock-firmware-config
-"$SD_PCRLOCK" unlock-gpt
-"$SD_PCRLOCK" unlock-machine-id
-"$SD_PCRLOCK" unlock-file-system
-"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock
-"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock
-
-(! "$SD_PCRLOCK" "")
-(! "$SD_PCRLOCK" predict --pcr=-1)
-(! "$SD_PCRLOCK" predict --pcr=foo)
-(! "$SD_PCRLOCK" predict --pcr=1+1)
-(! "$SD_PCRLOCK" predict --pcr=1+++++1)
-(! "$SD_PCRLOCK" make-policy --nv-index=0)
-(! "$SD_PCRLOCK" make-policy --nv-index=foo)
-(! "$SD_PCRLOCK" list-components --location=:)
-(! "$SD_PCRLOCK" lock-gpt "")
-(! "$SD_PCRLOCK" lock-gpt /dev/sr0)
-(! "$SD_PCRLOCK" lock-pe /dev/full)
-(! "$SD_PCRLOCK" lock-pe /bin/true)
-(! "$SD_PCRLOCK" lock-uki /dev/full)
-(! "$SD_PCRLOCK" lock-uki /bin/true)
-(! "$SD_PCRLOCK" lock-file-system "")
-
-# Exercise Varlink API a bit (but first turn off condition)
-
-mkdir -p /run/systemd/system/systemd-pcrlock.socket.d
-cat > /run/systemd/system/systemd-pcrlock.socket.d/50-no-condition.conf <<EOF
-[Unit]
-# Turn off all conditions
-ConditionSecurity=
-EOF
-
-systemctl daemon-reload
-systemctl restart systemd-pcrlock.socket
-
-varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.RemovePolicy '{}'
-varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.MakePolicy '{}'
-varlinkctl call --collect --json=pretty /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.ReadEventLog '{}'
-
-rm "$img" /tmp/pcrlockpwd
diff --git a/test/units/testsuite-70.service b/test/units/testsuite-70.service
deleted file mode 100644 (file)
index a9c1542..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-70-TPM2
-Wants=tpm2.target
-After=tpm2.target
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
diff --git a/test/units/testsuite-70.sh b/test/units/testsuite-70.sh
deleted file mode 100755 (executable)
index 9c2a033..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
diff --git a/test/units/testsuite-70.tpm2-setup.sh b/test/units/testsuite-70.tpm2-setup.sh
deleted file mode 100755 (executable)
index faf6fe7..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-export SYSTEMD_LOG_LEVEL=debug
-SD_TPM2SETUP="/usr/lib/systemd/systemd-tpm2-setup"
-
-if [[ ! -x "${SD_TPM2SETUP:?}" ]]; then
-    echo "$SD_TPM2SETUP not found, skipping the test"
-    exit 0
-fi
-
-"$SD_TPM2SETUP" --help
-"$SD_TPM2SETUP" --version
-"$SD_TPM2SETUP" --tpm2-device=list
-"$SD_TPM2SETUP" --tpm2-device=auto
-"$SD_TPM2SETUP" --tpm2-device=/dev/tpm0
-"$SD_TPM2SETUP" --early=yes
-"$SD_TPM2SETUP" --early=yes
-"$SD_TPM2SETUP" --early=no
-"$SD_TPM2SETUP" --early=no
-
-(! "$SD_TPM2SETUP" "")
-(! "$SD_TPM2SETUP" --tpm2-device=)
-(! "$SD_TPM2SETUP" --tpm2-device=/dev/null)
-(! "$SD_TPM2SETUP" --foo=bar)
diff --git a/test/units/testsuite-71.service b/test/units/testsuite-71.service
deleted file mode 100644 (file)
index 1718629..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-71-HOSTNAME
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-71.sh b/test/units/testsuite-71.sh
deleted file mode 100755 (executable)
index dc3f587..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-restore_hostname() {
-    if [[ -e /tmp/hostname.bak ]]; then
-        mv /tmp/hostname.bak /etc/hostname
-    else
-        rm -f /etc/hostname
-    fi
-}
-
-testcase_hostname() {
-    local orig=
-
-    if [[ -f /etc/hostname ]]; then
-        cp /etc/hostname /tmp/hostname.bak
-        orig=$(cat /etc/hostname)
-    fi
-
-    trap restore_hostname RETURN
-
-    # should activate daemon and work
-    if [[ -n "$orig" ]]; then
-        assert_in "Static hostname: $orig" "$(hostnamectl)"
-    fi
-    assert_in "Kernel: $(uname -s) $(uname -r)" "$(hostnamectl)"
-
-    # change hostname
-    assert_rc 0 hostnamectl set-hostname testhost
-    assert_eq "$(cat /etc/hostname)" "testhost"
-    assert_in "Static hostname: testhost" "$(hostnamectl)"
-
-    if [[ -n "$orig" ]]; then
-        # reset to original
-        assert_rc 0 hostnamectl set-hostname "$orig"
-        assert_eq "$(cat /etc/hostname)" "$orig"
-        assert_in "Static hostname: $orig" "$(hostnamectl)"
-    fi
-}
-
-restore_machine_info() {
-    if [[ -e /tmp/machine-info.bak ]]; then
-        mv /tmp/machine-info.bak /etc/machine-info
-    else
-        rm -f /etc/machine-info
-    fi
-}
-
-get_chassis() (
-    # shellcheck source=/dev/null
-    . /etc/machine-info
-
-    echo "$CHASSIS"
-)
-
-stop_hostnamed() {
-    systemctl stop systemd-hostnamed.service
-    # Reset trigger limit. This might fail if the unit was unloaded already, so ignore any errors.
-    systemctl reset-failed systemd-hostnamed || :
-}
-
-testcase_chassis() {
-    local i
-
-    if [[ -f /etc/machine-info ]]; then
-        cp /etc/machine-info /tmp/machine-info.bak
-    fi
-
-    trap restore_machine_info RETURN
-
-    # Invalid chassis type is refused
-    assert_rc 1 hostnamectl chassis hoge
-
-    # Valid chassis types
-    for i in vm container desktop laptop convertible server tablet handset watch embedded; do
-        hostnamectl chassis "$i"
-        assert_eq "$(hostnamectl chassis)" "$i"
-        assert_eq "$(get_chassis)" "$i"
-    done
-
-    stop_hostnamed
-    rm -f /etc/machine-info
-
-    # fallback chassis type
-    if systemd-detect-virt --quiet --container; then
-        assert_eq "$(hostnamectl chassis)" container
-    elif systemd-detect-virt --quiet --vm; then
-        assert_eq "$(hostnamectl chassis)" vm
-    fi
-}
-
-restore_sysfs_dmi() {
-    umount /sys/class/dmi/id
-    rm -rf /run/systemd/system/systemd-hostnamed.service.d
-    systemctl daemon-reload
-    stop_hostnamed
-}
-
-testcase_firmware_date() {
-    # No DMI on s390x or ppc
-    if [[ ! -d /sys/class/dmi/id ]]; then
-        echo "/sys/class/dmi/id not found, skipping firmware date tests."
-        return 0
-    fi
-
-    trap restore_sysfs_dmi RETURN
-
-    # Ignore /sys being mounted as tmpfs
-    mkdir -p /run/systemd/system/systemd-hostnamed.service.d/
-    cat >/run/systemd/system/systemd-hostnamed.service.d/override.conf <<EOF
-[Service]
-Environment="SYSTEMD_DEVICE_VERIFY_SYSFS=0"
-Environment="SYSTEMD_HOSTNAME_FORCE_DMI=1"
-EOF
-    systemctl daemon-reload
-
-    mount -t tmpfs none /sys/class/dmi/id
-    echo '1' >/sys/class/dmi/id/uevent
-
-    echo '09/08/2000' >/sys/class/dmi/id/bios_date
-    stop_hostnamed
-    assert_in '2000-09-08' "$(hostnamectl)"
-
-    echo '2022' >/sys/class/dmi/id/bios_date
-    stop_hostnamed
-    assert_not_in 'Firmware Date' "$(hostnamectl)"
-
-    echo 'garbage' >/sys/class/dmi/id/bios_date
-    stop_hostnamed
-    assert_not_in 'Firmware Date' "$(hostnamectl)"
-}
-
-testcase_nss-myhostname() {
-    local database host i
-
-    HOSTNAME="$(hostnamectl hostname)"
-
-    # Set up a dummy network for _gateway and _outbound labels
-    ip link add foo type dummy
-    ip link set up dev foo
-    ip addr add 10.0.0.2/24 dev foo
-    for i in {128..150}; do
-        ip addr add "10.0.0.$i/24" dev foo
-    done
-    ip route add 10.0.0.1 dev foo
-    ip route add default via 10.0.0.1 dev foo
-
-    # Note: `getent hosts` probes gethostbyname2(), whereas `getent ahosts` probes gethostbyname3()
-    #       and gethostbyname4() (through getaddrinfo() -> gaih_inet() -> get_nss_addresses())
-    getent hosts -s myhostname
-    getent ahosts -s myhostname
-
-    # With IPv6 disabled
-    sysctl -w net.ipv6.conf.all.disable_ipv6=1
-    # Everything under .localhost and .localhost.localdomain should resolve to localhost
-    for host in {foo.,foo.bar.baz.,.,}localhost{,.} {foo.,foo.bar.baz.,.,}localhost.localdomain{,.}; do
-        run_and_grep "^127\.0\.0\.1\s+localhost$" getent hosts -s myhostname "$host"
-        run_and_grep "^127\.0\.0\.1\s+STREAM\s+localhost" getent ahosts -s myhostname "$host"
-        run_and_grep "^127\.0\.0\.1\s+STREAM\s+localhost" getent ahostsv4 -s myhostname "$host"
-        (! getent ahostsv6 -s myhostname localhost)
-    done
-    for i in 2 {128..150}; do
-        run_and_grep "^10\.0\.0\.$i\s+$HOSTNAME$" getent hosts -s myhostname "$HOSTNAME"
-        run_and_grep "^10\.0\.0\.$i\s+" getent ahosts -s myhostname "$HOSTNAME"
-        run_and_grep "^10\.0\.0\.$i\s+" getent ahostsv4 -s myhostname "$HOSTNAME"
-        run_and_grep "^10\.0\.0\.$i\s+$HOSTNAME$" getent hosts -s myhostname "10.0.0.$i"
-        run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahosts -s myhostname "10.0.0.$i"
-        run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahostsv4 -s myhostname "10.0.0.$i"
-    done
-    for database in hosts ahosts ahostsv4 ahostsv6; do
-        (! getent "$database" -s myhostname ::1)
-    done
-    (! getent ahostsv6 -s myhostname "$HOSTNAME")
-    run_and_grep -n "^fe80:[^ ]+\s+STREAM$" getent ahosts -s myhostname "$HOSTNAME"
-
-    # With IPv6 enabled
-    sysctl -w net.ipv6.conf.all.disable_ipv6=0
-    # Everything under .localhost and .localhost.localdomain should resolve to localhost
-    for host in {foo.,foo.bar.baz.,.,}localhost{,.} {foo.,foo.bar.baz.,.,}localhost.localdomain{,.}; do
-        run_and_grep "^::1\s+localhost$" getent hosts -s myhostname "$host"
-        run_and_grep "^::1\s+STREAM" getent ahosts -s myhostname "$host"
-        run_and_grep "^127\.0\.0\.1\s+STREAM" getent ahosts -s myhostname "$host"
-        run_and_grep "^127\.0\.0\.1\s+STREAM" getent ahostsv4 -s myhostname "$host"
-        run_and_grep -n "^::1\s+STREAM" getent ahostsv4 -s myhostname "$host"
-        run_and_grep "^::1\s+STREAM" getent ahostsv6 -s myhostname "$host"
-        run_and_grep -n "^127\.0\.0\.1\s+STREAM" getent ahostsv6 -s myhostname "$host"
-    done
-    for i in 2 {128..150}; do
-        run_and_grep "^10\.0\.0\.$i\s+" getent ahosts -s myhostname "$HOSTNAME"
-        run_and_grep "^10\.0\.0\.$i\s+" getent ahostsv4 -s myhostname "$HOSTNAME"
-        run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahosts -s myhostname "10.0.0.$i"
-        run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahostsv4 -s myhostname "10.0.0.$i"
-    done
-    run_and_grep "^fe80:[^ ]+\s+$HOSTNAME$" getent hosts -s myhostname "$HOSTNAME"
-    run_and_grep "^fe80:[^ ]+\s+STREAM" getent ahosts -s myhostname "$HOSTNAME"
-    run_and_grep "^127\.0\.0\.1\s+localhost$" getent hosts -s myhostname 127.0.0.1
-    run_and_grep "^127\.0\.0\.1\s+STREAM\s+127\.0\.0\.1$" getent ahosts -s myhostname 127.0.0.1
-    run_and_grep "^::ffff:127\.0\.0\.1\s+STREAM\s+127\.0\.0\.1$" getent ahostsv6 -s myhostname 127.0.0.1
-    run_and_grep "^127\.0\.0\.2\s+$HOSTNAME$" getent hosts -s myhostname 127.0.0.2
-    run_and_grep "^::1\s+localhost $HOSTNAME$" getent hosts -s myhostname ::1
-    run_and_grep "^::1\s+STREAM\s+::1$" getent ahosts -s myhostname ::1
-    (! getent ahostsv4 -s myhostname ::1)
-
-    # _gateway
-    for host in _gateway{,.} 10.0.0.1; do
-        run_and_grep "^10\.0\.0\.1\s+_gateway$" getent hosts -s myhostname "$host"
-        run_and_grep "^10\.0\.0\.1\s+STREAM" getent ahosts -s myhostname "$host"
-    done
-
-    # _outbound
-    for host in _outbound{,.} 10.0.0.2; do
-        run_and_grep "^10\.0\.0\.2\s+" getent hosts -s myhostname "$host"
-        run_and_grep "^10\.0\.0\.2\s+STREAM" getent ahosts -s myhostname "$host"
-    done
-
-    # Non-existent records
-    for database in hosts ahosts ahostsv4 ahostsv6; do
-        (! getent "$database" -s myhostname this.should.not.exist)
-    done
-    (! getent hosts -s myhostname 10.254.254.1)
-    (! getent hosts -s myhostname fd00:dead:beef:cafe::1)
-}
-
-test_varlink() {
-    A="$(mktemp -u)"
-    B="$(mktemp -u)"
-    varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}' --json=short > "$A"
-    hostnamectl --json=short > "$B"
-    cmp "$A" "$B"
-}
-
-run_testcases
-
-touch /testok
diff --git a/test/units/testsuite-72.service b/test/units/testsuite-72.service
deleted file mode 100644 (file)
index 1640350..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-72-SYSUPDATE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-72.sh b/test/units/testsuite-72.sh
deleted file mode 100755 (executable)
index 5e658e0..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -eux
-set -o pipefail
-
-SYSUPDATE=/lib/systemd/systemd-sysupdate
-SECTOR_SIZES=(512 4096)
-WORKDIR="$(mktemp -d /var/tmp/test-72-XXXXXX)"
-BACKING_FILE="$WORKDIR/joined.raw"
-export SYSTEMD_ESP_PATH="$WORKDIR/esp"
-export SYSTEMD_XBOOTLDR_PATH="$WORKDIR/xbootldr"
-export SYSTEMD_PAGER=cat
-export SYSTEMD_LOG_LEVEL=debug
-
-if [[ ! -x "$SYSUPDATE" ]]; then
-    echo "no systemd-sysupdate" >/skipped
-    exit 77
-fi
-
-# Loopback devices may not be supported. They are used because sfdisk cannot
-# change the sector size of a file, and we want to test both 512 and 4096 byte
-# sectors. If loopback devices are not supported, we can only test one sector
-# size, and the underlying device is likely to have a sector size of 512 bytes.
-if [[ ! -e /dev/loop-control ]]; then
-    echo "No loopback device support"
-    SECTOR_SIZES=(512)
-fi
-
-at_exit() {
-    set +e
-
-    losetup -n --output NAME --associated "$BACKING_FILE" | while read -r loop_dev; do
-        losetup --detach "$loop_dev"
-    done
-
-    rm -rf "$WORKDIR"
-}
-
-trap at_exit EXIT
-
-new_version() {
-    local sector_size="${1:?}"
-    local version="${2:?}"
-
-    # Create a pair of random partition payloads, and compress one
-    dd if=/dev/urandom of="$WORKDIR/source/part1-$version.raw" bs="$sector_size" count=2048
-    dd if=/dev/urandom of="$WORKDIR/source/part2-$version.raw" bs="$sector_size" count=2048
-    gzip -k -f "$WORKDIR/source/part2-$version.raw"
-
-    # Create a random "UKI" payload
-    echo $RANDOM >"$WORKDIR/source/uki-$version.efi"
-
-    # Create a random extra payload
-    echo $RANDOM >"$WORKDIR/source/uki-extra-$version.efi"
-
-    # Create tarball of a directory
-    mkdir -p "$WORKDIR/source/dir-$version"
-    echo $RANDOM >"$WORKDIR/source/dir-$version/foo.txt"
-    echo $RANDOM >"$WORKDIR/source/dir-$version/bar.txt"
-    tar --numeric-owner -C "$WORKDIR/source/dir-$version/" -czf "$WORKDIR/source/dir-$version.tar.gz" .
-
-    (cd "$WORKDIR/source" && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS)
-}
-
-update_now() {
-    # Update to newest version. First there should be an update ready, then we
-    # do the update, and then there should not be any ready anymore
-
-    "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new
-    "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no update
-    (! "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new)
-}
-
-verify_version() {
-    local block_device="${1:?}"
-    local sector_size="${2:?}"
-    local version="${3:?}"
-    local part1_number="${4:?}"
-    local part2_number="${5:?}"
-    local gpt_reserved_sectors part1_offset part2_offset
-
-    gpt_reserved_sectors=$((1024 * 1024 / sector_size))
-    part1_offset=$(((part1_number - 1) * 2048 + gpt_reserved_sectors))
-    part2_offset=$(((part2_number - 1) * 2048 + gpt_reserved_sectors))
-
-    # Check the partitions
-    dd if="$block_device" bs="$sector_size" skip="$part1_offset" count=2048 | cmp "$WORKDIR/source/part1-$version.raw"
-    dd if="$block_device" bs="$sector_size" skip="$part2_offset" count=2048 | cmp "$WORKDIR/source/part2-$version.raw"
-
-    # Check the UKI
-    cmp "$WORKDIR/source/uki-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version+3-0.efi"
-    test -z "$(ls -A "$WORKDIR/esp/EFI/Linux")"
-
-    # Check the extra efi
-    cmp "$WORKDIR/source/uki-extra-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version.efi.extra.d/extra.addon.efi"
-
-    # Check the directories
-    cmp "$WORKDIR/source/dir-$version/foo.txt" "$WORKDIR/dirs/current/foo.txt"
-    cmp "$WORKDIR/source/dir-$version/bar.txt" "$WORKDIR/dirs/current/bar.txt"
-}
-
-for sector_size in "${SECTOR_SIZES[@]}"; do
-    # Disk size of:
-    # - 1MB for GPT
-    # - 4 partitions of 2048 sectors each
-    # - 1MB for backup GPT
-    disk_size=$((sector_size * 2048 * 4 + 1024 * 1024 * 2))
-    rm -f "$BACKING_FILE"
-    truncate -s "$disk_size" "$BACKING_FILE"
-
-    if [[ -e /dev/loop-control ]]; then
-        blockdev="$(losetup --find --show --sector-size "$sector_size" "$BACKING_FILE")"
-    else
-        blockdev="$BACKING_FILE"
-    fi
-
-    sfdisk "$blockdev" <<EOF
-label: gpt
-unit: sectors
-sector-size: $sector_size
-
-size=2048, type=4f68bce3-e8cd-4db1-96e7-fbcaf984b709, name=_empty
-size=2048, type=4f68bce3-e8cd-4db1-96e7-fbcaf984b709, name=_empty
-size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
-size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
-EOF
-
-    for d in "dirs" "defs"; do
-        rm -rf "${WORKDIR:?}/$d"
-        mkdir -p "$WORKDIR/$d"
-    done
-
-    cat >"$WORKDIR/defs/01-first.conf" <<EOF
-[Source]
-Type=regular-file
-Path=$WORKDIR/source
-MatchPattern=part1-@v.raw
-
-[Target]
-Type=partition
-Path=$blockdev
-MatchPattern=part1-@v
-MatchPartitionType=root-x86-64
-EOF
-
-    cat >"$WORKDIR/defs/02-second.conf" <<EOF
-[Source]
-Type=regular-file
-Path=$WORKDIR/source
-MatchPattern=part2-@v.raw.gz
-
-[Target]
-Type=partition
-Path=$blockdev
-MatchPattern=part2-@v
-MatchPartitionType=root-x86-64-verity
-EOF
-
-    cat >"$WORKDIR/defs/03-third.conf" <<EOF
-[Source]
-Type=directory
-Path=$WORKDIR/source
-MatchPattern=dir-@v
-
-[Target]
-Type=directory
-Path=$WORKDIR/dirs
-CurrentSymlink=$WORKDIR/dirs/current
-MatchPattern=dir-@v
-InstancesMax=3
-EOF
-
-    cat >"$WORKDIR/defs/04-fourth.conf" <<EOF
-[Source]
-Type=regular-file
-Path=$WORKDIR/source
-MatchPattern=uki-@v.efi
-
-[Target]
-Type=regular-file
-Path=/EFI/Linux
-PathRelativeTo=boot
-MatchPattern=uki_@v+@l-@d.efi \
-            uki_@v+@l.efi \
-            uki_@v.efi
-Mode=0444
-TriesLeft=3
-TriesDone=0
-InstancesMax=2
-EOF
-
-    cat >"$WORKDIR/defs/05-fifth.conf" <<EOF
-[Source]
-Type=regular-file
-Path=$WORKDIR/source
-MatchPattern=uki-extra-@v.efi
-
-[Target]
-Type=regular-file
-Path=/EFI/Linux
-PathRelativeTo=boot
-MatchPattern=uki_@v.efi.extra.d/extra.addon.efi
-Mode=0444
-InstancesMax=2
-EOF
-
-    rm -rf "${WORKDIR:?}"/{esp,xbootldr,source}
-    mkdir -p "$WORKDIR"/{source,esp/EFI/Linux,xbootldr/EFI/Linux}
-
-    # Install initial version and verify
-    new_version "$sector_size" v1
-    update_now
-    verify_version "$blockdev" "$sector_size" v1 1 3
-
-    # Create second version, update and verify that it is added
-    new_version "$sector_size" v2
-    update_now
-    verify_version "$blockdev" "$sector_size" v2 2 4
-
-    # Create third version, update and verify it replaced the first version
-    new_version "$sector_size" v3
-    update_now
-    verify_version "$blockdev" "$sector_size" v3 1 3
-    test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v1+3-0.efi"
-    test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v1.efi.extra.d/extra.addon.efi"
-    test ! -d "$WORKDIR/xbootldr/EFI/Linux/uki_v1.efi.extra.d"
-
-    # Create fourth version, and update through a file:// URL. This should be
-    # almost as good as testing HTTP, but is simpler for us to set up. file:// is
-    # abstracted in curl for us, and since our main goal is to test our own code
-    # (and not curl) this test should be quite good even if not comprehensive. This
-    # will test the SHA256SUMS logic at least (we turn off GPG validation though,
-    # see above)
-    new_version "$sector_size" v4
-
-    cat >"$WORKDIR/defs/02-second.conf" <<EOF
-[Source]
-Type=url-file
-Path=file://$WORKDIR/source
-MatchPattern=part2-@v.raw.gz
-
-[Target]
-Type=partition
-Path=$blockdev
-MatchPattern=part2-@v
-MatchPartitionType=root-x86-64-verity
-EOF
-
-    cat >"$WORKDIR/defs/03-third.conf" <<EOF
-[Source]
-Type=url-tar
-Path=file://$WORKDIR/source
-MatchPattern=dir-@v.tar.gz
-
-[Target]
-Type=directory
-Path=$WORKDIR/dirs
-CurrentSymlink=$WORKDIR/dirs/current
-MatchPattern=dir-@v
-InstancesMax=3
-EOF
-
-    update_now
-    verify_version "$blockdev" "$sector_size" v4 2 4
-
-    # Cleanup
-    [[ -b "$blockdev" ]] && losetup --detach "$blockdev"
-    rm "$BACKING_FILE"
-done
-
-touch /testok
diff --git a/test/units/testsuite-73.service b/test/units/testsuite-73.service
deleted file mode 100644 (file)
index 3ebd24d..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-73-LOCALE
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-73.sh b/test/units/testsuite-73.sh
deleted file mode 100755 (executable)
index 18539b8..0000000
+++ /dev/null
@@ -1,663 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-enable_debug() {
-    mkdir -p /run/systemd/system/systemd-localed.service.d
-    cat >>/run/systemd/system/systemd-localed.service.d/override.conf <<EOF
-[Service]
-Environment=SYSTEMD_LOG_LEVEL=debug
-EOF
-
-    mkdir -p /run/systemd/system/systemd-vconsole-setup.service.d
-    cat >>/run/systemd/system/systemd-vconsole-setup.service.d/override.conf <<EOF
-[Unit]
-StartLimitIntervalSec=0
-
-[Service]
-Environment=SYSTEMD_LOG_LEVEL=debug
-EOF
-
-    systemctl daemon-reload
-}
-
-testcase_locale() {
-    local i output
-
-    if [[ -f /etc/locale.conf ]]; then
-        cp /etc/locale.conf /tmp/locale.conf.bak
-    fi
-
-    # Debian/Ubuntu specific file
-    if [[ -f /etc/default/locale ]]; then
-        cp /etc/default/locale /tmp/default-locale.bak
-    fi
-
-    if [[ -f /etc/locale.gen ]]; then
-        cp /etc/locale.gen /tmp/locale.gen.bak
-    fi
-
-    # remove locale.conf to make /etc/default/locale used by Debian/Ubuntu
-    rm -f /etc/locale.conf
-    # also remove /etc/default/locale
-    rm -f /etc/default/locale
-    # and create /etc/default to make /etc/default/locale created by localed
-    mkdir -p /etc/default
-
-    trap restore_locale RETURN
-    # Ensure at least one UTF-8 locale exists.
-    generate_locale en_US.UTF-8
-
-    # create invalid locale
-    mkdir -p /usr/lib/locale/xx_XX.UTF-8
-    assert_not_in "xx_XX.UTF-8" "$(localectl list-locales)"
-
-    if [[ -z "$(localectl list-locales)" ]]; then
-        echo "No locale installed, skipping test."
-        return
-    fi
-
-    # start with a known default environment and make sure to also give a
-    # default value to LC_CTYPE= since we're about to also set/unset it. We
-    # also reload PID1 configuration to make sure that PID1 environment itself
-    # is updated as it's not always been the case.
-    assert_rc 0 localectl set-locale "LANG=en_US.UTF-8" "LC_CTYPE=C"
-    systemctl daemon-reload
-    output=$(localectl)
-    assert_in "System Locale: LANG=en_US.UTF-8" "$output"
-    assert_in "LC_CTYPE=C" "$output"
-    output=$(systemctl show-environment)
-    assert_in "LANG=en_US.UTF-8" "$output"
-    assert_in "LC_CTYPE=C" "$output"
-
-    # warn when kernel command line has locale settings
-    output=$(SYSTEMD_PROC_CMDLINE="locale.LANG=C.UTF-8 locale.LC_CTYPE=ja_JP.UTF-8" localectl 2>&1)
-    assert_in "Warning:" "$output"
-    assert_in "Command Line: LANG=C.UTF-8" "$output"
-    assert_in "LC_CTYPE=ja_JP.UTF-8" "$output"
-    assert_in "System Locale:" "$output"
-
-    # change locale
-    for i in $(localectl list-locales); do
-        assert_rc 0 localectl set-locale "LANG=C" "LC_CTYPE=$i"
-        if [[ -f /etc/default/locale ]]; then
-            assert_eq "$(cat /etc/default/locale)" "LANG=C
-LC_CTYPE=$i"
-        else
-            assert_eq "$(cat /etc/locale.conf)" "LANG=C
-LC_CTYPE=$i"
-        fi
-        output=$(localectl)
-        assert_in "System Locale: LANG=C" "$output"
-        assert_in "LC_CTYPE=$i" "$output"
-        output=$(systemctl show-environment)
-        assert_in "LANG=C" "$output"
-        assert_in "LC_CTYPE=$i" "$output"
-
-        assert_rc 0 localectl set-locale "$i"
-        if [[ -f /etc/default/locale ]]; then
-            assert_eq "$(cat /etc/default/locale)" "LANG=$i"
-        else
-            assert_eq "$(cat /etc/locale.conf)" "LANG=$i"
-        fi
-        output=$(localectl)
-        assert_in "System Locale: LANG=$i" "$output"
-        assert_not_in "LC_CTYPE=" "$output"
-        output=$(systemctl show-environment)
-        assert_in "LANG=$i" "$output"
-        assert_not_in "LC_CTYPE=" "$output"
-    done
-
-    # test if localed auto-runs locale-gen
-    if command -v locale-gen >/dev/null 2>&1 &&
-           ! localectl list-locales | grep -F "de_DE.UTF-8"; then
-
-        # clear previous locale
-        systemctl stop systemd-localed.service
-        rm -f /etc/locale.conf /etc/default/locale
-
-        # change locale
-        assert_rc 0 localectl set-locale de_DE.UTF-8
-        if [[ -f /etc/default/locale ]]; then
-            assert_eq "$(cat /etc/default/locale)" "LANG=de_DE.UTF-8"
-        else
-            assert_eq "$(cat /etc/locale.conf)" "LANG=de_DE.UTF-8"
-        fi
-        assert_in "System Locale: LANG=de_DE.UTF-8" "$(localectl)"
-        assert_in "LANG=de_DE.UTF-8" "$(systemctl show-environment)"
-
-        # ensure tested locale exists and works now
-        assert_in "de_DE.UTF-8" "$(localectl list-locales)"
-    fi
-}
-
-backup_keymap() {
-    if [[ -f /etc/vconsole.conf ]]; then
-        cp /etc/vconsole.conf /tmp/vconsole.conf.bak
-    fi
-
-    if [[ -f /etc/X11/xorg.conf.d/00-keyboard.conf ]]; then
-        cp /etc/X11/xorg.conf.d/00-keyboard.conf /tmp/00-keyboard.conf.bak
-    fi
-
-    # Debian/Ubuntu specific file
-    if [[ -f /etc/default/keyboard ]]; then
-        cp /etc/default/keyboard /tmp/default-keyboard.bak
-    fi
-
-    mkdir -p /etc/default
-}
-
-restore_keymap() {
-    if [[ -f /tmp/vconsole.conf.bak ]]; then
-        mv /tmp/vconsole.conf.bak /etc/vconsole.conf
-    else
-        rm -f /etc/vconsole.conf
-    fi
-
-    if [[ -f /tmp/00-keyboard.conf.bak ]]; then
-        mv /tmp/00-keyboard.conf.bak /etc/X11/xorg.conf.d/00-keyboard.conf
-    else
-        rm -f /etc/X11/xorg.conf.d/00-keyboard.conf
-    fi
-
-    if [[ -f /tmp/default-keyboard.bak ]]; then
-        mv /tmp/default-keyboard.bak /etc/default/keyboard
-    else
-        rm -f /etc/default/keyboard
-        rmdir --ignore-fail-on-non-empty /etc/default
-    fi
-}
-
-wait_vconsole_setup() {
-    local i ss
-    for i in {1..20}; do
-        (( i > 1 )) && sleep 0.5
-        ss="$(systemctl --property SubState --value show systemd-vconsole-setup.service)"
-        if [[ "$ss" == "exited" || "$ss" == "dead" || "$ss" == "condition" ]]; then
-            return 0
-        elif [[ "$ss" == "failed" ]]; then
-            echo "WARNING: systemd-vconsole-setup.service failed, ignoring." >&2
-            systemctl reset-failed systemd-vconsole-setup.service
-            return 0
-        fi
-    done
-
-    systemctl status systemd-vconsole-setup.service
-    return 1
-}
-
-testcase_vc_keymap() {
-    local i output vc
-
-    if [[ -z "$(localectl list-keymaps)" ]]; then
-        echo "No vconsole keymap installed, skipping test."
-        return
-    fi
-
-    backup_keymap
-    trap restore_keymap RETURN
-
-    # should activate daemon and work
-    assert_in "VC Keymap:" "$(localectl)"
-
-    for i in $(localectl list-keymaps); do
-        # clear previous conversion from VC -> X11 keymap
-        systemctl stop systemd-localed.service
-        wait_vconsole_setup
-        rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
-
-        # set VC keymap
-        assert_rc 0 localectl set-keymap "$i"
-        output=$(localectl)
-
-        # check VC keymap
-        vc=$(cat /etc/vconsole.conf)
-        assert_in "KEYMAP=$i" "$vc"
-        assert_in "VC Keymap: $i" "$output"
-
-        # check VC -> X11 keymap conversion
-        if [[ "$i" == "us" ]]; then
-            assert_in "X11 Layout: us" "$output"
-            assert_in "X11 Model: pc105\+inet" "$output"
-            assert_not_in "X11 Variant:" "$output"
-            assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
-
-            assert_in "XKBLAYOUT=us" "$vc"
-            assert_in "XKBMODEL=pc105\+inet" "$vc"
-            assert_not_in "XKBVARIANT" "$vc"
-            assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
-        elif [[ "$i" == "us-acentos" ]]; then
-            assert_in "X11 Layout: us" "$output"
-            assert_in "X11 Model: pc105" "$output"
-            assert_in "X11 Variant: intl" "$output"
-            assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
-
-            assert_in "XKBLAYOUT=us" "$vc"
-            assert_in "XKBMODEL=pc105" "$vc"
-            assert_in "XKBVARIANT=intl" "$vc"
-            assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
-        elif [[ "$i" =~ ^us-.* ]]; then
-            assert_in "X11 Layout: us" "$output"
-            assert_in "X11 Model: microsoftpro" "$output"
-            assert_in "X11 Variant:" "$output"
-            assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
-
-            assert_in "XKBLAYOUT=us" "$vc"
-            assert_in "XKBMODEL=microsoftpro" "$vc"
-            assert_in "XKBVARIANT=" "$vc"
-            assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
-        fi
-    done
-
-    # gets along without config file
-    systemctl stop systemd-localed.service
-    wait_vconsole_setup
-    rm -f /etc/vconsole.conf
-    assert_in "VC Keymap: .unset." "$(localectl)"
-}
-
-testcase_x11_keymap() {
-    local output
-
-    if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then
-        echo "No x11 keymap installed, skipping test."
-        return
-    fi
-
-    backup_keymap
-    trap restore_keymap RETURN
-
-    # should activate daemon and work
-    assert_in "X11 Layout:" "$(localectl)"
-
-    # set x11 keymap (layout, model, variant, options)
-    assert_rc 0 localectl set-x11-keymap us pc105+inet intl terminate:ctrl_alt_bksp
-
-    if [[ -f /etc/default/keyboard ]]; then
-        assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us
-XKBMODEL=pc105+inet
-XKBVARIANT=intl
-XKBOPTIONS=terminate:ctrl_alt_bksp"
-    else
-        output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
-        assert_in 'Option "XkbLayout" "us"' "$output"
-        assert_in 'Option "XkbModel" "pc105\+inet"' "$output"
-        assert_in 'Option "XkbVariant" "intl"' "$output"
-        assert_in 'Option "XkbOptions" "terminate:ctrl_alt_bksp"' "$output"
-
-        output=$(cat /etc/vconsole.conf)
-        assert_in 'XKBLAYOUT=us' "$output"
-        assert_in 'XKBMODEL=pc105\+inet' "$output"
-        assert_in 'XKBVARIANT=intl' "$output"
-        assert_in 'XKBOPTIONS=terminate:ctrl_alt_bksp' "$output"
-    fi
-
-    output=$(localectl)
-    assert_in "X11 Layout: us" "$output"
-    assert_in "X11 Model: pc105\+inet" "$output"
-    assert_in "X11 Variant: intl" "$output"
-    assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output"
-
-    # Debian/Ubuntu patch is buggy, unspecified settings are not cleared
-    rm -f /etc/default/keyboard
-
-    # set x11 keymap (layout, model, variant)
-    assert_rc 0 localectl set-x11-keymap us pc105+inet intl
-
-    if [[ -f /etc/default/keyboard ]]; then
-        assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us
-XKBMODEL=pc105+inet
-XKBVARIANT=intl"
-    else
-        output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
-        assert_in 'Option "XkbLayout" "us"' "$output"
-        assert_in 'Option "XkbModel" "pc105\+inet"' "$output"
-        assert_in 'Option "XkbVariant" "intl"' "$output"
-        assert_not_in 'Option "XkbOptions"' "$output"
-
-        output=$(cat /etc/vconsole.conf)
-        assert_in 'XKBLAYOUT=us' "$output"
-        assert_in 'XKBMODEL=pc105\+inet' "$output"
-        assert_in 'XKBVARIANT=intl' "$output"
-        assert_not_in 'XKBOPTIONS' "$output"
-    fi
-
-    output=$(localectl)
-    assert_in "X11 Layout: us" "$output"
-    assert_in "X11 Model: pc105\+inet" "$output"
-    assert_in "X11 Variant: intl" "$output"
-    assert_not_in "X11 Options:" "$output"
-
-    # Debian/Ubuntu patch is buggy, unspecified settings are not cleared
-    rm -f /etc/default/keyboard
-
-    # set x11 keymap (layout, model)
-    assert_rc 0 localectl set-x11-keymap us pc105+inet
-
-    if [[ -f /etc/default/keyboard ]]; then
-        assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us
-XKBMODEL=pc105+inet"
-    else
-        output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
-        assert_in 'Option "XkbLayout" "us"' "$output"
-        assert_in 'Option "XkbModel" "pc105\+inet"' "$output"
-        assert_not_in 'Option "XkbVariant"' "$output"
-        assert_not_in 'Option "XkbOptions"' "$output"
-
-        output=$(cat /etc/vconsole.conf)
-        assert_in 'XKBLAYOUT=us' "$output"
-        assert_in 'XKBMODEL=pc105\+inet' "$output"
-        assert_not_in 'XKBVARIANT' "$output"
-        assert_not_in 'XKBOPTIONS' "$output"
-    fi
-
-    output=$(localectl)
-    assert_in "X11 Layout: us" "$output"
-    assert_in "X11 Model: pc105\+inet" "$output"
-    assert_not_in "X11 Variant:" "$output"
-    assert_not_in "X11 Options:" "$output"
-
-    # Debian/Ubuntu patch is buggy, unspecified settings are not cleared
-    rm -f /etc/default/keyboard
-
-    # set x11 keymap (layout)
-    assert_rc 0 localectl set-x11-keymap us
-
-    if [[ -f /etc/default/keyboard ]]; then
-        assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us"
-    else
-        output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
-        assert_in 'Option "XkbLayout" "us"' "$output"
-        assert_not_in 'Option "XkbModel"' "$output"
-        assert_not_in 'Option "XkbVariant"' "$output"
-        assert_not_in 'Option "XkbOptions"' "$output"
-
-        output=$(cat /etc/vconsole.conf)
-        assert_in 'XKBLAYOUT=us' "$output"
-        assert_not_in 'XKBMODEL' "$output"
-        assert_not_in 'XKBVARIANT' "$output"
-        assert_not_in 'XKBOPTIONS' "$output"
-    fi
-
-    output=$(localectl)
-    assert_in "X11 Layout: us" "$output"
-    assert_not_in "X11 Model:" "$output"
-    assert_not_in "X11 Variant:" "$output"
-    assert_not_in "X11 Options:" "$output"
-
-    # gets along without config file
-    systemctl stop systemd-localed.service
-    rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
-    output=$(localectl)
-    assert_in "X11 Layout: .unset." "$output"
-    assert_not_in "X11 Model:" "$output"
-    assert_not_in "X11 Variant:" "$output"
-    assert_not_in "X11 Options:" "$output"
-}
-
-testcase_convert() {
-    if [[ -z "$(localectl list-keymaps)" ]]; then
-        echo "No vconsole keymap installed, skipping test."
-        return
-    fi
-
-    if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then
-        echo "No x11 keymap installed, skipping test."
-        return
-    fi
-
-    backup_keymap
-    trap restore_keymap RETURN
-
-    # clear previous settings
-    systemctl stop systemd-localed.service
-    wait_vconsole_setup
-    rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
-
-    # set VC keymap without conversion
-    assert_rc 0 localectl --no-convert set-keymap us
-    output=$(localectl)
-
-    # check VC keymap
-    vc=$(cat /etc/vconsole.conf)
-    assert_in "KEYMAP=us" "$vc"
-    assert_in "VC Keymap: us" "$output"
-
-    # check VC -> X11 keymap conversion (nothing set)
-    assert_in     "X11 Layout: .unset."  "$output"
-    assert_not_in "X11 Model:"           "$output"
-    assert_not_in "X11 Variant:"         "$output"
-    assert_not_in "X11 Options:"         "$output"
-
-    assert_not_in "XKBLAYOUT="  "$vc"
-    assert_not_in "XKBMODEL="   "$vc"
-    assert_not_in "XKBVARIANT=" "$vc"
-    assert_not_in "XKBOPTIONS=" "$vc"
-
-    # set VC keymap with conversion
-    assert_rc 0 localectl set-keymap us
-    output=$(localectl)
-
-    # check VC keymap
-    vc=$(cat /etc/vconsole.conf)
-    assert_in "KEYMAP=us" "$vc"
-    assert_in "VC Keymap: us" "$output"
-
-    # check VC -> X11 keymap conversion
-    assert_in     "X11 Layout: us"                       "$output"
-    assert_in     "X11 Model: pc105\+inet"               "$output"
-    assert_not_in "X11 Variant:"                         "$output"
-    assert_in     "X11 Options: terminate:ctrl_alt_bksp" "$output"
-
-    assert_in     "XKBLAYOUT=us"                       "$vc"
-    assert_in     "XKBMODEL=pc105\+inet"               "$vc"
-    assert_not_in "XKBVARIANT"                         "$vc"
-    assert_in     "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc"
-
-    # clear previous settings
-    systemctl stop systemd-localed.service
-    wait_vconsole_setup
-    rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
-
-    # set x11 keymap (layout) without conversion
-    assert_rc 0 localectl --no-convert set-x11-keymap us
-
-    assert_not_in "KEYMAP=" "$(cat /etc/vconsole.conf)"
-    assert_in "VC Keymap: .unset." "$(localectl)"
-
-    if [[ -f /etc/default/keyboard ]]; then
-        assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us"
-    else
-        output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
-        assert_in     'Option "XkbLayout" "us"' "$output"
-        assert_not_in 'Option "XkbModel"'       "$output"
-        assert_not_in 'Option "XkbVariant"'     "$output"
-        assert_not_in 'Option "XkbOptions"'     "$output"
-
-        output=$(cat /etc/vconsole.conf)
-        assert_in     'XKBLAYOUT=us' "$output"
-        assert_not_in 'XKBMODEL='    "$output"
-        assert_not_in 'XKBVARIANT='  "$output"
-        assert_not_in 'XKBOPTIONS='  "$output"
-    fi
-
-    output=$(localectl)
-    assert_in     "X11 Layout: us" "$output"
-    assert_not_in "X11 Model:"     "$output"
-    assert_not_in "X11 Variant:"   "$output"
-    assert_not_in "X11 Options:"   "$output"
-
-    # set x11 keymap (layout, model) with conversion
-    assert_rc 0 localectl set-x11-keymap us
-
-    assert_in "KEYMAP=us" "$(cat /etc/vconsole.conf)"
-    assert_in "VC Keymap: us" "$(localectl)"
-
-    if [[ -f /etc/default/keyboard ]]; then
-        assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us"
-    else
-        output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf)
-        assert_in     'Option "XkbLayout" "us"' "$output"
-        assert_not_in 'Option "XkbModel"'       "$output"
-        assert_not_in 'Option "XkbVariant"'     "$output"
-        assert_not_in 'Option "XkbOptions"'     "$output"
-
-        output=$(cat /etc/vconsole.conf)
-        assert_in     'XKBLAYOUT=us' "$output"
-        assert_not_in 'XKBMODEL='    "$output"
-        assert_not_in 'XKBVARIANT='  "$output"
-        assert_not_in 'XKBOPTIONS='  "$output"
-    fi
-
-    output=$(localectl)
-    assert_in     "X11 Layout: us" "$output"
-    assert_not_in "X11 Model:"     "$output"
-    assert_not_in "X11 Variant:"   "$output"
-    assert_not_in "X11 Options:"   "$output"
-}
-
-testcase_validate() {
-    if [[ -z "$(localectl list-keymaps)" ]]; then
-        echo "No vconsole keymap installed, skipping test."
-        return
-    fi
-
-    if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then
-        echo "No x11 keymap installed, skipping test."
-        return
-    fi
-
-    backup_keymap
-    trap restore_keymap RETURN
-
-    # clear previous settings
-    systemctl stop systemd-localed.service
-    wait_vconsole_setup
-    rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
-
-    # create invalid configs
-    cat >/etc/vconsole.conf <<EOF
-KEYMAP=foobar
-XKBLAYOUT=hogehoge
-EOF
-
-    # confirm that the invalid settings are not shown
-    output=$(localectl)
-    assert_in "VC Keymap: .unset."  "$output"
-    if [[ "$output" =~ "X11 Layout: hogehoge" ]]; then
-        # Debian/Ubuntu build systemd without xkbcommon.
-        echo "systemd built without xkbcommon, skipping test."
-        return
-    fi
-    assert_in "X11 Layout: .unset." "$output"
-
-    # only update the virtual console keymap
-    assert_rc 0 localectl --no-convert set-keymap us
-
-    output=$(localectl)
-    assert_in "VC Keymap: us"       "$output"
-    assert_in "X11 Layout: .unset." "$output"
-
-    output=$(cat /etc/vconsole.conf)
-    assert_in     "KEYMAP=us"  "$output"
-    assert_not_in "XKBLAYOUT=" "$output"
-
-    # clear previous settings
-    systemctl stop systemd-localed.service
-    wait_vconsole_setup
-    rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
-
-    # create invalid configs
-    cat >/etc/vconsole.conf <<EOF
-KEYMAP=foobar
-XKBLAYOUT=hogehoge
-EOF
-
-    # confirm that the invalid settings are not shown
-    output=$(localectl)
-    assert_in "VC Keymap: .unset."  "$output"
-    assert_in "X11 Layout: .unset." "$output"
-
-    # only update the X11 keyboard layout
-    assert_rc 0 localectl --no-convert set-x11-keymap us
-
-    output=$(localectl)
-    assert_in "VC Keymap: .unset."  "$output"
-    assert_in "X11 Layout: us"      "$output"
-
-    output=$(cat /etc/vconsole.conf)
-    assert_not_in "KEYMAP="      "$output"
-    assert_in     "XKBLAYOUT=us" "$output"
-
-    # clear previous settings
-    systemctl stop systemd-localed.service
-    wait_vconsole_setup
-    rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard
-
-    # create invalid configs
-    cat >/etc/vconsole.conf <<EOF
-KEYMAP=foobar
-XKBLAYOUT=hogehoge
-EOF
-
-    # update the virtual console keymap with conversion
-    assert_rc 0 localectl set-keymap us
-
-    output=$(localectl)
-    assert_in "VC Keymap: us"  "$output"
-    assert_in "X11 Layout: us" "$output"
-
-    output=$(cat /etc/vconsole.conf)
-    assert_in "KEYMAP=us"    "$output"
-    assert_in "XKBLAYOUT=us" "$output"
-}
-
-locale_gen_cleanup() {
-    # Some running apps might keep the mount point busy, hence the lazy unmount
-    mountpoint -q /usr/lib/locale && umount --lazy /usr/lib/locale
-    [[ -e /tmp/locale.gen.bak ]] && mv -f /tmp/locale.gen.bak /etc/locale.gen
-
-    return 0
-}
-
-# Issue: https://github.com/systemd/systemd/pull/27179
-testcase_locale_gen_leading_space() {
-    if ! command -v locale-gen >/dev/null; then
-        echo "No locale-gen support, skipping test."
-        return 0
-    fi
-
-    [[ -e /etc/locale.gen ]] && cp -f /etc/locale.gen /tmp/locale.gen.bak
-    trap locale_gen_cleanup RETURN
-    # Overmount the existing locale-gen database with an empty directory
-    # to force it to regenerate locales
-    mount -t tmpfs tmpfs /usr/lib/locale
-
-    {
-        echo -e "en_US.UTF-8 UTF-8"
-        echo -e " en_US.UTF-8 UTF-8"
-        echo -e "\ten_US.UTF-8 UTF-8"
-        echo -e " \t en_US.UTF-8 UTF-8 \t"
-    } >/etc/locale.gen
-
-    localectl set-locale de_DE.UTF-8
-    localectl set-locale en_US.UTF-8
-}
-
-# Make sure the content of kbd-model-map is the one that the tests expect
-# regardless of the version installed on the distro where the testsuite is
-# running on.
-export SYSTEMD_KBD_MODEL_MAP=/usr/lib/systemd/tests/testdata/test-keymap-util/kbd-model-map
-
-enable_debug
-run_testcases
-
-touch /testok
diff --git a/test/units/testsuite-74.battery-check.sh b/test/units/testsuite-74.battery-check.sh
deleted file mode 100755 (executable)
index 52a92b8..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-/usr/lib/systemd/systemd-battery-check --help
-/usr/lib/systemd/systemd-battery-check --version
-
-/usr/lib/systemd/systemd-battery-check || :
diff --git a/test/units/testsuite-74.bootctl.sh b/test/units/testsuite-74.bootctl.sh
deleted file mode 100755 (executable)
index 78c0e6e..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if systemd-detect-virt --quiet --container; then
-    echo "running on container, skipping."
-    exit 0
-fi
-
-if ! command -v bootctl >/dev/null; then
-    echo "bootctl not found, skipping."
-    exit 0
-fi
-
-if [[ ! -d /usr/lib/systemd/boot/efi ]]; then
-    echo "sd-boot is not installed, skipping."
-    exit 0
-fi
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-basic_tests() {
-    bootctl "$@" --help
-    bootctl "$@" --version
-
-    bootctl "$@" install --make-entry-directory=yes
-    bootctl "$@" remove  --make-entry-directory=yes
-
-    bootctl "$@" install --all-architectures
-    bootctl "$@" remove  --all-architectures
-
-    bootctl "$@" install --make-entry-directory=yes --all-architectures
-    bootctl "$@" remove  --make-entry-directory=yes --all-architectures
-
-    bootctl "$@" install
-    (! bootctl "$@" update)
-    bootctl "$@" update --graceful
-
-    bootctl "$@" is-installed
-    bootctl "$@" is-installed --graceful
-    bootctl "$@" random-seed
-
-    bootctl "$@"
-    bootctl "$@" status
-    bootctl "$@" status --quiet
-    bootctl "$@" list
-    bootctl "$@" list --quiet
-    bootctl "$@" list --json=short
-    bootctl "$@" list --json=pretty
-
-    bootctl "$@" remove
-    (! bootctl "$@" is-installed)
-    (! bootctl "$@" is-installed --graceful)
-}
-
-testcase_bootctl_basic() {
-    assert_in "$(bootctl --print-esp-path)" "^(/boot/|/efi)$"
-    assert_in "$(bootctl --print-boot-path)" "^(/boot/|/efi)$"
-    bootctl --print-root-device
-
-    basic_tests
-}
-
-cleanup_image() (
-    set +e
-
-    if [[ -z "${IMAGE_DIR:-}" ]]; then
-        return 0
-    fi
-
-    umount "${IMAGE_DIR}/root"
-
-    if [[ -n "${LOOPDEV:-}" ]]; then
-        losetup -d "${LOOPDEV}"
-        unset LOOPDEV
-    fi
-
-    udevadm settle
-
-    rm -rf "${IMAGE_DIR}"
-    unset IMAGE_DIR
-
-    return 0
-)
-
-testcase_bootctl_image() {
-    IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)"
-    trap cleanup_image RETURN
-
-    truncate -s 256m "${IMAGE_DIR}/image"
-
-    cat >"${IMAGE_DIR}/partscript" <<EOF
-label: gpt
-type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp  size=64M
-type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=64M bootable
-type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot
-EOF
-
-    LOOPDEV="$(losetup --show -P -f "${IMAGE_DIR}/image")"
-    sfdisk "$LOOPDEV" <"${IMAGE_DIR}/partscript"
-
-    udevadm settle
-
-    mkfs.vfat -n esp  "${LOOPDEV}p1"
-    mkfs.ext4 -L root "${LOOPDEV}p2"
-    mkfs.ext4 -L boot "${LOOPDEV}p3"
-
-    mkdir -p "${IMAGE_DIR}/root"
-    mount -t ext4 "${LOOPDEV}p2" "${IMAGE_DIR}/root"
-
-    mkdir -p "${IMAGE_DIR}/root/efi"
-    mkdir -p "${IMAGE_DIR}/root/boot"
-    mkdir -p "${IMAGE_DIR}/root/etc"
-    mkdir -p "${IMAGE_DIR}/root/usr/lib"
-    if [[ -f /usr/lib/os-release ]]; then
-        cp /usr/lib/os-release "${IMAGE_DIR}/root/usr/lib/."
-        ln -s ../usr/lib/os-release "${IMAGE_DIR}/root/etc/os-release"
-    else
-        cp -a /etc/os-release "${IMAGE_DIR}/root/etc/."
-    fi
-
-    umount "${IMAGE_DIR}/root"
-
-    assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-esp-path)" "/run/systemd/mount-rootfs/efi"
-    assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-esp-path --esp-path=/efi)" "/run/systemd/mount-rootfs/efi"
-    assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-boot-path)" "/run/systemd/mount-rootfs/boot"
-    assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-boot-path --boot-path=/boot)" "/run/systemd/mount-rootfs/boot"
-
-    # FIXME: This provides spurious result.
-    bootctl --image "${IMAGE_DIR}/image" --print-root-device || :
-
-    basic_tests --image "${IMAGE_DIR}/image"
-}
-
-cleanup_raid() (
-    set +e
-
-    if [[ -z "${IMAGE_DIR:-}" ]]; then
-        return 0
-    fi
-
-    systemd-umount "${IMAGE_DIR}/root/efi"
-    systemd-umount "${IMAGE_DIR}/root/boot"
-    systemd-umount "${IMAGE_DIR}/root"
-
-    mdadm --misc --stop /dev/md/raid-esp
-    mdadm --misc --stop /dev/md/raid-root
-
-    if [[ -n "${LOOPDEV1:-}" ]]; then
-        mdadm --misc --force --zero-superblock "${LOOPDEV1}p1"
-        mdadm --misc --force --zero-superblock "${LOOPDEV1}p2"
-    fi
-
-    if [[ -n "${LOOPDEV2:-}" ]]; then
-        mdadm --misc --force --zero-superblock "${LOOPDEV2}p1"
-        mdadm --misc --force --zero-superblock "${LOOPDEV2}p2"
-    fi
-
-    udevadm settle
-
-    if [[ -n "${LOOPDEV1:-}" ]]; then
-        mdadm --misc --force --zero-superblock "${LOOPDEV1}p1"
-        mdadm --misc --force --zero-superblock "${LOOPDEV1}p2"
-        losetup -d "${LOOPDEV1}"
-        unset LOOPDEV1
-    fi
-
-    if [[ -n "${LOOPDEV2:-}" ]]; then
-        mdadm --misc --force --zero-superblock "${LOOPDEV2}p1"
-        mdadm --misc --force --zero-superblock "${LOOPDEV2}p2"
-        losetup -d "${LOOPDEV2}"
-        unset LOOPDEV2
-    fi
-
-    udevadm settle
-
-    rm -rf "${IMAGE_DIR}"
-
-    return 0
-)
-
-testcase_bootctl_raid() {
-    if ! command -v mdadm >/dev/null; then
-        echo "mdadm not found, skipping."
-        return 0
-    fi
-
-    if ! command -v mkfs.btrfs >/dev/null; then
-        echo "mkfs.btrfs not found, skipping."
-        return 0
-    fi
-
-    IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)"
-    trap cleanup_raid RETURN
-
-    truncate -s 256m "${IMAGE_DIR}/image1"
-    truncate -s 256m "${IMAGE_DIR}/image2"
-
-    cat >"${IMAGE_DIR}/partscript" <<EOF
-label: gpt
-type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp  size=64M
-type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=64M bootable
-type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot
-EOF
-
-    LOOPDEV1="$(losetup --show -P -f "${IMAGE_DIR}/image1")"
-    LOOPDEV2="$(losetup --show -P -f "${IMAGE_DIR}/image2")"
-    sfdisk "$LOOPDEV1" <"${IMAGE_DIR}/partscript"
-    sfdisk "$LOOPDEV2" <"${IMAGE_DIR}/partscript"
-
-    udevadm settle
-
-    echo y | mdadm --create /dev/md/raid-esp --name "raid-esp" "${LOOPDEV1}p1" "${LOOPDEV2}p1" -v -f --level=1 --raid-devices=2
-    mkfs.vfat /dev/md/raid-esp
-    echo y | mdadm --create /dev/md/raid-root --name "raid-root" "${LOOPDEV1}p2" "${LOOPDEV2}p2" -v -f --level=1 --raid-devices=2
-    mkfs.ext4 /dev/md/raid-root
-    mkfs.btrfs -f -M -d raid1 -m raid1 -L "raid-boot" "${LOOPDEV1}p3" "${LOOPDEV2}p3"
-
-    mkdir -p "${IMAGE_DIR}/root"
-    mount -t ext4 /dev/md/raid-root "${IMAGE_DIR}/root"
-    mkdir -p "${IMAGE_DIR}/root/efi"
-    mount -t vfat /dev/md/raid-esp "${IMAGE_DIR}/root/efi"
-    mkdir -p "${IMAGE_DIR}/root/boot"
-    mount -t btrfs "${LOOPDEV1}p3" "${IMAGE_DIR}/root/boot"
-
-    mkdir -p "${IMAGE_DIR}/root/etc"
-    mkdir -p "${IMAGE_DIR}/root/usr/lib"
-    if [[ -f /usr/lib/os-release ]]; then
-        cp /usr/lib/os-release "${IMAGE_DIR}/root/usr/lib/."
-        ln -s ../usr/lib/os-release "${IMAGE_DIR}/root/etc/os-release"
-    else
-        cp -a /etc/os-release "${IMAGE_DIR}/root/etc/."
-    fi
-
-    # find_esp() does not support md RAID partition.
-    (! bootctl --root "${IMAGE_DIR}/root" --print-esp-path)
-    (! bootctl --root "${IMAGE_DIR}/root" --print-esp-path --esp-path=/efi)
-
-    # If the verification is relaxed, it accepts md RAID partition.
-    assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-esp-path)" "${IMAGE_DIR}/root/efi"
-    assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-esp-path --esp-path=/efi)" "${IMAGE_DIR}/root/efi"
-
-    # find_xbootldr() does not support btrfs RAID, and bootctl tries to fall back to use ESP.
-    # (but as in the above, the ESP verification is also failed in this case).
-    (! bootctl --root "${IMAGE_DIR}/root" --print-boot-path)
-    (! bootctl --root "${IMAGE_DIR}/root" --print-boot-path --boot-path=/boot)
-
-    # If the verification for ESP is relaxed, bootctl falls back to use ESP.
-    assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path)" "${IMAGE_DIR}/root/efi"
-
-    # If the verification is relaxed, it accepts the xbootldr partition.
-    assert_eq "$(SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path)" "${IMAGE_DIR}/root/boot"
-    assert_eq "$(SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path --boot-path=/boot)" "${IMAGE_DIR}/root/boot"
-
-    # FIXME: This provides spurious result.
-    bootctl --root "${IMAGE_DIR}/root" --print-root-device || :
-
-    SYSTEMD_RELAX_ESP_CHECKS=yes SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes basic_tests --root "${IMAGE_DIR}/root"
-}
-
-testcase_bootctl_varlink() {
-    varlinkctl call --collect /run/systemd/io.systemd.BootControl io.systemd.BootControl.ListBootEntries '{}'
-
-    # We may have UEFI in the test environment.
-    # If we don't have UEFI then we can test whether bootctl's varlink API fails cleanly.
-    # If we do have UEFI then the rest of the clean fail tests should be skipped.
-    if ! (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.GetRebootToFirmware '{}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported; then
-        return 0
-    fi
-    (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":true}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported
-    (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":false}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported
-}
-
-run_testcases
diff --git a/test/units/testsuite-74.busctl.sh b/test/units/testsuite-74.busctl.sh
deleted file mode 100755 (executable)
index aaf96d0..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Unset $PAGER so we don't have to use --no-pager everywhere
-export PAGER=
-
-busctl --help
-busctl help
-busctl --version
-busctl
-busctl list --no-pager --allow-interactive-authorization=no
-busctl list
-busctl list --unique --show-machine --full
-# Pass the JSON output (-j) through jq to check if it's valid
-busctl list --acquired --activatable --no-legend -j | jq
-busctl status
-busctl status --machine=.host --augment-creds=no
-busctl status --user --machine=testuser@.host
-busctl status org.freedesktop.systemd1
-# Ignore the exit code here, since this runs during machine bootup, so busctl
-# might attempt to introspect a job that already finished and fail, i.e.:
-# Failed to introspect object /org/freedesktop/systemd1/job/335 of service org.freedesktop.systemd1: Unknown object '/org/freedesktop/systemd1/job/335'.
-busctl tree || :
-busctl tree org.freedesktop.login1
-busctl tree --list org.freedesktop.login1
-busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1
-busctl introspect --watch-bind=yes --xml-interface org.freedesktop.systemd1 /org/freedesktop/LogControl1
-busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager
-
-busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-            GetDefaultTarget
-# Pass both JSON outputs through jq to check if the response JSON is valid
-busctl call --json=pretty \
-            org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-            ListUnitsByNames as 2 "systemd-journald.service" "systemd-logind.service" | jq
-busctl call --json=short \
-            org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-            ListUnitsByNames as 2 "systemd-journald.service" "systemd-logind.service" | jq
-# Get all properties on the org.freedesktop.systemd1.Manager interface and dump
-# them as JSON to exercise the internal JSON transformations
-busctl call -j \
-            org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.DBus.Properties \
-            GetAll s "org.freedesktop.systemd1.Manager" | jq -c
-busctl call --verbose --timeout=60 --expect-reply=yes \
-            org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-            ListUnitsByPatterns asas 1 "active" 2 "systemd-*.socket" "*.mount"
-
-busctl emit /org/freedesktop/login1 org.freedesktop.login1.Manager \
-            PrepareForSleep b false
-busctl emit --auto-start=no --destination=systemd-logind.service \
-            /org/freedesktop/login1 org.freedesktop.login1.Manager \
-            PrepareForShutdown b false
-
-busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-                    Version
-busctl get-property --verbose \
-                    org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-                    LogLevel LogTarget SystemState Version
-# Pass both JSON outputs through jq to check if the response JSON is valid
-busctl get-property --json=pretty \
-                    org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-                    LogLevel LogTarget SystemState Version | jq
-busctl get-property --json=short \
-                    org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-                    LogLevel LogTarget SystemState Version | jq
-
-# Set a property and check if it was indeed set
-busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-                    KExecWatchdogUSec t 666
-busctl get-property -j \
-                    org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-                    KExecWatchdogUSec | jq -e '.data == 666'
-
-(! busctl status org.freedesktop.systemd2)
-(! busctl tree org.freedesktop.systemd2)
-(! busctl introspect org.freedesktop.systemd1)
-(! busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd2)
-(! busctl introspect org.freedesktop.systemd2 /org/freedesktop/systemd1)
-
-# Invalid method
-(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-               ThisMethodDoesntExist)
-# Invalid signature
-(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-               ListUnitsByNames ab 1 false)
-# Invalid arguments
-(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-               GetUnitByPID u "hello")
-(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-               -- ListUnitsByNames as -1 "systemd-journald.service")
-# Not enough arguments
-(! busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-               ListUnitsByNames as 99 "systemd-journald.service")
-
-(! busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-                       NonexistentProperty)
-(! busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-                       Version NonexistentProperty Version)
-
-# Invalid property
-(! busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-                       NonexistentProperty t 666)
-# Invalid signature
-(! busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-                       KExecWatchdogUSec s 666)
-# Invalid argument
-(! busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
-                       KExecWatchdogUSec t "foo")
diff --git a/test/units/testsuite-74.capsule.sh b/test/units/testsuite-74.capsule.sh
deleted file mode 100755 (executable)
index e7b5c87..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-set -eux
-set -o pipefail
-
-at_exit() {
-    set +e
-    systemctl --no-block stop capsule@foobar.service
-    rm -rf /run/capsules/foobar
-    rm -rf /var/lib/capsules/foobar
-    rm -f /run/systemd/system/capsule@.service.d/99-asan.conf
-}
-
-trap at_exit EXIT
-
-# Appease ASan, since the capsule@.service uses DynamicUser=yes
-systemctl edit --runtime --stdin capsule@.service --drop-in=99-asan.conf <<EOF
-[Service]
-EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
-EOF
-
-(! test -f /run/capsules/foobar )
-(! test -f /var/lib/capsules/foobar )
-(! id -u c-foobar )
-
-systemctl start capsule@foobar.service
-
-test -d /run/capsules/foobar
-test -d /var/lib/capsules/foobar
-id -u c-foobar
-
-systemctl status capsule@foobar.service
-
-busctl -C foobar
-
-systemctl -C foobar
-
-systemd-run -C foobar -u sleepinfinity /bin/sleep infinity
-
-systemctl -C foobar status sleepinfinity
-
-systemctl -C foobar stop sleepinfinity
-
-(! systemctl clean capsule@foobar.service )
-
-systemctl stop capsule@foobar.service
-
-systemctl clean capsule@foobar.service --what=all
-
-(! test -f /run/capsules/foobar )
-(! test -f /var/lib/capsules/foobar )
-(! id -u c-foobar )
diff --git a/test/units/testsuite-74.cgls.sh b/test/units/testsuite-74.cgls.sh
deleted file mode 100755 (executable)
index 9268f42..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemd-cgls
-systemd-cgls --all --full
-systemd-cgls -k
-systemd-cgls --xattr=yes
-systemd-cgls --xattr=no
-systemd-cgls --cgroup-id=yes
-systemd-cgls --cgroup-id=no
-
-systemd-cgls /system.slice/systemd-journald.service
-systemd-cgls /system.slice/systemd-journald.service /init.scope
-systemd-cgls /sys/fs/cgroup/system.slice/systemd-journald.service /init.scope
-[[ -d /sys/fs/cgroup/init.scope ]] && init_scope="init.scope" || init_scope="systemd/init.scope"
-(cd "/sys/fs/cgroup/$init_scope" && systemd-cgls)
-systemd-cgls --unit=systemd-journald.service
-# There's most likely no user session running, so we need to create one
-systemd-run --user --wait --pipe -M testuser@.host systemd-cgls --user-unit=app.slice
-
-(! systemd-cgls /foo/bar)
-(! systemd-cgls --unit=hello.world)
-(! systemd-cgls --user-unit=hello.world)
-(! systemd-cgls --xattr=foo)
-(! systemd-cgls --cgroup-id=foo)
diff --git a/test/units/testsuite-74.cgtop.sh b/test/units/testsuite-74.cgtop.sh
deleted file mode 100755 (executable)
index cf98279..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Without tty attached cgtop should default to --iterations=1
-systemd-cgtop
-systemd-cgtop --iterations=1
-# Same as --iterations=1
-systemd-cgtop -1
-systemd-cgtop --delay=1ms
-systemd-cgtop --raw
-systemd-cgtop --batch
-systemd-cgtop --cpu=percentage
-systemd-cgtop --cpu=time
-systemd-cgtop -P
-systemd-cgtop -k
-systemd-cgtop --recursive=no -P
-systemd-cgtop --recursive=no -k
-systemd-cgtop --depth=0
-systemd-cgtop --depth=100
-
-for order in path tasks cpu memory io; do
-    systemd-cgtop --order="$order"
-done
-systemd-cgtop -p -t -c -m -i
-
-(! systemd-cgtop --cpu=foo)
-(! systemd-cgtop --order=foo)
-(! systemd-cgtop --depth=-1)
-(! systemd-cgtop --recursive=foo)
-(! systemd-cgtop --delay=1foo)
diff --git a/test/units/testsuite-74.coredump.sh b/test/units/testsuite-74.coredump.sh
deleted file mode 100755 (executable)
index f48607c..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
- . "$(dirname "$0")"/util.sh
-
-# Make sure the binary name fits into 15 characters
-CORE_TEST_BIN="/tmp/test-dump"
-CORE_TEST_UNPRIV_BIN="/tmp/test-usr-dump"
-MAKE_DUMP_SCRIPT="/tmp/make-dump"
-# Unset $PAGER so we don't have to use --no-pager everywhere
-export PAGER=
-
-at_exit() {
-    rm -fv -- "$CORE_TEST_BIN" "$CORE_TEST_UNPRIV_BIN" "$MAKE_DUMP_SCRIPT"
-}
-
-trap at_exit EXIT
-
-if systemd-detect-virt -cq; then
-    echo "Running in a container, skipping the systemd-coredump test..."
-    exit 0
-fi
-
-# To make all coredump entries stored in system.journal.
-journalctl --rotate
-
-# Check that we're the ones to receive coredumps
-sysctl kernel.core_pattern | grep systemd-coredump
-
-# Prepare "fake" binaries for coredumps, so we can properly exercise
-# the matching stuff too
-cp -vf /bin/sleep "${CORE_TEST_BIN:?}"
-cp -vf /bin/sleep "${CORE_TEST_UNPRIV_BIN:?}"
-# Simple script that spawns given "fake" binary and then kills it with
-# given signal
-cat >"${MAKE_DUMP_SCRIPT:?}" <<\EOF
-#!/bin/bash -ex
-
-bin="${1:?}"
-sig="${2:?}"
-
-ulimit -c unlimited
-"$bin" infinity &
-pid=$!
-# Sync with the "fake" binary, so we kill it once it's fully forked off,
-# otherwise we might kill it during fork and kernel would then report
-# "wrong" binary name (i.e. $MAKE_DUMP_SCRIPT instead of $CORE_TEST_BIN).
-# In this case, wait until the "fake" binary (sleep in this case) enters
-# the "interruptible sleep" state, at which point it should be ready
-# to be sacrificed.
-for _ in {0..9}; do
-    read -ra self_stat <"/proc/$pid/stat"
-    [[ "${self_stat[2]}" == S ]] && break
-    sleep .5
-done
-kill -s "$sig" "$pid"
-# This should always fail
-! wait "$pid"
-EOF
-chmod +x "$MAKE_DUMP_SCRIPT"
-
-# Privileged stuff
-[[ "$(id -u)" -eq 0 ]]
-# Trigger a couple of coredumps
-"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP"
-"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT"
-# In the tests we store the coredumps in journals, so let's generate a couple
-# with Storage=external as well
-mkdir -p /run/systemd/coredump.conf.d/
-printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf
-"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP"
-"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT"
-rm -fv /run/systemd/coredump.conf.d/99-external.conf
-# Wait a bit for the coredumps to get processed
-timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
-
-if cgroupfs_supports_user_xattrs; then
-    # Make sure we can forward crashes back to containers
-    CONTAINER="testsuite-74-container"
-
-    mkdir -p "/var/lib/machines/$CONTAINER"
-    mkdir -p "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d"
-    # Bind-mounting /etc into the container kinda defeats the purpose of --volatile=,
-    # but we need the ASan-related overrides scattered across /etc
-    cat > "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d/override.conf" <<EOF
-[Service]
-ExecStart=
-ExecStart=systemd-nspawn --quiet --link-journal=try-guest --keep-unit --machine=%i --boot \
-                         --volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id
-EOF
-    systemctl daemon-reload
-
-    [[ "$(systemd-detect-virt)" == "qemu" ]] && TIMEOUT=120 || TIMEOUT=60
-
-    machinectl start "$CONTAINER"
-    timeout "$TIMEOUT" bash -xec "until systemd-run -M '$CONTAINER' -q --wait --pipe true; do sleep .5; done"
-
-    [[ "$(systemd-run -M "$CONTAINER" -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l)" -eq 0 ]]
-    machinectl copy-to "$CONTAINER" "$MAKE_DUMP_SCRIPT"
-    systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGABRT"
-    systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGTRAP"
-    # Wait a bit for the coredumps to get processed
-    timeout 30 bash -c "while [[ \$(systemd-run -M $CONTAINER -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l) -lt 2 ]]; do sleep 1; done"
-
-    rm -rf "/var/lib/machines/$CONTAINER"
-fi
-
-coredumpctl
-SYSTEMD_LOG_LEVEL=debug coredumpctl
-coredumpctl --help
-coredumpctl --version
-coredumpctl --no-pager --no-legend
-coredumpctl --all
-coredumpctl -1
-coredumpctl -n 1
-coredumpctl --reverse
-coredumpctl -F COREDUMP_EXE
-coredumpctl --json=short | jq
-coredumpctl --json=pretty | jq
-coredumpctl --json=off
-coredumpctl --root=/
-coredumpctl --directory=/var/log/journal
-coredumpctl --file="/var/log/journal/$(</etc/machine-id)"/*.journal
-coredumpctl --since=@0
-coredumpctl --since=yesterday --until=tomorrow
-# We should have a couple of externally stored coredumps
-coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out
-grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out
-rm -f /tmp/coredumpctl.out
-
-coredumpctl info
-coredumpctl info "$CORE_TEST_BIN"
-coredumpctl info /foo /bar/ /baz "$CORE_TEST_BIN"
-coredumpctl info "${CORE_TEST_BIN##*/}"
-coredumpctl info foo bar baz "${CORE_TEST_BIN##*/}"
-coredumpctl info COREDUMP_EXE="$CORE_TEST_BIN"
-coredumpctl info COREDUMP_EXE=aaaaa COREDUMP_EXE= COREDUMP_EXE="$CORE_TEST_BIN"
-
-coredumpctl debug --debugger=/bin/true "$CORE_TEST_BIN"
-SYSTEMD_DEBUGGER=/bin/true coredumpctl debug "$CORE_TEST_BIN"
-coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_BIN##*/}"
-
-coredumpctl dump "$CORE_TEST_BIN" >/tmp/core.redirected
-test -s /tmp/core.redirected
-coredumpctl dump -o /tmp/core.output "${CORE_TEST_BIN##*/}"
-test -s /tmp/core.output
-rm -f /tmp/core.{output,redirected}
-
-# Unprivileged stuff
-# Related issue: https://github.com/systemd/systemd/issues/26912
-UNPRIV_CMD=(systemd-run --user --wait --pipe -M "testuser@.host" --)
-# Trigger a couple of coredumps as an unprivileged user
-"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
-"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
-# In the tests we store the coredumps in journals, so let's generate a couple
-# with Storage=external as well
-mkdir -p /run/systemd/coredump.conf.d/
-printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf
-"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
-"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
-rm -fv /run/systemd/coredump.conf.d/99-external.conf
-# Wait a bit for the coredumps to get processed
-timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_UNPRIV_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
-
-# root should see coredumps from both binaries
-coredumpctl info "$CORE_TEST_UNPRIV_BIN"
-coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}"
-# The test user should see only their own coredumps
-"${UNPRIV_CMD[@]}" coredumpctl
-"${UNPRIV_CMD[@]}" coredumpctl info "$CORE_TEST_UNPRIV_BIN"
-"${UNPRIV_CMD[@]}" coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}"
-(! "${UNPRIV_CMD[@]}" coredumpctl info --all "$CORE_TEST_BIN")
-(! "${UNPRIV_CMD[@]}" coredumpctl info --all "${CORE_TEST_BIN##*/}")
-# We should have a couple of externally stored coredumps
-"${UNPRIV_CMD[@]}" coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out
-grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out
-rm -f /tmp/coredumpctl.out
-
-"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true "$CORE_TEST_UNPRIV_BIN"
-"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_UNPRIV_BIN##*/}"
-
-"${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_UNPRIV_BIN" >/tmp/core.redirected
-test -s /tmp/core.redirected
-"${UNPRIV_CMD[@]}" coredumpctl dump -o /tmp/core.output "${CORE_TEST_UNPRIV_BIN##*/}"
-test -s /tmp/core.output
-rm -f /tmp/core.{output,redirected}
-(! "${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_BIN" >/dev/null)
-
-# --backtrace mode
-# Pass one of the existing journal coredump records to systemd-coredump and
-# use our PID as the source to make matching the coredump later easier
-# systemd-coredump args: PID UID GID SIGNUM TIMESTAMP CORE_SOFT_RLIMIT HOSTNAME
-journalctl -b -n 1 --output=export --output-fields=MESSAGE,COREDUMP COREDUMP_EXE="/usr/bin/test-dump" |
-    /usr/lib/systemd/systemd-coredump --backtrace $$ 0 0 6 1679509994 12345 mymachine
-# Wait a bit for the coredump to get processed
-timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $$ | wc -l) -eq 0 ]]; do sleep 1; done"
-coredumpctl info "$$"
-coredumpctl info COREDUMP_HOSTNAME="mymachine"
-
-# This used to cause a stack overflow
-systemd-run -t --property CoredumpFilter=all ls /tmp
-systemd-run -t --property CoredumpFilter=default ls /tmp
-
-(! coredumpctl --hello-world)
-(! coredumpctl -n 0)
-(! coredumpctl -n -1)
-(! coredumpctl --file=/dev/null)
-(! coredumpctl --since=0)
-(! coredumpctl --until='')
-(! coredumpctl --since=today --until=yesterday)
-(! coredumpctl --directory=/ --root=/)
-(! coredumpctl --json=foo)
-(! coredumpctl -F foo -F bar)
-(! coredumpctl list 0)
-(! coredumpctl list -- -1)
-(! coredumpctl list '')
-(! coredumpctl info /../.~=)
-(! coredumpctl info '')
-(! coredumpctl dump --output=/dev/full "$CORE_TEST_BIN")
-(! coredumpctl dump --output=/dev/null --output=/dev/null "$CORE_TEST_BIN")
-(! coredumpctl debug --debugger=/bin/false)
-(! coredumpctl debug --debugger=/bin/true --debugger-arguments='"')
diff --git a/test/units/testsuite-74.delta.sh b/test/units/testsuite-74.delta.sh
deleted file mode 100755 (executable)
index dabe234..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-at_exit() {
-    rm -rfv /{run,etc}/systemd/system/delta-test*
-}
-
-trap at_exit EXIT
-
-# Create a couple of supporting units with overrides
-#
-# Extended unit
-cat >"/run/systemd/system/delta-test-unit-extended.service" <<EOF
-[Service]
-ExecStart=true
-EOF
-mkdir -p "/run/systemd/system/delta-test-unit-extended.service.d"
-cat >"/run/systemd/system/delta-test-unit-extended.service.d/override.conf" <<EOF
-[Unit]
-Description=Foo Bar
-[Service]
-ExecStartPre=true
-EOF
-# Masked unit
-cp -fv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-masked.service
-systemctl mask delta-test-unit-masked.service
-# Overridden unit
-cp -fv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-overridden.service
-cp -fv /run/systemd/system/delta-test-unit-overridden.service /etc/systemd/system/delta-test-unit-overridden.service
-echo "ExecStartPost=/bin/true" >>/etc/systemd/system/delta-test-unit-overridden.service
-# Overridden but equivalent unit
-ln -srfv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-equivalent.service
-ln -sfv /run/systemd/system/delta-test-unit-extended.service /etc/systemd/system/delta-test-unit-equivalent.service
-# Redirected unit
-ln -srfv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-redirected.service
-ln -sfv /run/systemd/system/delta-test-unit-overidden.service /etc/systemd/system/delta-test-unit-extended.service
-
-systemctl daemon-reload
-
-systemd-delta
-systemd-delta /run
-systemd-delta systemd/system
-systemd-delta /run systemd/system /run
-systemd-delta /run foo/bar hello/world systemd/system /run
-systemd-delta foo/bar
-systemd-delta --diff=true
-systemd-delta --diff=false
-
-for type in masked equivalent redirected overridden extended unchanged; do
-    systemd-delta --type="$type"
-    systemd-delta --type="$type" /run
-done
-systemd-delta --type=equivalent,redirected
-
-(! systemd-delta --diff=foo)
-(! systemd-delta --type=foo)
-(! systemd-delta --type=equivalent,redirected,foo)
diff --git a/test/units/testsuite-74.escape.sh b/test/units/testsuite-74.escape.sh
deleted file mode 100755 (executable)
index e398d40..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# Simple wrapper to check both escaping and unescaping of given strings
-# Arguments:
-#   $1 - expected unescaped string
-#   $2 - expected escaped string
-#   $3 - optional arguments for systemd-escape
-check_escape() {
-    unescaped="${1?}"
-    escaped="${2?}"
-    shift 2
-
-    assert_eq "$(systemd-escape "$@" -- "$unescaped")" "$escaped"
-    assert_eq "$(systemd-escape "$@" --unescape -- "$escaped")" "$unescaped"
-}
-
-systemd-escape --help
-systemd-escape --version
-
-check_escape '' ''
-check_escape 'hello' 'hello'
-check_escape 'hello-world' 'hello\x2dworld'
-check_escape '-+ěščřž---🤔' '\x2d\x2b\xc4\x9b\xc5\xa1\xc4\x8d\xc5\x99\xc5\xbe\x2d\x2d\x2d\xf0\x9f\xa4\x94'
-check_escape '/this/is/a/path/a b c' '-this-is-a-path-a\x20b\x20c'
-
-# Multiple strings to escape/unescape
-assert_eq "$(systemd-escape 'hello-world' '/dev/loop1' 'template@🐍')" \
-          'hello\x2dworld -dev-loop1 template\x40\xf0\x9f\x90\x8d'
-assert_eq "$(systemd-escape --unescape -- 'hello\x2dworld' '-dev-loop1' 'template\x40\xf0\x9f\x90\x8d')" \
-          'hello-world /dev/loop1 template@🐍'
-
-# --suffix= is not compatible with --unescape
-assert_eq "$(systemd-escape --suffix=mount -- '-+ěščřž---🤔')" \
-          '\x2d\x2b\xc4\x9b\xc5\xa1\xc4\x8d\xc5\x99\xc5\xbe\x2d\x2d\x2d\xf0\x9f\xa4\x94.mount'
-assert_eq "$(systemd-escape --suffix=timer 'this has spaces')" \
-          'this\x20has\x20spaces.timer'
-assert_eq "$(systemd-escape --suffix=service 'trailing-spaces  ')" \
-          'trailing\x2dspaces\x20\x20.service'
-assert_eq "$(systemd-escape --suffix=automount '   leading-spaces')" \
-          '\x20\x20\x20leading\x2dspaces.automount'
-
-# --template=
-check_escape 'hello' 'hello@hello.service' --template=hello@.service
-check_escape '  what - is _ love? 🤔 ¯\_(ツ)_/¯' \
-             'hello@\x20\x20what\x20\x2d\x20is\x20_\x20love\x3f\x20\xf0\x9f\xa4\x94\x20\xc2\xaf\x5c_\x28\xe3\x83\x84\x29_-\xc2\xaf.service' \
-             --template=hello@.service
-check_escape '/this/is/where/my/stuff/is/ with spaces though ' \
-             'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service' \
-             --template=mount-my-stuff@.service
-check_escape '/this/is/where/my/stuff/is/ with spaces though ' \
-             'mount-my-stuff@this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service' \
-             --template=mount-my-stuff@.service --path
-
-# --instance (must be used with --unescape)
-assert_eq "$(systemd-escape --unescape --instance 'hello@\x20\x20what\x20\x2d\x20is\x20_\x20love\x3f\x20\xf0\x9f\xa4\x94\x20\xc2\xaf\x5c_\x28\xe3\x83\x84\x29_-\xc2\xaf.service')" \
-          '  what - is _ love? 🤔 ¯\_(ツ)_/¯'
-assert_eq "$(systemd-escape --unescape --instance 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')" \
-          '/this/is/where/my/stuff/is/ with spaces though '
-assert_eq "$(systemd-escape --unescape --instance --path 'mount-my-stuff@this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')" \
-          '/this/is/where/my/stuff/is/ with spaces though '
-
-# --path, reversible cases
-check_escape / '-' --path
-check_escape '/hello/world' 'hello-world' --path
-check_escape '/mnt/smb/おにぎり' \
-             'mnt-smb-\xe3\x81\x8a\xe3\x81\xab\xe3\x81\x8e\xe3\x82\x8a' \
-             --path
-
-# --path, non-reversible cases
-assert_eq "$(systemd-escape --path ///////////////)" '-'
-assert_eq "$(systemd-escape --path /..)" '-'
-assert_eq "$(systemd-escape --path /../.././../.././)" '-'
-assert_eq "$(systemd-escape --path /../.././../.././foo)" 'foo'
-
-# --mangle
-assert_eq "$(systemd-escape --mangle 'hello-world')" 'hello-world.service'
-assert_eq "$(systemd-escape --mangle '/mount/this')" 'mount-this.mount'
-assert_eq "$(systemd-escape --mangle 'my-service@ 🐱 ')" 'my-service@\x20\xf0\x9f\x90\xb1\x20.service'
-assert_eq "$(systemd-escape --mangle '/dev/disk/by-emoji/🍎')" 'dev-disk-by\x2demoji-\xf0\x9f\x8d\x8e.device'
-assert_eq "$(systemd-escape --mangle 'daily-existential-crisis .timer')" 'daily-existential-crisis\x20.timer'
-assert_eq "$(systemd-escape --mangle 'trailing-whitespace.mount ')" 'trailing-whitespace.mount\x20.service'
-
-(! systemd-escape)
-(! systemd-escape --suffix='' hello)
-(! systemd-escape --suffix=invalid hello)
-(! systemd-escape --suffix=mount --template=hello@.service hello)
-(! systemd-escape --suffix=mount --mangle)
-(! systemd-escape --template='')
-(! systemd-escape --template=@)
-(! systemd-escape --template='hello@.service' '')
-(! systemd-escape --unescape --template='hello@.service' '@hello.service')
-(! systemd-escape --unescape --template='hello@.service' 'hello@.service')
-(! systemd-escape --mangle --template=hello@.service hello)
-(! systemd-escape --instance 'hello@hello.service')
-(! systemd-escape --instance --template=hello@.service 'hello@hello.service')
-(! systemd-escape --unescape --instance --path 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')
-(! systemd-escape --path '/../hello/..')
-(! systemd-escape --path '.')
-(! systemd-escape --path '..')
-(! systemd-escape --path "$(set +x; printf '%0.sa' {0..256})")
-(! systemd-escape --unescape --path '')
-(! systemd-escape --mangle '')
diff --git a/test/units/testsuite-74.firstboot.sh b/test/units/testsuite-74.firstboot.sh
deleted file mode 100755 (executable)
index 7bab009..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-if ! command -v systemd-firstboot >/dev/null; then
-    echo "systemd-firstboot not found, skipping the test"
-    exit 0
-fi
-
-at_exit() {
-    if [[ -n "${ROOT:-}" ]]; then
-        ls -lR "$ROOT"
-        rm -fr "$ROOT"
-    fi
-
-    restore_locale
-}
-
-trap at_exit EXIT
-
-# Generated via `mkpasswd -m sha-512 -S foobarsalt password1`
-# shellcheck disable=SC2016
-ROOT_HASHED_PASSWORD1='$6$foobarsalt$YbwdaATX6IsFxvWbY3QcZj2gB31R/LFRFrjlFrJtTTqFtSfn4dfOAg/km2k4Sl.a2g7LOYDo31wMTaEsCo9j41'
-# Generated via `mkpasswd -m sha-512 -S foobarsalt password2`
-# shellcheck disable=SC2016
-ROOT_HASHED_PASSWORD2='$6$foobarsalt$q.P2932zYMLbKnjFwIxPI8y3iuxeuJ2BgE372LcZMMnj3Gcg/9mJg2LPKUl.ha0TG/.fRNNnRQcLfzM0SNot3.'
-
-if [[ -f /etc/locale.conf ]]; then
-    cp /etc/locale.conf /tmp/locale.conf.bak
-fi
-
-# Debian/Ubuntu specific file
-if [[ -f /etc/default/locale ]]; then
-    cp /etc/default/locale /tmp/default-locale.bak
-fi
-
-if [[ -f /etc/locale.gen ]]; then
-    cp /etc/locale.gen /tmp/locale.gen.bak
-fi
-
-# Make sure at least two locales exist (C.UTF-8 and en_US.UTF-8) as systemd-firstboot --prompt-locale will
-# skip writing the locale if it detects only one is installed.
-generate_locale en_US.UTF-8
-
-# Debian and Ubuntu use /etc/default/locale instead of /etc/locale.conf. Make
-# sure we use the appropriate path for locale configuration.
-LOCALE_PATH="/etc/locale.conf"
-[ -e "$LOCALE_PATH" ] || LOCALE_PATH="/etc/default/locale"
-[ -e "$LOCALE_PATH" ] || systemd-firstboot --locale=C.UTF-8
-
-# Create a minimal root so we don't modify the testbed
-ROOT=test-root
-mkdir -p "$ROOT/bin"
-# Dummy shell for --root-shell=
-touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
-
-systemd-firstboot --root="$ROOT" --locale=foo
-grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
-rm -fv "$ROOT$LOCALE_PATH"
-systemd-firstboot --root="$ROOT" --locale-messages=foo
-grep -q "LC_MESSAGES=foo" "$ROOT$LOCALE_PATH"
-rm -fv "$ROOT$LOCALE_PATH"
-systemd-firstboot --root="$ROOT" --locale=foo --locale-messages=bar
-grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
-grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
-
-systemd-firstboot --root="$ROOT" --keymap=foo
-grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
-
-systemd-firstboot --root="$ROOT" --timezone=Europe/Berlin
-readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin"
-
-systemd-firstboot --root="$ROOT" --hostname "foobar"
-grep -q "foobar" "$ROOT/etc/hostname"
-
-systemd-firstboot --root="$ROOT" --machine-id=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id"
-
-rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
-systemd-firstboot --root="$ROOT" --root-password=foo
-grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
-grep -q "^root:" "$ROOT/etc/shadow"
-rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow"
-echo "foo" >root.passwd
-systemd-firstboot --root="$ROOT" --root-password-file=root.passwd
-grep -q "^root:x:0:0:" "$ROOT/etc/passwd"
-grep -q "^root:" "$ROOT/etc/shadow"
-rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" root.passwd
-# Set the shell together with the password, as firstboot won't touch
-# /etc/passwd if it already exists
-systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1" --root-shell=/bin/fooshell
-grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
-grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
-
-systemd-firstboot --root="$ROOT" --kernel-command-line="foo.bar=42"
-grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline"
-
-# Configs should not get overwritten if they exist unless --force is used
-systemd-firstboot --root="$ROOT" \
-                  --locale=locale-overwrite \
-                  --locale-messages=messages-overwrite \
-                  --keymap=keymap-overwrite \
-                  --timezone=CET \
-                  --hostname=hostname-overwrite \
-                  --machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \
-                  --root-password-hashed="$ROOT_HASHED_PASSWORD2" \
-                  --root-shell=/bin/barshell \
-                  --kernel-command-line="hello.world=0"
-grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
-grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
-grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
-readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
-grep -q "foobar" "$ROOT/etc/hostname"
-grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id"
-grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
-grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow"
-grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline"
-
-# The same thing, but now with --force
-systemd-firstboot --root="$ROOT" --force \
-                  --locale=locale-overwrite \
-                  --locale-messages=messages-overwrite \
-                  --keymap=keymap-overwrite \
-                  --timezone=CET \
-                  --hostname=hostname-overwrite \
-                  --machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \
-                  --root-password-hashed="$ROOT_HASHED_PASSWORD2" \
-                  --root-shell=/bin/barshell \
-                  --kernel-command-line="hello.world=0"
-grep -q "LANG=locale-overwrite" "$ROOT$LOCALE_PATH"
-grep -q "LC_MESSAGES=messages-overwrite" "$ROOT$LOCALE_PATH"
-grep -q "KEYMAP=keymap-overwrite" "$ROOT/etc/vconsole.conf"
-readlink "$ROOT/etc/localtime" | grep -q "/CET$"
-grep -q "hostname-overwrite" "$ROOT/etc/hostname"
-grep -q "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" "$ROOT/etc/machine-id"
-grep -q "^root:x:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd"
-grep -q "^root:$ROOT_HASHED_PASSWORD2:" "$ROOT/etc/shadow"
-grep -q "hello.world=0" "$ROOT/etc/kernel/cmdline"
-
-# Test that --reset removes all files configured by firstboot.
-systemd-firstboot --root="$ROOT" --reset
-[[ ! -e "$ROOT/etc/locale.conf" ]]
-[[ ! -e "$ROOT/etc/vconsole.conf" ]]
-[[ ! -e "$ROOT/etc/localtime" ]]
-[[ ! -e "$ROOT/etc/hostname" ]]
-[[ ! -e "$ROOT/etc/machine-id" ]]
-[[ ! -e "$ROOT/etc/kernel/cmdline" ]]
-
-# --copy-* options
-rm -fr "$ROOT"
-mkdir "$ROOT"
-# Copy everything at once (--copy)
-systemd-firstboot --root="$ROOT" --copy
-diff $LOCALE_PATH "$ROOT$LOCALE_PATH"
-diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd")
-diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
-[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf"
-[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime")
-rm -fr "$ROOT"
-mkdir "$ROOT"
-# Copy everything at once, but now by using separate switches
-systemd-firstboot --root="$ROOT" --copy-locale --copy-keymap --copy-timezone --copy-root-password --copy-root-shell
-diff $LOCALE_PATH "$ROOT$LOCALE_PATH"
-diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd")
-diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
-[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf"
-[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime")
-
-# --prompt-* options
-rm -fr "$ROOT"
-mkdir -p "$ROOT/bin"
-touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell"
-# Temporarily disable pipefail to avoid `echo: write error: Broken pipe
-set +o pipefail
-# We can do only limited testing here, since it's all an interactive stuff,
-# so --prompt and --prompt-root-password are skipped on purpose
-echo -ne "\nfoo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale
-grep -q "LANG=foo" "$ROOT$LOCALE_PATH"
-grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH"
-# systemd-firstboot in prompt-keymap mode requires keymaps to be installed so
-# it can present them as a list to the user. As Debian does not ship/provide
-# compatible keymaps (from the kbd package), skip this test if the keymaps are
-# missing.
-if [ -d "/usr/share/keymaps/" ] || [ -d "/usr/share/kbd/keymaps/" ] || [ -d "/usr/lib/kbd/keymaps/" ] ; then
-   echo -ne "\nfoo\n" | systemd-firstboot --root="$ROOT" --prompt-keymap
-   grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf"
-fi
-echo -ne "\nEurope/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezone
-readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$"
-echo -ne "\nfoobar\n" | systemd-firstboot --root="$ROOT" --prompt-hostname
-grep -q "foobar" "$ROOT/etc/hostname"
-echo -ne "\n/bin/fooshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
-grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
-# Existing files should not get overwritten
-echo -ne "\n/bin/barshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell
-grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd"
-# Now without the welcome screen but with force
-echo -ne "/bin/barshell\n" | systemd-firstboot --root="$ROOT" --force --prompt-root-shell --welcome=no
-grep -q "^root:.*:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd"
-# Re-enable pipefail
-set -o pipefail
-
-# Assorted tests
-rm -fr "$ROOT"
-mkdir "$ROOT"
-
-systemd-firstboot --root="$ROOT" --setup-machine-id
-grep -E "[a-z0-9]{32}" "$ROOT/etc/machine-id"
-
-systemd-firstboot --root="$ROOT" --delete-root-password
-diff <(echo) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow")
-
-(! systemd-firstboot --root="$ROOT" --root-shell=/bin/nonexistentshell)
-(! systemd-firstboot --root="$ROOT" --machine-id=invalidmachineid)
-(! systemd-firstboot --root="$ROOT" --timezone=Foo/Bar)
diff --git a/test/units/testsuite-74.id128.sh b/test/units/testsuite-74.id128.sh
deleted file mode 100755 (executable)
index f91cd5f..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-systemd-id128 --help
-systemd-id128 help
-systemd-id128 show
-systemd-id128 show --pretty | tail
-systemd-id128 show --value | tail
-systemd-id128 show 4f68bce3e8cd4db196e7fbcaf984b709 # root-x86-64
-systemd-id128 show --pretty 4f68bce3e8cd4db196e7fbcaf984b709
-systemd-id128 show root-x86-64
-systemd-id128 show --pretty root-x86-64
-[[ "$(systemd-id128 show 4f68bce3e8cd4db196e7fbcaf984b709)" = "$(systemd-id128 show root-x86-64)" ]]
-[[ "$(systemd-id128 show 4f68bce3-e8cd-4db1-96e7-fbcaf984b709)" = "$(systemd-id128 show root-x86-64)" ]]
-
-systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
-systemd-id128 show --pretty root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
-[[ "$(systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709 -P)" = "8ee5535e7cb14c249e1d28b8dfbb939c" ]]
-
-systemd-id128 show -j
-systemd-id128 show --no-pager
-systemd-id128 show --json=short
-systemd-id128 show --no-legend
-systemd-id128 show --no-pager --no-legend
-systemd-id128 show root -P -u
-
-[[ "$(systemd-id128 new | wc -c)" -eq 33 ]]
-systemd-id128 new -p
-systemd-id128 new -u
-systemd-id128 new -a 4f68bce3e8cd4db196e7fbcaf984b709
-
-systemd-id128 machine-id
-systemd-id128 machine-id --pretty
-systemd-id128 machine-id --uuid
-systemd-id128 machine-id --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
-assert_eq "$(systemd-id128 machine-id)" "$(</etc/machine-id)"
-
-systemd-id128 boot-id
-systemd-id128 boot-id --pretty
-systemd-id128 boot-id --uuid
-systemd-id128 boot-id --app-specific=4f68bce3e8cd4db196e7fbcaf984b709
-assert_eq "$(systemd-id128 boot-id --uuid)" "$(</proc/sys/kernel/random/boot_id)"
-
-# shellcheck disable=SC2016
-systemd-run --wait --pipe bash -euxc '[[ $INVOCATION_ID == "$(systemd-id128 invocation-id)" ]]'
-
-(! systemd-id128)
-(! systemd-id128 new -a '')
-(! systemd-id128 new -a '0')
-(! systemd-id128 invocation-id -a 4f68bce3e8cd4db196e7fbcaf984b709)
-(! systemd-id128 show '')
-(! systemd-id128 show "$(set +x; printf '%0.s0' {0..64})")
diff --git a/test/units/testsuite-74.machine-id-setup.sh b/test/units/testsuite-74.machine-id-setup.sh
deleted file mode 100755 (executable)
index c2b9db5..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2064
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-root_mock() {
-    local root="${1:?}"
-
-    mkdir -p "$root"
-    # Put a tmpfs over the "root", so we're able to remount it as read-only
-    # when needed
-    mount -t tmpfs tmpfs "$root"
-    mkdir "$root/etc" "$root/run"
-}
-
-root_cleanup() {
-    local root="${1:?}"
-
-    umount --recursive "$root"
-    rm -fr "$root"
-}
-
-testcase_sanity() {
-    systemd-machine-id-setup
-    systemd-machine-id-setup --help
-    systemd-machine-id-setup --version
-    systemd-machine-id-setup --print
-    systemd-machine-id-setup --root= --print
-    systemd-machine-id-setup --root=/ --print
-
-    (! systemd-machine-id-setup "")
-    (! systemd-machine-id-setup --foo)
-}
-
-testcase_invalid() {
-    local root machine_id
-
-    root="$(mktemp -d)"
-    trap "root_cleanup $root" RETURN
-    root_mock "$root"
-
-    systemd-machine-id-setup --print --root "$root"
-    echo abc >>"$root/etc/machine-id"
-    machine_id="$(systemd-machine-id-setup --print --root "$root")"
-    diff <(echo "$machine_id") "$root/etc/machine-id"
-}
-
-testcase_transient() {
-    local root transient_id committed_id
-
-    root="$(mktemp -d)"
-    trap "root_cleanup $root" RETURN
-    root_mock "$root"
-
-    systemd-machine-id-setup --print --root "$root"
-    echo abc >>"$root/etc/machine-id"
-    mount -o remount,ro "$root"
-    mount -t tmpfs tmpfs "$root/run"
-    transient_id="$(systemd-machine-id-setup --print --root "$root")"
-    mount -o remount,rw "$root"
-    committed_id="$(systemd-machine-id-setup --print --commit --root "$root")"
-    [[ "$transient_id" == "$committed_id" ]]
-    diff "$root/etc/machine-id" "$root/run/machine-id"
-}
-
-# Check if we correctly processed the invalid machine ID we set up in the respective
-# test.sh file
-systemctl --state=failed --no-legend --no-pager >/failed
-test ! -s /failed
-
-run_testcases
diff --git a/test/units/testsuite-74.modules-load.sh b/test/units/testsuite-74.modules-load.sh
deleted file mode 100755 (executable)
index ceac826..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-MODULES_LOAD_BIN="/usr/lib/systemd/systemd-modules-load"
-CONFIG_FILE="/run/modules-load.d/99-test.conf"
-
-at_exit() {
-    rm -rfv "${CONFIG_FILE:?}"
-}
-
-trap at_exit EXIT
-
-if systemd-detect-virt -cq; then
-    echo "Running in a container, skipping the systemd-modules-load test..."
-    exit 0
-fi
-
-ORIG_MODULES_LOAD_CONFIG="$(systemd-analyze cat-config modules-load.d)"
-
-# Check if we have required kernel modules
-modprobe --all --resolve-alias dummy
-
-mkdir -p /run/modules-load.d/
-
-"$MODULES_LOAD_BIN"
-"$MODULES_LOAD_BIN" --help
-"$MODULES_LOAD_BIN" --version
-
-# Explicit config file
-modprobe -v --all --remove dummy
-printf "dummy" >"$CONFIG_FILE"
-"$MODULES_LOAD_BIN" "$CONFIG_FILE" |& tee /tmp/out.log
-grep -E "Inserted module .*dummy" /tmp/out.log
-
-# Implicit config file
-modprobe -v --all --remove dummy
-printf "dummy" >"$CONFIG_FILE"
-"$MODULES_LOAD_BIN" |& tee /tmp/out.log
-grep -E "Inserted module .*dummy" /tmp/out.log
-
-# Valid & invalid data mixed together
-modprobe -v --all --remove dummy
-cat >"$CONFIG_FILE" <<EOF
-
-dummy
-dummy
-dummy
-    dummy
-dummy
-    \\n\n\n\\\\\\
-
-dumm!@@123##2455
-# This is a comment
-$(printf "%.0sx" {0..4096})
-dummy
-dummy
-foo-bar-baz
-1
-"
-'
-EOF
-"$MODULES_LOAD_BIN" |& tee /tmp/out.log
-grep -E "^Inserted module .*dummy" /tmp/out.log
-grep -E "^Failed to find module .*foo-bar-baz" /tmp/out.log
-(! grep -E "This is a comment" /tmp/out.log)
-# Each module should be loaded only once, even if specified multiple times
-[[ "$(grep -Ec "^Inserted module" /tmp/out.log)" -eq 1 ]]
-[[ "$(grep -Ec "^Failed to find module" /tmp/out.log)" -eq 7 ]]
-
-# Command line arguments
-modprobe -v --all --remove dummy
-# Make sure we have no config files left over that might interfere with
-# following tests
-rm -fv "$CONFIG_FILE"
-[[ "$ORIG_MODULES_LOAD_CONFIG" == "$(systemd-analyze cat-config modules-load.d)" ]]
-CMDLINE="ro root= modules_load= modules_load=, / = modules_load=foo-bar-baz,dummy modules_load=dummy,dummy,dummy"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" "$MODULES_LOAD_BIN" |& tee /tmp/out.log
-grep -E "^Inserted module .*dummy" /tmp/out.log
-grep -E "^Failed to find module .*foo-bar-baz" /tmp/out.log
-# Each module should be loaded only once, even if specified multiple times
-[[ "$(grep -Ec "^Inserted module" /tmp/out.log)" -eq 1 ]]
-
-(! "$MODULES_LOAD_BIN" --nope)
-(! "$MODULES_LOAD_BIN" /foo/bar/baz)
diff --git a/test/units/testsuite-74.mount.sh b/test/units/testsuite-74.mount.sh
deleted file mode 100755 (executable)
index 57b06bb..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# We're going to play around with block/loop devices, so bail out early
-# if we're running in nspawn
-if systemd-detect-virt --container >/dev/null; then
-    echo "Container detected, skipping the test"
-    exit 0
-fi
-
-at_exit() {
-    set +e
-
-    [[ -n "${LOOP:-}" ]] && losetup -d "$LOOP"
-    [[ -n "${WORK_DIR:-}" ]] && rm -fr "$WORK_DIR"
-}
-
-trap at_exit EXIT
-
-WORK_DIR="$(mktemp -d)"
-
-systemd-mount --list
-systemd-mount --list --full
-systemd-mount --list --no-legend
-systemd-mount --list --no-pager
-systemd-mount --list --quiet
-
-# Set up a simple block device for further tests
-dd if=/dev/zero of="$WORK_DIR/simple.img" bs=1M count=16
-mkfs.ext4 -L sd-mount-test "$WORK_DIR/simple.img"
-LOOP="$(losetup --show --find "$WORK_DIR/simple.img")"
-udevadm wait --timeout 60 --settle "$LOOP"
-mkdir "$WORK_DIR/mnt"
-mount "$LOOP" "$WORK_DIR/mnt"
-touch "$WORK_DIR/mnt/foo.bar"
-umount "$LOOP"
-(! mountpoint "$WORK_DIR/mnt")
-
-# Mount with both source and destination set
-systemd-mount "$LOOP" "$WORK_DIR/mnt"
-systemctl status "$WORK_DIR/mnt"
-systemd-mount --list --full
-test -e "$WORK_DIR/mnt/foo.bar"
-systemd-umount "$WORK_DIR/mnt"
-# Same thing, but with explicitly specified filesystem and disabled filesystem check
-systemd-mount --type=ext4 --fsck=no --collect "$LOOP" "$WORK_DIR/mnt"
-systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").mount"
-test -e "$WORK_DIR/mnt/foo.bar"
-systemd-mount --umount "$LOOP"
-# Discover additional metadata (unit description should now contain filesystem label)
-systemd-mount --no-ask-password --discover "$LOOP" "$WORK_DIR/mnt"
-test -e "$WORK_DIR/mnt/foo.bar"
-systemctl show -P Description "$WORK_DIR/mnt" | grep -q sd-mount-test
-systemd-umount "$WORK_DIR/mnt"
-# Set a unit description
-systemd-mount --description="Very Important Unit" "$LOOP" "$WORK_DIR/mnt"
-test -e "$WORK_DIR/mnt/foo.bar"
-systemctl show -P Description "$WORK_DIR/mnt" | grep -q "Very Important Unit"
-systemd-umount "$WORK_DIR/mnt"
-# Set a property
-systemd-mount --property="Description=Foo Bar" "$LOOP" "$WORK_DIR/mnt"
-test -e "$WORK_DIR/mnt/foo.bar"
-systemctl show -P Description "$WORK_DIR/mnt" | grep -q "Foo Bar"
-systemd-umount "$WORK_DIR/mnt"
-# Set mount options
-systemd-mount --options=ro,x-foo-bar "$LOOP" "$WORK_DIR/mnt"
-test -e "$WORK_DIR/mnt/foo.bar"
-systemctl show -P Options "$WORK_DIR/mnt" | grep -Eq "(^ro|,ro)"
-systemctl show -P Options "$WORK_DIR/mnt" | grep -q "x-foo-bar"
-systemd-umount "$WORK_DIR/mnt"
-
-# Mount with only source set
-systemd-mount "$LOOP"
-systemctl status /run/media/system/sd-mount-test
-systemd-mount --list --full
-test -e /run/media/system/sd-mount-test/foo.bar
-systemd-umount LABEL=sd-mount-test
-
-# Automount
-systemd-mount --automount=yes "$LOOP" "$WORK_DIR/mnt"
-systemd-mount --list --full
-systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount"
-[[ "$(systemctl show -P ActiveState "$WORK_DIR/mnt")" == inactive ]]
-test -e "$WORK_DIR/mnt/foo.bar"
-systemctl status "$WORK_DIR/mnt"
-systemd-umount "$WORK_DIR/mnt"
-# Automount + automount-specific property
-systemd-mount -A --automount-property="Description=Bar Baz" "$LOOP" "$WORK_DIR/mnt"
-systemctl show -P Description "$(systemd-escape --path "$WORK_DIR/mnt").automount" | grep -q "Bar Baz"
-test -e "$WORK_DIR/mnt/foo.bar"
-# Call --umount via --machine=, first with a relative path (bad) and then with
-# an absolute one (good)
-(! systemd-umount --machine=.host "$(realpath --relative-to=. "$WORK_DIR/mnt")")
-systemd-umount --machine=.host "$WORK_DIR/mnt"
-
-# ext4 doesn't support uid=/gid=
-(! systemd-mount -t ext4 --owner=testuser "$LOOP" "$WORK_DIR/mnt")
-
-# Automount + --bind-device
-systemd-mount --automount=yes --bind-device --timeout-idle-sec=1 "$LOOP" "$WORK_DIR/mnt"
-systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount"
-# Trigger the automount
-test -e "$WORK_DIR/mnt/foo.bar"
-# Wait until it's idle again
-sleep 1.5
-# Safety net for slower/overloaded systems
-timeout 10s bash -c "while systemctl is-active -q $WORK_DIR/mnt; do sleep .2; done"
-systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount"
-# Disassemble the underlying block device
-losetup -d "$LOOP"
-unset LOOP
-# The automount unit should disappear once the underlying blockdev is gone
-timeout 10s bash -c "while systemctl status '$(systemd-escape --path "$WORK_DIR/mnt".automount)'; do sleep .2; done"
-
-# Mount a disk image
-systemd-mount --discover "$WORK_DIR/simple.img"
-# We can access files in the image even if the loopback block device is not initialized by udevd.
-test -e /run/media/system/simple.img/foo.bar
-# systemd-mount --list and systemd-umount require the loopback block device is initialized by udevd.
-udevadm settle --timeout 30
-assert_in "/dev/loop.* ext4 +sd-mount-test" "$(systemd-mount --list --full)"
-systemd-umount "$WORK_DIR/simple.img"
-
-# --owner + vfat
-#
-# Create a vfat image, as ext4 doesn't support uid=/gid= fixating for all
-# files/directories
-dd if=/dev/zero of="$WORK_DIR/owner-vfat.img" bs=1M count=16
-mkfs.vfat -n owner-vfat "$WORK_DIR/owner-vfat.img"
-LOOP="$(losetup --show --find "$WORK_DIR/owner-vfat.img")"
-udevadm wait --timeout 60 --settle "$LOOP"
-# Mount it and check the UID/GID
-[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt")" == "root:root" ]]
-systemd-mount --owner=testuser "$LOOP" "$WORK_DIR/mnt"
-systemctl status "$WORK_DIR/mnt"
-[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt")" == "testuser:testuser" ]]
-touch "$WORK_DIR/mnt/hello"
-[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt/hello")" == "testuser:testuser" ]]
-systemd-umount LABEL=owner-vfat
-
-# tmpfs
-mkdir -p "$WORK_DIR/mnt/foo/bar"
-systemd-mount --tmpfs "$WORK_DIR/mnt/foo"
-test ! -d "$WORK_DIR/mnt/foo/bar"
-touch "$WORK_DIR/mnt/foo/baz"
-systemd-umount "$WORK_DIR/mnt/foo"
-test -d "$WORK_DIR/mnt/foo/bar"
-test ! -e "$WORK_DIR/mnt/foo/baz"
-
-# overlay
-systemd-mount --type=overlay --options="lowerdir=/etc,upperdir=$WORK_DIR/upper,workdir=$WORK_DIR/work" /etc "$WORK_DIR/overlay"
-touch "$WORK_DIR/overlay/foo"
-test -e "$WORK_DIR/upper/foo"
-systemd-umount "$WORK_DIR/overlay"
diff --git a/test/units/testsuite-74.network-generator.sh b/test/units/testsuite-74.network-generator.sh
deleted file mode 100755 (executable)
index 5b5b0a1..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-at_exit() {
-    rm -f /run/credstore/network.conf.50-testme
-    rm -f /run/credstore/network.network.50-testme
-    rm -f /run/systemd/networkd.conf.d/50-testme.conf
-    rm -f /run/systemd/network/50-testme.network
-    rm -f /run/systemd/system/systemd-network-generator.service.d/50-testme.conf
-}
-
-trap at_exit EXIT
-
-mkdir -p /run/credstore
-cat > /run/credstore/network.conf.50-testme <<EOF
-[Network]
-SpeedMeter=yes
-EOF
-
-cat > /run/credstore/network.network.50-testme <<EOF
-[Match]
-Property=IDONTEXIST
-EOF
-
-systemctl edit systemd-network-generator.service --stdin --drop-in=50-testme.conf <<EOF
-[Service]
-LoadCredential=network.conf.50-testme
-LoadCredential=network.network.50-testme
-EOF
-
-systemctl restart systemd-network-generator
-
-diff /run/credstore/network.conf.50-testme /run/systemd/networkd.conf.d/50-testme.conf
-diff /run/credstore/network.network.50-testme /run/systemd/network/50-testme.network
diff --git a/test/units/testsuite-74.networkctl.sh b/test/units/testsuite-74.networkctl.sh
deleted file mode 100755 (executable)
index 3e333a2..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-at_exit() {
-    systemctl stop systemd-networkd
-
-    if [[ -v NETWORK_NAME && -v NETDEV_NAME && -v LINK_NAME ]]; then
-        rm -fvr {/usr/lib,/etc,/run}/systemd/network/"$NETWORK_NAME" "/usr/lib/systemd/network/$NETDEV_NAME" \
-            {/usr/lib,/etc}/systemd/network/"$LINK_NAME" "/etc/systemd/network/${NETWORK_NAME}.d" \
-            "new" "+4"
-    fi
-
-    rm -f /run/systemd/networkd.conf.d/10-hoge.conf
-}
-
-trap at_exit EXIT
-
-export NETWORK_NAME="10-networkctl-test-$RANDOM.network"
-export NETDEV_NAME="10-networkctl-test-$RANDOM.netdev"
-export LINK_NAME="10-networkctl-test-$RANDOM.link"
-cat >"/usr/lib/systemd/network/$NETWORK_NAME" <<EOF
-[Match]
-Name=test
-EOF
-
-# Test files
-
-networkctl mask --runtime "donotexist.network"
-assert_eq "$(readlink /run/systemd/network/donotexist.network)" "/dev/null"
-networkctl unmask "donotexist.network" # unmask should work even without --runtime
-[[ ! -e /run/systemd/network/donotexist.network ]]
-
-touch /usr/lib/systemd/network/donotexist.network
-(! networkctl unmask "donotexist.network")
-rm /usr/lib/systemd/network/donotexist.network
-
-networkctl cat "$NETWORK_NAME" | tail -n +2 | cmp - "/usr/lib/systemd/network/$NETWORK_NAME"
-
-cat >new <<EOF
-[Match]
-Name=test2
-EOF
-
-EDITOR='mv new' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null
-(! networkctl mask --runtime "$NETWORK_NAME")
-printf '%s\n' '[Match]' 'Name=test2' | cmp - "/run/systemd/network/$NETWORK_NAME"
-
-networkctl mask "$NETWORK_NAME"
-assert_eq "$(readlink "/etc/systemd/network/$NETWORK_NAME")" "/dev/null"
-(! networkctl edit "$NETWORK_NAME")
-(! networkctl edit --runtime "$NETWORK_NAME")
-(! networkctl cat "$NETWORK_NAME")
-networkctl unmask "$NETWORK_NAME"
-
-EDITOR='true' script -ec 'networkctl edit "$NETWORK_NAME"' /dev/null
-printf '%s\n' '[Match]' 'Name=test2' | cmp - "/etc/systemd/network/$NETWORK_NAME"
-
-(! networkctl mask "$NETWORK_NAME")
-(! EDITOR='true' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null)
-
-cat >"+4" <<EOF
-[Network]
-IPv6AcceptRA=no
-EOF
-
-EDITOR='cp' script -ec 'networkctl edit "$NETWORK_NAME" --drop-in test' /dev/null
-cmp "+4" "/etc/systemd/network/${NETWORK_NAME}.d/test.conf"
-
-networkctl cat "$NETWORK_NAME" | grep '^# ' |
-    cmp - <(printf '%s\n' "# /etc/systemd/network/$NETWORK_NAME" "# /etc/systemd/network/${NETWORK_NAME}.d/test.conf")
-
-cat >"/usr/lib/systemd/network/$NETDEV_NAME" <<EOF
-[NetDev]
-Name=test2
-Kind=dummy
-EOF
-
-networkctl cat "$NETDEV_NAME"
-
-cat >"/usr/lib/systemd/network/$LINK_NAME" <<EOF
-[Match]
-OriginalName=test2
-
-[Link]
-Alias=test_alias
-EOF
-
-SYSTEMD_LOG_LEVEL=debug EDITOR='true' script -ec 'networkctl edit "$LINK_NAME"' /dev/null
-cmp "/usr/lib/systemd/network/$LINK_NAME" "/etc/systemd/network/$LINK_NAME"
-
-# Test links
-systemctl unmask systemd-networkd
-systemctl stop systemd-networkd
-(! networkctl cat @test2)
-
-systemctl start systemd-networkd
-SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-networkd-wait-online -i test2:carrier --timeout 20
-networkctl cat @test2:network | cmp - <(networkctl cat "$NETWORK_NAME")
-
-EDITOR='cp' script -ec 'networkctl edit @test2 --drop-in test2.conf' /dev/null
-cmp "+4" "/etc/systemd/network/${NETWORK_NAME}.d/test2.conf"
-
-SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-networkd-wait-online -i test2:carrier --timeout 20
-(! EDITOR='true' script -ec 'networkctl edit @test2 --runtime --drop-in test2.conf' /dev/null)
-
-ip_link="$(ip link show test2)"
-if systemctl --quiet is-active systemd-udevd; then
-    assert_in 'alias test_alias' "$ip_link"
-fi
-
-mkdir -p /run/systemd/networkd.conf.d
-cat >/run/systemd/networkd.conf.d/10-hoge.conf <<EOF
-# TEST DROP-IN FILE
-[Network]
-SpeedMeter=yes
-EOF
-
-assert_in '# TEST DROP-IN FILE' "$(networkctl cat)"
diff --git a/test/units/testsuite-74.path.sh b/test/units/testsuite-74.path.sh
deleted file mode 100755 (executable)
index 79056a5..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-USER_DIRS_CONF="/root/.config/user-dirs.dirs"
-
-at_exit() {
-    set +e
-
-    rm -fv "${USER_DIRS_CONF:?}"
-}
-
-trap at_exit EXIT
-
-# Check that we indeed run under root to make the rest of the test work
-[[ "$(id -u)" -eq 0 ]]
-
-# Create a custom user-dirs.dir file to exercise the xdg-user-dirs part
-# of sd-path/from_user_dir()
-mkdir -p "/root/.config"
-cat >"${USER_DIRS_CONF:?}" <<\EOF
-XDG_DESKTOP_DIR="$HOME/my-fancy-desktop"
-XDG_INVALID
-
-XDG_DOWNLOAD_DIR   = "$HOME"
-XDG_TEMPLATES_DIR="/templates"
-# Invalid records
-XDG_TEMPLATES_DIR=/not-templates"
-XDG_TEMPLATES_DIR="/also-not-teplates
-XDG_TEMPLATES_DIR=""
-XDG_TEMPLATES_DIR="../"
-
-XDG_PUBLICSHARE_DIR="$HOME/cat-pictures"
-XDG_DOCUMENTS_DIR="$HOME/top/secret/documents"
-XDG_MUSIC_DIR="/tmp/vaporwave"
-XDG_PICTURES_DIR="$HOME/Pictures"
-XDG_VIDEOS_DIR="$HOME/🤔"
-EOF
-
-systemd-path --help
-systemd-path --version
-systemd-path
-systemd-path temporary system-binaries user binfmt
-
-assert_eq "$(systemd-path system-runtime)" "/run"
-assert_eq "$(systemd-path --suffix='' system-runtime)" "/run"
-assert_eq "$(systemd-path --suffix='🤔' system-runtime)" "/run/🤔"
-assert_eq "$(systemd-path --suffix=hello system-runtime)" "/run/hello"
-
-# Note for the stuff below: everything defaults to $HOME, only the desktop
-# directory defaults to $HOME/Desktop.
-#
-# Check the user-dirs.dir stuff from above
-assert_eq "$(systemd-path user)" "/root"
-assert_eq "$(systemd-path user-desktop)" "/root/my-fancy-desktop"
-assert_eq "$(systemd-path user-documents)" "/root/top/secret/documents"
-assert_eq "$(systemd-path user-download)" "/root"
-assert_eq "$(systemd-path user-music)" "/tmp/vaporwave"
-assert_eq "$(systemd-path user-pictures)" "/root/Pictures"
-assert_eq "$(systemd-path user-public)" "/root/cat-pictures"
-assert_eq "$(systemd-path user-templates)" "/templates"
-assert_eq "$(systemd-path user-videos)" "/root/🤔"
-
-# Remove the user-dirs.dir file and check the defaults
-rm -fv "$USER_DIRS_CONF"
-[[ ! -e "$USER_DIRS_CONF" ]]
-assert_eq "$(systemd-path user-desktop)" "/root/Desktop"
-for dir in "" documents download music pictures public templates videos; do
-    assert_eq "$(systemd-path "user${dir:+-$dir}")" "/root"
-done
-
-# sd-path should consider only absolute $HOME
-assert_eq "$(HOME=/hello-world systemd-path user)" "/hello-world"
-assert_eq "$(HOME=hello-world systemd-path user)" "/root"
-assert_eq "$(HOME=/hello systemd-path --suffix=world user)" "/hello/world"
-assert_eq "$(HOME=hello systemd-path --suffix=world user)" "/root/world"
-# Same with some other env variables
-assert_in "/my-config" "$(HOME='' XDG_CONFIG_HOME=/my-config systemd-path search-configuration)"
-assert_in "/my-config/foo" "$(HOME='' XDG_CONFIG_HOME=/my-config systemd-path --suffix=foo search-configuration)"
-assert_in "/my-home/.config/foo" "$(HOME=/my-home XDG_CONFIG_HOME=my-config systemd-path --suffix=foo search-configuration)"
-assert_not_in "my-config" "$(HOME=my-config XDG_CONFIG_HOME=my-config systemd-path search-configuration)"
-
-(! systemd-path '')
-(! systemd-path system-binaries 🤔 user)
-(! systemd-path --xyz)
diff --git a/test/units/testsuite-74.pstore.sh b/test/units/testsuite-74.pstore.sh
deleted file mode 100755 (executable)
index 9be8066..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-systemctl log-level info
-
-if systemd-detect-virt -cq; then
-    echo "Running in a container, skipping the systemd-pstore test..."
-    exit 0
-fi
-
-DUMMY_DMESG_0="$(mktemp)"
-cat >"$DUMMY_DMESG_0" <<\EOF
-6,17159,5340096332127,-;usb 1-4: USB disconnect, device number 124
-6,17160,5340109662397,-;input: WH-1000XM3 (AVRCP) as /devices/virtual/input/input293
-6,17161,5343126458360,-;loop0: detected capacity change from 0 to 3145728
-6,17162,5343126766065,-; loop0: p1 p2
-6,17163,5343126815038,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none.
-6,17164,5343158037334,-;EXT4-fs (loop0p1): unmounting filesystem.
-6,17165,5343158072598,-;loop0: detected capacity change from 0 to 3145728
-6,17166,5343158073563,-; loop0: p1 p2
-6,17167,5343158074325,-; loop0: p1 p2
-6,17168,5343158140859,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none.
-6,17169,5343158182977,-;EXT4-fs (loop0p1): unmounting filesystem.
-6,17170,5343158700241,-;loop0: detected capacity change from 0 to 3145728
-6,17171,5343158700439,-; loop0: p1 p2
-6,17172,5343158701120,-; loop0: p1 p2
-EOF
-
-DUMMY_DMESG_1="$(mktemp)"
-cat >"$DUMMY_DMESG_1" <<\EOF
-Nechť již hříšné saxofony ďáblů rozezvučí síň úděsnými tóny waltzu, tanga a quickstepu.
-Příliš žluťoučký kůň úpěl ďábelské ódy.
-Zvlášť zákeřný učeň s ďolíčky běží podél zóny úlů.
-Vyciď křišťálový nůž, ó učiň úděsné líbivým!
-Loď čeří kýlem tůň obzvlášť v Grónské úžině
-Ó, náhlý déšť již zvířil prach a čilá laň teď běží s houfcem gazel k úkrytům.
-Vypätá dcéra grófa Maxwella s IQ nižším ako kôň núti čeľaď hrýzť hŕbu jabĺk.
-Kŕdeľ šťastných ďatľov učí pri ústí Váhu mĺkveho koňa obhrýzať kôru a žrať čerstvé mäso.
-Stróż pchnął kość w quiz gędźb vel fax myjń.
-Portez ce vieux whisky au juge blond qui fume!
-EOF
-
-file_count() { find "${1:?}" -type f | wc -l; }
-file_size() { wc -l <"${1:?}"; }
-random_efi_timestamp() { printf "%0.10d" "$((1000000000 + RANDOM))"; }
-
-# The dmesg- filename contains the backend-type and the Common Platform Error Record, CPER,
-# record id, a 64-bit number.
-#
-# Files are processed in reverse lexigraphical order so as to properly reconstruct original dmesg.
-
-prepare_efi_logs() {
-    local file="${1:?}"
-    local timestamp="${2:?}"
-    local chunk count filename
-
-    # For the EFI backend, the 3 least significant digits of record id encodes a
-    # "count" number, the next 2 least significant digits for the dmesg part
-    # (chunk) number, and the remaining digits as the timestamp.  See
-    # linux/drivers/firmware/efi/efi-pstore.c in efi_pstore_write().
-    count="$(file_size "$file")"
-    chunk=0
-    # The sed in the process substitution below just reverses the file
-    while read -r line; do
-        filename="$(printf "dmesg-efi-%0.10d%0.2d%0.3d" "$timestamp" "$chunk" "$count")"
-        echo "$line" >"/sys/fs/pstore/$filename"
-        chunk=$((chunk + 1))
-    done < <(sed '1!G;h;$!d' "$file")
-
-    if [[ "$chunk" -eq 0 ]]; then
-        echo >&2 "No dmesg-efi files were created"
-        exit 1
-    fi
-}
-
-prepare_erst_logs() {
-    local file="${1:?}"
-    local start_id="${2:?}"
-    local id filename
-
-    # For the ERST backend, the record is a monotonically increasing number, seeded as
-    # a timestamp. See linux/drivers/acpi/apei/erst.c in erst_writer().
-    id="$start_id"
-    # The sed in the process substitution below just reverses the file
-    while read -r line; do
-        filename="$(printf "dmesg-erst-%0.16d" "$id")"
-        echo "$line" >"/sys/fs/pstore/$filename"
-        id=$((id + 1))
-    done < <(sed '1!G;h;$!d' "$file")
-
-    if [[ "$id" -eq "$start_id" ]]; then
-        echo >&2 "No dmesg-erst files were created"
-        exit 1
-    fi
-
-    # ID of the last dmesg file will be the ID of the erst subfolder
-    echo "$((id - 1))"
-}
-
-prepare_pstore_config() {
-    local storage="${1:?}"
-    local unlink="${2:?}"
-
-    systemctl stop systemd-pstore
-
-    rm -fr /sys/fs/pstore/* /var/lib/systemd/pstore/*
-
-    mkdir -p /run/systemd/pstore.conf.d
-    cat >/run/systemd/pstore.conf.d/99-test.conf <<EOF
-[PStore]
-Storage=$storage
-Unlink=$unlink
-EOF
-
-    systemd-analyze cat-config systemd/pstore.conf | grep "$storage"
-    systemd-analyze cat-config systemd/pstore.conf | grep "$unlink"
-}
-
-start_pstore() {
-    rm -f /tmp/journal.cursor
-    journalctl -q -n 0 --cursor-file=/tmp/journal.cursor
-    systemctl restart systemd-pstore
-    journalctl --sync
-}
-
-at_exit() {
-    set +e
-
-    mountpoint -q /sys/fs/pstore && umount /sys/fs/pstore
-    rm -fr /var/lib/systemd/pstore/*
-    rm -f /run/systemd/system/systemd-pstore.service.d/99-StartLimitInterval.conf
-    rm -f /run/systemd/pstore.conf.d/99-test.conf
-}
-
-trap at_exit EXIT
-
-# To avoid having to depend on the VM providing the pstore, let's simulate
-# it using a simple bind mount
-PSTORE_DIR="$(mktemp -d)"
-mount --bind "${PSTORE_DIR:?}" "/sys/fs/pstore"
-
-# Disable the start limit since we're going to restart the systemd-pstore
-# service quite a lot in a short time span
-mkdir -p /run/systemd/system/systemd-pstore.service.d
-cat >/run/systemd/system/systemd-pstore.service.d/99-StartLimitInterval.conf <<EOF
-[Unit]
-StartLimitInterval=0
-EOF
-systemctl daemon-reload
-
-# systemd-pstore is a no-op with Storage=none
-for unlink in yes no; do
-    : "Backend: N/A; Storage: none; Unlink: $unlink"
-    timestamp="$(random_efi_timestamp)"
-    prepare_pstore_config "none" "$unlink"
-    prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp"
-    old_count="$(file_count /sys/fs/pstore/)"
-    start_pstore
-    [[ "$(file_count /sys/fs/pstore)" -ge "$old_count" ]]
-    [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
-
-    : "Backend: EFI; Storage: external; Unlink: $unlink"
-    timestamp="$(random_efi_timestamp)"
-    prepare_pstore_config "external" "$unlink"
-    prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp"
-    [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
-    start_pstore
-    [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
-    [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
-    # We always log to journal
-    diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
-    filename="$(printf "/var/lib/systemd/pstore/%s/%0.3d/dmesg.txt" "$timestamp" "$(file_size "$DUMMY_DMESG_0")")"
-    diff "$DUMMY_DMESG_0" "$filename"
-
-    : "Backend: EFI; Storage: external; Unlink: $unlink; multiple dmesg files"
-    timestamps=()
-    timestamp="$(random_efi_timestamp)"
-    prepare_pstore_config "external" "$unlink"
-    for i in {0..6}; do
-        timestamp="$((timestamp + (i * 10)))"
-        timestamps+=("$timestamp")
-        # Create a name reference to one of the $DUMMY_DMESG_X variables
-        dmesg="DUMMY_DMESG_$((i % 2))"
-        prepare_efi_logs "${!dmesg}" "$timestamp"
-        # Add one "random" (non-dmesg) file as well
-        echo "hello world" >/sys/fs/pstore/foo.bar
-        [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
-        start_pstore
-        [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
-        [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
-        filename="$(printf "/var/lib/systemd/pstore/%s/%0.3d/dmesg.txt" "$timestamp" "$(file_size "${!dmesg}")")"
-        diff "${!dmesg}" "$filename"
-        grep "hello world" "/var/lib/systemd/pstore/foo.bar"
-    done
-    # Check that we kept all previous records as well
-    for timestamp in "${timestamps[@]}"; do
-        [[ -d "/var/lib/systemd/pstore/$timestamp" ]]
-        [[ "$(file_count "/var/lib/systemd/pstore/$timestamp/")" -gt 0 ]]
-    done
-
-    : "Backend: EFI; Storage: journal; Unlink: $unlink"
-    timestamp="$(random_efi_timestamp)"
-    prepare_pstore_config "journal" "$unlink"
-    prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp"
-    [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
-    start_pstore
-    [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
-    [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
-    diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
-
-    : "Backend: ERST; Storage: external; Unlink: $unlink"
-    prepare_pstore_config "external" "$unlink"
-    last_id="$(prepare_erst_logs "$DUMMY_DMESG_0" 0)"
-    [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
-    start_pstore
-    [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
-    [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
-    # We always log to journal
-    diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
-    filename="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id")"
-    diff "$DUMMY_DMESG_0" "$filename"
-
-    : "Backend: ERST; Storage: external; Unlink: $unlink; multiple dmesg files"
-    last_ids=()
-    prepare_pstore_config "external" "$unlink"
-    for i in {0..9}; do
-        # Create a name reference to one of the $DUMMY_DMESG_X variables
-        dmesg="DUMMY_DMESG_$((i % 2))"
-        last_id="$(prepare_erst_logs "${!dmesg}" "$((i * 100))")"
-        last_ids+=("$last_id")
-        # Add one "random" (non-dmesg) file as well
-        echo "hello world" >/sys/fs/pstore/foo.bar
-        [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
-        start_pstore
-        [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
-        [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
-        filename="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id")"
-        diff "${!dmesg}" "$filename"
-        grep "hello world" "/var/lib/systemd/pstore/foo.bar"
-    done
-    # Check that we kept all previous records as well
-    for last_id in "${last_ids[@]}"; do
-        directory="$(printf "/var/lib/systemd/pstore/%0.16d" "$last_id")"
-        [[ -d "$directory" ]]
-        [[ "$(file_count "$directory")" -gt 0 ]]
-    done
-
-    : "Backend: ERST; Storage: journal; Unlink: $unlink"
-    prepare_pstore_config "journal" "$unlink"
-    last_id="$(prepare_erst_logs "$DUMMY_DMESG_0" 0)"
-    [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
-    start_pstore
-    [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
-    [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
-    diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d")
-done
diff --git a/test/units/testsuite-74.run.sh b/test/units/testsuite-74.run.sh
deleted file mode 100755 (executable)
index b4ff72e..0000000
+++ /dev/null
@@ -1,247 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-systemd-run --help --no-pager
-systemd-run --version
-systemd-run --no-ask-password true
-systemd-run --no-block --collect true
-
-export PARENT_FOO=bar
-touch /tmp/public-marker
-
-: "Transient service (system daemon)"
-systemd-run --wait --pipe \
-            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.service$ ]]'
-systemd-run --wait --pipe --system \
-            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.service$ ]]'
-systemd-run --wait --pipe --slice=foo \
-            bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.service$ ]]'
-systemd-run --wait --pipe --slice=foo.slice \
-            bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.service$ ]]'
-systemd-run --wait --pipe --slice-inherit \
-            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.service$ ]]'
-systemd-run --wait --pipe --slice-inherit --slice=foo \
-            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/system-foo\.slice/run-.+\.service$ ]]'
-# We should not inherit caller's environment
-systemd-run --wait --pipe bash -xec '[[ -z "$PARENT_FOO" ]]'
-systemd-run --wait --pipe bash -xec '[[ "$PWD" == / && -n "$INVOCATION_ID" ]]'
-systemd-run --wait --pipe \
-            --send-sighup \
-            --working-directory="" \
-            --working-directory=/tmp \
-            bash -xec '[[ "$PWD" == /tmp ]]'
-systemd-run --wait --pipe --same-dir bash -xec "[[ \"\$PWD\" == $PWD ]]"
-systemd-run --wait --pipe \
-            --property=LimitCORE=1M:2M \
-            --property=LimitCORE=16M:32M \
-            --property=PrivateTmp=yes \
-            bash -xec '[[ "$(ulimit -c -S)" -eq 16384 && "$(ulimit -c -H)" -eq 32768 && ! -e /tmp/public-marker ]]'
-systemd-run --wait --pipe \
-            --uid=testuser \
-            bash -xec '[[ "$(id -nu)" == testuser && "$(id -ng)" == testuser ]]'
-systemd-run --wait --pipe \
-            --gid=testuser \
-            bash -xec '[[ "$(id -nu)" == root && "$(id -ng)" == testuser ]]'
-systemd-run --wait --pipe \
-            --uid=testuser \
-            --gid=root \
-            bash -xec '[[ "$(id -nu)" == testuser && "$(id -ng)" == root ]]'
-systemd-run --wait --pipe --expand-environment=no \
-            --nice=10 \
-            bash -xec 'read -r -a SELF_STAT </proc/self/stat && [[ "${SELF_STAT[18]}" -eq 10 ]]'
-systemd-run --wait --pipe \
-            --setenv=ENV_HELLO="nope" \
-            --setenv=ENV_HELLO="env world" \
-            --setenv=EMPTY= \
-            --setenv=PARENT_FOO \
-            --property=Environment="ALSO_HELLO='also world'" \
-            bash -xec '[[ "$ENV_HELLO" == "env world" && -z "$EMPTY" && "$PARENT_FOO" == bar && "$ALSO_HELLO" == "also world" ]]'
-
-UNIT="service-0-$RANDOM"
-systemd-run --remain-after-exit --unit="$UNIT" \
-            --service-type=simple \
-            --service-type=oneshot \
-            true
-systemctl cat "$UNIT"
-grep -q "^Type=oneshot" "/run/systemd/transient/$UNIT.service"
-systemctl stop "$UNIT"
-(! systemctl cat "$UNIT")
-
-: "Transient service (user daemon)"
-systemd-run --wait --pipe --user --machine=testuser@ \
-            bash -xec '[[ "$(</proc/self/cgroup)" =~ /user\.slice/.+/run-.+\.service$ ]]'
-systemd-run --wait --pipe --user --machine=testuser@ \
-            bash -xec '[[ "$(id -nu)" == testuser && "$(id -ng)" == testuser ]]'
-systemd-run --wait --pipe --user --machine=testuser@ \
-            bash -xec '[[ "$PWD" == /home/testuser && -n "$INVOCATION_ID" ]]'
-
-# PrivateTmp=yes implies PrivateUsers=yes for user manager, so skip this if we
-# don't have unprivileged user namespaces.
-if [[ "$(sysctl -ne kernel.apparmor_restrict_unprivileged_userns)" -ne 1 ]]; then
-    systemd-run --wait --pipe --user --machine=testuser@ \
-                --property=LimitCORE=1M:2M \
-                --property=LimitCORE=16M:32M \
-                --property=PrivateTmp=yes \
-                bash -xec '[[ "$(ulimit -c -S)" -eq 16384 && "$(ulimit -c -H)" -eq 32768 && ! -e /tmp/public-marker ]]'
-fi
-
-: "Transient scope (system daemon)"
-systemd-run --scope \
-            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.scope$ ]]'
-systemd-run --scope --system \
-            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.scope$ ]]'
-systemd-run --scope --slice=foo \
-            bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.scope$ ]]'
-systemd-run --scope --slice=foo.slice \
-            bash -xec '[[ "$(</proc/self/cgroup)" =~ /foo\.slice/run-.+\.scope$ ]]'
-systemd-run --scope --slice-inherit \
-            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/run-.+\.scope$ ]]'
-systemd-run --scope --slice-inherit --slice=foo \
-            bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/system-foo\.slice/run-.+\.scope$ ]]'
-# We should inherit caller's environment
-systemd-run --scope bash -xec '[[ "$PARENT_FOO" == bar ]]'
-systemd-run --scope \
-            --property=RuntimeMaxSec=10 \
-            --property=RuntimeMaxSec=infinity \
-            true
-
-: "Transient scope (user daemon)"
-# FIXME: https://github.com/systemd/systemd/issues/27883
-#systemd-run --scope --user --machine=testuser@ \
-#            bash -xec '[[ "$(</proc/self/cgroup)" =~ /user\.slice/run-.+\.scope$ ]]'
-# We should inherit caller's environment
-#systemd-run --scope --user --machine=testuser@ bash -xec '[[ "$PARENT_FOO" == bar ]]'
-
-: "Transient timer unit"
-UNIT="timer-0-$RANDOM"
-systemd-run --remain-after-exit \
-            --unit="$UNIT" \
-            --timer-property=OnUnitInactiveSec=16h \
-            true
-systemctl cat "$UNIT.service" "$UNIT.timer"
-grep -q "^OnUnitInactiveSec=16h$" "/run/systemd/transient/$UNIT.timer"
-grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
-systemctl stop "$UNIT.timer" "$UNIT.service" || :
-
-UNIT="timer-1-$RANDOM"
-systemd-run --remain-after-exit \
-            --unit="$UNIT" \
-            --on-active=10 \
-            --on-active=30s \
-            --on-boot=1s \
-            --on-startup=2m \
-            --on-unit-active=3h20m \
-            --on-unit-inactive="5d 4m 32s" \
-            --on-calendar="mon,fri *-1/2-1,3 *:30:45" \
-            --on-clock-change \
-            --on-clock-change \
-            --on-timezone-change \
-            --timer-property=After=systemd-journald.service \
-            --description="Hello world" \
-            --description="My Fancy Timer" \
-            true
-systemctl cat "$UNIT.service" "$UNIT.timer"
-systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.service"
-systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.timer"
-grep -q "^Description=My Fancy Timer$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnActiveSec=10s$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnActiveSec=30s$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnBootSec=1s$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnStartupSec=2min$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnUnitActiveSec=3h 20min$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnUnitInactiveSec=5d 4min 32s$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnCalendar=mon,fri \*\-1/2\-1,3 \*:30:45$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnClockChange=yes$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^OnTimezoneChange=yes$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^After=systemd-journald.service$" "/run/systemd/transient/$UNIT.timer"
-grep -q "^Description=My Fancy Timer$" "/run/systemd/transient/$UNIT.service"
-grep -q "^RemainAfterExit=yes$" "/run/systemd/transient/$UNIT.service"
-grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
-(! grep -q "^After=systemd-journald.service$" "/run/systemd/transient/$UNIT.service")
-systemctl stop "$UNIT.timer" "$UNIT.service" || :
-
-: "Transient path unit"
-UNIT="path-0-$RANDOM"
-systemd-run --remain-after-exit \
-            --unit="$UNIT" \
-            --path-property=PathExists=/tmp \
-            --path-property=PathExists=/tmp/foo \
-            --path-property=PathChanged=/root/bar \
-            true
-systemctl cat "$UNIT.service" "$UNIT.path"
-systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.service"
-systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.path"
-grep -q "^PathExists=/tmp$" "/run/systemd/transient/$UNIT.path"
-grep -q "^PathExists=/tmp/foo$" "/run/systemd/transient/$UNIT.path"
-grep -q "^PathChanged=/root/bar$" "/run/systemd/transient/$UNIT.path"
-grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
-systemctl stop "$UNIT.path" "$UNIT.service" || :
-
-: "Transient socket unit"
-UNIT="socket-0-$RANDOM"
-systemd-run --remain-after-exit \
-            --unit="$UNIT" \
-            --socket-property=ListenFIFO=/tmp/socket.fifo \
-            --socket-property=SocketMode=0666 \
-            --socket-property=SocketMode=0644 \
-            true
-systemctl cat "$UNIT.service" "$UNIT.socket"
-systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.service"
-systemd-analyze verify --recursive-errors=no "/run/systemd/transient/$UNIT.socket"
-grep -q "^ListenFIFO=/tmp/socket.fifo$" "/run/systemd/transient/$UNIT.socket"
-grep -q "^SocketMode=0666$" "/run/systemd/transient/$UNIT.socket"
-grep -q "^SocketMode=0644$" "/run/systemd/transient/$UNIT.socket"
-grep -qE "^ExecStart=.*/bin/true.*$" "/run/systemd/transient/$UNIT.service"
-systemctl stop "$UNIT.socket" "$UNIT.service" || :
-
-: "Interactive options"
-SHELL=/bin/true systemd-run --shell
-SHELL=/bin/true systemd-run --scope --shell
-systemd-run --wait --pty true
-systemd-run --wait --machine=.host --pty true
-(! SHELL=/bin/false systemd-run --quiet --shell)
-
-(! systemd-run)
-(! systemd-run "")
-(! systemd-run --foo=bar)
-(! systemd-run --wait --pipe --slice=foo.service true)
-
-for opt in nice on-{active,boot,calendar,startup,unit-active,unit-inactive} property service-type setenv; do
-    (! systemd-run "--$opt=" true)
-    (! systemd-run "--$opt=''" true)
-done
-
-# Let's make sure that ProtectProc= properly moves submounts of the original /proc over to the new proc
-BOOT_ID="$(</proc/sys/kernel/random/boot_id)"
-UNIT_BOOT_ID="$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/sys/kernel/random/boot_id)"
-assert_eq "$BOOT_ID" "$UNIT_BOOT_ID"
-
-TMP_KVER="/tmp/version.$RANDOM"
-KVER="$(</proc/version).piff"
-echo "$KVER" >"$TMP_KVER"
-mount --bind "$TMP_KVER" /proc/version
-UNIT_KVER="$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/version)"
-assert_eq "$KVER" "$UNIT_KVER"
-umount /proc/version
-rm -f "$TMP_KVER"
-
-if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; then
-    # Check that invoking the tool under the run0 alias name works
-    run0 ls /
-    assert_eq "$(run0 echo foo)" "foo"
-    # Check if we set some expected environment variables
-    for arg in "" "--user=root" "--user=testuser"; do
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER"
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
-        assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
-    done
-    # Let's chain a couple of run0 calls together, for fun
-    readarray -t cmdline < <(printf "%.0srun0\n" {0..31})
-    assert_eq "$("${cmdline[@]}" bash -c 'echo $SUDO_USER')" "$USER"
-fi
diff --git a/test/units/testsuite-74.service b/test/units/testsuite-74.service
deleted file mode 100644 (file)
index f782132..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-74-AUX-UTILS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-74.sh b/test/units/testsuite-74.sh
deleted file mode 100755 (executable)
index 9c2a033..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
diff --git a/test/units/testsuite-74.socket.sh b/test/units/testsuite-74.socket.sh
deleted file mode 100755 (executable)
index 7ef85fa..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-at_exit() {
-    systemctl stop per-source-limit.socket
-    rm -f /run/systemd/system/per-source-limit{@.service,.socket} /run/foo.conn{1..4}
-    systemctl daemon-reload
-}
-
-trap at_exit EXIT
-
-cat >/run/systemd/system/per-source-limit.socket <<EOF
-[Socket]
-ListenStream=/run/per-source-limit.sk
-MaxConnectionsPerSource=2
-Accept=yes
-EOF
-
-cat >/run/systemd/system/per-source-limit@.service <<EOF
-[Unit]
-BindsTo=per-source-limit.socket
-After=per-source-limit.socket
-
-[Service]
-ExecStartPre=echo waldo
-ExecStart=sleep infinity
-StandardOutput=socket
-EOF
-
-systemctl daemon-reload
-systemctl start per-source-limit.socket
-systemctl status per-source-limit.socket
-
-# So these two should take up the first two connection slots
-socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn1 &
-J1="$!"
-socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn2 &
-J2="$!"
-
-waitfor() {
-    local file="${1:?}"
-
-    for _ in {0..20}; do
-        if grep -q waldo "$file"; then
-            return 0
-        fi
-
-        sleep .5
-    done
-
-    echo >&2 "Timeout while waiting for the expected output"
-    return 1
-}
-
-# Wait until the word "waldo" shows in the output files
-waitfor /tmp/foo.conn1
-waitfor /tmp/foo.conn2
-
-# The next connection should fail, because the limit is hit
-socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn3 &
-J3="$!"
-
-# But this one should work, because done under a different UID
-setpriv --reuid=1 socat -U - UNIX-CONNECT:/run/per-source-limit.sk | tee /tmp/foo.conn4 &
-J4="$!"
-
-waitfor /tmp/foo.conn4
-
-# The third job should fail quickly, wait for it
-wait "$J3"
-
-# The other jobs will hang forever, since we run "sleep infinity" on the server side. Let's kill the jobs now.
-kill "$J1"
-kill "$J2"
-kill "$J4"
-
-# The 3rd connection should not have seen "waldo", since it should have been refused too early
-(! grep -q "waldo" /tmp/foo.conn3 )
diff --git a/test/units/testsuite-74.ssh.sh b/test/units/testsuite-74.ssh.sh
deleted file mode 100755 (executable)
index 5d87d9f..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-if ! command -v ssh &> /dev/null || ! command -v sshd &> /dev/null ; then
-    echo "ssh/sshd not found, skipping test." >&2
-    exit 0
-fi
-
-systemctl -q is-active sshd-unix-local.socket
-
-if test -e /dev/vsock ; then
-    systemctl -q is-active sshd-vsock.socket
-fi
-
-if test -d /run/host/unix-export ; then
-    systemctl -q is-active sshd-unix-export.socket
-fi
-
-# FIXME: sshd seems to crash inside asan currently, skip the actual ssh test hence
-if [[ -v ASAN_OPTIONS ]] ; then
-    exit 0
-fi
-
-ROOTID=$(mktemp -u)
-
-removesshid() {
-    rm -f "$ROOTID" "$ROOTID".pub
-}
-
-ssh-keygen -N '' -C '' -t rsa -f "$ROOTID"
-
-mkdir -p 0700 /root/.ssh
-# Add a newline in case authorized_keys wasn't terminated correctly.
-echo >>/root/.ssh/authorized_keys
-cat "$ROOTID".pub >>/root/.ssh/authorized_keys
-
-# set root pw to "foo", just to set it to something valid
-# shellcheck disable=SC2016
-usermod -p '$5$AAy6BYJ6rzz.QELv$6LpVEU3/RQmVz.svHu/33qoJWWWzZuJ3DM2fo9JgcUD' root
-usermod -U root
-
-mkdir -p /etc/ssh
-test -f /etc/ssh/ssh_host_rsa_key || ssh-keygen -t rsa -C '' -N '' -f /etc/ssh/ssh_host_rsa_key
-echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
-echo "LogLevel DEBUG3" >> /etc/ssh/sshd_config
-
-test -f /etc/ssh/ssh_config || echo 'Include /etc/ssh/ssh_config.d/*.conf' > /etc/ssh/ssh_config
-
-# ssh wants this dir around, but distros cannot agree on a common name for it, let's just create all that are aware of distros use
-mkdir -p /usr/share/empty.sshd /var/empty /var/empty/sshd /run/sshd
-
-ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" .host cat /etc/machine-id | cmp - /etc/machine-id
-ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" unix/run/ssh-unix-local/socket cat /etc/machine-id | cmp - /etc/machine-id
-
-modprobe vsock_loopback ||:
-if test -e /dev/vsock -a -d /sys/module/vsock_loopback ; then
-    ssh -o StrictHostKeyChecking=no -v -i "$ROOTID" vsock/1 cat /etc/machine-id | cmp - /etc/machine-id
-fi
diff --git a/test/units/testsuite-74.varlinkctl.sh b/test/units/testsuite-74.varlinkctl.sh
deleted file mode 100755 (executable)
index 7912360..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Unset $PAGER so we don't have to use --no-pager everywhere
-export PAGER=
-
-varlinkctl --help
-varlinkctl help --no-pager
-varlinkctl --version
-varlinkctl --json=help
-
-# TODO: abstract namespace sockets (@...)
-# Path to a socket
-varlinkctl info /run/systemd/journal/io.systemd.journal
-varlinkctl info /run/systemd/../systemd/../../run/systemd/journal/io.systemd.journal
-varlinkctl info "./$(realpath --relative-to="$PWD" /run/systemd/journal/io.systemd.journal)"
-varlinkctl info unix:/run/systemd/journal/io.systemd.journal
-varlinkctl info --json=off /run/systemd/journal/io.systemd.journal
-varlinkctl info --json=pretty /run/systemd/journal/io.systemd.journal | jq .
-varlinkctl info --json=short /run/systemd/journal/io.systemd.journal | jq .
-varlinkctl info -j /run/systemd/journal/io.systemd.journal | jq .
-
-varlinkctl list-interfaces /run/systemd/journal/io.systemd.journal
-varlinkctl list-interfaces -j /run/systemd/journal/io.systemd.journal | jq .
-
-varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal
-varlinkctl introspect -j /run/systemd/journal/io.systemd.journal io.systemd.Journal | jq .
-
-if command -v userdbctl >/dev/null; then
-    systemctl start systemd-userdbd
-    varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }'
-    varlinkctl call -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }' | jq .
-    varlinkctl call --more /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }'
-    varlinkctl call --more -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' | jq --seq .
-    varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }'
-    (! varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' | grep .)
-fi
-
-IDL_FILE="$(mktemp)"
-varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal | tee "${IDL_FILE:?}"
-varlinkctl validate-idl "$IDL_FILE"
-varlinkctl validate-idl "$IDL_FILE"
-cat /bin/sh >"$IDL_FILE"
-(! varlinkctl validate-idl "$IDL_FILE")
-
-if [[ -x /usr/lib/systemd/systemd-pcrextend ]]; then
-    # Path to an executable
-    varlinkctl info /usr/lib/systemd/systemd-pcrextend
-    varlinkctl info exec:/usr/lib/systemd/systemd-pcrextend
-    varlinkctl list-interfaces /usr/lib/systemd/systemd-pcrextend
-    varlinkctl introspect /usr/lib/systemd/systemd-pcrextend io.systemd.PCRExtend
-fi
-
-# SSH transport
-SSHBINDIR="$(mktemp -d)"
-
-rm_rf_sshbindir() {
-    rm -rf "$SSHBINDIR"
-}
-
-trap rm_rf_sshbindir EXIT
-
-# Create a fake "ssh" binary that validates everything works as expected
-cat > "$SSHBINDIR"/ssh <<'EOF'
-#!/bin/sh
-
-set -xe
-
-test "$1" = "-W"
-test "$2" = "/run/systemd/journal/io.systemd.journal"
-test "$3" = "foobar"
-
-exec socat - UNIX-CONNECT:/run/systemd/journal/io.systemd.journal
-EOF
-
-chmod +x "$SSHBINDIR"/ssh
-
-SYSTEMD_SSH="$SSHBINDIR/ssh" varlinkctl info ssh:foobar:/run/systemd/journal/io.systemd.journal
-
-# Go through all varlink sockets we can find under /run/systemd/ for some extra coverage
-find /run/systemd/ -name "io.systemd*" -type s | while read -r socket; do
-    varlinkctl info "$socket"
-
-    varlinkctl list-interfaces "$socket" | while read -r interface; do
-        varlinkctl introspect "$socket" "$interface"
-    done
-done
-
-(! varlinkctl)
-(! varlinkctl "")
-(! varlinkctl info)
-(! varlinkctl info "")
-(! varlinkctl info /run/systemd/notify)
-(! varlinkctl info /run/systemd/private)
-# Relative paths must begin with ./
-(! varlinkctl info "$(realpath --relative-to="$PWD" /run/systemd/journal/io.systemd.journal)")
-(! varlinkctl info unix:)
-(! varlinkctl info unix:"")
-(! varlinkctl info exec:)
-(! varlinkctl info exec:"")
-(! varlinkctl list-interfaces)
-(! varlinkctl list-interfaces "")
-(! varlinkctl introspect)
-(! varlinkctl introspect /run/systemd/journal/io.systemd.journal)
-(! varlinkctl introspect /run/systemd/journal/io.systemd.journal "")
-(! varlinkctl introspect "" "")
-(! varlinkctl call)
-(! varlinkctl call "")
-(! varlinkctl call "" "")
-(! varlinkctl call "" "" "")
-(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord </dev/null)
-(! varlinkctl validate-idl "")
-(! varlinkctl validate-idl </dev/null)
-
-varlinkctl info /run/systemd/io.systemd.Hostname
-varlinkctl introspect /run/systemd/io.systemd.Hostname io.systemd.Hostname
-varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}'
diff --git a/test/units/testsuite-74.vpick.sh b/test/units/testsuite-74.vpick.sh
deleted file mode 100755 (executable)
index 400097f..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-at_exit() {
-    set +e
-    rm -rf /var/lib/machines/mymachine.raw.v
-    rm -rf /var/lib/machines/mytree.v
-    rm -rf /var/lib/machines/testroot.v
-    umount -l /tmp/dotvroot
-    rmdir /tmp/dotvroot
-}
-
-trap at_exit EXIT
-
-mkdir -p /var/lib/machines/mymachine.raw.v
-
-touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw
-touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw
-touch /var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw
-touch /var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw
-
-mkdir -p /var/lib/machines/mytree.v
-
-mkdir /var/lib/machines/mytree.v/mytree_33.4
-mkdir /var/lib/machines/mytree.v/mytree_33.5
-mkdir /var/lib/machines/mytree.v/mytree_36.0+0-5
-mkdir /var/lib/machines/mytree.v/mytree_37.0_arm64+2-3
-mkdir /var/lib/machines/mytree.v/mytree_38.0_arm64+0-5
-
-ARCH="$(busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Architecture | cut -d\" -f 2)"
-
-export SYSTEMD_LOG_LEVEL=debug
-
-if [ "$ARCH" = "x86-64" ] ; then
-    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
-
-    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
-    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
-    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
-    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw"
-
-    systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
-elif [ "$ARCH" = "arm64" ] ; then
-    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
-
-    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
-    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
-    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
-    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
-
-    systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
-else
-    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
-
-    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
-    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
-    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
-    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
-
-    systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
-fi
-
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A x86-64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
-(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A ia64)
-
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p version)" = "7.6.0"
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p type)" = "reg"
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p filename)" = "mymachine_7.6.0_arm64.raw"
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p arch)" = "arm64"
-
-test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t reg)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
-(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t dir)
-(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t fifo)
-(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t sock)
-
-if [ "$ARCH" != "arm64" ] ; then
-    test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_33.5/"
-    test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_33.5/"
-else
-    test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
-    test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
-fi
-
-(! systemd-vpick /var/lib/machines/mytree.v --type=reg)
-
-mkdir /tmp/dotvroot
-mount --bind / /tmp/dotvroot
-
-mkdir /var/lib/machines/testroot.v
-mkdir /var/lib/machines/testroot.v/testroot_32
-ln -s /tmp/dotvroot /var/lib/machines/testroot.v/testroot_33
-mkdir /var/lib/machines/testroot.v/testroot_34
-
-ls -l /var/lib/machines/testroot.v
-
-test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
-test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
-(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
-
-find /var/lib/machines/testroot.v/testroot_34
-rm -rf /var/lib/machines/testroot.v/testroot_34
-test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_33/
-test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /tmp/dotvroot/
-systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true
-
-rm /var/lib/machines/testroot.v/testroot_33
-test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
-test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
-(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
-
-rm -rf /var/lib/machines/testroot.v/testroot_32
-(! systemd-vpick /var/lib/machines/testroot.v)
-(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
diff --git a/test/units/testsuite-75.service b/test/units/testsuite-75.service
deleted file mode 100644 (file)
index 111cde3..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=Tests for systemd-resolved
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-75.sh b/test/units/testsuite-75.sh
deleted file mode 100755 (executable)
index a4417ce..0000000
+++ /dev/null
@@ -1,902 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# vi: ts=4 sw=4 tw=0 et:
-
-# TODO:
-#   - IPv6-only stack
-#   - mDNS
-#   - LLMNR
-#   - DoT/DoH
-
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-# We need at least Knot 3.0 which support (among others) the ds-push directive
-if ! knotc -c /usr/lib/systemd/tests/testdata/knot-data/knot.conf conf-check; then
-    echo "This test requires at least Knot 3.0. skipping..." | tee --append /skipped
-    exit 77
-fi
-
-RUN_OUT="$(mktemp)"
-
-run() {
-    "$@" |& tee "$RUN_OUT"
-}
-
-run_delv() {
-    # Since [0] delv no longer loads /etc/(bind/)bind.keys by default, so we
-    # have to do that explicitly for each invocation
-    run delv -a /etc/bind.keys "$@"
-}
-
-disable_ipv6() {
-    sysctl -w net.ipv6.conf.all.disable_ipv6=1
-}
-
-enable_ipv6() {
-    sysctl -w net.ipv6.conf.all.disable_ipv6=0
-    networkctl reconfigure dns0
-    /usr/lib/systemd/systemd-networkd-wait-online --ipv4 --ipv6 --interface=dns0:routable --timeout=30
-}
-
-monitor_check_rr() (
-    set +x
-    set +o pipefail
-    local since="${1:?}"
-    local match="${2:?}"
-
-    # Wait until the first mention of the specified log message is
-    # displayed. We turn off pipefail for this, since we don't care about the
-    # lhs of this pipe expression, we only care about the rhs' result to be
-    # clean
-    timeout -v 30s journalctl -u resolvectl-monitor.service --since "$since" -f --full | grep -m1 "$match"
-)
-
-restart_resolved() {
-    systemctl stop systemd-resolved.service
-    (! systemctl is-failed systemd-resolved.service)
-    # Reset the restart counter since we call this method a bunch of times
-    # and can occasionally hit the default rate limit
-    systemctl reset-failed systemd-resolved.service
-    systemctl start systemd-resolved.service
-    systemctl service-log-level systemd-resolved.service debug
-}
-
-# Test for resolvectl, resolvconf
-systemctl unmask systemd-resolved.service
-systemctl enable --now systemd-resolved.service
-systemctl service-log-level systemd-resolved.service debug
-ip link add hoge type dummy
-ip link add hoge.foo type dummy
-resolvectl dns hoge 10.0.0.1 10.0.0.2
-resolvectl dns hoge.foo 10.0.0.3 10.0.0.4
-assert_in '10.0.0.1 10.0.0.2' "$(resolvectl dns hoge)"
-assert_in '10.0.0.3 10.0.0.4' "$(resolvectl dns hoge.foo)"
-resolvectl dns hoge 10.0.1.1 10.0.1.2
-resolvectl dns hoge.foo 10.0.1.3 10.0.1.4
-assert_in '10.0.1.1 10.0.1.2' "$(resolvectl dns hoge)"
-assert_in '10.0.1.3 10.0.1.4' "$(resolvectl dns hoge.foo)"
-if ! RESOLVCONF=$(command -v resolvconf 2>/dev/null); then
-    TMPDIR=$(mktemp -d -p /tmp resolvconf-tests.XXXXXX)
-    RESOLVCONF="$TMPDIR"/resolvconf
-    ln -s "$(command -v resolvectl 2>/dev/null)" "$RESOLVCONF"
-fi
-echo nameserver 10.0.2.1 10.0.2.2 | "$RESOLVCONF" -a hoge
-echo nameserver 10.0.2.3 10.0.2.4 | "$RESOLVCONF" -a hoge.foo
-assert_in '10.0.2.1 10.0.2.2' "$(resolvectl dns hoge)"
-assert_in '10.0.2.3 10.0.2.4' "$(resolvectl dns hoge.foo)"
-echo nameserver 10.0.3.1 10.0.3.2 | "$RESOLVCONF" -a hoge.inet.ipsec.192.168.35
-echo nameserver 10.0.3.3 10.0.3.4 | "$RESOLVCONF" -a hoge.foo.dhcp
-assert_in '10.0.3.1 10.0.3.2' "$(resolvectl dns hoge)"
-assert_in '10.0.3.3 10.0.3.4' "$(resolvectl dns hoge.foo)"
-
-# Tests for _localdnsstub and _localdnsproxy
-assert_in '127.0.0.53' "$(resolvectl query _localdnsstub)"
-assert_in '_localdnsstub' "$(resolvectl query 127.0.0.53)"
-assert_in '127.0.0.54' "$(resolvectl query _localdnsproxy)"
-assert_in '_localdnsproxy' "$(resolvectl query 127.0.0.54)"
-
-assert_in '127.0.0.53' "$(dig @127.0.0.53 _localdnsstub)"
-assert_in '_localdnsstub' "$(dig @127.0.0.53 -x 127.0.0.53)"
-assert_in '127.0.0.54' "$(dig @127.0.0.53 _localdnsproxy)"
-assert_in '_localdnsproxy' "$(dig @127.0.0.53 -x 127.0.0.54)"
-
-# Tests for mDNS and LLMNR settings
-mkdir -p /run/systemd/resolved.conf.d
-{
-    echo "[Resolve]"
-    echo "MulticastDNS=no"
-    echo "LLMNR=no"
-} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
-restart_resolved
-# make sure networkd is not running.
-systemctl stop systemd-networkd.service
-assert_in 'no' "$(resolvectl mdns hoge)"
-assert_in 'no' "$(resolvectl llmnr hoge)"
-# Tests that reloading works
-{
-    echo "[Resolve]"
-    echo "MulticastDNS=yes"
-    echo "LLMNR=yes"
-} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
-systemctl reload systemd-resolved.service
-# defaults to yes (both the global and per-link settings are yes)
-assert_in 'yes' "$(resolvectl mdns hoge)"
-assert_in 'yes' "$(resolvectl llmnr hoge)"
-# set per-link setting
-resolvectl mdns hoge yes
-resolvectl llmnr hoge yes
-assert_in 'yes' "$(resolvectl mdns hoge)"
-assert_in 'yes' "$(resolvectl llmnr hoge)"
-resolvectl mdns hoge resolve
-resolvectl llmnr hoge resolve
-assert_in 'resolve' "$(resolvectl mdns hoge)"
-assert_in 'resolve' "$(resolvectl llmnr hoge)"
-resolvectl mdns hoge no
-resolvectl llmnr hoge no
-assert_in 'no' "$(resolvectl mdns hoge)"
-assert_in 'no' "$(resolvectl llmnr hoge)"
-# downgrade global setting to resolve
-{
-    echo "[Resolve]"
-    echo "MulticastDNS=resolve"
-    echo "LLMNR=resolve"
-} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
-systemctl reload systemd-resolved.service
-# set per-link setting
-resolvectl mdns hoge yes
-resolvectl llmnr hoge yes
-assert_in 'resolve' "$(resolvectl mdns hoge)"
-assert_in 'resolve' "$(resolvectl llmnr hoge)"
-resolvectl mdns hoge resolve
-resolvectl llmnr hoge resolve
-assert_in 'resolve' "$(resolvectl mdns hoge)"
-assert_in 'resolve' "$(resolvectl llmnr hoge)"
-resolvectl mdns hoge no
-resolvectl llmnr hoge no
-assert_in 'no' "$(resolvectl mdns hoge)"
-assert_in 'no' "$(resolvectl llmnr hoge)"
-# downgrade global setting to no
-{
-    echo "[Resolve]"
-    echo "MulticastDNS=no"
-    echo "LLMNR=no"
-} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
-systemctl reload systemd-resolved.service
-# set per-link setting
-resolvectl mdns hoge yes
-resolvectl llmnr hoge yes
-assert_in 'no' "$(resolvectl mdns hoge)"
-assert_in 'no' "$(resolvectl llmnr hoge)"
-resolvectl mdns hoge resolve
-resolvectl llmnr hoge resolve
-assert_in 'no' "$(resolvectl mdns hoge)"
-assert_in 'no' "$(resolvectl llmnr hoge)"
-resolvectl mdns hoge no
-resolvectl llmnr hoge no
-assert_in 'no' "$(resolvectl mdns hoge)"
-assert_in 'no' "$(resolvectl llmnr hoge)"
-
-# Cleanup
-rm -f /run/systemd/resolved.conf.d/mdns-llmnr.conf
-ip link del hoge
-ip link del hoge.foo
-
-### SETUP ###
-# Configure network
-hostnamectl hostname ns1.unsigned.test
-cat >>/etc/hosts <<EOF
-10.0.0.1               ns1.unsigned.test
-fd00:dead:beef:cafe::1 ns1.unsigned.test
-
-127.128.0.5     localhost5 localhost5.localdomain localhost5.localdomain4 localhost.localdomain5 localhost5.localdomain5
-EOF
-
-mkdir -p /run/systemd/network
-cat >/run/systemd/network/10-dns0.netdev <<EOF
-[NetDev]
-Name=dns0
-Kind=dummy
-EOF
-cat >/run/systemd/network/10-dns0.network <<EOF
-[Match]
-Name=dns0
-
-[Network]
-IPv6AcceptRA=no
-Address=10.0.0.1/24
-Address=fd00:dead:beef:cafe::1/64
-DNSSEC=allow-downgrade
-DNS=10.0.0.1
-DNS=fd00:dead:beef:cafe::1
-EOF
-cat >/run/systemd/network/10-dns1.netdev <<EOF
-[NetDev]
-Name=dns1
-Kind=dummy
-EOF
-cat >/run/systemd/network/10-dns1.network <<EOF
-[Match]
-Name=dns1
-
-[Network]
-IPv6AcceptRA=no
-Address=10.99.0.1/24
-DNSSEC=no
-EOF
-systemctl edit --stdin --full --runtime --force "resolved-dummy-server.service" <<EOF
-[Service]
-Type=notify
-Environment=SYSTEMD_LOG_LEVEL=debug
-ExecStart=/usr/lib/systemd/tests/unit-tests/manual/test-resolved-dummy-server 10.99.0.1:53
-EOF
-
-DNS_ADDRESSES=(
-    "10.0.0.1"
-    "fd00:dead:beef:cafe::1"
-)
-
-mkdir -p /run/systemd/resolved.conf.d
-{
-    echo "[Resolve]"
-    echo "FallbackDNS="
-    echo "DNSSEC=allow-downgrade"
-    echo "DNSOverTLS=opportunistic"
-} >/run/systemd/resolved.conf.d/test.conf
-ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
-# Override the default NTA list, which turns off DNSSEC validation for (among
-# others) the test. domain
-mkdir -p "/etc/dnssec-trust-anchors.d/"
-echo local >/etc/dnssec-trust-anchors.d/local.negative
-
-# Copy over our knot configuration
-mkdir -p /var/lib/knot/zones/ /etc/knot/
-cp -rfv /usr/lib/systemd/tests/testdata/knot-data/zones/* /var/lib/knot/zones/
-cp -fv /usr/lib/systemd/tests/testdata/knot-data/knot.conf /etc/knot/knot.conf
-chgrp -R knot /etc/knot/ /var/lib/knot/
-chmod -R ug+rwX /var/lib/knot/
-chmod -R g+r /etc/knot/
-
-# Sign the root zone
-keymgr . generate algorithm=ECDSAP256SHA256 ksk=yes zsk=yes
-# Create a trust anchor for resolved with our root zone
-keymgr . ds | sed 's/ DS/ IN DS/g' >/etc/dnssec-trust-anchors.d/root.positive
-# Create a bind-compatible trust anchor (for delv)
-# Note: the trust-anchors directive is relatively new, so use the original
-#       managed-keys one until it's widespread enough
-{
-    echo 'managed-keys {'
-    keymgr . dnskey | sed -r 's/^\. DNSKEY ([0-9]+ [0-9]+ [0-9]+) (.+)$/. static-key \1 "\2";/g'
-    echo '};'
-} >/etc/bind.keys
-# Create an /etc/bind/bind.keys symlink, which is used by delv on Ubuntu
-mkdir -p /etc/bind
-ln -svf /etc/bind.keys /etc/bind/bind.keys
-
-# Start the services
-systemctl unmask systemd-networkd
-systemctl restart systemd-networkd
-/usr/lib/systemd/systemd-networkd-wait-online --interface=dns1:routable --timeout=60
-systemctl reload systemd-resolved
-systemctl start resolved-dummy-server
-
-# Create knot's runtime dir, since from certain version it's provided only by
-# the package and not created by tmpfiles/systemd
-if [[ ! -d /run/knot ]]; then
-    mkdir -p /run/knot
-    chown -R knot:knot /run/knot
-fi
-systemctl start knot
-# Wait a bit for the keys to propagate
-sleep 4
-
-systemctl status resolved-dummy-server
-networkctl status
-resolvectl status
-resolvectl log-level debug
-
-# Start monitoring queries
-systemd-run -u resolvectl-monitor.service -p Type=notify resolvectl monitor
-systemd-run -u resolvectl-monitor-json.service -p Type=notify resolvectl monitor --json=short
-
-# FIXME: knot, unfortunately, incorrectly complains about missing zone files for zones
-#        that are forwarded using the `dnsproxy` module. Until the issue is resolved,
-#        let's fall back to pre-processing the `zone-check` output a bit before checking it
-#
-# See: https://gitlab.nic.cz/knot/knot-dns/-/issues/913
-run knotc zone-check || :
-sed -i '/forwarded.test./d' "$RUN_OUT"
-[[ ! -s "$RUN_OUT" ]]
-# We need to manually propagate the DS records of onlinesign.test. to the parent
-# zone, since they're generated online
-knotc zone-begin test.
-if knotc zone-get test. onlinesign.test. ds | grep .; then
-    # Drop any old DS records, if present (e.g. on test re-run)
-    knotc zone-unset test. onlinesign.test. ds
-fi
-# Propagate the new DS records
-while read -ra line; do
-    knotc zone-set test. "${line[0]}" 600 "${line[@]:1}"
-done < <(keymgr onlinesign.test. ds)
-knotc zone-commit test.
-
-knotc reload
-sleep 2
-
-### SETUP END ###
-
-: "--- nss-resolve/nss-myhostname tests"
-# Sanity check
-TIMESTAMP=$(date '+%F %T')
-# Issue: https://github.com/systemd/systemd/issues/23951
-# With IPv6 enabled
-run getent -s resolve ahosts ns1.unsigned.test
-grep -qE "^fd00:dead:beef:cafe::1\s+STREAM\s+ns1\.unsigned\.test" "$RUN_OUT"
-monitor_check_rr "$TIMESTAMP" "ns1.unsigned.test IN AAAA fd00:dead:beef:cafe::1"
-# With IPv6 disabled
-# Issue: https://github.com/systemd/systemd/issues/23951
-disable_ipv6
-run getent -s resolve ahosts ns1.unsigned.test
-grep -qE "^10\.0\.0\.1\s+STREAM\s+ns1\.unsigned\.test" "$RUN_OUT"
-(! grep -qE "fd00:dead:beef:cafe::1" "$RUN_OUT")
-monitor_check_rr "$TIMESTAMP" "ns1.unsigned.test IN A 10.0.0.1"
-enable_ipv6
-
-# Issue: https://github.com/systemd/systemd/issues/18812
-# PR: https://github.com/systemd/systemd/pull/18896
-# Follow-up issue: https://github.com/systemd/systemd/issues/23152
-# Follow-up PR: https://github.com/systemd/systemd/pull/23161
-# With IPv6 enabled
-run getent -s resolve ahosts localhost
-grep -qE "^::1\s+STREAM\s+localhost" "$RUN_OUT"
-run getent -s myhostname ahosts localhost
-grep -qE "^::1\s+STREAM\s+localhost" "$RUN_OUT"
-# With IPv6 disabled
-disable_ipv6
-run getent -s resolve ahosts localhost
-grep -qE "^127\.0\.0\.1\s+STREAM\s+localhost" "$RUN_OUT"
-(! grep -qE "::1" "$RUN_OUT")
-run getent -s myhostname ahosts localhost
-grep -qE "^127\.0\.0\.1\s+STREAM\s+localhost" "$RUN_OUT"
-enable_ipv6
-
-# Issue: https://github.com/systemd/systemd/issues/25088
-run getent -s resolve hosts 127.128.0.5
-grep -qEx '127\.128\.0\.5\s+localhost5(\s+localhost5?\.localdomain[45]?){4}' "$RUN_OUT"
-[ "$(wc -l <"$RUN_OUT")" -eq 1 ]
-
-# Issue: https://github.com/systemd/systemd/issues/20158
-run dig +noall +answer +additional localhost5.
-grep -qEx 'localhost5\.\s+0\s+IN\s+A\s+127\.128\.0\.5' "$RUN_OUT"
-[ "$(wc -l <"$RUN_OUT")" -eq 1 ]
-run dig +noall +answer +additional localhost5.localdomain4.
-grep -qEx 'localhost5\.localdomain4\.\s+0\s+IN\s+CNAME\s+localhost5\.' "$RUN_OUT"
-grep -qEx 'localhost5\.\s+0\s+IN\s+A\s+127\.128\.0\.5' "$RUN_OUT"
-[ "$(wc -l <"$RUN_OUT")" -eq 2 ]
-
-: "--- Basic resolved tests ---"
-# Issue: https://github.com/systemd/systemd/issues/22229
-# PR: https://github.com/systemd/systemd/pull/22231
-FILTERED_NAMES=(
-    "0.in-addr.arpa"
-    "255.255.255.255.in-addr.arpa"
-    "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa"
-    "hello.invalid"
-    "hello.alt"
-)
-
-for name in "${FILTERED_NAMES[@]}"; do
-    (! run host "$name")
-    grep -qF "NXDOMAIN" "$RUN_OUT"
-done
-
-# Follow-up
-# Issue: https://github.com/systemd/systemd/issues/22401
-# PR: https://github.com/systemd/systemd/pull/22414
-run dig +noall +authority +comments SRV .
-grep -qF "status: NOERROR" "$RUN_OUT"
-grep -qE "IN\s+SOA\s+ns1\.unsigned\.test\." "$RUN_OUT"
-
-run resolvectl query -t SVCB svcb.test
-grep -qF 'alpn="dot"' "$RUN_OUT"
-grep -qF "ipv4hint=10.0.0.1" "$RUN_OUT"
-
-run resolvectl query -t HTTPS https.test
-grep -qF 'alpn="h2,h3"' "$RUN_OUT"
-
-: "--- ZONE: unsigned.test. ---"
-run dig @ns1.unsigned.test +short unsigned.test A unsigned.test AAAA
-grep -qF "10.0.0.101" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::101" "$RUN_OUT"
-run resolvectl query unsigned.test
-grep -qF "10.0.0.10" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::101" "$RUN_OUT"
-grep -qF "authenticated: no" "$RUN_OUT"
-run dig @ns1.unsigned.test +short MX unsigned.test
-grep -qF "15 mail.unsigned.test." "$RUN_OUT"
-run resolvectl query --legend=no -t MX unsigned.test
-grep -qF "unsigned.test IN MX 15 mail.unsigned.test" "$RUN_OUT"
-
-
-: "--- ZONE: signed.test (static DNSSEC) ---"
-# Check the trust chain (with and without systemd-resolved in between
-# Issue: https://github.com/systemd/systemd/issues/22002
-# PR: https://github.com/systemd/systemd/pull/23289
-run_delv @ns1.unsigned.test signed.test
-grep -qF "; fully validated" "$RUN_OUT"
-run_delv signed.test
-grep -qF "; fully validated" "$RUN_OUT"
-
-for addr in "${DNS_ADDRESSES[@]}"; do
-    run_delv "@$addr" -t A mail.signed.test
-    grep -qF "; fully validated" "$RUN_OUT"
-    run_delv "@$addr" -t AAAA mail.signed.test
-    grep -qF "; fully validated" "$RUN_OUT"
-done
-run resolvectl query mail.signed.test
-grep -qF "10.0.0.11" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::11" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-
-run dig +short signed.test
-grep -qF "10.0.0.10" "$RUN_OUT"
-run resolvectl query signed.test
-grep -qF "signed.test: 10.0.0.10" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-run dig @ns1.unsigned.test +short MX signed.test
-grep -qF "10 mail.signed.test." "$RUN_OUT"
-run resolvectl query --legend=no -t MX signed.test
-grep -qF "signed.test IN MX 10 mail.signed.test" "$RUN_OUT"
-# Check a non-existent domain
-run dig +dnssec this.does.not.exist.signed.test
-grep -qF "status: NXDOMAIN" "$RUN_OUT"
-# Check a wildcard record
-run resolvectl query -t TXT this.should.be.authenticated.wild.signed.test
-grep -qF 'this.should.be.authenticated.wild.signed.test IN TXT "this is a wildcard"' "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-# Check SRV support
-run resolvectl service _mysvc._tcp signed.test
-grep -qF "myservice.signed.test:1234" "$RUN_OUT"
-grep -qF "10.0.0.20" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::17" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-
-# Test service resolve over Varlink
-run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"name":"","type":"_mysvc._tcp","domain":"signed.test"}'
-grep -qF '"services":[{"priority":10,"weight":5,"port":1234,"hostname":"myservice.signed.test","canonicalName":"myservice.signed.test","addresses":[{"ifindex":' "$RUN_OUT"
-grep -qF '"family":10,"address":[253,0,222,173,190,239,202,254,0,0,0,0,0,0,0,23]' "$RUN_OUT"
-grep -qF '"family":2,"address":[10,0,0,20]' "$RUN_OUT"
-grep -qF '}]}],"txt":["This is TXT for myservice"],"canonical":{"name":null,"type":"_mysvc._tcp","domain":"signed.test"},"flags":' "$RUN_OUT"
-
-# without name
-run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test"}'
-# without txt (SD_RESOLVE_NO_TXT)
-run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":64}'
-(! grep -qF '"txt"' "$RUN_OUT")
-# without address (SD_RESOLVE_NO_ADDRESS)
-run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":128}'
-(! grep -qF '"addresses"' "$RUN_OUT")
-# without txt and address
-run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":192}'
-(! grep -qF '"txt"' "$RUN_OUT")
-(! grep -qF '"addresses"' "$RUN_OUT")
-
-(! run resolvectl service _invalidsvc._udp signed.test)
-grep -qE "invalidservice\.signed\.test' not found" "$RUN_OUT"
-run resolvectl service _untrustedsvc._udp signed.test
-grep -qF "myservice.untrusted.test:1111" "$RUN_OUT"
-grep -qF "10.0.0.123" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-# Check OPENPGPKEY support
-run_delv -t OPENPGPKEY 5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test
-grep -qF "; fully validated" "$RUN_OUT"
-run resolvectl openpgp mr.smith@signed.test
-grep -qF "5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-# Check zone transfers (AXFR/IXFR)
-# Note: since resolved doesn't support zone transfers, let's just make sure it
-#       simply refuses such requests without choking on them
-# See: https://github.com/systemd/systemd/pull/30809#issuecomment-1880102804
-run dig @ns1.unsigned.test AXFR signed.test
-grep -qE "SOA\s+ns1.unsigned.test. root.unsigned.test." "$RUN_OUT"
-run dig AXFR signed.test
-grep -qF "; Transfer failed" "$RUN_OUT"
-run dig @ns1.unsigned.test IXFR=43 signed.test
-grep -qE "SOA\s+ns1.unsigned.test. root.unsigned.test." "$RUN_OUT"
-run dig IXFR=43 signed.test
-grep -qF "; Transfer failed" "$RUN_OUT"
-
-# DNSSEC validation with multiple records of the same type for the same name
-# Issue: https://github.com/systemd/systemd/issues/22002
-# PR: https://github.com/systemd/systemd/pull/23289
-check_domain() {
-    local domain="${1:?}"
-    local record="${2:?}"
-    local message="${3:?}"
-    local addr
-
-    for addr in "${DNS_ADDRESSES[@]}"; do
-        run_delv "@$addr" -t "$record" "$domain"
-        grep -qF "$message" "$RUN_OUT"
-    done
-
-    run_delv -t "$record" "$domain"
-    grep -qF "$message" "$RUN_OUT"
-
-    run resolvectl query "$domain"
-    grep -qF "authenticated: yes" "$RUN_OUT"
-}
-
-check_domain "dupe.signed.test"       "A"    "; fully validated"
-check_domain "dupe.signed.test"       "AAAA" "; negative response, fully validated"
-check_domain "dupe-ipv6.signed.test"  "AAAA" "; fully validated"
-check_domain "dupe-ipv6.signed.test"  "A"    "; negative response, fully validated"
-check_domain "dupe-mixed.signed.test" "A"    "; fully validated"
-check_domain "dupe-mixed.signed.test" "AAAA" "; fully validated"
-
-# Test resolution of CNAME chains
-TIMESTAMP=$(date '+%F %T')
-run resolvectl query -t A cname-chain.signed.test
-grep -qF "follow14.final.signed.test IN A 10.0.0.14" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-
-monitor_check_rr "$TIMESTAMP" "follow10.so.close.signed.test IN CNAME follow11.yet.so.far.signed.test"
-monitor_check_rr "$TIMESTAMP" "follow11.yet.so.far.signed.test IN CNAME follow12.getting.hot.signed.test"
-monitor_check_rr "$TIMESTAMP" "follow12.getting.hot.signed.test IN CNAME follow13.almost.final.signed.test"
-monitor_check_rr "$TIMESTAMP" "follow13.almost.final.signed.test IN CNAME follow14.final.signed.test"
-monitor_check_rr "$TIMESTAMP" "follow14.final.signed.test IN A 10.0.0.14"
-
-# Non-existing RR + CNAME chain
-#run dig +dnssec AAAA cname-chain.signed.test
-#grep -qF "status: NOERROR" "$RUN_OUT"
-#grep -qE "^follow14\.final\.signed\.test\..+IN\s+NSEC\s+" "$RUN_OUT"
-
-
-: "--- ZONE: onlinesign.test (dynamic DNSSEC) ---"
-# Check the trust chain (with and without systemd-resolved in between
-# Issue: https://github.com/systemd/systemd/issues/22002
-# PR: https://github.com/systemd/systemd/pull/23289
-run_delv @ns1.unsigned.test sub.onlinesign.test
-grep -qF "; fully validated" "$RUN_OUT"
-run_delv sub.onlinesign.test
-grep -qF "; fully validated" "$RUN_OUT"
-
-run dig +short sub.onlinesign.test
-grep -qF "10.0.0.133" "$RUN_OUT"
-run resolvectl query sub.onlinesign.test
-grep -qF "sub.onlinesign.test: 10.0.0.133" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-run dig @ns1.unsigned.test +short TXT onlinesign.test
-grep -qF '"hello from onlinesign"' "$RUN_OUT"
-run resolvectl query --legend=no -t TXT onlinesign.test
-grep -qF 'onlinesign.test IN TXT "hello from onlinesign"' "$RUN_OUT"
-
-for addr in "${DNS_ADDRESSES[@]}"; do
-    run_delv "@$addr" -t A dual.onlinesign.test
-    grep -qF "10.0.0.135" "$RUN_OUT"
-    run_delv "@$addr" -t AAAA dual.onlinesign.test
-    grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT"
-    run_delv "@$addr" -t ANY ipv6.onlinesign.test
-    grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT"
-done
-run resolvectl query dual.onlinesign.test
-grep -qF "10.0.0.135" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-run resolvectl query ipv6.onlinesign.test
-grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-
-# Check a non-existent domain
-# Note: mod-onlinesign utilizes Minimally Covering NSEC Records, hence the
-#       different response than with "standard" DNSSEC
-run dig +dnssec this.does.not.exist.onlinesign.test
-grep -qF "status: NOERROR" "$RUN_OUT"
-grep -qF "NSEC \\000.this.does.not.exist.onlinesign.test." "$RUN_OUT"
-# Check a wildcard record
-run resolvectl query -t TXT this.should.be.authenticated.wild.onlinesign.test
-grep -qF 'this.should.be.authenticated.wild.onlinesign.test IN TXT "this is an onlinesign wildcard"' "$RUN_OUT"
-grep -qF "authenticated: yes" "$RUN_OUT"
-
-# Resolve via dbus method
-TIMESTAMP=$(date '+%F %T')
-run busctl call org.freedesktop.resolve1 /org/freedesktop/resolve1 org.freedesktop.resolve1.Manager ResolveHostname 'isit' 0 secondsub.onlinesign.test 0 0
-grep -qF '10 0 0 134 "secondsub.onlinesign.test"' "$RUN_OUT"
-monitor_check_rr "$TIMESTAMP" "secondsub.onlinesign.test IN A 10.0.0.134"
-
-
-: "--- ZONE: untrusted.test (DNSSEC without propagated DS records) ---"
-# Issue: https://github.com/systemd/systemd/issues/23955
-# FIXME
-resolvectl flush-caches
-#run dig +short untrusted.test A untrusted.test AAAA
-#grep -qF "10.0.0.121" "$RUN_OUT"
-#grep -qF "fd00:dead:beef:cafe::121" "$RUN_OUT"
-run resolvectl query untrusted.test
-grep -qF "untrusted.test:" "$RUN_OUT"
-grep -qF "10.0.0.121" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::121" "$RUN_OUT"
-grep -qF "authenticated: no" "$RUN_OUT"
-run resolvectl service _mysvc._tcp untrusted.test
-grep -qF "myservice.untrusted.test:1234" "$RUN_OUT"
-grep -qF "10.0.0.123" "$RUN_OUT"
-grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT"
-
-# Issue: https://github.com/systemd/systemd/issues/19472
-# 1) Query for a non-existing RR should return NOERROR + NSEC (?), not NXDOMAIN
-# FIXME: re-enable once the issue is resolved
-#run dig +dnssec AAAA untrusted.test
-#grep -qF "status: NOERROR" "$RUN_OUT"
-#grep -qE "^untrusted\.test\..+IN\s+NSEC\s+" "$RUN_OUT"
-## 2) Query for a non-existing name should return NXDOMAIN, not SERVFAIL
-#run dig +dnssec this.does.not.exist.untrusted.test
-#grep -qF "status: NXDOMAIN" "$RUN_OUT"
-
-: "--- ZONE: forwarded.test (queries forwarded to our dummy test server) ---"
-JOURNAL_CURSOR="$(mktemp)"
-journalctl -n0 -q --cursor-file="$JOURNAL_CURSOR"
-
-# See "test-resolved-dummy-server.c" for the server part
-(! run resolvectl query nope.forwarded.test)
-grep -qF "nope.forwarded.test" "$RUN_OUT"
-grep -qF "not found" "$RUN_OUT"
-
-# SERVFAIL + EDE code 6: DNSSEC Bogus
-(! run resolvectl query edns-bogus-dnssec.forwarded.test)
-grep -qE "^edns-bogus-dnssec.forwarded.test:.+: upstream-failure \(DNSSEC Bogus\)" "$RUN_OUT"
-# Same thing, but over Varlink
-(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-bogus-dnssec.forwarded.test"}')
-grep -qF "io.systemd.Resolve.DNSSECValidationFailed" "$RUN_OUT"
-grep -qF '{"result":"upstream-failure","extendedDNSErrorCode":6}' "$RUN_OUT"
-journalctl --sync
-journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(DNSSEC Bogus\). Lookup failed."
-
-# SERVFAIL + EDE code 16: Censored + extra text
-(! run resolvectl query edns-extra-text.forwarded.test)
-grep -qE "^edns-extra-text.forwarded.test.+: SERVFAIL \(Censored: Nothing to see here!\)" "$RUN_OUT"
-(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-extra-text.forwarded.test"}')
-grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
-grep -qF '{"rcode":2,"extendedDNSErrorCode":16,"extendedDNSErrorMessage":"Nothing to see here!"}' "$RUN_OUT"
-journalctl --sync
-journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Censored: Nothing to see here!\)"
-
-# SERVFAIL + EDE code 0: Other + extra text
-(! run resolvectl query edns-code-zero.forwarded.test)
-grep -qE "^edns-code-zero.forwarded.test:.+: SERVFAIL \(Other: 🐱\)" "$RUN_OUT"
-(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-code-zero.forwarded.test"}')
-grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
-grep -qF '{"rcode":2,"extendedDNSErrorCode":0,"extendedDNSErrorMessage":"🐱"}' "$RUN_OUT"
-journalctl --sync
-journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Other: 🐱\)"
-
-# SERVFAIL + invalid EDE code
-(! run resolvectl query edns-invalid-code.forwarded.test)
-grep -qE "^edns-invalid-code.forwarded.test:.+: SERVFAIL \([0-9]+\)" "$RUN_OUT"
-(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code.forwarded.test"}')
-grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
-grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+}' "$RUN_OUT"
-journalctl --sync
-journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+\)"
-
-# SERVFAIL + invalid EDE code + extra text
-(! run resolvectl query edns-invalid-code-with-extra-text.forwarded.test)
-grep -qE '^edns-invalid-code-with-extra-text.forwarded.test:.+: SERVFAIL \([0-9]+: Hello \[#\]\$%~ World\)' "$RUN_OUT"
-(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code-with-extra-text.forwarded.test"}')
-grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT"
-grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+,"extendedDNSErrorMessage":"Hello \[#\]\$%~ World"}' "$RUN_OUT"
-journalctl --sync
-journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+: Hello \[\#\]\\$%~ World\)"
-
-### Test resolvectl show-cache
-run resolvectl show-cache
-run resolvectl show-cache --json=short
-run resolvectl show-cache --json=pretty
-
-# Issue: https://github.com/systemd/systemd/issues/29580 (part #1)
-dig @127.0.0.54 signed.test
-
-systemctl stop resolvectl-monitor.service
-systemctl stop resolvectl-monitor-json.service
-
-# Issue: https://github.com/systemd/systemd/issues/29580 (part #2)
-#
-# Check for any warnings regarding malformed messages
-(! journalctl -u resolvectl-monitor.service -u reseolvectl-monitor-json.service -p warning --grep malformed)
-# Verify that all queries recorded by `resolvectl monitor --json` produced a valid JSON
-# with expected fields
-journalctl -p info -o cat _SYSTEMD_UNIT="resolvectl-monitor-json.service" | while read -r line; do
-    # Check that both "question" and "answer" fields are arrays
-    #
-    # The expression is slightly more complicated due to the fact that the "answer" field is optional,
-    # so we need to select it only if it's present, otherwise the type == "array" check would fail
-    echo "$line" | jq -e '[. | .question, (select(has("answer")) | .answer) | type == "array"] | all'
-done
-
-# Test serve stale feature and NFTSet= if nftables is installed
-if command -v nft >/dev/null; then
-    ### Test without serve stale feature ###
-    NFT_FILTER_NAME=dns_port_filter
-
-    drop_dns_outbound_traffic() {
-        nft add table inet $NFT_FILTER_NAME
-        nft add chain inet $NFT_FILTER_NAME output \{ type filter hook output priority 0 \; \}
-        nft add rule inet $NFT_FILTER_NAME output ip daddr 10.0.0.1 udp dport 53 drop
-        nft add rule inet $NFT_FILTER_NAME output ip daddr 10.0.0.1 tcp dport 53 drop
-        nft add rule inet $NFT_FILTER_NAME output ip6 daddr fd00:dead:beef:cafe::1 udp dport 53 drop
-        nft add rule inet $NFT_FILTER_NAME output ip6 daddr fd00:dead:beef:cafe::1 tcp dport 53 drop
-    }
-
-    run dig stale1.unsigned.test -t A
-    grep -qE "NOERROR" "$RUN_OUT"
-    sleep 2
-    drop_dns_outbound_traffic
-    set +e
-    # Make sure we give sd-resolved enough time to timeout (5-10s) before giving up
-    # See: https://github.com/systemd/systemd/issues/31639#issuecomment-2009152617
-    run dig +tries=1 +timeout=15 stale1.unsigned.test -t A
-    set -eux
-    grep -qE "no servers could be reached" "$RUN_OUT"
-    nft flush ruleset
-
-    ### Test TIMEOUT with serve stale feature ###
-
-    mkdir -p /run/systemd/resolved.conf.d
-    {
-        echo "[Resolve]"
-        echo "StaleRetentionSec=1d"
-    } >/run/systemd/resolved.conf.d/test.conf
-    ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
-    systemctl reload systemd-resolved.service
-
-    run dig stale1.unsigned.test -t A
-    grep -qE "NOERROR" "$RUN_OUT"
-    sleep 2
-    drop_dns_outbound_traffic
-    # Make sure we give sd-resolved enough time to timeout (5-10s) and serve the stale data (see above)
-    run dig +tries=1 +timeout=15 stale1.unsigned.test -t A
-    grep -qE "NOERROR" "$RUN_OUT"
-    grep -qE "10.0.0.112" "$RUN_OUT"
-
-    nft flush ruleset
-
-    ### Test NXDOMAIN with serve stale feature ###
-    # NXDOMAIN response should replace the cache with NXDOMAIN response
-    run dig stale1.unsigned.test -t A
-    grep -qE "NOERROR" "$RUN_OUT"
-    # Delete stale1 record from zone
-    knotc zone-begin unsigned.test
-    knotc zone-unset unsigned.test stale1 A
-    knotc zone-commit unsigned.test
-    knotc reload
-    sleep 2
-    run dig stale1.unsigned.test -t A
-    grep -qE "NXDOMAIN" "$RUN_OUT"
-
-    nft flush ruleset
-
-    ### NFTSet= test
-    nft add table inet sd_test
-    nft add set inet sd_test c '{ type cgroupsv2; }'
-    nft add set inet sd_test u '{ typeof meta skuid; }'
-    nft add set inet sd_test g '{ typeof meta skgid; }'
-
-    # service
-    systemd-run --unit test-nft.service --service-type=exec -p DynamicUser=yes \
-                -p 'NFTSet=cgroup:inet:sd_test:c user:inet:sd_test:u group:inet:sd_test:g' sleep 10000
-    run nft list set inet sd_test c
-    grep -qF "test-nft.service" "$RUN_OUT"
-    uid=$(getent passwd test-nft | cut -d':' -f3)
-    run nft list set inet sd_test u
-    grep -qF "$uid" "$RUN_OUT"
-    gid=$(getent passwd test-nft | cut -d':' -f4)
-    run nft list set inet sd_test g
-    grep -qF "$gid" "$RUN_OUT"
-    systemctl stop test-nft.service
-
-    # scope
-    run systemd-run --scope -u test-nft.scope -p 'NFTSet=cgroup:inet:sd_test:c' nft list set inet sd_test c
-    grep -qF "test-nft.scope" "$RUN_OUT"
-
-    mkdir -p /run/systemd/system
-    # socket
-    {
-        echo "[Socket]"
-        echo "ListenStream=12345"
-        echo "BindToDevice=lo"
-        echo "NFTSet=cgroup:inet:sd_test:c"
-    } >/run/systemd/system/test-nft.socket
-    {
-        echo "[Service]"
-        echo "ExecStart=/usr/bin/sleep 10000"
-    } >/run/systemd/system/test-nft.service
-    systemctl daemon-reload
-    systemctl start test-nft.socket
-    systemctl status test-nft.socket
-    run nft list set inet sd_test c
-    grep -qF "test-nft.socket" "$RUN_OUT"
-    systemctl stop test-nft.socket
-    rm -f /run/systemd/system/test-nft.{socket,service}
-
-    # slice
-    mkdir /run/systemd/system/system.slice.d
-    {
-        echo "[Slice]"
-        echo "NFTSet=cgroup:inet:sd_test:c"
-    } >/run/systemd/system/system.slice.d/00-test-nft.conf
-    systemctl daemon-reload
-    run nft list set inet sd_test c
-    grep -qF "system.slice" "$RUN_OUT"
-    rm -rf /run/systemd/system/system.slice.d
-
-    nft flush ruleset
-else
-    echo "nftables is not installed. Skipped serve stale feature and NFTSet= tests."
-fi
-
-### Test resolvectl show-server-state ###
-run resolvectl show-server-state
-grep -qF "10.0.0.1" "$RUN_OUT"
-grep -qF "Interface" "$RUN_OUT"
-
-run resolvectl show-server-state --json=short
-grep -qF "10.0.0.1" "$RUN_OUT"
-grep -qF "Interface" "$RUN_OUT"
-
-run resolvectl show-server-state --json=pretty
-grep -qF "10.0.0.1" "$RUN_OUT"
-grep -qF "Interface" "$RUN_OUT"
-
-### Test resolvectl statistics ###
-run resolvectl statistics
-grep -qF "Transactions" "$RUN_OUT"
-grep -qF "Cache" "$RUN_OUT"
-grep -qF "Failure Transactions" "$RUN_OUT"
-grep -qF "DNSSEC Verdicts" "$RUN_OUT"
-
-run resolvectl statistics --json=short
-grep -qF "transactions" "$RUN_OUT"
-grep -qF "cache" "$RUN_OUT"
-grep -qF "dnssec" "$RUN_OUT"
-
-run resolvectl statistics --json=pretty
-grep -qF "transactions" "$RUN_OUT"
-grep -qF "cache" "$RUN_OUT"
-grep -qF "dnssec" "$RUN_OUT"
-
-### Test resolvectl reset-statistics ###
-run resolvectl reset-statistics
-
-run resolvectl reset-statistics --json=pretty
-
-run resolvectl reset-statistics --json=short
-
-test "$(resolvectl --json=short query -t AAAA localhost)" == '{"key":{"class":1,"type":28,"name":"localhost"},"address":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]}'
-test "$(resolvectl --json=short query -t A localhost)" == '{"key":{"class":1,"type":1,"name":"localhost"},"address":[127,0,0,1]}'
-
-# Test ResolveRecord RR resolving via Varlink
-test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":1}' --json=short | jq -rc 'del(.rrs | .[] | .ifindex)')" == '{"rrs":[{"rr":{"key":{"class":1,"type":1,"name":"localhost"},"address":[127,0,0,1]},"raw":"CWxvY2FsaG9zdAAAAQABAAAAAAAEfwAAAQ=="}],"flags":786945}'
-test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":28}' --json=short | jq -rc 'del(.rrs | .[] | .ifindex)')" == '{"rrs":[{"rr":{"key":{"class":1,"type":28,"name":"localhost"},"address":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]},"raw":"CWxvY2FsaG9zdAAAHAABAAAAAAAQAAAAAAAAAAAAAAAAAAAAAQ=="}],"flags":786945}'
-
-# Ensure that reloading keeps the manually configured address
-{
-    echo "[Resolve]"
-    echo "DNS=8.8.8.8"
-} >/run/systemd/resolved.conf.d/reload.conf
-resolvectl dns dns0 1.1.1.1
-systemctl reload systemd-resolved.service
-resolvectl status
-resolvectl dns dns0 | grep -qF "1.1.1.1"
-# For some reason piping this last command to grep fails with:
-# 'resolvectl[1378]: Failed to print table: Broken pipe'
-# so use an intermediate file in /tmp/
-resolvectl >/tmp/output
-grep -qF "DNS Servers: 8.8.8.8" /tmp/output
-
-# Check if resolved exits cleanly.
-restart_resolved
-
-touch /testok
diff --git a/test/units/testsuite-76.service b/test/units/testsuite-76.service
deleted file mode 100644 (file)
index 3c8a9e8..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-76-SYSCTL
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-76.sh b/test/units/testsuite-76.sh
deleted file mode 100755 (executable)
index 855d0ef..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-export SYSTEMD_LOG_LEVEL=debug
-
-echo "foo.bar=42" >/tmp/foo.conf
-assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
-assert_rc 1 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
-
-echo "-foo.foo=42" >/tmp/foo.conf
-assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
-assert_rc 0 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
-
-if ! systemd-detect-virt --quiet --container; then
-    ip link add hoge type dummy
-    udevadm wait /sys/class/net/hoge
-
-    cat >/tmp/foo.conf <<EOF
-net.ipv4.conf.*.drop_gratuitous_arp=1
-net.ipv4.*.*.bootp_relay=1
-net.ipv4.aaa.*.disable_policy=1
-EOF
-
-    echo 0 >/proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp
-    echo 0 >/proc/sys/net/ipv4/conf/hoge/bootp_relay
-    echo 0 >/proc/sys/net/ipv4/conf/hoge/disable_policy
-
-    assert_rc 0 /usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/hoge /tmp/foo.conf
-    assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp)" "1"
-    assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/bootp_relay)" "1"
-    assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/disable_policy)" "0"
-fi
-
-touch /testok
diff --git a/test/units/testsuite-78.service b/test/units/testsuite-78.service
deleted file mode 100644 (file)
index 05f3eff..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-78-SIGQUEUE
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
diff --git a/test/units/testsuite-78.sh b/test/units/testsuite-78.sh
deleted file mode 100755 (executable)
index 46afd3c..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-if ! env --block-signal=SIGUSR1 true 2> /dev/null ; then
-    echo "env tool too old, can't block signals, skipping test." >&2
-    echo OK >/testok
-    exit 0
-fi
-
-systemd-analyze log-level debug
-
-UNIT="test-sigqueue-$RANDOM.service"
-
-systemd-run -u "$UNIT" -p Type=notify -p DynamicUser=1 -- env --block-signal=SIGRTMIN+7 systemd-notify --exec --ready \; sleep infinity
-
-systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT"
-systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT"
-systemctl kill --kill-whom=main --kill-value=7 --signal=SIGRTMIN+7 "$UNIT"
-systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT"
-systemctl kill --kill-whom=main --kill-value=32 --signal=SIGRTMIN+7 "$UNIT"
-systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT"
-
-# We simply check that six signals are queued now. There's no easy way to check
-# from shell which ones those are, hence we don't check that.
-P=$(systemctl show -P MainPID "$UNIT")
-
-test "$(grep SigQ: /proc/"$P"/status | cut -d: -f2 | cut -d/ -f1)" -eq 6
-
-systemctl stop $UNIT
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-79.service b/test/units/testsuite-79.service
deleted file mode 100644 (file)
index f2d24df..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-79-MEMPRESS
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-MemoryAccounting=1
diff --git a/test/units/testsuite-79.sh b/test/units/testsuite-79.sh
deleted file mode 100755 (executable)
index 205f7f3..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# We not just test if the file exists, but try to read from it, since if
-# CONFIG_PSI_DEFAULT_DISABLED is set in the kernel the file will exist and can
-# be opened, but any read()s will fail with EOPNOTSUPP, which we want to
-# detect.
-if ! cat /proc/pressure/memory >/dev/null ; then
-    echo "kernel too old, has no PSI." >&2
-    echo OK >/testok
-    exit 0
-fi
-
-systemd-analyze log-level debug
-
-CGROUP=/sys/fs/cgroup/"$(systemctl show testsuite-79.service -P ControlGroup)"
-test -d "$CGROUP"
-
-if ! test -f "$CGROUP"/memory.pressure ; then
-    echo "No memory accounting/PSI delegated via cgroup, can't test." >&2
-    echo OK >/testok
-    exit 0
-fi
-
-UNIT="test-mempress-$RANDOM.service"
-SCRIPT="/tmp/mempress-$RANDOM.sh"
-
-cat >"$SCRIPT" <<'EOF'
-#!/bin/bash
-
-set -ex
-
-export
-id
-
-test -n "$MEMORY_PRESSURE_WATCH"
-test "$MEMORY_PRESSURE_WATCH" != /dev/null
-test -w "$MEMORY_PRESSURE_WATCH"
-
-ls -al "$MEMORY_PRESSURE_WATCH"
-
-EXPECTED="$(echo -n -e "some 123000 2000000\x00" | base64)"
-
-test "$EXPECTED" = "$MEMORY_PRESSURE_WRITE"
-
-EOF
-
-chmod +x "$SCRIPT"
-
-systemd-run -u "$UNIT" -p Type=exec -p ProtectControlGroups=1 -p DynamicUser=1 -p MemoryPressureWatch=on -p MemoryPressureThresholdSec=123ms -p BindPaths=$SCRIPT --wait "$SCRIPT"
-
-rm "$SCRIPT"
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-80.service b/test/units/testsuite-80.service
deleted file mode 100644 (file)
index 4c7f5d5..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-80-NOTIFYACCESS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-80.sh b/test/units/testsuite-80.sh
deleted file mode 100755 (executable)
index 97b222a..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2016
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-mkfifo /tmp/syncfifo1 /tmp/syncfifo2
-
-sync_in() {
-    read -r x < /tmp/syncfifo1
-    test "$x" = "$1"
-}
-
-sync_out() {
-    echo "$1" > /tmp/syncfifo2
-}
-
-export SYSTEMD_LOG_LEVEL=debug
-
-systemctl --no-block start notify.service
-
-sync_in a
-
-assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all"
-assert_eq "$(systemctl show notify.service -p StatusText --value)" "Test starts"
-
-sync_out b
-sync_in c
-
-assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "main"
-assert_eq "$(systemctl show notify.service -p StatusText --value)" "Sending READY=1 in an unprivileged process"
-assert_rc 3 systemctl --quiet is-active notify.service
-
-sync_out d
-sync_in e
-
-systemctl --quiet is-active notify.service
-assert_eq "$(systemctl show notify.service -p StatusText --value)" "OK"
-assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "none"
-
-systemctl stop notify.service
-assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all"
-
-rm /tmp/syncfifo1 /tmp/syncfifo2
-
-# Now test basic fdstore behaviour
-
-MYSCRIPT="/tmp/myscript$RANDOM.sh"
-cat >> "$MYSCRIPT" <<'EOF'
-#!/usr/bin/env bash
-set -eux
-set -o pipefail
-test "$FDSTORE" -eq 7
-N="/tmp/$RANDOM"
-echo $RANDOM > "$N"
-systemd-notify --fd=4 --fdname=quux --pid=parent 4< "$N"
-rm "$N"
-systemd-notify --ready
-exec sleep infinity
-EOF
-
-chmod +x "$MYSCRIPT"
-
-MYUNIT="myunit$RANDOM.service"
-systemd-run -u "$MYUNIT" -p Type=notify -p FileDescriptorStoreMax=7 "$MYSCRIPT"
-
-test "$(systemd-analyze fdstore "$MYUNIT" | wc -l)" -eq 2
-systemd-analyze fdstore "$MYUNIT" --json=short
-systemd-analyze fdstore "$MYUNIT" --json=short | grep -P -q '\[{"fdname":"quux","type":.*,"devno":\[.*\],"inode":.*,"rdevno":null,"path":"/tmp/.*","flags":"ro"}\]'
-
-systemctl stop "$MYUNIT"
-rm "$MYSCRIPT"
-
-systemd-analyze log-level debug
-
-# Test fdstore pinning (this will pull in fdstore-pin.service fdstore-nopin.service)
-systemctl start fdstore-pin.target
-
-assert_eq "$(systemctl show fdstore-pin.service -P FileDescriptorStorePreserve)" yes
-assert_eq "$(systemctl show fdstore-nopin.service -P FileDescriptorStorePreserve)" restart
-assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
-assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
-assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
-assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1
-
-# The file descriptor store should survive service restarts
-systemctl restart fdstore-pin.service fdstore-nopin.service
-
-assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
-assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1
-assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
-assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
-
-# It should not survive the service stop plus a later start (unless pinned)
-systemctl stop fdstore-pin.service fdstore-nopin.service
-
-assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
-assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
-assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned
-assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
-
-systemctl start fdstore-pin.service fdstore-nopin.service
-
-assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
-assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
-assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running
-assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running
-
-systemctl stop fdstore-pin.service fdstore-nopin.service
-
-assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1
-assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
-assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned
-assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
-
-systemctl clean fdstore-pin.service --what=fdstore
-
-assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 0
-assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0
-assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead
-assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead
-
-touch /testok
diff --git a/test/units/testsuite-81.debug-generator.sh b/test/units/testsuite-81.debug-generator.sh
deleted file mode 100755 (executable)
index ef1e205..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/generator-utils.sh
-. "$(dirname "$0")/generator-utils.sh"
-
-GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-debug-generator"
-OUT_DIR="$(mktemp -d /tmp/debug-generator.XXX)"
-
-at_exit() {
-    rm -frv "${OUT_DIR:?}"
-}
-
-trap at_exit EXIT
-
-test -x "${GENERATOR_BIN:?}"
-
-# Potential FIXME:
-#   - debug-generator should gracefully handle duplicated mask/wants
-#   - also, handle gracefully empty mask/wants
-ARGS=(
-    "systemd.mask=masked-no-suffix"
-    "systemd.mask=masked.service"
-    "systemd.mask=masked.socket"
-    "systemd.wants=wanted-no-suffix"
-    "systemd.wants=wanted.service"
-    "systemd.wants=wanted.mount"
-    "rd.systemd.mask=masked-initrd.service"
-    "rd.systemd.wants=wanted-initrd.service"
-)
-
-# Regular (non-initrd) scenario
-#
-: "debug-shell: regular"
-CMDLINE="ro root=/ ${ARGS[*]} rd.systemd.debug_shell"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null
-link_eq "$OUT_DIR/early/masked.service" /dev/null
-link_eq "$OUT_DIR/early/masked.socket" /dev/null
-link_endswith "$OUT_DIR/early/default.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service
-link_endswith "$OUT_DIR/early/default.target.wants/wanted.service" /lib/systemd/system/wanted.service
-link_endswith "$OUT_DIR/early/default.target.wants/wanted.mount" /lib/systemd/system/wanted.mount
-# Following stuff should be ignored, as it's prefixed with rd.
-test ! -h "$OUT_DIR/early/masked-initrd.service"
-test ! -h "$OUT_DIR/early/default.target.wants/wants-initrd.service"
-test ! -h "$OUT_DIR/early/default.target.wants/debug-shell.service"
-test ! -d "$OUT_DIR/early/initrd.target.wants"
-
-# Let's re-run the generator with systemd.debug_shell that should be honored
-: "debug-shell: regular + systemd.debug_shell"
-CMDLINE="$CMDLINE systemd.debug_shell"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
-
-# Same thing, but with custom tty
-: "debug-shell: regular + systemd.debug_shell=/dev/tty666"
-CMDLINE="$CMDLINE systemd.debug_shell=/dev/tty666"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
-grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf"
-
-# Same thing, but with custom tty using systemd.default_debug_tty
-: "debug-shell: regular + systemd.default_debug_tty=/dev/tty666 systemd.debug_shell=yes"
-CMDLINE="$CMDLINE systemd.default_debug_tty=/dev/tty666 systemd.debug_shell=yes"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
-grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf"
-
-# Now override the default target via systemd.unit=
-: "debug-shell: regular + systemd.unit="
-CMDLINE="$CMDLINE systemd.unit=my-fancy.target"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null
-link_eq "$OUT_DIR/early/masked.service" /dev/null
-link_eq "$OUT_DIR/early/masked.socket" /dev/null
-link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service
-link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.service" /lib/systemd/system/wanted.service
-link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.mount" /lib/systemd/system/wanted.mount
-link_endswith "$OUT_DIR/early/my-fancy.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
-test ! -d "$OUT_DIR/early/default.target.wants"
-
-
-# Initrd scenario
-: "debug-shell: initrd"
-CMDLINE="ro root=/ ${ARGS[*]} systemd.debug_shell"
-SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null
-link_endswith "$OUT_DIR/early/initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service
-# The non-initrd stuff (i.e. without the rd. suffix) should be ignored in
-# this case
-test ! -h "$OUT_DIR/early/masked-no-suffix.service"
-test ! -h "$OUT_DIR/early/masked.service"
-test ! -h "$OUT_DIR/early/masked.socket"
-test ! -h "$OUT_DIR/early/initrd.target.wants/debug-shell.service"
-test ! -d "$OUT_DIR/early/default.target.wants"
-
-# Again, but with rd.systemd.debug_shell
-: "debug-shell: initrd + rd.systemd.debug_shell"
-CMDLINE="$CMDLINE rd.systemd.debug_shell"
-SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_endswith "$OUT_DIR/early/initrd.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
-
-# Override the default target
-: "debug-shell: initrd + rd.systemd.unit"
-CMDLINE="$CMDLINE rd.systemd.unit=my-fancy-initrd.target"
-SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null
-link_endswith "$OUT_DIR/early/my-fancy-initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service
-test ! -d "$OUT_DIR/early/initrd.target.wants"
diff --git a/test/units/testsuite-81.environment-d-generator.sh b/test/units/testsuite-81.environment-d-generator.sh
deleted file mode 100755 (executable)
index 5bc3978..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/generator-utils.sh
-. "$(dirname "$0")/generator-utils.sh"
-
-GENERATOR_BIN="/usr/lib/systemd/user-environment-generators/30-systemd-environment-d-generator"
-CONFIG_FILE="/run/environment.d/99-test.conf"
-OUT_FILE="$(mktemp)"
-
-at_exit() {
-    set +e
-    rm -frv "${CONFIG_FILE:?}" "${OUT_FILE:?}"
-    systemctl -M testuser@.host --user daemon-reload
-}
-
-trap at_exit EXIT
-
-test -x "${GENERATOR_BIN:?}"
-mkdir -p /run/environment.d/
-
-cat >"$CONFIG_FILE" <<EOF
-
-\t\n\t
-3
-=
-    =
-INVALID
-ALSO_INVALID=
-EMPTY_INVALID=""
-3_INVALID=foo
-xxxx xx xxxxxx
-# This is a comment
-$(printf "%.0sx" {0..4096})=
-SIMPLE=foo
-REF=\$SIMPLE
-ALSO_REF=\${SIMPLE}
-DEFAULT="\${NONEXISTENT:-default value}"
-ALTERNATE="\${SIMPLE:+alternate value}"
-LIST=foo,bar,baz
-SIMPLE=redefined
-UNASSIGNED=\$FOO_BAR_BAZ
-VERY_LONG="very $(printf "%.0sx" {0..4096})= long string"
-EOF
-
-# Source env assignments from a file and check them - do this in a subshell
-# to not pollute the test environment
-check_environment() {(
-    # shellcheck source=/dev/null
-    source "${1:?}"
-
-    [[ "$SIMPLE" == "redefined" ]]
-    [[ "$REF" == "foo" ]]
-    [[ "$ALSO_REF" == "foo" ]]
-    [[ "$DEFAULT" == "default value" ]]
-    [[ "$ALTERNATE" == "alternate value" ]]
-    [[ "$LIST" == "foo,bar,baz" ]]
-    [[ "$VERY_LONG" =~ ^very\  ]]
-    [[ "$VERY_LONG" =~ \ long\ string$ ]]
-    [[ -z "$UNASSIGNED" ]]
-    [[ ! -v INVALID ]]
-    [[ ! -v ALSO_INVALID ]]
-    [[ ! -v EMPTY_INVALID ]]
-    [[ ! -v 3_INVALID ]]
-)}
-
-# Check the output by directly calling the generator
-"$GENERATOR_BIN" | tee "$OUT_FILE"
-check_environment "$OUT_FILE"
-: >"$OUT_FILE"
-
-# Check if the generator is correctly called in a user session
-systemctl -M testuser@.host --user daemon-reload
-systemctl -M testuser@.host --user show-environment | tee "$OUT_FILE"
-check_environment "$OUT_FILE"
-
-(! "$GENERATOR_BIN" foo)
diff --git a/test/units/testsuite-81.fstab-generator.sh b/test/units/testsuite-81.fstab-generator.sh
deleted file mode 100755 (executable)
index 0c1b27e..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235,SC2233
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/generator-utils.sh
-. "$(dirname "$0")/generator-utils.sh"
-
-GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-fstab-generator"
-NETWORK_FS_RX="^(afs|ceph|cifs|gfs|gfs2|ncp|ncpfs|nfs|nfs4|ocfs2|orangefs|pvfs2|smb3|smbfs|davfs|glusterfs|lustre|sshfs)$"
-OUT_DIR="$(mktemp -d /tmp/fstab-generator.XXX)"
-FSTAB="$(mktemp)"
-
-at_exit() {
-    mountpoint -q /proc/cmdline && umount /proc/cmdline
-    rm -fr "${OUT_DIR:?}" "${FSTAB:?}"
-}
-
-trap at_exit EXIT
-
-test -x "${GENERATOR_BIN:?}"
-
-FSTAB_GENERAL=(
-    # Valid entries
-    "/dev/test2     /nofail                             ext4        nofail 0 0"
-    "/dev/test3     /regular                            btrfs       defaults 0 0"
-    "/dev/test4     /x-systemd.requires                 xfs         x-systemd.requires=foo.service 0 0"
-    "/dev/test5     /x-systemd.before-after             xfs         x-systemd.before=foo.service,x-systemd.after=bar.mount 0 0"
-    "/dev/test6     /x-systemd.wanted-required-by       xfs         x-systemd.wanted-by=foo.service,x-systemd.required-by=bar.device 0 0"
-    "/dev/test7     /x-systemd.requires-mounts-for      xfs         x-systemd.requires-mounts-for=/foo/bar/baz 0 0"
-    "/dev/test8     /x-systemd.automount-idle-timeout   vfat        x-systemd.automount,x-systemd.idle-timeout=50s 0 0"
-    "/dev/test9     /x-systemd.makefs                   xfs         x-systemd.makefs 0 0"
-    "/dev/test10    /x-systemd.growfs                   xfs         x-systemd.growfs 0 0"
-    "/dev/test11    /_netdev                            ext4        defaults,_netdev 0 0"
-    "/dev/test12    /_rwonly                            ext4        x-systemd.rw-only 0 0"
-    "/dev/test13    /chaos1                             zfs         x-systemd.rw-only,x-systemd.requires=hello.service,x-systemd.after=my.device 0 0"
-    "/dev/test14    /chaos2                             zfs         x.systemd.wanted-by=foo.service,x-systemd.growfs,x-systemd.makefs 0 0"
-    "/dev/test15    /fstype/auto                        auto        defaults 0 0"
-    "/dev/test16    /fsck/me                            ext4        defaults 0 1"
-    "/dev/test17    /also/fsck/me                       ext4        defaults,x-systemd.requires-mounts-for=/var/lib/foo 0 99"
-    "/dev/test18    /swap                               swap        defaults 0 0"
-    "/dev/test19    /swap/makefs                        swap        defaults,x-systemd.makefs 0 0"
-    "/dev/test20    /var                                xfs         defaults,x-systemd.device-timeout=1h 0 0"
-    "/dev/test21    /usr                                ext4        defaults 0 1"
-    "/dev/test22    /initrd/mount                       ext2        defaults,x-systemd.rw-only,x-initrd.mount 0 1"
-    "/dev/test23    /initrd/mount/nofail                ext3        defaults,nofail,x-initrd.mount 0 1"
-    "/dev/test24    /initrd/mount/deps                  ext4        x-initrd.mount,x-systemd.before=early.service,x-systemd.after=late.service 0 1"
-
-    # Incomplete, but valid entries
-    "/dev/incomplete1 /incomplete1"
-    "/dev/incomplete2 /incomplete2                      ext4"
-    "/dev/incomplete3 /incomplete3                      ext4        defaults"
-    "/dev/incomplete4 /incomplete4                      ext4        defaults 0"
-
-    # Remote filesystems
-    "/dev/remote1   /nfs                                nfs         bg 0 0"
-    "/dev/remote2   /nfs4                               nfs4        bg 0 0"
-    "bar.tld:/store /remote/storage                     nfs         ro,x-systemd.wanted-by=store.service 0 0"
-    "user@host.tld:/remote/dir /remote/top-secret       sshfs       rw,x-systemd.before=naughty.service 0 0"
-    "foo.tld:/hello /hello/world                        ceph        defaults 0 0"
-    "//192.168.0.1/storage /cifs-storage                cifs        automount,nofail 0 0"
-)
-
-FSTAB_GENERAL_ROOT=(
-    # rootfs with bunch of options we should ignore and fsck enabled
-    "/dev/test1     /                                   ext4        noauto,nofail,x-systemd.automount,x-systemd.wanted-by=foo,x-systemd.required-by=bar 0 1"
-    "${FSTAB_GENERAL[@]}"
-)
-
-FSTAB_MINIMAL=(
-    "/dev/loop1     /foo/bar                            ext3        defaults 0 0"
-)
-
-FSTAB_DUPLICATE=(
-    "/dev/dup1     /       ext4 defaults 0 1"
-    "/dev/dup2     /       ext4 defaults,x-systemd.requires=foo.mount 0 2"
-)
-
-FSTAB_INVALID=(
-    # Ignored entries
-    "/dev/ignored1  /sys/fs/cgroup/foo                  ext4        defaults    0 0"
-    "/dev/ignored2  /sys/fs/selinux                     ext4        defaults    0 0"
-    "/dev/ignored3  /dev/console                        ext4        defaults    0 0"
-    "/dev/ignored4  /proc/kmsg                          ext4        defaults    0 0"
-    "/dev/ignored5  /proc/sys                           ext4        defaults    0 0"
-    "/dev/ignored6  /proc/sys/kernel/random/boot_id     ext4        defaults    0 0"
-    "/dev/ignored7  /run/host                           ext4        defaults    0 0"
-    "/dev/ignored8  /run/host/foo                       ext4        defaults    0 0"
-    "/dev/ignored9  /autofs                             autofs      defaults    0 0"
-    "/dev/invalid1  not-a-path                          ext4        defaults    0 0"
-    ""
-    "/dev/invalid1"
-    "                  "
-    "\\"
-    "$"
-)
-
-check_fstab_mount_units() {
-    local what where fstype opts passno unit
-    local item opt split_options filtered_options supp service device arg
-    local array_name="${1:?}"
-    local out_dir="${2:?}/normal"
-    # Get a reference to the array from its name
-    local -n fstab_entries="$array_name"
-
-    # Running the checks in a container is pretty much useless, since we don't
-    # generate any mounts, but don't skip the whole test to test the "skip"
-    # paths as well
-    in_container && return 0
-
-    for item in "${fstab_entries[@]}"; do
-        # Don't use a pipe here, as it would make the variables out of scope
-        read -r what where fstype opts _ passno <<< "$item"
-
-        # Skip non-initrd mounts in initrd
-        if in_initrd_host && ! [[ "$opts" =~ x-initrd.mount ]]; then
-            continue
-        fi
-
-        if [[ "$fstype" == swap ]]; then
-            unit="$(systemd-escape --suffix=swap --path "${what:?}")"
-            cat "$out_dir/$unit"
-
-            grep -qE "^What=$what$" "$out_dir/$unit"
-            if [[ "$opts" != defaults ]]; then
-                grep -qE "^Options=$opts$" "$out_dir/$unit"
-            fi
-
-            if [[ "$opts" =~ x-systemd.makefs ]]; then
-                service="$(systemd-escape --template=systemd-mkswap@.service --path "$what")"
-                test -e "$out_dir/$service"
-            fi
-
-            continue
-        fi
-
-        # If we're parsing host's fstab in initrd, prefix all mount targets
-        # with /sysroot
-        in_initrd_host && where="/sysroot${where:?}"
-        unit="$(systemd-escape --suffix=mount --path "${where:?}")"
-        cat "$out_dir/$unit"
-
-        # Check the general stuff
-        grep -qE "^What=$what$" "$out_dir/$unit"
-        grep -qE "^Where=$where$" "$out_dir/$unit"
-        if [[ -n "$fstype" ]] && [[ "$fstype" != auto ]]; then
-            grep -qE "^Type=$fstype$" "$out_dir/$unit"
-        fi
-        if [[ -n "$opts" ]] && [[ "$opts" != defaults ]]; then
-            # Some options are not propagated to the generated unit
-            if [[ "$where" == / || "$where" == /usr ]]; then
-                filtered_options="$(opt_filter "$opts" "(noauto|nofail|x-systemd.(wanted-by=|required-by=|automount|device-timeout=))")"
-            else
-                filtered_options="$(opt_filter "$opts" "^x-systemd.device-timeout=")"
-            fi
-
-            if [[ "${filtered_options[*]}" != defaults ]]; then
-                grep -qE "^Options=.*$filtered_options.*$" "$out_dir/$unit"
-            fi
-        fi
-
-        if ! [[ "$opts" =~ (noauto|x-systemd.(wanted-by=|required-by=|automount)) ]]; then
-            # We don't create the Requires=/Wants= symlinks for noauto/automount mounts
-            # and for mounts that use x-systemd.wanted-by=/required-by=
-            if in_initrd_host; then
-                if [[ "$where" == / ]] || ! [[ "$opts" =~ nofail ]]; then
-                    link_eq "$out_dir/initrd-fs.target.requires/$unit" "../$unit"
-                else
-                    link_eq "$out_dir/initrd-fs.target.wants/$unit" "../$unit"
-                fi
-            elif [[ "$fstype" =~ $NETWORK_FS_RX || "$opts" =~ _netdev ]]; then
-                # Units with network filesystems should have a Requires= dependency
-                # on the remote-fs.target, unless they use nofail or are an nfs "bg"
-                # mounts, in which case the dependency is downgraded to Wants=
-                if [[ "$opts" =~ nofail ]] || [[ "$fstype" =~ ^(nfs|nfs4) && "$opts" =~ bg ]]; then
-                    link_eq "$out_dir/remote-fs.target.wants/$unit" "../$unit"
-                else
-                    link_eq "$out_dir/remote-fs.target.requires/$unit" "../$unit"
-                fi
-            else
-                # Similarly, local filesystems should have a Requires= dependency on
-                # the local-fs.target, unless they use nofail, in which case the
-                # dependency is downgraded to Wants=. Rootfs is a special case,
-                # since we always ignore nofail there
-                if [[ "$where" == / ]] || ! [[ "$opts" =~ nofail ]]; then
-                    link_eq "$out_dir/local-fs.target.requires/$unit" "../$unit"
-                else
-                    link_eq "$out_dir/local-fs.target.wants/$unit" "../$unit"
-                fi
-            fi
-        fi
-
-        if [[ "${passno:=0}" -ne 0 ]]; then
-            # Generate systemd-fsck@.service dependencies, if applicable
-            if in_initrd && [[ "$where" == / || "$where" == /usr ]]; then
-                continue
-            fi
-
-            if [[ "$where" == / ]]; then
-                link_endswith "$out_dir/local-fs.target.wants/systemd-fsck-root.service" "/lib/systemd/system/systemd-fsck-root.service"
-            else
-                service="$(systemd-escape --template=systemd-fsck@.service --path "$what")"
-                grep -qE "^After=$service$" "$out_dir/$unit"
-                if [[ "$where" == /usr ]]; then
-                    grep -qE "^Wants=$service$" "$out_dir/$unit"
-                else
-                    grep -qE "^Requires=$service$" "$out_dir/$unit"
-                fi
-            fi
-        fi
-
-        # Check various x-systemd options
-        #
-        # First, split them into an array to make splitting them even further
-        # easier
-        IFS="," read -ra split_options <<< "$opts"
-        # and process them one by one.
-        #
-        # Note: the "machinery" below might (and probably does) miss some
-        #       combinations of supported options, so tread carefully
-        for opt in "${split_options[@]}"; do
-            if [[ "$opt" =~ ^x-systemd.requires= ]]; then
-                service="$(opt_get_arg "$opt")"
-                grep -qE "^Requires=$service$" "$out_dir/$unit"
-                grep -qE "^After=$service$" "$out_dir/$unit"
-            elif [[ "$opt" =~ ^x-systemd.before= ]]; then
-                service="$(opt_get_arg "$opt")"
-                grep -qE "^Before=$service$" "$out_dir/$unit"
-            elif [[ "$opt" =~ ^x-systemd.after= ]]; then
-                service="$(opt_get_arg "$opt")"
-                grep -qE "^After=$service$" "$out_dir/$unit"
-            elif [[ "$opt" =~ ^x-systemd.wanted-by= ]]; then
-                service="$(opt_get_arg "$opt")"
-                if [[ "$where" == / ]]; then
-                    # This option is ignored for rootfs mounts
-                    (! link_eq "$out_dir/$service.wants/$unit" "../$unit")
-                else
-                    link_eq "$out_dir/$service.wants/$unit" "../$unit"
-                fi
-            elif [[ "$opt" =~ ^x-systemd.required-by= ]]; then
-                service="$(opt_get_arg "$opt")"
-                if [[ "$where" == / ]]; then
-                    # This option is ignored for rootfs mounts
-                    (! link_eq "$out_dir/$service.requires/$unit" "../$unit")
-                else
-                    link_eq "$out_dir/$service.requires/$unit" "../$unit"
-                fi
-            elif [[ "$opt" =~ ^x-systemd.requires-mounts-for= ]]; then
-                arg="$(opt_get_arg "$opt")"
-                grep -qE "^RequiresMountsFor=$arg$" "$out_dir/$unit"
-            elif [[ "$opt" == x-systemd.device-bound ]]; then
-                # This is implied for fstab mounts
-                :
-            elif [[ "$opt" == x-systemd.automount ]]; then
-                # The $unit should have an accompanying automount unit
-                supp="$(systemd-escape --suffix=automount --path "$where")"
-                if [[ "$where" == / ]]; then
-                    # This option is ignored for rootfs mounts
-                    test ! -e "$out_dir/$supp"
-                    (! link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp")
-                else
-                    test -e "$out_dir/$supp"
-                    link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp"
-                fi
-            elif [[ "$opt" =~ ^x-systemd.idle-timeout= ]]; then
-                # The timeout applies to the automount unit, not the original
-                # mount one
-                arg="$(opt_get_arg "$opt")"
-                supp="$(systemd-escape --suffix=automount --path "$where")"
-                grep -qE "^TimeoutIdleSec=$arg$" "$out_dir/$supp"
-            elif [[ "$opt" =~ ^x-systemd.device-timeout= ]]; then
-                arg="$(opt_get_arg "$opt")"
-                device="$(systemd-escape --suffix=device --path "$what")"
-                grep -qE "^JobRunningTimeoutSec=$arg$" "$out_dir/${device}.d/50-device-timeout.conf"
-            elif [[ "$opt" == x-systemd.makefs ]]; then
-                service="$(systemd-escape --template=systemd-makefs@.service --path "$what")"
-                test -e "$out_dir/$service"
-                link_eq "$out_dir/${unit}.requires/$service" "../$service"
-            elif [[ "$opt" == x-systemd.rw-only ]]; then
-                grep -qE "^ReadWriteOnly=yes$" "$out_dir/$unit"
-            elif [[ "$opt" == x-systemd.growfs ]]; then
-                service="$(systemd-escape --template=systemd-growfs@.service --path "$where")"
-                link_endswith "$out_dir/${unit}.wants/$service" "/lib/systemd/system/systemd-growfs@.service"
-            elif [[ "$opt" == bg ]] && [[ "$fstype" =~ ^(nfs|nfs4)$ ]]; then
-                # We "convert" nfs bg mounts to fg, so we can do the job-control
-                # ourselves
-                grep -qE "^Options=.*\bx-systemd.mount-timeout=infinity\b" "$out_dir/$unit"
-                grep -qE "^Options=.*\bfg\b.*" "$out_dir/$unit"
-            elif [[ "$opt" =~ ^x-systemd\. ]]; then
-                echo >&2 "Unhandled mount option: $opt"
-                exit 1
-            fi
-        done
-    done
-}
-
-: "fstab-generator: regular"
-printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB"
-cat "$FSTAB"
-SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-check_fstab_mount_units FSTAB_GENERAL_ROOT "$OUT_DIR"
-
-# Skip the rest when running in a container, as it makes little sense to check
-# initrd-related stuff there and fstab-generator might have a bit strange
-# behavior during certain tests, like https://github.com/systemd/systemd/issues/27156
-if in_container; then
-    echo "Running in a container, skipping the rest of the fstab-generator tests..."
-    exit 0
-fi
-
-# In this mode we treat the entries as "regular" ones
-: "fstab-generator: initrd - initrd fstab"
-printf "%s\n" "${FSTAB_GENERAL[@]}" >"$FSTAB"
-cat "$FSTAB"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_SYSROOT_FSTAB=/dev/null check_fstab_mount_units FSTAB_GENERAL "$OUT_DIR"
-
-# In this mode we prefix the mount target with /sysroot and ignore all mounts
-# that don't have the x-initrd.mount flag
-: "fstab-generator: initrd - host fstab"
-printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB"
-cat "$FSTAB"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_GENERAL_ROOT "$OUT_DIR"
-
-# Check the default stuff that we (almost) always create in initrd
-: "fstab-generator: initrd default"
-SYSTEMD_PROC_CMDLINE="root=/dev/sda2" SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-test -e "$OUT_DIR/normal/sysroot.mount"
-test -e "$OUT_DIR/normal/systemd-fsck-root.service"
-link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount"
-link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount"
-
-: "fstab-generator: run as systemd-sysroot-fstab-check in initrd"
-ln -svf "$GENERATOR_BIN" /tmp/systemd-sysroot-fstab-check
-(! /tmp/systemd-sysroot-fstab-check foo)
-(! SYSTEMD_IN_INITRD=0 /tmp/systemd-sysroot-fstab-check)
-printf "%s\n" "${FSTAB_GENERAL[@]}" >"$FSTAB"
-SYSTEMD_IN_INITRD=1 SYSTEMD_SYSROOT_FSTAB="$FSTAB" /tmp/systemd-sysroot-fstab-check
-
-: "fstab-generator: duplicate"
-printf "%s\n" "${FSTAB_DUPLICATE[@]}" >"$FSTAB"
-cat "$FSTAB"
-(! SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR")
-
-: "fstab-generator: invalid"
-printf "%s\n" "${FSTAB_INVALID[@]}" >"$FSTAB"
-cat "$FSTAB"
-# Don't care about the exit code here
-SYSTEMD_PROC_CMDLINE="" SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR" || :
-# No mounts should get created here
-[[ "$(find "$OUT_DIR" -name "*.mount" | wc -l)" -eq 0 ]]
-
-: "fstab-generator: kernel args - fstab=0"
-printf "%s\n" "${FSTAB_MINIMAL[@]}" >"$FSTAB"
-SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-(! SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR")
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-(! SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR")
-
-: "fstab-generator: kernel args - rd.fstab=0"
-printf "%s\n" "${FSTAB_MINIMAL[@]}" >"$FSTAB"
-SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-(! SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR")
-
-: "fstab-generator: kernel args - systemd.swap=0"
-printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB"
-cat "$FSTAB"
-SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="systemd.swap=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-# No swap units should get created here
-[[ "$(find "$OUT_DIR" -name "*.swap" | wc -l)" -eq 0 ]]
-
-# Possible TODO
-#   - combine the rootfs & usrfs arguments and mix them with fstab entries
-#   - systemd.volatile=
-: "fstab-generator: kernel args - root= + rootfstype= + rootflags="
-# shellcheck disable=SC2034
-EXPECTED_FSTAB=(
-    "/dev/disk/by-label/rootfs  /    ext4    noexec,ro   0 1"
-)
-CMDLINE="root=LABEL=rootfs rootfstype=ext4 rootflags=noexec"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-# The /proc/cmdline here is a dummy value to tell the in_initrd_host() function
-# we're parsing host's fstab, but it's all on the kernel cmdline instead
-SYSTEMD_IN_INITRD=1 SYSTEMD_SYSROOT_FSTAB=/proc/cmdline check_fstab_mount_units EXPECTED_FSTAB "$OUT_DIR"
-
-# This is a very basic sanity test that involves manual checks, since adding it
-# to the check_fstab_mount_units() function would make it way too complex
-# (yet another possible TODO)
-: "fstab-generator: kernel args - mount.usr= + mount.usrfstype= + mount.usrflags="
-CMDLINE="mount.usr=UUID=be780f43-8803-4a76-9732-02ceda6e9808 mount.usrfstype=ext4 mount.usrflags=noexec,nodev"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-cat "$OUT_DIR/normal/sysroot-usr.mount" "$OUT_DIR/normal/sysusr-usr.mount"
-# The general idea here is to mount the device to /sysusr/usr and then
-# bind-mount /sysusr/usr to /sysroot/usr
-grep -qE "^What=/dev/disk/by-uuid/be780f43-8803-4a76-9732-02ceda6e9808$" "$OUT_DIR/normal/sysusr-usr.mount"
-grep -qE "^Where=/sysusr/usr$" "$OUT_DIR/normal/sysusr-usr.mount"
-grep -qE "^Type=ext4$" "$OUT_DIR/normal/sysusr-usr.mount"
-grep -qE "^Options=noexec,nodev,ro$" "$OUT_DIR/normal/sysusr-usr.mount"
-link_eq "$OUT_DIR/normal/initrd-usr-fs.target.requires/sysusr-usr.mount" "../sysusr-usr.mount"
-grep -qE "^What=/sysusr/usr$" "$OUT_DIR/normal/sysroot-usr.mount"
-grep -qE "^Where=/sysroot/usr$" "$OUT_DIR/normal/sysroot-usr.mount"
-grep -qE "^Options=bind$" "$OUT_DIR/normal/sysroot-usr.mount"
-link_eq "$OUT_DIR/normal/initrd-fs.target.requires/sysroot-usr.mount" "../sysroot-usr.mount"
diff --git a/test/units/testsuite-81.getty-generator.sh b/test/units/testsuite-81.getty-generator.sh
deleted file mode 100755 (executable)
index d1dd22c..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-set -eux
-set -o pipefail
-# Disable history expansion so we don't have to escape ! in strings below
-set +o histexpand
-
-# shellcheck source=test/units/generator-utils.sh
-. "$(dirname "$0")/generator-utils.sh"
-
-GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-getty-generator"
-OUT_DIR="$(mktemp -d /tmp/getty-generator.XXX)"
-
-at_exit() {
-    rm -frv "${OUT_DIR:?}"
-}
-
-trap at_exit EXIT
-
-test -x "${GENERATOR_BIN:?}"
-
-if in_container; then
-    # Do a limited test in a container, as writing to /dev is usually restrited
-    : "getty-generator: \$container_ttys env (container)"
-    # In a container we allow only /dev/pts/* ptys
-    PID1_ENVIRON="container_ttys=tty0 pts/0 /dev/tty0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-
-    # console-getty.service is always pulled in in containers
-    link_endswith "$OUT_DIR/normal/getty.target.wants/console-getty.service" "/lib/systemd/system/console-getty.service"
-    link_endswith "$OUT_DIR/normal/getty.target.wants/container-getty@0.service" "/lib/systemd/system/container-getty@.service"
-    test ! -e "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service"
-    test ! -h "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service"
-
-    exit 0
-fi
-
-DUMMY_ACTIVE_CONSOLES=(
-    "hvc99"
-    "xvc99"
-    "hvsi99"
-    "sclp_line99"
-    "ttysclp99"
-    "3270!tty99"
-    "dummy99"
-)
-DUMMY_INACTIVE_CONSOLES=(
-    "inactive99"
-    "xvc199"
-)
-DUMMY_CONSOLES=(
-    "${DUMMY_ACTIVE_CONSOLES[@]}"
-    "${DUMMY_INACTIVE_CONSOLES[@]}"
-)
-# Create a bunch of dummy consoles
-for console in "${DUMMY_CONSOLES[@]}"; do
-    mknod "/dev/$console" c 4 0
-done
-# Sneak in one "not-a-tty" console
-touch /dev/notatty99
-# Temporarily replace /sys/class/tty/console/active with our list of dummy
-# consoles so getty-generator can process them
-echo -ne "${DUMMY_ACTIVE_CONSOLES[@]}" /dev/notatty99  >/tmp/dummy-active-consoles
-mount -v --bind /tmp/dummy-active-consoles /sys/class/tty/console/active
-
-: "getty-generator: no arguments"
-# Sneak in an invalid value for $SYSTEMD_GETTY_AUTO to test things out
-PID1_ENVIRON="SYSTEMD_GETTY_AUTO=foo" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-for console in "${DUMMY_ACTIVE_CONSOLES[@]}"; do
-    unit="$(systemd-escape --template serial-getty@.service "$console")"
-    link_endswith "$OUT_DIR/normal/getty.target.wants/$unit" "/lib/systemd/system/serial-getty@.service"
-done
-for console in "${DUMMY_INACTIVE_CONSOLES[@]}" /dev/notatty99; do
-    unit="$(systemd-escape --template serial-getty@.service "$console")"
-    test ! -e "$OUT_DIR/normal/getty.target.wants/$unit"
-    test ! -h "$OUT_DIR/normal/getty.target.wants/$unit"
-done
-
-: "getty-generator: systemd.getty_auto=0 on kernel cmdline"
-SYSTEMD_PROC_CMDLINE="systemd.getty_auto=foo systemd.getty_auto=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
-
-: "getty-generator: SYSTEMD_GETTY_AUTO=0 in PID1's environment"
-PID1_ENVIRON="SYSTEMD_GETTY_AUTO=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
-
-# Cleanup
-umount /sys/class/tty/console/active --lazy
-rm -f "${DUMMY_CONSOLES[@]/#//dev/}" /dev/notatty99
diff --git a/test/units/testsuite-81.run-generator.sh b/test/units/testsuite-81.run-generator.sh
deleted file mode 100755 (executable)
index 9bd74ef..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/generator-utils.sh
-. "$(dirname "$0")/generator-utils.sh"
-
-GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-run-generator"
-OUT_DIR="$(mktemp -d /tmp/run-generator.XXX)"
-
-at_exit() {
-    rm -frv "${OUT_DIR:?}"
-}
-
-trap at_exit EXIT
-
-test -x "${GENERATOR_BIN:?}"
-
-check_kernel_cmdline_target() {
-    local out_dir="${1:?}/normal"
-
-    cat "$out_dir/kernel-command-line.target"
-    grep -qE "^Requires=kernel-command-line.service$" "$out_dir/kernel-command-line.target"
-    grep -qE "^After=kernel-command-line.service$" "$out_dir/kernel-command-line.target"
-
-    link_eq "$out_dir/default.target" "kernel-command-line.target"
-}
-
-: "run-generator: empty cmdline"
-SYSTEMD_PROC_CMDLINE="" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
-
-: "run-generator: single command"
-CMDLINE="systemd.run='echo hello world'"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-check_kernel_cmdline_target "$OUT_DIR"
-UNIT="$OUT_DIR/normal/kernel-command-line.service"
-cat "$UNIT"
-systemd-analyze verify --man=no --recursive-errors=no "$UNIT"
-grep -qE "^SuccessAction=exit$" "$UNIT"
-grep -qE "^FailureAction=exit$" "$UNIT"
-grep -qE "^ExecStart=echo hello world$" "$UNIT"
-
-: "run-generator: multiple commands + success/failure actions"
-ARGS=(
-    # These should be ignored
-    "systemd.run"
-    "systemd.run_success_action"
-    "systemd.run_failure_action"
-
-    # Set actions which we will overwrite later
-    "systemd.run_success_action="
-    "systemd.run_failure_action="
-
-    "systemd.run=/bin/false"
-    "systemd.run="
-    "systemd.run=/bin/true"
-    "systemd.run='echo this is a long string'"
-
-    "systemd.run_success_action=reboot"
-    "systemd.run_failure_action=poweroff-force"
-)
-CMDLINE="${ARGS[*]}"
-SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-check_kernel_cmdline_target "$OUT_DIR"
-UNIT="$OUT_DIR/normal/kernel-command-line.service"
-cat "$UNIT"
-systemd-analyze verify --man=no --recursive-errors=no "$UNIT"
-grep -qE "^SuccessAction=reboot$" "$UNIT"
-grep -qE "^FailureAction=poweroff-force$" "$UNIT"
-grep -qE "^ExecStart=/bin/false$" "$UNIT"
-grep -qE "^ExecStart=$" "$UNIT"
-grep -qE "^ExecStart=/bin/true$" "$UNIT"
-grep -qE "^ExecStart=echo this is a long string$" "$UNIT"
diff --git a/test/units/testsuite-81.service b/test/units/testsuite-81.service
deleted file mode 100644 (file)
index 3b697b3..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-81-GENERATORS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-81.sh b/test/units/testsuite-81.sh
deleted file mode 100755 (executable)
index 9c2a033..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/test-control.sh
-. "$(dirname "$0")"/test-control.sh
-
-run_subtests
-
-touch /testok
diff --git a/test/units/testsuite-81.system-update-generator.sh b/test/units/testsuite-81.system-update-generator.sh
deleted file mode 100755 (executable)
index 8ee1fee..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2235
-set -eux
-set -o pipefail
-
-# shellcheck source=test/units/generator-utils.sh
-. "$(dirname "$0")/generator-utils.sh"
-
-GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-system-update-generator"
-OUT_DIR="$(mktemp -d /tmp/system-update-generator-generator.XXX)"
-
-at_exit() {
-    rm -frv "${OUT_DIR:?}" /system-update
-}
-
-trap at_exit EXIT
-
-test -x "${GENERATOR_BIN:?}"
-
-rm -f /system-update
-
-: "system-update-generator: no /system-update flag"
-run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]]
-
-: "system-update-generator: with /system-update flag"
-touch /system-update
-run_and_list "$GENERATOR_BIN" "$OUT_DIR"
-link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target"
-
-: "system-update-generator: kernel cmdline warnings"
-# We should warn if the default target is overridden on the kernel cmdline
-# by a runlevel or systemd.unit=, but still generate the symlink
-SYSTEMD_PROC_CMDLINE="systemd.unit=foo.bar 3" run_and_list "$GENERATOR_BIN" "$OUT_DIR" |& tee /tmp/system-update-generator.log
-link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target"
-grep -qE "Offline system update overridden .* systemd.unit=" /tmp/system-update-generator.log
-grep -qE "Offline system update overridden .* runlevel" /tmp/system-update-generator.log
diff --git a/test/units/testsuite-82.service b/test/units/testsuite-82.service
deleted file mode 100644 (file)
index a8fc4f9..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-82-SOFTREBOOT
-DefaultDependencies=no
-After=basic.target
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-FileDescriptorStoreMax=3
-NotifyAccess=all
diff --git a/test/units/testsuite-82.sh b/test/units/testsuite-82.sh
deleted file mode 100755 (executable)
index 9747602..0000000
+++ /dev/null
@@ -1,257 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-set -o pipefail
-
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
-at_exit() {
-    # Since the soft-reboot drops the enqueued end.service, we won't shutdown
-    # the test VM if the test fails and have to wait for the watchdog to kill
-    # us (which may take quite a long time). Let's just forcibly kill the machine
-    # instead to save CI resources.
-    if [[ $? -ne 0 ]]; then
-        echo >&2 "Test failed, shutting down the machine..."
-        systemctl poweroff -ff
-    fi
-}
-
-trap at_exit EXIT
-
-systemd-analyze log-level debug
-
-export SYSTEMD_LOG_LEVEL=debug
-
-if [ -f /run/testsuite82.touch3 ]; then
-    echo "This is the fourth boot!"
-    systemd-notify --status="Fourth Boot"
-
-    test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 3
-
-    rm /run/testsuite82.touch3
-    mount
-    rmdir /original-root /run/nextroot
-
-    # Check that the fdstore entry still exists
-    test "$LISTEN_FDS" -eq 3
-    read -r x <&5
-    test "$x" = "oinkoink"
-
-    # Check that the surviving services are still around
-    test "$(systemctl show -P ActiveState testsuite-82-survive.service)" = "active"
-    test "$(systemctl show -P ActiveState testsuite-82-survive-argv.service)" = "active"
-    test "$(systemctl show -P ActiveState testsuite-82-nosurvive-sigterm.service)" != "active"
-    test "$(systemctl show -P ActiveState testsuite-82-nosurvive.service)" != "active"
-
-    # Check journals
-    journalctl -o short-monotonic --no-hostname --grep '(will soft-reboot|KILL|corrupt)'
-    assert_eq "$(journalctl -q -o short-monotonic -u systemd-journald.service --grep 'corrupt')" ""
-
-    # All succeeded, exit cleanly now
-
-elif [ -f /run/testsuite82.touch2 ]; then
-    echo "This is the third boot!"
-    systemd-notify --status="Third Boot"
-
-    test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 2
-
-    rm /run/testsuite82.touch2
-
-    # Check that the fdstore entry still exists
-    test "$LISTEN_FDS" -eq 2
-    read -r x <&4
-    test "$x" = "miaumiau"
-
-    # Upload another entry
-    T="/dev/shm/fdstore.$RANDOM"
-    echo "oinkoink" >"$T"
-    systemd-notify --fd=3 --pid=parent 3<"$T"
-    rm "$T"
-
-    # Check that the surviving services are still around
-    test "$(systemctl show -P ActiveState testsuite-82-survive.service)" = "active"
-    test "$(systemctl show -P ActiveState testsuite-82-survive-argv.service)" = "active"
-    test "$(systemctl show -P ActiveState testsuite-82-nosurvive-sigterm.service)" != "active"
-    test "$(systemctl show -P ActiveState testsuite-82-nosurvive.service)" != "active"
-
-    # Test that we really are in the new overlayfs root fs
-    read -r x </lower
-    test "$x" = "miep"
-    cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release
-    grep -q MARKER=1 /etc/os-release
-
-    # Switch back to the original root, away from the overlayfs
-    mount --bind /original-root /run/nextroot
-    mount
-
-    # Restart the unit that is not supposed to survive
-    systemd-run --collect --service-type=exec --unit=testsuite-82-nosurvive.service sleep infinity
-
-    # Now issue the soft reboot. We should be right back soon.
-    touch /run/testsuite82.touch3
-    systemctl --no-block soft-reboot
-
-    # Now block until the soft-boot killing spree kills us
-    exec sleep infinity
-
-elif [ -f /run/testsuite82.touch ]; then
-    echo "This is the second boot!"
-    systemd-notify --status="Second Boot"
-
-    test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 1
-
-    # Clean up what we created earlier
-    rm /run/testsuite82.touch
-
-    # Check that the fdstore entry still exists
-    test "$LISTEN_FDS" -eq 1
-    read -r x <&3
-    test "$x" = "wuffwuff"
-
-    # Check that we got a PrepareForShutdownWithMetadata signal with the right type
-    cat /run/testsuite82.signal
-    test "$(jq -r '.payload.data[1].type.data' </run/testsuite82.signal)" = "soft-reboot"
-
-    # Check that the system credentials survived the soft reboot.
-    test "$(systemd-creds cat --system kernelcmdlinecred)" = "uff"
-
-    # Upload another entry
-    T="/dev/shm/fdstore.$RANDOM"
-    echo "miaumiau" >"$T"
-    systemd-notify --fd=3 --pid=parent 3<"$T"
-    rm "$T"
-
-    # Check that the surviving services are still around
-    test "$(systemctl show -P ActiveState testsuite-82-survive.service)" = "active"
-    test "$(systemctl show -P ActiveState testsuite-82-survive-argv.service)" = "active"
-    test "$(systemctl show -P ActiveState testsuite-82-nosurvive-sigterm.service)" != "active"
-    test "$(systemctl show -P ActiveState testsuite-82-nosurvive.service)" != "active"
-
-    # This time we test the /run/nextroot/ root switching logic. (We synthesize a new rootfs from the old via overlayfs)
-    mkdir -p /run/nextroot /tmp/nextroot-lower /original-root
-    mount -t tmpfs tmpfs /tmp/nextroot-lower
-    echo miep >/tmp/nextroot-lower/lower
-
-    # Copy os-release away, so that we can manipulate it and check that it is updated in the propagate
-    # directory across soft reboots. Try to cover corner cases by truncating it.
-    mkdir -p /tmp/nextroot-lower/usr/lib
-    grep ID /etc/os-release >/tmp/nextroot-lower/usr/lib/os-release
-    echo MARKER=1 >>/tmp/nextroot-lower/usr/lib/os-release
-    cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release
-    (! grep -q MARKER=1 /etc/os-release)
-
-    mount -t overlay nextroot /run/nextroot -o lowerdir=/tmp/nextroot-lower:/,ro
-
-    # Bind our current root into the target so that we later can return to it
-    mount --bind / /run/nextroot/original-root
-
-    # Restart the unit that is not supposed to survive
-    systemd-run --collect --service-type=exec --unit=testsuite-82-nosurvive.service sleep infinity
-
-    # Now ensure there are no naming clashes and a bunch of transient units all succeed
-    for _ in $(seq 1 25); do
-        systemd-run --wait true
-    done
-
-    # Now issue the soft reboot. We should be right back soon. Given /run/nextroot exists, we should
-    # automatically do a softreboot instead of normal reboot.
-    touch /run/testsuite82.touch2
-    systemctl --no-block reboot
-
-    # Now block until the soft-boot killing spree kills us
-    exec sleep infinity
-else
-    # This is the first boot
-    systemd-notify --status="First Boot"
-
-    test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 0
-
-    # Let's upload an fd to the fdstore, so that we can verify fdstore passing works correctly
-    T="/dev/shm/fdstore.$RANDOM"
-    echo "wuffwuff" >"$T"
-    systemd-notify --fd=3 --pid=parent 3<"$T"
-    rm "$T"
-
-    survive_sigterm="/dev/shm/survive-sigterm-$RANDOM.sh"
-    cat >"$survive_sigterm" <<EOF
-#!/bin/bash
-trap "" TERM
-systemd-notify --ready
-rm "$survive_sigterm"
-exec sleep infinity
-EOF
-    chmod +x "$survive_sigterm"
-
-    survive_argv="/dev/shm/survive-argv-$RANDOM.sh"
-    cat >"$survive_argv" <<EOF
-#!/bin/bash
-systemd-notify --ready
-rm "$survive_argv"
-exec -a @sleep sleep infinity
-EOF
-    chmod +x "$survive_argv"
-    # This sets DefaultDependencies=no so that they remain running until the very end, and
-    # IgnoreOnIsolate=yes so that they aren't stopped via the "testsuite.target" isolation we do on next boot,
-    # and will be killed by the final sigterm/sigkill spree.
-    systemd-run --collect --service-type=notify -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=testsuite-82-nosurvive-sigterm.service "$survive_sigterm"
-    systemd-run --collect --service-type=exec -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=testsuite-82-nosurvive.service sleep infinity
-
-    # Ensure that the unit doesn't get deactivated by dependencies on the source file. Given it's a verity
-    # image that is already open, even if the tmpfs with the image goes away, the file will be pinned by the
-    # kernel and will keep working.
-    cp /usr/share/minimal_0.* /tmp/
-
-    # Configure these transient units to survive the soft reboot - they will not conflict with shutdown.target
-    # and it will be ignored on the isolate that happens in the next boot. The first will use argv[0][0] =
-    # '@', and the second will use SurviveFinalKillSignal=yes. Both should survive.
-    systemd-run --service-type=notify --unit=testsuite-82-survive-argv.service \
-        --property SurviveFinalKillSignal=no \
-        --property IgnoreOnIsolate=yes \
-        --property DefaultDependencies=no \
-        --property After=basic.target \
-        --property "Conflicts=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
-        --property "Before=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
-         "$survive_argv"
-    systemd-run --service-type=exec --unit=testsuite-82-survive.service \
-        --property TemporaryFileSystem="/run /tmp /var" \
-        --property RootImage=/tmp/minimal_0.raw \
-        --property BindReadOnlyPaths=/dev/log \
-        --property BindReadOnlyPaths=/run/systemd/journal/socket \
-        --property BindReadOnlyPaths=/run/systemd/journal/stdout \
-        --property SurviveFinalKillSignal=yes \
-        --property IgnoreOnIsolate=yes \
-        --property DefaultDependencies=no \
-        --property After=basic.target \
-        --property "Conflicts=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
-        --property "Before=reboot.target kexec.target poweroff.target halt.target emergency.target rescue.target" \
-        sleep infinity
-
-    # Check that we can set up an inhibitor, and that busctl monitor sees the
-    # PrepareForShutdownWithMetadata signal and that it says 'soft-reboot'.
-    systemd-run --unit busctl.service --service-type=exec --property StandardOutput=file:/run/testsuite82.signal \
-        busctl monitor --json=pretty --match 'sender=org.freedesktop.login1,path=/org/freedesktop/login1,interface=org.freedesktop.login1.Manager,member=PrepareForShutdownWithMetadata,type=signal'
-    systemd-run --unit inhibit.service --service-type=exec \
-        systemd-inhibit --what=shutdown --who=test --why=test --mode=delay \
-            sleep infinity
-
-    # Enqueue a bunch of failing units to try and trigger the transient name clash that happens due to D-Bus
-    # being restarted and the "unique" bus IDs not being unique across restarts
-    for _ in $(seq 1 25); do
-        # Use --wait to ensure we connect to the system bus instead of the private bus (otherwise a UUID is
-        # used instead of the bus ID)
-        systemd-run --wait false || true
-    done
-
-    # Now issue the soft reboot. We should be right back soon.
-    touch /run/testsuite82.touch
-    systemctl --no-block --check-inhibitors=yes soft-reboot
-
-    # Now block until the soft-boot killing spree kills us
-    exec sleep infinity
-fi
-
-systemd-analyze log-level info
-
-touch /testok
-systemctl --no-block exit 123
diff --git a/test/units/testsuite-83.service b/test/units/testsuite-83.service
deleted file mode 100644 (file)
index 55ebb45..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-83-BTRFS
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-83.sh b/test/units/testsuite-83.sh
deleted file mode 100755 (executable)
index 4f10ae7..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-FSTYPE="$(stat --file-system --format "%T" /)"
-
-if [[ "$FSTYPE" != "btrfs" ]]; then
-    echo "Root filesystem is $FSTYPE instead of btrfs, skipping"
-    exit 77
-fi
-
-TEST_BTRFS_OFFSET=/usr/lib/systemd/tests/unit-tests/manual/test-btrfs-physical-offset
-
-SWAPFILE=/var/tmp/swapfile
-
-btrfs filesystem mkswapfile -s 10m "$SWAPFILE"
-sync -f "$SWAPFILE"
-
-offset_btrfs_progs="$(btrfs inspect-internal map-swapfile -r "$SWAPFILE")"
-echo "btrfs-progs: $offset_btrfs_progs"
-
-offset_btrfs_util="$("$TEST_BTRFS_OFFSET" "$SWAPFILE")"
-echo "btrfs-util: $offset_btrfs_util"
-
-(( offset_btrfs_progs == offset_btrfs_util ))
-
-rm -f "$SWAPFILE"
-
-/usr/lib/systemd/tests/unit-tests/manual/test-btrfs
-
-touch /testok
diff --git a/test/units/testsuite-84.service b/test/units/testsuite-84.service
deleted file mode 100644 (file)
index 2c25770..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-84-STORAGETM
-After=multi-user.target
-
-[Service]
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-84.sh b/test/units/testsuite-84.sh
deleted file mode 100755 (executable)
index eae87d5..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-modprobe -v nvmet-tcp
-modprobe -v nvme-tcp
-
-systemctl start sys-kernel-config.mount
-
-dd if=/dev/urandom of=/var/tmp/storagetm.test bs=1024 count=10240
-
-NVME_UUID="$(cat /proc/sys/kernel/random/uuid)"
-systemd-run -u teststoragetm.service -p Type=notify -p "Environment=SYSTEMD_NVME_UUID=${NVME_UUID:?}" /usr/lib/systemd/systemd-storagetm /var/tmp/storagetm.test --nqn=quux
-NVME_DEVICE="/dev/disk/by-id/nvme-uuid.${NVME_UUID:?}"
-
-nvme connect-all -t tcp -a 127.0.0.1 -s 16858 --hostid="$(cat /proc/sys/kernel/random/uuid)"
-udevadm wait --settle "$NVME_DEVICE"
-
-dd if="$NVME_DEVICE" bs=1024 | cmp /var/tmp/storagetm.test -
-
-nvme disconnect-all
-systemctl stop teststoragetm.service
-rm /var/tmp/storagetm.test
-
-touch /testok
index cc9d5ec48f6fe3673ad83b918b1acafb74e868c8..f5f6786d0f6fde2aee43463fc6f92600a15e7712 100755 (executable)
@@ -156,13 +156,13 @@ coverage_create_nspawn_dropin() {
 create_dummy_container() {
     local root="${1:?}"
 
-    if [[ ! -d /usr/share/testsuite-13-container-template ]]; then
+    if [[ ! -d /usr/share/TEST-13-NSPAWN-container-template ]]; then
         echo >&2 "Missing container template, probably not running in TEST-13-NSPAWN?"
         exit 1
     fi
 
     mkdir -p "$root"
-    cp -a /usr/share/testsuite-13-container-template/* "$root"
+    cp -a /usr/share/TEST-13-NSPAWN-container-template/* "$root"
     coverage_create_nspawn_dropin "$root"
 }