summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:40 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:40 +0000
commitfc53809803cd2bc2434e312b19a18fa36776da12 (patch)
treeb4b43bd6538f51965ce32856e9c053d0f90919c8 /test
parentAdding upstream version 255.5. (diff)
downloadsystemd-fc53809803cd2bc2434e312b19a18fa36776da12.tar.xz
systemd-fc53809803cd2bc2434e312b19a18fa36776da12.zip
Adding upstream version 256.upstream/256
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--mkosi.images/minimal-0/mkosi.extra/opt/some_file (renamed from test/testsuite-07.units/issue2467.socket)2
-rw-r--r--test/README.testsuite369
-rw-r--r--test/TEST-01-BASIC/meson.build7
-rwxr-xr-xtest/TEST-01-BASIC/test.sh10
-rw-r--r--test/TEST-02-UNITTESTS/meson.build16
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.service (renamed from test/testsuite-03.units/always-activating.service)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.socket (renamed from test/testsuite-03.units/always-activating.socket)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.service (renamed from test/testsuite-03.units/fails-on-restart-restartdirect.service)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.target (renamed from test/testsuite-03.units/fails-on-restart-restartdirect.target)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.service (renamed from test/testsuite-03.units/fails-on-restart.service)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.target (renamed from test/testsuite-03.units/fails-on-restart.target)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/hello-after-sleep.target (renamed from test/testsuite-03.units/hello-after-sleep.target)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/hello.service (renamed from test/testsuite-03.units/hello.service)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-and-pullin.target (renamed from test/testsuite-03.units/propagatestopto-and-pullin.target)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-indirect.target (renamed from test/testsuite-03.units/propagatestopto-indirect.target)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-only.target (renamed from test/testsuite-03.units/propagatestopto-only.target)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/sleep-infinity-simple.service (renamed from test/testsuite-03.units/sleep-infinity-simple.service)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/sleep.service (renamed from test/testsuite-03.units/sleep.service)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.service (renamed from test/testsuite-03.units/succeeds-on-restart-restartdirect.service)2
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.target (renamed from test/testsuite-03.units/succeeds-on-restart-restartdirect.target)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.service (renamed from test/testsuite-03.units/succeeds-on-restart.service)2
-rwxr-xr-xtest/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.sh (renamed from test/testsuite-03.units/succeeds-on-restart.sh)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.target (renamed from test/testsuite-03.units/succeeds-on-restart.target)0
-rw-r--r--test/TEST-03-JOBS/TEST-03-JOBS.units/unstoppable.service (renamed from test/testsuite-03.units/unstoppable.service)0
-rw-r--r--test/TEST-03-JOBS/meson.build9
-rw-r--r--test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/delegated-cgroup-filtering.service (renamed from test/testsuite-04.units/delegated-cgroup-filtering.service)3
-rw-r--r--test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/forever-print-hola.service (renamed from test/testsuite-04.units/forever-print-hola.service)0
-rw-r--r--test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/logs-filtering.service11
-rw-r--r--test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/silent-success.service11
-rw-r--r--test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/verbose-success.service13
-rw-r--r--test/TEST-04-JOURNAL/meson.build12
-rwxr-xr-xtest/TEST-04-JOURNAL/test.sh5
-rw-r--r--test/TEST-05-RLIMITS/meson.build7
-rw-r--r--test/TEST-06-SELINUX/TEST-06-SELINUX.units/hola.service (renamed from test/testsuite-06.units/hola.service)0
-rw-r--r--test/TEST-06-SELINUX/meson.build13
-rw-r--r--test/TEST-07-PID1/TEST-07-PID1.service (renamed from test/units/testsuite-07.service)3
-rw-r--r--test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.service (renamed from test/testsuite-07.units/issue14566-repro.service)2
-rwxr-xr-xtest/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.sh (renamed from test/testsuite-07.units/issue14566-repro.sh)0
-rw-r--r--test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-1.service (renamed from test/testsuite-07.units/issue16115-repro-1.service)0
-rw-r--r--test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-2.service (renamed from test/testsuite-07.units/issue16115-repro-2.service)0
-rw-r--r--test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-3.service (renamed from test/testsuite-07.units/issue16115-repro-3.service)0
-rw-r--r--test/TEST-07-PID1/TEST-07-PID1.units/issue2467.service (renamed from test/testsuite-07.units/issue2467.service)0
-rw-r--r--test/TEST-07-PID1/TEST-07-PID1.units/issue2467.socket7
l---------test/TEST-07-PID1/TEST-07-PID1.units/issue2730-alias.mount1
-rw-r--r--test/TEST-07-PID1/TEST-07-PID1.units/issue2730.mount8
-rw-r--r--test/TEST-07-PID1/TEST-07-PID1.units/issue27953.service (renamed from test/testsuite-07.units/issue27953.service)0
-rw-r--r--test/TEST-07-PID1/TEST-07-PID1.units/issue3166-fail-on-restart.service (renamed from test/testsuite-07.units/issue3166-fail-on-restart.service)0
l---------test/TEST-07-PID1/TEST-07-PID1.units/local-fs.target.wants/issue2730.mount1
-rw-r--r--test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-no.socket35
-rw-r--r--test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-yes.socket36
-rw-r--r--test/TEST-07-PID1/meson.build10
-rwxr-xr-xtest/TEST-07-PID1/test.sh25
-rw-r--r--test/TEST-08-INITRD/meson.build12
-rwxr-xr-xtest/TEST-08-INITRD/test.sh40
-rw-r--r--test/TEST-09-REBOOT/meson.build10
-rw-r--r--test/TEST-13-NSPAWN/meson.build8
-rwxr-xr-xtest/TEST-13-NSPAWN/test.sh8
-rw-r--r--test/TEST-15-DROPIN/meson.build7
-rw-r--r--test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.service (renamed from test/units/testsuite-16.service)10
-rwxr-xr-xtest/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh (renamed from test/testsuite-16.units/extend-timeout.sh)0
-rw-r--r--test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-runtime.service (renamed from test/testsuite-16.units/fail-runtime.service)2
-rw-r--r--test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-start.service (renamed from test/testsuite-16.units/fail-start.service)2
-rw-r--r--test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-stop.service (renamed from test/testsuite-16.units/fail-stop.service)2
-rw-r--r--test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-all.service (renamed from test/testsuite-16.units/success-all.service)2
-rw-r--r--test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-runtime.service (renamed from test/testsuite-16.units/success-runtime.service)2
-rw-r--r--test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-start.service (renamed from test/testsuite-16.units/success-start.service)2
-rw-r--r--test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-stop.service (renamed from test/testsuite-16.units/success-stop.service)2
-rw-r--r--test/TEST-16-EXTEND-TIMEOUT/meson.build10
-rw-r--r--test/TEST-17-UDEV/meson.build8
-rw-r--r--test/TEST-18-FAILUREACTION/meson.build9
-rw-r--r--test/TEST-19-CGROUP/meson.build7
-rw-r--r--test/TEST-21-DFUZZER/meson.build11
-rw-r--r--test/TEST-22-TMPFILES/meson.build7
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-binds-to.service10
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-bound-by.service (renamed from test/testsuite-23.units/testsuite-23-bound-by.service)2
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-fail.service (renamed from test/testsuite-23.units/testsuite-23-fail.service)4
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-1.service (renamed from test/testsuite-23.units/testsuite-23-joins-namespace-of-1.service)2
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-2.service (renamed from test/testsuite-23.units/testsuite-23-joins-namespace-of-2.service)2
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-3.service (renamed from test/testsuite-23.units/testsuite-23-joins-namespace-of-3.service)2
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-4.service10
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-5.service (renamed from test/testsuite-23.units/testsuite-23-joins-namespace-of-5.service)0
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-6.service10
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-7.service (renamed from test/testsuite-23.units/testsuite-23-joins-namespace-of-7.service)2
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-8.service (renamed from test/testsuite-23.units/testsuite-23-joins-namespace-of-8.service)2
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-9.service (renamed from test/testsuite-23.units/testsuite-23-joins-namespace-of-9.service)2
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-namespaced.service (renamed from test/testsuite-23.units/testsuite-23-namespaced.service)4
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-non-namespaced.service6
-rwxr-xr-xtest/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-oneshot-restartforce.sh11
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server.socket (renamed from test/units/testsuite-77-server.socket)2
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server@.service (renamed from test/units/testsuite-77-server@.service)2
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-one.service10
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-two.service (renamed from test/testsuite-23.units/testsuite-23-prop-stop-two.service)2
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-fail.service (renamed from test/testsuite-23.units/testsuite-23-retry-fail.service)2
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-upheld.service (renamed from test/testsuite-23.units/testsuite-23-retry-upheld.service)6
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-uphold.service (renamed from test/testsuite-23.units/testsuite-23-retry-uphold.service)4
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-short-lived.service (renamed from test/testsuite-23.units/testsuite-23-short-lived.service)2
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-depends-wants.service (renamed from test/testsuite-23.units/testsuite-23-specifier-j-depends-wants.service)0
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-wants.service (renamed from test/testsuite-23.units/testsuite-23-specifier-j-wants.service)4
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-success.service (renamed from test/testsuite-23.units/testsuite-23-success.service)4
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-upheldby-install.service (renamed from test/testsuite-23.units/testsuite-23-upheldby-install.service)4
-rw-r--r--test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-uphold.service (renamed from test/testsuite-23.units/testsuite-23-uphold.service)4
-rw-r--r--test/TEST-23-UNIT-FILE/meson.build9
-rw-r--r--test/TEST-24-CRYPTSETUP/keydev.repart/00-root.conf9
-rw-r--r--test/TEST-24-CRYPTSETUP/keyfile1
-rw-r--r--test/TEST-24-CRYPTSETUP/meson.build26
-rw-r--r--test/TEST-24-CRYPTSETUP/template.cfg8
-rwxr-xr-xtest/TEST-24-CRYPTSETUP/test.sh128
-rw-r--r--test/TEST-25-IMPORT/meson.build7
-rw-r--r--test/TEST-26-SYSTEMCTL/meson.build7
-rw-r--r--test/TEST-29-PORTABLE/meson.build8
-rw-r--r--test/TEST-30-ONCLOCKCHANGE/TEST-30-ONCLOCKCHANGE.units/systemd-timedated.service.d/watchdog.conf (renamed from test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf)0
-rw-r--r--test/TEST-30-ONCLOCKCHANGE/meson.build10
-rw-r--r--test/TEST-31-DEVICE-ENUMERATION/meson.build8
-rw-r--r--test/TEST-32-OOMPOLICY/meson.build11
-rw-r--r--test/TEST-34-DYNAMICUSERMIGRATE/meson.build7
-rw-r--r--test/TEST-35-LOGIN/meson.build7
-rw-r--r--test/TEST-36-NUMAPOLICY/meson.build8
-rw-r--r--test/TEST-38-FREEZER/meson.build8
-rw-r--r--test/TEST-43-PRIVATEUSER-UNPRIV/meson.build7
-rwxr-xr-xtest/TEST-43-PRIVATEUSER-UNPRIV/test.sh1
-rw-r--r--test/TEST-44-LOG-NAMESPACE/TEST-44-LOG-NAMESPACE.service11
-rw-r--r--test/TEST-44-LOG-NAMESPACE/meson.build8
-rw-r--r--test/TEST-45-TIMEDATE/meson.build7
-rw-r--r--test/TEST-46-HOMED/meson.build7
-rwxr-xr-xtest/TEST-46-HOMED/test.sh3
-rw-r--r--test/TEST-50-DISSECT/meson.build8
-rwxr-xr-xtest/TEST-50-DISSECT/test.sh11
-rw-r--r--test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.service (renamed from test/testsuite-52.units/test-honor-first-shutdown.service)2
-rwxr-xr-xtest/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.sh (renamed from test/testsuite-52.units/test-honor-first-shutdown.sh)0
-rw-r--r--test/TEST-52-HONORFIRSTSHUTDOWN/meson.build9
-rw-r--r--test/TEST-53-ISSUE-16347/meson.build11
-rwxr-xr-xtest/TEST-53-ISSUE-16347/mkosi.configure6
-rw-r--r--test/TEST-54-CREDS/meson.build31
-rw-r--r--test/TEST-54-CREDS/systemd.extra-unit.my-service.service (renamed from test/units/testsuite-69.service)4
-rw-r--r--test/TEST-54-CREDS/systemd.unit-dropin.my-service.service4
-rw-r--r--test/TEST-54-CREDS/systemd.unit-dropin.my-service.service~30-named.service4
-rwxr-xr-xtest/TEST-54-CREDS/test.sh20
-rw-r--r--test/TEST-55-OOMD/meson.build11
-rw-r--r--test/TEST-55-OOMD/systemd.unit-dropin.init.scope5
-rw-r--r--test/TEST-58-REPART/meson.build7
-rw-r--r--test/TEST-59-RELOADING-RESTART/meson.build7
-rw-r--r--test/TEST-60-MOUNT-RATELIMIT/meson.build7
-rw-r--r--test/TEST-62-RESTRICT-IFACES/meson.build8
-rw-r--r--test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.path (renamed from test/testsuite-63.units/test63-glob.path)0
-rw-r--r--test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.service (renamed from test/testsuite-63.units/test63-glob.service)0
-rw-r--r--test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577-dep.service (renamed from test/testsuite-63.units/test63-issue-24577-dep.service)0
-rw-r--r--test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.path (renamed from test/testsuite-63.units/test63-issue-24577.path)0
-rw-r--r--test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.service (renamed from test/testsuite-63.units/test63-issue-24577.service)0
-rw-r--r--test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.path (renamed from test/testsuite-63.units/test63-pr-30768.path)0
-rw-r--r--test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.service (renamed from test/testsuite-63.units/test63-pr-30768.service)0
-rw-r--r--test/TEST-63-PATH/TEST-63-PATH.units/test63.path (renamed from test/testsuite-63.units/test63.path)0
-rw-r--r--test/TEST-63-PATH/TEST-63-PATH.units/test63.service (renamed from test/testsuite-63.units/test63.service)0
-rw-r--r--test/TEST-63-PATH/meson.build9
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/btrfs_basic.configure26
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/iscsi_lvm.configure26
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/long_sysfs_path.configure32
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/lvm_basic.configure25
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/mdadm_basic.configure25
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/mdadm_lvm.configure25
-rw-r--r--test/TEST-64-UDEV-STORAGE/meson.build43
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/multipath_basic_failover.configure31
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/nvme_basic.configure39
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/nvme_subsystem.configure39
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/simultaneous_events.configure21
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/test.sh69
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/virtio_scsi_basic.configure28
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/virtio_scsi_identically_named_partitions.configure33
-rw-r--r--test/TEST-65-ANALYZE/meson.build7
-rw-r--r--test/TEST-66-DEVICE-ISOLATION/meson.build8
-rw-r--r--test/TEST-67-INTEGRITY/meson.build8
-rw-r--r--test/TEST-68-PROPAGATE-EXIT-STATUS/meson.build7
-rw-r--r--test/TEST-69-SHUTDOWN/TEST-69-SHUTDOWN.service12
-rw-r--r--test/TEST-69-SHUTDOWN/meson.build12
-rwxr-xr-xtest/TEST-69-SHUTDOWN/test.sh2
-rw-r--r--test/TEST-70-TPM2/meson.build12
-rw-r--r--test/TEST-71-HOSTNAME/meson.build7
-rw-r--r--test/TEST-72-SYSUPDATE/meson.build7
-rw-r--r--test/TEST-73-LOCALE/meson.build7
-rw-r--r--test/TEST-74-AUX-UTILS/meson.build8
-rwxr-xr-xtest/TEST-74-AUX-UTILS/test.sh15
-rw-r--r--test/TEST-75-RESOLVED/meson.build7
-rwxr-xr-xtest/TEST-75-RESOLVED/test.sh15
-rw-r--r--test/TEST-76-SYSCTL/meson.build7
l---------test/TEST-77-OPENFILE/Makefile1
-rwxr-xr-xtest/TEST-77-OPENFILE/test.sh16
-rw-r--r--test/TEST-78-SIGQUEUE/meson.build7
-rw-r--r--test/TEST-79-MEMPRESS/meson.build10
-rw-r--r--test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-nopin.service (renamed from test/testsuite-80.units/fdstore-nopin.service)2
-rw-r--r--test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.service (renamed from test/testsuite-80.units/fdstore-pin.service)2
-rwxr-xr-xtest/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh (renamed from test/testsuite-80.units/fdstore-pin.sh)0
-rw-r--r--test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.target (renamed from test/testsuite-80.units/fdstore-pin.target)0
-rw-r--r--test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify.service4
-rwxr-xr-xtest/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/test.sh (renamed from test/testsuite-80.units/test.sh)0
-rw-r--r--test/TEST-80-NOTIFYACCESS/meson.build9
-rw-r--r--test/TEST-81-GENERATORS/meson.build7
-rw-r--r--test/TEST-82-SOFTREBOOT/TEST-82-SOFTREBOOT.service (renamed from test/units/testsuite-82.service)2
-rw-r--r--test/TEST-82-SOFTREBOOT/meson.build11
-rwxr-xr-xtest/TEST-82-SOFTREBOOT/test.sh14
-rw-r--r--test/TEST-83-BTRFS/meson.build8
-rw-r--r--test/TEST-84-STORAGETM/meson.build8
-rw-r--r--test/TEST-85-NETWORK/Makefile6
-rw-r--r--test/TEST-85-NETWORK/meson.build31
-rw-r--r--test/fuzz/fuzz-catalog/systemd.pl.catalog2
-rw-r--r--test/fuzz/fuzz-execute-serialize/initial1
-rw-r--r--test/fuzz/fuzz-netdev-parser/bond.netdev1
-rw-r--r--test/fuzz/fuzz-network-parser/sysctl1
-rw-r--r--test/fuzz/fuzz-unit-file/binfmt_misc.automount2
-rw-r--r--test/fuzz/fuzz-unit-file/directives-all.service6
-rw-r--r--test/fuzz/fuzz-unit-file/syslog.socket4
-rw-r--r--test/fuzz/fuzz-unit-file/systemd-resolved.service4
-rwxr-xr-xtest/hwdb-test.sh1
-rwxr-xr-xtest/integration-test-wrapper.py196
-rw-r--r--test/knot-data/knot.conf41
-rw-r--r--test/knot-data/zones/signed.test.zone1
-rw-r--r--test/knot-data/zones/test.zone3
-rw-r--r--test/meson.build287
-rwxr-xr-xtest/networkd-test.py38
-rwxr-xr-xtest/run-integration-tests.sh178
-rwxr-xr-xtest/run-unit-tests.py10
-rwxr-xr-xtest/test-exec-deserialization.py214
-rw-r--r--test/test-execute/exec-ambientcapabilities-dynuser.service3
-rw-r--r--test/test-execute/exec-ambientcapabilities-merge-nfsnobody.service2
-rw-r--r--test/test-execute/exec-ambientcapabilities-merge-nobody.service2
-rw-r--r--test/test-execute/exec-ambientcapabilities-merge.service2
-rw-r--r--test/test-execute/exec-ambientcapabilities-nfsnobody.service2
-rw-r--r--test/test-execute/exec-ambientcapabilities-nobody.service2
-rw-r--r--test/test-execute/exec-ambientcapabilities.service2
-rw-r--r--test/test-execute/exec-bindpaths.service2
-rw-r--r--test/test-execute/exec-capabilityboundingset-invert.service2
-rw-r--r--test/test-execute/exec-capabilityboundingset-merge.service2
-rw-r--r--test/test-execute/exec-capabilityboundingset-reset.service2
-rw-r--r--test/test-execute/exec-capabilityboundingset-simple.service2
-rw-r--r--test/test-execute/exec-condition-failed.service2
-rw-r--r--test/test-execute/exec-condition-skip.service2
-rw-r--r--test/test-execute/exec-cpuaffinity1.service2
-rw-r--r--test/test-execute/exec-cpuaffinity2.service2
-rw-r--r--test/test-execute/exec-cpuaffinity3.service2
-rw-r--r--test/test-execute/exec-dynamicuser-fixeduser-adm.service4
-rw-r--r--test/test-execute/exec-dynamicuser-fixeduser-games.service4
-rw-r--r--test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service4
-rw-r--r--test/test-execute/exec-dynamicuser-fixeduser.service4
-rw-r--r--test/test-execute/exec-dynamicuser-runtimedirectory1.service7
-rw-r--r--test/test-execute/exec-dynamicuser-runtimedirectory2.service9
-rw-r--r--test/test-execute/exec-dynamicuser-runtimedirectory3.service9
-rw-r--r--test/test-execute/exec-dynamicuser-statedir-migrate-step1.service2
-rw-r--r--test/test-execute/exec-dynamicuser-statedir-migrate-step2.service3
-rw-r--r--test/test-execute/exec-dynamicuser-statedir.service1
-rw-r--r--test/test-execute/exec-dynamicuser-supplementarygroups.service5
-rw-r--r--test/test-execute/exec-environment-empty.service2
-rw-r--r--test/test-execute/exec-environment-multiple.service2
-rw-r--r--test/test-execute/exec-environment-no-substitute.service2
-rw-r--r--test/test-execute/exec-environment.service2
-rw-r--r--test/test-execute/exec-environmentfile.service2
-rw-r--r--test/test-execute/exec-execsearchpath-environment-path-set.service2
-rw-r--r--test/test-execute/exec-execsearchpath-environment.service2
-rw-r--r--test/test-execute/exec-execsearchpath-environmentfile-set.service2
-rw-r--r--test/test-execute/exec-execsearchpath-environmentfile.service2
-rw-r--r--test/test-execute/exec-execsearchpath-passenvironment-set.service2
-rw-r--r--test/test-execute/exec-execsearchpath-passenvironment.service2
-rw-r--r--test/test-execute/exec-execsearchpath-unit-specifier.service2
-rw-r--r--test/test-execute/exec-group-nfsnobody.service2
-rw-r--r--test/test-execute/exec-group-nobody.service2
-rw-r--r--test/test-execute/exec-group-nogroup.service2
-rw-r--r--test/test-execute/exec-group.service2
-rw-r--r--test/test-execute/exec-ignoresigpipe-no.service2
-rw-r--r--test/test-execute/exec-ignoresigpipe-yes.service2
-rw-r--r--test/test-execute/exec-inaccessiblepaths-mount-propagation.service2
-rw-r--r--test/test-execute/exec-inaccessiblepaths-sys.service2
-rw-r--r--test/test-execute/exec-ioschedulingclass-best-effort.service2
-rw-r--r--test/test-execute/exec-ioschedulingclass-idle.service2
-rw-r--r--test/test-execute/exec-ioschedulingclass-none.service2
-rw-r--r--test/test-execute/exec-ioschedulingclass-realtime.service2
-rw-r--r--test/test-execute/exec-load-credential.service8
-rw-r--r--test/test-execute/exec-networknamespacepath-privatemounts-no.service12
-rw-r--r--test/test-execute/exec-networknamespacepath-privatemounts-yes.service12
-rw-r--r--test/test-execute/exec-noexecpaths-simple.service2
-rw-r--r--test/test-execute/exec-oomscoreadjust-negative.service2
-rw-r--r--test/test-execute/exec-oomscoreadjust-positive.service2
-rw-r--r--test/test-execute/exec-passenvironment-absent.service2
-rw-r--r--test/test-execute/exec-passenvironment-empty.service2
-rw-r--r--test/test-execute/exec-passenvironment-repeated.service2
-rw-r--r--test/test-execute/exec-passenvironment.service2
-rw-r--r--test/test-execute/exec-personality-aarch64.service2
-rw-r--r--test/test-execute/exec-personality-loongarch64.service2
-rw-r--r--test/test-execute/exec-personality-ppc64.service2
-rw-r--r--test/test-execute/exec-personality-ppc64le.service2
-rw-r--r--test/test-execute/exec-personality-s390.service2
-rw-r--r--test/test-execute/exec-personality-s390x.service8
-rw-r--r--test/test-execute/exec-personality-x86-64.service2
-rw-r--r--test/test-execute/exec-personality-x86.service2
-rw-r--r--test/test-execute/exec-privatedevices-bind.service4
-rw-r--r--test/test-execute/exec-privatedevices-disabled-by-prefix.service2
-rw-r--r--test/test-execute/exec-privatedevices-no-capability-mknod.service2
-rw-r--r--test/test-execute/exec-privatedevices-no-capability-sys-rawio.service2
-rw-r--r--test/test-execute/exec-privatedevices-no.service2
-rw-r--r--test/test-execute/exec-privatedevices-yes-capability-mknod.service2
-rw-r--r--test/test-execute/exec-privatedevices-yes-capability-sys-rawio.service2
-rw-r--r--test/test-execute/exec-privatedevices-yes-with-group.service6
-rw-r--r--test/test-execute/exec-privatedevices-yes.service2
-rw-r--r--test/test-execute/exec-privatenetwork-yes-privatemounts-no.service6
-rw-r--r--test/test-execute/exec-privatenetwork-yes-privatemounts-yes.service6
-rw-r--r--test/test-execute/exec-privatetmp-disabled-by-prefix.service2
-rw-r--r--test/test-execute/exec-privatetmp-no.service2
-rw-r--r--test/test-execute/exec-privatetmp-yes.service2
-rw-r--r--test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service2
-rw-r--r--test/test-execute/exec-protectkernellogs-no-capabilities.service2
-rw-r--r--test/test-execute/exec-protectkernellogs-yes-capabilities.service2
-rw-r--r--test/test-execute/exec-protectkernelmodules-no-capabilities.service2
-rw-r--r--test/test-execute/exec-protectkernelmodules-yes-capabilities.service2
-rw-r--r--test/test-execute/exec-protectkernelmodules-yes-mount-propagation.service2
-rw-r--r--test/test-execute/exec-readonlypaths-mount-propagation.service2
-rw-r--r--test/test-execute/exec-readonlypaths-simple.service2
-rw-r--r--test/test-execute/exec-readonlypaths-with-bindpaths.service2
-rw-r--r--test/test-execute/exec-readonlypaths.service4
-rw-r--r--test/test-execute/exec-readwritepaths-mount-propagation.service2
-rw-r--r--test/test-execute/exec-runtimedirectory-mode.service4
-rw-r--r--test/test-execute/exec-runtimedirectory-owner-nfsnobody.service2
-rw-r--r--test/test-execute/exec-runtimedirectory-owner-nobody.service2
-rw-r--r--test/test-execute/exec-runtimedirectory-owner-nogroup.service2
-rw-r--r--test/test-execute/exec-runtimedirectory-owner.service2
-rw-r--r--test/test-execute/exec-runtimedirectory.service6
-rw-r--r--test/test-execute/exec-set-credential.service8
-rw-r--r--test/test-execute/exec-specifier-interpolation.service2
-rw-r--r--test/test-execute/exec-standardinput-data.service2
-rw-r--r--test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service8
-rw-r--r--test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service6
-rw-r--r--test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service4
-rw-r--r--test/test-execute/exec-supplementarygroups-single-group-user.service4
-rw-r--r--test/test-execute/exec-supplementarygroups-single-group.service4
-rw-r--r--test/test-execute/exec-supplementarygroups.service4
-rw-r--r--test/test-execute/exec-systemcallerrornumber-name.service2
-rw-r--r--test/test-execute/exec-systemcallerrornumber-number.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-failing.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-failing2.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-failing3.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-nonewprivileges-bounding1.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-nonewprivileges-bounding2.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-nonewprivileges-protectclock.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-nonewprivileges.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-not-failing.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-not-failing2.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-not-failing3.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-override-error-action.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-override-error-action2.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-system-user-nfsnobody.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-system-user-nobody.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-system-user.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-with-errno-in-allow-list.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-with-errno-multi.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-with-errno-name.service2
-rw-r--r--test/test-execute/exec-systemcallfilter-with-errno-number.service2
-rw-r--r--test/test-execute/exec-temporaryfilesystem-options.service8
-rw-r--r--test/test-execute/exec-temporaryfilesystem-ro.service16
-rw-r--r--test/test-execute/exec-temporaryfilesystem-usr.service6
-rw-r--r--test/test-execute/exec-umask-0177.service2
-rw-r--r--test/test-execute/exec-umask-default.service2
-rw-r--r--test/test-execute/exec-umask-namespace.service2
-rw-r--r--test/test-execute/exec-unsetenvironment.service2
-rw-r--r--test/test-execute/exec-user-nfsnobody.service2
-rw-r--r--test/test-execute/exec-user-nobody.service2
-rw-r--r--test/test-execute/exec-user.service2
-rw-r--r--test/test-execute/exec-workingdirectory-trailing-dot.service2
-rw-r--r--test/test-execute/exec-workingdirectory.service2
l---------test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount1
-rw-r--r--test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requiredby.mount4
-rw-r--r--test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.automount8
-rw-r--r--test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.mount14
-rw-r--r--test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby.mount4
l---------test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount1
-rw-r--r--test/test-fstab-generator/test-18-options.fstab.expected/mnt-requiredby.mount4
-rw-r--r--test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.automount8
-rw-r--r--test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.mount14
-rw-r--r--test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby.mount4
-rw-r--r--test/test-fstab-generator/test-18-options.fstab.input33
-rw-r--r--test/test-functions186
-rwxr-xr-xtest/test-network-generator-conversion.sh8
-rw-r--r--test/test-network/conf/11-test-unit-file.link20
-rw-r--r--test/test-network/conf/11-test-unit-file.link.d/dropin.conf2
-rw-r--r--test/test-network/conf/11-test-unit-file.netdev4
-rw-r--r--test/test-network/conf/11-test-unit-file.netdev.d/dropin.conf2
-rw-r--r--test/test-network/conf/11-test-unit-file.network6
-rw-r--r--test/test-network/conf/11-test-unit-file.network.d/dropin.conf2
-rw-r--r--test/test-network/conf/21-macvlan.netdev4
-rw-r--r--test/test-network/conf/24-rps-cpu-disable.link6
-rw-r--r--test/test-network/conf/24-rps-cpu-empty.link6
-rw-r--r--test/test-network/conf/24-rps-cpu-invalid.link6
-rw-r--r--test/test-network/conf/25-address-static.network.d/20-clear-addresses.conf6
-rw-r--r--test/test-network/conf/25-agent-bridge-port.network7
-rw-r--r--test/test-network/conf/25-agent-bridge.netdev4
-rw-r--r--test/test-network/conf/25-agent-bridge.network12
-rw-r--r--test/test-network/conf/25-agent-client-peer.network2
-rw-r--r--test/test-network/conf/25-agent-client.network2
-rw-r--r--test/test-network/conf/25-agent-server-peer.network2
-rw-r--r--test/test-network/conf/25-agent-server.network2
-rw-r--r--test/test-network/conf/25-bond-property.netdev10
-rw-r--r--test/test-network/conf/25-dhcp-server-veth-peer.network2
-rw-r--r--test/test-network/conf/25-dummy.netdev4
-rw-r--r--test/test-network/conf/25-dummy.network6
-rw-r--r--test/test-network/conf/25-fibrule-l3mdev.network15
-rw-r--r--test/test-network/conf/25-ipv6-neigh-retrans-time-0s.network7
-rw-r--r--test/test-network/conf/25-ipv6-neigh-retrans-time-3s.network7
-rw-r--r--test/test-network/conf/25-ipv6-neigh-retrans-time-4s.network7
-rw-r--r--test/test-network/conf/25-ipv6-neigh-retrans-time-infinity.network7
-rw-r--r--test/test-network/conf/25-ipv6-neigh-retrans-time-invalid.network7
-rw-r--r--test/test-network/conf/25-ipv6-neigh-retrans-time-toobig.network7
-rw-r--r--test/test-network/conf/25-ipv6-prefix-veth-token-prefixstable.network9
-rw-r--r--test/test-network/conf/25-ipv6-prefix.network2
-rw-r--r--test/test-network/conf/25-ipv6-proxy-ndp.network1
-rw-r--r--test/test-network/conf/25-neighbor-dummy.network (renamed from test/test-network/conf/25-neighbor-ip-dummy.network)0
-rw-r--r--test/test-network/conf/25-neighbor-dummy.network.d/10-step1.conf (renamed from test/test-network/conf/25-neighbor-section.network)6
-rw-r--r--test/test-network/conf/25-neighbor-dummy.network.d/10-step2.conf (renamed from test/test-network/conf/25-neighbor-section.network.d/override.conf)1
-rw-r--r--test/test-network/conf/25-neighbor-dummy.network.d/10-step3.conf4
-rw-r--r--test/test-network/conf/25-netdevsim.link11
-rw-r--r--test/test-network/conf/25-nexthop-1.network (renamed from test/test-network/conf/25-nexthop.network)1
-rw-r--r--test/test-network/conf/25-nexthop-2.network98
-rw-r--r--test/test-network/conf/25-nexthop-dummy-1.network (renamed from test/test-network/conf/25-nexthop-dummy.network)0
-rw-r--r--test/test-network/conf/25-nexthop-dummy-2.network20
-rw-r--r--test/test-network/conf/25-nexthop-test1.network12
-rw-r--r--test/test-network/conf/25-route-static.network2
-rw-r--r--test/test-network/conf/25-rps-cpu-0-1.link6
-rw-r--r--test/test-network/conf/25-rps-cpu-0-empty.link7
-rw-r--r--test/test-network/conf/25-rps-cpu-0-invalid.link7
-rw-r--r--test/test-network/conf/25-rps-cpu-0.link6
-rw-r--r--test/test-network/conf/25-rps-cpu-1.link6
-rw-r--r--test/test-network/conf/25-rps-cpu-all.link6
-rw-r--r--test/test-network/conf/25-rps-cpu-multi.link7
-rw-r--r--test/test-network/conf/25-sriov-udev.network2
-rw-r--r--test/test-network/conf/25-sriov.link3
-rw-r--r--test/test-network/conf/25-sriov.network2
-rw-r--r--test/test-network/conf/25-sysctl.network4
-rw-r--r--test/test-network/conf/25-veth-bridge.network2
-rw-r--r--test/test-network/conf/25-veth-peer-no-address.network6
-rw-r--r--test/test-network/conf/25-veth-router-high2.network18
-rw-r--r--test/test-network/conf/25-veth-router-hop-limit.network18
-rw-r--r--test/test-network/conf/25-veth-router-low2.network18
-rw-r--r--test/test-network/conf/25-veth-router.netdev9
-rw-r--r--test/test-network/conf/25-wireguard-endpoint-peer0-cred.txt1
-rw-r--r--test/test-network/conf/25-wireguard-no-peer-private-key-cred.txt1
-rw-r--r--test/test-network/conf/25-wireguard-no-peer.netdev2
-rw-r--r--test/test-network/conf/25-wireguard-preshared-key-peer2-cred.txt1
-rw-r--r--test/test-network/conf/25-wireguard.netdev4
-rw-r--r--test/test-network/conf/25-wireguard.netdev.d/peer2.conf2
-rw-r--r--test/test-network/conf/26-bridge-mac-master.network6
-rw-r--r--test/test-network/conf/26-bridge-mac-slave.network (renamed from test/test-network/conf/25-neighbor-next.network)5
-rw-r--r--test/test-network/conf/26-bridge-mac.link6
-rw-r--r--test/test-network/conf/26-bridge-mac.netdev5
-rw-r--r--test/test-network/conf/26-bridge-vlan-master-issue-20373.network1
-rw-r--r--test/test-network/conf/26-bridge-vlan-master.network6
-rw-r--r--test/test-network/conf/26-bridge-vlan-master.network.d/10-override.conf11
-rw-r--r--test/test-network/conf/26-bridge-vlan-master.network.d/20-override.conf9
-rw-r--r--test/test-network/conf/26-bridge-vlan-master.network.d/30-override.conf5
-rw-r--r--test/test-network/conf/26-bridge-vlan-slave-issue-20373.network1
-rw-r--r--test/test-network/conf/26-bridge-vlan-slave.network6
-rw-r--r--test/test-network/conf/26-bridge-vlan-slave.network.d/10-override.conf11
-rw-r--r--test/test-network/conf/26-bridge-vlan-slave.network.d/20-override.conf9
-rw-r--r--test/test-network/conf/26-bridge-vlan-slave.network.d/30-override.conf5
-rw-r--r--test/test-network/conf/networkd-manage-foreign-nexthops-no.conf3
-rw-r--r--test/test-network/conf/persist-leases-no.conf3
-rwxr-xr-xtest/test-network/systemd-networkd-tests.py2262
-rw-r--r--test/test-path/basic.target2
-rwxr-xr-xtest/test-rpm-macros.sh2
-rw-r--r--test/test-systemctl-enable.sh110
-rwxr-xr-xtest/test-sysusers.sh.in8
-rw-r--r--test/test.service.in12
-rw-r--r--test/testsuite-04.units/logs-filtering.service6
-rw-r--r--test/testsuite-04.units/silent-success.service7
-rw-r--r--test/testsuite-23.units/testsuite-23-binds-to.service10
-rw-r--r--test/testsuite-23.units/testsuite-23-joins-namespace-of-4.service10
-rw-r--r--test/testsuite-23.units/testsuite-23-joins-namespace-of-6.service10
-rw-r--r--test/testsuite-23.units/testsuite-23-non-namespaced.service6
-rw-r--r--test/testsuite-23.units/testsuite-23-prop-stop-one.service10
-rw-r--r--test/testsuite-80.units/notify.service4
-rwxr-xr-xtest/units/TEST-01-BASIC.sh (renamed from test/units/testsuite-01.sh)16
-rwxr-xr-xtest/units/TEST-02-UNITTESTS.sh102
-rwxr-xr-xtest/units/TEST-03-JOBS.sh (renamed from test/units/testsuite-03.sh)4
-rwxr-xr-xtest/units/TEST-04-JOURNAL.LogFilterPatterns.sh93
-rwxr-xr-xtest/units/TEST-04-JOURNAL.SYSTEMD_JOURNAL_COMPRESS.sh (renamed from test/units/testsuite-04.SYSTEMD_JOURNAL_COMPRESS.sh)7
-rwxr-xr-xtest/units/TEST-04-JOURNAL.bsod.sh (renamed from test/units/testsuite-04.bsod.sh)22
-rwxr-xr-xtest/units/TEST-04-JOURNAL.cat.sh15
-rwxr-xr-xtest/units/TEST-04-JOURNAL.corrupted-journals.sh (renamed from test/units/testsuite-04.corrupted-journals.sh)4
-rwxr-xr-xtest/units/TEST-04-JOURNAL.fss.sh (renamed from test/units/testsuite-04.fss.sh)0
-rwxr-xr-xtest/units/TEST-04-JOURNAL.journal-append.sh (renamed from test/units/testsuite-04.journal-append.sh)0
-rwxr-xr-xtest/units/TEST-04-JOURNAL.journal-corrupt.sh (renamed from test/units/testsuite-04.journal-corrupt.sh)2
-rwxr-xr-xtest/units/TEST-04-JOURNAL.journal-gatewayd.sh223
-rwxr-xr-xtest/units/TEST-04-JOURNAL.journal-remote.sh (renamed from test/units/testsuite-04.journal-remote.sh)0
-rwxr-xr-xtest/units/TEST-04-JOURNAL.journal.sh (renamed from test/units/testsuite-04.journal.sh)24
-rwxr-xr-xtest/units/TEST-04-JOURNAL.sh (renamed from test/units/testsuite-04.sh)0
-rwxr-xr-xtest/units/TEST-05-RLIMITS.effective-limit.sh68
-rwxr-xr-xtest/units/TEST-05-RLIMITS.rlimit.sh (renamed from test/units/testsuite-05.sh)6
-rwxr-xr-xtest/units/TEST-05-RLIMITS.sh (renamed from test/units/testsuite-13.sh)0
-rwxr-xr-xtest/units/TEST-06-SELINUX.sh (renamed from test/units/testsuite-06.sh)6
-rwxr-xr-xtest/units/TEST-07-PID1.aux-scope.sh34
-rwxr-xr-xtest/units/TEST-07-PID1.exec-context.sh (renamed from test/units/testsuite-07.exec-context.sh)19
-rwxr-xr-xtest/units/TEST-07-PID1.exec-deserialization.sh221
-rwxr-xr-xtest/units/TEST-07-PID1.exec-timestamps.sh17
-rwxr-xr-xtest/units/TEST-07-PID1.issue-14566.sh (renamed from test/units/testsuite-07.issue-14566.sh)0
-rwxr-xr-xtest/units/TEST-07-PID1.issue-16115.sh (renamed from test/units/testsuite-07.issue-16115.sh)0
-rwxr-xr-xtest/units/TEST-07-PID1.issue-1981.sh (renamed from test/units/testsuite-07.issue-1981.sh)2
-rwxr-xr-xtest/units/TEST-07-PID1.issue-2467.sh (renamed from test/units/testsuite-07.issue-2467.sh)0
-rwxr-xr-xtest/units/TEST-07-PID1.issue-27953.sh (renamed from test/units/testsuite-07.issue-27953.sh)0
-rwxr-xr-xtest/units/TEST-07-PID1.issue-30412.sh (renamed from test/units/testsuite-07.issue-30412.sh)0
-rwxr-xr-xtest/units/TEST-07-PID1.issue-3166.sh (renamed from test/units/testsuite-07.issue-3166.sh)0
-rwxr-xr-xtest/units/TEST-07-PID1.issue-3171.sh (renamed from test/units/testsuite-07.issue-3171.sh)2
-rwxr-xr-xtest/units/TEST-07-PID1.main-PID-change.sh (renamed from test/units/testsuite-07.main-PID-change.sh)20
-rwxr-xr-xtest/units/TEST-07-PID1.mount-invalid-chars.sh (renamed from test/units/testsuite-07.mount-invalid-chars.sh)0
-rwxr-xr-xtest/units/TEST-07-PID1.poll-limit.sh (renamed from test/units/testsuite-07.poll-limit.sh)8
-rwxr-xr-xtest/units/TEST-07-PID1.pr-31351.sh22
-rwxr-xr-xtest/units/TEST-07-PID1.private-network.sh (renamed from test/units/testsuite-07.private-network.sh)0
-rwxr-xr-xtest/units/TEST-07-PID1.sh (renamed from test/units/testsuite-07.sh)1
-rwxr-xr-xtest/units/TEST-07-PID1.socket-pass-fds.sh11
-rwxr-xr-xtest/units/TEST-07-PID1.type-exec-parallel.sh9
-rwxr-xr-xtest/units/TEST-08-INITRD.sh (renamed from test/units/testsuite-08.sh)6
-rwxr-xr-xtest/units/TEST-09-REBOOT.journal.sh (renamed from test/units/testsuite-09.journal.sh)60
-rwxr-xr-xtest/units/TEST-09-REBOOT.sh (renamed from test/units/testsuite-09.sh)2
-rwxr-xr-xtest/units/TEST-13-NSPAWN.importctl.sh66
-rwxr-xr-xtest/units/TEST-13-NSPAWN.machinectl.sh (renamed from test/units/testsuite-13.machinectl.sh)48
-rwxr-xr-xtest/units/TEST-13-NSPAWN.nspawn-oci.sh (renamed from test/units/testsuite-13.nspawn-oci.sh)6
-rwxr-xr-xtest/units/TEST-13-NSPAWN.nspawn.sh (renamed from test/units/testsuite-13.nspawn.sh)139
-rwxr-xr-xtest/units/TEST-13-NSPAWN.nss-mymachines.sh (renamed from test/units/testsuite-13.nss-mymachines.sh)7
-rwxr-xr-xtest/units/TEST-13-NSPAWN.sh18
-rwxr-xr-xtest/units/TEST-15-DROPIN.sh (renamed from test/units/testsuite-15.sh)18
-rwxr-xr-xtest/units/TEST-16-EXTEND-TIMEOUT.sh (renamed from test/units/testsuite-16.sh)0
-rwxr-xr-xtest/units/TEST-17-UDEV.00.sh (renamed from test/units/testsuite-17.00.sh)0
-rwxr-xr-xtest/units/TEST-17-UDEV.01.sh (renamed from test/units/testsuite-17.01.sh)0
-rwxr-xr-xtest/units/TEST-17-UDEV.02.sh (renamed from test/units/testsuite-17.02.sh)6
-rwxr-xr-xtest/units/TEST-17-UDEV.03.sh114
-rwxr-xr-xtest/units/TEST-17-UDEV.04.sh (renamed from test/units/testsuite-17.04.sh)0
-rwxr-xr-xtest/units/TEST-17-UDEV.05.sh (renamed from test/units/testsuite-17.05.sh)0
-rwxr-xr-xtest/units/TEST-17-UDEV.06.sh (renamed from test/units/testsuite-17.06.sh)0
-rwxr-xr-xtest/units/TEST-17-UDEV.07.sh (renamed from test/units/testsuite-17.07.sh)0
-rwxr-xr-xtest/units/TEST-17-UDEV.08.sh (renamed from test/units/testsuite-17.08.sh)0
-rwxr-xr-xtest/units/TEST-17-UDEV.09.sh (renamed from test/units/testsuite-17.09.sh)0
-rwxr-xr-xtest/units/TEST-17-UDEV.10.sh (renamed from test/units/testsuite-17.10.sh)4
-rwxr-xr-xtest/units/TEST-17-UDEV.11.sh (renamed from test/units/testsuite-17.11.sh)0
-rwxr-xr-xtest/units/TEST-17-UDEV.12.sh (renamed from test/units/testsuite-17.12.sh)0
-rwxr-xr-xtest/units/TEST-17-UDEV.13.sh (renamed from test/units/testsuite-17.13.sh)0
-rwxr-xr-xtest/units/TEST-17-UDEV.credentials.sh38
-rwxr-xr-xtest/units/TEST-17-UDEV.link-property.sh203
-rwxr-xr-xtest/units/TEST-17-UDEV.sh (renamed from test/units/testsuite-17.sh)0
-rwxr-xr-xtest/units/TEST-18-FAILUREACTION.sh (renamed from test/units/testsuite-18.sh)2
-rwxr-xr-xtest/units/TEST-19-CGROUP.ExitType-cgroup.sh (renamed from test/units/testsuite-19.ExitType-cgroup.sh)5
-rwxr-xr-xtest/units/TEST-19-CGROUP.cleanup-slice.sh (renamed from test/units/testsuite-19.cleanup-slice.sh)0
-rwxr-xr-xtest/units/TEST-19-CGROUP.delegate.sh (renamed from test/units/testsuite-19.delegate.sh)1
-rwxr-xr-xtest/units/TEST-19-CGROUP.sh (renamed from test/units/testsuite-19.sh)0
-rwxr-xr-xtest/units/TEST-21-DFUZZER.sh (renamed from test/units/testsuite-21.sh)28
-rwxr-xr-xtest/units/TEST-22-TMPFILES.01.sh (renamed from test/units/testsuite-22.01.sh)0
-rwxr-xr-xtest/units/TEST-22-TMPFILES.02.sh (renamed from test/units/testsuite-22.02.sh)16
-rwxr-xr-xtest/units/TEST-22-TMPFILES.03.sh (renamed from test/units/testsuite-22.03.sh)29
-rwxr-xr-xtest/units/TEST-22-TMPFILES.04.sh (renamed from test/units/testsuite-22.04.sh)6
-rwxr-xr-xtest/units/TEST-22-TMPFILES.05.sh (renamed from test/units/testsuite-22.05.sh)9
-rwxr-xr-xtest/units/TEST-22-TMPFILES.06.sh (renamed from test/units/testsuite-22.06.sh)9
-rwxr-xr-xtest/units/TEST-22-TMPFILES.07.sh (renamed from test/units/testsuite-22.07.sh)0
-rwxr-xr-xtest/units/TEST-22-TMPFILES.08.sh (renamed from test/units/testsuite-22.08.sh)0
-rwxr-xr-xtest/units/TEST-22-TMPFILES.09.sh (renamed from test/units/testsuite-22.09.sh)0
-rwxr-xr-xtest/units/TEST-22-TMPFILES.10.sh (renamed from test/units/testsuite-22.10.sh)0
-rwxr-xr-xtest/units/TEST-22-TMPFILES.11.sh (renamed from test/units/testsuite-22.11.sh)118
-rwxr-xr-xtest/units/TEST-22-TMPFILES.12.sh (renamed from test/units/testsuite-22.12.sh)10
-rwxr-xr-xtest/units/TEST-22-TMPFILES.13.sh (renamed from test/units/testsuite-22.13.sh)0
-rwxr-xr-xtest/units/TEST-22-TMPFILES.14.sh (renamed from test/units/testsuite-22.14.sh)0
-rwxr-xr-xtest/units/TEST-22-TMPFILES.15.sh (renamed from test/units/testsuite-22.15.sh)12
-rwxr-xr-xtest/units/TEST-22-TMPFILES.16.sh (renamed from test/units/testsuite-22.16.sh)5
-rwxr-xr-xtest/units/TEST-22-TMPFILES.17.sh (renamed from test/units/testsuite-22.17.sh)0
-rwxr-xr-xtest/units/TEST-22-TMPFILES.18.sh34
-rwxr-xr-xtest/units/TEST-22-TMPFILES.19.sh29
-rwxr-xr-xtest/units/TEST-22-TMPFILES.sh (renamed from test/units/testsuite-22.sh)0
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE-openfile-child.sh15
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE-short-lived.sh18
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.ExecReload.sh (renamed from test/units/testsuite-23.ExecReload.sh)6
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.ExecStopPost.sh (renamed from test/units/testsuite-23.ExecStopPost.sh)0
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.JoinsNamespaceOf.sh31
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.RuntimeDirectoryPreserve.sh (renamed from test/units/testsuite-23.RuntimeDirectoryPreserve.sh)0
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.StandardOutput.sh (renamed from test/units/testsuite-23.StandardOutput.sh)8
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.Upholds.sh99
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.clean-unit.sh (renamed from test/units/testsuite-23.clean-unit.sh)4
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.exec-command-ex.sh (renamed from test/units/testsuite-23.exec-command-ex.sh)0
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.oneshot-restart.sh101
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.openfile.sh55
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.percentj-wantedby.sh (renamed from test/units/testsuite-23.percentj-wantedby.sh)4
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh43
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.sh (renamed from test/units/testsuite-23.sh)0
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.start-stop-no-reload.sh93
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.statedir.sh (renamed from test/units/testsuite-23.statedir.sh)0
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.success-failure.sh (renamed from test/units/testsuite-23.success-failure.sh)0
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.type-exec.sh (renamed from test/units/testsuite-23.type-exec.sh)0
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.utmp.sh (renamed from test/units/testsuite-23.utmp.sh)0
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.verify-unit-files.sh45
-rwxr-xr-xtest/units/TEST-23-UNIT-FILE.whoami.sh (renamed from test/units/testsuite-23.whoami.sh)6
-rwxr-xr-xtest/units/TEST-24-CRYPTSETUP.sh (renamed from test/units/testsuite-24.sh)71
-rwxr-xr-xtest/units/TEST-25-IMPORT.sh (renamed from test/units/testsuite-25.sh)0
-rwxr-xr-xtest/units/TEST-26-SYSTEMCTL.sh (renamed from test/units/testsuite-26.sh)46
-rwxr-xr-xtest/units/TEST-29-PORTABLE.sh (renamed from test/units/testsuite-29.sh)120
-rwxr-xr-xtest/units/TEST-30-ONCLOCKCHANGE.sh (renamed from test/units/testsuite-30.sh)2
-rwxr-xr-xtest/units/TEST-31-DEVICE-ENUMERATION.sh (renamed from test/units/testsuite-31.sh)2
-rwxr-xr-xtest/units/TEST-32-OOMPOLICY.sh (renamed from test/units/testsuite-32.sh)2
-rwxr-xr-xtest/units/TEST-34-DYNAMICUSERMIGRATE.sh (renamed from test/units/testsuite-34.sh)0
-rwxr-xr-xtest/units/TEST-35-LOGIN.sh (renamed from test/units/testsuite-35.sh)154
-rwxr-xr-xtest/units/TEST-36-NUMAPOLICY.sh (renamed from test/units/testsuite-36.sh)9
-rw-r--r--test/units/TEST-38-FREEZER-sleep.service (renamed from test/units/testsuite-38-sleep.service)2
-rwxr-xr-xtest/units/TEST-38-FREEZER.sh (renamed from test/units/testsuite-38.sh)168
-rwxr-xr-xtest/units/TEST-43-PRIVATEUSER-UNPRIV.sh (renamed from test/units/testsuite-43.sh)6
-rwxr-xr-xtest/units/TEST-44-LOG-NAMESPACE.sh (renamed from test/units/testsuite-44.sh)12
-rwxr-xr-xtest/units/TEST-45-TIMEDATE.sh (renamed from test/units/testsuite-45.sh)18
-rwxr-xr-xtest/units/TEST-46-HOMED.sh620
-rwxr-xr-xtest/units/TEST-50-DISSECT.DDI.sh70
-rwxr-xr-xtest/units/TEST-50-DISSECT.dissect.sh745
-rwxr-xr-xtest/units/TEST-50-DISSECT.mountfsd.sh92
-rwxr-xr-xtest/units/TEST-50-DISSECT.sh215
-rwxr-xr-xtest/units/TEST-50-DISSECT.sysext.sh1012
-rwxr-xr-xtest/units/TEST-52-HONORFIRSTSHUTDOWN.sh (renamed from test/units/testsuite-52.sh)0
-rwxr-xr-xtest/units/TEST-53-ISSUE-16347.sh (renamed from test/units/testsuite-53.sh)0
-rwxr-xr-xtest/units/TEST-54-CREDS.sh (renamed from test/units/testsuite-54.sh)91
-rw-r--r--test/units/TEST-55-OOMD-testbloat.service (renamed from test/units/testsuite-55-testbloat.service)2
-rw-r--r--test/units/TEST-55-OOMD-testchill.service (renamed from test/units/testsuite-55-testchill.service)2
-rw-r--r--test/units/TEST-55-OOMD-testmunch.service (renamed from test/units/testsuite-55-testmunch.service)2
-rw-r--r--test/units/TEST-55-OOMD-workload.slice (renamed from test/units/testsuite-55-workload.slice)0
-rwxr-xr-xtest/units/TEST-55-OOMD.sh (renamed from test/units/testsuite-55.sh)68
-rwxr-xr-xtest/units/TEST-58-REPART.sh (renamed from test/units/testsuite-58.sh)16
-rwxr-xr-xtest/units/TEST-59-RELOADING-RESTART.sh (renamed from test/units/testsuite-59.sh)14
-rwxr-xr-xtest/units/TEST-60-MOUNT-RATELIMIT.sh (renamed from test/units/testsuite-60.sh)30
-rw-r--r--test/units/TEST-62-RESTRICT-IFACES-1.service9
-rw-r--r--test/units/TEST-62-RESTRICT-IFACES-2.service10
-rw-r--r--test/units/TEST-62-RESTRICT-IFACES-3.service (renamed from test/units/testsuite-62-3.service)6
-rw-r--r--test/units/TEST-62-RESTRICT-IFACES-4.service (renamed from test/units/testsuite-62-4.service)6
-rw-r--r--test/units/TEST-62-RESTRICT-IFACES-5.service (renamed from test/units/testsuite-62-5.service)6
-rw-r--r--test/units/TEST-62-RESTRICT-IFACES-6.service (renamed from test/units/testsuite-62-2.service)6
-rwxr-xr-xtest/units/TEST-62-RESTRICT-IFACES.sh (renamed from test/units/testsuite-62.sh)25
-rwxr-xr-xtest/units/TEST-63-PATH.sh (renamed from test/units/testsuite-63.sh)2
-rwxr-xr-xtest/units/TEST-64-UDEV-STORAGE.sh (renamed from test/units/testsuite-64.sh)124
-rwxr-xr-xtest/units/TEST-65-ANALYZE.sh (renamed from test/units/testsuite-65.sh)47
-rw-r--r--test/units/TEST-66-DEVICE-ISOLATION-device-isolation.service10
-rwxr-xr-xtest/units/TEST-66-DEVICE-ISOLATION.sh (renamed from test/units/testsuite-66.sh)6
-rwxr-xr-xtest/units/TEST-67-INTEGRITY.sh (renamed from test/units/testsuite-67.sh)0
-rwxr-xr-xtest/units/TEST-68-PROPAGATE-EXIT-STATUS.sh (renamed from test/units/testsuite-68.sh)0
-rwxr-xr-xtest/units/TEST-69-SHUTDOWN.py58
-rwxr-xr-xtest/units/TEST-70-TPM2.creds.sh (renamed from test/units/testsuite-70.creds.sh)0
-rwxr-xr-xtest/units/TEST-70-TPM2.cryptenroll.sh (renamed from test/units/testsuite-70.cryptenroll.sh)12
-rwxr-xr-xtest/units/TEST-70-TPM2.cryptsetup.sh (renamed from test/units/testsuite-70.cryptsetup.sh)1
-rwxr-xr-xtest/units/TEST-70-TPM2.measure.sh (renamed from test/units/testsuite-70.measure.sh)0
-rwxr-xr-xtest/units/TEST-70-TPM2.pcrextend.sh (renamed from test/units/testsuite-70.pcrextend.sh)0
-rwxr-xr-xtest/units/TEST-70-TPM2.pcrlock.sh (renamed from test/units/testsuite-70.pcrlock.sh)41
-rwxr-xr-xtest/units/TEST-70-TPM2.sh (renamed from test/units/testsuite-70.sh)0
-rwxr-xr-xtest/units/TEST-70-TPM2.tpm2-setup.sh (renamed from test/units/testsuite-70.tpm2-setup.sh)0
-rwxr-xr-xtest/units/TEST-71-HOSTNAME.sh (renamed from test/units/testsuite-71.sh)24
-rwxr-xr-xtest/units/TEST-72-SYSUPDATE.sh (renamed from test/units/testsuite-72.sh)165
-rwxr-xr-xtest/units/TEST-73-LOCALE.sh (renamed from test/units/testsuite-73.sh)34
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.battery-check.sh (renamed from test/units/testsuite-74.battery-check.sh)0
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.bootctl.sh (renamed from test/units/testsuite-74.bootctl.sh)17
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.busctl.sh (renamed from test/units/testsuite-74.busctl.sh)0
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.capsule.sh53
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.cgls.sh (renamed from test/units/testsuite-74.cgls.sh)0
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.cgtop.sh (renamed from test/units/testsuite-74.cgtop.sh)0
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.coredump.sh (renamed from test/units/testsuite-74.coredump.sh)28
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.delta.sh (renamed from test/units/testsuite-74.delta.sh)4
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.escape.sh (renamed from test/units/testsuite-74.escape.sh)0
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.firstboot.sh (renamed from test/units/testsuite-74.firstboot.sh)22
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.id128.sh (renamed from test/units/testsuite-74.id128.sh)7
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.machine-id-setup.sh (renamed from test/units/testsuite-74.machine-id-setup.sh)0
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.modules-load.sh (renamed from test/units/testsuite-74.modules-load.sh)42
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.mount.sh (renamed from test/units/testsuite-74.mount.sh)73
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.network-generator.sh37
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.networkctl.sh (renamed from test/units/testsuite-74.networkctl.sh)42
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.path.sh (renamed from test/units/testsuite-74.path.sh)0
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.pstore.sh (renamed from test/units/testsuite-74.pstore.sh)0
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.run.sh (renamed from test/units/testsuite-74.run.sh)41
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.sh (renamed from test/units/testsuite-74.sh)0
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.socket.sh84
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.ssh.sh60
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.varlinkctl.sh (renamed from test/units/testsuite-74.varlinkctl.sh)35
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.vpick.sh116
-rwxr-xr-xtest/units/TEST-75-RESOLVED.sh (renamed from test/units/testsuite-75.sh)219
-rwxr-xr-xtest/units/TEST-76-SYSCTL.sh (renamed from test/units/testsuite-76.sh)0
-rwxr-xr-xtest/units/TEST-78-SIGQUEUE.sh (renamed from test/units/testsuite-78.sh)0
-rwxr-xr-xtest/units/TEST-79-MEMPRESS.sh (renamed from test/units/testsuite-79.sh)14
-rwxr-xr-xtest/units/TEST-80-NOTIFYACCESS.sh (renamed from test/units/testsuite-80.sh)0
-rwxr-xr-xtest/units/TEST-81-GENERATORS.debug-generator.sh (renamed from test/units/testsuite-81.debug-generator.sh)7
-rwxr-xr-xtest/units/TEST-81-GENERATORS.environment-d-generator.sh (renamed from test/units/testsuite-81.environment-d-generator.sh)0
-rwxr-xr-xtest/units/TEST-81-GENERATORS.fstab-generator.sh (renamed from test/units/testsuite-81.fstab-generator.sh)7
-rwxr-xr-xtest/units/TEST-81-GENERATORS.getty-generator.sh (renamed from test/units/testsuite-81.getty-generator.sh)2
-rwxr-xr-xtest/units/TEST-81-GENERATORS.run-generator.sh (renamed from test/units/testsuite-81.run-generator.sh)0
-rwxr-xr-xtest/units/TEST-81-GENERATORS.sh (renamed from test/units/testsuite-81.sh)0
-rwxr-xr-xtest/units/TEST-81-GENERATORS.system-update-generator.sh (renamed from test/units/testsuite-81.system-update-generator.sh)0
-rwxr-xr-xtest/units/TEST-82-SOFTREBOOT.sh (renamed from test/units/testsuite-82.sh)111
-rwxr-xr-xtest/units/TEST-83-BTRFS.sh (renamed from test/units/testsuite-83.sh)7
-rwxr-xr-xtest/units/TEST-84-STORAGETM.sh (renamed from test/units/testsuite-84.sh)0
-rw-r--r--test/units/a-conj.service2
-rw-r--r--test/units/a.service2
-rw-r--r--test/units/autorelabel.service8
-rw-r--r--test/units/b.service2
-rw-r--r--test/units/c.service2
-rw-r--r--test/units/d.service2
-rw-r--r--test/units/daughter.service2
-rwxr-xr-xtest/units/delegated_cgroup_filtering_payload.sh11
-rwxr-xr-xtest/units/delegated_cgroup_filtering_payload_child.sh9
-rw-r--r--test/units/dml-discard-empty.service2
-rw-r--r--test/units/dml-discard-set-ml.service2
-rw-r--r--test/units/dml-override-empty.service2
-rw-r--r--test/units/dml-passthrough-empty.service2
-rw-r--r--test/units/dml-passthrough-set-dml.service2
-rw-r--r--test/units/dml-passthrough-set-ml.service2
-rw-r--r--test/units/e.service2
-rwxr-xr-xtest/units/end.sh10
-rw-r--r--test/units/f.service2
-rw-r--r--test/units/g.service2
-rwxr-xr-xtest/units/generator-utils.sh2
-rw-r--r--test/units/grandchild.service2
-rw-r--r--test/units/h.service2
-rw-r--r--test/units/i.service2
-rw-r--r--test/units/loopy.service2
-rw-r--r--test/units/loopy2.service2
-rw-r--r--test/units/loopy3.service2
-rw-r--r--test/units/loopy4.service2
-rw-r--r--test/units/nomemleaf.service2
-rw-r--r--test/units/sched_idle_bad.service2
-rw-r--r--test/units/sched_idle_ok.service2
-rw-r--r--test/units/sched_rr_bad.service2
-rw-r--r--test/units/sched_rr_change.service2
-rw-r--r--test/units/sched_rr_ok.service2
-rw-r--r--test/units/son.service2
-rw-r--r--test/units/test-control.sh2
-rw-r--r--test/units/testsuite-01.service10
-rw-r--r--test/units/testsuite-02.service8
-rwxr-xr-xtest/units/testsuite-02.sh113
-rw-r--r--test/units/testsuite-03.service9
-rwxr-xr-xtest/units/testsuite-04.LogFilterPatterns.sh86
-rwxr-xr-xtest/units/testsuite-04.journal-gatewayd.sh119
-rw-r--r--test/units/testsuite-04.service8
-rw-r--r--test/units/testsuite-05.service8
-rw-r--r--test/units/testsuite-06.service8
-rw-r--r--test/units/testsuite-08.service9
-rw-r--r--test/units/testsuite-09.service9
-rw-r--r--test/units/testsuite-13.service8
-rw-r--r--test/units/testsuite-15.service8
-rwxr-xr-xtest/units/testsuite-17.03.sh75
-rw-r--r--test/units/testsuite-17.service8
-rw-r--r--test/units/testsuite-18.service8
-rw-r--r--test/units/testsuite-19.service8
-rw-r--r--test/units/testsuite-21.service10
-rw-r--r--test/units/testsuite-22.service11
-rwxr-xr-xtest/units/testsuite-23-short-lived.sh18
-rwxr-xr-xtest/units/testsuite-23.JoinsNamespaceOf.sh31
-rwxr-xr-xtest/units/testsuite-23.Upholds.sh99
-rwxr-xr-xtest/units/testsuite-23.oneshot-restart.sh52
-rwxr-xr-xtest/units/testsuite-23.runtime-bind-paths.sh43
-rw-r--r--test/units/testsuite-23.service8
-rwxr-xr-xtest/units/testsuite-23.start-stop-no-reload.sh93
-rw-r--r--test/units/testsuite-24.service9
-rw-r--r--test/units/testsuite-25.service8
-rw-r--r--test/units/testsuite-26.service8
-rw-r--r--test/units/testsuite-29.service8
-rw-r--r--test/units/testsuite-30.service8
-rw-r--r--test/units/testsuite-31.service8
-rw-r--r--test/units/testsuite-32.service9
-rw-r--r--test/units/testsuite-34.service8
-rw-r--r--test/units/testsuite-35.service8
-rw-r--r--test/units/testsuite-36.service8
-rw-r--r--test/units/testsuite-38.service7
-rw-r--r--test/units/testsuite-43.service10
-rw-r--r--test/units/testsuite-44.service12
-rw-r--r--test/units/testsuite-45.service8
-rw-r--r--test/units/testsuite-46.service13
-rwxr-xr-xtest/units/testsuite-46.sh319
-rw-r--r--test/units/testsuite-50.service8
-rwxr-xr-xtest/units/testsuite-50.sh718
-rw-r--r--test/units/testsuite-52.service7
-rw-r--r--test/units/testsuite-53.service8
-rw-r--r--test/units/testsuite-54.service8
-rw-r--r--test/units/testsuite-55.service10
-rw-r--r--test/units/testsuite-58.service7
-rw-r--r--test/units/testsuite-59.service7
-rw-r--r--test/units/testsuite-60.service8
-rw-r--r--test/units/testsuite-62-1.service9
-rw-r--r--test/units/testsuite-62.service8
-rw-r--r--test/units/testsuite-63.service8
-rw-r--r--test/units/testsuite-64.service8
-rw-r--r--test/units/testsuite-65.service8
-rw-r--r--test/units/testsuite-66-deviceisolation.service10
-rw-r--r--test/units/testsuite-66.service8
-rw-r--r--test/units/testsuite-67.service9
-rw-r--r--test/units/testsuite-68.service7
-rw-r--r--test/units/testsuite-70.service7
-rw-r--r--test/units/testsuite-71.service8
-rw-r--r--test/units/testsuite-72.service8
-rw-r--r--test/units/testsuite-73.service8
-rw-r--r--test/units/testsuite-74.service8
-rw-r--r--test/units/testsuite-75.service8
-rw-r--r--test/units/testsuite-76.service8
-rwxr-xr-xtest/units/testsuite-77-client.sh14
-rwxr-xr-xtest/units/testsuite-77-run.sh14
-rw-r--r--test/units/testsuite-77.service10
-rwxr-xr-xtest/units/testsuite-77.sh38
-rw-r--r--test/units/testsuite-78.service7
-rw-r--r--test/units/testsuite-79.service8
-rw-r--r--test/units/testsuite-80.service8
-rw-r--r--test/units/testsuite-81.service8
-rw-r--r--test/units/testsuite-83.service8
-rw-r--r--test/units/testsuite-84.service9
-rw-r--r--test/units/unit-with-multiple-dashes.service2
-rwxr-xr-xtest/units/util.sh166
798 files changed, 12276 insertions, 4925 deletions
diff --git a/test/testsuite-07.units/issue2467.socket b/mkosi.images/minimal-0/mkosi.extra/opt/some_file
index af1317b..bd4fba4 100644
--- a/test/testsuite-07.units/issue2467.socket
+++ b/mkosi.images/minimal-0/mkosi.extra/opt/some_file
@@ -1,3 +1 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
-[Socket]
-ListenStream=/run/test.ctl
diff --git a/test/README.testsuite b/test/README.testsuite
index 9174e16..13ba157 100644
--- a/test/README.testsuite
+++ b/test/README.testsuite
@@ -1,10 +1,72 @@
+# Integration tests
+
+## Running the integration tests with meson + mkosi
+
+To run the integration tests with meson + mkosi, make sure you're running the
+latest version of mkosi. See
+[`docs/HACKING.md`](https://github.com/systemd/systemd/blob/main/test/README.md)
+for more specific details. Make sure `mkosi` is available in `$PATH` when
+reconfiguring meson to make sure it is picked up properly.
+
+We also need to make sure the required meson options are enabled:
+
+```shell
+$ meson setup --reconfigure build -Dremote=enabled
+```
+
+Next, we can build the integration test image:
+
+```shell
+$ meson compile -C build mkosi
+```
+
+After the image has been built, the integration tests can be run with:
+
+```shell
+$ SYSTEMD_INTEGRATION_TESTS=1 meson test -C build/ --suite integration-tests --num-processes "$(($(nproc) / 4))"
+```
+
+As usual, specific tests can be run in meson by appending the name of the test
+which is usually the name of the directory e.g.
+
+```shell
+$ SYSTEMD_INTEGRATION_TESTS=1 meson test -C build/ -v TEST-01-BASIC
+```
+
+See `meson introspect build --tests` for a list of tests.
+
+To interactively debug a failing integration test, the `--interactive` option
+(`-i`) for `meson test` can be used. Note that this requires meson v1.5.0 or
+newer:
+
+```shell
+$ SYSTEMD_INTEGRATION_TESTS=1 meson test -C build/ -i TEST-01-BASIC
+```
+
+Due to limitations in meson, the integration tests do not yet depend on the
+mkosi target, which means the mkosi target has to be manually rebuilt before
+running the integration tests. To rebuild the image and rerun a test, the
+following command can be used:
+
+```shell
+$ meson compile -C build mkosi && SYSTEMD_INTEGRATION_TESTS=1 meson test -C build -v TEST-01-BASIC
+```
+
+The integration tests use the same mkosi configuration that's used when you run
+mkosi in the systemd reposistory, so any local modifications to the mkosi
+configuration (e.g. in `mkosi.local.conf`) are automatically picked up and used
+by the integration tests as well.
+
+## Running the integration tests the old fashioned way
+
The extended testsuite only works with UID=0. It consists of the subdirectories
-named "test/TEST-??-*", each of which contains a description of an OS image and
+named `test/TEST-??-*`, each of which contains a description of an OS image and
a test which consists of systemd units and scripts to execute in this image.
The same image is used for execution under `systemd-nspawn` and `qemu`.
To run the extended testsuite do the following:
+```shell
$ ninja -C build # Avoid building anything as root later
$ sudo test/run-integration-tests.sh
ninja: Entering directory `/home/zbyszek/src/systemd/build'
@@ -20,111 +82,107 @@ make: Leaving directory '/home/zbyszek/src/systemd/test/TEST-01-BASIC'
--x-- Result of TEST-01-BASIC: 0 --x--
--x-- Running TEST-02-CRYPTSETUP --x--
+ make -C TEST-02-CRYPTSETUP clean setup run
+```
If one of the tests fails, then $subdir/test.log contains the log file of
the test.
To run just one of the cases:
+```shell
$ sudo make -C test/TEST-01-BASIC clean setup run
+```
-Specifying the build directory
-==============================
+### Specifying the build directory
If the build directory is not detected automatically, it can be specified
with BUILD_DIR=:
+```shell
$ sudo BUILD_DIR=some-other-build/ test/run-integration-tests
+```
or
+```shell
$ sudo make -C test/TEST-01-BASIC BUILD_DIR=../../some-other-build/ ...
+```
Note that in the second case, the path is relative to the test case directory.
An absolute path may also be used in both cases.
-Testing installed binaries instead of built
-===========================================
+### Testing installed binaries instead of built
To run the extended testsuite using the systemd installed on the system instead
of the systemd from a build, use the NO_BUILD=1:
+```shell
$ sudo NO_BUILD=1 test/run-integration-tests
+```
-Configuration variables
-=======================
+### Configuration variables
-TEST_NO_QEMU=1
- Don't run tests under qemu
+`TEST_NO_QEMU=1`: Don't run tests under qemu.
-TEST_QEMU_ONLY=1
- Run only tests that require qemu
+`TEST_QEMU_ONLY=1`: Run only tests that require qemu.
-TEST_NO_NSPAWN=1
- Don't run tests under systemd-nspawn
+`TEST_NO_NSPAWN=1`: Don't run tests under systemd-nspawn.
-TEST_PREFER_NSPAWN=1
- Run all tests that do not require qemu under systemd-nspawn
+`TEST_PREFER_NSPAWN=1`: Run all tests that do not require qemu under
+systemd-nspawn.
-TEST_NO_KVM=1
- Disable qemu KVM auto-detection (may be necessary when you're trying to run the
- *vanilla* qemu and have both qemu and qemu-kvm installed)
+`TEST_NO_KVM=1`: Disable qemu KVM auto-detection (may be necessary when you're
+trying to run the *vanilla* qemu and have both qemu and qemu-kvm installed)
-TEST_NESTED_KVM=1
- Allow tests to run with nested KVM. By default, the testsuite disables
- nested KVM if the host machine already runs under KVM. Setting this
- variable disables such checks
+`TEST_NESTED_KVM=1`: Allow tests to run with nested KVM. By default, the
+testsuite disables nested KVM if the host machine already runs under KVM.
+Setting this variable disables such checks.
-QEMU_MEM=512M
- Configure amount of memory for qemu VMs (defaults to 512M)
+`QEMU_MEM=512M`: Configure amount of memory for qemu VMs (defaults to 512M).
-QEMU_SMP=1
- Configure number of CPUs for qemu VMs (defaults to 1)
+`QEMU_SMP=1`: Configure number of CPUs for qemu VMs (defaults to 1).
-KERNEL_APPEND='...'
- Append additional parameters to the kernel command line
+`KERNEL_APPEND='...'`: Append additional parameters to the kernel command line.
-NSPAWN_ARGUMENTS='...'
- Specify additional arguments for systemd-nspawn
+`NSPAWN_ARGUMENTS='...'`: Specify additional arguments for systemd-nspawn.
-QEMU_TIMEOUT=infinity
- Set a timeout for tests under qemu (defaults to 1800 sec)
+`QEMU_TIMEOUT=infinity`: Set a timeout for tests under qemu (defaults to 1800
+sec).
-NSPAWN_TIMEOUT=infinity
- Set a timeout for tests under systemd-nspawn (defaults to 1800 sec)
+`NSPAWN_TIMEOUT=infinity`: Set a timeout for tests under systemd-nspawn
+(defaults to 1800 sec).
-INTERACTIVE_DEBUG=1
- Configure the machine to be more *user-friendly* for interactive debuggung
- (e.g. by setting a usable default terminal, suppressing the shutdown after
- the test, etc.)
+`INTERACTIVE_DEBUG=1`: Configure the machine to be more *user-friendly* for
+interactive debugging (e.g. by setting a usable default terminal, suppressing
+the shutdown after the test, etc.).
-TEST_MATCH_SUBTEST=subtest
- If the test makes use of `run_subtests` use this variable to provide
- a POSIX extended regex to run only subtests matching the expression
+`TEST_MATCH_SUBTEST=subtest`: If the test makes use of `run_subtests` use this
+variable to provide a POSIX extended regex to run only subtests matching the
+expression.
-TEST_MATCH_TESTCASE=testcase
- Same as $TEST_MATCH_SUBTEST but for subtests that make use of `run_testcases`
+`TEST_MATCH_TESTCASE=testcase`: Same as $TEST_MATCH_SUBTEST but for subtests
+that make use of `run_testcases`.
The kernel and initrd can be specified with $KERNEL_BIN and $INITRD. (Fedora's
or Debian's default kernel path and initrd are used by default.)
A script will try to find your qemu binary. If you want to specify a different
-one with $QEMU_BIN.
+one with `$QEMU_BIN`.
-Debugging the qemu image
-========================
+### Debugging the qemu image
-If you want to log in the testsuite virtual machine, use INTERACTIVE_DEBUG=1
+If you want to log in the testsuite virtual machine, use `INTERACTIVE_DEBUG=1`
and log in as root:
+```shell
$ sudo make -C test/TEST-01-BASIC INTERACTIVE_DEBUG=1 run
+```
The root password is empty.
-Ubuntu CI
-=========
+## Ubuntu CI
-New PR submitted to the project are run through regression tests, and one set
+New PRs submitted to the project are run through regression tests, and one set
of those is the 'autopkgtest' runs for several different architectures, called
'Ubuntu CI'. Part of that testing is to run all these tests. Sometimes these
tests are temporarily deny-listed from running in the 'autopkgtest' tests while
@@ -149,6 +207,28 @@ failed run, can be downloaded from the artifacts.tar.gz archive which will be
reachable in the same URL parent directory as the logs.gz that gets linked on
the Github CI status.
+The log URL can be derived following a simple algorithm, however the test
+completion timestamp is needed and it's not easy to find without access to the
+log itself. For example, a noble s390x job started on 2024-03-23 at 02:09:11
+will be stored at the following URL:
+
+https://autopkgtest.ubuntu.com/results/autopkgtest-noble-upstream-systemd-ci-systemd-ci/noble/s390x/s/systemd-upstream/20240323_020911_e8e88@/log.gz
+
+Fortunately a list of URLs listing file paths for recently completed test runs
+is available at:
+
+https://autopkgtest.ubuntu.com/results/autopkgtest-noble-upstream-systemd-ci-systemd-ci/
+
+paths listed at this URL can be appended to the URL to download them.
+
+The 5 characters at the end of the last directory are not random, but the first
+5 characters of a SHA1 hash generated based on the set of parameters given to
+the build plus the completion timestamp, such as:
+
+```shell
+$ echo -n 'systemd-upstream {"build-git": "https://salsa.debian.org/systemd-team/systemd.git#debian/master", "env": ["UPSTREAM_REPO=https://github.com/systemd/systemd.git", "CFLAGS=-O0", "DEB_BUILD_PROFILES=pkg.systemd.upstream noudeb", "TEST_UPSTREAM=1", "CONFFLAGS_UPSTREAM=--werror -Dslow-tests=true", "UPSTREAM_PULL_REQUEST=31444", "GITHUB_STATUSES_URL=https://api.github.com/repos/systemd/systemd/statuses/c27f600a1c47f10b22964eaedfb5e9f0d4279cd9"], "ppas": ["upstream-systemd-ci/systemd-ci"], "submit-time": "2024-02-27 17:06:27", "uuid": "02cd262f-af22-4f82-ac91-53fa5a9e7811"}' | sha1sum | cut -c1-5
+```
+
To add new dependencies or new binaries to the packages used during the tests,
a merge request can be sent to: https://salsa.debian.org/systemd-team/systemd
targeting the 'upstream-ci' branch.
@@ -158,6 +238,10 @@ located at:
https://git.launchpad.net/autopkgtest-cloud/
+A generic description of the testing infrastructure can be found at:
+
+https://wiki.ubuntu.com/ProposedMigration/AutopkgtestInfrastructure
+
In case of infrastructure issues with this CI, things might go wrong in two
places:
@@ -166,61 +250,92 @@ places:
- running a job: all currently running jobs are listed at
https://autopkgtest.ubuntu.com/running#pkg-systemd-upstream in case the PR
does not show the status for some reason
-- reporting the job result: this is done on Canonical's cloud infrastructure,
- if jobs are started and running but no status is visible on the PR, then it is
+- reporting the job result: this is done on Canonical's cloud infrastructure, if
+ jobs are started and running but no status is visible on the PR, then it is
likely that reporting back is not working
-For infrastructure help, reaching out to Canonical via the #ubuntu-devel channel
-on libera.chat is an effective way to receive support in general.
+The CI job needs a PPA in order to be accepted, and the
+upstream-systemd-ci/systemd-ci PPA is used. Note that this is necessary even
+when there are no packages to backport, but by default a PPA won't have a
+repository for a release if there are no packages built for it. To work around
+this problem, when a new empty release is needed the mark-suite-dirty tool from
+the https://git.launchpad.net/ubuntu-archive-tools can be used to force the PPA
+to publish an empty repository, for example:
+
+```shell
+$ ./mark-suite-dirty -A ppa:upstream-systemd-ci/ubuntu/systemd-ci -s noble
+```
+
+will create an empty 'noble' repository that can be used for 'noble' CI jobs.
+
+For infrastructure help, reaching out to 'qa-help' via the #ubuntu-quality
+channel on libera.chat is an effective way to receive support in general.
-Manually running a part of the Ubuntu CI test suite
-===================================================
+Given access to the shared secret, tests can be re-run using the generic
+retry-github-test tool:
+
+https://git.launchpad.net/autopkgtest-cloud/tree/charms/focal/autopkgtest-cloud-worker/autopkgtest-cloud/tools/retry-github-test
+
+A wrapper script that makes it easier to use is also available:
+
+https://piware.de/gitweb/?p=bin.git;a=blob;f=retry-gh-systemd-Test
+
+## Manually running a part of the Ubuntu CI test suite
In some situations one may want/need to run one of the tests run by Ubuntu CI
locally for debugging purposes. For this, you need a machine (or a VM) with
the same Ubuntu release as is used by Ubuntu CI (Jammy ATTOW).
First of all, clone the Debian systemd repository and sync it with the code of
-the PR (set by the $UPSTREAM_PULL_REQUEST env variable) you'd like to debug:
+the PR (set by the `$UPSTREAM_PULL_REQUEST` env variable) you'd like to debug:
-# git clone https://salsa.debian.org/systemd-team/systemd.git
-# cd systemd
-# git checkout upstream-ci
-# TEST_UPSTREAM=1 UPSTREAM_PULL_REQUEST=12345 ./debian/extra/checkout-upstream
+```shell
+$ git clone https://salsa.debian.org/systemd-team/systemd.git
+$ cd systemd
+$ git checkout upstream-ci
+$ TEST_UPSTREAM=1 UPSTREAM_PULL_REQUEST=12345 ./debian/extra/checkout-upstream
+```
Now install necessary build & test dependencies:
-## PPA with some newer Ubuntu packages required by upstream systemd
-# add-apt-repository -y --enable-source ppa:upstream-systemd-ci/systemd-ci
-# apt build-dep -y systemd
-# apt install -y autopkgtest debhelper genisoimage git qemu-system-x86 \
+```shell
+# PPA with some newer Ubuntu packages required by upstream systemd
+$ add-apt-repository -y --enable-source ppa:upstream-systemd-ci/systemd-ci
+$ apt build-dep -y systemd
+$ apt install -y autopkgtest debhelper genisoimage git qemu-system-x86 \
libcurl4-openssl-dev libfdisk-dev libtss2-dev libfido2-dev \
libssl-dev python3-pefile
+```
Build systemd deb packages with debug info:
-# TEST_UPSTREAM=1 DEB_BUILD_OPTIONS="nocheck nostrip noopt" dpkg-buildpackage -us -uc
-# cd ..
+```shell
+$ TEST_UPSTREAM=1 DEB_BUILD_OPTIONS="nocheck nostrip noopt" dpkg-buildpackage -us -uc
+$ cd ..
+```
Prepare a testbed image for autopkgtest (tweak the release as necessary):
-# autopkgtest-buildvm-ubuntu-cloud --ram-size 1024 -v -a amd64 -r jammy
+```shell
+$ autopkgtest-buildvm-ubuntu-cloud --ram-size 1024 -v -a amd64 -r jammy
+```
And finally run the autopkgtest itself:
-# autopkgtest -o logs *.deb systemd/ \
+```shell
+$ autopkgtest -o logs *.deb systemd/ \
--env=TEST_UPSTREAM=1 \
--timeout-factor=3 \
--test-name=boot-and-services \
--shell-fail \
-- autopkgtest-virt-qemu --cpus 4 --ram-size 2048 autopkgtest-jammy-amd64.img
+```
-where --test-name= is the name of the test you want to run/debug. The
---shell-fail option will pause the execution in case the test fails and shows
+where `--test-name=` is the name of the test you want to run/debug. The
+`--shell-fail` option will pause the execution in case the test fails and shows
you the information how to connect to the testbed for further debugging.
-Manually running CodeQL analysis
-=====================================
+## Manually running CodeQL analysis
This is mostly useful for debugging various CodeQL quirks.
@@ -230,31 +345,40 @@ binary from the unpacked archive in $PATH for brevity.
Switch to the systemd repository if not already:
+```shell
$ cd <systemd-repo>
+```
Create an initial CodeQL database:
+```shell
$ CCACHE_DISABLE=1 codeql database create codeqldb --language=cpp -vvv
+```
Disabling ccache is important, otherwise you might see CodeQL complaining:
-No source code was seen and extracted to /home/mrc0mmand/repos/@ci-incubator/systemd/codeqldb.
-This can occur if the specified build commands failed to compile or process any code.
- - Confirm that there is some source code for the specified language in the project.
- - For codebases written in Go, JavaScript, TypeScript, and Python, do not specify
- an explicit --command.
- - For other languages, the --command must specify a "clean" build which compiles
- all the source code files without reusing existing build artefacts.
+No source code was seen and extracted to
+/home/mrc0mmand/repos/@ci-incubator/systemd/codeqldb. This can occur if the
+specified build commands failed to compile or process any code.
+ - Confirm that there is some source code for the specified language in the
+ project.
+ - For codebases written in Go, JavaScript, TypeScript, and Python, do not
+ specify an explicit --command.
+ - For other languages, the --command must specify a "clean" build which
+ compiles all the source code files without reusing existing build artefacts.
If you want to run all queries systemd uses in CodeQL, run:
+```shell
$ codeql database analyze codeqldb/ --format csv --output results.csv .github/codeql-custom.qls .github/codeql-queries/*.ql -vvv
+```
Note: this will take a while.
If you're interested in a specific check, the easiest way (without hunting down
the specific CodeQL query file) is to create a custom query suite. For example:
+```shell
$ cat >test.qls <<EOF
- queries: .
from: codeql/cpp-queries
@@ -262,10 +386,13 @@ $ cat >test.qls <<EOF
id:
- cpp/missing-return
EOF
+```
And then execute it in the same way as above:
+```shell
$ codeql database analyze codeqldb/ --format csv --output results.csv test.qls -vvv
+```
More about query suites here: https://codeql.github.com/docs/codeql-cli/creating-codeql-query-suites/
@@ -273,13 +400,63 @@ The results are then located in the `results.csv` file as a comma separated
values list (obviously), which is the most human-friendly output format the
CodeQL utility provides (so far).
-Code coverage
-=============
+## Running Coverity locally
+
+Note: this requires a Coverity license, as the public tool
+[tarball](https://scan.coverity.com/download) doesn't contain cov-analyze and
+friends, so the usefulness of this guide is somewhat limited.
+
+Debugging certain pesky Coverity defects can be painful, especially since the
+OSS Coverity instance has a very strict limit on how many builds we can send it
+per day/week, so if you have an access to a non-OSS Coverity license, knowing
+how to debug defects locally might come in handy.
+
+After installing the necessary tooling we need to populate the emit DB first:
+
+```shell
+$ rm -rf build cov
+$ meson setup build -Dman=false
+$ cov-build --dir=./cov ninja -C build
+```
+
+From there it depends if you're interested in a specific defect or all of them.
+For the latter run:
+
+```shell
+$ cov-analyze --dir=./cov --wait-for-license
+```
+
+If you want to debug a specific defect, telling that to cov-analyze speeds
+things up a bit:
+
+```shell
+$ cov-analyze --dir=./cov --wait-for-license --disable-default --enable ASSERT_SIDE_EFFECT
+```
+
+The final step is getting the actual report which can be generated in multiple
+formats, for example:
+
+```shell
+$ cov-format-errors --dir ./cov --text-output-style multiline
+$ cov-format-errors --dir=./cov --emacs-style
+$ cov-format-errors --dir=./cov --html-output html-out
+```
+
+Which generate a text report, an emacs-compatible text report, and an HTML
+report respectively.
+
+Other useful options for cov-format-error include `--file <file>` to filter out
+defects for a specific file, `--checker-regex DEFECT_TYPE` to filter our only a
+specific defect (if this wasn't done already by cov-analyze), and many others,
+see `--help` for an exhaustive list.
+
+## Code coverage
We have a daily cron job in CentOS CI which runs all unit and integration tests,
-collects coverage using gcov/lcov, and uploads the report to Coveralls[0]. In
-order to collect the most accurate coverage information, some measures have
-to be taken regarding sandboxing, namely:
+collects coverage using gcov/lcov, and uploads the report to
+[Coveralls](https://coveralls.io/github/systemd/systemd). In order to collect
+the most accurate coverage information, some measures have to be taken regarding
+sandboxing, namely:
- ProtectSystem= and ProtectHome= need to be turned off
- the $BUILD_DIR with necessary .gcno files needs to be present in the image
@@ -288,23 +465,21 @@ to be taken regarding sandboxing, namely:
The first point is relatively easy to handle and is handled automagically by
our test "framework" by creating necessary dropins.
-Making the $BUILD_DIR accessible to _everything_ is slightly more complicated.
-First, and foremost, the $BUILD_DIR has a POSIX ACL that makes it writable
+Making the `$BUILD_DIR` accessible to _everything_ is slightly more complicated.
+First, and foremost, the `$BUILD_DIR` has a POSIX ACL that makes it writable
to everyone. However, this is not enough in some cases, like for services
that use DynamicUser=yes, since that implies ProtectSystem=strict that can't
-be turned off. A solution to this is to use ReadWritePaths=$BUILD_DIR, which
+be turned off. A solution to this is to use `ReadWritePaths=$BUILD_DIR`, which
works for the majority of cases, but can't be turned on globally, since
ReadWritePaths= creates its own mount namespace which might break some
-services. Hence, the ReadWritePaths=$BUILD_DIR is enabled for all services
+services. Hence, the `ReadWritePaths=$BUILD_DIR` is enabled for all services
with the `test-` prefix (i.e. test-foo.service or test-foo-bar.service), both
in the system and the user managers.
-So, if you're considering writing an integration test that makes use
-of DynamicUser=yes, or other sandboxing stuff that implies it, please prefix
-the test unit (be it a static one or a transient one created via systemd-run),
-with `test-`, unless the test unit needs to be able to install mount points
-in the main mount namespace - in that case use IGNORE_MISSING_COVERAGE=yes
-in the test definition (i.e. TEST-*-NAME/test.sh), which will skip the post-test
+So, if you're considering writing an integration test that makes use of
+DynamicUser=yes, or other sandboxing stuff that implies it, please prefix the
+test unit (be it a static one or a transient one created via systemd-run), with
+`test-`, unless the test unit needs to be able to install mount points in the
+main mount namespace - in that case use `IGNORE_MISSING_COVERAGE=yes` in the
+test definition (i.e. `TEST-*-NAME/test.sh`), which will skip the post-test
check for missing coverage for the respective test.
-
-[0] https://coveralls.io/github/systemd/systemd
diff --git a/test/TEST-01-BASIC/meson.build b/test/TEST-01-BASIC/meson.build
new file mode 100644
index 0000000..257dadd
--- /dev/null
+++ b/test/TEST-01-BASIC/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : 'TEST-01-BASIC',
+ },
+]
diff --git a/test/TEST-01-BASIC/test.sh b/test/TEST-01-BASIC/test.sh
index 93185cf..d961973 100755
--- a/test/TEST-01-BASIC/test.sh
+++ b/test/TEST-01-BASIC/test.sh
@@ -16,14 +16,4 @@ KERNEL_APPEND="foo -- -z bar --- baz $KERNEL_APPEND"
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
-test_append_files() {
- local workspace="${1:?}"
- local dst
-
- # Install tests manually so the test is functional even when -Dinstall-tests=false
- dst="$workspace/usr/lib/systemd/tests/testdata/units/"
- mkdir -p "$dst"
- cp -v "$TEST_UNITS_DIR"/{testsuite-01,end}.service "$TEST_UNITS_DIR/testsuite.target" "$dst"
-}
-
do_test "$@"
diff --git a/test/TEST-02-UNITTESTS/meson.build b/test/TEST-02-UNITTESTS/meson.build
new file mode 100644
index 0000000..b000339
--- /dev/null
+++ b/test/TEST-02-UNITTESTS/meson.build
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'cmdline' : integration_test_template['cmdline'] + [
+ '''
+
+systemd.setenv=TEST_CMDLINE_NEWLINE=foo
+ ''',
+ '''
+systemd.setenv=TEST_CMDLINE_NEWLINE=bar
+ ''',
+ ],
+ },
+]
diff --git a/test/testsuite-03.units/always-activating.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.service
index 93ddb85..93ddb85 100644
--- a/test/testsuite-03.units/always-activating.service
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.service
diff --git a/test/testsuite-03.units/always-activating.socket b/test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.socket
index c76fed2..c76fed2 100644
--- a/test/testsuite-03.units/always-activating.socket
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/always-activating.socket
diff --git a/test/testsuite-03.units/fails-on-restart-restartdirect.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.service
index 60ffd7a..60ffd7a 100644
--- a/test/testsuite-03.units/fails-on-restart-restartdirect.service
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.service
diff --git a/test/testsuite-03.units/fails-on-restart-restartdirect.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.target
index 58e2561..58e2561 100644
--- a/test/testsuite-03.units/fails-on-restart-restartdirect.target
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart-restartdirect.target
diff --git a/test/testsuite-03.units/fails-on-restart.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.service
index fb7e7ae..fb7e7ae 100644
--- a/test/testsuite-03.units/fails-on-restart.service
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.service
diff --git a/test/testsuite-03.units/fails-on-restart.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.target
index 865fb2a..865fb2a 100644
--- a/test/testsuite-03.units/fails-on-restart.target
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/fails-on-restart.target
diff --git a/test/testsuite-03.units/hello-after-sleep.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/hello-after-sleep.target
index b0ddb30..b0ddb30 100644
--- a/test/testsuite-03.units/hello-after-sleep.target
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/hello-after-sleep.target
diff --git a/test/testsuite-03.units/hello.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/hello.service
index 0c3f2f8..0c3f2f8 100644
--- a/test/testsuite-03.units/hello.service
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/hello.service
diff --git a/test/testsuite-03.units/propagatestopto-and-pullin.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-and-pullin.target
index 8e409af..8e409af 100644
--- a/test/testsuite-03.units/propagatestopto-and-pullin.target
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-and-pullin.target
diff --git a/test/testsuite-03.units/propagatestopto-indirect.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-indirect.target
index e8229a7..e8229a7 100644
--- a/test/testsuite-03.units/propagatestopto-indirect.target
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-indirect.target
diff --git a/test/testsuite-03.units/propagatestopto-only.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-only.target
index 327b7c1..327b7c1 100644
--- a/test/testsuite-03.units/propagatestopto-only.target
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/propagatestopto-only.target
diff --git a/test/testsuite-03.units/sleep-infinity-simple.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/sleep-infinity-simple.service
index 211346d..211346d 100644
--- a/test/testsuite-03.units/sleep-infinity-simple.service
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/sleep-infinity-simple.service
diff --git a/test/testsuite-03.units/sleep.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/sleep.service
index 32c2037..32c2037 100644
--- a/test/testsuite-03.units/sleep.service
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/sleep.service
diff --git a/test/testsuite-03.units/succeeds-on-restart-restartdirect.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.service
index b05f2f8..a2b9c33 100644
--- a/test/testsuite-03.units/succeeds-on-restart-restartdirect.service
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.service
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-03.units/succeeds-on-restart.sh
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-03-JOBS.units/succeeds-on-restart.sh
Restart=on-failure
RestartMode=direct
diff --git a/test/testsuite-03.units/succeeds-on-restart-restartdirect.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.target
index 2cf3c60..2cf3c60 100644
--- a/test/testsuite-03.units/succeeds-on-restart-restartdirect.target
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart-restartdirect.target
diff --git a/test/testsuite-03.units/succeeds-on-restart.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.service
index d7b3c7a..d0e9c2b 100644
--- a/test/testsuite-03.units/succeeds-on-restart.service
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.service
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-03.units/succeeds-on-restart.sh
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-03-JOBS.units/succeeds-on-restart.sh
Restart=on-failure
RestartMode=normal
diff --git a/test/testsuite-03.units/succeeds-on-restart.sh b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.sh
index 1428b18..1428b18 100755
--- a/test/testsuite-03.units/succeeds-on-restart.sh
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.sh
diff --git a/test/testsuite-03.units/succeeds-on-restart.target b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.target
index eb82f47..eb82f47 100644
--- a/test/testsuite-03.units/succeeds-on-restart.target
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/succeeds-on-restart.target
diff --git a/test/testsuite-03.units/unstoppable.service b/test/TEST-03-JOBS/TEST-03-JOBS.units/unstoppable.service
index 6eb7c19..6eb7c19 100644
--- a/test/testsuite-03.units/unstoppable.service
+++ b/test/TEST-03-JOBS/TEST-03-JOBS.units/unstoppable.service
diff --git a/test/TEST-03-JOBS/meson.build b/test/TEST-03-JOBS/meson.build
new file mode 100644
index 0000000..3484d21
--- /dev/null
+++ b/test/TEST-03-JOBS/meson.build
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-03-JOBS.units']
diff --git a/test/testsuite-04.units/delegated-cgroup-filtering.service b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/delegated-cgroup-filtering.service
index 2c4201a..7ecd358 100644
--- a/test/testsuite-04.units/delegated-cgroup-filtering.service
+++ b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/delegated-cgroup-filtering.service
@@ -2,7 +2,8 @@
Description=Test service for delegated logs filtering
[Service]
-Type=simple
+Type=oneshot
ExecStart=/usr/lib/systemd/tests/testdata/units/delegated_cgroup_filtering_payload.sh
Delegate=yes
SyslogLevel=notice
+LogLevelMax=info
diff --git a/test/testsuite-04.units/forever-print-hola.service b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/forever-print-hola.service
index 9273d6d..9273d6d 100644
--- a/test/testsuite-04.units/forever-print-hola.service
+++ b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/forever-print-hola.service
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
index 0000000..4c43005
--- /dev/null
+++ b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/logs-filtering.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Log filtering unit
+
+[Service]
+Type=oneshot
+ExecStart=sh -c 'echo "Logging from the service, and ~more~ foo bar"'
+# If the service finishes extremely fast, journald cannot find the source of the
+# stream. Hence, we need to call 'journalctl --sync' before service finishes.
+ExecStart=journalctl --sync
+SyslogLevel=notice
+LogLevelMax=info
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
index 0000000..d4541af
--- /dev/null
+++ b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/silent-success.service
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Silent successful service
+
+[Service]
+Type=oneshot
+LogLevelMax=notice
+ExecStart=/bin/true
+# If the service finishes extremely fast, journald cannot find the source of the
+# stream. Hence, we need to call 'journalctl --sync' before service finishes.
+ExecStart=journalctl --sync
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
index 0000000..004693b
--- /dev/null
+++ b/test/TEST-04-JOURNAL/TEST-04-JOURNAL.units/verbose-success.service
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Verbose successful service
+
+[Service]
+Type=oneshot
+ExecStart=echo success
+# If the service finishes extremely fast, journald cannot find the source of the
+# stream. Hence, we need to call 'journalctl --sync' before service finishes.
+ExecStart=journalctl --sync
+# Suppress debugging logs from PID1 or sd-executor. Otherwise, the client context
+# may be outdated when the stream from 'echo' command in the above comes.
+LogLevelMax=info
diff --git a/test/TEST-04-JOURNAL/meson.build b/test/TEST-04-JOURNAL/meson.build
new file mode 100644
index 0000000..5a0b073
--- /dev/null
+++ b/test/TEST-04-JOURNAL/meson.build
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'storage' : 'persistent',
+ },
+]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-04-JOURNAL.units']
diff --git a/test/TEST-04-JOURNAL/test.sh b/test/TEST-04-JOURNAL/test.sh
index 42574ce..a7aa71f 100755
--- a/test/TEST-04-JOURNAL/test.sh
+++ b/test/TEST-04-JOURNAL/test.sh
@@ -11,9 +11,6 @@ test_append_files() {
local workspace="${1:?}"
local dropin_dir
- mkdir -p "$workspace/test-journals/"
- cp -av "${TEST_BASE_DIR:?}/test-journals/"* "$workspace/test-journals/"
-
image_install curl setterm unzstd
image_install -o openssl
# Necessary for RH-based systems, otherwise MHD fails with:
@@ -23,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-05-RLIMITS/meson.build b/test/TEST-05-RLIMITS/meson.build
new file mode 100644
index 0000000..d8198fd
--- /dev/null
+++ b/test/TEST-05-RLIMITS/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : 'TEST-05-RLIMITS',
+ },
+]
diff --git a/test/testsuite-06.units/hola.service b/test/TEST-06-SELINUX/TEST-06-SELINUX.units/hola.service
index 94f9b47..94f9b47 100644
--- a/test/testsuite-06.units/hola.service
+++ b/test/TEST-06-SELINUX/TEST-06-SELINUX.units/hola.service
diff --git a/test/TEST-06-SELINUX/meson.build b/test/TEST-06-SELINUX/meson.build
new file mode 100644
index 0000000..7a850be
--- /dev/null
+++ b/test/TEST-06-SELINUX/meson.build
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'cmdline' : integration_test_template['cmdline'] + ['selinux=1', 'lsm=selinux'],
+ # FIXME; Figure out why reboot sometimes hangs with 'linux' firmware.
+ 'firmware' : 'uefi',
+ 'vm' : true,
+ },
+]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-06-SELINUX.units']
diff --git a/test/units/testsuite-07.service b/test/TEST-07-PID1/TEST-07-PID1.service
index 92302bf..53dd54c 100644
--- a/test/units/testsuite-07.service
+++ b/test/TEST-07-PID1/TEST-07-PID1.service
@@ -1,6 +1,9 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-07-PID1
+Wants=basic.target multi-user.target
+After=basic.target
+Before=getty-pre.target
[Service]
Type=oneshot
diff --git a/test/testsuite-07.units/issue14566-repro.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.service
index 5680596..757f978 100644
--- a/test/testsuite-07.units/issue14566-repro.service
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.service
@@ -3,6 +3,6 @@
Description=Issue 14566 Repro
[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-07.units/%N.sh
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-07-PID1.units/%N.sh
ExecStopPost=/bin/true
KillMode=mixed
diff --git a/test/testsuite-07.units/issue14566-repro.sh b/test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.sh
index 74fa760..74fa760 100755
--- a/test/testsuite-07.units/issue14566-repro.sh
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue14566-repro.sh
diff --git a/test/testsuite-07.units/issue16115-repro-1.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-1.service
index 90252b3..90252b3 100644
--- a/test/testsuite-07.units/issue16115-repro-1.service
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-1.service
diff --git a/test/testsuite-07.units/issue16115-repro-2.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-2.service
index 7c65691..7c65691 100644
--- a/test/testsuite-07.units/issue16115-repro-2.service
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-2.service
diff --git a/test/testsuite-07.units/issue16115-repro-3.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-3.service
index c68f93d..c68f93d 100644
--- a/test/testsuite-07.units/issue16115-repro-3.service
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue16115-repro-3.service
diff --git a/test/testsuite-07.units/issue2467.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue2467.service
index 99d886f..99d886f 100644
--- a/test/testsuite-07.units/issue2467.service
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue2467.service
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
index 0000000..209b6bb
--- /dev/null
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue2467.socket
@@ -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
index 0000000..802f026
--- /dev/null
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue2730-alias.mount
@@ -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
index 0000000..2ac76c0
--- /dev/null
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue2730.mount
@@ -0,0 +1,8 @@
+[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/TEST-07-PID1/TEST-07-PID1.units/issue27953.service
index f441067..f441067 100644
--- a/test/testsuite-07.units/issue27953.service
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue27953.service
diff --git a/test/testsuite-07.units/issue3166-fail-on-restart.service b/test/TEST-07-PID1/TEST-07-PID1.units/issue3166-fail-on-restart.service
index b8695d8..b8695d8 100644
--- a/test/testsuite-07.units/issue3166-fail-on-restart.service
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/issue3166-fail-on-restart.service
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
index 0000000..70a8534
--- /dev/null
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/local-fs.target.wants/issue2730.mount
@@ -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
index 0000000..8b7964b
--- /dev/null
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-no.socket
@@ -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
index 0000000..bff192d
--- /dev/null
+++ b/test/TEST-07-PID1/TEST-07-PID1.units/pass-fds-to-exec-yes.socket
@@ -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
diff --git a/test/TEST-07-PID1/meson.build b/test/TEST-07-PID1/meson.build
new file mode 100644
index 0000000..311860d
--- /dev/null
+++ b/test/TEST-07-PID1/meson.build
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'unit' : files('TEST-07-PID1.service'),
+ },
+]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-07-PID1.units']
diff --git a/test/TEST-07-PID1/test.sh b/test/TEST-07-PID1/test.sh
index cc8a81f..2513406 100755
--- a/test/TEST-07-PID1/test.sh
+++ b/test/TEST-07-PID1/test.sh
@@ -11,31 +11,6 @@ NSPAWN_ARGUMENTS="--capability=CAP_NET_ADMIN"
. "${TEST_BASE_DIR:?}/test-functions"
test_append_files() {
- local workspace="${1:?}"
-
- # We might not be fast enough to hit the 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 case.
- if ! get_bool "$QEMU_KVM" || get_bool "$IS_BUILT_WITH_COVERAGE"; then
- mkdir -p "$workspace/etc/systemd/system/issue2467.socket.d"
- printf "[Socket]\nTriggerLimitIntervalSec=10\n" >"$workspace/etc/systemd/system/issue2467.socket.d/TriggerLimitInterval.conf"
- fi
-
- # Issue: https://github.com/systemd/systemd/issues/2730
- mkdir -p "$workspace/etc/systemd/system/"
- cat >"$workspace/etc/systemd/system/issue2730.mount" <<EOF
-[Mount]
-What=tmpfs
-Where=/issue2730
-Type=tmpfs
-
-[Install]
-WantedBy=local-fs.target
-Alias=issue2730-alias.mount
-EOF
- "${SYSTEMCTL:?}" enable --root="$workspace" issue2730.mount
- ln -svrf "$workspace/etc/systemd/system/issue2730.mount" "$workspace/etc/systemd/system/issue2730-alias.mount"
-
image_install logger socat
}
diff --git a/test/TEST-08-INITRD/meson.build b/test/TEST-08-INITRD/meson.build
new file mode 100644
index 0000000..dcbfe20
--- /dev/null
+++ b/test/TEST-08-INITRD/meson.build
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'cmdline' : integration_test_template['cmdline'] + [
+ 'rd.systemd.wants=initrd-run-mount.service',
+ ],
+ 'exit-code' : 124,
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-08-INITRD/test.sh b/test/TEST-08-INITRD/test.sh
index e8dbb2c..73f743c 100755
--- a/test/TEST-08-INITRD/test.sh
+++ b/test/TEST-08-INITRD/test.sh
@@ -14,11 +14,11 @@ TEST_NO_NSPAWN=1
test_append_files() {
local workspace="${1:?}"
- local sd_initrd file dir
+ local exitrd file dir
- # Create a shutdown initrd
+ # Create an exitrd
#
- # This should provide coverage for shutdown initrd related issues, see:
+ # This should provide coverage for exitrd related issues, see:
# - https://github.com/systemd/systemd/issues/28645
# - https://github.com/systemd/systemd/pull/28648
# - https://github.com/systemd/systemd/pull/28793
@@ -26,25 +26,25 @@ test_append_files() {
# This is a bit messier than I originally anticipated, as installing our own libraries
# is handled implicitly by install_systemd() which I don't want to use here, since
# I need only the systemd-shutdown binary
- sd_initrd="$workspace/shutdown-initrd"
- mkdir -p "$sd_initrd/etc" "$sd_initrd/usr"
- initdir="$sd_initrd" image_install bash /usr/lib/os-release
- ln -srf "$sd_initrd/usr/lib/os-release" "$sd_initrd/etc/initrd-release"
- initdir="$sd_initrd" inst_binary "$workspace/usr/lib/systemd/systemd-shutdown" "/usr/lib/systemd/systemd-shutdown"
- initdir="$sd_initrd" inst_libs "$sd_initrd/usr/lib/systemd/systemd-shutdown"
+ exitrd="$workspace/exitrd"
+ mkdir -p "$exitrd/etc" "$exitrd/usr"
+ initdir="$exitrd" image_install bash /usr/lib/os-release
+ ln -srf "$exitrd/usr/lib/os-release" "$exitrd/etc/initrd-release"
+ initdir="$exitrd" inst_binary "$workspace/usr/lib/systemd/systemd-shutdown" "/usr/lib/systemd/systemd-shutdown"
+ initdir="$exitrd" inst_libs "$exitrd/usr/lib/systemd/systemd-shutdown"
# We need to deal with libsystemd stuff explicitly, as we don't call install_systemd() here
while read -r file; do
- initdir="$sd_initrd" inst_library "$file" "${file##"$workspace"}"
- initdir="$sd_initrd" inst_libs "$file"
+ initdir="$exitrd" inst_library "$file" "${file##"$workspace"}"
+ initdir="$exitrd" inst_libs "$file"
done < <(find "$workspace/usr/" -name "libsystemd*.so*")
# Call systemd-shutdown indirectly, so we can show a message that we can check for
- # later to make sure the shutdown initrd was actually executed
- cat >"$sd_initrd/shutdown" <<\EOF
+ # later to make sure the exitrd was actually executed
+ cat >"$exitrd/shutdown" <<\EOF
#!/usr/bin/bash -eu
-echo "Hello from shutdown initrd"
+echo "Hello from exitrd"
exec /usr/lib/systemd/systemd-shutdown "$@"
EOF
- chmod +x "$sd_initrd/shutdown"
+ chmod +x "$exitrd/shutdown"
}
check_result_qemu_hook() {
@@ -65,13 +65,13 @@ check_result_qemu_hook() {
# [ 6.245955] systemd-shutdown[1]: Failed to switch root to "/run/initramfs": Invalid argument
if grep -qE "systemd-shutdown.+: Failed to move /run/initramfs" "$console_log" ||
grep -qE "systemd-shutdown.+: Failed to switch root" "$console_log"; then
- derror "sd-shutdown failed to switch root in shutdown initrd"
+ derror "sd-shutdown failed to switch root in exitrd"
return 1
fi
- # Check if the shutdown initrd was executed at all
- if ! grep -qE "^Hello from shutdown initrd\s*$" "$console_log"; then
- derror "Missing 'hello' message from shutdown initrd"
+ # Check if the exitrd was executed at all
+ if ! grep -q "Hello from exitrd" "$console_log"; then
+ derror "Missing 'hello' message from exitrd"
return 1
fi
@@ -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() {
diff --git a/test/TEST-09-REBOOT/meson.build b/test/TEST-09-REBOOT/meson.build
new file mode 100644
index 0000000..c4b41bc
--- /dev/null
+++ b/test/TEST-09-REBOOT/meson.build
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'storage' : 'persistent',
+ # FIXME; Figure out why reboot sometimes hangs with 'linux' firmware.
+ 'firmware' : 'uefi',
+ },
+]
diff --git a/test/TEST-13-NSPAWN/meson.build b/test/TEST-13-NSPAWN/meson.build
new file mode 100644
index 0000000..77370ce
--- /dev/null
+++ b/test/TEST-13-NSPAWN/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-13-NSPAWN/test.sh b/test/TEST-13-NSPAWN/test.sh
index 93af59e..9a0404f 100755
--- a/test/TEST-13-NSPAWN/test.sh
+++ b/test/TEST-13-NSPAWN/test.sh
@@ -5,13 +5,19 @@ set -e
TEST_DESCRIPTION="systemd-nspawn tests"
IMAGE_NAME="nspawn"
TEST_NO_NSPAWN=1
+IMAGE_ADDITIONAL_ROOT_SIZE=500
+TEST_FORCE_NEWIMAGE=1
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
test_append_files() {
local workspace="${1:?}"
- local container="$workspace/testsuite-13-container-template"
+ local container="$workspace/usr/share/TEST-13-NSPAWN-container-template"
+
+ # For virtual wlan interface.
+ instmods mac80211_hwsim
+ generate_module_dependencies
# Create a dummy container "template" with a minimal toolset, which we can
# then use as a base for our nspawn/machinectl tests
diff --git a/test/TEST-15-DROPIN/meson.build b/test/TEST-15-DROPIN/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-15-DROPIN/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/units/testsuite-16.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.service
index d5494ae..3be8ef5 100644
--- a/test/units/testsuite-16.service
+++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.service
@@ -3,6 +3,10 @@
Description=TEST-16-EXTEND-TIMEOUT
# Testsuite: Assess all other testsuite-*.services worked as expected
+Wants=basic.target multi-user.target
+After=basic.target
+Before=getty-pre.target
+
Wants=success-all.service
Wants=success-start.service
Wants=success-runtime.service
@@ -14,7 +18,5 @@ StopWhenUnneeded=yes
[Service]
ExecStartPre=rm -f /failed /testok
-Type=exec
-TimeoutStartSec=infinity
-ExecStartPre=/usr/lib/systemd/tests/testdata/units/%N.sh
-ExecStart=true
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
diff --git a/test/testsuite-16.units/extend-timeout.sh b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
index 45a18b9..45a18b9 100755
--- a/test/testsuite-16.units/extend-timeout.sh
+++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/fail-runtime.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-runtime.service
index 1d74963..7380994 100644
--- a/test/testsuite-16.units/fail-runtime.service
+++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-runtime.service
@@ -10,4 +10,4 @@ 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
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/fail-start.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-start.service
index 38aebf2..984d8f9 100644
--- a/test/testsuite-16.units/fail-start.service
+++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-start.service
@@ -11,4 +11,4 @@ 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
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/fail-stop.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-stop.service
index 1910ac9..4293d7e 100644
--- a/test/testsuite-16.units/fail-stop.service
+++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/fail-stop.service
@@ -10,7 +10,7 @@ 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
+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/testsuite-16.units/success-all.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-all.service
index 1fdc363..7f26084 100644
--- a/test/testsuite-16.units/success-all.service
+++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-all.service
@@ -12,4 +12,4 @@ 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
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/success-runtime.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-runtime.service
index 2f5744d..33a36f3 100644
--- a/test/testsuite-16.units/success-runtime.service
+++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-runtime.service
@@ -11,4 +11,4 @@ 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
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/success-start.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-start.service
index be518e9..0453b34 100644
--- a/test/testsuite-16.units/success-start.service
+++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-start.service
@@ -10,4 +10,4 @@ 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
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/success-stop.service b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-stop.service
index 2bd3e3e..3719f54 100644
--- a/test/testsuite-16.units/success-stop.service
+++ b/test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.units/success-stop.service
@@ -10,4 +10,4 @@ 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
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-16-EXTEND-TIMEOUT.units/extend-timeout.sh
diff --git a/test/TEST-16-EXTEND-TIMEOUT/meson.build b/test/TEST-16-EXTEND-TIMEOUT/meson.build
new file mode 100644
index 0000000..15986ac
--- /dev/null
+++ b/test/TEST-16-EXTEND-TIMEOUT/meson.build
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'unit' : files('TEST-16-EXTEND-TIMEOUT.service'),
+ },
+]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-16-EXTEND-TIMEOUT.units']
diff --git a/test/TEST-17-UDEV/meson.build b/test/TEST-17-UDEV/meson.build
new file mode 100644
index 0000000..77370ce
--- /dev/null
+++ b/test/TEST-17-UDEV/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-18-FAILUREACTION/meson.build b/test/TEST-18-FAILUREACTION/meson.build
new file mode 100644
index 0000000..5edfbca
--- /dev/null
+++ b/test/TEST-18-FAILUREACTION/meson.build
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ # FIXME; Figure out why reboot sometimes hangs with 'linux' firmware.
+ 'firmware' : 'uefi',
+ },
+]
diff --git a/test/TEST-19-CGROUP/meson.build b/test/TEST-19-CGROUP/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-19-CGROUP/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-21-DFUZZER/meson.build b/test/TEST-21-DFUZZER/meson.build
new file mode 100644
index 0000000..f57be63
--- /dev/null
+++ b/test/TEST-21-DFUZZER/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'timeout' : 3600,
+ 'priority' : 50,
+ 'vm' : true,
+ 'enabled' : false,
+ },
+]
diff --git a/test/TEST-22-TMPFILES/meson.build b/test/TEST-22-TMPFILES/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-22-TMPFILES/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
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
index 0000000..022c37e
--- /dev/null
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-binds-to.service
@@ -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/testsuite-23.units/testsuite-23-bound-by.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-bound-by.service
index a2df5a1..c999c2e 100644
--- a/test/testsuite-23.units/testsuite-23-bound-by.service
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-bound-by.service
@@ -3,4 +3,4 @@
Description=Unit with BoundBy=
[Service]
-ExecStart=/bin/sleep 0.7
+ExecStart=sleep 0.7
diff --git a/test/testsuite-23.units/testsuite-23-fail.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-fail.service
index 36f8baa..b78e6b4 100644
--- a/test/testsuite-23.units/testsuite-23-fail.service
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-fail.service
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Failing unit
-OnFailure=testsuite-23-uphold.service
+OnFailure=TEST-23-UNIT-FILE-uphold.service
[Service]
-ExecStart=/bin/false
+ExecStart=false
diff --git a/test/testsuite-23.units/testsuite-23-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
index 9919a9f..47f0452 100644
--- a/test/testsuite-23.units/testsuite-23-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
@@ -4,4 +4,4 @@ Type=notify
NotifyAccess=all
MountAPIVFS=yes
PrivateTmp=yes
-ExecStart=/bin/bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
+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/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-2.service
index 36b4c27..42dd445 100644
--- a/test/testsuite-23.units/testsuite-23-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
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-1.service
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-1.service
[Service]
Type=oneshot
diff --git a/test/testsuite-23.units/testsuite-23-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
index 9094445..f745481 100644
--- a/test/testsuite-23.units/testsuite-23-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
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-1.service
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-1.service
[Service]
Type=oneshot
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
index 0000000..a0ff403
--- /dev/null
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-4.service
@@ -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/testsuite-23.units/testsuite-23-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
index c3d316b..c3d316b 100644
--- a/test/testsuite-23.units/testsuite-23-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
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
index 0000000..9227bda
--- /dev/null
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-6.service
@@ -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/testsuite-23.units/testsuite-23-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
index 60c083a..a5d62c6 100644
--- a/test/testsuite-23.units/testsuite-23-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
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-8.service
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-8.service
[Service]
Type=oneshot
diff --git a/test/testsuite-23.units/testsuite-23-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
index dac1cea..8e4944a 100644
--- a/test/testsuite-23.units/testsuite-23-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
@@ -6,4 +6,4 @@ MountAPIVFS=yes
PrivateTmp=yes
ExecStartPre=test -e /tmp/shared-private-file-x
ExecStartPre=test -e /tmp/hoge
-ExecStart=/bin/bash -c 'touch /tmp/shared-private-file-y && systemd-notify --ready && sleep infinity'
+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/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-joins-namespace-of-9.service
index 6c64873..a30f92a 100644
--- a/test/testsuite-23.units/testsuite-23-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
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
-JoinsNamespaceOf=testsuite-23-joins-namespace-of-8.service
+JoinsNamespaceOf=TEST-23-UNIT-FILE-joins-namespace-of-8.service
[Service]
Type=oneshot
diff --git a/test/testsuite-23.units/testsuite-23-namespaced.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-namespaced.service
index 5a6f5cd..82eb74c 100644
--- a/test/testsuite-23.units/testsuite-23-namespaced.service
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-namespaced.service
@@ -7,7 +7,7 @@ Type=notify
RemainAfterExit=yes
MountAPIVFS=yes
PrivateTmp=yes
-BindPaths=/run/testsuite-23-marker-fixed:/tmp/testfile-marker-fixed
+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=/bin/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'
+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
index 0000000..699b608
--- /dev/null
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-non-namespaced.service
@@ -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
index 0000000..4c9e10b
--- /dev/null
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-oneshot-restartforce.sh
@@ -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/units/testsuite-77-server.socket b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server.socket
index 4305077..c8f7595 100644
--- a/test/units/testsuite-77-server.socket
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server.socket
@@ -1,5 +1,5 @@
[Unit]
-Description=TEST-77-OPENFILE server socket
+Description=OpenFile= test server socket
[Socket]
ListenStream=/tmp/test.sock
diff --git a/test/units/testsuite-77-server@.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server@.service
index 8e99ac8..a57c804 100644
--- a/test/units/testsuite-77-server@.service
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-openfile-server@.service
@@ -1,5 +1,5 @@
[Unit]
-Description=TEST-77-OPENFILE server
+Description=OpenFile= test server service
[Service]
ExecStart=echo "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
index 0000000..0739aaf
--- /dev/null
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-one.service
@@ -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/testsuite-23.units/testsuite-23-prop-stop-two.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-two.service
index 2bcd209..b2bb869 100644
--- a/test/testsuite-23.units/testsuite-23-prop-stop-two.service
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-prop-stop-two.service
@@ -3,4 +3,4 @@
Description=Stop Propagation Sender
[Service]
-ExecStart=/bin/sleep 1.5
+ExecStart=sleep 1.5
diff --git a/test/testsuite-23.units/testsuite-23-retry-fail.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-fail.service
index 0fc27c4..319757a 100644
--- a/test/testsuite-23.units/testsuite-23-retry-fail.service
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-fail.service
@@ -5,5 +5,5 @@ Description=Failed Dependency Unit
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=/bin/sh -c "if [ -f /tmp/testsuite-23-retry-fail ]; then exit 0; else exit 1; fi"
+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/testsuite-23.units/testsuite-23-retry-upheld.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-upheld.service
index 0426d76..722a3b4 100644
--- a/test/testsuite-23.units/testsuite-23-retry-upheld.service
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-upheld.service
@@ -1,10 +1,10 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Upheld Unit
-Requires=testsuite-23-retry-fail.service
-After=testsuite-23-retry-fail.service
+Requires=TEST-23-UNIT-FILE-retry-fail.service
+After=TEST-23-UNIT-FILE-retry-fail.service
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=/bin/echo ok
+ExecStart=echo ok
diff --git a/test/testsuite-23.units/testsuite-23-retry-uphold.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-uphold.service
index f35e842..829ea5c 100644
--- a/test/testsuite-23.units/testsuite-23-retry-uphold.service
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-retry-uphold.service
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Upholding Unit
-Upholds=testsuite-23-retry-upheld.service
+Upholds=TEST-23-UNIT-FILE-retry-upheld.service
[Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
diff --git a/test/testsuite-23.units/testsuite-23-short-lived.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-short-lived.service
index 2dcb2ae..ac9583f 100644
--- a/test/testsuite-23.units/testsuite-23-short-lived.service
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-short-lived.service
@@ -8,4 +8,4 @@ StartLimitBurst=15
StartLimitIntervalSec=1h
[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/units/testsuite-23-short-lived.sh
+ExecStart=/usr/lib/systemd/tests/testdata/units/TEST-23-UNIT-FILE-short-lived.sh
diff --git a/test/testsuite-23.units/testsuite-23-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
index c45edd9..c45edd9 100644
--- a/test/testsuite-23.units/testsuite-23-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
diff --git a/test/testsuite-23.units/testsuite-23-specifier-j-wants.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-wants.service
index 9abb257..5aeee9f 100644
--- a/test/testsuite-23.units/testsuite-23-specifier-j-wants.service
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-specifier-j-wants.service
@@ -1,8 +1,8 @@
# 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
+Wants=TEST-23-UNIT-FILE-specifier-j-depends-%j.service
+After=TEST-23-UNIT-FILE-specifier-j-depends-%j.service
[Service]
Type=oneshot
diff --git a/test/testsuite-23.units/testsuite-23-success.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-success.service
index 410d4f8..1ae6271 100644
--- a/test/testsuite-23.units/testsuite-23-success.service
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-success.service
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Succeeding unit
-OnSuccess=testsuite-23-fail.service
+OnSuccess=TEST-23-UNIT-FILE-fail.service
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/testsuite-23.units/testsuite-23-upheldby-install.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-upheldby-install.service
index a456207..edb8fcb 100644
--- a/test/testsuite-23.units/testsuite-23-upheldby-install.service
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-upheldby-install.service
@@ -3,7 +3,7 @@
Description=Unit that sets UpheldBy= through [Install]
[Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
[Install]
-UpheldBy=testsuite-23-retry-uphold.service
+UpheldBy=TEST-23-UNIT-FILE-retry-uphold.service
diff --git a/test/testsuite-23.units/testsuite-23-uphold.service b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-uphold.service
index 3549d6a..36c5dae 100644
--- a/test/testsuite-23.units/testsuite-23-uphold.service
+++ b/test/TEST-23-UNIT-FILE/TEST-23-UNIT-FILE.units/TEST-23-UNIT-FILE-uphold.service
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Upholding Unit
-Upholds=testsuite-23-short-lived.service
+Upholds=TEST-23-UNIT-FILE-short-lived.service
[Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
diff --git a/test/TEST-23-UNIT-FILE/meson.build b/test/TEST-23-UNIT-FILE/meson.build
new file mode 100644
index 0000000..3f44662
--- /dev/null
+++ b/test/TEST-23-UNIT-FILE/meson.build
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-23-UNIT-FILE.units']
diff --git a/test/TEST-24-CRYPTSETUP/keydev.repart/00-root.conf b/test/TEST-24-CRYPTSETUP/keydev.repart/00-root.conf
new file mode 100644
index 0000000..d6cdad0
--- /dev/null
+++ b/test/TEST-24-CRYPTSETUP/keydev.repart/00-root.conf
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Partition]
+Type=linux-generic
+UUID=0fc63daf-8483-4772-8e79-3d69d8477de4
+Label=varcrypt_keydev
+SizeMinBytes=16M
+Format=ext4
+CopyFiles=/keyfile:/keyfile
diff --git a/test/TEST-24-CRYPTSETUP/keyfile b/test/TEST-24-CRYPTSETUP/keyfile
new file mode 100644
index 0000000..9daeafb
--- /dev/null
+++ b/test/TEST-24-CRYPTSETUP/keyfile
@@ -0,0 +1 @@
+test
diff --git a/test/TEST-24-CRYPTSETUP/meson.build b/test/TEST-24-CRYPTSETUP/meson.build
new file mode 100644
index 0000000..af41f16
--- /dev/null
+++ b/test/TEST-24-CRYPTSETUP/meson.build
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'credentials' : integration_test_template['credentials'] + [
+ files('keyfile'),
+ 'fstab.extra="/dev/mapper/test24_varcrypt /var ext4 defaults 0 1"',
+ ],
+ 'cmdline' : [
+ 'rd.systemd.wants=encrypted-var.service',
+ 'rd.luks=1',
+ 'luks.name=0d318174-56b0-4d6e-a324-ac1e7e7d235d=test24_varcrypt',
+ 'luks.key=0d318174-56b0-4d6e-a324-ac1e7e7d235d=/keyfile:LABEL=varcrypt_keydev',
+ 'luks.options=0d318174-56b0-4d6e-a324-ac1e7e7d235d=x-initrd.attach',
+ ],
+ 'qemu-args' : [
+ '-drive', 'id=keydev,if=none,format=raw,cache=unsafe,file=@0@'.format(project_build_root / 'mkosi.output/keydev.raw'),
+ '-device', 'scsi-hd,drive=keydev',
+ ],
+ 'mkosi-args' : integration_test_template['mkosi-args'] + [
+ '--runtime-size=11G',
+ ],
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-24-CRYPTSETUP/template.cfg b/test/TEST-24-CRYPTSETUP/template.cfg
new file mode 100644
index 0000000..42f8b9d
--- /dev/null
+++ b/test/TEST-24-CRYPTSETUP/template.cfg
@@ -0,0 +1,8 @@
+dn = "cn = systemd"
+expiration_days = 30
+
+signing_key
+encryption_key
+
+tls_www_client
+email_protection_key
diff --git a/test/TEST-24-CRYPTSETUP/test.sh b/test/TEST-24-CRYPTSETUP/test.sh
index 4ace177..a7e118c 100755
--- a/test/TEST-24-CRYPTSETUP/test.sh
+++ b/test/TEST-24-CRYPTSETUP/test.sh
@@ -18,6 +18,9 @@ KERNEL_OPTIONS=(
"luks.name=$PART_UUID=$DM_NAME"
"luks.key=$PART_UUID=/keyfile:LABEL=varcrypt_keydev"
"luks.options=$PART_UUID=x-initrd.attach"
+ # Forward journal to console to make debugging easier (or possible at all) if we fail to bring the
+ # encrypted /var up during boot
+ "systemd.journald.forward_to_console=1"
)
KERNEL_APPEND+=" ${KERNEL_OPTIONS[*]}"
QEMU_OPTIONS+=" -drive format=raw,cache=unsafe,file=${STATEDIR:?}/keydev.img"
@@ -39,6 +42,125 @@ check_result_qemu() {
return $ret
}
+can_test_pkcs11() {
+ if ! command -v "softhsm2-util" >/dev/null; then
+ ddebug "softhsm2-util not available, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! command -v "pkcs11-tool" >/dev/null; then
+ ddebug "pkcs11-tool not available, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! command -v "certtool" >/dev/null; then
+ ddebug "certtool not available, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! "${SYSTEMCTL:?}" --version | grep -q "+P11KIT"; then
+ ddebug "Support for p11-kit is disabled, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! "${SYSTEMCTL:?}" --version | grep -q "+OPENSSL"; then
+ ddebug "Support for openssl is disabled, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! "${SYSTEMCTL:?}" --version | grep -q "+LIBCRYPTSETUP\b"; then
+ ddebug "Support for libcryptsetup is disabled, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! "${SYSTEMCTL:?}" --version | grep -q "+LIBCRYPTSETUP_PLUGINS"; then
+ ddebug "Support for libcryptsetup plugins is disabled, skipping the PKCS#11 test"
+ return 1
+ fi
+
+ return 0
+}
+
+setup_pkcs11_token() {
+ dinfo "Setup PKCS#11 token"
+ local P11_MODULE_CONFIGS_DIR P11_MODULE_DIR SOFTHSM_MODULE
+
+ export SOFTHSM2_CONF="/tmp/softhsm2.conf"
+ mkdir -p "$initdir/usr/lib/softhsm/tokens/"
+ cat >${SOFTHSM2_CONF} <<EOF
+directories.tokendir = $initdir/usr/lib/softhsm/tokens/
+objectstore.backend = file
+slots.removable = false
+slots.mechanisms = ALL
+EOF
+ export GNUTLS_PIN="1234"
+ export GNUTLS_SO_PIN="12345678"
+ softhsm2-util --init-token --free --label "TestToken" --pin ${GNUTLS_PIN} --so-pin ${GNUTLS_SO_PIN}
+
+ if ! P11_MODULE_CONFIGS_DIR=$(pkg-config --variable=p11_module_configs p11-kit-1); then
+ echo "WARNING! Cannot get p11_module_configs from p11-kit-1.pc, assuming /usr/share/p11-kit/modules" >&2
+ P11_MODULE_CONFIGS_DIR="/usr/share/p11-kit/modules"
+ fi
+
+ if ! P11_MODULE_DIR=$(pkg-config --variable=p11_module_path p11-kit-1); then
+ echo "WARNING! Cannot get p11_module_path from p11-kit-1.pc, assuming /usr/lib/pkcs11" >&2
+ P11_MODULE_DIR="/usr/lib/pkcs11"
+ fi
+
+ SOFTHSM_MODULE=$(grep -F 'module:' "$P11_MODULE_CONFIGS_DIR/softhsm2.module"| cut -d ':' -f 2| xargs)
+ if [[ "$SOFTHSM_MODULE" =~ ^[^/] ]]; then
+ SOFTHSM_MODULE="$P11_MODULE_DIR/$SOFTHSM_MODULE"
+ fi
+
+ # RSA #####################################################
+ pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --keypairgen --key-type "RSA:2048" --label "RSATestKey" --usage-decrypt
+
+ certtool --generate-self-signed \
+ --load-privkey="pkcs11:token=TestToken;object=RSATestKey;type=private" \
+ --load-pubkey="pkcs11:token=TestToken;object=RSATestKey;type=public" \
+ --template "$TEST_BASE_DIR/$TESTNAME/template.cfg" \
+ --outder --outfile "/tmp/rsa_test.crt"
+
+ pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --write-object "/tmp/rsa_test.crt" --type cert --label "RSATestKey"
+ rm "/tmp/rsa_test.crt"
+
+ # prime256v1 ##############################################
+ pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --keypairgen --key-type "EC:prime256v1" --label "ECTestKey" --usage-derive
+
+ certtool --generate-self-signed \
+ --load-privkey="pkcs11:token=TestToken;object=ECTestKey;type=private" \
+ --load-pubkey="pkcs11:token=TestToken;object=ECTestKey;type=public" \
+ --template "$TEST_BASE_DIR/$TESTNAME/template.cfg" \
+ --outder --outfile "/tmp/ec_test.crt"
+
+ pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --write-object "/tmp/ec_test.crt" --type cert --label "ECTestKey"
+ rm "/tmp/ec_test.crt"
+
+ ###########################################################
+ rm ${SOFTHSM2_CONF}
+ unset SOFTHSM2_CONF
+
+ inst_libs "$SOFTHSM_MODULE"
+ inst_library "$SOFTHSM_MODULE"
+ inst_simple "$P11_MODULE_CONFIGS_DIR/softhsm2.module"
+
+ cat >"$initdir/etc/softhsm2.conf" <<EOF
+directories.tokendir = /usr/lib/softhsm/tokens/
+objectstore.backend = file
+slots.removable = false
+slots.mechanisms = ALL
+log.level = INFO
+EOF
+
+ mkdir -p "$initdir/etc/systemd/system/systemd-cryptsetup@.service.d"
+ cat >"$initdir/etc/systemd/system/systemd-cryptsetup@.service.d/PKCS11.conf" <<EOF
+[Unit]
+# Make sure we can start systemd-cryptsetup@empty_pkcs11_auto.service many times
+StartLimitBurst=10
+
+[Service]
+Environment="SOFTHSM2_CONF=/etc/softhsm2.conf"
+Environment="PIN=$GNUTLS_PIN"
+EOF
+
+ unset GNUTLS_PIN
+ unset GNUTLS_SO_PIN
+}
+
test_create_image() {
create_empty_image_rootdir
@@ -57,6 +179,10 @@ test_create_image() {
install_dmevent
generate_module_dependencies
+ if can_test_pkcs11; then
+ setup_pkcs11_token
+ fi
+
# Create a keydev
dd if=/dev/zero of="${STATEDIR:?}/keydev.img" bs=1M count=16
mkfs.ext4 -L varcrypt_keydev "$STATEDIR/keydev.img"
@@ -84,7 +210,7 @@ EOF
if command -v dracut >/dev/null; then
dracut --force --verbose --add crypt "$INITRD"
elif command -v mkinitcpio >/dev/null; then
- mkinitcpio --addhooks sd-encrypt --generate "$INITRD"
+ mkinitcpio -S autodetect --addhooks sd-encrypt --generate "$INITRD"
elif command -v mkinitramfs >/dev/null; then
# The cryptroot hook is provided by the cryptsetup-initramfs package
if ! dpkg-query -s cryptsetup-initramfs; then
diff --git a/test/TEST-25-IMPORT/meson.build b/test/TEST-25-IMPORT/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-25-IMPORT/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-26-SYSTEMCTL/meson.build b/test/TEST-26-SYSTEMCTL/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-26-SYSTEMCTL/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-29-PORTABLE/meson.build b/test/TEST-29-PORTABLE/meson.build
new file mode 100644
index 0000000..77370ce
--- /dev/null
+++ b/test/TEST-29-PORTABLE/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'vm' : true,
+ },
+]
diff --git a/test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf b/test/TEST-30-ONCLOCKCHANGE/TEST-30-ONCLOCKCHANGE.units/systemd-timedated.service.d/watchdog.conf
index e2e25d5..e2e25d5 100644
--- a/test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf
+++ b/test/TEST-30-ONCLOCKCHANGE/TEST-30-ONCLOCKCHANGE.units/systemd-timedated.service.d/watchdog.conf
diff --git a/test/TEST-30-ONCLOCKCHANGE/meson.build b/test/TEST-30-ONCLOCKCHANGE/meson.build
new file mode 100644
index 0000000..cd0f1f6
--- /dev/null
+++ b/test/TEST-30-ONCLOCKCHANGE/meson.build
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'vm' : true,
+ },
+]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-30-ONCLOCKCHANGE.units']
diff --git a/test/TEST-31-DEVICE-ENUMERATION/meson.build b/test/TEST-31-DEVICE-ENUMERATION/meson.build
new file mode 100644
index 0000000..77370ce
--- /dev/null
+++ b/test/TEST-31-DEVICE-ENUMERATION/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-32-OOMPOLICY/meson.build b/test/TEST-32-OOMPOLICY/meson.build
new file mode 100644
index 0000000..b715c0d
--- /dev/null
+++ b/test/TEST-32-OOMPOLICY/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'configuration' : integration_test_template['configuration'] + {
+ 'memory-accounting' : 'yes',
+ },
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-34-DYNAMICUSERMIGRATE/meson.build b/test/TEST-34-DYNAMICUSERMIGRATE/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-34-DYNAMICUSERMIGRATE/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-35-LOGIN/meson.build b/test/TEST-35-LOGIN/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-35-LOGIN/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-36-NUMAPOLICY/meson.build b/test/TEST-36-NUMAPOLICY/meson.build
new file mode 100644
index 0000000..77370ce
--- /dev/null
+++ b/test/TEST-36-NUMAPOLICY/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-38-FREEZER/meson.build b/test/TEST-38-FREEZER/meson.build
new file mode 100644
index 0000000..77370ce
--- /dev/null
+++ b/test/TEST-38-FREEZER/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-43-PRIVATEUSER-UNPRIV/meson.build b/test/TEST-43-PRIVATEUSER-UNPRIV/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-43-PRIVATEUSER-UNPRIV/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-43-PRIVATEUSER-UNPRIV/test.sh b/test/TEST-43-PRIVATEUSER-UNPRIV/test.sh
index 1d1dab4..c055735 100755
--- a/test/TEST-43-PRIVATEUSER-UNPRIV/test.sh
+++ b/test/TEST-43-PRIVATEUSER-UNPRIV/test.sh
@@ -12,6 +12,7 @@ has_user_dbus_socket || exit 0
test_require_bin mksquashfs
test_append_files() {
+ inst_binary mksquashfs
inst_binary unsquashfs
install_verity_minimal
}
diff --git a/test/TEST-44-LOG-NAMESPACE/TEST-44-LOG-NAMESPACE.service b/test/TEST-44-LOG-NAMESPACE/TEST-44-LOG-NAMESPACE.service
new file mode 100644
index 0000000..1b1dd98
--- /dev/null
+++ b/test/TEST-44-LOG-NAMESPACE/TEST-44-LOG-NAMESPACE.service
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-44-LOG-NAMESPACE
+Wants=basic.target multi-user.target systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
+After=basic.target systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
+Before=getty-pre.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/TEST-44-LOG-NAMESPACE/meson.build b/test/TEST-44-LOG-NAMESPACE/meson.build
new file mode 100644
index 0000000..555ba7b
--- /dev/null
+++ b/test/TEST-44-LOG-NAMESPACE/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'unit' : files('TEST-44-LOG-NAMESPACE.service'),
+ },
+]
diff --git a/test/TEST-45-TIMEDATE/meson.build b/test/TEST-45-TIMEDATE/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-45-TIMEDATE/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-46-HOMED/meson.build b/test/TEST-46-HOMED/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-46-HOMED/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-46-HOMED/test.sh b/test/TEST-46-HOMED/test.sh
index 3bf3891..923e002 100755
--- a/test/TEST-46-HOMED/test.sh
+++ b/test/TEST-46-HOMED/test.sh
@@ -21,6 +21,9 @@ test_append_files() {
install_btrfs
generate_module_dependencies
fi
+ inst_binary ssh
+ inst_binary sshd
+ inst_binary ssh-keygen
}
do_test "$@"
diff --git a/test/TEST-50-DISSECT/meson.build b/test/TEST-50-DISSECT/meson.build
new file mode 100644
index 0000000..77370ce
--- /dev/null
+++ b/test/TEST-50-DISSECT/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-50-DISSECT/test.sh b/test/TEST-50-DISSECT/test.sh
index f1abce8..a0cd677 100755
--- a/test/TEST-50-DISSECT/test.sh
+++ b/test/TEST-50-DISSECT/test.sh
@@ -12,6 +12,15 @@ TEST_INSTALL_VERITY_MINIMAL=1
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
+# On Ubuntu the BPF LSM is not enabled by default, so we need to do it via the
+# kernel command line on boot
+if [ "$LOOKS_LIKE_UBUNTU" = "yes" ]; then
+ KERNEL_OPTIONS=(
+ "lsm=lockdown,capability,landlock,yama,apparmor,bpf"
+ )
+ KERNEL_APPEND+=" ${KERNEL_OPTIONS[*]}"
+fi
+
test_require_bin mksquashfs veritysetup sfdisk
test_append_files() {
@@ -21,11 +30,13 @@ test_append_files() {
generate_module_dependencies
inst_binary wc
inst_binary sha256sum
+ inst_binary tar
if command -v openssl >/dev/null 2>&1; then
inst_binary openssl
fi
inst_binary mksquashfs
inst_binary unsquashfs
+ inst_binary pkcheck
install_verity_minimal
}
diff --git a/test/testsuite-52.units/test-honor-first-shutdown.service b/test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.service
index abcf578..bbeac3a 100644
--- a/test/testsuite-52.units/test-honor-first-shutdown.service
+++ b/test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.service
@@ -4,7 +4,7 @@ Description=Honor First Shutdown feature
After=multi-user.target
[Service]
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-52.units/%N.sh
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-52-HONORFIRSTSHUTDOWN.units/%N.sh
ExecStop=sh -c 'kill -KILL $MAINPID'
FailureAction=reboot
diff --git a/test/testsuite-52.units/test-honor-first-shutdown.sh b/test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.sh
index 0cb574f..0cb574f 100755
--- a/test/testsuite-52.units/test-honor-first-shutdown.sh
+++ b/test/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units/test-honor-first-shutdown.sh
diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/meson.build b/test/TEST-52-HONORFIRSTSHUTDOWN/meson.build
new file mode 100644
index 0000000..08503e8
--- /dev/null
+++ b/test/TEST-52-HONORFIRSTSHUTDOWN/meson.build
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-52-HONORFIRSTSHUTDOWN.units']
diff --git a/test/TEST-53-ISSUE-16347/meson.build b/test/TEST-53-ISSUE-16347/meson.build
new file mode 100644
index 0000000..4c764a2
--- /dev/null
+++ b/test/TEST-53-ISSUE-16347/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'mkosi-args' : integration_test_template['mkosi-args'] + [
+ '--configure-script', meson.current_source_dir() / 'mkosi.configure',
+ ],
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-53-ISSUE-16347/mkosi.configure b/test/TEST-53-ISSUE-16347/mkosi.configure
new file mode 100755
index 0000000..d754fe4
--- /dev/null
+++ b/test/TEST-53-ISSUE-16347/mkosi.configure
@@ -0,0 +1,6 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+RTC="$(date -u +%Y-%m-%dT%H:%M:%S -d "+3 days")"
+jq ".QemuArgs += [\"-rtc\", \"base=$RTC\"]"
diff --git a/test/TEST-54-CREDS/meson.build b/test/TEST-54-CREDS/meson.build
new file mode 100644
index 0000000..8edb043
--- /dev/null
+++ b/test/TEST-54-CREDS/meson.build
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'cmdline' : integration_test_template['cmdline'] + [
+ 'systemd.set_credential=kernelcmdlinecred:uff',
+ 'systemd.set_credential=sysctl.extra:kernel.domainname=sysctltest',
+ 'systemd.set_credential=login.motd:hello',
+ 'systemd.set_credential=login.issue:welcome',
+ 'systemd.set_credential_binary=waldi:d29vb29mZmZ3dWZmZnd1ZmYK',
+ 'rd.systemd.import_credentials=no',
+ 'rd.systemd.wants=initrdcred.service',
+ ],
+ 'credentials' : integration_test_template['credentials'] + [
+ 'mynspawncredential=strangevalue',
+ files('systemd.extra-unit.my-service.service'),
+ files('systemd.unit-dropin.my-service.service'),
+ files('systemd.unit-dropin.my-service.service~30-named.service'),
+ ],
+ 'qemu-args' : integration_test_template['qemu-args'] + [
+ '-fw_cfg', 'name=opt/io.systemd.credentials/myqemucredential,string=othervalue',
+ '-smbios', 'type=11,value=io.systemd.credential:smbioscredential=magicdata',
+ '-smbios', 'type=11,value=io.systemd.credential.binary:binarysmbioscredential=bWFnaWNiaW5hcnlkYXRh',
+ '-smbios', 'type=11,value=io.systemd.credential.binary:sysusers.extra=dSBjcmVkdGVzdHVzZXIK',
+ '-smbios', 'type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg==',
+ '-smbios', 'type=11,value=io.systemd.credential.binary:fstab.extra=aW5qZWN0ZWQgL2luamVjdGVkIHRtcGZzIFgtbW91bnQubWtkaXIgMCAwCg==',
+ '-smbios', 'type=11,value=io.systemd.credential:getty.ttys.container=idontexist',
+ ],
+ },
+]
diff --git a/test/units/testsuite-69.service b/test/TEST-54-CREDS/systemd.extra-unit.my-service.service
index 7aa0664..e29d0ee 100644
--- a/test/units/testsuite-69.service
+++ b/test/TEST-54-CREDS/systemd.extra-unit.my-service.service
@@ -1,7 +1,5 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-69-SHUTDOWN
[Service]
Type=oneshot
-ExecStart=/bin/true
+ExecStart=touch /tmp/unit-cred
diff --git a/test/TEST-54-CREDS/systemd.unit-dropin.my-service.service b/test/TEST-54-CREDS/systemd.unit-dropin.my-service.service
new file mode 100644
index 0000000..279a941
--- /dev/null
+++ b/test/TEST-54-CREDS/systemd.unit-dropin.my-service.service
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Service]
+ExecStart=touch /tmp/unit-dropin
diff --git a/test/TEST-54-CREDS/systemd.unit-dropin.my-service.service~30-named.service b/test/TEST-54-CREDS/systemd.unit-dropin.my-service.service~30-named.service
new file mode 100644
index 0000000..67d87ca
--- /dev/null
+++ b/test/TEST-54-CREDS/systemd.unit-dropin.my-service.service~30-named.service
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Service]
+ExecStart=touch /tmp/unit-named-dropin
diff --git a/test/TEST-54-CREDS/test.sh b/test/TEST-54-CREDS/test.sh
index c0a9d7a..99c44e3 100755
--- a/test/TEST-54-CREDS/test.sh
+++ b/test/TEST-54-CREDS/test.sh
@@ -9,6 +9,23 @@ NSPAWN_CREDS=(
)
NSPAWN_ARGUMENTS="${NSPAWN_ARGUMENTS:-} ${NSPAWN_CREDS[*]}"
+UNIT_CRED=$(base64 -w 0 <<EOF
+[Service]
+Type=oneshot
+ExecStart=touch /tmp/unit-cred
+EOF
+)
+DROPIN_CRED=$(base64 -w 0 <<EOF
+[Service]
+ExecStart=touch /tmp/unit-dropin
+EOF
+)
+NAMED_DROPIN_CRED=$(base64 -w 0 <<EOF
+[Service]
+ExecStart=touch /tmp/unit-named-dropin
+EOF
+)
+
QEMU_CREDS=(
"-fw_cfg name=opt/io.systemd.credentials/myqemucredential,string=othervalue"
"-smbios type=11,value=io.systemd.credential:smbioscredential=magicdata"
@@ -17,6 +34,9 @@ QEMU_CREDS=(
"-smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg=="
"-smbios type=11,value=io.systemd.credential.binary:fstab.extra=aW5qZWN0ZWQgL2luamVjdGVkIHRtcGZzIFgtbW91bnQubWtkaXIgMCAwCg=="
"-smbios type=11,value=io.systemd.credential:getty.ttys.container=idontexist"
+ "-smbios type=11,value=io.systemd.credential.binary:systemd.extra-unit.my-service.service=$UNIT_CRED"
+ "-smbios type=11,value=io.systemd.credential.binary:systemd.unit-dropin.my-service.service=$DROPIN_CRED"
+ "-smbios type=11,value=io.systemd.credential.binary:systemd.unit-dropin.my-service.service~30-named=$NAMED_DROPIN_CRED"
)
QEMU_OPTIONS="${QEMU_OPTIONS:-} ${QEMU_CREDS[*]}"
diff --git a/test/TEST-55-OOMD/meson.build b/test/TEST-55-OOMD/meson.build
new file mode 100644
index 0000000..adc0509
--- /dev/null
+++ b/test/TEST-55-OOMD/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'credentials' : integration_test_template['credentials'] + [
+ files('systemd.unit-dropin.init.scope'),
+ ],
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-55-OOMD/systemd.unit-dropin.init.scope b/test/TEST-55-OOMD/systemd.unit-dropin.init.scope
new file mode 100644
index 0000000..31b7f90
--- /dev/null
+++ b/test/TEST-55-OOMD/systemd.unit-dropin.init.scope
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Scope]
+MemoryHigh=infinity
+StartupMemoryHigh=10G
diff --git a/test/TEST-58-REPART/meson.build b/test/TEST-58-REPART/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-58-REPART/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-59-RELOADING-RESTART/meson.build b/test/TEST-59-RELOADING-RESTART/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-59-RELOADING-RESTART/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-60-MOUNT-RATELIMIT/meson.build b/test/TEST-60-MOUNT-RATELIMIT/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-60-MOUNT-RATELIMIT/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-62-RESTRICT-IFACES/meson.build b/test/TEST-62-RESTRICT-IFACES/meson.build
new file mode 100644
index 0000000..77370ce
--- /dev/null
+++ b/test/TEST-62-RESTRICT-IFACES/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'vm' : true,
+ },
+]
diff --git a/test/testsuite-63.units/test63-glob.path b/test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.path
index 5f237a9..5f237a9 100644
--- a/test/testsuite-63.units/test63-glob.path
+++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.path
diff --git a/test/testsuite-63.units/test63-glob.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.service
index 3f49dd4..3f49dd4 100644
--- a/test/testsuite-63.units/test63-glob.service
+++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-glob.service
diff --git a/test/testsuite-63.units/test63-issue-24577-dep.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577-dep.service
index e332ea4..e332ea4 100644
--- a/test/testsuite-63.units/test63-issue-24577-dep.service
+++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577-dep.service
diff --git a/test/testsuite-63.units/test63-issue-24577.path b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.path
index 80ba1db..80ba1db 100644
--- a/test/testsuite-63.units/test63-issue-24577.path
+++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.path
diff --git a/test/testsuite-63.units/test63-issue-24577.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.service
index 568518b..568518b 100644
--- a/test/testsuite-63.units/test63-issue-24577.service
+++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-issue-24577.service
diff --git a/test/testsuite-63.units/test63-pr-30768.path b/test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.path
index b541358..b541358 100644
--- a/test/testsuite-63.units/test63-pr-30768.path
+++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.path
diff --git a/test/testsuite-63.units/test63-pr-30768.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.service
index 5739084..5739084 100644
--- a/test/testsuite-63.units/test63-pr-30768.service
+++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63-pr-30768.service
diff --git a/test/testsuite-63.units/test63.path b/test/TEST-63-PATH/TEST-63-PATH.units/test63.path
index 64d5ed6..64d5ed6 100644
--- a/test/testsuite-63.units/test63.path
+++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63.path
diff --git a/test/testsuite-63.units/test63.service b/test/TEST-63-PATH/TEST-63-PATH.units/test63.service
index 01a928b..01a928b 100644
--- a/test/testsuite-63.units/test63.service
+++ b/test/TEST-63-PATH/TEST-63-PATH.units/test63.service
diff --git a/test/TEST-63-PATH/meson.build b/test/TEST-63-PATH/meson.build
new file mode 100644
index 0000000..4aa3afd
--- /dev/null
+++ b/test/TEST-63-PATH/meson.build
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-63-PATH.units']
diff --git a/test/TEST-64-UDEV-STORAGE/btrfs_basic.configure b/test/TEST-64-UDEV-STORAGE/btrfs_basic.configure
new file mode 100755
index 0000000..8ef58a9
--- /dev/null
+++ b/test/TEST-64-UDEV-STORAGE/btrfs_basic.configure
@@ -0,0 +1,26 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import json
+import sys
+
+
+config = json.load(sys.stdin)
+
+config["QemuArgs"] += ["-device", "virtio-scsi-pci,id=scsi0"]
+
+for i in range(4):
+ id = f"drivebtrfsbasic{i}"
+ config["QemuDrives"] += [
+ {
+ "Id": id,
+ "Size": "350M" if i == 0 else "128M",
+ "Options": "cache=unsafe",
+ }
+ ]
+ config["QemuArgs"] += [
+ "-device",
+ f"scsi-hd,drive={id},vendor=systemd,product=foobar,serial=deadbeefbtrfs{i}",
+ ]
+
+json.dump(config, sys.stdout)
diff --git a/test/TEST-64-UDEV-STORAGE/iscsi_lvm.configure b/test/TEST-64-UDEV-STORAGE/iscsi_lvm.configure
new file mode 100755
index 0000000..ca23e33
--- /dev/null
+++ b/test/TEST-64-UDEV-STORAGE/iscsi_lvm.configure
@@ -0,0 +1,26 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import json
+import sys
+
+
+config = json.load(sys.stdin)
+
+config["QemuArgs"] += ["-device", "virtio-scsi-pci,id=scsi0"]
+
+for i in range(4):
+ id = f"driveiscsibasic{i}"
+ config["QemuDrives"] += [
+ {
+ "Id": id,
+ "Size": "150M" if i == 0 else "70M",
+ "Options": "cache=unsafe",
+ }
+ ]
+ config["QemuArgs"] += [
+ "-device",
+ f"scsi-hd,drive={id},vendor=systemd,product=foobar,serial=deadbeefiscsi{i}",
+ ]
+
+json.dump(config, sys.stdout)
diff --git a/test/TEST-64-UDEV-STORAGE/long_sysfs_path.configure b/test/TEST-64-UDEV-STORAGE/long_sysfs_path.configure
new file mode 100755
index 0000000..6108bdb
--- /dev/null
+++ b/test/TEST-64-UDEV-STORAGE/long_sysfs_path.configure
@@ -0,0 +1,32 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import json
+import sys
+
+
+config = json.load(sys.stdin)
+
+config["QemuDrives"] += [
+ {
+ "Id": "drive0",
+ "Size": "64M",
+ "Options": "cache=unsafe",
+ }
+]
+config["QemuArgs"] += ["-device", "pci-bridge,id=pci_bridge0,chassis_nr=64"]
+
+# Create 25 additional PCI bridges, each one connected to the previous one
+# (basically a really long extension cable), and attach a virtio drive to
+# the last one. This should force udev into attempting to create a device
+# unit with a _really_ long name.
+for bridge in range(1, 26):
+ config["QemuArgs"] += [
+ "-device",
+ f"pci-bridge,id=pci_bridge{bridge},bus=pci_bridge{bridge - 1},"
+ f"chassis_nr={64 + bridge}"
+ ]
+
+config["QemuArgs"] += ["-device", f"virtio-blk-pci,drive=drive0,scsi=off,bus=pci_bridge25"]
+
+json.dump(config, sys.stdout)
diff --git a/test/TEST-64-UDEV-STORAGE/lvm_basic.configure b/test/TEST-64-UDEV-STORAGE/lvm_basic.configure
new file mode 100755
index 0000000..9387b83
--- /dev/null
+++ b/test/TEST-64-UDEV-STORAGE/lvm_basic.configure
@@ -0,0 +1,25 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import json
+import sys
+
+
+config = json.load(sys.stdin)
+
+config["QemuArgs"] += ["-device", "virtio-scsi-pci,id=scsi0"]
+
+for i in range(4):
+ id = f"drivelvmbasic{i}"
+ config["QemuDrives"] += [
+ {
+ "Id": id,
+ "Size": "32M",
+ "Options": "cache=unsafe",
+ }
+ ]
+ config["QemuArgs"] += [
+ "-device", f"scsi-hd,drive={id},vendor=systemd,product=foobar,serial=deadbeeflvm{i}",
+ ]
+
+json.dump(config, sys.stdout)
diff --git a/test/TEST-64-UDEV-STORAGE/mdadm_basic.configure b/test/TEST-64-UDEV-STORAGE/mdadm_basic.configure
new file mode 100755
index 0000000..3f00afa
--- /dev/null
+++ b/test/TEST-64-UDEV-STORAGE/mdadm_basic.configure
@@ -0,0 +1,25 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import json
+import sys
+
+
+config = json.load(sys.stdin)
+
+config["QemuArgs"] += ["-device", "virtio-scsi-pci,id=scsi0"]
+
+for i in range(5):
+ id = f"drivemdadmbasic{i}"
+ config["QemuDrives"] += [
+ {
+ "Id": id,
+ "Size": "64M",
+ "Options": "cache=unsafe",
+ }
+ ]
+ config["QemuArgs"] += [
+ "-device", f"scsi-hd,drive={id},vendor=systemd,product=foobar,serial=deadbeefmdadm{i}",
+ ]
+
+json.dump(config, sys.stdout)
diff --git a/test/TEST-64-UDEV-STORAGE/mdadm_lvm.configure b/test/TEST-64-UDEV-STORAGE/mdadm_lvm.configure
new file mode 100755
index 0000000..b7661d9
--- /dev/null
+++ b/test/TEST-64-UDEV-STORAGE/mdadm_lvm.configure
@@ -0,0 +1,25 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import json
+import sys
+
+
+config = json.load(sys.stdin)
+
+config["QemuArgs"] += ["-device", "virtio-scsi-pci,id=scsi0"]
+
+for i in range(5):
+ id = f"drivemdadmlvm{i}"
+ config["QemuDrives"] += [
+ {
+ "Id": id,
+ "Size": "64M",
+ "Options": "cache=unsafe",
+ }
+ ]
+ config["QemuArgs"] += [
+ "-device", f"scsi-hd,drive={id},vendor=systemd,product=foobar,serial=deadbeefmdadmlvm{i}",
+ ]
+
+json.dump(config, sys.stdout)
diff --git a/test/TEST-64-UDEV-STORAGE/meson.build b/test/TEST-64-UDEV-STORAGE/meson.build
new file mode 100644
index 0000000..15981ce
--- /dev/null
+++ b/test/TEST-64-UDEV-STORAGE/meson.build
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+name = fs.name(meson.current_source_dir())
+unit = configure_file(
+ input : files('../test.service.in'),
+ output : '@0@.service'.format(name),
+ # Disable user service manager by default for performance.
+ configuration : integration_test_template['configuration'] + {
+ 'wants' : '',
+ 'after' : '',
+ },
+)
+
+foreach testcase : [
+ 'btrfs_basic',
+ 'iscsi_lvm',
+ 'long_sysfs_path',
+ 'lvm_basic',
+ 'mdadm_basic',
+ 'mdadm_lvm',
+ 'multipath_basic_failover',
+ 'nvme_basic',
+ 'nvme_subsystem',
+ 'simultaneous_events',
+ 'virtio_scsi_basic',
+ 'virtio_scsi_identically_named_partitions',
+]
+ integration_tests += [
+ integration_test_template + {
+ 'name' : '@0@-@1@'.format(name, testcase),
+ # Make sure the service is still named TEST-64-UDEV-STORAGE.service.
+ 'unit' : unit,
+ 'cmdline' : integration_test_template['cmdline'] + [
+ 'systemd.setenv=TEST_FUNCTION_NAME=testcase_@0@'.format(testcase)
+ ],
+ 'mkosi-args' : integration_test_template['mkosi-args'] + [
+ '--configure-script', files('@0@.configure'.format(testcase)),
+ ],
+ 'priority' : 10,
+ 'vm' : true,
+ },
+ ]
+endforeach
diff --git a/test/TEST-64-UDEV-STORAGE/multipath_basic_failover.configure b/test/TEST-64-UDEV-STORAGE/multipath_basic_failover.configure
new file mode 100755
index 0000000..5f323b8
--- /dev/null
+++ b/test/TEST-64-UDEV-STORAGE/multipath_basic_failover.configure
@@ -0,0 +1,31 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import json
+import sys
+
+
+config = json.load(sys.stdin)
+
+# Add 16 multipath devices, each backed by 4 paths
+# We don't use --qemu-drive for this since they have to share the file.
+for ndisk in range(16):
+ wwn = f"0xDEADDEADBEEF{ndisk:04d}"
+ if ndisk == 0:
+ size = "16M"
+ else:
+ size = "1M"
+
+ for nback in range(4):
+ id = f"drive{ndisk}x{nback}"
+ config["QemuDrives"] += [
+ {
+ "Id": id,
+ "Size": size,
+ "Options": "cache=unsafe",
+ "FileId": str(ndisk),
+ }
+ ]
+ config["QemuArgs"] += ["-device", f"scsi-hd,drive={id},serial=MPIO{ndisk},wwn={wwn}"]
+
+json.dump(config, sys.stdout)
diff --git a/test/TEST-64-UDEV-STORAGE/nvme_basic.configure b/test/TEST-64-UDEV-STORAGE/nvme_basic.configure
new file mode 100755
index 0000000..37d0d35
--- /dev/null
+++ b/test/TEST-64-UDEV-STORAGE/nvme_basic.configure
@@ -0,0 +1,39 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import json
+import os
+import subprocess
+import sys
+
+
+config = json.load(sys.stdin)
+
+qemu = f"qemu-system-{os.environ["QEMU_ARCHITECTURE"]}"
+result = subprocess.run([qemu, '-device', 'help'], check=True, text=True, stdout=subprocess.PIPE)
+if 'name "nvme"' not in result.stdout:
+ print("nvme device driver is not available, skipping test...", file=sys.stderr)
+ exit(77)
+
+def add_drive(i: int, serial: str) -> None:
+ global config
+ id = f"nvme{i}"
+ config["QemuDrives"] += [
+ {
+ "Id": id,
+ "Size": "1M",
+ "Options": "cache=unsafe",
+ }
+ ]
+ config["QemuArgs"] += ["-device", f"nvme,drive={id},serial={serial},num_queues=8"]
+
+for i in range(5):
+ add_drive(i, serial=f"deadbeef{i}")
+for i in range(5, 10):
+ add_drive(i, serial=f" deadbeef {i} ")
+for i in range(10, 15):
+ add_drive(i, serial=f" dead/beef/{i} ")
+for i in range(15, 20):
+ add_drive(i, serial=f"dead/../../beef/{i}")
+
+json.dump(config, sys.stdout)
diff --git a/test/TEST-64-UDEV-STORAGE/nvme_subsystem.configure b/test/TEST-64-UDEV-STORAGE/nvme_subsystem.configure
new file mode 100755
index 0000000..eb601a6
--- /dev/null
+++ b/test/TEST-64-UDEV-STORAGE/nvme_subsystem.configure
@@ -0,0 +1,39 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import json
+import os
+import subprocess
+import sys
+
+
+config = json.load(sys.stdin)
+
+qemu = f"qemu-system-{os.environ["QEMU_ARCHITECTURE"]}"
+result = subprocess.run([qemu, '-device', 'help'], check=True, text=True, stdout=subprocess.PIPE)
+if 'name "nvme"' not in result.stdout:
+ print("nvme device driver is not available, skipping test...", file=sys.stderr)
+ exit(77)
+
+for id in ("nvme0", "nvme1"):
+ config["QemuDrives"] += [
+ {
+ "Id": id,
+ "Size": "1M",
+ "Options": "cache=unsafe",
+ }
+
+ ]
+
+config["QemuArgs"] += [
+ # Create an NVM Subsystem Device
+ "-device", "nvme-subsys,id=nvme-subsys-64,nqn=subsys64",
+ # Attach two NVM controllers to it
+ "-device", "nvme,subsys=nvme-subsys-64,serial=deadbeef",
+ "-device", "nvme,subsys=nvme-subsys-64,serial=deadbeef",
+ # And create two shared namespaces attached to both controllers
+ "-device", "nvme-ns,drive=nvme0,nsid=16,shared=on",
+ "-device", "nvme-ns,drive=nvme1,nsid=17,shared=on",
+]
+
+json.dump(config, sys.stdout)
diff --git a/test/TEST-64-UDEV-STORAGE/simultaneous_events.configure b/test/TEST-64-UDEV-STORAGE/simultaneous_events.configure
new file mode 100755
index 0000000..a0edb01
--- /dev/null
+++ b/test/TEST-64-UDEV-STORAGE/simultaneous_events.configure
@@ -0,0 +1,21 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import json
+import sys
+
+
+config = json.load(sys.stdin)
+
+for i in range(10):
+ id = f"drivesimultaneousevents{i}"
+ config["QemuDrives"] += [
+ {
+ "Id": id,
+ "Size": "128M",
+ "Options": "cache=unsafe",
+ }
+ ]
+ config["QemuArgs"] += ["-device", f"scsi-hd,drive={id},serial=deadbeeftest{i}"]
+
+json.dump(config, sys.stdout)
diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh
index b9e7bdf..b5a70ca 100755
--- a/test/TEST-64-UDEV-STORAGE/test.sh
+++ b/test/TEST-64-UDEV-STORAGE/test.sh
@@ -159,18 +159,18 @@ test_run() {
return 0
}
-testcase_megasas2_basic() {
- if ! "${QEMU_BIN:?}" -device help | grep 'name "megasas-gen2"'; then
- echo "megasas-gen2 device driver is not available, skipping test..."
+testcase_virtio_scsi_basic() {
+ if ! "${QEMU_BIN:?}" -device help | grep 'name "virtio-scsi-pci"'; then
+ echo "virtio-scsi-pci device driver is not available, skipping test..."
return 77
fi
local i
local qemu_opts=(
- "-device megasas-gen2,id=scsi0"
- "-device megasas-gen2,id=scsi1"
- "-device megasas-gen2,id=scsi2"
- "-device megasas-gen2,id=scsi3"
+ "-device virtio-scsi-pci,id=scsi0"
+ "-device virtio-scsi-pci,id=scsi1"
+ "-device virtio-scsi-pci,id=scsi2"
+ "-device virtio-scsi-pci,id=scsi3"
)
for i in {0..127}; do
@@ -268,24 +268,15 @@ testcase_virtio_scsi_identically_named_partitions() {
# and attach them to a virtio-scsi controller
local qemu_opts=("-device virtio-scsi-pci,id=scsi0,num_queues=4")
local diskpath="${TESTDIR:?}/namedpart0.img"
- local i lodev num_disk num_part qemu_timeout
+ local i num_disk qemu_timeout
if get_bool "${IS_BUILT_WITH_ASAN:=}" || ! get_bool "$QEMU_KVM"; then
num_disk=4
- num_part=4
else
num_disk=16
- num_part=8
fi
dd if=/dev/zero of="$diskpath" bs=1M count=18
- lodev="$(losetup --show -f -P "$diskpath")"
- sfdisk "${lodev:?}" <<EOF
-label: gpt
-
-$(for ((i = 1; i <= num_part; i++)); do echo 'name="Hello world", size=2M'; done)
-EOF
- losetup -d "$lodev"
for ((i = 0; i < num_disk; i++)); do
diskpath="${TESTDIR:?}/namedpart$i.img"
@@ -325,19 +316,9 @@ testcase_multipath_basic_failover() {
local qemu_opts=("-device virtio-scsi-pci,id=scsi")
local partdisk="${TESTDIR:?}/multipathpartitioned.img"
- local image lodev nback ndisk wwn
+ local image nback ndisk wwn
dd if=/dev/zero of="$partdisk" bs=1M count=16
- lodev="$(losetup --show -f -P "$partdisk")"
- sfdisk "${lodev:?}" <<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" "${lodev}p2"
- losetup -d "$lodev"
# Add 16 multipath devices, each backed by 4 paths
for ndisk in {0..15}; do
@@ -388,7 +369,7 @@ testcase_lvm_basic() {
return 77
fi
- local qemu_opts=("-device ahci,id=ahci0")
+ local qemu_opts=("-device virtio-scsi-pci,id=scsi0")
local diskpath i
# Attach 4 SATA disks to the VM (and set their model and serial fields
@@ -397,7 +378,7 @@ testcase_lvm_basic() {
diskpath="${TESTDIR:?}/lvmbasic${i}.img"
dd if=/dev/zero of="$diskpath" bs=1M count=32
qemu_opts+=(
- "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeeflvm$i"
+ "-device scsi-hd,drive=drive$i,vendor=systemd,product=foobar,serial=deadbeeflvm$i"
"-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i"
)
done
@@ -415,7 +396,7 @@ testcase_btrfs_basic() {
return 77
fi
- local qemu_opts=("-device ahci,id=ahci0")
+ local qemu_opts=("-device virtio-scsi-pci,id=scsi0")
local diskpath i size
for i in {0..3}; do
@@ -425,7 +406,7 @@ testcase_btrfs_basic() {
dd if=/dev/zero of="$diskpath" bs=1M count="$size"
qemu_opts+=(
- "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefbtrfs$i"
+ "-device scsi-hd,drive=drive$i,vendor=systemd,product=foobar,serial=deadbeefbtrfs$i"
"-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i"
)
done
@@ -443,7 +424,7 @@ testcase_iscsi_lvm() {
return 77
fi
- local qemu_opts=("-device ahci,id=ahci0")
+ local qemu_opts=("-device virtio-scsi-pci,id=scsi0")
local diskpath i size
for i in {0..3}; do
@@ -454,7 +435,7 @@ testcase_iscsi_lvm() {
dd if=/dev/zero of="$diskpath" bs=1M count="$size"
qemu_opts+=(
- "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefiscsi$i"
+ "-device scsi-hd,drive=drive$i,vendor=systemd,product=foobar,serial=deadbeefiscsi$i"
"-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i"
)
done
@@ -475,18 +456,6 @@ testcase_long_sysfs_path() {
)
dd if=/dev/zero of="$testdisk" bs=1M count=64
- lodev="$(losetup --show -f -P "$testdisk")"
- sfdisk "${lodev:?}" <<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" "${lodev}p1"
- mkfs.ext4 -U "deadbeef-dead-dead-beef-222222222222" -L "data_vol" "${lodev}p2"
- losetup -d "$lodev"
-
# Create 25 additional PCI bridges, each one connected to the previous one
# (basically a really long extension cable), and attach a virtio drive to
# the last one. This should force udev into attempting to create a device
@@ -510,7 +479,7 @@ testcase_mdadm_basic() {
return 77
fi
- local qemu_opts=("-device ahci,id=ahci0")
+ local qemu_opts=("-device virtio-scsi-pci,id=scsi0")
local diskpath i size
for i in {0..4}; do
@@ -518,7 +487,7 @@ testcase_mdadm_basic() {
dd if=/dev/zero of="$diskpath" bs=1M count=64
qemu_opts+=(
- "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefmdadm$i"
+ "-device scsi-hd,drive=drive$i,vendor=systemd,product=foobar,serial=deadbeefmdadm$i"
"-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i"
)
done
@@ -536,7 +505,7 @@ testcase_mdadm_lvm() {
return 77
fi
- local qemu_opts=("-device ahci,id=ahci0")
+ local qemu_opts=("-device virtio-scsi-pci,id=scsi0")
local diskpath i size
for i in {0..4}; do
@@ -544,7 +513,7 @@ testcase_mdadm_lvm() {
dd if=/dev/zero of="$diskpath" bs=1M count=64
qemu_opts+=(
- "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefmdadmlvm$i"
+ "-device scsi-hd,drive=drive$i,vendor=systemd,product=foobar,serial=deadbeefmdadmlvm$i"
"-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i"
)
done
diff --git a/test/TEST-64-UDEV-STORAGE/virtio_scsi_basic.configure b/test/TEST-64-UDEV-STORAGE/virtio_scsi_basic.configure
new file mode 100755
index 0000000..ab8d530
--- /dev/null
+++ b/test/TEST-64-UDEV-STORAGE/virtio_scsi_basic.configure
@@ -0,0 +1,28 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import json
+import sys
+
+
+config = json.load(sys.stdin)
+
+for i in range(4):
+ config["QemuArgs"] += ['-device', f"virtio-scsi-pci,id=scsi{i}"]
+
+for i in range(128):
+ id = f"drive{i}"
+ config["QemuDrives"] += [
+ {
+ "Id": id,
+ "Size": "1M",
+ "Options": "cache=unsafe",
+ }
+ ]
+ config["QemuArgs"] += [
+ '-device',
+ f"scsi-hd,drive={id},bus=scsi{i // 32}.0,channel=0,"
+ f"scsi-id={i % 32},lun=0",
+ ]
+
+json.dump(config, sys.stdout)
diff --git a/test/TEST-64-UDEV-STORAGE/virtio_scsi_identically_named_partitions.configure b/test/TEST-64-UDEV-STORAGE/virtio_scsi_identically_named_partitions.configure
new file mode 100755
index 0000000..e850247
--- /dev/null
+++ b/test/TEST-64-UDEV-STORAGE/virtio_scsi_identically_named_partitions.configure
@@ -0,0 +1,33 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import json
+import os
+import subprocess
+import sys
+
+
+config = json.load(sys.stdin)
+
+qemu = f"qemu-system-{os.environ["QEMU_ARCHITECTURE"]}"
+result = subprocess.run([qemu, '-device', 'help'], check=True, text=True, stdout=subprocess.PIPE)
+if 'name "virtio-scsi-pci"' not in result.stdout:
+ print("virtio-scsi-pci device driver is not available, skipping test...", file=sys.stderr)
+ exit(77)
+
+num_disk = 16
+
+config["QemuArgs"] += ["-device", "virtio-scsi-pci,id=scsi0,num_queues=4"]
+
+for i in range(0, num_disk):
+ id = f"drive{i}"
+ config["QemuDrives"] += [
+ {
+ "Id": id,
+ "Size": "40M",
+ "Options": "cache=unsafe"
+ }
+ ]
+ config["QemuArgs"] += ["-device", f"scsi-hd,drive={id},bus=scsi0.0,channel=0,scsi-id=0,lun={i}"]
+
+json.dump(config, sys.stdout)
diff --git a/test/TEST-65-ANALYZE/meson.build b/test/TEST-65-ANALYZE/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-65-ANALYZE/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-66-DEVICE-ISOLATION/meson.build b/test/TEST-66-DEVICE-ISOLATION/meson.build
new file mode 100644
index 0000000..77370ce
--- /dev/null
+++ b/test/TEST-66-DEVICE-ISOLATION/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-67-INTEGRITY/meson.build b/test/TEST-67-INTEGRITY/meson.build
new file mode 100644
index 0000000..77370ce
--- /dev/null
+++ b/test/TEST-67-INTEGRITY/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-68-PROPAGATE-EXIT-STATUS/meson.build b/test/TEST-68-PROPAGATE-EXIT-STATUS/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-68-PROPAGATE-EXIT-STATUS/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-69-SHUTDOWN/TEST-69-SHUTDOWN.service b/test/TEST-69-SHUTDOWN/TEST-69-SHUTDOWN.service
new file mode 100644
index 0000000..1467f71
--- /dev/null
+++ b/test/TEST-69-SHUTDOWN/TEST-69-SHUTDOWN.service
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-69-SHUTDOWN
+Wants=basic.target multi-user.target
+After=basic.target
+Before=getty-pre.target
+
+ConditionPathExists=/usr/bin/python3
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/TEST-69-SHUTDOWN.py
diff --git a/test/TEST-69-SHUTDOWN/meson.build b/test/TEST-69-SHUTDOWN/meson.build
new file mode 100644
index 0000000..c1b1ab5
--- /dev/null
+++ b/test/TEST-69-SHUTDOWN/meson.build
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'unit' : files('TEST-69-SHUTDOWN.service'),
+ 'configuration' : integration_test_template['configuration'] + {
+ 'wants' : '@0@ systemd-user-sessions.service'.format(integration_test_template['configuration']['wants']),
+ 'after' : '@0@ systemd-user-sessions.service'.format(integration_test_template['configuration']['after']),
+ },
+ },
+]
diff --git a/test/TEST-69-SHUTDOWN/test.sh b/test/TEST-69-SHUTDOWN/test.sh
index 0e12857..0229d89 100755
--- a/test/TEST-69-SHUTDOWN/test.sh
+++ b/test/TEST-69-SHUTDOWN/test.sh
@@ -17,7 +17,7 @@ SYSTEMD_NSPAWN="${STATEDIR:?}/run-nspawn"
setup_nspawn_root_hook() {
cat >"${STATEDIR:?}/run-nspawn" <<EOF
#!/bin/bash
-exec "${TEST_BASE_DIR:?}/test-shutdown.py" -v -- "$_ORIG_NSPAWN" "\$@"
+exec "${TEST_BASE_DIR:?}/test-shutdown.py" -v -- "$_ORIG_NSPAWN" --background= "\$@"
exit 1
EOF
chmod 755 "${STATEDIR:?}"/run-nspawn
diff --git a/test/TEST-70-TPM2/meson.build b/test/TEST-70-TPM2/meson.build
new file mode 100644
index 0000000..d84c2b7
--- /dev/null
+++ b/test/TEST-70-TPM2/meson.build
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'configuration' : integration_test_template['configuration'] + {
+ 'wants' : '@0@ tpm2.target'.format(integration_test_template['configuration']['wants']),
+ 'after' : '@0@ tpm2.target'.format(integration_test_template['configuration']['after']),
+ },
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-71-HOSTNAME/meson.build b/test/TEST-71-HOSTNAME/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-71-HOSTNAME/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-72-SYSUPDATE/meson.build b/test/TEST-72-SYSUPDATE/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-72-SYSUPDATE/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-73-LOCALE/meson.build b/test/TEST-73-LOCALE/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-73-LOCALE/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-74-AUX-UTILS/meson.build b/test/TEST-74-AUX-UTILS/meson.build
new file mode 100644
index 0000000..43a733e
--- /dev/null
+++ b/test/TEST-74-AUX-UTILS/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'storage': 'persistent',
+ },
+]
diff --git a/test/TEST-74-AUX-UTILS/test.sh b/test/TEST-74-AUX-UTILS/test.sh
index e3eb62f..2ee4a75 100755
--- a/test/TEST-74-AUX-UTILS/test.sh
+++ b/test/TEST-74-AUX-UTILS/test.sh
@@ -8,8 +8,9 @@ NSPAWN_ARGUMENTS="--private-network"
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
-# (Hopefully) a temporary workaround for https://github.com/systemd/systemd/issues/30573
-KERNEL_APPEND="${KERNEL_APPEND:-} SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST=100"
+# Make sure vsock is available in the VM
+CID=$((RANDOM + 3))
+QEMU_OPTIONS+=" -device vhost-vsock-pci,guest-cid=$CID"
test_append_files() {
local workspace="${1:?}"
@@ -25,6 +26,16 @@ test_append_files() {
install_mdadm
generate_module_dependencies
fi
+
+ inst_binary socat
+ inst_binary ssh
+ inst_binary sshd
+ inst_binary ssh-keygen
+ inst_binary usermod
+ instmods vmw_vsock_virtio_transport
+ instmods vsock_loopback
+ instmods vmw_vsock_vmci_transport
+ generate_module_dependencies
}
do_test "$@"
diff --git a/test/TEST-75-RESOLVED/meson.build b/test/TEST-75-RESOLVED/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-75-RESOLVED/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-75-RESOLVED/test.sh b/test/TEST-75-RESOLVED/test.sh
index 55a9f1b..6272d5a 100755
--- a/test/TEST-75-RESOLVED/test.sh
+++ b/test/TEST-75-RESOLVED/test.sh
@@ -11,14 +11,7 @@ NSPAWN_ARGUMENTS="--private-network"
test_require_bin knotd
-# We need at least Knot 3.0 which support (among others) the ds-push directive
-if ! knotc -c "${TEST_BASE_DIR:?}/knot-data/knot.conf" conf-check; then
- echo "This test requires at least Knot 3.0. skipping..."
- exit 0
-fi
-
test_append_files() {
- local workspace="${1:?}"
# Install knot
image_install kzonecheck keymgr kjournalprint knotc knotd
image_install "${ROOTLIBDIR:?}/system/knot.service"
@@ -26,14 +19,6 @@ test_append_files() {
image_install -o /etc/dbus-1/system.d/cz.nic.knotd.conf
image_install -o /etc/default/knot
- # Copy over our configuration
- mkdir -p "${workspace:?}/var/lib/knot/zones/" "${workspace:?}/etc/knot/"
- cp -rfv "${TEST_BASE_DIR:?}"/knot-data/zones/* "$workspace/var/lib/knot/zones/"
- cp -fv "${TEST_BASE_DIR:?}/knot-data/knot.conf" "$workspace/etc/knot/knot.conf"
- chgrp -R knot "$workspace/etc/knot/" "$workspace/var/lib/knot/"
- chmod -R ug+rwX "$workspace/var/lib/knot/"
- chmod -R g+r "$workspace/etc/knot/"
-
# Install DNS-related utilities (usually found in the bind-utils package)
image_install delv dig host nslookup
diff --git a/test/TEST-76-SYSCTL/meson.build b/test/TEST-76-SYSCTL/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-76-SYSCTL/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-77-OPENFILE/Makefile b/test/TEST-77-OPENFILE/Makefile
deleted file mode 120000
index e9f93b1..0000000
--- a/test/TEST-77-OPENFILE/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-77-OPENFILE/test.sh b/test/TEST-77-OPENFILE/test.sh
deleted file mode 100755
index 92afa4c..0000000
--- a/test/TEST-77-OPENFILE/test.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
-
-TEST_DESCRIPTION="Openfile tests"
-
-# shellcheck source=test/test-functions
-. "${TEST_BASE_DIR:?}/test-functions"
-
-test_append_files() {
- local workspace="${1:?}"
- echo "Open" >"$workspace/test-77-open.dat"
- echo "File" >"$workspace/test-77-file.dat"
-}
-
-do_test "$@"
diff --git a/test/TEST-78-SIGQUEUE/meson.build b/test/TEST-78-SIGQUEUE/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-78-SIGQUEUE/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/TEST-79-MEMPRESS/meson.build b/test/TEST-79-MEMPRESS/meson.build
new file mode 100644
index 0000000..2b832a8
--- /dev/null
+++ b/test/TEST-79-MEMPRESS/meson.build
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'configuration' : integration_test_template['configuration'] + {
+ 'memory-accounting' : 'yes',
+ },
+ },
+]
diff --git a/test/testsuite-80.units/fdstore-nopin.service b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-nopin.service
index 29ffd23..f658853 100644
--- a/test/testsuite-80.units/fdstore-nopin.service
+++ b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-nopin.service
@@ -3,4 +3,4 @@ Type=notify
NotifyAccess=all
FileDescriptorStoreMax=10
FileDescriptorStorePreserve=restart
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/fdstore-pin.sh 0
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh 0
diff --git a/test/testsuite-80.units/fdstore-pin.service b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.service
index 913daa2..393b5ac 100644
--- a/test/testsuite-80.units/fdstore-pin.service
+++ b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.service
@@ -3,4 +3,4 @@ Type=notify
NotifyAccess=all
FileDescriptorStoreMax=10
FileDescriptorStorePreserve=yes
-ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/fdstore-pin.sh 1
+ExecStart=/usr/lib/systemd/tests/testdata/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh 1
diff --git a/test/testsuite-80.units/fdstore-pin.sh b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh
index 4cb041a..4cb041a 100755
--- a/test/testsuite-80.units/fdstore-pin.sh
+++ b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.sh
diff --git a/test/testsuite-80.units/fdstore-pin.target b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.target
index 319b7e1..319b7e1 100644
--- a/test/testsuite-80.units/fdstore-pin.target
+++ b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/fdstore-pin.target
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
index 0000000..5693be6
--- /dev/null
+++ b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/notify.service
@@ -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/testsuite-80.units/test.sh b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/test.sh
index 565ed8d..565ed8d 100755
--- a/test/testsuite-80.units/test.sh
+++ b/test/TEST-80-NOTIFYACCESS/TEST-80-NOTIFYACCESS.units/test.sh
diff --git a/test/TEST-80-NOTIFYACCESS/meson.build b/test/TEST-80-NOTIFYACCESS/meson.build
new file mode 100644
index 0000000..f78c6fd
--- /dev/null
+++ b/test/TEST-80-NOTIFYACCESS/meson.build
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
+
+testdata_subdirs += [meson.current_source_dir() / 'TEST-80-NOTIFYACCESS.units']
diff --git a/test/TEST-81-GENERATORS/meson.build b/test/TEST-81-GENERATORS/meson.build
new file mode 100644
index 0000000..8dec5f3
--- /dev/null
+++ b/test/TEST-81-GENERATORS/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ },
+]
diff --git a/test/units/testsuite-82.service b/test/TEST-82-SOFTREBOOT/TEST-82-SOFTREBOOT.service
index a8fc4f9..f3892a7 100644
--- a/test/units/testsuite-82.service
+++ b/test/TEST-82-SOFTREBOOT/TEST-82-SOFTREBOOT.service
@@ -2,7 +2,9 @@
[Unit]
Description=TEST-82-SOFTREBOOT
DefaultDependencies=no
+Wants=basic.target multi-user.target
After=basic.target
+Before=getty-pre.target
[Service]
Type=oneshot
diff --git a/test/TEST-82-SOFTREBOOT/meson.build b/test/TEST-82-SOFTREBOOT/meson.build
new file mode 100644
index 0000000..a7c1bf0
--- /dev/null
+++ b/test/TEST-82-SOFTREBOOT/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'unit' : files('TEST-82-SOFTREBOOT.service'),
+ 'cmdline' : ['systemd.set_credential=kernelcmdlinecred:uff'],
+ 'storage' : 'persistent',
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-82-SOFTREBOOT/test.sh b/test/TEST-82-SOFTREBOOT/test.sh
index 0494149..2109cc8 100755
--- a/test/TEST-82-SOFTREBOOT/test.sh
+++ b/test/TEST-82-SOFTREBOOT/test.sh
@@ -7,8 +7,22 @@ TEST_DESCRIPTION="Test Soft-Rebooting"
IGNORE_MISSING_COVERAGE=yes
# Prevent shutdown in test suite, the expect script does that manually.
TEST_SKIP_SHUTDOWN=yes
+IMAGE_NAME="softreboot"
+TEST_NO_NSPAWN=1
+TEST_INSTALL_VERITY_MINIMAL=1
+KERNEL_APPEND="${KERNEL_APPEND:-} systemd.set_credential=kernelcmdlinecred:uff"
# shellcheck source=test/test-functions
. "$TEST_BASE_DIR/test-functions"
+test_require_bin mksquashfs veritysetup sfdisk
+
+test_append_files() {
+ instmods squashfs =squashfs
+ instmods dm_verity =md
+ install_dmevent
+ generate_module_dependencies
+ install_verity_minimal
+}
+
do_test "$@"
diff --git a/test/TEST-83-BTRFS/meson.build b/test/TEST-83-BTRFS/meson.build
new file mode 100644
index 0000000..77370ce
--- /dev/null
+++ b/test/TEST-83-BTRFS/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-84-STORAGETM/meson.build b/test/TEST-84-STORAGETM/meson.build
new file mode 100644
index 0000000..77370ce
--- /dev/null
+++ b/test/TEST-84-STORAGETM/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+integration_tests += [
+ integration_test_template + {
+ 'name' : fs.name(meson.current_source_dir()),
+ 'vm' : true,
+ },
+]
diff --git a/test/TEST-85-NETWORK/Makefile b/test/TEST-85-NETWORK/Makefile
new file mode 100644
index 0000000..653f161
--- /dev/null
+++ b/test/TEST-85-NETWORK/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+all setup run clean clean-again:
+ true
+
+.PHONY: all setup run clean clean-again
diff --git a/test/TEST-85-NETWORK/meson.build b/test/TEST-85-NETWORK/meson.build
new file mode 100644
index 0000000..47ec029
--- /dev/null
+++ b/test/TEST-85-NETWORK/meson.build
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+name = fs.name(meson.current_source_dir())
+unit = configure_file(
+ input : files('../test.service.in'),
+ output : '@0@.service'.format(name),
+ configuration : integration_test_template['configuration'] + {
+ 'command' : '@0@ --no-journal'.format(testdata_dir / 'test-network/systemd-networkd-tests.py')
+ },
+)
+
+systemd_networkd_tests_py = files('../test-network/systemd-networkd-tests.py')
+network_testcases = run_command('sed',
+ '-ne',
+ '/^class .*Tests/ { s/^class *//; s/(.*$//; p}',
+ systemd_networkd_tests_py,
+ check : true).stdout().split()
+
+foreach testcase : network_testcases
+ integration_tests += [
+ integration_test_template + {
+ 'name' : '@0@-@1@'.format(name, testcase),
+ 'unit' : unit,
+ 'cmdline' : integration_test_template['cmdline'] + [
+ 'systemd.setenv=TEST_MATCH_TESTCASE=@0@'.format(testcase)
+ ],
+ 'priority' : 10,
+ 'vm' : true,
+ },
+ ]
+endforeach
diff --git a/test/fuzz/fuzz-catalog/systemd.pl.catalog b/test/fuzz/fuzz-catalog/systemd.pl.catalog
index 6e99c04..99a62ce 100644
--- a/test/fuzz/fuzz-catalog/systemd.pl.catalog
+++ b/test/fuzz/fuzz-catalog/systemd.pl.catalog
@@ -4,7 +4,7 @@
# Polish translation
# The catalog format is documented on
-# https://www.freedesktop.org/wiki/Software/systemd/catalog
+# https://systemd.io/CATALOG
# For an explanation why we do all this, see https://xkcd.com/1024/
diff --git a/test/fuzz/fuzz-execute-serialize/initial b/test/fuzz/fuzz-execute-serialize/initial
index 403cf08..4d9103c 100644
--- a/test/fuzz/fuzz-execute-serialize/initial
+++ b/test/fuzz/fuzz-execute-serialize/initial
@@ -31,7 +31,6 @@ exec-context-protect-home=
exec-context-protect-system=
exec-context-mount-api-vfs=
exec-context-same-pgrp=
-exec-context-cpu-sched-reset-on-fork=
exec-context-non-blocking=
exec-context-ignore-sigpipe=
exec-context-memory-deny-write-execute=
diff --git a/test/fuzz/fuzz-netdev-parser/bond.netdev b/test/fuzz/fuzz-netdev-parser/bond.netdev
index 4e4885c..04e237d 100644
--- a/test/fuzz/fuzz-netdev-parser/bond.netdev
+++ b/test/fuzz/fuzz-netdev-parser/bond.netdev
@@ -14,5 +14,6 @@ MinLinks=1
AdActorSystemPriority=1218
AdUserPortKey=811
AdActorSystem=00:11:22:33:44:55
+ARPMissedMax=10
# feed the sanitizer
AdActorSystem=00:11:22:33:44:55
diff --git a/test/fuzz/fuzz-network-parser/sysctl b/test/fuzz/fuzz-network-parser/sysctl
index 2452fb7..01b45a2 100644
--- a/test/fuzz/fuzz-network-parser/sysctl
+++ b/test/fuzz/fuzz-network-parser/sysctl
@@ -7,4 +7,5 @@ IPv6PrivacyExtensions=true
IPv6DuplicateAddressDetection=3
IPv6HopLimit=5
IPv4ProxyARP=true
+IPv4ProxyARPPrivateVLAN=true
IPv6ProxyNDP=true
diff --git a/test/fuzz/fuzz-unit-file/binfmt_misc.automount b/test/fuzz/fuzz-unit-file/binfmt_misc.automount
index 8d761b0..d9fd0f0 100644
--- a/test/fuzz/fuzz-unit-file/binfmt_misc.automount
+++ b/test/fuzz/fuzz-unit-file/binfmt_misc.automount
@@ -11,7 +11,7 @@ automount
[Unit]
Description=Arbitrary Executable File Formats File System Automount Point
Documentation=https://docs.kernel.org/admin-guide/binfmt-misc.html
-Documentation=https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems
+Documentation=https://systemd.io/API_FILE_SYSTEMS
DefaultDependencies=no
Before=sysinit.target
ConditionPathExists=/proc/sys/fs/binfmt_misc/
diff --git a/test/fuzz/fuzz-unit-file/directives-all.service b/test/fuzz/fuzz-unit-file/directives-all.service
index 4bdc48a..dbd56ec 100644
--- a/test/fuzz/fuzz-unit-file/directives-all.service
+++ b/test/fuzz/fuzz-unit-file/directives-all.service
@@ -185,6 +185,7 @@ PAMName=
PIDFile=
PartOf=
PassCredentials=
+PassFileDescriptorsToExec=
PassSecurity=
PassPacketInfo=
PathChanged=
@@ -276,6 +277,7 @@ User=
WakeSystem=
WantedBy=
Wants=
+WantsMountsFor=
WatchdogSec=
What=
Where=
@@ -471,6 +473,7 @@ IPForward=
IPMasquerade=
IPv4LLRoute=
IPv4ProxyARP=
+IPv4ProxyARPPrivateVLAN=
IPv6AcceptRA=
IPv6DuplicateAddressDetection=
IPv6FlowLabel=
@@ -494,6 +497,7 @@ KernelVersion=
Key=
Kind=
L2MissNotification=
+L3MasterDevice=
L3MissNotification=
LACPTransmitRate=
LLDP=
@@ -702,6 +706,7 @@ CPUAffinity=
CapabilityBoundingSet=
CrashChangeVT=
CrashReboot=
+CrashAction=
CrashShell=
CtrlAltDelBurstAction=
DefaultBlockIOAccounting=
@@ -862,6 +867,7 @@ MaxLevelKMsg=
MaxLevelStore=
MaxLevelSyslog=
MaxLevelWall=
+MaxLevelSocket=
MaxRetentionSec=
MaxUse=
MemoryDenyWriteExecute=
diff --git a/test/fuzz/fuzz-unit-file/syslog.socket b/test/fuzz/fuzz-unit-file/syslog.socket
index 969ee42..bb046a5 100644
--- a/test/fuzz/fuzz-unit-file/syslog.socket
+++ b/test/fuzz/fuzz-unit-file/syslog.socket
@@ -11,7 +11,7 @@ socket
[Unit]
Description=Syslog Socket
Documentation=man:systemd.special(7)
-Documentation=https://www.freedesktop.org/wiki/Software/systemd/syslog
+Documentation=https://systemd.io/SYSLOG
DefaultDependencies=no
Before=sockets.target
@@ -45,7 +45,7 @@ ReceiveBuffer=8M
# [Install]
# Alias=syslog.service
#
-# See https://www.freedesktop.org/wiki/Software/systemd/syslog for details.
+# See https://systemd.io/SYSLOG for details.
[Socket]
ListenStream=1.2.3.4:1234
diff --git a/test/fuzz/fuzz-unit-file/systemd-resolved.service b/test/fuzz/fuzz-unit-file/systemd-resolved.service
index 49d272b..1b7cc15 100644
--- a/test/fuzz/fuzz-unit-file/systemd-resolved.service
+++ b/test/fuzz/fuzz-unit-file/systemd-resolved.service
@@ -12,8 +12,8 @@ service
Description=Network Name Resolution
Documentation=man:systemd-resolved.service(8)
Documentation=man:org.freedesktop.resolve1(5)
-Documentation=https://www.freedesktop.org/wiki/Software/systemd/writing-network-configuration-managers
-Documentation=https://www.freedesktop.org/wiki/Software/systemd/writing-resolver-clients
+Documentation=https://systemd.io/WRITING_NETWORK_CONFIGURATION_MANAGERS
+Documentation=https://systemd.io/WRITING_RESOLVER_CLIENTS
DefaultDependencies=no
After=systemd-sysusers.service systemd-networkd.service
Before=network.target nss-lookup.target shutdown.target
diff --git a/test/hwdb-test.sh b/test/hwdb-test.sh
index 432a49f..89a5c7e 100755
--- a/test/hwdb-test.sh
+++ b/test/hwdb-test.sh
@@ -10,6 +10,7 @@
set -e
export SYSTEMD_LOG_LEVEL=info
+export SYSTEMD_HWDB_UPDATE_BYPASS=0
ROOTDIR="$(dirname "$(dirname "$(readlink -f "$0")")")"
SYSTEMD_HWDB="${1:?}"
diff --git a/test/integration-test-wrapper.py b/test/integration-test-wrapper.py
new file mode 100755
index 0000000..5b098a3
--- /dev/null
+++ b/test/integration-test-wrapper.py
@@ -0,0 +1,196 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+'''Test wrapper command for driving integration tests.
+
+Note: This is deliberately rough and only intended to drive existing tests
+with the expectation that as part of formally defining the API it will be tidy.
+
+'''
+
+import argparse
+import json
+import os
+import shlex
+import subprocess
+import sys
+import textwrap
+from pathlib import Path
+
+
+EMERGENCY_EXIT_DROPIN = """\
+[Unit]
+Wants=emergency-exit.service
+"""
+
+
+EMERGENCY_EXIT_SERVICE = """\
+[Unit]
+DefaultDependencies=no
+Conflicts=shutdown.target
+Conflicts=rescue.service
+Before=shutdown.target
+Before=rescue.service
+FailureAction=exit
+
+[Service]
+ExecStart=false
+"""
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('--mkosi', required=True)
+ parser.add_argument('--meson-source-dir', required=True, type=Path)
+ parser.add_argument('--meson-build-dir', required=True, type=Path)
+ parser.add_argument('--name', required=True)
+ parser.add_argument('--unit', required=True)
+ parser.add_argument('--storage', required=True)
+ parser.add_argument('--firmware', required=True)
+ parser.add_argument('--slow', action=argparse.BooleanOptionalAction)
+ parser.add_argument('--vm', action=argparse.BooleanOptionalAction)
+ parser.add_argument('--exit-code', required=True, type=int)
+ parser.add_argument('mkosi_args', nargs="*")
+ args = parser.parse_args()
+
+ if not bool(int(os.getenv("SYSTEMD_INTEGRATION_TESTS", "0"))):
+ print(f"SYSTEMD_INTEGRATION_TESTS=1 not found in environment, skipping {args.name}", file=sys.stderr)
+ exit(77)
+
+ if args.slow and not bool(int(os.getenv("SYSTEMD_SLOW_TESTS", "0"))):
+ print(f"SYSTEMD_SLOW_TESTS=1 not found in environment, skipping {args.name}", file=sys.stderr)
+ exit(77)
+
+ name = args.name + (f"-{i}" if (i := os.getenv("MESON_TEST_ITERATION")) else "")
+
+ dropin = textwrap.dedent(
+ """\
+ [Unit]
+ SuccessAction=exit
+ SuccessActionExitStatus=123
+
+ [Service]
+ StandardOutput=journal+console
+ """
+ )
+
+ if os.getenv("TEST_MATCH_SUBTEST"):
+ dropin += textwrap.dedent(
+ f"""
+ [Service]
+ Environment=TEST_MATCH_SUBTEST={os.environ["TEST_MATCH_SUBTEST"]}
+ """
+ )
+
+ if os.getenv("TEST_MATCH_TESTCASE"):
+ dropin += textwrap.dedent(
+ f"""
+ [Service]
+ Environment=TEST_MATCH_TESTCASE={os.environ["TEST_MATCH_TESTCASE"]}
+ """
+ )
+
+ if not sys.stderr.isatty():
+ dropin += textwrap.dedent(
+ """
+ [Unit]
+ FailureAction=exit
+ """
+ )
+
+ journal_file = (args.meson_build_dir / (f"test/journal/{name}.journal")).absolute()
+ journal_file.unlink(missing_ok=True)
+ else:
+ journal_file = None
+
+ cmd = [
+ args.mkosi,
+ '--directory', os.fspath(args.meson_source_dir),
+ '--output-dir', os.fspath(args.meson_build_dir / 'mkosi.output'),
+ '--extra-search-path', os.fspath(args.meson_build_dir),
+ '--machine', name,
+ '--ephemeral',
+ *(['--forward-journal', journal_file] if journal_file else []),
+ *(
+ [
+ '--credential',
+ f"systemd.extra-unit.emergency-exit.service={shlex.quote(EMERGENCY_EXIT_SERVICE)}",
+ '--credential',
+ f"systemd.unit-dropin.emergency.target={shlex.quote(EMERGENCY_EXIT_DROPIN)}",
+ ]
+ if not sys.stderr.isatty()
+ else []
+ ),
+ '--credential',
+ f"systemd.unit-dropin.{args.unit}={shlex.quote(dropin)}",
+ '--runtime-network=none',
+ '--runtime-scratch=no',
+ *args.mkosi_args,
+ '--append',
+ '--qemu-firmware', args.firmware,
+ '--kernel-command-line-extra',
+ ' '.join([
+ 'systemd.hostname=H',
+ f"SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/{args.name}.units:/usr/lib/systemd/tests/testdata/units:",
+ f"systemd.unit={args.unit}",
+ 'systemd.mask=systemd-networkd-wait-online.service',
+ *(
+ [
+ "systemd.mask=serial-getty@.service",
+ "systemd.show_status=no",
+ "systemd.crash_shell=0",
+ "systemd.crash_action=poweroff",
+ ]
+ if not sys.stderr.isatty()
+ else []
+ ),
+ ]),
+ '--credential', f"journal.storage={'persistent' if sys.stderr.isatty() else args.storage}",
+ 'qemu' if args.vm or os.getuid() != 0 else 'boot',
+ ]
+
+ result = subprocess.run(cmd)
+
+ if result.returncode in (args.exit_code, 77):
+ # Do not keep journal files for tests that don't fail.
+ if journal_file:
+ journal_file.unlink(missing_ok=True)
+
+ exit(0 if result.returncode == args.exit_code else 77)
+
+ if journal_file:
+ ops = []
+
+ if os.getenv("GITHUB_ACTIONS"):
+ id = os.environ["GITHUB_RUN_ID"]
+ iteration = os.environ["GITHUB_RUN_ATTEMPT"]
+ j = json.loads(
+ subprocess.run(
+ [
+ args.mkosi,
+ "--directory", os.fspath(args.meson_source_dir),
+ "--json",
+ "summary",
+ ],
+ stdout=subprocess.PIPE,
+ text=True,
+ ).stdout
+ )
+ images = {image["Image"]: image for image in j["Images"]}
+ distribution = images["system"]["Distribution"]
+ release = images["system"]["Release"]
+ artifact = f"ci-mkosi-{id}-{iteration}-{distribution}-{release}-failed-test-journals"
+ ops += [f"gh run download {id} --name {artifact} -D ci/{artifact}"]
+ journal_file = Path(f"ci/{artifact}/test/journal/{name}.journal")
+
+ ops += [f"journalctl --file {journal_file} --no-hostname -o short-monotonic -u {args.unit} -p info"]
+
+ print("Test failed, relevant logs can be viewed with: \n\n"
+ f"{(' && '.join(ops))}\n", file=sys.stderr)
+
+ # 0 also means we failed so translate that to a non-zero exit code to mark the test as failed.
+ exit(result.returncode or 1)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/knot-data/knot.conf b/test/knot-data/knot.conf
index b925812..b8b9e79 100644
--- a/test/knot-data/knot.conf
+++ b/test/knot-data/knot.conf
@@ -19,19 +19,27 @@ acl:
address: fd00:dead:beef:cafe::/64
action: update
+ - id: transfer_acl
+ address: 10.0.0.0/24
+ address: fd00:dead:beef:cafe::/64
+ action: transfer
+
remote:
- id: parent_zone_server
address: 10.0.0.1@53
address: fd00:dead:beef:cafe::1@53
+ - id: forwarded
+ address: 10.99.0.1@53
+
submission:
- id: parent_zone_sbm
check-interval: 2s
parent: [parent_zone_server]
-# Auto ZSK/KSK rollover for DNSSEC-enabled zones + pushing the respective DS
-# records to the parent zone
policy:
+ # Auto ZSK/KSK rollover for DNSSEC-enabled zones + pushing the respective DS
+ # records to the parent zone
- id: auto_rollover
algorithm: ECDSAP256SHA256
cds-cdnskey-publish: always
@@ -43,8 +51,7 @@ policy:
zone-max-ttl: 1s
zsk-lifetime: 60d
-# Same as auto_rollover, but with NSEC3 turned on
-policy:
+ # Same as auto_rollover, but with NSEC3 turned on
- id: auto_rollover_nsec3
algorithm: ECDSAP256SHA256
cds-cdnskey-publish: always
@@ -58,17 +65,20 @@ policy:
zone-max-ttl: 1s
zsk-lifetime: 60d
-policy:
- id: untrusted
cds-cdnskey-publish: none
-# Manual ZSK/KSK management
-policy:
+ # Manual ZSK/KSK management
- id: manual
manual: on
-# Sign everything by default and propagate the respective DS records to the parent
+mod-dnsproxy:
+ - id: forwarded
+ remote: forwarded
+ fallback: off
+
template:
+ # Sign everything by default and propagate the respective DS records to the parent
- id: default
acl: update_acl
dnssec-policy: auto_rollover
@@ -77,14 +87,18 @@ template:
semantic-checks: on
storage: "/var/lib/knot/zones"
-# A template for unsigned zones (i.e. without DNSSEC)
-template:
+ # A template for unsigned zones (i.e. without DNSSEC)
- id: unsigned
dnssec-signing: off
file: "%s.zone"
semantic-checks: on
storage: "/var/lib/knot/zones"
+ - id: forwarded
+ dnssec-signing: off
+ module: mod-dnsproxy/forwarded
+ zonefile-load: none
+
zone:
# Create our own DNSSEC-aware root zone, so we can test the whole chain of
# trust. This needs a ZSK/KSK keypair to be generated before running knot +
@@ -98,8 +112,9 @@ zone:
- domain: test
dnssec-policy: auto_rollover_nsec3
- # A fully (pre-)signed zone
+ # A fully (pre-)signed zone with allowed zone transfers (AXFR/IXFR)
- domain: signed.test
+ acl: [update_acl, transfer_acl]
# A fully (online)-signed zone
# See: https://www.knot-dns.cz/docs/3.1/singlehtml/index.html#mod-onlinesign
@@ -117,3 +132,7 @@ zone:
# An unsigned zone
- domain: unsigned.test
template: unsigned
+
+ # Forward all queries for this zone to our dummy test server
+ - domain: forwarded.test
+ template: forwarded
diff --git a/test/knot-data/zones/signed.test.zone b/test/knot-data/zones/signed.test.zone
index a2baac4..5d75aa0 100644
--- a/test/knot-data/zones/signed.test.zone
+++ b/test/knot-data/zones/signed.test.zone
@@ -53,6 +53,7 @@ follow14.final A 10.0.0.14
myservice A 10.0.0.20
myservice AAAA fd00:dead:beef:cafe::17
_mysvc._tcp SRV 10 5 1234 myservice
+_mysvc._tcp TXT "This is TXT for myservice"
_invalidsvc._udp SRV 5 5 1111 invalidservice
diff --git a/test/knot-data/zones/test.zone b/test/knot-data/zones/test.zone
index ba5fceb..065ff7e 100644
--- a/test/knot-data/zones/test.zone
+++ b/test/knot-data/zones/test.zone
@@ -19,3 +19,6 @@ ns1.unsigned AAAA fd00:dead:beef:cafe::1
onlinesign NS ns1.unsigned
signed NS ns1.unsigned
unsigned NS ns1.unsigned
+
+svcb SVCB 1 . alpn=dot ipv4hint=10.0.0.1 ipv6hint=fd00:dead:beef:cafe::1
+https HTTPS 1 . alpn="h2,h3"
diff --git a/test/meson.build b/test/meson.build
index b47fa61..173d90c 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -1,65 +1,5 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
-if install_tests
- foreach subdir : [
- 'auxv',
- 'journal-data',
- '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() does not handle symlinks correctly which causes a very ugly warning when
- # installing, so if rsync is available we use that instead. TODO: switch to the new option
- # 'follow_symlinks' once we require Meson 1.3.0 or greater, see:
- # https://github.com/mesonbuild/meson/commit/0af126fec798d6dbb0d1ad52168cc1f3f1758acd
- if rsync.found()
- rsync_r = rsync.full_path() + ' -rlpt --exclude .gitattributes -- "@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',
- install_dir : testdata_dir)
- endif
- endforeach
-
- 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',
@@ -330,3 +270,230 @@ if want_tests != 'false' and conf.get('ENABLE_KERNEL_INSTALL') == 1
depends : deps,
suite : 'kernel-install')
endif
+
+############################################################
+
+integration_test_wrapper = find_program('integration-test-wrapper.py')
+integration_tests = []
+integration_test_template = {
+ 'mkosi-args' : [],
+ 'timeout' : 1800,
+ 'storage' : 'volatile',
+ 'priority' : 0,
+ 'firmware' : 'linux',
+ 'enabled' : true,
+ 'configuration' : {
+ 'memory-accounting' : 'no',
+ 'command' : testdata_dir / 'units/%N.sh',
+ 'wants' : 'multi-user.target user@4711.service',
+ 'after' : 'user@4711.service',
+ },
+ 'cmdline' : [],
+ 'credentials' : [],
+ 'qemu-args' : [],
+ 'exit-code' : 123,
+ 'vm' : 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',
+ 'TEST-02-UNITTESTS',
+ 'TEST-03-JOBS',
+ 'TEST-04-JOURNAL',
+ 'TEST-05-RLIMITS',
+ 'TEST-06-SELINUX',
+ 'TEST-07-PID1',
+ 'TEST-08-INITRD',
+ 'TEST-09-REBOOT',
+ 'TEST-13-NSPAWN',
+ 'TEST-15-DROPIN',
+ 'TEST-16-EXTEND-TIMEOUT',
+ 'TEST-17-UDEV',
+ 'TEST-18-FAILUREACTION',
+ 'TEST-19-CGROUP',
+ 'TEST-21-DFUZZER',
+ 'TEST-22-TMPFILES',
+ 'TEST-23-UNIT-FILE',
+ 'TEST-24-CRYPTSETUP',
+ 'TEST-25-IMPORT',
+ 'TEST-26-SYSTEMCTL',
+ 'TEST-29-PORTABLE',
+ 'TEST-30-ONCLOCKCHANGE',
+ 'TEST-31-DEVICE-ENUMERATION',
+ 'TEST-32-OOMPOLICY',
+ 'TEST-34-DYNAMICUSERMIGRATE',
+ 'TEST-35-LOGIN',
+ 'TEST-36-NUMAPOLICY',
+ 'TEST-38-FREEZER',
+ 'TEST-43-PRIVATEUSER-UNPRIV',
+ 'TEST-44-LOG-NAMESPACE',
+ 'TEST-45-TIMEDATE',
+ 'TEST-46-HOMED',
+ 'TEST-50-DISSECT',
+ 'TEST-52-HONORFIRSTSHUTDOWN',
+ 'TEST-53-ISSUE-16347',
+ 'TEST-54-CREDS',
+ 'TEST-55-OOMD',
+ 'TEST-58-REPART',
+ 'TEST-59-RELOADING-RESTART',
+ 'TEST-60-MOUNT-RATELIMIT',
+ 'TEST-62-RESTRICT-IFACES',
+ 'TEST-63-PATH',
+ 'TEST-64-UDEV-STORAGE',
+ 'TEST-65-ANALYZE',
+ 'TEST-66-DEVICE-ISOLATION',
+ 'TEST-67-INTEGRITY',
+ 'TEST-68-PROPAGATE-EXIT-STATUS',
+ 'TEST-69-SHUTDOWN',
+ 'TEST-70-TPM2',
+ 'TEST-71-HOSTNAME',
+ 'TEST-72-SYSUPDATE',
+ 'TEST-73-LOCALE',
+ 'TEST-74-AUX-UTILS',
+ 'TEST-75-RESOLVED',
+ 'TEST-76-SYSCTL',
+ 'TEST-78-SIGQUEUE',
+ 'TEST-79-MEMPRESS',
+ 'TEST-80-NOTIFYACCESS',
+ 'TEST-81-GENERATORS',
+ 'TEST-82-SOFTREBOOT',
+ 'TEST-83-BTRFS',
+ 'TEST-84-STORAGETM',
+ 'TEST-85-NETWORK',
+]
+ subdir(dirname)
+endforeach
+
+foreach integration_test : integration_tests
+ integration_test_args = [
+ '--meson-source-dir', meson.project_source_root(),
+ '--meson-build-dir', meson.project_build_root(),
+ '--name', integration_test['name'],
+ '--storage', integration_test['storage'],
+ '--firmware', integration_test['firmware'],
+ '--exit-code', integration_test['exit-code'].to_string(),
+ ]
+
+ if 'unit' in integration_test
+ integration_test_unit = integration_test['unit']
+ else
+ integration_test_unit = configure_file(
+ input : 'test.service.in',
+ output : '@0@.service'.format(integration_test['name']),
+ configuration : integration_test['configuration'],
+ )
+ endif
+
+ integration_test_args += ['--unit', fs.name(integration_test_unit)]
+ if install_tests
+ install_data(integration_test_unit, install_dir : testdata_dir / 'units')
+ endif
+
+ if integration_test['vm']
+ integration_test_args += ['--vm']
+ endif
+
+ if not mkosi.found()
+ continue
+ endif
+
+ integration_test_args += ['--mkosi', mkosi.full_path(), '--']
+
+ if integration_test['cmdline'].length() > 0
+ integration_test_args += [
+ '--kernel-command-line-extra=@0@'.format(' '.join(integration_test['cmdline']))
+ ]
+ endif
+
+ foreach credential : integration_test['credentials']
+ integration_test_args += ['--credential', credential]
+ endforeach
+
+ if integration_test['qemu-args'].length() > 0
+ integration_test_args += ['--qemu-args=@0@'.format(' '.join(integration_test['qemu-args']))]
+ endif
+
+ integration_test_args += integration_test['mkosi-args']
+
+ integration_test_env = {}
+
+ if want_integration_tests
+ integration_test_env += {'SYSTEMD_INTEGRATION_TESTS': '1'}
+ endif
+
+ if not integration_test['enabled']
+ continue
+ endif
+
+ # We don't explicitly depend on the "mkosi" target because that means the image is rebuilt on every
+ # "ninja -C build". Instead, the mkosi target has to be rebuilt manually before running the
+ # integration tests with mkosi.
+ test(
+ integration_test['name'],
+ integration_test_wrapper,
+ env : integration_test_env,
+ args : integration_test_args,
+ timeout : integration_test['timeout'],
+ priority : integration_test['priority'],
+ 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
diff --git a/test/networkd-test.py b/test/networkd-test.py
index 512137c..47a3ad7 100755
--- a/test/networkd-test.py
+++ b/test/networkd-test.py
@@ -888,8 +888,10 @@ class NetworkdClientTest(ClientTestBase, unittest.TestCase):
set -eu
mkdir -p /run/systemd/network
mkdir -p /run/systemd/netif
+mkdir -p /var/lib/systemd/network
mount -t tmpfs none /run/systemd/network
mount -t tmpfs none /run/systemd/netif
+mount -t tmpfs none /var/lib/systemd/network
[ ! -e /run/dbus ] || mount -t tmpfs none /run/dbus
# create router/client veth pair
cat <<EOF >/run/systemd/network/50-test.netdev
@@ -917,6 +919,10 @@ DNS=192.168.5.1
{dhopts}
EOF
+# For the networkd instance invoked below cannot support varlink connection.
+# Hence, 'networkctl persistent-storage yes' cannot be used.
+export SYSTEMD_NETWORK_PERSISTENT_STORAGE_READY=1
+
# run networkd as in systemd-networkd.service
exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ {{ s/^.*=//; s/^[@+-]//; s/^!*//; p}}')
'''.format(ifr=self.if_router,
@@ -930,6 +936,7 @@ exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ {{ s/^.*=/
'-p', 'InaccessibleDirectories=-/etc/systemd/network',
'-p', 'InaccessibleDirectories=-/run/systemd/network',
'-p', 'InaccessibleDirectories=-/run/systemd/netif',
+ '-p', 'InaccessibleDirectories=-/var/lib/systemd/network',
'--service-type=notify', script])
# wait until devices got created
@@ -1017,17 +1024,19 @@ DNS=127.0.0.1
self.start_unit('systemd-resolved')
self.start_unit('systemd-networkd')
- for timeout in range(50):
+ subprocess.check_call([NETWORKD_WAIT_ONLINE, '--interface', 'dummy0', '--timeout=10'])
+
+ out = subprocess.check_output(['networkctl', 'status', 'dummy0'])
+ self.assertIn(b'50-test.network.d/dns.conf', out)
+
+ for _ in range(50):
with open(RESOLV_CONF) as f:
contents = f.read()
- if ' 127.0.0.1' in contents and '192.168.42.1' in contents:
+ if 'nameserver 127.0.0.1\n' in contents and 'nameserver 192.168.42.1\n' in contents:
break
time.sleep(0.1)
- self.assertIn('nameserver 192.168.42.1\n', contents)
- self.assertIn('nameserver 127.0.0.1\n', contents)
-
- out = subprocess.check_output(['networkctl', 'status', 'dummy0'])
- self.assertIn(b'50-test.network.d/dns.conf', out)
+ else:
+ self.fail(f'Expected DNS servers not found in resolv.conf: {contents}')
def test_dhcp_timezone(self):
'''networkd sets time zone from DHCP'''
@@ -1046,13 +1055,16 @@ DNS=127.0.0.1
self.create_iface(dhcpserver_opts='EmitTimezone=yes\nTimezone=Pacific/Honolulu')
self.do_test(coldplug=None, extra_opts='IPv6AcceptRA=false\n[DHCP]\nUseTimezone=true', dhcp_mode='ipv4')
- # should have applied the received timezone
- try:
- self.assertEqual(get_tz(), 'Pacific/Honolulu')
- except AssertionError:
+ # Should have applied the received timezone. This is asynchronous, so we need to wait for a while:
+ for _ in range(20):
+ tz = get_tz()
+ if tz == 'Pacific/Honolulu':
+ break
+ time.sleep(0.5)
+ else:
self.show_journal('systemd-networkd.service')
- self.show_journal('systemd-hostnamed.service')
- raise
+ self.show_journal('systemd-timedated.service')
+ self.fail(f'Timezone: {tz}, expected: Pacific/Honolulu')
class MatchClientTest(unittest.TestCase, NetworkdTestingUtilities):
diff --git a/test/run-integration-tests.sh b/test/run-integration-tests.sh
index 2a2b075..e1c0c0a 100755
--- a/test/run-integration-tests.sh
+++ b/test/run-integration-tests.sh
@@ -2,130 +2,156 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
-if [ "$NO_BUILD" ]; then
+is_valid_target() {
+ local target="${1:?}"
+ local t
+
+ for t in all setup run clean clean-again; do
+ [[ "$target" == "$t" ]] && return 0
+ done
+
+ return 1
+}
+
+pass_deny_list() {
+ local test="${1:?}"
+ local marker
+
+ for marker in $DENY_LIST_MARKERS $BLACKLIST_MARKERS; do
+ if [[ -f "$test/$marker" ]]; then
+ echo "========== DENY-LISTED: $test ($marker) =========="
+ return 1
+ fi
+ done
+
+ return 0
+}
+
+test_run() {
+ local test_name="${1:?}"
+ shift
+
+ if [[ $# -eq 0 ]]; then
+ echo >&2 "test_run: missing arguments"
+ exit 1
+ fi
+
+ # Note: let's be very explicit in reporting the return code of the test command here, i.e don't rely on
+ # `set -e` or the return code of the last statement in the function, since reporting false positive
+ # would be very bad in this case.
+ if [[ "${SPLIT_TEST_LOGS:-0}" -ne 0 && -n "${ARTIFACT_DIRECTORY:-}" ]]; then
+ (set -x; "$@") &>>"$ARTIFACT_DIRECTORY/$test_name.log" || return $?
+ else
+ (set -x; "$@") || return $?
+ fi
+}
+
+ARGS=(setup run clean-again)
+CLEAN=0
+CLEAN_AGAIN=0
+COUNT=0
+FAILURES=0
+declare -A RESULTS
+declare -A TIMES
+
+if [[ "${NO_BUILD:-0}" =~ ^(1|yes|true)$ ]]; then
BUILD_DIR=""
elif BUILD_DIR="$("$(dirname "$0")/../tools/find-build-dir.sh")"; then
ninja -C "$BUILD_DIR"
else
- echo "No build found, please set BUILD_DIR or NO_BUILD" >&2
+ echo >&2 "No build found, please set BUILD_DIR or NO_BUILD"
exit 1
fi
-if [ $# -gt 0 ]; then
- args="$*"
-else
- args="setup run clean-again"
+if [[ $# -gt 0 ]]; then
+ ARGS=("$@")
fi
-VALID_TARGETS="all setup run clean clean-again"
-
-is_valid_target() {
- for target in $VALID_TARGETS; do
- [ "$1" = "$target" ] && return 0
- done
- return 1
-}
-
-# reject invalid make targets in $args
-for arg in $args; do
+# Reject invalid make targets
+for arg in "${ARGS[@]}"; do
if ! is_valid_target "$arg"; then
- echo "Invalid target: $arg" >&2
+ echo >&2 "Invalid target: $arg"
exit 1
fi
done
-CLEAN=0
-CLEANAGAIN=0
-
-# separate 'clean' and 'clean-again' operations
-[[ "$args" =~ "clean-again" ]] && CLEANAGAIN=1
-args=${args/clean-again}
-[[ "$args" =~ "clean" ]] && CLEAN=1
-args=${args/clean}
-
-declare -A results
-declare -A times
-
-COUNT=0
-FAILURES=0
+# Separate 'clean' and 'clean-again' operations
+args_filtered=()
+for arg in "${ARGS[@]}"; do
+ if [[ "$arg" == "clean-again" ]]; then
+ CLEAN_AGAIN=1
+ elif [[ "$arg" == "clean" ]]; then
+ CLEAN=1
+ else
+ args_filtered+=("$arg")
+ fi
+done
+ARGS=("${args_filtered[@]}")
cd "$(dirname "$0")"
-pass_deny_list() {
- for marker in $DENY_LIST_MARKERS $BLACKLIST_MARKERS; do
- if [ -f "$1/$marker" ]; then
- echo "========== DENY-LISTED: $1 ($marker) =========="
- return 1
- fi
- done
- return 0
-}
-
SELECTED_TESTS="${SELECTED_TESTS:-TEST-??-*}"
# Let's always do the cleaning operation first, because it destroys the image
# cache.
-if [ $CLEAN = 1 ]; then
- for TEST in $SELECTED_TESTS; do
- ( set -x ; make -C "$TEST" clean )
+if [[ $CLEAN -eq 1 ]]; then
+ for test in $SELECTED_TESTS; do
+ test_run "$test" make -C "$test" clean
done
fi
# Run actual tests (if requested)
-if [[ $args =~ [a-z] ]]; then
- for TEST in $SELECTED_TESTS; do
- COUNT=$((COUNT+1))
+if [[ ${#ARGS[@]} -ne 0 ]]; then
+ for test in $SELECTED_TESTS; do
+ COUNT=$((COUNT + 1))
- pass_deny_list "$TEST" || continue
- start=$(date +%s)
+ pass_deny_list "$test" || continue
+ SECONDS=0
- echo -e "\n[$(date +%R:%S)] --x-- Running $TEST --x--"
+ echo -e "\n[$(date +%R:%S)] --x-- Running $test --x--"
set +e
- # shellcheck disable=SC2086
- ( set -x ; make -C "$TEST" $args )
- RESULT=$?
+ test_run "$test" make -C "$test" "${ARGS[@]}"
+ result=$?
set -e
- echo "[$(date +%R:%S)] --x-- Result of $TEST: $RESULT --x--"
-
- results["$TEST"]="$RESULT"
- times["$TEST"]=$(( $(date +%s) - start ))
+ echo "[$(date +%R:%S)] --x-- Result of $test: $result --x--"
- [ "$RESULT" -ne "0" ] && FAILURES=$((FAILURES+1))
- done
-fi
+ RESULTS["$test"]="$result"
+ TIMES["$test"]="$SECONDS"
-# Run clean-again, if requested, and if no tests failed
-if [[ $FAILURES -eq 0 && $CLEANAGAIN -eq 1 ]]; then
- for TEST in "${!results[@]}"; do
- ( set -x ; make -C "$TEST" clean-again )
+ # Run clean-again here to free up space, if requested, and if the test succeeded
+ if [[ "$result" -ne 0 ]]; then
+ FAILURES=$((FAILURES + 1))
+ elif [[ $CLEAN_AGAIN -eq 1 ]]; then
+ test_run "$test" make -C "$test" clean-again
+ fi
done
fi
echo ""
-for TEST in "${!results[@]}"; do
- RESULT="${results[$TEST]}"
- time="${times[$TEST]}"
- string=$([ "$RESULT" = "0" ] && echo "SUCCESS" || echo "FAIL")
- printf "%-35s %-8s (%3s s)\n" "${TEST}:" "${string}" "$time"
+for test in "${!RESULTS[@]}"; do
+ result="${RESULTS[$test]}"
+ time="${TIMES[$test]}"
+ [[ "$result" -eq 0 ]] && string="SUCCESS" || string="FAIL"
+ printf "%-35s %-8s (%3s s)\n" "$test:" "$string" "$time"
done | sort
-if [ "$FAILURES" -eq 0 ] ; then
+if [[ "$FAILURES" -eq 0 ]]; then
echo -e "\nALL $COUNT TESTS PASSED"
else
echo -e "\nTOTAL FAILURES: $FAILURES OF $COUNT"
fi
# If we have coverage files, merge them into a single report for upload
-if [ -n "${ARTIFACT_DIRECTORY}" ]; then
+if [[ -n "$ARTIFACT_DIRECTORY" ]]; then
lcov_args=()
while read -r info_file; do
- lcov_args+=(--add-tracefile "${info_file}")
- done < <(find "${ARTIFACT_DIRECTORY}" -maxdepth 1 -name "*.coverage-info")
+ lcov_args+=(--add-tracefile "$info_file")
+ done < <(find "$ARTIFACT_DIRECTORY" -maxdepth 1 -name "*.coverage-info")
- if [ ${#lcov_args[@]} -gt 1 ]; then
- lcov "${lcov_args[@]}" --output-file "${ARTIFACT_DIRECTORY}/merged.coverage-info"
+ if [[ ${#lcov_args[@]} -gt 1 ]]; then
+ lcov "${lcov_args[@]}" --output-file "$ARTIFACT_DIRECTORY/merged.coverage-info"
fi
fi
diff --git a/test/run-unit-tests.py b/test/run-unit-tests.py
index e6f26c2..de8ac5c 100755
--- a/test/run-unit-tests.py
+++ b/test/run-unit-tests.py
@@ -28,6 +28,9 @@ def argument_parser():
help='run "unsafe" tests too')
p.add_argument('-A', '--artifact_directory',
help='store output from failed tests in this dir')
+ p.add_argument('-s', '--skip', action='append', default=[],
+ help='skip the named test')
+
return p
opts = argument_parser().parse_args()
@@ -42,9 +45,14 @@ if not opts.artifact_directory and os.getenv('ARTIFACT_DIRECTORY'):
opts.artifact_directory = os.getenv('ARTIFACT_DIRECTORY')
total.total = len(tests)
-for test in tests:
+for test in sorted(tests):
name = os.path.basename(test)
+ if name in opts.skip:
+ print(f'{YELLOW}SKIP: {name} (by user) {RESET_ALL}')
+ total.skip += 1
+ continue
+
ex = subprocess.run(test, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if ex.returncode == 0:
print(f'{GREEN}PASS: {name}{RESET_ALL}')
diff --git a/test/test-exec-deserialization.py b/test/test-exec-deserialization.py
deleted file mode 100755
index f8f3a6d..0000000
--- a/test/test-exec-deserialization.py
+++ /dev/null
@@ -1,214 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Copyright © 2017 Michal Sekletar <msekleta@redhat.com>
-
-# ATTENTION: This uses the *installed* systemd, not the one from the built
-# source tree.
-
-import os
-import subprocess
-import sys
-import time
-import unittest
-import uuid
-from enum import Enum
-
-class InstallChange(Enum):
- NO_CHANGE = 0
- LINES_SWAPPED = 1
- COMMAND_ADDED_BEFORE = 2
- COMMAND_ADDED_AFTER = 3
- COMMAND_INTERLEAVED = 4
- REMOVAL = 5
-
-class ExecutionResumeTest(unittest.TestCase):
- def setUp(self):
- self.unit = 'test-issue-518.service'
- self.unitfile_path = f'/run/systemd/system/{self.unit}'
- self.output_file = f"/tmp/test-issue-518-{uuid.uuid4()}"
- self.unit_files = {}
-
- unit_file_content = f'''
- [Service]
- Type=oneshot
- ExecStart=/bin/sleep 3
- ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
- '''
- self.unit_files[InstallChange.NO_CHANGE] = unit_file_content
-
- unit_file_content = f'''
- [Service]
- Type=oneshot
- ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
- ExecStart=/bin/sleep 3
- '''
- self.unit_files[InstallChange.LINES_SWAPPED] = unit_file_content
-
- unit_file_content = f'''
- [Service]
- Type=oneshot
- ExecStart=/bin/bash -c "echo bar >>{self.output_file}"
- ExecStart=/bin/sleep 3
- ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
- '''
- self.unit_files[InstallChange.COMMAND_ADDED_BEFORE] = unit_file_content
-
- unit_file_content = f'''
- [Service]
- Type=oneshot
- ExecStart=/bin/sleep 3
- ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
- ExecStart=/bin/bash -c "echo bar >>{self.output_file}"
- '''
- self.unit_files[InstallChange.COMMAND_ADDED_AFTER] = unit_file_content
-
- unit_file_content = f'''
- [Service]
- Type=oneshot
- ExecStart=/bin/bash -c "echo baz >>{self.output_file}"
- ExecStart=/bin/sleep 3
- ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
- ExecStart=/bin/bash -c "echo bar >>{self.output_file}"
- '''
- self.unit_files[InstallChange.COMMAND_INTERLEAVED] = unit_file_content
-
- unit_file_content = f'''
- [Service]
- Type=oneshot
- ExecStart=/bin/bash -c "echo bar >>{self.output_file}"
- ExecStart=/bin/bash -c "echo baz >>{self.output_file}"
- '''
- self.unit_files[InstallChange.REMOVAL] = unit_file_content
-
- def reload(self):
- subprocess.check_call(['systemctl', 'daemon-reload'])
-
- def write_unit_file(self, unit_file_change):
- if not isinstance(unit_file_change, InstallChange):
- raise ValueError('Unknown unit file change')
-
- content = self.unit_files[unit_file_change]
-
- with open(self.unitfile_path, 'w', encoding='utf-8') as f:
- f.write(content)
-
- self.reload()
-
- def check_output(self, expected_output):
- for _ in range(15):
- # Wait until the unit finishes so we don't check an incomplete log
- if subprocess.call(['systemctl', '-q', 'is-active', self.unit]) == 0:
- continue
-
- os.sync()
-
- try:
- with open(self.output_file, 'r', encoding='utf-8') as log:
- output = log.read()
- self.assertEqual(output, expected_output)
- return
- except IOError:
- pass
-
- time.sleep(1)
-
- self.fail(f'Timed out while waiting for the output file {self.output_file} to appear')
-
- def setup_unit(self):
- self.write_unit_file(InstallChange.NO_CHANGE)
- subprocess.check_call(['systemctl', '--job-mode=replace', '--no-block', 'start', self.unit])
- time.sleep(1)
-
- def test_no_change(self):
- expected_output = 'foo\n'
-
- self.setup_unit()
- self.reload()
-
- self.check_output(expected_output)
-
- def test_swapped(self):
- self.setup_unit()
- self.write_unit_file(InstallChange.LINES_SWAPPED)
- self.reload()
-
- self.assertTrue(not os.path.exists(self.output_file))
-
- def test_added_before(self):
- expected_output = 'foo\n'
-
- self.setup_unit()
- self.write_unit_file(InstallChange.COMMAND_ADDED_BEFORE)
- self.reload()
-
- self.check_output(expected_output)
-
- def test_added_after(self):
- expected_output = 'foo\nbar\n'
-
- self.setup_unit()
- self.write_unit_file(InstallChange.COMMAND_ADDED_AFTER)
- self.reload()
-
- self.check_output(expected_output)
-
- def test_interleaved(self):
- expected_output = 'foo\nbar\n'
-
- self.setup_unit()
- self.write_unit_file(InstallChange.COMMAND_INTERLEAVED)
- self.reload()
-
- self.check_output(expected_output)
-
- def test_removal(self):
- self.setup_unit()
- self.write_unit_file(InstallChange.REMOVAL)
- self.reload()
-
- self.assertTrue(not os.path.exists(self.output_file))
-
- def test_issue_6533(self):
- unit = "test-issue-6533.service"
- unitfile_path = f"/run/systemd/system/{unit}"
-
- content = '''
- [Service]
- ExecStart=/bin/sleep 5
- '''
-
- with open(unitfile_path, 'w', encoding='utf-8') as f:
- f.write(content)
-
- self.reload()
-
- subprocess.check_call(['systemctl', '--job-mode=replace', '--no-block', 'start', unit])
- time.sleep(2)
-
- content = '''
- [Service]
- ExecStart=/bin/sleep 5
- ExecStart=/bin/true
- '''
-
- with open(unitfile_path, 'w', encoding='utf-8') as f:
- f.write(content)
-
- self.reload()
- time.sleep(5)
-
- self.assertNotEqual(subprocess.call("journalctl -b _PID=1 | grep -q 'Freezing execution'", shell=True), 0)
-
- def tearDown(self):
- for f in [self.output_file, self.unitfile_path]:
- try:
- os.remove(f)
- except OSError:
- # ignore error if log file doesn't exist
- pass
-
- self.reload()
-
-if __name__ == '__main__':
- unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=3))
diff --git a/test/test-execute/exec-ambientcapabilities-dynuser.service b/test/test-execute/exec-ambientcapabilities-dynuser.service
index 560628e..ab815f3 100644
--- a/test/test-execute/exec-ambientcapabilities-dynuser.service
+++ b/test/test-execute/exec-ambientcapabilities-dynuser.service
@@ -3,8 +3,9 @@
Description=Test for AmbientCapabilities (dynamic user)
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002081"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002081"'
Type=oneshot
AmbientCapabilities=CAP_CHOWN CAP_SETUID CAP_NET_RAW
DynamicUser=yes
PrivateUsers=yes
+EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
diff --git a/test/test-execute/exec-ambientcapabilities-merge-nfsnobody.service b/test/test-execute/exec-ambientcapabilities-merge-nfsnobody.service
index 4960da5..a170b3d 100644
--- a/test/test-execute/exec-ambientcapabilities-merge-nfsnobody.service
+++ b/test/test-execute/exec-ambientcapabilities-merge-nfsnobody.service
@@ -3,7 +3,7 @@
Description=Test for AmbientCapabilities
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
Type=oneshot
User=nfsnobody
AmbientCapabilities=CAP_CHOWN
diff --git a/test/test-execute/exec-ambientcapabilities-merge-nobody.service b/test/test-execute/exec-ambientcapabilities-merge-nobody.service
index 4c72b2e..2e21bbc 100644
--- a/test/test-execute/exec-ambientcapabilities-merge-nobody.service
+++ b/test/test-execute/exec-ambientcapabilities-merge-nobody.service
@@ -3,7 +3,7 @@
Description=Test for AmbientCapabilities
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
Type=oneshot
User=nobody
AmbientCapabilities=CAP_CHOWN
diff --git a/test/test-execute/exec-ambientcapabilities-merge.service b/test/test-execute/exec-ambientcapabilities-merge.service
index 13a5d45..c4bb21b 100644
--- a/test/test-execute/exec-ambientcapabilities-merge.service
+++ b/test/test-execute/exec-ambientcapabilities-merge.service
@@ -3,7 +3,7 @@
Description=Test for AmbientCapabilities (daemon)
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
Type=oneshot
User=daemon
AmbientCapabilities=CAP_CHOWN
diff --git a/test/test-execute/exec-ambientcapabilities-nfsnobody.service b/test/test-execute/exec-ambientcapabilities-nfsnobody.service
index 10cb440..0bf91cc 100644
--- a/test/test-execute/exec-ambientcapabilities-nfsnobody.service
+++ b/test/test-execute/exec-ambientcapabilities-nfsnobody.service
@@ -3,7 +3,7 @@
Description=Test for AmbientCapabilities
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
Type=oneshot
User=nfsnobody
AmbientCapabilities=CAP_CHOWN CAP_NET_RAW
diff --git a/test/test-execute/exec-ambientcapabilities-nobody.service b/test/test-execute/exec-ambientcapabilities-nobody.service
index 5400cac..8bd7ac4 100644
--- a/test/test-execute/exec-ambientcapabilities-nobody.service
+++ b/test/test-execute/exec-ambientcapabilities-nobody.service
@@ -3,7 +3,7 @@
Description=Test for AmbientCapabilities
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
Type=oneshot
User=nobody
AmbientCapabilities=CAP_CHOWN CAP_NET_RAW
diff --git a/test/test-execute/exec-ambientcapabilities.service b/test/test-execute/exec-ambientcapabilities.service
index 5336bec..1bbc727 100644
--- a/test/test-execute/exec-ambientcapabilities.service
+++ b/test/test-execute/exec-ambientcapabilities.service
@@ -3,7 +3,7 @@
Description=Test for AmbientCapabilities (daemon)
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
+ExecStart=sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000002001"'
Type=oneshot
User=daemon
AmbientCapabilities=CAP_CHOWN CAP_NET_RAW
diff --git a/test/test-execute/exec-bindpaths.service b/test/test-execute/exec-bindpaths.service
index bf6968f..12e92e2 100644
--- a/test/test-execute/exec-bindpaths.service
+++ b/test/test-execute/exec-bindpaths.service
@@ -11,7 +11,7 @@ ExecStart=test -f /tmp/thisisasimpletest
# Also, through /tmp/test-exec-bindreadonlypaths
ExecStart=test -f /tmp/test-exec-bindreadonlypaths/thisisasimpletest
# The file cannot modify through /tmp/test-exec-bindreadonlypaths
-ExecStart=/bin/sh -x -c '! touch /tmp/test-exec-bindreadonlypaths/thisisasimpletest'
+ExecStart=sh -x -c '! touch /tmp/test-exec-bindreadonlypaths/thisisasimpletest'
# Cleanup
ExecStart=rm /tmp/thisisasimpletest
BindPaths=/tmp:/tmp/test-exec-bindpaths
diff --git a/test/test-execute/exec-capabilityboundingset-invert.service b/test/test-execute/exec-capabilityboundingset-invert.service
index 1b1217e..14f16c6 100644
--- a/test/test-execute/exec-capabilityboundingset-invert.service
+++ b/test/test-execute/exec-capabilityboundingset-invert.service
@@ -4,6 +4,6 @@ Description=Test for CapabilityBoundingSet
[Service]
# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep "^Bounding set .*cap_chown"'
+ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep "^Bounding set .*cap_chown"'
Type=oneshot
CapabilityBoundingSet=~CAP_CHOWN
diff --git a/test/test-execute/exec-capabilityboundingset-merge.service b/test/test-execute/exec-capabilityboundingset-merge.service
index 1ed3ccb..d3a2370 100644
--- a/test/test-execute/exec-capabilityboundingset-merge.service
+++ b/test/test-execute/exec-capabilityboundingset-merge.service
@@ -3,7 +3,7 @@
Description=Test for CapabilityBoundingSet
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_chown,cap_fowner,cap_kill"'
+ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_chown,cap_fowner,cap_kill"'
Type=oneshot
CapabilityBoundingSet=CAP_FOWNER
CapabilityBoundingSet=CAP_KILL CAP_CHOWN
diff --git a/test/test-execute/exec-capabilityboundingset-reset.service b/test/test-execute/exec-capabilityboundingset-reset.service
index 8eb142c..2443951 100644
--- a/test/test-execute/exec-capabilityboundingset-reset.service
+++ b/test/test-execute/exec-capabilityboundingset-reset.service
@@ -3,7 +3,7 @@
Description=Test for CapabilityBoundingSet
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set ="'
+ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set ="'
Type=oneshot
CapabilityBoundingSet=CAP_FOWNER CAP_KILL
CapabilityBoundingSet=
diff --git a/test/test-execute/exec-capabilityboundingset-simple.service b/test/test-execute/exec-capabilityboundingset-simple.service
index be5a5e5..3df3e6d 100644
--- a/test/test-execute/exec-capabilityboundingset-simple.service
+++ b/test/test-execute/exec-capabilityboundingset-simple.service
@@ -3,6 +3,6 @@
Description=Test for CapabilityBoundingSet
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_fowner,cap_kill"'
+ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_fowner,cap_kill"'
Type=oneshot
CapabilityBoundingSet=CAP_FOWNER CAP_KILL
diff --git a/test/test-execute/exec-condition-failed.service b/test/test-execute/exec-condition-failed.service
index 342219c..eb136ff 100644
--- a/test/test-execute/exec-condition-failed.service
+++ b/test/test-execute/exec-condition-failed.service
@@ -9,4 +9,4 @@ Type=oneshot
ExecCondition=/bin/sh -c 'exit 255'
# This should not get run
-ExecStart=/bin/sh -c 'true'
+ExecStart=sh -c 'true'
diff --git a/test/test-execute/exec-condition-skip.service b/test/test-execute/exec-condition-skip.service
index b69e161..4ee58b9 100644
--- a/test/test-execute/exec-condition-skip.service
+++ b/test/test-execute/exec-condition-skip.service
@@ -13,4 +13,4 @@ ExecCondition=/bin/sh -c 'exit 254'
ExecCondition=/bin/sh -c 'exit 255'
# This should not get run
-ExecStart=/bin/sh -c 'true'
+ExecStart=sh -c 'true'
diff --git a/test/test-execute/exec-cpuaffinity1.service b/test/test-execute/exec-cpuaffinity1.service
index 2a8544a..c0941a5 100644
--- a/test/test-execute/exec-cpuaffinity1.service
+++ b/test/test-execute/exec-cpuaffinity1.service
@@ -3,5 +3,5 @@
Description=Test for CPUAffinity (simple)
[Service]
-ExecStart=/bin/sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 1'
+ExecStart=sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 1'
CPUAffinity=0
diff --git a/test/test-execute/exec-cpuaffinity2.service b/test/test-execute/exec-cpuaffinity2.service
index bed48c8..d699ecc 100644
--- a/test/test-execute/exec-cpuaffinity2.service
+++ b/test/test-execute/exec-cpuaffinity2.service
@@ -3,7 +3,7 @@
Description=Test for CPUAffinity (reset)
[Service]
-ExecStart=/bin/sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 1'
+ExecStart=sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 1'
CPUAffinity=0-1 3
CPUAffinity=
CPUAffinity=0
diff --git a/test/test-execute/exec-cpuaffinity3.service b/test/test-execute/exec-cpuaffinity3.service
index 774cd64..8e8f782 100644
--- a/test/test-execute/exec-cpuaffinity3.service
+++ b/test/test-execute/exec-cpuaffinity3.service
@@ -3,6 +3,6 @@
Description=Test for CPUAffinity (merge)
[Service]
-ExecStart=/bin/sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 7'
+ExecStart=sh -c 'test $$(cat /proc/self/status | grep Cpus_allowed: | rev | cut -c 1) = 7'
CPUAffinity=0,1
CPUAffinity=1-2
diff --git a/test/test-execute/exec-dynamicuser-fixeduser-adm.service b/test/test-execute/exec-dynamicuser-fixeduser-adm.service
index daaed6c..1b7f232 100644
--- a/test/test-execute/exec-dynamicuser-fixeduser-adm.service
+++ b/test/test-execute/exec-dynamicuser-fixeduser-adm.service
@@ -5,8 +5,8 @@ Description=Test DynamicUser with static User= whose uid and gid are different
[Service]
Type=oneshot
-ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"'
+ExecStart=sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"'
# Multiple ExecStart= lines causes the issue #9702.
-ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"'
+ExecStart=sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"'
DynamicUser=yes
User=adm
diff --git a/test/test-execute/exec-dynamicuser-fixeduser-games.service b/test/test-execute/exec-dynamicuser-fixeduser-games.service
index db8b88e..b13c23a 100644
--- a/test/test-execute/exec-dynamicuser-fixeduser-games.service
+++ b/test/test-execute/exec-dynamicuser-fixeduser-games.service
@@ -5,8 +5,8 @@ Description=Test DynamicUser with static User= whose uid and gid are different
[Service]
Type=oneshot
-ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"'
+ExecStart=sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"'
# Multiple ExecStart= lines causes the issue #9702.
-ExecStart=/bin/sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"'
+ExecStart=sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"'
DynamicUser=yes
User=games
diff --git a/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service b/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service
index bbb1af5..e494c33 100644
--- a/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service
+++ b/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service
@@ -3,8 +3,8 @@
Description=Test DynamicUser with User= and SupplementaryGroups=
[Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
Type=oneshot
User=1
DynamicUser=yes
diff --git a/test/test-execute/exec-dynamicuser-fixeduser.service b/test/test-execute/exec-dynamicuser-fixeduser.service
index c5828c2..4ebfc20 100644
--- a/test/test-execute/exec-dynamicuser-fixeduser.service
+++ b/test/test-execute/exec-dynamicuser-fixeduser.service
@@ -3,8 +3,8 @@
Description=Test DynamicUser with User=
[Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
Type=oneshot
User=1
DynamicUser=yes
diff --git a/test/test-execute/exec-dynamicuser-runtimedirectory1.service b/test/test-execute/exec-dynamicuser-runtimedirectory1.service
index 790279a..59d3bf0 100644
--- a/test/test-execute/exec-dynamicuser-runtimedirectory1.service
+++ b/test/test-execute/exec-dynamicuser-runtimedirectory1.service
@@ -3,10 +3,11 @@
Description=Test for RuntimeDirectory with RuntimeDirectoryPreserve=yes and DynamicUser=yes
[Service]
-ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve'
-ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"'
-ExecStart=/bin/sh -x -c 'touch $$RUNTIME_DIRECTORY/test'
+ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve'
+ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"'
+ExecStart=sh -x -c 'touch $$RUNTIME_DIRECTORY/test'
Type=oneshot
RuntimeDirectory=test-exec_runtimedirectorypreserve
RuntimeDirectoryPreserve=yes
DynamicUser=yes
+EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
diff --git a/test/test-execute/exec-dynamicuser-runtimedirectory2.service b/test/test-execute/exec-dynamicuser-runtimedirectory2.service
index 18df74e..6ff9d75 100644
--- a/test/test-execute/exec-dynamicuser-runtimedirectory2.service
+++ b/test/test-execute/exec-dynamicuser-runtimedirectory2.service
@@ -3,11 +3,12 @@
Description=Test for RuntimeDirectory with RuntimeDirectoryPreserve=yes and DynamicUser=yes 2nd trial
[Service]
-ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve'
-ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"'
-ExecStart=/bin/sh -x -c 'test -f $$RUNTIME_DIRECTORY/test'
-ExecStart=/bin/sh -x -c 'touch $$RUNTIME_DIRECTORY/test'
+ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve'
+ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"'
+ExecStart=sh -x -c 'test -f $$RUNTIME_DIRECTORY/test'
+ExecStart=sh -x -c 'touch $$RUNTIME_DIRECTORY/test'
Type=oneshot
RuntimeDirectory=test-exec_runtimedirectorypreserve
RuntimeDirectoryPreserve=yes
DynamicUser=yes
+EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
diff --git a/test/test-execute/exec-dynamicuser-runtimedirectory3.service b/test/test-execute/exec-dynamicuser-runtimedirectory3.service
index 831a808..cebb819 100644
--- a/test/test-execute/exec-dynamicuser-runtimedirectory3.service
+++ b/test/test-execute/exec-dynamicuser-runtimedirectory3.service
@@ -3,10 +3,11 @@
Description=Test for RuntimeDirectory with DynamicUser=yes migrated from RuntimeDirectoryPreserve=yes
[Service]
-ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve'
-ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"'
-ExecStart=/bin/sh -x -c 'test -f $$RUNTIME_DIRECTORY/test'
-ExecStart=/bin/sh -x -c 'touch $$RUNTIME_DIRECTORY/test'
+ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectorypreserve'
+ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectorypreserve"'
+ExecStart=sh -x -c 'test -f $$RUNTIME_DIRECTORY/test'
+ExecStart=sh -x -c 'touch $$RUNTIME_DIRECTORY/test'
Type=oneshot
RuntimeDirectory=test-exec_runtimedirectorypreserve
DynamicUser=yes
+EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
diff --git a/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service b/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service
index 2a5a1e1..12375af 100644
--- a/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service
+++ b/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service
@@ -11,7 +11,7 @@ ExecStart=test -d %S/test-dynamicuser-migrate
ExecStart=test -d %S/test-dynamicuser-migrate2/hoge
ExecStart=touch %S/test-dynamicuser-migrate/yay
ExecStart=touch %S/test-dynamicuser-migrate2/hoge/yayyay
-ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
+ExecStart=sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
Type=oneshot
DynamicUser=no
diff --git a/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service b/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service
index e89f0c5..7261f4a 100644
--- a/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service
+++ b/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service
@@ -19,8 +19,9 @@ ExecStart=touch %S/test-dynamicuser-migrate/yay
ExecStart=touch %S/test-dynamicuser-migrate2/hoge/yayyay
ExecStart=touch %S/private/test-dynamicuser-migrate/yay
ExecStart=touch %S/private/test-dynamicuser-migrate2/hoge/yayyay
-ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
+ExecStart=sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
Type=oneshot
DynamicUser=yes
StateDirectory=test-dynamicuser-migrate test-dynamicuser-migrate2/hoge
+EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
diff --git a/test/test-execute/exec-dynamicuser-statedir.service b/test/test-execute/exec-dynamicuser-statedir.service
index 734fa20..636a702 100644
--- a/test/test-execute/exec-dynamicuser-statedir.service
+++ b/test/test-execute/exec-dynamicuser-statedir.service
@@ -83,3 +83,4 @@ ExecStart=sh -x -c 'test "$$STATE_DIRECTORY" = "%S/aaa:%S/aaa/bbb:%S/aaa/ccc:%S/
Type=oneshot
DynamicUser=yes
StateDirectory=waldo quux/pief aaa/bbb aaa aaa/ccc xxx/yyy:aaa/111 xxx:aaa/222 xxx/zzz:aaa/333 abc:d\:ef
+EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
diff --git a/test/test-execute/exec-dynamicuser-supplementarygroups.service b/test/test-execute/exec-dynamicuser-supplementarygroups.service
index d601af2..be1b8f7 100644
--- a/test/test-execute/exec-dynamicuser-supplementarygroups.service
+++ b/test/test-execute/exec-dynamicuser-supplementarygroups.service
@@ -3,8 +3,9 @@
Description=Test DynamicUser with SupplementaryGroups=
[Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
Type=oneshot
DynamicUser=yes
SupplementaryGroups=1 2
+EnvironmentFile=-/usr/lib/systemd/systemd-asan-env
diff --git a/test/test-execute/exec-environment-empty.service b/test/test-execute/exec-environment-empty.service
index 6c31186..e5af6ff 100644
--- a/test/test-execute/exec-environment-empty.service
+++ b/test/test-execute/exec-environment-empty.service
@@ -3,7 +3,7 @@
Description=Test for Environment
[Service]
-ExecStart=/bin/sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset"'
+ExecStart=sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset"'
Type=oneshot
Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6"
Environment=
diff --git a/test/test-execute/exec-environment-multiple.service b/test/test-execute/exec-environment-multiple.service
index d9b8d22..4199a46 100644
--- a/test/test-execute/exec-environment-multiple.service
+++ b/test/test-execute/exec-environment-multiple.service
@@ -3,7 +3,7 @@
Description=Test for Environment
[Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = foobar'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = foobar'
Type=oneshot
Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6"
Environment="VAR3=foobar"
diff --git a/test/test-execute/exec-environment-no-substitute.service b/test/test-execute/exec-environment-no-substitute.service
index b5cb2a4..7396576 100644
--- a/test/test-execute/exec-environment-no-substitute.service
+++ b/test/test-execute/exec-environment-no-substitute.service
@@ -3,7 +3,7 @@
Description=Test for No Environment Variable Substitution
[Service]
-ExecStart=/bin/sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2}" = "word3" && test "$${VAR3-unset}" = \'$word 5 6\''
+ExecStart=sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2}" = "word3" && test "$${VAR3-unset}" = \'$word 5 6\''
ExecStart=:/bin/sh -x -c 'test "$${VAR1-unset}" != "unset" && test "$${VAR2}" != "word3" && test "$${VAR3-unset}" != \'$word 5 6\''
Type=oneshot
Environment="VAR2=word3" "VAR3=$word 5 6"
diff --git a/test/test-execute/exec-environment.service b/test/test-execute/exec-environment.service
index 5655be0..7e3cb0e 100644
--- a/test/test-execute/exec-environment.service
+++ b/test/test-execute/exec-environment.service
@@ -3,6 +3,6 @@
Description=Test for Environment
[Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6"'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6"'
Type=oneshot
Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6"
diff --git a/test/test-execute/exec-environmentfile.service b/test/test-execute/exec-environmentfile.service
index 4ad5a9b..3f739fa 100644
--- a/test/test-execute/exec-environmentfile.service
+++ b/test/test-execute/exec-environmentfile.service
@@ -3,6 +3,6 @@
Description=Test for EnvironmentFile
[Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes'
Type=oneshot
EnvironmentFile=/tmp/test-exec_environmentfile.conf
diff --git a/test/test-execute/exec-execsearchpath-environment-path-set.service b/test/test-execute/exec-execsearchpath-environment-path-set.service
index 5969cc6..424c4ac 100644
--- a/test/test-execute/exec-execsearchpath-environment-path-set.service
+++ b/test/test-execute/exec-execsearchpath-environment-path-set.service
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
-ExecStart=/bin/sh -x -c 'test "$$PATH" = "/usr" && test "$$VAR1" = word3 && test "$$VAR2" = "\\$$word 5 6"'
+ExecStart=sh -x -c 'test "$$PATH" = "/usr" && test "$$VAR1" = word3 && test "$$VAR2" = "\\$$word 5 6"'
Type=oneshot
ExecSearchPath=/tmp:/bin
Environment="PATH=/usr" VAR1=word3 "VAR2=$word 5 6"
diff --git a/test/test-execute/exec-execsearchpath-environment.service b/test/test-execute/exec-execsearchpath-environment.service
index b0fa6a3..5c39d9c 100644
--- a/test/test-execute/exec-execsearchpath-environment.service
+++ b/test/test-execute/exec-execsearchpath-environment.service
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$PATH" = "/tmp:/bin"'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$PATH" = "/tmp:/bin"'
Type=oneshot
ExecSearchPath=/tmp:/bin
Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6"
diff --git a/test/test-execute/exec-execsearchpath-environmentfile-set.service b/test/test-execute/exec-execsearchpath-environmentfile-set.service
index 5f55a4b..8741582 100644
--- a/test/test-execute/exec-execsearchpath-environmentfile-set.service
+++ b/test/test-execute/exec-execsearchpath-environmentfile-set.service
@@ -3,7 +3,7 @@
Description=Test for ExecSearchPath with EnvironmentFile where EnvironmentFile sets PATH
[Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = /usr'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = /usr'
Type=oneshot
EnvironmentFile=/tmp/test-exec_execsearchpath_environmentfile-set.conf
ExecSearchPath=/tmp:/bin
diff --git a/test/test-execute/exec-execsearchpath-environmentfile.service b/test/test-execute/exec-execsearchpath-environmentfile.service
index b8335bc..53cede8 100644
--- a/test/test-execute/exec-execsearchpath-environmentfile.service
+++ b/test/test-execute/exec-execsearchpath-environmentfile.service
@@ -3,7 +3,7 @@
Description=Test for ExecSearchPath with EnvironmentFile where EnvironmentFile does not set PATH
[Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/tmp:/bin"'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/tmp:/bin"'
Type=oneshot
ExecSearchPath=/tmp:/bin
EnvironmentFile=/tmp/test-exec_execsearchpath_environmentfile.conf
diff --git a/test/test-execute/exec-execsearchpath-passenvironment-set.service b/test/test-execute/exec-execsearchpath-passenvironment-set.service
index a151161..2d4e75a 100644
--- a/test/test-execute/exec-execsearchpath-passenvironment-set.service
+++ b/test/test-execute/exec-execsearchpath-passenvironment-set.service
@@ -3,7 +3,7 @@
Description=Test for PassEnvironment with ExecSearchPath with PATH set by user
[Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/usr"'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/usr"'
Type=oneshot
PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5 PATH
ExecSearchPath=/tmp:/bin
diff --git a/test/test-execute/exec-execsearchpath-passenvironment.service b/test/test-execute/exec-execsearchpath-passenvironment.service
index d8a41c1..5bdab47 100644
--- a/test/test-execute/exec-execsearchpath-passenvironment.service
+++ b/test/test-execute/exec-execsearchpath-passenvironment.service
@@ -3,7 +3,7 @@
Description=Test for PassEnvironment with ExecSearchPath with PATH not set by user
[Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/tmp:/bin"'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes && test "$$PATH" = "/tmp:/bin"'
Type=oneshot
PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5
ExecSearchPath=/tmp:/bin
diff --git a/test/test-execute/exec-execsearchpath-unit-specifier.service b/test/test-execute/exec-execsearchpath-unit-specifier.service
index 30d6b32..a2037e9 100644
--- a/test/test-execute/exec-execsearchpath-unit-specifier.service
+++ b/test/test-execute/exec-execsearchpath-unit-specifier.service
@@ -5,4 +5,4 @@ Description=Test for specifiers with exec search path
[Service]
Type=oneshot
ExecSearchPath=/tmp:/bin:/usr/bin:%V
-ExecStart=/bin/sh -x -c 'test %V = /var/tmp && test "$$PATH" = "/tmp:/bin:/usr/bin:/var/tmp"'
+ExecStart=sh -x -c 'test %V = /var/tmp && test "$$PATH" = "/tmp:/bin:/usr/bin:/var/tmp"'
diff --git a/test/test-execute/exec-group-nfsnobody.service b/test/test-execute/exec-group-nfsnobody.service
index a1e59c5..aebb198 100644
--- a/test/test-execute/exec-group-nfsnobody.service
+++ b/test/test-execute/exec-group-nfsnobody.service
@@ -3,6 +3,6 @@
Description=Test for Group
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "nfsnobody"'
+ExecStart=sh -x -c 'test "$$(id -n -g)" = "nfsnobody"'
Type=oneshot
Group=nfsnobody
diff --git a/test/test-execute/exec-group-nobody.service b/test/test-execute/exec-group-nobody.service
index 58dce1e..cf283cb 100644
--- a/test/test-execute/exec-group-nobody.service
+++ b/test/test-execute/exec-group-nobody.service
@@ -3,6 +3,6 @@
Description=Test for Group
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "nobody"'
+ExecStart=sh -x -c 'test "$$(id -n -g)" = "nobody"'
Type=oneshot
Group=nobody
diff --git a/test/test-execute/exec-group-nogroup.service b/test/test-execute/exec-group-nogroup.service
index 7f16729..46c3dd3 100644
--- a/test/test-execute/exec-group-nogroup.service
+++ b/test/test-execute/exec-group-nogroup.service
@@ -3,6 +3,6 @@
Description=Test for Group
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "nogroup"'
+ExecStart=sh -x -c 'test "$$(id -n -g)" = "nogroup"'
Type=oneshot
Group=nogroup
diff --git a/test/test-execute/exec-group.service b/test/test-execute/exec-group.service
index 9f21557..bd5ac2d 100644
--- a/test/test-execute/exec-group.service
+++ b/test/test-execute/exec-group.service
@@ -3,6 +3,6 @@
Description=Test for Group (daemon)
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "daemon"'
+ExecStart=sh -x -c 'test "$$(id -n -g)" = "daemon"'
Type=oneshot
Group=daemon
diff --git a/test/test-execute/exec-ignoresigpipe-no.service b/test/test-execute/exec-ignoresigpipe-no.service
index e972481..ce8b258 100644
--- a/test/test-execute/exec-ignoresigpipe-no.service
+++ b/test/test-execute/exec-ignoresigpipe-no.service
@@ -3,6 +3,6 @@
Description=Test for IgnoreSIGPIPE=no
[Service]
-ExecStart=/bin/sh -x -c 'kill -PIPE 0'
+ExecStart=sh -x -c 'kill -PIPE 0'
Type=oneshot
IgnoreSIGPIPE=no
diff --git a/test/test-execute/exec-ignoresigpipe-yes.service b/test/test-execute/exec-ignoresigpipe-yes.service
index ee3aa9a..a26f53c 100644
--- a/test/test-execute/exec-ignoresigpipe-yes.service
+++ b/test/test-execute/exec-ignoresigpipe-yes.service
@@ -3,6 +3,6 @@
Description=Test for IgnoreSIGPIPE=yes
[Service]
-ExecStart=/bin/sh -x -c 'kill -PIPE 0'
+ExecStart=sh -x -c 'kill -PIPE 0'
Type=oneshot
IgnoreSIGPIPE=yes
diff --git a/test/test-execute/exec-inaccessiblepaths-mount-propagation.service b/test/test-execute/exec-inaccessiblepaths-mount-propagation.service
index 520bc53..8580f52 100644
--- a/test/test-execute/exec-inaccessiblepaths-mount-propagation.service
+++ b/test/test-execute/exec-inaccessiblepaths-mount-propagation.service
@@ -4,5 +4,5 @@ Description=Test to make sure that InaccessiblePaths= disconnect mount propagati
[Service]
InaccessiblePaths=-/i-dont-exist
-ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
+ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
Type=oneshot
diff --git a/test/test-execute/exec-inaccessiblepaths-sys.service b/test/test-execute/exec-inaccessiblepaths-sys.service
index 0d64aa1..64a570c 100644
--- a/test/test-execute/exec-inaccessiblepaths-sys.service
+++ b/test/test-execute/exec-inaccessiblepaths-sys.service
@@ -4,5 +4,5 @@ Description=Test to make sure that mount namespace setup works properly with the
[Service]
InaccessiblePaths=/sys
-ExecStart=/bin/sh -x -c 'test "$$(stat -c %%a /sys)" = "0"'
+ExecStart=sh -x -c 'test "$$(stat -c %%a /sys)" = "0"'
Type=oneshot
diff --git a/test/test-execute/exec-ioschedulingclass-best-effort.service b/test/test-execute/exec-ioschedulingclass-best-effort.service
index 3b946b7..569183f 100644
--- a/test/test-execute/exec-ioschedulingclass-best-effort.service
+++ b/test/test-execute/exec-ioschedulingclass-best-effort.service
@@ -3,6 +3,6 @@
Description=Test for IOSchedulingClass=best-effort
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "best-effort"'
+ExecStart=sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "best-effort"'
Type=oneshot
IOSchedulingClass=best-effort
diff --git a/test/test-execute/exec-ioschedulingclass-idle.service b/test/test-execute/exec-ioschedulingclass-idle.service
index b1e64bb..93377ea 100644
--- a/test/test-execute/exec-ioschedulingclass-idle.service
+++ b/test/test-execute/exec-ioschedulingclass-idle.service
@@ -3,6 +3,6 @@
Description=Test for IOSchedulingClass=idle
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "idle"'
+ExecStart=sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "idle"'
Type=oneshot
IOSchedulingClass=idle
diff --git a/test/test-execute/exec-ioschedulingclass-none.service b/test/test-execute/exec-ioschedulingclass-none.service
index 0494d45..b8198d1 100644
--- a/test/test-execute/exec-ioschedulingclass-none.service
+++ b/test/test-execute/exec-ioschedulingclass-none.service
@@ -4,6 +4,6 @@ Description=Test for IOSchedulingClass=none
[Service]
# Old kernels might report "none" here, new kernels "best-effort".
-ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "none" -o "$${c%%:*}" = "best-effort"'
+ExecStart=sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "none" -o "$${c%%:*}" = "best-effort"'
Type=oneshot
IOSchedulingClass=none
diff --git a/test/test-execute/exec-ioschedulingclass-realtime.service b/test/test-execute/exec-ioschedulingclass-realtime.service
index ef8e2eb..a7edb6d 100644
--- a/test/test-execute/exec-ioschedulingclass-realtime.service
+++ b/test/test-execute/exec-ioschedulingclass-realtime.service
@@ -3,6 +3,6 @@
Description=Test for IOSchedulingClass=realtime
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "realtime"'
+ExecStart=sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "realtime"'
Type=oneshot
IOSchedulingClass=realtime
diff --git a/test/test-execute/exec-load-credential.service b/test/test-execute/exec-load-credential.service
index 3a29b6d..9da19e6 100644
--- a/test/test-execute/exec-load-credential.service
+++ b/test/test-execute/exec-load-credential.service
@@ -3,9 +3,9 @@
Description=Test for LoadCredential=
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
-ExecStartPost=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
-ExecStop=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
-ExecStopPost=/bin/sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
+ExecStart=sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
+ExecStartPost=sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
+ExecStop=sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
+ExecStopPost=sh -x -c 'test "$$(cat %d/test-execute.load-credential)" = "foo"'
Type=oneshot
LoadCredential=test-execute.load-credential
diff --git a/test/test-execute/exec-networknamespacepath-privatemounts-no.service b/test/test-execute/exec-networknamespacepath-privatemounts-no.service
index 49277e3..07c0525 100644
--- a/test/test-execute/exec-networknamespacepath-privatemounts-no.service
+++ b/test/test-execute/exec-networknamespacepath-privatemounts-no.service
@@ -3,14 +3,14 @@
Description=Test for NetworkNamespacePath= without mount namespacing
[Service]
-ExecStart=/bin/sh -x -c '! ip link show dummy-test-exec'
-ExecStart=/bin/sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
+ExecStart=sh -x -c '! ip link show dummy-test-exec'
+ExecStart=sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
# Without mount namespacing, we can access the dummy-test-exec interface through sysfs.
-ExecStart=/bin/sh -x -c 'test -e /sys/class/net/dummy-test-exec'
-ExecStart=/bin/sh -x -c 'ip link show dummy-test-ns'
-ExecStart=/bin/sh -x -c 'test -e /proc/sys/net/ipv4/conf/dummy-test-ns'
+ExecStart=sh -x -c 'test -e /sys/class/net/dummy-test-exec'
+ExecStart=sh -x -c 'ip link show dummy-test-ns'
+ExecStart=sh -x -c 'test -e /proc/sys/net/ipv4/conf/dummy-test-ns'
# Without mount namespacing, we cannot access the dummy-test-ns interface through sysfs.
-ExecStart=/bin/sh -x -c 'test ! -e /sys/class/net/dummy-test-ns'
+ExecStart=sh -x -c 'test ! -e /sys/class/net/dummy-test-ns'
Type=oneshot
NetworkNamespacePath=/run/netns/test-execute-netns
PrivateMounts=no
diff --git a/test/test-execute/exec-networknamespacepath-privatemounts-yes.service b/test/test-execute/exec-networknamespacepath-privatemounts-yes.service
index 078fba8..10bc192 100644
--- a/test/test-execute/exec-networknamespacepath-privatemounts-yes.service
+++ b/test/test-execute/exec-networknamespacepath-privatemounts-yes.service
@@ -3,14 +3,14 @@
Description=Test for NetworkNamespacePath= with mount namespacing
[Service]
-ExecStart=/bin/sh -x -c '! ip link show dummy-test-exec'
-ExecStart=/bin/sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
+ExecStart=sh -x -c '! ip link show dummy-test-exec'
+ExecStart=sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
# With mount namespacing, we cannot access the dummy-test-exec interface through sysfs.
-ExecStart=/bin/sh -x -c 'test ! -e /sys/class/net/dummy-test-exec'
-ExecStart=/bin/sh -x -c 'ip link show dummy-test-ns'
-ExecStart=/bin/sh -x -c 'test -e /proc/sys/net/ipv4/conf/dummy-test-ns'
+ExecStart=sh -x -c 'test ! -e /sys/class/net/dummy-test-exec'
+ExecStart=sh -x -c 'ip link show dummy-test-ns'
+ExecStart=sh -x -c 'test -e /proc/sys/net/ipv4/conf/dummy-test-ns'
# With mount namespacing, we can access the dummy-test-ns interface through sysfs.
-ExecStart=/bin/sh -x -c 'test -e /sys/class/net/dummy-test-ns'
+ExecStart=sh -x -c 'test -e /sys/class/net/dummy-test-ns'
Type=oneshot
NetworkNamespacePath=/run/netns/test-execute-netns
# NetworkNamespacePath= implies PrivateMounts=yes
diff --git a/test/test-execute/exec-noexecpaths-simple.service b/test/test-execute/exec-noexecpaths-simple.service
index 5d954da..503be5a 100644
--- a/test/test-execute/exec-noexecpaths-simple.service
+++ b/test/test-execute/exec-noexecpaths-simple.service
@@ -7,5 +7,5 @@ Type=oneshot
# This should work, as we explicitly disable the effect of NoExecPaths=
ExecStart=+/bin/sh -c '/bin/cat /dev/null'
# This should also work, as we do not disable the effect of NoExecPaths= but invert the exit code
-ExecStart=/bin/sh -x -c '! /bin/cat /dev/null'
+ExecStart=sh -x -c '! /bin/cat /dev/null'
NoExecPaths=/bin/cat
diff --git a/test/test-execute/exec-oomscoreadjust-negative.service b/test/test-execute/exec-oomscoreadjust-negative.service
index 25b5f1f..5656030 100644
--- a/test/test-execute/exec-oomscoreadjust-negative.service
+++ b/test/test-execute/exec-oomscoreadjust-negative.service
@@ -3,6 +3,6 @@
Description=Test for OOMScoreAdjust
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(cat /proc/self/oom_score_adj); test "$$c" -eq -100'
+ExecStart=sh -x -c 'c=$$(cat /proc/self/oom_score_adj); test "$$c" -eq -100'
Type=oneshot
OOMScoreAdjust=-100
diff --git a/test/test-execute/exec-oomscoreadjust-positive.service b/test/test-execute/exec-oomscoreadjust-positive.service
index ea6c23f..a2079b8 100644
--- a/test/test-execute/exec-oomscoreadjust-positive.service
+++ b/test/test-execute/exec-oomscoreadjust-positive.service
@@ -3,6 +3,6 @@
Description=Test for OOMScoreAdjust
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(cat /proc/self/oom_score_adj); test "$$c" -eq 100'
+ExecStart=sh -x -c 'c=$$(cat /proc/self/oom_score_adj); test "$$c" -eq 100'
Type=oneshot
OOMScoreAdjust=100
diff --git a/test/test-execute/exec-passenvironment-absent.service b/test/test-execute/exec-passenvironment-absent.service
index 6b19a12..b2e5c20 100644
--- a/test/test-execute/exec-passenvironment-absent.service
+++ b/test/test-execute/exec-passenvironment-absent.service
@@ -3,6 +3,6 @@
Description=Test for PassEnvironment with variables absent from the execution environment
[Service]
-ExecStart=/bin/sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset" && test "$${VAR4-unset}" = "unset" && test "$${VAR5-unset}" = "unset"'
+ExecStart=sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset" && test "$${VAR4-unset}" = "unset" && test "$${VAR5-unset}" = "unset"'
Type=oneshot
PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5
diff --git a/test/test-execute/exec-passenvironment-empty.service b/test/test-execute/exec-passenvironment-empty.service
index 6ffc5e7..a5fd092 100644
--- a/test/test-execute/exec-passenvironment-empty.service
+++ b/test/test-execute/exec-passenvironment-empty.service
@@ -3,7 +3,7 @@
Description=Test for PassEnvironment and erasing the variable list
[Service]
-ExecStart=/bin/sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset" && test "$${VAR4-unset}" = "unset" && test "$${VAR5-unset}" = "unset"'
+ExecStart=sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2-unset}" = "unset" && test "$${VAR3-unset}" = "unset" && test "$${VAR4-unset}" = "unset" && test "$${VAR5-unset}" = "unset"'
Type=oneshot
PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5
PassEnvironment=
diff --git a/test/test-execute/exec-passenvironment-repeated.service b/test/test-execute/exec-passenvironment-repeated.service
index b8e904f..f3b886c 100644
--- a/test/test-execute/exec-passenvironment-repeated.service
+++ b/test/test-execute/exec-passenvironment-repeated.service
@@ -3,7 +3,7 @@
Description=Test for PassEnvironment with a variable name repeated
[Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes'
Type=oneshot
PassEnvironment=VAR1 VAR2
PassEnvironment=VAR1 VAR3
diff --git a/test/test-execute/exec-passenvironment.service b/test/test-execute/exec-passenvironment.service
index b69592a..1dcbcf9 100644
--- a/test/test-execute/exec-passenvironment.service
+++ b/test/test-execute/exec-passenvironment.service
@@ -3,6 +3,6 @@
Description=Test for PassEnvironment
[Service]
-ExecStart=/bin/sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes'
+ExecStart=sh -x -c 'test "$$VAR1" = "word1 word2" && test "$$VAR2" = word3 && test "$$VAR3" = "\\$$word 5 6" && test "$$VAR4" = "new\nline" && test "$$VAR5" = passwordwithbackslashes'
Type=oneshot
PassEnvironment=VAR1 VAR2 VAR3 VAR4 VAR5
diff --git a/test/test-execute/exec-personality-aarch64.service b/test/test-execute/exec-personality-aarch64.service
index 0783a87..e4ea294 100644
--- a/test/test-execute/exec-personality-aarch64.service
+++ b/test/test-execute/exec-personality-aarch64.service
@@ -3,6 +3,6 @@
Description=Test for Personality=aarch64
[Service]
-ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "aarch64")'
+ExecStart=sh -c 'echo $(uname -m); exit $(test $(uname -m) = "aarch64")'
Type=oneshot
Personality=aarch64
diff --git a/test/test-execute/exec-personality-loongarch64.service b/test/test-execute/exec-personality-loongarch64.service
index 0531ad1..31c6b25 100644
--- a/test/test-execute/exec-personality-loongarch64.service
+++ b/test/test-execute/exec-personality-loongarch64.service
@@ -2,6 +2,6 @@
Description=Test for Personality=loongarch64
[Service]
-ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "loongarch64")'
+ExecStart=sh -c 'echo $(uname -m); exit $(test $(uname -m) = "loongarch64")'
Type=oneshot
Personality=loongarch64
diff --git a/test/test-execute/exec-personality-ppc64.service b/test/test-execute/exec-personality-ppc64.service
index 72f063a..dd83bf6 100644
--- a/test/test-execute/exec-personality-ppc64.service
+++ b/test/test-execute/exec-personality-ppc64.service
@@ -3,6 +3,6 @@
Description=Test for Personality=ppc64
[Service]
-ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64")'
+ExecStart=sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64")'
Type=oneshot
Personality=ppc64
diff --git a/test/test-execute/exec-personality-ppc64le.service b/test/test-execute/exec-personality-ppc64le.service
index 5e38029..3f19d82 100644
--- a/test/test-execute/exec-personality-ppc64le.service
+++ b/test/test-execute/exec-personality-ppc64le.service
@@ -3,6 +3,6 @@
Description=Test for Personality=ppc64le
[Service]
-ExecStart=/bin/sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64le")'
+ExecStart=sh -c 'echo $(uname -m); exit $(test $(uname -m) = "ppc64le")'
Type=oneshot
Personality=ppc64le
diff --git a/test/test-execute/exec-personality-s390.service b/test/test-execute/exec-personality-s390.service
index 439dc5f..7d120cd 100644
--- a/test/test-execute/exec-personality-s390.service
+++ b/test/test-execute/exec-personality-s390.service
@@ -3,6 +3,6 @@
Description=Test for Personality=s390
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(uname -m); test "$$c" = "s390"'
+ExecStart=sh -x -c 'c=$$(uname -m); test "$$c" = "s390"'
Type=oneshot
Personality=s390
diff --git a/test/test-execute/exec-personality-s390x.service b/test/test-execute/exec-personality-s390x.service
new file mode 100644
index 0000000..4545dee
--- /dev/null
+++ b/test/test-execute/exec-personality-s390x.service
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for Personality=s390x
+
+[Service]
+ExecStart=sh -x -c 'c=$$(uname -m); test "$$c" = "s390x"'
+Type=oneshot
+Personality=s390x
diff --git a/test/test-execute/exec-personality-x86-64.service b/test/test-execute/exec-personality-x86-64.service
index c6a0a40..e7b945c 100644
--- a/test/test-execute/exec-personality-x86-64.service
+++ b/test/test-execute/exec-personality-x86-64.service
@@ -3,6 +3,6 @@
Description=Test for Personality=x86-64
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(uname -m); test "$$c" = "x86_64"'
+ExecStart=sh -x -c 'c=$$(uname -m); test "$$c" = "x86_64"'
Type=oneshot
Personality=x86-64
diff --git a/test/test-execute/exec-personality-x86.service b/test/test-execute/exec-personality-x86.service
index 8b820b3..95ec353 100644
--- a/test/test-execute/exec-personality-x86.service
+++ b/test/test-execute/exec-personality-x86.service
@@ -3,6 +3,6 @@
Description=Test for Personality=x86
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(uname -m); test "$$c" = "i686" -o "$$c" = "x86_64"'
+ExecStart=sh -x -c 'c=$$(uname -m); test "$$c" = "i686" -o "$$c" = "x86_64"'
Type=oneshot
Personality=x86
diff --git a/test/test-execute/exec-privatedevices-bind.service b/test/test-execute/exec-privatedevices-bind.service
index dbbbb4e..c2229a4 100644
--- a/test/test-execute/exec-privatedevices-bind.service
+++ b/test/test-execute/exec-privatedevices-bind.service
@@ -3,8 +3,8 @@
Description=Test for PrivateDevices=yes with a bind mounted device
[Service]
-ExecStart=/bin/sh -c 'test -c /dev/kmsg'
-ExecStart=/bin/sh -c 'test ! -w /dev/'
+ExecStart=sh -c 'test -c /dev/kmsg'
+ExecStart=sh -c 'test ! -w /dev/'
Type=oneshot
PrivateDevices=yes
BindPaths=/dev/kmsg
diff --git a/test/test-execute/exec-privatedevices-disabled-by-prefix.service b/test/test-execute/exec-privatedevices-disabled-by-prefix.service
index 021cadf..8f09c4a 100644
--- a/test/test-execute/exec-privatedevices-disabled-by-prefix.service
+++ b/test/test-execute/exec-privatedevices-disabled-by-prefix.service
@@ -3,7 +3,7 @@
Description=Test for PrivateDevices=yes with prefix
[Service]
-ExecStart=/bin/sh -x -c '! test -c /dev/kmsg'
+ExecStart=sh -x -c '! test -c /dev/kmsg'
ExecStart=+/bin/sh -x -c 'test -c /dev/kmsg'
Type=oneshot
PrivateDevices=yes
diff --git a/test/test-execute/exec-privatedevices-no-capability-mknod.service b/test/test-execute/exec-privatedevices-no-capability-mknod.service
index a07e822..811f4ad 100644
--- a/test/test-execute/exec-privatedevices-no-capability-mknod.service
+++ b/test/test-execute/exec-privatedevices-no-capability-mknod.service
@@ -5,5 +5,5 @@ Description=Test CAP_MKNOD capability for PrivateDevices=no
[Service]
PrivateDevices=no
# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod'
+ExecStart=sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod'
Type=oneshot
diff --git a/test/test-execute/exec-privatedevices-no-capability-sys-rawio.service b/test/test-execute/exec-privatedevices-no-capability-sys-rawio.service
index b0ce2d4..47be622 100644
--- a/test/test-execute/exec-privatedevices-no-capability-sys-rawio.service
+++ b/test/test-execute/exec-privatedevices-no-capability-sys-rawio.service
@@ -5,5 +5,5 @@ Description=Test CAP_SYS_RAWIO capability for PrivateDevices=no
[Service]
PrivateDevices=no
# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio'
+ExecStart=sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio'
Type=oneshot
diff --git a/test/test-execute/exec-privatedevices-no.service b/test/test-execute/exec-privatedevices-no.service
index 31a5e3c..5b8a051 100644
--- a/test/test-execute/exec-privatedevices-no.service
+++ b/test/test-execute/exec-privatedevices-no.service
@@ -3,6 +3,6 @@
Description=Test for PrivateDevices=no
[Service]
-ExecStart=/bin/sh -x -c 'test -c /dev/kmsg'
+ExecStart=sh -x -c 'test -c /dev/kmsg'
Type=oneshot
PrivateDevices=no
diff --git a/test/test-execute/exec-privatedevices-yes-capability-mknod.service b/test/test-execute/exec-privatedevices-yes-capability-mknod.service
index f798f31..3d29a9c 100644
--- a/test/test-execute/exec-privatedevices-yes-capability-mknod.service
+++ b/test/test-execute/exec-privatedevices-yes-capability-mknod.service
@@ -5,5 +5,5 @@ Description=Test CAP_MKNOD capability for PrivateDevices=yes
[Service]
PrivateDevices=yes
# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod'
+ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod'
Type=oneshot
diff --git a/test/test-execute/exec-privatedevices-yes-capability-sys-rawio.service b/test/test-execute/exec-privatedevices-yes-capability-sys-rawio.service
index d902c23..b1c0617 100644
--- a/test/test-execute/exec-privatedevices-yes-capability-sys-rawio.service
+++ b/test/test-execute/exec-privatedevices-yes-capability-sys-rawio.service
@@ -5,5 +5,5 @@ Description=Test CAP_SYS_RAWIO capability for PrivateDevices=yes
[Service]
PrivateDevices=yes
# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio'
+ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio'
Type=oneshot
diff --git a/test/test-execute/exec-privatedevices-yes-with-group.service b/test/test-execute/exec-privatedevices-yes-with-group.service
index a39ae0f..094ac22 100644
--- a/test/test-execute/exec-privatedevices-yes-with-group.service
+++ b/test/test-execute/exec-privatedevices-yes-with-group.service
@@ -8,10 +8,10 @@ Group=daemon
Type=oneshot
# Check the group applied
-ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "daemon"'
+ExecStart=sh -x -c 'test "$$(id -n -g)" = "daemon"'
# Check that the namespace applied
-ExecStart=/bin/sh -c 'test ! -c /dev/kmsg'
+ExecStart=sh -c 'test ! -c /dev/kmsg'
# Check that the owning group of a node is not daemon (should be the host root)
-ExecStart=/bin/sh -x -c 'test ! "$$(stat -c %%G /dev/stderr)" = "daemon"'
+ExecStart=sh -x -c 'test ! "$$(stat -c %%G /dev/stderr)" = "daemon"'
diff --git a/test/test-execute/exec-privatedevices-yes.service b/test/test-execute/exec-privatedevices-yes.service
index 564e958..2d32753 100644
--- a/test/test-execute/exec-privatedevices-yes.service
+++ b/test/test-execute/exec-privatedevices-yes.service
@@ -3,6 +3,6 @@
Description=Test for PrivateDevices=yes
[Service]
-ExecStart=/bin/sh -c 'test ! -c /dev/kmsg'
+ExecStart=sh -c 'test ! -c /dev/kmsg'
Type=oneshot
PrivateDevices=yes
diff --git a/test/test-execute/exec-privatenetwork-yes-privatemounts-no.service b/test/test-execute/exec-privatenetwork-yes-privatemounts-no.service
index 83708df..c16102d 100644
--- a/test/test-execute/exec-privatenetwork-yes-privatemounts-no.service
+++ b/test/test-execute/exec-privatenetwork-yes-privatemounts-no.service
@@ -3,10 +3,10 @@
Description=Test for PrivateNetwork= without mount namespacing
[Service]
-ExecStart=/bin/sh -x -c '! ip link show dummy-test-exec'
-ExecStart=/bin/sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
+ExecStart=sh -x -c '! ip link show dummy-test-exec'
+ExecStart=sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
# Without mount namespacing, we can access the dummy-test-exec interface through sysfs
-ExecStart=/bin/sh -x -c 'test -d /sys/class/net/dummy-test-exec'
+ExecStart=sh -x -c 'test -d /sys/class/net/dummy-test-exec'
Type=oneshot
PrivateNetwork=yes
PrivateMounts=no
diff --git a/test/test-execute/exec-privatenetwork-yes-privatemounts-yes.service b/test/test-execute/exec-privatenetwork-yes-privatemounts-yes.service
index 874f100..eb48d6e 100644
--- a/test/test-execute/exec-privatenetwork-yes-privatemounts-yes.service
+++ b/test/test-execute/exec-privatenetwork-yes-privatemounts-yes.service
@@ -3,10 +3,10 @@
Description=Test for PrivateNetwork= with mount namespacing
[Service]
-ExecStart=/bin/sh -x -c '! ip link show dummy-test-exec'
-ExecStart=/bin/sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
+ExecStart=sh -x -c '! ip link show dummy-test-exec'
+ExecStart=sh -x -c 'test ! -e /proc/sys/net/ipv4/conf/dummy-test-exec'
# With mount namespacing, we cannot access the dummy-test-exec interface through sysfs.
-ExecStart=/bin/sh -x -c 'test ! -e /sys/class/net/dummy-test-exec'
+ExecStart=sh -x -c 'test ! -e /sys/class/net/dummy-test-exec'
Type=oneshot
PrivateNetwork=yes
# PrivateNetwork=yes implies PrivateMounts=yes
diff --git a/test/test-execute/exec-privatetmp-disabled-by-prefix.service b/test/test-execute/exec-privatetmp-disabled-by-prefix.service
index f67afee..9dfcecc 100644
--- a/test/test-execute/exec-privatetmp-disabled-by-prefix.service
+++ b/test/test-execute/exec-privatetmp-disabled-by-prefix.service
@@ -3,7 +3,7 @@
Description=Test for PrivateTmp=yes with prefix
[Service]
-ExecStart=/bin/sh -x -c 'test ! -f /tmp/test-exec_privatetmp'
+ExecStart=sh -x -c 'test ! -f /tmp/test-exec_privatetmp'
ExecStart=+/bin/sh -x -c 'test -f /tmp/test-exec_privatetmp'
Type=oneshot
PrivateTmp=yes
diff --git a/test/test-execute/exec-privatetmp-no.service b/test/test-execute/exec-privatetmp-no.service
index 6a8a3fc..599203a 100644
--- a/test/test-execute/exec-privatetmp-no.service
+++ b/test/test-execute/exec-privatetmp-no.service
@@ -3,6 +3,6 @@
Description=Test for PrivateTmp=no
[Service]
-ExecStart=/bin/sh -x -c 'test -f /tmp/test-exec_privatetmp'
+ExecStart=sh -x -c 'test -f /tmp/test-exec_privatetmp'
Type=oneshot
PrivateTmp=no
diff --git a/test/test-execute/exec-privatetmp-yes.service b/test/test-execute/exec-privatetmp-yes.service
index 6395be0..5ea5263 100644
--- a/test/test-execute/exec-privatetmp-yes.service
+++ b/test/test-execute/exec-privatetmp-yes.service
@@ -3,6 +3,6 @@
Description=Test for PrivateTmp=yes
[Service]
-ExecStart=/bin/sh -x -c 'test ! -f /tmp/test-exec_privatetmp'
+ExecStart=sh -x -c 'test ! -f /tmp/test-exec_privatetmp'
Type=oneshot
PrivateTmp=yes
diff --git a/test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service b/test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service
index f84e6b6..c51cacf 100644
--- a/test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service
+++ b/test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service
@@ -7,4 +7,4 @@ Description=Test ProtectHome=tmpfs vs ProtectSystem=strict
ProtectHome=tmpfs
ProtectSystem=strict
Type=oneshot
-ExecStart=/bin/sh -x -c 'test "$$(stat -fc %%T /home)" = "tmpfs"'
+ExecStart=sh -x -c 'test "$$(stat -fc %%T /home)" = "tmpfs"'
diff --git a/test/test-execute/exec-protectkernellogs-no-capabilities.service b/test/test-execute/exec-protectkernellogs-no-capabilities.service
index 5478962..be64c58 100644
--- a/test/test-execute/exec-protectkernellogs-no-capabilities.service
+++ b/test/test-execute/exec-protectkernellogs-no-capabilities.service
@@ -5,5 +5,5 @@ Description=Test CAP_SYSLOG for ProtectKernelLogs=no
[Service]
ProtectKernelLogs=no
# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog'
+ExecStart=sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog'
Type=oneshot
diff --git a/test/test-execute/exec-protectkernellogs-yes-capabilities.service b/test/test-execute/exec-protectkernellogs-yes-capabilities.service
index 6fe1241..646ff75 100644
--- a/test/test-execute/exec-protectkernellogs-yes-capabilities.service
+++ b/test/test-execute/exec-protectkernellogs-yes-capabilities.service
@@ -5,5 +5,5 @@ Description=Test CAP_SYSLOG for ProtectKernelLogs=yes
[Service]
ProtectKernelLogs=yes
# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog'
+ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog'
Type=oneshot
diff --git a/test/test-execute/exec-protectkernelmodules-no-capabilities.service b/test/test-execute/exec-protectkernelmodules-no-capabilities.service
index 7236af2..cefdb60 100644
--- a/test/test-execute/exec-protectkernelmodules-no-capabilities.service
+++ b/test/test-execute/exec-protectkernelmodules-no-capabilities.service
@@ -5,5 +5,5 @@ Description=Test CAP_SYS_MODULE ProtectKernelModules=no
[Service]
ProtectKernelModules=no
# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module'
+ExecStart=sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module'
Type=oneshot
diff --git a/test/test-execute/exec-protectkernelmodules-yes-capabilities.service b/test/test-execute/exec-protectkernelmodules-yes-capabilities.service
index e40160d..1f327a2 100644
--- a/test/test-execute/exec-protectkernelmodules-yes-capabilities.service
+++ b/test/test-execute/exec-protectkernelmodules-yes-capabilities.service
@@ -5,5 +5,5 @@ Description=Test CAP_SYS_MODULE for ProtectKernelModules=yes
[Service]
ProtectKernelModules=yes
# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
-ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module'
+ExecStart=sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module'
Type=oneshot
diff --git a/test/test-execute/exec-protectkernelmodules-yes-mount-propagation.service b/test/test-execute/exec-protectkernelmodules-yes-mount-propagation.service
index 0ecf1a2..16399bd 100644
--- a/test/test-execute/exec-protectkernelmodules-yes-mount-propagation.service
+++ b/test/test-execute/exec-protectkernelmodules-yes-mount-propagation.service
@@ -4,5 +4,5 @@ Description=Test to make sure that passing ProtectKernelModules=yes disconnect m
[Service]
ProtectKernelModules=yes
-ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
+ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
Type=oneshot
diff --git a/test/test-execute/exec-readonlypaths-mount-propagation.service b/test/test-execute/exec-readonlypaths-mount-propagation.service
index abc180b..e896bac 100644
--- a/test/test-execute/exec-readonlypaths-mount-propagation.service
+++ b/test/test-execute/exec-readonlypaths-mount-propagation.service
@@ -4,5 +4,5 @@ Description=Test to make sure that passing ReadOnlyPaths= disconnect mount propa
[Service]
ReadOnlyPaths=-/i-dont-exist
-ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
+ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
Type=oneshot
diff --git a/test/test-execute/exec-readonlypaths-simple.service b/test/test-execute/exec-readonlypaths-simple.service
index 5587e8d..80e6c83 100644
--- a/test/test-execute/exec-readonlypaths-simple.service
+++ b/test/test-execute/exec-readonlypaths-simple.service
@@ -7,6 +7,6 @@ Type=oneshot
# This should work, as we explicitly disable the effect of ReadOnlyPaths=
ExecStart=+/bin/sh -c 'touch /tmp/thisisasimpletest'
# This should also work, as we do not disable the effect of ReadOnlyPaths= but invert the exit code
-ExecStart=/bin/sh -x -c '! touch /tmp/thisisasimpletest'
+ExecStart=sh -x -c '! touch /tmp/thisisasimpletest'
ExecStart=+/bin/sh -c 'rm /tmp/thisisasimpletest'
ReadOnlyPaths=/tmp
diff --git a/test/test-execute/exec-readonlypaths-with-bindpaths.service b/test/test-execute/exec-readonlypaths-with-bindpaths.service
index 71c7e7b..7a18367 100644
--- a/test/test-execute/exec-readonlypaths-with-bindpaths.service
+++ b/test/test-execute/exec-readonlypaths-with-bindpaths.service
@@ -5,5 +5,5 @@ Description=Test for ReadOnlyPaths=
[Service]
ReadOnlyPaths=/etc -/i-dont-exist /usr
BindPaths=/etc:/tmp/etc2
-ExecStart=/bin/sh -x -c 'test ! -w /etc && test ! -w /usr && test ! -e /i-dont-exist && test -w /var'
+ExecStart=sh -x -c 'test ! -w /etc && test ! -w /usr && test ! -e /i-dont-exist && test -w /var'
Type=oneshot
diff --git a/test/test-execute/exec-readonlypaths.service b/test/test-execute/exec-readonlypaths.service
index 21814c2..a0eff8b 100644
--- a/test/test-execute/exec-readonlypaths.service
+++ b/test/test-execute/exec-readonlypaths.service
@@ -5,6 +5,6 @@ Description=Test for ReadOnlyPaths=
[Service]
ReadOnlyPaths=/usr /etc /sys /dev -/i-dont-exist
PrivateDevices=yes
-ExecStart=/bin/sh -x -c 'test ! -w /usr && test ! -w /etc && test ! -w /sys && test ! -w /sys/fs/cgroup'
-ExecStart=/bin/sh -x -c 'test ! -w /dev && test ! -w /dev/shm && test ! -e /i-dont-exist && test -w /var'
+ExecStart=sh -x -c 'test ! -w /usr && test ! -w /etc && test ! -w /sys && test ! -w /sys/fs/cgroup'
+ExecStart=sh -x -c 'test ! -w /dev && test ! -w /dev/shm && test ! -e /i-dont-exist && test -w /var'
Type=oneshot
diff --git a/test/test-execute/exec-readwritepaths-mount-propagation.service b/test/test-execute/exec-readwritepaths-mount-propagation.service
index 35e736f..9b844cf 100644
--- a/test/test-execute/exec-readwritepaths-mount-propagation.service
+++ b/test/test-execute/exec-readwritepaths-mount-propagation.service
@@ -4,5 +4,5 @@ Description=Test to make sure that passing ReadWritePaths= disconnect mount prop
[Service]
ReadWritePaths=-/i-dont-exist
-ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
+ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); trap "umount \'$$d\' && rmdir \'$$d\'" EXIT; mount -t tmpfs tmpfs "$$d"; grep "$$d" /proc/self/mountinfo && ! grep "$$d" /proc/$${PPID}/mountinfo && ! grep "$$d" /proc/1/mountinfo'
Type=oneshot
diff --git a/test/test-execute/exec-runtimedirectory-mode.service b/test/test-execute/exec-runtimedirectory-mode.service
index 580bac9..e75e0d2 100644
--- a/test/test-execute/exec-runtimedirectory-mode.service
+++ b/test/test-execute/exec-runtimedirectory-mode.service
@@ -3,8 +3,8 @@
Description=Test for RuntimeDirectoryMode
[Service]
-ExecStart=/bin/sh -x -c 'mode=$$(stat -c %%a %t/test-exec_runtimedirectory-mode); test "$$mode" = "750"'
-ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory-mode"'
+ExecStart=sh -x -c 'mode=$$(stat -c %%a %t/test-exec_runtimedirectory-mode); test "$$mode" = "750"'
+ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory-mode"'
Type=oneshot
RuntimeDirectory=test-exec_runtimedirectory-mode
RuntimeDirectoryMode=0750
diff --git a/test/test-execute/exec-runtimedirectory-owner-nfsnobody.service b/test/test-execute/exec-runtimedirectory-owner-nfsnobody.service
index 79bebc4..4bc3361 100644
--- a/test/test-execute/exec-runtimedirectory-owner-nfsnobody.service
+++ b/test/test-execute/exec-runtimedirectory-owner-nfsnobody.service
@@ -3,7 +3,7 @@
Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set)
[Service]
-ExecStart=/bin/sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nfsnobody"'
+ExecStart=sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nfsnobody"'
Type=oneshot
Group=nfsnobody
User=root
diff --git a/test/test-execute/exec-runtimedirectory-owner-nobody.service b/test/test-execute/exec-runtimedirectory-owner-nobody.service
index 3b42a9f..5f94bf9 100644
--- a/test/test-execute/exec-runtimedirectory-owner-nobody.service
+++ b/test/test-execute/exec-runtimedirectory-owner-nobody.service
@@ -3,7 +3,7 @@
Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set)
[Service]
-ExecStart=/bin/sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nobody"'
+ExecStart=sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nobody"'
Type=oneshot
Group=nobody
User=root
diff --git a/test/test-execute/exec-runtimedirectory-owner-nogroup.service b/test/test-execute/exec-runtimedirectory-owner-nogroup.service
index 804048e..6d50895 100644
--- a/test/test-execute/exec-runtimedirectory-owner-nogroup.service
+++ b/test/test-execute/exec-runtimedirectory-owner-nogroup.service
@@ -3,7 +3,7 @@
Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set)
[Service]
-ExecStart=/bin/sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nogroup"'
+ExecStart=sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner); test "$$group" = "nogroup"'
Type=oneshot
Group=nogroup
User=root
diff --git a/test/test-execute/exec-runtimedirectory-owner.service b/test/test-execute/exec-runtimedirectory-owner.service
index e2c0890..64d66b3 100644
--- a/test/test-execute/exec-runtimedirectory-owner.service
+++ b/test/test-execute/exec-runtimedirectory-owner.service
@@ -3,7 +3,7 @@
Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set)
[Service]
-ExecStart=/bin/sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner-daemon); test "$$group" = "daemon"'
+ExecStart=sh -x -c 'group=$$(stat -c %%G %t/test-exec_runtimedirectory-owner-daemon); test "$$group" = "daemon"'
Type=oneshot
Group=daemon
User=root
diff --git a/test/test-execute/exec-runtimedirectory.service b/test/test-execute/exec-runtimedirectory.service
index 1928c57..f60110a 100644
--- a/test/test-execute/exec-runtimedirectory.service
+++ b/test/test-execute/exec-runtimedirectory.service
@@ -3,9 +3,9 @@
Description=Test for RuntimeDirectory
[Service]
-ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectory'
-ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectory2/hogehoge'
-ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory:%t/test-exec_runtimedirectory2/hogehoge"'
+ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectory'
+ExecStart=sh -x -c 'test -d %t/test-exec_runtimedirectory2/hogehoge'
+ExecStart=sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory:%t/test-exec_runtimedirectory2/hogehoge"'
Type=oneshot
RuntimeDirectory=test-exec_runtimedirectory
RuntimeDirectory=./test-exec_runtimedirectory2///./hogehoge/.
diff --git a/test/test-execute/exec-set-credential.service b/test/test-execute/exec-set-credential.service
index 9db6c5f..2263436 100644
--- a/test/test-execute/exec-set-credential.service
+++ b/test/test-execute/exec-set-credential.service
@@ -3,9 +3,9 @@
Description=Test for SetCredential=
[Service]
-ExecStart=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
-ExecStartPost=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
-ExecStop=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
-ExecStopPost=/bin/sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
+ExecStart=sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
+ExecStartPost=sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
+ExecStop=sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
+ExecStopPost=sh -x -c 'test "$$(cat %d/test-execute.set-credential)" = "hoge"'
Type=oneshot
SetCredential=test-execute.set-credential:hoge
diff --git a/test/test-execute/exec-specifier-interpolation.service b/test/test-execute/exec-specifier-interpolation.service
index 2e8882c..aa0ecdf 100644
--- a/test/test-execute/exec-specifier-interpolation.service
+++ b/test/test-execute/exec-specifier-interpolation.service
@@ -4,4 +4,4 @@ Description=https://github.com/systemd/systemd/issues/2637
[Service]
Type=oneshot
-ExecStart=/bin/bash -x -c "[[ %%U == ?U ]]"
+ExecStart=bash -x -c "[[ %%U == ?U ]]"
diff --git a/test/test-execute/exec-standardinput-data.service b/test/test-execute/exec-standardinput-data.service
index 838fea7..fd56f7e 100644
--- a/test/test-execute/exec-standardinput-data.service
+++ b/test/test-execute/exec-standardinput-data.service
@@ -3,7 +3,7 @@
Description=Test for StandardInputText= and StandardInputData=
[Service]
-ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); echo -e "this is a test\nand this is more\nsomething encoded!\nsomething in multiple lines\nand some more\nand a more bas64 data\nsomething with strange\nembedded\tcharacters\nand something with a exec-stdin-data.service specifier" >$d/text ; cmp $d/text ; rm -rf $d'
+ExecStart=sh -x -c 'd=$$(mktemp -d -p /tmp); echo -e "this is a test\nand this is more\nsomething encoded!\nsomething in multiple lines\nand some more\nand a more bas64 data\nsomething with strange\nembedded\tcharacters\nand something with a exec-stdin-data.service specifier" >$d/text ; cmp $d/text ; rm -rf $d'
Type=oneshot
StandardInput=data
StandardInputText=this is a test
diff --git a/test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service b/test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service
index 0ecc344..3c90124 100644
--- a/test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service
+++ b/test/test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service
@@ -3,9 +3,9 @@
Description=Test for Supplementary Group with multiple groups without Group and User
[Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "%G" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "%G" && test "$$(id -u)" = "%U"'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "%G" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'test "$$(id -g)" = "%G" && test "$$(id -u)" = "%U"'
Type=oneshot
SupplementaryGroups=1 2
diff --git a/test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service b/test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service
index cd1021b..0fd1c62 100644
--- a/test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service
+++ b/test/test-execute/exec-supplementarygroups-multiple-groups-withgid.service
@@ -3,9 +3,9 @@
Description=Test for Supplementary Group with multiple groups and Group=1
[Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "%U"'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "%U"'
Type=oneshot
Group=1
SupplementaryGroups=1 2
diff --git a/test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service b/test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service
index 7913a2c..c430e54 100644
--- a/test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service
+++ b/test/test-execute/exec-supplementarygroups-multiple-groups-withuid.service
@@ -3,8 +3,8 @@
Description=Test for Supplementary Group with multiple groups and Uid=1
[Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "2" && exit 0; done; exit 1'
Type=oneshot
User=1
SupplementaryGroups=1 2
diff --git a/test/test-execute/exec-supplementarygroups-single-group-user.service b/test/test-execute/exec-supplementarygroups-single-group-user.service
index ee4017e..20a3561 100644
--- a/test/test-execute/exec-supplementarygroups-single-group-user.service
+++ b/test/test-execute/exec-supplementarygroups-single-group-user.service
@@ -3,8 +3,8 @@
Description=Test for Supplementary Group with only one group and uid 1
[Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"'
Type=oneshot
User=1
Group=1
diff --git a/test/test-execute/exec-supplementarygroups-single-group.service b/test/test-execute/exec-supplementarygroups-single-group.service
index 6227520..8c81257 100644
--- a/test/test-execute/exec-supplementarygroups-single-group.service
+++ b/test/test-execute/exec-supplementarygroups-single-group.service
@@ -3,8 +3,8 @@
Description=Test for Supplementary Group with only one group
[Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "0"'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "0"'
Type=oneshot
Group=1
SupplementaryGroups=1
diff --git a/test/test-execute/exec-supplementarygroups.service b/test/test-execute/exec-supplementarygroups.service
index 03406c3..0a3d370 100644
--- a/test/test-execute/exec-supplementarygroups.service
+++ b/test/test-execute/exec-supplementarygroups.service
@@ -3,7 +3,7 @@
Description=Test for Supplementary Group
[Service]
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "%G" && exit 0; done; exit 1'
-ExecStart=/bin/sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "%G" && exit 0; done; exit 1'
+ExecStart=sh -x -c 'for g in $$(id -G); do test "$$g" = "1" && exit 0; done; exit 1'
Type=oneshot
SupplementaryGroups=1
diff --git a/test/test-execute/exec-systemcallerrornumber-name.service b/test/test-execute/exec-systemcallerrornumber-name.service
index f2be600..00a4508 100644
--- a/test/test-execute/exec-systemcallerrornumber-name.service
+++ b/test/test-execute/exec-systemcallerrornumber-name.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallErrorNumber
[Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
Type=oneshot
SystemCallFilter=~uname
SystemCallErrorNumber=EACCES
diff --git a/test/test-execute/exec-systemcallerrornumber-number.service b/test/test-execute/exec-systemcallerrornumber-number.service
index 5d99a97..3b5fb6e 100644
--- a/test/test-execute/exec-systemcallerrornumber-number.service
+++ b/test/test-execute/exec-systemcallerrornumber-number.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallErrorNumber
[Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
Type=oneshot
SystemCallFilter=~uname
SystemCallErrorNumber=255
diff --git a/test/test-execute/exec-systemcallfilter-failing.service b/test/test-execute/exec-systemcallfilter-failing.service
index 3aad372..7437d30 100644
--- a/test/test-execute/exec-systemcallfilter-failing.service
+++ b/test/test-execute/exec-systemcallfilter-failing.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallFilter
[Service]
-ExecStart=/bin/sh -c '/bin/echo "This should not be seen"'
+ExecStart=sh -c '/bin/echo "This should not be seen"'
Type=oneshot
LimitCORE=0
SystemCallFilter=ioperm
diff --git a/test/test-execute/exec-systemcallfilter-failing2.service b/test/test-execute/exec-systemcallfilter-failing2.service
index 8cdb8de..92672d1 100644
--- a/test/test-execute/exec-systemcallfilter-failing2.service
+++ b/test/test-execute/exec-systemcallfilter-failing2.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallFilter
[Service]
-ExecStart=/bin/sh -c '/bin/echo "This should not be seen"'
+ExecStart=sh -c '/bin/echo "This should not be seen"'
Type=oneshot
LimitCORE=0
SystemCallFilter=~write open execve fexecve execveat exit_group close mmap munmap fstat DONOTEXIST
diff --git a/test/test-execute/exec-systemcallfilter-failing3.service b/test/test-execute/exec-systemcallfilter-failing3.service
index 98c88fd..4e7b812 100644
--- a/test/test-execute/exec-systemcallfilter-failing3.service
+++ b/test/test-execute/exec-systemcallfilter-failing3.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallFilter
[Service]
-ExecStart=/bin/sh -c '/bin/echo "This should not be seen"'
+ExecStart=sh -c '/bin/echo "This should not be seen"'
Type=oneshot
LimitCORE=0
SystemCallArchitectures=native
diff --git a/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding1.service b/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding1.service
index 8f8192c..eaa75df 100644
--- a/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding1.service
+++ b/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding1.service
@@ -3,7 +3,7 @@
Description=Test bounding set is right with SystemCallFilter and non-root user
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_net_bind_service"'
+ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_net_bind_service"'
Type=oneshot
User=1
SystemCallFilter=@system-service
diff --git a/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding2.service b/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding2.service
index d78c323..fd0e3a2 100644
--- a/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding2.service
+++ b/test/test-execute/exec-systemcallfilter-nonewprivileges-bounding2.service
@@ -3,7 +3,7 @@
Description=Test bounding set is right with SystemCallFilter and non-root user
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_setpcap,cap_net_bind_service,cap_sys_admin"'
+ExecStart=sh -x -c 'c=$$(capsh --print | grep "Bounding set "); test "$$c" = "Bounding set =cap_setpcap,cap_net_bind_service,cap_sys_admin"'
Type=oneshot
User=1
SystemCallFilter=@system-service
diff --git a/test/test-execute/exec-systemcallfilter-nonewprivileges-protectclock.service b/test/test-execute/exec-systemcallfilter-nonewprivileges-protectclock.service
index f33a2a0..76b028c 100644
--- a/test/test-execute/exec-systemcallfilter-nonewprivileges-protectclock.service
+++ b/test/test-execute/exec-systemcallfilter-nonewprivileges-protectclock.service
@@ -3,7 +3,7 @@
Description=Test no_new_privs is unset for ProtectClock and non-root user
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(cat /proc/self/status | grep "NoNewPrivs: "); test "$$c" = "NoNewPrivs: 0"'
+ExecStart=sh -x -c 'c=$$(cat /proc/self/status | grep "NoNewPrivs: "); test "$$c" = "NoNewPrivs: 0"'
Type=oneshot
User=1
ProtectClock=yes
diff --git a/test/test-execute/exec-systemcallfilter-nonewprivileges.service b/test/test-execute/exec-systemcallfilter-nonewprivileges.service
index 8bfd0a7..2091b71 100644
--- a/test/test-execute/exec-systemcallfilter-nonewprivileges.service
+++ b/test/test-execute/exec-systemcallfilter-nonewprivileges.service
@@ -3,7 +3,7 @@
Description=Test no_new_privs is unset for SystemCallFilter and non-root user
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(cat /proc/self/status | grep "NoNewPrivs: "); test "$$c" = "NoNewPrivs: 0"'
+ExecStart=sh -x -c 'c=$$(cat /proc/self/status | grep "NoNewPrivs: "); test "$$c" = "NoNewPrivs: 0"'
Type=oneshot
User=1
SystemCallFilter=@system-service
diff --git a/test/test-execute/exec-systemcallfilter-not-failing.service b/test/test-execute/exec-systemcallfilter-not-failing.service
index c7eddea..bb2ea55 100644
--- a/test/test-execute/exec-systemcallfilter-not-failing.service
+++ b/test/test-execute/exec-systemcallfilter-not-failing.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallFilter
[Service]
-ExecStart=/bin/sh -c 'echo "Foo bar"'
+ExecStart=sh -c 'echo "Foo bar"'
Type=oneshot
SystemCallFilter=~read write open execve ioperm
SystemCallFilter=ioctl
diff --git a/test/test-execute/exec-systemcallfilter-not-failing2.service b/test/test-execute/exec-systemcallfilter-not-failing2.service
index 96eaf16..d9f0a37 100644
--- a/test/test-execute/exec-systemcallfilter-not-failing2.service
+++ b/test/test-execute/exec-systemcallfilter-not-failing2.service
@@ -3,6 +3,6 @@
Description=Test for SystemCallFilter
[Service]
-ExecStart=/bin/sh -c 'echo "Foo bar"'
+ExecStart=sh -c 'echo "Foo bar"'
Type=oneshot
SystemCallFilter=
diff --git a/test/test-execute/exec-systemcallfilter-not-failing3.service b/test/test-execute/exec-systemcallfilter-not-failing3.service
index f8f4092..df4e662 100644
--- a/test/test-execute/exec-systemcallfilter-not-failing3.service
+++ b/test/test-execute/exec-systemcallfilter-not-failing3.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallFilter
[Service]
-ExecStart=/bin/sh -c 'echo "Foo bar"'
+ExecStart=sh -c 'echo "Foo bar"'
Type=oneshot
SystemCallArchitectures=native
SystemCallFilter=
diff --git a/test/test-execute/exec-systemcallfilter-override-error-action.service b/test/test-execute/exec-systemcallfilter-override-error-action.service
index de2c6ad..6107d11 100644
--- a/test/test-execute/exec-systemcallfilter-override-error-action.service
+++ b/test/test-execute/exec-systemcallfilter-override-error-action.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallFilter with specific kill action overriding default errno action
[Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
Type=oneshot
SystemCallFilter=~uname:kill
SystemCallErrorNumber=EILSEQ
diff --git a/test/test-execute/exec-systemcallfilter-override-error-action2.service b/test/test-execute/exec-systemcallfilter-override-error-action2.service
index ffa35e6..e049275 100644
--- a/test/test-execute/exec-systemcallfilter-override-error-action2.service
+++ b/test/test-execute/exec-systemcallfilter-override-error-action2.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallFilter with specific errno action overriding default kill action
[Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
Type=oneshot
SystemCallFilter=~uname:EILSEQ
SystemCallErrorNumber=kill
diff --git a/test/test-execute/exec-systemcallfilter-system-user-nfsnobody.service b/test/test-execute/exec-systemcallfilter-system-user-nfsnobody.service
index deba154..1912286 100644
--- a/test/test-execute/exec-systemcallfilter-system-user-nfsnobody.service
+++ b/test/test-execute/exec-systemcallfilter-system-user-nfsnobody.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallFilter in system mode with User set
[Service]
-ExecStart=/bin/sh -c 'echo "Foo bar"'
+ExecStart=sh -c 'echo "Foo bar"'
Type=oneshot
User=nfsnobody
SystemCallFilter=~read write open execve ioperm
diff --git a/test/test-execute/exec-systemcallfilter-system-user-nobody.service b/test/test-execute/exec-systemcallfilter-system-user-nobody.service
index 43fb9c3..0c2ebdd 100644
--- a/test/test-execute/exec-systemcallfilter-system-user-nobody.service
+++ b/test/test-execute/exec-systemcallfilter-system-user-nobody.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallFilter in system mode with User set
[Service]
-ExecStart=/bin/sh -c 'echo "Foo bar"'
+ExecStart=sh -c 'echo "Foo bar"'
Type=oneshot
User=nobody
SystemCallFilter=~read write open execve ioperm
diff --git a/test/test-execute/exec-systemcallfilter-system-user.service b/test/test-execute/exec-systemcallfilter-system-user.service
index 005c4ac..6de3964 100644
--- a/test/test-execute/exec-systemcallfilter-system-user.service
+++ b/test/test-execute/exec-systemcallfilter-system-user.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallFilter in system mode with User set (daemon)
[Service]
-ExecStart=/bin/sh -c 'echo "Foo bar"'
+ExecStart=sh -c 'echo "Foo bar"'
Type=oneshot
User=daemon
SystemCallFilter=~read write open execve ioperm
diff --git a/test/test-execute/exec-systemcallfilter-with-errno-in-allow-list.service b/test/test-execute/exec-systemcallfilter-with-errno-in-allow-list.service
index c7a4c4a..a8dc10f 100644
--- a/test/test-execute/exec-systemcallfilter-with-errno-in-allow-list.service
+++ b/test/test-execute/exec-systemcallfilter-with-errno-in-allow-list.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallFilter with errno name (for issue #18916)
[Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
Type=oneshot
SystemCallFilter=@system-service
SystemCallFilter=~uname:EILSEQ
diff --git a/test/test-execute/exec-systemcallfilter-with-errno-multi.service b/test/test-execute/exec-systemcallfilter-with-errno-multi.service
index 2678323..224df01 100644
--- a/test/test-execute/exec-systemcallfilter-with-errno-multi.service
+++ b/test/test-execute/exec-systemcallfilter-with-errno-multi.service
@@ -4,7 +4,7 @@ Description=Test for SystemCallFilter updating errno
# test for issue #9939 which is fixed by a5404992cc7724ebf7572a0aa89d9fdb26ce0b62 (#9942)
[Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
Type=oneshot
SystemCallFilter=~uname:ENOENT uname:EILSEQ
SystemCallErrorNumber=EACCES
diff --git a/test/test-execute/exec-systemcallfilter-with-errno-name.service b/test/test-execute/exec-systemcallfilter-with-errno-name.service
index a902331..bed7961 100644
--- a/test/test-execute/exec-systemcallfilter-with-errno-name.service
+++ b/test/test-execute/exec-systemcallfilter-with-errno-name.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallFilter with errno name
[Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
Type=oneshot
SystemCallFilter=~uname:EILSEQ
SystemCallErrorNumber=EACCES
diff --git a/test/test-execute/exec-systemcallfilter-with-errno-number.service b/test/test-execute/exec-systemcallfilter-with-errno-number.service
index ffbc84a..8db2281 100644
--- a/test/test-execute/exec-systemcallfilter-with-errno-number.service
+++ b/test/test-execute/exec-systemcallfilter-with-errno-number.service
@@ -3,7 +3,7 @@
Description=Test for SystemCallFilter with errno number
[Service]
-ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
+ExecStart=python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
Type=oneshot
SystemCallFilter=~uname:255
SystemCallErrorNumber=EACCES
diff --git a/test/test-execute/exec-temporaryfilesystem-options.service b/test/test-execute/exec-temporaryfilesystem-options.service
index 1610c63..b000301 100644
--- a/test/test-execute/exec-temporaryfilesystem-options.service
+++ b/test/test-execute/exec-temporaryfilesystem-options.service
@@ -10,8 +10,8 @@ Type=oneshot
TemporaryFileSystem=/var:ro,mode=0700,nostrictatime
# Check /proc/self/mountinfo
-ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$11 !~ /(^|,)mode=700(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
+ExecStart=sh -x -c 'test "$$(awk \'$$5 == "/var" && $$11 !~ /(^|,)mode=700(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
-ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 !~ /(^|,)ro(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
-ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 !~ /(^|,)nodev(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
-ExecStart=/bin/sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 ~ /(^|,)strictatime(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
+ExecStart=sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 !~ /(^|,)ro(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
+ExecStart=sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 !~ /(^|,)nodev(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
+ExecStart=sh -x -c 'test "$$(awk \'$$5 == "/var" && $$6 ~ /(^|,)strictatime(,|$$)/ { print $$6 }\' /proc/self/mountinfo)" = ""'
diff --git a/test/test-execute/exec-temporaryfilesystem-ro.service b/test/test-execute/exec-temporaryfilesystem-ro.service
index 2ee5c26..0a4b0f2 100644
--- a/test/test-execute/exec-temporaryfilesystem-ro.service
+++ b/test/test-execute/exec-temporaryfilesystem-ro.service
@@ -6,31 +6,31 @@ Description=Test for TemporaryFileSystem with read-only mode
Type=oneshot
# Check directories exist
-ExecStart=/bin/sh -c 'test -d /var/test-exec-temporaryfilesystem/rw && test -d /var/test-exec-temporaryfilesystem/ro'
+ExecStart=sh -c 'test -d /var/test-exec-temporaryfilesystem/rw && test -d /var/test-exec-temporaryfilesystem/ro'
# Check TemporaryFileSystem= are empty
-ExecStart=/bin/sh -c 'for i in $$(ls -A /var); do test $$i = test-exec-temporaryfilesystem || false; done'
+ExecStart=sh -c 'for i in $$(ls -A /var); do test $$i = test-exec-temporaryfilesystem || false; done'
# Check default mode
ExecStart=sh -x -c 'test "$$(stat -c %%a /var)" = "755"'
# Cannot create a file in /var
-ExecStart=/bin/sh -c '! touch /var/hoge'
+ExecStart=sh -c '! touch /var/hoge'
# Create a file in /var/test-exec-temporaryfilesystem/rw
-ExecStart=/bin/sh -c 'touch /var/test-exec-temporaryfilesystem/rw/thisisasimpletest-temporaryfilesystem'
+ExecStart=sh -c 'touch /var/test-exec-temporaryfilesystem/rw/thisisasimpletest-temporaryfilesystem'
# Then, the file can be access through /tmp
-ExecStart=/bin/sh -c 'test -f /tmp/thisisasimpletest-temporaryfilesystem'
+ExecStart=sh -c 'test -f /tmp/thisisasimpletest-temporaryfilesystem'
# Also, through /var/test-exec-temporaryfilesystem/ro
-ExecStart=/bin/sh -c 'test -f /var/test-exec-temporaryfilesystem/ro/thisisasimpletest-temporaryfilesystem'
+ExecStart=sh -c 'test -f /var/test-exec-temporaryfilesystem/ro/thisisasimpletest-temporaryfilesystem'
# The file cannot modify through /var/test-exec-temporaryfilesystem/ro
-ExecStart=/bin/sh -c '! touch /var/test-exec-temporaryfilesystem/ro/thisisasimpletest-temporaryfilesystem'
+ExecStart=sh -c '! touch /var/test-exec-temporaryfilesystem/ro/thisisasimpletest-temporaryfilesystem'
# Cleanup
-ExecStart=/bin/sh -c 'rm /tmp/thisisasimpletest-temporaryfilesystem'
+ExecStart=sh -c 'rm /tmp/thisisasimpletest-temporaryfilesystem'
TemporaryFileSystem=/var:ro
BindPaths=/tmp:/var/test-exec-temporaryfilesystem/rw
diff --git a/test/test-execute/exec-temporaryfilesystem-usr.service b/test/test-execute/exec-temporaryfilesystem-usr.service
index f62ce1a..455344e 100644
--- a/test/test-execute/exec-temporaryfilesystem-usr.service
+++ b/test/test-execute/exec-temporaryfilesystem-usr.service
@@ -6,11 +6,11 @@ Description=Test for TemporaryFileSystem on /usr
Type=oneshot
# Check TemporaryFileSystem= are empty
-ExecStart=/bin/sh -c 'for i in $$(ls -A /usr); do test $$i = lib -o $$i = lib64 -o $$i = bin -o $$i = sbin || false; done'
+ExecStart=sh -c 'for i in $$(ls -A /usr); do test $$i = lib -o $$i = lib64 -o $$i = bin -o $$i = sbin || false; done'
# Cannot create files under /usr
-ExecStart=/bin/sh -c '! touch /usr/hoge'
-ExecStart=/bin/sh -c '! touch /usr/bin/hoge'
+ExecStart=sh -c '! touch /usr/hoge'
+ExecStart=sh -c '! touch /usr/bin/hoge'
TemporaryFileSystem=/usr:ro
BindReadOnlyPaths=-/usr/lib -/usr/lib64 /usr/bin /usr/sbin
diff --git a/test/test-execute/exec-umask-0177.service b/test/test-execute/exec-umask-0177.service
index 380cb82..de9ac5a 100644
--- a/test/test-execute/exec-umask-0177.service
+++ b/test/test-execute/exec-umask-0177.service
@@ -3,7 +3,7 @@
Description=Test for UMask
[Service]
-ExecStart=/bin/sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "600"'
+ExecStart=sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "600"'
Type=oneshot
UMask=0177
PrivateTmp=yes
diff --git a/test/test-execute/exec-umask-default.service b/test/test-execute/exec-umask-default.service
index b28023d..6d13c0b 100644
--- a/test/test-execute/exec-umask-default.service
+++ b/test/test-execute/exec-umask-default.service
@@ -3,6 +3,6 @@
Description=Test for UMask default
[Service]
-ExecStart=/bin/sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "644"'
+ExecStart=sh -x -c 'rm /tmp/test-exec-umask; touch /tmp/test-exec-umask; mode=$$(stat -c %%a /tmp/test-exec-umask); test "$$mode" = "644"'
Type=oneshot
PrivateTmp=yes
diff --git a/test/test-execute/exec-umask-namespace.service b/test/test-execute/exec-umask-namespace.service
index 8419c86..aac1dad 100644
--- a/test/test-execute/exec-umask-namespace.service
+++ b/test/test-execute/exec-umask-namespace.service
@@ -3,7 +3,7 @@
Description=Test for UMask= + namespacing
[Service]
-ExecStart=/bin/ls -lahd /tmp/subdir
+ExecStart=ls -lahd /tmp/subdir
Type=oneshot
User=65534
Group=65534
diff --git a/test/test-execute/exec-unsetenvironment.service b/test/test-execute/exec-unsetenvironment.service
index b79e3d4..9c5e277 100644
--- a/test/test-execute/exec-unsetenvironment.service
+++ b/test/test-execute/exec-unsetenvironment.service
@@ -3,7 +3,7 @@
Description=Test for UnsetEnvironment
[Service]
-ExecStart=/bin/sh -x -c 'test "$$FOO" = "bar" && test "$${QUUX-X}" = "X" && test "$$VAR3" = "value3" && test "$${VAR4-X}" = "X" && test "$$VAR5" = "value5" && test "$${X%b-X}" = "X"'
+ExecStart=sh -x -c 'test "$$FOO" = "bar" && test "$${QUUX-X}" = "X" && test "$$VAR3" = "value3" && test "$${VAR4-X}" = "X" && test "$$VAR5" = "value5" && test "$${X%b-X}" = "X"'
Type=oneshot
Environment=FOO=bar QUUX=waldo VAR3=value3 VAR4=value4 VAR5=value5 X%b=%U
UnsetEnvironment=QUUX=waldo VAR3=somethingelse VAR4 X%b=%U
diff --git a/test/test-execute/exec-user-nfsnobody.service b/test/test-execute/exec-user-nfsnobody.service
index 8f0943c..1ce5f08 100644
--- a/test/test-execute/exec-user-nfsnobody.service
+++ b/test/test-execute/exec-user-nfsnobody.service
@@ -3,6 +3,6 @@
Description=Test for User
[Service]
-ExecStart=/bin/sh -x -c 'test "$$USER" = "nfsnobody"'
+ExecStart=sh -x -c 'test "$$USER" = "nfsnobody"'
Type=oneshot
User=nfsnobody
diff --git a/test/test-execute/exec-user-nobody.service b/test/test-execute/exec-user-nobody.service
index 834d11a..003b873 100644
--- a/test/test-execute/exec-user-nobody.service
+++ b/test/test-execute/exec-user-nobody.service
@@ -3,6 +3,6 @@
Description=Test for User
[Service]
-ExecStart=/bin/sh -x -c 'test "$$USER" = "nobody"'
+ExecStart=sh -x -c 'test "$$USER" = "nobody"'
Type=oneshot
User=nobody
diff --git a/test/test-execute/exec-user.service b/test/test-execute/exec-user.service
index b9863d2..696c7e5 100644
--- a/test/test-execute/exec-user.service
+++ b/test/test-execute/exec-user.service
@@ -3,6 +3,6 @@
Description=Test for User (daemon)
[Service]
-ExecStart=/bin/sh -x -c 'test "$$USER" = "daemon"'
+ExecStart=sh -x -c 'test "$$USER" = "daemon"'
Type=oneshot
User=daemon
diff --git a/test/test-execute/exec-workingdirectory-trailing-dot.service b/test/test-execute/exec-workingdirectory-trailing-dot.service
index 130d9d5..3c4869d 100644
--- a/test/test-execute/exec-workingdirectory-trailing-dot.service
+++ b/test/test-execute/exec-workingdirectory-trailing-dot.service
@@ -3,6 +3,6 @@
Description=Test for WorkingDirectory with trailing dot
[Service]
-ExecStart=/bin/sh -x -c 'test "$$PWD" = "/tmp/test-exec_workingdirectory"'
+ExecStart=sh -x -c 'test "$$PWD" = "/tmp/test-exec_workingdirectory"'
Type=oneshot
WorkingDirectory=/tmp///./test-exec_workingdirectory/.
diff --git a/test/test-execute/exec-workingdirectory.service b/test/test-execute/exec-workingdirectory.service
index b53bf60..4c40faf 100644
--- a/test/test-execute/exec-workingdirectory.service
+++ b/test/test-execute/exec-workingdirectory.service
@@ -3,6 +3,6 @@
Description=Test for WorkingDirectory
[Service]
-ExecStart=/bin/sh -x -c 'test "$$PWD" = "/tmp/test-exec_workingdirectory"'
+ExecStart=sh -x -c 'test "$$PWD" = "/tmp/test-exec_workingdirectory"'
Type=oneshot
WorkingDirectory=/tmp/test-exec_workingdirectory
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount
new file mode 120000
index 0000000..14fd395
--- /dev/null
+++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/foo.service.wants/mnt-wantedby-automount.automount
@@ -0,0 +1 @@
+../mnt-wantedby-automount.automount \ No newline at end of file
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requiredby.mount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requiredby.mount
index 5edc4dd..96fd672 100644
--- a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requiredby.mount
+++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-requiredby.mount
@@ -3,7 +3,9 @@
[Unit]
Documentation=man:fstab(5) man:systemd-fstab-generator(8)
SourcePath=/etc/fstab
-Before=local-fs.target
+DefaultDependencies=no
+Conflicts=umount.target
+Before=umount.target
After=blockdev@dev-sdx8.target
[Mount]
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.automount
new file mode 100644
index 0000000..b9f2d28
--- /dev/null
+++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.automount
@@ -0,0 +1,8 @@
+# Automatically generated by systemd-fstab-generator
+
+[Unit]
+SourcePath=/etc/fstab
+Documentation=man:fstab(5) man:systemd-fstab-generator(8)
+
+[Automount]
+Where=/mnt/wantedby-automount
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.mount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.mount
new file mode 100644
index 0000000..d909476
--- /dev/null
+++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby-automount.mount
@@ -0,0 +1,14 @@
+# Automatically generated by systemd-fstab-generator
+
+[Unit]
+Documentation=man:fstab(5) man:systemd-fstab-generator(8)
+SourcePath=/etc/fstab
+DefaultDependencies=no
+Conflicts=umount.target
+Before=umount.target
+After=blockdev@dev-sdx17.target
+
+[Mount]
+What=/dev/sdx17
+Where=/mnt/wantedby
+Options=x-systemd.wanted-by=foo.service,x-systemd.automount
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby.mount b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby.mount
index e12df82..ff76a6f 100644
--- a/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby.mount
+++ b/test/test-fstab-generator/test-18-options.fstab.expected.sysroot/mnt-wantedby.mount
@@ -3,7 +3,9 @@
[Unit]
Documentation=man:fstab(5) man:systemd-fstab-generator(8)
SourcePath=/etc/fstab
-Before=local-fs.target
+DefaultDependencies=no
+Conflicts=umount.target
+Before=umount.target
After=blockdev@dev-sdx7.target
[Mount]
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount
new file mode 120000
index 0000000..14fd395
--- /dev/null
+++ b/test/test-fstab-generator/test-18-options.fstab.expected/foo.service.wants/mnt-wantedby-automount.automount
@@ -0,0 +1 @@
+../mnt-wantedby-automount.automount \ No newline at end of file
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-requiredby.mount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-requiredby.mount
index 5edc4dd..96fd672 100644
--- a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-requiredby.mount
+++ b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-requiredby.mount
@@ -3,7 +3,9 @@
[Unit]
Documentation=man:fstab(5) man:systemd-fstab-generator(8)
SourcePath=/etc/fstab
-Before=local-fs.target
+DefaultDependencies=no
+Conflicts=umount.target
+Before=umount.target
After=blockdev@dev-sdx8.target
[Mount]
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.automount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.automount
new file mode 100644
index 0000000..b9f2d28
--- /dev/null
+++ b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.automount
@@ -0,0 +1,8 @@
+# Automatically generated by systemd-fstab-generator
+
+[Unit]
+SourcePath=/etc/fstab
+Documentation=man:fstab(5) man:systemd-fstab-generator(8)
+
+[Automount]
+Where=/mnt/wantedby-automount
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.mount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.mount
new file mode 100644
index 0000000..d909476
--- /dev/null
+++ b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby-automount.mount
@@ -0,0 +1,14 @@
+# Automatically generated by systemd-fstab-generator
+
+[Unit]
+Documentation=man:fstab(5) man:systemd-fstab-generator(8)
+SourcePath=/etc/fstab
+DefaultDependencies=no
+Conflicts=umount.target
+Before=umount.target
+After=blockdev@dev-sdx17.target
+
+[Mount]
+What=/dev/sdx17
+Where=/mnt/wantedby
+Options=x-systemd.wanted-by=foo.service,x-systemd.automount
diff --git a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby.mount b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby.mount
index e12df82..ff76a6f 100644
--- a/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby.mount
+++ b/test/test-fstab-generator/test-18-options.fstab.expected/mnt-wantedby.mount
@@ -3,7 +3,9 @@
[Unit]
Documentation=man:fstab(5) man:systemd-fstab-generator(8)
SourcePath=/etc/fstab
-Before=local-fs.target
+DefaultDependencies=no
+Conflicts=umount.target
+Before=umount.target
After=blockdev@dev-sdx7.target
[Mount]
diff --git a/test/test-fstab-generator/test-18-options.fstab.input b/test/test-fstab-generator/test-18-options.fstab.input
index 98ef0b9..2112d68 100644
--- a/test/test-fstab-generator/test-18-options.fstab.input
+++ b/test/test-fstab-generator/test-18-options.fstab.input
@@ -1,16 +1,17 @@
-/dev/sdx1 /sysroot auto defaults 0 1
-/dev/sdx2 /mnt/timeout auto x-systemd.mount-timeout=10m 0 0
-/dev/sdx3 /mnt/after auto x-systemd.after=foo.service 0 0
-/dev/sdx4 /mnt/before auto x-systemd.before=foo.service 0 0
-/dev/sdx5 /mnt/requires auto x-systemd.requires=foo.service 0 0
-/dev/sdx6 /mnt/reqmounts auto x-systemd.requires-mounts-for=/hoge 0 0
-/dev/sdx7 /mnt/wantedby auto x-systemd.wanted-by=foo.service 0 0
-/dev/sdx8 /mnt/requiredby auto x-systemd.required-by=foo.service 0 0
-/dev/sdx9 /mnt/automount1 auto x-systemd.automount,x-systemd.idle-timeout=30m 0 0
-/dev/sdx10 /mnt/automount2 auto x-systemd.automount,nofail 0 0
-/dev/sdx11 /mnt/rwonly auto x-systemd.rw-only 0 0
-/dev/sdx12 /mnt/mkfs ext4 x-systemd.makefs 0 0
-/dev/sdx13 /mnt/growfs auto x-systemd.growfs 0 0
-/dev/sdx14 /mnt/pcrfs auto x-systemd.pcrfs 0 0
-/dev/sdx15 /mnt/noauto auto noauto 0 0
-/dev/sdx16 /mnt/nofail auto nofail 0 0
+/dev/sdx1 /sysroot auto defaults 0 1
+/dev/sdx2 /mnt/timeout auto x-systemd.mount-timeout=10m 0 0
+/dev/sdx3 /mnt/after auto x-systemd.after=foo.service 0 0
+/dev/sdx4 /mnt/before auto x-systemd.before=foo.service 0 0
+/dev/sdx5 /mnt/requires auto x-systemd.requires=foo.service 0 0
+/dev/sdx6 /mnt/reqmounts auto x-systemd.requires-mounts-for=/hoge 0 0
+/dev/sdx7 /mnt/wantedby auto x-systemd.wanted-by=foo.service 0 0
+/dev/sdx8 /mnt/requiredby auto x-systemd.required-by=foo.service 0 0
+/dev/sdx9 /mnt/automount1 auto x-systemd.automount,x-systemd.idle-timeout=30m 0 0
+/dev/sdx10 /mnt/automount2 auto x-systemd.automount,nofail 0 0
+/dev/sdx11 /mnt/rwonly auto x-systemd.rw-only 0 0
+/dev/sdx12 /mnt/mkfs ext4 x-systemd.makefs 0 0
+/dev/sdx13 /mnt/growfs auto x-systemd.growfs 0 0
+/dev/sdx14 /mnt/pcrfs auto x-systemd.pcrfs 0 0
+/dev/sdx15 /mnt/noauto auto noauto 0 0
+/dev/sdx16 /mnt/nofail auto nofail 0 0
+/dev/sdx17 /mnt/wantedby-automount auto x-systemd.wanted-by=foo.service,x-systemd.automount 0 0
diff --git a/test/test-functions b/test/test-functions
index f7376bf..be6eb1d 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -39,6 +39,8 @@ os_release=$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os
# shellcheck source=/dev/null
source "$os_release"
[[ "$ID" == "debian" || " $ID_LIKE " == *" debian "* ]] && LOOKS_LIKE_DEBIAN=yes || LOOKS_LIKE_DEBIAN=no
+# shellcheck disable=SC2034
+[[ "$ID" == "ubuntu" ]] && LOOKS_LIKE_UBUNTU=yes || LOOKS_LIKE_UBUNTU=no
[[ "$ID" == "arch" || " $ID_LIKE " == *" arch "* ]] && LOOKS_LIKE_ARCH=yes || LOOKS_LIKE_ARCH=no
[[ "$ID" == "fedora" ]] && LOOKS_LIKE_FEDORA=yes || LOOKS_LIKE_FEDORA=no
[[ " $ID_LIKE " == *" suse "* ]] && LOOKS_LIKE_SUSE=yes || LOOKS_LIKE_SUSE=no
@@ -120,6 +122,7 @@ SYSTEMD="${SYSTEMD:-$(command -v "$BUILD_DIR/systemd" || command -v "$ROOTLIBDIR
SYSTEMD_NSPAWN="${SYSTEMD_NSPAWN:-$(command -v "$BUILD_DIR/systemd-nspawn" || command -v systemd-nspawn)}"
JOURNALCTL="${JOURNALCTL:-$(command -v "$BUILD_DIR/journalctl" || command -v journalctl)}"
SYSTEMCTL="${SYSTEMCTL:-$(command -v "$BUILD_DIR/systemctl" || command -v systemctl)}"
+SYSTEMD_ID128="${SYSTEMD_ID128:-$(command -v "$BUILD_DIR/systemd-id128" || command -v systemd-id128)}"
TESTFILE="${BASH_SOURCE[1]}"
if [ -z "$TESTFILE" ]; then
@@ -143,12 +146,6 @@ 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
- exit 1
-fi
BASICTOOLS=(
awk
@@ -206,6 +203,7 @@ BASICTOOLS=(
mkfifo
mknod
mktemp
+ modinfo
modprobe
mount
mountpoint
@@ -221,7 +219,6 @@ BASICTOOLS=(
rm
rmdir
rmmod
- route
script
sed
seq
@@ -474,8 +471,7 @@ run_qemu() {
local CONSOLE=ttyS0
- # Reset the boot counter, if present
- rm -f "${initdir:?}/var/tmp/.systemd_reboot_count"
+ find "${initdir:?}/var/tmp" -mindepth 1 -delete
rm -f "$initdir"/{testok,failed,skipped}
# make sure the initdir is not mounted to avoid concurrent access
cleanup_initdir
@@ -565,9 +561,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"}
@@ -649,8 +645,7 @@ run_qemu() {
# success), or 1 if nspawn is not available.
run_nspawn() {
[[ -d /run/systemd/system ]] || return 1
- # Reset the boot counter, if present
- rm -f "${initdir:?}/var/tmp/.systemd_reboot_count"
+ find "${initdir:?}/var/tmp" -mindepth 1 -delete
rm -f "${initdir:?}"/{testok,failed,skipped}
local nspawn_cmd=()
@@ -658,13 +653,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"}
)
@@ -733,6 +728,7 @@ install_verity_minimal() {
BASICTOOLS=(
bash
cat
+ echo
grep
mount
sleep
@@ -780,110 +776,6 @@ EOF
mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_1.raw" -noappend
veritysetup format "$oldinitdir/usr/share/minimal_1.raw" "$oldinitdir/usr/share/minimal_1.verity" | \
grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_1.roothash"
-
- # Rolling distros like Arch do not set VERSION_ID
- local version_id=""
- if grep -q "^VERSION_ID=" "$os_release"; then
- version_id="$(grep "^VERSION_ID=" "$os_release")"
- fi
-
- export initdir="$TESTDIR/app0"
- mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
- grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app0"
- echo "${version_id}" >>"$initdir/usr/lib/extension-release.d/extension-release.app0"
- ( echo "${version_id}"
- echo "SYSEXT_IMAGE_ID=app" ) >>"$initdir/usr/lib/extension-release.d/extension-release.app0"
- cat >"$initdir/usr/lib/systemd/system/app0.service" <<EOF
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStart=/opt/script0.sh
-TemporaryFileSystem=/var/lib
-StateDirectory=app0
-RuntimeDirectory=app0
-EOF
- cat >"$initdir/opt/script0.sh" <<EOF
-#!/bin/bash
-set -e
-test -e /usr/lib/os-release
-echo bar >\${STATE_DIRECTORY}/foo
-cat /usr/lib/extension-release.d/extension-release.app0
-EOF
- chmod +x "$initdir/opt/script0.sh"
- echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file"
- mksquashfs "$initdir" "$oldinitdir/usr/share/app0.raw" -noappend
-
- export initdir="$TESTDIR/conf0"
- mkdir -p "$initdir/etc/extension-release.d" "$initdir/etc/systemd/system" "$initdir/opt"
- grep "^ID=" "$os_release" >"$initdir/etc/extension-release.d/extension-release.conf0"
- echo "${version_id}" >>"$initdir/etc/extension-release.d/extension-release.conf0"
- ( echo "${version_id}"
- echo "CONFEXT_IMAGE_ID=app" ) >>"$initdir/etc/extension-release.d/extension-release.conf0"
- echo MARKER_1 >"$initdir/etc/systemd/system/some_file"
- mksquashfs "$initdir" "$oldinitdir/usr/share/conf0.raw" -noappend
-
- export initdir="$TESTDIR/app1"
- mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
- grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app2"
- ( echo "${version_id}"
- echo "SYSEXT_SCOPE=portable"
- echo "SYSEXT_IMAGE_ID=app"
- echo "SYSEXT_IMAGE_VERSION=1"
- echo "PORTABLE_PREFIXES=app1" ) >>"$initdir/usr/lib/extension-release.d/extension-release.app2"
- setfattr -n user.extension-release.strict -v false "$initdir/usr/lib/extension-release.d/extension-release.app2"
- cat >"$initdir/usr/lib/systemd/system/app1.service" <<EOF
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStart=/opt/script1.sh
-StateDirectory=app1
-RuntimeDirectory=app1
-EOF
- cat >"$initdir/opt/script1.sh" <<EOF
-#!/bin/bash
-set -e
-test -e /usr/lib/os-release
-echo baz >\${STATE_DIRECTORY}/foo
-cat /usr/lib/extension-release.d/extension-release.app2
-EOF
- chmod +x "$initdir/opt/script1.sh"
- echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file"
- mksquashfs "$initdir" "$oldinitdir/usr/share/app1.raw" -noappend
-
- export initdir="$TESTDIR/app-nodistro"
- mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system"
- ( echo "ID=_any"
- echo "ARCHITECTURE=_any" ) >"$initdir/usr/lib/extension-release.d/extension-release.app-nodistro"
- echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file"
- mksquashfs "$initdir" "$oldinitdir/usr/share/app-nodistro.raw" -noappend
-
- export initdir="$TESTDIR/service-scoped-test"
- mkdir -p "$initdir/etc/extension-release.d" "$initdir/etc/systemd/system"
- ( echo "ID=_any"
- echo "ARCHITECTURE=_any" ) >"$initdir/etc/extension-release.d/extension-release.service-scoped-test"
- echo MARKER_CONFEXT_123 >"$initdir/etc/systemd/system/some_file"
- mksquashfs "$initdir" "$oldinitdir/etc/service-scoped-test.raw" -noappend
-
- # We need to create a dedicated sysext image to test the reload mechanism. If we share an image to install the
- # 'foo.service' it will be loaded from another test run, which will impact the targeted test.
- export initdir="$TESTDIR/app-reload"
- mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system"
- ( echo "ID=_any"
- echo "ARCHITECTURE=_any"
- echo "EXTENSION_RELOAD_MANAGER=1" ) >"$initdir/usr/lib/extension-release.d/extension-release.app-reload"
- mkdir -p "$initdir/usr/lib/systemd/system/multi-user.target.d"
- cat >"${initdir}/usr/lib/systemd/system/foo.service" <<EOF
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-SyslogIdentifier=sysext-foo
-ExecStart=echo foo
-
-[Install]
-WantedBy=multi-user.target
-EOF
- { echo "[Unit]"; echo "Upholds=foo.service"; } > "$initdir/usr/lib/systemd/system/multi-user.target.d/10-foo-service.conf"
- mksquashfs "$initdir" "$oldinitdir/usr/share/app-reload.raw" -noappend
)
}
@@ -997,7 +889,7 @@ create_asan_wrapper() {
[[ -z "$ASAN_RT_PATH" ]] && dfatal "ASAN_RT_PATH is empty, but it shouldn't be"
- default_asan_options="${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1}"
+ default_asan_options="${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:disable_coredump=0:use_madv_dontdump=1}"
default_ubsan_options="${UBSAN_OPTIONS:-print_stacktrace=1:print_summary=1:halt_on_error=1}"
if [[ "$ASAN_COMPILER" == "clang" ]]; then
@@ -1115,6 +1007,9 @@ install_fs_tools() {
# we use mkfs in system-repart tests
image_install /sbin/mkfs.ext4
image_install /sbin/mkfs.vfat
+
+ # we use mkswap in udev-storage tests
+ image_install /sbin/mkswap
}
install_modules() {
@@ -1153,9 +1048,9 @@ install_multipath() {
image_install kpartx /lib/udev/kpartx_id lsmod mpathpersist multipath multipathd partx
image_install "${ROOTLIBDIR:?}"/system/multipathd.{service,socket}
if get_bool "$LOOKS_LIKE_DEBIAN"; then
- # Note: try both 60-kpartx.rules (as seen on Debian Sid with 0.9.4-7) and 90-kpartx.rules (as seen on
+ # Note: try both 60-kpartx.rules (as seen on Debian Sid with 0.9.4-7) and 95-kpartx.rules (as seen on
# Ubuntu Jammy with 0.8.8-1ubuntu1.22.04.4)
- inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-kpartx.rules 60-multipath.rules 68-del-part-nodes.rules 90-kpartx.rules
+ inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-kpartx.rules 60-multipath.rules 68-del-part-nodes.rules 95-kpartx.rules
else
inst_rules 11-dm-mpath.rules 11-dm-parts.rules 62-multipath.rules 66-kpartx.rules 68-del-part-nodes.rules
fi
@@ -1226,7 +1121,8 @@ install_iscsi() {
image_install iscsi-iname iscsiadm iscsid iscsistart
image_install -o "${ROOTLIBDIR:?}"/system/iscsi-{init,onboot,shutdown}.service
image_install "${ROOTLIBDIR:?}"/system/iscsid.{service,socket}
- image_install "${ROOTLIBDIR:?}"/system/iscsi.service
+ image_install -o "${ROOTLIBDIR:?}"/system/iscsi.service
+ image_install -o /usr/lib/open-iscsi/startup-checks.sh
mkdir -p "${initdir:?}"/var/lib/iscsi/{ifaces,isns,nodes,send_targets,slp,static}
mkdir -p "${initdir:?}/etc/iscsi"
echo "iscsid.startup = /bin/systemctl start iscsid.socket" >"${initdir:?}/etc/iscsi/iscsid.conf"
@@ -1256,7 +1152,11 @@ install_iscsi() {
if [[ -z "$inst" || "$inst" =~ (server|target) ]]; then
image_install tgt-admin tgt-setup-lun tgtadm tgtd tgtimg
image_install -o /etc/sysconfig/tgtd
- image_install "${ROOTLIBDIR:?}"/system/tgtd.service
+ if get_bool "$LOOKS_LIKE_DEBIAN"; then
+ image_install "${ROOTLIBDIR:?}"/system/tgt.service
+ else
+ image_install "${ROOTLIBDIR:?}"/system/tgtd.service
+ fi
mkdir -p "${initdir:?}/etc/tgt"
touch "${initdir:?}"/etc/tgt/{tgtd,targets}.conf
# Install perl modules required by tgt-admin
@@ -1495,7 +1395,7 @@ install_systemd() {
# units using DynamicUser=yes. Do this only for services with test- prefix and a couple of
# known-to-use DynamicUser=yes services, as setting this system-wide has many undesirable
# side-effects, as it creates its own namespace.
- for service in test- systemd-journal-{gatewayd,upload}; do
+ for service in capsule@ test- systemd-journal-{gatewayd,upload}; do
mkdir -p "$initdir/etc/systemd/system/$service.service.d/"
echo -ne "[Service]\nReadWritePaths=${BUILD_DIR:?}\n" >"$initdir/etc/systemd/system/$service.service.d/99-gcov-rwpaths-override.conf"
done
@@ -1556,7 +1456,7 @@ install_missing_libraries() {
local lib path
# A number of dependencies is now optional via dlopen, so the install
# script will not pick them up, since it looks at linkage.
- for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1; do
+ for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1 libarchive libgcrypt libkmod; do
ddebug "Searching for $lib via pkg-config"
if pkg-config --exists "$lib"; then
path="$(pkg-config --variable=libdir "$lib")"
@@ -1659,7 +1559,7 @@ create_empty_image() {
sfdisk "$LOOPDEV" <<EOF
label: gpt
type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp size=${esp_size}M
-type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=${root_size}M bootable
+type=$("${SYSTEMD_ID128:?}" show root -Pu) name=root size=${root_size}M bootable
type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot size=${boot_size}M
type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=data
EOF
@@ -1789,7 +1689,7 @@ check_coverage_reports() {
# Create a coverage report that will later be uploaded. Remove info about system
# libraries/headers and generated files, as we don't really care about them.
- lcov --directory "${root}/${BUILD_DIR:?}" --capture --output-file "${dest}.new"
+ lcov --directory "${root}/${BUILD_DIR:?}" --capture --exclude "*.gperf" --output-file "${dest}.new"
if [[ -f "$dest" ]]; then
# If the destination report file already exists, don't overwrite it, but
# merge it with the already present one - this usually happens when
@@ -1845,10 +1745,11 @@ 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" + PRIORITY=4 + PRIORITY=3 + PRIORITY=2 + PRIORITY=1 + PRIORITY=0
+ _SYSTEMD_UNIT="${TESTNAME:?}.service" + SYSLOG_IDENTIFIER="$TESTNAME.sh" + \
+ PRIORITY=4 + PRIORITY=3 + PRIORITY=2 + PRIORITY=1 + PRIORITY=0
if get_bool "$save"; then
# If we don't have systemd-journal-remote copy all journals from /var/log/journal/
@@ -2398,7 +2299,7 @@ install_zoneinfo() {
inst_any /usr/share/zoneinfo/Australia/Sydney
inst_any /usr/share/zoneinfo/Europe/Berlin
inst_any /usr/share/zoneinfo/Europe/Dublin
- inst_any /usr/share/zoneinfo/Europe/Kiev
+ inst_any /usr/share/zoneinfo/Europe/Kyiv
inst_any /usr/share/zoneinfo/Pacific/Auckland
inst_any /usr/share/zoneinfo/Pacific/Honolulu
inst_any /usr/share/zoneinfo/CET
@@ -3338,6 +3239,13 @@ test_cleanup_again() {
[ -n "$TESTDIR" ] || return
rm -rf "$TESTDIR/unprivileged-nspawn-root"
[[ -n "$initdir" ]] && _umount_dir "$initdir"
+ # Test specific images are not reused, so delete them or we run out of disk space
+ if [[ -n "$IMAGE_PUBLIC" ]] && [ "$(basename "$IMAGE_PUBLIC")" != "default.img" ]; then
+ rm -vf "$IMAGE_PUBLIC"
+ fi
+ if [[ -n "$IMAGE_PRIVATE" ]] && [ "$(basename "$IMAGE_PRIVATE")" != "default.img" ]; then
+ rm -vf "$IMAGE_PRIVATE"
+ fi
}
test_create_image() {
@@ -3405,7 +3313,7 @@ test_setup() {
if get_bool "$IS_BUILT_WITH_COVERAGE"; then
# Do an initial coverage capture, to make sure the final report includes
# files that the tests didn't touch at all
- lcov --initial --capture --directory "${initdir}/${BUILD_DIR:?}" --output-file "${TESTDIR:?}/coverage-base"
+ lcov --initial --capture --directory "${initdir}/${BUILD_DIR:?}" --exclude "*.gperf" --output-file "${TESTDIR:?}/coverage-base"
fi
if get_bool "$hook_defined"; then
@@ -3417,11 +3325,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"
@@ -3429,7 +3337,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"
@@ -3437,7 +3345,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"
@@ -3493,7 +3401,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]"
@@ -3525,7 +3433,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/test-network-generator-conversion.sh b/test/test-network-generator-conversion.sh
index 9a4732c..12254ac 100755
--- a/test/test-network-generator-conversion.sh
+++ b/test/test-network-generator-conversion.sh
@@ -49,15 +49,15 @@ run_network_generator() {
rm -rf "${WORK_DIR:?}"/*
stderr="$WORK_DIR/stderr"
- if ! "$GENERATOR_BIN" --root "$WORK_DIR" 2>"$stderr"; then
+ if ! SYSTEMD_LOG_LEVEL="info" "$GENERATOR_BIN" --root "$WORK_DIR" 2>"$stderr"; then
echo >&2 "Generator failed when parsing $SYSTEMD_PROC_CMDLINE"
- cat "$stderr"
+ cat >&2 "$stderr"
return 1
fi
if [[ -s "$stderr" ]]; then
echo >&2 "Generator generated unexpected messages on stderr"
- cat "$stderr"
+ cat >&2 "$stderr"
return 1
fi
@@ -226,7 +226,7 @@ for f in "$TEST_DATA"/test-*.input; do
"$GENERATOR_BIN" --root "$out" -- $(cat "$f")
if ! diff -u "$out/run/systemd/network" "${f%.input}.expected"; then
- echo "**** Unexpected output for $f"
+ echo >&2 "**** Unexpected output for $f"
exit 1
fi
diff --git a/test/test-network/conf/11-test-unit-file.link b/test/test-network/conf/11-test-unit-file.link
new file mode 100644
index 0000000..429ac31
--- /dev/null
+++ b/test/test-network/conf/11-test-unit-file.link
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: MIT-0
+#
+# This config file is installed as part of systemd.
+# It may be freely copied and edited (following the MIT No Attribution license).
+#
+# To make local modifications, one of the following methods may be used:
+# 1. add a drop-in file that extends this file by creating the
+# /etc/systemd/network/99-default.link.d/ directory and creating a
+# new .conf file there.
+# 2. copy this file into /etc/systemd/network or one of the other paths checked
+# by systemd-udevd and edit it there.
+# This file should not be edited in place, because it'll be overwritten on upgrades.
+
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=keep kernel database onboard slot path
+AlternativeNamesPolicy=database onboard slot path
+MACAddressPolicy=persistent
diff --git a/test/test-network/conf/11-test-unit-file.link.d/dropin.conf b/test/test-network/conf/11-test-unit-file.link.d/dropin.conf
new file mode 100644
index 0000000..dfddc0a
--- /dev/null
+++ b/test/test-network/conf/11-test-unit-file.link.d/dropin.conf
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Link]
diff --git a/test/test-network/conf/11-test-unit-file.netdev b/test/test-network/conf/11-test-unit-file.netdev
new file mode 100644
index 0000000..86af17f
--- /dev/null
+++ b/test/test-network/conf/11-test-unit-file.netdev
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
+Name=test1
+Kind=dummy
diff --git a/test/test-network/conf/11-test-unit-file.netdev.d/dropin.conf b/test/test-network/conf/11-test-unit-file.netdev.d/dropin.conf
new file mode 100644
index 0000000..6fde598
--- /dev/null
+++ b/test/test-network/conf/11-test-unit-file.netdev.d/dropin.conf
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
diff --git a/test/test-network/conf/11-test-unit-file.network b/test/test-network/conf/11-test-unit-file.network
new file mode 100644
index 0000000..0a4511b
--- /dev/null
+++ b/test/test-network/conf/11-test-unit-file.network
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
diff --git a/test/test-network/conf/11-test-unit-file.network.d/dropin.conf b/test/test-network/conf/11-test-unit-file.network.d/dropin.conf
new file mode 100644
index 0000000..f2f6099
--- /dev/null
+++ b/test/test-network/conf/11-test-unit-file.network.d/dropin.conf
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Network]
diff --git a/test/test-network/conf/21-macvlan.netdev b/test/test-network/conf/21-macvlan.netdev
index fdc81ea..d742f64 100644
--- a/test/test-network/conf/21-macvlan.netdev
+++ b/test/test-network/conf/21-macvlan.netdev
@@ -3,3 +3,7 @@
Name=macvlan99
Kind=macvlan
MTUBytes=2000
+
+[MACVLAN]
+BroadcastMulticastQueueLength=1234
+BroadcastQueueThreshold=2147483647
diff --git a/test/test-network/conf/24-rps-cpu-disable.link b/test/test-network/conf/24-rps-cpu-disable.link
new file mode 100644
index 0000000..fb3451a
--- /dev/null
+++ b/test/test-network/conf/24-rps-cpu-disable.link
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+OriginalName=dummy98
+
+[Link]
+ReceivePacketSteeringCPUMask=disable
diff --git a/test/test-network/conf/24-rps-cpu-empty.link b/test/test-network/conf/24-rps-cpu-empty.link
new file mode 100644
index 0000000..fc1342b
--- /dev/null
+++ b/test/test-network/conf/24-rps-cpu-empty.link
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+OriginalName=dummy98
+
+[Link]
+ReceivePacketSteeringCPUMask=
diff --git a/test/test-network/conf/24-rps-cpu-invalid.link b/test/test-network/conf/24-rps-cpu-invalid.link
new file mode 100644
index 0000000..76d6713
--- /dev/null
+++ b/test/test-network/conf/24-rps-cpu-invalid.link
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+OriginalName=dummy98
+
+[Link]
+ReceivePacketSteeringCPUMask=0 3 8-invalid
diff --git a/test/test-network/conf/25-address-static.network.d/20-clear-addresses.conf b/test/test-network/conf/25-address-static.network.d/20-clear-addresses.conf
new file mode 100644
index 0000000..a38b07c
--- /dev/null
+++ b/test/test-network/conf/25-address-static.network.d/20-clear-addresses.conf
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+[Network]
+# An empty string clears previously configured addresses.
+Address=
+Address=10.4.0.1/16
diff --git a/test/test-network/conf/25-agent-bridge-port.network b/test/test-network/conf/25-agent-bridge-port.network
new file mode 100644
index 0000000..709a783
--- /dev/null
+++ b/test/test-network/conf/25-agent-bridge-port.network
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=client-peer
+
+[Network]
+Bridge=bridge-relay
+IPv6AcceptRA=no
diff --git a/test/test-network/conf/25-agent-bridge.netdev b/test/test-network/conf/25-agent-bridge.netdev
new file mode 100644
index 0000000..a611337
--- /dev/null
+++ b/test/test-network/conf/25-agent-bridge.netdev
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
+Name=bridge-relay
+Kind=bridge
diff --git a/test/test-network/conf/25-agent-bridge.network b/test/test-network/conf/25-agent-bridge.network
new file mode 100644
index 0000000..8383790
--- /dev/null
+++ b/test/test-network/conf/25-agent-bridge.network
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=bridge-relay
+
+[Network]
+Address=192.168.2.1/24
+DHCPServer=yes
+IPv6AcceptRA=no
+
+[DHCPServer]
+RelayTarget=192.168.1.1
+RelayAgentRemoteId=string:aabbccdd
diff --git a/test/test-network/conf/25-agent-client-peer.network b/test/test-network/conf/25-agent-client-peer.network
index e31108b..4d7d758 100644
--- a/test/test-network/conf/25-agent-client-peer.network
+++ b/test/test-network/conf/25-agent-client-peer.network
@@ -5,7 +5,7 @@ Name=client-peer
[Network]
Address=192.168.6.2/24
DHCPServer=yes
-IPForward=ipv4
+IPv4Forwarding=yes
IPv6AcceptRA=no
[DHCPServer]
diff --git a/test/test-network/conf/25-agent-client.network b/test/test-network/conf/25-agent-client.network
index cfa7e5a..219d40a 100644
--- a/test/test-network/conf/25-agent-client.network
+++ b/test/test-network/conf/25-agent-client.network
@@ -4,5 +4,5 @@ Name=client
[Network]
DHCP=yes
-IPForward=ipv4
+IPv4Forwarding=yes
IPv6AcceptRA=no
diff --git a/test/test-network/conf/25-agent-server-peer.network b/test/test-network/conf/25-agent-server-peer.network
index 1f6fa4b..5e005c7 100644
--- a/test/test-network/conf/25-agent-server-peer.network
+++ b/test/test-network/conf/25-agent-server-peer.network
@@ -4,5 +4,5 @@ Name=server-peer
[Network]
Address=192.168.5.2/24
-IPForward=ipv4
+IPv4Forwarding=yes
IPv6AcceptRA=no
diff --git a/test/test-network/conf/25-agent-server.network b/test/test-network/conf/25-agent-server.network
index 905508f..0108039 100644
--- a/test/test-network/conf/25-agent-server.network
+++ b/test/test-network/conf/25-agent-server.network
@@ -4,7 +4,7 @@ Name=server
[Network]
Address=192.168.5.1/24
-IPForward=ipv4
+IPv4Forwarding=yes
DHCPServer=yes
IPv6AcceptRA=no
diff --git a/test/test-network/conf/25-bond-property.netdev b/test/test-network/conf/25-bond-property.netdev
new file mode 100644
index 0000000..3954689
--- /dev/null
+++ b/test/test-network/conf/25-bond-property.netdev
@@ -0,0 +1,10 @@
+[NetDev]
+Name=bond97
+Kind=bond
+
+[Bond]
+Mode=active-backup
+PrimaryReselectPolicy=always
+MIIMonitorSec=1s
+ARPMissedMax=10
+PeerNotifyDelaySec=300s
diff --git a/test/test-network/conf/25-dhcp-server-veth-peer.network b/test/test-network/conf/25-dhcp-server-veth-peer.network
index d5cc6d3..abe3fa2 100644
--- a/test/test-network/conf/25-dhcp-server-veth-peer.network
+++ b/test/test-network/conf/25-dhcp-server-veth-peer.network
@@ -6,3 +6,5 @@ Name=veth-peer
IPv6AcceptRA=no
Address=2600::1/0
Address=192.168.5.1/24
+# To make the kernel send NA with IsRouter flag.
+IPv6Forwarding=yes
diff --git a/test/test-network/conf/25-dummy.netdev b/test/test-network/conf/25-dummy.netdev
new file mode 100644
index 0000000..d7cf7b4
--- /dev/null
+++ b/test/test-network/conf/25-dummy.netdev
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
+Name=test25
+Kind=dummy
diff --git a/test/test-network/conf/25-dummy.network b/test/test-network/conf/25-dummy.network
new file mode 100644
index 0000000..a6e93fd
--- /dev/null
+++ b/test/test-network/conf/25-dummy.network
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test25
+
+[Network]
+IPv6AcceptRA=no
diff --git a/test/test-network/conf/25-fibrule-l3mdev.network b/test/test-network/conf/25-fibrule-l3mdev.network
new file mode 100644
index 0000000..a1afcd2
--- /dev/null
+++ b/test/test-network/conf/25-fibrule-l3mdev.network
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
+
+[RoutingPolicyRule]
+Priority=1500
+L3MasterDevice=true
+
+[RoutingPolicyRule]
+Priority=2000
+L3MasterDevice=true
+Type=unreachable
diff --git a/test/test-network/conf/25-ipv6-neigh-retrans-time-0s.network b/test/test-network/conf/25-ipv6-neigh-retrans-time-0s.network
new file mode 100644
index 0000000..04c7c49
--- /dev/null
+++ b/test/test-network/conf/25-ipv6-neigh-retrans-time-0s.network
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test25
+
+[Network]
+IPv6AcceptRA=no
+IPv6RetransmissionTimeSec=0
diff --git a/test/test-network/conf/25-ipv6-neigh-retrans-time-3s.network b/test/test-network/conf/25-ipv6-neigh-retrans-time-3s.network
new file mode 100644
index 0000000..b4dbd06
--- /dev/null
+++ b/test/test-network/conf/25-ipv6-neigh-retrans-time-3s.network
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test25
+
+[Network]
+IPv6AcceptRA=no
+IPv6RetransmissionTimeSec=3
diff --git a/test/test-network/conf/25-ipv6-neigh-retrans-time-4s.network b/test/test-network/conf/25-ipv6-neigh-retrans-time-4s.network
new file mode 100644
index 0000000..cbdf4f3
--- /dev/null
+++ b/test/test-network/conf/25-ipv6-neigh-retrans-time-4s.network
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test25
+
+[Network]
+IPv6AcceptRA=no
+IPv6RetransmissionTimeSec=4
diff --git a/test/test-network/conf/25-ipv6-neigh-retrans-time-infinity.network b/test/test-network/conf/25-ipv6-neigh-retrans-time-infinity.network
new file mode 100644
index 0000000..085cb30
--- /dev/null
+++ b/test/test-network/conf/25-ipv6-neigh-retrans-time-infinity.network
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test25
+
+[Network]
+IPv6AcceptRA=no
+IPv6RetransmissionTimeSec=infinity
diff --git a/test/test-network/conf/25-ipv6-neigh-retrans-time-invalid.network b/test/test-network/conf/25-ipv6-neigh-retrans-time-invalid.network
new file mode 100644
index 0000000..8a0bf83
--- /dev/null
+++ b/test/test-network/conf/25-ipv6-neigh-retrans-time-invalid.network
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test25
+
+[Network]
+IPv6AcceptRA=no
+IPv6RetransmissionTimeSec=-2
diff --git a/test/test-network/conf/25-ipv6-neigh-retrans-time-toobig.network b/test/test-network/conf/25-ipv6-neigh-retrans-time-toobig.network
new file mode 100644
index 0000000..0976bae
--- /dev/null
+++ b/test/test-network/conf/25-ipv6-neigh-retrans-time-toobig.network
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test25
+
+[Network]
+IPv6AcceptRA=no
+IPv6RetransmissionTimeSec=999999999999999999
diff --git a/test/test-network/conf/25-ipv6-prefix-veth-token-prefixstable.network b/test/test-network/conf/25-ipv6-prefix-veth-token-prefixstable.network
index ac50700..11502fd 100644
--- a/test/test-network/conf/25-ipv6-prefix-veth-token-prefixstable.network
+++ b/test/test-network/conf/25-ipv6-prefix-veth-token-prefixstable.network
@@ -6,8 +6,6 @@ Name=veth99
IPv6AcceptRA=true
[IPv6AcceptRA]
-Token=prefixstable:2002:da8:1::
-Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a
# invalid tokens
Token=prefixstable:2002:da8:1::,00000000000000000000000000000000
Token=prefixstable:2002:da8:1::,
@@ -17,3 +15,10 @@ Token=prefixstable@
Token=static
Token=static:
Token=static:::
+# valid token
+Token=prefixstable:2002:da8:1::
+Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a
+# reset token
+Token=
+# set token again
+Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a
diff --git a/test/test-network/conf/25-ipv6-prefix.network b/test/test-network/conf/25-ipv6-prefix.network
index 9cf8f25..e801581 100644
--- a/test/test-network/conf/25-ipv6-prefix.network
+++ b/test/test-network/conf/25-ipv6-prefix.network
@@ -10,6 +10,8 @@ IPv6SendRA=yes
DNS=_link_local 2002:da8:1:0::1
DNSLifetimeSec=1min
Domains=hogehoge.test
+ReachableTimeSec=42
+RetransmitSec=500ms
[IPv6Prefix]
Prefix=2002:da8:1:0::/64
diff --git a/test/test-network/conf/25-ipv6-proxy-ndp.network b/test/test-network/conf/25-ipv6-proxy-ndp.network
index 81302ab..d01a633 100644
--- a/test/test-network/conf/25-ipv6-proxy-ndp.network
+++ b/test/test-network/conf/25-ipv6-proxy-ndp.network
@@ -9,7 +9,6 @@ IPv6ProxyNDPAddress=2607:5300:203:5215:3::1
IPv6ProxyNDPAddress=2607:5300:203:5215:2::1
IPv6ProxyNDPAddress=2607:5300:203:5215:1::1
IPv6AcceptRA=no
-IPForward=yes
Address=66.70.129.136/32
Address=66.70.129.142/32
Address=66.70.129.143/32
diff --git a/test/test-network/conf/25-neighbor-ip-dummy.network b/test/test-network/conf/25-neighbor-dummy.network
index f1b1151..f1b1151 100644
--- a/test/test-network/conf/25-neighbor-ip-dummy.network
+++ b/test/test-network/conf/25-neighbor-dummy.network
diff --git a/test/test-network/conf/25-neighbor-section.network b/test/test-network/conf/25-neighbor-dummy.network.d/10-step1.conf
index 59e21eb..727089e 100644
--- a/test/test-network/conf/25-neighbor-section.network
+++ b/test/test-network/conf/25-neighbor-dummy.network.d/10-step1.conf
@@ -1,10 +1,4 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
-[Match]
-Name=dummy98
-
-[Network]
-IPv6AcceptRA=no
-
[Neighbor]
Address=192.168.10.1
LinkLayerAddress=00:00:5e:00:02:65
diff --git a/test/test-network/conf/25-neighbor-section.network.d/override.conf b/test/test-network/conf/25-neighbor-dummy.network.d/10-step2.conf
index 01027e3..e28e3fc 100644
--- a/test/test-network/conf/25-neighbor-section.network.d/override.conf
+++ b/test/test-network/conf/25-neighbor-dummy.network.d/10-step2.conf
@@ -1,5 +1,4 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
-
[Neighbor]
Address=192.168.10.1
LinkLayerAddress=00:00:5e:00:03:65
diff --git a/test/test-network/conf/25-neighbor-dummy.network.d/10-step3.conf b/test/test-network/conf/25-neighbor-dummy.network.d/10-step3.conf
new file mode 100644
index 0000000..9262b74
--- /dev/null
+++ b/test/test-network/conf/25-neighbor-dummy.network.d/10-step3.conf
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Neighbor]
+Address=192.168.10.1
+LinkLayerAddress=00:00:5e:00:03:66
diff --git a/test/test-network/conf/25-netdevsim.link b/test/test-network/conf/25-netdevsim.link
new file mode 100644
index 0000000..f8beb55
--- /dev/null
+++ b/test/test-network/conf/25-netdevsim.link
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Driver=netdevsim
+
+[Link]
+NamePolicy=keep kernel database onboard slot path
+AlternativeNamesPolicy=database onboard slot path mac
+# Also set a fixed name. Workaround for bug in kernel 6.9:
+# https://github.com/torvalds/linux/commit/8debcf5832c3e8a6baaea27c75ad8a6ba5077beb
+AlternativeName=sim99
+MACAddressPolicy=persistent
diff --git a/test/test-network/conf/25-nexthop.network b/test/test-network/conf/25-nexthop-1.network
index f53a58b..a5a8d81 100644
--- a/test/test-network/conf/25-nexthop.network
+++ b/test/test-network/conf/25-nexthop-1.network
@@ -6,7 +6,6 @@ Name=veth99
IPv6AcceptRA=no
Address=2001:1234:5:8f63::1/120
Address=192.168.5.10/24
-Gateway=192.168.5.1
[NextHop]
Id=1
diff --git a/test/test-network/conf/25-nexthop-2.network b/test/test-network/conf/25-nexthop-2.network
new file mode 100644
index 0000000..63062f3
--- /dev/null
+++ b/test/test-network/conf/25-nexthop-2.network
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=no
+Address=2001:1234:5:8f63::1/120
+Address=192.168.5.10/24
+
+# Commented out lines are specified in 25-nexthop.network
+
+[NextHop]
+#Id=1
+Id=6
+Gateway=192.168.5.1
+
+[NextHop]
+#Id=2
+Id=7
+Gateway=2001:1234:5:8f63::2
+
+[NextHop]
+#Id=3
+Id=4
+Family=ipv6
+
+[NextHop]
+#Id=4
+Id=3
+Family=ipv4
+
+[NextHop]
+Id=5
+#Gateway=192.168.10.1
+#OnLink=yes
+Gateway=192.168.5.3
+OnLink=no
+
+[NextHop]
+#Id=6
+Id=1
+Family=ipv4
+Blackhole=yes
+
+[NextHop]
+#Id=7
+Id=2
+Family=ipv6
+Blackhole=yes
+
+[NextHop]
+Id=8
+Gateway=fe80::222:4dff:ff:ff:ff:ff
+
+[NextHop]
+Gateway=192.168.5.2
+
+[NextHop]
+Family=ipv4
+Blackhole=yes
+
+[NextHop]
+Family=ipv6
+Blackhole=yes
+
+[Route]
+#NextHop=1
+NextHop=6
+Destination=10.10.10.10
+
+[Route]
+#NextHop=2
+NextHop=7
+Destination=10.10.10.11
+
+[Route]
+#NextHop=2
+NextHop=7
+Destination=2001:1234:5:8f62::1
+
+[Route]
+NextHop=5
+Destination=10.10.10.12
+
+[Route]
+#NextHop=6
+NextHop=1
+Destination=10.10.10.13
+
+[Route]
+#NextHop=7
+NextHop=2
+Destination=2001:1234:5:8f62::2
+
+[Route]
+#NextHop=21
+NextHop=20
+Destination=10.10.10.14
diff --git a/test/test-network/conf/25-nexthop-dummy.network b/test/test-network/conf/25-nexthop-dummy-1.network
index a7bdaa9..a7bdaa9 100644
--- a/test/test-network/conf/25-nexthop-dummy.network
+++ b/test/test-network/conf/25-nexthop-dummy-1.network
diff --git a/test/test-network/conf/25-nexthop-dummy-2.network b/test/test-network/conf/25-nexthop-dummy-2.network
new file mode 100644
index 0000000..2556b1f
--- /dev/null
+++ b/test/test-network/conf/25-nexthop-dummy-2.network
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+Address=192.168.20.20/24
+IPv6AcceptRA=no
+
+# Commented out lines are specified in 25-nexthop-dummy.network
+
+[NextHop]
+#Id=20
+Id=21
+Gateway=192.168.20.1
+
+[NextHop]
+#Id=21
+#Group=1:3 20:1
+Id=20
+Group=5:3 21:1
diff --git a/test/test-network/conf/25-nexthop-test1.network b/test/test-network/conf/25-nexthop-test1.network
new file mode 100644
index 0000000..5a4c596
--- /dev/null
+++ b/test/test-network/conf/25-nexthop-test1.network
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test1
+
+[Network]
+Address=192.168.20.21/24
+IPv6AcceptRA=no
+
+[Route]
+Destination=10.10.11.10
+# Nexthop 21 is configured as a group nexthop of 1 and 20
+NextHop=21
diff --git a/test/test-network/conf/25-route-static.network b/test/test-network/conf/25-route-static.network
index 7ef211d..5ddd7de 100644
--- a/test/test-network/conf/25-route-static.network
+++ b/test/test-network/conf/25-route-static.network
@@ -96,7 +96,7 @@ MultiPathRoute=149.10.124.59 10
MultiPathRoute=149.10.124.60 5
[Route]
-Destination=2001:1234:5:7fff:ff:ff:ff:ff/128
+Destination=2001:1234:5:bfff:ff:ff:ff:ff/128
MultiPathRoute=2001:1234:5:6fff:ff:ff:ff:ff@test1 20
MultiPathRoute=2001:1234:5:7fff:ff:ff:ff:ff@test1 30
MultiPathRoute=2001:1234:5:8fff:ff:ff:ff:ff@dummy98 10
diff --git a/test/test-network/conf/25-rps-cpu-0-1.link b/test/test-network/conf/25-rps-cpu-0-1.link
new file mode 100644
index 0000000..d026248
--- /dev/null
+++ b/test/test-network/conf/25-rps-cpu-0-1.link
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+OriginalName=dummy98
+
+[Link]
+ReceivePacketSteeringCPUMask=0 1
diff --git a/test/test-network/conf/25-rps-cpu-0-empty.link b/test/test-network/conf/25-rps-cpu-0-empty.link
new file mode 100644
index 0000000..b25b417
--- /dev/null
+++ b/test/test-network/conf/25-rps-cpu-0-empty.link
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+OriginalName=dummy98
+
+[Link]
+ReceivePacketSteeringCPUMask=0
+ReceivePacketSteeringCPUMask=
diff --git a/test/test-network/conf/25-rps-cpu-0-invalid.link b/test/test-network/conf/25-rps-cpu-0-invalid.link
new file mode 100644
index 0000000..26147a0
--- /dev/null
+++ b/test/test-network/conf/25-rps-cpu-0-invalid.link
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+OriginalName=dummy98
+
+[Link]
+ReceivePacketSteeringCPUMask=0
+ReceivePacketSteeringCPUMask=invalid
diff --git a/test/test-network/conf/25-rps-cpu-0.link b/test/test-network/conf/25-rps-cpu-0.link
new file mode 100644
index 0000000..b1f4bc2
--- /dev/null
+++ b/test/test-network/conf/25-rps-cpu-0.link
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+OriginalName=dummy98
+
+[Link]
+ReceivePacketSteeringCPUMask=0
diff --git a/test/test-network/conf/25-rps-cpu-1.link b/test/test-network/conf/25-rps-cpu-1.link
new file mode 100644
index 0000000..d24d713
--- /dev/null
+++ b/test/test-network/conf/25-rps-cpu-1.link
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+OriginalName=dummy98
+
+[Link]
+ReceivePacketSteeringCPUMask=1
diff --git a/test/test-network/conf/25-rps-cpu-all.link b/test/test-network/conf/25-rps-cpu-all.link
new file mode 100644
index 0000000..b7a8eda
--- /dev/null
+++ b/test/test-network/conf/25-rps-cpu-all.link
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+OriginalName=dummy98
+
+[Link]
+ReceivePacketSteeringCPUMask=all
diff --git a/test/test-network/conf/25-rps-cpu-multi.link b/test/test-network/conf/25-rps-cpu-multi.link
new file mode 100644
index 0000000..d7d4d04
--- /dev/null
+++ b/test/test-network/conf/25-rps-cpu-multi.link
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+OriginalName=dummy98
+
+[Link]
+ReceivePacketSteeringCPUMask=0 1
+ReceivePacketSteeringCPUMask=2,3
diff --git a/test/test-network/conf/25-sriov-udev.network b/test/test-network/conf/25-sriov-udev.network
index e914131..308f5a0 100644
--- a/test/test-network/conf/25-sriov-udev.network
+++ b/test/test-network/conf/25-sriov-udev.network
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
-Name=eni99np1
+Name=sim99
[Network]
Address=192.168.100.100/24
diff --git a/test/test-network/conf/25-sriov.link b/test/test-network/conf/25-sriov.link
index cc19561..8f6e377 100644
--- a/test/test-network/conf/25-sriov.link
+++ b/test/test-network/conf/25-sriov.link
@@ -5,6 +5,9 @@ Driver=netdevsim
[Link]
NamePolicy=keep kernel database onboard slot path
AlternativeNamesPolicy=database onboard slot path mac
+# Also set a fixed name. Workaround for bug in kernel 6.9:
+# https://github.com/torvalds/linux/commit/8debcf5832c3e8a6baaea27c75ad8a6ba5077beb
+AlternativeName=sim99
MACAddressPolicy=persistent
[SR-IOV]
diff --git a/test/test-network/conf/25-sriov.network b/test/test-network/conf/25-sriov.network
index d87615e..46573d9 100644
--- a/test/test-network/conf/25-sriov.network
+++ b/test/test-network/conf/25-sriov.network
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
-Name=eni99np1
+Name=sim99
[Network]
Address=192.168.100.100/24
diff --git a/test/test-network/conf/25-sysctl.network b/test/test-network/conf/25-sysctl.network
index a71ffb2..759fe65 100644
--- a/test/test-network/conf/25-sysctl.network
+++ b/test/test-network/conf/25-sysctl.network
@@ -3,10 +3,12 @@
Name=dummy98
[Network]
-IPForward=yes
+IPv4Forwarding=yes
+IPv6Forwarding=yes
IPv6DuplicateAddressDetection=3
IPv6HopLimit=5
IPv4ProxyARP=yes
+IPv4ProxyARPPrivateVLAN=yes
IPv6ProxyNDP=yes
IPv6AcceptRA=no
IPv4AcceptLocal=yes
diff --git a/test/test-network/conf/25-veth-bridge.network b/test/test-network/conf/25-veth-bridge.network
index b2f1634..a8be06d 100644
--- a/test/test-network/conf/25-veth-bridge.network
+++ b/test/test-network/conf/25-veth-bridge.network
@@ -1,10 +1,10 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=client-p
+Name=router-p
Name=router-high-p
Name=router-low-p
[Network]
Bridge=bridge99
IPv6AcceptRA=no
-IPv6SendRA=yes
diff --git a/test/test-network/conf/25-veth-peer-no-address.network b/test/test-network/conf/25-veth-peer-no-address.network
new file mode 100644
index 0000000..dbea3bd
--- /dev/null
+++ b/test/test-network/conf/25-veth-peer-no-address.network
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=veth-peer
+
+[Network]
+IPv6AcceptRA=no
diff --git a/test/test-network/conf/25-veth-router-high2.network b/test/test-network/conf/25-veth-router-high2.network
new file mode 100644
index 0000000..47e8cd7
--- /dev/null
+++ b/test/test-network/conf/25-veth-router-high2.network
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=router-low
+
+[Network]
+IPv6AcceptRA=no
+IPv6SendRA=yes
+
+[IPv6SendRA]
+# changed from low to high
+RouterPreference=high
+EmitDNS=no
+EmitDomains=no
+
+[IPv6Prefix]
+Prefix=2002:da8:1:98::/64
+PreferredLifetimeSec=1000s
+ValidLifetimeSec=2100s
diff --git a/test/test-network/conf/25-veth-router-hop-limit.network b/test/test-network/conf/25-veth-router-hop-limit.network
new file mode 100644
index 0000000..643e362
--- /dev/null
+++ b/test/test-network/conf/25-veth-router-hop-limit.network
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=router
+
+[Network]
+IPv6AcceptRA=no
+IPv6SendRA=yes
+
+[IPv6SendRA]
+RouterPreference=high
+EmitDNS=no
+EmitDomains=no
+HopLimit=42
+
+[IPv6Prefix]
+Prefix=2002:da8:1:99::/64
+PreferredLifetimeSec=1000s
+ValidLifetimeSec=2100s
diff --git a/test/test-network/conf/25-veth-router-low2.network b/test/test-network/conf/25-veth-router-low2.network
new file mode 100644
index 0000000..f318938
--- /dev/null
+++ b/test/test-network/conf/25-veth-router-low2.network
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=router-high
+
+[Network]
+IPv6AcceptRA=no
+IPv6SendRA=yes
+
+[IPv6SendRA]
+# changed from high to low
+RouterPreference=low
+EmitDNS=no
+EmitDomains=no
+
+[IPv6Prefix]
+Prefix=2002:da8:1:99::/64
+PreferredLifetimeSec=1000s
+ValidLifetimeSec=2100s
diff --git a/test/test-network/conf/25-veth-router.netdev b/test/test-network/conf/25-veth-router.netdev
new file mode 100644
index 0000000..d462313
--- /dev/null
+++ b/test/test-network/conf/25-veth-router.netdev
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
+Name=router
+Kind=veth
+MACAddress=12:34:56:78:9a:99
+
+[Peer]
+Name=router-p
+MACAddress=12:34:56:78:9b:99
diff --git a/test/test-network/conf/25-wireguard-endpoint-peer0-cred.txt b/test/test-network/conf/25-wireguard-endpoint-peer0-cred.txt
new file mode 100644
index 0000000..b4251c3
--- /dev/null
+++ b/test/test-network/conf/25-wireguard-endpoint-peer0-cred.txt
@@ -0,0 +1 @@
+192.168.27.3:51820
diff --git a/test/test-network/conf/25-wireguard-no-peer-private-key-cred.txt b/test/test-network/conf/25-wireguard-no-peer-private-key-cred.txt
new file mode 100644
index 0000000..8011c64
--- /dev/null
+++ b/test/test-network/conf/25-wireguard-no-peer-private-key-cred.txt
@@ -0,0 +1 @@
+EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
diff --git a/test/test-network/conf/25-wireguard-no-peer.netdev b/test/test-network/conf/25-wireguard-no-peer.netdev
index ce3b31a..8c90735 100644
--- a/test/test-network/conf/25-wireguard-no-peer.netdev
+++ b/test/test-network/conf/25-wireguard-no-peer.netdev
@@ -4,6 +4,6 @@ Name=wg97
Kind=wireguard
[WireGuard]
-PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
+#PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
ListenPort=51821
FwMark=1235
diff --git a/test/test-network/conf/25-wireguard-preshared-key-peer2-cred.txt b/test/test-network/conf/25-wireguard-preshared-key-peer2-cred.txt
new file mode 100644
index 0000000..5e79c19
--- /dev/null
+++ b/test/test-network/conf/25-wireguard-preshared-key-peer2-cred.txt
@@ -0,0 +1 @@
+6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=
diff --git a/test/test-network/conf/25-wireguard.netdev b/test/test-network/conf/25-wireguard.netdev
index 4fed38e..6a2bb88 100644
--- a/test/test-network/conf/25-wireguard.netdev
+++ b/test/test-network/conf/25-wireguard.netdev
@@ -13,8 +13,8 @@ RouteMetric=456
[WireGuardPeer]
PublicKey=RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=
AllowedIPs=fd31:bf08:57cb::/48,192.168.26.3/24
-#Endpoint=wireguard.example.com:51820
-Endpoint=192.168.27.3:51820
+#Endpoint=192.168.27.3:51820
+Endpoint=@network.wireguard.peer0.endpoint
PresharedKey=IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=
PersistentKeepalive=20
RouteTable=1234
diff --git a/test/test-network/conf/25-wireguard.netdev.d/peer2.conf b/test/test-network/conf/25-wireguard.netdev.d/peer2.conf
index bf99a5a..f3440df 100644
--- a/test/test-network/conf/25-wireguard.netdev.d/peer2.conf
+++ b/test/test-network/conf/25-wireguard.netdev.d/peer2.conf
@@ -1,5 +1,5 @@
[WireGuardPeer]
PublicKey=9uioxkGzjvGjkse3V35I9AhorWfIjBcrf3UPMS0bw2c=
-PresharedKey=6Fsg8XN0DE6aPQgAX4r2oazEYJOGqyHUz3QRH/jCB+I=
+PresharedKey=@network.wireguard.peer2.psk
AllowedIPs=192.168.124.3
diff --git a/test/test-network/conf/26-bridge-mac-master.network b/test/test-network/conf/26-bridge-mac-master.network
new file mode 100644
index 0000000..d08970b
--- /dev/null
+++ b/test/test-network/conf/26-bridge-mac-master.network
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=bridge99
+
+[Network]
+IPv6AcceptRA=false
diff --git a/test/test-network/conf/25-neighbor-next.network b/test/test-network/conf/26-bridge-mac-slave.network
index 6911f48..81a0b46 100644
--- a/test/test-network/conf/25-neighbor-next.network
+++ b/test/test-network/conf/26-bridge-mac-slave.network
@@ -4,7 +4,4 @@ Name=dummy98
[Network]
IPv6AcceptRA=no
-
-[Neighbor]
-Address=192.168.10.1
-LinkLayerAddress=00:00:5e:00:02:66
+Bridge=bridge99
diff --git a/test/test-network/conf/26-bridge-mac.link b/test/test-network/conf/26-bridge-mac.link
new file mode 100644
index 0000000..82ed937
--- /dev/null
+++ b/test/test-network/conf/26-bridge-mac.link
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+OriginalName=bridge99
+
+[Link]
+MACAddressPolicy=none
diff --git a/test/test-network/conf/26-bridge-mac.netdev b/test/test-network/conf/26-bridge-mac.netdev
new file mode 100644
index 0000000..2d26a03
--- /dev/null
+++ b/test/test-network/conf/26-bridge-mac.netdev
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
+Name=bridge99
+Kind=bridge
+MACAddress=none
diff --git a/test/test-network/conf/26-bridge-vlan-master-issue-20373.network b/test/test-network/conf/26-bridge-vlan-master-issue-20373.network
index 7a69757..67011ac 100644
--- a/test/test-network/conf/26-bridge-vlan-master-issue-20373.network
+++ b/test/test-network/conf/26-bridge-vlan-master-issue-20373.network
@@ -4,7 +4,6 @@ Name=bridge99
[Network]
VLAN=vlan99
-IPForward=yes
ConfigureWithoutCarrier=yes
LLDP=yes
IPv6AcceptRA=false
diff --git a/test/test-network/conf/26-bridge-vlan-master.network b/test/test-network/conf/26-bridge-vlan-master.network
index 4bbbc56..90ae0b2 100644
--- a/test/test-network/conf/26-bridge-vlan-master.network
+++ b/test/test-network/conf/26-bridge-vlan-master.network
@@ -6,4 +6,8 @@ Name=bridge99
IPv6AcceptRA=false
[BridgeVLAN]
-VLAN=4060-4094
+PVID=1020
+VLAN=1018-1023
+VLAN=1200-1210
+EgressUntagged=1022-1025
+EgressUntagged=1203-1208
diff --git a/test/test-network/conf/26-bridge-vlan-master.network.d/10-override.conf b/test/test-network/conf/26-bridge-vlan-master.network.d/10-override.conf
new file mode 100644
index 0000000..25d213f
--- /dev/null
+++ b/test/test-network/conf/26-bridge-vlan-master.network.d/10-override.conf
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[BridgeVLAN]
+PVID=
+VLAN=
+EgressUntagged=
+
+PVID=2020
+VLAN=2018-2023
+VLAN=2200-2210
+EgressUntagged=2022-2025
+EgressUntagged=2203-2208
diff --git a/test/test-network/conf/26-bridge-vlan-master.network.d/20-override.conf b/test/test-network/conf/26-bridge-vlan-master.network.d/20-override.conf
new file mode 100644
index 0000000..0dcaee0
--- /dev/null
+++ b/test/test-network/conf/26-bridge-vlan-master.network.d/20-override.conf
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[BridgeVLAN]
+PVID=
+VLAN=
+EgressUntagged=
+
+PVID=2020
+VLAN=2018-2023
+EgressUntagged=2022-2025
diff --git a/test/test-network/conf/26-bridge-vlan-master.network.d/30-override.conf b/test/test-network/conf/26-bridge-vlan-master.network.d/30-override.conf
new file mode 100644
index 0000000..409296f
--- /dev/null
+++ b/test/test-network/conf/26-bridge-vlan-master.network.d/30-override.conf
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[BridgeVLAN]
+PVID=no
+VLAN=
+EgressUntagged=
diff --git a/test/test-network/conf/26-bridge-vlan-slave-issue-20373.network b/test/test-network/conf/26-bridge-vlan-slave-issue-20373.network
index 876219f..bc7c3b0 100644
--- a/test/test-network/conf/26-bridge-vlan-slave-issue-20373.network
+++ b/test/test-network/conf/26-bridge-vlan-slave-issue-20373.network
@@ -4,7 +4,6 @@ Name=test1
[Network]
IPv6AcceptRA=no
-IPForward=yes
Bridge=bridge99
LinkLocalAddressing=no
EmitLLDP=nearest-bridge
diff --git a/test/test-network/conf/26-bridge-vlan-slave.network b/test/test-network/conf/26-bridge-vlan-slave.network
index 9ac8510..d23d0fc 100644
--- a/test/test-network/conf/26-bridge-vlan-slave.network
+++ b/test/test-network/conf/26-bridge-vlan-slave.network
@@ -7,4 +7,8 @@ IPv6AcceptRA=no
Bridge=bridge99
[BridgeVLAN]
-VLAN=4064-4094
+PVID=1010
+VLAN=1008-1013
+VLAN=1100-1110
+EgressUntagged=1012-1015
+EgressUntagged=1103-1108
diff --git a/test/test-network/conf/26-bridge-vlan-slave.network.d/10-override.conf b/test/test-network/conf/26-bridge-vlan-slave.network.d/10-override.conf
new file mode 100644
index 0000000..0511b64
--- /dev/null
+++ b/test/test-network/conf/26-bridge-vlan-slave.network.d/10-override.conf
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[BridgeVLAN]
+PVID=
+VLAN=
+EgressUntagged=
+
+PVID=2010
+VLAN=2008-2013
+VLAN=2100-2110
+EgressUntagged=2012-2015
+EgressUntagged=2103-2108
diff --git a/test/test-network/conf/26-bridge-vlan-slave.network.d/20-override.conf b/test/test-network/conf/26-bridge-vlan-slave.network.d/20-override.conf
new file mode 100644
index 0000000..58df686
--- /dev/null
+++ b/test/test-network/conf/26-bridge-vlan-slave.network.d/20-override.conf
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[BridgeVLAN]
+PVID=
+VLAN=
+EgressUntagged=
+
+PVID=2010
+VLAN=2008-2013
+EgressUntagged=2012-2015
diff --git a/test/test-network/conf/26-bridge-vlan-slave.network.d/30-override.conf b/test/test-network/conf/26-bridge-vlan-slave.network.d/30-override.conf
new file mode 100644
index 0000000..409296f
--- /dev/null
+++ b/test/test-network/conf/26-bridge-vlan-slave.network.d/30-override.conf
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[BridgeVLAN]
+PVID=no
+VLAN=
+EgressUntagged=
diff --git a/test/test-network/conf/networkd-manage-foreign-nexthops-no.conf b/test/test-network/conf/networkd-manage-foreign-nexthops-no.conf
new file mode 100644
index 0000000..9bf5d7e
--- /dev/null
+++ b/test/test-network/conf/networkd-manage-foreign-nexthops-no.conf
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Network]
+ManageForeignNextHops=no
diff --git a/test/test-network/conf/persist-leases-no.conf b/test/test-network/conf/persist-leases-no.conf
new file mode 100644
index 0000000..5025265
--- /dev/null
+++ b/test/test-network/conf/persist-leases-no.conf
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[DHCPServer]
+PersistLeases=no
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index a871d19..92cb07f 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -4,6 +4,16 @@
# These tests can be executed in the systemd mkosi image when booted in QEMU. After booting the QEMU VM,
# simply run this file which can be found in the VM at /usr/lib/systemd/tests/testdata/test-network/systemd-networkd-tests.py.
+#
+# To run an individual test, specify it as a command line argument in the form
+# of <class>.<test_function>. E.g. the NetworkdMTUTests class has a test
+# function called test_ipv6_mtu(). To run just that test use:
+#
+# sudo ./systemd-networkd-tests.py NetworkdMTUTests.test_ipv6_mtu
+#
+# Similarly, other individual tests can be run, eg.:
+#
+# sudo ./systemd-networkd-tests.py NetworkdNetworkTests.test_ipv6_neigh_retrans_time
import argparse
import datetime
@@ -12,6 +22,7 @@ import itertools
import json
import os
import pathlib
+import random
import re
import shutil
import signal
@@ -27,6 +38,7 @@ network_unit_dir = '/run/systemd/network'
networkd_conf_dropin_dir = '/run/systemd/networkd.conf.d'
networkd_ci_temp_dir = '/run/networkd-ci'
udev_rules_dir = '/run/udev/rules.d'
+credstore_dir = '/run/credstore'
dnsmasq_pid_file = '/run/networkd-ci/test-dnsmasq.pid'
dnsmasq_log_file = '/run/networkd-ci/test-dnsmasq.log'
@@ -39,18 +51,18 @@ radvd_pid_file = '/run/networkd-ci/test-radvd.pid'
systemd_lib_paths = ['/usr/lib/systemd', '/lib/systemd']
which_paths = ':'.join(systemd_lib_paths + os.getenv('PATH', os.defpath).lstrip(':').split(':'))
-systemd_source_dir = None
networkd_bin = shutil.which('systemd-networkd', path=which_paths)
resolved_bin = shutil.which('systemd-resolved', path=which_paths)
timesyncd_bin = shutil.which('systemd-timesyncd', path=which_paths)
-udevd_bin = shutil.which('systemd-udevd', path=which_paths)
wait_online_bin = shutil.which('systemd-networkd-wait-online', path=which_paths)
networkctl_bin = shutil.which('networkctl', path=which_paths)
resolvectl_bin = shutil.which('resolvectl', path=which_paths)
timedatectl_bin = shutil.which('timedatectl', path=which_paths)
udevadm_bin = shutil.which('udevadm', path=which_paths)
-systemd_udev_rules_build_dir = None
+test_ndisc_send = None
+build_dir = None
+source_dir = None
use_valgrind = False
valgrind_cmd = ''
@@ -61,6 +73,7 @@ asan_options = None
lsan_options = None
ubsan_options = None
with_coverage = False
+show_journal = True # When true, show journal on stopping networkd.
active_units = []
protected_links = {
@@ -93,7 +106,7 @@ def cp(src, dst):
shutil.copy(src, dst)
def cp_r(src, dst):
- shutil.copytree(src, dst, copy_function=shutil.copy)
+ shutil.copytree(src, dst, copy_function=shutil.copy, dirs_exist_ok=True)
def mkdir_p(path):
os.makedirs(path, exist_ok=True)
@@ -180,8 +193,10 @@ def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable():
def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
def f(func):
- rc = call_quiet('ip rule add not from 192.168.100.19 ipproto tcp table 7')
- call_quiet('ip rule del not from 192.168.100.19 ipproto tcp table 7')
+ # IP protocol name is parsed by getprotobyname(), and it requires /etc/protocols.
+ # Hence. here we use explicit number: 6 == tcp.
+ rc = call_quiet('ip rule add not from 192.168.100.19 ipproto 6 table 7')
+ call_quiet('ip rule del not from 192.168.100.19 ipproto 6 table 7')
return func if rc == 0 else unittest.expectedFailure(func)
return f
@@ -197,6 +212,14 @@ def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable():
return f
+def expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable():
+ def f(func):
+ rc = call_quiet('ip rule add not from 192.168.100.19 l3mdev')
+ call_quiet('ip rule del not from 192.168.100.19 l3mdev')
+ return func if rc == 0 else unittest.expectedFailure(func)
+
+ return f
+
def expectedFailureIfNexthopIsNotAvailable():
def f(func):
rc = call_quiet('ip nexthop list')
@@ -246,6 +269,21 @@ def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
return f
+def expectedFailureIfKernelReturnsInvalidFlags():
+ '''
+ This checks the kernel bug caused by 3ddc2231c8108302a8229d3c5849ee792a63230d (v6.9),
+ fixed by 1af7f88af269c4e06a4dc3bc920ff6cdf7471124 (v6.10, backported to 6.9.3).
+ '''
+ def f(func):
+ call_quiet('ip link add dummy98 type dummy')
+ call_quiet('ip link set up dev dummy98')
+ call_quiet('ip address add 192.0.2.1/24 dev dummy98 noprefixroute')
+ output = check_output('ip address show dev dummy98')
+ remove_link('dummy98')
+ return func if 'noprefixroute' in output else unittest.expectedFailure(func)
+
+ return f
+
# pylint: disable=C0415
def compare_kernel_version(min_kernel_version):
try:
@@ -258,12 +296,11 @@ def compare_kernel_version(min_kernel_version):
# Get only the actual kernel version without any build/distro/arch stuff
# e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
kver = platform.release().split('-')[0]
+ # Get also rid of '+'
+ kver = kver.split('+')[0]
return version.parse(kver) >= version.parse(min_kernel_version)
-def udev_reload():
- check_output(*udevadm_cmd, 'control', '--reload')
-
def copy_network_unit(*units, copy_dropins=True):
"""
Copy networkd unit files into the testbed.
@@ -294,7 +331,12 @@ def copy_network_unit(*units, copy_dropins=True):
has_link = True
if has_link:
- udev_reload()
+ udevadm_reload()
+
+def copy_credential(src, target):
+ mkdir_p(credstore_dir)
+ cp(os.path.join(networkd_ci_temp_dir, src),
+ os.path.join(credstore_dir, target))
def remove_network_unit(*units):
"""
@@ -311,7 +353,7 @@ def remove_network_unit(*units):
has_link = True
if has_link:
- udev_reload()
+ udevadm_reload()
def clear_network_units():
has_link = False
@@ -324,7 +366,7 @@ def clear_network_units():
rm_rf(network_unit_dir)
if has_link:
- udev_reload()
+ udevadm_reload()
def copy_networkd_conf_dropin(*dropins):
"""Copy networkd.conf dropin files into the testbed."""
@@ -341,12 +383,16 @@ def clear_networkd_conf_dropins():
rm_rf(networkd_conf_dropin_dir)
def setup_systemd_udev_rules():
- if not systemd_udev_rules_build_dir:
+ if not build_dir and not source_dir:
return
mkdir_p(udev_rules_dir)
- for path in [systemd_udev_rules_build_dir, os.path.join(systemd_source_dir, "rules.d")]:
+ for path in [build_dir, source_dir]:
+ if not path:
+ continue
+
+ path = os.path.join(path, "rules.d")
print(f"Copying udev rules from {path} to {udev_rules_dir}")
for rule in os.listdir(path):
@@ -354,6 +400,9 @@ def setup_systemd_udev_rules():
continue
cp(os.path.join(path, rule), udev_rules_dir)
+def clear_networkd_state_files():
+ rm_rf('/var/lib/systemd/network/')
+
def copy_udev_rule(*rules):
"""Copy udev rules"""
mkdir_p(udev_rules_dir)
@@ -388,11 +437,12 @@ def create_unit_dropin(unit, contents):
f.write('\n'.join(contents))
def create_service_dropin(service, command, additional_settings=None):
- drop_in = [
- '[Service]',
- 'ExecStart=',
- f'ExecStart=!!{valgrind_cmd}{command}',
- ]
+ drop_in = ['[Service]']
+ if command:
+ drop_in += [
+ 'ExecStart=',
+ f'ExecStart=!!{valgrind_cmd}{command}',
+ ]
if enable_debug:
drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
if asan_options:
@@ -420,11 +470,99 @@ def create_service_dropin(service, command, additional_settings=None):
create_unit_dropin(f'{service}.service', drop_in)
-def link_exists(link):
- return call_quiet(f'ip link show {link}') == 0
+def setup_system_units():
+ if build_dir or source_dir:
+ mkdir_p('/run/systemd/system/')
+
+ for unit in [
+ 'systemd-networkd.service',
+ 'systemd-networkd.socket',
+ 'systemd-networkd-persistent-storage.service',
+ 'systemd-resolved.service',
+ 'systemd-timesyncd.service',
+ 'systemd-udevd.service',
+ ]:
+ for path in [build_dir, source_dir]:
+ if not path:
+ continue
+
+ fullpath = os.path.join(os.path.join(path, "units"), unit)
+ if os.path.exists(fullpath):
+ print(f"Copying unit file from {fullpath} to /run/systemd/system/")
+ cp(fullpath, '/run/systemd/system/')
+ break
+
+ create_service_dropin('systemd-networkd', networkd_bin,
+ ['[Service]',
+ 'Restart=no',
+ 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
+ '[Unit]',
+ 'StartLimitIntervalSec=0'])
+ create_service_dropin('systemd-resolved', resolved_bin)
+ create_service_dropin('systemd-timesyncd', timesyncd_bin)
+
+ # TODO: also run udevd with sanitizers, valgrind, or coverage
+ create_unit_dropin(
+ 'systemd-udevd.service',
+ [
+ '[Service]',
+ 'ExecStart=',
+ f'ExecStart=!!@{udevadm_bin} systemd-udevd',
+ ]
+ )
+ create_unit_dropin(
+ 'systemd-networkd.socket',
+ [
+ '[Unit]',
+ 'StartLimitIntervalSec=0',
+ ]
+ )
+ create_unit_dropin(
+ 'systemd-networkd-persistent-storage.service',
+ [
+ '[Unit]',
+ 'StartLimitIntervalSec=0',
+ '[Service]',
+ 'ExecStart=',
+ f'ExecStart={networkctl_bin} persistent-storage yes',
+ 'ExecStop=',
+ f'ExecStop={networkctl_bin} persistent-storage no',
+ 'Environment=SYSTEMD_LOG_LEVEL=debug' if enable_debug else '',
+ ]
+ )
+
+ check_output('systemctl daemon-reload')
+ print(check_output('systemctl cat systemd-networkd.service'))
+ print(check_output('systemctl cat systemd-networkd-persistent-storage.service'))
+ print(check_output('systemctl cat systemd-resolved.service'))
+ print(check_output('systemctl cat systemd-timesyncd.service'))
+ print(check_output('systemctl cat systemd-udevd.service'))
+ check_output('systemctl restart systemd-resolved.service')
+ check_output('systemctl restart systemd-timesyncd.service')
+ check_output('systemctl restart systemd-udevd.service')
+
+def clear_system_units():
+ def rm_unit(name):
+ rm_f(f'/run/systemd/system/{name}')
+ rm_rf(f'/run/systemd/system/{name}.d')
+
+ rm_unit('systemd-networkd.service')
+ rm_unit('systemd-networkd.socket')
+ rm_unit('systemd-networkd-persistent-storage.service')
+ rm_unit('systemd-resolved.service')
+ rm_unit('systemd-timesyncd.service')
+ rm_unit('systemd-udevd.service')
+ check_output('systemctl daemon-reload')
+ check_output('systemctl restart systemd-udevd.service')
+
+def link_exists(*links):
+ for link in links:
+ if call_quiet(f'ip link show {link}') != 0:
+ return False
+ return True
def link_resolve(link):
- return check_output(f'ip link show {link}').split(':')[1].strip()
+ return check_output(f'ip link show {link}').split(':')[1].strip().split('@')[0]
def remove_link(*links, protect=False):
for link in links:
@@ -442,6 +580,15 @@ def save_existing_links():
print('### The following links will be protected:')
print(', '.join(sorted(list(protected_links))))
+def unmanage_existing_links():
+ mkdir_p(network_unit_dir)
+
+ with open(os.path.join(network_unit_dir, '00-unmanaged.network'), mode='w', encoding='utf-8') as f:
+ f.write('[Match]\n')
+ for link in protected_links:
+ f.write(f'Name={link}\n')
+ f.write('\n[Link]\nUnmanaged=yes\n')
+
def flush_links():
links = os.listdir('/sys/class/net')
remove_link(*links, protect=True)
@@ -565,9 +712,16 @@ def read_ip_sysctl_attr(link, attribute, ipv):
with open(os.path.join('/proc/sys/net', ipv, 'conf', link, attribute), encoding='utf-8') as f:
return f.readline().strip()
+def read_ip_neigh_sysctl_attr(link, attribute, ipv):
+ with open(os.path.join('/proc/sys/net', ipv, 'neigh', link, attribute), encoding='utf-8') as f:
+ return f.readline().strip()
+
def read_ipv6_sysctl_attr(link, attribute):
return read_ip_sysctl_attr(link, attribute, 'ipv6')
+def read_ipv6_neigh_sysctl_attr(link, attribute):
+ return read_ip_neigh_sysctl_attr(link, attribute, 'ipv6')
+
def read_ipv4_sysctl_attr(link, attribute):
return read_ip_sysctl_attr(link, attribute, 'ipv4')
@@ -700,11 +854,16 @@ def radvd_check_config(config_file):
def networkd_invocation_id():
return check_output('systemctl show --value -p InvocationID systemd-networkd.service')
+def networkd_pid():
+ return check_output('systemctl show --value -p MainPID systemd-networkd.service')
+
def read_networkd_log(invocation_id=None, since=None):
if not invocation_id:
invocation_id = networkd_invocation_id()
command = [
'journalctl',
+ '--no-hostname',
+ '--output=short-monotonic',
f'_SYSTEMD_INVOCATION_ID={invocation_id}',
]
if since:
@@ -716,23 +875,36 @@ def networkd_is_failed():
return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1
def stop_networkd(show_logs=True):
+ global show_journal
+ show_logs = show_logs and show_journal
if show_logs:
invocation_id = networkd_invocation_id()
check_output('systemctl stop systemd-networkd.socket')
check_output('systemctl stop systemd-networkd.service')
if show_logs:
print(read_networkd_log(invocation_id))
+ # Check if networkd exits cleanly.
+ assert not networkd_is_failed()
def start_networkd():
check_output('systemctl start systemd-networkd')
+ invocation_id = networkd_invocation_id()
+ pid = networkd_pid()
+ print(f'Started systemd-networkd.service: PID={pid}, Invocation ID={invocation_id}')
def restart_networkd(show_logs=True):
+ global show_journal
+ show_logs = show_logs and show_journal
if show_logs:
invocation_id = networkd_invocation_id()
check_output('systemctl restart systemd-networkd.service')
if show_logs:
print(read_networkd_log(invocation_id))
+ invocation_id = networkd_invocation_id()
+ pid = networkd_pid()
+ print(f'Restarted systemd-networkd.service: PID={pid}, Invocation ID={invocation_id}')
+
def networkd_pid():
return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
@@ -751,12 +923,8 @@ def networkctl_json(*args):
def networkctl_reconfigure(*links):
networkctl('reconfigure', *links)
-def networkctl_reload(sleep_time=1):
+def networkctl_reload():
networkctl('reload')
- # 'networkctl reload' asynchronously reconfigure links.
- # Hence, we need to wait for a short time for link to be in configuring state.
- if sleep_time > 0:
- time.sleep(sleep_time)
def resolvectl(*args):
return check_output(*(resolvectl_cmd + list(args)), env=env)
@@ -764,8 +932,24 @@ def resolvectl(*args):
def timedatectl(*args):
return check_output(*(timedatectl_cmd + list(args)), env=env)
+def udevadm(*args):
+ return check_output(*(udevadm_cmd + list(args)))
+
+def udevadm_reload():
+ udevadm('control', '--reload')
+
+def udevadm_trigger(*args, action='add'):
+ udevadm('trigger', '--settle', f'--action={action}', *args)
+
def setup_common():
+ # Protect existing links
+ unmanage_existing_links()
+
+ # We usually show something in each test. So, let's break line to make the title of a test and output
+ # from the test mixed. Then, flush stream buffer and journals.
print()
+ sys.stdout.flush()
+ check_output('journalctl --sync')
def tear_down_common():
# 1. stop DHCP/RA servers
@@ -790,6 +974,7 @@ def tear_down_common():
# 6. remove configs
clear_network_units()
clear_networkd_conf_dropins()
+ clear_networkd_state_files()
# 7. flush settings
flush_fou_ports()
@@ -797,12 +982,17 @@ def tear_down_common():
flush_routing_policy_rules()
flush_routes()
+ # 8. flush stream buffer and journals to make not any output from the test with the next one
+ sys.stdout.flush()
+ check_output('journalctl --sync')
+
def setUpModule():
rm_rf(networkd_ci_temp_dir)
cp_r(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf'), networkd_ci_temp_dir)
clear_network_units()
clear_networkd_conf_dropins()
+ clear_networkd_state_files()
clear_udev_rules()
setup_systemd_udev_rules()
@@ -815,68 +1005,32 @@ def setUpModule():
save_routing_policy_rules()
save_timezone()
- create_service_dropin('systemd-networkd', networkd_bin,
- ['[Service]',
- 'Restart=no',
- 'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
- '[Unit]',
- 'StartLimitIntervalSec=0'])
- create_service_dropin('systemd-resolved', resolved_bin)
- create_service_dropin('systemd-timesyncd', timesyncd_bin)
-
- # TODO: also run udevd with sanitizers, valgrind, or coverage
- #create_service_dropin('systemd-udevd', udevd_bin,
- # f'{udevadm_bin} control --reload --timeout 0')
- create_unit_dropin(
- 'systemd-udevd.service',
- [
- '[Service]',
- 'ExecStart=',
- f'ExecStart=!!@{udevd_bin} systemd-udevd',
- ]
- )
- create_unit_dropin(
- 'systemd-networkd.socket',
- [
- '[Unit]',
- 'StartLimitIntervalSec=0',
- ]
- )
-
- check_output('systemctl daemon-reload')
- print(check_output('systemctl cat systemd-networkd.service'))
- print(check_output('systemctl cat systemd-resolved.service'))
- print(check_output('systemctl cat systemd-timesyncd.service'))
- print(check_output('systemctl cat systemd-udevd.service'))
- check_output('systemctl restart systemd-resolved.service')
- check_output('systemctl restart systemd-timesyncd.service')
- check_output('systemctl restart systemd-udevd.service')
+ setup_system_units()
def tearDownModule():
rm_rf(networkd_ci_temp_dir)
clear_udev_rules()
clear_network_units()
clear_networkd_conf_dropins()
+ clear_networkd_state_files()
restore_timezone()
- rm_rf('/run/systemd/system/systemd-networkd.service.d')
- rm_rf('/run/systemd/system/systemd-networkd.socket.d')
- rm_rf('/run/systemd/system/systemd-resolved.service.d')
- rm_rf('/run/systemd/system/systemd-timesyncd.service.d')
- rm_rf('/run/systemd/system/systemd-udevd.service.d')
- check_output('systemctl daemon-reload')
- check_output('systemctl restart systemd-udevd.service')
+ clear_system_units()
restore_active_units()
+ # Flush stream buffer and journals before showing the test summary.
+ sys.stdout.flush()
+ check_output('journalctl --sync')
+
class Utilities():
# pylint: disable=no-member
- def check_link_exists(self, link, expected=True):
+ def check_link_exists(self, *link, expected=True):
if expected:
- self.assertTrue(link_exists(link))
+ self.assertTrue(link_exists(*link))
else:
- self.assertFalse(link_exists(link))
+ self.assertFalse(link_exists(*link))
def check_link_attr(self, *args):
self.assertEqual(read_link_attr(*args[:-1]), args[-1])
@@ -894,38 +1048,29 @@ class Utilities():
def check_ipv6_sysctl_attr(self, link, attribute, expected):
self.assertEqual(read_ipv6_sysctl_attr(link, attribute), expected)
- def wait_links(self, *links, timeout=20, fail_assert=True):
- def links_exist(*links):
- for link in links:
- if not link_exists(link):
- return False
- return True
-
- for iteration in range(timeout + 1):
- if iteration > 0:
- time.sleep(1)
+ def check_ipv6_neigh_sysctl_attr(self, link, attribute, expected):
+ self.assertEqual(read_ipv6_neigh_sysctl_attr(link, attribute), expected)
- if links_exist(*links):
- return True
- if fail_assert:
+ def wait_links(self, *links, trial=40):
+ for _ in range(trial):
+ if link_exists(*links):
+ break
+ time.sleep(0.5)
+ else:
self.fail('Timed out waiting for all links to be created: ' + ', '.join(list(links)))
- return False
- def wait_activated(self, link, state='down', timeout=20, fail_assert=True):
+ def wait_activated(self, link, state='down', trial=40):
# wait for the interface is activated.
needle = f'{link}: Bringing link {state}'
flag = state.upper()
- for iteration in range(timeout + 1):
- if iteration != 0:
- time.sleep(1)
- if not link_exists(link):
- continue
- output = read_networkd_log()
- if needle in output and flag in check_output(f'ip link show {link}'):
- return True
- if fail_assert:
+ self.wait_links(link, trial=trial)
+ self.check_networkd_log(needle, trial=trial)
+ for _ in range(trial):
+ if flag in check_output(f'ip link show {link}'):
+ break
+ time.sleep(0.5)
+ else:
self.fail(f'Timed out waiting for {link} activated.')
- return False
def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True):
"""Wait for the link to reach the specified operstate and/or setup state.
@@ -946,20 +1091,20 @@ class Utilities():
if not setup_state:
setup_state = r'\S+'
- for secs in range(setup_timeout + 1):
- if secs != 0:
- time.sleep(1)
+ for _ in range(setup_timeout * 2):
if not link_exists(link):
+ time.sleep(0.5)
continue
output = networkctl_status(link)
if re.search(rf'(?m)^\s*State:\s+{operstate}\s+\({setup_state}\)\s*$', output):
return True
+ time.sleep(0.5)
if fail_assert:
self.fail(f'Timed out waiting for {link} to reach state {operstate}/{setup_state}')
return False
- def wait_online(self, links_with_operstate, timeout='20s', bool_any=False, ipv4=False, ipv6=False, setup_state='configured', setup_timeout=5):
+ def wait_online(self, *links_with_operstate, timeout='20s', bool_any=False, ipv4=False, ipv6=False, setup_state='configured', setup_timeout=5):
"""Wait for the links to reach the specified operstate and/or setup state.
This is similar to wait_operstate() but can be used for multiple links,
@@ -1000,42 +1145,50 @@ class Utilities():
for link in links_with_operstate:
name = link.split(':')[0]
if link_exists(name):
- networkctl_status(name)
+ print(networkctl_status(name))
+ else:
+ print(f'Interface {name} not found.')
raise
if not bool_any and setup_state:
for link in links_with_operstate:
self.wait_operstate(link.split(':')[0], None, setup_state, setup_timeout)
def wait_address(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
- for i in range(timeout_sec):
- if i > 0:
- time.sleep(1)
+ for _ in range(timeout_sec * 2):
output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
if re.search(address_regex, output) and 'tentative' not in output:
break
+ time.sleep(0.5)
self.assertRegex(output, address_regex)
def wait_address_dropped(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
- for i in range(timeout_sec):
- if i > 0:
- time.sleep(1)
+ for _ in range(timeout_sec * 2):
output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
if not re.search(address_regex, output):
break
+ time.sleep(0.5)
self.assertNotRegex(output, address_regex)
def wait_route(self, link, route_regex, table='main', ipv='', timeout_sec=100):
- for i in range(timeout_sec):
- if i > 0:
- time.sleep(1)
+ for _ in range(timeout_sec * 2):
output = check_output(f'ip {ipv} route show dev {link} table {table}')
if re.search(route_regex, output):
break
+ time.sleep(0.5)
self.assertRegex(output, route_regex)
+ def wait_route_dropped(self, link, route_regex, table='main', ipv='', timeout_sec=100):
+ for _ in range(timeout_sec * 2):
+ output = check_output(f'ip {ipv} route show dev {link} table {table}')
+ if not re.search(route_regex, output):
+ break
+ time.sleep(0.5)
+
+ self.assertNotRegex(output, route_regex)
+
def check_netlabel(self, interface, address, label='system_u:object_r:root_t:s0'):
if not shutil.which('selinuxenabled'):
print('## Checking NetLabel skipped: selinuxenabled command not found.')
@@ -1079,6 +1232,14 @@ class Utilities():
print(output)
self.assertRegex(output, r'.*elements = { [^}]*' + contents + r'[^}]* }.*')
+ def check_networkd_log(self, contents, since=None, trial=20):
+ for _ in range(trial):
+ if contents in read_networkd_log(since=since):
+ break
+ time.sleep(0.5)
+ else:
+ self.fail(f'"{contents}" not found in journal.')
+
class NetworkctlTests(unittest.TestCase, Utilities):
def setUp(self):
@@ -1091,7 +1252,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
def test_altname(self):
copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
start_networkd()
- self.wait_online(['dummy98:degraded'])
+ self.wait_online('dummy98:degraded')
output = networkctl_status('dummy98')
self.assertRegex(output, 'hogehogehogehogehogehoge')
@@ -1101,16 +1262,16 @@ class NetworkctlTests(unittest.TestCase, Utilities):
copy_network_unit('26-netdev-link-local-addressing-yes.network',
'12-dummy.netdev', '12-dummy-rename-to-altname.link')
start_networkd()
- self.wait_online(['dummyalt:degraded'])
+ self.wait_online('dummyalt:degraded')
output = networkctl_status('dummyalt')
self.assertIn('hogehogehogehogehogehoge', output)
self.assertNotIn('dummy98', output)
def test_reconfigure(self):
- copy_network_unit('25-address-static.network', '12-dummy.netdev')
+ copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('ip -4 address show dev dummy98')
print(output)
@@ -1123,7 +1284,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
check_output('ip address del 10.2.2.4/16 dev dummy98')
networkctl_reconfigure('dummy98')
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('ip -4 address show dev dummy98')
print(output)
@@ -1142,9 +1303,9 @@ class NetworkctlTests(unittest.TestCase, Utilities):
self.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
self.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
- copy_network_unit('25-address-static.network')
+ copy_network_unit('25-address-static.network', copy_dropins=False)
networkctl_reload()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('ip -4 address show dev dummy98')
print(output)
@@ -1154,10 +1315,10 @@ class NetworkctlTests(unittest.TestCase, Utilities):
def test_renew(self):
def check():
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
output = networkctl_status('veth99')
print(output)
- self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
+ self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
self.assertIn('Gateway: 192.168.5.3', output)
self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
@@ -1174,18 +1335,18 @@ class NetworkctlTests(unittest.TestCase, Utilities):
check()
def test_up_down(self):
- copy_network_unit('25-address-static.network', '12-dummy.netdev')
+ copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
networkctl('down', 'dummy98')
- self.wait_online(['dummy98:off'])
+ self.wait_online('dummy98:off')
networkctl('up', 'dummy98')
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
networkctl('down', 'dummy98', 'dummy98', 'dummy98')
- self.wait_online(['dummy98:off'])
+ self.wait_online('dummy98:off')
networkctl('up', 'dummy98', 'dummy98', 'dummy98')
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
def test_reload(self):
start_networkd()
@@ -1196,7 +1357,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
copy_network_unit('11-dummy.network')
networkctl_reload()
- self.wait_online(['test1:degraded'])
+ self.wait_online('test1:degraded')
remove_network_unit('11-dummy.network')
networkctl_reload()
@@ -1214,7 +1375,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
copy_network_unit('11-dummy.netdev', '11-dummy.network')
start_networkd()
- self.wait_online(['test1:degraded'])
+ self.wait_online('test1:degraded')
output = networkctl('list')
self.assertRegex(output, '1 lo ')
@@ -1240,7 +1401,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
copy_network_unit('11-dummy-mtu.netdev', '11-dummy.network')
start_networkd()
- self.wait_online(['test1:degraded'])
+ self.wait_online('test1:degraded')
output = networkctl_status('test1')
self.assertRegex(output, 'MTU: 1600')
@@ -1248,7 +1409,7 @@ class NetworkctlTests(unittest.TestCase, Utilities):
def test_type(self):
copy_network_unit('11-dummy.netdev', '11-dummy.network')
start_networkd()
- self.wait_online(['test1:degraded'])
+ self.wait_online('test1:degraded')
output = networkctl_status('test1')
print(output)
@@ -1258,36 +1419,35 @@ class NetworkctlTests(unittest.TestCase, Utilities):
print(output)
self.assertRegex(output, 'Type: loopback')
- def test_udev_link_file(self):
- copy_network_unit('11-dummy.netdev', '11-dummy.network', '25-default.link')
+ def test_unit_file(self):
+ copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
start_networkd()
- self.wait_online(['test1:degraded'])
+ self.wait_online('test1:degraded')
output = networkctl_status('test1')
print(output)
- self.assertRegex(output, r'Link File: /run/systemd/network/25-default.link')
- self.assertRegex(output, r'Network File: /run/systemd/network/11-dummy.network')
+ self.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output)
+ self.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output)
+ self.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output)
+ self.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output)
+
+ self.check_networkd_log('test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).')
# This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
# In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
# Let's reprocess the interface and drop the property.
- check_output(*udevadm_cmd, 'trigger', '--settle', '--action=add', '/sys/class/net/lo')
+ udevadm_trigger('/sys/class/net/lo')
output = networkctl_status('lo')
print(output)
- self.assertRegex(output, r'Link File: n/a')
- self.assertRegex(output, r'Network File: n/a')
+ self.assertIn('Link File: n/a', output)
+ self.assertIn('Network File: n/a', output)
def test_delete_links(self):
- copy_network_unit('11-dummy.netdev', '11-dummy.network',
- '25-veth.netdev', '26-netdev-link-local-addressing-yes.network')
+ copy_network_unit('11-dummy.netdev', '25-veth.netdev')
start_networkd()
-
- self.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
-
+ self.wait_links('test1', 'veth99', 'veth-peer')
networkctl('delete', 'test1', 'veth99')
- self.check_link_exists('test1', expected=False)
- self.check_link_exists('veth99', expected=False)
- self.check_link_exists('veth-peer', expected=False)
+ self.check_link_exists('test1', 'veth99', 'veth-peer', expected=False)
def test_label(self):
networkctl('label')
@@ -1310,7 +1470,7 @@ class NetworkdMatchTests(unittest.TestCase, Utilities):
'12-dummy-altname.link')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = networkctl_status('dummy98')
self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-01.network', output)
output = check_output('ip -4 address show dev dummy98')
@@ -1320,7 +1480,7 @@ class NetworkdMatchTests(unittest.TestCase, Utilities):
check_output('ip link set dev dummy98 address 12:34:56:78:9a:02')
self.wait_address('dummy98', '10.0.0.2/16', ipv='-4', timeout_sec=10)
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = networkctl_status('dummy98')
self.assertIn('Network File: /run/systemd/network/12-dummy-match-mac-02.network', output)
@@ -1328,23 +1488,23 @@ class NetworkdMatchTests(unittest.TestCase, Utilities):
check_output('ip link set dev dummy98 name dummy98-1')
self.wait_address('dummy98-1', '10.0.1.2/16', ipv='-4', timeout_sec=10)
- self.wait_online(['dummy98-1:routable'])
+ self.wait_online('dummy98-1:routable')
output = networkctl_status('dummy98-1')
self.assertIn('Network File: /run/systemd/network/12-dummy-match-renamed.network', output)
check_output('ip link set dev dummy98-1 down')
check_output('ip link set dev dummy98-1 name dummy98-2')
- check_output(*udevadm_cmd, 'trigger', '--action=add', '/sys/class/net/dummy98-2')
+ udevadm_trigger('/sys/class/net/dummy98-2')
self.wait_address('dummy98-2', '10.0.2.2/16', ipv='-4', timeout_sec=10)
- self.wait_online(['dummy98-2:routable'])
+ self.wait_online('dummy98-2:routable')
output = networkctl_status('dummy98-2')
self.assertIn('Network File: /run/systemd/network/12-dummy-match-altname.network', output)
def test_match_udev_property(self):
copy_network_unit('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = networkctl_status('dummy98')
print(output)
@@ -1362,7 +1522,7 @@ class WaitOnlineTests(unittest.TestCase, Utilities):
copy_network_unit('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
start_networkd()
- self.wait_online(['bridge99', 'test1:degraded'], bool_any=True)
+ self.wait_online('bridge99', 'test1:degraded', bool_any=True)
self.wait_operstate('bridge99', '(off|no-carrier)', setup_state='configuring')
self.wait_operstate('test1', 'degraded')
@@ -1379,7 +1539,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
copy_network_unit('10-dropin-test.netdev', '15-name-conflict-test.netdev')
start_networkd()
- self.wait_online(['dropin-test:off'], setup_state='unmanaged')
+ self.wait_online('dropin-test:off', setup_state='unmanaged')
output = check_output('ip link show dropin-test')
print(output)
@@ -1390,7 +1550,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
copy_network_unit('25-bareudp.netdev', '26-netdev-link-local-addressing-yes.network')
start_networkd()
- self.wait_online(['bareudp99:degraded'])
+ self.wait_online('bareudp99:degraded')
output = check_output('ip -d link show bareudp99')
print(output)
@@ -1402,7 +1562,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
copy_network_unit('25-batadv.netdev', '26-netdev-link-local-addressing-yes.network')
start_networkd()
- self.wait_online(['batadv99:degraded'])
+ self.wait_online('batadv99:degraded')
output = check_output('ip -d link show batadv99')
print(output)
@@ -1412,7 +1572,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
copy_network_unit('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
start_networkd()
- self.wait_online(['bridge99:no-carrier'])
+ self.wait_online('bridge99:no-carrier')
tick = os.sysconf('SC_CLK_TCK')
self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick))
@@ -1438,10 +1598,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
self.assertIn('vlan_default_pvid 9 ', output)
def test_bond(self):
- copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev')
+ copy_network_unit('25-bond.netdev', '25-bond-balanced-tlb.netdev', '25-bond-property.netdev')
start_networkd()
- self.wait_online(['bond99:off', 'bond98:off'], setup_state='unmanaged')
+ self.wait_online('bond99:off', 'bond98:off', 'bond97:off', setup_state='unmanaged')
self.check_link_attr('bond99', 'bonding', 'mode', '802.3ad 4')
self.check_link_attr('bond99', 'bonding', 'xmit_hash_policy', 'layer3+4 1')
@@ -1469,12 +1629,18 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
print(output)
self.assertIn('Mode: balance-tlb', output)
+ output = networkctl_status('bond97')
+ print(output)
+
+ self.check_link_attr('bond97', 'bonding', 'arp_missed_max', '10')
+ self.check_link_attr('bond97', 'bonding', 'peer_notif_delay', '300000')
+
def test_vlan(self):
copy_network_unit('21-vlan.netdev', '11-dummy.netdev',
'21-vlan.network', '21-vlan-test1.network')
start_networkd()
- self.wait_online(['test1:degraded', 'vlan99:routable'])
+ self.wait_online('test1:degraded', 'vlan99:routable')
output = check_output('ip -d link show test1')
print(output)
@@ -1507,23 +1673,14 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
copy_network_unit('21-bond-802.3ad.netdev', '21-bond-802.3ad.network',
'21-vlan-on-bond.netdev', '21-vlan-on-bond.network')
start_networkd()
- self.wait_online(['bond99:off'])
+ self.wait_online('bond99:off')
self.wait_operstate('vlan99', operstate='off', setup_state='configuring', setup_timeout=10)
- # The commit b05e52000b4eee764b383cc3031da0a3739e996e adds ", ignoring". To make it easily confirmed
- # that the issue is fixed by the commit, let's allow to match both string.
- log_re = re.compile('vlan99: Could not bring up interface(, ignoring|): Network is down$', re.MULTILINE)
- for i in range(20):
- if i > 0:
- time.sleep(0.5)
- if log_re.search(read_networkd_log()):
- break
- else:
- self.fail()
+ self.check_networkd_log('vlan99: Could not bring up interface, ignoring: Network is down')
copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '21-dummy-bond-slave.network')
networkctl_reload()
- self.wait_online(['test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable'])
+ self.wait_online('test1:enslaved', 'dummy98:enslaved', 'bond99:carrier', 'vlan99:routable')
def test_macvtap(self):
first = True
@@ -1541,13 +1698,14 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
f.write('[MACVTAP]\nMode=' + mode)
start_networkd()
- self.wait_online(['macvtap99:degraded',
- 'test1:carrier' if mode == 'passthru' else 'test1:degraded'])
+ self.wait_online('macvtap99:degraded',
+ 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
output = check_output('ip -d link show macvtap99')
print(output)
self.assertRegex(output, 'macvtap mode ' + mode + ' ')
+ @expectedFailureIfModuleIsNotAvailable('macvlan')
def test_macvlan(self):
first = True
for mode in ['private', 'vepa', 'bridge', 'passthru']:
@@ -1564,33 +1722,36 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
f.write('[MACVLAN]\nMode=' + mode)
start_networkd()
- self.wait_online(['macvlan99:degraded',
- 'test1:carrier' if mode == 'passthru' else 'test1:degraded'])
+ self.wait_online('macvlan99:degraded',
+ 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
output = check_output('ip -d link show test1')
print(output)
- self.assertRegex(output, ' mtu 2000 ')
+ self.assertIn(' mtu 2000 ', output)
output = check_output('ip -d link show macvlan99')
print(output)
- self.assertRegex(output, ' mtu 2000 ')
- self.assertRegex(output, 'macvlan mode ' + mode + ' ')
+ self.assertIn(' mtu 2000 ', output)
+ self.assertIn(f' macvlan mode {mode} ', output)
remove_link('test1')
time.sleep(1)
check_output("ip link add test1 type dummy")
- self.wait_online(['macvlan99:degraded',
- 'test1:carrier' if mode == 'passthru' else 'test1:degraded'])
+ self.wait_online('macvlan99:degraded',
+ 'test1:carrier' if mode == 'passthru' else 'test1:degraded')
output = check_output('ip -d link show test1')
print(output)
- self.assertRegex(output, ' mtu 2000 ')
+ self.assertIn(' mtu 2000 ', output)
output = check_output('ip -d link show macvlan99')
print(output)
- self.assertRegex(output, ' mtu 2000 ')
- self.assertRegex(output, 'macvlan mode ' + mode + ' ')
+ self.assertIn(' mtu 2000 ', output)
+ self.assertIn(f' macvlan mode {mode} ', output)
+ self.assertIn(' bcqueuelen 1234 ', output)
+ if ' bclim ' in output: # This is new in kernel and iproute2 v6.4
+ self.assertIn(' bclim 2147483647 ', output)
@expectedFailureIfModuleIsNotAvailable('ipvlan')
def test_ipvlan(self):
@@ -1609,7 +1770,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
f.write('[IPVLAN]\nMode=' + mode + '\nFlags=' + flag)
start_networkd()
- self.wait_online(['ipvlan99:degraded', 'test1:degraded'])
+ self.wait_online('ipvlan99:degraded', 'test1:degraded')
output = check_output('ip -d link show ipvlan99')
print(output)
@@ -1632,7 +1793,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
f.write('[IPVTAP]\nMode=' + mode + '\nFlags=' + flag)
start_networkd()
- self.wait_online(['ipvtap99:degraded', 'test1:degraded'])
+ self.wait_online('ipvtap99:degraded', 'test1:degraded')
output = check_output('ip -d link show ipvtap99')
print(output)
@@ -1643,7 +1804,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-veth-mtu.netdev')
start_networkd()
- self.wait_online(['veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded'])
+ self.wait_online('veth99:degraded', 'veth-peer:degraded', 'veth-mtu:degraded', 'veth-mtu-peer:degraded')
output = check_output('ip -d link show veth99')
print(output)
@@ -1661,91 +1822,82 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'link/ether 12:34:56:78:9a:bf')
self.assertRegex(output, 'mtu 1800')
- def test_tuntap(self):
- copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
- start_networkd()
-
- self.wait_online(['testtun99:degraded', 'testtap99:degraded'])
-
+ def check_tuntap(self, attached):
pid = networkd_pid()
name = psutil.Process(pid).name()[:15]
- output = check_output('ip -d tuntap show')
+ output = check_output('ip -d -oneline tuntap show')
print(output)
- self.assertRegex(output, fr'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
- self.assertRegex(output, fr'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
+ self.assertRegex(output, r'testtap99: tap pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:')
+ self.assertRegex(output, r'testtun99: tun pi (multi_queue |)vnet_hdr persist filter.*\tAttached to processes:')
- output = check_output('ip -d link show testtun99')
- print(output)
- # Old ip command does not support IFF_ flags
- self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
- self.assertIn('UP,LOWER_UP', output)
+ if attached:
+ self.assertRegex(output, fr'testtap99: .*{name}\({pid}\)')
+ self.assertRegex(output, fr'testtun99: .*{name}\({pid}\)')
+ self.assertRegex(output, r'testtap99: .*systemd\(1\)')
+ self.assertRegex(output, r'testtun99: .*systemd\(1\)')
- output = check_output('ip -d link show testtap99')
- print(output)
- self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
- self.assertIn('UP,LOWER_UP', output)
+ output = check_output('ip -d link show testtun99')
+ print(output)
+ # Old ip command does not support IFF_ flags
+ self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
+ self.assertIn('UP,LOWER_UP', output)
- remove_network_unit('26-netdev-link-local-addressing-yes.network')
+ output = check_output('ip -d link show testtap99')
+ print(output)
+ self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
+ self.assertIn('UP,LOWER_UP', output)
- restart_networkd()
- self.wait_online(['testtun99:degraded', 'testtap99:degraded'], setup_state='unmanaged')
+ else:
+ self.assertNotIn(f'{name}({pid})', output)
+ self.assertNotIn('systemd(1)', output)
- pid = networkd_pid()
- name = psutil.Process(pid).name()[:15]
+ for _ in range(20):
+ output = check_output('ip -d link show testtun99')
+ print(output)
+ self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
+ if 'NO-CARRIER' in output:
+ break
+ time.sleep(0.5)
+ else:
+ self.fail()
- output = check_output('ip -d tuntap show')
- print(output)
- self.assertRegex(output, fr'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
- self.assertRegex(output, fr'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
+ for _ in range(20):
+ output = check_output('ip -d link show testtap99')
+ print(output)
+ self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
+ if 'NO-CARRIER' in output:
+ break
+ time.sleep(0.5)
+ else:
+ self.fail()
- output = check_output('ip -d link show testtun99')
- print(output)
- self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
- self.assertIn('UP,LOWER_UP', output)
+ def test_tuntap(self):
+ copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
+ start_networkd()
+ self.wait_online('testtun99:degraded', 'testtap99:degraded')
- output = check_output('ip -d link show testtap99')
- print(output)
- self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
- self.assertIn('UP,LOWER_UP', output)
+ self.check_tuntap(True)
- clear_network_units()
+ remove_network_unit('26-netdev-link-local-addressing-yes.network')
restart_networkd()
- self.wait_online(['testtun99:off', 'testtap99:off'], setup_state='unmanaged')
+ self.wait_online('testtun99:degraded', 'testtap99:degraded', setup_state='unmanaged')
- output = check_output('ip -d tuntap show')
- print(output)
- self.assertRegex(output, r'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
- self.assertRegex(output, r'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
+ self.check_tuntap(True)
- for i in range(10):
- if i != 0:
- time.sleep(1)
- output = check_output('ip -d link show testtun99')
- print(output)
- self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
- if 'NO-CARRIER' in output:
- break
- else:
- self.fail()
+ clear_network_units()
+ unmanage_existing_links()
+ restart_networkd()
+ self.wait_online('testtun99:off', 'testtap99:off', setup_state='unmanaged')
- for i in range(10):
- if i != 0:
- time.sleep(1)
- output = check_output('ip -d link show testtap99')
- print(output)
- self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
- if 'NO-CARRIER' in output:
- break
- else:
- self.fail()
+ self.check_tuntap(False)
@expectedFailureIfModuleIsNotAvailable('vrf')
def test_vrf(self):
copy_network_unit('25-vrf.netdev', '26-netdev-link-local-addressing-yes.network')
start_networkd()
- self.wait_online(['vrf99:carrier'])
+ self.wait_online('vrf99:carrier')
@expectedFailureIfModuleIsNotAvailable('vcan')
def test_vcan(self):
@@ -1753,7 +1905,9 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-vcan98.netdev', '25-vcan98.network')
start_networkd()
- self.wait_online(['vcan99:carrier', 'vcan98:carrier'])
+ self.wait_online('vcan99:carrier', 'vcan98:carrier')
+ # For can devices, 'carrier' is the default required operational state.
+ self.wait_online('vcan99', 'vcan98')
# https://github.com/systemd/systemd/issues/30140
output = check_output('ip -d link show vcan99')
@@ -1769,16 +1923,22 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
copy_network_unit('25-vxcan.netdev', '26-netdev-link-local-addressing-yes.network')
start_networkd()
- self.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
+ self.wait_online('vxcan99:carrier', 'vxcan-peer:carrier')
+ # For can devices, 'carrier' is the default required operational state.
+ self.wait_online('vxcan99', 'vxcan-peer')
@expectedFailureIfModuleIsNotAvailable('wireguard')
def test_wireguard(self):
+ copy_credential('25-wireguard-endpoint-peer0-cred.txt', 'network.wireguard.peer0.endpoint')
+ copy_credential('25-wireguard-preshared-key-peer2-cred.txt', 'network.wireguard.peer2.psk')
+ copy_credential('25-wireguard-no-peer-private-key-cred.txt', 'network.wireguard.private.25-wireguard-no-peer')
+
copy_network_unit('25-wireguard.netdev', '25-wireguard.network',
'25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
'25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
'25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
start_networkd()
- self.wait_online(['wg99:routable', 'wg98:routable', 'wg97:carrier'])
+ self.wait_online('wg99:routable', 'wg98:routable', 'wg97:carrier')
output = check_output('ip -4 address show dev wg99')
print(output)
@@ -1802,7 +1962,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
output = check_output('ip -4 route show dev wg99 table 1234')
print(output)
- self.assertIn('192.168.26.0/24 proto static metric 123', output)
+ self.assertIn('192.168.26.0/24 proto static scope link metric 123', output)
output = check_output('ip -6 route show dev wg99 table 1234')
print(output)
@@ -1899,7 +2059,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
copy_network_unit('25-geneve.netdev', '26-netdev-link-local-addressing-yes.network')
start_networkd()
- self.wait_online(['geneve99:degraded'])
+ self.wait_online('geneve99:degraded')
output = check_output('ip -d link show geneve99')
print(output)
@@ -1915,7 +2075,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
'25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
start_networkd()
- self.wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded'])
+ self.wait_online('ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded')
output = check_output('ip -d link show ipiptun99')
print(output)
@@ -1937,7 +2097,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
'25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
start_networkd()
- self.wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded'])
+ self.wait_online('gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded')
output = check_output('ip -d link show gretun99')
print(output)
@@ -1998,7 +2158,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-gretap-tunnel.netdev', '25-tunnel.network',
'25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
start_networkd()
- self.wait_online(['gretap99:routable', 'gretap98:routable', 'dummy98:degraded'])
+ self.wait_online('gretap99:routable', 'gretap98:routable', 'dummy98:degraded')
output = check_output('ip -d link show gretap99')
print(output)
@@ -2022,7 +2182,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-ip6gretap-tunnel.netdev', '25-tunnel.network',
'25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
start_networkd()
- self.wait_online(['ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded'])
+ self.wait_online('ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded')
output = check_output('ip -d link show ip6gretap99')
print(output)
@@ -2038,7 +2198,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
'25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
start_networkd()
- self.wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded'])
+ self.wait_online('vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded')
output = check_output('ip -d link show vtitun99')
print(output)
@@ -2059,7 +2219,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
'25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
start_networkd()
- self.wait_online(['vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded'])
+ self.wait_online('vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded')
output = check_output('ip -d link show vti6tun99')
print(output)
@@ -2080,9 +2240,9 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network',
'25-ip6tnl-tunnel-external.netdev', '26-netdev-link-local-addressing-yes.network')
start_networkd()
- self.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
- 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
- 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded'])
+ self.wait_online('ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable',
+ 'ip6tnl-slaac:degraded', 'ip6tnl-external:degraded',
+ 'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded')
output = check_output('ip -d link show ip6tnl99')
print(output)
@@ -2116,7 +2276,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
'25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
start_networkd()
- self.wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded'])
+ self.wait_online('sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded')
output = check_output('ip -d link show sittun99')
print(output)
@@ -2135,7 +2295,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
copy_network_unit('12-dummy.netdev', '25-isatap.network',
'25-isatap-tunnel.netdev', '25-tunnel.network')
start_networkd()
- self.wait_online(['isataptun99:routable', 'dummy98:degraded'])
+ self.wait_online('isataptun99:routable', 'dummy98:degraded')
output = check_output('ip -d link show isataptun99')
print(output)
@@ -2145,7 +2305,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
copy_network_unit('12-dummy.netdev', '25-6rd.network',
'25-6rd-tunnel.netdev', '25-tunnel.network')
start_networkd()
- self.wait_online(['sittun99:routable', 'dummy98:degraded'])
+ self.wait_online('sittun99:routable', 'dummy98:degraded')
output = check_output('ip -d link show sittun99')
print(output)
@@ -2157,7 +2317,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-erspan0-tunnel.netdev', '25-tunnel.network',
'25-erspan0-tunnel-local-any.netdev', '25-tunnel-local-any.network')
start_networkd()
- self.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
+ self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
output = check_output('ip -d link show erspan99')
print(output)
@@ -2185,7 +2345,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-erspan1-tunnel.netdev', '25-tunnel.network',
'25-erspan1-tunnel-local-any.netdev', '25-tunnel-local-any.network')
start_networkd()
- self.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
+ self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
output = check_output('ip -d link show erspan99')
print(output)
@@ -2216,7 +2376,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-erspan2-tunnel.netdev', '25-tunnel.network',
'25-erspan2-tunnel-local-any.netdev', '25-tunnel-local-any.network')
start_networkd()
- self.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
+ self.wait_online('erspan99:routable', 'erspan98:routable', 'dummy98:degraded')
output = check_output('ip -d link show erspan99')
print(output)
@@ -2245,13 +2405,13 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
copy_network_unit('25-ipip-tunnel-independent.netdev', '26-netdev-link-local-addressing-yes.network')
start_networkd()
- self.wait_online(['ipiptun99:carrier'])
+ self.wait_online('ipiptun99:carrier')
def test_tunnel_independent_loopback(self):
copy_network_unit('25-ipip-tunnel-independent-loopback.netdev', '26-netdev-link-local-addressing-yes.network')
start_networkd()
- self.wait_online(['ipiptun99:carrier'])
+ self.wait_online('ipiptun99:carrier')
@expectedFailureIfModuleIsNotAvailable('xfrm_interface')
def test_xfrm(self):
@@ -2260,7 +2420,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'26-netdev-link-local-addressing-yes.network')
start_networkd()
- self.wait_online(['dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded'])
+ self.wait_online('dummy98:degraded', 'xfrm98:degraded', 'xfrm99:degraded')
output = check_output('ip -d link show dev xfrm98')
print(output)
@@ -2283,7 +2443,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-fou-gre.netdev', '25-fou-gretap.netdev')
start_networkd()
- self.wait_online(['ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off'], setup_state='unmanaged')
+ self.wait_online('ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off', setup_state='unmanaged')
output = check_output('ip fou show')
print(output)
@@ -2312,8 +2472,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-vxlan-local-slaac.netdev', '25-vxlan-local-slaac.network')
start_networkd()
- self.wait_online(['test1:degraded', 'veth99:routable', 'veth-peer:degraded',
- 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded'])
+ self.wait_online('test1:degraded', 'veth99:routable', 'veth-peer:degraded',
+ 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded', 'vxlan-slaac:degraded')
output = check_output('ip -d -d link show vxlan99')
print(output)
@@ -2364,7 +2524,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'26-macsec.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:degraded', 'macsec99:routable'])
+ self.wait_online('dummy98:degraded', 'macsec99:routable')
output = check_output('ip -d link show macsec99')
print(output)
@@ -2391,14 +2551,119 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
copy_network_unit('25-nlmon.netdev', '26-netdev-link-local-addressing-yes.network')
start_networkd()
- self.wait_online(['nlmon99:carrier'])
+ self.wait_online('nlmon99:carrier')
@expectedFailureIfModuleIsNotAvailable('ifb')
def test_ifb(self):
copy_network_unit('25-ifb.netdev', '26-netdev-link-local-addressing-yes.network')
start_networkd()
- self.wait_online(['ifb99:degraded'])
+ self.wait_online('ifb99:degraded')
+
+ @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test")
+ def test_rps_cpu_1(self):
+ copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-1.link')
+ start_networkd()
+
+ self.wait_online('dummy98:carrier')
+
+ output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
+ print(output)
+ self.assertEqual(int(output.replace(',', ''), base=16), 2)
+
+ @unittest.skipUnless(os.cpu_count() >= 2, reason="CPU count should be >= 2 to pass this test")
+ def test_rps_cpu_0_1(self):
+ copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-0-1.link')
+ start_networkd()
+
+ self.wait_online('dummy98:carrier')
+
+ output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
+ print(output)
+ self.assertEqual(int(output.replace(',', ''), base=16), 3)
+
+ @unittest.skipUnless(os.cpu_count() >= 4, reason="CPU count should be >= 4 to pass this test")
+ def test_rps_cpu_multi(self):
+ copy_network_unit('12-dummy.netdev', '12-dummy.network', '25-rps-cpu-multi.link')
+ start_networkd()
+
+ self.wait_online('dummy98:carrier')
+
+ output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
+ print(output)
+ self.assertEqual(int(output.replace(',', ''), base=16), 15)
+
+ def test_rps_cpu(self):
+ cpu_count = os.cpu_count()
+
+ copy_network_unit('12-dummy.netdev', '12-dummy.network')
+ start_networkd()
+
+ self.wait_online('dummy98:carrier')
+
+ # 0
+ copy_network_unit('25-rps-cpu-0.link')
+ udevadm_trigger('/sys/class/net/dummy98')
+ output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
+ print(output)
+ self.assertEqual(int(output.replace(',', ''), base=16), 1)
+ remove_network_unit('25-rps-cpu-0.link')
+
+ # all
+ copy_network_unit('25-rps-cpu-all.link')
+ udevadm_trigger('/sys/class/net/dummy98')
+ output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
+ print(output)
+ self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
+ remove_network_unit('25-rps-cpu-all.link')
+
+ # disable
+ copy_network_unit('24-rps-cpu-disable.link')
+ udevadm_trigger('/sys/class/net/dummy98')
+ output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
+ print(output)
+ self.assertEqual(int(output.replace(',', ''), base=16), 0)
+ remove_network_unit('24-rps-cpu-disable.link')
+
+ # set all again
+ copy_network_unit('25-rps-cpu-all.link')
+ udevadm_trigger('/sys/class/net/dummy98')
+ output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
+ print(output)
+ self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
+ remove_network_unit('25-rps-cpu-all.link')
+
+ # empty -> unchanged
+ copy_network_unit('24-rps-cpu-empty.link')
+ udevadm_trigger('/sys/class/net/dummy98')
+ output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
+ print(output)
+ self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
+ remove_network_unit('24-rps-cpu-empty.link')
+
+ # 0, then empty -> unchanged
+ copy_network_unit('25-rps-cpu-0-empty.link')
+ udevadm_trigger('/sys/class/net/dummy98')
+ output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
+ print(output)
+ self.assertEqual(f"{int(output.replace(',', ''), base=16):x}", f'{(1 << cpu_count) - 1:x}')
+ remove_network_unit('25-rps-cpu-0-empty.link')
+
+ # 0, then invalid -> 0
+ copy_network_unit('25-rps-cpu-0-invalid.link')
+ udevadm_trigger('/sys/class/net/dummy98')
+ output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
+ print(output)
+ self.assertEqual(int(output.replace(',', ''), base=16), 1)
+ remove_network_unit('25-rps-cpu-0-invalid.link')
+
+ # invalid -> unchanged
+ copy_network_unit('24-rps-cpu-invalid.link')
+ udevadm_trigger('/sys/class/net/dummy98')
+ output = check_output('cat /sys/class/net/dummy98/queues/rx-0/rps_cpus')
+ print(output)
+ self.assertEqual(int(output.replace(',', ''), base=16), 1)
+ remove_network_unit('24-rps-cpu-invalid.link')
class NetworkdL2TPTests(unittest.TestCase, Utilities):
@@ -2414,7 +2679,7 @@ class NetworkdL2TPTests(unittest.TestCase, Utilities):
'25-l2tp-udp.netdev', '25-l2tp.network')
start_networkd()
- self.wait_online(['test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded'])
+ self.wait_online('test1:routable', 'l2tp-ses1:degraded', 'l2tp-ses2:degraded')
output = check_output('ip l2tp show tunnel tunnel_id 10')
print(output)
@@ -2442,7 +2707,7 @@ class NetworkdL2TPTests(unittest.TestCase, Utilities):
'25-l2tp-ip.netdev', '25-l2tp.network')
start_networkd()
- self.wait_online(['test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded'])
+ self.wait_online('test1:routable', 'l2tp-ses3:degraded', 'l2tp-ses4:degraded')
output = check_output('ip l2tp show tunnel tunnel_id 10')
print(output)
@@ -2581,6 +2846,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
check_json(networkctl_json())
+ @expectedFailureIfKernelReturnsInvalidFlags()
def test_address_static(self):
copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
self.setup_nftset('addr4', 'ipv4_addr')
@@ -2588,7 +2854,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.setup_nftset('ifindex', 'iface_index')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
ip4_null_16 = None
ip4_null_24 = None
@@ -2650,7 +2916,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
copy_network_unit('25-address-static.network.d/10-override.conf')
networkctl_reload()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
self.verify_address_static(
label1='new-label1',
label2='dummy98',
@@ -2682,7 +2948,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
)
networkctl_reconfigure('dummy98')
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
self.verify_address_static(
label1='new-label1',
label2='dummy98',
@@ -2724,7 +2990,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
# 2. reconfigure the interface, and check the deprecated flag is set again
networkctl_reconfigure('dummy98')
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
self.verify_address_static(
label1='new-label1',
label2='dummy98',
@@ -2758,18 +3024,27 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
# test for ENOBUFS issue #17012 (with reload)
copy_network_unit('25-address-static.network.d/10-many-address.conf')
networkctl_reload()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('ip -4 address show dev dummy98')
for i in range(1, 254):
self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
# (with reconfigure)
networkctl_reconfigure('dummy98')
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('ip -4 address show dev dummy98')
for i in range(1, 254):
self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
+ # test for an empty string assignment for Address= in [Network]
+ copy_network_unit('25-address-static.network.d/20-clear-addresses.conf')
+ networkctl_reload()
+ self.wait_online('dummy98:routable')
+ output = check_output('ip -4 address show dev dummy98')
+ for i in range(1, 254):
+ self.assertNotIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
+ self.assertIn('inet 10.4.0.1/16 brd 10.4.255.255', output)
+
def test_address_ipv4acd(self):
check_output('ip netns add ns99')
check_output('ip link add veth99 type veth peer veth-peer')
@@ -2780,7 +3055,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
copy_network_unit('25-address-ipv4acd-veth99.network', copy_dropins=False)
start_networkd()
- self.wait_online(['veth99:routable'])
+ self.wait_online('veth99:routable')
output = check_output('ip -4 address show dev veth99')
print(output)
@@ -2806,7 +3081,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
else:
restart_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('ip -4 address show dev dummy98')
self.assertIn('inet 100.64.0.1 peer 100.64.0.2/32 scope global', output)
@@ -2822,7 +3097,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
else:
restart_networkd()
- self.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier'])
+ self.wait_online('dummy98:routable', 'test1:routable', 'vrf99:carrier')
output = check_output('ip route show table 42 dev dummy98')
print('### ip route show table 42 dev dummy98')
@@ -2886,7 +3161,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
copy_network_unit('25-test1.network.d/configure-without-carrier.conf', copy_dropins=False)
restart_networkd()
- self.wait_online(['test1:no-carrier'])
+ self.wait_online('test1:no-carrier')
carrier_map = {'on': '1', 'off': '0'}
routable_map = {'on': 'routable', 'off': 'no-carrier'}
@@ -2894,7 +3169,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
with self.subTest(carrier=carrier):
if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
check_output(f'ip link set dev test1 carrier {carrier}')
- self.wait_online([f'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
+ self.wait_online(f'test1:{routable_map[carrier]}:{routable_map[carrier]}')
output = networkctl_status('test1')
print(output)
@@ -2910,7 +3185,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
copy_network_unit('25-test1.network')
restart_networkd()
- self.wait_online(['test1:no-carrier'])
+ self.wait_online('test1:no-carrier')
carrier_map = {'on': '1', 'off': '0'}
routable_map = {'on': 'routable', 'off': 'no-carrier'}
@@ -2918,7 +3193,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
with self.subTest(carrier=carrier, have_config=have_config):
if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
check_output(f'ip link set dev test1 carrier {carrier}')
- self.wait_online([f'test1:{routable_map[carrier]}:{routable_map[carrier]}'])
+ self.wait_online(f'test1:{routable_map[carrier]}:{routable_map[carrier]}')
output = networkctl_status('test1')
print(output)
@@ -2933,7 +3208,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
def test_routing_policy_rule(self):
copy_network_unit('25-routing-policy-rule-test1.network', '11-dummy.netdev')
start_networkd()
- self.wait_online(['test1:degraded'])
+ self.wait_online('test1:degraded')
output = check_output('ip rule list iif test1 priority 111')
print(output)
@@ -2973,7 +3248,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
for trial in range(3):
restart_networkd(show_logs=(trial > 0))
- self.wait_online(['test1:degraded', 'dummy98:degraded'])
+ self.wait_online('test1:degraded', 'dummy98:degraded')
output = check_output('ip rule list table 7')
print(output)
@@ -2986,7 +3261,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
def test_routing_policy_rule_reconfigure(self):
copy_network_unit('25-routing-policy-rule-reconfigure2.network', '11-dummy.netdev')
start_networkd()
- self.wait_online(['test1:degraded'])
+ self.wait_online('test1:degraded')
output = check_output('ip rule list table 1011')
print(output)
@@ -3001,7 +3276,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
copy_network_unit('25-routing-policy-rule-reconfigure1.network', '11-dummy.netdev')
networkctl_reload()
- self.wait_online(['test1:degraded'])
+ self.wait_online('test1:degraded')
output = check_output('ip rule list table 1011')
print(output)
@@ -3030,7 +3305,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertEqual(output, '')
networkctl_reconfigure('test1')
- self.wait_online(['test1:degraded'])
+ self.wait_online('test1:degraded')
output = check_output('ip rule list table 1011')
print(output)
@@ -3047,42 +3322,54 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
def test_routing_policy_rule_port_range(self):
copy_network_unit('25-fibrule-port-range.network', '11-dummy.netdev')
start_networkd()
- self.wait_online(['test1:degraded'])
+ self.wait_online('test1:degraded')
output = check_output('ip rule')
print(output)
- self.assertRegex(output, '111')
- self.assertRegex(output, 'from 192.168.100.18')
- self.assertRegex(output, '1123-1150')
- self.assertRegex(output, '3224-3290')
- self.assertRegex(output, 'tcp')
- self.assertRegex(output, 'lookup 7')
+ self.assertIn('111:', output)
+ self.assertIn('from 192.168.100.18 ', output)
+ self.assertIn('sport 1123-1150 ', output)
+ self.assertIn('dport 3224-3290 ', output)
+ self.assertRegex(output, 'ipproto (tcp|ipproto-6) ')
+ self.assertIn('lookup 7 ', output)
@expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
def test_routing_policy_rule_invert(self):
copy_network_unit('25-fibrule-invert.network', '11-dummy.netdev')
start_networkd()
- self.wait_online(['test1:degraded'])
+ self.wait_online('test1:degraded')
output = check_output('ip rule')
print(output)
- self.assertRegex(output, '111')
- self.assertRegex(output, 'not.*?from.*?192.168.100.18')
- self.assertRegex(output, 'tcp')
- self.assertRegex(output, 'lookup 7')
+ self.assertIn('111:', output)
+ self.assertIn('not ', output)
+ self.assertIn('from 192.168.100.18 ', output)
+ self.assertRegex(output, 'ipproto (tcp|ipproto-6) ')
+ self.assertIn('lookup 7 ', output)
+
+ @expectedFailureIfRoutingPolicyL3MasterDeviceIsNotAvailable()
+ def test_routing_policy_rule_l3mdev(self):
+ copy_network_unit('25-fibrule-l3mdev.network', '11-dummy.netdev')
+ start_networkd()
+ self.wait_online('test1:degraded')
+
+ output = check_output('ip rule')
+ print(output)
+ self.assertIn('1500: from all lookup [l3mdev-table]', output)
+ self.assertIn('2000: from all lookup [l3mdev-table] unreachable', output)
@expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
def test_routing_policy_rule_uidrange(self):
copy_network_unit('25-fibrule-uidrange.network', '11-dummy.netdev')
start_networkd()
- self.wait_online(['test1:degraded'])
+ self.wait_online('test1:degraded')
output = check_output('ip rule')
print(output)
- self.assertRegex(output, '111')
- self.assertRegex(output, 'from 192.168.100.18')
- self.assertRegex(output, 'lookup 7')
- self.assertRegex(output, 'uidrange 100-200')
+ self.assertIn('111:', output)
+ self.assertIn('from 192.168.100.18 ', output)
+ self.assertIn('lookup 7 ', output)
+ self.assertIn('uidrange 100-200 ', output)
def _test_route_static(self, manage_foreign_routes):
if not manage_foreign_routes:
@@ -3091,7 +3378,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
copy_network_unit('25-route-static.network', '12-dummy.netdev',
'25-route-static-test1.network', '11-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = networkctl_status('dummy98')
print(output)
@@ -3186,11 +3473,11 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertIn('dev dummy98 weight 10', output)
self.assertIn('dev dummy98 weight 5', output)
- print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
- output = check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
+ print('### ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff')
+ output = check_output('ip -6 route show 2001:1234:5:bfff:ff:ff:ff:ff')
print(output)
# old ip command does not show 'nexthop' keyword and weight...
- self.assertIn('2001:1234:5:7fff:ff:ff:ff:ff', output)
+ self.assertIn('2001:1234:5:bfff:ff:ff:ff:ff', output)
self.assertIn('via 2001:1234:5:6fff:ff:ff:ff:ff dev test1', output)
self.assertIn('via 2001:1234:5:7fff:ff:ff:ff:ff dev test1', output)
self.assertIn('via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98', output)
@@ -3198,9 +3485,9 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
check_json(networkctl_json())
- copy_network_unit('25-address-static.network')
+ copy_network_unit('25-address-static.network', copy_dropins=False)
networkctl_reload()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
# check all routes managed by Manager are removed
print('### ip -4 route show type blackhole')
@@ -3235,7 +3522,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
remove_network_unit('25-address-static.network')
networkctl_reload()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
# check all routes managed by Manager are reconfigured
print('### ip -4 route show type blackhole')
@@ -3302,8 +3589,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
print(output)
self.assertEqual(output, '')
- self.tearDown()
-
def test_route_static(self):
first = True
for manage_foreign_routes in [True, False]:
@@ -3320,7 +3605,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
def test_route_via_ipv6(self):
copy_network_unit('25-route-via-ipv6.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = networkctl_status('dummy98')
print(output)
@@ -3341,7 +3626,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
def test_route_congctl(self):
copy_network_unit('25-route-congctl.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
print('### ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
output = check_output('ip -6 route show dev dummy98 2001:1234:5:8fff:ff:ff:ff:ff')
@@ -3361,7 +3646,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
copy_network_unit('25-route-vrf.network', '12-dummy.netdev',
'25-vrf.netdev', '25-vrf.network')
start_networkd()
- self.wait_online(['dummy98:routable', 'vrf99:carrier'])
+ self.wait_online('dummy98:routable', 'vrf99:carrier')
output = check_output('ip route show vrf vrf99')
print(output)
@@ -3374,7 +3659,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
def test_gateway_reconfigure(self):
copy_network_unit('25-gateway-static.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
print('### ip -4 route show dev dummy98 default')
output = check_output('ip -4 route show dev dummy98 default')
print(output)
@@ -3384,7 +3669,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
remove_network_unit('25-gateway-static.network')
copy_network_unit('25-gateway-next-static.network')
networkctl_reload()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
print('### ip -4 route show dev dummy98 default')
output = check_output('ip -4 route show dev dummy98 default')
print(output)
@@ -3397,7 +3682,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
# tentative state, and do our test on that
copy_network_unit('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:enslaved', 'bond199:routable'])
+ self.wait_online('dummy98:enslaved', 'bond199:routable')
output = check_output('ip -6 route list dev bond199')
print(output)
@@ -3412,7 +3697,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
if i != 0:
networkctl_reconfigure('dummy98')
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('ip -6 route list dev dummy98')
print(output)
@@ -3421,7 +3706,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
def test_ip_link_mac_address(self):
copy_network_unit('25-address-link-section.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:degraded'])
+ self.wait_online('dummy98:degraded')
output = check_output('ip link show dummy98')
print(output)
@@ -3436,7 +3721,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
def test_ipv6_address_label(self):
copy_network_unit('25-ipv6-address-label-section.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:degraded'])
+ self.wait_online('dummy98:degraded')
output = check_output('ip addrlabel list')
print(output)
@@ -3446,17 +3731,82 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
copy_network_unit('25-ipv6-proxy-ndp.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('ip neighbor show proxy dev dummy98')
print(output)
for i in range(1, 5):
self.assertRegex(output, f'2607:5300:203:5215:{i}::1 *proxy')
- def test_neighbor_section(self):
- copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins=False)
+ def test_ipv6_neigh_retrans_time(self):
+ link='test25'
+ copy_network_unit('25-dummy.netdev', '25-dummy.network')
+ start_networkd()
+
+ self.wait_online(f'{link}:degraded')
+ remove_network_unit('25-dummy.network')
+
+ # expect retrans_time_ms updated
+ copy_network_unit('25-ipv6-neigh-retrans-time-3s.network')
+ networkctl_reload()
+ self.wait_online(f'{link}:degraded')
+ self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
+ remove_network_unit('25-ipv6-neigh-retrans-time-3s.network')
+
+ # expect retrans_time_ms unchanged
+ copy_network_unit('25-ipv6-neigh-retrans-time-0s.network')
+ networkctl_reload()
+ self.wait_online(f'{link}:degraded')
+ self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
+ remove_network_unit('25-ipv6-neigh-retrans-time-0s.network')
+
+ # expect retrans_time_ms unchanged
+ copy_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
+ networkctl_reload()
+ self.wait_online(f'{link}:degraded')
+ self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
+ remove_network_unit('25-ipv6-neigh-retrans-time-toobig.network')
+
+ # expect retrans_time_ms unchanged
+ copy_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
+ networkctl_reload()
+ self.wait_online(f'{link}:degraded')
+ self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
+ remove_network_unit('25-ipv6-neigh-retrans-time-infinity.network')
+
+ # expect retrans_time_ms unchanged
+ copy_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
+ networkctl_reload()
+ self.wait_online(f'{link}:degraded')
+ self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '3000')
+ remove_network_unit('25-ipv6-neigh-retrans-time-invalid.network')
+
+ # expect retrans_time_ms updated
+ copy_network_unit('25-ipv6-neigh-retrans-time-4s.network')
+ networkctl_reload()
+ self.wait_online(f'{link}:degraded')
+ self.check_ipv6_neigh_sysctl_attr(link, 'retrans_time_ms', '4000')
+ remove_network_unit('25-ipv6-neigh-retrans-time-4s.network')
+
+ def test_neighbor(self):
+ copy_network_unit('12-dummy.netdev', '25-neighbor-dummy.network', '25-neighbor-dummy.network.d/10-step1.conf',
+ '25-gre-tunnel-remote-any.netdev', '25-neighbor-ip.network',
+ '25-ip6gre-tunnel-remote-any.netdev', '25-neighbor-ipv6.network',
+ copy_dropins=False)
start_networkd()
- self.wait_online(['dummy98:degraded'])
+ self.wait_online('dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable')
+
+ print('### ip neigh list dev gretun97')
+ output = check_output('ip neigh list dev gretun97')
+ print(output)
+ self.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output)
+ self.assertNotIn('10.0.0.23', output)
+
+ print('### ip neigh list dev ip6gretun97')
+ output = check_output('ip neigh list dev ip6gretun97')
+ print(output)
+ self.assertRegex(output, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
+ self.assertNotIn('2001:db8:0:f102::18', output)
print('### ip neigh list dev dummy98')
output = check_output('ip neigh list dev dummy98')
@@ -3469,64 +3819,43 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
check_json(networkctl_json())
- copy_network_unit('25-neighbor-section.network.d/override.conf')
+ # Here, 10-step1.conf is intendedly kept, to verify that 10-step2.conf overrides
+ # the valid configurations in 10-step1.conf.
+ copy_network_unit('25-neighbor-dummy.network.d/10-step2.conf')
networkctl_reload()
- self.wait_online(['dummy98:degraded'])
+ self.wait_online('dummy98:degraded')
- print('### ip neigh list dev dummy98 (after reloading)')
+ print('### ip neigh list dev dummy98')
output = check_output('ip neigh list dev dummy98')
print(output)
self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:65 PERMANENT', output)
self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:03:66 PERMANENT', output)
self.assertNotIn('2004:da8:1:0::2', output)
self.assertNotIn('192.168.10.2', output)
- self.assertNotIn('00:00:5e:00:02', output)
-
- def test_neighbor_reconfigure(self):
- copy_network_unit('25-neighbor-section.network', '12-dummy.netdev', copy_dropins=False)
- start_networkd()
- self.wait_online(['dummy98:degraded'])
+ self.assertNotIn('00:00:5e:00:02:67', output)
- print('### ip neigh list dev dummy98')
- output = check_output('ip neigh list dev dummy98')
- print(output)
- self.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:65 PERMANENT', output)
- self.assertIn('2004:da8:1::1 lladdr 00:00:5e:00:02:66 PERMANENT', output)
+ check_json(networkctl_json())
- remove_network_unit('25-neighbor-section.network')
- copy_network_unit('25-neighbor-next.network')
+ remove_network_unit('25-neighbor-dummy.network.d/10-step1.conf',
+ '25-neighbor-dummy.network.d/10-step2.conf')
+ copy_network_unit('25-neighbor-dummy.network.d/10-step3.conf')
networkctl_reload()
- self.wait_online(['dummy98:degraded'])
+ self.wait_online('dummy98:degraded')
+
print('### ip neigh list dev dummy98')
output = check_output('ip neigh list dev dummy98')
print(output)
+ self.assertIn('192.168.10.1 lladdr 00:00:5e:00:03:66 PERMANENT', output)
self.assertNotIn('00:00:5e:00:02:65', output)
- self.assertIn('192.168.10.1 lladdr 00:00:5e:00:02:66 PERMANENT', output)
+ self.assertNotIn('00:00:5e:00:02:66', output)
+ self.assertNotIn('00:00:5e:00:03:65', output)
self.assertNotIn('2004:da8:1::1', output)
- def test_neighbor_gre(self):
- copy_network_unit('25-neighbor-ip.network', '25-neighbor-ipv6.network', '25-neighbor-ip-dummy.network',
- '12-dummy.netdev', '25-gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel-remote-any.netdev')
- start_networkd()
- self.wait_online(['dummy98:degraded', 'gretun97:routable', 'ip6gretun97:routable'], timeout='40s')
-
- output = check_output('ip neigh list dev gretun97')
- print(output)
- self.assertIn('10.0.0.22 lladdr 10.65.223.239 PERMANENT', output)
- self.assertNotIn('10.0.0.23', output)
-
- output = check_output('ip neigh list dev ip6gretun97')
- print(output)
- self.assertRegex(output, '2001:db8:0:f102::17 lladdr 2a:?00:ff:?de:45:?67:ed:?de:[0:]*:49:?88 PERMANENT')
- self.assertNotIn('2001:db8:0:f102::18', output)
-
- check_json(networkctl_json())
-
def test_link_local_addressing(self):
copy_network_unit('25-link-local-addressing-yes.network', '11-dummy.netdev',
'25-link-local-addressing-no.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['test1:degraded', 'dummy98:carrier'])
+ self.wait_online('test1:degraded', 'dummy98:carrier')
output = check_output('ip address show dev test1')
print(output)
@@ -3556,7 +3885,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
def test_link_local_addressing_ipv6ll(self):
copy_network_unit('26-link-local-addressing-ipv6.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:degraded'])
+ self.wait_online('dummy98:degraded')
# An IPv6LL address exists by default.
output = check_output('ip address show dev dummy98')
@@ -3565,7 +3894,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
copy_network_unit('25-link-local-addressing-no.network')
networkctl_reload()
- self.wait_online(['dummy98:carrier'])
+ self.wait_online('dummy98:carrier')
# Check if the IPv6LL address is removed.
output = check_output('ip address show dev dummy98')
@@ -3574,19 +3903,18 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
remove_network_unit('25-link-local-addressing-no.network')
networkctl_reload()
- self.wait_online(['dummy98:degraded'])
+ self.wait_online('dummy98:degraded')
# Check if a new IPv6LL address is assigned.
output = check_output('ip address show dev dummy98')
print(output)
self.assertRegex(output, 'inet6 .* scope link')
- @unittest.skip("Re-enable once https://github.com/systemd/systemd/issues/30056 is resolved")
def test_sysctl(self):
copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins=False)
start_networkd()
- self.wait_online(['dummy98:degraded'])
+ self.wait_online('dummy98:degraded')
self.check_ipv6_sysctl_attr('dummy98', 'forwarding', '1')
self.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '1')
@@ -3595,12 +3923,13 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.check_ipv6_sysctl_attr('dummy98', 'proxy_ndp', '1')
self.check_ipv4_sysctl_attr('dummy98', 'forwarding', '1')
self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp', '1')
+ self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1')
self.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
self.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
networkctl_reload()
- self.wait_online(['dummy98:degraded'])
+ self.wait_online('dummy98:degraded')
self.check_ipv6_sysctl_attr('dummy98', 'use_tempaddr', '2')
@@ -3612,7 +3941,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('ip -4 address show dummy98')
print(output)
@@ -3636,7 +3965,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
restart_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('ip -4 address show dummy98')
print(output)
@@ -3667,7 +3996,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
# add one bound interface. The interface will be up.
check_output('ip link add dummy98 type dummy')
check_output('ip link set dummy98 up')
- self.wait_online(['test1:routable'])
+ self.wait_online('test1:routable')
output = check_output('ip address show test1')
print(output)
self.assertIn('UP,LOWER_UP', output)
@@ -3701,7 +4030,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
# bring up the bound interface. The interface will be up.
check_output('ip link set dummy99 up')
- self.wait_online(['test1:routable'])
+ self.wait_online('test1:routable')
output = check_output('ip address show test1')
print(output)
self.assertIn('UP,LOWER_UP', output)
@@ -3720,7 +4049,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
# re-add one bound interface. The interface will be up.
check_output('ip link add dummy98 type dummy')
check_output('ip link set dummy98 up')
- self.wait_online(['test1:routable'])
+ self.wait_online('test1:routable')
output = check_output('ip address show test1')
print(output)
self.assertIn('UP,LOWER_UP', output)
@@ -3794,7 +4123,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
if policy.endswith('down') or policy == 'manual':
self.wait_operstate('test1', 'off', setup_state='configuring')
else:
- self.wait_online(['test1'])
+ self.wait_online('test1')
if policy == 'always-down':
# if always-down, required for online is forced to no
@@ -3832,7 +4161,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
def test_domain(self):
copy_network_unit('12-dummy.netdev', '24-search-domain.network')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = networkctl_status('dummy98')
print(output)
@@ -3853,85 +4182,146 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
copy_network_unit('24-keep-configuration-static.network')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('ip address show dummy98')
print(output)
self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
- @expectedFailureIfNexthopIsNotAvailable()
- def test_nexthop(self):
- def check_nexthop(self):
- self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
+ def check_nexthop(self, manage_foreign_nexthops, first):
+ self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
- output = check_output('ip nexthop list dev veth99')
- print(output)
+ output = check_output('ip nexthop list dev veth99')
+ print(output)
+ if first:
self.assertIn('id 1 via 192.168.5.1 dev veth99', output)
self.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output)
- self.assertIn('id 3 dev veth99', output)
- self.assertIn('id 4 dev veth99', output)
+ else:
+ self.assertIn('id 6 via 192.168.5.1 dev veth99', output)
+ self.assertIn('id 7 via 2001:1234:5:8f63::2 dev veth99', output)
+ self.assertIn('id 3 dev veth99', output)
+ self.assertIn('id 4 dev veth99', output)
+ if first:
self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
- self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output)
+ else:
+ self.assertIn('id 5 via 192.168.5.3 dev veth99', output)
+ self.assertNotRegex(output, 'id 5 via 192.168.5.3 dev veth99 .*onlink')
+ self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output)
+ if manage_foreign_nexthops:
self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99')
- output = check_output('ip nexthop list dev dummy98')
- print(output)
+ output = check_output('ip nexthop list dev dummy98')
+ print(output)
+ if first:
self.assertIn('id 20 via 192.168.20.1 dev dummy98', output)
+ else:
+ self.assertIn('id 21 via 192.168.20.1 dev dummy98', output)
+ if manage_foreign_nexthops:
+ self.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output)
+ else:
+ self.assertIn('id 42 via 192.168.20.2 dev dummy98', output)
- # kernel manages blackhole nexthops on lo
- output = check_output('ip nexthop list dev lo')
- print(output)
+ # kernel manages blackhole nexthops on lo
+ output = check_output('ip nexthop list dev lo')
+ print(output)
+ if first:
self.assertIn('id 6 blackhole', output)
self.assertIn('id 7 blackhole', output)
+ else:
+ self.assertIn('id 1 blackhole', output)
+ self.assertIn('id 2 blackhole', output)
- # group nexthops are shown with -0 option
+ # group nexthops are shown with -0 option
+ if first:
output = check_output('ip -0 nexthop list id 21')
print(output)
self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)')
-
- output = check_output('ip route show dev veth99 10.10.10.10')
+ else:
+ output = check_output('ip -0 nexthop list id 20')
print(output)
+ self.assertRegex(output, r'id 20 group (5,3/21|21/5,3)')
+
+ output = check_output('ip route show dev veth99 10.10.10.10')
+ print(output)
+ if first:
self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output)
+ else:
+ self.assertEqual('10.10.10.10 nhid 6 via 192.168.5.1 proto static', output)
- output = check_output('ip route show dev veth99 10.10.10.11')
- print(output)
+ output = check_output('ip route show dev veth99 10.10.10.11')
+ print(output)
+ if first:
self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output)
+ else:
+ self.assertEqual('10.10.10.11 nhid 7 via inet6 2001:1234:5:8f63::2 proto static', output)
- output = check_output('ip route show dev veth99 10.10.10.12')
- print(output)
+ output = check_output('ip route show dev veth99 10.10.10.12')
+ print(output)
+ if first:
self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output)
+ else:
+ self.assertEqual('10.10.10.12 nhid 5 via 192.168.5.3 proto static', output)
- output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
- print(output)
+ output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
+ print(output)
+ if first:
self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
+ else:
+ self.assertEqual('2001:1234:5:8f62::1 nhid 7 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
- output = check_output('ip route show 10.10.10.13')
- print(output)
+ output = check_output('ip route show 10.10.10.13')
+ print(output)
+ if first:
self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output)
+ else:
+ self.assertEqual('blackhole 10.10.10.13 nhid 1 dev lo proto static', output)
- output = check_output('ip -6 route show 2001:1234:5:8f62::2')
- print(output)
+ output = check_output('ip -6 route show 2001:1234:5:8f62::2')
+ print(output)
+ if first:
self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output)
+ else:
+ self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 2 dev lo proto static metric 1024 pref medium', output)
- output = check_output('ip route show 10.10.10.14')
- print(output)
+ output = check_output('ip route show 10.10.10.14')
+ print(output)
+ if first:
self.assertIn('10.10.10.14 nhid 21 proto static', output)
- self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
+ else:
+ self.assertIn('10.10.10.14 nhid 20 proto static', output)
+ self.assertIn('nexthop via 192.168.5.3 dev veth99 weight 3', output)
+ self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
- check_json(networkctl_json())
+ output = networkctl_json()
+ check_json(output)
+ self.assertNotIn('"Destination":[10.10.10.14]', output)
+
+ def _test_nexthop(self, manage_foreign_nexthops):
+ if not manage_foreign_nexthops:
+ copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
+
+ check_output('ip link add dummy98 type dummy')
+ check_output('ip link set dummy98 up')
+ check_output('ip address add 192.168.20.20/24 dev dummy98')
+ check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
- copy_network_unit('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network',
- '12-dummy.netdev', '25-nexthop-dummy.network')
+ copy_network_unit('25-nexthop-1.network', '25-veth.netdev', '25-veth-peer.network',
+ '12-dummy.netdev', '25-nexthop-dummy-1.network')
start_networkd()
- check_nexthop(self)
+ self.check_nexthop(manage_foreign_nexthops, first=True)
- remove_network_unit('25-nexthop.network')
+ remove_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
+ copy_network_unit('25-nexthop-2.network', '25-nexthop-dummy-2.network')
+ networkctl_reload()
+ self.check_nexthop(manage_foreign_nexthops, first=False)
+
+ remove_network_unit('25-nexthop-2.network')
copy_network_unit('25-nexthop-nothing.network')
networkctl_reload()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
output = check_output('ip nexthop list dev veth99')
print(output)
@@ -3940,12 +4330,47 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
print(output)
self.assertEqual(output, '')
- remove_network_unit('25-nexthop-nothing.network')
- copy_network_unit('25-nexthop.network')
- networkctl_reconfigure('dummy98')
+ remove_network_unit('25-nexthop-nothing.network', '25-nexthop-dummy-2.network')
+ copy_network_unit('25-nexthop-1.network', '25-nexthop-dummy-1.network')
+ # Of course, networkctl_reconfigure() below is unnecessary in normal operation, but it is intentional
+ # here to test reconfiguring with different .network files does not trigger race.
+ # See also comments in link_drop_requests().
+ networkctl_reconfigure('dummy98') # reconfigured with 25-nexthop-dummy-2.network
+ networkctl_reload() # reconfigured with 25-nexthop-dummy-1.network
+
+ self.check_nexthop(manage_foreign_nexthops, first=True)
+
+ # Remove nexthop with ID 20
+ check_output('ip nexthop del id 20')
+ copy_network_unit('11-dummy.netdev', '25-nexthop-test1.network')
networkctl_reload()
- check_nexthop(self)
+ # 25-nexthop-test1.network requests a route with nexthop ID 21,
+ # which is silently removed by the kernel when nexthop with ID 20 is removed in the above,
+ # hence test1 should be stuck in the configuring state.
+ self.wait_operstate('test1', operstate='routable', setup_state='configuring')
+
+ # Wait for a while, and check if the interface is still in the configuring state.
+ time.sleep(1)
+ output = networkctl_status('test1')
+ self.assertIn('State: routable (configuring)', output)
+
+ # Check if the route which needs nexthop 20 and 21 are forgotten.
+ output = networkctl_json()
+ check_json(output)
+ self.assertNotIn('"Destination":[10.10.10.14]', output)
+
+ # Reconfigure the interface that has nexthop with ID 20 and 21,
+ # then the route requested by test1 can be configured.
+ networkctl_reconfigure('dummy98')
+ self.wait_online('test1:routable')
+
+ # Check if the requested route actually configured.
+ output = check_output('ip route show 10.10.11.10')
+ print(output)
+ self.assertIn('10.10.11.10 nhid 21 proto static', output)
+ self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
+ self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
remove_link('veth99')
time.sleep(2)
@@ -3954,6 +4379,19 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
print(output)
self.assertEqual(output, '')
+ @expectedFailureIfNexthopIsNotAvailable()
+ def test_nexthop(self):
+ first = True
+ for manage_foreign_nexthops in [True, False]:
+ if first:
+ first = False
+ else:
+ self.tearDown()
+
+ print(f'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
+ with self.subTest(manage_foreign_nexthops=manage_foreign_nexthops):
+ self._test_nexthop(manage_foreign_nexthops)
+
class NetworkdTCTests(unittest.TestCase, Utilities):
def setUp(self):
@@ -3966,7 +4404,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_cake(self):
copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -3990,7 +4428,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_codel(self):
copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4001,7 +4439,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_drr(self):
copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4014,7 +4452,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_ets(self):
copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4028,7 +4466,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_fq(self):
copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4042,7 +4480,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_fq_codel(self):
copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4053,7 +4491,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_fq_pie(self):
copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4065,7 +4503,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_gred(self):
copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4076,7 +4514,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_hhf(self):
copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4087,7 +4525,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_htb_fifo(self):
copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4124,7 +4562,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
'25-qdisc-ingress.network', '11-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable', 'test1:routable'])
+ self.wait_online('dummy98:routable', 'test1:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4139,7 +4577,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
'25-qdisc-netem-compat.network', '11-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable', 'test1:routable'])
+ self.wait_online('dummy98:routable', 'test1:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4155,7 +4593,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_pie(self):
copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4166,7 +4604,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_qfq(self):
copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4180,7 +4618,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_sfb(self):
copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4191,7 +4629,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_sfq(self):
copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4202,7 +4640,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_tbf(self):
copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4217,7 +4655,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
start_networkd()
self.wait_links('dummy98')
check_output('modprobe sch_teql max_equalizers=2')
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
output = check_output('tc qdisc show dev dummy98')
print(output)
@@ -4227,7 +4665,7 @@ class NetworkdTCTests(unittest.TestCase, Utilities):
def test_qdisc_drop(self):
copy_network_unit('12-dummy.netdev', '12-dummy.network')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
# Test case for issue #32247 and #32254.
for _ in range(20):
@@ -4251,7 +4689,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
def test_state_file(self):
copy_network_unit('12-dummy.netdev', '25-state-file-tests.network')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
# make link state file updated
resolvectl('revert', 'dummy98')
@@ -4328,7 +4766,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
copy_network_unit('12-dummy.netdev', '12-dummy-no-address.network')
start_networkd()
- self.wait_online(['dummy98:degraded'])
+ self.wait_online('dummy98:degraded')
output = read_link_state_file('dummy98')
self.assertIn('IPV4_ADDRESS_STATE=off', output)
@@ -4336,8 +4774,8 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
# with a routable IPv4 address
check_output('ip address add 10.1.2.3/16 dev dummy98')
- self.wait_online(['dummy98:routable'], ipv4=True)
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable', ipv4=True)
+ self.wait_online('dummy98:routable')
output = read_link_state_file('dummy98')
self.assertIn('IPV4_ADDRESS_STATE=routable', output)
@@ -4347,8 +4785,8 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
# with a routable IPv6 address
check_output('ip address add 2002:da8:1:0:1034:56ff:fe78:9abc/64 dev dummy98')
- self.wait_online(['dummy98:routable'], ipv6=True)
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable', ipv6=True)
+ self.wait_online('dummy98:routable')
output = read_link_state_file('dummy98')
self.assertIn('IPV4_ADDRESS_STATE=off', output)
@@ -4369,7 +4807,7 @@ class NetworkdBondTests(unittest.TestCase, Utilities):
copy_network_unit('23-keep-master.network')
start_networkd()
- self.wait_online(['dummy98:enslaved'])
+ self.wait_online('dummy98:enslaved')
output = check_output('ip -d link show bond199')
print(output)
@@ -4382,7 +4820,7 @@ class NetworkdBondTests(unittest.TestCase, Utilities):
def test_bond_active_slave(self):
copy_network_unit('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:enslaved', 'bond199:degraded'])
+ self.wait_online('dummy98:enslaved', 'bond199:degraded')
output = check_output('ip -d link show bond199')
print(output)
@@ -4391,13 +4829,13 @@ class NetworkdBondTests(unittest.TestCase, Utilities):
# test case for issue #31165.
since = datetime.datetime.now()
networkctl_reconfigure('dummy98')
- self.wait_online(['dummy98:enslaved', 'bond199:degraded'])
+ self.wait_online('dummy98:enslaved', 'bond199:degraded')
self.assertNotIn('dummy98: Bringing link down', read_networkd_log(since=since))
def test_bond_primary_slave(self):
copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:enslaved', 'bond199:degraded'])
+ self.wait_online('dummy98:enslaved', 'bond199:degraded')
output = check_output('ip -d link show bond199')
print(output)
@@ -4410,7 +4848,7 @@ class NetworkdBondTests(unittest.TestCase, Utilities):
f.write(f'[Link]\nMACAddress={mac}\n')
networkctl_reload()
- self.wait_online(['dummy98:enslaved', 'bond199:degraded'])
+ self.wait_online('dummy98:enslaved', 'bond199:degraded')
output = check_output('ip -d link show bond199')
print(output)
@@ -4420,7 +4858,7 @@ class NetworkdBondTests(unittest.TestCase, Utilities):
copy_network_unit('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
'25-bond99.network', '25-bond-slave.network')
start_networkd()
- self.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bond99:routable'])
+ self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bond99:routable')
output = check_output('ip -d link show dummy98')
print(output)
@@ -4469,32 +4907,139 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
def tearDown(self):
tear_down_common()
+ def test_bridge_mac_none(self):
+ copy_network_unit('12-dummy-mac.netdev', '26-bridge-mac-slave.network',
+ '26-bridge-mac.netdev', '26-bridge-mac-master.network', '26-bridge-mac.link')
+ start_networkd()
+ self.wait_online('dummy98:enslaved', 'bridge99:degraded')
+
+ output = check_output('ip link show dev dummy98')
+ print(output)
+ self.assertIn('link/ether 12:34:56:78:9a:01', output)
+
+ output = check_output('ip link show dev bridge99')
+ print(output)
+ self.assertIn('link/ether 12:34:56:78:9a:01', output)
+
def test_bridge_vlan(self):
copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave.network',
- '26-bridge.netdev', '26-bridge-vlan-master.network')
+ '26-bridge.netdev', '26-bridge-vlan-master.network',
+ copy_dropins=False)
start_networkd()
- self.wait_online(['test1:enslaved', 'bridge99:degraded'])
+ self.wait_online('test1:enslaved', 'bridge99:degraded')
+
+ output = check_output('bridge vlan show dev test1')
+ print(output)
+ # check if the default VID is removed
+ self.assertNotIn('1 Egress Untagged', output)
+ for i in range(1000, 3000):
+ if i == 1010:
+ self.assertIn(f'{i} PVID', output)
+ elif i in range(1012, 1016) or i in range(1103, 1109):
+ self.assertIn(f'{i} Egress Untagged', output)
+ elif i in range(1008, 1014) or i in range(1100, 1111):
+ self.assertIn(f'{i}', output)
+ else:
+ self.assertNotIn(f'{i}', output)
+
+ output = check_output('bridge vlan show dev bridge99')
+ print(output)
+ # check if the default VID is removed
+ self.assertNotIn('1 Egress Untagged', output)
+ for i in range(1000, 3000):
+ if i == 1020:
+ self.assertIn(f'{i} PVID', output)
+ elif i in range(1022, 1026) or i in range(1203, 1209):
+ self.assertIn(f'{i} Egress Untagged', output)
+ elif i in range(1018, 1024) or i in range(1200, 1211):
+ self.assertIn(f'{i}', output)
+ else:
+ self.assertNotIn(f'{i}', output)
+
+ # Change vlan IDs
+ copy_network_unit('26-bridge-vlan-slave.network.d/10-override.conf',
+ '26-bridge-vlan-master.network.d/10-override.conf')
+ networkctl_reload()
+ self.wait_online('test1:enslaved', 'bridge99:degraded')
+
+ output = check_output('bridge vlan show dev test1')
+ print(output)
+ for i in range(1000, 3000):
+ if i == 2010:
+ self.assertIn(f'{i} PVID', output)
+ elif i in range(2012, 2016) or i in range(2103, 2109):
+ self.assertIn(f'{i} Egress Untagged', output)
+ elif i in range(2008, 2014) or i in range(2100, 2111):
+ self.assertIn(f'{i}', output)
+ else:
+ self.assertNotIn(f'{i}', output)
+
+ output = check_output('bridge vlan show dev bridge99')
+ print(output)
+ for i in range(1000, 3000):
+ if i == 2020:
+ self.assertIn(f'{i} PVID', output)
+ elif i in range(2022, 2026) or i in range(2203, 2209):
+ self.assertIn(f'{i} Egress Untagged', output)
+ elif i in range(2018, 2024) or i in range(2200, 2211):
+ self.assertIn(f'{i}', output)
+ else:
+ self.assertNotIn(f'{i}', output)
+
+ # Remove several vlan IDs
+ copy_network_unit('26-bridge-vlan-slave.network.d/20-override.conf',
+ '26-bridge-vlan-master.network.d/20-override.conf')
+ networkctl_reload()
+ self.wait_online('test1:enslaved', 'bridge99:degraded')
+
+ output = check_output('bridge vlan show dev test1')
+ print(output)
+ for i in range(1000, 3000):
+ if i == 2010:
+ self.assertIn(f'{i} PVID', output)
+ elif i in range(2012, 2016):
+ self.assertIn(f'{i} Egress Untagged', output)
+ elif i in range(2008, 2014):
+ self.assertIn(f'{i}', output)
+ else:
+ self.assertNotIn(f'{i}', output)
+
+ output = check_output('bridge vlan show dev bridge99')
+ print(output)
+ for i in range(1000, 3000):
+ if i == 2020:
+ self.assertIn(f'{i} PVID', output)
+ elif i in range(2022, 2026):
+ self.assertIn(f'{i} Egress Untagged', output)
+ elif i in range(2018, 2024):
+ self.assertIn(f'{i}', output)
+ else:
+ self.assertNotIn(f'{i}', output)
+
+ # Remove all vlan IDs
+ copy_network_unit('26-bridge-vlan-slave.network.d/30-override.conf',
+ '26-bridge-vlan-master.network.d/30-override.conf')
+ networkctl_reload()
+ self.wait_online('test1:enslaved', 'bridge99:degraded')
output = check_output('bridge vlan show dev test1')
print(output)
- self.assertNotRegex(output, '4063')
- for i in range(4064, 4095):
- self.assertRegex(output, f'{i}')
- self.assertNotRegex(output, '4095')
+ self.assertNotIn('PVID', output)
+ for i in range(1000, 3000):
+ self.assertNotIn(f'{i}', output)
output = check_output('bridge vlan show dev bridge99')
print(output)
- self.assertNotRegex(output, '4059')
- for i in range(4060, 4095):
- self.assertRegex(output, f'{i}')
- self.assertNotRegex(output, '4095')
+ self.assertNotIn('PVID', output)
+ for i in range(1000, 3000):
+ self.assertNotIn(f'{i}', output)
def test_bridge_vlan_issue_20373(self):
copy_network_unit('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
'26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
'21-vlan.netdev', '21-vlan.network')
start_networkd()
- self.wait_online(['test1:enslaved', 'bridge99:degraded', 'vlan99:routable'])
+ self.wait_online('test1:enslaved', 'bridge99:degraded', 'vlan99:routable')
output = check_output('bridge vlan show dev test1')
print(output)
@@ -4512,7 +5057,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
copy_network_unit('11-dummy.netdev', '26-bridge-mdb-slave.network',
'26-bridge.netdev', '26-bridge-mdb-master.network')
start_networkd()
- self.wait_online(['test1:enslaved', 'bridge99:degraded'])
+ self.wait_online('test1:enslaved', 'bridge99:degraded')
output = check_output('bridge mdb show dev bridge99')
print(output)
@@ -4532,7 +5077,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
copy_network_unit('23-keep-master.network')
start_networkd()
- self.wait_online(['dummy98:enslaved'])
+ self.wait_online('dummy98:enslaved')
output = check_output('ip -d link show dummy98')
print(output)
@@ -4559,7 +5104,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
'26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
'25-bridge99.network')
start_networkd()
- self.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
+ self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
output = check_output('ip -d link show bridge99')
print(output)
@@ -4644,7 +5189,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
check_output('ip link add dummy98 type dummy')
- self.wait_online(['dummy98:enslaved', 'bridge99:routable'])
+ self.wait_online('dummy98:enslaved', 'bridge99:routable')
output = check_output('ip -d link show bridge99')
print(output)
@@ -4682,22 +5227,22 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
elif test == 'slave-up':
# bring up slave, which will have carrier; bridge gains carrier
check_output('ip link set dev test1 up')
- self.wait_online(['bridge99:routable'])
+ self.wait_online('bridge99:routable')
self.check_link_attr('bridge99', 'carrier', '1')
elif test == 'slave-no-carrier':
# drop slave carrier; bridge loses carrier
check_output('ip link set dev test1 carrier off')
- self.wait_online(['bridge99:no-carrier:no-carrier'])
+ self.wait_online('bridge99:no-carrier:no-carrier')
self.check_link_attr('bridge99', 'carrier', '0')
elif test == 'slave-carrier':
# restore slave carrier; bridge gains carrier
check_output('ip link set dev test1 carrier on')
- self.wait_online(['bridge99:routable'])
+ self.wait_online('bridge99:routable')
self.check_link_attr('bridge99', 'carrier', '1')
elif test == 'slave-down':
# bring down slave; bridge loses carrier
check_output('ip link set dev test1 down')
- self.wait_online(['bridge99:no-carrier:no-carrier'])
+ self.wait_online('bridge99:no-carrier:no-carrier')
self.check_link_attr('bridge99', 'carrier', '0')
output = networkctl_status('bridge99')
@@ -4709,7 +5254,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
'26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
'25-bridge99-ignore-carrier-loss.network')
start_networkd()
- self.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
+ self.wait_online('dummy98:enslaved', 'test1:enslaved', 'bridge99:routable')
check_output('ip address add 192.168.0.16/24 dev bridge99')
remove_link('test1', 'dummy98')
@@ -4725,7 +5270,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
copy_network_unit('26-bridge.netdev', '26-bridge-slave-interface-1.network',
'25-bridge99-ignore-carrier-loss.network')
start_networkd()
- self.wait_online(['bridge99:no-carrier'])
+ self.wait_online('bridge99:no-carrier')
for trial in range(4):
check_output('ip link add dummy98 type dummy')
@@ -4733,7 +5278,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
if trial < 3:
remove_link('dummy98')
- self.wait_online(['bridge99:routable', 'dummy98:enslaved'])
+ self.wait_online('bridge99:routable', 'dummy98:enslaved')
output = check_output('ip address show bridge99')
print(output)
@@ -4751,22 +5296,28 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities):
def tearDown(self):
tear_down_common()
- @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
- def test_sriov(self):
- copy_network_unit('25-default.link', '25-sriov.network')
-
+ def setup_netdevsim(self, id=99, num_ports=1, num_vfs=0):
call('modprobe netdevsim')
+ # Create netdevsim device.
with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f:
- f.write('99 1')
+ f.write(f'{id} {num_ports}')
+
+ # Create VF.
+ if num_vfs > 0:
+ with open(f'/sys/bus/netdevsim/devices/netdevsim{id}/sriov_numvfs', mode='w', encoding='utf-8') as f:
+ f.write(f'{num_vfs}')
+
+ @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
+ def test_sriov(self):
+ copy_network_unit('25-netdevsim.link', '25-sriov.network')
- with open('/sys/bus/netdevsim/devices/netdevsim99/sriov_numvfs', mode='w', encoding='utf-8') as f:
- f.write('3')
+ self.setup_netdevsim(num_vfs=3)
start_networkd()
- self.wait_online(['eni99np1:routable'])
+ self.wait_online('sim99:routable')
- output = check_output('ip link show dev eni99np1')
+ output = check_output('ip link show dev sim99')
print(output)
self.assertRegex(output,
'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
@@ -4778,18 +5329,15 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities):
def test_sriov_udev(self):
copy_network_unit('25-sriov.link', '25-sriov-udev.network')
- call('modprobe netdevsim')
-
- with open('/sys/bus/netdevsim/new_device', mode='w', encoding='utf-8') as f:
- f.write('99 1')
+ self.setup_netdevsim()
start_networkd()
- self.wait_online(['eni99np1:routable'])
+ self.wait_online('sim99:routable')
- # the name eni99np1 may be an alternative name.
- ifname = link_resolve('eni99np1')
+ # The name sim99 is an alternative name, and cannot be used by udevadm below.
+ ifname = link_resolve('sim99')
- output = check_output('ip link show dev eni99np1')
+ output = check_output('ip link show dev sim99')
print(output)
self.assertRegex(output,
'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
@@ -4802,10 +5350,10 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities):
with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
f.write('[Link]\nSR-IOVVirtualFunctions=4\n')
- udev_reload()
- check_output(*udevadm_cmd, 'trigger', '--action=add', '--settle', f'/sys/devices/netdevsim99/net/{ifname}')
+ udevadm_reload()
+ udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
- output = check_output('ip link show dev eni99np1')
+ output = check_output('ip link show dev sim99')
print(output)
self.assertRegex(output,
'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
@@ -4818,10 +5366,10 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities):
with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
f.write('[Link]\nSR-IOVVirtualFunctions=\n')
- udev_reload()
- check_output(*udevadm_cmd, 'trigger', '--action=add', '--settle', f'/sys/devices/netdevsim99/net/{ifname}')
+ udevadm_reload()
+ udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
- output = check_output('ip link show dev eni99np1')
+ output = check_output('ip link show dev sim99')
print(output)
self.assertRegex(output,
'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
@@ -4834,10 +5382,10 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities):
with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
f.write('[Link]\nSR-IOVVirtualFunctions=2\n')
- udev_reload()
- check_output(*udevadm_cmd, 'trigger', '--action=add', '--settle', f'/sys/devices/netdevsim99/net/{ifname}')
+ udevadm_reload()
+ udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
- output = check_output('ip link show dev eni99np1')
+ output = check_output('ip link show dev sim99')
print(output)
self.assertRegex(output,
'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
@@ -4850,10 +5398,10 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities):
with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
f.write('[Link]\nSR-IOVVirtualFunctions=\n')
- udev_reload()
- check_output(*udevadm_cmd, 'trigger', '--action=add', '--settle', f'/sys/devices/netdevsim99/net/{ifname}')
+ udevadm_reload()
+ udevadm_trigger(f'/sys/devices/netdevsim99/net/{ifname}')
- output = check_output('ip link show dev eni99np1')
+ output = check_output('ip link show dev sim99')
print(output)
self.assertRegex(output,
'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
@@ -4874,19 +5422,105 @@ class NetworkdLLDPTests(unittest.TestCase, Utilities):
def test_lldp(self):
copy_network_unit('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
start_networkd()
- self.wait_online(['veth99:degraded', 'veth-peer:degraded'])
+ self.wait_online('veth99:degraded', 'veth-peer:degraded')
- for trial in range(10):
- if trial > 0:
- time.sleep(1)
+ for _ in range(20):
+ output = networkctl('lldp')
+ print(output)
+ if re.search(r'veth99 .* veth-peer .* .......a...', output):
+ break
+ time.sleep(0.5)
+ else:
+ self.fail()
+
+ # With interface name
+ output = networkctl('lldp', 'veth99');
+ print(output)
+ self.assertRegex(output, r'veth99 .* veth-peer .* .......a...')
+
+ # With interface name pattern
+ output = networkctl('lldp', 've*9');
+ print(output)
+ self.assertRegex(output, r'veth99 .* veth-peer .* .......a...')
+ # json format
+ output = networkctl('--json=short', 'lldp')
+ print(output)
+ self.assertIn('"InterfaceName":"veth99"', output)
+ self.assertIn('"PortID":"veth-peer"', output)
+ self.assertIn('"EnabledCapabilities":128', output)
+
+ # json format with interface name
+ output = networkctl('--json=short', 'lldp', 'veth99')
+ print(output)
+ self.assertIn('"InterfaceName":"veth99"', output)
+ self.assertIn('"PortID":"veth-peer"', output)
+ self.assertIn('"EnabledCapabilities":128', output)
+
+ # json format with interface name pattern
+ output = networkctl('--json=short', 'lldp', 've*9')
+ print(output)
+ self.assertIn('"InterfaceName":"veth99"', output)
+ self.assertIn('"PortID":"veth-peer"', output)
+ self.assertIn('"EnabledCapabilities":128', output)
+
+ # LLDP neighbors in status
+ output = networkctl_status('veth99')
+ print(output)
+ self.assertRegex(output, r'Connected To: .* on port veth-peer')
+
+ # enable forwarding, to enable the Router flag
+ with open(os.path.join(network_unit_dir, '23-emit-lldp.network'), mode='a', encoding='utf-8') as f:
+ f.write('[Network]\nIPv4Forwarding=yes\n')
+
+ networkctl_reload()
+ self.wait_online('veth-peer:degraded')
+
+ for _ in range(20):
output = networkctl('lldp')
print(output)
- if re.search(r'veth99 .* veth-peer', output):
+ if re.search(r'veth99 .* veth-peer .* ....r......', output):
break
+ time.sleep(0.5)
else:
self.fail()
+ # With interface name
+ output = networkctl('lldp', 'veth99');
+ print(output)
+ self.assertRegex(output, r'veth99 .* veth-peer .* ....r......')
+
+ # With interface name pattern
+ output = networkctl('lldp', 've*9');
+ print(output)
+ self.assertRegex(output, r'veth99 .* veth-peer .* ....r......')
+
+ # json format
+ output = networkctl('--json=short', 'lldp')
+ print(output)
+ self.assertIn('"InterfaceName":"veth99"', output)
+ self.assertIn('"PortID":"veth-peer"', output)
+ self.assertIn('"EnabledCapabilities":16', output)
+
+ # json format with interface name
+ output = networkctl('--json=short', 'lldp', 'veth99')
+ print(output)
+ self.assertIn('"InterfaceName":"veth99"', output)
+ self.assertIn('"PortID":"veth-peer"', output)
+ self.assertIn('"EnabledCapabilities":16', output)
+
+ # json format with interface name pattern
+ output = networkctl('--json=short', 'lldp', 've*9')
+ print(output)
+ self.assertIn('"InterfaceName":"veth99"', output)
+ self.assertIn('"PortID":"veth-peer"', output)
+ self.assertIn('"EnabledCapabilities":16', output)
+
+ # LLDP neighbors in status
+ output = networkctl_status('veth99')
+ print(output)
+ self.assertRegex(output, r'Connected To: .* on port veth-peer')
+
class NetworkdRATests(unittest.TestCase, Utilities):
def setUp(self):
@@ -4901,7 +5535,10 @@ class NetworkdRATests(unittest.TestCase, Utilities):
self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
self.setup_nftset('ifindex', 'iface_index')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+ self.wait_online('veth99:routable', 'veth-peer:degraded')
+
+ # IPv6SendRA=yes implies IPv6Forwarding.
+ self.check_ipv6_sysctl_attr('veth-peer', 'forwarding', '1')
output = resolvectl('dns', 'veth99')
print(output)
@@ -4916,6 +5553,9 @@ class NetworkdRATests(unittest.TestCase, Utilities):
print(output)
self.assertRegex(output, '2002:da8:1:0')
+ self.check_ipv6_neigh_sysctl_attr('veth99', 'base_reachable_time_ms', '42000')
+ self.check_ipv6_neigh_sysctl_attr('veth99', 'retrans_time_ms', '500')
+
self.check_netlabel('veth99', '2002:da8:1::/64')
self.check_netlabel('veth99', '2002:da8:2::/64')
@@ -4927,10 +5567,8 @@ class NetworkdRATests(unittest.TestCase, Utilities):
self.teardown_nftset('addr6', 'network6', 'ifindex')
- def test_ipv6_token_static(self):
- copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
- start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+ def check_ipv6_token_static(self):
+ self.wait_online('veth99:routable', 'veth-peer:degraded')
output = networkctl_status('veth99')
print(output)
@@ -4939,26 +5577,178 @@ class NetworkdRATests(unittest.TestCase, Utilities):
self.assertRegex(output, '2002:da8:2:0:1a:2b:3c:4d')
self.assertRegex(output, '2002:da8:2:0:fa:de:ca:fe')
+ def test_ipv6_token_static(self):
+ copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
+ start_networkd()
+
+ self.check_ipv6_token_static()
+
+ for _ in range(20):
+ check_output('ip link set veth99 down')
+ check_output('ip link set veth99 up')
+
+ self.check_ipv6_token_static()
+
+ for _ in range(20):
+ check_output('ip link set veth99 down')
+ time.sleep(random.uniform(0, 0.1))
+ check_output('ip link set veth99 up')
+ time.sleep(random.uniform(0, 0.1))
+
+ self.check_ipv6_token_static()
+
+ def test_ndisc_redirect(self):
+ if not os.path.exists(test_ndisc_send):
+ self.skipTest(f"{test_ndisc_send} does not exist.")
+
+ copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-static.network')
+ start_networkd()
+
+ self.check_ipv6_token_static()
+
+ # Introduce three redirect routes.
+ check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:1:1a:2b:3c:4d --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
+ check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:2:1a:2b:3c:4d --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
+ check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address 2002:da8:1:3:1a:2b:3c:4d --redirect-destination 2002:da8:1:3:1a:2b:3c:4d')
+ self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
+ self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
+ self.wait_route('veth99', '2002:da8:1:3:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
+
+ # Change the target address of the redirects.
+ check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::1 --redirect-destination 2002:da8:1:1:1a:2b:3c:4d')
+ check_output(f'{test_ndisc_send} --interface veth-peer --type redirect --target-address fe80::2 --redirect-destination 2002:da8:1:2:1a:2b:3c:4d')
+ self.wait_route_dropped('veth99', '2002:da8:1:1:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
+ self.wait_route_dropped('veth99', '2002:da8:1:2:1a:2b:3c:4d proto redirect', ipv='-6', timeout_sec=10)
+ self.wait_route('veth99', '2002:da8:1:1:1a:2b:3c:4d via fe80::1 proto redirect', ipv='-6', timeout_sec=10)
+ self.wait_route('veth99', '2002:da8:1:2:1a:2b:3c:4d via fe80::2 proto redirect', ipv='-6', timeout_sec=10)
+
+ # Send Neighbor Advertisement without the router flag to announce the default router is not available anymore.
+ # Then, verify that all redirect routes and the default route are dropped.
+ output = check_output('ip -6 address show dev veth-peer scope link')
+ veth_peer_ipv6ll = re.search('fe80:[:0-9a-f]*', output).group()
+ print(f'veth-peer IPv6LL address: {veth_peer_ipv6ll}')
+ check_output(f'{test_ndisc_send} --interface veth-peer --type neighbor-advertisement --target-address {veth_peer_ipv6ll} --is-router no')
+ self.wait_route_dropped('veth99', 'proto ra', ipv='-6', timeout_sec=10)
+ self.wait_route_dropped('veth99', 'proto redirect', ipv='-6', timeout_sec=10)
+
+ # Check if sd-radv refuses RS from the same interface.
+ # See https://github.com/systemd/systemd/pull/32267#discussion_r1566721306
+ since = datetime.datetime.now()
+ check_output(f'{test_ndisc_send} --interface veth-peer --type rs --dest {veth_peer_ipv6ll}')
+ self.check_networkd_log('veth-peer: RADV: Received RS from the same interface, ignoring.', since=since)
+
+ def check_ndisc_mtu(self, mtu):
+ for _ in range(20):
+ output = read_ipv6_sysctl_attr('veth99', 'mtu')
+ if output == f'{mtu}':
+ break
+ time.sleep(0.5)
+ else:
+ self.fail(f'IPv6 MTU does not matches: value={output}, expected={mtu}')
+
+ def test_ndisc_mtu(self):
+ if not os.path.exists(test_ndisc_send):
+ self.skipTest(f"{test_ndisc_send} does not exist.")
+
+ copy_network_unit('25-veth.netdev',
+ '25-veth-peer-no-address.network',
+ '25-ipv6-prefix-veth-token-static.network')
+ start_networkd()
+ self.wait_online('veth-peer:degraded')
+
+ self.check_networkd_log('veth99: NDISC: Started IPv6 Router Solicitation client')
+
+ check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1400')
+ self.check_ndisc_mtu(1400)
+
+ check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1410')
+ self.check_ndisc_mtu(1410)
+
+ check_output('ip link set dev veth99 mtu 1600')
+ self.check_ndisc_mtu(1410)
+
+ check_output(f'{test_ndisc_send} --interface veth-peer --type ra --lifetime 1hour --mtu 1700')
+ self.check_ndisc_mtu(1600)
+
+ check_output('ip link set dev veth99 mtu 1800')
+ self.check_ndisc_mtu(1700)
+
def test_ipv6_token_prefixstable(self):
copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable.network')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+ self.wait_online('veth99:routable', 'veth-peer:degraded')
- output = networkctl_status('veth99')
+ output = check_output('ip -6 address show dev veth99')
print(output)
- self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
- self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output) # EUI64
+ self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
+ self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
+
+ with open(os.path.join(network_unit_dir, '25-ipv6-prefix-veth-token-prefixstable.network'), mode='a', encoding='utf-8') as f:
+ f.write('\n[IPv6AcceptRA]\nPrefixAllowList=2002:da8:1:0::/64\n')
+
+ networkctl_reload()
+ self.wait_online('veth99:routable')
+
+ output = check_output('ip -6 address show dev veth99')
+ print(output)
+ self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e/64', output) # the 1st prefixstable
+ self.assertNotIn('2002:da8:2:0:1034:56ff:fe78:9abc/64', output) # EUI64
+
+ check_output('ip address del 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth99')
+ check_output('ip address add 2002:da8:1:0:b47e:7975:fc7a:7d6e/64 dev veth-peer nodad')
+
+ networkctl_reconfigure('veth99')
+ self.wait_online('veth99:routable')
+ self.wait_address('veth99', '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', ipv='-6', timeout_sec=10) # the 2nd prefixstable
+ self.wait_address_dropped('veth99', '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', ipv='-6', timeout_sec=10) # the 1st prefixstable
+
+ check_output('ip address del 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth99')
+ check_output('ip address add 2002:da8:1:0:da5d:e50a:43fd:5d0f/64 dev veth-peer nodad')
+
+ networkctl_reconfigure('veth99')
+ self.wait_online('veth99:routable')
+ self.wait_address('veth99', '2002:da8:1:0:c7e4:77ec:eb31:1b0d/64', ipv='-6', timeout_sec=10) # the 3rd prefixstable
+ self.wait_address_dropped('veth99', '2002:da8:1:0:da5d:e50a:43fd:5d0f/64', ipv='-6', timeout_sec=10) # the 2nd prefixstable
+ self.wait_address_dropped('veth99', '2002:da8:1:0:b47e:7975:fc7a:7d6e/64', ipv='-6', timeout_sec=10) # the 1st prefixstable
def test_ipv6_token_prefixstable_without_address(self):
copy_network_unit('25-veth.netdev', '25-ipv6-prefix.network', '25-ipv6-prefix-veth-token-prefixstable-without-address.network')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+ self.wait_online('veth99:routable', 'veth-peer:degraded')
output = networkctl_status('veth99')
print(output)
self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output)
+ def test_router_hop_limit(self):
+ copy_network_unit('25-veth-client.netdev',
+ '25-veth-router.netdev',
+ '26-bridge.netdev',
+ '25-veth-bridge.network',
+ '25-veth-client.network',
+ '25-veth-router-hop-limit.network',
+ '25-bridge99.network')
+ start_networkd()
+ self.wait_online('client:routable', 'client-p:enslaved',
+ 'router:degraded', 'router-p:enslaved',
+ 'bridge99:routable')
+
+ self.check_ipv6_sysctl_attr('client', 'hop_limit', '42')
+
+ with open(os.path.join(network_unit_dir, '25-veth-router-hop-limit.network'), mode='a', encoding='utf-8') as f:
+ f.write('\n[IPv6SendRA]\nHopLimit=43\n')
+
+ networkctl_reload()
+
+ for _ in range(20):
+ output = read_ipv6_sysctl_attr('client', 'hop_limit')
+ if output == '43':
+ break
+ time.sleep(0.5)
+
+ self.check_ipv6_sysctl_attr('client', 'hop_limit', '43')
+
def test_router_preference(self):
copy_network_unit('25-veth-client.netdev',
'25-veth-router-high.netdev',
@@ -4970,13 +5760,13 @@ class NetworkdRATests(unittest.TestCase, Utilities):
'25-veth-router-low.network',
'25-bridge99.network')
start_networkd()
- self.wait_online(['client-p:enslaved',
- 'router-high:degraded', 'router-high-p:enslaved',
- 'router-low:degraded', 'router-low-p:enslaved',
- 'bridge99:routable'])
+ self.wait_online('client-p:enslaved',
+ 'router-high:degraded', 'router-high-p:enslaved',
+ 'router-low:degraded', 'router-low-p:enslaved',
+ 'bridge99:routable')
networkctl_reconfigure('client')
- self.wait_online(['client:routable'])
+ self.wait_online('client:routable')
self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
self.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
@@ -4985,16 +5775,18 @@ class NetworkdRATests(unittest.TestCase, Utilities):
output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
print(output)
+ self.assertIn('metric 512', output)
self.assertIn('pref high', output)
output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
print(output)
+ self.assertIn('metric 2048', output)
self.assertIn('pref low', output)
with open(os.path.join(network_unit_dir, '25-veth-client.network'), mode='a', encoding='utf-8') as f:
f.write('\n[Link]\nMACAddress=12:34:56:78:9a:01\n[IPv6AcceptRA]\nRouteMetric=100:200:300\n')
networkctl_reload()
- self.wait_online(['client:routable'])
+ self.wait_online('client:routable')
self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a01/64', ipv='-6', timeout_sec=10)
self.wait_address('client', '2002:da8:1:98:1034:56ff:fe78:9a01/64', ipv='-6', timeout_sec=10)
@@ -5003,11 +5795,35 @@ class NetworkdRATests(unittest.TestCase, Utilities):
output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
print(output)
+ self.assertIn('metric 100', output)
+ self.assertNotIn('metric 512', output)
self.assertIn('pref high', output)
output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
print(output)
+ self.assertIn('metric 300', output)
+ self.assertNotIn('metric 2048', output)
self.assertIn('pref low', output)
+ # swap the preference (for issue #28439)
+ remove_network_unit('25-veth-router-high.network', '25-veth-router-low.network')
+ copy_network_unit('25-veth-router-high2.network', '25-veth-router-low2.network')
+ networkctl_reload()
+ self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a99 proto ra metric 300', ipv='-6', timeout_sec=10)
+ self.wait_route('client', 'default via fe80::1034:56ff:fe78:9a98 proto ra metric 100', ipv='-6', timeout_sec=10)
+
+ output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a99')
+ print(output)
+ self.assertIn('metric 300', output)
+ self.assertNotIn('metric 100', output)
+ self.assertIn('pref low', output)
+ self.assertNotIn('pref high', output)
+ output = check_output('ip -6 route show dev client default via fe80::1034:56ff:fe78:9a98')
+ print(output)
+ self.assertIn('metric 100', output)
+ self.assertNotIn('metric 300', output)
+ self.assertIn('pref high', output)
+ self.assertNotIn('pref low', output)
+
@unittest.skipUnless(radvd_check_config('captive-portal.conf'), "Installed radvd doesn't support captive portals")
def test_captive_portal(self):
copy_network_unit('25-veth-client.netdev',
@@ -5018,12 +5834,12 @@ class NetworkdRATests(unittest.TestCase, Utilities):
'25-veth-bridge-captive.network',
'25-bridge99.network')
start_networkd()
- self.wait_online(['bridge99:routable', 'client-p:enslaved',
- 'router-captive:degraded', 'router-captivep:enslaved'])
+ self.wait_online('bridge99:routable', 'client-p:enslaved',
+ 'router-captive:degraded', 'router-captivep:enslaved')
start_radvd(config_file='captive-portal.conf')
networkctl_reconfigure('client')
- self.wait_online(['client:routable'])
+ self.wait_online('client:routable')
self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
output = networkctl_status('client')
@@ -5050,8 +5866,8 @@ class NetworkdRATests(unittest.TestCase, Utilities):
'25-veth-bridge-captive.network',
'25-bridge99.network')
start_networkd()
- self.wait_online(['bridge99:routable', 'client-p:enslaved',
- 'router-captive:degraded', 'router-captivep:enslaved'])
+ self.wait_online('bridge99:routable', 'client-p:enslaved',
+ 'router-captive:degraded', 'router-captivep:enslaved')
for uri in captive_portal_uris:
print(f"Captive portal: {uri}")
@@ -5059,7 +5875,7 @@ class NetworkdRATests(unittest.TestCase, Utilities):
stop_radvd()
start_radvd(config_file='bogus-captive-portal.conf')
networkctl_reconfigure('client')
- self.wait_online(['client:routable'])
+ self.wait_online('client:routable')
self.wait_address('client', '2002:da8:1:99:1034:56ff:fe78:9a00/64', ipv='-6', timeout_sec=10)
output = networkctl_status('client')
@@ -5074,25 +5890,62 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
def tearDown(self):
tear_down_common()
- def test_dhcp_server(self):
- copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
- start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
-
+ def check_dhcp_server(self, persist_leases=True):
output = networkctl_status('veth99')
print(output)
- self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
+ self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
self.assertIn('Gateway: 192.168.5.3', output)
self.assertRegex(output, 'DNS: 192.168.5.1\n *192.168.5.10')
self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
output = networkctl_status('veth-peer')
+ print(output)
self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*")
+ if persist_leases:
+ with open('/var/lib/systemd/network/dhcp-server-lease/veth-peer', encoding='utf-8') as f:
+ check_json(f.read())
+ else:
+ self.assertFalse(os.path.exists('/var/lib/systemd/network/dhcp-server-lease/veth-peer'))
+
+ def test_dhcp_server(self):
+ copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
+ start_networkd()
+ self.wait_online('veth99:routable', 'veth-peer:routable')
+
+ self.check_dhcp_server()
+
+ networkctl_reconfigure('veth-peer')
+ self.wait_online('veth-peer:routable')
+
+ for _ in range(10):
+ output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env)
+ if 'Offered DHCP leases: 192.168.5.' in output:
+ break
+ time.sleep(.2)
+ else:
+ self.fail()
+
+ def test_dhcp_server_persist_leases_no(self):
+ copy_networkd_conf_dropin('persist-leases-no.conf')
+ copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server.network')
+ start_networkd()
+ self.wait_online('veth99:routable', 'veth-peer:routable')
+
+ self.check_dhcp_server(persist_leases=False)
+
+ remove_networkd_conf_dropin('persist-leases-no.conf')
+ with open(os.path.join(network_unit_dir, '25-dhcp-server.network'), mode='a', encoding='utf-8') as f:
+ f.write('[DHCPServer]\nPersistLeases=no')
+ restart_networkd()
+ self.wait_online('veth99:routable', 'veth-peer:routable')
+
+ self.check_dhcp_server(persist_leases=False)
+
def test_dhcp_server_null_server_address(self):
copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
output = check_output('ip --json address show dev veth-peer')
server_address = json.loads(output)[0]['addr_info'][0]['local']
@@ -5104,7 +5957,29 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
output = networkctl_status('veth99')
print(output)
- self.assertRegex(output, rf'Address: {client_address} \(DHCP4 via {server_address}\)')
+ self.assertRegex(output, rf'Address: {client_address} \(DHCPv4 via {server_address}\)')
+ self.assertIn(f'Gateway: {server_address}', output)
+ self.assertIn(f'DNS: {server_address}', output)
+ self.assertIn(f'NTP: {server_address}', output)
+
+ output = networkctl_status('veth-peer')
+ self.assertIn(f'Offered DHCP leases: {client_address}', output)
+
+ # Check if the same addresses are used even if the service is restarted.
+ restart_networkd()
+ self.wait_online('veth99:routable', 'veth-peer:routable')
+
+ output = check_output('ip -4 address show dev veth-peer')
+ print(output)
+ self.assertIn(f'{server_address}', output)
+
+ output = check_output('ip -4 address show dev veth99')
+ print(output)
+ self.assertIn(f'{client_address}', output)
+
+ output = networkctl_status('veth99')
+ print(output)
+ self.assertRegex(output, rf'Address: {client_address} \(DHCPv4 via {server_address}\)')
self.assertIn(f'Gateway: {server_address}', output)
self.assertIn(f'DNS: {server_address}', output)
self.assertIn(f'NTP: {server_address}', output)
@@ -5116,11 +5991,11 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
'12-dummy.netdev', '25-dhcp-server-uplink.network')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
output = networkctl_status('veth99')
print(output)
- self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
+ self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
self.assertIn('Gateway: 192.168.5.3', output)
self.assertIn('DNS: 192.168.5.1', output)
self.assertIn('NTP: 192.168.5.1', output)
@@ -5128,33 +6003,33 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
def test_emit_router_timezone(self):
copy_network_unit('25-veth.netdev', '25-dhcp-client-timezone-router.network', '25-dhcp-server-timezone-router.network')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
output = networkctl_status('veth99')
print(output)
- self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCP4 via 192.168.5.1\)')
+ self.assertRegex(output, r'Address: 192.168.5.[0-9]* \(DHCPv4 via 192.168.5.1\)')
self.assertIn('Gateway: 192.168.5.1', output)
self.assertIn('Time Zone: Europe/Berlin', output)
def test_dhcp_server_static_lease(self):
copy_network_unit('25-veth.netdev', '25-dhcp-client-static-lease.network', '25-dhcp-server-static-lease.network')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
output = networkctl_status('veth99')
print(output)
- self.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output)
- self.assertIn('DHCP4 Client ID: 12:34:56:78:9a:bc', output)
+ self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
+ self.assertIn('DHCPv4 Client ID: 12:34:56:78:9a:bc', output)
def test_dhcp_server_static_lease_default_client_id(self):
copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-static-lease.network')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
output = networkctl_status('veth99')
print(output)
- self.assertIn('Address: 10.1.1.200 (DHCP4 via 10.1.1.1)', output)
- self.assertRegex(output, 'DHCP4 Client ID: IAID:[0-9a-z]*/DUID')
+ self.assertIn('Address: 10.1.1.200 (DHCPv4 via 10.1.1.1)', output)
+ self.assertRegex(output, 'DHCPv4 Client ID: IAID:[0-9a-z]*/DUID')
class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
@@ -5173,11 +6048,23 @@ class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
'25-agent-server-peer.network')
start_networkd()
- self.wait_online(['client:routable'])
+ self.wait_online('client:routable')
output = networkctl_status('client')
print(output)
- self.assertRegex(output, r'Address: 192.168.5.150 \(DHCP4 via 192.168.5.1\)')
+ self.assertRegex(output, r'Address: 192.168.5.150 \(DHCPv4 via 192.168.5.1\)')
+
+ def test_relay_agent_on_bridge(self):
+ copy_network_unit('25-agent-bridge.netdev',
+ '25-agent-veth-client.netdev',
+ '25-agent-bridge.network',
+ '25-agent-bridge-port.network',
+ '25-agent-client.network')
+ start_networkd()
+ self.wait_online('bridge-relay:routable', 'client-peer:enslaved')
+
+ # For issue #30763.
+ self.check_networkd_log('bridge-relay: DHCPv4 server: STARTED')
class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
@@ -5191,7 +6078,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
# information request mode
# The name ipv6-only option may not be supported by older dnsmasq
@@ -5200,7 +6087,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'--dhcp-option=option6:dns-server,[2600::ee]',
'--dhcp-option=option6:ntp-server,[2600::ff]',
ra_mode='ra-stateless')
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
# DHCPv6 REPLY for INFORMATION-REQUEST may be received after the link entered configured state.
# Let's wait for the expected DNS server being listed in the state file.
@@ -5242,7 +6129,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'--dhcp-option=option6:dns-server,[2600::ee]',
'--dhcp-option=option6:ntp-server,[2600::ff]')
networkctl_reconfigure('veth99')
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
# checking address
output = check_output('ip address show dev veth99 scope global')
@@ -5300,7 +6187,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'--dhcp-option=option6:ntp-server,[2600::ff]')
networkctl_reload()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
# checking address
output = check_output('ip address show dev veth99 scope global')
@@ -5346,7 +6233,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
def test_dhcp_client_ipv6_dbus_status(self):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only.network')
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
# Note that at this point the DHCPv6 client has not been started because no RA (with managed
# bit set) has yet been received and the configuration does not include WithoutRA=true
@@ -5359,7 +6246,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertEqual(state, 'selecting')
start_dnsmasq('--dhcp-option=108,00:00:02:00')
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
state = get_dhcp6_client_state('veth99')
print(f"DHCPv6 client state = {state}")
@@ -5404,9 +6291,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv6-only-custom-client-identifier.network')
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
# checking address
output = check_output('ip address show dev veth99 scope global')
@@ -5423,6 +6310,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertIn('DHCPREPLY(veth-peer)', output)
self.assertIn('sent size: 0 option: 14 rapid-commit', output)
+ @expectedFailureIfKernelReturnsInvalidFlags()
def test_dhcp_client_ipv4_only(self):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
@@ -5431,13 +6319,13 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.setup_nftset('ifindex', 'iface_index')
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7',
'--dhcp-option=option:sip-server,192.168.5.21,192.168.5.22',
'--dhcp-option=option:domain-search,example.com',
'--dhcp-alternate-port=67,5555',
ipv4_range='192.168.5.110,192.168.5.119')
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
print('## ip address show dev veth99 scope global')
@@ -5531,7 +6419,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.wait_address_dropped('veth99', f'inet {address1}/24', ipv='-4', timeout_sec=120)
self.wait_address('veth99', r'inet 192.168.5.12[0-9]*/24', ipv='-4')
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
print('## ip address show dev veth99 scope global')
output = check_output('ip address show dev veth99 scope global')
@@ -5624,7 +6512,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
def test_dhcp_client_ipv4_dbus_status(self):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
state = get_dhcp4_client_state('veth99')
print(f"State = {state}")
@@ -5634,7 +6522,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'--dhcp-option=option:domain-search,example.com',
'--dhcp-alternate-port=67,5555',
ipv4_range='192.168.5.110,192.168.5.119')
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
self.wait_address('veth99', r'inet 192.168.5.11[0-9]*/24', ipv='-4')
state = get_dhcp4_client_state('veth99')
@@ -5645,50 +6533,32 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-allow-list.network', copy_dropins=False)
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
since = datetime.datetime.now()
start_dnsmasq()
- expect = 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
- for _ in range(20):
- if expect in read_networkd_log(since=since):
- break
- time.sleep(0.5)
- else:
- self.fail()
+ self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since)
copy_network_unit('25-dhcp-client-allow-list.network.d/00-allow-list.conf')
since = datetime.datetime.now()
networkctl_reload()
- expect = 'veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.'
- for _ in range(20):
- if expect in read_networkd_log(since=since):
- break
- time.sleep(0.5)
- else:
- self.fail()
+ self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 not found in allow-list, ignoring offer.', since=since)
copy_network_unit('25-dhcp-client-allow-list.network.d/10-deny-list.conf')
since = datetime.datetime.now()
networkctl_reload()
- expect = 'veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.'
- for _ in range(20):
- if expect in read_networkd_log(since=since):
- break
- time.sleep(0.5)
- else:
- self.fail()
+ self.check_networkd_log('veth99: DHCPv4 server IP address 192.168.5.1 found in deny-list, ignoring offer.', since=since)
@unittest.skipUnless("--dhcp-rapid-commit" in run("dnsmasq --help").stdout, reason="dnsmasq is missing dhcp-rapid-commit support")
def test_dhcp_client_rapid_commit(self):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
start_dnsmasq('--dhcp-rapid-commit')
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
state = get_dhcp4_client_state('veth99')
@@ -5706,7 +6576,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'25-dhcp-server-ipv6-only-mode.network',
'25-dhcp-client-ipv6-only-mode.network')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:routable'], timeout='40s')
+ self.wait_online('veth99:routable', 'veth-peer:routable', timeout='40s')
self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24', ipv='-4')
state = get_dhcp4_client_state('veth99')
@@ -5734,7 +6604,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
copy_network_unit(*testunits, copy_dropins=False)
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
additional_options = [
'--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
'--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
@@ -5745,7 +6615,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'--dhcp-option=option:classless-static-route,0.0.0.0/0,192.168.5.4,8.0.0.0/8,192.168.5.5,192.168.5.64/26,192.168.5.5'
]
start_dnsmasq(*additional_options)
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
output = check_output('ip -4 route show dev veth99')
print(output)
@@ -5810,9 +6680,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
def test_dhcp_client_settings_anonymize(self):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-anonymize.network')
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
print('## dnsmasq log')
output = read_dnsmasq_log_file()
@@ -5826,9 +6696,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'25-dhcp-server-veth-peer.network',
'25-dhcp-client-keep-configuration-dhcp.network')
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
output = check_output('ip address show dev veth99 scope global')
print(output)
@@ -5860,7 +6730,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
f.write('[Network]\nDHCP=no\n')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
# Still the lease address should be kept after networkd restarted
output = check_output('ip address show dev veth99 scope global')
@@ -5873,9 +6743,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'25-dhcp-server-veth-peer.network',
'25-dhcp-client-keep-configuration-dhcp-on-stop.network')
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
output = check_output('ip address show dev veth99 scope global')
print(output)
@@ -5889,7 +6759,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
start_networkd()
- self.wait_online(['veth-peer:routable'])
+ self.wait_online('veth-peer:routable')
output = check_output('ip address show dev veth99 scope global')
print(output)
@@ -5898,9 +6768,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
def test_dhcp_client_reuse_address_as_static(self):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network')
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
# link become 'routable' when at least one protocol provide an valid address.
self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
@@ -5918,7 +6788,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
f.write(static_network)
restart_networkd()
- self.wait_online(['veth99:routable'])
+ self.wait_online('veth99:routable')
output = check_output('ip -4 address show dev veth99 scope global')
print(output)
@@ -5935,9 +6805,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-vrf.network',
'25-vrf.netdev', '25-vrf.network')
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier'])
+ self.wait_online('veth99:routable', 'veth-peer:routable', 'vrf99:carrier')
# link become 'routable' when at least one protocol provide an valid address.
self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
@@ -5978,9 +6848,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network',
'25-dhcp-client-gateway-onlink-implicit.network')
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable')
output = networkctl_status('veth99')
print(output)
@@ -5999,7 +6869,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
start_networkd()
# we need to increase timeout above default, as this will need to wait for
# systemd-networkd to get the dhcpv4 transient failure event
- self.wait_online(['veth99:degraded', 'veth-peer:routable'], timeout='60s')
+ self.wait_online('veth99:degraded', 'veth-peer:routable', timeout='60s')
output = check_output('ip -4 address show dev veth99')
print(output)
@@ -6010,7 +6880,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
self.wait_address('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4')
self.wait_address_dropped('veth99', r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4')
- self.wait_online(['veth99:routable'])
+ self.wait_online('veth99:routable')
output = check_output('ip -4 address show dev veth99')
print(output)
@@ -6039,7 +6909,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
f.write('\n[IPv6AcceptRA]\nUseDNS=no')
networkctl_reload()
- self.wait_online(['veth99:routable'])
+ self.wait_online('veth99:routable')
# link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
@@ -6064,7 +6934,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
'--dhcp-option=option6:dns-server,[2600::1]')
@@ -6073,6 +6943,54 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
check(self, False, True)
check(self, False, False)
+ def test_dhcp_client_default_use_domains(self):
+ def check(self, common, ipv4, ipv6):
+ mkdir_p(networkd_conf_dropin_dir)
+ with open(os.path.join(networkd_conf_dropin_dir, 'default_use_domains.conf'), mode='w', encoding='utf-8') as f:
+ f.write('[Network]\nUseDomains=')
+ f.write('yes\n' if common else 'no\n')
+ f.write('[DHCPv4]\nUseDomains=')
+ f.write('yes\n' if ipv4 else 'no\n')
+ f.write('[DHCPv6]\nUseDomains=')
+ f.write('yes\n' if ipv6 else 'no\n')
+
+ restart_networkd()
+ self.wait_online('veth-peer:carrier')
+ start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1',
+ '--dhcp-option=option6:dns-server,[2600::1]',
+ '--dhcp-option=option:domain-search,example.com',
+ '--dhcp-option=option6:domain-search,example.com')
+
+ self.wait_online('veth99:routable')
+
+ # link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
+
+ for _ in range(20):
+ output = resolvectl('domain', 'veth99')
+ if common or ipv4 or ipv6:
+ if 'example.com' in output:
+ break
+ else:
+ if 'example.com' not in output:
+ break
+ time.sleep(0.5)
+ else:
+ print(output)
+ print(read_link_state_file('veth99'))
+ self.fail('unexpected domain setting in resolved...')
+
+ stop_dnsmasq()
+ remove_networkd_conf_dropin('default_use_domains.conf')
+
+ copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
+ check(self, True, False, False)
+ check(self, False, True, True)
+ check(self, False, True, False)
+ check(self, False, False, True)
+ check(self, False, False, False)
+
def test_dhcp_client_use_captive_portal(self):
def check(self, ipv4, ipv6):
os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
@@ -6084,7 +7002,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
networkctl_reload()
- self.wait_online(['veth99:routable'])
+ self.wait_online('veth99:routable')
# link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
@@ -6102,7 +7020,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
start_dnsmasq('--dhcp-option=114,http://systemd.io',
'--dhcp-option=option6:103,http://systemd.io')
@@ -6122,7 +7040,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
f.write('\n[IPv6AcceptRA]\nUseCaptivePortal=no')
networkctl_reload()
- self.wait_online(['veth99:routable'])
+ self.wait_online('veth99:routable')
# link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
@@ -6138,7 +7056,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
start_networkd()
- self.wait_online(['veth-peer:carrier'])
+ self.wait_online('veth-peer:carrier')
masq = lambda bs: ':'.join(f'{b:02x}' for b in bs)
start_dnsmasq('--dhcp-option=114,' + masq(b'http://\x00invalid/url'),
'--dhcp-option=option6:103,' + masq(b'http://\x00/invalid/url'))
@@ -6181,9 +7099,9 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-address.network')
start_networkd()
- self.wait_online(['veth-peer:routable'])
+ self.wait_online('veth-peer:routable')
start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
- self.wait_online(['veth99:degraded'])
+ self.wait_online('veth99:degraded')
print('### ip -6 address show dev veth99 scope global')
output = check_output('ip -6 address show dev veth99 scope global')
@@ -6198,9 +7116,9 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
copy_network_unit('25-veth.netdev', '25-dhcp6pd-server.network', '25-dhcp6pd-upstream-no-assign.network')
start_networkd()
- self.wait_online(['veth-peer:routable'])
+ self.wait_online('veth-peer:routable')
start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd-no-range.conf', ipv='-6')
- self.wait_online(['veth99:degraded'])
+ self.wait_online('veth99:degraded')
print('### ip -6 address show dev veth99 scope global')
output = check_output('ip -6 address show dev veth99 scope global')
@@ -6219,10 +7137,10 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
'13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network')
start_networkd()
- self.wait_online(['veth-peer:routable'])
+ self.wait_online('veth-peer:routable')
start_isc_dhcpd(conf_file='isc-dhcpd-dhcp6pd.conf', ipv='-6')
- self.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
- 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
+ self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
+ 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
self.setup_nftset('addr6', 'ipv6_addr')
self.setup_nftset('network6', 'ipv6_addr', 'flags interval;')
@@ -6367,7 +7285,7 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
# Test case for a downstream which appears later
check_output('ip link add dummy97 type dummy')
- self.wait_online(['dummy97:routable'])
+ self.wait_online('dummy97:routable')
print('### ip -6 address show dev dummy97 scope global')
output = check_output('ip -6 address show dev dummy97 scope global')
@@ -6384,7 +7302,7 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
# Test case for reconfigure
networkctl_reconfigure('dummy98', 'dummy99')
- self.wait_online(['dummy98:routable', 'dummy99:degraded'])
+ self.wait_online('dummy98:routable', 'dummy99:degraded')
print('### ip -6 address show dev dummy98 scope global')
output = check_output('ip -6 address show dev dummy98 scope global')
@@ -6623,7 +7541,7 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
'80-6rd-tunnel.network')
start_networkd()
- self.wait_online(['veth-peer:routable'])
+ self.wait_online('veth-peer:routable')
# ipv4masklen: 8
# 6rd-prefix: 2001:db8::/32
@@ -6632,8 +7550,8 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
start_dnsmasq('--dhcp-option=212,08:20:20:01:0d:b8:00:00:00:00:00:00:00:00:00:00:00:00:0a:00:00:01',
ipv4_range='10.100.100.100,10.100.100.200',
ipv4_router='10.0.0.1')
- self.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
- 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
+ self.wait_online('veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded',
+ 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
# Check the DBus interface for assigned prefix information
prefixInfo = get_dhcp_6rd_prefix('veth99')
@@ -6645,7 +7563,7 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
# Test case for a downstream which appears later
check_output('ip link add dummy97 type dummy')
- self.wait_online(['dummy97:routable'])
+ self.wait_online('dummy97:routable')
# Find tunnel name
tunnel_name = None
@@ -6654,21 +7572,21 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
tunnel_name = name
break
- self.wait_online([f'{tunnel_name}:routable'])
+ self.wait_online(f'{tunnel_name}:routable')
self.verify_dhcp4_6rd(tunnel_name)
# Test case for reconfigure
networkctl_reconfigure('dummy98', 'dummy99')
- self.wait_online(['dummy98:routable', 'dummy99:degraded'])
+ self.wait_online('dummy98:routable', 'dummy99:degraded')
self.verify_dhcp4_6rd(tunnel_name)
print('Wait for the DHCP lease to be renewed/rebind')
time.sleep(120)
- self.wait_online(['veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
- 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable'])
+ self.wait_online('veth99:routable', 'test1:routable', 'dummy97:routable', 'dummy98:routable', 'dummy99:degraded',
+ 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable')
self.verify_dhcp4_6rd(tunnel_name)
@@ -6685,7 +7603,7 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
'12-dummy.netdev', '25-ipv6ra-uplink.network')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
output = check_output('ip address show dev veth-peer')
print(output)
@@ -6736,7 +7654,7 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
'12-dummy.netdev', '25-ipv6ra-uplink.network')
start_networkd()
- self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
+ self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')
output = check_output('ip address show dev veth-peer')
print(output)
@@ -6777,13 +7695,13 @@ class NetworkdMTUTests(unittest.TestCase, Utilities):
# test normal start
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
self.check_link_attr('dummy98', 'mtu', mtu)
self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
# test normal restart
restart_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online('dummy98:routable')
self.check_link_attr('dummy98', 'mtu', mtu)
self.check_ipv6_sysctl_attr('dummy98', 'mtu', ipv6_mtu)
@@ -6846,63 +7764,33 @@ if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
parser.add_argument('--source-dir', help='Path to source dir/git tree', dest='source_dir')
- parser.add_argument('--networkd', help='Path to systemd-networkd', dest='networkd_bin')
- parser.add_argument('--resolved', help='Path to systemd-resolved', dest='resolved_bin')
- parser.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest='timesyncd_bin')
- parser.add_argument('--udevd', help='Path to systemd-udevd', dest='udevd_bin')
- parser.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest='wait_online_bin')
- parser.add_argument('--networkctl', help='Path to networkctl', dest='networkctl_bin')
- parser.add_argument('--resolvectl', help='Path to resolvectl', dest='resolvectl_bin')
- parser.add_argument('--timedatectl', help='Path to timedatectl', dest='timedatectl_bin')
- parser.add_argument('--udevadm', help='Path to udevadm', dest='udevadm_bin')
parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
parser.add_argument('--lsan-options', help='LSAN options', dest='lsan_options')
parser.add_argument('--ubsan-options', help='UBSAN options', dest='ubsan_options')
parser.add_argument('--with-coverage', help='Loosen certain sandbox restrictions to make gcov happy', dest='with_coverage', type=bool, nargs='?', const=True, default=with_coverage)
+ parser.add_argument('--no-journal', help='Do not show journal of systemd-networkd on stop', dest='show_journal', action='store_false')
ns, unknown_args = parser.parse_known_args(namespace=unittest)
if ns.build_dir:
- if ns.networkd_bin or ns.resolved_bin or ns.timesyncd_bin or ns.udevd_bin or \
- ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin or ns.udevadm_bin:
- print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
timesyncd_bin = os.path.join(ns.build_dir, 'systemd-timesyncd')
- udevd_bin = os.path.join(ns.build_dir, 'udevadm')
wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
udevadm_bin = os.path.join(ns.build_dir, 'udevadm')
- systemd_udev_rules_build_dir = os.path.join(ns.build_dir, 'rules.d')
- else:
- if ns.networkd_bin:
- networkd_bin = ns.networkd_bin
- if ns.resolved_bin:
- resolved_bin = ns.resolved_bin
- if ns.timesyncd_bin:
- timesyncd_bin = ns.timesyncd_bin
- if ns.udevd_bin:
- udevd_bin = ns.udevd_bin
- if ns.wait_online_bin:
- wait_online_bin = ns.wait_online_bin
- if ns.networkctl_bin:
- networkctl_bin = ns.networkctl_bin
- if ns.resolvectl_bin:
- resolvectl_bin = ns.resolvectl_bin
- if ns.timedatectl_bin:
- timedatectl_bin = ns.timedatectl_bin
- if ns.udevadm_bin:
- udevadm_bin = ns.udevadm_bin
+ build_dir = ns.build_dir
if ns.source_dir:
- systemd_source_dir = ns.source_dir
+ source_dir = ns.source_dir
+ assert os.path.exists(os.path.join(source_dir, "meson_options.txt")), f"{source_dir} doesn't appear to be a systemd source tree."
+ elif os.path.exists(os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../meson_options.txt"))):
+ source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
else:
- systemd_source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
- if not os.path.exists(os.path.join(systemd_source_dir, "meson_options.txt")):
- raise RuntimeError(f"{systemd_source_dir} doesn't appear to be a systemd source tree")
+ source_dir = None
use_valgrind = ns.use_valgrind
enable_debug = ns.enable_debug
@@ -6910,6 +7798,7 @@ if __name__ == '__main__':
lsan_options = ns.lsan_options
ubsan_options = ns.ubsan_options
with_coverage = ns.with_coverage
+ show_journal = ns.show_journal
if use_valgrind:
# Do not forget the trailing space.
@@ -6921,6 +7810,11 @@ if __name__ == '__main__':
udevadm_cmd = valgrind_cmd.split() + [udevadm_bin]
wait_online_cmd = valgrind_cmd.split() + [wait_online_bin]
+ if build_dir:
+ test_ndisc_send = os.path.normpath(os.path.join(build_dir, 'test-ndisc-send'))
+ else:
+ test_ndisc_send = '/usr/lib/tests/test-ndisc-send'
+
if asan_options:
env.update({'ASAN_OPTIONS': asan_options})
if lsan_options:
@@ -6934,5 +7828,11 @@ if __name__ == '__main__':
if enable_debug:
wait_online_env.update({'SYSTEMD_LOG_LEVEL': 'debug'})
- sys.argv[1:] = unknown_args
- unittest.main(verbosity=3)
+ unittest.main(
+ verbosity=3,
+ argv=[
+ sys.argv[0],
+ *unknown_args,
+ *(["-k", match] if (match := os.getenv("TEST_MATCH_TESTCASE")) else [])
+ ],
+ )
diff --git a/test/test-path/basic.target b/test/test-path/basic.target
index 45f71aa..c4b04c4 100644
--- a/test/test-path/basic.target
+++ b/test/test-path/basic.target
@@ -12,4 +12,4 @@ After=sysinit.target sockets.target paths.target slices.target tmp.mount
# require /var and /var/tmp, but only add a Wants= type dependency on /tmp, as
# we support that unit being masked, and this should not be considered an error.
RequiresMountsFor=/var /var/tmp
-Wants=tmp.mount
+WantsMountsFor=/tmp
diff --git a/test/test-rpm-macros.sh b/test/test-rpm-macros.sh
index c7107de..c9a45dc 100755
--- a/test/test-rpm-macros.sh
+++ b/test/test-rpm-macros.sh
@@ -137,7 +137,7 @@ for i in sysusers tmpfiles; do
PKG_DATA_FILE="$(mktemp "$WORK_DIR/pkg-data-XXX")"
EXP_OUT="$(mktemp "$WORK_DIR/exp-out-XXX.log")"
- CONF_DIR="$(pkg-config --variable="${i}dir" systemd)"
+ CONF_DIR="$(PKG_CONFIG_PATH="${BUILD_DIR}/src/core" pkg-config --variable="${i}dir" systemd)"
EXTRA_ARGS=()
if [[ "$i" == tmpfiles ]]; then
diff --git a/test/test-systemctl-enable.sh b/test/test-systemctl-enable.sh
index e22a3ef..5615c90 100644
--- a/test/test-systemctl-enable.sh
+++ b/test/test-systemctl-enable.sh
@@ -20,10 +20,10 @@ islink() {
test "$(readlink "$1")" = "$2" || return 2
}
-: '------enable nonexistent------------------------------------'
+: '-------enable nonexistent--------------------------------------'
( ! "$systemctl" --root="$root" enable test1.service )
-: '------basic enablement--------------------------------------'
+: '-------basic enablement----------------------------------------'
mkdir -p "$root/etc/systemd/system"
cat >"$root/etc/systemd/system/test1.service" <<EOF
[Install]
@@ -43,7 +43,7 @@ test -h "$root/etc/systemd/system/special.target.requires/test1.service"
test ! -h "$root/etc/systemd/system/default.target.wants/test1.service"
test ! -h "$root/etc/systemd/system/special.target.requires/test1.service"
-: '------enable when link already exists-----------------------'
+: '-------enable when link already exists-------------------------'
# We don't read the symlink target, so it's OK for the symlink to point
# to something else. We should just silently accept this.
@@ -64,7 +64,7 @@ test -h "$root/etc/systemd/system/special.target.requires/test1.service"
test ! -h "$root/etc/systemd/system/default.target.wants/test1.service"
test ! -h "$root/etc/systemd/system/special.target.requires/test1.service"
-: '------suffix guessing---------------------------------------'
+: '-------suffix guessing-----------------------------------------'
"$systemctl" --root="$root" enable test1
test -h "$root/etc/systemd/system/default.target.wants/test1.service"
test -h "$root/etc/systemd/system/special.target.requires/test1.service"
@@ -77,7 +77,7 @@ test -h "$root/etc/systemd/system/special.target.requires/test1.service"
test ! -e "$root/etc/systemd/system/default.target.wants/test1.service"
test ! -e "$root/etc/systemd/system/special.target.requires/test1.service"
-: '-------aliases----------------------------------------------'
+: '-------aliases-------------------------------------------------'
cat >>"$root/etc/systemd/system/test1.service" <<EOF
Alias=test1-goodalias.service
Alias=test1@badalias.service
@@ -97,7 +97,7 @@ test ! -e "$root/etc/systemd/system/test1-badalias.target"
test ! -e "$root/etc/systemd/system/test1-badalias.socket"
test -h "$root/etc/systemd/system/test1-goodalias2.service"
-: '-------aliases in reeanble----------------------------------'
+: '-------aliases in reenable-------------------------------------'
( ! "$systemctl" --root="$root" reenable test1 )
test -h "$root/etc/systemd/system/default.target.wants/test1.service"
test ! -e "$root/etc/systemd/system/test1-goodalias.service"
@@ -112,7 +112,7 @@ test ! -e "$root/etc/systemd/system/default.target.wants/test1.service"
test ! -e "$root/etc/systemd/system/special.target.requires/test1.service"
test ! -e "$root/etc/systemd/system/test1-goodalias.service"
-: '-------aliases when link already exists---------------------'
+: '-------aliases when link already exists------------------------'
cat >"$root/etc/systemd/system/test1a.service" <<EOF
[Install]
Alias=test1a-alias.service
@@ -126,7 +126,7 @@ test -h "$root/etc/systemd/system/test1a-alias.service"
"$systemctl" --root="$root" disable test1a.service
test ! -h "$root/etc/systemd/system/test1a-alias.service"
-: '-------also units-------------------------------------------'
+: '-------also units----------------------------------------------'
cat >"$root/etc/systemd/system/test2.socket" <<EOF
[Install]
WantedBy=sockets.target
@@ -152,7 +152,7 @@ test ! -e "$root/etc/systemd/system/default.target.wants/test2.service"
test ! -e "$root/etc/systemd/system/sockets.target.wants/test2.socket"
-: '-------link-------------------------------------------------'
+: '-------link----------------------------------------------------'
# File doesn't exist yet
test ! -e "$root/link1.path"
( ! "$systemctl" --root="$root" link '/link1.path' )
@@ -166,65 +166,65 @@ EOF
"$systemctl" --root="$root" link '/link1.path'
islink "$root/etc/systemd/system/link1.path" "/link1.path"
-: '-------link already linked same path------------------------'
+: '-------link already linked same path---------------------------'
SYSTEMD_LOG_LEVEL=debug "$systemctl" --root="$root" link '/link1.path' # this passes
islink "$root/etc/systemd/system/link1.path" "/link1.path"
-: '-------link already linked different path-------------------'
+: '-------link already linked different path----------------------'
mkdir "$root/subdir"
cp "$root/link1.path" "$root/subdir/"
( ! "$systemctl" --root="$root" link '/subdir/link1.path' )
islink "$root/etc/systemd/system/link1.path" "/link1.path"
-: '-------link bad suffix--------------------------------------'
+: '-------link bad suffix-----------------------------------------'
cp "$root/link1.path" "$root/subdir/link1.suffix"
( ! "$systemctl" --root="$root" link '/subdir/link1.suffix' )
test ! -e "$root/etc/systemd/system/link1.suffix"
-: '-------unlink by unit name----------------------------------'
+: '-------unlink by unit name-------------------------------------'
"$systemctl" --root="$root" disable 'link1.path'
test ! -e "$root/etc/systemd/system/link1.path"
-: '-------unlink by path---------------------------------------'
+: '-------unlink by path------------------------------------------'
"$systemctl" --root="$root" link '/link1.path'
test -h "$root/etc/systemd/system/link1.path"
"$systemctl" --root="$root" disable '/link1.path'
test ! -e "$root/etc/systemd/system/link1.path"
-: '-------unlink by wrong path---------------------------------'
+: '-------unlink by wrong path------------------------------------'
"$systemctl" --root="$root" link '/link1.path'
test -h "$root/etc/systemd/system/link1.path"
"$systemctl" --root="$root" disable '/subdir/link1.path' # we only care about the name
test ! -e "$root/etc/systemd/system/link1.path"
-: '-------link and enable--------------------------------------'
+: '-------link and enable-----------------------------------------'
"$systemctl" --root="$root" enable '/link1.path'
islink "$root/etc/systemd/system/link1.path" "/link1.path"
islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path"
-: '-------enable already linked same path----------------------'
+: '-------enable already linked same path-------------------------'
"$systemctl" --root="$root" enable '/link1.path'
islink "$root/etc/systemd/system/link1.path" "/link1.path"
islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path"
-: '-------enable already linked different path-----------------'
+: '-------enable already linked different path--------------------'
( ! "$systemctl" --root="$root" enable '/subdir/link1.path' )
islink "$root/etc/systemd/system/link1.path" "/link1.path"
islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path"
-: '-------enable bad suffix------------------------------------'
+: '-------enable bad suffix---------------------------------------'
cp "$root/link1.path" "$root/subdir/link1.suffix"
( ! "$systemctl" --root="$root" enable '/subdir/link1.suffix' )
test ! -e "$root/etc/systemd/system/link1.suffix"
test ! -e "$root/etc/systemd/system/paths.target.wants/link1.suffix"
-: '-------disable by unit name---------------------------------'
+: '-------disable by unit name------------------------------------'
"$systemctl" --root="$root" disable 'link1.path'
test ! -e "$root/etc/systemd/system/link1.path"
test ! -e "$root/etc/systemd/system/paths.target.wants/link1.path"
-: '-------disable by path--------------------------------------'
+: '-------disable by path-----------------------------------------'
"$systemctl" --root="$root" enable '/link1.path'
test -h "$root/etc/systemd/system/link1.path"
test -h "$root/etc/systemd/system/paths.target.wants/link1.path"
@@ -233,7 +233,7 @@ test ! -e "$root/etc/systemd/system/link1.path"
test ! -e "$root/etc/systemd/system/paths.target.wants/link1.path"
-: '-------link and enable-------------------------------------'
+: '-------link and enable-----------------------------------------'
"$systemctl" --root="$root" link '/link1.path'
islink "$root/etc/systemd/system/link1.path" "/link1.path"
test ! -h "$root/etc/systemd/system/paths.target.wants/link1.path"
@@ -246,7 +246,30 @@ islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path"
islink "$root/etc/systemd/system/link1.path" "/link1.path"
islink "$root/etc/systemd/system/paths.target.wants/link1.path" "/link1.path"
-: '-------manual link------------------------------------------'
+: '-------link instance and enable--------------------------------'
+cat >"$root/link-instance@.service" <<EOF
+[Service]
+ExecStart=true
+[Install]
+WantedBy=services.target
+EOF
+
+"$systemctl" --root="$root" link '/link-instance@.service'
+islink "$root/etc/systemd/system/link-instance@.service" "/link-instance@.service"
+
+"$systemctl" --root="$root" enable 'link-instance@first.service'
+islink "$root/etc/systemd/system/link-instance@first.service" "/link-instance@.service"
+islink "$root/etc/systemd/system/services.target.wants/link-instance@first.service" "/link-instance@.service"
+
+SYSTEMD_LOG_LEVEL=debug "$systemctl" --root="$root" reenable 'link-instance@first.service'
+islink "$root/etc/systemd/system/link-instance@first.service" "/link-instance@.service"
+islink "$root/etc/systemd/system/services.target.wants/link-instance@first.service" "/link-instance@.service"
+
+"$systemctl" --root="$root" disable 'link-instance@first.service'
+test ! -h "$root/etc/systemd/system/link-instance@first.service"
+test ! -h "$root/etc/systemd/system/services.target.wants/link-instance@first.service"
+
+: '-------manual link---------------------------------------------'
cat >"$root/link3.suffix" <<EOF
[Install]
WantedBy=services.target
@@ -263,18 +286,18 @@ SYSTEMD_LOG_LEVEL=debug SYSTEMD_LOG_LOCATION=1 "$systemctl" --root="$root" disab
test ! -h "$root/etc/systemd/system/link3.service"
test ! -h "$root/etc/systemd/system/services.target.wants/link3.service"
-: '-------enable on masked-------------------------------------'
+: '-------enable on masked----------------------------------------'
ln -s "/dev/null" "$root/etc/systemd/system/masked.service"
( ! "$systemctl" --root="$root" enable 'masked.service' )
( ! "$systemctl" --root="$root" enable '/etc/systemd/system/masked.service' )
-: '-------enable on masked alias-------------------------------'
+: '-------enable on masked alias----------------------------------'
test -h "$root/etc/systemd/system/masked.service"
ln -s "masked.service" "$root/etc/systemd/system/masked-alias.service"
( ! "$systemctl" --root="$root" enable 'masked-alias.service' )
( ! "$systemctl" --root="$root" enable '/etc/systemd/system/masked-alias.service' )
-: '-------issue 22000: link in subdirectory--------------------'
+: '-------issue 22000: link in subdirectory-----------------------'
mkdir -p "$root/etc/systemd/system/myown.d"
cat >"$root/etc/systemd/system/link5-also.service" <<EOF
[Install]
@@ -295,7 +318,7 @@ test ! -h "$root/etc/systemd/system/services.target.wants/link5-also.service"
test ! -h "$root/etc/systemd/system/services.target.wants/link5.service"
islink "$root/etc/systemd/system/services.target.wants/link5-also.service" "/etc/systemd/system/link5-also.service"
-: '-------template enablement----------------------------------'
+: '-------template enablement-------------------------------------'
cat >"$root/etc/systemd/system/templ1@.service" <<EOF
[Install]
WantedBy=services.target
@@ -314,6 +337,11 @@ test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@one.service" "/etc/systemd/system/templ1@.service"
islink "$root/etc/systemd/system/services.target.wants/templ1@two.service" "/etc/systemd/system/templ1@.service"
+"$systemctl" --root="$root" reenable 'templ1@two.service'
+test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
+islink "$root/etc/systemd/system/services.target.wants/templ1@one.service" "/etc/systemd/system/templ1@.service"
+islink "$root/etc/systemd/system/services.target.wants/templ1@two.service" "/etc/systemd/system/templ1@.service"
+
"$systemctl" --root="$root" disable 'templ1@one.service'
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@one.service"
@@ -324,7 +352,7 @@ test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@one.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@two.service"
-: '-------template enablement w/ default instance--------------'
+: '-------template enablement w/ default instance-----------------'
cat >"$root/etc/systemd/system/templ1@.service" <<EOF
[Install]
# check enablement with
@@ -373,7 +401,7 @@ test ! -h "$root/etc/systemd/system/other@templ1.target.requires/templ1@one.serv
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@two.service"
test ! -h "$root/etc/systemd/system/other@templ1.target.requires/templ1@two.service"
-: '-------removal of relative enablement symlinks--------------'
+: '-------removal of relative enablement symlinks-----------------'
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@.service"
ln -s '../templ1@one.service' "$root/etc/systemd/system/services.target.wants/templ1@one.service"
ln -s 'templ1@two.service' "$root/etc/systemd/system/services.target.wants/templ1@two.service"
@@ -393,7 +421,7 @@ test ! -h "$root/etc/systemd/system/services.target.wants/templ1@five.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@six.service"
test ! -h "$root/etc/systemd/system/services.target.wants/templ1@seven.service"
-: '-------template enablement for another template-------------'
+: '-------template enablement for another template----------------'
cat >"$root/etc/systemd/system/templ2@.service" <<EOF
[Install]
RequiredBy=another-template@.target
@@ -418,7 +446,7 @@ test ! -h "$root/etc/systemd/system/another-template@.target.requires/templ2@two
test ! -h "$root/etc/systemd/system/another-template@.target.requires/templ2@.service"
test ! -h "$root/etc/systemd/system/another-template@.target.requires/templ2@two.service"
-: '-------aliases w/ and w/o instance--------------------------'
+: '-------aliases w/ and w/o instance-----------------------------'
test ! -e "$root/etc/systemd/system/link4.service"
cat >"$root/etc/systemd/system/link4.service" <<EOF
[Install]
@@ -443,7 +471,7 @@ test ! -h "$root/etc/systemd/system/link4@inst.service"
test ! -h "$root/etc/systemd/system/link4alias.service"
test ! -h "$root/etc/systemd/system/link4alias2.service"
-: '-------systemctl enable on path to unit file----------------'
+: '-------systemctl enable on path to unit file-------------------'
cat >"$root/etc/systemd/system/link4.service" <<EOF
[Install]
Alias=link4alias.service
@@ -461,7 +489,7 @@ test ! -h "$root/etc/systemd/system/link4.service"
test ! -h "$root/etc/systemd/system/link4alias.service"
test ! -h "$root/etc/systemd/system/link4alias2.service"
-: '-------issue 661: enable on unit file--------------'
+: '-------issue 661: enable on unit file--------------------------'
test ! -e "$root/etc/systemd/system/link5.service"
cat >"$root/etc/systemd/system/link5.service" <<EOF
[Install]
@@ -479,7 +507,7 @@ islink "$root/etc/systemd/system/link5alias2.service" "/etc/systemd/system/link5
test ! -h "$root/etc/systemd/system/link5alias.service"
test ! -h "$root/etc/systemd/system/link5alias2.service"
-: '-------issue 661: link and enable on unit file--------------'
+: '-------issue 661: link and enable on unit file-----------------'
test ! -e "$root/etc/systemd/system/link5copy.service"
cat >"$root/link5copy.service" <<EOF
[Install]
@@ -506,15 +534,15 @@ test ! -h "$root/etc/systemd/system/link5alias2.service"
"$systemctl" --root="$root" enable '/link5copy.service'
islink "$root/etc/systemd/system/link5copy.service" '/link5copy.service'
-islink "$root/etc/systemd/system/link5alias.service" '/link5copy.service'
-islink "$root/etc/systemd/system/link5alias2.service" '/link5copy.service'
+islink "$root/etc/systemd/system/link5alias.service" '/etc/systemd/system/link5copy.service'
+islink "$root/etc/systemd/system/link5alias2.service" '/etc/systemd/system/link5copy.service'
"$systemctl" --root="$root" disable 'link5copy.service'
test ! -h "$root/etc/systemd/system/link5copy.service"
test ! -h "$root/etc/systemd/system/link5alias.service"
test ! -h "$root/etc/systemd/system/link5alias2.service"
-: '----issue 19437: plain templates in .wants/ or .requires/---'
+: '-------issue 19437: plain templates in .wants/ or .requires/---'
test ! -e "$root/etc/systemd/system/link5@.path"
cat >"$root/etc/systemd/system/link5@.path" <<EOF
[Install]
@@ -538,7 +566,7 @@ test ! -h "$root/etc/systemd/system/target5@.target.requires/link5@.path"
test ! -h "$root/etc/systemd/system/target5@inst.target.wants/link5@.path"
test ! -h "$root/etc/systemd/system/target5@inst.target.requires/link5@.path"
-: '-------removal of symlinks not listed in [Install]----------'
+: '-------removal of symlinks not listed in [Install]-------------'
# c.f. 66a19d85a533b15ed32f4066ec880b5a8c06babd
test ! -e "$root/etc/systemd/system/multilink.mount"
cat >"$root/etc/systemd/system/multilink.mount" <<EOF
@@ -557,7 +585,7 @@ test ! -h "$root/etc/systemd/system/default.target.wants/"
test ! -h "$root/etc/systemd/system/multilink-alias.mount"
test ! -h "$root/etc/systemd/system/multilink-badalias.service"
-: '-------merge 20017: specifiers in the unit file-------------'
+: '-------merge 20017: specifiers in the unit file----------------'
test ! -e "$root/etc/systemd/system/some-some-link6@.socket"
# c.f. de61a04b188f81a85cdb5c64ddb4987dcd9d30d3
@@ -661,7 +689,7 @@ uname -r | grep -q '[^a-zA-Z0-9_.\\-]' || \
# %z is not defined
( ! check_alias z 'z' )
-: '-------specifiers in WantedBy-------------------------------'
+: '-------specifiers in WantedBy----------------------------------'
# We don't need to repeat all the tests. Let's do a basic check that specifier
# expansion is performed.
@@ -687,7 +715,7 @@ test ! -h "$root/etc/systemd/system/another-target2@.target.requires/some-some-l
# TODO: repeat the tests above for presets
-: '-------SYSTEMD_OS_RELEASE relative to root-------------------'
+: '-------SYSTEMD_OS_RELEASE relative to root---------------------'
# check that os-release overwriting works as expected with root
test -e "$root/etc/os-release"
diff --git a/test/test-sysusers.sh.in b/test/test-sysusers.sh.in
index 11e3940..9af253f 100755
--- a/test/test-sysusers.sh.in
+++ b/test/test-sysusers.sh.in
@@ -36,12 +36,12 @@ preprocess() {
compare() {
if ! diff -u "$TESTDIR/etc/passwd" <(preprocess "$1.expected-passwd" "$3"); then
- echo "**** Unexpected output for $f $2"
+ echo >&2 "**** Unexpected output for $f $2"
exit 1
fi
if ! diff -u "$TESTDIR/etc/group" <(preprocess "$1.expected-group" "$3"); then
- echo "**** Unexpected output for $f $2"
+ echo >&2 "**** Unexpected output for $f $2"
exit 1
fi
}
@@ -168,8 +168,8 @@ for f in $(find "$SOURCE"/unhappy-*.input | sort -V); do
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
SYSTEMD_LOG_LEVEL=info $SYSUSERS --root="$TESTDIR" 2>&1 | tail -n1 | sed -r 's/^[^:]+:[^:]+://' >"$TESTDIR/err"
if ! diff -u "$TESTDIR/err" "${f%.*}.expected-err"; then
- echo "**** Unexpected error output for $f"
- cat "$TESTDIR/err"
+ echo >&2 "**** Unexpected error output for $f"
+ cat >&2 "$TESTDIR/err"
exit 1
fi
done
diff --git a/test/test.service.in b/test/test.service.in
new file mode 100644
index 0000000..790c513
--- /dev/null
+++ b/test/test.service.in
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=%N
+Wants=basic.target network.target @wants@
+After=basic.target network.target @after@
+Before=getty-pre.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=@command@
+Type=oneshot
+MemoryAccounting=@memory-accounting@
diff --git a/test/testsuite-04.units/logs-filtering.service b/test/testsuite-04.units/logs-filtering.service
deleted file mode 100644
index 6e2af9a..0000000
--- a/test/testsuite-04.units/logs-filtering.service
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=Log filtering unit
-
-[Service]
-ExecStart=sh -c 'while true; do echo "Logging from the service, and ~more~ foo bar"; sleep .25; done'
-SyslogLevel=notice
diff --git a/test/testsuite-04.units/silent-success.service b/test/testsuite-04.units/silent-success.service
deleted file mode 100644
index 3d83f87..0000000
--- a/test/testsuite-04.units/silent-success.service
+++ /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-23.units/testsuite-23-binds-to.service b/test/testsuite-23.units/testsuite-23-binds-to.service
deleted file mode 100644
index 637fea4..0000000
--- a/test/testsuite-23.units/testsuite-23-binds-to.service
+++ /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=/bin/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-joins-namespace-of-4.service b/test/testsuite-23.units/testsuite-23-joins-namespace-of-4.service
deleted file mode 100644
index 5e823a1..0000000
--- a/test/testsuite-23.units/testsuite-23-joins-namespace-of-4.service
+++ /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=/bin/bash -c 'touch /tmp/shared-private-file && systemd-notify --ready && sleep infinity'
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
index bbbfd7c..0000000
--- a/test/testsuite-23.units/testsuite-23-joins-namespace-of-6.service
+++ /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=/bin/bash -c 'touch /tmp/shared-private-file-x && systemd-notify --ready && sleep infinity'
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
index 6f93c3b..0000000
--- a/test/testsuite-23.units/testsuite-23-non-namespaced.service
+++ /dev/null
@@ -1,6 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Service]
-RuntimeMaxSec=5
-Type=notify
-RemainAfterExit=yes
-ExecStart=/bin/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-prop-stop-one.service b/test/testsuite-23.units/testsuite-23-prop-stop-one.service
deleted file mode 100644
index f068daf..0000000
--- a/test/testsuite-23.units/testsuite-23-prop-stop-one.service
+++ /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=/bin/sleep infinity
-ExecStopPost=systemctl kill --kill-whom=main -sUSR2 testsuite-23.service
diff --git a/test/testsuite-80.units/notify.service b/test/testsuite-80.units/notify.service
deleted file mode 100644
index 196b076..0000000
--- a/test/testsuite-80.units/notify.service
+++ /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/units/testsuite-01.sh b/test/units/TEST-01-BASIC.sh
index 870b62d..bb3ff2f 100755
--- a/test/units/testsuite-01.sh
+++ b/test/units/TEST-01-BASIC.sh
@@ -48,14 +48,16 @@ systemd-analyze blame
# Test for 'systemd-update-utmp runlevel' vs 'systemctl daemon-reexec'.
# See issue #27163.
# shellcheck disable=SC2034
-for _ in {0..10}; do
- systemctl daemon-reexec &
- pid_reexec=$!
- # shellcheck disable=SC2034
+if [[ -x /usr/lib/systemd/systemd-update-utmp ]]; then
for _ in {0..10}; do
- SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-update-utmp runlevel
+ 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
- wait "$pid_reexec"
-done
+fi
touch /testok
diff --git a/test/units/TEST-02-UNITTESTS.sh b/test/units/TEST-02-UNITTESTS.sh
new file mode 100755
index 0000000..6392425
--- /dev/null
+++ b/test/units/TEST-02-UNITTESTS.sh
@@ -0,0 +1,102 @@
+#!/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##*/}"
+ local environment=
+
+ echo "Executing test $name as unit $name.service"
+
+ case "$name" in
+ test-journal-flush)
+ environment="SYSTEMD_LOG_LEVEL=info"
+ ;;
+ test-journal-verify)
+ environment="SYSTEMD_LOG_LEVEL=crit"
+ ;;
+ esac
+
+ systemd-run \
+ --quiet \
+ --property Delegate=1 \
+ --property EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
+ --property "Environment=$environment" \
+ --unit="$name" \
+ --wait "$test" && ret=0 || ret=$?
+
+ exec {LOCK_FD}> /lock
+ flock --exclusive ${LOCK_FD}
+
+ if [[ $ret -eq 77 ]] || [[ $ret -eq 127 ]]; then
+ echo "$name skipped"
+ echo "$name" >>/skipped-tests
+ {
+ echo "--- $name begin ---"
+ journalctl --unit="$name" --no-hostname -o short-monotonic
+ echo "--- $name end ---"
+ } >>/skipped
+ elif [[ $ret -ne 0 ]]; 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
+ 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.sh b/test/units/TEST-03-JOBS.sh
index e3567c2..115b941 100755
--- a/test/units/testsuite-03.sh
+++ b/test/units/TEST-03-JOBS.sh
@@ -80,13 +80,13 @@ cat <<EOF >/run/systemd/system/wait2.service
[Unit]
Description=Wait for 2 seconds
[Service]
-ExecStart=/bin/sh -ec 'sleep 2'
+ExecStart=sh -ec 'sleep 2'
EOF
cat <<EOF >/run/systemd/system/wait5fail.service
[Unit]
Description=Wait for 5 seconds and fail
[Service]
-ExecStart=/bin/sh -ec 'sleep 5; false'
+ExecStart=sh -ec 'sleep 5; false'
EOF
# wait2 succeeds
diff --git a/test/units/TEST-04-JOURNAL.LogFilterPatterns.sh b/test/units/TEST-04-JOURNAL.LogFilterPatterns.sh
new file mode 100755
index 0000000..ec99b86
--- /dev/null
+++ b/test/units/TEST-04-JOURNAL.LogFilterPatterns.sh
@@ -0,0 +1,93 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if ! cgroupfs_supports_user_xattrs; then
+ echo "CGroup does not support user xattrs, skipping LogFilterPatterns= tests."
+ exit 0
+fi
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+NEEDS_RELOAD=
+
+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"
+ NEEDS_RELOAD=1
+}
+
+run_service_and_fetch_logs() {
+ local unit="${1:?}"
+ local start
+
+ if [[ -n "$NEEDS_RELOAD" ]]; then
+ systemctl daemon-reload
+ NEEDS_RELOAD=
+ fi
+
+ journalctl --sync
+ start="$(date '+%Y-%m-%d %T.%6N')"
+ systemctl start "$unit"
+ journalctl -q -u "$unit" -S "$start" -p notice
+}
+
+at_exit() {
+ rm -rf /run/systemd/system/{logs-filtering,delegated-cgroup-filtering}.service.d
+ systemctl daemon-reload
+}
+
+trap at_exit EXIT
+
+# 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") ]]
diff --git a/test/units/testsuite-04.SYSTEMD_JOURNAL_COMPRESS.sh b/test/units/TEST-04-JOURNAL.SYSTEMD_JOURNAL_COMPRESS.sh
index 96d096d..6da9f5e 100755
--- a/test/units/testsuite-04.SYSTEMD_JOURNAL_COMPRESS.sh
+++ b/test/units/TEST-04-JOURNAL.SYSTEMD_JOURNAL_COMPRESS.sh
@@ -7,6 +7,12 @@ set -o pipefail
mkdir /run/systemd/system/systemd-journald.service.d
MACHINE_ID="$(</etc/machine-id)"
+mkdir -p /run/systemd/journald.conf.d
+cat <<EOF >/run/systemd/journald.conf.d/compress.conf
+[Journal]
+Compress=yes
+EOF
+
# Reset the start-limit counters, as we're going to restart journald a couple of times
systemctl reset-failed systemd-journald.service
@@ -35,6 +41,7 @@ EOF
fi
done
+rm /run/systemd/journald.conf.d/compress.conf
rm /run/systemd/system/systemd-journald.service.d/compress.conf
systemctl daemon-reload
systemctl restart systemd-journald.service
diff --git a/test/units/testsuite-04.bsod.sh b/test/units/TEST-04-JOURNAL.bsod.sh
index 30f0cb0..83feb89 100755
--- a/test/units/testsuite-04.bsod.sh
+++ b/test/units/TEST-04-JOURNAL.bsod.sh
@@ -4,7 +4,12 @@ set -eux
set -o pipefail
if systemd-detect-virt -cq; then
- echo "This test requires a VM, skipping the test"
+ 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
@@ -17,8 +22,22 @@ at_exit() {
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
@@ -88,6 +107,7 @@ 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
diff --git a/test/units/TEST-04-JOURNAL.cat.sh b/test/units/TEST-04-JOURNAL.cat.sh
new file mode 100755
index 0000000..bef3e18
--- /dev/null
+++ b/test/units/TEST-04-JOURNAL.cat.sh
@@ -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/testsuite-04.corrupted-journals.sh b/test/units/TEST-04-JOURNAL.corrupted-journals.sh
index 2123b10..479011e 100755
--- a/test/units/testsuite-04.corrupted-journals.sh
+++ b/test/units/TEST-04-JOURNAL.corrupted-journals.sh
@@ -6,11 +6,11 @@ set -o pipefail
JOURNAL_DIR="$(mktemp -d)"
REMOTE_OUT="$(mktemp -d)"
# tar on C8S doesn't support the --zstd option
-unzstd --stdout "/test-journals/afl-corrupted-journals.tar.zst" | tar -xC "$JOURNAL_DIR/"
+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 /test-journals/corrupted/ -name "*.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
diff --git a/test/units/testsuite-04.fss.sh b/test/units/TEST-04-JOURNAL.fss.sh
index 03351b8..03351b8 100755
--- a/test/units/testsuite-04.fss.sh
+++ b/test/units/TEST-04-JOURNAL.fss.sh
diff --git a/test/units/testsuite-04.journal-append.sh b/test/units/TEST-04-JOURNAL.journal-append.sh
index 35f9433..35f9433 100755
--- a/test/units/testsuite-04.journal-append.sh
+++ b/test/units/TEST-04-JOURNAL.journal-append.sh
diff --git a/test/units/testsuite-04.journal-corrupt.sh b/test/units/TEST-04-JOURNAL.journal-corrupt.sh
index 051d0ab..c25cf54 100755
--- a/test/units/testsuite-04.journal-corrupt.sh
+++ b/test/units/TEST-04-JOURNAL.journal-corrupt.sh
@@ -10,6 +10,7 @@ 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
@@ -20,6 +21,7 @@ for _ in {0..9}; do
done
systemctl stop --user -M testuser@ user-sleep.service
+loginctl disable-linger testuser
journalctl --sync
journalctl --rotate --vacuum-files=1
diff --git a/test/units/TEST-04-JOURNAL.journal-gatewayd.sh b/test/units/TEST-04-JOURNAL.journal-gatewayd.sh
new file mode 100755
index 0000000..9c7a3d0
--- /dev/null
+++ b/test/units/TEST-04-JOURNAL.journal-gatewayd.sh
@@ -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/testsuite-04.journal-remote.sh b/test/units/TEST-04-JOURNAL.journal-remote.sh
index c7b99b1..c7b99b1 100755
--- a/test/units/testsuite-04.journal-remote.sh
+++ b/test/units/TEST-04-JOURNAL.journal-remote.sh
diff --git a/test/units/testsuite-04.journal.sh b/test/units/TEST-04-JOURNAL.journal.sh
index 3b72aa4..bd9f8a5 100755
--- a/test/units/testsuite-04.journal.sh
+++ b/test/units/TEST-04-JOURNAL.journal.sh
@@ -1,6 +1,5 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2317
set -eux
set -o pipefail
@@ -105,9 +104,16 @@ diff /tmp/expected /tmp/output
# 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
+[[ -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
@@ -124,6 +130,8 @@ journalctl -b -n 1 -r --user-unit "*"
# 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
@@ -135,6 +143,8 @@ 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
@@ -235,7 +245,7 @@ JOURNAL_DIR="$(mktemp -d)"
while read -r file; do
filename="${file##*/}"
unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}"
-done < <(find /test-journals/no-rtc -name "*.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'
@@ -243,9 +253,6 @@ diff -u /tmp/lb1 - <<'EOF'
EOF
rm -rf "$JOURNAL_DIR" /tmp/lb1
-# v255-only: skip the following test case, as it suffers from systemd/systemd#30886
-exit 0
-
# 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
@@ -253,8 +260,8 @@ 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 -xec "echo hello; echo world"
-journalctl --sync
+systemd-run --unit="$UNIT_NAME" --wait --service-type=exec -p LogLevelMax=info \
+ bash -ec "set -x; echo hello; echo world; set +x; 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
@@ -267,6 +274,7 @@ diff <(journalctl --cursor-file="$CURSOR_FILE" -p info -o cat _SYSTEMD_UNIT="$UN
hello
+ echo world
world
++ set +x
EOF
rm -f "$CURSOR_FILE"
diff --git a/test/units/testsuite-04.sh b/test/units/TEST-04-JOURNAL.sh
index 9c2a033..9c2a033 100755
--- a/test/units/testsuite-04.sh
+++ b/test/units/TEST-04-JOURNAL.sh
diff --git a/test/units/TEST-05-RLIMITS.effective-limit.sh b/test/units/TEST-05-RLIMITS.effective-limit.sh
new file mode 100755
index 0000000..f6639f1
--- /dev/null
+++ b/test/units/TEST-05-RLIMITS.effective-limit.sh
@@ -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=400M
+MemoryHigh=400M
+TasksMax=400
+EOF
+
+cat >/run/systemd/system/"$pre"alpha-beta.slice <<EOF
+[Slice]
+MemoryMax=100M
+MemoryHigh=100M
+TasksMax=100
+EOF
+
+cat >/run/systemd/system/"$pre"alpha-beta-gamma.slice <<EOF
+[Slice]
+MemoryMax=200M
+MemoryHigh=200M
+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=50M \
+ -p MemoryHigh=50M \
+ -p TasksMax=50 \
+ sleep inf
+
+# Compare with inequality because test can run in a constrained container
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "52428800"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "52428800"
+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")" "104857600"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "104857600"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "100"
+
+systemctl set-property "$slc0" \
+ MemoryMax=50M \
+ MemoryHigh=50M \
+ TasksMax=50
+
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "52428800"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "52428800"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
+
+systemctl stop "$srv"
+
+rm -f /run/systemd/system/"$pre"* || :
diff --git a/test/units/testsuite-05.sh b/test/units/TEST-05-RLIMITS.rlimit.sh
index 870845d..6b425d4 100755
--- a/test/units/testsuite-05.sh
+++ b/test/units/TEST-05-RLIMITS.rlimit.sh
@@ -16,12 +16,10 @@ 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" ]]
+[[ "$(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" ]]'
-
-touch /testok
diff --git a/test/units/testsuite-13.sh b/test/units/TEST-05-RLIMITS.sh
index 9c2a033..9c2a033 100755
--- a/test/units/testsuite-13.sh
+++ b/test/units/TEST-05-RLIMITS.sh
diff --git a/test/units/testsuite-06.sh b/test/units/TEST-06-SELINUX.sh
index 7fc3c37..937a040 100755
--- a/test/units/testsuite-06.sh
+++ b/test/units/TEST-06-SELINUX.sh
@@ -3,6 +3,12 @@
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)
diff --git a/test/units/TEST-07-PID1.aux-scope.sh b/test/units/TEST-07-PID1.aux-scope.sh
new file mode 100755
index 0000000..8e2a99c
--- /dev/null
+++ b/test/units/TEST-07-PID1.aux-scope.sh
@@ -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/testsuite-07.exec-context.sh b/test/units/TEST-07-PID1.exec-context.sh
index b44658f..a3379ef 100755
--- a/test/units/testsuite-07.exec-context.sh
+++ b/test/units/TEST-07-PID1.exec-context.sh
@@ -32,10 +32,19 @@ proc_supports_option() {
# 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; test ! -w /boot; test -w /etc; test -w /var"
+ 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; test ! -w /boot; test ! -w /etc; test -w /var"
+ 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 \
@@ -149,13 +158,13 @@ if ! systemd-detect-virt -cq; then
-p DevicePolicy=closed
-p DevicePolicy=strict
-p DeviceAllow="char-mem rm" # Allow read & mknod for /dev/{null,zero,...}
- -p DeviceAllow="/dev/loop0 rw"
- -p DeviceAllow="/dev/loop0 w" # Allow write for /dev/loop0
+ -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 /dev/loop0; test -w /dev/loop0; test ! -r /dev/tty; test ! -w /dev/tty'
+ 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*=
diff --git a/test/units/TEST-07-PID1.exec-deserialization.sh b/test/units/TEST-07-PID1.exec-deserialization.sh
new file mode 100755
index 0000000..04f17c8
--- /dev/null
+++ b/test/units/TEST-07-PID1.exec-deserialization.sh
@@ -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
index 0000000..0211166
--- /dev/null
+++ b/test/units/TEST-07-PID1.exec-timestamps.sh
@@ -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/testsuite-07.issue-14566.sh b/test/units/TEST-07-PID1.issue-14566.sh
index d4be5b5..d4be5b5 100755
--- a/test/units/testsuite-07.issue-14566.sh
+++ b/test/units/TEST-07-PID1.issue-14566.sh
diff --git a/test/units/testsuite-07.issue-16115.sh b/test/units/TEST-07-PID1.issue-16115.sh
index 8f63826..8f63826 100755
--- a/test/units/testsuite-07.issue-16115.sh
+++ b/test/units/TEST-07-PID1.issue-16115.sh
diff --git a/test/units/testsuite-07.issue-1981.sh b/test/units/TEST-07-PID1.issue-1981.sh
index 6eb802c..dcfa9b1 100755
--- a/test/units/testsuite-07.issue-1981.sh
+++ b/test/units/TEST-07-PID1.issue-1981.sh
@@ -24,7 +24,7 @@ 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=/bin/echo Timer runs me
+ExecStart=echo Timer runs me
EOF
cat >/run/systemd/system/my.timer <<EOF
diff --git a/test/units/testsuite-07.issue-2467.sh b/test/units/TEST-07-PID1.issue-2467.sh
index de0577b..de0577b 100755
--- a/test/units/testsuite-07.issue-2467.sh
+++ b/test/units/TEST-07-PID1.issue-2467.sh
diff --git a/test/units/testsuite-07.issue-27953.sh b/test/units/TEST-07-PID1.issue-27953.sh
index 8659970..8659970 100755
--- a/test/units/testsuite-07.issue-27953.sh
+++ b/test/units/TEST-07-PID1.issue-27953.sh
diff --git a/test/units/testsuite-07.issue-30412.sh b/test/units/TEST-07-PID1.issue-30412.sh
index c1cb00e..c1cb00e 100755
--- a/test/units/testsuite-07.issue-30412.sh
+++ b/test/units/TEST-07-PID1.issue-30412.sh
diff --git a/test/units/testsuite-07.issue-3166.sh b/test/units/TEST-07-PID1.issue-3166.sh
index 6677901..6677901 100755
--- a/test/units/testsuite-07.issue-3166.sh
+++ b/test/units/TEST-07-PID1.issue-3166.sh
diff --git a/test/units/testsuite-07.issue-3171.sh b/test/units/TEST-07-PID1.issue-3171.sh
index db17c25..374df54 100755
--- a/test/units/testsuite-07.issue-3171.sh
+++ b/test/units/TEST-07-PID1.issue-3171.sh
@@ -24,7 +24,7 @@ cat >/run/systemd/system/issue-3171@.service <<EOF
Description=Test service
[Service]
StandardInput=socket
-ExecStart=/bin/sh -x -c cat
+ExecStart=sh -x -c cat
EOF
systemctl start issue-3171.socket
diff --git a/test/units/testsuite-07.main-PID-change.sh b/test/units/TEST-07-PID1.main-PID-change.sh
index bd1144c..16f3510 100755
--- a/test/units/testsuite-07.main-PID-change.sh
+++ b/test/units/TEST-07-PID1.main-PID-change.sh
@@ -10,7 +10,7 @@ set -o pipefail
# The main service PID should be the parent bash process
MAINPID="${PPID:?}"
-test "$(systemctl show -P MainPID testsuite-07.service)" -eq "$MAINPID"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
# Start a test process inside of our own cgroup
sleep infinity &
@@ -23,41 +23,41 @@ 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"
+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 testsuite-07.service)" -eq "$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 testsuite-07.service)" -eq "$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 testsuite-07.service)" -eq "$MAINPID"
+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 testsuite-07.service)" -eq "$MAINPID"
+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 testsuite-07.service)" -eq "$MAINPID"
+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 testsuite-07.service)" -eq "$MAINPID"
+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 testsuite-07.service)" -eq "$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 testsuite-07.service)" -eq "$MAINPID"
+test "$(systemctl show -P MainPID TEST-07-PID1.service)" -eq "$MAINPID"
cat >/tmp/test-mainpid.sh <<\EOF
#!/usr/bin/env bash
diff --git a/test/units/testsuite-07.mount-invalid-chars.sh b/test/units/TEST-07-PID1.mount-invalid-chars.sh
index a879334..a879334 100755
--- a/test/units/testsuite-07.mount-invalid-chars.sh
+++ b/test/units/TEST-07-PID1.mount-invalid-chars.sh
diff --git a/test/units/testsuite-07.poll-limit.sh b/test/units/TEST-07-PID1.poll-limit.sh
index 480d7ee..ca988b2 100755
--- a/test/units/testsuite-07.poll-limit.sh
+++ b/test/units/TEST-07-PID1.poll-limit.sh
@@ -5,12 +5,12 @@ set -o pipefail
systemd-analyze log-level debug
-cat > /run/systemd/system/floodme@.service <<EOF
+cat >/run/systemd/system/floodme@.service <<EOF
[Service]
-ExecStart=/bin/true
+ExecStart=true
EOF
-cat > /run/systemd/system/floodme.socket <<EOF
+cat >/run/systemd/system/floodme.socket <<EOF
[Socket]
ListenStream=/tmp/floodme
PollLimitIntervalSec=10s
@@ -24,7 +24,7 @@ systemctl start floodme.socket
START=$(date +%s%N)
# Trigger this 100 times in a flood
-for (( i=0 ; i < 100; i++ )) ; do
+for _ in {1..100}; do
logger -u /tmp/floodme foo &
done
diff --git a/test/units/TEST-07-PID1.pr-31351.sh b/test/units/TEST-07-PID1.pr-31351.sh
new file mode 100755
index 0000000..f6911f2
--- /dev/null
+++ b/test/units/TEST-07-PID1.pr-31351.sh
@@ -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/testsuite-07.private-network.sh b/test/units/TEST-07-PID1.private-network.sh
index 37658f7..37658f7 100755
--- a/test/units/testsuite-07.private-network.sh
+++ b/test/units/TEST-07-PID1.private-network.sh
diff --git a/test/units/testsuite-07.sh b/test/units/TEST-07-PID1.sh
index 2ee1457..873d638 100755
--- a/test/units/testsuite-07.sh
+++ b/test/units/TEST-07-PID1.sh
@@ -13,3 +13,4 @@ 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
index 0000000..a61b1c0
--- /dev/null
+++ b/test/units/TEST-07-PID1.socket-pass-fds.sh
@@ -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
index 0000000..30f80c5
--- /dev/null
+++ b/test/units/TEST-07-PID1.type-exec-parallel.sh
@@ -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/testsuite-08.sh b/test/units/TEST-08-INITRD.sh
index 5c6b4ce..4804527 100755
--- a/test/units/testsuite-08.sh
+++ b/test/units/TEST-08-INITRD.sh
@@ -13,7 +13,7 @@ fi
if [[ "$(systemctl show -P InitRDTimestampMonotonic)" -eq 0 ]]; then
echo "systemd didn't run in the initrd, skipping the test"
touch /skipped
- exit 0
+ exit 77
fi
# We should've created a mount under /run in initrd (see the other half of the test)
@@ -22,9 +22,9 @@ 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
+# Copy the prepared exitrd to its intended location. Check the respective
# test.sh file for details
mkdir -p /run/initramfs
-cp -r /shutdown-initrd/* /run/initramfs/
+cp -r /exitrd/* /run/initramfs/
touch /testok
diff --git a/test/units/testsuite-09.journal.sh b/test/units/TEST-09-REBOOT.journal.sh
index 2ef192c..726e800 100755
--- a/test/units/testsuite-09.journal.sh
+++ b/test/units/TEST-09-REBOOT.journal.sh
@@ -22,6 +22,22 @@ get_last_timestamp() {
journalctl -b "${1:?}" -o json -n 1 | jq -r '.__REALTIME_TIMESTAMP'
}
+# There may be huge amount of pending messages in sockets. Processing them may cause journal rotation.
+# If the journal is rotated in the loop below, some journal file may not be loaded and an unexpected
+# result may be provided. To mitigate such, flush (if not yet) and sync before reading journals.
+# Workaround for #32890.
+journalctl --flush
+journalctl --sync
+# Sometimes, loading partially written .journal file, and journalctl handled that as 'truncated':
+# ===
+# May 21 02:25:55 TEST-09-REBOOT.sh[433]: + journalctl --list-boots -o json
+# May 21 02:25:55 journalctl[433]: Journal file /var/log/journal/173da2fad3064e3e9211a7ed7d59360b/system.journal is truncated, ignoring file.
+# ===
+# If that happens, the entries stored in the journal file are ignored, and the results of --list-boots
+# and subsequent call of journalctl may become inconsistent. To prevent such issue, let's also rotate
+# the journal. Then, all journal entries we are interested in are stored in the archived journal files.
+journalctl --rotate
+
# Issue: #29275, second part
# Now let's check if the boot entries are in the correct/expected order
index=0
@@ -70,3 +86,47 @@ journalctl --list-boots -o json | jq -r '.[] | [.index, .boot_id, .first_entry,
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.sh b/test/units/TEST-09-REBOOT.sh
index cd95660..85630b6 100755
--- a/test/units/testsuite-09.sh
+++ b/test/units/TEST-09-REBOOT.sh
@@ -3,7 +3,7 @@
set -eux
set -o pipefail
-NUM_REBOOT=4
+export NUM_REBOOT=4
# shellcheck source=test/units/test-control.sh
. "$(dirname "$0")"/test-control.sh
diff --git a/test/units/TEST-13-NSPAWN.importctl.sh b/test/units/TEST-13-NSPAWN.importctl.sh
new file mode 100755
index 0000000..a13e3fd
--- /dev/null
+++ b/test/units/TEST-13-NSPAWN.importctl.sh
@@ -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/testsuite-13.machinectl.sh b/test/units/TEST-13-NSPAWN.machinectl.sh
index b5f90f6..462cc6a 100755
--- a/test/units/testsuite-13.machinectl.sh
+++ b/test/units/TEST-13-NSPAWN.machinectl.sh
@@ -20,9 +20,12 @@ at_exit() {
trap at_exit EXIT
-# Mount tmpfs over /var/lib/machines to not pollute the image
+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 -t tmpfs tmpfs /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
@@ -32,7 +35,7 @@ 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
+#!/usr/bin/bash
PID=0
@@ -129,6 +132,9 @@ machinectl show-image clone1
machinectl rename clone1 clone2
(! machinectl show-image clone1)
machinectl show-image clone2
+# `machinectl read-only` uses chattr (ioctl(FS_IOC_SETFLAGS)) when the container is backed by a directory,
+# and this operation might not be implemented on certain filesystems (i.e. tmpfs on older kernels), so check
+# if we have chattr support before running following tests
if lsattr -d /var/lib/machines >/dev/null; then
[[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]]
machinectl read-only clone2 yes
@@ -151,37 +157,37 @@ test ! -d /var/lib/machines/.hidden1
# Prepare a simple raw container
mkdir -p /tmp/mnt
-dd if=/dev/zero of=/tmp/container.raw bs=1M count=64
-mkfs.ext4 /tmp/container.raw
-mount -o loop /tmp/container.raw /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 /tmp/container.raw container-raw
+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 /tmp/container-export.raw
-machinectl import-raw /tmp/container-export.raw container-raw-reimport
+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 /tmp/container{,-export}.raw
+rm -f /var/tmp/container{,-export}.raw
# Prepare a simple tar.gz container
-tar -pczf /tmp/container.tar.gz -C /var/lib/machines/container1 .
+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 /tmp/container.tar.gz container-tar
-[[ "$(machinectl show-image --property=Type --value container-tar)" == "directory" ]]
+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 /tmp/container-export.tar.gz
-machinectl import-tar /tmp/container-export.tar.gz container-tar-reimport
-[[ "$(machinectl show-image --property=Type --value container-tar-reimport)" == "directory" ]]
-rm -f /tmp/container{,-export}.tar.gz
+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 /tmp/container.dir
-machinectl import-fs /tmp/container.dir container-dir
-[[ "$(machinectl show-image --property=Type --value container-dir)" == "directory" ]]
+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 /tmp/container.dir
+rm -fr /var/tmp/container.dir
timeout 10 bash -c "until machinectl clean --all; do sleep .5; done"
diff --git a/test/units/testsuite-13.nspawn-oci.sh b/test/units/TEST-13-NSPAWN.nspawn-oci.sh
index 8fa0bc4..65dcc96 100755
--- a/test/units/testsuite-13.nspawn-oci.sh
+++ b/test/units/TEST-13-NSPAWN.nspawn-oci.sh
@@ -23,9 +23,9 @@ at_exit() {
trap at_exit EXIT
-# Mount tmpfs over /var/lib/machines to not pollute the image
+# Mount temporary directory over /var/lib/machines to not pollute the image
mkdir -p /var/lib/machines
-mount -t tmpfs tmpfs /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)"
@@ -34,7 +34,7 @@ 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)"
+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"
diff --git a/test/units/testsuite-13.nspawn.sh b/test/units/TEST-13-NSPAWN.nspawn.sh
index 01f6eb6..7901e98 100755
--- a/test/units/testsuite-13.nspawn.sh
+++ b/test/units/TEST-13-NSPAWN.nspawn.sh
@@ -71,9 +71,9 @@ if unshare -U bash -c :; then
IS_USERNS_SUPPORTED=yes
fi
-# Mount tmpfs over /var/lib/machines to not pollute the image
+# Mount temporary directory over /var/lib/machines to not pollute the image
mkdir -p /var/lib/machines
-mount -t tmpfs tmpfs /var/lib/machines
+mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines
testcase_sanity() {
local template root image uuid tmpdir
@@ -82,8 +82,8 @@ testcase_sanity() {
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=64
+ 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
@@ -94,7 +94,7 @@ testcase_sanity() {
systemd-nspawn --version
# --template=
- root="$(mktemp -u -d /var/lib/machines/testsuite-13.sanity.XXX)"
+ 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
@@ -271,6 +271,13 @@ EOF
--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
@@ -324,7 +331,7 @@ EOF
}
nspawn_settings_cleanup() {
- for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-ipvlan{1,2}; do
+ 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
@@ -332,19 +339,25 @@ nspawn_settings_cleanup() {
}
testcase_nspawn_settings() {
- local root container dev private_users
+ local root container dev private_users wlan_names
mkdir -p /run/systemd/nspawn
- root="$(mktemp -d /var/lib/machines/testsuite-13.nspawn-settings.XXX)"
+ 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}
- for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do
+ # 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
@@ -394,7 +407,7 @@ Private=yes
VirtualEthernet=yes
VirtualEthernetExtra=my-fancy-veth1
VirtualEthernetExtra=fancy-veth2:my-fancy-veth2
-Interface=sd-shared1 sd-shared2:sd-shared2
+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
@@ -440,12 +453,22 @@ 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-shared2
+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
@@ -482,7 +505,7 @@ bind_user_cleanup() {
testcase_bind_user() {
local root
- root="$(mktemp -d /var/lib/machines/testsuite-13.bind-user.XXX)"
+ 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
@@ -531,7 +554,7 @@ 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)"
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.bind-tmp-path.XXX)"
create_dummy_container "$root"
: >/tmp/bind
systemd-nspawn --register=no \
@@ -546,7 +569,7 @@ testcase_norbind() {
# https://github.com/systemd/systemd/issues/13170
local root
- root="$(mktemp -d /var/lib/machines/testsuite-13.norbind-path.XXX)"
+ 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
@@ -573,7 +596,7 @@ testcase_rootidmap() {
local root cmd permissions
local owner=1000
- root="$(mktemp -d /var/lib/machines/testsuite-13.rootidmap-path.XXX)"
+ 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
@@ -606,12 +629,82 @@ testcase_rootidmap() {
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/testsuite-13.check_notification_socket.XXX)"
+ 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"
@@ -623,7 +716,7 @@ testcase_notification_socket() {
testcase_os_release() {
local root entrypoint os_release_source
- root="$(mktemp -d /var/lib/machines/testsuite-13.os-release.XXX)"
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.os-release.XXX)"
create_dummy_container "$root"
entrypoint="$root/entrypoint.sh"
cat >"$entrypoint" <<\EOF
@@ -665,7 +758,7 @@ 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)"
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.machinectl-bind.XXX)"
create_dummy_container "$root"
container_name="$(basename "$root")"
@@ -700,7 +793,7 @@ testcase_selinux() {
local root
- root="$(mktemp -d /var/lib/machines/testsuite-13.selinux.XXX)"
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.selinux.XXX)"
create_dummy_container "$root"
chcon -R -t container_t "$root"
@@ -717,7 +810,7 @@ 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)"
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.ephemeral-config.XXX)"
create_dummy_container "$root"
container_name="$(basename "$root")"
@@ -760,7 +853,7 @@ matrix_run_one() {
return 0
fi
- root="$(mktemp -d "/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3.XXX")"
+ 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" \
@@ -848,8 +941,8 @@ 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)"
+ 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=(
diff --git a/test/units/testsuite-13.nss-mymachines.sh b/test/units/TEST-13-NSPAWN.nss-mymachines.sh
index b566c73..817431b 100755
--- a/test/units/testsuite-13.nss-mymachines.sh
+++ b/test/units/TEST-13-NSPAWN.nss-mymachines.sh
@@ -17,8 +17,9 @@ at_exit() {
trap at_exit EXIT
+# Mount temporary directory over /var/lib/machines to not pollute the image
mkdir -p /var/lib/machines
-mount -t tmpfs tmpfs /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
@@ -56,7 +57,7 @@ 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
+ip addr add fd00:dead:beef:cafe::2/64 dev ve-manyips nodad
ip addr show dev ve-manyips
touch /initialized
sleep infinity
@@ -90,7 +91,7 @@ done
# 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
+ip addr add fd00:dead:beef:cafe::1/64 dev ve-manyips nodad
getent hosts -s mymachines
getent ahosts -s mymachines
diff --git a/test/units/TEST-13-NSPAWN.sh b/test/units/TEST-13-NSPAWN.sh
new file mode 100755
index 0000000..dd7f274
--- /dev/null
+++ b/test/units/TEST-13-NSPAWN.sh
@@ -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/testsuite-15.sh b/test/units/TEST-15-DROPIN.sh
index e790b37..5e4df52 100755
--- a/test/units/testsuite-15.sh
+++ b/test/units/TEST-15-DROPIN.sh
@@ -5,6 +5,8 @@ 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:?}"
@@ -256,6 +258,8 @@ EOF
'MemoryMax' t 1000000002 \
0
+ timeout 1m bash -c 'until systemctl is-active a-b-c.slice; do sleep 1s; done'
+
# The override takes precedence for MemoryMax
check_ok a-b-c.slice MemoryMax "1000000000"
# The transient setting replaces the default
@@ -273,6 +277,8 @@ EOF
StopUnit 'ss' \
'a-b-c.slice' 'replace'
+ timeout 1m bash -c 'while systemctl is-active a-b-c.slice; do sleep 1s; done'
+
rm -f "/run/systemd/system/$dropin/override.conf"
done
@@ -692,15 +698,15 @@ testcase_symlink_dropin_directory() {
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
+ 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/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
+ 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
diff --git a/test/units/testsuite-16.sh b/test/units/TEST-16-EXTEND-TIMEOUT.sh
index c60995a..c60995a 100755
--- a/test/units/testsuite-16.sh
+++ b/test/units/TEST-16-EXTEND-TIMEOUT.sh
diff --git a/test/units/testsuite-17.00.sh b/test/units/TEST-17-UDEV.00.sh
index d2aec60..d2aec60 100755
--- a/test/units/testsuite-17.00.sh
+++ b/test/units/TEST-17-UDEV.00.sh
diff --git a/test/units/testsuite-17.01.sh b/test/units/TEST-17-UDEV.01.sh
index 44f36f5..44f36f5 100755
--- a/test/units/testsuite-17.01.sh
+++ b/test/units/TEST-17-UDEV.01.sh
diff --git a/test/units/testsuite-17.02.sh b/test/units/TEST-17-UDEV.02.sh
index b232fca..96430e6 100755
--- a/test/units/testsuite-17.02.sh
+++ b/test/units/TEST-17-UDEV.02.sh
@@ -145,6 +145,7 @@ EOF
# make sure that 'udevadm monitor' actually monitor uevents
sleep 1
+ journalctl --sync
since="$(date '+%H:%M:%S')"
# add another interface which will conflict with an existing interface
@@ -172,7 +173,10 @@ EOF
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"
+ journalctl --sync
+ set +o pipefail
+ timeout -v 30 journalctl _PID=1 _COMM=systemd --since "$since" -n all --follow | grep -m 1 -q -F 'foobar: systemd-udevd failed to process the device, ignoring: File exists'
+ set -o pipefail
# 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"
}
diff --git a/test/units/TEST-17-UDEV.03.sh b/test/units/TEST-17-UDEV.03.sh
new file mode 100755
index 0000000..d6b3162
--- /dev/null
+++ b/test/units/TEST-17-UDEV.03.sh
@@ -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/testsuite-17.04.sh b/test/units/TEST-17-UDEV.04.sh
index d1c3c85..d1c3c85 100755
--- a/test/units/testsuite-17.04.sh
+++ b/test/units/TEST-17-UDEV.04.sh
diff --git a/test/units/testsuite-17.05.sh b/test/units/TEST-17-UDEV.05.sh
index 60be31a..60be31a 100755
--- a/test/units/testsuite-17.05.sh
+++ b/test/units/TEST-17-UDEV.05.sh
diff --git a/test/units/testsuite-17.06.sh b/test/units/TEST-17-UDEV.06.sh
index 6d83645..6d83645 100755
--- a/test/units/testsuite-17.06.sh
+++ b/test/units/TEST-17-UDEV.06.sh
diff --git a/test/units/testsuite-17.07.sh b/test/units/TEST-17-UDEV.07.sh
index 629393a..629393a 100755
--- a/test/units/testsuite-17.07.sh
+++ b/test/units/TEST-17-UDEV.07.sh
diff --git a/test/units/testsuite-17.08.sh b/test/units/TEST-17-UDEV.08.sh
index e570c69..e570c69 100755
--- a/test/units/testsuite-17.08.sh
+++ b/test/units/TEST-17-UDEV.08.sh
diff --git a/test/units/testsuite-17.09.sh b/test/units/TEST-17-UDEV.09.sh
index 9993196..9993196 100755
--- a/test/units/testsuite-17.09.sh
+++ b/test/units/TEST-17-UDEV.09.sh
diff --git a/test/units/testsuite-17.10.sh b/test/units/TEST-17-UDEV.10.sh
index f229dcf..a2b8b14 100755
--- a/test/units/testsuite-17.10.sh
+++ b/test/units/TEST-17-UDEV.10.sh
@@ -45,8 +45,8 @@ udevadm control -S
udevadm control -R
udevadm control -p HELLO=world
udevadm control -m 42
-udevadm control --ping
-udevadm control -t 5
+udevadm control --ping -t 5
+udevadm control --load-credentials
udevadm control -h
udevadm info /dev/null
diff --git a/test/units/testsuite-17.11.sh b/test/units/TEST-17-UDEV.11.sh
index 42b925f..42b925f 100755
--- a/test/units/testsuite-17.11.sh
+++ b/test/units/TEST-17-UDEV.11.sh
diff --git a/test/units/testsuite-17.12.sh b/test/units/TEST-17-UDEV.12.sh
index ccc91bf..ccc91bf 100755
--- a/test/units/testsuite-17.12.sh
+++ b/test/units/TEST-17-UDEV.12.sh
diff --git a/test/units/testsuite-17.13.sh b/test/units/TEST-17-UDEV.13.sh
index d9dfdd7..d9dfdd7 100755
--- a/test/units/testsuite-17.13.sh
+++ b/test/units/TEST-17-UDEV.13.sh
diff --git a/test/units/TEST-17-UDEV.credentials.sh b/test/units/TEST-17-UDEV.credentials.sh
new file mode 100755
index 0000000..42d3883
--- /dev/null
+++ b/test/units/TEST-17-UDEV.credentials.sh
@@ -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
index 0000000..517cc3f
--- /dev/null
+++ b/test/units/TEST-17-UDEV.link-property.sh
@@ -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/testsuite-17.sh b/test/units/TEST-17-UDEV.sh
index 14ceeba..14ceeba 100755
--- a/test/units/testsuite-17.sh
+++ b/test/units/TEST-17-UDEV.sh
diff --git a/test/units/testsuite-18.sh b/test/units/TEST-18-FAILUREACTION.sh
index 44b792f..364c20d 100755
--- a/test/units/testsuite-18.sh
+++ b/test/units/TEST-18-FAILUREACTION.sh
@@ -11,7 +11,7 @@ if ! test -f /firstphase ; then
systemd-run --wait -p SuccessAction=reboot true
else
echo OK >/testok
- systemd-run --wait -p FailureAction=poweroff false
+ 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/TEST-19-CGROUP.ExitType-cgroup.sh
index cd221d7..65c2606 100755
--- a/test/units/testsuite-19.ExitType-cgroup.sh
+++ b/test/units/TEST-19-CGROUP.ExitType-cgroup.sh
@@ -27,8 +27,9 @@ disown
systemd-notify --ready
-# Run the stop/kill command
-\$1 &
+# Run the stop/kill command, but sleep a bit to make the sleep infinity
+# below actually started before stopping/killing the service.
+(sleep 1; \$1) &
# process tree: systemd -> bash -> sleep
sleep infinity
diff --git a/test/units/testsuite-19.cleanup-slice.sh b/test/units/TEST-19-CGROUP.cleanup-slice.sh
index 5d63160..5d63160 100755
--- a/test/units/testsuite-19.cleanup-slice.sh
+++ b/test/units/TEST-19-CGROUP.cleanup-slice.sh
diff --git a/test/units/testsuite-19.delegate.sh b/test/units/TEST-19-CGROUP.delegate.sh
index 74d36c4..022515f 100755
--- a/test/units/testsuite-19.delegate.sh
+++ b/test/units/TEST-19-CGROUP.delegate.sh
@@ -32,6 +32,7 @@ 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="MemoryAccounting=1" \
--property="DynamicUser=1" \
--property="Delegate=" \
test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \
diff --git a/test/units/testsuite-19.sh b/test/units/TEST-19-CGROUP.sh
index 9c2a033..9c2a033 100755
--- a/test/units/testsuite-19.sh
+++ b/test/units/TEST-19-CGROUP.sh
diff --git a/test/units/testsuite-21.sh b/test/units/TEST-21-DFUZZER.sh
index 02673ab..08ebfd9 100755
--- a/test/units/testsuite-21.sh
+++ b/test/units/TEST-21-DFUZZER.sh
@@ -3,6 +3,12 @@
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
@@ -21,14 +27,32 @@ at_exit() {
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
-sed -i '/\[org.freedesktop.systemd1\]/aorg.freedesktop.systemd1.Manager:Reexecute FIXME' /etc/dfuzzer.conf
-sed -i '/\[org.freedesktop.systemd1\]/aorg.freedesktop.systemd1.Manager:SoftReboot destructive' /etc/dfuzzer.conf
+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?
diff --git a/test/units/testsuite-22.01.sh b/test/units/TEST-22-TMPFILES.01.sh
index 2276b75..2276b75 100755
--- a/test/units/testsuite-22.01.sh
+++ b/test/units/TEST-22-TMPFILES.01.sh
diff --git a/test/units/testsuite-22.02.sh b/test/units/TEST-22-TMPFILES.02.sh
index b883a96..f191ae3 100755
--- a/test/units/testsuite-22.02.sh
+++ b/test/units/TEST-22-TMPFILES.02.sh
@@ -14,6 +14,14 @@ mkdir /tmp/{C,d,D,e}
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 - -
@@ -104,6 +112,14 @@ 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
diff --git a/test/units/testsuite-22.03.sh b/test/units/TEST-22-TMPFILES.03.sh
index 6fce4c0..d158498 100755
--- a/test/units/testsuite-22.03.sh
+++ b/test/units/TEST-22-TMPFILES.03.sh
@@ -12,6 +12,14 @@ 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
@@ -189,6 +197,11 @@ 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
@@ -200,6 +213,12 @@ 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
@@ -207,6 +226,12 @@ 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
@@ -223,6 +248,10 @@ 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
diff --git a/test/units/testsuite-22.04.sh b/test/units/TEST-22-TMPFILES.04.sh
index 7bf2b28..2c0ffe3 100755
--- a/test/units/testsuite-22.04.sh
+++ b/test/units/TEST-22-TMPFILES.04.sh
@@ -9,6 +9,12 @@ 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
diff --git a/test/units/testsuite-22.05.sh b/test/units/TEST-22-TMPFILES.05.sh
index cde9b5d..d78e7ee 100755
--- a/test/units/testsuite-22.05.sh
+++ b/test/units/TEST-22-TMPFILES.05.sh
@@ -12,6 +12,15 @@ mkdir /tmp/{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 - -
diff --git a/test/units/testsuite-22.06.sh b/test/units/TEST-22-TMPFILES.06.sh
index f64a95c..45aea9c 100755
--- a/test/units/testsuite-22.06.sh
+++ b/test/units/TEST-22-TMPFILES.06.sh
@@ -6,7 +6,14 @@ set -eux
set -o pipefail
test_snippet() {
- systemd-tmpfiles "$@" - <<EOF
+ # 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
diff --git a/test/units/testsuite-22.07.sh b/test/units/TEST-22-TMPFILES.07.sh
index de20d5e..de20d5e 100755
--- a/test/units/testsuite-22.07.sh
+++ b/test/units/TEST-22-TMPFILES.07.sh
diff --git a/test/units/testsuite-22.08.sh b/test/units/TEST-22-TMPFILES.08.sh
index 40fafd3..40fafd3 100755
--- a/test/units/testsuite-22.08.sh
+++ b/test/units/TEST-22-TMPFILES.08.sh
diff --git a/test/units/testsuite-22.09.sh b/test/units/TEST-22-TMPFILES.09.sh
index 0857773..0857773 100755
--- a/test/units/testsuite-22.09.sh
+++ b/test/units/TEST-22-TMPFILES.09.sh
diff --git a/test/units/testsuite-22.10.sh b/test/units/TEST-22-TMPFILES.10.sh
index 99052c8..99052c8 100755
--- a/test/units/testsuite-22.10.sh
+++ b/test/units/TEST-22-TMPFILES.10.sh
diff --git a/test/units/testsuite-22.11.sh b/test/units/TEST-22-TMPFILES.11.sh
index f71a95f..6f75888 100755
--- a/test/units/testsuite-22.11.sh
+++ b/test/units/TEST-22-TMPFILES.11.sh
@@ -12,6 +12,19 @@ mkdir /tmp/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
@@ -34,6 +47,19 @@ test ! -f /tmp/x/z2
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
@@ -56,6 +82,20 @@ test ! -f /tmp/x/z2
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]
@@ -79,6 +119,20 @@ test -f /tmp/x/z2
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]
@@ -102,6 +156,19 @@ test -f /tmp/x/z2
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
@@ -125,17 +192,58 @@ test -f /tmp/x/2/a/y2
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
-# X is not supposed to influence R
+# 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 -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/TEST-22-TMPFILES.12.sh
index b8c4da8..57788da 100755
--- a/test/units/testsuite-22.12.sh
+++ b/test/units/TEST-22-TMPFILES.12.sh
@@ -52,6 +52,16 @@ sleep 1
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 -
diff --git a/test/units/testsuite-22.13.sh b/test/units/TEST-22-TMPFILES.13.sh
index 33ef451..33ef451 100755
--- a/test/units/testsuite-22.13.sh
+++ b/test/units/TEST-22-TMPFILES.13.sh
diff --git a/test/units/testsuite-22.14.sh b/test/units/TEST-22-TMPFILES.14.sh
index 2132de7..2132de7 100755
--- a/test/units/testsuite-22.14.sh
+++ b/test/units/TEST-22-TMPFILES.14.sh
diff --git a/test/units/testsuite-22.15.sh b/test/units/TEST-22-TMPFILES.15.sh
index 6cbb498..6ee0e63 100755
--- a/test/units/testsuite-22.15.sh
+++ b/test/units/TEST-22-TMPFILES.15.sh
@@ -13,6 +13,12 @@ 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
@@ -26,6 +32,12 @@ 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
diff --git a/test/units/testsuite-22.16.sh b/test/units/TEST-22-TMPFILES.16.sh
index 555e07f..3d3d0c8 100755
--- a/test/units/testsuite-22.16.sh
+++ b/test/units/TEST-22-TMPFILES.16.sh
@@ -12,6 +12,11 @@ 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
diff --git a/test/units/testsuite-22.17.sh b/test/units/TEST-22-TMPFILES.17.sh
index f43aba5..f43aba5 100755
--- a/test/units/testsuite-22.17.sh
+++ b/test/units/TEST-22-TMPFILES.17.sh
diff --git a/test/units/TEST-22-TMPFILES.18.sh b/test/units/TEST-22-TMPFILES.18.sh
new file mode 100755
index 0000000..5d24197
--- /dev/null
+++ b/test/units/TEST-22-TMPFILES.18.sh
@@ -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
index 0000000..c5a0966
--- /dev/null
+++ b/test/units/TEST-22-TMPFILES.19.sh
@@ -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/testsuite-22.sh b/test/units/TEST-22-TMPFILES.sh
index 9c2a033..9c2a033 100755
--- a/test/units/testsuite-22.sh
+++ b/test/units/TEST-22-TMPFILES.sh
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
index 0000000..4828b9d
--- /dev/null
+++ b/test/units/TEST-23-UNIT-FILE-openfile-child.sh
@@ -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
index 0000000..4b13070
--- /dev/null
+++ b/test/units/TEST-23-UNIT-FILE-short-lived.sh
@@ -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/testsuite-23.ExecReload.sh b/test/units/TEST-23-UNIT-FILE.ExecReload.sh
index b497f73..d544ce6 100755
--- a/test/units/testsuite-23.ExecReload.sh
+++ b/test/units/TEST-23-UNIT-FILE.ExecReload.sh
@@ -14,7 +14,7 @@ SERVICE_NAME="${SERVICE_PATH##*/}"
echo "[#1] Failing ExecReload= should not kill the service"
cat >"$SERVICE_PATH" <<EOF
[Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
ExecReload=/bin/false
EOF
@@ -30,7 +30,7 @@ systemctl stop "$SERVICE_NAME"
echo "[#2] Failing ExecReload= should not kill the service (multiple ExecReload=)"
cat >"$SERVICE_PATH" <<EOF
[Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
ExecReload=/bin/true
ExecReload=/bin/false
ExecReload=/bin/true
@@ -47,7 +47,7 @@ systemctl stop "$SERVICE_NAME"
echo "[#3] Failing ExecReload=- should not affect reload's exit code"
cat >"$SERVICE_PATH" <<EOF
[Service]
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
ExecReload=-/bin/false
EOF
diff --git a/test/units/testsuite-23.ExecStopPost.sh b/test/units/TEST-23-UNIT-FILE.ExecStopPost.sh
index aeaf3aa..aeaf3aa 100755
--- a/test/units/testsuite-23.ExecStopPost.sh
+++ b/test/units/TEST-23-UNIT-FILE.ExecStopPost.sh
diff --git a/test/units/TEST-23-UNIT-FILE.JoinsNamespaceOf.sh b/test/units/TEST-23-UNIT-FILE.JoinsNamespaceOf.sh
new file mode 100755
index 0000000..4fd68d8
--- /dev/null
+++ b/test/units/TEST-23-UNIT-FILE.JoinsNamespaceOf.sh
@@ -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/testsuite-23.RuntimeDirectoryPreserve.sh b/test/units/TEST-23-UNIT-FILE.RuntimeDirectoryPreserve.sh
index ca57702..ca57702 100755
--- a/test/units/testsuite-23.RuntimeDirectoryPreserve.sh
+++ b/test/units/TEST-23-UNIT-FILE.RuntimeDirectoryPreserve.sh
diff --git a/test/units/testsuite-23.StandardOutput.sh b/test/units/TEST-23-UNIT-FILE.StandardOutput.sh
index 50b9ac2..a951b13 100755
--- a/test/units/testsuite-23.StandardOutput.sh
+++ b/test/units/TEST-23-UNIT-FILE.StandardOutput.sh
@@ -7,7 +7,7 @@ set -o pipefail
systemd-analyze log-level debug
-systemd-run --wait --unit=testsuite-23-standard-output-one \
+systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-one \
-p StandardOutput=file:/tmp/stdout \
-p StandardError=file:/tmp/stderr \
-p Type=exec \
@@ -19,7 +19,7 @@ cmp /tmp/stderr <<EOF
y
EOF
-systemd-run --wait --unit=testsuite-23-standard-output-two \
+systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-two \
-p StandardOutput=file:/tmp/stdout \
-p StandardError=file:/tmp/stderr \
-p Type=exec \
@@ -31,7 +31,7 @@ cmp /tmp/stderr <<EOF
a
EOF
-systemd-run --wait --unit=testsuite-23-standard-output-three \
+systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-three \
-p StandardOutput=append:/tmp/stdout \
-p StandardError=append:/tmp/stderr \
-p Type=exec \
@@ -45,7 +45,7 @@ a
c
EOF
-systemd-run --wait --unit=testsuite-23-standard-output-four \
+systemd-run --wait --unit=TEST-23-UNIT-FILE-standard-output-four \
-p StandardOutput=truncate:/tmp/stdout \
-p StandardError=truncate:/tmp/stderr \
-p Type=exec \
diff --git a/test/units/TEST-23-UNIT-FILE.Upholds.sh b/test/units/TEST-23-UNIT-FILE.Upholds.sh
new file mode 100755
index 0000000..20d2d2b
--- /dev/null
+++ b/test/units/TEST-23-UNIT-FILE.Upholds.sh
@@ -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/testsuite-23.clean-unit.sh b/test/units/TEST-23-UNIT-FILE.clean-unit.sh
index a82b54f..a883243 100755
--- a/test/units/testsuite-23.clean-unit.sh
+++ b/test/units/TEST-23-UNIT-FILE.clean-unit.sh
@@ -26,7 +26,7 @@ StateDirectory=test-service
CacheDirectory=test-service
LogsDirectory=test-service
RuntimeDirectoryPreserve=yes
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
Type=exec
EOF
@@ -97,7 +97,7 @@ StateDirectory=test-service
CacheDirectory=test-service
LogsDirectory=test-service
RuntimeDirectoryPreserve=yes
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
Type=exec
EOF
diff --git a/test/units/testsuite-23.exec-command-ex.sh b/test/units/TEST-23-UNIT-FILE.exec-command-ex.sh
index f926e7d..f926e7d 100755
--- a/test/units/testsuite-23.exec-command-ex.sh
+++ b/test/units/TEST-23-UNIT-FILE.exec-command-ex.sh
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
index 0000000..d06dbaa
--- /dev/null
+++ b/test/units/TEST-23-UNIT-FILE.oneshot-restart.sh
@@ -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
index 0000000..644b6f4
--- /dev/null
+++ b/test/units/TEST-23-UNIT-FILE.openfile.sh
@@ -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/testsuite-23.percentj-wantedby.sh b/test/units/TEST-23-UNIT-FILE.percentj-wantedby.sh
index e9ffaba..c091bd6 100755
--- a/test/units/testsuite-23.percentj-wantedby.sh
+++ b/test/units/TEST-23-UNIT-FILE.percentj-wantedby.sh
@@ -8,8 +8,8 @@ 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" \
+ --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
index 0000000..3a78234
--- /dev/null
+++ b/test/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh
@@ -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/testsuite-23.sh b/test/units/TEST-23-UNIT-FILE.sh
index a929c8b..a929c8b 100755
--- a/test/units/testsuite-23.sh
+++ b/test/units/TEST-23-UNIT-FILE.sh
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
index 0000000..61a6592
--- /dev/null
+++ b/test/units/TEST-23-UNIT-FILE.start-stop-no-reload.sh
@@ -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/testsuite-23.statedir.sh b/test/units/TEST-23-UNIT-FILE.statedir.sh
index b592314..b592314 100755
--- a/test/units/testsuite-23.statedir.sh
+++ b/test/units/TEST-23-UNIT-FILE.statedir.sh
diff --git a/test/units/testsuite-23.success-failure.sh b/test/units/TEST-23-UNIT-FILE.success-failure.sh
index 8fc9596..8fc9596 100755
--- a/test/units/testsuite-23.success-failure.sh
+++ b/test/units/TEST-23-UNIT-FILE.success-failure.sh
diff --git a/test/units/testsuite-23.type-exec.sh b/test/units/TEST-23-UNIT-FILE.type-exec.sh
index 87f32cc..87f32cc 100755
--- a/test/units/testsuite-23.type-exec.sh
+++ b/test/units/TEST-23-UNIT-FILE.type-exec.sh
diff --git a/test/units/testsuite-23.utmp.sh b/test/units/TEST-23-UNIT-FILE.utmp.sh
index 4f84315..4f84315 100755
--- a/test/units/testsuite-23.utmp.sh
+++ b/test/units/TEST-23-UNIT-FILE.utmp.sh
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
index 0000000..3e83d44
--- /dev/null
+++ b/test/units/TEST-23-UNIT-FILE.verify-unit-files.sh
@@ -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/testsuite-23.whoami.sh b/test/units/TEST-23-UNIT-FILE.whoami.sh
index a0c73b8..b538d94 100755
--- a/test/units/testsuite-23.whoami.sh
+++ b/test/units/TEST-23-UNIT-FILE.whoami.sh
@@ -5,11 +5,11 @@
set -eux
set -o pipefail
-test "$(systemctl whoami)" = testsuite-23.service
-test "$(systemctl whoami $$)" = testsuite-23.service
+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
-testsuite-23.service
+TEST-23-UNIT-FILE.service
init.scope
EOF
diff --git a/test/units/testsuite-24.sh b/test/units/TEST-24-CRYPTSETUP.sh
index c815f90..b788c82 100755
--- a/test/units/testsuite-24.sh
+++ b/test/units/TEST-24-CRYPTSETUP.sh
@@ -5,8 +5,6 @@ set -o pipefail
# TODO:
# - /proc/cmdline parsing
-# - figure out token support (apart from TPM2, as that's covered by TEST-70-TPM2)
-# - this might help https://www.qemu.org/docs/master/system/devices/ccid.html
# - expect + interactive auth?
# We set up an encrypted /var partition which should get mounted automatically
@@ -35,6 +33,7 @@ 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
@@ -42,6 +41,11 @@ cryptsetup_start_and_check() {
shift
fi
+ if [[ "${1:?}" == "-u" ]]; then
+ umount_header_and_key=1
+ shift
+ fi
+
for volume in "$@"; do
unit="systemd-cryptsetup@$volume.service"
@@ -64,6 +68,12 @@ cryptsetup_start_and_check() {
return 1
fi
+ if [[ "$umount_header_and_key" -ne 0 ]]; then
+ umount "$TMPFS_DETACHED_KEYFILE"
+ umount "$TMPFS_DETACHED_HEADER"
+ udevadm settle --timeout=60
+ fi
+
systemctl status "$unit"
test -e "/dev/mapper/$volume"
systemctl stop "$unit"
@@ -133,22 +143,31 @@ cryptsetup luksAddKey --batch-mode \
STORE_IMAGE="$WORKDIR/store.img"
truncate -s 64M "$STORE_IMAGE"
STORE_LOOP="$(losetup --show --find --partscan "$STORE_IMAGE")"
-sfdisk "$STORE_LOOP" <<EOF
+udevadm lock --device "$STORE_LOOP" 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
+udevadm settle --timeout=60
mkdir -p /mnt
-mkfs.ext4 -L header_store "/dev/disk/by-partlabel/header_store"
+udevadm lock --device "/dev/disk/by-partlabel/header_store" 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"
+udevadm lock --device "/dev/disk/by-partlabel/keyfile_store" 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
-udevadm settle --timeout=30
+
+# 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=60
# Prepare our test crypttab
[[ -e /etc/crypttab ]] && cp -fv /etc/crypttab /tmp/crypttab.bak
@@ -164,6 +183,7 @@ empty0 $IMAGE_EMPTY - headless=1,
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
@@ -177,6 +197,7 @@ detached_fail4 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,
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
@@ -207,10 +228,46 @@ mkdir -p /run/cryptsetup-keys.d
cp "$IMAGE_EMPTY_KEYFILE" /run/cryptsetup-keys.d/empty_nokey.key
cryptsetup_start_and_check empty_nokey
+if [[ -d /usr/lib/softhsm/tokens ]]; 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.sh b/test/units/TEST-25-IMPORT.sh
index b298c50..b298c50 100755
--- a/test/units/testsuite-25.sh
+++ b/test/units/TEST-25-IMPORT.sh
diff --git a/test/units/testsuite-26.sh b/test/units/TEST-26-SYSTEMCTL.sh
index 1e11c42..ae7a5d6 100755
--- a/test/units/testsuite-26.sh
+++ b/test/units/TEST-26-SYSTEMCTL.sh
@@ -16,12 +16,12 @@ at_exit() {
return 0
}
-trap at_exit EXIT
-
# 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
@@ -56,6 +56,20 @@ 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
@@ -127,7 +141,7 @@ 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"
-systemctl kill "$UNIT_NAME"
+timeout 10 systemctl kill --wait "$UNIT_NAME"
(! systemctl is-active "$UNIT_NAME")
systemctl restart "$UNIT_NAME"
systemctl is-active "$UNIT_NAME"
@@ -188,6 +202,24 @@ test_mask_unmask_revert() {
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"
@@ -209,7 +241,7 @@ systemctl revert "$UNIT_NAME"
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/"
+grep -r "CPUQuota=10.00%" "/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/")
@@ -306,7 +338,7 @@ done
# Aux verbs & assorted checks
systemctl is-active "*-journald.service"
-systemctl cat "*journal*"
+systemctl cat "*udevd*"
systemctl cat "$UNIT_NAME"
systemctl help "$UNIT_NAME"
systemctl service-watchdogs
@@ -348,6 +380,10 @@ if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then
# 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
diff --git a/test/units/testsuite-29.sh b/test/units/TEST-29-PORTABLE.sh
index 676330c..27c24a0 100755
--- a/test/units/testsuite-29.sh
+++ b/test/units/TEST-29-PORTABLE.sh
@@ -5,6 +5,11 @@
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
@@ -31,9 +36,9 @@ 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 /usr/share/app0.raw | grep -q '✓ sysext for portable service'
-systemd-dissect --no-pager /usr/share/app1.raw | grep -q '✓ sysext for portable service'
-systemd-dissect --no-pager /usr/share/conf0.raw | grep -q '✓ confext for 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/
@@ -117,7 +122,7 @@ 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 /usr/share/app0.raw /usr/share/minimal_0.raw app0
+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)"
@@ -127,7 +132,7 @@ grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.atta
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 /usr/share/app0.raw /usr/share/minimal_1.raw app0
+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)"
@@ -137,12 +142,12 @@ grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_1.raw" /run/systemd/system.atta
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 /usr/share/app0.raw /usr/share/minimal_1.raw app0
+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 /usr/share/app0.raw /tmp/app0_1.0.raw
+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
@@ -152,28 +157,28 @@ status="$(portablectl is-attached --extension app0_1 minimal_0)"
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 /usr/share/app1.raw /usr/share/minimal_0.raw app1
+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 /usr/share/app1.raw /tmp/app1_2.raw
+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 /usr/share/app1.raw /usr/share/minimal_1.raw app1
+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 /usr/share/app1.raw /usr/share/minimal_1.raw app1
-portablectl "${ARGS[@]}" attach --force --no-reload --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
+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
@@ -181,7 +186,27 @@ systemctl is-active app1.service
status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "running-runtime" ]]
-portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
+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
@@ -190,7 +215,7 @@ 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 /usr/share/app0.raw /tmp/app10.raw
+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
@@ -206,21 +231,59 @@ 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 /usr/share/app0.raw --extension /usr/share/conf0.raw /usr/share/minimal_0.raw app0
+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 /usr/share/app0.raw --extension /usr/share/conf0.raw /usr/share/minimal_0.raw)"
+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 /usr/share/app0.raw --extension /usr/share/conf0.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /usr/share/conf0.raw"
+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 --now --runtime --extension /usr/share/app0.raw --extension /usr/share/conf0.raw /usr/share/minimal_0.raw app0
+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 /usr/share/app0.raw /tmp/app0
-mount /usr/share/app1.raw /tmp/app1
+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
@@ -266,7 +329,22 @@ grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd
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 --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+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
diff --git a/test/units/testsuite-30.sh b/test/units/TEST-30-ONCLOCKCHANGE.sh
index 104c87b..83698b8 100755
--- a/test/units/testsuite-30.sh
+++ b/test/units/TEST-30-ONCLOCKCHANGE.sh
@@ -16,7 +16,7 @@ systemd-run --on-clock-change touch /tmp/clock-changed
test ! -f /tmp/timezone-changed
test ! -f /tmp/clock-changed
-timedatectl set-timezone Europe/Kiev
+timedatectl set-timezone Europe/Kyiv
while test ! -f /tmp/timezone-changed ; do sleep .5 ; done
diff --git a/test/units/testsuite-31.sh b/test/units/TEST-31-DEVICE-ENUMERATION.sh
index 03aba36..5e99302 100755
--- a/test/units/testsuite-31.sh
+++ b/test/units/TEST-31-DEVICE-ENUMERATION.sh
@@ -3,7 +3,7 @@
set -eux
set -o pipefail
-if journalctl -b -t systemd --grep '\.device: Changed plugged -> dead'; then
+if journalctl -b -t systemd --grep '(?<!loop.|diskseq-.)\.device: Changed plugged -> dead'; then
exit 1
fi
diff --git a/test/units/testsuite-32.sh b/test/units/TEST-32-OOMPOLICY.sh
index 83b548a..046b8b9 100755
--- a/test/units/testsuite-32.sh
+++ b/test/units/TEST-32-OOMPOLICY.sh
@@ -9,7 +9,7 @@ set -o pipefail
# 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
+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
diff --git a/test/units/testsuite-34.sh b/test/units/TEST-34-DYNAMICUSERMIGRATE.sh
index d15b675..d15b675 100755
--- a/test/units/testsuite-34.sh
+++ b/test/units/TEST-34-DYNAMICUSERMIGRATE.sh
diff --git a/test/units/testsuite-35.sh b/test/units/TEST-35-LOGIN.sh
index 36e26da..78e0c1e 100755
--- a/test/units/testsuite-35.sh
+++ b/test/units/TEST-35-LOGIN.sh
@@ -25,14 +25,22 @@ setup_test_user() {
trap cleanup_test_user EXIT
}
-test_enable_debug() {
- mkdir -p /run/systemd/system/systemd-logind.service.d
- cat >/run/systemd/system/systemd-logind.service.d/debug.conf <<EOF
+test_write_dropin() {
+ systemctl edit --runtime --stdin systemd-logind.service --drop-in=debug.conf <<EOF
[Service]
Environment=SYSTEMD_LOG_LEVEL=debug
EOF
- systemctl daemon-reload
- systemctl stop systemd-logind.service
+
+ # 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() {
@@ -57,6 +65,25 @@ EOF
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
@@ -231,7 +258,7 @@ cleanup_session() (
systemctl stop getty@tty2.service
- for s in $(loginctl --no-legend list-sessions | awk '$3 == "logind-test-user" { print $1 }'); do
+ 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
@@ -281,18 +308,18 @@ check_session() (
local seat session leader_pid
- if [[ $(loginctl --no-legend | grep -c "logind-test-user") != 1 ]]; then
+ 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 'logind-test-user *seat' | awk '{ print $4 }')
+ 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 | awk '$3 == "logind-test-user" { print $1 }')
+ 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
@@ -337,7 +364,7 @@ EOF
check_session && break
done
check_session
- assert_eq "$(loginctl --no-legend | awk '$3=="logind-test-user" { print $5 }')" "tty2"
+ assert_eq "$(loginctl --no-legend | grep -v manager | awk '$3=="logind-test-user" { print $7 }')" "tty2"
}
testcase_sanity_check() {
@@ -355,13 +382,15 @@ testcase_sanity_check() {
# 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 | awk '$3 == "logind-test-user" { print $1; exit; }')
+ 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"
@@ -428,7 +457,7 @@ EOF
udevadm info "$dev"
# trigger logind and activate session
- loginctl activate "$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')"
+ loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')"
# check ACL
sleep 1
@@ -469,7 +498,7 @@ testcase_lock_idle_action() {
return
fi
- if loginctl --no-legend | grep -q logind-test-user; then
+ 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
@@ -478,6 +507,7 @@ testcase_lock_idle_action() {
create_session
+ journalctl --sync
ts="$(date '+%H:%M:%S')"
mkdir -p /run/systemd/logind.conf.d
@@ -493,18 +523,24 @@ EOF
# 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"
+ journalctl --sync
+ set +o pipefail
+ timeout -v 35 journalctl -b -u systemd-logind.service --since="$ts" -n all --follow | grep -m 1 -q 'Sent message type=signal .* member=Lock'
+ set -o pipefail
+
+ # 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 2 ]]; 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
+ journalctl --sync
+ set +o pipefail
+ timeout -v 35 journalctl -b -u systemd-logind.service --since="$ts" -n all --follow | grep -m "$((locks + 1))" -q 'Sent message type=signal .* member=Lock'
+ timeout -v 35 journalctl -b -u systemd-logind.service --since="$ts" -n all --follow | grep -m 2 -q -F 'System idle. Will be locked now.'
+ set -o pipefail
}
testcase_session_properties() {
@@ -518,7 +554,7 @@ testcase_session_properties() {
trap cleanup_session RETURN
create_session
- s=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $1 }')
+ 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
}
@@ -534,17 +570,17 @@ testcase_list_users_sessions_seats() {
create_session
# Activate the session
- loginctl activate "$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')"
+ loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')"
- session=$(loginctl list-sessions --no-legend | 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 | awk '$3 == "logind-test-user" { print $2 }')" "$(id -ru logind-test-user)"
- seat=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $4 }')
- assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $5 }')" tty2
- assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $6 }')" active
- assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $7 }')" no
- assert_eq "$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $8 }')" '-'
+ 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?}"
@@ -555,15 +591,15 @@ testcase_list_users_sessions_seats() {
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 | awk '$3 == "logind-test-user" { print $1 }'); do
+ 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 -q logind-test-user; do sleep 1; done"; then
+ 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
+ timeout 30 bash -c "until [[ \"\$(loginctl list-users --no-legend | awk '\$2 == \"logind-test-user\" { print \$4 }')\" == lingering ]]; do sleep 1; done"
}
teardown_stop_idle_session() (
@@ -586,7 +622,9 @@ testcase_stop_idle_session() {
create_session
trap teardown_stop_idle_session RETURN
- id="$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1; }')"
+ id="$(loginctl --no-legend | grep tty | awk '$3 == "logind-test-user" { print $1; }')"
+
+ journalctl --sync
ts="$(date '+%H:%M:%S')"
mkdir -p /run/systemd/logind.conf.d
@@ -597,8 +635,9 @@ EOF
systemctl restart systemd-logind.service
sleep 5
+ journalctl --sync
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 -c "logind-test-user")" 0
+ assert_eq "$(loginctl --no-legend | grep -v manager | grep -c "logind-test-user")" 0
}
testcase_ambient_caps() {
@@ -653,8 +692,53 @@ EOF
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_enable_debug
+test_write_dropin
run_testcases
touch /testok
diff --git a/test/units/testsuite-36.sh b/test/units/TEST-36-NUMAPOLICY.sh
index 8a53b98..fcd3d6d 100755
--- a/test/units/testsuite-36.sh
+++ b/test/units/TEST-36-NUMAPOLICY.sh
@@ -3,6 +3,11 @@
set -eux
set -o pipefail
+if [[ -n "${ASAN_OPTIONS:-}" ]]; then
+ echo "This test does not support running with sanitizers, skipping the test" | tee --append /skipped
+ exit 77
+fi
+
# shellcheck disable=SC2317
at_exit() {
# shellcheck disable=SC2181
@@ -29,7 +34,7 @@ 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=1
+sleepAfterStart=3
# Journal cursor for easier navigation
journalCursorFile="jounalCursorFile"
@@ -80,7 +85,7 @@ EOF
writeTestUnit() {
mkdir -p "$testUnitFile.d/"
- printf "[Service]\nExecStart=/bin/sleep 3600\n" >"$testUnitFile"
+ printf "[Service]\nExecStart=sleep 3600\n" >"$testUnitFile"
}
writeTestUnitNUMAPolicy() {
diff --git a/test/units/testsuite-38-sleep.service b/test/units/TEST-38-FREEZER-sleep.service
index c116c80..1bb9ddf 100644
--- a/test/units/testsuite-38-sleep.service
+++ b/test/units/TEST-38-FREEZER-sleep.service
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
-ExecStart=/bin/sleep 3600
+ExecStart=sleep 3600
diff --git a/test/units/testsuite-38.sh b/test/units/TEST-38-FREEZER.sh
index 5fc87fc..0759784 100755
--- a/test/units/testsuite-38.sh
+++ b/test/units/TEST-38-FREEZER.sh
@@ -9,7 +9,7 @@ set -o pipefail
systemd-analyze log-level debug
-unit=testsuite-38-sleep.service
+unit=TEST-38-FREEZER-sleep.service
start_test_service() {
systemctl daemon-reload
@@ -94,7 +94,7 @@ check_freezer_state() {
# Ignore the intermediate freezing & thawing states in case we check
# the unit state too quickly
- [[ "$state" =~ ^(freezing|thawing)$ ]] || break
+ [[ "$state" =~ ^(freezing|thawing) ]] || break
sleep .5
done
@@ -105,7 +105,14 @@ check_freezer_state() {
}
check_cgroup_state() {
- grep -q "frozen $2" /sys/fs/cgroup/system.slice/"$1"/cgroup.events
+ # 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() {
@@ -142,31 +149,6 @@ testcase_dbus_api() {
echo
}
-testcase_jobs() {
- local pid_before=
- local pid_after=
- echo "Test that it is possible to apply jobs on frozen units:"
-
- systemctl start "${unit}"
- dbus_freeze "${unit}"
- check_freezer_state "${unit}" "frozen"
-
- echo -n " - restart: "
- pid_before=$(systemctl show -p MainPID "${unit}" --value)
- systemctl restart "${unit}"
- pid_after=$(systemctl show -p MainPID "${unit}" --value)
- [ "$pid_before" != "$pid_after" ] && echo "[ OK ]"
-
- dbus_freeze "${unit}"
- check_freezer_state "${unit}" "frozen"
-
- echo -n " - stop: "
- timeout 5s systemctl stop "${unit}"
- echo "[ OK ]"
-
- echo
-}
-
testcase_systemctl() {
echo "Test that systemctl freeze/thaw verbs:"
@@ -224,22 +206,101 @@ testcase_recursive() {
echo "Test recursive freezing:"
- echo -n " - freeze: "
+ echo -n " - freeze/thaw parent: "
systemctl freeze "$slice"
- check_freezer_state "${slice}" "frozen"
- check_freezer_state "${unit}" "frozen"
- grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events
- grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events
+ 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 " - thaw: "
+ 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 "${unit}" "running"
- check_freezer_state "${slice}" "running"
- grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events
- grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events
+ 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"
@@ -255,38 +316,39 @@ testcase_preserve_state() {
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
+ 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"
+ check_freezer_state "$slice" "running"
+ check_freezer_state "$unit" "running"
# However actual kernel state should be frozen
- grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events
- grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events
+ 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
+ echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze
sleep 1
- check_freezer_state "${unit}" "running"
- check_freezer_state "${slice}" "running"
- grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events
- grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events
+ 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"
+ 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"
diff --git a/test/units/testsuite-43.sh b/test/units/TEST-43-PRIVATEUSER-UNPRIV.sh
index 4f31a33..165af47 100755
--- a/test/units/testsuite-43.sh
+++ b/test/units/TEST-43-PRIVATEUSER-UNPRIV.sh
@@ -6,9 +6,11 @@ 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 0
+ exit 77
fi
systemd-analyze log-level debug
@@ -130,7 +132,7 @@ 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 /usr/share/app1.raw
+ 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 \
diff --git a/test/units/testsuite-44.sh b/test/units/TEST-44-LOG-NAMESPACE.sh
index fbd4ae6..0819a4b 100755
--- a/test/units/testsuite-44.sh
+++ b/test/units/TEST-44-LOG-NAMESPACE.sh
@@ -4,12 +4,24 @@ 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)
diff --git a/test/units/testsuite-45.sh b/test/units/TEST-45-TIMEDATE.sh
index b426927..dff3ed0 100755
--- a/test/units/testsuite-45.sh
+++ b/test/units/TEST-45-TIMEDATE.sh
@@ -57,12 +57,12 @@ testcase_timezone() {
assert_in "Local time:" "$(timedatectl --no-pager)"
echo 'change timezone'
- assert_eq "$(timedatectl --no-pager set-timezone Europe/Kiev 2>&1)" ""
- assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "Europe/Kiev"
+ 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/Kiev"
+ assert_eq "$(cat /etc/timezone)" "Europe/Kyiv"
fi
- assert_in "Time zone: Europe/Kiev \(EES*T, \+0[0-9]00\)" "$(timedatectl)"
+ assert_in "Time zone: Europe/Kyiv \(EES*T, \+0[0-9]00\)" "$(timedatectl)"
if [[ -n "$ORIG_TZ" ]]; then
echo 'reset timezone to original'
@@ -218,7 +218,7 @@ assert_ntp() {
assert_timedated_signal() {
local timestamp="${1:?}"
local value="${2:?}"
- local args=(-q -n 1 --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor" + SYSLOG_IDENTIFIER="busctl-monitor")
+ local args=(-q -n 1 --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service")
journalctl --sync
@@ -258,12 +258,12 @@ ConditionVirtualization=
Type=simple
AmbientCapabilities=
ExecStart=
-ExecStart=/bin/sleep infinity
+ExecStart=sleep infinity
EOF
systemctl daemon-reload
fi
- systemd-run --unit busctl-monitor.service -p SyslogIdentifier=busctl-monitor --service-type=notify \
+ 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'
@@ -298,7 +298,7 @@ assert_timesyncd_signal() {
local timestamp="${1:?}"
local property="${2:?}"
local value="${3:?}"
- local args=(-q --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service" + SYSLOG_IDENTIFIER="busctl-monitor")
+ local args=(-q --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service")
journalctl --sync
@@ -359,7 +359,7 @@ EOF
systemctl restart systemd-networkd
networkctl status ntp99
- systemd-run --unit busctl-monitor.service -p SyslogIdentifier=busctl-monitor --service-type=notify \
+ 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
diff --git a/test/units/TEST-46-HOMED.sh b/test/units/TEST-46-HOMED.sh
new file mode 100755
index 0000000..61590a1
--- /dev/null
+++ b/test/units/TEST-46-HOMED.sh
@@ -0,0 +1,620 @@
+#!/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" /)"
+
+systemctl start systemd-homed.service systemd-userdbd.socket
+
+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
+
+TMP_SKEL=$(mktemp -d)
+echo hogehoge >"$TMP_SKEL"/hoge
+
+# 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 \
+ --skel="$TMP_SKEL"
+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)
+[[ $(PASSWORD=xEhErW0ndafV4s homectl with test-user -- stat -c %U /home/test-user/hoge) == "test-user" ]]
+[[ $(PASSWORD=xEhErW0ndafV4s homectl with test-user -- cat /home/test-user/hoge) == "$(cat "$TMP_SKEL"/hoge)" ]]
+
+# 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
index 0000000..42c9a43
--- /dev/null
+++ b/test/units/TEST-50-DISSECT.DDI.sh
@@ -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
index 0000000..563206c
--- /dev/null
+++ b/test/units/TEST-50-DISSECT.dissect.sh
@@ -0,0 +1,745 @@
+#!/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
+journalctl --sync
+set +o pipefail
+# "journalctl -u foo.service" may not work as expected, especially entries for _TRANSPORT=stdout,
+# for short-living services or when the service manager generates debugging logs.
+# Instead, SYSLOG_IDENTIFIER= should be reliable for stdout. Let's use it.
+timeout -v 30s journalctl -b SYSLOG_IDENTIFIER=echo _TRANSPORT=stdout -o cat -n all --follow | grep -m 1 -q '^foo$'
+set -o pipefail
+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
index 0000000..604a9db
--- /dev/null
+++ b/test/units/TEST-50-DISSECT.mountfsd.sh
@@ -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.sh b/test/units/TEST-50-DISSECT.sh
new file mode 100755
index 0000000..0e378a8
--- /dev/null
+++ b/test/units/TEST-50-DISSECT.sh
@@ -0,0 +1,215 @@
+#!/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"
+udevadm settle --timeout=60
+
+: "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
index 0000000..3bc8899
--- /dev/null
+++ b/test/units/TEST-50-DISSECT.sysext.sh
@@ -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/testsuite-52.sh b/test/units/TEST-52-HONORFIRSTSHUTDOWN.sh
index 16ff507..16ff507 100755
--- a/test/units/testsuite-52.sh
+++ b/test/units/TEST-52-HONORFIRSTSHUTDOWN.sh
diff --git a/test/units/testsuite-53.sh b/test/units/TEST-53-ISSUE-16347.sh
index 84cd661..84cd661 100755
--- a/test/units/testsuite-53.sh
+++ b/test/units/TEST-53-ISSUE-16347.sh
diff --git a/test/units/testsuite-54.sh b/test/units/TEST-54-CREDS.sh
index bcbe7a1..fe410d5 100755
--- a/test/units/testsuite-54.sh
+++ b/test/units/TEST-54-CREDS.sh
@@ -3,15 +3,25 @@
# shellcheck disable=SC2016
set -eux
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
systemd-analyze log-level debug
-run_with_cred_compare() {
+run_with_cred_compare() (
local cred="${1:?}"
local exp="${2?}"
+ local log_file
shift 2
- diff <(systemd-run -p SetCredential="$cred" --wait --pipe -- systemd-creds "$@") <(echo -ne "$exp")
-}
+ 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
#
@@ -197,6 +207,12 @@ elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then
[ "$(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=""
@@ -297,11 +313,40 @@ fi
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.service \
+ --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"
@@ -314,6 +359,44 @@ if ! systemd-detect-virt -q -c ; then
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/TEST-55-OOMD-testbloat.service
index 6c8e3c9..ba4f2bc 100644
--- a/test/units/testsuite-55-testbloat.service
+++ b/test/units/TEST-55-OOMD-testbloat.service
@@ -6,5 +6,5 @@ Description=Create a lot of memory pressure
# 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
+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/testsuite-55-testchill.service b/test/units/TEST-55-OOMD-testchill.service
index 369b802..1f708dd 100644
--- a/test/units/testsuite-55-testchill.service
+++ b/test/units/TEST-55-OOMD-testchill.service
@@ -4,5 +4,5 @@ Description=No memory pressure
[Service]
MemoryHigh=3M
-Slice=testsuite-55-workload.slice
+Slice=TEST-55-OOMD-workload.slice
ExecStart=sleep infinity
diff --git a/test/units/testsuite-55-testmunch.service b/test/units/TEST-55-OOMD-testmunch.service
index 3730059..5659906 100644
--- a/test/units/testsuite-55-testmunch.service
+++ b/test/units/TEST-55-OOMD-testmunch.service
@@ -4,5 +4,5 @@ Description=Create some memory pressure
[Service]
MemoryHigh=12M
-Slice=testsuite-55-workload.slice
+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/testsuite-55-workload.slice b/test/units/TEST-55-OOMD-workload.slice
index d117b75..d117b75 100644
--- a/test/units/testsuite-55-workload.slice
+++ b/test/units/TEST-55-OOMD-workload.slice
diff --git a/test/units/testsuite-55.sh b/test/units/TEST-55-OOMD.sh
index 81617db..b04ebca 100755
--- a/test/units/testsuite-55.sh
+++ b/test/units/TEST-55-OOMD.sh
@@ -6,6 +6,14 @@ 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
@@ -16,13 +24,14 @@ test "$(cat /sys/fs/cgroup/init.scope/memory.high)" != "max"
[[ "$(get_cgroup_hierarchy)" == "unified" ]] || echo "no cgroupsv2" >>/skipped
[[ -x /usr/lib/systemd/systemd-oomd ]] || echo "no oomd" >>/skipped
if [[ -s /skipped ]]; then
- exit 0
+ exit 77
fi
-rm -rf /run/systemd/system/testsuite-55-testbloat.service.d
+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
@@ -78,35 +87,34 @@ if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
# 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.
- mkdir -p /run/systemd/system/testsuite-55-testchill.service.d/
- cat >/run/systemd/system/testsuite-55-testchill.service.d/99-MemoryHigh.conf <<EOF
+ 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/testsuite-55-testchill.service.d/ /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 testsuite-55-testchill.service
-systemctl start testsuite-55-testbloat.service
+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 "/testsuite-55-workload.slice"; do sleep 1; done'
-oomctl | grep "/testsuite-55-workload.slice"
+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 testsuite-55-testchill.service
+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 testsuite-55-testbloat.service; then
+ if ! systemctl status TEST-55-OOMD-testbloat.service; then
break
fi
oomctl
@@ -114,28 +122,28 @@ for _ in {0..59}; do
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
+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 testsuite-55-testchill.service
-systemctl start --machine "testuser@.host" --user testsuite-55-testbloat.service
+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 "/testsuite-55-workload.slice"; do sleep 1; done'
-oomctl | grep -E "/user.slice.*/testsuite-55-workload.slice"
+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 testsuite-55-testchill.service
+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 testsuite-55-testbloat.service; then
+ if ! systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testbloat.service; then
break
fi
oomctl
@@ -143,8 +151,8 @@ for _ in {0..59}; do
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
+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
@@ -152,19 +160,19 @@ loginctl disable-linger testuser
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
+ 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 testsuite-55-testchill.service
- systemctl start testsuite-55-testmunch.service
- systemctl start testsuite-55-testbloat.service
+ 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 testsuite-55-testmunch.service; then
+ if ! systemctl status TEST-55-OOMD-testmunch.service; then
break
fi
oomctl
@@ -172,9 +180,9 @@ EOF
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
+ 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
diff --git a/test/units/testsuite-58.sh b/test/units/TEST-58-REPART.sh
index c64b203..8a014ac 100755
--- a/test/units/testsuite-58.sh
+++ b/test/units/TEST-58-REPART.sh
@@ -9,7 +9,7 @@ set -o pipefail
if ! command -v systemd-repart >/dev/null; then
echo "no systemd-repart" >/skipped
- exit 0
+ exit 77
fi
# shellcheck source=test/units/test-control.sh
@@ -87,7 +87,7 @@ elif [ "${machine}" = "ppc64le" ]; then
usr_uuid=C0D0823B-8040-4C7C-A629-026248E297FB
architecture="ppc64-le"
else
- echo "Unexpected uname -m: ${machine} in testsuite-58.sh, please fix me"
+ echo "Unexpected uname -m: ${machine} in TEST-58-REPART.sh, please fix me"
exit 1
fi
@@ -373,7 +373,7 @@ $imgs/zzz7 : start= 6291416, size= 98304, type=0FC63DAF-8483-4772-8E79
fi
loop="$(losetup -P --show --find "$imgs/zzz")"
- udevadm wait --timeout 60 --settle "${loop:?}"
+ udevadm wait --timeout 60 --settle "${loop:?}p7"
volume="test-repart-$RANDOM"
@@ -435,7 +435,7 @@ EOF
"offset" : 1048576,
"old_size" : 0,
"raw_size" : 33554432,
- "size" : "-> 32.0M",
+ "size" : "-> 32M",
"old_padding" : 0,
"raw_padding" : 0,
"padding" : "-> 0B",
@@ -496,7 +496,7 @@ EOF
"offset" : 1048576,
"old_size" : 0,
"raw_size" : 33554432,
- "size" : "-> 32.0M",
+ "size" : "-> 32M",
"old_padding" : 0,
"raw_padding" : 0,
"padding" : "-> 0B",
@@ -512,7 +512,7 @@ EOF
"offset" : 34603008,
"old_size" : 0,
"raw_size" : 33554432,
- "size" : "-> 32.0M",
+ "size" : "-> 32M",
"old_padding" : 0,
"raw_padding" : 0,
"padding" : "-> 0B",
@@ -961,7 +961,7 @@ EOF
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs' ; losetup -d '$loop'" RETURN ERR
- udevadm wait --timeout 60 --settle "${loop:?}"
+ udevadm wait --timeout 60 --settle "${loop:?}p1" "${loop:?}p2"
# Check that the verity block sizes are as expected
veritysetup dump "${loop}p2" | grep 'Data block size:' | grep -q '4096'
@@ -1026,7 +1026,7 @@ EOF
fi
loop=$(losetup -P --show -f "$imgs/zzz")
- udevadm wait --timeout 60 --settle "${loop:?}"
+ udevadm wait --timeout 60 --settle "${loop:?}p1" "${loop:?}p2"
# Test that /usr/def did not end up in the root partition but other files did.
mkdir "$imgs/mnt"
diff --git a/test/units/testsuite-59.sh b/test/units/TEST-59-RELOADING-RESTART.sh
index 1b622b3..0e04403 100755
--- a/test/units/testsuite-59.sh
+++ b/test/units/TEST-59-RELOADING-RESTART.sh
@@ -36,7 +36,7 @@ Description=TEST-59-RELOADING-RESTART Normal exit
[Service]
Type=notify
-ExecStart=/bin/bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
+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
@@ -45,7 +45,7 @@ Description=TEST-59-RELOADING-RESTART Restart=on-failure
[Service]
Type=notify
-ExecStart=/bin/bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
+ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 1; exit 1"
Restart=on-failure
StartLimitBurst=1
EOF
@@ -57,7 +57,7 @@ Description=TEST-59-RELOADING-RESTART Restart=on-abort
[Service]
Type=notify
-ExecStart=/bin/bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 5; exit 1"
+ExecStart=bash -c "systemd-notify --ready; systemd-notify RELOADING=1; sleep 5; exit 1"
Restart=on-abort
EOF
@@ -104,6 +104,14 @@ 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
diff --git a/test/units/testsuite-60.sh b/test/units/TEST-60-MOUNT-RATELIMIT.sh
index e800a7a..a0e99dc 100755
--- a/test/units/testsuite-60.sh
+++ b/test/units/TEST-60-MOUNT-RATELIMIT.sh
@@ -23,8 +23,8 @@ teardown_test_dependencies() (
losetup -d "${LOOP_1}" || :
fi
- rm -f /tmp/testsuite-60-dependencies-0.img
- rm -f /tmp/testsuite-60-dependencies-1.img
+ 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
@@ -33,13 +33,13 @@ teardown_test_dependencies() (
)
setup_loop() {
- truncate -s 30m "/tmp/testsuite-60-dependencies-${1?}.img"
- sfdisk --wipe=always "/tmp/testsuite-60-dependencies-${1?}.img" <<EOF
+ 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/testsuite-60-dependencies-${1?}.img")
+ 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"
}
@@ -203,7 +203,7 @@ EOF
}
test_issue_23796() {
- local mount_path mount_mytmpfs
+ local mount_path mount_mytmpfs since
mount_path="$(command -v mount 2>/dev/null)"
mount_mytmpfs="${mount_path/\/bin/\/sbin}.mytmpfs"
@@ -225,6 +225,9 @@ EOF
# shellcheck disable=SC2064
trap "rm -f /run/systemd/system/tmp-hoge.mount '$mount_mytmpfs'" RETURN
+ journalctl --sync
+ since="$(date '+%H:%M:%S')"
+
for _ in {1..10}; do
systemctl --no-block start tmp-hoge.mount
sleep ".$RANDOM"
@@ -233,7 +236,7 @@ EOF
sleep 1
if [[ "$(systemctl is-failed tmp-hoge.mount)" == "failed" ]] || \
- journalctl -u tmp-hoge.mount -q --grep "but there is no mount"; then
+ journalctl --since="$since" -u tmp-hoge.mount -q --grep "but there is no mount"; then
exit 1
fi
@@ -249,6 +252,8 @@ 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")"
+
+journalctl --sync
TS="$(date '+%H:%M:%S')"
mkdir -p "$LONGPATH"
@@ -271,6 +276,9 @@ for ((i = 0; i < NUM_DIRS; i++)); do
mkdir "/tmp/meow${i}"
done
+# The following loop may produce many journal entries.
+# Let's process all pending entries before testing.
+journalctl --sync
TS="$(date '+%H:%M:%S')"
for ((i = 0; i < NUM_DIRS; i++)); do
@@ -286,9 +294,13 @@ 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"
+set +o pipefail
+journalctl --sync
+if timeout 2m journalctl -u init.scope --since="$TS" -n all --follow | grep -m 1 -q -F '(mount-monitor-dispatch) entered rate limit'; then
+ journalctl --sync
+ timeout 2m journalctl -u init.scope --since="$TS" -n all --follow | grep -m 1 -q -F '(mount-monitor-dispatch) left rate limit'
fi
+set -o pipefail
# 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.
diff --git a/test/units/TEST-62-RESTRICT-IFACES-1.service b/test/units/TEST-62-RESTRICT-IFACES-1.service
new file mode 100644
index 0000000..16695c1
--- /dev/null
+++ b/test/units/TEST-62-RESTRICT-IFACES-1.service
@@ -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
index 0000000..bce7e8e
--- /dev/null
+++ b/test/units/TEST-62-RESTRICT-IFACES-2.service
@@ -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/testsuite-62-3.service b/test/units/TEST-62-RESTRICT-IFACES-3.service
index b6c8e7a..116530b 100644
--- a/test/units/testsuite-62-3.service
+++ b/test/units/TEST-62-RESTRICT-IFACES-3.service
@@ -2,9 +2,9 @@
[Unit]
Description=TEST-62-RESTRICT-IFACES-deny-list
[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'
+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/TEST-62-RESTRICT-IFACES-4.service
index 053e6d2..200a383 100644
--- a/test/units/testsuite-62-4.service
+++ b/test/units/TEST-62-RESTRICT-IFACES-4.service
@@ -2,9 +2,9 @@
[Unit]
Description=TEST-62-RESTRICT-IFACES-empty-assignment
[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'
+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/TEST-62-RESTRICT-IFACES-5.service
index a8f268d..51761ba 100644
--- a/test/units/testsuite-62-5.service
+++ b/test/units/TEST-62-RESTRICT-IFACES-5.service
@@ -2,9 +2,9 @@
[Unit]
Description=TEST-62-RESTRICT-IFACES-invert-assignment
[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'
+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
diff --git a/test/units/testsuite-62-2.service b/test/units/TEST-62-RESTRICT-IFACES-6.service
index b83362d..876d8f3 100644
--- a/test/units/testsuite-62-2.service
+++ b/test/units/TEST-62-RESTRICT-IFACES-6.service
@@ -1,10 +1,10 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
-Description=TEST-62-RESTRICT-IFACES-allow-list
+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
-RestrictNetworkInterfaces=veth1
+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.sh b/test/units/TEST-62-RESTRICT-IFACES.sh
index ed40821..35ab862 100755
--- a/test/units/testsuite-62.sh
+++ b/test/units/TEST-62-RESTRICT-IFACES.sh
@@ -17,6 +17,7 @@ setup() {
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
}
@@ -33,31 +34,23 @@ teardown() {
systemd-analyze log-level info
}
-KERNEL_VERSION="$(uname -r)"
-KERNEL_MAJOR="${KERNEL_VERSION%%.*}"
-KERNEL_MINOR="${KERNEL_VERSION#"$KERNEL_MAJOR".}"
-KERNEL_MINOR="${KERNEL_MINOR%%.*}"
-
-MAJOR_REQUIRED=5
-MINOR_REQUIRED=7
-
-if [[ "$KERNEL_MAJOR" -lt $MAJOR_REQUIRED || ("$KERNEL_MAJOR" -eq $MAJOR_REQUIRED && "$KERNEL_MINOR" -lt $MINOR_REQUIRED) ]]; then
+if systemd-analyze compare-versions "$(uname -r)" lt 5.7; then
echo "kernel is not 5.7+" >>/skipped
- exit 0
+ exit 77
fi
if systemctl --version | grep -q -F -- "-BPF_FRAMEWORK"; then
echo "bpf-framework is disabled" >>/skipped
- exit 0
+ 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
+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/testsuite-63.sh b/test/units/TEST-63-PATH.sh
index ea8cd94..cdd323c 100755
--- a/test/units/testsuite-63.sh
+++ b/test/units/TEST-63-PATH.sh
@@ -118,7 +118,7 @@ timeout 30 bash -c 'until test "$(systemctl show test63-pr-30768.service -P Acti
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'
+timeout 30 bash -c 'until diff /tmp/copyme /tmp/copied >/dev/null; do sleep .2; done'
systemctl log-level info
diff --git a/test/units/testsuite-64.sh b/test/units/TEST-64-UDEV-STORAGE.sh
index 65e5f6c..5ddddf5 100755
--- a/test/units/testsuite-64.sh
+++ b/test/units/TEST-64-UDEV-STORAGE.sh
@@ -148,7 +148,7 @@ check_device_units() {(
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.+/ { print $1 }' | sed -e 's/\.device$//')
+ done < <(systemctl list-units --all --type=device --no-legend dev-* | awk '$1 !~ /dev-tty.+/ && $4 == "plugged" { print $1 }' | sed -e 's/\.device$//')
return 0
)}
@@ -168,7 +168,7 @@ helper_check_device_units() {(
check_device_units 1 "$@"
)}
-testcase_megasas2_basic() {
+testcase_virtio_scsi_basic() {
lsblk -S
[[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]]
}
@@ -239,16 +239,28 @@ testcase_nvme_subsystem() {
}
testcase_virtio_scsi_identically_named_partitions() {
- local num
+ local num_part num_disk i j
if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
- num=$((4 * 4))
+ num_part=4
+ num_disk=4
else
- num=$((16 * 8))
+ num_part=8
+ num_disk=16
fi
+ for ((i = 0; i < num_disk; i++)); do
+ udevadm lock --device "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive$i" \
+ sfdisk "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive$i" <<EOF
+label: gpt
+
+$(for ((j = 1; j <= num_part; j++)); do echo 'name="Hello world", size=2M'; done)
+EOF
+ done
+
+ udevadm settle
lsblk --noheadings -a -o NAME,PARTLABEL
- [[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq "$num" ]]
+ [[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq "$((num_part * num_disk))" ]]
}
testcase_multipath_basic_failover() {
@@ -270,6 +282,18 @@ blacklist_exceptions {
blacklist {
}
EOF
+
+ udevadm lock --device /dev/disk/by-id/wwn-0xdeaddeadbeef0000 \
+ sfdisk /dev/disk/by-id/wwn-0xdeaddeadbeef0000 <<EOF
+label: gpt
+
+name="first_partition", size=5M
+uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M
+EOF
+ udevadm settle
+ udevadm lock --device /dev/disk/by-id/wwn-0xdeaddeadbeef0000-part2 \
+ mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" /dev/disk/by-id/wwn-0xdeaddeadbeef0000-part2
+
modprobe -v dm_multipath
systemctl start multipathd.service
systemctl status multipathd.service
@@ -362,7 +386,7 @@ testcase_simultaneous_events_1() {
else
num_part=10
iterations=100
- timeout=30
+ timeout=60
fi
for disk in {0..9}; do
@@ -521,9 +545,15 @@ testcase_lvm_basic() {
local i iterations partitions part timeout
local vgroup="MyTestGroup$RANDOM"
local devices=(
- /dev/disk/by-id/ata-foobar_deadbeeflvm{0..3}
+ /dev/disk/by-id/scsi-0systemd_foobar_deadbeeflvm{0..3}
)
+ . /etc/os-release
+ if [[ "$ID" == "ubuntu" ]]; then
+ echo "LVM on Ubuntu is broken, skipping the test" | tee --append /skipped
+ exit 77
+ fi
+
if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then
timeout=180
else
@@ -544,6 +574,7 @@ testcase_lvm_basic() {
lvm lvs
udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1"
+ udevadm trigger --settle "/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
@@ -599,6 +630,7 @@ testcase_lvm_basic() {
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 trigger --settle "/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
@@ -693,9 +725,14 @@ testcase_lvm_basic() {
testcase_btrfs_basic() {
local dev_stub i label mpoint uuid
local devices=(
- /dev/disk/by-id/ata-foobar_deadbeefbtrfs{0..3}
+ /dev/disk/by-id/scsi-0systemd_foobar_deadbeefbtrfs{0..3}
)
+ if ! modinfo btrfs; then
+ echo "This test requires the btrfs kernel module but it is not installed, skipping the test" | tee --append /skipped
+ exit 77
+ fi
+
ls -l "${devices[@]}"
echo "Single device: default settings"
@@ -731,10 +768,10 @@ EOF
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 \
+ --device=/dev/disk/by-id/scsi-0systemd_foobar_deadbeefbtrfs0 \
+ --device=/dev/disk/by-id/scsi-0systemd_foobar_deadbeefbtrfs1 \
+ --device=/dev/disk/by-id/scsi-0systemd_foobar_deadbeefbtrfs2 \
+ --device=/dev/disk/by-id/scsi-0systemd_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
@@ -755,9 +792,10 @@ EOF
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 lock --device="${devices[$i]}" \
+ 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
@@ -824,14 +862,28 @@ testcase_iscsi_lvm() {
local vgroup="iscsi_lvm$RANDOM"
local expected_symlinks=()
local devices=(
- /dev/disk/by-id/ata-foobar_deadbeefiscsi{0..3}
+ /dev/disk/by-id/scsi-0systemd_foobar_deadbeefiscsi{0..3}
)
+ . /etc/os-release
+ if [[ "$ID" == "ubuntu" ]]; then
+ echo "LVM on Ubuntu is broken, skipping the test" | tee --append /skipped
+ exit 77
+ fi
+
ls -l "${devices[@]}"
- # Start the target daemon
- systemctl start tgtd
- systemctl status tgtd
+ # Start the target daemon (debian names it tgt.service so make sure we handle that)
+ if systemctl list-unit-files tgt.service; then
+ systemctl start tgt
+ systemctl status tgt
+ elif systemctl list-unit-files tgtd.service; then
+ systemctl start tgtd
+ systemctl status tgtd
+ else
+ echo "This test requires tgtd but it is not installed, skipping ..." | tee --append /skipped
+ exit 77
+ fi
echo "iSCSI LUNs backed by devices"
# See RFC3721 and RFC7143
@@ -867,7 +919,7 @@ testcase_iscsi_lvm() {
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 lock --device "${devices[0]}" mkfs.ext4 -L iscsi_store "${devices[0]}"
udevadm wait --settle --timeout=30 "${devices[0]}"
mount "${devices[0]}" "$mpoint"
for i in {1..4}; do
@@ -903,6 +955,7 @@ testcase_iscsi_lvm() {
lvm lvs
udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1"
+ udevadm trigger --settle "/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
@@ -950,6 +1003,16 @@ testcase_long_sysfs_path() {
stat /sys/block/vda
readlink -f /sys/block/vda/dev
+ dev="/dev/vda"
+ udevadm lock --device "$dev" sfdisk "$dev" <<EOF
+label: gpt
+
+name="test_swap", size=32M
+uuid="deadbeef-dead-dead-beef-000000000000", name="test_part", size=5M
+EOF
+ udevadm settle
+ udevadm lock --device "${dev}1" mkswap -U "deadbeef-dead-dead-beef-111111111111" -L "swap_vol" "${dev}1"
+ udevadm lock --device "${dev}2" 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)
@@ -992,7 +1055,7 @@ 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}
+ /dev/disk/by-id/scsi-0systemd_foobar_deadbeefmdadm{0..4}
)
ls -l "${devices[@]}"
@@ -1009,9 +1072,11 @@ testcase_mdadm_basic() {
"/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
+ echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/scsi-0systemd_foobar_deadbeefmdadm{0..1} -v -f --level=1 --raid-devices=2
udevadm wait --settle --timeout=30 "$raid_dev"
+ # udevd does not lock md devices, hence we need to trigger uevent after creating filesystem.
mkfs.ext4 -L "$part_name" "$raid_dev"
+ udevadm trigger --settle "$raid_dev"
udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
for i in {0..9}; do
echo "Disassemble - reassemble loop, iteration #$i"
@@ -1038,9 +1103,10 @@ testcase_mdadm_basic() {
"/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
+ echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/scsi-0systemd_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 trigger --settle "$raid_dev"
udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
for i in {0..9}; do
echo "Disassemble - reassemble loop, iteration #$i"
@@ -1078,10 +1144,11 @@ testcase_mdadm_basic() {
"/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
+ echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/scsi-0systemd_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.
+ # We need to trigger uevents after sfdisk and mkfs.
sfdisk --wipe=always "$raid_dev" <<EOF
label: gpt
@@ -1089,8 +1156,10 @@ 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 trigger --settle --parent-match "$raid_dev"
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 trigger --settle "/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"
@@ -1112,7 +1181,7 @@ 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}
+ /dev/disk/by-id/scsi-0systemd_foobar_deadbeefmdadmlvm{0..4}
)
ls -l "${devices[@]}"
@@ -1131,7 +1200,7 @@ testcase_mdadm_lvm() {
"/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
+ echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/scsi-0systemd_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"
@@ -1144,6 +1213,7 @@ testcase_mdadm_lvm() {
lvm lvs
udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2"
mkfs.ext4 -L "$part_name" "/dev/$vgroup/mypart2"
+ udevadm trigger --settle "/dev/$vgroup/mypart2"
udevadm wait --settle --timeout=30 "${expected_symlinks[@]}"
# Disassemble the array
lvm vgchange -an "$vgroup"
diff --git a/test/units/testsuite-65.sh b/test/units/TEST-65-ANALYZE.sh
index a6bb38d..18f5c4d 100755
--- a/test/units/testsuite-65.sh
+++ b/test/units/TEST-65-ANALYZE.sh
@@ -20,6 +20,7 @@ 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 || :
@@ -30,6 +31,7 @@ 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)"
@@ -52,6 +54,7 @@ systemd-analyze dot --order systemd-journald.service systemd-logind.service >/de
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
@@ -73,8 +76,11 @@ 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 malloc >/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
@@ -82,6 +88,7 @@ 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
@@ -91,11 +98,13 @@ 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"
@@ -107,7 +116,7 @@ ConditionKernelVersion=>1.0
ConditionPathExists=/etc/os-release
[Service]
-ExecStart=/bin/true
+ExecStart=true
EOF
systemctl daemon-reload
systemd-analyze condition --unit="$UNIT_NAME"
@@ -119,17 +128,20 @@ systemd-analyze condition 'ConditionKernelVersion = ! <4.0' \
(! 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'
@@ -144,6 +156,7 @@ systemd-analyze calendar --base-time=yesterday --iterations=5 '*-* *:*:*'
(! 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
@@ -152,6 +165,7 @@ systemd-analyze timestamp yesterday now 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'
@@ -159,6 +173,7 @@ systemd-analyze timespan 1s 300s '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
@@ -170,16 +185,21 @@ 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}
@@ -282,6 +302,11 @@ systemd-analyze security --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"
+# Make sure that running generators under systemd-analyze verify works.
+# Note: sd-analyze spawns generators in a sandbox which makes gcov unhapy, so temporarily override
+# $GCOV_PREFIX to make it skip generating any coverage reports
+GCOV_PREFIX=/tmp systemd-analyze verify --generators /tmp/testfile.service
+
rm /tmp/testfile.service
cat <<EOF >/tmp/img/usr/lib/systemd/system/testfile.service
@@ -334,6 +359,17 @@ 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
@@ -904,6 +940,13 @@ 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
index 0000000..85b3d0d
--- /dev/null
+++ b/test/units/TEST-66-DEVICE-ISOLATION-device-isolation.service
@@ -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/testsuite-66.sh b/test/units/TEST-66-DEVICE-ISOLATION.sh
index 147335a..ccdfcb2 100755
--- a/test/units/testsuite-66.sh
+++ b/test/units/TEST-66-DEVICE-ISOLATION.sh
@@ -3,11 +3,11 @@
set -eux
set -o pipefail
-RESULTS_FILE=/tmp/testsuite66serviceresults
+RESULTS_FILE=/tmp/TEST-66-DEVICE-ISOLATION.serviceresults
systemd-analyze log-level debug
-systemctl start testsuite-66-deviceisolation.service
+systemctl start TEST-66-DEVICE-ISOLATION-device-isolation.service
sleep 5
grep -q "Operation not permitted" "$RESULTS_FILE"
@@ -15,7 +15,7 @@ grep -q "Operation not permitted" "$RESULTS_FILE"
systemctl daemon-reload
systemctl daemon-reexec
-systemctl stop testsuite-66-deviceisolation.service
+systemctl stop TEST-66-DEVICE-ISOLATION-device-isolation.service
grep -q "thisshouldnotbehere" "$RESULTS_FILE" && exit 42
diff --git a/test/units/testsuite-67.sh b/test/units/TEST-67-INTEGRITY.sh
index a42fd66..a42fd66 100755
--- a/test/units/testsuite-67.sh
+++ b/test/units/TEST-67-INTEGRITY.sh
diff --git a/test/units/testsuite-68.sh b/test/units/TEST-68-PROPAGATE-EXIT-STATUS.sh
index 11da48a..11da48a 100755
--- a/test/units/testsuite-68.sh
+++ b/test/units/TEST-68-PROPAGATE-EXIT-STATUS.sh
diff --git a/test/units/TEST-69-SHUTDOWN.py b/test/units/TEST-69-SHUTDOWN.py
new file mode 100755
index 0000000..eb790f4
--- /dev/null
+++ b/test/units/TEST-69-SHUTDOWN.py
@@ -0,0 +1,58 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# pylint: disable=broad-except
+
+import logging
+import sys
+
+import pexpect
+
+
+def main():
+ logger = logging.getLogger("test-shutdown")
+
+ consoles = []
+ for _ in range(2):
+ # Use script to allocate a separate pseudo tty to run the login shell in.
+ console = pexpect.spawn(
+ "script", ["--quiet", "--return", "--flush", "--command", "login -f root", "/dev/null"],
+ logfile=sys.stdout,
+ env={"TERM": "dumb"},
+ encoding="utf-8",
+ timeout=60,
+ )
+
+ logger.info("waiting for login prompt")
+ console.expect(".*# ", 10)
+
+ consoles += [console]
+
+ consoles[1].sendline("tty")
+ consoles[1].expect(r"/dev/(pts/\d+)")
+ pty = console.match.group(1)
+ logger.info("window 1 at tty %s", pty)
+
+ logger.info("schedule reboot")
+ consoles[1].sendline("shutdown -r")
+ consoles[1].expect("Reboot scheduled for (?P<date>.*), use 'shutdown -c' to cancel", 2)
+ date = consoles[1].match.group("date")
+ logger.info("reboot scheduled for %s", date)
+
+ logger.info("verify broadcast message")
+ consoles[0].expect(f"Broadcast message from root@H on {pty}", 2)
+ consoles[0].expect(f"The system will reboot at {date}!", 2)
+
+ logger.info("check show output")
+ consoles[1].sendline("shutdown --show")
+ consoles[1].expect(f"Reboot scheduled for {date}, use 'shutdown -c' to cancel", 2)
+
+ logger.info("cancel shutdown")
+ consoles[1].sendline("shutdown -c")
+ consoles[0].expect("System shutdown has been cancelled", 2)
+
+ consoles[0].sendline("> /testok")
+
+if __name__ == "__main__":
+ main()
+
+# vim: sw=4 et
diff --git a/test/units/testsuite-70.creds.sh b/test/units/TEST-70-TPM2.creds.sh
index e66bfd1..e66bfd1 100755
--- a/test/units/testsuite-70.creds.sh
+++ b/test/units/TEST-70-TPM2.creds.sh
diff --git a/test/units/testsuite-70.cryptenroll.sh b/test/units/TEST-70-TPM2.cryptenroll.sh
index 3f8c14e..f18ef02 100755
--- a/test/units/testsuite-70.cryptenroll.sh
+++ b/test/units/TEST-70-TPM2.cryptenroll.sh
@@ -59,6 +59,18 @@ 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")
diff --git a/test/units/testsuite-70.cryptsetup.sh b/test/units/TEST-70-TPM2.cryptsetup.sh
index 4cd627f..cb7c8b1 100755
--- a/test/units/testsuite-70.cryptsetup.sh
+++ b/test/units/TEST-70-TPM2.cryptsetup.sh
@@ -212,6 +212,7 @@ 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"p1
systemd-cryptsetup attach dditest "$DEVICE"p1 - tpm2-device=auto,headless=yes
mkdir /tmp/dditest.mnt
mount -t ext4 /dev/mapper/dditest /tmp/dditest.mnt
diff --git a/test/units/testsuite-70.measure.sh b/test/units/TEST-70-TPM2.measure.sh
index 3336f38..3336f38 100755
--- a/test/units/testsuite-70.measure.sh
+++ b/test/units/TEST-70-TPM2.measure.sh
diff --git a/test/units/testsuite-70.pcrextend.sh b/test/units/TEST-70-TPM2.pcrextend.sh
index 318fce0..318fce0 100755
--- a/test/units/testsuite-70.pcrextend.sh
+++ b/test/units/TEST-70-TPM2.pcrextend.sh
diff --git a/test/units/testsuite-70.pcrlock.sh b/test/units/TEST-70-TPM2.pcrlock.sh
index 3da9926..fd51161 100755
--- a/test/units/testsuite-70.pcrlock.sh
+++ b/test/units/TEST-70-TPM2.pcrlock.sh
@@ -74,7 +74,7 @@ if [[ -n "$SD_STUB" ]]; then
"$SD_PCRLOCK" lock-uki <"$SD_STUB"
fi
-PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=yes
+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
@@ -85,7 +85,7 @@ 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 --tpm2-public-key= --wipe-slot=tpm2 "$img"
+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
@@ -102,7 +102,7 @@ systemd-cryptsetup detach pcrlock
# 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=yes
+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
@@ -110,6 +110,10 @@ 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
@@ -118,7 +122,20 @@ echo -n test70-take-two | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/92
systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
systemd-cryptsetup detach pcrlock
-"$SD_PCRLOCK" remove-policy
+# 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
@@ -143,4 +160,20 @@ systemd-cryptsetup detach pcrlock
(! "$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.sh b/test/units/TEST-70-TPM2.sh
index 9c2a033..9c2a033 100755
--- a/test/units/testsuite-70.sh
+++ b/test/units/TEST-70-TPM2.sh
diff --git a/test/units/testsuite-70.tpm2-setup.sh b/test/units/TEST-70-TPM2.tpm2-setup.sh
index faf6fe7..faf6fe7 100755
--- a/test/units/testsuite-70.tpm2-setup.sh
+++ b/test/units/TEST-70-TPM2.tpm2-setup.sh
diff --git a/test/units/testsuite-71.sh b/test/units/TEST-71-HOSTNAME.sh
index da765a9..dc3f587 100755
--- a/test/units/testsuite-71.sh
+++ b/test/units/TEST-71-HOSTNAME.sh
@@ -61,6 +61,12 @@ get_chassis() (
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
@@ -80,7 +86,7 @@ testcase_chassis() {
assert_eq "$(get_chassis)" "$i"
done
- systemctl stop systemd-hostnamed.service
+ stop_hostnamed
rm -f /etc/machine-info
# fallback chassis type
@@ -95,7 +101,7 @@ restore_sysfs_dmi() {
umount /sys/class/dmi/id
rm -rf /run/systemd/system/systemd-hostnamed.service.d
systemctl daemon-reload
- systemctl stop systemd-hostnamed
+ stop_hostnamed
}
testcase_firmware_date() {
@@ -120,15 +126,15 @@ EOF
echo '1' >/sys/class/dmi/id/uevent
echo '09/08/2000' >/sys/class/dmi/id/bios_date
- systemctl stop systemd-hostnamed
+ stop_hostnamed
assert_in '2000-09-08' "$(hostnamectl)"
echo '2022' >/sys/class/dmi/id/bios_date
- systemctl stop systemd-hostnamed
+ stop_hostnamed
assert_not_in 'Firmware Date' "$(hostnamectl)"
echo 'garbage' >/sys/class/dmi/id/bios_date
- systemctl stop systemd-hostnamed
+ stop_hostnamed
assert_not_in 'Firmware Date' "$(hostnamectl)"
}
@@ -223,6 +229,14 @@ testcase_nss-myhostname() {
(! 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.sh b/test/units/TEST-72-SYSUPDATE.sh
index de657a2..5e658e0 100755
--- a/test/units/testsuite-72.sh
+++ b/test/units/TEST-72-SYSUPDATE.sh
@@ -6,16 +6,17 @@ set -eux
set -o pipefail
SYSUPDATE=/lib/systemd/systemd-sysupdate
-SECTOR_SIZES="512 4096"
-BACKING_FILE=/var/tmp/72-joined.raw
-export SYSTEMD_ESP_PATH=/var/tmp/72-esp
-export SYSTEMD_XBOOTLDR_PATH=/var/tmp/72-xbootldr
+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 ! test -x "$SYSUPDATE"; then
+if [[ ! -x "$SYSUPDATE" ]]; then
echo "no systemd-sysupdate" >/skipped
- exit 0
+ exit 77
fi
# Loopback devices may not be supported. They are used because sfdisk cannot
@@ -24,93 +25,93 @@ fi
# 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"
+ SECTOR_SIZES=(512)
fi
-trap cleanup ERR
-cleanup() {
- set +o pipefail
- blockdev="$( losetup --list --output NAME,BACK-FILE | grep $BACKING_FILE | cut -d' ' -f1)"
- [ -n "$blockdev" ] && losetup --detach "$blockdev"
- rm -f "$BACKING_FILE"
- rm -rf /var/tmp/72-{dirs,defs,source,xbootldr,esp}
- rm -f /testok
+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() {
- # Inputs:
- # $1: sector size
- # $2: version
+ local sector_size="${1:?}"
+ local version="${2:?}"
# Create a pair of random partition payloads, and compress one
- dd if=/dev/urandom of="/var/tmp/72-source/part1-$2.raw" bs="$1" count=2048
- dd if=/dev/urandom of="/var/tmp/72-source/part2-$2.raw" bs="$1" count=2048
- gzip -k -f "/var/tmp/72-source/part2-$2.raw"
+ 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 >"/var/tmp/72-source/uki-$2.efi"
+ echo $RANDOM >"$WORKDIR/source/uki-$version.efi"
# Create a random extra payload
- echo $RANDOM >"/var/tmp/72-source/uki-extra-$2.efi"
+ echo $RANDOM >"$WORKDIR/source/uki-extra-$version.efi"
# Create tarball of a directory
- mkdir -p "/var/tmp/72-source/dir-$2"
- echo $RANDOM >"/var/tmp/72-source/dir-$2/foo.txt"
- echo $RANDOM >"/var/tmp/72-source/dir-$2/bar.txt"
- tar --numeric-owner -C "/var/tmp/72-source/dir-$2/" -czf "/var/tmp/72-source/dir-$2.tar.gz" .
+ 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 /var/tmp/72-source/ && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS )
+ (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=/var/tmp/72-defs --verify=no check-new
- "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no update
- ( ! "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no check-new )
+ "$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() {
- # Inputs:
- # $1: block device
- # $2: sector size
- # $3: version
- # $4: partition number of part1
- # $5: partition number of part2
+ 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 / $2 ))
- part1_offset=$(( ( $4 - 1 ) * 2048 + gpt_reserved_sectors ))
- part2_offset=$(( ( $5 - 1 ) * 2048 + gpt_reserved_sectors ))
+ 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="$1" bs="$2" skip="$part1_offset" count=2048 | cmp "/var/tmp/72-source/part1-$3.raw"
- dd if="$1" bs="$2" skip="$part2_offset" count=2048 | cmp "/var/tmp/72-source/part2-$3.raw"
+ 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 "/var/tmp/72-source/uki-$3.efi" "/var/tmp/72-xbootldr/EFI/Linux/uki_$3+3-0.efi"
- test -z "$(ls -A /var/tmp/72-esp/EFI/Linux)"
+ 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 "/var/tmp/72-source/uki-extra-$3.efi" "/var/tmp/72-xbootldr/EFI/Linux/uki_$3.efi.extra.d/extra.addon.efi"
+ cmp "$WORKDIR/source/uki-extra-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version.efi.extra.d/extra.addon.efi"
# Check the directories
- cmp "/var/tmp/72-source/dir-$3/foo.txt" /var/tmp/72-dirs/current/foo.txt
- cmp "/var/tmp/72-source/dir-$3/bar.txt" /var/tmp/72-dirs/current/bar.txt
+ 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
+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 ))
+ 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
- # shellcheck disable=SC2086
- blockdev="$(losetup --find --show --sector-size $sector_size $BACKING_FILE)"
+ blockdev="$(losetup --find --show --sector-size "$sector_size" "$BACKING_FILE")"
else
blockdev="$BACKING_FILE"
fi
@@ -126,16 +127,15 @@ size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
size=2048, type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, name=_empty
EOF
- rm -rf /var/tmp/72-dirs
- mkdir -p /var/tmp/72-dirs
+ for d in "dirs" "defs"; do
+ rm -rf "${WORKDIR:?}/$d"
+ mkdir -p "$WORKDIR/$d"
+ done
- rm -rf /var/tmp/72-defs
- mkdir -p /var/tmp/72-defs
-
- cat >/var/tmp/72-defs/01-first.conf <<EOF
+ cat >"$WORKDIR/defs/01-first.conf" <<EOF
[Source]
Type=regular-file
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
MatchPattern=part1-@v.raw
[Target]
@@ -145,10 +145,10 @@ MatchPattern=part1-@v
MatchPartitionType=root-x86-64
EOF
- cat >/var/tmp/72-defs/02-second.conf <<EOF
+ cat >"$WORKDIR/defs/02-second.conf" <<EOF
[Source]
Type=regular-file
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
MatchPattern=part2-@v.raw.gz
[Target]
@@ -158,24 +158,24 @@ MatchPattern=part2-@v
MatchPartitionType=root-x86-64-verity
EOF
- cat >/var/tmp/72-defs/03-third.conf <<EOF
+ cat >"$WORKDIR/defs/03-third.conf" <<EOF
[Source]
Type=directory
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
MatchPattern=dir-@v
[Target]
Type=directory
-Path=/var/tmp/72-dirs
-CurrentSymlink=/var/tmp/72-dirs/current
+Path=$WORKDIR/dirs
+CurrentSymlink=$WORKDIR/dirs/current
MatchPattern=dir-@v
InstancesMax=3
EOF
- cat >/var/tmp/72-defs/04-fourth.conf <<EOF
+ cat >"$WORKDIR/defs/04-fourth.conf" <<EOF
[Source]
Type=regular-file
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
MatchPattern=uki-@v.efi
[Target]
@@ -191,10 +191,10 @@ TriesDone=0
InstancesMax=2
EOF
- cat >/var/tmp/72-defs/05-fifth.conf <<EOF
+ cat >"$WORKDIR/defs/05-fifth.conf" <<EOF
[Source]
Type=regular-file
-Path=/var/tmp/72-source
+Path=$WORKDIR/source
MatchPattern=uki-extra-@v.efi
[Target]
@@ -206,11 +206,8 @@ Mode=0444
InstancesMax=2
EOF
- rm -rf /var/tmp/72-esp /var/tmp/72-xbootldr
- mkdir -p /var/tmp/72-esp/EFI/Linux /var/tmp/72-xbootldr/EFI/Linux
-
- rm -rf /var/tmp/72-source
- mkdir -p /var/tmp/72-source
+ 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
@@ -226,9 +223,9 @@ EOF
new_version "$sector_size" v3
update_now
verify_version "$blockdev" "$sector_size" v3 1 3
- test ! -f "/var/tmp/72-xbootldr/EFI/Linux/uki_v1+3-0.efi"
- test ! -f "/var/tmp/72-xbootldr/EFI/Linux/uki_v1.efi.extra.d/extra.addon.efi"
- test ! -d "/var/tmp/72-xbootldr/EFI/Linux/uki_v1.efi.extra.d"
+ 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
@@ -238,10 +235,10 @@ EOF
# see above)
new_version "$sector_size" v4
- cat >/var/tmp/72-defs/02-second.conf <<EOF
+ cat >"$WORKDIR/defs/02-second.conf" <<EOF
[Source]
Type=url-file
-Path=file:///var/tmp/72-source
+Path=file://$WORKDIR/source
MatchPattern=part2-@v.raw.gz
[Target]
@@ -251,16 +248,16 @@ MatchPattern=part2-@v
MatchPartitionType=root-x86-64-verity
EOF
- cat >/var/tmp/72-defs/03-third.conf <<EOF
+ cat >"$WORKDIR/defs/03-third.conf" <<EOF
[Source]
Type=url-tar
-Path=file:///var/tmp/72-source
+Path=file://$WORKDIR/source
MatchPattern=dir-@v.tar.gz
[Target]
Type=directory
-Path=/var/tmp/72-dirs
-CurrentSymlink=/var/tmp/72-dirs/current
+Path=$WORKDIR/dirs
+CurrentSymlink=$WORKDIR/dirs/current
MatchPattern=dir-@v
InstancesMax=3
EOF
@@ -269,10 +266,8 @@ EOF
verify_version "$blockdev" "$sector_size" v4 2 4
# Cleanup
- [ -b "$blockdev" ] && losetup --detach "$blockdev"
+ [[ -b "$blockdev" ]] && losetup --detach "$blockdev"
rm "$BACKING_FILE"
done
-rm -r /var/tmp/72-{dirs,defs,source,xbootldr,esp}
-
touch /testok
diff --git a/test/units/testsuite-73.sh b/test/units/TEST-73-LOCALE.sh
index df5af4b..18539b8 100755
--- a/test/units/testsuite-73.sh
+++ b/test/units/TEST-73-LOCALE.sh
@@ -28,31 +28,6 @@ EOF
systemctl daemon-reload
}
-restore_locale() {
- if [[ -d /usr/lib/locale/xx_XX.UTF-8 ]]; then
- rmdir /usr/lib/locale/xx_XX.UTF-8
- fi
-
- if [[ -f /tmp/locale.conf.bak ]]; then
- mv /tmp/locale.conf.bak /etc/locale.conf
- else
- rm -f /etc/locale.conf
- fi
-
- if [[ -f /tmp/default-locale.bak ]]; then
- mv /tmp/default-locale.bak /etc/default/locale
- else
- rm -f /etc/default/locale
- rmdir --ignore-fail-on-non-empty /etc/default
- fi
-
- if [[ -f /tmp/locale.gen.bak ]]; then
- mv /tmp/locale.gen.bak /etc/locale.gen
- else
- rm -f /etc/locale.gen
- fi
-}
-
testcase_locale() {
local i output
@@ -77,13 +52,8 @@ testcase_locale() {
mkdir -p /etc/default
trap restore_locale RETURN
-
- if command -v locale-gen >/dev/null 2>&1 &&
- ! localectl list-locales | grep -F "en_US.UTF-8"; then
- # ensure at least one utf8 locale exist
- echo "en_US.UTF-8 UTF-8" >/etc/locale.gen
- locale-gen en_US.UTF-8
- fi
+ # 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
diff --git a/test/units/testsuite-74.battery-check.sh b/test/units/TEST-74-AUX-UTILS.battery-check.sh
index 52a92b8..52a92b8 100755
--- a/test/units/testsuite-74.battery-check.sh
+++ b/test/units/TEST-74-AUX-UTILS.battery-check.sh
diff --git a/test/units/testsuite-74.bootctl.sh b/test/units/TEST-74-AUX-UTILS.bootctl.sh
index 4be7bfd..78c0e6e 100755
--- a/test/units/testsuite-74.bootctl.sh
+++ b/test/units/TEST-74-AUX-UTILS.bootctl.sh
@@ -59,8 +59,8 @@ basic_tests() {
}
testcase_bootctl_basic() {
- assert_eq "$(bootctl --print-esp-path)" "/efi"
- assert_eq "$(bootctl --print-boot-path)" "/boot"
+ assert_in "$(bootctl --print-esp-path)" "^(/boot/|/efi)$"
+ assert_in "$(bootctl --print-boot-path)" "^(/boot/|/efi)$"
bootctl --print-root-device
basic_tests
@@ -263,4 +263,17 @@ EOF
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/TEST-74-AUX-UTILS.busctl.sh
index aaf96d0..aaf96d0 100755
--- a/test/units/testsuite-74.busctl.sh
+++ b/test/units/TEST-74-AUX-UTILS.busctl.sh
diff --git a/test/units/TEST-74-AUX-UTILS.capsule.sh b/test/units/TEST-74-AUX-UTILS.capsule.sh
new file mode 100755
index 0000000..e7b5c87
--- /dev/null
+++ b/test/units/TEST-74-AUX-UTILS.capsule.sh
@@ -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/testsuite-74.cgls.sh b/test/units/TEST-74-AUX-UTILS.cgls.sh
index 9268f42..9268f42 100755
--- a/test/units/testsuite-74.cgls.sh
+++ b/test/units/TEST-74-AUX-UTILS.cgls.sh
diff --git a/test/units/testsuite-74.cgtop.sh b/test/units/TEST-74-AUX-UTILS.cgtop.sh
index cf98279..cf98279 100755
--- a/test/units/testsuite-74.cgtop.sh
+++ b/test/units/TEST-74-AUX-UTILS.cgtop.sh
diff --git a/test/units/testsuite-74.coredump.sh b/test/units/TEST-74-AUX-UTILS.coredump.sh
index 6552643..b9c8fde 100755
--- a/test/units/testsuite-74.coredump.sh
+++ b/test/units/TEST-74-AUX-UTILS.coredump.sh
@@ -77,24 +77,26 @@ 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"
-# 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
+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
+ systemctl daemon-reload
+
+ [[ "$(systemd-detect-virt)" == "qemu" ]] && TIMEOUT=120 || TIMEOUT=60
-if cgroupfs_supports_user_xattrs; then
machinectl start "$CONTAINER"
- timeout 60 bash -xec "until systemd-run -M '$CONTAINER' -q --wait --pipe true; do sleep .5; done"
+ 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"
@@ -102,6 +104,10 @@ if cgroupfs_supports_user_xattrs; then
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"
+
+ machinectl stop "$CONTAINER"
+ rm -rf "/var/lib/machines/$CONTAINER"
+ unset CONTAINER
fi
coredumpctl
diff --git a/test/units/testsuite-74.delta.sh b/test/units/TEST-74-AUX-UTILS.delta.sh
index a0e1cb5..dabe234 100755
--- a/test/units/testsuite-74.delta.sh
+++ b/test/units/TEST-74-AUX-UTILS.delta.sh
@@ -14,14 +14,14 @@ trap at_exit EXIT
# Extended unit
cat >"/run/systemd/system/delta-test-unit-extended.service" <<EOF
[Service]
-ExecStart=/bin/true
+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=/bin/true
+ExecStartPre=true
EOF
# Masked unit
cp -fv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-masked.service
diff --git a/test/units/testsuite-74.escape.sh b/test/units/TEST-74-AUX-UTILS.escape.sh
index e398d40..e398d40 100755
--- a/test/units/testsuite-74.escape.sh
+++ b/test/units/TEST-74-AUX-UTILS.escape.sh
diff --git a/test/units/testsuite-74.firstboot.sh b/test/units/TEST-74-AUX-UTILS.firstboot.sh
index be08575..7bab009 100755
--- a/test/units/testsuite-74.firstboot.sh
+++ b/test/units/TEST-74-AUX-UTILS.firstboot.sh
@@ -3,6 +3,9 @@
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
@@ -13,6 +16,8 @@ at_exit() {
ls -lR "$ROOT"
rm -fr "$ROOT"
fi
+
+ restore_locale
}
trap at_exit EXIT
@@ -24,6 +29,23 @@ ROOT_HASHED_PASSWORD1='$6$foobarsalt$YbwdaATX6IsFxvWbY3QcZj2gB31R/LFRFrjlFrJtTTq
# 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"
diff --git a/test/units/testsuite-74.id128.sh b/test/units/TEST-74-AUX-UTILS.id128.sh
index c1b80d6..f91cd5f 100755
--- a/test/units/testsuite-74.id128.sh
+++ b/test/units/TEST-74-AUX-UTILS.id128.sh
@@ -22,6 +22,13 @@ 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
diff --git a/test/units/testsuite-74.machine-id-setup.sh b/test/units/TEST-74-AUX-UTILS.machine-id-setup.sh
index c2b9db5..c2b9db5 100755
--- a/test/units/testsuite-74.machine-id-setup.sh
+++ b/test/units/TEST-74-AUX-UTILS.machine-id-setup.sh
diff --git a/test/units/testsuite-74.modules-load.sh b/test/units/TEST-74-AUX-UTILS.modules-load.sh
index 3d00e07..ceac826 100755
--- a/test/units/testsuite-74.modules-load.sh
+++ b/test/units/TEST-74-AUX-UTILS.modules-load.sh
@@ -17,8 +17,10 @@ if systemd-detect-virt -cq; then
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 loop dummy
+modprobe --all --resolve-alias dummy
mkdir -p /run/modules-load.d/
@@ -27,62 +29,58 @@ mkdir -p /run/modules-load.d/
"$MODULES_LOAD_BIN" --version
# Explicit config file
-modprobe -v --all --remove loop dummy
-printf "loop\ndummy" >"$CONFIG_FILE"
+modprobe -v --all --remove dummy
+printf "dummy" >"$CONFIG_FILE"
"$MODULES_LOAD_BIN" "$CONFIG_FILE" |& tee /tmp/out.log
-grep -E "Inserted module .*loop" /tmp/out.log
grep -E "Inserted module .*dummy" /tmp/out.log
# Implicit config file
-modprobe -v --all --remove loop dummy
-printf "loop\ndummy" >"$CONFIG_FILE"
+modprobe -v --all --remove dummy
+printf "dummy" >"$CONFIG_FILE"
"$MODULES_LOAD_BIN" |& tee /tmp/out.log
-grep -E "Inserted module .*loop" /tmp/out.log
grep -E "Inserted module .*dummy" /tmp/out.log
# Valid & invalid data mixed together
-modprobe -v --all --remove loop dummy
+modprobe -v --all --remove dummy
cat >"$CONFIG_FILE" <<EOF
-loop
-loop
-loop
- loop
+dummy
+dummy
+dummy
+ dummy
dummy
\\n\n\n\\\\\\
-
-loo!@@123##2455
+
+dumm!@@123##2455
# This is a comment
$(printf "%.0sx" {0..4096})
dummy
-loop
+dummy
foo-bar-baz
1
"
'
EOF
"$MODULES_LOAD_BIN" |& tee /tmp/out.log
-grep -E "^Inserted module .*loop" /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 2 ]]
+[[ "$(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 loop dummy
+modprobe -v --all --remove dummy
# Make sure we have no config files left over that might interfere with
# following tests
rm -fv "$CONFIG_FILE"
-[[ -z "$(systemd-analyze cat-config modules-load.d)" ]]
-CMDLINE="ro root= modules_load= modules_load=, / = modules_load=foo-bar-baz,dummy modules_load=loop,loop,loop"
+[[ "$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 .*loop" /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 2 ]]
+[[ "$(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/TEST-74-AUX-UTILS.mount.sh
index 41c5c86..14253c3 100755
--- a/test/units/testsuite-74.mount.sh
+++ b/test/units/TEST-74-AUX-UTILS.mount.sh
@@ -6,13 +6,6 @@ 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
@@ -23,6 +16,7 @@ at_exit() {
trap at_exit EXIT
WORK_DIR="$(mktemp -d)"
+mkdir -p "$WORK_DIR/mnt"
systemd-mount --list
systemd-mount --list --full
@@ -30,15 +24,43 @@ systemd-mount --list --no-legend
systemd-mount --list --no-pager
systemd-mount --list --quiet
+# 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"
+
+# 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
+
# 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")"
-mkfs.ext4 -L sd-mount-test "$LOOP"
-mkdir "$WORK_DIR/mnt"
+udevadm wait --timeout 60 --settle "$LOOP"
+# Also wait for the .device unit for the loop device is active. Otherwise, the .device unit activation
+# that is triggered by the .mount unit introduced by systemd-mount below may time out.
+timeout 60 bash -c "until systemctl is-active $LOOP; do sleep 1; done"
mount "$LOOP" "$WORK_DIR/mnt"
touch "$WORK_DIR/mnt/foo.bar"
umount "$LOOP"
(! mountpoint "$WORK_DIR/mnt")
+# Wait for the mount unit to be unloaded. Otherwise, creation of the transient unit below may fail.
+MOUNT_UNIT=$(systemd-escape --path --suffix=mount "$WORK_DIR/mnt")
+timeout 60 bash -c "while [[ -n \$(systemctl list-units --all --no-legend $MOUNT_UNIT) ]]; do sleep 1; done"
# Mount with both source and destination set
systemd-mount "$LOOP" "$WORK_DIR/mnt"
@@ -123,15 +145,37 @@ 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)"
+LOOP_AUTO=$(systemd-mount --list --full --no-legend | awk '$6 == "sd-mount-test" { print $1 }')
+LOOP_AUTO_DEVPATH=$(udevadm info --query property --property DEVPATH --value "$LOOP_AUTO")
systemd-umount "$WORK_DIR/simple.img"
+# Wait for 'change' uevent for the device with DISK_MEDIA_CHANGE=1.
+# After the event, the backing_file attribute should be removed.
+timeout 60 bash -c "while [[ -e /sys/$LOOP_AUTO_DEVPATH/loop/backing_file ]]; do sleep 1; done"
# --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")"
-mkfs.vfat -n owner-vfat "$LOOP"
+# If the synthesized uevent triggered by inotify event has been processed earlier than the kernel finishes to
+# attach the backing file, then SYSTEMD_READY=0 is set for the device. As a workaround, monitor sysattr
+# and re-trigger uevent after that.
+LOOP_DEVPATH=$(udevadm info --query property --property DEVPATH --value "$LOOP")
+timeout 60 bash -c "until [[ -e /sys/$LOOP_DEVPATH/loop/backing_file ]]; do sleep 1; done"
+udevadm trigger --settle "$LOOP"
+# Also wait for the .device unit for the loop device is active. Otherwise, the .device unit activation
+# that is triggered by the .mount unit introduced by systemd-mount below may time out.
+if ! timeout 60 bash -c "until systemctl is-active $LOOP; do sleep 1; done"; then
+ # For debugging issue like
+ # https://github.com/systemd/systemd/issues/32680#issuecomment-2120959238
+ # https://github.com/systemd/systemd/issues/32680#issuecomment-2122074805
+ udevadm info "$LOOP"
+ udevadm info --attribute-walk "$LOOP"
+ cat /sys/"$(udevadm info --query property --property DEVPATH --value "$LOOP")"/loop/backing_file || :
+ false
+fi
# Mount it and check the UID/GID
[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt")" == "root:root" ]]
systemd-mount --owner=testuser "$LOOP" "$WORK_DIR/mnt"
@@ -140,12 +184,3 @@ systemctl status "$WORK_DIR/mnt"
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"
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
index 0000000..5b5b0a1
--- /dev/null
+++ b/test/units/TEST-74-AUX-UTILS.network-generator.sh
@@ -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/testsuite-74.networkctl.sh b/test/units/TEST-74-AUX-UTILS.networkctl.sh
index 0a687af..3e333a2 100755
--- a/test/units/testsuite-74.networkctl.sh
+++ b/test/units/TEST-74-AUX-UTILS.networkctl.sh
@@ -11,10 +11,12 @@ at_exit() {
systemctl stop systemd-networkd
if [[ -v NETWORK_NAME && -v NETDEV_NAME && -v LINK_NAME ]]; then
- rm -fvr {/usr/lib,/etc}/systemd/network/"$NETWORK_NAME" "/usr/lib/systemd/network/$NETDEV_NAME" \
+ 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
@@ -28,6 +30,16 @@ 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
@@ -35,9 +47,23 @@ cat >new <<EOF
Name=test2
EOF
-EDITOR='mv new' script -ec 'networkctl edit "$NETWORK_NAME"' /dev/null
+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
@@ -80,7 +106,19 @@ 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/TEST-74-AUX-UTILS.path.sh
index 79056a5..79056a5 100755
--- a/test/units/testsuite-74.path.sh
+++ b/test/units/TEST-74-AUX-UTILS.path.sh
diff --git a/test/units/testsuite-74.pstore.sh b/test/units/TEST-74-AUX-UTILS.pstore.sh
index 9be8066..9be8066 100755
--- a/test/units/testsuite-74.pstore.sh
+++ b/test/units/TEST-74-AUX-UTILS.pstore.sh
diff --git a/test/units/testsuite-74.run.sh b/test/units/TEST-74-AUX-UTILS.run.sh
index e894932..b4ff72e 100755
--- a/test/units/testsuite-74.run.sh
+++ b/test/units/TEST-74-AUX-UTILS.run.sh
@@ -218,19 +218,30 @@ for opt in nice on-{active,boot,calendar,startup,unit-active,unit-inactive} prop
done
# Let's make sure that ProtectProc= properly moves submounts of the original /proc over to the new proc
-
-A=$(cat /proc/sys/kernel/random/boot_id)
-B=$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/sys/kernel/random/boot_id)
-assert_eq "$A" "$B"
-
-V="/tmp/version.$RANDOM"
-A="$(cat /proc/version).piff"
-echo "$A" > "$V"
-mount --bind "$V" /proc/version
-
-B=$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/version)
-
-assert_eq "$A" "$B"
-
+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 "$V"
+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.sh b/test/units/TEST-74-AUX-UTILS.sh
index 9c2a033..9c2a033 100755
--- a/test/units/testsuite-74.sh
+++ b/test/units/TEST-74-AUX-UTILS.sh
diff --git a/test/units/TEST-74-AUX-UTILS.socket.sh b/test/units/TEST-74-AUX-UTILS.socket.sh
new file mode 100755
index 0000000..7ef85fa
--- /dev/null
+++ b/test/units/TEST-74-AUX-UTILS.socket.sh
@@ -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
index 0000000..5d87d9f
--- /dev/null
+++ b/test/units/TEST-74-AUX-UTILS.ssh.sh
@@ -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/testsuite-74.varlinkctl.sh b/test/units/TEST-74-AUX-UTILS.varlinkctl.sh
index 5a96269..1d5a98b 100755
--- a/test/units/testsuite-74.varlinkctl.sh
+++ b/test/units/TEST-74-AUX-UTILS.varlinkctl.sh
@@ -32,8 +32,9 @@ 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 .
+ # We ignore the return value of the following two calls, since if no memberships are defined at all this will return a NotFound error, which is OK
+ (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
@@ -53,6 +54,32 @@ if [[ -x /usr/lib/systemd/systemd-pcrextend ]]; then
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"
@@ -87,3 +114,7 @@ done
(! 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
index 0000000..400097f
--- /dev/null
+++ b/test/units/TEST-74-AUX-UTILS.vpick.sh
@@ -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/testsuite-75.sh b/test/units/TEST-75-RESOLVED.sh
index 86d602d..a4417ce 100755
--- a/test/units/testsuite-75.sh
+++ b/test/units/TEST-75-RESOLVED.sh
@@ -14,6 +14,12 @@ 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() {
@@ -46,8 +52,7 @@ monitor_check_rr() (
# 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
- # v255-only: match against a syslog tag as well to work around systemd/systemd#30886
- timeout -v 30s journalctl --since "$since" -f --full _SYSTEMD_UNIT="resolvectl-monitor.service" + SYSLOG_IDENTIFIER="resolvectl-monitor" | grep -m1 "$match"
+ timeout -v 30s journalctl -u resolvectl-monitor.service --since "$since" -f --full | grep -m1 "$match"
)
restart_resolved() {
@@ -103,12 +108,21 @@ assert_in '_localdnsproxy' "$(dig @127.0.0.53 -x 127.0.0.54)"
mkdir -p /run/systemd/resolved.conf.d
{
echo "[Resolve]"
- echo "MulticastDNS=yes"
- echo "LLMNR=yes"
+ 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)"
@@ -131,7 +145,7 @@ assert_in 'no' "$(resolvectl llmnr hoge)"
echo "MulticastDNS=resolve"
echo "LLMNR=resolve"
} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
-restart_resolved
+systemctl reload systemd-resolved.service
# set per-link setting
resolvectl mdns hoge yes
resolvectl llmnr hoge yes
@@ -151,7 +165,7 @@ assert_in 'no' "$(resolvectl llmnr hoge)"
echo "MulticastDNS=no"
echo "LLMNR=no"
} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
-restart_resolved
+systemctl reload systemd-resolved.service
# set per-link setting
resolvectl mdns hoge yes
resolvectl llmnr hoge yes
@@ -181,23 +195,44 @@ fd00:dead:beef:cafe::1 ns1.unsigned.test
127.128.0.5 localhost5 localhost5.localdomain localhost5.localdomain4 localhost.localdomain5 localhost5.localdomain5
EOF
-mkdir -p /etc/systemd/network
-cat >/etc/systemd/network/10-dns0.netdev <<EOF
+mkdir -p /run/systemd/network
+cat >/run/systemd/network/10-dns0.netdev <<EOF
[NetDev]
Name=dns0
Kind=dummy
EOF
-cat >/etc/systemd/network/10-dns0.network <<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"
@@ -217,6 +252,14 @@ ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
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
@@ -235,8 +278,11 @@ ln -svf /etc/bind.keys /etc/bind/bind.keys
# Start the services
systemctl unmask systemd-networkd
-systemctl start systemd-networkd
-restart_resolved
+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
@@ -247,17 +293,22 @@ 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 SyslogIdentifier=resolvectl-monitor -p Type=notify resolvectl monitor
-systemd-run -u resolvectl-monitor-json.service -p SyslogIdentifier=resolvectl-monitor-json -p Type=notify resolvectl monitor --json=short
+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
-# Check if all the zones are valid (zone-check always returns 0, so let's check
-# if it produces any errors/warnings)
-run knotc zone-check
+# 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
@@ -273,6 +324,7 @@ done < <(keymgr onlinesign.test. ds)
knotc zone-commit test.
knotc reload
+sleep 2
### SETUP END ###
@@ -348,6 +400,12 @@ 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
@@ -405,6 +463,27 @@ 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
@@ -418,6 +497,18 @@ 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
@@ -460,9 +551,9 @@ monitor_check_rr "$TIMESTAMP" "follow13.almost.final.signed.test IN CNAME follow
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"
+#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) ---"
@@ -545,6 +636,61 @@ grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT"
#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
@@ -559,10 +705,10 @@ systemctl stop resolvectl-monitor-json.service
# Issue: https://github.com/systemd/systemd/issues/29580 (part #2)
#
# Check for any warnings regarding malformed messages
-(! journalctl -p warning --grep malformed _SYSTEMD_UNIT="resolvectl-monitor-json.service" + SYSLOG_IDENTIFIER="resolvectl-monitor-json")
+(! 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" + SYSLOG_IDENTIFIER="resolvectl-monitor-json" | while read -r line; do
+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,
@@ -589,7 +735,9 @@ if command -v nft >/dev/null; then
sleep 2
drop_dns_outbound_traffic
set +e
- run dig stale1.unsigned.test -t A
+ # 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
@@ -602,13 +750,14 @@ if command -v nft >/dev/null; then
echo "StaleRetentionSec=1d"
} >/run/systemd/resolved.conf.d/test.conf
ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
- restart_resolved
+ systemctl reload systemd-resolved.service
run dig stale1.unsigned.test -t A
grep -qE "NOERROR" "$RUN_OUT"
sleep 2
drop_dns_outbound_traffic
- run dig stale1.unsigned.test -t A
+ # 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"
@@ -725,6 +874,28 @@ 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
diff --git a/test/units/testsuite-76.sh b/test/units/TEST-76-SYSCTL.sh
index 855d0ef..855d0ef 100755
--- a/test/units/testsuite-76.sh
+++ b/test/units/TEST-76-SYSCTL.sh
diff --git a/test/units/testsuite-78.sh b/test/units/TEST-78-SIGQUEUE.sh
index 46afd3c..46afd3c 100755
--- a/test/units/testsuite-78.sh
+++ b/test/units/TEST-78-SIGQUEUE.sh
diff --git a/test/units/testsuite-79.sh b/test/units/TEST-79-MEMPRESS.sh
index 205f7f3..2b1de20 100755
--- a/test/units/testsuite-79.sh
+++ b/test/units/TEST-79-MEMPRESS.sh
@@ -15,7 +15,7 @@ fi
systemd-analyze log-level debug
-CGROUP=/sys/fs/cgroup/"$(systemctl show testsuite-79.service -P ControlGroup)"
+CGROUP=/sys/fs/cgroup/"$(systemctl show TEST-79-MEMPRESS.service -P ControlGroup)"
test -d "$CGROUP"
if ! test -f "$CGROUP"/memory.pressure ; then
@@ -49,7 +49,17 @@ 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"
+systemd-run \
+ -u "$UNIT" \
+ -p Type=exec \
+ -p ProtectControlGroups=1 \
+ -p DynamicUser=1 \
+ -p MemoryPressureWatch=on \
+ -p MemoryPressureThresholdSec=123ms \
+ -p BindPaths=$SCRIPT \
+ `# Make sanitizers happy when DynamicUser=1 pulls in instrumented systemd NSS modules` \
+ -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
+ --wait "$SCRIPT"
rm "$SCRIPT"
diff --git a/test/units/testsuite-80.sh b/test/units/TEST-80-NOTIFYACCESS.sh
index 97b222a..97b222a 100755
--- a/test/units/testsuite-80.sh
+++ b/test/units/TEST-80-NOTIFYACCESS.sh
diff --git a/test/units/testsuite-81.debug-generator.sh b/test/units/TEST-81-GENERATORS.debug-generator.sh
index fddf85a..ef1e205 100755
--- a/test/units/testsuite-81.debug-generator.sh
+++ b/test/units/TEST-81-GENERATORS.debug-generator.sh
@@ -62,6 +62,13 @@ 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"
diff --git a/test/units/testsuite-81.environment-d-generator.sh b/test/units/TEST-81-GENERATORS.environment-d-generator.sh
index 5bc3978..5bc3978 100755
--- a/test/units/testsuite-81.environment-d-generator.sh
+++ b/test/units/TEST-81-GENERATORS.environment-d-generator.sh
diff --git a/test/units/testsuite-81.fstab-generator.sh b/test/units/TEST-81-GENERATORS.fstab-generator.sh
index 50c4b2f..0c1b27e 100755
--- a/test/units/testsuite-81.fstab-generator.sh
+++ b/test/units/TEST-81-GENERATORS.fstab-generator.sh
@@ -13,6 +13,7 @@ OUT_DIR="$(mktemp -d /tmp/fstab-generator.XXX)"
FSTAB="$(mktemp)"
at_exit() {
+ mountpoint -q /proc/cmdline && umount /proc/cmdline
rm -fr "${OUT_DIR:?}" "${FSTAB:?}"
}
@@ -148,7 +149,7 @@ check_fstab_mount_units() {
fi
if [[ -n "$opts" ]] && [[ "$opts" != defaults ]]; then
# Some options are not propagated to the generated unit
- if [[ "$where" == / ]]; then
+ 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=")"
@@ -325,7 +326,7 @@ SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" check
# Check the default stuff that we (almost) always create in initrd
: "fstab-generator: initrd default"
-SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+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"
@@ -347,7 +348,7 @@ cat "$FSTAB"
printf "%s\n" "${FSTAB_INVALID[@]}" >"$FSTAB"
cat "$FSTAB"
# Don't care about the exit code here
-SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR" || :
+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 ]]
diff --git a/test/units/testsuite-81.getty-generator.sh b/test/units/TEST-81-GENERATORS.getty-generator.sh
index 103e966..d1dd22c 100755
--- a/test/units/testsuite-81.getty-generator.sh
+++ b/test/units/TEST-81-GENERATORS.getty-generator.sh
@@ -85,5 +85,5 @@ 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
+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/TEST-81-GENERATORS.run-generator.sh
index 9bd74ef..9bd74ef 100755
--- a/test/units/testsuite-81.run-generator.sh
+++ b/test/units/TEST-81-GENERATORS.run-generator.sh
diff --git a/test/units/testsuite-81.sh b/test/units/TEST-81-GENERATORS.sh
index 9c2a033..9c2a033 100755
--- a/test/units/testsuite-81.sh
+++ b/test/units/TEST-81-GENERATORS.sh
diff --git a/test/units/testsuite-81.system-update-generator.sh b/test/units/TEST-81-GENERATORS.system-update-generator.sh
index 8ee1fee..8ee1fee 100755
--- a/test/units/testsuite-81.system-update-generator.sh
+++ b/test/units/TEST-81-GENERATORS.system-update-generator.sh
diff --git a/test/units/testsuite-82.sh b/test/units/TEST-82-SOFTREBOOT.sh
index b5e6ded..f86bc92 100755
--- a/test/units/testsuite-82.sh
+++ b/test/units/TEST-82-SOFTREBOOT.sh
@@ -23,11 +23,13 @@ systemd-analyze log-level debug
export SYSTEMD_LOG_LEVEL=debug
-if [ -f /run/testsuite82.touch3 ]; then
+if [ -f /run/TEST-82-SOFTREBOOT.touch3 ]; then
echo "This is the fourth boot!"
systemd-notify --status="Fourth Boot"
- rm /run/testsuite82.touch3
+ 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
@@ -37,22 +39,31 @@ if [ -f /run/testsuite82.touch3 ]; then
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"
-
+ 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"
+
+ [[ ! -e /run/credentials/TEST-82-SOFTREBOOT-nosurvive.service ]]
+ assert_eq "$(cat /run/credentials/TEST-82-SOFTREBOOT-survive-argv.service/preserve)" "yay"
+
+ # There may be huge amount of pending messages in sockets. Processing them may cause journal rotation and
+ # removal of old archived journal files. If a journal file is removed during journalctl reading it,
+ # the command may fail. To mitigate such, sync before reading journals. Workaround for #32834.
+ journalctl --sync
# 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
+elif [ -f /run/TEST-82-SOFTREBOOT.touch2 ]; then
echo "This is the third boot!"
systemd-notify --status="Third Boot"
- rm /run/testsuite82.touch2
+ 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
@@ -66,10 +77,10 @@ elif [ -f /run/testsuite82.touch2 ]; then
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 "$(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
@@ -82,21 +93,23 @@ elif [ -f /run/testsuite82.touch2 ]; then
mount
# Restart the unit that is not supposed to survive
- systemd-run --collect --service-type=exec --unit=testsuite-82-nosurvive.service sleep infinity
+ 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/testsuite82.touch3
+ 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/testsuite82.touch ]; then
+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/testsuite82.touch
+ rm /run/TEST-82-SOFTREBOOT.touch
# Check that the fdstore entry still exists
test "$LISTEN_FDS" -eq 1
@@ -104,8 +117,11 @@ elif [ -f /run/testsuite82.touch ]; then
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"
+ 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"
@@ -114,10 +130,10 @@ elif [ -f /run/testsuite82.touch ]; then
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 "$(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
@@ -138,11 +154,16 @@ elif [ -f /run/testsuite82.touch ]; then
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
+ 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/testsuite82.touch2
+ touch /run/TEST-82-SOFTREBOOT.touch2
systemctl --no-block reboot
# Now block until the soft-boot killing spree kills us
@@ -151,6 +172,8 @@ 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"
@@ -178,39 +201,61 @@ EOF
# 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
+ 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 -p SetCredential=gone:hoge --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=testsuite-82-survive-argv.service \
+ # By writing to stdout, which is connected to the journal, we also ensure logging doesn't break across
+ # soft reboots due to journald being temporarily stopped.
+ 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" \
+ --property SetCredential=preserve:yay \
"$survive_argv"
- systemd-run --service-type=exec --unit=testsuite-82-survive.service \
+ # shellcheck disable=SC2016
+ 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
+ bash -c 'count=0; while echo "$count"; do count=$[$count +1]; sleep 1; done'
# 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 \
+ 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/testsuite82.touch
+ touch /run/TEST-82-SOFTREBOOT.touch
systemctl --no-block --check-inhibitors=yes soft-reboot
# Now block until the soft-boot killing spree kills us
@@ -220,4 +265,4 @@ fi
systemd-analyze log-level info
touch /testok
-systemctl --no-block poweroff
+systemctl --no-block exit 123
diff --git a/test/units/testsuite-83.sh b/test/units/TEST-83-BTRFS.sh
index a722c79..4f10ae7 100755
--- a/test/units/testsuite-83.sh
+++ b/test/units/TEST-83-BTRFS.sh
@@ -3,6 +3,13 @@
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
diff --git a/test/units/testsuite-84.sh b/test/units/TEST-84-STORAGETM.sh
index eae87d5..eae87d5 100755
--- a/test/units/testsuite-84.sh
+++ b/test/units/TEST-84-STORAGETM.sh
diff --git a/test/units/a-conj.service b/test/units/a-conj.service
index 3a7c9e1..13927e7 100644
--- a/test/units/a-conj.service
+++ b/test/units/a-conj.service
@@ -6,4 +6,4 @@ After=a.service
Before=a.service
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/a.service b/test/units/a.service
index ec5d059..0cc1320 100644
--- a/test/units/a.service
+++ b/test/units/a.service
@@ -5,4 +5,4 @@ Requires=b.service
Before=b.service
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/autorelabel.service b/test/units/autorelabel.service
index 7e5f9a2..5f8386e 100644
--- a/test/units/autorelabel.service
+++ b/test/units/autorelabel.service
@@ -3,17 +3,17 @@
Description=Relabel all filesystems
DefaultDependencies=no
Requires=local-fs.target
-Conflicts=shutdown.target
After=local-fs.target
-Before=sysinit.target shutdown.target
+Conflicts=shutdown.target
+Before=shutdown.target basic.target
ConditionSecurity=selinux
ConditionPathExists=|/.autorelabel
+SuccessAction=reboot
[Service]
-ExecStart=sh -xec 'echo 0 >/sys/fs/selinux/enforce; fixfiles -f -F relabel; rm /.autorelabel; systemctl --force reboot'
+ExecStart=sh -xec 'echo 0 >/sys/fs/selinux/enforce; fixfiles -f -F relabel; rm /.autorelabel;'
Type=oneshot
TimeoutSec=infinity
-RemainAfterExit=yes
[Install]
WantedBy=basic.target
diff --git a/test/units/b.service b/test/units/b.service
index 4503cf3..e875714 100644
--- a/test/units/b.service
+++ b/test/units/b.service
@@ -4,4 +4,4 @@ Description=B
Wants=f.service
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/c.service b/test/units/c.service
index a1ce28c..3fc3717 100644
--- a/test/units/c.service
+++ b/test/units/c.service
@@ -4,4 +4,4 @@ Description=C
Requires=a.service
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/d.service b/test/units/d.service
index 8202325..0438607 100644
--- a/test/units/d.service
+++ b/test/units/d.service
@@ -6,4 +6,4 @@ Before=a.service
Requires=a.service
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/daughter.service b/test/units/daughter.service
index 385fbed..0ee4f24 100644
--- a/test/units/daughter.service
+++ b/test/units/daughter.service
@@ -5,5 +5,5 @@ Description=Daughter Service
[Service]
Slice=parent.slice
Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
CPUAccounting=true
diff --git a/test/units/delegated_cgroup_filtering_payload.sh b/test/units/delegated_cgroup_filtering_payload.sh
index 50d01a5..1181fbb 100755
--- a/test/units/delegated_cgroup_filtering_payload.sh
+++ b/test/units/delegated_cgroup_filtering_payload.sh
@@ -2,11 +2,8 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
mkdir /sys/fs/cgroup/system.slice/delegated-cgroup-filtering.service/the_child
-/bin/sh /usr/lib/systemd/tests/testdata/units/delegated_cgroup_filtering_payload_child.sh &
+/bin/sh /usr/lib/systemd/tests/testdata/units/delegated_cgroup_filtering_payload_child.sh
-while true
-do
- echo "parent_process: hello, world!"
- echo "parent_process: hello, people!"
- sleep .15
-done
+echo "parent_process: hello, world!"
+echo "parent_process: hello, people!"
+sleep 2
diff --git a/test/units/delegated_cgroup_filtering_payload_child.sh b/test/units/delegated_cgroup_filtering_payload_child.sh
index b5635b5..94f0d3a 100755
--- a/test/units/delegated_cgroup_filtering_payload_child.sh
+++ b/test/units/delegated_cgroup_filtering_payload_child.sh
@@ -3,9 +3,6 @@
echo $$ >/sys/fs/cgroup/system.slice/delegated-cgroup-filtering.service/the_child/cgroup.procs
-while true
-do
- echo "child_process: hello, world!"
- echo "child_process: hello, people!"
- sleep .15
-done
+echo "child_process: hello, world!"
+echo "child_process: hello, people!"
+sleep .15
diff --git a/test/units/dml-discard-empty.service b/test/units/dml-discard-empty.service
index 720c1da..c176989 100644
--- a/test/units/dml-discard-empty.service
+++ b/test/units/dml-discard-empty.service
@@ -5,4 +5,4 @@ Description=DML discard empty service
[Service]
Slice=dml-discard.slice
Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/dml-discard-set-ml.service b/test/units/dml-discard-set-ml.service
index 93246ac..0fba2ac 100644
--- a/test/units/dml-discard-set-ml.service
+++ b/test/units/dml-discard-set-ml.service
@@ -5,5 +5,5 @@ Description=DML discard set ml service
[Service]
Slice=dml-discard.slice
Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
MemoryLow=15
diff --git a/test/units/dml-override-empty.service b/test/units/dml-override-empty.service
index ac96de0..5f0c143 100644
--- a/test/units/dml-override-empty.service
+++ b/test/units/dml-override-empty.service
@@ -5,4 +5,4 @@ Description=DML override empty service
[Service]
Slice=dml-override.slice
Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/dml-passthrough-empty.service b/test/units/dml-passthrough-empty.service
index 1e1ba34..8966226 100644
--- a/test/units/dml-passthrough-empty.service
+++ b/test/units/dml-passthrough-empty.service
@@ -5,4 +5,4 @@ Description=DML passthrough empty service
[Service]
Slice=dml-passthrough.slice
Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/dml-passthrough-set-dml.service b/test/units/dml-passthrough-set-dml.service
index 9a15311..ec82174 100644
--- a/test/units/dml-passthrough-set-dml.service
+++ b/test/units/dml-passthrough-set-dml.service
@@ -5,5 +5,5 @@ Description=DML passthrough set DML service
[Service]
Slice=dml-passthrough.slice
Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
DefaultMemoryLow=15
diff --git a/test/units/dml-passthrough-set-ml.service b/test/units/dml-passthrough-set-ml.service
index 65083bc..63ec305 100644
--- a/test/units/dml-passthrough-set-ml.service
+++ b/test/units/dml-passthrough-set-ml.service
@@ -5,5 +5,5 @@ Description=DML passthrough set ML service
[Service]
Slice=dml-passthrough.slice
Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
MemoryLow=0
diff --git a/test/units/e.service b/test/units/e.service
index 5bbcde2..c2acb92 100644
--- a/test/units/e.service
+++ b/test/units/e.service
@@ -6,4 +6,4 @@ Before=a.service
Wants=a.service
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/end.sh b/test/units/end.sh
index 230b716..cc1d7ee 100755
--- a/test/units/end.sh
+++ b/test/units/end.sh
@@ -4,10 +4,14 @@
set -eux
set -o pipefail
-(! journalctl -q -o short-monotonic --grep "didn't pass validation" >>/failed)
+(! journalctl -q -o short-monotonic --grep "didn't pass validation" | grep -v "test-varlink-idl" >>/failed)
-# Here, the redundant '[.]' at the end is for making not the logged self command hit the grep.
-(! journalctl -q -o short-monotonic --grep 'Attempted to close sd-bus after fork whose connection is opened before the fork, this should not happen[.]' >>/failed)
+# Here, the redundant '[ ]' in the pattern is required in order not to match the logged command itself.
+(! journalctl -q -o short-monotonic --grep 'Warning: cannot close sd-bus connection[ ].*after fork' >>/failed)
+
+# Check if sd-executor doesn't complain about not being able to (de)serialize stuff
+(! journalctl -q -o short-monotonic --grep "[F]ailed to parse serialized line" >>/failed)
+(! journalctl -q -o short-monotonic --grep "[F]ailed to (de)?serialize \w+" >>/failed)
systemctl poweroff --no-block
exit 0
diff --git a/test/units/f.service b/test/units/f.service
index ca20053..a66043e 100644
--- a/test/units/f.service
+++ b/test/units/f.service
@@ -3,4 +3,4 @@
Description=F
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/g.service b/test/units/g.service
index 5fd794d..bfb3d78 100644
--- a/test/units/g.service
+++ b/test/units/g.service
@@ -4,4 +4,4 @@ Description=G
Conflicts=e.service
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/generator-utils.sh b/test/units/generator-utils.sh
index fb62747..97a63d8 100755
--- a/test/units/generator-utils.sh
+++ b/test/units/generator-utils.sh
@@ -72,7 +72,7 @@ run_and_list() {
ls -lR "$out_dir"
if [[ -n "${environ:-}" ]]; then
- umount /proc/1/environ
+ umount /proc/1/environ --lazy
rm -f "$environ"
fi
}
diff --git a/test/units/grandchild.service b/test/units/grandchild.service
index 4fe77b4..bdccfe1 100644
--- a/test/units/grandchild.service
+++ b/test/units/grandchild.service
@@ -5,4 +5,4 @@ Description=Grandchild Service
[Service]
Slice=parent-deep.slice
Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/h.service b/test/units/h.service
index 5361d42..1c4dbb4 100644
--- a/test/units/h.service
+++ b/test/units/h.service
@@ -4,4 +4,4 @@ Description=H
Wants=g.service
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/i.service b/test/units/i.service
index 2b5e821..783ac65 100644
--- a/test/units/i.service
+++ b/test/units/i.service
@@ -6,4 +6,4 @@ Wants=b.service
After=b.service
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/loopy.service b/test/units/loopy.service
index 7fc0e42..4c1a4a3 100644
--- a/test/units/loopy.service
+++ b/test/units/loopy.service
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/loopy2.service b/test/units/loopy2.service
index 7fc0e42..4c1a4a3 100644
--- a/test/units/loopy2.service
+++ b/test/units/loopy2.service
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/loopy3.service b/test/units/loopy3.service
index b2af20a..f7a2f67 100644
--- a/test/units/loopy3.service
+++ b/test/units/loopy3.service
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
-ExecStart=/bin/true
+ExecStart=true
[Unit]
Conflicts=loopy4.service
diff --git a/test/units/loopy4.service b/test/units/loopy4.service
index b2af20a..f7a2f67 100644
--- a/test/units/loopy4.service
+++ b/test/units/loopy4.service
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Service]
-ExecStart=/bin/true
+ExecStart=true
[Unit]
Conflicts=loopy4.service
diff --git a/test/units/nomemleaf.service b/test/units/nomemleaf.service
index 14ce5ad..2e5c8ce 100644
--- a/test/units/nomemleaf.service
+++ b/test/units/nomemleaf.service
@@ -5,6 +5,6 @@ Description=Nomem Leaf Service
[Service]
Slice=nomem.slice
Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
IOWeight=200
MemoryAccounting=true
diff --git a/test/units/sched_idle_bad.service b/test/units/sched_idle_bad.service
index be8f1c2..d9fd6cc 100644
--- a/test/units/sched_idle_bad.service
+++ b/test/units/sched_idle_bad.service
@@ -3,5 +3,5 @@
Description=Bad sched priority for Idle
[Service]
-ExecStart=/bin/true
+ExecStart=true
CPUSchedulingPriority=1
diff --git a/test/units/sched_idle_ok.service b/test/units/sched_idle_ok.service
index 5a1d809..a7238ed 100644
--- a/test/units/sched_idle_ok.service
+++ b/test/units/sched_idle_ok.service
@@ -3,5 +3,5 @@
Description=Sched idle with prio 0
[Service]
-ExecStart=/bin/true
+ExecStart=true
CPUSchedulingPriority=0
diff --git a/test/units/sched_rr_bad.service b/test/units/sched_rr_bad.service
index b51b868..3f3bf39 100644
--- a/test/units/sched_rr_bad.service
+++ b/test/units/sched_rr_bad.service
@@ -3,7 +3,7 @@
Description=Bad sched priority for RR
[Service]
-ExecStart=/bin/true
+ExecStart=true
CPUSchedulingPriority=-1
CPUSchedulingPriority=100
CPUSchedulingPolicy=rr
diff --git a/test/units/sched_rr_change.service b/test/units/sched_rr_change.service
index 6ae1feb..3a72bd6 100644
--- a/test/units/sched_rr_change.service
+++ b/test/units/sched_rr_change.service
@@ -3,7 +3,7 @@
Description=Change prio
[Service]
-ExecStart=/bin/true
+ExecStart=true
CPUSchedulingPriority=1
CPUSchedulingPriority=2
CPUSchedulingPriority=99
diff --git a/test/units/sched_rr_ok.service b/test/units/sched_rr_ok.service
index 00b9822..5c71f30 100644
--- a/test/units/sched_rr_ok.service
+++ b/test/units/sched_rr_ok.service
@@ -3,5 +3,5 @@
Description=Default prio for RR
[Service]
-ExecStart=/bin/true
+ExecStart=true
CPUSchedulingPolicy=rr
diff --git a/test/units/son.service b/test/units/son.service
index 2059118..0242509 100644
--- a/test/units/son.service
+++ b/test/units/son.service
@@ -5,5 +5,5 @@ Description=Son Service
[Service]
Slice=parent.slice
Type=oneshot
-ExecStart=/bin/true
+ExecStart=true
CPUShares=100
diff --git a/test/units/test-control.sh b/test/units/test-control.sh
index 0a1611b..4cede74 100644
--- a/test/units/test-control.sh
+++ b/test/units/test-control.sh
@@ -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
index 9074e09..0000000
--- a/test/units/testsuite-01.service
+++ /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-02.service b/test/units/testsuite-02.service
deleted file mode 100644
index dea2c4f..0000000
--- a/test/units/testsuite-02.service
+++ /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
index 2a3cb08..0000000
--- a/test/units/testsuite-02.sh
+++ /dev/null
@@ -1,113 +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}
-mapfile -t TEST_LIST < <(find /usr/lib/systemd/tests/unit-tests/ -maxdepth 1 -type f -name "${TESTS_GLOB}")
-
-# Reset state
-rm -fv /failed /skipped /testok
-
-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
-report_result() {
- if [[ $# -ne 2 ]]; then
- echo >&2 "check_result: missing arguments"
- exit 1
- fi
-
- local name="${1##*/}"
- local ret=$2
-
- if [[ $ret -ne 0 && $ret != 77 && $ret != 127 ]]; then
- echo "$name failed with $ret"
- echo "$name" >>/failed-tests
- {
- echo "--- $name begin ---"
- cat "/$name.log"
- echo "--- $name end ---"
- } >>/failed
- elif [[ $ret == 77 || $ret == 127 ]]; then
- echo "$name skipped"
- echo "$name" >>/skipped-tests
- {
- echo "--- $name begin ---"
- cat "/$name.log"
- echo "--- $name end ---"
- } >>/skipped
- else
- echo "$name OK"
- echo "$name" >>/testok
- fi
-}
-
-set +x
-# Associative array for running tasks, where running[test-path]=PID
-declare -A running=()
-for task in "${TEST_LIST[@]}"; do
- # If there's MAX_QUEUE_SIZE running tasks, keep checking the running queue
- # until one of the tasks finishes, so we can replace it.
- while [[ ${#running[@]} -ge $MAX_QUEUE_SIZE ]]; do
- for key in "${!running[@]}"; do
- if ! kill -0 "${running[$key]}" &>/dev/null; then
- # Task has finished, report its result and drop it from the queue
- wait "${running[$key]}" && ec=0 || ec=$?
- report_result "$key" "$ec"
- unset "running[$key]"
- # Break from inner for loop and outer while loop to skip
- # the sleep below when we find a free slot in the queue
- break 2
- fi
- done
-
- # Precisely* calculated constant to keep the spinlock from burning the CPU(s)
- sleep 0.01
- done
-
- if [[ -x $task ]]; then
- echo "Executing test '$task'"
- log_file="/${task##*/}.log"
- $task &>"$log_file" &
- running[$task]=$!
- fi
-done
-
-# Wait for remaining running tasks
-for key in "${!running[@]}"; do
- echo "Waiting for test '$key' to finish"
- wait "${running[$key]}" && ec=0 || ec=$?
- report_result "$key" "$ec"
- unset "running[$key]"
-done
-
-set -x
-
-# 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
index 836f962..0000000
--- a/test/units/testsuite-03.service
+++ /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-04.LogFilterPatterns.sh b/test/units/testsuite-04.LogFilterPatterns.sh
deleted file mode 100755
index 2192e84..0000000
--- a/test/units/testsuite-04.LogFilterPatterns.sh
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# This fails due to https://github.com/systemd/systemd/issues/30886
-# but it is too complex and risky to backport, so disable the test
-exit 0
-
-# 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 end
-
- start="$(date '+%Y-%m-%d %T.%6N')"
- systemctl restart "$unit"
- sleep .5
- journalctl --sync
- end="$(date '+%Y-%m-%d %T.%6N')"
-
- journalctl -q -u "$unit" -S "$start" -U "$end" -p notice
- systemctl stop "$unit"
-}
-
-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.journal-gatewayd.sh b/test/units/testsuite-04.journal-gatewayd.sh
deleted file mode 100755
index 5755ef1..0000000
--- a/test/units/testsuite-04.journal-gatewayd.sh
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-# pipefail is disabled intentionally, as `curl | grep -q` is very SIGPIPE happy
-
-if [[ ! -x /usr/lib/systemd/systemd-journal-gatewayd ]]; then
- echo "Built without systemd-journal-gatewayd support, skipping the test"
- exit 0
-fi
-
-TEST_MESSAGE="-= This is a test message $RANDOM =-"
-TEST_TAG="$(systemd-id128 new)"
-
-echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG"
-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; }')"
-
-/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 -Lfs http://localhost:19531 | grep -qF "<title>Journal</title>"
-curl -Lfs http://localhost:19531/browse | grep -qF "<title>Journal</title>"
-(! curl -Lfs http://localhost:19531/foo/bar/baz)
-(! curl -Lfs http://localhost:19531/foo/../../../bar/../baz)
-
-# /entries
-# Accept: text/plain should be the default
-curl -Lfs http://localhost:19531/entries | \
- grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE"
-curl -Lfs --header "Accept: text/plain" http://localhost:19531/entries | \
- grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE"
-curl -Lfs --header "Accept: application/json" http://localhost:19531/entries | \
- jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")"
-curl -Lfs --header "Accept: application/json" http://localhost:19531/entries?boot | \
- jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")"
-curl -Lfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \
- jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")"
-# Show 10 entries starting from $BOOT_CURSOR, skip the first 5
-curl -Lfs --header "Accept: application/json" --header "Range: entries=$BOOT_CURSOR:5:10" http://localhost:19531/entries | \
- jq -se "length == 10"
-# Check if the specified cursor refers to an existing entry and return just that entry
-curl -Lfs --header "Accept: application/json" --header "Range: entries=$TEST_CURSOR" http://localhost:19531/entries?discrete | \
- jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")"
-# 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 -Lfs --header "Accept: text/event-stream" http://localhost:19531/entries | \
- awk '!/^(data: \{.+\}|)$/ { exit 1; }'
-# Same thing as journalctl --output=export
-mkdir /tmp/remote-journal
-curl -Lfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries | \
- /usr/lib/systemd/systemd-journal-remote --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/*
-# 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 -Lfs http://localhost:19531/machine | jq
-
-# /fields
-curl -Lfs http://localhost:19531/fields/MESSAGE | grep -qE -- "$TEST_MESSAGE"
-curl -Lfs http://localhost:19531/fields/_TRANSPORT
-(! curl -Lfs http://localhost:19531/fields)
-(! curl -Lfs 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 -Lfsk https://localhost:19531 | grep -qF "<title>Journal</title>"
-curl -Lfsk https://localhost:19531/entries | \
- grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE"
-curl -Lfsk --header "Accept: application/json" https://localhost:19531/entries | \
- jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")"
-curl -Lfsk https://localhost:19531/machine | jq
-curl -Lfsk https://localhost:19531/fields/_TRANSPORT
-
-kill "$GATEWAYD_PID"
diff --git a/test/units/testsuite-04.service b/test/units/testsuite-04.service
deleted file mode 100644
index 63a0104..0000000
--- a/test/units/testsuite-04.service
+++ /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-05.service b/test/units/testsuite-05.service
deleted file mode 100644
index ab72d8f..0000000
--- a/test/units/testsuite-05.service
+++ /dev/null
@@ -1,8 +0,0 @@
-# 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/testsuite-06.service b/test/units/testsuite-06.service
deleted file mode 100644
index c4c1d87..0000000
--- a/test/units/testsuite-06.service
+++ /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-08.service b/test/units/testsuite-08.service
deleted file mode 100644
index 2db35cf..0000000
--- a/test/units/testsuite-08.service
+++ /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-09.service b/test/units/testsuite-09.service
deleted file mode 100644
index 6c957ec..0000000
--- a/test/units/testsuite-09.service
+++ /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-13.service b/test/units/testsuite-13.service
deleted file mode 100644
index 95c9a02..0000000
--- a/test/units/testsuite-13.service
+++ /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-15.service b/test/units/testsuite-15.service
deleted file mode 100644
index 10af93f..0000000
--- a/test/units/testsuite-15.service
+++ /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-17.03.sh b/test/units/testsuite-17.03.sh
deleted file mode 100755
index 56e352e..0000000
--- a/test/units/testsuite-17.03.sh
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
-
-TEST_RULE="/run/udev/rules.d/49-test.rules"
-KILL_PID=
-
-setup() {
- mkdir -p "${TEST_RULE%/*}"
- [[ -e /etc/udev/udev.conf ]] && cp -f /etc/udev/udev.conf /etc/udev/udev.conf.bak
- # Don't bother storing the coredumps in journal for this particular test
- mkdir -p /run/systemd/coredump.conf.d/
- echo -ne "[Coredump]\nStorage=external\n" >/run/systemd/coredump.conf.d/99-storage-journal.conf
-
- cat >"${TEST_RULE}" <<EOF
-ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
-ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", PROGRAM=="/bin/sleep 60"
-EOF
- cat >/etc/udev/udev.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"
- [[ -e /etc/udev/udev.conf.bak ]] && mv -f /etc/udev/udev.conf.bak /etc/udev/udev.conf
- rm /run/systemd/coredump.conf.d/99-storage-journal.conf
- systemctl restart systemd-udevd.service
-}
-
-run_test() {
- local since
-
- since="$(date '+%F %T')"
-
- 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 coredumpctl --since "$since" --no-legend --no-pager | grep /bin/udevadm ; then
- 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
- return 0
- fi
- sleep .5
- done
-
- return 1
-}
-
-trap teardown EXIT
-
-setup
-run_test
-
-exit 0
diff --git a/test/units/testsuite-17.service b/test/units/testsuite-17.service
deleted file mode 100644
index d218d72..0000000
--- a/test/units/testsuite-17.service
+++ /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-18.service b/test/units/testsuite-18.service
deleted file mode 100644
index 16d90a1..0000000
--- a/test/units/testsuite-18.service
+++ /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-19.service b/test/units/testsuite-19.service
deleted file mode 100644
index 9ee5fc9..0000000
--- a/test/units/testsuite-19.service
+++ /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-21.service b/test/units/testsuite-21.service
deleted file mode 100644
index a5f77d0..0000000
--- a/test/units/testsuite-21.service
+++ /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-22.service b/test/units/testsuite-22.service
deleted file mode 100644
index a5ed660..0000000
--- a/test/units/testsuite-22.service
+++ /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-23-short-lived.sh b/test/units/testsuite-23-short-lived.sh
deleted file mode 100755
index 4a12c7f..0000000
--- a/test/units/testsuite-23-short-lived.sh
+++ /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.JoinsNamespaceOf.sh b/test/units/testsuite-23.JoinsNamespaceOf.sh
deleted file mode 100755
index 68ba465..0000000
--- a/test/units/testsuite-23.JoinsNamespaceOf.sh
+++ /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.Upholds.sh b/test/units/testsuite-23.Upholds.sh
deleted file mode 100755
index e62f9c6..0000000
--- a/test/units/testsuite-23.Upholds.sh
+++ /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.oneshot-restart.sh b/test/units/testsuite-23.oneshot-restart.sh
deleted file mode 100755
index 433cd69..0000000
--- a/test/units/testsuite-23.oneshot-restart.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eux
-set -o pipefail
-
-# Test oneshot unit restart on failure
-
-# wait this many secs for each test service to succeed in what is being tested
-MAX_SECS=60
-
-systemd-analyze 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-41-oneshot-restart-test"
-
-: >$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
-
-systemd-analyze log-level info
diff --git a/test/units/testsuite-23.runtime-bind-paths.sh b/test/units/testsuite-23.runtime-bind-paths.sh
deleted file mode 100755
index 65c2dbf..0000000
--- a/test/units/testsuite-23.runtime-bind-paths.sh
+++ /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
index 26f5226..0000000
--- a/test/units/testsuite-23.service
+++ /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.start-stop-no-reload.sh b/test/units/testsuite-23.start-stop-no-reload.sh
deleted file mode 100755
index 9c4f17d..0000000
--- a/test/units/testsuite-23.start-stop-no-reload.sh
+++ /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=/bin/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=/bin/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=/bin/sleep infinity
-EOF
-
-systemctl restart testsuite-23-no-reload.target
-
-systemctl is-active testsuite-23-no-reload.service
diff --git a/test/units/testsuite-24.service b/test/units/testsuite-24.service
deleted file mode 100644
index e192d1c..0000000
--- a/test/units/testsuite-24.service
+++ /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-25.service b/test/units/testsuite-25.service
deleted file mode 100644
index 503eabb..0000000
--- a/test/units/testsuite-25.service
+++ /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-26.service b/test/units/testsuite-26.service
deleted file mode 100644
index d8fdaff..0000000
--- a/test/units/testsuite-26.service
+++ /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-29.service b/test/units/testsuite-29.service
deleted file mode 100644
index 035c6bf..0000000
--- a/test/units/testsuite-29.service
+++ /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-30.service b/test/units/testsuite-30.service
deleted file mode 100644
index 253f7b5..0000000
--- a/test/units/testsuite-30.service
+++ /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-31.service b/test/units/testsuite-31.service
deleted file mode 100644
index f0e78a9..0000000
--- a/test/units/testsuite-31.service
+++ /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-32.service b/test/units/testsuite-32.service
deleted file mode 100644
index 50f5823..0000000
--- a/test/units/testsuite-32.service
+++ /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-34.service b/test/units/testsuite-34.service
deleted file mode 100644
index 6917afe..0000000
--- a/test/units/testsuite-34.service
+++ /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-35.service b/test/units/testsuite-35.service
deleted file mode 100644
index 0599f61..0000000
--- a/test/units/testsuite-35.service
+++ /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-36.service b/test/units/testsuite-36.service
deleted file mode 100644
index 5746dc1..0000000
--- a/test/units/testsuite-36.service
+++ /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-38.service b/test/units/testsuite-38.service
deleted file mode 100644
index ac77836..0000000
--- a/test/units/testsuite-38.service
+++ /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-43.service b/test/units/testsuite-43.service
deleted file mode 100644
index e36afea..0000000
--- a/test/units/testsuite-43.service
+++ /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-44.service b/test/units/testsuite-44.service
deleted file mode 100644
index 4dffdea..0000000
--- a/test/units/testsuite-44.service
+++ /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-45.service b/test/units/testsuite-45.service
deleted file mode 100644
index b16ce99..0000000
--- a/test/units/testsuite-45.service
+++ /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-46.service b/test/units/testsuite-46.service
deleted file mode 100644
index 5efb9cc..0000000
--- a/test/units/testsuite-46.service
+++ /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
index a77683b..0000000
--- a/test/units/testsuite-46.sh
+++ /dev/null
@@ -1,319 +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 0
-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\):' /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
-}
-
-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.
-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
-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
-
-PASSWORD=xEhErW0ndafV4s homectl activate test-user
-inspect test-user
-
-homectl deactivate test-user
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Offline test"
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl activate test-user
-inspect test-user
-
-homectl deactivate test-user
-inspect test-user
-
-# Do some resize tests, but only if we run on real kernels, as quota inside of containers will fail
-if ! systemd-detect-virt -cq ; 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
- 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
-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)
-
-wait_for_state test-user inactive
-homectl remove test-user
-
-if ! systemd-detect-virt -cq ; then
- wait_for_state test-user2 active
- homectl deactivate test-user2
- wait_for_state test-user2 inactive
- homectl remove test-user2
-fi
-
-# 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
-
-systemd-analyze log-level info
-
-touch /testok
diff --git a/test/units/testsuite-50.service b/test/units/testsuite-50.service
deleted file mode 100644
index bcafe6e..0000000
--- a/test/units/testsuite-50.service
+++ /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
index 3726b32..0000000
--- a/test/units/testsuite-50.sh
+++ /dev/null
@@ -1,718 +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
-
-export SYSTEMD_LOG_LEVEL=debug
-
-# shellcheck disable=SC2317
-cleanup() {(
- set +ex
-
- if [ -z "${image_dir}" ]; then
- return
- fi
- umount "${image_dir}/app0"
- umount "${image_dir}/app1"
- umount "${image_dir}/app-nodistro"
- umount "${image_dir}/service-scoped-test"
- rm -rf "${image_dir}"
-)}
-
-udevadm control --log-level=debug
-
-cd /tmp
-
-image_dir="$(mktemp -d -t -p /tmp tmp.XXXXXX)"
-if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then
- echo "mktemp under /tmp failed"
- exit 1
-fi
-
-trap cleanup EXIT
-
-cp /usr/share/minimal* "${image_dir}/"
-image="${image_dir}/minimal_0"
-roothash="$(cat "${image}.roothash")"
-
-os_release="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)"
-
-systemd-dissect --json=short "${image}.raw" | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
-systemd-dissect "${image}.raw" | grep -q -F "MARKER=1"
-systemd-dissect "${image}.raw" | grep -q -F -f <(sed 's/"//g' "$os_release")
-
-systemd-dissect --list "${image}.raw" | grep -q '^etc/os-release$'
-systemd-dissect --mtree "${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 "${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 "${image}.raw" etc/os-release | sha256sum)
-test "$SHA256SUM1" != ""
-read -r SHA256SUM2 _ < <(systemd-dissect --read-only --with "${image}.raw" sha256sum etc/os-release)
-test "$SHA256SUM2" != ""
-test "$SHA256SUM1" = "$SHA256SUM2"
-
-mv "${image}.verity" "${image}.fooverity"
-mv "${image}.roothash" "${image}.foohash"
-systemd-dissect --json=short "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
-systemd-dissect "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F "MARKER=1"
-systemd-dissect "${image}.raw" --root-hash="${roothash}" --verity-data="${image}.fooverity" | grep -q -F -f <(sed 's/"//g' "$os_release")
-mv "${image}.fooverity" "${image}.verity"
-mv "${image}.foohash" "${image}.roothash"
-
-mkdir -p "${image_dir}/mount" "${image_dir}/mount2"
-systemd-dissect --mount "${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 "${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 ${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="${image}.raw" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-mv "${image}.verity" "${image}.fooverity"
-mv "${image}.roothash" "${image}.foohash"
-systemd-run -P -p RootImage="${image}.raw" -p RootHash="${image}.foohash" -p RootVerity="${image}.fooverity" 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="${image}.raw" --property RootHash="${roothash}" --property RootVerity="${image}.fooverity" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-mv "${image}.fooverity" "${image}.verity"
-mv "${image}.foohash" "${image}.roothash"
-
-# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2
-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" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "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" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "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}" = "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}" = "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}" = "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"
-else
- echo "Unexpected uname -m: ${machine} in testsuite-50.sh, please fix me"
- exit 1
-fi
-# du rounds up to block size, which is more helpful for partitioning
-root_size="$(du -k "${image}.raw" | cut -f1)"
-verity_size="$(du -k "${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="${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"
-
-HAVE_OPENSSL=0
-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 2>&1; then
- echo "openssl missing" >/failed
- exit 1
- fi
-
- HAVE_OPENSSL=1
- 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
-
- # Create key pair
- openssl req -config "$OPENSSL_CONFIG" -new -x509 -newkey rsa:1024 -keyout "${image}.key" -out "${image}.crt" -days 365 -nodes
- # Sign Verity root hash with it
- openssl smime -sign -nocerts -noattr -binary -in "${image}.roothash" -inkey "${image}.key" -signer "${image}.crt" -outform der -out "${image}.roothash.p7s"
- # Generate signature partition JSON data
- echo '{"rootHash":"'"${roothash}"'","signature":"'"$(base64 -w 0 <"${image}.roothash.p7s")"'"}' >"${image}.verity-sig"
- # Pad it
- truncate -s "${signature_size}" "${image}.verity-sig"
- # Register certificate in the (userspace) verity key ring
- mkdir -p /run/verity.d
- ln -s "${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 "${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 "${image}.gpt"
-uuid="$(tail -c 32 "${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 "${image}.gpt" --append
-if [ "${HAVE_OPENSSL}" -eq 1 ]; then
- echo -e "size=${signature_size}, type=${signature_guid}" | sfdisk "${image}.gpt" --append
-fi
-sfdisk --part-label "${image}.gpt" 1 "Root Partition"
-sfdisk --part-label "${image}.gpt" 2 "Verity Partition"
-if [ "${HAVE_OPENSSL}" -eq 1 ]; then
- sfdisk --part-label "${image}.gpt" 3 "Signature Partition"
-fi
-loop="$(losetup --show -P -f "${image}.gpt")"
-partitions=(
- "${loop:?}p1"
- "${loop:?}p2"
-)
-if [ "${HAVE_OPENSSL}" -eq 1 ]; 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="${image}.raw" of="${loop}p1"
-udevadm lock --device="${loop}p2" dd if="${image}.verity" of="${loop}p2"
-if [ "${HAVE_OPENSSL}" -eq 1 ]; then
- udevadm lock --device="${loop}p3" dd if="${image}.verity-sig" of="${loop}p3"
-fi
-losetup -d "${loop}"
-
-# Derive partition UUIDs from root hash, in UUID syntax
-ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)"
-VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)"
-
-systemd-dissect --json=short --root-hash "${roothash}" "${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 "${roothash}" "${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 [ "${HAVE_OPENSSL}" -eq 1 ]; then
- systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q -E '{"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 "${roothash}" "${image}.gpt" | grep -q -F "MARKER=1"
-systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F -f <(sed 's/"//g' "$os_release")
-
-# Test image policies
-systemd-dissect --validate "${image}.gpt"
-systemd-dissect --validate "${image}.gpt" --image-policy='*'
-(! systemd-dissect --validate "${image}.gpt" --image-policy='~')
-(! systemd-dissect --validate "${image}.gpt" --image-policy='-')
-(! systemd-dissect --validate "${image}.gpt" --image-policy=root=absent)
-(! systemd-dissect --validate "${image}.gpt" --image-policy=swap=unprotected+encrypted+verity)
-systemd-dissect --validate "${image}.gpt" --image-policy=root=unprotected
-systemd-dissect --validate "${image}.gpt" --image-policy=root=verity
-systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:root-verity-sig=unused+absent
-systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:swap=absent
-systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:swap=absent+unprotected
-(! systemd-dissect --validate "${image}.gpt" --image-policy=root=verity:root-verity=unused+absent)
-systemd-dissect --validate "${image}.gpt" --image-policy=root=signed
-(! systemd-dissect --validate "${image}.gpt" --image-policy=root=signed:root-verity-sig=unused+absent)
-(! systemd-dissect --validate "${image}.gpt" --image-policy=root=signed:root-verity=unused+absent)
-
-# Test RootImagePolicy= unit file setting
-systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='*' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='~' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
-(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='-' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
-(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=absent' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
-systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=verity' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=signed' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-(! systemd-run --wait -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p RootImagePolicy='root=encrypted' -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1")
-
-systemd-dissect --root-hash "${roothash}" --mount "${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 "${roothash}" --mount "${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="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
-
-systemd-run -P -p RootImage="${image}.raw" -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P -p RootImage="${image}.gpt" -p RootImageOptions="root:ro,noatime root:ro,dev" 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=${image}.raw
-RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
-RootImageOptions=nosuid,dev
-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=${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
-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="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2:nosuid,dev" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P -p MountImages="${image}.gpt:/run/img1:root:nosuid ${image}.raw:/run/img2:home:suid" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P -p MountImages="${image}.raw:/run/img2\:3" cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p MountImages="${image}.raw:/run/img2\:3:nosuid" mount | grep -F "squashfs" | grep -q -F "nosuid"
-systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.raw" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.raw" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
-systemd-run -P -p TemporaryFileSystem=/run -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" 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=${image}.raw
-MountImages=${image}.gpt:/run/img1:root:noatime:home:relatime
-MountImages=${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=/bin/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 "${image}.raw" /tmp/img root:nosuid
-
-while systemctl show -P SubState testservice-50d.service | grep -q running
-do
- sleep 0.1
-done
-
-systemctl is-active testservice-50d.service
-
-# ExtensionImages will set up an overlay
-systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
-systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionImages=/usr/share/app-nodistro.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionImages=/etc/service-scoped-test.raw --property RootImage="${image}.raw" 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 /usr/share/symlink-test/
-cp /usr/share/app-nodistro.raw /usr/share/symlink-test/app-nodistro-v1.raw
-ln -fs /usr/share/symlink-test/app-nodistro-v1.raw /usr/share/symlink-test/app-nodistro.raw
-systemd-run -P --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw --property RootImage="${image}.raw" 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="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-# And again mixing sysext and confext
-systemd-run -P \
- --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
- --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
- --property RootImage="${image}.raw" cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
-systemd-run -P \
- --property ExtensionImages=/usr/share/symlink-test/app-nodistro.raw \
- --property ExtensionImages=/etc/symlink-test/service-scoped-test.raw \
- --property RootImage="${image}.raw" 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=${image}.raw
-ExtensionImages=/usr/share/app0.raw /usr/share/app1.raw:nosuid
-# Relevant only for sanitizer runs
-UnsetEnvironment=LD_PRELOAD
-ExecStart=/bin/bash -c '/opt/script0.sh | grep ID'
-ExecStart=/bin/bash -c '/opt/script1.sh | grep ID'
-Type=oneshot
-RemainAfterExit=yes
-EOF
-systemctl start testservice-50e.service
-systemctl is-active testservice-50e.service
-
-# 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="${image}.raw" cat /opt/script0.sh)
-(! systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh)
-systemd-dissect --mount /usr/share/app0.raw "${image_dir}/app0"
-systemd-dissect --mount /usr/share/app1.raw "${image_dir}/app1"
-systemd-dissect --mount /usr/share/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="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" 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="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" 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="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionDirectories="${image_dir}/app-nodistro" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
-systemd-run -P --property ExtensionDirectories="${image_dir}/service-scoped-test" --property RootImage="${image}.raw" 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=${image}.raw
-ExtensionDirectories=${image_dir}/app0 ${image_dir}/app1
-# Relevant only for sanitizer runs
-UnsetEnvironment=LD_PRELOAD
-ExecStart=/bin/bash -c '/opt/script0.sh | grep ID'
-ExecStart=/bin/bash -c '/opt/script1.sh | grep ID'
-Type=oneshot
-RemainAfterExit=yes
-EOF
-systemctl start testservice-50f.service
-systemctl is-active testservice-50f.service
-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 /usr/share/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
-
-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
-
-# Check that the /sbin/mount.ddi helper works
-T="/tmp/mounthelper.$RANDOM"
-mount -t ddi "${image}.gpt" "$T" -o ro,X-mount.mkdir,discard
-umount -R "$T"
-rmdir "$T"
-
-LOOP="$(systemd-dissect --attach --loop-ref=waldo "${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' "${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' "${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" "${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" "${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 "${image}.raw"
-(! systemd-dissect --detach "${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 "${image}.raw"
-systemd-run --unit=test-root-ephemeral \
- -p RootDirectory=/tmp/img \
- -p RootEphemeral=yes \
- -p Type=exec \
- 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
-systemd-dissect --list /tmp/img
-
-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="${image}.raw" cat /run/host/os-release | cmp "${os_release}"
-
-# Test that systemd-sysext reloads the daemon.
-mkdir -p /var/lib/extensions/
-ln -s /usr/share/app-reload.raw /var/lib/extensions/app-reload.raw
-systemd-sysext merge --no-reload
-# the service should not be running
-if systemctl --quiet is-active foo.service; then
- echo "foo.service should not be active"
- exit 1
-fi
-systemd-sysext unmerge --no-reload
-systemd-sysext merge
-for RETRY in $(seq 60) LAST; do
- if [[ "$(journalctl --boot _SYSTEMD_UNIT="foo.service" + SYSLOG_IDENTIFIER="sysext-foo" -p info -o cat)" == "foo" ]]; then
- break
- fi
- if [ "${RETRY}" = LAST ]; then
- echo "Output of foo.service not found"
- exit 1
- fi
- sleep 0.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
-
-# Test systemd-repart --make-ddi=:
-if command -v mksquashfs >/dev/null 2>&1; then
-
- 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
-
- read -r X < /etc/waldo
- test "$X" = 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)
-fi
-
-# 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)
-
-touch /testok
diff --git a/test/units/testsuite-52.service b/test/units/testsuite-52.service
deleted file mode 100644
index b9f2909..0000000
--- a/test/units/testsuite-52.service
+++ /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-53.service b/test/units/testsuite-53.service
deleted file mode 100644
index cf3adbb..0000000
--- a/test/units/testsuite-53.service
+++ /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-54.service b/test/units/testsuite-54.service
deleted file mode 100644
index ba8cdad..0000000
--- a/test/units/testsuite-54.service
+++ /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-55.service b/test/units/testsuite-55.service
deleted file mode 100644
index 00fb499..0000000
--- a/test/units/testsuite-55.service
+++ /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-58.service b/test/units/testsuite-58.service
deleted file mode 100644
index f843527..0000000
--- a/test/units/testsuite-58.service
+++ /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-59.service b/test/units/testsuite-59.service
deleted file mode 100644
index f85cfab..0000000
--- a/test/units/testsuite-59.service
+++ /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-60.service b/test/units/testsuite-60.service
deleted file mode 100644
index 1a929e4..0000000
--- a/test/units/testsuite-60.service
+++ /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-62-1.service b/test/units/testsuite-62-1.service
deleted file mode 100644
index fa3a7e7..0000000
--- a/test/units/testsuite-62-1.service
+++ /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=/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=
-Type=oneshot
diff --git a/test/units/testsuite-62.service b/test/units/testsuite-62.service
deleted file mode 100644
index 5c3f94d..0000000
--- a/test/units/testsuite-62.service
+++ /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-63.service b/test/units/testsuite-63.service
deleted file mode 100644
index 483c6a8..0000000
--- a/test/units/testsuite-63.service
+++ /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-64.service b/test/units/testsuite-64.service
deleted file mode 100644
index f75a3d7..0000000
--- a/test/units/testsuite-64.service
+++ /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-65.service b/test/units/testsuite-65.service
deleted file mode 100644
index 3610baf..0000000
--- a/test/units/testsuite-65.service
+++ /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-66-deviceisolation.service b/test/units/testsuite-66-deviceisolation.service
deleted file mode 100644
index 2d815a9..0000000
--- a/test/units/testsuite-66-deviceisolation.service
+++ /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=/bin/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
index 7e9dc3b..0000000
--- a/test/units/testsuite-66.service
+++ /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-67.service b/test/units/testsuite-67.service
deleted file mode 100644
index 82f998e..0000000
--- a/test/units/testsuite-67.service
+++ /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-68.service b/test/units/testsuite-68.service
deleted file mode 100644
index 2d86e1f..0000000
--- a/test/units/testsuite-68.service
+++ /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-70.service b/test/units/testsuite-70.service
deleted file mode 100644
index c13c2d5..0000000
--- a/test/units/testsuite-70.service
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-70-TPM2
-
-[Service]
-Type=oneshot
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
diff --git a/test/units/testsuite-71.service b/test/units/testsuite-71.service
deleted file mode 100644
index 1718629..0000000
--- a/test/units/testsuite-71.service
+++ /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-72.service b/test/units/testsuite-72.service
deleted file mode 100644
index 1640350..0000000
--- a/test/units/testsuite-72.service
+++ /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-73.service b/test/units/testsuite-73.service
deleted file mode 100644
index 3ebd24d..0000000
--- a/test/units/testsuite-73.service
+++ /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-74.service b/test/units/testsuite-74.service
deleted file mode 100644
index f782132..0000000
--- a/test/units/testsuite-74.service
+++ /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-75.service b/test/units/testsuite-75.service
deleted file mode 100644
index 111cde3..0000000
--- a/test/units/testsuite-75.service
+++ /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-76.service b/test/units/testsuite-76.service
deleted file mode 100644
index 3c8a9e8..0000000
--- a/test/units/testsuite-76.service
+++ /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-77-client.sh b/test/units/testsuite-77-client.sh
deleted file mode 100755
index 0d9487a..0000000
--- a/test/units/testsuite-77-client.sh
+++ /dev/null
@@ -1,14 +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
-
-assert_eq "$LISTEN_FDS" "1"
-assert_eq "$LISTEN_FDNAMES" "socket"
-read -r -u 3 text
-assert_eq "$text" "Socket"
diff --git a/test/units/testsuite-77-run.sh b/test/units/testsuite-77-run.sh
deleted file mode 100755
index fadd34d..0000000
--- a/test/units/testsuite-77-run.sh
+++ /dev/null
@@ -1,14 +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
-
-assert_eq "$LISTEN_FDS" "1"
-assert_eq "$LISTEN_FDNAMES" "new-file"
-read -r -u 3 text
-assert_eq "$text" "New"
diff --git a/test/units/testsuite-77.service b/test/units/testsuite-77.service
deleted file mode 100644
index 6ed8add..0000000
--- a/test/units/testsuite-77.service
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Unit]
-Description=TEST-77-OPENFILE
-
-[Service]
-OpenFile=/test-77-open.dat:open:read-only
-OpenFile=/test-77-file.dat
-ExecStartPre=rm -f /failed /testok
-ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
-Type=oneshot
diff --git a/test/units/testsuite-77.sh b/test/units/testsuite-77.sh
deleted file mode 100755
index 2b85a8c..0000000
--- a/test/units/testsuite-77.sh
+++ /dev/null
@@ -1,38 +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
-
-assert_eq "$LISTEN_FDS" "2"
-assert_eq "$LISTEN_FDNAMES" "open:test-77-file.dat"
-read -r -u 3 text
-assert_eq "$text" "Open"
-read -r -u 4 text
-assert_eq "$text" "File"
-
-# Test for socket
-systemctl start testsuite-77-server.socket
-systemd-run -p OpenFile=/tmp/test.sock:socket:read-only \
- --wait \
- --pipe \
- /usr/lib/systemd/tests/testdata/units/testsuite-77-client.sh
-
-# Tests for D-Bus
-diff <(systemctl show -p OpenFile testsuite-77) - <<EOF
-OpenFile=/test-77-open.dat:open:read-only
-OpenFile=/test-77-file.dat
-EOF
-echo "New" >/test-77-new-file.dat
-systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only "$(dirname "$0")"/testsuite-77-run.sh
-
-assert_rc 202 systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only -p OpenFile=/test-77-mssing-file.dat:missing-file:read-only "$(dirname "$0")"/testsuite-77-run.sh
-
-assert_rc 0 systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only -p OpenFile=/test-77-mssing-file.dat:missing-file:read-only,graceful "$(dirname "$0")"/testsuite-77-run.sh
-
-# End
-touch /testok
diff --git a/test/units/testsuite-78.service b/test/units/testsuite-78.service
deleted file mode 100644
index 05f3eff..0000000
--- a/test/units/testsuite-78.service
+++ /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-79.service b/test/units/testsuite-79.service
deleted file mode 100644
index f2d24df..0000000
--- a/test/units/testsuite-79.service
+++ /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-80.service b/test/units/testsuite-80.service
deleted file mode 100644
index 4c7f5d5..0000000
--- a/test/units/testsuite-80.service
+++ /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-81.service b/test/units/testsuite-81.service
deleted file mode 100644
index 3b697b3..0000000
--- a/test/units/testsuite-81.service
+++ /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-83.service b/test/units/testsuite-83.service
deleted file mode 100644
index 55ebb45..0000000
--- a/test/units/testsuite-83.service
+++ /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-84.service b/test/units/testsuite-84.service
deleted file mode 100644
index 2c25770..0000000
--- a/test/units/testsuite-84.service
+++ /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/unit-with-multiple-dashes.service b/test/units/unit-with-multiple-dashes.service
index 4aca904..9ac3c8c 100644
--- a/test/units/unit-with-multiple-dashes.service
+++ b/test/units/unit-with-multiple-dashes.service
@@ -4,4 +4,4 @@ Description=A unit with multiple dashes
Documentation=man:test
[Service]
-ExecStart=/bin/true
+ExecStart=true
diff --git a/test/units/util.sh b/test/units/util.sh
index b5ed732..f5f6786 100755
--- a/test/units/util.sh
+++ b/test/units/util.sh
@@ -28,6 +28,15 @@ assert_eq() {(
fi
)}
+assert_le() {(
+ set +ex
+
+ if [[ "${1:?}" -gt "${2:?}" ]]; then
+ echo "FAIL: '$1' > '$2'" >&2
+ exit 1
+ fi
+)}
+
assert_in() {(
set +ex
@@ -147,13 +156,13 @@ coverage_create_nspawn_dropin() {
create_dummy_container() {
local root="${1:?}"
- if [[ ! -d /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 /testsuite-13-container-template/* "$root"
+ cp -a /usr/share/TEST-13-NSPAWN-container-template/* "$root"
coverage_create_nspawn_dropin "$root"
}
@@ -216,3 +225,156 @@ kernel_supports_lsm() {
return 1
}
+
+install_extension_images() {
+ local os_release
+ os_release="$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)"
+
+ # Rolling distros like Arch do not set VERSION_ID
+ local version_id=""
+ if grep -q "^VERSION_ID=" "$os_release"; then
+ version_id="$(grep "^VERSION_ID=" "$os_release")"
+ fi
+
+ local initdir="/var/tmp/app0"
+ mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
+ grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app0"
+ echo "$version_id" >>"$initdir/usr/lib/extension-release.d/extension-release.app0"
+ (
+ echo "$version_id"
+ echo "SYSEXT_IMAGE_ID=app"
+ ) >>"$initdir/usr/lib/extension-release.d/extension-release.app0"
+ cat >"$initdir/usr/lib/systemd/system/app0.service" <<EOF
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/opt/script0.sh
+TemporaryFileSystem=/var/lib
+StateDirectory=app0
+RuntimeDirectory=app0
+EOF
+ cat >"$initdir/opt/script0.sh" <<EOF
+#!/bin/bash
+set -e
+test -e /usr/lib/os-release
+echo bar >\${STATE_DIRECTORY}/foo
+cat /usr/lib/extension-release.d/extension-release.app0
+EOF
+ chmod +x "$initdir/opt/script0.sh"
+ echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file"
+ mksquashfs "$initdir" /tmp/app0.raw -noappend
+
+ initdir="/var/tmp/conf0"
+ mkdir -p "$initdir/etc/extension-release.d" "$initdir/etc/systemd/system" "$initdir/opt"
+ grep "^ID=" "$os_release" >"$initdir/etc/extension-release.d/extension-release.conf0"
+ echo "$version_id" >>"$initdir/etc/extension-release.d/extension-release.conf0"
+ (
+ echo "$version_id"
+ echo "CONFEXT_IMAGE_ID=app"
+ ) >>"$initdir/etc/extension-release.d/extension-release.conf0"
+ echo MARKER_1 >"$initdir/etc/systemd/system/some_file"
+ mksquashfs "$initdir" /tmp/conf0.raw -noappend
+
+ initdir="/var/tmp/app1"
+ mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
+ grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app2"
+ (
+ echo "$version_id"
+ echo "SYSEXT_SCOPE=portable"
+ echo "SYSEXT_IMAGE_ID=app"
+ echo "SYSEXT_IMAGE_VERSION=1"
+ echo "PORTABLE_PREFIXES=app1"
+ ) >>"$initdir/usr/lib/extension-release.d/extension-release.app2"
+ setfattr -n user.extension-release.strict -v false "$initdir/usr/lib/extension-release.d/extension-release.app2"
+ cat >"$initdir/usr/lib/systemd/system/app1.service" <<EOF
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/opt/script1.sh
+StateDirectory=app1
+RuntimeDirectory=app1
+EOF
+ cat >"$initdir/opt/script1.sh" <<EOF
+#!/bin/bash
+set -e
+test -e /usr/lib/os-release
+echo baz >\${STATE_DIRECTORY}/foo
+cat /usr/lib/extension-release.d/extension-release.app2
+EOF
+ chmod +x "$initdir/opt/script1.sh"
+ echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file"
+ mksquashfs "$initdir" /tmp/app1.raw -noappend
+
+ initdir="/var/tmp/app-nodistro"
+ mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system"
+ (
+ echo "ID=_any"
+ echo "ARCHITECTURE=_any"
+ ) >"$initdir/usr/lib/extension-release.d/extension-release.app-nodistro"
+ echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file"
+ mksquashfs "$initdir" /tmp/app-nodistro.raw -noappend
+
+ initdir="/var/tmp/service-scoped-test"
+ mkdir -p "$initdir/etc/extension-release.d" "$initdir/etc/systemd/system"
+ (
+ echo "ID=_any"
+ echo "ARCHITECTURE=_any"
+ ) >"$initdir/etc/extension-release.d/extension-release.service-scoped-test"
+ echo MARKER_CONFEXT_123 >"$initdir/etc/systemd/system/some_file"
+ mksquashfs "$initdir" /etc/service-scoped-test.raw -noappend
+
+ # We need to create a dedicated sysext image to test the reload mechanism. If we share an image to install the
+ # 'foo.service' it will be loaded from another test run, which will impact the targeted test.
+ initdir="/var/tmp/app-reload"
+ mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system"
+ (
+ echo "ID=_any"
+ echo "ARCHITECTURE=_any"
+ echo "EXTENSION_RELOAD_MANAGER=1"
+ ) >"$initdir/usr/lib/extension-release.d/extension-release.app-reload"
+ mkdir -p "$initdir/usr/lib/systemd/system/multi-user.target.d"
+ cat >"$initdir/usr/lib/systemd/system/foo.service" <<EOF
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=echo foo
+
+[Install]
+WantedBy=multi-user.target
+EOF
+ echo -e "[Unit]\nUpholds=foo.service" >"$initdir/usr/lib/systemd/system/multi-user.target.d/10-foo-service.conf"
+ mksquashfs "$initdir" /tmp/app-reload.raw -noappend
+}
+
+restore_locale() {
+ if [[ -d /usr/lib/locale/xx_XX.UTF-8 ]]; then
+ rmdir /usr/lib/locale/xx_XX.UTF-8
+ fi
+
+ if [[ -f /tmp/locale.conf.bak ]]; then
+ mv /tmp/locale.conf.bak /etc/locale.conf
+ else
+ rm -f /etc/locale.conf
+ fi
+
+ if [[ -f /tmp/default-locale.bak ]]; then
+ mv /tmp/default-locale.bak /etc/default/locale
+ else
+ rm -rf /etc/default
+ fi
+
+ if [[ -f /tmp/locale.gen.bak ]]; then
+ mv /tmp/locale.gen.bak /etc/locale.gen
+ else
+ rm -f /etc/locale.gen
+ fi
+}
+
+generate_locale() {
+ local locale="${1:?}"
+
+ if command -v locale-gen >/dev/null && ! localectl list-locales | grep -F "$locale"; then
+ echo "$locale UTF-8" >/etc/locale.gen
+ locale-gen "$locale"
+ fi
+}