summaryrefslogtreecommitdiffstats
path: root/test/units
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--test/TEST-07-PID1/TEST-07-PID1.service (renamed from test/units/testsuite-07.service)3
-rw-r--r--test/TEST-16-EXTEND-TIMEOUT/TEST-16-EXTEND-TIMEOUT.service (renamed from test/units/testsuite-16.service)10
-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-54-CREDS/systemd.extra-unit.my-service.service (renamed from test/units/testsuite-69.service)4
-rw-r--r--test/TEST-82-SOFTREBOOT/TEST-82-SOFTREBOOT.service (renamed from test/units/testsuite-82.service)2
-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
332 files changed, 7320 insertions, 3069 deletions
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/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/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/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/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/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
+}