From 78e9bb837c258ac0ec7712b3d612cc2f407e731e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 05:50:42 +0200 Subject: Merging upstream version 256. Signed-off-by: Daniel Baumann --- test/units/TEST-01-BASIC.sh | 63 + test/units/TEST-02-UNITTESTS.sh | 102 ++ test/units/TEST-03-JOBS.sh | 169 +++ test/units/TEST-04-JOURNAL.LogFilterPatterns.sh | 93 ++ .../TEST-04-JOURNAL.SYSTEMD_JOURNAL_COMPRESS.sh | 49 + test/units/TEST-04-JOURNAL.bsod.sh | 123 ++ test/units/TEST-04-JOURNAL.cat.sh | 15 + test/units/TEST-04-JOURNAL.corrupted-journals.sh | 48 + test/units/TEST-04-JOURNAL.fss.sh | 46 + test/units/TEST-04-JOURNAL.journal-append.sh | 46 + test/units/TEST-04-JOURNAL.journal-corrupt.sh | 38 + test/units/TEST-04-JOURNAL.journal-gatewayd.sh | 223 ++++ test/units/TEST-04-JOURNAL.journal-remote.sh | 230 ++++ test/units/TEST-04-JOURNAL.journal.sh | 287 +++++ test/units/TEST-04-JOURNAL.sh | 11 + test/units/TEST-05-RLIMITS.effective-limit.sh | 68 + test/units/TEST-05-RLIMITS.rlimit.sh | 25 + test/units/TEST-05-RLIMITS.sh | 11 + test/units/TEST-06-SELINUX.sh | 49 + test/units/TEST-07-PID1.aux-scope.sh | 34 + test/units/TEST-07-PID1.exec-context.sh | 386 ++++++ test/units/TEST-07-PID1.exec-deserialization.sh | 221 ++++ test/units/TEST-07-PID1.exec-timestamps.sh | 17 + test/units/TEST-07-PID1.issue-14566.sh | 29 + test/units/TEST-07-PID1.issue-16115.sh | 16 + test/units/TEST-07-PID1.issue-1981.sh | 47 + test/units/TEST-07-PID1.issue-2467.sh | 17 + test/units/TEST-07-PID1.issue-27953.sh | 11 + test/units/TEST-07-PID1.issue-30412.sh | 32 + test/units/TEST-07-PID1.issue-3166.sh | 16 + test/units/TEST-07-PID1.issue-3171.sh | 50 + test/units/TEST-07-PID1.main-PID-change.sh | 172 +++ test/units/TEST-07-PID1.mount-invalid-chars.sh | 70 ++ test/units/TEST-07-PID1.poll-limit.sh | 48 + test/units/TEST-07-PID1.pr-31351.sh | 22 + test/units/TEST-07-PID1.private-network.sh | 7 + test/units/TEST-07-PID1.sh | 16 + test/units/TEST-07-PID1.socket-pass-fds.sh | 11 + test/units/TEST-07-PID1.type-exec-parallel.sh | 9 + test/units/TEST-08-INITRD.sh | 30 + test/units/TEST-09-REBOOT.journal.sh | 132 ++ test/units/TEST-09-REBOOT.sh | 25 + test/units/TEST-13-NSPAWN.importctl.sh | 66 + test/units/TEST-13-NSPAWN.machinectl.sh | 224 ++++ test/units/TEST-13-NSPAWN.nspawn-oci.sh | 467 +++++++ test/units/TEST-13-NSPAWN.nspawn.sh | 977 +++++++++++++++ test/units/TEST-13-NSPAWN.nss-mymachines.sh | 136 ++ test/units/TEST-13-NSPAWN.sh | 18 + test/units/TEST-15-DROPIN.sh | 717 +++++++++++ test/units/TEST-16-EXTEND-TIMEOUT.sh | 119 ++ test/units/TEST-17-UDEV.00.sh | 57 + test/units/TEST-17-UDEV.01.sh | 75 ++ test/units/TEST-17-UDEV.02.sh | 186 +++ test/units/TEST-17-UDEV.03.sh | 114 ++ test/units/TEST-17-UDEV.04.sh | 49 + test/units/TEST-17-UDEV.05.sh | 23 + test/units/TEST-17-UDEV.06.sh | 69 ++ test/units/TEST-17-UDEV.07.sh | 205 +++ test/units/TEST-17-UDEV.08.sh | 72 ++ test/units/TEST-17-UDEV.09.sh | 70 ++ test/units/TEST-17-UDEV.10.sh | 254 ++++ test/units/TEST-17-UDEV.11.sh | 447 +++++++ test/units/TEST-17-UDEV.12.sh | 86 ++ test/units/TEST-17-UDEV.13.sh | 89 ++ test/units/TEST-17-UDEV.credentials.sh | 38 + test/units/TEST-17-UDEV.link-property.sh | 203 +++ test/units/TEST-17-UDEV.sh | 13 + test/units/TEST-18-FAILUREACTION.sh | 17 + test/units/TEST-19-CGROUP.ExitType-cgroup.sh | 103 ++ test/units/TEST-19-CGROUP.cleanup-slice.sh | 49 + test/units/TEST-19-CGROUP.delegate.sh | 116 ++ test/units/TEST-19-CGROUP.sh | 11 + test/units/TEST-21-DFUZZER.sh | 134 ++ test/units/TEST-22-TMPFILES.01.sh | 13 + test/units/TEST-22-TMPFILES.02.sh | 183 +++ test/units/TEST-22-TMPFILES.03.sh | 275 ++++ test/units/TEST-22-TMPFILES.04.sh | 49 + test/units/TEST-22-TMPFILES.05.sh | 54 + test/units/TEST-22-TMPFILES.06.sh | 45 + test/units/TEST-22-TMPFILES.07.sh | 30 + test/units/TEST-22-TMPFILES.08.sh | 32 + test/units/TEST-22-TMPFILES.09.sh | 59 + test/units/TEST-22-TMPFILES.10.sh | 28 + test/units/TEST-22-TMPFILES.11.sh | 249 ++++ test/units/TEST-22-TMPFILES.12.sh | 206 +++ test/units/TEST-22-TMPFILES.13.sh | 75 ++ test/units/TEST-22-TMPFILES.14.sh | 37 + test/units/TEST-22-TMPFILES.15.sh | 44 + test/units/TEST-22-TMPFILES.16.sh | 41 + test/units/TEST-22-TMPFILES.17.sh | 15 + test/units/TEST-22-TMPFILES.18.sh | 34 + test/units/TEST-22-TMPFILES.19.sh | 29 + test/units/TEST-22-TMPFILES.sh | 11 + test/units/TEST-23-UNIT-FILE-openfile-child.sh | 15 + test/units/TEST-23-UNIT-FILE-short-lived.sh | 18 + test/units/TEST-23-UNIT-FILE.ExecReload.sh | 61 + test/units/TEST-23-UNIT-FILE.ExecStopPost.sh | 104 ++ test/units/TEST-23-UNIT-FILE.JoinsNamespaceOf.sh | 31 + .../TEST-23-UNIT-FILE.RuntimeDirectoryPreserve.sh | 26 + test/units/TEST-23-UNIT-FILE.StandardOutput.sh | 60 + test/units/TEST-23-UNIT-FILE.Upholds.sh | 99 ++ test/units/TEST-23-UNIT-FILE.clean-unit.sh | 329 +++++ test/units/TEST-23-UNIT-FILE.exec-command-ex.sh | 44 + test/units/TEST-23-UNIT-FILE.oneshot-restart.sh | 101 ++ test/units/TEST-23-UNIT-FILE.openfile.sh | 55 + test/units/TEST-23-UNIT-FILE.percentj-wantedby.sh | 15 + test/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh | 43 + test/units/TEST-23-UNIT-FILE.sh | 12 + .../TEST-23-UNIT-FILE.start-stop-no-reload.sh | 93 ++ test/units/TEST-23-UNIT-FILE.statedir.sh | 60 + test/units/TEST-23-UNIT-FILE.success-failure.sh | 49 + test/units/TEST-23-UNIT-FILE.type-exec.sh | 63 + test/units/TEST-23-UNIT-FILE.utmp.sh | 22 + test/units/TEST-23-UNIT-FILE.verify-unit-files.sh | 45 + test/units/TEST-23-UNIT-FILE.whoami.sh | 15 + test/units/TEST-24-CRYPTSETUP.sh | 273 ++++ test/units/TEST-25-IMPORT.sh | 143 +++ test/units/TEST-26-SYSTEMCTL.sh | 501 ++++++++ test/units/TEST-29-PORTABLE.sh | 371 ++++++ test/units/TEST-30-ONCLOCKCHANGE.sh | 29 + test/units/TEST-31-DEVICE-ENUMERATION.sh | 10 + test/units/TEST-32-OOMPOLICY.sh | 36 + test/units/TEST-34-DYNAMICUSERMIGRATE.sh | 160 +++ test/units/TEST-35-LOGIN.sh | 744 +++++++++++ test/units/TEST-36-NUMAPOLICY.sh | 357 ++++++ test/units/TEST-38-FREEZER-sleep.service | 3 + test/units/TEST-38-FREEZER.sh | 363 ++++++ test/units/TEST-43-PRIVATEUSER-UNPRIV.sh | 145 +++ test/units/TEST-44-LOG-NAMESPACE.sh | 30 + test/units/TEST-45-TIMEDATE.sh | 408 ++++++ test/units/TEST-46-HOMED.sh | 620 ++++++++++ test/units/TEST-50-DISSECT.DDI.sh | 70 ++ test/units/TEST-50-DISSECT.dissect.sh | 745 +++++++++++ test/units/TEST-50-DISSECT.mountfsd.sh | 92 ++ test/units/TEST-50-DISSECT.sh | 215 ++++ test/units/TEST-50-DISSECT.sysext.sh | 1012 +++++++++++++++ test/units/TEST-52-HONORFIRSTSHUTDOWN.sh | 11 + test/units/TEST-53-ISSUE-16347.sh | 31 + test/units/TEST-54-CREDS.sh | 402 ++++++ test/units/TEST-55-OOMD-testbloat.service | 10 + test/units/TEST-55-OOMD-testchill.service | 8 + test/units/TEST-55-OOMD-testmunch.service | 8 + test/units/TEST-55-OOMD-workload.slice | 11 + test/units/TEST-55-OOMD.sh | 190 +++ test/units/TEST-58-REPART.sh | 1307 ++++++++++++++++++++ test/units/TEST-59-RELOADING-RESTART.sh | 168 +++ test/units/TEST-60-MOUNT-RATELIMIT.sh | 320 +++++ test/units/TEST-62-RESTRICT-IFACES-1.service | 9 + test/units/TEST-62-RESTRICT-IFACES-2.service | 10 + test/units/TEST-62-RESTRICT-IFACES-3.service | 10 + test/units/TEST-62-RESTRICT-IFACES-4.service | 10 + test/units/TEST-62-RESTRICT-IFACES-5.service | 11 + test/units/TEST-62-RESTRICT-IFACES-6.service | 10 + test/units/TEST-62-RESTRICT-IFACES.sh | 56 + test/units/TEST-63-PATH.sh | 125 ++ test/units/TEST-64-UDEV-STORAGE.sh | 1262 +++++++++++++++++++ test/units/TEST-65-ANALYZE.sh | 952 ++++++++++++++ ...ST-66-DEVICE-ISOLATION-device-isolation.service | 10 + test/units/TEST-66-DEVICE-ISOLATION.sh | 24 + test/units/TEST-67-INTEGRITY.sh | 121 ++ test/units/TEST-68-PROPAGATE-EXIT-STATUS.sh | 216 ++++ test/units/TEST-69-SHUTDOWN.py | 58 + test/units/TEST-70-TPM2.creds.sh | 16 + test/units/TEST-70-TPM2.cryptenroll.sh | 96 ++ test/units/TEST-70-TPM2.cryptsetup.sh | 227 ++++ test/units/TEST-70-TPM2.measure.sh | 130 ++ test/units/TEST-70-TPM2.pcrextend.sh | 124 ++ test/units/TEST-70-TPM2.pcrlock.sh | 179 +++ test/units/TEST-70-TPM2.sh | 11 + test/units/TEST-70-TPM2.tpm2-setup.sh | 27 + test/units/TEST-71-HOSTNAME.sh | 242 ++++ test/units/TEST-72-SYSUPDATE.sh | 273 ++++ test/units/TEST-73-LOCALE.sh | 663 ++++++++++ test/units/TEST-74-AUX-UTILS.battery-check.sh | 9 + test/units/TEST-74-AUX-UTILS.bootctl.sh | 279 +++++ test/units/TEST-74-AUX-UTILS.busctl.sh | 110 ++ test/units/TEST-74-AUX-UTILS.capsule.sh | 53 + test/units/TEST-74-AUX-UTILS.cgls.sh | 27 + test/units/TEST-74-AUX-UTILS.cgtop.sh | 32 + test/units/TEST-74-AUX-UTILS.coredump.sh | 227 ++++ test/units/TEST-74-AUX-UTILS.delta.sh | 59 + test/units/TEST-74-AUX-UTILS.escape.sh | 108 ++ test/units/TEST-74-AUX-UTILS.firstboot.sh | 219 ++++ test/units/TEST-74-AUX-UTILS.id128.sh | 57 + test/units/TEST-74-AUX-UTILS.machine-id-setup.sh | 77 ++ test/units/TEST-74-AUX-UTILS.modules-load.sh | 86 ++ test/units/TEST-74-AUX-UTILS.mount.sh | 186 +++ test/units/TEST-74-AUX-UTILS.network-generator.sh | 37 + test/units/TEST-74-AUX-UTILS.networkctl.sh | 124 ++ test/units/TEST-74-AUX-UTILS.path.sh | 89 ++ test/units/TEST-74-AUX-UTILS.pstore.sh | 258 ++++ test/units/TEST-74-AUX-UTILS.run.sh | 247 ++++ test/units/TEST-74-AUX-UTILS.sh | 11 + test/units/TEST-74-AUX-UTILS.socket.sh | 84 ++ test/units/TEST-74-AUX-UTILS.ssh.sh | 60 + test/units/TEST-74-AUX-UTILS.varlinkctl.sh | 120 ++ test/units/TEST-74-AUX-UTILS.vpick.sh | 116 ++ test/units/TEST-75-RESOLVED.sh | 902 ++++++++++++++ test/units/TEST-76-SYSCTL.sh | 39 + test/units/TEST-78-SIGQUEUE.sh | 35 + test/units/TEST-79-MEMPRESS.sh | 68 + test/units/TEST-80-NOTIFYACCESS.sh | 126 ++ test/units/TEST-81-GENERATORS.debug-generator.sh | 112 ++ .../TEST-81-GENERATORS.environment-d-generator.sh | 80 ++ test/units/TEST-81-GENERATORS.fstab-generator.sh | 407 ++++++ test/units/TEST-81-GENERATORS.getty-generator.sh | 89 ++ test/units/TEST-81-GENERATORS.run-generator.sh | 76 ++ test/units/TEST-81-GENERATORS.sh | 11 + .../TEST-81-GENERATORS.system-update-generator.sh | 38 + test/units/TEST-82-SOFTREBOOT.sh | 268 ++++ test/units/TEST-83-BTRFS.sh | 32 + test/units/TEST-84-STORAGETM.sh | 26 + test/units/a-conj.service | 2 +- test/units/a.service | 2 +- test/units/autorelabel.service | 8 +- test/units/b.service | 2 +- test/units/c.service | 2 +- test/units/d.service | 2 +- test/units/daughter.service | 2 +- test/units/delegated_cgroup_filtering_payload.sh | 11 +- .../delegated_cgroup_filtering_payload_child.sh | 9 +- test/units/dml-discard-empty.service | 2 +- test/units/dml-discard-set-ml.service | 2 +- test/units/dml-override-empty.service | 2 +- test/units/dml-passthrough-empty.service | 2 +- test/units/dml-passthrough-set-dml.service | 2 +- test/units/dml-passthrough-set-ml.service | 2 +- test/units/e.service | 2 +- test/units/end.sh | 10 +- test/units/f.service | 2 +- test/units/g.service | 2 +- test/units/generator-utils.sh | 2 +- test/units/grandchild.service | 2 +- test/units/h.service | 2 +- test/units/i.service | 2 +- test/units/loopy.service | 2 +- test/units/loopy2.service | 2 +- test/units/loopy3.service | 2 +- test/units/loopy4.service | 2 +- test/units/nomemleaf.service | 2 +- test/units/sched_idle_bad.service | 2 +- test/units/sched_idle_ok.service | 2 +- test/units/sched_rr_bad.service | 2 +- test/units/sched_rr_change.service | 2 +- test/units/sched_rr_ok.service | 2 +- test/units/son.service | 2 +- test/units/test-control.sh | 2 +- test/units/testsuite-01.service | 10 - test/units/testsuite-01.sh | 61 - test/units/testsuite-02.service | 8 - test/units/testsuite-02.sh | 113 -- test/units/testsuite-03.service | 9 - test/units/testsuite-03.sh | 169 --- test/units/testsuite-04.LogFilterPatterns.sh | 86 -- .../units/testsuite-04.SYSTEMD_JOURNAL_COMPRESS.sh | 42 - test/units/testsuite-04.bsod.sh | 103 -- test/units/testsuite-04.corrupted-journals.sh | 48 - test/units/testsuite-04.fss.sh | 46 - test/units/testsuite-04.journal-append.sh | 46 - test/units/testsuite-04.journal-corrupt.sh | 36 - test/units/testsuite-04.journal-gatewayd.sh | 119 -- test/units/testsuite-04.journal-remote.sh | 230 ---- test/units/testsuite-04.journal.sh | 279 ----- test/units/testsuite-04.service | 8 - test/units/testsuite-04.sh | 11 - test/units/testsuite-05.service | 8 - test/units/testsuite-05.sh | 27 - test/units/testsuite-06.service | 8 - test/units/testsuite-06.sh | 43 - test/units/testsuite-07.exec-context.sh | 377 ------ test/units/testsuite-07.issue-14566.sh | 29 - test/units/testsuite-07.issue-16115.sh | 16 - test/units/testsuite-07.issue-1981.sh | 47 - test/units/testsuite-07.issue-2467.sh | 17 - test/units/testsuite-07.issue-27953.sh | 11 - test/units/testsuite-07.issue-30412.sh | 32 - test/units/testsuite-07.issue-3166.sh | 16 - test/units/testsuite-07.issue-3171.sh | 50 - test/units/testsuite-07.main-PID-change.sh | 172 --- test/units/testsuite-07.mount-invalid-chars.sh | 70 -- test/units/testsuite-07.poll-limit.sh | 48 - test/units/testsuite-07.private-network.sh | 7 - test/units/testsuite-07.service | 13 - test/units/testsuite-07.sh | 15 - test/units/testsuite-08.service | 9 - test/units/testsuite-08.sh | 30 - test/units/testsuite-09.journal.sh | 72 -- test/units/testsuite-09.service | 9 - test/units/testsuite-09.sh | 25 - test/units/testsuite-13.machinectl.sh | 218 ---- test/units/testsuite-13.nspawn-oci.sh | 467 ------- test/units/testsuite-13.nspawn.sh | 884 ------------- test/units/testsuite-13.nss-mymachines.sh | 135 -- test/units/testsuite-13.service | 8 - test/units/testsuite-13.sh | 11 - test/units/testsuite-15.service | 8 - test/units/testsuite-15.sh | 711 ----------- test/units/testsuite-16.service | 20 - test/units/testsuite-16.sh | 119 -- test/units/testsuite-17.00.sh | 57 - test/units/testsuite-17.01.sh | 75 -- test/units/testsuite-17.02.sh | 182 --- test/units/testsuite-17.03.sh | 75 -- test/units/testsuite-17.04.sh | 49 - test/units/testsuite-17.05.sh | 23 - test/units/testsuite-17.06.sh | 69 -- test/units/testsuite-17.07.sh | 205 --- test/units/testsuite-17.08.sh | 72 -- test/units/testsuite-17.09.sh | 70 -- test/units/testsuite-17.10.sh | 254 ---- test/units/testsuite-17.11.sh | 447 ------- test/units/testsuite-17.12.sh | 86 -- test/units/testsuite-17.13.sh | 89 -- test/units/testsuite-17.service | 8 - test/units/testsuite-17.sh | 13 - test/units/testsuite-18.service | 8 - test/units/testsuite-18.sh | 17 - test/units/testsuite-19.ExitType-cgroup.sh | 102 -- test/units/testsuite-19.cleanup-slice.sh | 49 - test/units/testsuite-19.delegate.sh | 115 -- test/units/testsuite-19.service | 8 - test/units/testsuite-19.sh | 11 - test/units/testsuite-21.service | 10 - test/units/testsuite-21.sh | 110 -- test/units/testsuite-22.01.sh | 13 - test/units/testsuite-22.02.sh | 167 --- test/units/testsuite-22.03.sh | 246 ---- test/units/testsuite-22.04.sh | 43 - test/units/testsuite-22.05.sh | 45 - test/units/testsuite-22.06.sh | 38 - test/units/testsuite-22.07.sh | 30 - test/units/testsuite-22.08.sh | 32 - test/units/testsuite-22.09.sh | 59 - test/units/testsuite-22.10.sh | 28 - test/units/testsuite-22.11.sh | 141 --- test/units/testsuite-22.12.sh | 196 --- test/units/testsuite-22.13.sh | 75 -- test/units/testsuite-22.14.sh | 37 - test/units/testsuite-22.15.sh | 32 - test/units/testsuite-22.16.sh | 36 - test/units/testsuite-22.17.sh | 15 - test/units/testsuite-22.service | 11 - test/units/testsuite-22.sh | 11 - test/units/testsuite-23-short-lived.sh | 18 - test/units/testsuite-23.ExecReload.sh | 61 - test/units/testsuite-23.ExecStopPost.sh | 104 -- test/units/testsuite-23.JoinsNamespaceOf.sh | 31 - .../units/testsuite-23.RuntimeDirectoryPreserve.sh | 26 - test/units/testsuite-23.StandardOutput.sh | 60 - test/units/testsuite-23.Upholds.sh | 99 -- test/units/testsuite-23.clean-unit.sh | 329 ----- test/units/testsuite-23.exec-command-ex.sh | 44 - test/units/testsuite-23.oneshot-restart.sh | 52 - test/units/testsuite-23.percentj-wantedby.sh | 15 - test/units/testsuite-23.runtime-bind-paths.sh | 43 - test/units/testsuite-23.service | 8 - test/units/testsuite-23.sh | 12 - test/units/testsuite-23.start-stop-no-reload.sh | 93 -- test/units/testsuite-23.statedir.sh | 60 - test/units/testsuite-23.success-failure.sh | 49 - test/units/testsuite-23.type-exec.sh | 63 - test/units/testsuite-23.utmp.sh | 22 - test/units/testsuite-23.whoami.sh | 15 - test/units/testsuite-24.service | 9 - test/units/testsuite-24.sh | 216 ---- test/units/testsuite-25.service | 8 - test/units/testsuite-25.sh | 143 --- test/units/testsuite-26.service | 8 - test/units/testsuite-26.sh | 465 ------- test/units/testsuite-29.service | 8 - test/units/testsuite-29.sh | 293 ----- test/units/testsuite-30.service | 8 - test/units/testsuite-30.sh | 29 - test/units/testsuite-31.service | 8 - test/units/testsuite-31.sh | 10 - test/units/testsuite-32.service | 9 - test/units/testsuite-32.sh | 36 - test/units/testsuite-34.service | 8 - test/units/testsuite-34.sh | 160 --- test/units/testsuite-35.service | 8 - test/units/testsuite-35.sh | 660 ---------- test/units/testsuite-36.service | 8 - test/units/testsuite-36.sh | 352 ------ test/units/testsuite-38-sleep.service | 3 - test/units/testsuite-38.service | 7 - test/units/testsuite-38.sh | 301 ----- test/units/testsuite-43.service | 10 - test/units/testsuite-43.sh | 143 --- test/units/testsuite-44.service | 12 - test/units/testsuite-44.sh | 18 - test/units/testsuite-45.service | 8 - test/units/testsuite-45.sh | 408 ------ test/units/testsuite-46.service | 13 - test/units/testsuite-46.sh | 319 ----- test/units/testsuite-50.service | 8 - test/units/testsuite-50.sh | 718 ----------- test/units/testsuite-52.service | 7 - test/units/testsuite-52.sh | 11 - test/units/testsuite-53.service | 8 - test/units/testsuite-53.sh | 31 - test/units/testsuite-54.service | 8 - test/units/testsuite-54.sh | 319 ----- test/units/testsuite-55-testbloat.service | 10 - test/units/testsuite-55-testchill.service | 8 - test/units/testsuite-55-testmunch.service | 8 - test/units/testsuite-55-workload.slice | 11 - test/units/testsuite-55.service | 10 - test/units/testsuite-55.sh | 182 --- test/units/testsuite-58.service | 7 - test/units/testsuite-58.sh | 1307 -------------------- test/units/testsuite-59.service | 7 - test/units/testsuite-59.sh | 160 --- test/units/testsuite-60.service | 8 - test/units/testsuite-60.sh | 308 ----- test/units/testsuite-62-1.service | 9 - test/units/testsuite-62-2.service | 10 - test/units/testsuite-62-3.service | 10 - test/units/testsuite-62-4.service | 10 - test/units/testsuite-62-5.service | 11 - test/units/testsuite-62.service | 8 - test/units/testsuite-62.sh | 63 - test/units/testsuite-63.service | 8 - test/units/testsuite-63.sh | 125 -- test/units/testsuite-64.service | 8 - test/units/testsuite-64.sh | 1192 ------------------ test/units/testsuite-65.service | 8 - test/units/testsuite-65.sh | 909 -------------- test/units/testsuite-66-deviceisolation.service | 10 - test/units/testsuite-66.service | 8 - test/units/testsuite-66.sh | 24 - test/units/testsuite-67.service | 9 - test/units/testsuite-67.sh | 121 -- test/units/testsuite-68.service | 7 - test/units/testsuite-68.sh | 216 ---- test/units/testsuite-69.service | 7 - test/units/testsuite-70.creds.sh | 16 - test/units/testsuite-70.cryptenroll.sh | 84 -- test/units/testsuite-70.cryptsetup.sh | 226 ---- test/units/testsuite-70.measure.sh | 130 -- test/units/testsuite-70.pcrextend.sh | 124 -- test/units/testsuite-70.pcrlock.sh | 146 --- test/units/testsuite-70.service | 7 - test/units/testsuite-70.sh | 11 - test/units/testsuite-70.tpm2-setup.sh | 27 - test/units/testsuite-71.service | 8 - test/units/testsuite-71.sh | 228 ---- test/units/testsuite-72.service | 8 - test/units/testsuite-72.sh | 278 ----- test/units/testsuite-73.service | 8 - test/units/testsuite-73.sh | 693 ----------- test/units/testsuite-74.battery-check.sh | 9 - test/units/testsuite-74.bootctl.sh | 266 ---- test/units/testsuite-74.busctl.sh | 110 -- test/units/testsuite-74.cgls.sh | 27 - test/units/testsuite-74.cgtop.sh | 32 - test/units/testsuite-74.coredump.sh | 221 ---- test/units/testsuite-74.delta.sh | 59 - test/units/testsuite-74.escape.sh | 108 -- test/units/testsuite-74.firstboot.sh | 197 --- test/units/testsuite-74.id128.sh | 50 - test/units/testsuite-74.machine-id-setup.sh | 77 -- test/units/testsuite-74.modules-load.sh | 88 -- test/units/testsuite-74.mount.sh | 151 --- test/units/testsuite-74.networkctl.sh | 86 -- test/units/testsuite-74.path.sh | 89 -- test/units/testsuite-74.pstore.sh | 258 ---- test/units/testsuite-74.run.sh | 236 ---- test/units/testsuite-74.service | 8 - test/units/testsuite-74.sh | 11 - test/units/testsuite-74.varlinkctl.sh | 89 -- test/units/testsuite-75.service | 8 - test/units/testsuite-75.sh | 731 ----------- test/units/testsuite-76.service | 8 - test/units/testsuite-76.sh | 39 - test/units/testsuite-77-client.sh | 14 - test/units/testsuite-77-run.sh | 14 - test/units/testsuite-77-server.socket | 6 - test/units/testsuite-77-server@.service | 7 - test/units/testsuite-77.service | 10 - test/units/testsuite-77.sh | 38 - test/units/testsuite-78.service | 7 - test/units/testsuite-78.sh | 35 - test/units/testsuite-79.service | 8 - test/units/testsuite-79.sh | 58 - test/units/testsuite-80.service | 8 - test/units/testsuite-80.sh | 126 -- test/units/testsuite-81.debug-generator.sh | 105 -- test/units/testsuite-81.environment-d-generator.sh | 80 -- test/units/testsuite-81.fstab-generator.sh | 406 ------ test/units/testsuite-81.getty-generator.sh | 89 -- test/units/testsuite-81.run-generator.sh | 76 -- test/units/testsuite-81.service | 8 - test/units/testsuite-81.sh | 11 - test/units/testsuite-81.system-update-generator.sh | 38 - test/units/testsuite-82.service | 11 - test/units/testsuite-82.sh | 223 ---- test/units/testsuite-83.service | 8 - test/units/testsuite-83.sh | 25 - test/units/testsuite-84.service | 9 - test/units/testsuite-84.sh | 26 - test/units/unit-with-multiple-dashes.service | 2 +- test/units/util.sh | 166 ++- 502 files changed, 30050 insertions(+), 25868 deletions(-) create mode 100755 test/units/TEST-01-BASIC.sh create mode 100755 test/units/TEST-02-UNITTESTS.sh create mode 100755 test/units/TEST-03-JOBS.sh create mode 100755 test/units/TEST-04-JOURNAL.LogFilterPatterns.sh create mode 100755 test/units/TEST-04-JOURNAL.SYSTEMD_JOURNAL_COMPRESS.sh create mode 100755 test/units/TEST-04-JOURNAL.bsod.sh create mode 100755 test/units/TEST-04-JOURNAL.cat.sh create mode 100755 test/units/TEST-04-JOURNAL.corrupted-journals.sh create mode 100755 test/units/TEST-04-JOURNAL.fss.sh create mode 100755 test/units/TEST-04-JOURNAL.journal-append.sh create mode 100755 test/units/TEST-04-JOURNAL.journal-corrupt.sh create mode 100755 test/units/TEST-04-JOURNAL.journal-gatewayd.sh create mode 100755 test/units/TEST-04-JOURNAL.journal-remote.sh create mode 100755 test/units/TEST-04-JOURNAL.journal.sh create mode 100755 test/units/TEST-04-JOURNAL.sh create mode 100755 test/units/TEST-05-RLIMITS.effective-limit.sh create mode 100755 test/units/TEST-05-RLIMITS.rlimit.sh create mode 100755 test/units/TEST-05-RLIMITS.sh create mode 100755 test/units/TEST-06-SELINUX.sh create mode 100755 test/units/TEST-07-PID1.aux-scope.sh create mode 100755 test/units/TEST-07-PID1.exec-context.sh create mode 100755 test/units/TEST-07-PID1.exec-deserialization.sh create mode 100755 test/units/TEST-07-PID1.exec-timestamps.sh create mode 100755 test/units/TEST-07-PID1.issue-14566.sh create mode 100755 test/units/TEST-07-PID1.issue-16115.sh create mode 100755 test/units/TEST-07-PID1.issue-1981.sh create mode 100755 test/units/TEST-07-PID1.issue-2467.sh create mode 100755 test/units/TEST-07-PID1.issue-27953.sh create mode 100755 test/units/TEST-07-PID1.issue-30412.sh create mode 100755 test/units/TEST-07-PID1.issue-3166.sh create mode 100755 test/units/TEST-07-PID1.issue-3171.sh create mode 100755 test/units/TEST-07-PID1.main-PID-change.sh create mode 100755 test/units/TEST-07-PID1.mount-invalid-chars.sh create mode 100755 test/units/TEST-07-PID1.poll-limit.sh create mode 100755 test/units/TEST-07-PID1.pr-31351.sh create mode 100755 test/units/TEST-07-PID1.private-network.sh create mode 100755 test/units/TEST-07-PID1.sh create mode 100755 test/units/TEST-07-PID1.socket-pass-fds.sh create mode 100755 test/units/TEST-07-PID1.type-exec-parallel.sh create mode 100755 test/units/TEST-08-INITRD.sh create mode 100755 test/units/TEST-09-REBOOT.journal.sh create mode 100755 test/units/TEST-09-REBOOT.sh create mode 100755 test/units/TEST-13-NSPAWN.importctl.sh create mode 100755 test/units/TEST-13-NSPAWN.machinectl.sh create mode 100755 test/units/TEST-13-NSPAWN.nspawn-oci.sh create mode 100755 test/units/TEST-13-NSPAWN.nspawn.sh create mode 100755 test/units/TEST-13-NSPAWN.nss-mymachines.sh create mode 100755 test/units/TEST-13-NSPAWN.sh create mode 100755 test/units/TEST-15-DROPIN.sh create mode 100755 test/units/TEST-16-EXTEND-TIMEOUT.sh create mode 100755 test/units/TEST-17-UDEV.00.sh create mode 100755 test/units/TEST-17-UDEV.01.sh create mode 100755 test/units/TEST-17-UDEV.02.sh create mode 100755 test/units/TEST-17-UDEV.03.sh create mode 100755 test/units/TEST-17-UDEV.04.sh create mode 100755 test/units/TEST-17-UDEV.05.sh create mode 100755 test/units/TEST-17-UDEV.06.sh create mode 100755 test/units/TEST-17-UDEV.07.sh create mode 100755 test/units/TEST-17-UDEV.08.sh create mode 100755 test/units/TEST-17-UDEV.09.sh create mode 100755 test/units/TEST-17-UDEV.10.sh create mode 100755 test/units/TEST-17-UDEV.11.sh create mode 100755 test/units/TEST-17-UDEV.12.sh create mode 100755 test/units/TEST-17-UDEV.13.sh create mode 100755 test/units/TEST-17-UDEV.credentials.sh create mode 100755 test/units/TEST-17-UDEV.link-property.sh create mode 100755 test/units/TEST-17-UDEV.sh create mode 100755 test/units/TEST-18-FAILUREACTION.sh create mode 100755 test/units/TEST-19-CGROUP.ExitType-cgroup.sh create mode 100755 test/units/TEST-19-CGROUP.cleanup-slice.sh create mode 100755 test/units/TEST-19-CGROUP.delegate.sh create mode 100755 test/units/TEST-19-CGROUP.sh create mode 100755 test/units/TEST-21-DFUZZER.sh create mode 100755 test/units/TEST-22-TMPFILES.01.sh create mode 100755 test/units/TEST-22-TMPFILES.02.sh create mode 100755 test/units/TEST-22-TMPFILES.03.sh create mode 100755 test/units/TEST-22-TMPFILES.04.sh create mode 100755 test/units/TEST-22-TMPFILES.05.sh create mode 100755 test/units/TEST-22-TMPFILES.06.sh create mode 100755 test/units/TEST-22-TMPFILES.07.sh create mode 100755 test/units/TEST-22-TMPFILES.08.sh create mode 100755 test/units/TEST-22-TMPFILES.09.sh create mode 100755 test/units/TEST-22-TMPFILES.10.sh create mode 100755 test/units/TEST-22-TMPFILES.11.sh create mode 100755 test/units/TEST-22-TMPFILES.12.sh create mode 100755 test/units/TEST-22-TMPFILES.13.sh create mode 100755 test/units/TEST-22-TMPFILES.14.sh create mode 100755 test/units/TEST-22-TMPFILES.15.sh create mode 100755 test/units/TEST-22-TMPFILES.16.sh create mode 100755 test/units/TEST-22-TMPFILES.17.sh create mode 100755 test/units/TEST-22-TMPFILES.18.sh create mode 100755 test/units/TEST-22-TMPFILES.19.sh create mode 100755 test/units/TEST-22-TMPFILES.sh create mode 100755 test/units/TEST-23-UNIT-FILE-openfile-child.sh create mode 100755 test/units/TEST-23-UNIT-FILE-short-lived.sh create mode 100755 test/units/TEST-23-UNIT-FILE.ExecReload.sh create mode 100755 test/units/TEST-23-UNIT-FILE.ExecStopPost.sh create mode 100755 test/units/TEST-23-UNIT-FILE.JoinsNamespaceOf.sh create mode 100755 test/units/TEST-23-UNIT-FILE.RuntimeDirectoryPreserve.sh create mode 100755 test/units/TEST-23-UNIT-FILE.StandardOutput.sh create mode 100755 test/units/TEST-23-UNIT-FILE.Upholds.sh create mode 100755 test/units/TEST-23-UNIT-FILE.clean-unit.sh create mode 100755 test/units/TEST-23-UNIT-FILE.exec-command-ex.sh create mode 100755 test/units/TEST-23-UNIT-FILE.oneshot-restart.sh create mode 100755 test/units/TEST-23-UNIT-FILE.openfile.sh create mode 100755 test/units/TEST-23-UNIT-FILE.percentj-wantedby.sh create mode 100755 test/units/TEST-23-UNIT-FILE.runtime-bind-paths.sh create mode 100755 test/units/TEST-23-UNIT-FILE.sh create mode 100755 test/units/TEST-23-UNIT-FILE.start-stop-no-reload.sh create mode 100755 test/units/TEST-23-UNIT-FILE.statedir.sh create mode 100755 test/units/TEST-23-UNIT-FILE.success-failure.sh create mode 100755 test/units/TEST-23-UNIT-FILE.type-exec.sh create mode 100755 test/units/TEST-23-UNIT-FILE.utmp.sh create mode 100755 test/units/TEST-23-UNIT-FILE.verify-unit-files.sh create mode 100755 test/units/TEST-23-UNIT-FILE.whoami.sh create mode 100755 test/units/TEST-24-CRYPTSETUP.sh create mode 100755 test/units/TEST-25-IMPORT.sh create mode 100755 test/units/TEST-26-SYSTEMCTL.sh create mode 100755 test/units/TEST-29-PORTABLE.sh create mode 100755 test/units/TEST-30-ONCLOCKCHANGE.sh create mode 100755 test/units/TEST-31-DEVICE-ENUMERATION.sh create mode 100755 test/units/TEST-32-OOMPOLICY.sh create mode 100755 test/units/TEST-34-DYNAMICUSERMIGRATE.sh create mode 100755 test/units/TEST-35-LOGIN.sh create mode 100755 test/units/TEST-36-NUMAPOLICY.sh create mode 100644 test/units/TEST-38-FREEZER-sleep.service create mode 100755 test/units/TEST-38-FREEZER.sh create mode 100755 test/units/TEST-43-PRIVATEUSER-UNPRIV.sh create mode 100755 test/units/TEST-44-LOG-NAMESPACE.sh create mode 100755 test/units/TEST-45-TIMEDATE.sh create mode 100755 test/units/TEST-46-HOMED.sh create mode 100755 test/units/TEST-50-DISSECT.DDI.sh create mode 100755 test/units/TEST-50-DISSECT.dissect.sh create mode 100755 test/units/TEST-50-DISSECT.mountfsd.sh create mode 100755 test/units/TEST-50-DISSECT.sh create mode 100755 test/units/TEST-50-DISSECT.sysext.sh create mode 100755 test/units/TEST-52-HONORFIRSTSHUTDOWN.sh create mode 100755 test/units/TEST-53-ISSUE-16347.sh create mode 100755 test/units/TEST-54-CREDS.sh create mode 100644 test/units/TEST-55-OOMD-testbloat.service create mode 100644 test/units/TEST-55-OOMD-testchill.service create mode 100644 test/units/TEST-55-OOMD-testmunch.service create mode 100644 test/units/TEST-55-OOMD-workload.slice create mode 100755 test/units/TEST-55-OOMD.sh create mode 100755 test/units/TEST-58-REPART.sh create mode 100755 test/units/TEST-59-RELOADING-RESTART.sh create mode 100755 test/units/TEST-60-MOUNT-RATELIMIT.sh create mode 100644 test/units/TEST-62-RESTRICT-IFACES-1.service create mode 100644 test/units/TEST-62-RESTRICT-IFACES-2.service create mode 100644 test/units/TEST-62-RESTRICT-IFACES-3.service create mode 100644 test/units/TEST-62-RESTRICT-IFACES-4.service create mode 100644 test/units/TEST-62-RESTRICT-IFACES-5.service create mode 100644 test/units/TEST-62-RESTRICT-IFACES-6.service create mode 100755 test/units/TEST-62-RESTRICT-IFACES.sh create mode 100755 test/units/TEST-63-PATH.sh create mode 100755 test/units/TEST-64-UDEV-STORAGE.sh create mode 100755 test/units/TEST-65-ANALYZE.sh create mode 100644 test/units/TEST-66-DEVICE-ISOLATION-device-isolation.service create mode 100755 test/units/TEST-66-DEVICE-ISOLATION.sh create mode 100755 test/units/TEST-67-INTEGRITY.sh create mode 100755 test/units/TEST-68-PROPAGATE-EXIT-STATUS.sh create mode 100755 test/units/TEST-69-SHUTDOWN.py create mode 100755 test/units/TEST-70-TPM2.creds.sh create mode 100755 test/units/TEST-70-TPM2.cryptenroll.sh create mode 100755 test/units/TEST-70-TPM2.cryptsetup.sh create mode 100755 test/units/TEST-70-TPM2.measure.sh create mode 100755 test/units/TEST-70-TPM2.pcrextend.sh create mode 100755 test/units/TEST-70-TPM2.pcrlock.sh create mode 100755 test/units/TEST-70-TPM2.sh create mode 100755 test/units/TEST-70-TPM2.tpm2-setup.sh create mode 100755 test/units/TEST-71-HOSTNAME.sh create mode 100755 test/units/TEST-72-SYSUPDATE.sh create mode 100755 test/units/TEST-73-LOCALE.sh create mode 100755 test/units/TEST-74-AUX-UTILS.battery-check.sh create mode 100755 test/units/TEST-74-AUX-UTILS.bootctl.sh create mode 100755 test/units/TEST-74-AUX-UTILS.busctl.sh create mode 100755 test/units/TEST-74-AUX-UTILS.capsule.sh create mode 100755 test/units/TEST-74-AUX-UTILS.cgls.sh create mode 100755 test/units/TEST-74-AUX-UTILS.cgtop.sh create mode 100755 test/units/TEST-74-AUX-UTILS.coredump.sh create mode 100755 test/units/TEST-74-AUX-UTILS.delta.sh create mode 100755 test/units/TEST-74-AUX-UTILS.escape.sh create mode 100755 test/units/TEST-74-AUX-UTILS.firstboot.sh create mode 100755 test/units/TEST-74-AUX-UTILS.id128.sh create mode 100755 test/units/TEST-74-AUX-UTILS.machine-id-setup.sh create mode 100755 test/units/TEST-74-AUX-UTILS.modules-load.sh create mode 100755 test/units/TEST-74-AUX-UTILS.mount.sh create mode 100755 test/units/TEST-74-AUX-UTILS.network-generator.sh create mode 100755 test/units/TEST-74-AUX-UTILS.networkctl.sh create mode 100755 test/units/TEST-74-AUX-UTILS.path.sh create mode 100755 test/units/TEST-74-AUX-UTILS.pstore.sh create mode 100755 test/units/TEST-74-AUX-UTILS.run.sh create mode 100755 test/units/TEST-74-AUX-UTILS.sh create mode 100755 test/units/TEST-74-AUX-UTILS.socket.sh create mode 100755 test/units/TEST-74-AUX-UTILS.ssh.sh create mode 100755 test/units/TEST-74-AUX-UTILS.varlinkctl.sh create mode 100755 test/units/TEST-74-AUX-UTILS.vpick.sh create mode 100755 test/units/TEST-75-RESOLVED.sh create mode 100755 test/units/TEST-76-SYSCTL.sh create mode 100755 test/units/TEST-78-SIGQUEUE.sh create mode 100755 test/units/TEST-79-MEMPRESS.sh create mode 100755 test/units/TEST-80-NOTIFYACCESS.sh create mode 100755 test/units/TEST-81-GENERATORS.debug-generator.sh create mode 100755 test/units/TEST-81-GENERATORS.environment-d-generator.sh create mode 100755 test/units/TEST-81-GENERATORS.fstab-generator.sh create mode 100755 test/units/TEST-81-GENERATORS.getty-generator.sh create mode 100755 test/units/TEST-81-GENERATORS.run-generator.sh create mode 100755 test/units/TEST-81-GENERATORS.sh create mode 100755 test/units/TEST-81-GENERATORS.system-update-generator.sh create mode 100755 test/units/TEST-82-SOFTREBOOT.sh create mode 100755 test/units/TEST-83-BTRFS.sh create mode 100755 test/units/TEST-84-STORAGETM.sh delete mode 100644 test/units/testsuite-01.service delete mode 100755 test/units/testsuite-01.sh delete mode 100644 test/units/testsuite-02.service delete mode 100755 test/units/testsuite-02.sh delete mode 100644 test/units/testsuite-03.service delete mode 100755 test/units/testsuite-03.sh delete mode 100755 test/units/testsuite-04.LogFilterPatterns.sh delete mode 100755 test/units/testsuite-04.SYSTEMD_JOURNAL_COMPRESS.sh delete mode 100755 test/units/testsuite-04.bsod.sh delete mode 100755 test/units/testsuite-04.corrupted-journals.sh delete mode 100755 test/units/testsuite-04.fss.sh delete mode 100755 test/units/testsuite-04.journal-append.sh delete mode 100755 test/units/testsuite-04.journal-corrupt.sh delete mode 100755 test/units/testsuite-04.journal-gatewayd.sh delete mode 100755 test/units/testsuite-04.journal-remote.sh delete mode 100755 test/units/testsuite-04.journal.sh delete mode 100644 test/units/testsuite-04.service delete mode 100755 test/units/testsuite-04.sh delete mode 100644 test/units/testsuite-05.service delete mode 100755 test/units/testsuite-05.sh delete mode 100644 test/units/testsuite-06.service delete mode 100755 test/units/testsuite-06.sh delete mode 100755 test/units/testsuite-07.exec-context.sh delete mode 100755 test/units/testsuite-07.issue-14566.sh delete mode 100755 test/units/testsuite-07.issue-16115.sh delete mode 100755 test/units/testsuite-07.issue-1981.sh delete mode 100755 test/units/testsuite-07.issue-2467.sh delete mode 100755 test/units/testsuite-07.issue-27953.sh delete mode 100755 test/units/testsuite-07.issue-30412.sh delete mode 100755 test/units/testsuite-07.issue-3166.sh delete mode 100755 test/units/testsuite-07.issue-3171.sh delete mode 100755 test/units/testsuite-07.main-PID-change.sh delete mode 100755 test/units/testsuite-07.mount-invalid-chars.sh delete mode 100755 test/units/testsuite-07.poll-limit.sh delete mode 100755 test/units/testsuite-07.private-network.sh delete mode 100644 test/units/testsuite-07.service delete mode 100755 test/units/testsuite-07.sh delete mode 100644 test/units/testsuite-08.service delete mode 100755 test/units/testsuite-08.sh delete mode 100755 test/units/testsuite-09.journal.sh delete mode 100644 test/units/testsuite-09.service delete mode 100755 test/units/testsuite-09.sh delete mode 100755 test/units/testsuite-13.machinectl.sh delete mode 100755 test/units/testsuite-13.nspawn-oci.sh delete mode 100755 test/units/testsuite-13.nspawn.sh delete mode 100755 test/units/testsuite-13.nss-mymachines.sh delete mode 100644 test/units/testsuite-13.service delete mode 100755 test/units/testsuite-13.sh delete mode 100644 test/units/testsuite-15.service delete mode 100755 test/units/testsuite-15.sh delete mode 100644 test/units/testsuite-16.service delete mode 100755 test/units/testsuite-16.sh delete mode 100755 test/units/testsuite-17.00.sh delete mode 100755 test/units/testsuite-17.01.sh delete mode 100755 test/units/testsuite-17.02.sh delete mode 100755 test/units/testsuite-17.03.sh delete mode 100755 test/units/testsuite-17.04.sh delete mode 100755 test/units/testsuite-17.05.sh delete mode 100755 test/units/testsuite-17.06.sh delete mode 100755 test/units/testsuite-17.07.sh delete mode 100755 test/units/testsuite-17.08.sh delete mode 100755 test/units/testsuite-17.09.sh delete mode 100755 test/units/testsuite-17.10.sh delete mode 100755 test/units/testsuite-17.11.sh delete mode 100755 test/units/testsuite-17.12.sh delete mode 100755 test/units/testsuite-17.13.sh delete mode 100644 test/units/testsuite-17.service delete mode 100755 test/units/testsuite-17.sh delete mode 100644 test/units/testsuite-18.service delete mode 100755 test/units/testsuite-18.sh delete mode 100755 test/units/testsuite-19.ExitType-cgroup.sh delete mode 100755 test/units/testsuite-19.cleanup-slice.sh delete mode 100755 test/units/testsuite-19.delegate.sh delete mode 100644 test/units/testsuite-19.service delete mode 100755 test/units/testsuite-19.sh delete mode 100644 test/units/testsuite-21.service delete mode 100755 test/units/testsuite-21.sh delete mode 100755 test/units/testsuite-22.01.sh delete mode 100755 test/units/testsuite-22.02.sh delete mode 100755 test/units/testsuite-22.03.sh delete mode 100755 test/units/testsuite-22.04.sh delete mode 100755 test/units/testsuite-22.05.sh delete mode 100755 test/units/testsuite-22.06.sh delete mode 100755 test/units/testsuite-22.07.sh delete mode 100755 test/units/testsuite-22.08.sh delete mode 100755 test/units/testsuite-22.09.sh delete mode 100755 test/units/testsuite-22.10.sh delete mode 100755 test/units/testsuite-22.11.sh delete mode 100755 test/units/testsuite-22.12.sh delete mode 100755 test/units/testsuite-22.13.sh delete mode 100755 test/units/testsuite-22.14.sh delete mode 100755 test/units/testsuite-22.15.sh delete mode 100755 test/units/testsuite-22.16.sh delete mode 100755 test/units/testsuite-22.17.sh delete mode 100644 test/units/testsuite-22.service delete mode 100755 test/units/testsuite-22.sh delete mode 100755 test/units/testsuite-23-short-lived.sh delete mode 100755 test/units/testsuite-23.ExecReload.sh delete mode 100755 test/units/testsuite-23.ExecStopPost.sh delete mode 100755 test/units/testsuite-23.JoinsNamespaceOf.sh delete mode 100755 test/units/testsuite-23.RuntimeDirectoryPreserve.sh delete mode 100755 test/units/testsuite-23.StandardOutput.sh delete mode 100755 test/units/testsuite-23.Upholds.sh delete mode 100755 test/units/testsuite-23.clean-unit.sh delete mode 100755 test/units/testsuite-23.exec-command-ex.sh delete mode 100755 test/units/testsuite-23.oneshot-restart.sh delete mode 100755 test/units/testsuite-23.percentj-wantedby.sh delete mode 100755 test/units/testsuite-23.runtime-bind-paths.sh delete mode 100644 test/units/testsuite-23.service delete mode 100755 test/units/testsuite-23.sh delete mode 100755 test/units/testsuite-23.start-stop-no-reload.sh delete mode 100755 test/units/testsuite-23.statedir.sh delete mode 100755 test/units/testsuite-23.success-failure.sh delete mode 100755 test/units/testsuite-23.type-exec.sh delete mode 100755 test/units/testsuite-23.utmp.sh delete mode 100755 test/units/testsuite-23.whoami.sh delete mode 100644 test/units/testsuite-24.service delete mode 100755 test/units/testsuite-24.sh delete mode 100644 test/units/testsuite-25.service delete mode 100755 test/units/testsuite-25.sh delete mode 100644 test/units/testsuite-26.service delete mode 100755 test/units/testsuite-26.sh delete mode 100644 test/units/testsuite-29.service delete mode 100755 test/units/testsuite-29.sh delete mode 100644 test/units/testsuite-30.service delete mode 100755 test/units/testsuite-30.sh delete mode 100644 test/units/testsuite-31.service delete mode 100755 test/units/testsuite-31.sh delete mode 100644 test/units/testsuite-32.service delete mode 100755 test/units/testsuite-32.sh delete mode 100644 test/units/testsuite-34.service delete mode 100755 test/units/testsuite-34.sh delete mode 100644 test/units/testsuite-35.service delete mode 100755 test/units/testsuite-35.sh delete mode 100644 test/units/testsuite-36.service delete mode 100755 test/units/testsuite-36.sh delete mode 100644 test/units/testsuite-38-sleep.service delete mode 100644 test/units/testsuite-38.service delete mode 100755 test/units/testsuite-38.sh delete mode 100644 test/units/testsuite-43.service delete mode 100755 test/units/testsuite-43.sh delete mode 100644 test/units/testsuite-44.service delete mode 100755 test/units/testsuite-44.sh delete mode 100644 test/units/testsuite-45.service delete mode 100755 test/units/testsuite-45.sh delete mode 100644 test/units/testsuite-46.service delete mode 100755 test/units/testsuite-46.sh delete mode 100644 test/units/testsuite-50.service delete mode 100755 test/units/testsuite-50.sh delete mode 100644 test/units/testsuite-52.service delete mode 100755 test/units/testsuite-52.sh delete mode 100644 test/units/testsuite-53.service delete mode 100755 test/units/testsuite-53.sh delete mode 100644 test/units/testsuite-54.service delete mode 100755 test/units/testsuite-54.sh delete mode 100644 test/units/testsuite-55-testbloat.service delete mode 100644 test/units/testsuite-55-testchill.service delete mode 100644 test/units/testsuite-55-testmunch.service delete mode 100644 test/units/testsuite-55-workload.slice delete mode 100644 test/units/testsuite-55.service delete mode 100755 test/units/testsuite-55.sh delete mode 100644 test/units/testsuite-58.service delete mode 100755 test/units/testsuite-58.sh delete mode 100644 test/units/testsuite-59.service delete mode 100755 test/units/testsuite-59.sh delete mode 100644 test/units/testsuite-60.service delete mode 100755 test/units/testsuite-60.sh delete mode 100644 test/units/testsuite-62-1.service delete mode 100644 test/units/testsuite-62-2.service delete mode 100644 test/units/testsuite-62-3.service delete mode 100644 test/units/testsuite-62-4.service delete mode 100644 test/units/testsuite-62-5.service delete mode 100644 test/units/testsuite-62.service delete mode 100755 test/units/testsuite-62.sh delete mode 100644 test/units/testsuite-63.service delete mode 100755 test/units/testsuite-63.sh delete mode 100644 test/units/testsuite-64.service delete mode 100755 test/units/testsuite-64.sh delete mode 100644 test/units/testsuite-65.service delete mode 100755 test/units/testsuite-65.sh delete mode 100644 test/units/testsuite-66-deviceisolation.service delete mode 100644 test/units/testsuite-66.service delete mode 100755 test/units/testsuite-66.sh delete mode 100644 test/units/testsuite-67.service delete mode 100755 test/units/testsuite-67.sh delete mode 100644 test/units/testsuite-68.service delete mode 100755 test/units/testsuite-68.sh delete mode 100644 test/units/testsuite-69.service delete mode 100755 test/units/testsuite-70.creds.sh delete mode 100755 test/units/testsuite-70.cryptenroll.sh delete mode 100755 test/units/testsuite-70.cryptsetup.sh delete mode 100755 test/units/testsuite-70.measure.sh delete mode 100755 test/units/testsuite-70.pcrextend.sh delete mode 100755 test/units/testsuite-70.pcrlock.sh delete mode 100644 test/units/testsuite-70.service delete mode 100755 test/units/testsuite-70.sh delete mode 100755 test/units/testsuite-70.tpm2-setup.sh delete mode 100644 test/units/testsuite-71.service delete mode 100755 test/units/testsuite-71.sh delete mode 100644 test/units/testsuite-72.service delete mode 100755 test/units/testsuite-72.sh delete mode 100644 test/units/testsuite-73.service delete mode 100755 test/units/testsuite-73.sh delete mode 100755 test/units/testsuite-74.battery-check.sh delete mode 100755 test/units/testsuite-74.bootctl.sh delete mode 100755 test/units/testsuite-74.busctl.sh delete mode 100755 test/units/testsuite-74.cgls.sh delete mode 100755 test/units/testsuite-74.cgtop.sh delete mode 100755 test/units/testsuite-74.coredump.sh delete mode 100755 test/units/testsuite-74.delta.sh delete mode 100755 test/units/testsuite-74.escape.sh delete mode 100755 test/units/testsuite-74.firstboot.sh delete mode 100755 test/units/testsuite-74.id128.sh delete mode 100755 test/units/testsuite-74.machine-id-setup.sh delete mode 100755 test/units/testsuite-74.modules-load.sh delete mode 100755 test/units/testsuite-74.mount.sh delete mode 100755 test/units/testsuite-74.networkctl.sh delete mode 100755 test/units/testsuite-74.path.sh delete mode 100755 test/units/testsuite-74.pstore.sh delete mode 100755 test/units/testsuite-74.run.sh delete mode 100644 test/units/testsuite-74.service delete mode 100755 test/units/testsuite-74.sh delete mode 100755 test/units/testsuite-74.varlinkctl.sh delete mode 100644 test/units/testsuite-75.service delete mode 100755 test/units/testsuite-75.sh delete mode 100644 test/units/testsuite-76.service delete mode 100755 test/units/testsuite-76.sh delete mode 100755 test/units/testsuite-77-client.sh delete mode 100755 test/units/testsuite-77-run.sh delete mode 100644 test/units/testsuite-77-server.socket delete mode 100644 test/units/testsuite-77-server@.service delete mode 100644 test/units/testsuite-77.service delete mode 100755 test/units/testsuite-77.sh delete mode 100644 test/units/testsuite-78.service delete mode 100755 test/units/testsuite-78.sh delete mode 100644 test/units/testsuite-79.service delete mode 100755 test/units/testsuite-79.sh delete mode 100644 test/units/testsuite-80.service delete mode 100755 test/units/testsuite-80.sh delete mode 100755 test/units/testsuite-81.debug-generator.sh delete mode 100755 test/units/testsuite-81.environment-d-generator.sh delete mode 100755 test/units/testsuite-81.fstab-generator.sh delete mode 100755 test/units/testsuite-81.getty-generator.sh delete mode 100755 test/units/testsuite-81.run-generator.sh delete mode 100644 test/units/testsuite-81.service delete mode 100755 test/units/testsuite-81.sh delete mode 100755 test/units/testsuite-81.system-update-generator.sh delete mode 100644 test/units/testsuite-82.service delete mode 100755 test/units/testsuite-82.sh delete mode 100644 test/units/testsuite-83.service delete mode 100755 test/units/testsuite-83.sh delete mode 100644 test/units/testsuite-84.service delete mode 100755 test/units/testsuite-84.sh (limited to 'test/units') diff --git a/test/units/TEST-01-BASIC.sh b/test/units/TEST-01-BASIC.sh new file mode 100755 index 0000000..bb3ff2f --- /dev/null +++ b/test/units/TEST-01-BASIC.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# Check if the colored --version output behaves correctly +SYSTEMD_COLORS=256 systemctl --version + +# Check if we properly differentiate between a full systemd setup and a "light" +# version of it that's done during daemon-reexec +# +# See: https://github.com/systemd/systemd/issues/27106 +if systemd-detect-virt -q --container; then + # We initialize /run/systemd/container only during a full setup + test -e /run/systemd/container + cp -afv /run/systemd/container /tmp/container + rm -fv /run/systemd/container + systemctl daemon-reexec + test ! -e /run/systemd/container + cp -afv /tmp/container /run/systemd/container +else + # We bring the loopback netdev up only during a full setup, so it should + # not get brought back up during reexec if we disable it beforehand + [[ "$(ip -o link show lo)" =~ LOOPBACK,UP ]] + ip link set lo down + [[ "$(ip -o link show lo)" =~ state\ DOWN ]] + systemctl daemon-reexec + [[ "$(ip -o link show lo)" =~ state\ DOWN ]] + ip link set lo up + + # We also disable coredumps only during a full setup + sysctl -w kernel.core_pattern=dont-overwrite-me + systemctl daemon-reexec + diff <(echo dont-overwrite-me) <(sysctl --values kernel.core_pattern) +fi + +# Collect failed units & do one daemon-reload to a basic sanity check +systemctl --state=failed --no-legend --no-pager | tee /failed +test ! -s /failed +systemctl daemon-reload + +# Check that the early setup is actually skipped on reexec. +# If the early setup is done more than once, then several timestamps, +# e.g. SecurityStartTimestamp, are re-initialized, and causes an ABRT +# of systemd-analyze blame. See issue #27187. +systemd-analyze blame + +# Test for 'systemd-update-utmp runlevel' vs 'systemctl daemon-reexec'. +# See issue #27163. +# shellcheck disable=SC2034 +if [[ -x /usr/lib/systemd/systemd-update-utmp ]]; then + for _ in {0..10}; do + systemctl daemon-reexec & + pid_reexec=$! + # shellcheck disable=SC2034 + for _ in {0..10}; do + SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-update-utmp runlevel + done + wait "$pid_reexec" + done +fi + +touch /testok diff --git a/test/units/TEST-02-UNITTESTS.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/TEST-03-JOBS.sh b/test/units/TEST-03-JOBS.sh new file mode 100755 index 0000000..115b941 --- /dev/null +++ b/test/units/TEST-03-JOBS.sh @@ -0,0 +1,169 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +# Simple test for that daemon-reexec works in container. +# See: https://github.com/systemd/systemd/pull/23883 +systemctl daemon-reexec + +# Test merging of a --job-mode=ignore-dependencies job into a previously +# installed job. + +systemctl start --no-block hello-after-sleep.target + +systemctl list-jobs >/root/list-jobs.txt +until grep 'sleep\.service.*running' /root/list-jobs.txt; do + systemctl list-jobs >/root/list-jobs.txt +done + +grep 'hello\.service.*waiting' /root/list-jobs.txt + +# This is supposed to finish quickly, not wait for sleep to finish. +START_SEC=$(date -u '+%s') +systemctl start --job-mode=ignore-dependencies hello +END_SEC=$(date -u '+%s') +ELAPSED=$((END_SEC-START_SEC)) + +test "$ELAPSED" -lt 3 + +# sleep should still be running, hello not. +systemctl list-jobs >/root/list-jobs.txt +grep 'sleep\.service.*running' /root/list-jobs.txt +grep 'hello\.service' /root/list-jobs.txt && exit 1 +systemctl stop sleep.service hello-after-sleep.target + +# Some basic testing that --show-transaction does something useful +(! systemctl is-active systemd-importd) +systemctl -T start systemd-importd +systemctl is-active systemd-importd +systemctl --show-transaction stop systemd-importd +(! systemctl is-active systemd-importd) + +# Test for a crash when enqueuing a JOB_NOP when other job already exists +systemctl start --no-block hello-after-sleep.target +# hello.service should still be waiting, so these try-restarts will collapse +# into NOPs. +systemctl try-restart --job-mode=fail hello.service +systemctl try-restart hello.service +systemctl stop hello.service sleep.service hello-after-sleep.target + +# TODO: add more job queueing/merging tests here. + +# Test that restart propagates to activating units +systemctl -T --no-block start always-activating.service +systemctl list-jobs | grep 'always-activating.service' +ACTIVATING_ID_PRE=$(systemctl show -P InvocationID always-activating.service) +systemctl -T start always-activating.socket # Wait for the socket to come up +systemctl -T restart always-activating.socket +ACTIVATING_ID_POST=$(systemctl show -P InvocationID always-activating.service) +[ "$ACTIVATING_ID_PRE" != "$ACTIVATING_ID_POST" ] || exit 1 + +# Test for irreversible jobs +systemctl start unstoppable.service + +# This is expected to fail with 'job cancelled' +systemctl stop unstoppable.service && exit 1 +# But this should succeed +systemctl stop --job-mode=replace-irreversibly unstoppable.service + +# We're going to shutdown soon. Let's see if it succeeds when +# there's an active service that tries to be unstoppable. +# Shutdown of the container/VM will hang if not. +systemctl start unstoppable.service + +# Test waiting for a started units to terminate again +cat </run/systemd/system/wait2.service +[Unit] +Description=Wait for 2 seconds +[Service] +ExecStart=sh -ec 'sleep 2' +EOF +cat </run/systemd/system/wait5fail.service +[Unit] +Description=Wait for 5 seconds and fail +[Service] +ExecStart=sh -ec 'sleep 5; false' +EOF + +# wait2 succeeds +START_SEC=$(date -u '+%s') +systemctl start --wait wait2.service +END_SEC=$(date -u '+%s') +ELAPSED=$((END_SEC-START_SEC)) +[[ "$ELAPSED" -ge 2 ]] && [[ "$ELAPSED" -le 4 ]] || exit 1 + +# wait5fail fails, so systemctl should fail +START_SEC=$(date -u '+%s') +(! systemctl start --wait wait2.service wait5fail.service) +END_SEC=$(date -u '+%s') +ELAPSED=$((END_SEC-START_SEC)) +[[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1 + +# Test time-limited scopes +START_SEC=$(date -u '+%s') +set +e +systemd-run --scope --property=RuntimeMaxSec=3s sleep 10 +RESULT=$? +END_SEC=$(date -u '+%s') +ELAPSED=$((END_SEC-START_SEC)) +[[ "$ELAPSED" -ge 3 ]] && [[ "$ELAPSED" -le 5 ]] || exit 1 +[[ "$RESULT" -ne 0 ]] || exit 1 + +# Test transactions with cycles +# Provides coverage for issues like https://github.com/systemd/systemd/issues/26872 +for i in {0..19}; do + cat >"/run/systemd/system/transaction-cycle$i.service" <"/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/TEST-04-JOURNAL.SYSTEMD_JOURNAL_COMPRESS.sh b/test/units/TEST-04-JOURNAL.SYSTEMD_JOURNAL_COMPRESS.sh new file mode 100755 index 0000000..6da9f5e --- /dev/null +++ b/test/units/TEST-04-JOURNAL.SYSTEMD_JOURNAL_COMPRESS.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# https://bugzilla.redhat.com/show_bug.cgi?id=2183546 +mkdir /run/systemd/system/systemd-journald.service.d +MACHINE_ID="$(/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 + +for c in NONE XZ LZ4 ZSTD; do + cat >/run/systemd/system/systemd-journald.service.d/compress.conf <&1 | grep -q -F 'compress=${c}'; do sleep .5; done" + + # $SYSTEMD_JOURNAL_COMPRESS= also works for journal-remote + if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then + for cc in NONE XZ LZ4 ZSTD; do + rm -f /tmp/foo.journal + SYSTEMD_JOURNAL_COMPRESS="${cc}" /usr/lib/systemd/systemd-journal-remote --split-mode=none -o /tmp/foo.journal --getter="journalctl -b -o export -t $ID" + SYSTEMD_LOG_LEVEL=debug journalctl --verify --quiet --file /tmp/foo.journal 2>&1 | grep -q -F "compress=${cc}" + journalctl -t "$ID" -o cat --file /tmp/foo.journal | grep -q -F "hoge with ${c}" + done + fi +done + +rm /run/systemd/journald.conf.d/compress.conf +rm /run/systemd/system/systemd-journald.service.d/compress.conf +systemctl daemon-reload +systemctl restart systemd-journald.service +systemctl reset-failed systemd-journald.service +journalctl --rotate diff --git a/test/units/TEST-04-JOURNAL.bsod.sh b/test/units/TEST-04-JOURNAL.bsod.sh new file mode 100755 index 0000000..83feb89 --- /dev/null +++ b/test/units/TEST-04-JOURNAL.bsod.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +if systemd-detect-virt -cq; then + echo "This test requires a VM, skipping the test" | tee --append /skipped + exit 0 +fi + +if [[ ! -x /usr/lib/systemd/systemd-bsod ]]; then + echo "systemd-bsod is not installed, skipping the test" | tee --append /skipped + exit 0 +fi + +# shellcheck disable=SC2317 +at_exit() { + local EC=$? + + if [[ $EC -ne 0 ]] && [[ -e /tmp/console.dump ]]; then + cat /tmp/console.dump + fi + + if mountpoint -q /var/log/journal; then + # In order to preserve the journal from the just run test we need to do a little dance, as + # --relinquish-var is not a "true" opposite of --flush, meaning that it won't move the existing + # journal(s) from /var/log/ to /run/log/. To do that, let's rotate the journal first, so all + # important bits are in the archived journal(s)... + journalctl --rotate + # ...then instruct sd-journald to write further entries to the runtime journal... + journalctl --relinquish-var + # ...make sure there are no outstanding writes to the persistent journal that might block us from + # unmounting the tmpfs... + journalctl --sync + # ...move the archived journals to the runtime storage... + mv -v "/var/log/journal/$(/run/systemd/system/systemd-bsod.service.d/99-override.conf +systemctl daemon-reload +systemctl start systemd-bsod +systemd-cat -p emerg echo "Service emergency message" +vcs_dump_and_check "Service emergency message" +systemctl status systemd-bsod +systemctl stop systemd-bsod + +# Wipe the journal +journalctl --vacuum-size=1 --rotate +(! journalctl -q -b -p emerg --grep .) + +# Same as above, but make sure the service responds to signals even when there are +# no "emerg" messages, see systemd/systemd#30084 +(! systemctl is-active systemd-bsod) +systemctl start systemd-bsod +timeout 5s bash -xec 'until systemctl is-active systemd-bsod; do sleep .5; done' +timeout 5s systemctl stop systemd-bsod +timeout 5s bash -xec 'while systemctl is-active systemd-bsod; do sleep .5; done' diff --git a/test/units/TEST-04-JOURNAL.cat.sh b/test/units/TEST-04-JOURNAL.cat.sh new file mode 100755 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/TEST-04-JOURNAL.corrupted-journals.sh b/test/units/TEST-04-JOURNAL.corrupted-journals.sh new file mode 100755 index 0000000..479011e --- /dev/null +++ b/test/units/TEST-04-JOURNAL.corrupted-journals.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +JOURNAL_DIR="$(mktemp -d)" +REMOTE_OUT="$(mktemp -d)" +# tar on C8S doesn't support the --zstd option +unzstd --stdout "/usr/lib/systemd/tests/testdata/test-journals/afl-corrupted-journals.tar.zst" | tar -xC "$JOURNAL_DIR/" +while read -r file; do + filename="${file##*/}" + unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}" +done < <(find /usr/lib/systemd/tests/testdata/test-journals/corrupted/ -name "*.zst") +# First, try each of them sequentially. Skip this part when running with plain +# QEMU, as it is excruciatingly slow +# Note: we care only about exit code 124 (timeout) and special bash exit codes +# >124 (like signals) +if [[ "$(systemd-detect-virt -v)" != "qemu" ]]; then + while read -r file; do + timeout 10 journalctl --file="$file" --boot >/dev/null || [[ $? -lt 124 ]] + timeout 10 journalctl --file="$file" --verify >/dev/null || [[ $? -lt 124 ]] + timeout 10 journalctl --file="$file" --output=export >/dev/null || [[ $? -lt 124 ]] + timeout 10 journalctl --file="$file" --fields >/dev/null || [[ $? -lt 124 ]] + timeout 10 journalctl --file="$file" --list-boots >/dev/null || [[ $? -lt 124 ]] + if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then + timeout 10 /usr/lib/systemd/systemd-journal-remote \ + --getter="journalctl --file=$file --output=export" \ + --split-mode=none \ + --output="$REMOTE_OUT/system.journal" || [[ $? -lt 124 ]] + timeout 10 journalctl --directory="$REMOTE_OUT" >/dev/null || [[ $? -lt 124 ]] + rm -f "$REMOTE_OUT"/* + fi + done < <(find "$JOURNAL_DIR" -type f) +fi +# And now all at once +timeout 30 journalctl --directory="$JOURNAL_DIR" --boot >/dev/null || [[ $? -lt 124 ]] +timeout 30 journalctl --directory="$JOURNAL_DIR" --verify >/dev/null || [[ $? -lt 124 ]] +timeout 30 journalctl --directory="$JOURNAL_DIR" --output=export >/dev/null || [[ $? -lt 124 ]] +timeout 30 journalctl --directory="$JOURNAL_DIR" --fields >/dev/null || [[ $? -lt 124 ]] +timeout 30 journalctl --directory="$JOURNAL_DIR" --list-boots >/dev/null || [[ $? -lt 124 ]] +if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then + timeout 30 /usr/lib/systemd/systemd-journal-remote \ + --getter="journalctl --directory=$JOURNAL_DIR --output=export" \ + --split-mode=none \ + --output="$REMOTE_OUT/system.journal" || [[ $? -lt 124 ]] + timeout 30 journalctl --directory="$REMOTE_OUT" >/dev/null || [[ $? -lt 124 ]] + rm -f "$REMOTE_OUT"/* +fi diff --git a/test/units/TEST-04-JOURNAL.fss.sh b/test/units/TEST-04-JOURNAL.fss.sh new file mode 100755 index 0000000..03351b8 --- /dev/null +++ b/test/units/TEST-04-JOURNAL.fss.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# Forward Secure Sealing + +if ! journalctl --version | grep -qF +GCRYPT; then + echo "Built without gcrypt, skipping the FSS tests" + exit 0 +fi + +journalctl --force --setup-keys --interval=2 |& tee /tmp/fss +FSS_VKEY="$(sed -rn '/([a-f0-9]{6}\-){3}[a-f0-9]{6}\/[a-f0-9]+\-[a-f0-9]+/p' /tmp/fss)" +[[ -n "$FSS_VKEY" ]] + +# Generate some buzz in the journal and wait until the FSS key is changed +# at least once +systemd-cat cat /etc/os-release +sleep 4 +# Seal the journal +journalctl --rotate +# Verification should fail without a valid FSS key +(! journalctl --verify) +(! journalctl --verify --verify-key="") +(! journalctl --verify --verify-key="000000-000000-000000-000000/00000000-00000") +# FIXME: ignore --verify result until #27532 is resolved +journalctl --verify --verify-key="$FSS_VKEY" || : + +# Sealing + systemd-journal-remote +/usr/lib/systemd/systemd-journal-remote --getter="journalctl -n 5 -o export" \ + --split-mode=none \ + --seal=yes \ + --output=/tmp/sealed.journal +(! journalctl --file=/tmp/sealed.journal --verify) +(! journalctl --file=/tmp/sealed.journal --verify --verify-key="") +(! journalctl --file=/tmp/sealed.journal --verify --verify-key="000000-000000-000000-000000/00000000-00000") +# FIXME: ignore --verify result until #27532 is resolved +journalctl --file=/tmp/sealed.journal --verify --verify-key="$FSS_VKEY" || : +rm -f /tmp/sealed.journal + +# Return back to a journal without FSS +rm -fv "/var/log/journal/$("$LOG_FILE" +grep -qF "Journal" "$LOG_FILE" +curl -LSfs http://localhost:19531/browse >"$LOG_FILE" +grep -qF "Journal" "$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 <"$LOG_FILE" +grep -qF "Journal" "$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 "Journal" "$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 "Journal" "$LOG_FILE" + +# Nuke the journal file +mv "$GATEWAYD_FILE" "$GATEWAYD_FILE.bak" +(! curl --fail-with-body -L http://localhost:19531/fields/_PID) +mv "$GATEWAYD_FILE.bak" "$GATEWAYD_FILE" +curl -LSfs http://localhost:19531/fields/_PID + +systemctl stop test-gatewayd.{socket,service} +rm -f "$GATEWAYD_FILE" diff --git a/test/units/TEST-04-JOURNAL.journal-remote.sh b/test/units/TEST-04-JOURNAL.journal-remote.sh new file mode 100755 index 0000000..c7b99b1 --- /dev/null +++ b/test/units/TEST-04-JOURNAL.journal-remote.sh @@ -0,0 +1,230 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2016 +set -eux +set -o pipefail + +if [[ ! -x /usr/lib/systemd/systemd-journal-remote || ! -x /usr/lib/systemd/systemd-journal-upload ]]; then + echo "Built without systemd-journal-remote/upload support, skipping the test" + exit 0 +fi + +if ! command -v openssl >/dev/null; then + echo "openssl command not available, skipping the tests" + exit 0 +fi + +at_exit() { + set +e + + systemctl stop systemd-journal-upload + systemctl stop systemd-journal-remote.{socket,service} + # Remove any remote journals on exit, so we don't try to export them together + # with the local journals, causing a mess + rm -rf /var/log/journal/remote +} + +trap at_exit EXIT + +TEST_MESSAGE="-= This is a test message $RANDOM =-" +TEST_TAG="$(systemd-id128 new)" + +echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG" +journalctl --sync + +/usr/lib/systemd/systemd-journal-remote --version +/usr/lib/systemd/systemd-journal-remote --help +/usr/lib/systemd/systemd-journal-upload --version +/usr/lib/systemd/systemd-journal-upload --help + +# Generate a self-signed certificate for systemd-journal-remote +# +# Note: older OpenSSL requires a config file with some extra options, unfortunately +# Note2: /run here is used on purpose, since the systemd-journal-remote service uses PrivateTmp=yes +mkdir -p /run/systemd/journal-remote-tls +cat >/tmp/openssl.conf </run/systemd/journal-remote.conf.d/99-test.conf </run/systemd/journal-upload.conf.d/99-test.conf </run/systemd/remote-pki/ca.conf </run/systemd/remote-pki/client.conf </run/systemd/remote-pki/server.conf </run/systemd/remote-pki/ca.srl +# Generate a client key and signing request +openssl req -nodes -newkey rsa:2048 -sha256 \ + -config /run/systemd/remote-pki/client.conf \ + -keyout /run/systemd/remote-pki/client.key \ + -out /run/systemd/remote-pki/client.csr +# Sign the request with the CA key +openssl x509 -req -days 7 \ + -in /run/systemd/remote-pki/client.csr \ + -CA /run/systemd/remote-pki/ca.crt \ + -CAkey /run/systemd/remote-pki/ca.key \ + -out /run/systemd/remote-pki/client.crt +# And do the same for the server +openssl req -nodes -newkey rsa:2048 -sha256 \ + -config /run/systemd/remote-pki/server.conf \ + -keyout /run/systemd/remote-pki/server.key \ + -out /run/systemd/remote-pki/server.csr +openssl x509 -req -days 7 \ + -in /run/systemd/remote-pki/server.csr \ + -CA /run/systemd/remote-pki/ca.crt \ + -CAkey /run/systemd/remote-pki/ca.key \ + -out /run/systemd/remote-pki/server.crt +chown -R systemd-journal-remote:systemd-journal /run/systemd/remote-pki +chmod -R g+rwX /run/systemd/remote-pki + +# Reconfigure journal-upload/journal remote with the new keys +cat >/run/systemd/journal-remote.conf.d/99-test.conf </run/systemd/journal-upload.conf.d/99-test.conf </run/systemd/journal-upload.conf.d/99-test.conf </run/systemd/system/systemd-journal-upload.service.d/99-test.conf <\n<6>\n<7>\n" "" --level-prefix true +# Remove trailing spaces +write_and_match "Trailing spaces \t \n" "Trailing spaces\n" --level-prefix false +write_and_match "<5>Trailing spaces \t \n" "Trailing spaces\n" --level-prefix true +# Don't remove leading spaces +write_and_match " \t Leading spaces\n" " \t Leading spaces\n" --level-prefix false +write_and_match "<5> \t Leading spaces\n" " \t Leading spaces\n" --level-prefix true + +# --output-fields restricts output +ID="$(systemd-id128 new)" +echo -ne "foo" | systemd-cat -t "$ID" --level-prefix false +# Let's test varlinkctl a bit, i.e. implement the equivalent of 'journalctl --sync' via varlinkctl +varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Synchronize '{}' +journalctl -b -o export --output-fields=MESSAGE,FOO --output-fields=PRIORITY,MESSAGE -t "$ID" >/tmp/output +[[ $(wc -l /dev/null + +# -b always behaves like -b0 +journalctl -q -b-1 -b0 | head -1 >/tmp/expected +journalctl -q -b-1 -b | head -1 >/tmp/output +diff /tmp/expected /tmp/output +# ... even when another option follows (both of these should fail due to -m) +{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 >/tmp/expected +{ journalctl -ball -b -m 2>&1 || :; } | head -1 >/tmp/output +diff /tmp/expected /tmp/output + +# https://github.com/systemd/systemd/issues/13708 +ID=$(systemd-id128 new) +systemd-cat -t "$ID" bash -c 'echo parent; (echo child) & wait' & +PID=$! +wait $PID +journalctl --sync +# We can drop this grep when https://github.com/systemd/systemd/issues/13937 +# has a fix. +journalctl -b -o export -t "$ID" --output-fields=_PID | grep '^_PID=' >/tmp/output +[[ $(wc -l /tmp/expected +systemd-cat -t "$ID" /bin/sh -c 'env echo -n "This will";echo;env echo -n "usually fail";echo;env echo -n "and be truncated";echo;' +journalctl --sync +journalctl -b -o cat -t "$ID" >/tmp/output +diff /tmp/expected /tmp/output +[[ $(journalctl -b -o cat -t "$ID" --output-fields=_TRANSPORT | grep -Pc "^stdout$") -eq 3 ]] +[[ $(journalctl -b -o cat -t "$ID" --output-fields=_LINE_BREAK | grep -Pc "^pid-change$") -eq 3 ]] +[[ $(journalctl -b -o cat -t "$ID" --output-fields=_PID | sort -u | grep -c "^.*$") -eq 3 ]] +[[ $(journalctl -b -o cat -t "$ID" --output-fields=MESSAGE | grep -Pc "^(This will|usually fail|and be truncated)$") -eq 3 ]] + +# test that LogLevelMax can also suppress logging about services, not only by services +systemctl start silent-success +[[ -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 +journalctl -b -n 1 /bin/true + /bin/false +journalctl -b -n 1 -r --unit "systemd*" + +systemd-run --user -M "testuser@.host" /bin/echo hello +journalctl --sync +journalctl -b -n 1 -r --user-unit "*" + +(! journalctl -b /dev/lets-hope-this-doesnt-exist) +(! journalctl -b /dev/null /dev/zero /dev/this-also-shouldnt-exist) +(! journalctl -b --unit "this-unit-should-not-exist*") + +# Facilities & priorities +journalctl --facility help +journalctl --facility help | grep -F 'kern' +journalctl --facility help | grep -F 'mail' +journalctl --facility kern -n 1 +journalctl --facility syslog --priority 0..3 -n 1 +journalctl --facility syslog --priority 3..0 -n 1 +journalctl --facility user --priority 0..0 -n 1 +journalctl --facility daemon --priority warning -n 1 +journalctl --facility daemon --priority warning..info -n 1 +journalctl --facility daemon --priority notice..crit -n 1 +journalctl --facility daemon --priority 5..crit -n 1 + +# Assorted combinations +journalctl -o help +journalctl -o help | grep -F 'short' +journalctl -o help | grep -F 'export' +journalctl -q -n all -a | grep . >/dev/null +journalctl -q --no-full | grep . >/dev/null +journalctl -q --user --system | grep . >/dev/null +journalctl --namespace "*" | grep . >/dev/null +journalctl --namespace "" | grep . >/dev/null +journalctl -q --namespace "+foo-bar-baz-$RANDOM" | grep . >/dev/null +(! journalctl -q --namespace "foo-bar-baz-$RANDOM" | grep .) +journalctl --root / | grep . >/dev/null +journalctl --cursor "t=0;t=-1;t=0;t=0x0" | grep . >/dev/null +journalctl --header | grep system.journal +journalctl --field _EXE | grep . >/dev/null +journalctl --no-hostname --utc --catalog | grep . >/dev/null +# Exercise executable_is_script() and the related code, e.g. `journalctl -b /path/to/a/script.sh` should turn +# into ((_EXE=/bin/bash AND _COMM=script.sh) AND _BOOT_ID=c002e3683ba14fa8b6c1e12878386514) +journalctl -b "$(readlink -f "$0")" | grep . >/dev/null +journalctl -b "$(systemd-id128 boot-id)" | grep . >/dev/null +journalctl --since yesterday --reverse | grep . >/dev/null +journalctl --machine .host | grep . >/dev/null +# Log something that journald will forward to wall +echo "Oh no!" | systemd-cat -t "emerg$RANDOM" -p emerg --stderr-priority emerg + +TAG="$(systemd-id128 new)" +echo "Foo Bar Baz" | systemd-cat -t "$TAG" +journalctl --sync +# Relevant excerpt from journalctl(1): +# If the pattern is all lowercase, matching is case insensitive. Otherwise, matching is case sensitive. +# This can be overridden with the --case-sensitive option +journalctl -e -t "$TAG" --grep "Foo Bar Baz" +journalctl -e -t "$TAG" --grep "foo bar baz" +(! journalctl -e -t "$TAG" --grep "foo Bar baz") +journalctl -e -t "$TAG" --case-sensitive=false --grep "foo Bar baz" + +(! journalctl --facility hopefully-an-unknown-facility) +(! journalctl --priority hello-world) +(! journalctl --priority 0..128) +(! journalctl --priority 0..systemd) + +# Other options +journalctl --disk-usage +journalctl --dmesg -n 1 +journalctl --fields +journalctl --list-boots +journalctl --update-catalog +journalctl --list-catalog + +# Add new tests before here, the journald restarts below +# may make tests flappy. + +# Don't lose streams on restart +systemctl start forever-print-hola +sleep 3 +systemctl restart systemd-journald +sleep 3 +systemctl stop forever-print-hola +[[ ! -f "/tmp/i-lose-my-logs" ]] + +# https://github.com/systemd/systemd/issues/4408 +rm -f /tmp/i-lose-my-logs +systemctl start forever-print-hola +sleep 3 +systemctl kill --signal=SIGKILL systemd-journald +sleep 3 +[[ ! -f "/tmp/i-lose-my-logs" ]] +systemctl stop forever-print-hola + +set +o pipefail +# https://github.com/systemd/systemd/issues/15528 +journalctl --follow --file=/var/log/journal/*/* | head -n1 | grep . +# https://github.com/systemd/systemd/issues/24565 +journalctl --follow --merge | head -n1 | grep . +set -o pipefail + +# https://github.com/systemd/systemd/issues/26746 +rm -f /tmp/issue-26746-log /tmp/issue-26746-cursor +ID="$(systemd-id128 new)" +journalctl -t "$ID" --follow --cursor-file=/tmp/issue-26746-cursor | tee /tmp/issue-26746-log & +systemd-cat -t "$ID" /bin/sh -c 'echo hogehoge' +# shellcheck disable=SC2016 +timeout 10 bash -c 'until [[ -f /tmp/issue-26746-log && "$(cat /tmp/issue-26746-log)" =~ hogehoge ]]; do sleep .5; done' +pkill -TERM journalctl +timeout 10 bash -c 'until test -f /tmp/issue-26746-cursor; do sleep .5; done' +CURSOR_FROM_FILE="$(cat /tmp/issue-26746-cursor)" +CURSOR_FROM_JOURNAL="$(journalctl -t "$ID" --output=export MESSAGE=hogehoge | sed -n -e '/__CURSOR=/ { s/__CURSOR=//; p }')" +test "$CURSOR_FROM_FILE" = "$CURSOR_FROM_JOURNAL" + +# Check that the seqnum field at least superficially works +systemd-cat echo "ya" +journalctl --sync +SEQNUM1=$(journalctl -o export -n 1 | grep -Ea "^__SEQNUM=" | cut -d= -f2) +systemd-cat echo "yo" +journalctl --sync +SEQNUM2=$(journalctl -o export -n 1 | grep -Ea "^__SEQNUM=" | cut -d= -f2) +test "$SEQNUM2" -gt "$SEQNUM1" + +# Test for journals without RTC +# See: https://github.com/systemd/systemd/issues/662 +JOURNAL_DIR="$(mktemp -d)" +while read -r file; do + filename="${file##*/}" + unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}" +done < <(find /usr/lib/systemd/tests/testdata/test-journals/no-rtc -name "*.zst") + +journalctl --directory="$JOURNAL_DIR" --list-boots --output=json >/tmp/lb1 +diff -u /tmp/lb1 - <<'EOF' +[{"index":-3,"boot_id":"5ea5fc4f82a14186b5332a788ef9435e","first_entry":1666569600994371,"last_entry":1666584266223608},{"index":-2,"boot_id":"bea6864f21ad4c9594c04a99d89948b0","first_entry":1666569601005945,"last_entry":1666584347230411},{"index":-1,"boot_id":"4c708e1fd0744336be16f3931aa861fb","first_entry":1666569601017222,"last_entry":1666584354649355},{"index":0,"boot_id":"35e8501129134edd9df5267c49f744a4","first_entry":1666569601009823,"last_entry":1666584438086856}] +EOF +rm -rf "$JOURNAL_DIR" /tmp/lb1 + +# Check that using --after-cursor/--cursor-file= together with journal filters doesn't +# skip over entries matched by the filter +# See: https://github.com/systemd/systemd/issues/30288 +UNIT_NAME="test-cursor-$RANDOM.service" +CURSOR_FILE="$(mktemp)" +# Generate some messages we can match against +journalctl --cursor-file="$CURSOR_FILE" -n1 +systemd-run --unit="$UNIT_NAME" --wait --service-type=exec -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 +[[ "$(journalctl -n 1 -p info -o cat --unit="$UNIT_NAME" --after-cursor="$(<"$CURSOR_FILE")" _PID=1 )" =~ ^Starting\ ]] +# There should be no such messages before the cursor +[[ -z "$(journalctl -n 1 -p info -o cat --unit="$UNIT_NAME" --after-cursor="$(<"$CURSOR_FILE")" --reverse)" ]] +# --cursor-file= + a journal filter +diff <(journalctl --cursor-file="$CURSOR_FILE" -p info -o cat _SYSTEMD_UNIT="$UNIT_NAME") - </run/systemd/system/"$pre"alpha.slice </run/systemd/system/"$pre"alpha-beta.slice </run/systemd/system/"$pre"alpha-beta-gamma.slice <$P/rlimits.conf <>/skipped + exit 77 +fi + +# Note: ATTOW the following checks should work with both Fedora and upstream reference policy +# (with or without MCS/MLS) + +sestatus + +# We should end up in permissive mode +[[ "$(getenforce)" == "Permissive" ]] + +# Check PID 1's context +PID1_CONTEXT="$(ps -h -o label 1)" +[[ "$PID1_CONTEXT" =~ ^system_u:system_r:init_t(:s0)?$ ]] +# The same label should be attached to all PID 1's journal messages +journalctl -q -b -p info -n 5 --grep . _SELINUX_CONTEXT="$PID1_CONTEXT" + +# Check context on a couple of arbitrarily-selected files/directories +[[ "$(stat --printf %C /run/systemd/journal/)" =~ ^system_u:object_r:(syslogd_runtime_t|syslogd_var_run_t)(:s0)?$ ]] +[[ "$(stat --printf %C /run/systemd/notify)" =~ ^system_u:object_r:(init_runtime_t|init_var_run_t)(:s0)?$ ]] +[[ "$(stat --printf %C /run/systemd/sessions/)" =~ ^system_u:object_r:(systemd_sessions_runtime_t|systemd_logind_sessions_t)(:s0)?$ ]] + +# Check if our SELinux-related functionality works +# +# Since the SELinux policies vary wildly, use a context from some existing file +# as our test context +CONTEXT="$(stat -c %C /proc/sys/kernel/core_pattern)" + +[[ "$(systemd-run --wait --pipe -p SELinuxContext="$CONTEXT" cat /proc/self/attr/current | tr -d '\0')" == "$CONTEXT" ]] +(! systemd-run --wait --pipe -p SELinuxContext="foo:bar:baz" cat /proc/self/attr/current) +(! systemd-run --wait --pipe -p ConditionSecurity='selinux' false) +systemd-run --wait --pipe -p ConditionSecurity='!selinux' false + +NSPAWN_ARGS=(systemd-nspawn -q --volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id) +[[ "$("${NSPAWN_ARGS[@]}" cat /proc/self/attr/current | tr -d '\0')" != "$CONTEXT" ]] +[[ "$("${NSPAWN_ARGS[@]}" --selinux-context="$CONTEXT" cat /proc/self/attr/current | tr -d '\0')" == "$CONTEXT" ]] +[[ "$("${NSPAWN_ARGS[@]}" stat --printf %C /run)" != "$CONTEXT" ]] +[[ "$("${NSPAWN_ARGS[@]}" --selinux-apifs-context="$CONTEXT" stat --printf %C /run)" == "$CONTEXT" ]] +[[ "$("${NSPAWN_ARGS[@]}" --selinux-apifs-context="$CONTEXT" --tmpfs=/tmp stat --printf %C /tmp)" == "$CONTEXT" ]] + +touch /testok diff --git a/test/units/TEST-07-PID1.aux-scope.sh b/test/units/TEST-07-PID1.aux-scope.sh new file mode 100755 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/TEST-07-PID1.exec-context.sh b/test/units/TEST-07-PID1.exec-context.sh new file mode 100755 index 0000000..a3379ef --- /dev/null +++ b/test/units/TEST-07-PID1.exec-context.sh @@ -0,0 +1,386 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2016 +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +# Make sure the unit's exec context matches its configuration +# See: https://github.com/systemd/systemd/pull/29552 + +# Even though hidepid= was introduced in kernel 3.3, we support only +# the post 5.8 implementation that allows us to apply the option per-instance, +# instead of the whole namespace. To distinguish between these two implementations +# lets check if we can mount procfs with a named value (e.g. hidepid=off), since +# support for this was introduced in the same commit as the per-instance stuff +proc_supports_option() { + local option="${1:?}" + local proc_tmp ec + + proc_tmp="$(mktemp -d)" + mount -t proc -o "$option" proc "$proc_tmp" && ec=0 || ec=$? + mountpoint -q "$proc_tmp" && umount -q "$proc_tmp" + rm -rf "$proc_tmp" + + return $ec +} + +# In coverage builds we disable ProtectSystem= and ProtectHome= via a service.d +# dropin in /etc. This dropin has, unfortunately, higher priority than +# the transient stuff from systemd-run. Let's just skip the following tests +# in that case instead of complicating the test setup even more */ +if [[ -z "${COVERAGE_BUILD_DIR:-}" ]]; then + if ! systemd-detect-virt -cq && command -v bootctl >/dev/null; then + boot_path="$(bootctl --print-boot-path)" + esp_path="$(bootctl --print-esp-path)" + + # If the mount points are handled by automount units, make sure we trigger + # them before proceeding further + ls -l "$boot_path" "$esp_path" + fi + + systemd-run --wait --pipe -p ProtectSystem=yes \ + bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test -w /etc; test -w /var" + systemd-run --wait --pipe -p ProtectSystem=full \ + bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test ! -w /etc; test -w /var" + systemd-run --wait --pipe -p ProtectSystem=strict \ + bash -xec "test ! -w /; test ! -w /etc; test ! -w /var; test -w /dev; test -w /proc" + systemd-run --wait --pipe -p ProtectSystem=no \ + bash -xec "test -w /; test -w /etc; test -w /var; test -w /dev; test -w /proc" + + MARK="$(mktemp /root/.exec-context.XXX)" + systemd-run --wait --pipe -p ProtectHome=yes \ + bash -xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test ! -e $MARK" + systemd-run --wait --pipe -p ProtectHome=read-only \ + bash -xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test -e $MARK" + systemd-run --wait --pipe -p ProtectHome=tmpfs \ + bash -xec "test -w /home; test -w /root; test -w /run/user; test ! -e $MARK" + systemd-run --wait --pipe -p ProtectHome=no \ + bash -xec "test -w /home; test -w /root; test -w /run/user; test -e $MARK" + rm -f "$MARK" +fi + +if proc_supports_option "hidepid=off"; then + systemd-run --wait --pipe -p ProtectProc=noaccess -p User=testuser \ + bash -xec 'test -e /proc/1; test ! -r /proc/1; test -r /proc/$$$$/comm' + systemd-run --wait --pipe -p ProtectProc=invisible -p User=testuser \ + bash -xec 'test ! -e /proc/1; test -r /proc/$$$$/comm' + systemd-run --wait --pipe -p ProtectProc=ptraceable -p User=testuser \ + bash -xec 'test ! -e /proc/1; test -r /proc/$$$$/comm' + systemd-run --wait --pipe -p ProtectProc=ptraceable -p User=testuser -p AmbientCapabilities=CAP_SYS_PTRACE \ + bash -xec 'test -r /proc/1; test -r /proc/$$$$/comm' + systemd-run --wait --pipe -p ProtectProc=default -p User=testuser \ + bash -xec 'test -r /proc/1; test -r /proc/$$$$/comm' +fi + +if proc_supports_option "subset=pid"; then + systemd-run --wait --pipe -p ProcSubset=pid -p User=testuser \ + bash -xec "test -r /proc/1/comm; test ! -e /proc/cpuinfo" + systemd-run --wait --pipe -p ProcSubset=all -p User=testuser \ + bash -xec "test -r /proc/1/comm; test -r /proc/cpuinfo" +fi + +if ! systemd-detect-virt -cq; then + systemd-run --wait --pipe -p ProtectKernelLogs=yes -p User=testuser \ + bash -xec "test ! -r /dev/kmsg" + systemd-run --wait --pipe -p ProtectKernelLogs=no -p User=testuser \ + bash -xec "test -r /dev/kmsg" +fi + +systemd-run --wait --pipe -p BindPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \ + bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; ! mountpoint /usr" +systemd-run --wait --pipe -p BindReadOnlyPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \ + bash -xec "test ! -w /etc; test ! -w /mnt; ! mountpoint /usr" +# Make sure we properly serialize/deserialize paths with spaces +# See: https://github.com/systemd/systemd/issues/30747 +touch "/tmp/test file with spaces" +systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces" \ + bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/test file with spaces'" +systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces:/tmp/destination\ wi\:th\ spaces" \ + bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/destination wi:th spaces'" + +# Check if we correctly serialize, deserialize, and set directives that +# have more complex internal handling +if ! systemd-detect-virt -cq; then + # Funny detail: this originally used the underlying rootfs device, but that, + # for some reason, caused "divide error" in kernel, followed by a kernel panic + TEMPFILE="$(mktemp)" + LODEV="$(losetup --show -f "$TEMPFILE")" + ROOT_DEV_MAJ_MIN="$(lsblk -nro MAJ:MIN "$LODEV")" + EXPECTED_IO_MAX="$ROOT_DEV_MAJ_MIN rbps=1000 wbps=1000000000000 riops=2000000000 wiops=4000" + EXPECTED_IO_LATENCY="$ROOT_DEV_MAJ_MIN target=69000" + SERVICE_NAME="test-io-directives-$RANDOM.service" + CGROUP_PATH="/sys/fs/cgroup/system.slice/$SERVICE_NAME" + + # IO*= + ARGUMENTS=( + # Throw in a couple of invalid entries just to test things out + -p IOReadBandwidthMax="/foo/bar 1M" + -p IOReadBandwidthMax="/foo/baz 1M" + -p IOReadBandwidthMax="$LODEV 1M" + -p IOReadBandwidthMax="$LODEV 1K" + -p IOWriteBandwidthMax="$LODEV 1G" + -p IOWriteBandwidthMax="$LODEV 1T" + -p IOReadIOPSMax="$LODEV 2G" + -p IOWriteIOPSMax="$LODEV 4K" + -p IODeviceLatencyTargetSec="$LODEV 666ms" + -p IODeviceLatencyTargetSec="/foo/bar 69ms" + -p IODeviceLatencyTargetSec="$LODEV 69ms" + -p IOReadBandwidthMax="/foo/bar 1M" + -p IOReadBandwidthMax="/foo/baz 1M" + # TODO: IODeviceWeight= doesn't work on loop devices and virtual disks + -p IODeviceWeight="$LODEV 999" + -p IODeviceWeight="/foo/bar 999" + ) + + systemctl set-property system.slice IOAccounting=yes + # io.latency not available by default on Debian stable + if [[ -e /sys/fs/cgroup/system.slice/io.latency ]]; then + systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \ + bash -xec "diff <(echo $EXPECTED_IO_MAX) $CGROUP_PATH/io.max; diff <(echo $EXPECTED_IO_LATENCY) $CGROUP_PATH/io.latency" + fi + + # CPUScheduling= + ARGUMENTS=( + -p CPUSchedulingPolicy=rr # ID: 2 + -p CPUSchedulingPolicy=fifo # ID: 1 + -p CPUSchedulingPriority=5 # Actual prio: 94 (99 - prio) + -p CPUSchedulingPriority=10 # Actual prio: 89 (99 - prio) + ) + + systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \ + bash -xec 'grep -E "^policy\s*:\s*1$" /proc/self/sched; grep -E "^prio\s*:\s*89$" /proc/self/sched' + + # Device*= + ARGUMENTS=( + -p DevicePolicy=closed + -p DevicePolicy=strict + -p DeviceAllow="char-mem rm" # Allow read & mknod for /dev/{null,zero,...} + -p DeviceAllow="$LODEV rw" + -p DeviceAllow="$LODEV w" # Allow write for the loop + # Everything else should be disallowed per the strict policy + ) + + systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \ + bash -xec "test -r /dev/null; test ! -w /dev/null; test ! -r $LODEV; test -w $LODEV; test ! -r /dev/tty; test ! -w /dev/tty" + + if ! systemctl --version | grep -qF -- "-BPF_FRAMEWORK"; then + # SocketBind*= + ARGUMENTS=( + -p SocketBindAllow= + -p SocketBindAllow=1234 + -p SocketBindAllow=ipv4:udp:any + -p SocketBindAllow=ipv6:6666 + # Everything but the last assignment is superfluous, but it still exercises + # the parsing machinery + -p SocketBindDeny= + -p SocketBindDeny=1111 + -p SocketBindDeny=ipv4:1111 + -p SocketBindDeny=ipv4:any + -p SocketBindDeny=ipv4:tcp:any + -p SocketBindDeny=ipv4:udp:10000-11000 + -p SocketBindDeny=ipv6:1111 + -p SocketBindDeny=any + ) + + # We should fail with EPERM when trying to bind to a socket not on the allow list + # (nc exits with 2 in that case) + systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \ + bash -xec 'timeout 1s nc -l 127.0.0.1 9999; exit 42' + systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \ + bash -xec 'timeout 1s nc -l ::1 9999; exit 42' + systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \ + bash -xec 'timeout 1s nc -6 -u -l ::1 9999; exit 42' + systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \ + bash -xec 'timeout 1s nc -4 -l 127.0.0.1 6666; exit 42' + systemd-run --wait -p SuccessExitStatus="1 2" --pipe -p SocketBindDeny=any \ + bash -xec 'timeout 1s nc -l 127.0.0.1 9999; exit 42' + # Consequently, we should succeed when binding to a socket on the allow list + # and keep listening on it until we're killed by `timeout` (EC 124) + systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \ + bash -xec 'timeout 1s nc -4 -l 127.0.0.1 1234; exit 1' + systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \ + bash -xec 'timeout 1s nc -4 -u -l 127.0.0.1 5678; exit 1' + systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \ + bash -xec 'timeout 1s nc -6 -l ::1 1234; exit 1' + systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \ + bash -xec 'timeout 1s nc -6 -l ::1 6666; exit 1' + fi + + losetup -d "$LODEV" + rm -f "$TEMPFILE" +fi + +# {Cache,Configuration,Logs,Runtime,State}Directory= +ARGUMENTS=( + -p CacheDirectory="foo/bar/baz also\ with\ spaces" + -p CacheDirectory="foo" + -p CacheDirectory="context" + -p CacheDirectoryMode="0123" + -p CacheDirectoryMode="0666" + -p ConfigurationDirectory="context/foo also_context/bar context/nested/baz context/semi\:colon" + -p ConfigurationDirectoryMode="0400" + -p LogsDirectory="context/foo" + -p LogsDirectory="" + -p LogsDirectory="context/a/very/nested/logs/dir" + -p RuntimeDirectory="context/with\ spaces" + # Note: {Runtime,State,Cache,Logs}Directory= directives support the directory:symlink syntax, which + # requires an additional level of escaping for the colon character + -p RuntimeDirectory="also_context:a\ symlink\ with\ \\\:\ col\\\:ons\ and\ \ spaces" + -p RuntimeDirectoryPreserve=yes + -p StateDirectory="context" + -p StateDirectory="./././././././context context context" + -p StateDirectoryMode="0000" +) + +rm -rf /run/context +systemd-run --wait --pipe "${ARGUMENTS[@]}" \ + bash -xec '[[ $CACHE_DIRECTORY == "/var/cache/also with spaces:/var/cache/context:/var/cache/foo:/var/cache/foo/bar/baz" ]]; + [[ $(stat -c "%a" "${CACHE_DIRECTORY##*:}") == 666 ]]' +systemd-run --wait --pipe "${ARGUMENTS[@]}" \ + bash -xec '[[ $CONFIGURATION_DIRECTORY == /etc/also_context/bar:/etc/context/foo:/etc/context/nested/baz:/etc/context/semi:colon ]]; + [[ $(stat -c "%a" "${CONFIGURATION_DIRECTORY%%:*}") == 400 ]]' +systemd-run --wait --pipe "${ARGUMENTS[@]}" \ + bash -xec '[[ $LOGS_DIRECTORY == /var/log/context/a/very/nested/logs/dir:/var/log/context/foo ]]; + [[ $(stat -c "%a" "${LOGS_DIRECTORY##*:}") == 755 ]]' +systemd-run --wait --pipe "${ARGUMENTS[@]}" \ + bash -xec '[[ $RUNTIME_DIRECTORY == "/run/also_context:/run/context/with spaces" ]]; + [[ $(stat -c "%a" "${RUNTIME_DIRECTORY##*:}") == 755 ]]; + [[ $(stat -c "%a" "${RUNTIME_DIRECTORY%%:*}") == 755 ]]' +systemd-run --wait --pipe "${ARGUMENTS[@]}" \ + bash -xec '[[ $STATE_DIRECTORY == /var/lib/context ]]; [[ $(stat -c "%a" $STATE_DIRECTORY) == 0 ]]' +test -d "/run/context/with spaces" +test -s "/run/a symlink with : col:ons and spaces" +rm -rf /var/{cache,lib,log}/context /etc/{also_,}context + +# Limit*= +# +# Note: keep limits of LimitDATA= and LimitAS= unlimited, otherwise ASan (LSan) +# won't be able to mmap the shadow maps +ARGUMENTS=( + -p LimitCPU=15 + -p LimitCPU=10:15 # ulimit -t + -p LimitFSIZE=96G # ulimit -f + -p LimitDATA=8T:infinity + -p LimitDATA=infinity # ulimit -d + -p LimitSTACK=8M # ulimit -s + -p LimitCORE=infinity + -p LimitCORE=17M # ulimit -c + -p LimitRSS=27G # ulimit -m + -p LimitNOFILE=7:127 # ulimit -n + -p LimitAS=infinity # ulimit -v + -p LimitNPROC=1 + -p LimitNPROC=64:infinity # ulimit -u + -p LimitMEMLOCK=37M # ulimit -l + -p LimitLOCKS=19:1021 # ulimit -x + -p LimitSIGPENDING=21 # ulimit -i + -p LimitMSGQUEUE=666 # ulimit -q + -p LimitNICE=4 # ulimit -e + -p LimitRTPRIO=8 # ulimit -r + -p LimitRTTIME=666666 # ulimit -R +) +# Do all the checks in one giant inline shell blob to avoid the overhead of spawning +# a new service for each check +# +# Note: ulimit shows storage-related values in 1024-byte increments* +# Note2: ulimit -R requires bash >= 5.1 +# +# * in POSIX mode -c a -f options show values in 512-byte increments; let's hope +# we never run in the POSIX mode +systemd-run --wait --pipe "${ARGUMENTS[@]}" \ + bash -xec 'KB=1; MB=$((KB * 1024)); GB=$((MB * 1024)); TB=$((GB * 1024)); + : CPU; [[ $(ulimit -St) -eq 10 ]]; [[ $(ulimit -Ht) -eq 15 ]]; + : FSIZE; [[ $(ulimit -Sf) -eq $((96 * GB)) ]]; [[ $(ulimit -Hf) -eq $((96 * GB)) ]]; + : DATA; [[ $(ulimit -Sd) == unlimited ]]; [[ $(ulimit -Hd) == unlimited ]]; + : STACK; [[ $(ulimit -Ss) -eq $((8 * MB)) ]]; [[ $(ulimit -Hs) -eq $((8 * MB)) ]]; + : CORE; [[ $(ulimit -Sc) -eq $((17 * MB)) ]]; [[ $(ulimit -Hc) -eq $((17 * MB)) ]]; + : RSS; [[ $(ulimit -Sm) -eq $((27 * GB)) ]]; [[ $(ulimit -Hm) -eq $((27 * GB)) ]]; + : NOFILE; [[ $(ulimit -Sn) -eq 7 ]]; [[ $(ulimit -Hn) -eq 127 ]]; + : AS; [[ $(ulimit -Sv) == unlimited ]]; [[ $(ulimit -Hv) == unlimited ]]; + : NPROC; [[ $(ulimit -Su) -eq 64 ]]; [[ $(ulimit -Hu) == unlimited ]]; + : MEMLOCK; [[ $(ulimit -Sl) -eq $((37 * MB)) ]]; [[ $(ulimit -Hl) -eq $((37 * MB)) ]]; + : LOCKS; [[ $(ulimit -Sx) -eq 19 ]]; [[ $(ulimit -Hx) -eq 1021 ]]; + : SIGPENDING; [[ $(ulimit -Si) -eq 21 ]]; [[ $(ulimit -Hi) -eq 21 ]]; + : MSGQUEUE; [[ $(ulimit -Sq) -eq 666 ]]; [[ $(ulimit -Hq) -eq 666 ]]; + : NICE; [[ $(ulimit -Se) -eq 4 ]]; [[ $(ulimit -He) -eq 4 ]]; + : RTPRIO; [[ $(ulimit -Sr) -eq 8 ]]; [[ $(ulimit -Hr) -eq 8 ]]; + ulimit -R || exit 0; + : RTTIME; [[ $(ulimit -SR) -eq 666666 ]]; [[ $(ulimit -HR) -eq 666666 ]];' + +# RestrictFileSystems= +# +# Note: running instrumented binaries requires at least /proc to be accessible, so let's +# skip the test when we're running under sanitizers +# +# Note: $GCOV_ERROR_LOG is used during coverage runs to suppress errors when creating *.gcda files, +# since gcov can't access the restricted filesystem (as expected) +if [[ ! -v ASAN_OPTIONS ]] && systemctl --version | grep "+BPF_FRAMEWORK" && kernel_supports_lsm bpf; then + ROOTFS="$(df --output=fstype /usr/bin | sed --quiet 2p)" + systemd-run --wait --pipe -p RestrictFileSystems="" ls / + systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar" ls / + (! systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS" ls /proc) + (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="foo" ls /) + systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar baz proc" ls /proc + systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /proc + systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /sys/fs/cgroup + + systemd-run --wait --pipe -p RestrictFileSystems="~" ls / + systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls / + systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls / + (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="~$ROOTFS" ls /) + (! systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls /proc) + (! systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls /proc) + (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /proc) + (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /sys) + systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls / + (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /proc) + (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /dev) + (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /sys) +fi + +# Make sure we properly (de)serialize various string arrays, including whitespaces +# See: https://github.com/systemd/systemd/issues/31214 +systemd-run --wait --pipe -p Environment="FOO='bar4 '" \ + bash -xec '[[ $FOO == "bar4 " ]]' +systemd-run --wait --pipe -p Environment="FOO='bar4 ' BAR='\n\n'" \ + bash -xec "[[ \$FOO == 'bar4 ' && \$BAR == $'\n\n' ]]" +systemd-run --wait --pipe -p Environment='FOO="bar4 \\ "' -p Environment="BAR='\n\t'" \ + bash -xec "[[ \$FOO == 'bar4 \\ ' && \$BAR == $'\n\t' ]]" +TEST_ENV_FILE="/tmp/test-env-file-$RANDOM- " +cat >"$TEST_ENV_FILE" <"$unit_path" <>$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" <>$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" <>$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" <>$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" <>$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" <>$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" <"$unit_path" <>$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/TEST-07-PID1.issue-14566.sh b/test/units/TEST-07-PID1.issue-14566.sh new file mode 100755 index 0000000..d4be5b5 --- /dev/null +++ b/test/units/TEST-07-PID1.issue-14566.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# Test that KillMode=mixed does not leave left over processes with ExecStopPost= +# Issue: https://github.com/systemd/systemd/issues/14566 + +if [[ -n "${ASAN_OPTIONS:-}" ]]; then + # Temporarily skip this test when running with sanitizers due to a deadlock + # See: https://bugzilla.redhat.com/show_bug.cgi?id=2098125 + echo "Sanitizers detected, skipping the test..." + exit 0 +fi + +systemctl start issue14566-repro +sleep 4 +systemctl status issue14566-repro + +leaked_pid=$(cat /leakedtestpid) + +systemctl stop issue14566-repro +sleep 4 + +# Leaked PID will still be around if we're buggy. +# I personally prefer to see 42. +ps -p "$leaked_pid" && exit 42 + +exit 0 diff --git a/test/units/TEST-07-PID1.issue-16115.sh b/test/units/TEST-07-PID1.issue-16115.sh new file mode 100755 index 0000000..8f63826 --- /dev/null +++ b/test/units/TEST-07-PID1.issue-16115.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# Test ExecCondition= does not restart on abnormal or failure +# Issue: https://github.com/systemd/systemd/issues/16115 + +systemctl start issue16115-repro-1 +systemctl start issue16115-repro-2 +systemctl start issue16115-repro-3 +sleep 5 # wait a bit in case there are restarts so we can count them below + +[[ "$(systemctl show issue16115-repro-1 -P NRestarts)" == "0" ]] +[[ "$(systemctl show issue16115-repro-2 -P NRestarts)" == "0" ]] +[[ "$(systemctl show issue16115-repro-3 -P NRestarts)" == "0" ]] diff --git a/test/units/TEST-07-PID1.issue-1981.sh b/test/units/TEST-07-PID1.issue-1981.sh new file mode 100755 index 0000000..dcfa9b1 --- /dev/null +++ b/test/units/TEST-07-PID1.issue-1981.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# Segmentation fault in timer_enter_waiting while masking a unit +# Issue: https://github.com/systemd/systemd/issues/1981 + +at_exit() { + set +e + + systemctl stop my.timer my.service + rm -f /run/systemd/system/my.{service,timer} + systemctl daemon-reload +} + +trap at_exit EXIT + +mkdir -p /run/systemd/system + +cat >/run/systemd/system/my.service <<\EOF +[Service] +Type=oneshot +ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = my.timer' +ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_REALTIME_USEC"' +ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_MONOTONIC_USEC"' +ExecStart=echo Timer runs me +EOF + +cat >/run/systemd/system/my.timer </run/systemd/system/my.timer.d/override.conf </run/systemd/system/badbin_assert.service </run/systemd/system/badbin_assert.socket <$U </run/systemd/system/issue-3171@.service </tmp/test-mainpid.sh <<\EOF +#!/usr/bin/env bash + +set -eux +set -o pipefail + +# Create a number of children, and make one the main one +sleep infinity & +disown + +sleep infinity & +MAINPID=$! +disown + +sleep infinity & +disown + +echo $MAINPID >/run/mainpidsh/pid +EOF +chmod +x /tmp/test-mainpid.sh + +systemd-run --unit=test-mainpidsh.service \ + -p StandardOutput=tty \ + -p StandardError=tty \ + -p Type=forking \ + -p RuntimeDirectory=mainpidsh \ + -p PIDFile=/run/mainpidsh/pid \ + /tmp/test-mainpid.sh +test "$(systemctl show -P MainPID test-mainpidsh.service)" -eq "$(cat /run/mainpidsh/pid)" + +cat >/tmp/test-mainpid2.sh <<\EOF +#!/usr/bin/env bash + +set -eux +set -o pipefail + +# Create a number of children, and make one the main one +sleep infinity & +disown + +sleep infinity & +MAINPID=$! +disown + +sleep infinity & +disown + +echo $MAINPID >/run/mainpidsh2/pid +chown 1001:1001 /run/mainpidsh2/pid +EOF +chmod +x /tmp/test-mainpid2.sh + +systemd-run --unit=test-mainpidsh2.service \ + -p StandardOutput=tty \ + -p StandardError=tty \ + -p Type=forking \ + -p RuntimeDirectory=mainpidsh2 \ + -p PIDFile=/run/mainpidsh2/pid \ + /tmp/test-mainpid2.sh +test "$(systemctl show -P MainPID test-mainpidsh2.service)" -eq "$(cat /run/mainpidsh2/pid)" + +cat >/dev/shm/test-mainpid3.sh <>"$TMP_MOUNTINFO" +mount --bind "$TMP_MOUNTINFO" /proc/1/mountinfo +systemctl daemon-reload +# On affected versions this would throw an error: +# Failed to get properties: Bad message +systemctl status foo-mountinfo.mount + +umount /proc/1/mountinfo +systemctl daemon-reload +rm -f "$TMP_MOUNTINFO" + +# Check invalid characters in a mount unit +# +# systemd already handles this and refuses to load the invalid string, e.g.: +# foo-fstab.mount:9: String is not UTF-8 clean, ignoring assignment: What=//localhost/foo���bar +# +# a) Unit generated from /etc/fstab +[[ -e /etc/fstab ]] && cp -f /etc/fstab /tmp/fstab.bak + +LANG="C.UTF-8" printf '//localhost/foo\ufffebar /foo/fstab cifs defaults 0 0\n' >/etc/fstab +systemctl daemon-reload +[[ "$(systemctl show -P UnitFileState foo-fstab.mount)" == bad ]] + +# b) Unit generated from /etc/fstab (but the invalid character is in options) +LANG="C.UTF-8" printf '//localhost/foobar /foo/fstab/opt cifs nosuid,a\ufffeb,noexec 0 0\n' >/etc/fstab +systemctl daemon-reload +[[ "$(systemctl show -P UnitFileState foo-fstab-opt.mount)" == bad ]] +rm -f /etc/fstab + +[[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab.bak /etc/fstab +systemctl daemon-reload + +# c) Mount unit +mkdir -p /run/systemd/system +LANG="C.UTF-8" printf '[Mount]\nWhat=//localhost/foo\ufffebar\nWhere=/foo/unit\nType=cifs\nOptions=noexec\n' >/run/systemd/system/foo-unit.mount +systemctl daemon-reload +[[ "$(systemctl show -P UnitFileState foo-unit.mount)" == bad ]] +rm -f /run/systemd/system/foo-unit.mount + +# d) Mount unit (but the invalid character is in Options=) +mkdir -p /run/systemd/system +LANG="C.UTF-8" printf '[Mount]\nWhat=//localhost/foobar\nWhere=/foo/unit/opt\nType=cifs\nOptions=noexec,a\ufffeb,nosuid\n' >/run/systemd/system/foo-unit-opt.mount +systemctl daemon-reload +[[ "$(systemctl show -P UnitFileState foo-unit-opt.mount)" == bad ]] +rm -f /run/systemd/system/foo-unit-opt.mount diff --git a/test/units/TEST-07-PID1.poll-limit.sh b/test/units/TEST-07-PID1.poll-limit.sh new file mode 100755 index 0000000..ca988b2 --- /dev/null +++ b/test/units/TEST-07-PID1.poll-limit.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +systemd-analyze log-level debug + +cat >/run/systemd/system/floodme@.service </run/systemd/system/floodme.socket </run/systemd/system/nonexistent-execstart-exit-status.service < 0 )) + +systemctl stop nonexistent-execstart-exit-status.service +rm /run/systemd/system/nonexistent-execstart-exit-status.service diff --git a/test/units/TEST-07-PID1.private-network.sh b/test/units/TEST-07-PID1.private-network.sh new file mode 100755 index 0000000..37658f7 --- /dev/null +++ b/test/units/TEST-07-PID1.private-network.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# For issue https://github.com/systemd/systemd/issues/29526 +systemd-run -p PrivateNetwork=yes --wait /bin/true diff --git a/test/units/TEST-07-PID1.sh b/test/units/TEST-07-PID1.sh new file mode 100755 index 0000000..873d638 --- /dev/null +++ b/test/units/TEST-07-PID1.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + +# Issue: https://github.com/systemd/systemd/issues/2730 +# See TEST-07-PID1/test.sh for the first "half" of the test +mountpoint /issue2730 + +run_subtests + +touch /testok +systemctl --no-block exit 123 diff --git a/test/units/TEST-07-PID1.socket-pass-fds.sh b/test/units/TEST-07-PID1.socket-pass-fds.sh new file mode 100755 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/TEST-08-INITRD.sh b/test/units/TEST-08-INITRD.sh new file mode 100755 index 0000000..4804527 --- /dev/null +++ b/test/units/TEST-08-INITRD.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +if systemd-detect-virt -qc; then + echo >&2 "This test can't run in a container" + exit 1 +fi + +# This test requires systemd to run in the initrd as well, which is not the case +# for mkinitrd-based initrd (Ubuntu/Debian) +if [[ "$(systemctl show -P InitRDTimestampMonotonic)" -eq 0 ]]; then + echo "systemd didn't run in the initrd, skipping the test" + touch /skipped + exit 77 +fi + +# We should've created a mount under /run in initrd (see the other half of the test) +# that should've survived the transition from initrd to the real system +test -d /run/initrd-mount-target +mountpoint /run/initrd-mount-target +[[ -e /run/initrd-mount-target/hello-world ]] + +# Copy the prepared exitrd to its intended location. Check the respective +# test.sh file for details +mkdir -p /run/initramfs +cp -r /exitrd/* /run/initramfs/ + +touch /testok diff --git a/test/units/TEST-09-REBOOT.journal.sh b/test/units/TEST-09-REBOOT.journal.sh new file mode 100755 index 0000000..726e800 --- /dev/null +++ b/test/units/TEST-09-REBOOT.journal.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +get_first_boot_id() { + journalctl -b "${1:?}" -o json -n +1 | jq -r '._BOOT_ID' +} + +get_last_boot_id() { + journalctl -b "${1:?}" -o json -n 1 | jq -r '._BOOT_ID' +} + +get_first_timestamp() { + journalctl -b "${1:?}" -o json -n +1 | jq -r '.__REALTIME_TIMESTAMP' +} + +get_last_timestamp() { + journalctl -b "${1:?}" -o json -n 1 | jq -r '.__REALTIME_TIMESTAMP' +} + +# 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 +SYSTEMD_LOG_LEVEL=debug journalctl --list-boots +journalctl --list-boots -o json | jq -r '.[] | [.index, .boot_id, .first_entry, .last_entry] | @tsv' | + while read -r offset boot_id first_ts last_ts; do + : "Boot #$((++index)) ($offset) with ID $boot_id" + + # Try the "regular" (non-json) variants first, as they provide a helpful + # error message if something is not right + SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$index" + SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$offset" + SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$boot_id" + + # Check the boot ID of the first entry + entry_boot_id="$(get_first_boot_id "$index")" + assert_eq "$entry_boot_id" "$boot_id" + entry_boot_id="$(get_first_boot_id "$offset")" + assert_eq "$entry_boot_id" "$boot_id" + entry_boot_id="$(get_first_boot_id "$boot_id")" + assert_eq "$entry_boot_id" "$boot_id" + + # Check the timestamp of the first entry + entry_ts="$(get_first_timestamp "$index")" + assert_eq "$entry_ts" "$first_ts" + entry_ts="$(get_first_timestamp "$offset")" + assert_eq "$entry_ts" "$first_ts" + entry_ts="$(get_first_timestamp "$boot_id")" + assert_eq "$entry_ts" "$first_ts" + + # Check the boot ID of the last entry + entry_boot_id="$(get_last_boot_id "$index")" + assert_eq "$entry_boot_id" "$boot_id" + entry_boot_id="$(get_last_boot_id "$offset")" + assert_eq "$entry_boot_id" "$boot_id" + entry_boot_id="$(get_last_boot_id "$boot_id")" + assert_eq "$entry_boot_id" "$boot_id" + + # Check the timestamp of the last entry + if [[ "$offset" != "0" ]]; then + entry_ts="$(get_last_timestamp "$index")" + assert_eq "$entry_ts" "$last_ts" + entry_ts="$(get_last_timestamp "$offset")" + assert_eq "$entry_ts" "$last_ts" + entry_ts="$(get_last_timestamp "$boot_id")" + assert_eq "$entry_ts" "$last_ts" + fi + done + +verify_seqnum() { + if [[ "$REBOOT_COUNT" -ne "$NUM_REBOOT" ]]; then + return 0 + fi + + journalctl --flush + journalctl --sync + + ls -lR /var/log/journal/ + ls -lR /run/log/journal/ + + journalctl --system --header + + (! journalctl --system -q -o short-monotonic -u systemd-journald.service --grep 'Journal file uses a different sequence number ID, rotating') + + set +x + previous_seqnum=0 + previous_seqnum_id= + previous_boot_id= + journalctl --system -q -o json | jq -r '[.__SEQNUM, .__SEQNUM_ID, ._BOOT_ID] | @tsv' | + while read -r seqnum seqnum_id boot_id; do + + if [[ -n "$previous_seqnum_id" ]]; then + if ! test "$seqnum" -gt "$previous_seqnum"; then + echo "seqnum=$seqnum is not greater than previous_seqnum=$previous_seqnum" + echo "seqnum_id=$seqnum_id, previous_seqnum_id=$previous_seqnum_id" + echo "boot_id=$boot_id, previous_boot_id=$previous_boot_id" + return 1 + fi + + assert_eq "$seqnum_id" "$previous_seqnum_id" + fi + + previous_seqnum="$seqnum" + previous_seqnum_id="$seqnum_id" + previous_boot_id="$boot_id" + done + set -x + + return 0 +} + +verify_seqnum diff --git a/test/units/TEST-09-REBOOT.sh b/test/units/TEST-09-REBOOT.sh new file mode 100755 index 0000000..85630b6 --- /dev/null +++ b/test/units/TEST-09-REBOOT.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +export NUM_REBOOT=4 + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +systemd-cat echo "Reboot count: $REBOOT_COUNT" +systemd-cat journalctl --list-boots + +run_subtests + +if [[ "$REBOOT_COUNT" -lt "$NUM_REBOOT" ]]; then + systemctl_final reboot +elif [[ "$REBOOT_COUNT" -gt "$NUM_REBOOT" ]]; then + assert_not_reached +fi + +touch /testok diff --git a/test/units/TEST-13-NSPAWN.importctl.sh b/test/units/TEST-13-NSPAWN.importctl.sh new file mode 100755 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/TEST-13-NSPAWN.machinectl.sh b/test/units/TEST-13-NSPAWN.machinectl.sh new file mode 100755 index 0000000..462cc6a --- /dev/null +++ b/test/units/TEST-13-NSPAWN.machinectl.sh @@ -0,0 +1,224 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2016 +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +export PAGER= + +at_exit() { + set +e + + machinectl status long-running >/dev/null && machinectl kill --signal=KILL long-running + mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done" + [[ -n "${NSPAWN_FRAGMENT:-}" ]] && rm -f "/etc/systemd/nspawn/$NSPAWN_FRAGMENT" "/var/lib/machines/$NSPAWN_FRAGMENT" + rm -f /run/systemd/nspawn/*.nspawn +} + +trap at_exit EXIT + +systemctl service-log-level systemd-machined debug +systemctl service-log-level systemd-importd debug + +# Mount temporary directory over /var/lib/machines to not pollute the image +mkdir -p /var/lib/machines +mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines + +# Create a couple of containers we can refer to in tests +for i in {0..4}; do + create_dummy_container "/var/lib/machines/container$i" + machinectl start "container$i" +done +# Create one "long running" container with some basic signal handling +create_dummy_container /var/lib/machines/long-running +cat >/var/lib/machines/long-running/sbin/init <<\EOF +#!/usr/bin/bash + +PID=0 + +trap "touch /poweroff" RTMIN+4 +trap "touch /reboot" INT +trap "touch /trap" TRAP +trap 'kill $PID' EXIT + +# We need to wait for the sleep process asynchronously in order to allow +# bash to process signals +sleep infinity & +PID=$! +while :; do + wait || : +done +EOF +machinectl start long-running + +machinectl +machinectl --no-pager --help +machinectl --version +machinectl list +machinectl list --no-legend --no-ask-password + +machinectl status long-running long-running long-running +machinectl status --full long-running +machinectl status --quiet --lines=1 long-running +machinectl status --lines=0 --max-addresses=0 long-running +machinectl status --machine=testuser@.host long-running +machinectl status --output=help long-running +while read -r output; do + machinectl status --output="$output" long-running +done < <(machinectl --output=help) + +machinectl show +machinectl show --all +machinectl show --all --machine=root@ +machinectl show --all --machine=testuser@ +[[ "$(machinectl show --property=PoolPath --value)" == "/var/lib/machines" ]] +machinectl show long-running +machinectl show long-running long-running long-running --all +[[ "$(machinectl show --property=RootDirectory --value long-running)" == "/var/lib/machines/long-running" ]] + +machinectl enable long-running +test -L /etc/systemd/system/machines.target.wants/systemd-nspawn@long-running.service +machinectl enable long-running long-running long-running container1 +machinectl disable long-running +test ! -L /etc/systemd/system/machines.target.wants/systemd-nspawn@long-running.service +machinectl disable long-running long-running long-running container1 + +[[ "$(machinectl shell testuser@ /usr/bin/bash -c 'echo -ne $FOO')" == "" ]] +[[ "$(machinectl shell --setenv=FOO=bar testuser@ /usr/bin/bash -c 'echo -ne $FOO')" == "bar" ]] + +[[ "$(machinectl show --property=State --value long-running)" == "running" ]] +# Equivalent to machinectl kill --signal=SIGRTMIN+4 --kill-whom=leader +rm -f /var/lib/machines/long-running/poweroff +machinectl poweroff long-running +timeout 10 bash -c "until test -e /var/lib/machines/long-running/poweroff; do sleep .5; done" +# Equivalent to machinectl kill --signal=SIGINT --kill-whom=leader +rm -f /var/lib/machines/long-running/reboot +machinectl reboot long-running +timeout 10 bash -c "until test -e /var/lib/machines/long-running/reboot; do sleep .5; done" +# Skip machinectl terminate for now, as it doesn't play well with our "init" +rm -f /var/lib/machines/long-running/trap +machinectl kill --signal=SIGTRAP --kill-whom=leader long-running +timeout 10 bash -c "until test -e /var/lib/machines/long-running/trap; do sleep .5; done" +# Multiple machines at once +machinectl poweroff long-running long-running long-running +machinectl reboot long-running long-running long-running +machinectl kill --signal=SIGTRAP --kill-whom=leader long-running long-running long-running +# All used signals should've been caught by a handler +[[ "$(machinectl show --property=State --value long-running)" == "running" ]] + +cp /etc/machine-id /tmp/foo +machinectl copy-to long-running /tmp/foo /root/foo +test -f /var/lib/machines/long-running/root/foo +machinectl copy-from long-running /root/foo /tmp/bar +diff /tmp/foo /tmp/bar +rm -f /tmp/{foo,bar} + +# machinectl bind is covered by testcase_check_machinectl_bind() in nspawn tests + +machinectl list-images +machinectl list-images --no-legend +machinectl image-status +machinectl image-status container1 +machinectl image-status container1 container1 container{0..4} +machinectl show-image +machinectl show-image container1 +machinectl show-image container1 container1 container{0..4} + +machinectl clone container1 clone1 +machinectl show-image clone1 +machinectl rename clone1 clone2 +(! machinectl show-image clone1) +machinectl show-image clone2 +# `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 + [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == yes ]] + machinectl read-only clone2 no + [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]] +fi +machinectl remove clone2 +for i in {0..4}; do + machinectl clone container1 "clone$i" +done +machinectl remove clone{0..4} +for i in {0..4}; do + machinectl clone container1 ".hidden$i" +done +machinectl list-images --all +test -d /var/lib/machines/.hidden1 +machinectl clean +test ! -d /var/lib/machines/.hidden1 + +# Prepare a simple raw container +mkdir -p /tmp/mnt +dd if=/dev/zero of=/var/tmp/container.raw bs=1M count=256 +mkfs.ext4 /var/tmp/container.raw +mount -o loop /var/tmp/container.raw /tmp/mnt +cp -r /var/lib/machines/container1/* /tmp/mnt +umount /tmp/mnt +# Try to import it, run it, export it, and re-import it +machinectl import-raw /var/tmp/container.raw container-raw +[[ "$(machinectl show-image --property=Type --value container-raw)" == "raw" ]] +machinectl start container-raw +machinectl export-raw container-raw /var/tmp/container-export.raw +machinectl import-raw /var/tmp/container-export.raw container-raw-reimport +[[ "$(machinectl show-image --property=Type --value container-raw-reimport)" == "raw" ]] +rm -f /var/tmp/container{,-export}.raw + +# Prepare a simple tar.gz container +tar -pczf /var/tmp/container.tar.gz -C /var/lib/machines/container1 . +# Try to import it, run it, export it, and re-import it +machinectl import-tar /var/tmp/container.tar.gz container-tar +[[ "$(machinectl show-image --property=Type --value container-tar)" =~ directory|subvolume ]] +machinectl start container-tar +machinectl export-tar container-tar /var/tmp/container-export.tar.gz +machinectl import-tar /var/tmp/container-export.tar.gz container-tar-reimport +[[ "$(machinectl show-image --property=Type --value container-tar-reimport)" =~ directory|subvolume ]] +rm -f /var/tmp/container{,-export}.tar.gz + +# Try to import a container directory & run it +cp -r /var/lib/machines/container1 /var/tmp/container.dir +machinectl import-fs /var/tmp/container.dir container-dir +[[ "$(machinectl show-image --property=Type --value container-dir)" =~ directory|subvolume ]] +machinectl start container-dir +rm -fr /var/tmp/container.dir + +timeout 10 bash -c "until machinectl clean --all; do sleep .5; done" + +NSPAWN_FRAGMENT="machinectl-test-$RANDOM.nspawn" +cat >"/var/lib/machines/$NSPAWN_FRAGMENT" </tmp/fragment.nspawn <"$OCI/config.json" <"$OCI/config.json" </prestart" + ], + "env" : [ + "PRESTART_FOO=prestart_bar", + "ALSO_FOO=also_bar" + ], + "timeout" : 666 + }, + { + "path" : "/bin/touch", + "args" : [ + "/tmp/also-prestart" + ] + } + ], + "poststart" : [ + { + "path" : "/bin/sh", + "args" : [ + "touch", + "/poststart" + ] + } + ], + "poststop" : [ + { + "path" : "/bin/sh", + "args" : [ + "touch", + "/poststop" + ] + } + ] + }, + "annotations" : { + "hello.world" : "1", + "foo" : "bar" + } +} +EOF +# Create a simple "entrypoint" script that validates that the container +# is created correctly according to the OCI config +cat >"$OCI/rootfs/entrypoint.sh" <"$OCI/config.json" <"$OCI/config.json" <"$root/bin/getent" <<\EOF +#!/bin/bash + +if [[ $# -eq 0 ]]; then + : +elif [[ $1 == passwd ]]; then + echo "testuser:x:1000:1000:testuser:/:/bin/sh" +elif [[ $1 == initgroups ]]; then + echo "testuser" +fi +EOF + chmod +x "$root/bin/getent" + systemd-nspawn --directory="$root" bash -xec '[[ $USER == root ]]' + systemd-nspawn --directory="$root" --user=testuser bash -xec '[[ $USER == testuser ]]' + + # --settings= + .nspawn files + mkdir -p /run/systemd/nspawn/ + uuid="deadbeef-dead-dead-beef-000000000000" + echo -ne "[Exec]\nMachineID=deadbeef-dead-dead-beef-111111111111" >/run/systemd/nspawn/foo-bar.nspawn + systemd-nspawn --directory="$root" \ + --machine=foo-bar \ + --settings=yes \ + bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]' + systemd-nspawn --directory="$root" \ + --machine=foo-bar \ + --uuid="$uuid" \ + --settings=yes \ + bash -xec "[[ \$container_uuid == $uuid ]]" + systemd-nspawn --directory="$root" \ + --machine=foo-bar \ + --uuid="$uuid" \ + --settings=override \ + bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]' + systemd-nspawn --directory="$root" \ + --machine=foo-bar \ + --uuid="$uuid" \ + --settings=trusted \ + bash -xec "[[ \$container_uuid == $uuid ]]" + + # Mounts + mkdir "$tmpdir"/{1,2,3} + touch "$tmpdir/1/one" "$tmpdir/2/two" "$tmpdir/3/three" + touch "$tmpdir/foo" + # --bind= + systemd-nspawn --directory="$root" \ + ${COVERAGE_BUILD_DIR:+--bind="$COVERAGE_BUILD_DIR"} \ + --bind="$tmpdir:/foo" \ + --bind="$tmpdir:/also-foo:noidmap,norbind" \ + bash -xec 'test -e /foo/foo; touch /foo/bar; test -e /also-foo/bar' + test -e "$tmpdir/bar" + # --bind-ro= + systemd-nspawn --directory="$root" \ + --bind-ro="$tmpdir:/foo" \ + --bind-ro="$tmpdir:/bar:noidmap,norbind" \ + bash -xec 'test -e /foo/foo; touch /foo/baz && exit 1; touch /bar && exit 1; true' + # --inaccessible= + systemd-nspawn --directory="$root" \ + --inaccessible=/var \ + bash -xec 'touch /var/foo && exit 1; true' + # --tmpfs= + systemd-nspawn --directory="$root" \ + --tmpfs=/var:rw,nosuid,noexec \ + bash -xec 'touch /var/nope' + test ! -e "$root/var/nope" + # --overlay= + systemd-nspawn --directory="$root" \ + --overlay="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \ + bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/foo' + test -e "$tmpdir/3/foo" + # --overlay-ro= + systemd-nspawn --directory="$root" \ + --overlay-ro="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \ + bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/nope && exit 1; true' + test ! -e "$tmpdir/3/nope" + rm -fr "$tmpdir" + + # --port (sanity only) + systemd-nspawn --network-veth --directory="$root" --port=80 --port=90 true + systemd-nspawn --network-veth --directory="$root" --port=80:8080 true + systemd-nspawn --network-veth --directory="$root" --port=tcp:80 true + systemd-nspawn --network-veth --directory="$root" --port=tcp:80:8080 true + systemd-nspawn --network-veth --directory="$root" --port=udp:80 true + systemd-nspawn --network-veth --directory="$root" --port=udp:80:8080 --port=tcp:80:8080 true + (! systemd-nspawn --network-veth --directory="$root" --port= true) + (! systemd-nspawn --network-veth --directory="$root" --port=-1 true) + (! systemd-nspawn --network-veth --directory="$root" --port=: true) + (! systemd-nspawn --network-veth --directory="$root" --port=icmp:80:8080 true) + (! systemd-nspawn --network-veth --directory="$root" --port=tcp::8080 true) + (! systemd-nspawn --network-veth --directory="$root" --port=8080: true) + # Exercise adding/removing ports from an interface + systemd-nspawn --directory="$root" \ + --network-veth \ + --port=6667 \ + --port=80:8080 \ + --port=udp:53 \ + --port=tcp:22:2222 \ + bash -xec 'ip addr add dev host0 10.0.0.10/24; ip a; ip addr del dev host0 10.0.0.10/24' + + # --load-credential=, --set-credential= + echo "foo bar" >/tmp/cred.path + systemd-nspawn --directory="$root" \ + --load-credential=cred.path:/tmp/cred.path \ + --set-credential="cred.set:hello world" \ + bash -xec '[[ "$("/run/systemd/nspawn/$container.nspawn" <"$root/entrypoint.sh" <<\EOF +#!/bin/bash +set -ex + +env + +[[ "$1" == "foo bar" ]] +[[ "$2" == "bar baz" ]] + +[[ "$USER" == root ]] +[[ "$FOO" == bar ]] +[[ "$BAZ" == "hello world" ]] +[[ "$PWD" == /tmp ]] +[[ "$container_uuid" == f28f129b-5187-4b12-80a8-9421ec4b4ad4 ]] +[[ "$(ulimit -S -n)" -eq 1024 ]] +[[ "$(ulimit -H -n)" -eq 2048 ]] +[[ "$(ulimit -S -r)" -eq 8 ]] +[[ "$(ulimit -H -r)" -eq 16 ]] +[[ "$(>"$root/entrypoint.sh" <<\EOF +ip link | grep wlan0 +ip link | grep wl-renamed1 +EOF + fi + + timeout 30 systemd-nspawn --directory="$root" + + # And now for stuff that needs to run separately + # + # Note on the condition below: since our container tree is owned by root, + # both "yes" and "identity" private users settings will behave the same + # as PrivateUsers=0:65535, which makes BindUser= fail as the UID already + # exists there, so skip setting it in such case + for private_users in "131072:65536" yes identity pick; do + cat >"/run/systemd/nspawn/$container.nspawn" <"$root/etc/passwd" + (! systemd-nspawn --directory="$root" \ + --private-users=pick \ + --bind-user=nspawn-bind-user-1 \ + --bind-user=nspawn-bind-user-2 \ + true) + rm -f "$root/etc/passwd" + + echo "nspawn-bind-user-2:x:1000:" >"$root/etc/group" + (! systemd-nspawn --directory="$root" \ + --private-users=pick \ + --bind-user=nspawn-bind-user-1 \ + --bind-user=nspawn-bind-user-2 \ + true) + rm -f "$root/etc/group" + + rm -fr "$root" +} + +testcase_bind_tmp_path() { + # https://github.com/systemd/systemd/issues/4789 + local root + + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.bind-tmp-path.XXX)" + create_dummy_container "$root" + : >/tmp/bind + systemd-nspawn --register=no \ + --directory="$root" \ + --bind=/tmp/bind \ + bash -c 'test -e /tmp/bind' + + rm -fr "$root" /tmp/bind +} + +testcase_norbind() { + # https://github.com/systemd/systemd/issues/13170 + local root + + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.norbind-path.XXX)" + mkdir -p /tmp/binddir/subdir + echo -n "outer" >/tmp/binddir/subdir/file + mount -t tmpfs tmpfs /tmp/binddir/subdir + echo -n "inner" >/tmp/binddir/subdir/file + create_dummy_container "$root" + + systemd-nspawn --register=no \ + --directory="$root" \ + --bind=/tmp/binddir:/mnt:norbind \ + bash -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; exit 1; fi' + + umount /tmp/binddir/subdir + rm -fr "$root" /tmp/binddir/ +} + +rootidmap_cleanup() { + local dir="${1:?}" + + mountpoint -q "$dir/bind" && umount "$dir/bind" + rm -fr "$dir" +} + +testcase_rootidmap() { + local root cmd permissions + local owner=1000 + + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.rootidmap-path.XXX)" + # Create ext4 image, as ext4 supports idmapped-mounts. + mkdir -p /tmp/rootidmap/bind + dd if=/dev/zero of=/tmp/rootidmap/ext4.img bs=4k count=2048 + mkfs.ext4 /tmp/rootidmap/ext4.img + mount /tmp/rootidmap/ext4.img /tmp/rootidmap/bind + trap "rootidmap_cleanup /tmp/rootidmap/" RETURN + + touch /tmp/rootidmap/bind/file + chown -R "$owner:$owner" /tmp/rootidmap/bind + + create_dummy_container "$root" + cmd='PERMISSIONS=$(stat -c "%u:%g" /mnt/file); if [[ $PERMISSIONS != "0:0" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /mnt/other_file' + if ! SYSTEMD_LOG_TARGET=console \ + systemd-nspawn --register=no \ + --directory="$root" \ + --bind=/tmp/rootidmap/bind:/mnt:rootidmap \ + bash -c "$cmd" |& tee nspawn.out; then + if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then + echo "idmapped mounts are not supported, skipping the test..." + return 0 + fi + + return 1 + fi + + permissions=$(stat -c "%u:%g" /tmp/rootidmap/bind/other_file) + if [[ $permissions != "$owner:$owner" ]]; then + echo "*** wrong permissions: $permissions" + [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1 + fi +} + +owneridmap_cleanup() { + local dir="${1:?}" + + mountpoint -q "$dir/bind" && umount "$dir/bind" + rm -fr "$dir" +} + +testcase_owneridmap() { + local root cmd permissions + local owner=1000 + + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.owneridmap-path.XXX)" + # Create ext4 image, as ext4 supports idmapped-mounts. + mkdir -p /tmp/owneridmap/bind + dd if=/dev/zero of=/tmp/owneridmap/ext4.img bs=4k count=2048 + mkfs.ext4 /tmp/owneridmap/ext4.img + mount /tmp/owneridmap/ext4.img /tmp/owneridmap/bind + trap "owneridmap_cleanup /tmp/owneridmap/" RETURN + + touch /tmp/owneridmap/bind/file + chown -R "$owner:$owner" /tmp/owneridmap/bind + + # Allow users to read and execute / in order to execute binaries + chmod o+rx "$root" + + create_dummy_container "$root" + + # --user= + # "Fake" getent passwd's bare minimum, so we don't have to pull it in + # with all the DSO shenanigans + cat >"$root/bin/getent" <<\EOF +#!/bin/bash + +if [[ $# -eq 0 ]]; then + : +elif [[ $1 == passwd ]]; then + echo "testuser:x:1010:1010:testuser:/:/bin/sh" +elif [[ $1 == initgroups ]]; then + echo "testuser" +fi +EOF + chmod +x "$root/bin/getent" + + mkdir -p "$root/home/testuser" + chown 1010:1010 "$root/home/testuser" + + cmd='PERMISSIONS=$(stat -c "%u:%g" /home/testuser/file); if [[ $PERMISSIONS != "1010:1010" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /home/testuser/other_file' + if ! SYSTEMD_LOG_TARGET=console \ + systemd-nspawn --register=no \ + --directory="$root" \ + -U \ + --user=testuser \ + --bind=/tmp/owneridmap/bind:/home/testuser:owneridmap \ + ${COVERAGE_BUILD_DIR:+--bind="$COVERAGE_BUILD_DIR"} \ + /usr/bin/bash -c "$cmd" |& tee nspawn.out; then + if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then + echo "idmapped mounts are not supported, skipping the test..." + return 0 + fi + + return 1 + fi + + permissions=$(stat -c "%u:%g" /tmp/owneridmap/bind/other_file) + if [[ $permissions != "$owner:$owner" ]]; then + echo "*** wrong permissions: $permissions" + [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1 + fi +} + +testcase_notification_socket() { + # https://github.com/systemd/systemd/issues/4944 + local root + local cmd='echo a | nc -U -u -w 1 /run/host/notify' + + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.check_notification_socket.XXX)" + create_dummy_container "$root" + + systemd-nspawn --register=no --directory="$root" bash -x -c "$cmd" + systemd-nspawn --register=no --directory="$root" -U bash -x -c "$cmd" + + rm -fr "$root" +} + +testcase_os_release() { + local root entrypoint os_release_source + + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.os-release.XXX)" + create_dummy_container "$root" + entrypoint="$root/entrypoint.sh" + cat >"$entrypoint" <<\EOF +#!/usr/bin/bash -ex + +. /tmp/os-release +[[ -n "${ID:-}" && "$ID" != "$container_host_id" ]] && exit 1 +[[ -n "${VERSION_ID:-}" && "$VERSION_ID" != "$container_host_version_id" ]] && exit 1 +[[ -n "${BUILD_ID:-}" && "$BUILD_ID" != "$container_host_build_id" ]] && exit 1 +[[ -n "${VARIANT_ID:-}" && "$VARIANT_ID" != "$container_host_variant_id" ]] && exit 1 + +cd /tmp +(cd /run/host && md5sum os-release) | md5sum -c +EOF + chmod +x "$entrypoint" + + os_release_source="/etc/os-release" + if [[ ! -r "$os_release_source" ]]; then + os_release_source="/usr/lib/os-release" + elif [[ -L "$os_release_source" ]]; then + # Ensure that /etc always wins if available + cp --remove-destination -fv /usr/lib/os-release /etc/os-release + echo MARKER=1 >>/etc/os-release + fi + + systemd-nspawn --register=no \ + --directory="$root" \ + --bind="$os_release_source:/tmp/os-release" \ + "${entrypoint##"$root"}" + + if grep -q MARKER /etc/os-release; then + ln -svrf /usr/lib/os-release /etc/os-release + fi + + rm -fr "$root" +} + +testcase_machinectl_bind() { + local service_path service_name root container_name ec + local cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep .5; done; exit 1;' + + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.machinectl-bind.XXX)" + create_dummy_container "$root" + container_name="$(basename "$root")" + + service_path="$(mktemp /run/systemd/system/nspawn-machinectl-bind-XXX.service)" + service_name="${service_path##*/}" + cat >"$service_path" </dev/null || ! selinuxenabled; then + echo >&2 "SELinux is not enabled, skipping SELinux-related tests" + return 0 + fi + + local root + + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.selinux.XXX)" + create_dummy_container "$root" + chcon -R -t container_t "$root" + + systemd-nspawn --register=no \ + --boot \ + --directory="$root" \ + --selinux-apifs-context=system_u:object_r:container_file_t:s0:c0,c1 \ + --selinux-context=system_u:system_r:container_t:s0:c0,c1 + + rm -fr "$root" +} + +testcase_ephemeral_config() { + # https://github.com/systemd/systemd/issues/13297 + local root container_name + + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.ephemeral-config.XXX)" + create_dummy_container "$root" + container_name="$(basename "$root")" + + mkdir -p /run/systemd/nspawn/ + rm -f "/etc/systemd/nspawn/$container_name.nspawn" + cat >"/run/systemd/nspawn/$container_name.nspawn" <&2 "Unified cgroup hierarchy is not supported, skipping..." + return 0 + fi + + if [[ "$use_cgns" == "yes" && "$IS_CGNS_SUPPORTED" == "no" ]]; then + echo >&2 "CGroup namespaces are not supported, skipping..." + return 0 + fi + + root="$(mktemp -d "/var/lib/machines/TEST-13-NSPAWN.unified-$1-cgns-$2-api-vfs-writable-$3.XXX")" + create_dummy_container "$root" + + SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ + systemd-nspawn --register=no \ + --directory="$root" \ + --boot + + SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ + systemd-nspawn --register=no \ + --directory="$root" \ + --private-network \ + --boot + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ + systemd-nspawn --register=no \ + --directory="$root" \ + --private-users=pick \ + --boot; then + [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "network" ]] && return 1 + else + [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "network" ]] && return 1 + fi + + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ + systemd-nspawn --register=no \ + --directory="$root" \ + --private-network \ + --private-users=pick \ + --boot; then + [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "yes" ]] && return 1 + else + [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "yes" ]] && return 1 + fi + + local netns_opt="--network-namespace-path=/proc/self/ns/net" + local net_opt + local net_opts=( + "--network-bridge=lo" + "--network-interface=lo" + "--network-ipvlan=lo" + "--network-macvlan=lo" + "--network-veth" + "--network-veth-extra=lo" + "--network-zone=zone" + ) + + # --network-namespace-path and network-related options cannot be used together + for net_opt in "${net_opts[@]}"; do + echo "$netns_opt in combination with $net_opt should fail" + if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ + systemd-nspawn --register=no \ + --directory="$root" \ + --boot \ + "$netns_opt" \ + "$net_opt"; then + echo >&2 "unexpected pass" + return 1 + fi + done + + # allow combination of --network-namespace-path and --private-network + SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ + systemd-nspawn --register=no \ + --directory="$root" \ + --boot \ + --private-network \ + "$netns_opt" + + # test --network-namespace-path works with a network namespace created by "ip netns" + ip netns add nspawn_test + netns_opt="--network-namespace-path=/run/netns/nspawn_test" + SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ + systemd-nspawn --register=no \ + --directory="$root" \ + --network-namespace-path=/run/netns/nspawn_test \ + ip a | grep -v -E '^1: lo.*UP' + ip netns del nspawn_test + + rm -fr "$root" + + return 0 +} + +testcase_check_os_release() { + # https://github.com/systemd/systemd/issues/29185 + local base common_opts root + + base="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.check_os_release_base.XXX)" + root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.check_os_release.XXX)" + create_dummy_container "$base" + cp -d "$base"/{bin,sbin,lib,lib64} "$root/" + common_opts=( + --boot + --register=no + --directory="$root" + --bind-ro="$base/usr:/usr" + ) + + # Empty /etc/ & /usr/ + (! systemd-nspawn "${common_opts[@]}") + (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}") + (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=foo systemd-nspawn "${common_opts[@]}") + SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}" + + # Empty /usr/ + a broken /etc/os-release -> /usr/os-release symlink + ln -svrf "$root/etc/os-release" "$root/usr/os-release" + (! systemd-nspawn "${common_opts[@]}") + (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}") + SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}" + + rm -fr "$root" "$base" +} + +run_testcases + +for api_vfs_writable in yes no network; do + matrix_run_one no no $api_vfs_writable + matrix_run_one yes no $api_vfs_writable + matrix_run_one no yes $api_vfs_writable + matrix_run_one yes yes $api_vfs_writable +done diff --git a/test/units/TEST-13-NSPAWN.nss-mymachines.sh b/test/units/TEST-13-NSPAWN.nss-mymachines.sh new file mode 100755 index 0000000..817431b --- /dev/null +++ b/test/units/TEST-13-NSPAWN.nss-mymachines.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2016 +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +at_exit() { + set +e + + machinectl kill --signal=KILL nss-mymachines-{noip,singleip,manyips} + mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done" + rm -f /run/systemd/nspawn/*.nspawn +} + +trap at_exit EXIT + +# Mount temporary directory over /var/lib/machines to not pollute the image +mkdir -p /var/lib/machines +mount --bind "$(mktemp --tmpdir=/var/tmp -d)" /var/lib/machines + +# Create a bunch of containers that: +# 1) Have no IP addresses assigned +create_dummy_container /var/lib/machines/nss-mymachines-noip +cat >/var/lib/machines/nss-mymachines-noip/sbin/init <<\EOF +#!/usr/bin/bash -ex + +ip addr show dev ve-noip +touch /initialized +sleep infinity & +# Run the sleep command asynchronously, so bash is able to process signals +while :; do + wait || : +done +EOF +# 2) Have one IP address assigned (IPv4 only) +create_dummy_container /var/lib/machines/nss-mymachines-singleip +cat >/var/lib/machines/nss-mymachines-singleip/sbin/init <<\EOF +#!/usr/bin/bash -ex + +ip addr add 10.1.0.2/24 dev ve-singleip +ip addr show dev ve-singleip +touch /initialized +sleep infinity & +while :; do + wait || : +done +EOF +# 3) Have bunch of IP addresses assigned (both IPv4 and IPv6) +create_dummy_container /var/lib/machines/nss-mymachines-manyips +cat >/var/lib/machines/nss-mymachines-manyips/sbin/init <<\EOF +#!/usr/bin/bash -ex + +ip addr add 10.2.0.2/24 dev ve-manyips +for i in {100..120}; do + ip addr add 10.2.0.$i/24 dev ve-manyips +done +ip addr add fd00:dead:beef:cafe::2/64 dev ve-manyips nodad +ip addr show dev ve-manyips +touch /initialized +sleep infinity +while :; do + wait || : +done +EOF +# Create the respective .nspawn config files +mkdir -p /run/systemd/nspawn +for container in noip singleip manyips; do + cat >"/run/systemd/nspawn/nss-mymachines-$container.nspawn" </dev/null || : + rm -f /{etc,run,usr/lib}/systemd/system/"$unit_name" + rm -fr /{etc,run,usr/lib}/systemd/system/"$unit_name".d + rm -fr /{etc,run,usr/lib}/systemd/system/"$unit_name".{wants,requires} + if [[ $unit_name == *@* ]]; then + base="${unit_name%@*}" + suffix="${unit_name##*.}" + systemctl stop "$base@"*."$suffix" 2>/dev/null || : + rm -f /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix" + rm -fr /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix".d + rm -fr /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix".{wants,requires} + fi +} + +clear_units() { + for u in "$@"; do + clear_unit "$u" + done + systemctl daemon-reload +} + +create_service() { + local service_name="${1:?}" + clear_units "${service_name}".service + + cat >/etc/systemd/system/"$service_name".service </run/systemd/system/service.d/override.conf <"/run/systemd/system/$dropin/override.conf" <"/run/systemd/system/$dropin/override.conf" </run/systemd/system/a-b-c.slice </etc/systemd/system/service.d/drop1.conf + echo -e '[Service]\nStandardInputText=bbb' >/etc/systemd/system/a-.service.d/drop2.conf + echo -e '[Service]\nStandardInputText=ccc' >/etc/systemd/system/a-b-.service.d/drop3.conf + echo -e '[Service]\nStandardInputText=ddd' >/etc/systemd/system/a-b-c.service.d/drop4.conf + + # There's no fragment yet, so this fails + systemctl cat a-b-c.service && exit 1 + + # xxx → eHh4Cg== + systemd-run -u a-b-c.service -p StandardInputData=eHh4Cg== sleep infinity + + data=$(systemctl show -P StandardInputData a-b-c.service) + # xxx\naaa\n\bbb\nccc\nddd\n → eHh4… + test "$data" = "eHh4CmFhYQpiYmIKY2NjCmRkZAo=" + + # Do a reload and check again + systemctl daemon-reload + data=$(systemctl show -P StandardInputData a-b-c.service) + test "$data" = "eHh4CmFhYQpiYmIKY2NjCmRkZAo=" + + clear_units a-b-c.service + rm /etc/systemd/system/service.d/drop1.conf \ + /etc/systemd/system/a-.service.d/drop2.conf \ + /etc/systemd/system/a-b-.service.d/drop3.conf +} + +testcase_transient_slice_dropins() { + echo "Testing dropins for a transient slice..." + echo "*** test transient slice drop-ins" + + # FIXME: implement reloading of individual units. + # + # The settings here are loaded twice. For most settings it doesn't matter, + # but Documentation is not deduplicated, so we current get repeated entried + # which is a bug. + + mkdir -p /etc/systemd/system/slice.d + mkdir -p /etc/systemd/system/a-.slice.d + mkdir -p /etc/systemd/system/a-b-.slice.d + mkdir -p /etc/systemd/system/a-b-c.slice.d + + echo -e '[Unit]\nDocumentation=man:drop1' >/etc/systemd/system/slice.d/drop1.conf + echo -e '[Unit]\nDocumentation=man:drop2' >/etc/systemd/system/a-.slice.d/drop2.conf + echo -e '[Unit]\nDocumentation=man:drop3' >/etc/systemd/system/a-b-.slice.d/drop3.conf + echo -e '[Unit]\nDocumentation=man:drop4' >/etc/systemd/system/a-b-c.slice.d/drop4.conf + + # Invoke daemon-reload to make sure that the call below doesn't fail + systemctl daemon-reload + + # No fragment is required, so this works + systemctl cat a-b-c.slice + + busctl call \ + org.freedesktop.systemd1 \ + /org/freedesktop/systemd1 \ + org.freedesktop.systemd1.Manager \ + StartTransientUnit 'ssa(sv)a(sa(sv))' \ + 'a-b-c.slice' 'replace' \ + 1 \ + 'Documentation' as 1 'man:drop5' \ + 0 + + data=$(systemctl show -P Documentation a-b-c.slice) + test "$data" = "man:drop1 man:drop2 man:drop3 man:drop4 man:drop5 man:drop1 man:drop2 man:drop3 man:drop4" + + # Do a reload and check again + systemctl daemon-reload + data=$(systemctl show -P Documentation a-b-c.slice) + test "$data" = "man:drop5 man:drop1 man:drop2 man:drop3 man:drop4" + + clear_units a-b-c.slice + rm /etc/systemd/system/slice.d/drop1.conf \ + /etc/systemd/system/a-.slice.d/drop2.conf \ + /etc/systemd/system/a-b-.slice.d/drop3.conf +} + +testcase_template_dropins() { + echo "Testing template dropins..." + + create_services foo bar@ yup@ + + # Declare some deps to check if the body was loaded + cat >>/etc/systemd/system/bar@.service <>/etc/systemd/system/yup@.service </usr/lib/systemd/system/test15-a.service.d/override.conf </usr/lib/systemd/system/test15-a.service.d/wants-b.conf </tmp/TEST-15-DROPIN-test15-a-dropin-directory/override.conf <>"$TESTLOG" + fi +} + +wait_for_timeout() +{ + local unit="$1" + local time="$2" + + while [[ $time -gt 0 ]]; do + if [[ "$(systemctl show --property=Result "$unit")" == "Result=timeout" ]]; then + return 0 + fi + + sleep 1 + time=$((time - 1)) + done + + journalctl -u "$unit" >>"$TESTLOG" + + return 1 +} + +# This checks all stages, start, runtime and stop, can be extended by +# EXTEND_TIMEOUT_USEC + +wait_for success_all + +# These check that EXTEND_TIMEOUT_USEC that occurs at greater than the +# extend timeout interval but less then the stage limit (TimeoutStartSec, +# RuntimeMaxSec, TimeoutStopSec) still succeed. + +wait_for success_start +wait_for success_runtime +wait_for success_stop + +# These ensure that EXTEND_TIMEOUT_USEC will still timeout in the +# appropriate stage, after the stage limit, when the EXTEND_TIMEOUT_USEC +# message isn't sent within the extend timeout interval. + +wait_for fail_start startfail +wait_for fail_stop stopfail +wait_for fail_runtime runtimefail + +# These ensure that RuntimeMaxSec is honored for scope and service units +# when they are created. +runtime_max_sec=5 + +systemd-run \ + --property=RuntimeMaxSec=${runtime_max_sec}s \ + -u runtime-max-sec-test-1.service \ + /usr/bin/sh -c "while true; do sleep 1; done" +wait_for_timeout runtime-max-sec-test-1.service $((runtime_max_sec + 2)) + +systemd-run \ + --property=RuntimeMaxSec=${runtime_max_sec}s \ + --scope \ + -u runtime-max-sec-test-2.scope \ + /usr/bin/sh -c "while true; do sleep 1; done" & +wait_for_timeout runtime-max-sec-test-2.scope $((runtime_max_sec + 2)) + +# These ensure that RuntimeMaxSec is honored for scope and service +# units if the value is changed and then the manager is reloaded. +systemd-run \ + -u runtime-max-sec-test-3.service \ + /usr/bin/sh -c "while true; do sleep 1; done" +mkdir -p /etc/systemd/system/runtime-max-sec-test-3.service.d/ +cat > /etc/systemd/system/runtime-max-sec-test-3.service.d/override.conf << EOF +[Service] +RuntimeMaxSec=${runtime_max_sec}s +EOF +systemctl daemon-reload +wait_for_timeout runtime-max-sec-test-3.service $((runtime_max_sec + 2)) + +systemd-run \ + --scope \ + -u runtime-max-sec-test-4.scope \ + /usr/bin/sh -c "while true; do sleep 1; done" & + +# Wait until the unit is running to avoid race with creating the override. +until systemctl is-active runtime-max-sec-test-4.scope; do + sleep 1 +done +mkdir -p /etc/systemd/system/runtime-max-sec-test-4.scope.d/ +cat > /etc/systemd/system/runtime-max-sec-test-4.scope.d/override.conf << EOF +[Scope] +RuntimeMaxSec=${runtime_max_sec}s +EOF +systemctl daemon-reload +wait_for_timeout runtime-max-sec-test-4.scope $((runtime_max_sec + 2)) + +if [[ -f "$TESTLOG" ]]; then + # no mv + cp "$TESTLOG" /test.log + exit 1 +fi + +touch /testok diff --git a/test/units/TEST-17-UDEV.00.sh b/test/units/TEST-17-UDEV.00.sh new file mode 100755 index 0000000..d2aec60 --- /dev/null +++ b/test/units/TEST-17-UDEV.00.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +# Tests for issue #28588 and #28653. + +# On boot, services need to be started in the following order: +# 1. systemd-tmpfiles-setup-dev-early.service +# 2. systemd-sysusers.service +# 3. systemd-tmpfiles-setup-dev.service +# 4. systemd-udevd.service + +output="$(systemctl show --property After --value systemd-udevd.service)" +assert_in "systemd-tmpfiles-setup-dev-early.service" "$output" +assert_in "systemd-sysusers.service" "$output" +assert_in "systemd-tmpfiles-setup-dev.service" "$output" + +output="$(systemctl show --property After --value systemd-tmpfiles-setup-dev.service)" +assert_in "systemd-tmpfiles-setup-dev-early.service" "$output" +assert_in "systemd-sysusers.service" "$output" + +output="$(systemctl show --property After --value systemd-sysusers.service)" +assert_in "systemd-tmpfiles-setup-dev-early.service" "$output" + +check_owner_and_mode() { + local dev=${1?} + local user=${2?} + local group=${3?} + local mode=${4:-} + + if [[ -e "$dev" ]]; then + assert_in "$user" "$(stat --format=%U "$dev")" + assert_in "$group" "$(stat --format=%G "$dev")" + if [[ -n "$mode" ]]; then + assert_in "$mode" "$(stat --format=%#0a "$dev")" + fi + fi + + return 0 +} + +# Check owner and access mode specified in static-nodes-permissions.conf +check_owner_and_mode /dev/snd/seq root audio 0660 +check_owner_and_mode /dev/snd/timer root audio 0660 +check_owner_and_mode /dev/loop-control root disk 0660 +check_owner_and_mode /dev/net/tun root root 0666 +check_owner_and_mode /dev/fuse root root 0666 +check_owner_and_mode /dev/vfio/vfio root root 0666 +check_owner_and_mode /dev/kvm root kvm +check_owner_and_mode /dev/vhost-net root kvm +check_owner_and_mode /dev/vhost-vsock root kvm + +exit 0 diff --git a/test/units/TEST-17-UDEV.01.sh b/test/units/TEST-17-UDEV.01.sh new file mode 100755 index 0000000..44f36f5 --- /dev/null +++ b/test/units/TEST-17-UDEV.01.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +mkdir -p /run/udev/rules.d/ + +rm -f /run/udev/rules.d/50-testsuite.rules +udevadm control --reload +udevadm trigger --settle /dev/sda + +while : ; do + ( + udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service + udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service + systemctl show -p WantedBy foobar.service | grep -q -v sda + systemctl show -p WantedBy waldo.service | grep -q -v sda + ) && break + + sleep .5 +done + +cat >/run/udev/rules.d/50-testsuite.rules </run/udev/rules.d/50-testsuite.rules </run/udev/rules.d/50-testsuite.rules </run/udev/rules.d/50-testsuite.rules </run/udev/rules.d/50-testsuite.rules <"$TMPDIR"/monitor.txt & + KILL_PID="$!" + + # 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 + ip link add foobar type dummy + + for _ in {1..40}; do + if ( + grep -q 'ACTION=add' "$TMPDIR"/monitor.txt + grep -q 'DEVPATH=/devices/virtual/net/foobar' "$TMPDIR"/monitor.txt + grep -q 'SUBSYSTEM=net' "$TMPDIR"/monitor.txt + grep -q 'INTERFACE=foobar' "$TMPDIR"/monitor.txt + grep -q 'ID_NET_DRIVER=dummy' "$TMPDIR"/monitor.txt + grep -q 'ID_NET_NAME=foobar' "$TMPDIR"/monitor.txt + # Even when network interface renaming is failed, SYSTEMD_ALIAS with the conflicting name will be broadcast. + grep -q 'SYSTEMD_ALIAS=/sys/subsystem/net/devices/hoge' "$TMPDIR"/monitor.txt + grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt + grep -q 'UDEV_WORKER_ERRNO=17' "$TMPDIR"/monitor.txt + grep -q 'UDEV_WORKER_ERRNO_NAME=EEXIST' "$TMPDIR"/monitor.txt + ); then + cat "$TMPDIR"/monitor.txt + found=1 + break + fi + sleep .5 + done + test -n "$found" + + 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" +} + +test_netif_renaming_conflict + +exit 0 diff --git a/test/units/TEST-17-UDEV.03.sh b/test/units/TEST-17-UDEV.03.sh new file mode 100755 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}" <"$TEST_CONF" <"$TMPDIR"/monitor.txt & + KILL_PID="$!" + + SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null + + for _ in {1..40}; do + if grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt; then + sleep .5 + kill "$KILL_PID" + KILL_PID= + + cat "$TMPDIR"/monitor.txt + (! grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt) + (! grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt) + (! grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt) + grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt + rm -rf "$TMPDIR" + return 0 + fi + sleep .5 + done + + return 1 +} + +run_test_killed() { + local killed= + + TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX) + udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt & + KILL_PID="$!" + + rm -f /tmp/test-udev-marker + SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null + + for _ in {1..40}; do + if [[ -z "$killed" ]]; then + if [[ -e /tmp/test-udev-marker ]]; then + killall --signal ABRT --regexp udev-worker + killed=1 + fi + elif grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt; then + sleep .5 + kill "$KILL_PID" + KILL_PID= + + cat "$TMPDIR"/monitor.txt + grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt + grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt + grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt + (! grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt) + rm -rf "$TMPDIR" + return 0 + fi + sleep .5 + done + + return 1 +} + +trap teardown EXIT + +setup +run_test_timeout +run_test_killed + +exit 0 diff --git a/test/units/TEST-17-UDEV.04.sh b/test/units/TEST-17-UDEV.04.sh new file mode 100755 index 0000000..d1c3c85 --- /dev/null +++ b/test/units/TEST-17-UDEV.04.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +mkdir -p /run/udev/rules.d/ + +test ! -f /run/udev/tags/added/c1:3 +test ! -f /run/udev/tags/changed/c1:3 +udevadm info /dev/null | grep -E 'E: (TAGS|CURRENT_TAGS)=.*:(added|changed):' && exit 1 + +cat >/run/udev/rules.d/50-testsuite.rules </run/udev/rules.d/00-debug.rules </run/udev/rules.d/50-testsuite.rules </run/udev/rules.d/50-testsuite.rules < 1 )) && sleep 0.5 + if systemctl --quiet is-active "${1?}"; then + return 0 + fi + done + return 1 +)} + +wait_service_inactive() {( + set +ex + for i in {1..20}; do + (( i > 1 )) && sleep 0.5 + systemctl --quiet is-active "${1?}" + if [[ "$?" == "3" ]]; then + return 0 + fi + done + return 1 +)} + +mkdir -p /run/systemd/system +cat >/run/systemd/system/both.service </run/systemd/system/on-add.service </run/systemd/system/on-change.service </run/udev/rules.d/50-testsuite.rules </run/systemd/system/both.service </run/systemd/system/on-add.service </run/systemd/system/on-change.service </run/udev/rules.d/50-testsuite.rules < 1)) && sleep .5 + + ( + systemctl -q is-active /dev/test/symlink-to-null-on-add + ! systemctl -q is-active /dev/test/symlink-to-null-on-change + systemctl -q is-active /sys/test/alias-to-null-on-add + ! systemctl -q is-active /sys/test/alias-to-null-on-change + ) && break +done +assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-add +assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-change +assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-add +assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-change + +udevadm trigger --settle --action change /dev/null +for i in {1..20}; do + ((i > 1)) && sleep .5 + + ( + ! systemctl -q is-active /dev/test/symlink-to-null-on-add + systemctl -q is-active /dev/test/symlink-to-null-on-change + ! systemctl -q is-active /sys/test/alias-to-null-on-add + systemctl -q is-active /sys/test/alias-to-null-on-change + ) && break +done +assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-add +assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-change +assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-add +assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-change + +udevadm trigger --settle --action add /dev/null +for i in {1..20}; do + ((i > 1)) && sleep .5 + + ( + systemctl -q is-active /dev/test/symlink-to-null-on-add + ! systemctl -q is-active /dev/test/symlink-to-null-on-change + systemctl -q is-active /sys/test/alias-to-null-on-add + ! systemctl -q is-active /sys/test/alias-to-null-on-change + ) && break +done +assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-add +assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-change +assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-add +assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-change + +# cleanup +rm -f /run/udev/rules.d/50-testsuite.rules +udevadm control --reload + +exit 0 diff --git a/test/units/TEST-17-UDEV.09.sh b/test/units/TEST-17-UDEV.09.sh new file mode 100755 index 0000000..9993196 --- /dev/null +++ b/test/units/TEST-17-UDEV.09.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +# This is a test for issue #24987. + +mkdir -p /run/udev/rules.d/ +cat >/run/udev/rules.d/50-testsuite.rules <"$TMPDIR"/monitor.txt 2>&1 & +KILL_PID="$!" + +FOUND= +for _ in {1..40}; do + if grep -F 'UDEV - the event which udev sends out after rule processing' "$TMPDIR"/monitor.txt; then + FOUND=1 + break + fi + sleep .5 +done +[[ -n "$FOUND" ]] + +udevadm trigger --verbose --settle --action add /dev/null + +FOUND= +for _ in {1..40}; do + if ! grep -e 'UDEV *\[[0-9.]*\] *add *\/devices\/virtual\/mem\/null (mem)' "$TMPDIR"/monitor.txt; then + sleep .5 + continue + fi + + FOUND=1 + for i in {1..100}; do + if ! grep -F "$(printf 'XXX%03i=0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' "$i")" "$TMPDIR"/monitor.txt; then + FOUND= + break + fi + done + if [[ -n "$FOUND" ]]; then + break; + fi + + sleep .5 +done +[[ -n "$FOUND" ]] + +# cleanup +rm -f /run/udev/rules.d/50-testsuite.rules +udevadm control --reload + +kill "$KILL_PID" +rm -rf "$TMPDIR" + +exit 0 diff --git a/test/units/TEST-17-UDEV.10.sh b/test/units/TEST-17-UDEV.10.sh new file mode 100755 index 0000000..a2b8b14 --- /dev/null +++ b/test/units/TEST-17-UDEV.10.sh @@ -0,0 +1,254 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +# Coverage test for udevadm + +# shellcheck disable=SC2317 +cleanup_17_10() { + set +e + + losetup -d "$loopdev" + rm -f "$blk" + + ip link delete "$netdev" +} + +# Set up some test devices +trap cleanup_17_10 EXIT + +netdev=dummy17.10 +ip link add $netdev type dummy + +blk="$(mktemp)" +dd if=/dev/zero of="$blk" bs=1M count=1 +loopdev="$(losetup --show -f "$blk")" + +udevadm -h + +udevadm control -e +udevadm control -l emerg +udevadm control -l alert +udevadm control -l crit +udevadm control -l err +udevadm control -l warning +udevadm control -l notice +udevadm control --log-level info +udevadm control --log-level debug +(! udevadm control -l hello) +udevadm control -s +udevadm control -S +udevadm control -R +udevadm control -p HELLO=world +udevadm control -m 42 +udevadm control --ping -t 5 +udevadm control --load-credentials +udevadm control -h + +udevadm info /dev/null +udevadm info /sys/class/net/$netdev +udevadm info "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)" +udevadm info --property DEVNAME /sys/class/net/$netdev +udevadm info --property DEVNAME --value /sys/class/net/$netdev +udevadm info --property HELLO /sys/class/net/$netdev +udevadm info -p class/net/$netdev +udevadm info -p /class/net/$netdev +udevadm info --json=off -p class/net/$netdev +udevadm info --json=pretty -p class/net/$netdev | jq . +udevadm info --json=short -p class/net/$netdev | jq . +udevadm info -n null +udevadm info -q all /sys/class/net/$netdev +udevadm info -q name /dev/null +udevadm info -q path /sys/class/net/$netdev +udevadm info -q property /sys/class/net/$netdev +udevadm info -q symlink /sys/class/net/$netdev +udevadm info -q name -r /dev/null +udevadm info --query symlink --root /sys/class/net/$netdev +(! udevadm info -q hello -r /sys/class/net/$netdev) +udevadm info -a /sys/class/net/$netdev +udevadm info -t >/dev/null +udevadm info --tree /sys/class/net/$netdev +udevadm info -x /sys/class/net/$netdev +udevadm info -x -q path /sys/class/net/$netdev +udevadm info -P TEST_ /sys/class/net/$netdev +udevadm info -d /dev/null +udevadm info -e >/dev/null +udevadm info -e --json=off >/dev/null +udevadm info -e --json=pretty | jq . >/dev/null +udevadm info -e --json=short | jq . >/dev/null +udevadm info -e --subsystem-match acpi >/dev/null +udevadm info -e --subsystem-nomatch acpi >/dev/null +udevadm info -e --attr-match ifindex=2 >/dev/null +udevadm info -e --attr-nomatch ifindex=2 >/dev/null +udevadm info -e --property-match SUBSYSTEM=acpi >/dev/null +udevadm info -e --tag-match systemd >/dev/null +udevadm info -e --sysname-match lo >/dev/null +udevadm info -e --name-match /sys/class/net/$netdev >/dev/null +udevadm info -e --parent-match /sys/class/net/$netdev >/dev/null +udevadm info -e --initialized-match >/dev/null +udevadm info -e --initialized-nomatch >/dev/null +# udevadm info -c +udevadm info -w /sys/class/net/$netdev +udevadm info --wait-for-initialization=5 /sys/class/net/$netdev +udevadm info -h + +assert_rc 124 timeout 1 udevadm monitor +assert_rc 124 timeout 1 udevadm monitor -k +assert_rc 124 timeout 1 udevadm monitor -u +assert_rc 124 timeout 1 udevadm monitor -s net +assert_rc 124 timeout 1 udevadm monitor --subsystem-match net/$netdev +assert_rc 124 timeout 1 udevadm monitor -t systemd +assert_rc 124 timeout 1 udevadm monitor --tag-match hello +udevadm monitor -h + +udevadm settle +udevadm settle -t 5 +udevadm settle -E /sys/class/net/$netdev +udevadm settle -h + +udevadm test /dev/null +udevadm info /sys/class/net/$netdev +udevadm test "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)" +udevadm test -a add /sys/class/net/$netdev +udevadm test -a change /sys/class/net/$netdev +udevadm test -a move /sys/class/net/$netdev +udevadm test -a online /sys/class/net/$netdev +udevadm test -a offline /sys/class/net/$netdev +udevadm test -a bind /sys/class/net/$netdev +udevadm test -a unbind /sys/class/net/$netdev +udevadm test -a help /sys/class/net/$netdev +udevadm test --action help +(! udevadm test -a hello /sys/class/net/$netdev) +udevadm test -N early /sys/class/net/$netdev +udevadm test -N late /sys/class/net/$netdev +udevadm test --resolve-names never /sys/class/net/$netdev +(! udevadm test -N hello /sys/class/net/$netdev) +udevadm test -h + +# udevadm test-builtin path_id "$loopdev" +udevadm test-builtin net_id /sys/class/net/$netdev +udevadm test-builtin net_id "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)" +udevadm test-builtin -a add net_id /sys/class/net/$netdev +udevadm test-builtin -a remove net_id /sys/class/net/$netdev +udevadm test-builtin -a change net_id /sys/class/net/$netdev +udevadm test-builtin -a move net_id /sys/class/net/$netdev +udevadm test-builtin -a online net_id /sys/class/net/$netdev +udevadm test-builtin -a offline net_id /sys/class/net/$netdev +udevadm test-builtin -a bind net_id /sys/class/net/$netdev +udevadm test-builtin -a unbind net_id /sys/class/net/$netdev +udevadm test-builtin -a help net_id /sys/class/net/$netdev +udevadm test-builtin net_setup_link /sys/class/net/$netdev +udevadm test-builtin blkid "$loopdev" +udevadm test-builtin input_id /sys/class/net/$netdev +udevadm test-builtin keyboard /dev/null +# udevadm test-builtin kmod /sys/class/net/$netdev +udevadm test-builtin uaccess /dev/null +# udevadm test-builtin usb_id dev/null +(! udevadm test-builtin hello /sys/class/net/$netdev) +# systemd-hwdb update is extremely slow when combined with sanitizers and run +# in a VM without acceleration, so let's just skip the one particular test +# if we detect this combination +if ! [[ -v ASAN_OPTIONS && "$(systemd-detect-virt -v)" == "qemu" ]]; then + modprobe scsi_debug + scsidev=$(readlink -f /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/[0-9]*) + mkdir -p /etc/udev/hwdb.d + cat >/etc/udev/hwdb.d/99-test.hwdb <"${workdir}/default_output_1_success" <"${workdir}/default_output_1_fail" <"${workdir}/output_0_files" <"${out}" + if [ -f "${exo}" ]; then + diff -u "${exo}" "${out}" + elif [ -f "${rules}" ]; then + diff -u "${workdir}/default_output_1_success" "${out}" + fi +} + +assert_0() { + assert_0_impl "$@" + next_test_number +} + +assert_1_impl() { + local rc + set +e + udevadm verify "$@" >"${out}" 2>"${err}" + rc=$? + set -e + assert_eq "$rc" 1 + + if [ -f "${exp}" ]; then + diff -u "${exp}" "${err}" + fi + + if [ -f "${exo}" ]; then + diff -u "${exo}" "${out}" + elif [ -f "${rules}" ]; then + diff -u "${workdir}/default_output_1_fail" "${out}" + fi +} + +assert_1() { + assert_1_impl "$@" + next_test_number +} + +# initialize variables +next_test_number + +assert_0 -h +assert_0 --help +assert_0 -V +assert_0 --version +assert_0 /dev/null + +# unrecognized option '--unknown' +assert_1 --unknown +# option requires an argument -- 'N' +assert_1 -N +# --resolve-names= takes "early" or "never" +assert_1 -N now +# option '--resolve-names' requires an argument +assert_1 --resolve-names +# --resolve-names= takes "early" or "never" +assert_1 --resolve-names=now +# Failed to parse rules file ./nosuchfile: No such file or directory +assert_1 ./nosuchfile +# Failed to parse rules file ./nosuchfile: No such file or directory +cat >"${exo}" <"${exo}" +assert_0 --root="${workdir}" --no-summary + +# Directory with a single *.rules file. +cp "${workdir}/default_output_1_success" "${exo}" +assert_0 "${rules_dir}" + +# Combination of --root= and FILEs is not supported. +assert_1 --root="${workdir}" /dev/null +# No rules files found in nosuchdir +assert_1 --root=nosuchdir + +cd "${rules_dir}" + +# UDEV_LINE_SIZE 16384 +printf '%16383s\n' ' ' >"${rules}" +assert_0 "${rules}" + +# Failed to parse rules file ${rules}: No buffer space available +printf '%16384s\n' ' ' >"${rules}" +echo "Failed to parse rules file ${rules}: No buffer space available" >"${exp}" +assert_1 "${rules}" + +{ + printf 'RUN+="/bin/true",%8174s\\\n' ' ' + printf 'RUN+="/bin/false"%8174s\\\n' ' ' + echo +} >"${rules}" +assert_0 "${rules}" + +printf 'RUN+="/bin/true"%8176s\\\n #\n' ' ' ' ' >"${rules}" +echo >>"${rules}" +cat >"${exp}" <"${rules}" +cat >"${exp}" <"${rules}" + cat >"${exp}" <"${rules}" + cat >"${exp}" <"${rules}" <<'EOF' +KERNEL=="a|b", KERNEL=="a|c", NAME="d" +KERNEL=="a|b", KERNEL!="a|c", NAME="d" +KERNEL!="a", KERNEL!="b", NAME="c" +KERNEL=="|a", KERNEL=="|b", NAME="c" +KERNEL=="*", KERNEL=="a*", NAME="b" +KERNEL=="a*", KERNEL=="c*|ab*", NAME="d" +PROGRAM="a", RESULT=="b" +EOF +assert_0 "${rules}" + +echo 'GOTO="a"' >"${rules}" +cat >"${exp}" <"${rules}" <<'EOF' +GOTO="a" +LABEL="a" +EOF +assert_0 "${rules}" + +cat >"${rules}" <<'EOF' +GOTO="b" +LABEL="b" +LABEL="b" +EOF +cat >"${exp}" <"${rules}" <<'EOF' +GOTO="a" +LABEL="a", LABEL="b" +EOF +cat >"${exp}" <"${rules}" <<'EOF' +KERNEL!="", KERNEL=="?*", KERNEL=="", NAME="a" +EOF +cat >"${exp}" <"${rules}" <<'EOF' +ACTION=="a"NAME="b" +EOF +cat >"${exp}" <"${rules}" <<'EOF' +ACTION=="a" ,NAME="b" +EOF +cat >"${exp}" <"${workdir}/${exp}" +cd - +assert_1 --root="${workdir}" +cd - + +# udevadm verify path/ +sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}" +cd - +assert_1 "${rules_dir}" +cd - + +exit 0 diff --git a/test/units/TEST-17-UDEV.12.sh b/test/units/TEST-17-UDEV.12.sh new file mode 100755 index 0000000..ccc91bf --- /dev/null +++ b/test/units/TEST-17-UDEV.12.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +create_link_file() { + name=${1?} + + mkdir -p /run/systemd/network/ + cat >/run/systemd/network/10-test.link < "$rules" <<'EOF' +ENV{FOO}=="?*", ENV{PROP_FOO}="$env{FOO}" +ENV{BAR}=="?*", ENV{PROP_BAR}="$env{BAR}" +EOF + +udevadm control --reload + +test_not_property /dev/null PROP_FOO +test_not_property /dev/null PROP_BAR + +: Setting of a property works + +udevadm control --property FOO=foo +udevadm trigger --action change --settle /dev/null +test_property /dev/null PROP_FOO foo +test_not_property /dev/null PROP_BAR + +: Change of a property works + +udevadm control --property FOO=goo +udevadm trigger --action change --settle /dev/null +test_property /dev/null PROP_FOO goo + +: Removal of a property works + +udevadm control --property FOO= +udevadm trigger --action change --settle /dev/null +test_not_property /dev/null PROP_FOO + +: Repeated removal of a property does nothing + +udevadm control --property FOO= +udevadm trigger --action change --settle /dev/null +test_not_property /dev/null PROP_FOO + +: Multiple properties can be set at once + +udevadm control --property FOO=foo --property BAR=bar +udevadm trigger --action change --settle /dev/null +test_property /dev/null PROP_FOO foo +test_property /dev/null PROP_BAR bar + +: Multiple setting of the same property is handled correctly + +udevadm control --property FOO=foo --property FOO=42 +udevadm trigger --action change --settle /dev/null +test_property /dev/null PROP_FOO 42 + +: Mix of settings and removals of the same property is handled correctly + +udevadm control -p FOO= -p FOO=foo -p BAR=car -p BAR= +udevadm trigger --action change --settle /dev/null +test_property /dev/null PROP_FOO foo +test_not_property /dev/null PROP_BAR + +exit 0 diff --git a/test/units/TEST-17-UDEV.credentials.sh b/test/units/TEST-17-UDEV.credentials.sh new file mode 100755 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 < /run/credstore/udev.rules.50-testme </run/systemd/network/10-test.link </run/systemd/network/10-test.link.d/10-override.conf </run/systemd/network/10-test.link.d/11-override.conf </run/systemd/network/10-test.link.d/12-override.conf </run/systemd/network/10-test.link.d/13-override.conf </firstphase + systemd-run --wait -p SuccessAction=reboot true +else + echo OK >/testok + systemd-run --wait -p FailureAction=exit -p FailureActionExitStatus=123 false +fi + +sleep infinity diff --git a/test/units/TEST-19-CGROUP.ExitType-cgroup.sh b/test/units/TEST-19-CGROUP.ExitType-cgroup.sh new file mode 100755 index 0000000..65c2606 --- /dev/null +++ b/test/units/TEST-19-CGROUP.ExitType-cgroup.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash +set -eux + +# Test ExitType=cgroup + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +if [[ "$(get_cgroup_hierarchy)" != unified ]]; then + echo "Skipping $0 as we're not running with the unified cgroup hierarchy" + exit 0 +fi + +systemd-analyze log-level debug + +# Multiple level process tree, parent process stays up +cat >/tmp/test19-exit-cgroup.sh < sleep +sleep infinity & +disown + +# process tree: systemd -> bash -> bash -> sleep +((sleep infinity); true) & + +systemd-notify --ready + +# 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 +EOF +chmod +x /tmp/test19-exit-cgroup.sh + +# service should be stopped cleanly +systemd-run --wait \ + --unit=one \ + --property="Type=notify" \ + --property="ExitType=cgroup" \ + /tmp/test19-exit-cgroup.sh 'systemctl stop one' + +# same thing with a truthy exec condition +systemd-run --wait \ + --unit=two \ + --property="Type=notify" \ + --property="ExitType=cgroup" \ + --property="ExecCondition=true" \ + /tmp/test19-exit-cgroup.sh 'systemctl stop two' + +# false exec condition: systemd-run should exit immediately with status code: 1 +(! systemd-run --wait \ + --unit=three \ + --property="Type=notify" \ + --property="ExitType=cgroup" \ + --property="ExecCondition=false" \ + /tmp/test19-exit-cgroup.sh) + +# service should exit uncleanly (main process exits with SIGKILL) +(! systemd-run --wait \ + --unit=four \ + --property="Type=notify" \ + --property="ExitType=cgroup" \ + /tmp/test19-exit-cgroup.sh 'systemctl kill --signal 9 four') + + +# Multiple level process tree, parent process exits quickly +cat >/tmp/test19-exit-cgroup-parentless.sh < sleep +sleep infinity & + +# process tree: systemd -> bash -> sleep +((sleep infinity); true) & + +systemd-notify --ready + +# Run the stop/kill command after this bash process exits +(sleep 1; \$1) & +EOF +chmod +x /tmp/test19-exit-cgroup-parentless.sh + +# service should be stopped cleanly +systemd-run --wait \ + --unit=five \ + --property="Type=notify" \ + --property="ExitType=cgroup" \ + /tmp/test19-exit-cgroup-parentless.sh 'systemctl stop five' + +# service should still exit cleanly despite SIGKILL (the main process already exited cleanly) +systemd-run --wait \ + --unit=six \ + --property="Type=notify" \ + --property="ExitType=cgroup" \ + /tmp/test19-exit-cgroup-parentless.sh 'systemctl kill --signal 9 six' + + +systemd-analyze log-level info diff --git a/test/units/TEST-19-CGROUP.cleanup-slice.sh b/test/units/TEST-19-CGROUP.cleanup-slice.sh new file mode 100755 index 0000000..5d63160 --- /dev/null +++ b/test/units/TEST-19-CGROUP.cleanup-slice.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +export SYSTEMD_LOG_LEVEL=debug + +# Create service with KillMode=none inside a slice +cat </run/systemd/system/test19cleanup.service +[Unit] +Description=Test 19 cleanup Service +[Service] +Slice=test19cleanup.slice +Type=exec +ExecStart=sleep infinity +KillMode=none +EOF +cat </run/systemd/system/test19cleanup.slice +[Unit] +Description=Test 19 cleanup Slice +EOF + +# Start service +systemctl start test19cleanup.service +assert_rc 0 systemd-cgls /test19cleanup.slice + +pid=$(systemctl show --property MainPID --value test19cleanup) +ps "$pid" + +# Stop slice +# The sleep process will not be killed because of KillMode=none +# Since there is still a process running under it, the /test19cleanup.slice cgroup won't be removed +systemctl stop test19cleanup.slice + +ps "$pid" + +# Kill sleep process manually +kill -s TERM "$pid" +while kill -0 "$pid" 2>/dev/null; do sleep 0.1; done + +timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice/test19cleanup.service >& /dev/null; do sleep .5; done' +assert_rc 1 systemd-cgls /test19cleanup.slice/test19cleanup.service + +# Check that empty cgroup /test19cleanup.slice has been removed +timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice >& /dev/null; do sleep .5; done' +assert_rc 1 systemd-cgls /test19cleanup.slice diff --git a/test/units/TEST-19-CGROUP.delegate.sh b/test/units/TEST-19-CGROUP.delegate.sh new file mode 100755 index 0000000..022515f --- /dev/null +++ b/test/units/TEST-19-CGROUP.delegate.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# Test cgroup delegation in the unified hierarchy + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +if [[ "$(get_cgroup_hierarchy)" != unified ]]; then + echo "Skipping $0 as we're not running with the unified cgroup hierarchy" + exit 0 +fi + +at_exit() { + set +e + userdel -r test +} + +systemd-run --wait \ + --unit=test-0.service \ + --property="DynamicUser=1" \ + --property="Delegate=" \ + test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \ + -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.procs -a \ + -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.subtree_control + +# Test if this also works for some of the more recent attrs the kernel might or might not support +for attr in cgroup.threads memory.oom.group memory.reclaim ; do + + if grep -q "$attr" /sys/kernel/cgroup/delegate ; then + systemd-run --wait \ + --unit=test-0.service \ + --property="MemoryAccounting=1" \ + --property="DynamicUser=1" \ + --property="Delegate=" \ + test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \ + -w /sys/fs/cgroup/system.slice/test-0.service/"$attr" + fi +done + +systemd-run --wait \ + --unit=test-1.service \ + --property="DynamicUser=1" \ + --property="Delegate=memory pids" \ + grep -q memory /sys/fs/cgroup/system.slice/test-1.service/cgroup.controllers + +systemd-run --wait \ + --unit=test-2.service \ + --property="DynamicUser=1" \ + --property="Delegate=memory pids" \ + grep -q pids /sys/fs/cgroup/system.slice/test-2.service/cgroup.controllers + +# "io" is not among the controllers enabled by default for all units, verify that +grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers + +# Run a service with "io" enabled, and verify it works +systemd-run --wait \ + --unit=test-3.service \ + --property="IOAccounting=yes" \ + --property="Slice=system-foo-bar-baz.slice" \ + grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test-3.service/cgroup.controllers + +# We want to check if "io" is removed again from the controllers +# list. However, PID 1 (rightfully) does this asynchronously. In order +# to force synchronization on this, let's start a short-lived service +# which requires PID 1 to refresh the cgroup tree, so that we can +# verify that this all works. +systemd-run --wait --unit=test-4.service true + +# And now check again, "io" should have vanished +grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers + +# Check that unprivileged delegation works for scopes +useradd test ||: +systemd-run --uid=test \ + --property="User=test" \ + --property="Delegate=yes" \ + --slice workload.slice \ + --unit test-workload0.scope\ + --scope \ + test -w /sys/fs/cgroup/workload.slice/test-workload0.scope -a \ + -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.procs -a \ + -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.subtree_control + +# Verify that DelegateSubgroup= affects ownership correctly +unit="test-subgroup-$RANDOM.service" +systemd-run --wait \ + --unit="$unit" \ + --property="DynamicUser=1" \ + --property="Delegate=pids" \ + --property="DelegateSubgroup=foo" \ + test -w "/sys/fs/cgroup/system.slice/$unit" -a \ + -w "/sys/fs/cgroup/system.slice/$unit/foo" + +# Check that for the subgroup also attributes that aren't covered by +# regular (i.e. main cgroup) delegation ownership rules are delegated properly +if test -f /sys/fs/cgroup/cgroup.max.depth; then + unit="test-subgroup-$RANDOM.service" + systemd-run --wait \ + --unit="$unit" \ + --property="DynamicUser=1" \ + --property="Delegate=pids" \ + --property="DelegateSubgroup=zzz" \ + test -w "/sys/fs/cgroup/system.slice/$unit/zzz/cgroup.max.depth" +fi + +# Check that the invoked process itself is also in the subgroup +unit="test-subgroup-$RANDOM.service" +systemd-run --wait \ + --unit="$unit" \ + --property="DynamicUser=1" \ + --property="Delegate=pids" \ + --property="DelegateSubgroup=bar" \ + grep -q -x -F "0::/system.slice/$unit/bar" /proc/self/cgroup diff --git a/test/units/TEST-19-CGROUP.sh b/test/units/TEST-19-CGROUP.sh new file mode 100755 index 0000000..9c2a033 --- /dev/null +++ b/test/units/TEST-19-CGROUP.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + +run_subtests + +touch /testok diff --git a/test/units/TEST-21-DFUZZER.sh b/test/units/TEST-21-DFUZZER.sh new file mode 100755 index 0000000..08ebfd9 --- /dev/null +++ b/test/units/TEST-21-DFUZZER.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# check dfuzzer is present before testing +if ! command -v dfuzzer &>/dev/null; then + echo "dfuzzer is not installed, skipping" | tee --append /skipped + exit 77 +fi + +# Save the end.service state before we start fuzzing, as it might get changed +# on the fly by one of the fuzzers +systemctl list-jobs | grep -F 'end.service' && SHUTDOWN_AT_EXIT=1 || SHUTDOWN_AT_EXIT=0 + +# shellcheck disable=SC2317 +at_exit() { + set +e + # We have to call the end.service/poweroff explicitly even if it's specified on + # the kernel cmdline via systemd.wants=end.service, since dfuzzer calls + # org.freedesktop.systemd1.Manager.ClearJobs() which drops the service + # from the queue + if [[ $SHUTDOWN_AT_EXIT -ne 0 ]] && ! systemctl poweroff; then + # PID1 is down let's try to save the journal + journalctl --sync # journal can be down as well so let's ignore exit codes here + systemctl -ff poweroff # sync() and reboot(RB_POWER_OFF) + fi +} + +add_suppression() { + local interface="${1:?}" + local suppression="${2:?}" + + sed -i "\%\[$interface\]%a$suppression" /etc/dfuzzer.conf +} + +trap at_exit EXIT + +systemctl log-level info + +# FIXME: systemd-run doesn't play well with daemon-reexec +# See: https://github.com/systemd/systemd/issues/27204 +add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:Reexecute FIXME" + +add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:SoftReboot destructive" +add_suppression "org.freedesktop.login1" "Sleep destructive" + +# Skip calling start and stop methods on unit objects, as doing that is not only time consuming, but it also +# starts/stops units that interfere with the machine state. The actual code paths should be covered (to some +# degree) by the respective method counterparts on the manager object. +for method in Start Stop Restart ReloadOrRestart ReloadOrTryRestart Kill; do + add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Unit:$method" +done + +cat /etc/dfuzzer.conf + +# TODO +# * check for possibly newly introduced buses? +BUS_LIST=( + org.freedesktop.home1 + org.freedesktop.hostname1 + org.freedesktop.import1 + org.freedesktop.locale1 + org.freedesktop.login1 + org.freedesktop.machine1 + org.freedesktop.portable1 + org.freedesktop.resolve1 + org.freedesktop.systemd1 + org.freedesktop.timedate1 +) + +# systemd-oomd requires PSI +if tail -n +1 /proc/pressure/{cpu,io,memory}; then + BUS_LIST+=( + org.freedesktop.oom1 + ) +fi + +# Some services require specific conditions: +# - systemd-timesyncd can't run in a container +# - systemd-networkd can run in a container if it has CAP_NET_ADMIN capability +if ! systemd-detect-virt --container; then + BUS_LIST+=( + org.freedesktop.network1 + org.freedesktop.timesync1 + ) +elif busctl introspect org.freedesktop.network1 / &>/dev/null; then + BUS_LIST+=( + org.freedesktop.network1 + ) +fi + +SESSION_BUS_LIST=( + org.freedesktop.systemd1 +) + +# Maximum payload size generated by dfuzzer (in bytes) - default: 50K +PAYLOAD_MAX=50000 +# Tweak the maximum payload size if we're running under sanitizers, since +# with larger payloads we start hitting reply timeouts +if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then + PAYLOAD_MAX=10000 # 10K +fi + +# Overmount /var/lib/machines with a size-limited tmpfs, as fuzzing +# the org.freedesktop.machine1 stuff makes quite a mess +mount -t tmpfs -o size=50M tmpfs /var/lib/machines + +# Fuzz both the system and the session buses (where applicable) +for bus in "${BUS_LIST[@]}"; do + echo "Bus: $bus (system)" + systemd-run --pipe --wait \ + -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus" + + # Let's reload the systemd daemon to test (de)serialization as well + systemctl daemon-reload + # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved + systemctl daemon-reexec +done + +umount /var/lib/machines + +for bus in "${SESSION_BUS_LIST[@]}"; do + echo "Bus: $bus (session)" + systemd-run --machine 'testuser@.host' --user --pipe --wait \ + -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus" + + # Let's reload the systemd user daemon to test (de)serialization as well + systemctl --machine 'testuser@.host' --user daemon-reload + # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved + systemctl --machine 'testuser@.host' --user daemon-reexec +done + +touch /testok diff --git a/test/units/TEST-22-TMPFILES.01.sh b/test/units/TEST-22-TMPFILES.01.sh new file mode 100755 index 0000000..2276b75 --- /dev/null +++ b/test/units/TEST-22-TMPFILES.01.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# With "e" don't attempt to set permissions when file doesn't exist, see +# https://github.com/systemd/systemd/pull/6682. +set -eux +set -o pipefail + +rm -fr /tmp/test + +echo "e /tmp/test - root root 1d" | systemd-tmpfiles --create - + +test ! -e /tmp/test diff --git a/test/units/TEST-22-TMPFILES.02.sh b/test/units/TEST-22-TMPFILES.02.sh new file mode 100755 index 0000000..f191ae3 --- /dev/null +++ b/test/units/TEST-22-TMPFILES.02.sh @@ -0,0 +1,183 @@ +#!/bin/bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Basic tests for types creating directories +set -eux +set -o pipefail + +rm -fr /tmp/{C,d,D,e} +mkdir /tmp/{C,d,D,e} + +# +# 'd' +# +mkdir /tmp/d/2 +chmod 777 /tmp/d/2 + +systemd-tmpfiles --dry-run --create - < /tmp/C/3/f1 + +systemd-tmpfiles --create - </tmp/F/truncated +echo "This should be truncated" >/tmp/F/truncated-with-content + +systemd-tmpfiles --create - </tmp/F/rw-fs/foo +(! systemd-tmpfiles --create -) </tmp/F/rw-fs/foo +(! systemd-tmpfiles --create -) < fails. +(! systemd-tmpfiles --create -) </dev/null | grep -qv '^[\?\-]$'; then + TEST_TMPFILES_AGEBY_BTIME=1 +fi + +touch -a --date "2 minutes ago" /tmp/ageby/d1/f1 +touch -m --date "4 minutes ago" /tmp/ageby/d2/f1 + +# Create a bunch of other files. +touch /tmp/ageby/d{1,2}/f{2..4} + +# For "ctime". +touch /tmp/ageby/d3/f1 +chmod +x /tmp/ageby/d3/f1 +sleep 1 + +# For "btime". +touch /tmp/ageby/d4/f1 +sleep 1 + +# More files with recent "{a,b}time" values. +touch /tmp/ageby/d{3,4}/f{2..4} + +# Check for cleanup of "f1" in each of "/tmp/d{1..4}". +systemd-tmpfiles --dry-run --clean - <<-EOF +d /tmp/ageby/d1 - - - a:1m - +e /tmp/ageby/d2 - - - m:3m - +D /tmp/ageby/d3 - - - c:2s - +EOF + +for d in d{1..3}; do + test -f "/tmp/ageby/${d}/f1" +done + +systemd-tmpfiles --clean - <<-EOF +d /tmp/ageby/d1 - - - a:1m - +e /tmp/ageby/d2 - - - m:3m - +D /tmp/ageby/d3 - - - c:2s - +EOF + +for d in d{1..3}; do + test ! -f "/tmp/ageby/${d}/f1" +done + +if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then + systemd-tmpfiles --clean - <<-EOF +d /tmp/ageby/d4 - - - b:1s - +EOF + + test ! -f "/tmp/ageby/d4/f1" +else + # Remove the file manually. + rm "/tmp/ageby/d4/f1" +fi + +# Check for an invalid "age" and "age-by" arguments. +for a in ':' ':1s' '2:1h' 'nope:42h' '" :7m"' 'm:' '::' '"+r^w-x:2/h"' 'b ar::64'; do + systemd-tmpfiles --clean - <&1 | grep -q -F 'Invalid age' +d /tmp/ageby - - - ${a} - +EOF +done + +for d in d{1..4}; do + for f in f{2..4}; do + test -f "/tmp/ageby/${d}/${f}" + done +done + +# Check for parsing with whitespace, repeated values +# for "age-by" (valid arguments). +for a in '" a:24h"' 'cccaab:2h' '" aa : 4h"' '" a A B C c:1h"'; do + systemd-tmpfiles --clean - </usr/lib/tmpfiles.d/L-z.conf</etc/tmpfiles.d/L-z.conf</usr/lib/tmpfiles.d/L-a.conf</etc/tmpfiles.d/L-a.conf</usr/lib/tmpfiles.d/w-$i.conf</etc/tmpfiles.d/w-$i.conf</usr/lib/tmpfiles.d/w-b.conf</tmp/TEST-23-UNIT-FILE.counter + +if [ "$counter" -eq 5 ] ; then + systemctl kill --kill-whom=main -sUSR1 TEST-23-UNIT-FILE.service +fi + +exec sleep 1.5 diff --git a/test/units/TEST-23-UNIT-FILE.ExecReload.sh b/test/units/TEST-23-UNIT-FILE.ExecReload.sh new file mode 100755 index 0000000..d544ce6 --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE.ExecReload.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# Test ExecReload= (PR #13098) + +systemd-analyze log-level debug + +export SYSTEMD_PAGER= +SERVICE_PATH="$(mktemp /etc/systemd/system/execreloadXXX.service)" +SERVICE_NAME="${SERVICE_PATH##*/}" + +echo "[#1] Failing ExecReload= should not kill the service" +cat >"$SERVICE_PATH" <"$SERVICE_PATH" <"$SERVICE_PATH" </tmp/forking1.sh </tmp/forking2.sh </tmp/notify1.sh <&2' +cmp /tmp/stdout <&2' +cmp /tmp/stdout <&2' +cmp /tmp/stdout <&2' +cmp /tmp/stdout </run/systemd/system/test-service.service </run/systemd/system/test-service.service </run/systemd/system/tmp-hoge.mount </run/systemd/system/test-service.socket <$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" <"/run/systemd/system/$ONSUCCESS_UNIT_NAME" <$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) <'/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) </run/TEST-23-UNIT-FILE-marker-fixed +mkdir /run/inaccessible + +systemctl start TEST-23-UNIT-FILE-namespaced.service + +# Ensure that inaccessible paths aren't bypassed by the runtime setup, +(! systemctl bind --mkdir TEST-23-UNIT-FILE-namespaced.service /run/TEST-23-UNIT-FILE-marker-fixed /run/inaccessible/testfile-marker-fixed) + +echo "MARKER_WRONG" >/run/TEST-23-UNIT-FILE-marker-wrong +echo "MARKER_RUNTIME" >/run/TEST-23-UNIT-FILE-marker-runtime + +# Mount twice to exercise mount-beneath (on kernel 6.5+, on older kernels it will just overmount) +systemctl bind --mkdir TEST-23-UNIT-FILE-namespaced.service /run/TEST-23-UNIT-FILE-marker-wrong /tmp/testfile-marker-runtime +test "$(systemctl show -P SubState TEST-23-UNIT-FILE-namespaced.service)" = "running" +systemctl bind --mkdir TEST-23-UNIT-FILE-namespaced.service /run/TEST-23-UNIT-FILE-marker-runtime /tmp/testfile-marker-runtime + +timeout 10 bash -xec 'while [[ "$(systemctl show -P SubState TEST-23-UNIT-FILE-namespaced.service)" == running ]]; do sleep .5; done' +systemctl is-active TEST-23-UNIT-FILE-namespaced.service + +# Now test that systemctl bind fails when attempted on a non-namespaced unit +systemctl start TEST-23-UNIT-FILE-non-namespaced.service + +(! systemctl bind --mkdir TEST-23-UNIT-FILE-non-namespaced.service /run/TEST-23-UNIT-FILE-marker-runtime /tmp/testfile-marker-runtime) + +timeout 10 bash -xec 'while [[ "$(systemctl show -P SubState TEST-23-UNIT-FILE-non-namespaced.service)" == running ]]; do sleep .5; done' +(! systemctl is-active TEST-23-UNIT-FILE-non-namespaced.service) diff --git a/test/units/TEST-23-UNIT-FILE.sh b/test/units/TEST-23-UNIT-FILE.sh new file mode 100755 index 0000000..a929c8b --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + +# Note: the signal shenanigans are necessary for the Upholds= tests +run_subtests_with_signals SIGUSR1 SIGUSR2 SIGRTMIN+1 + +touch /testok diff --git a/test/units/TEST-23-UNIT-FILE.start-stop-no-reload.sh b/test/units/TEST-23-UNIT-FILE.start-stop-no-reload.sh new file mode 100755 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 </run/systemd/system/TEST-23-UNIT-FILE-no-reload.service </run/systemd/system/TEST-23-UNIT-FILE-no-reload.service </run/systemd/system/TEST-23-UNIT-FILE-no-reload.target </run/systemd/system/TEST-23-UNIT-FILE-no-reload.service </dev/null; then + echo "Built without systemd-analyze, skipping the test" + exit 0 +fi + +mapfile -t UNIT_FILES <"$UNIT_FILE_LIST" + +if [[ "${#UNIT_FILES[@]}" -le 0 ]]; then + echo >&2 "The unit file list is empty, this is most likely a bug" + exit 1 +fi + +for unit_file in "${UNIT_FILES[@]}"; do + # Skip the check for a couple of units, namely: + # - syslog.socket: the corresponding syslog.service might not be installed + # - rc-local.service: compat API, /etc/rc.d/rc.local most likely won't be present + if [[ "$unit_file" =~ /(syslog.socket|rc-local.service)$ ]]; then + continue + fi + + # Skip missing unit files - this is useful for $NO_BUILD runs, where certain unit files might be dropped + # in distro packaging + if [[ ! -e "$unit_file" ]]; then + echo "$unit_file not found, skipping" + continue + fi + + systemd-analyze --recursive-errors=no --man=no verify "$unit_file" +done diff --git a/test/units/TEST-23-UNIT-FILE.whoami.sh b/test/units/TEST-23-UNIT-FILE.whoami.sh new file mode 100755 index 0000000..b538d94 --- /dev/null +++ b/test/units/TEST-23-UNIT-FILE.whoami.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -eux +set -o pipefail + +test "$(systemctl whoami)" = TEST-23-UNIT-FILE.service +test "$(systemctl whoami $$)" = TEST-23-UNIT-FILE.service + +systemctl whoami 1 $$ 1 | cmp - /dev/fd/3 3<<'EOF' +init.scope +TEST-23-UNIT-FILE.service +init.scope +EOF diff --git a/test/units/TEST-24-CRYPTSETUP.sh b/test/units/TEST-24-CRYPTSETUP.sh new file mode 100755 index 0000000..b788c82 --- /dev/null +++ b/test/units/TEST-24-CRYPTSETUP.sh @@ -0,0 +1,273 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# TODO: +# - /proc/cmdline parsing +# - expect + interactive auth? + +# We set up an encrypted /var partition which should get mounted automatically +# on boot +mountpoint /var + +systemctl --state=failed --no-legend --no-pager | tee /failed +if [[ -s /failed ]]; then + echo >&2 "Found units in failed state" + exit 1 +fi + +at_exit() { + set +e + + mountpoint -q /proc/cmdline && umount /proc/cmdline + rm -f /etc/crypttab + [[ -e /tmp/crypttab.bak ]] && cp -fv /tmp/crypttab.bak /etc/crypttab + [[ -n "${STORE_LOOP:-}" ]] && losetup -d "$STORE_LOOP" + [[ -n "${WORKDIR:-}" ]] && rm -rf "$WORKDIR" + + systemctl daemon-reload +} + +trap at_exit EXIT + +cryptsetup_start_and_check() { + local expect_fail=0 + local umount_header_and_key=0 + local ec volume unit + + if [[ "${1:?}" == "-f" ]]; then + expect_fail=1 + shift + fi + + if [[ "${1:?}" == "-u" ]]; then + umount_header_and_key=1 + shift + fi + + for volume in "$@"; do + unit="systemd-cryptsetup@$volume.service" + + # The unit existence check should always pass + [[ "$(systemctl show -P LoadState "$unit")" == loaded ]] + systemctl list-unit-files "$unit" + + systemctl start "$unit" && ec=0 || ec=$? + if [[ "$expect_fail" -ne 0 ]]; then + if [[ "$ec" -eq 0 ]]; then + echo >&2 "Unexpected pass when starting $unit" + return 1 + fi + + return 0 + fi + + if [[ "$ec" -ne 0 ]]; then + echo >&2 "Unexpected fail when starting $unit" + return 1 + fi + + if [[ "$umount_header_and_key" -ne 0 ]]; then + umount "$TMPFS_DETACHED_KEYFILE" + umount "$TMPFS_DETACHED_HEADER" + udevadm settle --timeout=60 + fi + + systemctl status "$unit" + test -e "/dev/mapper/$volume" + systemctl stop "$unit" + test ! -e "/dev/mapper/$volume" + done + + return 0 +} + +# Note: some stuff (especially TPM-related) is already tested by TEST-70-TPM2, +# so focus more on other areas instead + +# Use a common workdir to make the cleanup easier +WORKDIR="$(mktemp -d)" + +# Prepare a couple of LUKS2-encrypted disk images +# +# 1) Image with an empty password +IMAGE_EMPTY="$WORKDIR/empty.img)" +IMAGE_EMPTY_KEYFILE="$WORKDIR/empty.keyfile" +IMAGE_EMPTY_KEYFILE_ERASE="$WORKDIR/empty-erase.keyfile" +IMAGE_EMPTY_KEYFILE_ERASE_FAIL="$WORKDIR/empty-erase-fail.keyfile)" +truncate -s 32M "$IMAGE_EMPTY" +echo -n passphrase >"$IMAGE_EMPTY_KEYFILE" +chmod 0600 "$IMAGE_EMPTY_KEYFILE" +cryptsetup luksFormat --batch-mode \ + --pbkdf pbkdf2 \ + --pbkdf-force-iterations 1000 \ + --use-urandom \ + "$IMAGE_EMPTY" "$IMAGE_EMPTY_KEYFILE" +PASSWORD=passphrase NEWPASSWORD="" systemd-cryptenroll --password "$IMAGE_EMPTY" +# Duplicate the key file to test keyfile-erase as well +cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE" +# The key should get erased even on a failed attempt, so test that too +cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL" + +# 2) Image with a detached header and a key file offset + size +IMAGE_DETACHED="$WORKDIR/detached.img" +IMAGE_DETACHED_KEYFILE="$WORKDIR/detached.keyfile" +IMAGE_DETACHED_KEYFILE2="$WORKDIR/detached.keyfile2" +IMAGE_DETACHED_HEADER="$WORKDIR/detached.header" +truncate -s 32M "$IMAGE_DETACHED" +dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE" count=64 bs=1 +dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE2" count=32 bs=1 +chmod 0600 "$IMAGE_DETACHED_KEYFILE" "$IMAGE_DETACHED_KEYFILE2" +cryptsetup luksFormat --batch-mode \ + --pbkdf pbkdf2 \ + --pbkdf-force-iterations 1000 \ + --use-urandom \ + --header "$IMAGE_DETACHED_HEADER" \ + --keyfile-offset 32 \ + --keyfile-size 16 \ + "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE" +# Also, add a second key file to key slot 8 +# Note: --key-slot= behaves as --new-key-slot= when used alone for backwards compatibility +cryptsetup luksAddKey --batch-mode \ + --header "$IMAGE_DETACHED_HEADER" \ + --key-file "$IMAGE_DETACHED_KEYFILE" \ + --keyfile-offset 32 \ + --keyfile-size 16 \ + --key-slot 8 \ + "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE2" + +# Prepare a couple of dummy devices we'll store a copy of the detached header +# and one of the keys on to test if systemd-cryptsetup correctly mounts them +# when necessary +STORE_IMAGE="$WORKDIR/store.img" +truncate -s 64M "$STORE_IMAGE" +STORE_LOOP="$(losetup --show --find --partscan "$STORE_IMAGE")" +udevadm lock --device "$STORE_LOOP" sfdisk "$STORE_LOOP" </etc/crypttab </tmp/cmdline.tmp +mount --bind /tmp/cmdline.tmp /proc/cmdline +# Run the systemd-cryptsetup-generator once explicitly, to collect coverage, +# as during daemon-reload we run generators in a sandbox +mkdir -p /tmp/systemd-cryptsetup-generator.out +/usr/lib/systemd/system-generators/systemd-cryptsetup-generator /tmp/systemd-cryptsetup-generator.out/ +systemctl daemon-reload +systemctl list-unit-files "systemd-cryptsetup@*" + +cryptsetup_start_and_check empty_key +test -e "$IMAGE_EMPTY_KEYFILE_ERASE" +cryptsetup_start_and_check empty_key_erase +test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE" +test -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL" +cryptsetup_start_and_check -f empty_key_erase_fail +test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL" +cryptsetup_start_and_check -f empty_fail{0..1} +cryptsetup_start_and_check empty{0..1} +# First, check if we correctly fail without any key +cryptsetup_start_and_check -f empty_nokey +# And now provide the key via /{etc,run}/cryptsetup-keys.d/ +mkdir -p /run/cryptsetup-keys.d +cp "$IMAGE_EMPTY_KEYFILE" /run/cryptsetup-keys.d/empty_nokey.key +cryptsetup_start_and_check empty_nokey + +if [[ -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/TEST-25-IMPORT.sh b/test/units/TEST-25-IMPORT.sh new file mode 100755 index 0000000..b298c50 --- /dev/null +++ b/test/units/TEST-25-IMPORT.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +export SYSTEMD_PAGER=cat + +dd if=/dev/urandom of=/var/tmp/testimage.raw bs=$((1024*1024+7)) count=5 + +# Test import +machinectl import-raw /var/tmp/testimage.raw +machinectl image-status testimage +test -f /var/lib/machines/testimage.raw +cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw + +# Test export +machinectl export-raw testimage /var/tmp/testimage2.raw +cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw +rm /var/tmp/testimage2.raw + +# Test compressed export (gzip) +machinectl export-raw testimage /var/tmp/testimage2.raw.gz +gunzip /var/tmp/testimage2.raw.gz +cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw +rm /var/tmp/testimage2.raw + +# Test clone +machinectl clone testimage testimage3 +test -f /var/lib/machines/testimage3.raw +machinectl image-status testimage3 +test -f /var/lib/machines/testimage.raw +machinectl image-status testimage +cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw +cmp /var/tmp/testimage.raw /var/lib/machines/testimage3.raw + +# Test removal +machinectl remove testimage +test ! -f /var/lib/machines/testimage.raw +(! machinectl image-status testimage) + +# Test export of clone +machinectl export-raw testimage3 /var/tmp/testimage3.raw +cmp /var/tmp/testimage.raw /var/tmp/testimage3.raw +rm /var/tmp/testimage3.raw + +# Test rename +machinectl rename testimage3 testimage4 +test -f /var/lib/machines/testimage4.raw +machinectl image-status testimage4 +test ! -f /var/lib/machines/testimage3.raw +(! machinectl image-status testimage3) +cmp /var/tmp/testimage.raw /var/lib/machines/testimage4.raw + +# Test export of rename +machinectl export-raw testimage4 /var/tmp/testimage4.raw +cmp /var/tmp/testimage.raw /var/tmp/testimage4.raw +rm /var/tmp/testimage4.raw + +# Test removal +machinectl remove testimage4 +test ! -f /var/lib/machines/testimage4.raw +(! machinectl image-status testimage4) + +# → And now, let's test directory trees ← # + +# Set up a directory we can import +mkdir /var/tmp/scratch +mv /var/tmp/testimage.raw /var/tmp/scratch/ +touch /var/tmp/scratch/anotherfile +mkdir /var/tmp/scratch/adirectory +echo "piep" >/var/tmp/scratch/adirectory/athirdfile + +# Test import-fs +machinectl import-fs /var/tmp/scratch/ +test -d /var/lib/machines/scratch +machinectl image-status scratch + +# Test export-tar +machinectl export-tar scratch /var/tmp/scratch.tar.gz +test -f /var/tmp/scratch.tar.gz +mkdir /var/tmp/extract +(cd /var/tmp/extract ; tar xzf /var/tmp/scratch.tar.gz) +diff -r /var/tmp/scratch/ /var/tmp/extract/ +rm -rf /var/tmp/extract + +# Test import-tar +machinectl import-tar /var/tmp/scratch.tar.gz scratch2 +test -d /var/lib/machines/scratch2 +machinectl image-status scratch2 +diff -r /var/tmp/scratch/ /var/lib/machines/scratch2 + +# Test removal +machinectl remove scratch +test ! -f /var/lib/machines/scratch +(! machinectl image-status scratch) + +# Test clone +machinectl clone scratch2 scratch3 +test -d /var/lib/machines/scratch2 +machinectl image-status scratch2 +test -d /var/lib/machines/scratch3 +machinectl image-status scratch3 +diff -r /var/tmp/scratch/ /var/lib/machines/scratch3 + +# Test removal +machinectl remove scratch2 +test ! -f /var/lib/machines/scratch2 +(! machinectl image-status scratch2) + +# Test rename +machinectl rename scratch3 scratch4 +test -d /var/lib/machines/scratch4 +machinectl image-status scratch4 +test ! -f /var/lib/machines/scratch3 +(! machinectl image-status scratch3) +diff -r /var/tmp/scratch/ /var/lib/machines/scratch4 + +# Test removal +machinectl remove scratch4 +test ! -f /var/lib/machines/scratch4 +(! machinectl image-status scratch4) + +# Test import-tar hyphen/stdin pipe behavior +# shellcheck disable=SC2002 +cat /var/tmp/scratch.tar.gz | machinectl import-tar - scratch5 +test -d /var/lib/machines/scratch5 +machinectl image-status scratch5 +diff -r /var/tmp/scratch/ /var/lib/machines/scratch5 + +# Test export-tar hyphen/stdout pipe behavior +mkdir -p /var/tmp/extract +machinectl export-tar scratch5 - | tar xvf - -C /var/tmp/extract/ +diff -r /var/tmp/scratch/ /var/tmp/extract/ +rm -rf /var/tmp/extract + +rm -rf /var/tmp/scratch + +# Test removal +machinectl remove scratch5 +test ! -f /var/lib/machines/scratch5 +(! machinectl image-status scratch5) + +touch /testok diff --git a/test/units/TEST-26-SYSTEMCTL.sh b/test/units/TEST-26-SYSTEMCTL.sh new file mode 100755 index 0000000..ae7a5d6 --- /dev/null +++ b/test/units/TEST-26-SYSTEMCTL.sh @@ -0,0 +1,501 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2016 +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +at_exit() { + if [[ -v UNIT_NAME && -e "/usr/lib/systemd/system/$UNIT_NAME" ]]; then + rm -fvr "/usr/lib/systemd/system/$UNIT_NAME" "/etc/systemd/system/$UNIT_NAME.d" "+4" + fi + + rm -f /etc/init.d/issue-24990 + return 0 +} + +# Create a simple unit file for testing +# Note: the service file is created under /usr on purpose to test +# the 'revert' verb as well +export UNIT_NAME="systemctl-test-$RANDOM.service" +export UNIT_NAME2="systemctl-test-$RANDOM.service" + +cat >"/usr/lib/systemd/system/$UNIT_NAME" <<\EOF +[Unit] +Description=systemctl test + +[Service] +ExecStart=sleep infinity +ExecReload=true + +# For systemctl clean +CacheDirectory=%n +ConfigurationDirectory=%n +LogsDirectory=%n +RuntimeDirectory=%n +StateDirectory=%n + +[Install] +WantedBy=multi-user.target +EOF + +# Configure the preset setting for the unit file +mkdir /run/systemd/system-preset/ +echo "disable $UNIT_NAME" >/run/systemd/system-preset/99-systemd-test.preset + +EDITOR='true' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null +[ ! -e "/etc/systemd/system/$UNIT_NAME.d/override.conf" ] + +printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' >"+4" +EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null +printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf" + +printf '%b' '[Service]\n' 'ExecStart=\n' 'ExecStart=sleep 10d' >"+4" +EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null +printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf" + +systemctl edit "$UNIT_NAME" --stdin --drop-in=override2.conf </run/systemd/system/test-disable@.service </dev/null || : +systemctl status -a --state active,running,plugged >/dev/null +systemctl status "systemd-*.timer" +systemctl status "systemd-journald*.socket" +systemctl status "sys-devices-*-ttyS0.device" +systemctl status -- -.mount +systemctl status 1 + +# --marked +systemctl restart "$UNIT_NAME" +systemctl set-property "$UNIT_NAME" Markers=needs-restart +systemctl show -P Markers "$UNIT_NAME" | grep needs-restart +systemctl reload-or-restart --marked +(! systemctl show -P Markers "$UNIT_NAME" | grep needs-restart) + +# --dry-run with destructive verbs +# kexec is skipped intentionally, as it requires a bit more involved setup +VERBS=( + default + emergency + exit + halt + hibernate + hybrid-sleep + poweroff + reboot + rescue + suspend + suspend-then-hibernate +) + +for verb in "${VERBS[@]}"; do + systemctl --dry-run "$verb" + + if [[ "$verb" =~ (halt|poweroff|reboot) ]]; then + systemctl --dry-run --message "Hello world" "$verb" + systemctl --dry-run --no-wall "$verb" + systemctl --dry-run -f "$verb" + systemctl --dry-run -ff "$verb" + fi +done + +# Aux verbs & assorted checks +systemctl is-active "*-journald.service" +systemctl cat "*udevd*" +systemctl cat "$UNIT_NAME" +systemctl help "$UNIT_NAME" +systemctl service-watchdogs +systemctl service-watchdogs "$(systemctl service-watchdogs)" + +# show/set-environment +# Make sure PATH is set +systemctl show-environment | grep -q '^PATH=' +# Let's add an entry and override a built-in one +systemctl set-environment PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/testaddition FOO=BAR +# Check that both are set +systemctl show-environment | grep -q '^PATH=.*testaddition$' +systemctl show-environment | grep -q '^FOO=BAR$' +systemctl daemon-reload +# Check again after the reload +systemctl show-environment | grep -q '^PATH=.*testaddition$' +systemctl show-environment | grep -q '^FOO=BAR$' +# Check that JSON output is supported +systemctl show-environment --output=json | grep -q '^{.*"FOO":"BAR".*}$' +# Drop both +systemctl unset-environment FOO PATH +# Check that one is gone and the other reverted to the built-in +systemctl show-environment | grep '^FOO=$' && exit 1 +systemctl show-environment | grep '^PATH=.*testaddition$' && exit 1 +systemctl show-environment | grep -q '^PATH=' +# Check import-environment +export IMPORT_THIS=hello +export IMPORT_THIS_TOO=world +systemctl import-environment IMPORT_THIS IMPORT_THIS_TOO +systemctl show-environment | grep "^IMPORT_THIS=$IMPORT_THIS" +systemctl show-environment | grep "^IMPORT_THIS_TOO=$IMPORT_THIS_TOO" +systemctl unset-environment IMPORT_THIS IMPORT_THIS_TOO +(! systemctl show-environment | grep "^IMPORT_THIS=") +(! systemctl show-environment | grep "^IMPORT_THIS_TOO=") + +# test for sysv-generator (issue #24990) +if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then + # This is configurable via -Dsysvinit-path=, but we can't get the value + # at runtime, so let's just support the two most common paths for now. + [[ -d /etc/rc.d/init.d ]] && SYSVINIT_PATH="/etc/rc.d/init.d" || SYSVINIT_PATH="/etc/init.d" + + # OpenSUSE leaves sysvinit-path enabled, which means systemd-sysv-generator is built + # but may not create the directory if there's no services that use it. + mkdir -p "$SYSVINIT_PATH" + + # invalid dependency + cat >"${SYSVINIT_PATH:?}/issue-24990" <<\EOF +#!/bin/bash + +### BEGIN INIT INFO +# Provides:test1 test2 +# Required-Start:test1 $remote_fs $network +# Required-Stop:test1 $remote_fs $network +# Description:Test +# Short-Description: Test +### END INIT INFO + +case "$1" in + start) + echo "Starting issue-24990.service" + sleep 1000 & + ;; + stop) + echo "Stopping issue-24990.service" + sleep 10 & + ;; + *) + echo "Usage: service test {start|stop|restart|status}" + ;; +esac +EOF + + chmod +x "$SYSVINIT_PATH/issue-24990" + systemctl daemon-reload + [[ -L /run/systemd/generator.late/test1.service ]] + [[ -L /run/systemd/generator.late/test2.service ]] + assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service" + assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service" + output=$(systemctl cat issue-24990) + assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output" + assert_in "Description=LSB: Test" "$output" + assert_in "After=test1.service" "$output" + assert_in "After=remote-fs.target" "$output" + assert_in "After=network-online.target" "$output" + assert_in "Wants=network-online.target" "$output" + assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output" + assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output" + systemctl status issue-24990 || : + systemctl show issue-24990 + assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)" + assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)" + + if ! systemctl is-active network-online.target; then + systemctl start network-online.target + fi + + systemctl restart issue-24990 + systemctl stop issue-24990 + + # valid dependency + cat >"$SYSVINIT_PATH/issue-24990" <<\EOF +#!/bin/bash + +### BEGIN INIT INFO +# Provides:test1 test2 +# Required-Start:$remote_fs +# Required-Stop:$remote_fs +# Description:Test +# Short-Description: Test +### END INIT INFO + +case "$1" in + start) + echo "Starting issue-24990.service" + sleep 1000 & + ;; + stop) + echo "Stopping issue-24990.service" + sleep 10 & + ;; + *) + echo "Usage: service test {start|stop|restart|status}" + ;; +esac +EOF + + chmod +x "$SYSVINIT_PATH/issue-24990" + systemctl daemon-reload + [[ -L /run/systemd/generator.late/test1.service ]] + [[ -L /run/systemd/generator.late/test2.service ]] + assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service" + assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service" + output=$(systemctl cat issue-24990) + assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output" + assert_in "Description=LSB: Test" "$output" + assert_in "After=remote-fs.target" "$output" + assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output" + assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output" + systemctl status issue-24990 || : + systemctl show issue-24990 + assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)" + assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)" + + systemctl restart issue-24990 + systemctl stop issue-24990 +fi + +# %J in WantedBy= causes ABRT (#26467) +cat >/run/systemd/system/test-WantedBy.service </run/systemd/system.conf.d/10-timeout.conf </run/systemd/system/systemd-portabled.service.d/override.conf +[Service] +Environment=SYSTEMD_LOG_LEVEL=debug +EOF + +portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0 + +portablectl is-attached minimal-app0 +portablectl inspect /usr/share/minimal_0.raw minimal-app0.service +systemctl is-active minimal-app0.service +systemctl is-active minimal-app0-foo.service +systemctl is-active minimal-app0-bar.service && exit 1 + +portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0 + +portablectl is-attached minimal-app0 +portablectl inspect /usr/share/minimal_0.raw minimal-app0.service +systemctl is-active minimal-app0.service +systemctl is-active minimal-app0-bar.service +systemctl is-active minimal-app0-foo.service && exit 1 + +portablectl list | grep -q -F "minimal_1" +busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' + +portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0 + +portablectl list | grep -q -F "No images." +busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 + +# Ensure we don't regress (again) when using --force + +portablectl "${ARGS[@]}" attach --force --now --runtime /usr/share/minimal_0.raw minimal-app0 + +portablectl is-attached --force minimal-app0 +portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service +systemctl is-active minimal-app0.service +systemctl is-active minimal-app0-foo.service +systemctl is-active minimal-app0-bar.service && exit 1 + +portablectl "${ARGS[@]}" reattach --force --now --runtime /usr/share/minimal_1.raw minimal-app0 + +portablectl is-attached --force minimal-app0 +portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service +systemctl is-active minimal-app0.service +systemctl is-active minimal-app0-bar.service +systemctl is-active minimal-app0-foo.service && exit 1 + +portablectl list | grep -q -F "minimal_1" +busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' + +portablectl detach --force --now --runtime /usr/share/minimal_1.raw minimal-app0 + +portablectl list | grep -q -F "No images." +busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 + +# portablectl also works with directory paths rather than images + +unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw +unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw + +portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0 + +systemctl is-active minimal-app0.service +systemctl is-active minimal-app0-foo.service +systemctl is-active minimal-app0-bar.service && exit 1 + +portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0 + +systemctl is-active minimal-app0.service +systemctl is-active minimal-app0-bar.service +systemctl is-active minimal-app0-foo.service && exit 1 + +portablectl list | grep -q -F "minimal_1" +busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' + +portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0 + +portablectl list | grep -q -F "No images." +busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 + +portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 + +systemctl is-active app0.service +status="$(portablectl is-attached --extension app0 minimal_0)" +[[ "${status}" == "running-runtime" ]] + +grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf + +portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0 + +systemctl is-active app0.service +status="$(portablectl is-attached --extension app0 minimal_1)" +[[ "${status}" == "running-runtime" ]] + +grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_1.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf + +portablectl detach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0 + +# Ensure versioned images are accepted without needing to use --force to override the extension-release +# matching + +cp /tmp/app0.raw /tmp/app0_1.0.raw +portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_0.raw app0 + +systemctl is-active app0.service +status="$(portablectl is-attached --extension app0_1 minimal_0)" +[[ "${status}" == "running-runtime" ]] + +portablectl detach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_1.raw app0 +rm -f /tmp/app0_1.0.raw + +portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1 + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1 minimal_0)" +[[ "${status}" == "running-runtime" ]] + +# Ensure that adding or removing a version to the image doesn't break reattaching +cp /tmp/app1.raw /tmp/app1_2.raw +portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1_2.raw /usr/share/minimal_1.raw app1 + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1_2 minimal_1)" +[[ "${status}" == "running-runtime" ]] + +portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1 + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1 minimal_1)" +[[ "${status}" == "running-runtime" ]] + +portablectl detach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1 +portablectl "${ARGS[@]}" attach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1 +systemctl daemon-reload +systemctl restart app1.service + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1 minimal_0)" +[[ "${status}" == "running-runtime" ]] + +portablectl detach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1 + +# Ensure vpick works, including reattaching to a new image +mkdir -p /tmp/app1.v/ +cp /tmp/app1.raw /tmp/app1.v/app1_1.0.raw +cp /tmp/app1_2.raw /tmp/app1.v/app1_2.0.raw +portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1 + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1_2.0.raw minimal_1)" +[[ "${status}" == "running-runtime" ]] + +rm -f /tmp/app1.v/app1_2.0.raw +portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1 + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1_1.0.raw minimal_1)" +[[ "${status}" == "running-runtime" ]] + +portablectl detach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_0.raw app1 +rm -f /tmp/app1.v/app1_1.0.raw + +# Ensure that the combination of read-only images, state directory and dynamic user works, and that +# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while +# after the service is attached before the file appears. +grep -q -F bar "${STATE_DIRECTORY}/app0/foo" +grep -q -F baz "${STATE_DIRECTORY}/app1/foo" + +# Ensure that we can override the check on extension-release.NAME +cp /tmp/app0.raw /tmp/app10.raw +portablectl "${ARGS[@]}" attach --force --now --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 + +systemctl is-active app0.service +status="$(portablectl is-attached --extension /tmp/app10.raw /usr/share/minimal_0.raw)" +[[ "${status}" == "running-runtime" ]] + +portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/app10.raw" + +# Ensure that we can detach even when an image has been deleted already (stop the unit manually as +# portablectl won't find it) +rm -f /tmp/app10.raw +systemctl stop app0.service +portablectl detach --force --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 + +# portablectl also accepts confexts +portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 + +systemctl is-active app0.service +status="$(portablectl is-attached --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw)" +[[ "${status}" == "running-runtime" ]] + +portablectl inspect --force --cat --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/conf0.raw" + +portablectl detach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 + +# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned) +portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 +test -f /run/portables/app0.raw +test -f /run/portables/minimal_0.raw +test -f /run/systemd/system.attached/app0.service +test -L /run/systemd/system.attached/app0.service.d/10-profile.conf +portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 + +# Ensure that when two portables share the same base image, removing one doesn't remove the other too + +portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0 +portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1 + +status="$(portablectl is-attached --extension app0 minimal_0)" +[[ "${status}" == "attached-runtime" ]] +status="$(portablectl is-attached --extension app1 minimal_0)" +[[ "${status}" == "attached-runtime" ]] + +(! portablectl detach --runtime /usr/share/minimal_0.raw app) + +status="$(portablectl is-attached --extension app0 minimal_0)" +[[ "${status}" == "attached-runtime" ]] +status="$(portablectl is-attached --extension app1 minimal_0)" +[[ "${status}" == "attached-runtime" ]] + +# Ensure 'portablectl list' shows the correct status for both images +portablectl list +portablectl list | grep -F "minimal_0" | grep -q -F "attached-runtime" +portablectl list | grep -F "app0" | grep -q -F "attached-runtime" +portablectl list | grep -F "app1" | grep -q -F "attached-runtime" + +portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app + +status="$(portablectl is-attached --extension app1 minimal_0)" +[[ "${status}" == "attached-runtime" ]] + +portablectl detach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app + +# portablectl also works with directory paths rather than images + +mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc +mount /tmp/app0.raw /tmp/app0 +mount /tmp/app1.raw /tmp/app1 +mount /usr/share/minimal_0.raw /tmp/rootdir + +# Fix up os-release to drop the valid PORTABLE_SERVICES field (because we are +# bypassing the sysext logic in portabled here it will otherwise not see the +# extensions additional valid prefix) +grep -v "^PORTABLE_PREFIXES=" /tmp/rootdir/etc/os-release >/tmp/os-release-fix/etc/os-release + +mount -t overlay overlay -o lowerdir=/tmp/os-release-fix:/tmp/app1:/tmp/rootdir /tmp/overlay + +grep . /tmp/overlay/usr/lib/extension-release.d/* +grep . /tmp/overlay/etc/os-release + +portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/overlay app1 + +systemctl is-active app1.service + +portablectl detach --now --runtime overlay app1 + +umount /tmp/overlay + +portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 + +systemctl is-active app0.service +systemctl is-active app1.service + +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0 +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2 +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service + +grep -q -F "LogExtraFields=PORTABLE=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app0.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app0.service.d/20-portable.conf + +grep -q -F "LogExtraFields=PORTABLE=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app1.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app1.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app1.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf +grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app1.service.d/20-portable.conf + +portablectl detach --clean --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 + +# Ensure --clean remove state and other directories belonging to the portable image being detached +test ! -d /var/lib/app0 +test ! -d /run/app0 + +# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned) +portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 +test -d /run/portables/app0 +test -d /run/portables/app1 +test -d /run/portables/rootdir +test -f /run/systemd/system.attached/app0.service +test -f /run/systemd/system.attached/app1.service +test -L /run/systemd/system.attached/app0.service.d/10-profile.conf +test -L /run/systemd/system.attached/app1.service.d/10-profile.conf +portablectl detach --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 + +# Attempt to disable the app unit during detaching. Requires --copy=symlink to reproduce. +# Provides coverage for https://github.com/systemd/systemd/issues/23481 +portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0 +portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0 +# attach and detach again to check if all drop-in configs are removed even if the main unit files are removed +portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0 +portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0 + +umount /tmp/rootdir +umount /tmp/app0 +umount /tmp/app1 + +# Lack of ID field in os-release should be rejected, but it caused a crash in the past instead +mkdir -p /tmp/emptyroot/usr/lib +mkdir -p /tmp/emptyext/usr/lib/extension-release.d +touch /tmp/emptyroot/usr/lib/os-release +touch /tmp/emptyext/usr/lib/extension-release.d/extension-release.emptyext + +# Remote peer disconnected -> portabled crashed +res="$(! portablectl attach --extension /tmp/emptyext /tmp/emptyroot 2> >(grep "Remote peer disconnected"))" +test -z "${res}" + +touch /testok diff --git a/test/units/TEST-30-ONCLOCKCHANGE.sh b/test/units/TEST-30-ONCLOCKCHANGE.sh new file mode 100755 index 0000000..83698b8 --- /dev/null +++ b/test/units/TEST-30-ONCLOCKCHANGE.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +systemd-analyze log-level debug + +systemctl disable --now systemd-timesyncd.service + +timedatectl set-timezone Europe/Berlin +timedatectl set-time 1980-10-15 + +systemd-run --on-timezone-change touch /tmp/timezone-changed +systemd-run --on-clock-change touch /tmp/clock-changed + +test ! -f /tmp/timezone-changed +test ! -f /tmp/clock-changed + +timedatectl set-timezone Europe/Kyiv + +while test ! -f /tmp/timezone-changed ; do sleep .5 ; done + +timedatectl set-time 2018-1-1 + +while test ! -f /tmp/clock-changed ; do sleep .5 ; done + +systemd-analyze log-level info + +touch /testok diff --git a/test/units/TEST-31-DEVICE-ENUMERATION.sh b/test/units/TEST-31-DEVICE-ENUMERATION.sh new file mode 100755 index 0000000..5e99302 --- /dev/null +++ b/test/units/TEST-31-DEVICE-ENUMERATION.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +if journalctl -b -t systemd --grep '(? dead'; then + exit 1 +fi + +touch /testok diff --git a/test/units/TEST-32-OOMPOLICY.sh b/test/units/TEST-32-OOMPOLICY.sh new file mode 100755 index 0000000..046b8b9 --- /dev/null +++ b/test/units/TEST-32-OOMPOLICY.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# Let's run this test only if the "memory.oom.group" cgroupfs attribute +# exists. This test is a bit too strict, since the "memory.events"/"oom_kill" +# logic has been around since a longer time than "memory.oom.group", but it's +# an easier thing to test for, and also: let's not get confused by older +# kernels where the concept was still new. + +if test -f /sys/fs/cgroup/system.slice/TEST-32-OOMPOLICY.service/memory.oom.group; then + systemd-analyze log-level debug + + # Run a service that is guaranteed to be the first candidate for OOM killing + systemd-run --unit=oomtest.service \ + -p Type=exec -p OOMScoreAdjust=1000 -p OOMPolicy=stop -p MemoryAccounting=yes \ + sleep infinity + + # Trigger an OOM killer run + echo 1 >/proc/sys/kernel/sysrq + echo f >/proc/sysrq-trigger + + while : ; do + STATE="$(systemctl show -P ActiveState oomtest.service)" + [ "$STATE" = "failed" ] && break + sleep .5 + done + + RESULT="$(systemctl show -P Result oomtest.service)" + test "$RESULT" = "oom-kill" + + systemd-analyze log-level info +fi + +touch /testok diff --git a/test/units/TEST-34-DYNAMICUSERMIGRATE.sh b/test/units/TEST-34-DYNAMICUSERMIGRATE.sh new file mode 100755 index 0000000..d15b675 --- /dev/null +++ b/test/units/TEST-34-DYNAMICUSERMIGRATE.sh @@ -0,0 +1,160 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +systemd-analyze log-level debug + +test_directory() { + local directory="$1" + local path="$2" + + # cleanup for previous invocation + for i in xxx xxx2 yyy zzz x:yz x:yz2; do + rm -rf "${path:?}/${i}" "${path:?}/private/${i}" + done + + # Set everything up without DynamicUser=1 + + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz touch "${path}"/zzz/test + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}=zzz:xxx zzz:xxx2" -p TemporaryFileSystem="${path}" bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test" + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test + (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing) + + test -d "${path}"/zzz + test ! -L "${path}"/zzz + test ! -e "${path}"/private/zzz + + test ! -e "${path}"/xxx + test ! -e "${path}"/private/xxx + test ! -e "${path}"/xxx2 + test ! -e "${path}"/private/xxx2 + test -L "${path}"/yyy + test ! -e "${path}"/private/yyy + + test -f "${path}"/zzz/test + test ! -e "${path}"/zzz/test-missing + + # Convert to DynamicUser=1 + + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}=zzz:xxx zzz:xxx2" \ + -p TemporaryFileSystem="${path}" -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test" + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test + (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing) + + test -L "${path}"/zzz + test -d "${path}"/private/zzz + + test ! -e "${path}"/xxx + test ! -e "${path}"/private/xxx + test ! -e "${path}"/xxx2 + test ! -e "${path}"/private/xxx2 + test -L "${path}"/yyy # previous symlink is not removed + test ! -e "${path}"/private/yyy + + test -f "${path}"/zzz/test + test ! -e "${path}"/zzz/test-missing + + # Convert back + + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}" test -f "${path}"/xxx/test + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}=zzz:xxx zzz:xxx2" -p TemporaryFileSystem="${path}" bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test" + systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test + (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing) + + test -d "${path}"/zzz + test ! -L "${path}"/zzz + test ! -e "${path}"/private/zzz + + test ! -e "${path}"/xxx + test ! -e "${path}"/private/xxx + test ! -e "${path}"/xxx2 + test ! -e "${path}"/private/xxx2 + test -L "${path}"/yyy + test ! -e "${path}"/private/yyy + + test -f "${path}"/zzz/test + test ! -e "${path}"/zzz/test-missing + + # Exercise the unit parsing paths too + cat >/run/systemd/system/testservice-34.service </run/systemd/system/testservice-34-check-writable.service <<\EOF +[Unit] +Description=Check writable directories when DynamicUser= with StateDirectory= + +[Service] +# Relevant only for sanitizer runs +EnvironmentFile=-/usr/lib/systemd/systemd-asan-env + +Type=oneshot +DynamicUser=yes +StateDirectory=waldo quux/pief aaa/bbb aaa aaa/ccc xxx/yyy:aaa/111 xxx:aaa/222 xxx/zzz:aaa/333 + +# Make sure that the state directories are really the only writable directory besides the obvious candidates +ExecStart=bash -c ' \ + set -eux; \ + set -o pipefail; \ + declare -a writable_dirs; \ + readarray -t writable_dirs < <(find / \( -path /var/tmp -o -path /tmp -o -path /proc -o -path /dev/mqueue -o -path /dev/shm -o \ + -path /sys/fs/bpf -o -path /dev/.lxc -o -path /sys/devices/system/cpu \) \ + -prune -o -type d -writable -print 2>/dev/null | sort -u); \ + [[ "$${#writable_dirs[@]}" == "8" ]]; \ + [[ "$${writable_dirs[0]}" == "/var/lib/private/aaa" ]]; \ + [[ "$${writable_dirs[1]}" == "/var/lib/private/aaa/bbb" ]]; \ + [[ "$${writable_dirs[2]}" == "/var/lib/private/aaa/ccc" ]]; \ + [[ "$${writable_dirs[3]}" == "/var/lib/private/quux/pief" ]]; \ + [[ "$${writable_dirs[4]}" == "/var/lib/private/waldo" ]]; \ + [[ "$${writable_dirs[5]}" == "/var/lib/private/xxx" ]]; \ + [[ "$${writable_dirs[6]}" == "/var/lib/private/xxx/yyy" ]]; \ + [[ "$${writable_dirs[7]}" == "/var/lib/private/xxx/zzz" ]]; \ +' +EOF + systemctl daemon-reload + systemctl start testservice-34-check-writable.service +} + +test_directory "StateDirectory" "/var/lib" +test_directory "RuntimeDirectory" "/run" +test_directory "CacheDirectory" "/var/cache" +test_directory "LogsDirectory" "/var/log" + +test_check_writable + +systemd-analyze log-level info + +touch /testok diff --git a/test/units/TEST-35-LOGIN.sh b/test/units/TEST-35-LOGIN.sh new file mode 100755 index 0000000..78e0c1e --- /dev/null +++ b/test/units/TEST-35-LOGIN.sh @@ -0,0 +1,744 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +cleanup_test_user() ( + set +ex + + pkill -u "$(id -u logind-test-user)" + sleep 1 + pkill -KILL -u "$(id -u logind-test-user)" + userdel -r logind-test-user + + return 0 +) + +setup_test_user() { + mkdir -p /var/spool/cron /var/spool/mail + useradd -m -s /bin/bash logind-test-user + trap cleanup_test_user EXIT +} + +test_write_dropin() { + systemctl edit --runtime --stdin systemd-logind.service --drop-in=debug.conf </run/systemd/logind.conf.d/kill-user-processes.conf </run/systemd/logind.conf.d/kill-user-processes.conf </run/systemd/logind.conf.d/sleep-operations.conf </dev/null; then + echo "command evemu-device not found, skipping" + return + fi + if ! command -v evemu-event >/dev/null; then + echo "command evemu-event not found, skipping" + return + fi + + trap teardown_suspend RETURN + + # save pid + pid=$(systemctl show systemd-logind.service -p ExecMainPID --value) + + # create fake suspend + mkdir -p /run/systemd/system/systemd-suspend.service.d + cat >/run/systemd/system/systemd-suspend.service.d/override.conf </run/udev/rules.d/70-logindtest-lid.rules </run/lidswitch.evemu <&2 + exit 1 + fi + input_name=${input_name%/device/name} + lid_dev=/dev/${input_name#/sys/class/} + udevadm info --wait-for-initialization=10s "$lid_dev" + udevadm settle + + # close lid + evemu-event "$lid_dev" --sync --type 5 --code 0 --value 1 + # need to wait for 30s suspend inhibition after boot + wait_suspend 31 + # open lid again + evemu-event "$lid_dev" --sync --type 5 --code 0 --value 0 + + # waiting for 30s inhibition time between suspends + sleep 30 + + # now closing lid should cause instant suspend + evemu-event "$lid_dev" --sync --type 5 --code 0 --value 1 + wait_suspend 2 + evemu-event "$lid_dev" --sync --type 5 --code 0 --value 0 + + assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid" +} + +testcase_shutdown() { + local pid + + # save pid + pid=$(systemctl show systemd-logind.service -p ExecMainPID --value) + + # scheduled shutdown with wall message + shutdown 2>&1 + sleep 5 + shutdown -c || : + # logind should still be running + assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid" + + # scheduled shutdown without wall message + shutdown --no-wall 2>&1 + sleep 5 + shutdown -c --no-wall || true + assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid" +} + +cleanup_session() ( + set +ex + + local uid s + + uid=$(id -u logind-test-user) + + loginctl disable-linger logind-test-user + + systemctl stop getty@tty2.service + + for s in $(loginctl --no-legend list-sessions | grep -v manager | awk '$3 == "logind-test-user" { print $1 }'); do + echo "INFO: stopping session $s" + loginctl terminate-session "$s" + done + + loginctl terminate-user logind-test-user + + if ! timeout 30 bash -c "while loginctl --no-legend | grep -q logind-test-user; do sleep 1; done"; then + echo "WARNING: session for logind-test-user still active, ignoring." + fi + + pkill -u "$uid" + sleep 1 + pkill -KILL -u "$uid" + + if ! timeout 30 bash -c "while systemctl is-active --quiet user@${uid}.service; do sleep 1; done"; then + echo "WARNING: user@${uid}.service is still active, ignoring." + fi + + if ! timeout 30 bash -c "while systemctl is-active --quiet user-runtime-dir@${uid}.service; do sleep 1; done"; then + echo "WARNING: user-runtime-dir@${uid}.service is still active, ignoring." + fi + + if ! timeout 30 bash -c "while systemctl is-active --quiet user-${uid}.slice; do sleep 1; done"; then + echo "WARNING: user-${uid}.slice is still active, ignoring." + fi + + rm -rf /run/systemd/system/getty@tty2.service.d + systemctl daemon-reload + + return 0 +) + +teardown_session() ( + set +ex + + cleanup_session + + rm -f /run/udev/rules.d/70-logindtest-scsi_debug-user.rules + udevadm control --reload + rmmod scsi_debug + + return 0 +) + +check_session() ( + set +ex + + local seat session leader_pid + + if [[ $(loginctl --no-legend | grep -v manager | grep -c "logind-test-user") != 1 ]]; then + echo "no session or multiple sessions for logind-test-user." >&2 + return 1 + fi + + seat=$(loginctl --no-legend | grep -v manager | grep 'logind-test-user *seat' | awk '{ print $4 }') + if [[ -z "$seat" ]]; then + echo "no seat found for user logind-test-user" >&2 + return 1 + fi + + session=$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }') + if [[ -z "$session" ]]; then + echo "no session found for user logind-test-user" >&2 + return 1 + fi + + if ! loginctl session-status "$session" | grep -q "Unit: session-${session}\.scope"; then + echo "cannot find scope unit for session $session" >&2 + return 1 + fi + + leader_pid=$(loginctl session-status "$session" | awk '$1 == "Leader:" { print $2 }') + if [[ -z "$leader_pid" ]]; then + echo "cannot found leader process for session $session" >&2 + return 1 + fi + + # cgroup v1: "1:name=systemd:/user.slice/..."; unified hierarchy: "0::/user.slice" + if ! grep -q -E '(name=systemd|^0:):.*session.*scope' /proc/"$leader_pid"/cgroup; then + echo "FAIL: process $leader_pid is not in the session cgroup" >&2 + cat /proc/self/cgroup + return 1 + fi +) + +create_session() { + # login with the test user to start a session + mkdir -p /run/systemd/system/getty@tty2.service.d + cat >/run/systemd/system/getty@tty2.service.d/override.conf < 1 )) && sleep 1 + check_session && break + done + check_session + assert_eq "$(loginctl --no-legend | grep -v manager | awk '$3=="logind-test-user" { print $7 }')" "tty2" +} + +testcase_sanity_check() { + # Exercise basic loginctl options + + if [[ ! -c /dev/tty2 ]]; then + echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}." + return + fi + + trap cleanup_session RETURN + create_session + + # Run most of the loginctl commands from a user session to make + # the seat/session autodetection work-ish + systemd-run --user --pipe --wait -M "logind-test-user@.host" bash -eux <<\EOF + loginctl list-sessions + loginctl list-sessions -j + loginctl list-sessions --json=short + loginctl session-status + loginctl show-session + loginctl show-session -P DelayInhibited + + # We're not in the same session scope, so in this case we need to specify + # the session ID explicitly + session=$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1; exit; }') + loginctl kill-session --signal=SIGCONT "$session" + # FIXME(?) + #loginctl kill-session --signal=SIGCONT --kill-whom=leader "$session" + + loginctl list-users + loginctl user-status + loginctl show-user -a + loginctl show-user -P IdleAction + loginctl kill-user --signal=SIGCONT "" + + loginctl list-seats + loginctl seat-status + loginctl show-seat + loginctl show-seat -P IdleActionUSec +EOF + + # Requires root privileges + loginctl lock-sessions + loginctl unlock-sessions + loginctl flush-devices +} + +testcase_session() { + local dev + + if systemd-detect-virt --quiet --container; then + echo "Skipping ACL tests in container" + return + fi + + if [[ ! -c /dev/tty2 ]]; then + echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}." + return + fi + + trap teardown_session RETURN + + create_session + + # scsi_debug should not be loaded yet + if [[ -d /sys/bus/pseudo/drivers/scsi_debug ]]; then + echo "scsi_debug module is already loaded." >&2 + exit 1 + fi + + # we use scsi_debug to create new devices which we can put ACLs on + # tell udev about the tagging, so that logind can pick it up + mkdir -p /run/udev/rules.d + cat >/run/udev/rules.d/70-logindtest-scsi_debug-user.rules </dev/null; do sleep 1; done' + dev=/dev/$(ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null) + if [[ ! -b "$dev" ]]; then + echo "cannot find suitable scsi block device" >&2 + exit 1 + fi + udevadm settle + udevadm info "$dev" + + # trigger logind and activate session + loginctl activate "$(loginctl --no-legend | grep -v manager | awk '$3 == "logind-test-user" { print $1 }')" + + # check ACL + sleep 1 + assert_in "user:logind-test-user:rw-" "$(getfacl -p "$dev")" + + # hotplug: new device appears while logind is running + rmmod scsi_debug + modprobe scsi_debug + timeout 30 bash -c 'until ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null; do sleep 1; done' + dev=/dev/$(ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null) + if [[ ! -b "$dev" ]]; then + echo "cannot find suitable scsi block device" >&2 + exit 1 + fi + udevadm settle + + # check ACL + sleep 1 + assert_in "user:logind-test-user:rw-" "$(getfacl -p "$dev")" +} + +teardown_lock_idle_action() ( + set +eux + + rm -f /run/systemd/logind.conf.d/idle-action-lock.conf + systemctl restart systemd-logind.service + + cleanup_session + + return 0 +) + +testcase_lock_idle_action() { + local ts + + if [[ ! -c /dev/tty2 ]]; then + echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}." + return + fi + + if loginctl --no-legend | grep -v manager | grep -q logind-test-user; then + echo >&2 "Session of the 'logind-test-user' is already present." + exit 1 + fi + + trap teardown_lock_idle_action RETURN + + create_session + + journalctl --sync + ts="$(date '+%H:%M:%S')" + + mkdir -p /run/systemd/logind.conf.d + cat >/run/systemd/logind.conf.d/idle-action-lock.conf </run/systemd/logind.conf.d/stop-idle-session.conf <&2 + return + fi + + typeset -i BND MASK + + # Get PID 1's bounding set + BND="0x$(grep 'CapBnd:' /proc/1/status | cut -d: -f2 | tr -d '[:space:]')" + + # CAP_CHOWN | CAP_KILL + MASK=$(((1 << 0) | (1 << 5))) + + if [ $((BND & MASK)) -ne "$MASK" ] ; then + echo "CAP_CHOWN or CAP_KILL not available in bounding set, skipping test." >&2 + return + fi + + PAMSERVICE="pamserv$RANDOM" + TRANSIENTUNIT="capwakealarm$RANDOM.service" + SCRIPT="/tmp/capwakealarm$RANDOM.sh" + + cat > /etc/pam.d/"$PAMSERVICE" < "$SCRIPT" <<'EOF' +#!/bin/bash +set -ex +typeset -i AMB MASK +AMB="0x$(grep 'CapAmb:' /proc/self/status | cut -d: -f2 | tr -d '[:space:]')" +MASK=$(((1 << 0) | (1 << 5))) +test "$AMB" -eq "$MASK" +EOF + + chmod +x "$SCRIPT" + + systemd-run -u "$TRANSIENTUNIT" -p PAMName="$PAMSERVICE" -p Type=oneshot -p User=logind-test-user -p StandardError=tty "$SCRIPT" + + rm -f "$SCRIPT" "$PAMSERVICE" +} + +background_at_return() { + rm -f /etc/pam.d/"$PAMSERVICE" + unset PAMSERVICE +} + +testcase_background() { + + local uid TRANSIENTUNIT1 TRANSIENTUNIT2 + + uid=$(id -u logind-test-user) + + systemctl stop user@"$uid".service + + PAMSERVICE="pamserv$RANDOM" + TRANSIENTUNIT1="bg$RANDOM.service" + TRANSIENTUNIT2="bgg$RANDOM.service" + + trap background_at_return RETURN + + cat > /etc/pam.d/"$PAMSERVICE" </dev/null; do sleep 0.1; done +} + +startJournalctl() { + : >"$journalCursorFile" + # Save journal's cursor for later navigation + journalctl --no-pager --cursor-file="$journalCursorFile" -n0 -ocat +} + +stopJournalctl() { + local unit="${1:-init.scope}" + # Using journalctl --sync should be better than using SIGRTMIN+1, as + # the --sync wait until the synchronization is complete + echo "Force journald to write all queued messages" + journalctl --sync + journalctl -u "$unit" --cursor-file="$journalCursorFile" >"$journalLog" +} + +checkNUMA() { + # NUMA enabled system should have at least NUMA node0 + test -e /sys/devices/system/node/node0 +} + +writePID1NUMAPolicy() { + cat >"$confDir/numa.conf" <"$testUnitFile" +} + +writeTestUnitNUMAPolicy() { + cat >"$testUnitNUMAConf" <"$LOGFILE" + grep "NUMAPolicy=$NUMA_POLICY" "$LOGFILE" + + : >"$LOGFILE" + + if [ -n "$NUMA_MASK" ]; then + systemctl show -p NUMAMask "$UNIT_NAME" >"$LOGFILE" + grep "NUMAMask=$NUMA_MASK" "$LOGFILE" + fi +} + +writeTestUnit + +# Create systemd config drop-in directory +confDir="/run/systemd/system.conf.d/" +mkdir -p "$confDir" + +if ! checkNUMA; then + echo >&2 "NUMA is not supported on this machine, switching to a simple sanity check" + + echo "PID1 NUMAPolicy=default && NUMAMask=0 check without NUMA support" + writePID1NUMAPolicy "default" "0" + startJournalctl + systemctl daemon-reload + stopJournalctl + grep "NUMA support not available, ignoring" "$journalLog" + + echo "systemd-run NUMAPolicy=default && NUMAMask=0 check without NUMA support" + runUnit='numa-systemd-run-test.service' + startJournalctl + systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit "$runUnit" sleep 1000 + sleep $sleepAfterStart + pid1StopUnit "$runUnit" + stopJournalctl "$runUnit" + grep "NUMA support not available, ignoring" "$journalLog" + +else + echo "PID1 NUMAPolicy support - Default policy w/o mask" + writePID1NUMAPolicy "default" + pid1ReloadWithStrace + # Kernel requires that nodemask argument is set to NULL when setting default policy + grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog" + + echo "PID1 NUMAPolicy support - Default policy w/ mask" + writePID1NUMAPolicy "default" "0" + pid1ReloadWithStrace + grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog" + + echo "PID1 NUMAPolicy support - Bind policy w/o mask" + writePID1NUMAPolicy "bind" + pid1ReloadWithJournal + grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog" + + echo "PID1 NUMAPolicy support - Bind policy w/ mask" + writePID1NUMAPolicy "bind" "0" + pid1ReloadWithStrace + grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" "$straceLog" + + echo "PID1 NUMAPolicy support - Interleave policy w/o mask" + writePID1NUMAPolicy "interleave" + pid1ReloadWithJournal + grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog" + + echo "PID1 NUMAPolicy support - Interleave policy w/ mask" + writePID1NUMAPolicy "interleave" "0" + pid1ReloadWithStrace + grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" "$straceLog" + + echo "PID1 NUMAPolicy support - Preferred policy w/o mask" + writePID1NUMAPolicy "preferred" + pid1ReloadWithJournal + # Preferred policy with empty node mask is actually allowed and should reset allocation policy to default + grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog" && { echo >&2 "unexpected pass"; exit 1; } + + echo "PID1 NUMAPolicy support - Preferred policy w/ mask" + writePID1NUMAPolicy "preferred" "0" + pid1ReloadWithStrace + grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" "$straceLog" + + echo "PID1 NUMAPolicy support - Local policy w/o mask" + writePID1NUMAPolicy "local" + pid1ReloadWithStrace + # Kernel requires that nodemask argument is set to NULL when setting default policy + # The unpatched versions of strace don't recognize the MPOL_LOCAL constant and + # return a numerical constant instead (with a comment): + # set_mempolicy(0x4 /* MPOL_??? */, NULL, 0) = 0 + # Let's cover this scenario as well + grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog" + + echo "PID1 NUMAPolicy support - Local policy w/ mask" + writePID1NUMAPolicy "local" "0" + pid1ReloadWithStrace + grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog" + + echo "Unit file NUMAPolicy support - Default policy w/o mask" + writeTestUnitNUMAPolicy "default" + pid1StartUnitWithStrace "$testUnit" + systemctlCheckNUMAProperties "$testUnit" "default" + pid1StopUnit "$testUnit" + grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog" + + echo "Unit file NUMAPolicy support - Default policy w/ mask" + writeTestUnitNUMAPolicy "default" "0" + pid1StartUnitWithStrace "$testUnit" + systemctlCheckNUMAProperties "$testUnit" "default" "0" + pid1StopUnit $testUnit + # Mask must be ignored + grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog" + + echo "Unit file NUMAPolicy support - Bind policy w/o mask" + writeTestUnitNUMAPolicy "bind" + pid1StartUnitWithJournal "$testUnit" + pid1StopUnit "$testUnit" + [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]] + + echo "Unit file NUMAPolicy support - Bind policy w/ mask" + writeTestUnitNUMAPolicy "bind" "0" + pid1StartUnitWithStrace "$testUnit" + systemctlCheckNUMAProperties "$testUnit" "bind" "0" + pid1StopUnit "$testUnit" + grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" "$straceLog" + + echo "Unit file NUMAPolicy support - Interleave policy w/o mask" + writeTestUnitNUMAPolicy "interleave" + pid1StartUnitWithStrace "$testUnit" + pid1StopUnit "$testUnit" + [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]] + + echo "Unit file NUMAPolicy support - Interleave policy w/ mask" + writeTestUnitNUMAPolicy "interleave" "0" + pid1StartUnitWithStrace "$testUnit" + systemctlCheckNUMAProperties "$testUnit" "interleave" "0" + pid1StopUnit "$testUnit" + grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" "$straceLog" + + echo "Unit file NUMAPolicy support - Preferred policy w/o mask" + writeTestUnitNUMAPolicy "preferred" + pid1StartUnitWithJournal "$testUnit" + systemctlCheckNUMAProperties "$testUnit" "preferred" + pid1StopUnit "$testUnit" + [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]] && { echo >&2 "unexpected pass"; exit 1; } + + echo "Unit file NUMAPolicy support - Preferred policy w/ mask" + writeTestUnitNUMAPolicy "preferred" "0" + pid1StartUnitWithStrace "$testUnit" + systemctlCheckNUMAProperties "$testUnit" "preferred" "0" + pid1StopUnit "$testUnit" + grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" "$straceLog" + + echo "Unit file NUMAPolicy support - Local policy w/o mask" + writeTestUnitNUMAPolicy "local" + pid1StartUnitWithStrace "$testUnit" + systemctlCheckNUMAProperties "$testUnit" "local" + pid1StopUnit "$testUnit" + grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog" + + echo "Unit file NUMAPolicy support - Local policy w/ mask" + writeTestUnitNUMAPolicy "local" "0" + pid1StartUnitWithStrace "$testUnit" + systemctlCheckNUMAProperties "$testUnit" "local" "0" + pid1StopUnit "$testUnit" + # Mask must be ignored + grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog" + + echo "Unit file CPUAffinity=NUMA support" + writeTestUnitNUMAPolicy "bind" "0" + echo "CPUAffinity=numa" >>"$testUnitNUMAConf" + systemctl daemon-reload + systemctl start "$testUnit" + systemctlCheckNUMAProperties "$testUnit" "bind" "0" + cpulist="$(cat /sys/devices/system/node/node0/cpulist)" + affinity_systemd="$(systemctl show --value -p CPUAffinity "$testUnit")" + [ "$cpulist" = "$affinity_systemd" ] + pid1StopUnit "$testUnit" + + echo "systemd-run NUMAPolicy support" + runUnit='numa-systemd-run-test.service' + + systemd-run -p NUMAPolicy=default --unit "$runUnit" sleep 1000 + systemctlCheckNUMAProperties "$runUnit" "default" + pid1StopUnit "$runUnit" + + systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit "$runUnit" sleep 1000 + systemctlCheckNUMAProperties "$runUnit" "default" "" + pid1StopUnit "$runUnit" + + systemd-run -p NUMAPolicy=bind -p NUMAMask=0 --unit "$runUnit" sleep 1000 + systemctlCheckNUMAProperties "$runUnit" "bind" "0" + pid1StopUnit "$runUnit" + + systemd-run -p NUMAPolicy=interleave -p NUMAMask=0 --unit "$runUnit" sleep 1000 + systemctlCheckNUMAProperties "$runUnit" "interleave" "0" + pid1StopUnit "$runUnit" + + systemd-run -p NUMAPolicy=preferred -p NUMAMask=0 --unit "$runUnit" sleep 1000 + systemctlCheckNUMAProperties "$runUnit" "preferred" "0" + pid1StopUnit "$runUnit" + + systemd-run -p NUMAPolicy=local --unit "$runUnit" sleep 1000 + systemctlCheckNUMAProperties "$runUnit" "local" + pid1StopUnit "$runUnit" + + systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit "$runUnit" sleep 1000 + systemctlCheckNUMAProperties "$runUnit" "local" "" + pid1StopUnit "$runUnit" + + systemd-run -p NUMAPolicy=local -p NUMAMask=0 -p CPUAffinity=numa --unit "$runUnit" sleep 1000 + systemctlCheckNUMAProperties "$runUnit" "local" "" + systemctl cat "$runUnit" | grep -q 'CPUAffinity=numa' + pid1StopUnit "$runUnit" +fi + +# Cleanup +rm -rf "$confDir" +systemctl daemon-reload + +systemd-analyze log-level info + +touch /testok diff --git a/test/units/TEST-38-FREEZER-sleep.service b/test/units/TEST-38-FREEZER-sleep.service new file mode 100644 index 0000000..1bb9ddf --- /dev/null +++ b/test/units/TEST-38-FREEZER-sleep.service @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Service] +ExecStart=sleep 3600 diff --git a/test/units/TEST-38-FREEZER.sh b/test/units/TEST-38-FREEZER.sh new file mode 100755 index 0000000..0759784 --- /dev/null +++ b/test/units/TEST-38-FREEZER.sh @@ -0,0 +1,363 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2317 +set -eux +set -o pipefail + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + +systemd-analyze log-level debug + +unit=TEST-38-FREEZER-sleep.service + +start_test_service() { + systemctl daemon-reload + systemctl start "${unit}" +} + +dbus_freeze() { + local name object_path suffix + + suffix="${1##*.}" + name="${1%".$suffix"}" + object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}" + + busctl call \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + Freeze +} + +dbus_thaw() { + local name object_path suffix + + suffix="${1##*.}" + name="${1%".$suffix"}" + object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}" + + busctl call \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + Thaw +} + +dbus_freeze_unit() { + busctl call \ + org.freedesktop.systemd1 \ + /org/freedesktop/systemd1 \ + org.freedesktop.systemd1.Manager \ + FreezeUnit \ + s \ + "$1" +} + +dbus_thaw_unit() { + busctl call \ + org.freedesktop.systemd1 \ + /org/freedesktop/systemd1 \ + org.freedesktop.systemd1.Manager \ + ThawUnit \ + s \ + "$1" +} + +dbus_can_freeze() { + local name object_path suffix + + suffix="${1##*.}" + name="${1%".$suffix"}" + object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}" + + busctl get-property \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + CanFreeze +} + +check_freezer_state() { + local name object_path suffix + + suffix="${1##*.}" + name="${1%".$suffix"}" + object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}" + + for _ in {0..10}; do + state=$(busctl get-property \ + org.freedesktop.systemd1 \ + "${object_path}" \ + org.freedesktop.systemd1.Unit \ + FreezerState | cut -d " " -f2 | tr -d '"') + + # Ignore the intermediate freezing & thawing states in case we check + # the unit state too quickly + [[ "$state" =~ ^(freezing|thawing) ]] || break + sleep .5 + done + + [ "$state" = "$2" ] || { + echo "error: unexpected freezer state, expected: $2, actual: $state" >&2 + exit 1 + } +} + +check_cgroup_state() { + # foo.unit -> /system.slice/foo.unit/ + # foo.slice/ -> /foo.slice/./ + # foo.slice/foo.unit -> /foo.slice/foo.unit/ + local slice unit + unit="${1##*/}" + slice="${1%"$unit"}" + slice="${slice%/}" + grep -q "frozen $2" /sys/fs/cgroup/"${slice:-system.slice}"/"${unit:-.}"/cgroup.events +} + +testcase_dbus_api() { + echo "Test that DBus API works:" + echo -n " - Freeze(): " + dbus_freeze "${unit}" + check_freezer_state "${unit}" "frozen" + check_cgroup_state "$unit" 1 + echo "[ OK ]" + + echo -n " - Thaw(): " + dbus_thaw "${unit}" + check_freezer_state "${unit}" "running" + check_cgroup_state "$unit" 0 + echo "[ OK ]" + + echo -n " - FreezeUnit(): " + dbus_freeze_unit "${unit}" + check_freezer_state "${unit}" "frozen" + check_cgroup_state "$unit" 1 + echo "[ OK ]" + + echo -n " - ThawUnit(): " + dbus_thaw_unit "${unit}" + check_freezer_state "${unit}" "running" + check_cgroup_state "$unit" 0 + echo "[ OK ]" + + echo -n " - CanFreeze(): " + output=$(dbus_can_freeze "${unit}") + [ "$output" = "b true" ] + echo "[ OK ]" + + echo +} + +testcase_systemctl() { + echo "Test that systemctl freeze/thaw verbs:" + + systemctl start "$unit" + + echo -n " - freeze: " + systemctl freeze "$unit" + check_freezer_state "${unit}" "frozen" + check_cgroup_state "$unit" 1 + # Freezing already frozen unit should be NOP and return quickly + timeout 3s systemctl freeze "$unit" + echo "[ OK ]" + + echo -n " - thaw: " + systemctl thaw "$unit" + check_freezer_state "${unit}" "running" + check_cgroup_state "$unit" 0 + # Likewise thawing already running unit shouldn't block + timeout 3s systemctl thaw "$unit" + echo "[ OK ]" + + systemctl stop "$unit" + + echo +} + +testcase_systemctl_show() { + echo "Test systemctl show integration:" + + systemctl start "$unit" + + echo -n " - FreezerState property: " + state=$(systemctl show -p FreezerState --value "$unit") + [ "$state" = "running" ] + systemctl freeze "$unit" + state=$(systemctl show -p FreezerState --value "$unit") + [ "$state" = "frozen" ] + systemctl thaw "$unit" + echo "[ OK ]" + + echo -n " - CanFreeze property: " + state=$(systemctl show -p CanFreeze --value "$unit") + [ "$state" = "yes" ] + echo "[ OK ]" + + systemctl stop "$unit" + echo +} + +testcase_recursive() { + local slice="bar.slice" + local unit="baz.service" + + systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 + + echo "Test recursive freezing:" + + echo -n " - freeze/thaw parent: " + systemctl freeze "$slice" + check_freezer_state "$slice" "frozen" + check_freezer_state "$unit" "frozen-by-parent" + check_cgroup_state "$slice/" 1 + check_cgroup_state "$slice/$unit" 1 + systemctl thaw "$slice" + check_freezer_state "$slice" "running" + check_freezer_state "$unit" "running" + check_cgroup_state "$slice/" 0 + check_cgroup_state "$slice/$unit" 0 + echo "[ OK ]" + + echo -n " - child freeze/thaw during frozen parent: " + systemctl freeze "$slice" + check_freezer_state "$slice" "frozen" + check_freezer_state "$unit" "frozen-by-parent" + check_cgroup_state "$slice/" 1 + check_cgroup_state "$slice/$unit" 1 + systemctl freeze "$unit" + check_freezer_state "$slice" "frozen" + check_freezer_state "$unit" "frozen" + check_cgroup_state "$slice/" 1 + check_cgroup_state "$slice/$unit" 1 + systemctl thaw "$unit" + check_freezer_state "$slice" "frozen" + check_freezer_state "$unit" "frozen-by-parent" + check_cgroup_state "$slice/" 1 + check_cgroup_state "$slice/$unit" 1 + systemctl thaw "$slice" + check_freezer_state "$slice" "running" + check_freezer_state "$unit" "running" + check_cgroup_state "$slice/" 0 + check_cgroup_state "$slice/$unit" 0 + echo "[ OK ]" + + echo -n " - pre-frozen child not thawed by parent: " + systemctl freeze "$unit" + check_freezer_state "$slice" "running" + check_freezer_state "$unit" "frozen" + check_cgroup_state "$slice/" 0 + check_cgroup_state "$slice/$unit" 1 + systemctl freeze "$slice" + check_freezer_state "$slice" "frozen" + check_freezer_state "$unit" "frozen" + check_cgroup_state "$slice/" 1 + check_cgroup_state "$slice/$unit" 1 + systemctl thaw "$slice" + check_freezer_state "$slice" "running" + check_freezer_state "$unit" "frozen" + check_cgroup_state "$slice/" 0 + check_cgroup_state "$slice/$unit" 1 + echo "[ OK ]" + + echo -n " - pre-frozen child demoted and thawed by parent: " + systemctl freeze "$slice" + check_freezer_state "$slice" "frozen" + check_freezer_state "$unit" "frozen" + check_cgroup_state "$slice/" 1 + check_cgroup_state "$slice/$unit" 1 + systemctl thaw "$unit" + check_freezer_state "$slice" "frozen" + check_freezer_state "$unit" "frozen-by-parent" + check_cgroup_state "$slice/" 1 + check_cgroup_state "$slice/$unit" 1 + systemctl thaw "$slice" + check_freezer_state "$slice" "running" + check_freezer_state "$unit" "running" + check_cgroup_state "$slice/" 0 + check_cgroup_state "$slice/$unit" 0 + echo "[ OK ]" + + echo -n " - child promoted and not thawed by parent: " + systemctl freeze "$slice" + check_freezer_state "$slice" "frozen" + check_freezer_state "$unit" "frozen-by-parent" + check_cgroup_state "$slice/" 1 + check_cgroup_state "$slice/$unit" 1 + systemctl freeze "$unit" + check_freezer_state "$slice" "frozen" + check_freezer_state "$unit" "frozen" + check_cgroup_state "$slice/" 1 + check_cgroup_state "$slice/$unit" 1 + systemctl thaw "$slice" + check_freezer_state "$slice" "running" + check_freezer_state "$unit" "frozen" + check_cgroup_state "$slice/" 0 + check_cgroup_state "$slice/$unit" 1 + echo "[ OK ]" + + echo -n " - can't stop a frozen unit: " + (! systemctl -q stop "$unit" ) + echo "[ OK ]" + systemctl thaw "$unit" + + systemctl stop "$unit" + systemctl stop "$slice" + + echo +} + +testcase_preserve_state() { + local slice="bar.slice" + local unit="baz.service" + + systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 + + echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):" + + echo -n " - freeze from outside: " + echo 1 >/sys/fs/cgroup/"$slice"/cgroup.freeze + # Give kernel some time to freeze the slice + sleep 1 + + # Our state should not be affected + check_freezer_state "$slice" "running" + check_freezer_state "$unit" "running" + + # However actual kernel state should be frozen + check_cgroup_state "$slice/" 1 + check_cgroup_state "$slice/$unit" 1 + echo "[ OK ]" + + echo -n " - thaw from outside: " + echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze + sleep 1 + + check_freezer_state "$unit" "running" + check_freezer_state "$slice" "running" + check_cgroup_state "$slice/" 0 + check_cgroup_state "$slice/$unit" 0 + echo "[ OK ]" + + echo -n " - thaw from outside while inner service is frozen: " + systemctl freeze "$unit" + check_freezer_state "$unit" "frozen" + echo 1 >/sys/fs/cgroup/"$slice"/cgroup.freeze + echo 0 >/sys/fs/cgroup/"$slice"/cgroup.freeze + check_freezer_state "$slice" "running" + check_freezer_state "$unit" "frozen" + echo "[ OK ]" + + systemctl thaw "$unit" + systemctl stop "$unit" + systemctl stop "$slice" + + echo +} + +if [[ -e /sys/fs/cgroup/system.slice/cgroup.freeze ]]; then + start_test_service + run_testcases +fi + +touch /testok diff --git a/test/units/TEST-43-PRIVATEUSER-UNPRIV.sh b/test/units/TEST-43-PRIVATEUSER-UNPRIV.sh new file mode 100755 index 0000000..165af47 --- /dev/null +++ b/test/units/TEST-43-PRIVATEUSER-UNPRIV.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +install_extension_images + +if [[ "$(sysctl -ne kernel.apparmor_restrict_unprivileged_userns)" -eq 1 ]]; then + echo "Cannot create unprivileged user namespaces" >/skipped + exit 77 +fi + +systemd-analyze log-level debug + +runas testuser systemd-run --wait --user --unit=test-private-users \ + -p PrivateUsers=yes -P echo hello + +runas testuser systemctl --user log-level debug + +runas testuser systemd-run --wait --user --unit=test-private-tmp-innerfile \ + -p PrivateTmp=yes \ + -P touch /tmp/innerfile.txt +# File should not exist outside the job's tmp directory. +test ! -e /tmp/innerfile.txt + +touch /tmp/outerfile.txt +# File should not appear in unit's private tmp. +runas testuser systemd-run --wait --user --unit=test-private-tmp-outerfile \ + -p PrivateTmp=yes \ + -P test ! -e /tmp/outerfile.txt + +# Confirm that creating a file in home works +runas testuser systemd-run --wait --user --unit=test-unprotected-home \ + -P touch /home/testuser/works.txt +test -e /home/testuser/works.txt + +# Confirm that creating a file in home is blocked under read-only +(! runas testuser systemd-run --wait --user --unit=test-protect-home-read-only \ + -p ProtectHome=read-only \ + -P bash -c ' + test -e /home/testuser/works.txt || exit 10 + touch /home/testuser/blocked.txt && exit 11 + ') +test ! -e /home/testuser/blocked.txt + +# Check that tmpfs hides the whole directory +runas testuser systemd-run --wait --user --unit=test-protect-home-tmpfs \ + -p ProtectHome=tmpfs \ + -P test ! -e /home/testuser + +# Confirm that home, /root, and /run/user are inaccessible under "yes" +# shellcheck disable=SC2016 +runas testuser systemd-run --wait --user --unit=test-protect-home-yes \ + -p ProtectHome=yes \ + -P bash -c ' + test "$(stat -c %a /home)" = "0" + test "$(stat -c %a /root)" = "0" + test "$(stat -c %a /run/user)" = "0" + ' + +# Confirm we cannot change groups because we only have one mapping in the user +# namespace (no CAP_SETGID in the parent namespace to write the additional +# mapping of the user supplied group and thus cannot change groups to an +# unmapped group ID) +(! runas testuser systemd-run --wait --user --unit=test-group-fail \ + -p PrivateUsers=yes -p Group=daemon \ + -P true) + +# Check that with a new user namespace we can bind mount +# files and use a different root directory +runas testuser systemd-run --wait --user --unit=test-bind-mount \ + -p BindPaths=/dev/null:/etc/os-release \ + test ! -s /etc/os-release + +runas testuser systemd-run --wait --user --unit=test-read-write \ + -p ReadOnlyPaths=/ \ + -p ReadWritePaths="/var /run /tmp" \ + -p NoExecPaths=/ -p ExecPaths=/usr \ + test ! -w /etc/os-release + +runas testuser systemd-run --wait --user --unit=test-caps \ + -p PrivateUsers=yes -p AmbientCapabilities=CAP_SYS_ADMIN \ + -p CapabilityBoundingSet=CAP_SYS_ADMIN \ + test -s /etc/os-release + +runas testuser systemd-run --wait --user --unit=test-devices \ + -p PrivateDevices=yes -p PrivateIPC=yes \ + sh -c "ls -1 /dev/ | wc -l | grep -q -F 18" + +# Same check as test/test-execute/exec-privatenetwork-yes.service +runas testuser systemd-run --wait --user --unit=test-network \ + -p PrivateNetwork=yes \ + /bin/sh -x -c '! ip link | grep -E "^[0-9]+: " | grep -Ev ": (lo|(erspan|gre|gretap|ip_vti|ip6_vti|ip6gre|ip6tnl|sit|tunl)0@.*):"' + +(! runas testuser systemd-run --wait --user --unit=test-hostname \ + -p ProtectHostname=yes \ + hostnamectl hostname foo) + +(! runas testuser systemd-run --wait --user --unit=test-clock \ + -p ProtectClock=yes \ + timedatectl set-time "2012-10-30 18:17:16") + +(! runas testuser systemd-run --wait --user --unit=test-kernel-tunable \ + -p ProtectKernelTunables=yes \ + sh -c "echo 0 >/proc/sys/user/max_user_namespaces") + +(! runas testuser systemd-run --wait --user --unit=test-kernel-mod \ + -p ProtectKernelModules=yes \ + sh -c "modprobe -r overlay && modprobe overlay") + +if sysctl kernel.dmesg_restrict=0; then + (! runas testuser systemd-run --wait --user --unit=test-kernel-log \ + -p ProtectKernelLogs=yes -p LogNamespace=yes \ + dmesg) +fi + +unsquashfs -no-xattrs -d /tmp/img /usr/share/minimal_0.raw +runas testuser systemd-run --wait --user --unit=test-root-dir \ + -p RootDirectory=/tmp/img \ + grep MARKER=1 /etc/os-release + +mkdir /tmp/img_bind +mount --bind /tmp/img /tmp/img_bind +runas testuser systemd-run --wait --user --unit=test-root-dir-bind \ + -p RootDirectory=/tmp/img_bind -p MountFlags=private \ + grep MARKER=1 /etc/os-release +umount /tmp/img_bind + +# Unprivileged overlayfs was added to Linux 5.11, so try to detect it first +mkdir -p /tmp/a /tmp/b /tmp/c +if unshare --mount --user --map-root-user mount -t overlay overlay /tmp/c -o lowerdir=/tmp/a:/tmp/b; then + unsquashfs -no-xattrs -d /tmp/app2 /tmp/app1.raw + runas testuser systemd-run --wait --user --unit=test-extension-dir \ + -p ExtensionDirectories=/tmp/app2 \ + -p TemporaryFileSystem=/run -p RootDirectory=/tmp/img \ + -p MountAPIVFS=yes \ + grep PORTABLE_PREFIXES=app1 /usr/lib/extension-release.d/extension-release.app2 +fi + +systemd-analyze log-level info + +touch /testok diff --git a/test/units/TEST-44-LOG-NAMESPACE.sh b/test/units/TEST-44-LOG-NAMESPACE.sh new file mode 100755 index 0000000..0819a4b --- /dev/null +++ b/test/units/TEST-44-LOG-NAMESPACE.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux + +systemd-analyze log-level debug + +journalctl --list-namespaces -o json | jq . + +systemd-run --wait -p LogNamespace=foobar echo "hello world" +systemd-run --wait -p LogNamespace=foobaz echo "hello world" + +journalctl --namespace=foobar --sync +journalctl --namespace=foobaz --sync +ls -l /var/log/journal/ +journalctl --list-namespaces + +journalctl -o cat --namespace=foobar >/tmp/hello-world +journalctl -o cat >/tmp/no-hello-world + +journalctl --list-namespaces | grep foobar +journalctl --list-namespaces | grep foobaz +journalctl --list-namespaces -o json | jq . +[[ "$(journalctl --root=/tmp --list-namespaces --quiet)" == "" ]] + +grep "^hello world$" /tmp/hello-world +(! grep "^hello world$" /tmp/no-hello-world) + +systemd-analyze log-level info + +touch /testok diff --git a/test/units/TEST-45-TIMEDATE.sh b/test/units/TEST-45-TIMEDATE.sh new file mode 100755 index 0000000..dff3ed0 --- /dev/null +++ b/test/units/TEST-45-TIMEDATE.sh @@ -0,0 +1,408 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later + +set -eux +set -o pipefail + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +testcase_timedatectl() { + timedatectl --no-pager --help + timedatectl --version + + timedatectl + timedatectl --no-ask-password + timedatectl status --machine=testuser@.host + timedatectl status + timedatectl show + timedatectl show --all + timedatectl show -p NTP + timedatectl show -p NTP --value + timedatectl list-timezones + + if ! systemd-detect-virt -qc; then + systemctl enable --runtime --now systemd-timesyncd + timedatectl timesync-status + timedatectl show-timesync + fi +} + +restore_timezone() { + if [[ -f /tmp/timezone.bak ]]; then + mv /tmp/timezone.bak /etc/timezone + else + rm -f /etc/timezone + fi +} + +testcase_timezone() { + local ORIG_TZ= + + # Debian/Ubuntu specific file + if [[ -f /etc/timezone ]]; then + mv /etc/timezone /tmp/timezone.bak + fi + + trap restore_timezone RETURN + + if [[ -L /etc/localtime ]]; then + ORIG_TZ=$(readlink /etc/localtime | sed 's#^.*zoneinfo/##') + echo "original tz: $ORIG_TZ" + fi + + echo 'timedatectl works' + assert_in "Local time:" "$(timedatectl --no-pager)" + + echo 'change timezone' + assert_eq "$(timedatectl --no-pager set-timezone Europe/Kyiv 2>&1)" "" + assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "Europe/Kyiv" + if [[ -f /etc/timezone ]]; then + assert_eq "$(cat /etc/timezone)" "Europe/Kyiv" + fi + assert_in "Time zone: Europe/Kyiv \(EES*T, \+0[0-9]00\)" "$(timedatectl)" + + if [[ -n "$ORIG_TZ" ]]; then + echo 'reset timezone to original' + assert_eq "$(timedatectl set-timezone "$ORIG_TZ" 2>&1)" "" + assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "$ORIG_TZ" + if [[ -f /etc/timezone ]]; then + assert_eq "$(cat /etc/timezone)" "$ORIG_TZ" + fi + fi +} + +restore_adjtime() { + if [[ -e /etc/adjtime.bak ]]; then + mv /etc/adjtime.bak /etc/adjtime + else + rm /etc/adjtime + fi +} + +check_adjtime_not_exist() { + if [[ -e /etc/adjtime ]]; then + echo "/etc/adjtime unexpectedly exists." >&2 + exit 1 + fi +} + +testcase_adjtime() { + # test setting UTC vs. LOCAL in /etc/adjtime + if [[ -e /etc/adjtime ]]; then + mv /etc/adjtime /etc/adjtime.bak + fi + + trap restore_adjtime RETURN + + echo 'no adjtime file' + rm -f /etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + timedatectl set-local-rtc 0 + check_adjtime_not_exist + + echo 'UTC set in adjtime file' + printf '0.0 0 0\n0\nUTC\n' >/etc/adjtime + timedatectl set-local-rtc 0 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +UTC" + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + + echo 'non-zero values in adjtime file' + printf '0.1 123 0\n0\nLOCAL\n' >/etc/adjtime + timedatectl set-local-rtc 0 + assert_eq "$(cat /etc/adjtime)" "0.1 123 0 +0 +UTC" + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.1 123 0 +0 +LOCAL" + + echo 'fourth line adjtime file' + printf '0.0 0 0\n0\nLOCAL\nsomethingelse\n' >/etc/adjtime + timedatectl set-local-rtc 0 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +UTC +somethingelse" + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL +somethingelse" + + echo 'no final newline in adjtime file' + printf '0.0 0 0\n0\nUTC' >/etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + printf '0.0 0 0\n0\nUTC' >/etc/adjtime + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + + echo 'only one line in adjtime file' + printf '0.0 0 0\n' >/etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + printf '0.0 0 0\n' >/etc/adjtime + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + + echo 'only one line in adjtime file, no final newline' + printf '0.0 0 0' >/etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + printf '0.0 0 0' >/etc/adjtime + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + + echo 'only two lines in adjtime file' + printf '0.0 0 0\n0\n' >/etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + printf '0.0 0 0\n0\n' >/etc/adjtime + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + + echo 'only two lines in adjtime file, no final newline' + printf '0.0 0 0\n0' >/etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + printf '0.0 0 0\n0' >/etc/adjtime + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" + + echo 'unknown value in 3rd line of adjtime file' + printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime + timedatectl set-local-rtc 0 + check_adjtime_not_exist + printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime + timedatectl set-local-rtc 1 + assert_eq "$(cat /etc/adjtime)" "0.0 0 0 +0 +LOCAL" +} + +assert_ntp() { + local value="${1:?}" + + for _ in {0..9}; do + [[ "$(busctl get-property org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 NTP)" == "b $value" ]] && return 0 + sleep .5 + done + + return 1 +} + +assert_timedated_signal() { + local timestamp="${1:?}" + local value="${2:?}" + local args=(-q -n 1 --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor.service") + + journalctl --sync + + for _ in {0..9}; do + if journalctl "${args[@]}" --grep .; then + [[ "$(journalctl "${args[@]}" -o cat | jq -r '.payload.data[1].NTP.data')" == "$value" ]]; + return 0 + fi + + sleep .5 + done + + return 1 +} + +assert_timesyncd_state() { + local state="${1:?}" + + for _ in {0..9}; do + [[ "$(systemctl show systemd-timesyncd.service -P ActiveState)" == "$state" ]] && return 0 + sleep .5 + done + + return 1 +} + +testcase_ntp() { + # timesyncd has ConditionVirtualization=!container by default; drop/mock that for testing + if systemd-detect-virt --container --quiet; then + systemctl disable --quiet --now systemd-timesyncd + mkdir -p /run/systemd/system/systemd-timesyncd.service.d + cat >/run/systemd/system/systemd-timesyncd.service.d/container.conf </dev/null; then + echo "This test requires systemd-networkd, skipping..." + return 0 + fi + + # Create a dummy interface managed by networkd, so we can configure link NTP servers + mkdir -p /run/systemd/network/ + cat >/etc/systemd/network/10-ntp99.netdev </etc/systemd/network/10-ntp99.network </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" </etc/ssh/sshd_config </run/systemd/system/mysshserver.socket </run/systemd/system/mysshserver@.service </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 "$(/run/systemd/system/testservice-50a.service </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 </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 </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 </run/systemd/system/testservice-50e.service </run/systemd/system/testservice-50f.service </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 </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:?}" <"$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/. + 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. 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. 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. file - + # this causes the /usr hierarchy to also become mutable + extension_data_dir_usr="$fake_root/var/lib/extensions.mutable/usr" + prepend_trap "rm -rf ${extension_data_dir_usr@Q}" + fi + + permissions_after_merge=$(stat --format=%A "$full_path") + + run_systemd_sysext "$fake_root" unmerge + + permissions_after_unmerge=$(stat --format=%A "$full_path") + + if [[ "$permissions_before_merge" != "$permissions_after_merge" ]]; then + echo >&2 "Broken hierarchy permissions after merging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_merge@Q}" + exit 1 + fi + + if [[ "$permissions_before_merge" != "$permissions_after_unmerge" ]]; then + echo >&2 "Broken hierarchy permissions after unmerging with mutable mode ${mutable_mode@Q}, expected ${permissions_before_merge@Q}, got ${permissions_after_unmerge@Q}" + exit 1 + fi + ) +done + + +( init_trap +: "Check if merging fails in case of invalid mutable directory permissions" + +fake_root=${roots_dir:+"$roots_dir/mutable-directory-with-invalid-permissions"} +hierarchy=/opt +extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy" + +prepare_root "$fake_root" "$hierarchy" +prepare_extension_image "$fake_root" "$hierarchy" +prepare_extension_mutable_dir "$extension_data_dir" +prepare_hierarchy "$fake_root" "$hierarchy" + +old_mode=$(stat --format '%#a' "$fake_root$hierarchy") +chmod 0755 "$fake_root$hierarchy" +prepend_trap "chmod ${old_mode@Q} ${fake_root@Q}${hierarchy@Q}" +chmod 0700 "$extension_data_dir" + +(! run_systemd_sysext "$fake_root" --mutable=yes merge) +) + +} # End of run_sysext_tests + + +# For preparing /, we need mutable /usr/. If it is read only, skip running the +# sysext tests on /. +if [[ -w /usr ]]; then + run_sysext_tests '' +fi +run_sysext_tests "$FAKE_ROOTS_DIR" + + +exit 0 diff --git a/test/units/TEST-52-HONORFIRSTSHUTDOWN.sh b/test/units/TEST-52-HONORFIRSTSHUTDOWN.sh new file mode 100755 index 0000000..16ff507 --- /dev/null +++ b/test/units/TEST-52-HONORFIRSTSHUTDOWN.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +systemd-analyze log-level debug + +systemctl enable test-honor-first-shutdown.service +systemctl start test-honor-first-shutdown.service + +touch /testok diff --git a/test/units/TEST-53-ISSUE-16347.sh b/test/units/TEST-53-ISSUE-16347.sh new file mode 100755 index 0000000..84cd661 --- /dev/null +++ b/test/units/TEST-53-ISSUE-16347.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +: >/failed + +# Reset host date to current time, 3 days in the past. +date -s "-3 days" + +# Run a timer for every 15 minutes. +systemd-run --unit test-timer --on-calendar "*:0/15:0" true + +next_elapsed=$(systemctl show test-timer.timer -p NextElapseUSecRealtime --value) +next_elapsed=$(date -d "${next_elapsed}" +%s) +now=$(date +%s) +time_delta=$((next_elapsed - now)) + +# Check that the timer will elapse in less than 20 minutes. +((0 < time_delta && time_delta < 1200)) || { + echo 'Timer elapse outside of the expected 20 minute window.' + echo " next_elapsed=${next_elapsed}" + echo " now=${now}" + echo " time_delta=${time_delta}" + echo '' +} >>/failed + +if test ! -s /failed ; then + rm -f /failed + touch /testok +fi diff --git a/test/units/TEST-54-CREDS.sh b/test/units/TEST-54-CREDS.sh new file mode 100755 index 0000000..fe410d5 --- /dev/null +++ b/test/units/TEST-54-CREDS.sh @@ -0,0 +1,402 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2016 +set -eux + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +systemd-analyze log-level debug + +run_with_cred_compare() ( + local cred="${1:?}" + local exp="${2?}" + local log_file + shift 2 + + log_file="$(mktemp)" + # shellcheck disable=SC2064 + trap "rm -f '$log_file'" RETURN + + set -o pipefail + systemd-run -p SetCredential="$cred" --wait --pipe -- systemd-creds "$@" | tee "$log_file" + diff "$log_file" <(echo -ne "$exp") +) + +# Sanity checks +# +# Create a dummy "full" disk (similar to /dev/full) to check out-of-space +# scenarios +mkdir /tmp/full +mount -t tmpfs -o size=1,nr_inodes=1 tmpfs /tmp/full + +# verb: setup +# Run this first, otherwise any encrypted credentials wouldn't be decryptable +# as we regenerate the host key +rm -fv /var/lib/systemd/credential.secret +systemd-creds setup +test -e /var/lib/systemd/credential.secret +rm -fv /var/lib/systemd/credential.secret + +# Prepare a couple of dummy credentials for the cat/list verbs +CRED_DIR="$(mktemp -d)" +ENC_CRED_DIR="$(mktemp -d)" +echo foo >"$CRED_DIR/secure-or-weak" +echo foo >"$CRED_DIR/insecure" +echo foo | systemd-creds --name="encrypted" encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted" +echo foo | systemd-creds encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted-unnamed" +chmod -R 0400 "$CRED_DIR" "$ENC_CRED_DIR" +chmod -R 0444 "$CRED_DIR/insecure" +mkdir /tmp/empty/ + +systemd-creds --system +systemd-creds --no-pager --help +systemd-creds --version +systemd-creds has-tpm2 || : +systemd-creds has-tpm2 -q || : + +# verb: list +systemd-creds list --system +ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --no-legend +ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=pretty | jq +ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=short | jq +ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=off +ENCRYPTED_CREDENTIALS_DIRECTORY="/tmp/empty/" CREDENTIALS_DIRECTORY="/tmp/empty/" systemd-creds list + +# verb: cat +for cred in secure-or-weak insecure encrypted encrypted-unnamed; do + ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds cat "$cred" +done +run_with_cred_compare "mycred:" "" cat mycred +run_with_cred_compare "mycred:\n" "\n" cat mycred +run_with_cred_compare "mycred:foo" "foo" cat mycred +run_with_cred_compare "mycred:foo" "foofoofoo" cat mycred mycred mycred +# Note: --newline= does nothing when stdout is not a tty, which is the case here +run_with_cred_compare "mycred:foo" "foo" --newline=yes cat mycred +run_with_cred_compare "mycred:foo" "foo" --newline=no cat mycred +run_with_cred_compare "mycred:foo" "foo" --newline=auto cat mycred +run_with_cred_compare "mycred:foo" "foo" --transcode=no cat mycred +run_with_cred_compare "mycred:foo" "foo" --transcode=0 cat mycred +run_with_cred_compare "mycred:foo" "foo" --transcode=false cat mycred +run_with_cred_compare "mycred:foo" "Zm9v" --transcode=base64 cat mycred +run_with_cred_compare "mycred:Zm9v" "foo" --transcode=unbase64 cat mycred +run_with_cred_compare "mycred:Zm9v" "foofoofoo" --transcode=unbase64 cat mycred mycred mycred +run_with_cred_compare "mycred:Zm9vCg==" "foo\n" --transcode=unbase64 cat mycred +run_with_cred_compare "mycred:hello world" "68656c6c6f20776f726c64" --transcode=hex cat mycred +run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello world" --transcode=unhex cat mycred +run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello worldhello world" --transcode=unhex cat mycred mycred +run_with_cred_compare "mycred:68656c6c6f0a776f726c64" "hello\nworld" --transcode=unhex cat mycred +run_with_cred_compare 'mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' '{"foo":"bar","baz":[3,4]}\n' --json=short cat mycred +systemd-run -p SetCredential='mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' --wait --pipe -- systemd-creds --json=pretty cat mycred | jq + +# verb: encrypt/decrypt +echo "According to all known laws of aviation..." >/tmp/cred.orig +systemd-creds --with-key=host encrypt /tmp/cred.orig /tmp/cred.enc +systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec +diff /tmp/cred.orig /tmp/cred.dec +rm -f /tmp/cred.{enc,dec} +# --pretty +cred_name="fo'''o''bar" +cred_option="$(systemd-creds --pretty --name="$cred_name" encrypt /tmp/cred.orig -)" +mkdir -p /run/systemd/system +cat >/run/systemd/system/test-54-pretty-cred.service </tmp/ts54-concat +(cat /etc/passwd /etc/shadow && echo -n wuff) | cmp /tmp/ts54-concat +rm /tmp/ts54-concat + +# Test that SetCredential= acts as fallback for LoadCredential= +echo piff >/tmp/ts54-fallback +[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "piff" ] +rm /tmp/ts54-fallback +[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "poff" ] + +if systemd-detect-virt -q -c ; then + expected_credential=mynspawncredential + expected_value=strangevalue +elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then + # Verify that passing creds through kernel cmdline works + [ "$(systemd-creds --system cat kernelcmdlinecred)" = "uff" ] + [ "$(systemd-creds --system cat waldi)" = "woooofffwufffwuff" ] + + # And that it also works via SMBIOS + [ "$(systemd-creds --system cat smbioscredential)" = "magicdata" ] + [ "$(systemd-creds --system cat binarysmbioscredential)" = "magicbinarydata" ] + + # If we aren't run in nspawn, we are run in qemu + systemd-detect-virt -q -v + expected_credential=myqemucredential + expected_value=othervalue + + # Verify that writing a sysctl via the kernel cmdline worked + [ "$(cat /proc/sys/kernel/domainname)" = "sysctltest" ] + + # Verify that creating a user via sysusers via the kernel cmdline worked + grep -q ^credtestuser: /etc/passwd + + # Verify that writing a file via tmpfiles worked + [ "$(cat /tmp/sourcedfromcredential)" = "tmpfilessecret" ] + [ "$(cat /etc/motd.d/50-provision.conf)" = "hello" ] + [ "$(cat /etc/issue.d/50-provision.conf)" = "welcome" ] + + # Verify that adding a unit and drop-in via credentials worked + systemctl start my-service + test -f /tmp/unit-cred + test -f /tmp/unit-dropin + test -f /tmp/unit-named-dropin +else + echo "qemu_fw_cfg support missing in kernel. Sniff!" + expected_credential="" + expected_value="" +fi + +if [ "$expected_credential" != "" ] ; then + # If this test is run in nspawn a credential should have been passed to us. See test/TEST-54-CREDS/test.sh + [ "$(systemd-creds --system cat "$expected_credential")" = "$expected_value" ] + + # Test that propagation from system credential to service credential works + [ "$(systemd-run -p LoadCredential="$expected_credential" --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ] + + # Check it also works, if we rename it while propagating it + [ "$(systemd-run -p LoadCredential=miau:"$expected_credential" --pipe --wait systemd-creds cat miau)" = "$expected_value" ] + + # Combine it with a fallback (which should have no effect, given the cred should be passed down) + [ "$(systemd-run -p LoadCredential="$expected_credential" -p SetCredential="$expected_credential":zzz --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ] + + # This should succeed + systemd-run -p AssertCredential="$expected_credential" -p Type=oneshot true + + # And this should fail + (! systemd-run -p AssertCredential="undefinedcredential" -p Type=oneshot true) +fi + +# Verify that the creds are immutable +(! systemd-run -p LoadCredential=passwd:/etc/passwd \ + -p DynamicUser=1 \ + --unit=test-54-immutable-touch.service \ + --wait \ + touch '${CREDENTIALS_DIRECTORY}/passwd') +(! systemd-run -p LoadCredential=passwd:/etc/passwd \ + -p DynamicUser=1 \ + --unit=test-54-immutable-rm.service \ + --wait \ + rm '${CREDENTIALS_DIRECTORY}/passwd') + +# Check directory-based loading +mkdir -p /tmp/ts54-creds/sub +echo -n a >/tmp/ts54-creds/foo +echo -n b >/tmp/ts54-creds/bar +echo -n c >/tmp/ts54-creds/baz +echo -n d >/tmp/ts54-creds/sub/qux +systemd-run -p LoadCredential=cred:/tmp/ts54-creds \ + -p DynamicUser=1 \ + --unit=test-54-dir.service \ + --wait \ + --pipe \ + cat '${CREDENTIALS_DIRECTORY}/cred_foo' \ + '${CREDENTIALS_DIRECTORY}/cred_bar' \ + '${CREDENTIALS_DIRECTORY}/cred_baz' \ + '${CREDENTIALS_DIRECTORY}/cred_sub_qux' >/tmp/ts54-concat +cmp /tmp/ts54-concat <(echo -n abcd) +rm /tmp/ts54-concat +rm -rf /tmp/ts54-creds + +# Check that globs work as expected +mkdir -p /run/credstore +echo -n a >/run/credstore/test.creds.first +echo -n b >/run/credstore/test.creds.second +mkdir -p /etc/credstore +echo -n c >/etc/credstore/test.creds.third +systemd-run -p "ImportCredential=test.creds.*" \ + --unit=test-54-ImportCredential.service \ + -p DynamicUser=1 \ + --wait \ + --pipe \ + cat '${CREDENTIALS_DIRECTORY}/test.creds.first' \ + '${CREDENTIALS_DIRECTORY}/test.creds.second' \ + '${CREDENTIALS_DIRECTORY}/test.creds.third' >/tmp/ts54-concat +cmp /tmp/ts54-concat <(echo -n abc) + +# Now test encrypted credentials (only supported when built with OpenSSL though) +if systemctl --version | grep -q -- +OPENSSL ; then + echo -n $RANDOM >/tmp/test-54-plaintext + systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext + systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext + + systemd-run -p LoadCredentialEncrypted=test-54:/tmp/test-54-ciphertext \ + --wait \ + --pipe \ + cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext + + echo -n $RANDOM >/tmp/test-54-plaintext + systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext + systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext + + systemd-run -p SetCredentialEncrypted=test-54:"$(cat /tmp/test-54-ciphertext)" \ + --wait \ + --pipe \ + cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext + + rm /tmp/test-54-plaintext /tmp/test-54-ciphertext +fi + +# https://github.com/systemd/systemd/issues/27275 +systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \ + -p 'ExecStartPre=true' \ + -p 'ExecStartPre=systemd-creds cat os' \ + --unit=test-54-exec-start-pre.service \ + --wait \ + --pipe \ + true | cmp /etc/os-release + +# https://github.com/systemd/systemd/issues/31194 +systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \ + -p 'ExecStartPost=systemd-creds cat os' \ + --unit=test-54-exec-start-post.service \ + --service-type=oneshot --wait --pipe \ + true | cmp /etc/os-release + +# https://github.com/systemd/systemd/pull/24734#issuecomment-1925440546 +# Also ExecStartPre= should be able to update creds +dd if=/dev/urandom of=/tmp/cred-huge bs=600K count=1 +chmod 777 /tmp/cred-huge +systemd-run -p ProtectSystem=full \ + -p 'LoadCredential=huge:/tmp/cred-huge' \ + -p 'ExecStartPre=true' \ + -p 'ExecStartPre=bash -c "echo fresh >/tmp/cred-huge"' \ + --unit=test-54-huge-cred.service \ + --wait --pipe \ + systemd-creds cat huge | cmp - <(echo "fresh") +rm /tmp/cred-huge + +echo stable >/tmp/cred-stable +systemd-run -p 'LoadCredential=stable:/tmp/cred-stable' \ + -p 'ExecStartPost=systemd-creds cat stable' \ + --unit=test-54-stable.service \ + --service-type=oneshot --wait --pipe \ + bash -c "echo bogus >/tmp/cred-stable" | cmp - <(echo "stable") +assert_eq "$(cat /tmp/cred-stable)" "bogus" +rm /tmp/cred-stable + +if ! systemd-detect-virt -q -c ; then + # Validate that the credential we inserted via the initrd logic arrived + test "$(systemd-creds cat --system myinitrdcred)" = "guatemala" + + # Check that the fstab credential logic worked + test -d /injected + grep -q /injected /proc/self/mountinfo + + # Make sure the getty generator processed the credentials properly + systemctl -P Wants show getty.target | grep -q container-getty@idontexist.service +fi + +# Decrypt/encrypt via varlink + +echo '{"data":"Zm9vYmFyCg=="}' > /tmp/vlcredsdata + +varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Encrypt "$(cat /tmp/vlcredsdata)" | \ + varlinkctl call --json=short /run/systemd/io.systemd.Credentials io.systemd.Credentials.Decrypt > /tmp/vlcredsdata2 + +cmp /tmp/vlcredsdata /tmp/vlcredsdata2 +rm /tmp/vlcredsdata /tmp/vlcredsdata2 + +clean_usertest() { + rm -f /tmp/usertest.data /tmp/usertest.data +} + +trap clean_usertest EXIT +dd if=/dev/urandom of=/tmp/usertest.data bs=4096 count=1 + +systemd-creds encrypt --user /tmp/usertest.data /tmp/usertest.cred + +systemd-creds decrypt --user /tmp/usertest.cred - | cmp /tmp/usertest.data + +# Decryption must fail if it's not done in user context +(! systemd-creds decrypt /tmp/usertest.cred - ) + +# Decryption must also fail if a different user is used +(! systemd-creds decrypt --user --uid=65534 /tmp/usertest.cred - ) + +# Try the reverse +systemd-creds encrypt --user --uid=65534 /tmp/usertest.data /tmp/usertest.cred +(! systemd-creds decrypt --user /tmp/usertest.cred - ) +systemd-creds decrypt --user --uid=65534 /tmp/usertest.cred - | cmp /tmp/usertest.data + +systemd-creds encrypt --user /tmp/usertest.data /tmp/usertest.creds --name=mytest + +# Make sure we actually can decode this in user context +systemctl start user@0.service +XDG_RUNTIME_DIR=/run/user/0 systemd-run --pipe --user --unit=waldi.service -p LoadCredentialEncrypted=mytest:/tmp/usertest.creds cat /run/user/0/credentials/waldi.service/mytest | cmp /tmp/usertest.data + +systemd-analyze log-level info + +touch /testok diff --git a/test/units/TEST-55-OOMD-testbloat.service b/test/units/TEST-55-OOMD-testbloat.service new file mode 100644 index 0000000..ba4f2bc --- /dev/null +++ b/test/units/TEST-55-OOMD-testbloat.service @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=Create a lot of memory pressure + +[Service] +# A VERY small memory.high will cause the 'stress' (trying to use a lot of memory) +# to throttle and be put under heavy pressure. +MemoryHigh=3M +Slice=TEST-55-OOMD-workload.slice +ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1 diff --git a/test/units/TEST-55-OOMD-testchill.service b/test/units/TEST-55-OOMD-testchill.service new file mode 100644 index 0000000..1f708dd --- /dev/null +++ b/test/units/TEST-55-OOMD-testchill.service @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=No memory pressure + +[Service] +MemoryHigh=3M +Slice=TEST-55-OOMD-workload.slice +ExecStart=sleep infinity diff --git a/test/units/TEST-55-OOMD-testmunch.service b/test/units/TEST-55-OOMD-testmunch.service new file mode 100644 index 0000000..5659906 --- /dev/null +++ b/test/units/TEST-55-OOMD-testmunch.service @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=Create some memory pressure + +[Service] +MemoryHigh=12M +Slice=TEST-55-OOMD-workload.slice +ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1 diff --git a/test/units/TEST-55-OOMD-workload.slice b/test/units/TEST-55-OOMD-workload.slice new file mode 100644 index 0000000..d117b75 --- /dev/null +++ b/test/units/TEST-55-OOMD-workload.slice @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=Test slice for memory pressure kills + +[Slice] +CPUAccounting=true +MemoryAccounting=true +IOAccounting=true +TasksAccounting=true +ManagedOOMMemoryPressure=kill +ManagedOOMMemoryPressureLimit=20% diff --git a/test/units/TEST-55-OOMD.sh b/test/units/TEST-55-OOMD.sh new file mode 100755 index 0000000..b04ebca --- /dev/null +++ b/test/units/TEST-55-OOMD.sh @@ -0,0 +1,190 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh + . "$(dirname "$0")"/util.sh + +. /etc/os-release +# OpenSUSE does not have the stress tool packaged. It does have stress-ng but the stress-ng does not support +# --vm-stride which this test uses. +if [[ "$ID" =~ "opensuse" ]]; then + echo "Skipping due to missing stress package in OpenSUSE" >>/skipped + exit 77 +fi + +systemd-analyze log-level debug + +# Ensure that the init.scope.d drop-in is applied on boot +test "$(cat /sys/fs/cgroup/init.scope/memory.high)" != "max" + +# Loose checks to ensure the environment has the necessary features for systemd-oomd +[[ -e /proc/pressure ]] || echo "no PSI" >>/skipped +[[ "$(get_cgroup_hierarchy)" == "unified" ]] || echo "no cgroupsv2" >>/skipped +[[ -x /usr/lib/systemd/systemd-oomd ]] || echo "no oomd" >>/skipped +if [[ -s /skipped ]]; then + exit 77 +fi + +rm -rf /run/systemd/system/TEST-55-OOMD-testbloat.service.d + +# Activate swap file if we are in a VM +if systemd-detect-virt --vm --quiet; then + swapoff --all + if [[ "$(findmnt -n -o FSTYPE /)" == btrfs ]]; then + btrfs filesystem mkswapfile -s 64M /swapfile + else + dd if=/dev/zero of=/swapfile bs=1M count=64 + chmod 0600 /swapfile + mkswap /swapfile + fi + + swapon /swapfile + swapon --show +fi + +# Configure oomd explicitly to avoid conflicts with distro dropins +mkdir -p /run/systemd/oomd.conf.d/ +cat >/run/systemd/oomd.conf.d/99-oomd-test.conf </run/systemd/system/-.slice.d/99-oomd-test.conf </run/systemd/system/user@.service.d/99-oomd-test.conf </run/systemd/system/systemd-oomd.service.d/debug.conf </run/systemd/system/TEST-55-OOMD-testbloat.service.d/override.conf </dev/null; then + echo "no systemd-repart" >/skipped + exit 77 +fi + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +export SYSTEMD_LOG_LEVEL=debug +export PAGER=cat + +# Disable use of special glyphs such as → +export SYSTEMD_UTF8=0 + +seed=750b6cd5c4ae4012a15e7be3c29e6a47 + +if ! systemd-detect-virt --quiet --container; then + udevadm control --log-level debug +fi + +machine="$(uname -m)" +if [ "${machine}" = "x86_64" ]; then + root_guid=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709 + root_uuid=60F33797-1D71-4DCB-AA6F-20564F036CD0 + root_uuid2=73A4CCD2-EAF5-44DA-A366-F99188210FDC + usr_guid=8484680C-9521-48C6-9C11-B0720656F69E + usr_uuid=7E3369DD-D653-4513-ADF5-B993A9F20C16 + architecture="x86-64" +elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then + root_guid=44479540-F297-41B2-9AF7-D131D5F0458A + root_uuid=02B4253F-29A4-404E-8972-1669D3B03C87 + root_uuid2=268E0FD3-B468-4806-A823-E533FE9BB9CC + usr_guid=75250D76-8CC6-458E-BD66-BD47CC81A812 + usr_uuid=7B42FFB0-B0E1-4395-B20B-C78F4A571648 + architecture="x86" +elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then + root_guid=B921B045-1DF0-41C3-AF44-4C6F280D3FAE + root_uuid=055D0227-53A6-4033-85C3-9A5973EFF483 + root_uuid2=F7DBBE48-8FD0-4833-8411-AA34E7C8E60A + usr_guid=B0E01050-EE5F-4390-949A-9101B17104E9 + usr_uuid=FCE3C75E-D6A4-44C0-87F0-4C105183FB1F + architecture="arm64" +elif [ "${machine}" = "arm" ]; then + root_guid=69DAD710-2CE4-4E3C-B16C-21A1D49ABED3 + root_uuid=567DA89E-8DE2-4499-8D10-18F212DFF034 + root_uuid2=813ECFE5-4C89-4193-8A52-437493F2F96E + usr_guid=7D0359A3-02B3-4F0A-865C-654403E70625 + usr_uuid=71E93DC2-5073-42CB-8A84-A354E64D8966 + architecture="arm" +elif [ "${machine}" = "loongarch64" ]; then + root_guid=77055800-792C-4F94-B39A-98C91B762BB6 + root_uuid=D8EFC2D2-0133-41E4-BDCB-3B9F4CFDDDE8 + root_uuid2=36499F9E-0688-40C1-A746-EA8FD9543C56 + usr_guid=E611C702-575C-4CBE-9A46-434FA0BF7E3F + usr_uuid=031FFA75-00BB-49B6-A70D-911D2D82A5B7 + architecture="loongarch64" +elif [ "${machine}" = "ia64" ]; then + root_guid=993D8D3D-F80E-4225-855A-9DAF8ED7EA97 + root_uuid=DCF33449-0896-4EA9-BC24-7D58AEEF522D + root_uuid2=C2A6CAB7-ABEA-4FBA-8C48-CB4C52E6CA38 + usr_guid=4301D2A6-4E3B-4B2A-BB94-9E0B2C4225EA + usr_uuid=BC2BCCE7-80D6-449A-85CC-637424CE5241 + architecture="ia64" +elif [ "${machine}" = "s390x" ]; then + root_guid=5EEAD9A9-FE09-4A1E-A1D7-520D00531306 + root_uuid=7EBE0C85-E27E-48EC-B164-F4807606232E + root_uuid2=2A074E1C-2A19-4094-A0C2-24B1A5D52FCB + usr_guid=8A4F5770-50AA-4ED3-874A-99B710DB6FEA + usr_uuid=51171D30-35CF-4A49-B8B5-9478B9B796A5 + architecture="s390x" +elif [ "${machine}" = "ppc64le" ]; then + root_guid=C31C45E6-3F39-412E-80FB-4809C4980599 + root_uuid=061E67A1-092F-482F-8150-B525D50D6654 + root_uuid2=A6687CEF-4E4F-44E7-90B3-CDA52EA81739 + usr_guid=15BB03AF-77E7-4D4A-B12B-C0D084F7491C + usr_uuid=C0D0823B-8040-4C7C-A629-026248E297FB + architecture="ppc64-le" +else + echo "Unexpected uname -m: ${machine} in TEST-58-REPART.sh, please fix me" + exit 1 +fi + +testcase_basic() { + local defs imgs output + local loop volume + + defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN + chmod 0755 "$defs" + + echo "*** 1. create an empty image ***" + + systemd-repart --offline="$OFFLINE" \ + --empty=create \ + --size=1G \ + --seed="$seed" \ + "$imgs/zzz" + + output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$') + + assert_eq "$output" "label: gpt +label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD +device: $imgs/zzz +unit: sectors +first-lba: 2048 +last-lba: 2097118" + + echo "*** 2. Testing with root, root2, home, and swap ***" + + tee "$defs/root.conf" <>"$defs/home.conf" + echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>"$defs/home.conf" + + systemd-repart --offline="$OFFLINE" \ + --definitions="$defs" \ + --dry-run=no \ + --seed="$seed" \ + "$imgs/zzz" + + output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$') + + assert_eq "$output" "label: gpt +label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD +device: $imgs/zzz +unit: sectors +first-lba: 2048 +last-lba: 2097118 +$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\" +$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\" +$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\" +$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\" +$imgs/zzz5 : start= 1908696, size= 188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"" + + echo "*** 4. Resizing to 2G ***" + + systemd-repart --offline="$OFFLINE" \ + --definitions="$defs" \ + --size=2G \ + --dry-run=no \ + --seed="$seed" \ + "$imgs/zzz" + + output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$') + + assert_eq "$output" "label: gpt +label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD +device: $imgs/zzz +unit: sectors +first-lba: 2048 +last-lba: 4194270 +$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\" +$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\" +$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\" +$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\" +$imgs/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"" + + echo "*** 5. Testing with root, root2, home, swap, another partition, and partition copy ***" + + dd if=/dev/urandom of="$imgs/block-copy" bs=4096 count=10240 + + tee "$defs/extra2.conf" </dev/null + umount "$imgs/mount" +} + +testcase_dropin() { + local defs imgs output + + defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN + chmod 0755 "$defs" + + tee "$defs/root.conf" < 32M", + "old_padding" : 0, + "raw_padding" : 0, + "padding" : "-> 0B", + "activity" : "create", + "drop-in_files" : [ + "$defs/root.conf.d/override1.conf", + "$defs/root.conf.d/override2.conf" + ] + } +] +EOF +} + +testcase_multiple_definitions() { + local defs imgs output + + defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN + chmod 0755 "$defs" + + mkdir -p "$defs/1" + tee "$defs/1/root1.conf" < 32M", + "old_padding" : 0, + "raw_padding" : 0, + "padding" : "-> 0B", + "activity" : "create" + }, + { + "type" : "swap", + "label" : "label2", + "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3", + "partno" : 1, + "file" : "$defs/2/root2.conf", + "node" : "$imgs/zzz2", + "offset" : 34603008, + "old_size" : 0, + "raw_size" : 33554432, + "size" : "-> 32M", + "old_padding" : 0, + "raw_padding" : 0, + "padding" : "-> 0B", + "activity" : "create" + } +] +EOF +} + +testcase_copy_blocks() { + local defs imgs output + + defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN + chmod 0755 "$defs" + + echo "*** First, create a disk image and verify its in order ***" + + tee "$defs/esp.conf" <"$defs/verity.openssl.cnf" </dev/null; then + continue + fi + + tee "$defs/root-$format.conf" </dev/null; then + tee "$defs/root-squashfs.conf" </dev/null; then + echo "Skipping free area calculation test without squashfs." + return + fi + + defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")" + imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$defs' '$imgs'" RETURN + chmod 0755 "$defs" + + # https://github.com/systemd/systemd/issues/28225 + echo "*** free area calculation ***" + + tee "$defs/00-ESP.conf" <"$defs/10-part1.conf" + echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=1\n" >"$defs/11-dropped-first.conf" + echo -ne "[Partition]\nType=root\n" >"$defs/12-part2.conf" + echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=2\n" >"$defs/13-dropped-second.conf" + + systemd-repart --empty=allow --pretty=yes --dry-run=no --definitions="$defs" "$image" + + sfdisk -q -l "$image" + [[ "$(sfdisk -q -l "$image" | grep -c "$image")" -eq 2 ]] +} + +OFFLINE="yes" +run_testcases + +# Online image builds need loop devices so we can't run them in nspawn. +if ! systemd-detect-virt --container; then + OFFLINE="no" + run_testcases +fi + +# Valid block sizes on the Linux block layer are >= 512 and <= PAGE_SIZE, and +# must be powers of 2. Which leaves exactly four different ones to test on +# typical hardware +test_sector 512 +test_sector 1024 +test_sector 2048 +test_sector 4096 + +touch /testok diff --git a/test/units/TEST-59-RELOADING-RESTART.sh b/test/units/TEST-59-RELOADING-RESTART.sh new file mode 100755 index 0000000..0e04403 --- /dev/null +++ b/test/units/TEST-59-RELOADING-RESTART.sh @@ -0,0 +1,168 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +fail() { + systemd-analyze log-level info + exit 1 +} + +# Wait for a service to enter a state within a timeout period, if it doesn't +# enter the desired state within the timeout period then this function will +# exit the test case with a non zero exit code. +wait_on_state_or_fail() { + service=$1 + expected_state=$2 + timeout=$3 + + state=$(systemctl show "$service" --property=ActiveState --value) + while [ "$state" != "$expected_state" ]; do + if [ "$timeout" = "0" ]; then + fail + fi + timeout=$((timeout - 1)) + sleep 1 + state=$(systemctl show "$service" --property=ActiveState --value) + done +} + +systemd-analyze log-level debug + + +cat >/run/systemd/system/testservice-fail-59.service </run/systemd/system/testservice-fail-restart-59.service </run/systemd/system/testservice-abort-restart-59.service </run/systemd/system.conf.d/50-test-59-reload.conf </run/notify-reload-test.sh </run/systemd/system/tmp-deptest.mount </run/systemd/system/"$unit" <&2 "Test mount \"$unit\" unit isn't mounted" + return 1 + } + mountpoint -q "$tmpdir" + + trap 'systemctl stop $unit' RETURN + + # Trigger the mount ratelimiting + cd "$(mktemp -d)" + mkdir foo + for _ in {1..50}; do + mount --bind foo foo + umount foo + done + + # Unmount the test mount and start it immediately again via systemd + umount "$tmpdir" + systemctl start "$unit" + + # Make sure it is seen as mounted by systemd and it actually is mounted + [[ "$(systemctl show --property SubState --value "$unit")" = "mounted" ]] || { + echo >&2 "Test mount \"$unit\" unit isn't in \"mounted\" state" + return 1 + } + + mountpoint -q "$tmpdir" || { + echo >&2 "Test mount \"$unit\" is in \"mounted\" state, actually is not mounted" + return 1 + } +} + +test_issue_23796() { + local mount_path mount_mytmpfs since + + mount_path="$(command -v mount 2>/dev/null)" + mount_mytmpfs="${mount_path/\/bin/\/sbin}.mytmpfs" + cat >"$mount_mytmpfs" </run/systemd/system/tmp-hoge.mount <>/skipped + exit 77 +fi + +if systemctl --version | grep -q -F -- "-BPF_FRAMEWORK"; then + echo "bpf-framework is disabled" >>/skipped + exit 77 +fi + +trap teardown EXIT +setup + +systemctl start --wait TEST-62-RESTRICT-IFACES-1.service +systemctl start --wait TEST-62-RESTRICT-IFACES-2.service +systemctl start --wait TEST-62-RESTRICT-IFACES-3.service +systemctl start --wait TEST-62-RESTRICT-IFACES-4.service +systemctl start --wait TEST-62-RESTRICT-IFACES-5.service + +touch /testok diff --git a/test/units/TEST-63-PATH.sh b/test/units/TEST-63-PATH.sh new file mode 100755 index 0000000..cdd323c --- /dev/null +++ b/test/units/TEST-63-PATH.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +systemctl log-level debug + +# Test that a path unit continuously triggering a service that fails condition checks eventually fails with +# the trigger-limit-hit error. +rm -f /tmp/nonexistent +systemctl start test63.path +touch /tmp/test63 + +# Make sure systemd has sufficient time to hit the trigger limit for test63.path. +# shellcheck disable=SC2016 +timeout 30 bash -c 'until test "$(systemctl show test63.path -P ActiveState)" = failed; do sleep .2; done' +test "$(systemctl show test63.service -P ActiveState)" = inactive +test "$(systemctl show test63.service -P Result)" = success +test "$(systemctl show test63.path -P Result)" = trigger-limit-hit + +# Test that starting the service manually doesn't affect the path unit. +rm -f /tmp/test63 +systemctl reset-failed +systemctl start test63.path +systemctl start test63.service +test "$(systemctl show test63.service -P ActiveState)" = inactive +test "$(systemctl show test63.service -P Result)" = success +test "$(systemctl show test63.path -P ActiveState)" = active +test "$(systemctl show test63.path -P Result)" = success + +# Test that glob matching works too, with $TRIGGER_PATH +systemctl start test63-glob.path +touch /tmp/test63-glob-foo +timeout 60 bash -c 'until systemctl -q is-active test63-glob.service; do sleep .2; done' +test "$(systemctl show test63-glob.service -P ActiveState)" = active +test "$(systemctl show test63-glob.service -P Result)" = success + +test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[["trigger_unit","test63-glob.path"],["trigger_path","/tmp/test63-glob-foo"]]}' + +systemctl stop test63-glob.path test63-glob.service + +test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[]}' + +# tests for issue https://github.com/systemd/systemd/issues/24577#issuecomment-1522628906 +rm -f /tmp/hoge +systemctl start test63-issue-24577.path +systemctl status -n 0 test63-issue-24577.path +systemctl status -n 0 test63-issue-24577.service || : +systemctl list-jobs +output=$(systemctl list-jobs --no-legend) +assert_not_in "test63-issue-24577.service" "$output" +assert_not_in "test63-issue-24577-dep.service" "$output" + +touch /tmp/hoge +systemctl status -n 0 test63-issue-24577.path +systemctl status -n 0 test63-issue-24577.service || : +systemctl list-jobs +output=$(systemctl list-jobs --no-legend) +assert_in "test63-issue-24577.service" "$output" +assert_in "test63-issue-24577-dep.service" "$output" + +# even if the service is stopped, it will be soon retriggered. +systemctl stop test63-issue-24577.service +systemctl status -n 0 test63-issue-24577.path +systemctl status -n 0 test63-issue-24577.service || : +systemctl list-jobs +output=$(systemctl list-jobs --no-legend) +assert_in "test63-issue-24577.service" "$output" +assert_in "test63-issue-24577-dep.service" "$output" + +rm -f /tmp/hoge +systemctl stop test63-issue-24577.service +systemctl status -n 0 test63-issue-24577.path +systemctl status -n 0 test63-issue-24577.service || : +systemctl list-jobs +output=$(systemctl list-jobs --no-legend) +assert_not_in "test63-issue-24577.service" "$output" +assert_in "test63-issue-24577-dep.service" "$output" + +# Test for race condition fixed by https://github.com/systemd/systemd/pull/30768 +# Here's the schedule of events that we to happen during this test: +# (This test) (The service) +# .path unit monitors /tmp/copyme for changes +# Take lock on /tmp/noexeit ↓ +# Write to /tmp/copyme ↓ +# Wait for deactivating Started +# ↓ Copies /tmp/copyme to /tmp/copied +# ↓ Tells manager it's shutting down +# Ensure service did the copy Tries to lock /tmp/noexit and blocks +# Write to /tmp/copyme ↓ +# +# Now at this point the test can diverge. If we regress, this second write is +# missed and we'll see: +# ... (second write) ... (blocked) +# Drop lock on /tmp/noexit ↓ +# Wait for service to do copy Unblocks and exits +# ↓ (dead) +# ↓ +# (timeout) +# Test fails +# +# Otherwise, we'll see: +# ... (second write) ... (blocked) +# Drop lock on /tmp/noexit ↓ and .path unit queues a new start job +# Wait for service to do copy Unblocks and exits +# ↓ Starts again b/c of queued job +# ↓ Copies again +# Test Passes +systemctl start test63-pr-30768.path +exec {lock}<>/tmp/noexit +flock -e $lock +echo test1 > /tmp/copyme +# shellcheck disable=SC2016 +timeout 30 bash -c 'until test "$(systemctl show test63-pr-30768.service -P ActiveState)" = deactivating; do sleep .2; done' +diff /tmp/copyme /tmp/copied +echo test2 > /tmp/copyme +exec {lock}<&- +timeout 30 bash -c 'until diff /tmp/copyme /tmp/copied >/dev/null; do sleep .2; done' + +systemctl log-level info + +touch /testok diff --git a/test/units/TEST-64-UDEV-STORAGE.sh b/test/units/TEST-64-UDEV-STORAGE.sh new file mode 100755 index 0000000..5ddddf5 --- /dev/null +++ b/test/units/TEST-64-UDEV-STORAGE.sh @@ -0,0 +1,1262 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# vi: ts=4 sw=4 tw=0 et: + +set -eux +set -o pipefail + +# Check if all symlinks under /dev/disk/ are valid +# shellcheck disable=SC2120 +helper_check_device_symlinks() {( + set +x + + local dev link path paths target + + [[ $# -gt 0 ]] && paths=("$@") || paths=("/dev/disk" "/dev/mapper") + + # Check if all given paths are valid + for path in "${paths[@]}"; do + if ! test -e "$path"; then + echo >&2 "Path '$path' doesn't exist" + return 1 + fi + done + + while read -r link; do + target="$(readlink -f "$link")" + # Both checks should do virtually the same thing, but check both to be + # on the safe side + if [[ ! -e "$link" || ! -e "$target" ]]; then + echo >&2 "ERROR: symlink '$link' points to '$target' which doesn't exist" + return 1 + fi + + # Check if the symlink points to the correct device in /dev + dev="/dev/$(udevadm info -q name "$link")" + if [[ "$target" != "$dev" ]]; then + echo >&2 "ERROR: symlink '$link' points to '$target' but '$dev' was expected" + return 1 + fi + done < <(find "${paths[@]}" -type l) +)} + +helper_check_udev_watch() {( + set +x + + local link target id dev + + while read -r link; do + target="$(readlink "$link")" + if [[ ! -L "/run/udev/watch/$target" ]]; then + echo >&2 "ERROR: symlink /run/udev/watch/$target does not exist" + return 1 + fi + if [[ "$(readlink "/run/udev/watch/$target")" != "$(basename "$link")" ]]; then + echo >&2 "ERROR: symlink target of /run/udev/watch/$target is inconsistent with $link" + return 1 + fi + + if [[ "$target" =~ ^[0-9]+$ ]]; then + # $link is ID -> wd + id="$(basename "$link")" + else + # $link is wd -> ID + id="$target" + fi + + if [[ "${id:0:1}" == "b" ]]; then + dev="/dev/block/${id:1}" + elif [[ "${id:0:1}" == "c" ]]; then + dev="/dev/char/${id:1}" + else + echo >&2 "ERROR: unexpected device ID '$id'" + return 1 + fi + + if [[ ! -e "$dev" ]]; then + echo >&2 "ERROR: device '$dev' corresponding to symlink '$link' does not exist" + return 1 + fi + done < <(find /run/udev/watch -type l) +)} + +check_device_unit() {( + set +x + + local log_level link links path syspath unit + + log_level="${1?}" + path="${2?}" + unit=$(systemd-escape --path --suffix=device "$path") + + [[ "$log_level" == 1 ]] && echo "INFO: check_device_unit($unit)" + + syspath=$(systemctl show --value --property SysFSPath "$unit" 2>/dev/null) + if [[ -z "$syspath" ]]; then + [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit not found." + return 1 + fi + + if [[ ! -L "$path" ]]; then + if [[ ! -d "$syspath" ]]; then + [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit exists for $syspath but it does not exist." + return 1 + fi + return 0 + fi + + if [[ ! -b "$path" && ! -c "$path" ]]; then + [[ "$log_level" == 1 ]] && echo >&2 "ERROR: invalid file type $path" + return 1 + fi + + read -r -a links < <(udevadm info -q symlink "$syspath" 2>/dev/null) + for link in "${links[@]}"; do + if [[ "/dev/$link" == "$path" ]]; then # DEVLINKS= given by -q symlink are relative to /dev + return 0 + fi + done + + read -r -a links < <(udevadm info "$syspath" | sed -ne '/SYSTEMD_ALIAS=/ { s/^E: SYSTEMD_ALIAS=//; p }' 2>/dev/null) + for link in "${links[@]}"; do + if [[ "$link" == "$path" ]]; then # SYSTEMD_ALIAS= are absolute + return 0 + fi + done + + [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit exists for $syspath but it does not have the corresponding DEVLINKS or SYSTEMD_ALIAS." + return 1 +)} + +check_device_units() {( + set +x + + local log_level path paths + + log_level="${1?}" + shift + paths=("$@") + + for path in "${paths[@]}"; do + if ! check_device_unit "$log_level" "$path"; then + return 1 + fi + done + + while read -r unit _; do + path=$(systemd-escape --path --unescape "$unit") + if ! check_device_unit "$log_level" "$path"; then + return 1 + fi + done < <(systemctl list-units --all --type=device --no-legend dev-* | awk '$1 !~ /dev-tty.+/ && $4 == "plugged" { print $1 }' | sed -e 's/\.device$//') + + return 0 +)} + +helper_check_device_units() {( + set +x + + local i + + for i in {1..20}; do + (( i > 1 )) && sleep 0.5 + if check_device_units 0 "$@"; then + return 0 + fi + done + + check_device_units 1 "$@" +)} + +testcase_virtio_scsi_basic() { + lsblk -S + [[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]] +} + +testcase_nvme_basic() { + local expected_symlinks=() + local i + + for (( i = 0; i < 5; i++ )); do + expected_symlinks+=( + # both replace mode provides the same devlink + /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef"$i" + # with nsid + /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef"$i"_1 + ) + done + for (( i = 5; i < 10; i++ )); do + expected_symlinks+=( + # old replace mode + /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl__deadbeef_"$i" + # newer replace mode + /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____deadbeef__"$i" + # with nsid + /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____deadbeef__"$i"_1 + ) + done + for (( i = 10; i < 15; i++ )); do + expected_symlinks+=( + # old replace mode does not provide devlink, as serial contains "/" + # newer replace mode + /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____dead_beef_"$i" + # with nsid + /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____dead_beef_"$i"_1 + ) + done + for (( i = 15; i < 20; i++ )); do + expected_symlinks+=( + # old replace mode does not provide devlink, as serial contains "/" + # newer replace mode + /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_dead_.._.._beef_"$i" + # with nsid + /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_dead_.._.._beef_"$i"_1 + ) + done + + udevadm settle + ls /dev/disk/by-id + for i in "${expected_symlinks[@]}"; do + udevadm wait --settle --timeout=30 "$i" + done + + lsblk --noheadings | grep "^nvme" + [[ "$(lsblk --noheadings | grep -c "^nvme")" -ge 20 ]] +} + +testcase_nvme_subsystem() { + local expected_symlinks=( + # Controller(s) + /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef + /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_16 + /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_17 + # Shared namespaces + /dev/disk/by-path/pci-*-nvme-16 + /dev/disk/by-path/pci-*-nvme-17 + ) + + udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" +} + +testcase_virtio_scsi_identically_named_partitions() { + local num_part num_disk i j + + if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then + num_part=4 + num_disk=4 + else + num_part=8 + num_disk=16 + fi + + for ((i = 0; i < num_disk; i++)); do + udevadm lock --device "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive$i" \ + sfdisk "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive$i" </etc/multipath.conf <<\EOF +defaults { + # Use /dev/mapper/$WWN paths instead of /dev/mapper/mpathX + user_friendly_names no + find_multipaths yes + enable_foreign "^$" +} + +blacklist_exceptions { + property "(SCSI_IDENT_|ID_WWN)" +} + +blacklist { +} +EOF + + udevadm lock --device /dev/disk/by-id/wwn-0xdeaddeadbeef0000 \ + sfdisk /dev/disk/by-id/wwn-0xdeaddeadbeef0000 <"$mpoint/test" + # Sanity check we actually wrote what we wanted + [[ "$(<"$mpoint/test")" == "$expected" ]] + + for device in "${devices[@]}"; do + echo offline >"/sys/class/scsi_device/$device/device/state" + [[ "$(<"$mpoint/test")" == "$expected" ]] + expected="$((expected + 1))" + echo -n "$expected" >"$mpoint/test" + + # Make sure all symlinks are still valid + udevadm wait --settle --timeout=30 "${part_links[@]}" + helper_check_device_units "${part_links[@]}" + done + + multipath -l "$path" + # Three paths should be now marked as 'offline' and one as 'running' + [[ "$(multipath -l "$path" | grep -c offline)" -eq 3 ]] + [[ "$(multipath -l "$path" | grep -c running)" -eq 1 ]] + + umount "$mpoint" + rm -fr "$mpoint" +} + +testcase_simultaneous_events_1() { + local disk expected i iterations key link num_part part partscript rule target timeout + local -a devices symlinks + local -A running + + if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then + num_part=2 + iterations=10 + timeout=240 + else + num_part=10 + iterations=100 + timeout=60 + fi + + for disk in {0..9}; do + link="/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_deadbeeftest${disk}" + target="$(readlink -f "$link")" + if [[ ! -b "$target" ]]; then + echo "ERROR: failed to find the test SCSI block device $link" + return 1 + fi + + devices+=("$target") + done + + for ((part = 1; part <= num_part; part++)); do + symlinks+=( + "/dev/disk/by-partlabel/test${part}" + ) + done + + partscript="$(mktemp)" + + cat >"$partscript" <"$rule" <&2 "ERROR: symlink '/dev/disk/by-partlabel/test${part}' points to '$target' but '$expected' was expected" + return 1 + fi + done + fi + done + + helper_check_device_units + rm -f "$rule" "$partscript" + + udevadm control --reload +} + +testcase_simultaneous_events_2() { + local disk expected i iterations key link num_part part script_dir target timeout + local -a devices symlinks + local -A running + + script_dir="$(mktemp --directory "/tmp/test-udev-storage.script.XXXXXXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$script_dir'" RETURN + + if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then + num_part=20 + iterations=1 + timeout=2400 + else + num_part=100 + iterations=3 + timeout=300 + fi + + for disk in {0..9}; do + link="/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_deadbeeftest${disk}" + target="$(readlink -f "$link")" + if [[ ! -b "$target" ]]; then + echo "ERROR: failed to find the test SCSI block device $link" + return 1 + fi + + devices+=("$target") + done + + for ((i = 1; i <= iterations; i++)); do + cat >"$script_dir/partscript-$i" <>/etc/fstab + systemctl daemon-reload + mount "/tmp/lvmluksmnt" + mountpoint "/tmp/lvmluksmnt" + # Temporarily suspend the LUKS device and trigger udev - basically what `cryptsetup resize` + # does but in a more deterministic way suitable for a test/reproducer + for _ in {0..5}; do + dmsetup suspend "/dev/mapper/lvmluksmap" + udevadm trigger -v --settle "/dev/mapper/lvmluksmap" + dmsetup resume "/dev/mapper/lvmluksmap" + # The mount should survive this sequence of events + mountpoint "/tmp/lvmluksmnt" + done + # Cleanup + umount "/tmp/lvmluksmnt" + cryptsetup close "/dev/mapper/lvmluksmap" + sed -i "/lvmluksfs/d" "/etc/fstab" + systemctl daemon-reload + + # Disable the VG and check symlinks... + lvm vgchange -an "$vgroup" + udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup" "/dev/disk/by-label/mylvpart1" + helper_check_device_symlinks "/dev/disk" + helper_check_device_units + + # reenable the VG and check the symlinks again if all LVs are properly activated + lvm vgchange -ay "$vgroup" + udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1" + helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" + helper_check_device_units + + # Same as above, but now with more "stress" + if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then + iterations=10 + else + iterations=50 + fi + + for ((i = 1; i <= iterations; i++)); do + lvm vgchange -an "$vgroup" + lvm vgchange -ay "$vgroup" + + if ((i % 5 == 0)); then + udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1" + helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" + helper_check_device_units + fi + done + + # Remove the first LV + lvm lvremove -y "$vgroup/mypart1" + udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/mypart1" + udevadm wait --timeout=0 "/dev/$vgroup/mypart2" + helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" + helper_check_device_units + + # Create & remove LVs in a loop, i.e. with more "stress" + if [[ -v ASAN_OPTIONS ]]; then + iterations=8 + partitions=16 + elif [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then + iterations=8 + partitions=8 + else + iterations=16 + partitions=16 + fi + + for ((i = 1; i <= iterations; i++)); do + # 1) Create some logical volumes + for ((part = 0; part < partitions; part++)); do + lvm lvcreate -y -L 4M "$vgroup" -n "looppart$part" + done + + # 2) Immediately remove them + lvm lvremove -y $(seq -f "$vgroup/looppart%g" 0 "$((partitions - 1))") + + # 3) On every 4th iteration settle udev and check if all partitions are + # indeed gone, and if all symlinks are still valid + if ((i % 4 == 0)); then + for ((part = 0; part < partitions; part++)); do + udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/looppart$part" + done + helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" + helper_check_device_units + fi + done +} + +testcase_btrfs_basic() { + local dev_stub i label mpoint uuid + local devices=( + /dev/disk/by-id/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" + uuid="deadbeef-dead-dead-beef-000000000000" + label="btrfs_root" + udevadm lock --device="${devices[0]}" mkfs.btrfs -f -L "$label" -U "$uuid" "${devices[0]}" + udevadm wait --settle --timeout=30 "${devices[0]}" "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label" + btrfs filesystem show + helper_check_device_symlinks + helper_check_device_units + + echo "Multiple devices: using partitions, data: single, metadata: raid1" + uuid="deadbeef-dead-dead-beef-000000000001" + label="btrfs_mpart" + udevadm lock --device="${devices[0]}" sfdisk --wipe=always "${devices[0]}" </etc/crypttab + for ((i = 0; i < ${#devices[@]}; i++)); do + # Intentionally use weaker cipher-related settings, since we don't care + # about security here as it's a throwaway LUKS partition + 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 + echo "encbtrfs$i UUID=deadbeef-dead-dead-beef-11111111111$i /etc/btrfs_keyfile luks" >>/etc/crypttab + systemctl daemon-reload + systemctl start "systemd-cryptsetup@encbtrfs$i" + done + helper_check_device_symlinks + helper_check_device_units + # Check if we have all necessary DM devices + ls -l /dev/mapper/encbtrfs{0..3} + # Create a multi-device btrfs filesystem on the LUKS devices + udevadm lock \ + --device=/dev/mapper/encbtrfs0 \ + --device=/dev/mapper/encbtrfs1 \ + --device=/dev/mapper/encbtrfs2 \ + --device=/dev/mapper/encbtrfs3 \ + mkfs.btrfs -f -M -d raid1 -m raid1 -L "$label" -U "$uuid" /dev/mapper/encbtrfs{0..3} + udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label" + btrfs filesystem show + helper_check_device_symlinks + helper_check_device_units + # Mount it and write some data to it we can compare later + mount -t btrfs /dev/mapper/encbtrfs0 "$mpoint" + echo "hello there" >"$mpoint/test" + # "Deconstruct" the btrfs device and check if we're in a sane state (symlink-wise) + umount "$mpoint" + systemctl stop systemd-cryptsetup@encbtrfs{0..3} + udevadm wait --settle --timeout=30 --removed "/dev/disk/by-uuid/$uuid" + helper_check_device_symlinks + helper_check_device_units + # Add the mount point to /etc/fstab and check if the device can be put together + # automagically. The source device is the DM name of the first LUKS device + # (from /etc/crypttab). We have to specify all LUKS devices manually, as + # registering the necessary devices is usually initrd's job (via btrfs device scan) + dev_stub="/dev/mapper/encbtrfs" + echo "/dev/mapper/encbtrfs0 $mpoint btrfs device=${dev_stub}0,device=${dev_stub}1,device=${dev_stub}2,device=${dev_stub}3 0 2" >>/etc/fstab + # Tell systemd about the new mount + systemctl daemon-reload + # Restart cryptsetup.target to trigger autounlock of partitions in /etc/crypttab + systemctl restart cryptsetup.target + # Start the corresponding mount unit and check if the btrfs device was reconstructed + # correctly + systemctl start "${mpoint##*/}.mount" + udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label" + btrfs filesystem show + helper_check_device_symlinks + helper_check_device_units + grep "hello there" "$mpoint/test" + # Cleanup + systemctl stop "${mpoint##*/}.mount" + systemctl stop systemd-cryptsetup@encbtrfs{0..3} + sed -i "/${mpoint##*/}/d" /etc/fstab + : >/etc/crypttab + rm -fr "$mpoint" + systemctl daemon-reload + udevadm settle +} + +testcase_iscsi_lvm() { + local dev i label link lun_id mpoint target_name uuid + local target_ip="127.0.0.1" + local target_port="3260" + local vgroup="iscsi_lvm$RANDOM" + local expected_symlinks=() + local devices=( + /dev/disk/by-id/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 (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 + target_name="iqn.2021-09.com.example:iscsi.test" + # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each + # backed by a device + tgtadm --lld iscsi --op new --mode target --tid=1 --targetname "$target_name" + for ((i = 0; i < ${#devices[@]}; i++)); do + # lun-0 is reserved by iSCSI + lun_id="$((i + 1))" + tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun "$lun_id" -b "${devices[$i]}" + tgtadm --lld iscsi --op update --mode logicalunit --tid 1 --lun "$lun_id" + expected_symlinks+=( + "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$lun_id" + ) + done + tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL + # Configure the iSCSI initiator + iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover + iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login + udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" + helper_check_device_symlinks + helper_check_device_units + # Cleanup + iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout + tgtadm --lld iscsi --op delete --mode target --tid=1 + + echo "iSCSI LUNs backed by files + LVM" + # Note: we use files here to "trick" LVM the disks are indeed on a different + # host, so it doesn't automagically detect another path to the backing + # device once we disconnect the iSCSI devices + target_name="iqn.2021-09.com.example:iscsi.lvm.test" + mpoint="$(mktemp -d /iscsi_storeXXX)" + expected_symlinks=() + # Use the first device as it's configured with larger capacity + 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 + dd if=/dev/zero of="$mpoint/lun$i.img" bs=1M count=32 + done + # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each + # backed by a file + tgtadm --lld iscsi --op new --mode target --tid=2 --targetname "$target_name" + # lun-0 is reserved by iSCSI + for i in {1..4}; do + tgtadm --lld iscsi --op new --mode logicalunit --tid 2 --lun "$i" -b "$mpoint/lun$i.img" + tgtadm --lld iscsi --op update --mode logicalunit --tid 2 --lun "$i" + expected_symlinks+=( + "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$i" + ) + done + tgtadm --lld iscsi --op bind --mode target --tid 2 -I ALL + # Configure the iSCSI initiator + iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover + iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login + udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" + helper_check_device_symlinks + helper_check_device_units + # Add all iSCSI devices into a LVM volume group, create two logical volumes, + # and check if necessary symlinks exist (and are valid) + lvm pvcreate -y "${expected_symlinks[@]}" + lvm pvs + lvm vgcreate "$vgroup" -y "${expected_symlinks[@]}" + lvm vgs + lvm vgchange -ay "$vgroup" + lvm lvcreate -y -L 4M "$vgroup" -n mypart1 + lvm lvcreate -y -L 8M "$vgroup" -n mypart2 + lvm lvs + udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" + mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1" + udevadm 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 + # Disconnect the iSCSI devices and check all the symlinks + iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout + # "Reset" the DM state, since we yanked the backing storage from under the LVM, + # so the currently active VGs/LVs are invalid + dmsetup remove_all --deferred + # The LVM and iSCSI related symlinks should be gone + udevadm wait --settle --timeout=30 --removed "/dev/$vgroup" "/dev/disk/by-label/mylvpart1" "${expected_symlinks[@]}" + helper_check_device_symlinks "/dev/disk" + helper_check_device_units + # Reconnect the iSCSI devices and check if everything get detected correctly + iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover + iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login + udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1" + helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" + helper_check_device_units + # Cleanup + iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout + tgtadm --lld iscsi --op delete --mode target --tid=2 + umount "$mpoint" + rm -rf "$mpoint" +} + +testcase_long_sysfs_path() { + local cursor link logfile mpoint + local expected_symlinks=( + "/dev/disk/by-label/data_vol" + "/dev/disk/by-label/swap_vol" + "/dev/disk/by-partlabel/test_swap" + "/dev/disk/by-partlabel/test_part" + "/dev/disk/by-partuuid/deadbeef-dead-dead-beef-000000000000" + "/dev/disk/by-uuid/deadbeef-dead-dead-beef-111111111111" + "/dev/disk/by-uuid/deadbeef-dead-dead-beef-222222222222" + ) + + # Create a cursor file to skip messages generated by udevd in initrd, as it + # might not be the same up-to-date version as we currently run (hence generating + # messages we check for later and making the test fail) + cursor="$(mktemp)" + journalctl --cursor-file="${cursor:?}" -n0 -q + + # Make sure the test device is connected and show its "wonderful" path + stat /sys/block/vda + readlink -f /sys/block/vda/dev + + dev="/dev/vda" + udevadm lock --device "$dev" sfdisk "$dev" <>/etc/fstab + systemctl daemon-reload + mount "$mpoint" + timeout 30 bash -c "until systemctl -q is-active '$mpoint'; do sleep .2; done" + test -e "$mpoint/test" + umount "$mpoint" + + # Test out the swap partition + swapon -v -L swap_vol + swapoff -v -L swap_vol + + udevadm settle + + logfile="$(mktemp)" + # Check state of affairs after https://github.com/systemd/systemd/pull/22759 + # Note: can't use `--cursor-file` here, since we don't want to update the cursor + # after using it + [[ "$(journalctl --after-cursor="$(<"$cursor")" -q --no-pager -o short-monotonic -p info --grep "Device path.*vda.?' too long to fit into unit name" | wc -l)" -eq 0 ]] + [[ "$(journalctl --after-cursor="$(<"$cursor")" -q --no-pager -o short-monotonic --grep "Unit name .*vda.?\.device\" too long, falling back to hashed unit name" | wc -l)" -gt 0 ]] + # Check if the respective "hashed" units exist and are active (plugged) + systemctl status --no-pager "$(readlink -f /sys/block/vda/vda1)" + systemctl status --no-pager "$(readlink -f /sys/block/vda/vda2)" + # Make sure we don't unnecessarily spam the log + { journalctl -b -q --no-pager -o short-monotonic -p info --grep "/sys/devices/.+/vda[0-9]?" _PID=1 + UNIT=systemd-udevd.service || :;} | tee "$logfile" + [[ "$(wc -l <"$logfile")" -lt 10 ]] + + : >/etc/fstab + rm -fr "${cursor:?}" "${logfile:?}" "${mpoint:?}" +} + +testcase_mdadm_basic() { + local i part_name raid_name raid_dev uuid + local expected_symlinks=() + local devices=( + /dev/disk/by-id/scsi-0systemd_foobar_deadbeefmdadm{0..4} + ) + + ls -l "${devices[@]}" + + echo "Mirror raid (RAID 1)" + raid_name="mdmirror" + raid_dev="/dev/md/$raid_name" + part_name="${raid_name}_part" + uuid="aaaaaaaa:bbbbbbbb:cccccccc:00000001" + expected_symlinks=( + "$raid_dev" + "/dev/disk/by-id/md-name-H:$raid_name" + "/dev/disk/by-id/md-uuid-$uuid" + "/dev/disk/by-label/$part_name" # ext4 partition + ) + # Create a simple RAID 1 with an ext4 filesystem + echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/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" + mdadm -v --stop "$raid_dev" + udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}" + mdadm --assemble "$raid_dev" --name "$raid_name" -v + udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" + done + helper_check_device_symlinks + helper_check_device_units + # Cleanup + mdadm -v --stop "$raid_dev" + udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}" + + echo "Parity raid (RAID 5)" + raid_name="mdparity" + raid_dev="/dev/md/$raid_name" + part_name="${raid_name}_part" + uuid="aaaaaaaa:bbbbbbbb:cccccccc:00000101" + expected_symlinks=( + "$raid_dev" + "/dev/disk/by-id/md-name-H:$raid_name" + "/dev/disk/by-id/md-uuid-$uuid" + "/dev/disk/by-label/$part_name" # ext4 partition + ) + # Create a simple RAID 5 with an ext4 filesystem + echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/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" + mdadm -v --stop "$raid_dev" + udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}" + mdadm --assemble "$raid_dev" --name "$raid_name" -v + udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" + done + helper_check_device_symlinks + helper_check_device_units + # Cleanup + mdadm -v --stop "$raid_dev" + udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}" + helper_check_device_units + + echo "Mirror + parity raid (RAID 10) + multiple partitions" + raid_name="mdmirpar" + raid_dev="/dev/md/$raid_name" + part_name="${raid_name}_part" + uuid="aaaaaaaa:bbbbbbbb:cccccccc:00001010" + expected_symlinks=( + "$raid_dev" + "/dev/disk/by-id/md-name-H:$raid_name" + "/dev/disk/by-id/md-uuid-$uuid" + "/dev/disk/by-label/$part_name" # ext4 partition + # Partitions + "${raid_dev}1" + "${raid_dev}2" + "${raid_dev}3" + "/dev/disk/by-id/md-name-H:$raid_name-part1" + "/dev/disk/by-id/md-name-H:$raid_name-part2" + "/dev/disk/by-id/md-name-H:$raid_name-part3" + "/dev/disk/by-id/md-uuid-$uuid-part1" + "/dev/disk/by-id/md-uuid-$uuid-part2" + "/dev/disk/by-id/md-uuid-$uuid-part3" + ) + # Create a simple RAID 10 with an ext4 filesystem + echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/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" <&2 "Missing verification handler for test case '$TEST_FUNCTION_NAME'" + exit 1 +fi + +echo "TEST_FUNCTION_NAME=$TEST_FUNCTION_NAME" +"$TEST_FUNCTION_NAME" +udevadm settle + +echo "Check if all symlinks under /dev/disk/ are valid (post-test)" +helper_check_device_symlinks + +udevadm control --log-level info + +systemctl status systemd-udevd + +touch /testok diff --git a/test/units/TEST-65-ANALYZE.sh b/test/units/TEST-65-ANALYZE.sh new file mode 100755 index 0000000..18f5c4d --- /dev/null +++ b/test/units/TEST-65-ANALYZE.sh @@ -0,0 +1,952 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2016 +set -eux + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +systemctl log-level debug +export SYSTEMD_LOG_LEVEL=debug + +# Sanity checks +# +# We can't really test time, critical-chain and plot verbs here, as +# the testsuite service is a part of the boot transaction, so let's assume +# they fail +systemd-analyze || : +systemd-analyze time || : +systemd-analyze critical-chain || : +# blame +systemd-analyze blame +systemd-run --wait --user --pipe -M testuser@.host systemd-analyze blame +(! systemd-analyze blame --global) +# plot +systemd-analyze plot >/dev/null || : +systemd-analyze plot --json=pretty >/dev/null || : +systemd-analyze plot --json=short >/dev/null || : +systemd-analyze plot --json=off >/dev/null || : +systemd-analyze plot --json=pretty --no-legend >/dev/null || : +systemd-analyze plot --json=short --no-legend >/dev/null || : +systemd-analyze plot --json=off --no-legend >/dev/null || : +systemd-analyze plot --table >/dev/null || : +systemd-analyze plot --table --no-legend >/dev/null || : +(! systemd-analyze plot --global) +# legacy/deprecated options (moved to systemctl, but still usable from analyze) +systemd-analyze log-level +systemd-analyze log-level "$(systemctl log-level)" +systemd-analyze get-log-level +systemd-analyze set-log-level "$(systemctl log-level)" +systemd-analyze log-target +systemd-analyze log-target "$(systemctl log-target)" +systemd-analyze get-log-target +systemd-analyze set-log-target "$(systemctl log-target)" +systemd-analyze service-watchdogs +systemd-analyze service-watchdogs "$(systemctl service-watchdogs)" +# dot +systemd-analyze dot >/dev/null +systemd-analyze dot systemd-journald.service >/dev/null +systemd-analyze dot systemd-journald.service systemd-logind.service >/dev/null +systemd-analyze dot --from-pattern="*" --from-pattern="*.service" systemd-journald.service >/dev/null +systemd-analyze dot --to-pattern="*" --to-pattern="*.service" systemd-journald.service >/dev/null +systemd-analyze dot --from-pattern="*.service" --to-pattern="*.service" systemd-journald.service >/dev/null +systemd-analyze dot --order systemd-journald.service systemd-logind.service >/dev/null +systemd-analyze dot --require systemd-journald.service systemd-logind.service >/dev/null +systemd-analyze dot "systemd-*.service" >/dev/null +(! systemd-analyze dot systemd-journald.service systemd-logind.service "*" bbb ccc) +(! systemd-analyze dot --global systemd-journald.service) +# dump +# this should be rate limited to 10 calls in 10 minutes for unprivileged callers +for _ in {1..10}; do + runas testuser systemd-analyze dump systemd-journald.service >/dev/null +done +(! runas testuser systemd-analyze dump >/dev/null) +# still limited after a reload +systemctl daemon-reload +(! runas testuser systemd-analyze dump >/dev/null) +# and a re-exec +systemctl daemon-reexec +(! runas testuser systemd-analyze dump >/dev/null) +# privileged call, so should not be rate limited +for _ in {1..10}; do + systemd-analyze dump systemd-journald.service >/dev/null +done +systemd-analyze dump >/dev/null +systemd-analyze dump "*" >/dev/null +systemd-analyze dump "*.socket" >/dev/null +systemd-analyze dump "*.socket" "*.service" aaaaaaa ... >/dev/null +systemd-analyze dump systemd-journald.service >/dev/null +(! systemd-analyze dump "") +(! systemd-analyze dump --global systemd-journald.service) +# malloc +systemd-analyze malloc >/dev/null +(! systemd-analyze malloc --global) +# unit-files +systemd-analyze unit-files >/dev/null +systemd-analyze unit-files systemd-journald.service >/dev/null +systemd-analyze unit-files "*" >/dev/null +systemd-analyze unit-files "*" aaaaaa "*.service" "*.target" >/dev/null +systemd-analyze unit-files --user >/dev/null +systemd-analyze unit-files --user "*" aaaaaa "*.service" "*.target" >/dev/null +(! systemd-analyze unit-files --global) +# unit-paths +systemd-analyze unit-paths +systemd-analyze unit-paths --user +systemd-analyze unit-paths --global +# exist-status +systemd-analyze exit-status +systemd-analyze exit-status STDOUT BPF +systemd-analyze exit-status 0 1 {63..65} +(! systemd-analyze exit-status STDOUT BPF "hello*") +(! systemd-analyze exit-status --global) +# capability +systemd-analyze capability +systemd-analyze capability cap_chown CAP_KILL +systemd-analyze capability 0 1 {30..32} +(! systemd-analyze capability cap_chown CAP_KILL "hello*") +(! systemd-analyze capability --global) +# condition +mkdir -p /run/systemd/system +UNIT_NAME="analyze-condition-$RANDOM.service" +cat >"/run/systemd/system/$UNIT_NAME" <1.0 +ConditionPathExists=/etc/os-release + +[Service] +ExecStart=true +EOF +systemctl daemon-reload +systemd-analyze condition --unit="$UNIT_NAME" +systemd-analyze condition 'ConditionKernelVersion = ! <4.0' \ + 'ConditionKernelVersion = >=3.1' \ + 'ConditionACPower=|false' \ + 'ConditionArchitecture=|!arm' \ + 'AssertPathExists=/etc/os-release' +(! systemd-analyze condition 'ConditionArchitecture=|!arm' 'AssertXYZ=foo') +(! systemd-analyze condition 'ConditionKernelVersion=<1.0') +(! systemd-analyze condition 'AssertKernelVersion=<1.0') +(! systemd-analyze condition --global 'ConditionKernelVersion = ! <4.0') +# syscall-filter +systemd-analyze syscall-filter >/dev/null +systemd-analyze syscall-filter @chown @sync +systemd-analyze syscall-filter @sync @sync @sync +(! systemd-analyze syscall-filter @chown @sync @foobar) +(! systemd-analyze syscall-filter --global) +# filesystems (requires libbpf support) +if systemctl --version | grep "+BPF_FRAMEWORK"; then + systemd-analyze filesystems >/dev/null + systemd-analyze filesystems @basic-api + systemd-analyze filesystems @basic-api @basic-api @basic-api + (! systemd-analyze filesystems @basic-api @basic-api @foobar @basic-api) + (! systemd-analyze filesystems --global @basic-api) +fi +# calendar +systemd-analyze calendar '*-2-29 0:0:0' +systemd-analyze calendar --iterations=5 '*-2-29 0:0:0' +systemd-analyze calendar '*-* *:*:*' +systemd-analyze calendar --iterations=5 '*-* *:*:*' +systemd-analyze calendar --iterations=50 '*-* *:*:*' +systemd-analyze calendar --iterations=0 '*-* *:*:*' +systemd-analyze calendar --iterations=5 '01-01-22 01:00:00' +systemd-analyze calendar --base-time=yesterday --iterations=5 '*-* *:*:*' +(! systemd-analyze calendar --iterations=0 '*-* 99:*:*') +(! systemd-analyze calendar --base-time=never '*-* *:*:*') +(! systemd-analyze calendar 1) +(! systemd-analyze calendar "") +(! systemd-analyze calendar --global '*-2-29 0:0:0') +# timestamp +systemd-analyze timestamp now +systemd-analyze timestamp -- -1 +systemd-analyze timestamp yesterday now tomorrow +(! systemd-analyze timestamp yesterday never tomorrow) +(! systemd-analyze timestamp 1) +(! systemd-analyze timestamp '*-2-29 0:0:0') +(! systemd-analyze timestamp "") +(! systemd-analyze timestamp --global now) +# timespan +systemd-analyze timespan 1 +systemd-analyze timespan 1s 300s '1year 0.000001s' +(! systemd-analyze timespan 1s 300s aaaaaa '1year 0.000001s') +(! systemd-analyze timespan -- -1) +(! systemd-analyze timespan '*-2-29 0:0:0') +(! systemd-analyze timespan "") +(! systemd-analyze timespan --global 1) +# cat-config +systemd-analyze cat-config systemd/system.conf >/dev/null +systemd-analyze cat-config /etc/systemd/system.conf >/dev/null +systemd-analyze cat-config systemd/system.conf systemd/journald.conf >/dev/null +systemd-analyze cat-config systemd/system.conf foo/bar systemd/journald.conf >/dev/null +systemd-analyze cat-config foo/bar +systemd-analyze cat-config --tldr systemd/system.conf >/dev/null +systemd-analyze cat-config --tldr /etc/systemd/system.conf >/dev/null +systemd-analyze cat-config --tldr systemd/system.conf systemd/journald.conf >/dev/null +systemd-analyze cat-config --tldr systemd/system.conf foo/bar systemd/journald.conf >/dev/null +systemd-analyze cat-config --tldr foo/bar +(! systemd-analyze cat-config --global systemd/system.conf) +# security +systemd-analyze security +systemd-analyze security --json=off +systemd-analyze security --json=pretty | jq +systemd-analyze security --json=short | jq +(! systemd-analyze security --global) + +if [[ ! -v ASAN_OPTIONS ]]; then + # check that systemd-analyze cat-config paths work in a chroot + mkdir -p /tmp/root + mount --bind / /tmp/root + if mountpoint -q /usr; then + mount --bind /usr /tmp/root/usr + fi + systemd-analyze cat-config systemd/system-preset >/tmp/out1 + chroot /tmp/root systemd-analyze cat-config systemd/system-preset >/tmp/out2 + diff /tmp/out{1,2} +fi + +# verify +mkdir -p /tmp/img/usr/lib/systemd/system/ +mkdir -p /tmp/img/opt/ + +touch /tmp/img/opt/script0.sh +chmod +x /tmp/img/opt/script0.sh + +cat </tmp/img/usr/lib/systemd/system/testfile.service +[Service] +ExecStart = /opt/script0.sh +EOF + +set +e +# Default behaviour is to recurse through all dependencies when unit is loaded +(! systemd-analyze verify --root=/tmp/img/ testfile.service) + +# As above, recurses through all dependencies when unit is loaded +(! systemd-analyze verify --recursive-errors=yes --root=/tmp/img/ testfile.service) + +# Recurses through unit file and its direct dependencies when unit is loaded +(! systemd-analyze verify --recursive-errors=one --root=/tmp/img/ testfile.service) + +set -e + +# zero exit status since dependencies are ignored when unit is loaded +systemd-analyze verify --recursive-errors=no --root=/tmp/img/ testfile.service + +rm /tmp/img/usr/lib/systemd/system/testfile.service + +cat </tmp/testfile.service +[Unit] +foo = bar + +[Service] +ExecStart = echo hello +EOF + +cat </tmp/testfile2.service +[Unit] +Requires = testfile.service + +[Service] +ExecStart = echo hello +EOF + +# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded +systemd-analyze verify --recursive-errors=no /tmp/testfile2.service + +set +e +# Non-zero exit status since all associated dependencies are recursively loaded when the unit file is loaded +(! systemd-analyze verify --recursive-errors=yes /tmp/testfile2.service) +set -e + +rm /tmp/testfile.service +rm /tmp/testfile2.service + +cat </tmp/sample.service +[Unit] +Description = A Sample Service + +[Service] +ExecStart = echo hello +Slice=support.slice +EOF + +# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded +systemd-analyze verify --recursive-errors=no /tmp/sample.service + +cat </tmp/testfile.service +[Service] +ExecStart = echo hello +DeviceAllow=/dev/sda +EOF + +# Prevent regression from #13380 and #20859 where we can't verify hidden files +cp /tmp/testfile.service /tmp/.testfile.service + +systemd-analyze verify /tmp/.testfile.service + +rm /tmp/.testfile.service + +# Alias a unit file's name on disk (see #20061) +cp /tmp/testfile.service /tmp/testsrvc + +(! systemd-analyze verify /tmp/testsrvc) + +systemd-analyze verify /tmp/testsrvc:alias.service + +# Zero exit status since the value used for comparison determine exposure to security threats is by default 100 +systemd-analyze security --offline=true /tmp/testfile.service + +#The overall exposure level assigned to the unit is greater than the set threshold +(! systemd-analyze security --threshold=90 --offline=true /tmp/testfile.service) + +# Ensure we print the list of ACLs, see https://github.com/systemd/systemd/issues/23185 +systemd-analyze security --offline=true /tmp/testfile.service | grep -q -F "/dev/sda" + +# 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 </tmp/img/usr/lib/systemd/system/testfile.service +[Service] +ExecStart = echo hello +PrivateNetwork = yes +PrivateDevices = yes +PrivateUsers = yes +EOF + +# The new overall exposure level assigned to the unit is less than the set thresholds +# Verifies that the --offline= option works with --root= +systemd-analyze security --threshold=90 --offline=true --root=/tmp/img/ testfile.service + +cat </tmp/foo@.service +[Service] +ExecStart=ls +EOF + +cat </tmp/hoge@test.service +[Service] +ExecStart=ls +EOF + +# issue #30357 +pushd /tmp +systemd-analyze verify foo@bar.service +systemd-analyze verify foo@.service +systemd-analyze verify hoge@test.service +(! systemd-analyze verify hoge@nonexist.service) +(! systemd-analyze verify hoge@.service) +popd +pushd / +systemd-analyze verify tmp/foo@bar.service +systemd-analyze verify tmp/foo@.service +systemd-analyze verify tmp/hoge@test.service +(! systemd-analyze verify tmp/hoge@nonexist.service) +(! systemd-analyze verify tmp/hoge@.service) +popd +pushd /usr +systemd-analyze verify ../tmp/foo@bar.service +systemd-analyze verify ../tmp/foo@.service +systemd-analyze verify ../tmp/hoge@test.service +(! systemd-analyze verify ../tmp/hoge@nonexist.service) +(! systemd-analyze verify ../tmp/hoge@.service) +popd +systemd-analyze verify /tmp/foo@bar.service +systemd-analyze verify /tmp/foo@.service +systemd-analyze verify /tmp/hoge@test.service +(! systemd-analyze verify /tmp/hoge@nonexist.service) +(! systemd-analyze verify /tmp/hoge@.service) + +# test that all commands are verified. +cat </tmp/multi-exec-start.service +[Service] +Type=oneshot +ExecStart=true +ExecStart=ls +EOF +systemd-analyze verify /tmp/multi-exec-start.service +echo 'ExecStart=command-should-not-exist' >>/tmp/multi-exec-start.service +(! systemd-analyze verify /tmp/multi-exec-start.service) + +# Added an additional "INVALID_ID" id to the .json to verify that nothing breaks when input is malformed +# The PrivateNetwork id description and weight was changed to verify that 'security' is actually reading in +# values from the .json file when required. The default weight for "PrivateNetwork" is 2500, and the new weight +# assigned to that id in the .json file is 6000. This increased weight means that when the "PrivateNetwork" key is +# set to 'yes' (as above in the case of testfile.service) in the content of the unit file, the overall exposure +# level for the unit file should decrease to account for that increased weight. +cat </tmp/testfile.json +{"UserOrDynamicUser": + {"description_bad": "Service runs as root user", + "weight": 0, + "range": 10 + }, +"SupplementaryGroups": + {"description_good": "Service has no supplementary groups", + "description_bad": "Service runs with supplementary groups", + "description_na": "Service runs as root, option does not matter", + "weight": 200, + "range": 1 + }, +"PrivateDevices": + {"description_good": "Service has no access to hardware devices", + "description_bad": "Service potentially has access to hardware devices", + "weight": 1000, + "range": 1 + }, +"PrivateMounts": + {"description_good": "Service cannot install system mounts", + "description_bad": "Service may install system mounts", + "weight": 1000, + "range": 1 + }, +"PrivateNetwork": + {"description_good": "Service doesn't have access to the host's network", + "description_bad": "Service has access to the host's network", + "weight": 6000, + "range": 1 + }, +"PrivateTmp": + {"description_good": "Service has no access to other software's temporary files", + "description_bad": "Service has access to other software's temporary files", + "weight": 1000, + "range": 1 + }, +"PrivateUsers": + {"description_good": "Service does not have access to other users", + "description_bad": "Service has access to other users", + "weight": 1000, + "range": 1 + }, +"ProtectControlGroups": + {"description_good": "Service cannot modify the control group file system", + "description_bad": "Service may modify the control group file system", + "weight": 1000, + "range": 1 + }, +"ProtectKernelModules": + {"description_good": "Service cannot load or read kernel modules", + "description_bad": "Service may load or read kernel modules", + "weight": 1000, + "range": 1 + }, +"ProtectKernelTunables": + {"description_good": "Service cannot alter kernel tunables (/proc/sys, …)", + "description_bad": "Service may alter kernel tunables", + "weight": 1000, + "range": 1 + }, +"ProtectKernelLogs": + {"description_good": "Service cannot read from or write to the kernel log ring buffer", + "description_bad": "Service may read from or write to the kernel log ring buffer", + "weight": 1000, + "range": 1 + }, +"ProtectClock": + {"description_good": "Service cannot write to the hardware clock or system clock", + "description_bad": "Service may write to the hardware clock or system clock", + "weight": 1000, + "range": 1 + }, +"ProtectHome": + {"weight": 1000, + "range": 10 + }, +"ProtectHostname": + {"description_good": "Service cannot change system host/domainname", + "description_bad": "Service may change system host/domainname", + "weight": 50, + "range": 1 + }, +"ProtectSystem": + {"weight": 1000, + "range": 10 + }, +"RootDirectoryOrRootImage": + {"description_good": "Service has its own root directory/image", + "description_bad": "Service runs within the host's root directory", + "weight": 200, + "range": 1 + }, +"LockPersonality": + {"description_good": "Service cannot change ABI personality", + "description_bad": "Service may change ABI personality", + "weight": 100, + "range": 1 + }, +"MemoryDenyWriteExecute": + {"description_good": "Service cannot create writable executable memory mappings", + "description_bad": "Service may create writable executable memory mappings", + "weight": 100, + "range": 1 + }, +"NoNewPrivileges": + {"description_good": "Service processes cannot acquire new privileges", + "description_bad": "Service processes may acquire new privileges", + "weight": 1000, + "range": 1 + }, +"CapabilityBoundingSet_CAP_SYS_ADMIN": + {"description_good": "Service has no administrator privileges", + "description_bad": "Service has administrator privileges", + "weight": 1500, + "range": 1 + }, +"CapabilityBoundingSet_CAP_SET_UID_GID_PCAP": + {"description_good": "Service cannot change UID/GID identities/capabilities", + "description_bad": "Service may change UID/GID identities/capabilities", + "weight": 1500, + "range": 1 + }, +"CapabilityBoundingSet_CAP_SYS_PTRACE": + {"description_good": "Service has no ptrace() debugging abilities", + "description_bad": "Service has ptrace() debugging abilities", + "weight": 1500, + "range": 1 + }, +"CapabilityBoundingSet_CAP_SYS_TIME": + {"description_good": "Service processes cannot change the system clock", + "description_bad": "Service processes may change the system clock", + "weight": 1000, + "range": 1 + }, +"CapabilityBoundingSet_CAP_NET_ADMIN": + {"description_good": "Service has no network configuration privileges", + "description_bad": "Service has network configuration privileges", + "weight": 1000, + "range": 1 + }, +"CapabilityBoundingSet_CAP_SYS_RAWIO": + {"description_good": "Service has no raw I/O access", + "description_bad": "Service has raw I/O access", + "weight": 1000, + "range": 1 + }, +"CapabilityBoundingSet_CAP_SYS_MODULE": + {"description_good": "Service cannot load kernel modules", + "description_bad": "Service may load kernel modules", + "weight": 1000, + "range": 1 + }, +"CapabilityBoundingSet_CAP_AUDIT": + {"description_good": "Service has no audit subsystem access", + "description_bad": "Service has audit subsystem access", + "weight": 500, + "range": 1 + }, +"CapabilityBoundingSet_CAP_SYSLOG": + {"description_good": "Service has no access to kernel logging", + "description_bad": "Service has access to kernel logging", + "weight": 500, + "range": 1 + }, +"CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE": + {"description_good": "Service has no privileges to change resource use parameters", + "description_bad": "Service has privileges to change resource use parameters", + "weight": 500, + "range": 1 + }, +"CapabilityBoundingSet_CAP_MKNOD": + {"description_good": "Service cannot create device nodes", + "description_bad": "Service may create device nodes", + "weight": 500, + "range": 1 + }, +"CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP": + {"description_good": "Service cannot change file ownership/access mode/capabilities", + "description_bad": "Service may change file ownership/access mode/capabilities unrestricted", + "weight": 1000, + "range": 1 + }, +"CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER": + {"description_good": "Service cannot override UNIX file/IPC permission checks", + "description_bad": "Service may override UNIX file/IPC permission checks", + "weight": 1000, + "range": 1 + }, +"CapabilityBoundingSet_CAP_KILL": + {"description_good": "Service cannot send UNIX signals to arbitrary processes", + "description_bad": "Service may send UNIX signals to arbitrary processes", + "weight": 500, + "range": 1 + }, +"CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW": + {"description_good": "Service has no elevated networking privileges", + "description_bad": "Service has elevated networking privileges", + "weight": 500, + "range": 1 + }, +"CapabilityBoundingSet_CAP_SYS_BOOT": + {"description_good": "Service cannot issue reboot()", + "description_bad": "Service may issue reboot()", + "weight": 100, + "range": 1 + }, +"CapabilityBoundingSet_CAP_MAC": + {"description_good": "Service cannot adjust SMACK MAC", + "description_bad": "Service may adjust SMACK MAC", + "weight": 100, + "range": 1 + }, +"CapabilityBoundingSet_CAP_LINUX_IMMUTABLE": + {"description_good": "Service cannot mark files immutable", + "description_bad": "Service may mark files immutable", + "weight": 75, + "range": 1 + }, +"CapabilityBoundingSet_CAP_IPC_LOCK": + {"description_good": "Service cannot lock memory into RAM", + "description_bad": "Service may lock memory into RAM", + "weight": 50, + "range": 1 + }, +"CapabilityBoundingSet_CAP_SYS_CHROOT": + {"description_good": "Service cannot issue chroot()", + "description_bad": "Service may issue chroot()", + "weight": 50, + "range": 1 + }, +"CapabilityBoundingSet_CAP_BLOCK_SUSPEND": + {"description_good": "Service cannot establish wake locks", + "description_bad": "Service may establish wake locks", + "weight": 25, + "range": 1 + }, +"CapabilityBoundingSet_CAP_WAKE_ALARM": + {"description_good": "Service cannot program timers that wake up the system", + "description_bad": "Service may program timers that wake up the system", + "weight": 25, + "range": 1 + }, +"CapabilityBoundingSet_CAP_LEASE": + {"description_good": "Service cannot create file leases", + "description_bad": "Service may create file leases", + "weight": 25, + "range": 1 + }, +"CapabilityBoundingSet_CAP_SYS_TTY_CONFIG": + {"description_good": "Service cannot issue vhangup()", + "description_bad": "Service may issue vhangup()", + "weight": 25, + "range": 1 + }, +"CapabilityBoundingSet_CAP_SYS_PACCT": + {"description_good": "Service cannot use acct()", + "description_bad": "Service may use acct()", + "weight": 25, + "range": 1 + }, +"CapabilityBoundingSet_CAP_BPF": + {"description_good": "Service may load BPF programs", + "description_bad": "Service may not load BPF programs", + "weight": 25, + "range": 1 + }, +"UMask": + {"weight": 100, + "range": 10 + }, +"KeyringMode": + {"description_good": "Service doesn't share key material with other services", + "description_bad": "Service shares key material with other service", + "weight": 1000, + "range": 1 + }, +"ProtectProc": + {"description_good": "Service has restricted access to process tree(/proc hidepid=)", + "description_bad": "Service has full access to process tree(/proc hidepid=)", + "weight": 1000, + "range": 3 + }, +"ProcSubset": + {"description_good": "Service has no access to non-process/proc files(/proc subset=)", + "description_bad": "Service has full access to non-process/proc files(/proc subset=)", + "weight": 10, + "range": 1 + }, +"NotifyAccess": + {"description_good": "Service child processes cannot alter service state", + "description_bad": "Service child processes may alter service state", + "weight": 1000, + "range": 1 + }, +"RemoveIPC": + {"description_good": "Service user cannot leave SysV IPC objects around", + "description_bad": "Service user may leave SysV IPC objects around", + "description_na": "Service runs as root, option does not apply", + "weight": 100, + "range": 1 + }, +"Delegate": + {"description_good": "Service does not maintain its own delegated control group subtree", + "description_bad": "Service maintains its own delegated control group subtree", + "weight": 100, + "range": 1 + }, +"RestrictRealtime": + {"description_good": "Service realtime scheduling access is restricted", + "description_bad": "Service may acquire realtime scheduling", + "weight": 500, + "range": 1 + }, +"RestrictSUIDSGID": + {"description_good": "SUID/SGIDfilecreationbyserviceisrestricted", + "description_bad": "ServicemaycreateSUID/SGIDfiles", + "weight": 1000, + "range": 1 + }, +"RestrictNamespaces_user": + {"description_good": "Servicecannotcreateusernamespaces", + "description_bad": "Servicemaycreateusernamespaces", + "weight": 1500, + "range": 1 + }, +"RestrictNamespaces_mnt": + {"description_good": "Service cannot create file system namespaces", + "description_bad": "Service may create file system namespaces", + "weight": 500, + "range": 1 + }, +"RestrictNamespaces_ipc": + {"description_good": "Service cannot create IPC namespaces", + "description_bad": "Service may create IPC namespaces", + "weight": 500, + "range": 1 + }, +"RestrictNamespaces_pid": + {"description_good": "Service cannot create process namespaces", + "description_bad": "Service may create process namespaces", + "weight": 500, + "range": 1 + }, +"RestrictNamespaces_cgroup": + {"description_good": "Service cannot create cgroup namespaces", + "description_bad": "Service may create cgroup namespaces", + "weight": 500, + "range": 1 + }, +"RestrictNamespaces_net": + {"description_good": "Service cannot create network namespaces", + "description_bad": "Service may create network namespaces", + "weight": 500, + "range": 1 + }, +"RestrictNamespaces_uts": + {"description_good": "Service cannot create hostname namespaces", + "description_bad": "Service may create hostname namespaces", + "weight": 100, + "range": 1 + }, +"RestrictAddressFamilies_AF_INET_INET6": + {"description_good": "Service cannot allocate Internet sockets", + "description_bad": "Service may allocate Internet sockets", + "weight": 1500, + "range": 1 + }, +"RestrictAddressFamilies_AF_UNIX": + {"description_good": "Service cannot allocate local sockets", + "description_bad": "Service may allocate local sockets", + "weight": 25, + "range": 1 + }, +"RestrictAddressFamilies_AF_NETLINK": + {"description_good": "Service cannot allocate netlink sockets", + "description_bad": "Service may allocate netlink sockets", + "weight": 200, + "range": 1 + }, +"RestrictAddressFamilies_AF_PACKET": + {"description_good": "Service cannot allocate packet sockets", + "description_bad": "Service may allocate packet sockets", + "weight": 1000, + "range": 1 + }, +"RestrictAddressFamilies_OTHER": + {"description_good": "Service cannot allocate exotic sockets", + "description_bad": "Service may allocate exotic sockets", + "weight": 1250, + "range": 1 + }, +"SystemCallArchitectures": + {"weight": 1000, + "range": 10 + }, +"SystemCallFilter_swap": + {"weight": 1000, + "range": 10 + }, +"SystemCallFilter_obsolete": + {"weight": 250, + "range": 10 + }, +"SystemCallFilter_clock": + {"weight": 1000, + "range": 10 + }, +"SystemCallFilter_cpu_emulation": + {"weight": 250, + "range": 10 + }, +"SystemCallFilter_debug": + {"weight": 1000, + "range": 10 + }, +"SystemCallFilter_mount": + {"weight": 1000, + "range": 10 + }, +"SystemCallFilter_module": + {"weight": 1000, + "range": 10 + }, +"SystemCallFilter_raw_io": + {"weight": 1000, + "range": 10 + }, +"SystemCallFilter_reboot": + {"weight": 1000, + "range": 10 + }, +"SystemCallFilter_privileged": + {"weight": 700, + "range": 10 + }, +"SystemCallFilter_resources": + {"weight": 700, + "range": 10 + }, +"IPAddressDeny": + {"weight": 1000, + "range": 10 + }, +"DeviceAllow": + {"weight": 1000, + "range": 10 + }, +"AmbientCapabilities": + {"description_good": "Service process does not receive ambient capabilities", + "description_bad": "Service process receives ambient capabilities", + "weight": 500, + "range": 1 + }, +"INVALID_ID": + {"weight": 1000, + "range": 10 + } +} +EOF + +# Reads in custom security requirements from the parsed .json file and uses these for comparison +systemd-analyze security --threshold=90 --offline=true \ + --security-policy=/tmp/testfile.json \ + --root=/tmp/img/ testfile.service + +# The strict profile adds a lot of sanboxing options +systemd-analyze security --threshold=25 --offline=true \ + --security-policy=/tmp/testfile.json \ + --profile=strict \ + --root=/tmp/img/ testfile.service + +# The trusted profile doesn't add any sanboxing options +(! systemd-analyze security --threshold=25 --offline=true \ + --security-policy=/tmp/testfile.json \ + --profile=/usr/lib/systemd/portable/profile/trusted/service.conf \ + --root=/tmp/img/ testfile.service) + +(! systemd-analyze security --threshold=50 --offline=true \ + --security-policy=/tmp/testfile.json \ + --root=/tmp/img/ testfile.service) + +rm /tmp/img/usr/lib/systemd/system/testfile.service + +if systemd-analyze --version | grep -q -F "+ELFUTILS"; then + systemd-analyze inspect-elf --json=short /lib/systemd/systemd | grep -q -F '"elfType":"executable"' +fi + +systemd-analyze --threshold=90 security systemd-journald.service + +# issue 23663 +check() {( + set +x + output=$(systemd-analyze security --offline="${2?}" "${3?}" | grep -F 'SystemCallFilter=') + assert_in "System call ${1?} list" "$output" + assert_in "[+✓] SystemCallFilter=~@swap" "$output" + assert_in "[+✓] SystemCallFilter=~@resources" "$output" + assert_in "[+✓] SystemCallFilter=~@reboot" "$output" + assert_in "[+✓] SystemCallFilter=~@raw-io" "$output" + assert_in "[-✗] SystemCallFilter=~@privileged" "$output" + assert_in "[+✓] SystemCallFilter=~@obsolete" "$output" + assert_in "[+✓] SystemCallFilter=~@mount" "$output" + assert_in "[+✓] SystemCallFilter=~@module" "$output" + assert_in "[+✓] SystemCallFilter=~@debug" "$output" + assert_in "[+✓] SystemCallFilter=~@cpu-emulation" "$output" + assert_in "[-✗] SystemCallFilter=~@clock" "$output" +)} + +export -n SYSTEMD_LOG_LEVEL + +mkdir -p /run/systemd/system +cat >/run/systemd/system/allow-list.service </run/systemd/system/deny-list.service <&1) +name=$(echo "$output" | awk '{ print $4 }' | cut -d';' -f1) + +check allow yes /run/systemd/transient/"$name" +check allow no "$name" + +output=$(systemd-run -p "SystemCallFilter=~@known" -p "SystemCallFilter=@system-service" -p "SystemCallFilter=~@resources:ENOANO @privileged" -p "SystemCallFilter=@clock" sleep 60 2>&1) +name=$(echo "$output" | awk '{ print $4 }' | cut -d';' -f1) + +check deny yes /run/systemd/transient/"$name" +check deny no "$name" + +# Let's also test the "image-policy" verb + +systemd-analyze image-policy '*' 2>&1 | grep -q -F "Long form: =verity+signed+encrypted+unprotected+unused+absent" +systemd-analyze image-policy '-' 2>&1 | grep -q -F "Long form: =unused+absent" +systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -F "Long form: usr=verity:home=encrypted:=unused+absent" +systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^home \+encrypted \+' +systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^usr \+verity \+' +systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^root \+ignore \+' +systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^usr-verity \+unprotected \+' + +(! systemd-analyze image-policy 'doedel') + +# Output is very hard to predict, but let's run it for coverage anyway +systemd-analyze pcrs +systemd-analyze pcrs --json=pretty +systemd-analyze pcrs 14 7 0 ima + +systemd-analyze architectures +systemd-analyze architectures --json=pretty +systemd-analyze architectures x86 +systemd-analyze architectures x86-64 +systemd-analyze architectures native +systemd-analyze architectures uname + +systemd-analyze log-level info + +touch /testok diff --git a/test/units/TEST-66-DEVICE-ISOLATION-device-isolation.service b/test/units/TEST-66-DEVICE-ISOLATION-device-isolation.service new file mode 100644 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/TEST-66-DEVICE-ISOLATION.sh b/test/units/TEST-66-DEVICE-ISOLATION.sh new file mode 100755 index 0000000..ccdfcb2 --- /dev/null +++ b/test/units/TEST-66-DEVICE-ISOLATION.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +RESULTS_FILE=/tmp/TEST-66-DEVICE-ISOLATION.serviceresults + +systemd-analyze log-level debug + +systemctl start TEST-66-DEVICE-ISOLATION-device-isolation.service + +sleep 5 +grep -q "Operation not permitted" "$RESULTS_FILE" + +systemctl daemon-reload +systemctl daemon-reexec + +systemctl stop TEST-66-DEVICE-ISOLATION-device-isolation.service + +grep -q "thisshouldnotbehere" "$RESULTS_FILE" && exit 42 + +systemd-analyze log-level info + +touch /testok diff --git a/test/units/TEST-67-INTEGRITY.sh b/test/units/TEST-67-INTEGRITY.sh new file mode 100755 index 0000000..a42fd66 --- /dev/null +++ b/test/units/TEST-67-INTEGRITY.sh @@ -0,0 +1,121 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -euxo pipefail + +export DM_NAME="integrity_test" +export FULL_DM_DEV_NAME="/dev/mapper/${DM_NAME}" +export FS_UUID="01234567-ffff-eeee-eeee-0123456789ab" +export GEN="/var/run/systemd/generator" + +image_dir="" + +cleanup() +{ + if [ -z "${image_dir}" ]; then + return + fi + + if [ -f "${image_dir}/image" ]; then + if [ -e "${FULL_DM_DEV_NAME}" ]; then + integritysetup close "${DM_NAME}" + fi + losetup -d "${loop}" + fi + + rm -rf "${image_dir}" +} + +trap cleanup EXIT + +build_integrity_tab() +{ +cat <"/etc/integritytab" +${DM_NAME} ${loop} - integrity-algorithm=$1 +EOF +} + +image_dir="$(mktemp -d -t -p / integrity.tmp.XXXXXX)" +if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then + echo "mktemp under / failed" + exit 1 +fi + +dd if=/dev/zero of="${image_dir}/image" bs=1048576 count=64 || exit 1 +dd if=/dev/zero of="${image_dir}/data" bs=1048576 count=64 || exit 1 +loop="$(losetup --show -f "${image_dir}/image")" + +if [[ ! -e ${loop} ]]; then + echo "Loopback device created not found!" + exit 1 +fi + +# Do one iteration with a separate data device, to test those branches +separate_data=1 + +for algorithm in crc32c crc32 sha1 sha256 +do + if [ "${separate_data}" -eq 1 ]; then + data_option="--data-device=${image_dir}/data" + else + data_option="" + fi + integritysetup format "${loop}" --batch-mode -I "${algorithm}" "${data_option}" || exit 1 + integritysetup open -I "${algorithm}" "${loop}" "${DM_NAME}" "${data_option}" || exit 1 + mkfs.ext4 -U "${FS_UUID}" "${FULL_DM_DEV_NAME}" || exit 1 + + # Give userspace time to handle udev events for new FS showing up ... + udevadm settle + + integritysetup close "${DM_NAME}" || exit 1 + + # create integritytab, generate units, start service + if [ "${separate_data}" -eq 1 ]; then + data_option=",data-device=${image_dir}/data" + else + data_option="" + fi + build_integrity_tab "${algorithm}${data_option}" + + # Cause the generator to re-run + systemctl daemon-reload || exit 1 + + # Check for existence of unit files... + if [[ ! -e "/run/systemd/generator/systemd-integritysetup@${DM_NAME}.service" ]]; then + echo "Service file does not exist!" + exit 1 + fi + + # Make sure we are in a consistent state, e.g. not already active before we start + systemctl stop systemd-integritysetup@"${DM_NAME}".service || exit 1 + systemctl start systemd-integritysetup@"${DM_NAME}".service || exit 1 + # Reset the start-limit counters, as we're going to restart the service a couple of times + systemctl reset-failed systemd-integritysetup@"${DM_NAME}".service + + # Check the signature on the FS to ensure we can retrieve it and that is matches + if [ -e "${FULL_DM_DEV_NAME}" ]; then + # If a separate device is used for the metadata storage, then blkid will return one of the loop devices + if [ "${separate_data}" -eq 1 ]; then + dev_name="$(integritysetup status ${DM_NAME} | grep '^\s*device:' | awk '{print $2}')" + else + dev_name="${FULL_DM_DEV_NAME}" + fi + if [ "${dev_name}" != "$(blkid -U "${FS_UUID}")" ]; then + echo "Failed to locate FS with matching UUID!" + exit 1 + fi + else + echo "Failed to bring up integrity device!" + exit 1 + fi + + systemctl stop systemd-integritysetup@"${DM_NAME}".service || exit 1 + + if [ -e "${FULL_DM_DEV_NAME}" ]; then + echo "Expecting ${FULL_DM_DEV_NAME} to not exist after stopping unit!" + exit 1 + fi + + separate_data=0 +done + +touch /testok diff --git a/test/units/TEST-68-PROPAGATE-EXIT-STATUS.sh b/test/units/TEST-68-PROPAGATE-EXIT-STATUS.sh new file mode 100755 index 0000000..11da48a --- /dev/null +++ b/test/units/TEST-68-PROPAGATE-EXIT-STATUS.sh @@ -0,0 +1,216 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# Wait for a service to enter a state within a timeout period, if it doesn't +# enter the desired state within the timeout period then this function will +# exit the test case with a non zero exit code. +wait_on_state_or_fail() { + service=$1 + expected_state=$2 + timeout=$3 + + state=$(systemctl show "$service" --property=ActiveState --value) + while [ "$state" != "$expected_state" ]; do + if [ "$timeout" = "0" ]; then + systemd-analyze log-level info + exit 1 + fi + timeout=$((timeout - 1)) + sleep 1 + state=$(systemctl show "$service" --property=ActiveState --value) + done +} + +systemd-analyze log-level debug + +cat >/run/systemd/system/testservice-failure-68.service </run/systemd/system/testservice-failure-68-template.service </run/systemd/system/testservice-success-68.service </run/systemd/system/testservice-success-68-template.service </tmp/check_on_success.sh <<"EOF" +#!/bin/sh + +set -ex +env | sort +if [ "$MONITOR_SERVICE_RESULT" != "success" ]; then + echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'success'" + exit 1 +fi + +if [ "$MONITOR_EXIT_CODE" != "exited" ]; then + echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'" + exit 1 +fi + +if [ "$MONITOR_EXIT_STATUS" != "0" ]; then + echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '0'" + exit 1 +fi + +if [ -z "$MONITOR_INVOCATION_ID" ]; then + echo "MONITOR_INVOCATION_ID unset" + exit 1 +fi + +if [ "$MONITOR_UNIT" != "testservice-success-68.service" ] && + [ "$MONITOR_UNIT" != "testservice-success-68-template.service" ] && + [ "$MONITOR_UNIT" != "testservice-transient-success-68.service" ]; then + + echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-success-68[-template].service'" + exit 1 +fi + +exit 0 +EOF +chmod +x /tmp/check_on_success.sh + +cat >/run/systemd/system/testservice-success-exit-handler-68.service </run/systemd/system/testservice-success-exit-handler-68-template@.service </tmp/check_on_failure.sh <<"EOF" +#!/bin/sh + +set -ex +env | sort +if [ "$MONITOR_SERVICE_RESULT" != "exit-code" ]; then + echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'exit-code'" + exit 1 +fi + +if [ "$MONITOR_EXIT_CODE" != "exited" ]; then + echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'" + exit 1 +fi + +if [ "$MONITOR_EXIT_STATUS" != "1" ]; then + echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '1'" + exit 1 +fi + +if [ -z "$MONITOR_INVOCATION_ID" ]; then + echo "MONITOR_INVOCATION_ID unset" + exit 1 +fi + +if [ "$MONITOR_UNIT" != "testservice-failure-68.service" ] && + [ "$MONITOR_UNIT" != "testservice-failure-68-template.service" ] && + [ "$MONITOR_UNIT" != "testservice-transient-failure-68.service" ]; then + + echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-failure-68[-template].service'" + exit 1 +fi + +exit 0 +EOF +chmod +x /tmp/check_on_failure.sh + + +cat >/run/systemd/system/testservice-failure-exit-handler-68.service </run/systemd/system/testservice-failure-exit-handler-68-template@.service <.*), 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/TEST-70-TPM2.creds.sh b/test/units/TEST-70-TPM2.creds.sh new file mode 100755 index 0000000..e66bfd1 --- /dev/null +++ b/test/units/TEST-70-TPM2.creds.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +export SYSTEMD_LOG_LEVEL=debug + +# Ensure that sandboxing doesn't stop creds from being accessible +echo "test" > /tmp/testdata +systemd-creds encrypt /tmp/testdata /tmp/testdata.encrypted --with-key=tpm2 +# LoadCredentialEncrypted +systemd-run -p PrivateDevices=yes -p LoadCredentialEncrypted=testdata.encrypted:/tmp/testdata.encrypted --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata +# SetCredentialEncrypted +systemd-run -p PrivateDevices=yes -p SetCredentialEncrypted=testdata.encrypted:"$(cat /tmp/testdata.encrypted)" --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata + +rm -f /tmp/testdata diff --git a/test/units/TEST-70-TPM2.cryptenroll.sh b/test/units/TEST-70-TPM2.cryptenroll.sh new file mode 100755 index 0000000..f18ef02 --- /dev/null +++ b/test/units/TEST-70-TPM2.cryptenroll.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +cryptenroll_wipe_and_check() {( + set +o pipefail + + : >/tmp/cryptenroll.out + systemd-cryptenroll "$@" |& tee /tmp/cryptenroll.out + grep -qE "Wiped slot [[:digit:]]+" /tmp/cryptenroll.out +)} + +# There is an external issue with libcryptsetup on ppc64 that hits 95% of Ubuntu ppc64 test runs, so skip it +if [[ "$(uname -m)" == "ppc64le" ]]; then + echo "Skipping systemd-cryptenroll tests on ppc64le, see https://github.com/systemd/systemd/issues/27716" + exit 0 +fi + +export SYSTEMD_LOG_LEVEL=debug +IMAGE="$(mktemp /tmp/systemd-cryptenroll-XXX.image)" + +truncate -s 20M "$IMAGE" +echo -n password >/tmp/password +# Change file mode to avoid "/tmp/password has 0644 mode that is too permissive" messages +chmod 0600 /tmp/password +cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/password + +# Enroll additional tokens, keys, and passwords to exercise the list and wipe stuff +systemd-cryptenroll --unlock-key-file=/tmp/password --tpm2-device=auto "$IMAGE" +NEWPASSWORD="" systemd-cryptenroll --unlock-key-file=/tmp/password --password "$IMAGE" +NEWPASSWORD=foo systemd-cryptenroll --unlock-key-file=/tmp/password --password "$IMAGE" +for _ in {0..9}; do + systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE" +done +PASSWORD="" NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE" +# Do some basic checks before we start wiping stuff +systemd-cryptenroll "$IMAGE" +systemd-cryptenroll "$IMAGE" | grep password +systemd-cryptenroll "$IMAGE" | grep recovery +# Let's start wiping +cryptenroll_wipe_and_check "$IMAGE" --wipe=empty +(! cryptenroll_wipe_and_check "$IMAGE" --wipe=empty) +cryptenroll_wipe_and_check "$IMAGE" --wipe=empty,0 +PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --wipe=0,0,empty,0,pkcs11,fido2,000,recovery,password --password +systemd-cryptenroll "$IMAGE" | grep password +(! systemd-cryptenroll "$IMAGE" | grep recovery) +# We shouldn't be able to wipe all keyslots without enrolling a new key first +(! systemd-cryptenroll "$IMAGE" --wipe=all) +PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --password --wipe=all +# Check if the newly (and only) enrolled password works +(! systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE") +(! PASSWORD="" systemd-cryptenroll --recovery-key "$IMAGE") +PASSWORD=foo systemd-cryptenroll --recovery-key "$IMAGE" + +systemd-cryptenroll --fido2-with-client-pin=false "$IMAGE" +systemd-cryptenroll --fido2-with-user-presence=false "$IMAGE" +systemd-cryptenroll --fido2-with-user-verification=false "$IMAGE" +systemd-cryptenroll --tpm2-pcrs=8 "$IMAGE" +systemd-cryptenroll --tpm2-pcrs=boot-loader-code+boot-loader-config "$IMAGE" + +# Unlocking using TPM2 +PASSWORD=foo systemd-cryptenroll --tpm2-device=auto "$IMAGE" +systemd-cryptenroll --unlock-tpm2-device=auto --recovery-key "$IMAGE" +systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --wipe-slot=tpm2 "$IMAGE" + +# Add PIN to TPM2 enrollment +NEWPIN=1234 systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --tpm2-with-pin=yes "$IMAGE" + +# Change PIN on TPM2 enrollment +PIN=1234 NEWPIN=4321 systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --tpm2-with-pin=yes "$IMAGE" +PIN=4321 systemd-cryptenroll --unlock-tpm2-device=auto --recovery-key "$IMAGE" + +(! systemd-cryptenroll --fido2-with-client-pin=false) +(! systemd-cryptenroll --fido2-with-user-presence=f "$IMAGE" /tmp/foo) +(! systemd-cryptenroll --fido2-with-client-pin=1234 "$IMAGE") +(! systemd-cryptenroll --fido2-with-user-presence=1234 "$IMAGE") +(! systemd-cryptenroll --fido2-with-user-verification=1234 "$IMAGE") +(! systemd-cryptenroll --tpm2-with-pin=1234 "$IMAGE") +(! systemd-cryptenroll --recovery-key --password "$IMAGE") +(! systemd-cryptenroll --password --recovery-key "$IMAGE") +(! systemd-cryptenroll --password --fido2-device=auto "$IMAGE") +(! systemd-cryptenroll --password --pkcs11-token-uri=auto "$IMAGE") +(! systemd-cryptenroll --password --tpm2-device=auto "$IMAGE") +(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-fido2-device=auto "$IMAGE") +(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-key-file=/tmp/unlock "$IMAGE") +(! systemd-cryptenroll --fido2-credential-algorithm=es512 "$IMAGE") +(! systemd-cryptenroll --tpm2-public-key-pcrs=key "$IMAGE") +(! systemd-cryptenroll --tpm2-pcrs=key "$IMAGE") +(! systemd-cryptenroll --tpm2-pcrs=44+8 "$IMAGE") +(! systemd-cryptenroll --tpm2-pcrs=hello "$IMAGE") +(! systemd-cryptenroll --wipe-slot "$IMAGE") +(! systemd-cryptenroll --wipe-slot=10240000 "$IMAGE") +(! systemd-cryptenroll --fido2-device=auto --unlock-fido2-device=auto "$IMAGE") + +rm -f "$IMAGE" diff --git a/test/units/TEST-70-TPM2.cryptsetup.sh b/test/units/TEST-70-TPM2.cryptsetup.sh new file mode 100755 index 0000000..cb7c8b1 --- /dev/null +++ b/test/units/TEST-70-TPM2.cryptsetup.sh @@ -0,0 +1,227 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +export SYSTEMD_LOG_LEVEL=debug + +cryptsetup_has_token_plugin_support() { + local plugin_path + + plugin_path="$(cryptsetup --help | sed -nr 's/.*LUKS2 external token plugin path: (.*)\./\1/p')/libcryptsetup-token-systemd-tpm2.so)" + cryptsetup --help | grep -q 'LUKS2 external token plugin support is compiled-in' && [[ -f "$plugin_path" ]] +} + +tpm_check_failure_with_wrong_pin() { + local testIMAGE="${1:?}" + local badpin="${2:?}" + local goodpin="${3:?}" + + # We need to be careful not to trigger DA lockout; allow 2 failures + tpm2_dictionarylockout -s -n 2 + (! PIN=$badpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1) + # Verify the correct PIN works, to be sure the failure wasn't a DA lockout + PIN=$goodpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1 + systemd-cryptsetup detach test-volume + # Clear/reset the DA lockout counter + tpm2_dictionarylockout -c +} + +at_exit() { + # Evict the TPM primary key that we persisted + if [[ -n "${PERSISTENT_HANDLE:-}" ]]; then + tpm2_evictcontrol -c "$PERSISTENT_HANDLE" + fi +} + +trap at_exit EXIT + +# Prepare a fresh disk image +IMAGE="$(mktemp /tmp/systemd-cryptsetup-XXX.IMAGE)" + +truncate -s 20M "$IMAGE" +echo -n passphrase >/tmp/passphrase +# Change file mode to avoid "/tmp/passphrase has 0644 mode that is too permissive" messages +chmod 0600 /tmp/passphrase +cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase + +# Unlocking via keyfile +systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto "$IMAGE" + +# Enroll unlock with default PCR policy +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +# Check with wrong PCR +tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000 +(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1) + +# Enroll unlock with PCR+PIN policy +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE" +PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +# Check failure with wrong PIN; try a few times to make sure we avoid DA lockout +for _ in {0..3}; do + tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456 +done + +# Check LUKS2 token plugin unlock (i.e. without specifying tpm2-device=auto) +if cryptsetup_has_token_plugin_support; then + PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - headless=1 + systemd-cryptsetup detach test-volume + + # Check failure with wrong PIN + for _ in {0..3}; do + tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456 + done +else + echo 'cryptsetup has no LUKS2 token plugin support, skipping' +fi + +# Check failure with wrong PCR (and correct PIN) +tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000 +(! PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1) + +# Enroll unlock with PCR 0+7 +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +# Check with wrong PCR 0 +tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000 +(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1) + +if tpm_has_pcr sha256 12; then + # Enroll using an explicit PCR value (that does match current PCR value) + systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" + EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) + PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE" + systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 + systemd-cryptsetup detach test-volume + + # Same as above plus more PCRs without the value or alg specified + systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" + EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) + PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE" + systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 + systemd-cryptsetup detach test-volume + + # Same as above plus more PCRs with hash alg specified but hash value not specified + systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" + EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) + PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1:sha256,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE" + systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 + systemd-cryptsetup detach test-volume + + # Now the interesting part, enrolling using a hash value that doesn't match the current PCR value + systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" + tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12 + CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) + EXPECTED_PCR_VALUE=$(cat /tmp/pcr.dat /tmp/pcr.dat | openssl dgst -sha256 -r | cut -d ' ' -f 1) + PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE" + (! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1) + tpm2_pcrextend "12:sha256=$CURRENT_PCR_VALUE" + systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 + systemd-cryptsetup detach test-volume + + # enroll TPM using device key instead of direct access, then verify unlock using TPM + tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12 + CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) + tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub + systemd-analyze srk > /tmp/srk2.pub + cmp /tmp/srk.pub /tmp/srk2.pub + if [ -f /run/systemd/tpm2-srk-public-key.tpm2b_public ] ; then + cmp /tmp/srk.pub /run/systemd/tpm2-srk-public-key.tpm2b_public + fi + + # --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS + if openssl_supports_kdf SSKDF; then + PASSWORD=passphrase systemd-cryptenroll --tpm2-device-key=/tmp/srk.pub --tpm2-pcrs="12:sha256=$CURRENT_PCR_VALUE" "$IMAGE" + systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 + systemd-cryptsetup detach test-volume + fi + + rm -f /tmp/pcr.dat /tmp/srk.pub +fi + +# Use default (0) seal key handle +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0 "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x0 "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +# Use SRK seal key handle +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=81000001 "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x81000001 "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +# Test invalid ranges: pcr, nv, session, permanent +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=7 "$IMAGE") # PCR +(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x01000001 "$IMAGE") # NV index +(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x02000001 "$IMAGE") # HMAC/loaded session +(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x03000001 "$IMAGE") # Policy/saved session +(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x40000001 "$IMAGE") # Permanent + +# Use non-SRK persistent seal key handle (by creating/persisting new key) +PRIMARY=/tmp/primary.ctx +tpm2_createprimary -c "$PRIMARY" +PERSISTENT_LINE=$(tpm2_evictcontrol -c "$PRIMARY" | grep persistent-handle) +PERSISTENT_HANDLE="0x${PERSISTENT_LINE##*0x}" +tpm2_flushcontext -t + +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="${PERSISTENT_HANDLE#0x}" "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="$PERSISTENT_HANDLE" "$IMAGE" +systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 +systemd-cryptsetup detach test-volume + +# --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS +if openssl_supports_kdf SSKDF; then + # Make sure that --tpm2-device-key= also works with systemd-repart + tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub + mkdir /tmp/dditest + cat > /tmp/dditest/50-root.conf </tmp/tpmdata1 +echo foobar >/tmp/tpmdata2 + +cat >/tmp/result </tmp/result.json </tmp/result </tmp/result.json </dev/null; then + MEASURE_BANKS+=("--bank=sha1") +fi + +# Sign current PCR state with it +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: | tee "/tmp/pcrsign.sig" +dd if=/dev/urandom of=/tmp/pcrtestdata bs=1024 count=64 +systemd-creds encrypt /tmp/pcrtestdata /tmp/pcrtestdata.encrypted --with-key=host+tpm2-with-public-key --tpm2-public-key="/tmp/pcrsign-public.pem" +systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" | cmp - /tmp/pcrtestdata + +# Invalidate PCR, decrypting should fail now +tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000 +(! systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" >/dev/null) + +# Sign new PCR state, decrypting should work now. +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig2" +systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig2" | cmp - /tmp/pcrtestdata + +# Now, do the same, but with a cryptsetup binding +truncate -s 20M "$IMAGE" +cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase +# Ensure that an unrelated signature, when not requested, is not used +touch /run/systemd/tpm2-pcr-signature.json +systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" "$IMAGE" +# Reset and use the signature now +rm -f /run/systemd/tpm2-pcr-signature.json +systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" --tpm2-signature="/tmp/pcrsign.sig2" "$IMAGE" + +# Check if we can activate that (without the token module stuff) +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1 +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup detach test-volume2 + +# Check if we can activate that (and a second time with the token module stuff enabled) +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1 +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup detach test-volume2 + +# After extending the PCR things should fail +tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000 +(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1) +(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1) + +# But once we sign the current PCRs, we should be able to unlock again +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig3" +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1 +systemd-cryptsetup detach test-volume2 +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1 +systemd-cryptsetup detach test-volume2 + +# Test --append mode and de-duplication. With the same parameters signing should not add a new entry +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig3" >"/tmp/pcrsign.sig4" +cmp "/tmp/pcrsign.sig3" "/tmp/pcrsign.sig4" + +# Sign one more phase, this should +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig4" >"/tmp/pcrsign.sig5" +(! cmp "/tmp/pcrsign.sig4" "/tmp/pcrsign.sig5") + +# Should still be good to unlock, given the old entry still exists +SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig5",headless=1 +systemd-cryptsetup detach test-volume2 + +# Adding both signatures once more should not change anything, due to the deduplication +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig5" >"/tmp/pcrsign.sig6" +"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig6" >"/tmp/pcrsign.sig7" +cmp "/tmp/pcrsign.sig5" "/tmp/pcrsign.sig7" + +rm -f "$IMAGE" diff --git a/test/units/TEST-70-TPM2.pcrextend.sh b/test/units/TEST-70-TPM2.pcrextend.sh new file mode 100755 index 0000000..318fce0 --- /dev/null +++ b/test/units/TEST-70-TPM2.pcrextend.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +export SYSTEMD_LOG_LEVEL=debug +SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend" + +if [[ ! -x "${SD_PCREXTEND:?}" ]] || ! tpm_has_pcr sha256 11 || ! tpm_has_pcr sha256 15; then + echo "$SD_PCREXTEND or PCR sysfs files not found, skipping PCR extension tests" + exit 0 +fi + +at_exit() { + if [[ $? -ne 0 ]]; then + # Dump the event log on fail, to make debugging a bit easier + jq --seq --slurp /etc/machine-id +SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --machine-id +mv /etc/machine-id.save /etc/machine-id +tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15 + +# And check it matches expectations +diff /tmp/newpcr15 \ + <(cat /tmp/oldpcr15 <(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256) + +# Check that the event log record was properly written +test "$(jq --seq --slurp ".[$RECORD_COUNT].pcr" /run/systemd/system/systemd-pcrextend.socket.d/50-no-condition.conf </tmp/borked +set +e +SYSTEMD_MEASURE_LOG_USERSPACE=/tmp/borked "$SD_PCRLOCK" cel --no-pager --json=pretty +ret=$? +set -e +# If it crashes the exit code will be 149 +test $ret -eq 1 + +SYSTEMD_COLORS=256 "$SD_PCRLOCK" +"$SD_PCRLOCK" cel --no-pager --json=pretty +"$SD_PCRLOCK" log --pcr="$PCRS" +"$SD_PCRLOCK" log --json=pretty --pcr="$PCRS" +"$SD_PCRLOCK" list-components +"$SD_PCRLOCK" list-components --location=250- +"$SD_PCRLOCK" list-components --location=250-:350- +"$SD_PCRLOCK" lock-firmware-config +"$SD_PCRLOCK" lock-gpt +"$SD_PCRLOCK" lock-machine-id +"$SD_PCRLOCK" lock-file-system +"$SD_PCRLOCK" lock-file-system / +"$SD_PCRLOCK" predict --pcr="$PCRS" +"$SD_PCRLOCK" predict --pcr="0x1+0x3+4" +"$SD_PCRLOCK" predict --json=pretty --pcr="$PCRS" + +SD_STUB="$(find /usr/lib/systemd/boot/efi/ -name "systemd-boot*.efi" | head -n1)" +if [[ -n "$SD_STUB" ]]; then + "$SD_PCRLOCK" lock-pe "$SD_STUB" + "$SD_PCRLOCK" lock-pe <"$SD_STUB" + "$SD_PCRLOCK" lock-uki "$SD_STUB" + "$SD_PCRLOCK" lock-uki <"$SD_STUB" +fi + +PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query +# Repeat immediately (this call will have to reuse the nvindex, rather than create it) +"$SD_PCRLOCK" make-policy --pcr="$PCRS" +"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force + +img="/tmp/pcrlock.img" +truncate -s 20M "$img" +echo -n hoho >/tmp/pcrlockpwd +chmod 0600 /tmp/pcrlockpwd +cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$img" /tmp/pcrlockpwd + +systemd-cryptenroll --unlock-key-file=/tmp/pcrlockpwd --tpm2-device=auto --tpm2-pcrlock=/var/lib/systemd/pcrlock.json --wipe-slot=tpm2 "$img" +systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless +systemd-cryptsetup detach pcrlock + +# Measure something into PCR 16 (the "debug" PCR), which should make the activation fail +"$SD_PCREXTEND" --pcr=16 test70 + +"$SD_PCRLOCK" cel --json=pretty + +(! systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless ) + +# Now add a component for it, rebuild policy and it should work (we'll rebuild +# once like that, but don't provide the recovery pin. This should fail, since +# the PCR is hosed after all. But then we'll use recovery pin, and it should +# work. +echo -n test70 | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock --pcr=16 +(! "$SD_PCRLOCK" make-policy --pcr="$PCRS") +PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query + +systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless +systemd-cryptsetup detach pcrlock + +# And now let's do it the clean way, and generate the right policy ahead of time. +echo -n test70-take-two | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock --pcr=16 +"$SD_PCRLOCK" make-policy --pcr="$PCRS" +# the next one should be skipped because redundant +"$SD_PCRLOCK" make-policy --pcr="$PCRS" +# but this one should not be skipped, even if redundant, because we force it +"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force --recovery-pin=show + +"$SD_PCREXTEND" --pcr=16 test70-take-two + +"$SD_PCRLOCK" cel --json=pretty + +systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless +systemd-cryptsetup detach pcrlock + +# Now use the root fs support, i.e. make the tool write a copy of the pcrlock +# file as service credential to some temporary dir and remove the local copy, so that +# it has to use the credential version. +mkdir /tmp/fakexbootldr +SYSTEMD_XBOOTLDR_PATH=/tmp/fakexbootldr SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 "$SD_PCRLOCK" make-policy --pcr="$PCRS" --force +mv /var/lib/systemd/pcrlock.json /var/lib/systemd/pcrlock.json.gone + +systemd-creds decrypt /tmp/fakexbootldr/loader/credentials/pcrlock.*.cred + +SYSTEMD_ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY=/tmp/fakexbootldr/loader/credentials systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,headless +systemd-cryptsetup detach pcrlock + +mv /var/lib/systemd/pcrlock.json.gone /var/lib/systemd/pcrlock.json +SYSTEMD_XBOOTLDR_PATH=/tmp/fakexbootldr SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 "$SD_PCRLOCK" remove-policy + +"$SD_PCRLOCK" unlock-firmware-config +"$SD_PCRLOCK" unlock-gpt +"$SD_PCRLOCK" unlock-machine-id +"$SD_PCRLOCK" unlock-file-system +"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock +"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock + +(! "$SD_PCRLOCK" "") +(! "$SD_PCRLOCK" predict --pcr=-1) +(! "$SD_PCRLOCK" predict --pcr=foo) +(! "$SD_PCRLOCK" predict --pcr=1+1) +(! "$SD_PCRLOCK" predict --pcr=1+++++1) +(! "$SD_PCRLOCK" make-policy --nv-index=0) +(! "$SD_PCRLOCK" make-policy --nv-index=foo) +(! "$SD_PCRLOCK" list-components --location=:) +(! "$SD_PCRLOCK" lock-gpt "") +(! "$SD_PCRLOCK" lock-gpt /dev/sr0) +(! "$SD_PCRLOCK" lock-pe /dev/full) +(! "$SD_PCRLOCK" lock-pe /bin/true) +(! "$SD_PCRLOCK" lock-uki /dev/full) +(! "$SD_PCRLOCK" lock-uki /bin/true) +(! "$SD_PCRLOCK" lock-file-system "") + +# Exercise Varlink API a bit (but first turn off condition) + +mkdir -p /run/systemd/system/systemd-pcrlock.socket.d +cat > /run/systemd/system/systemd-pcrlock.socket.d/50-no-condition.conf </run/systemd/system/systemd-hostnamed.service.d/override.conf </sys/class/dmi/id/uevent + + echo '09/08/2000' >/sys/class/dmi/id/bios_date + stop_hostnamed + assert_in '2000-09-08' "$(hostnamectl)" + + echo '2022' >/sys/class/dmi/id/bios_date + stop_hostnamed + assert_not_in 'Firmware Date' "$(hostnamectl)" + + echo 'garbage' >/sys/class/dmi/id/bios_date + stop_hostnamed + assert_not_in 'Firmware Date' "$(hostnamectl)" +} + +testcase_nss-myhostname() { + local database host i + + HOSTNAME="$(hostnamectl hostname)" + + # Set up a dummy network for _gateway and _outbound labels + ip link add foo type dummy + ip link set up dev foo + ip addr add 10.0.0.2/24 dev foo + for i in {128..150}; do + ip addr add "10.0.0.$i/24" dev foo + done + ip route add 10.0.0.1 dev foo + ip route add default via 10.0.0.1 dev foo + + # Note: `getent hosts` probes gethostbyname2(), whereas `getent ahosts` probes gethostbyname3() + # and gethostbyname4() (through getaddrinfo() -> gaih_inet() -> get_nss_addresses()) + getent hosts -s myhostname + getent ahosts -s myhostname + + # With IPv6 disabled + sysctl -w net.ipv6.conf.all.disable_ipv6=1 + # Everything under .localhost and .localhost.localdomain should resolve to localhost + for host in {foo.,foo.bar.baz.,.,}localhost{,.} {foo.,foo.bar.baz.,.,}localhost.localdomain{,.}; do + run_and_grep "^127\.0\.0\.1\s+localhost$" getent hosts -s myhostname "$host" + run_and_grep "^127\.0\.0\.1\s+STREAM\s+localhost" getent ahosts -s myhostname "$host" + run_and_grep "^127\.0\.0\.1\s+STREAM\s+localhost" getent ahostsv4 -s myhostname "$host" + (! getent ahostsv6 -s myhostname localhost) + done + for i in 2 {128..150}; do + run_and_grep "^10\.0\.0\.$i\s+$HOSTNAME$" getent hosts -s myhostname "$HOSTNAME" + run_and_grep "^10\.0\.0\.$i\s+" getent ahosts -s myhostname "$HOSTNAME" + run_and_grep "^10\.0\.0\.$i\s+" getent ahostsv4 -s myhostname "$HOSTNAME" + run_and_grep "^10\.0\.0\.$i\s+$HOSTNAME$" getent hosts -s myhostname "10.0.0.$i" + run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahosts -s myhostname "10.0.0.$i" + run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahostsv4 -s myhostname "10.0.0.$i" + done + for database in hosts ahosts ahostsv4 ahostsv6; do + (! getent "$database" -s myhostname ::1) + done + (! getent ahostsv6 -s myhostname "$HOSTNAME") + run_and_grep -n "^fe80:[^ ]+\s+STREAM$" getent ahosts -s myhostname "$HOSTNAME" + + # With IPv6 enabled + sysctl -w net.ipv6.conf.all.disable_ipv6=0 + # Everything under .localhost and .localhost.localdomain should resolve to localhost + for host in {foo.,foo.bar.baz.,.,}localhost{,.} {foo.,foo.bar.baz.,.,}localhost.localdomain{,.}; do + run_and_grep "^::1\s+localhost$" getent hosts -s myhostname "$host" + run_and_grep "^::1\s+STREAM" getent ahosts -s myhostname "$host" + run_and_grep "^127\.0\.0\.1\s+STREAM" getent ahosts -s myhostname "$host" + run_and_grep "^127\.0\.0\.1\s+STREAM" getent ahostsv4 -s myhostname "$host" + run_and_grep -n "^::1\s+STREAM" getent ahostsv4 -s myhostname "$host" + run_and_grep "^::1\s+STREAM" getent ahostsv6 -s myhostname "$host" + run_and_grep -n "^127\.0\.0\.1\s+STREAM" getent ahostsv6 -s myhostname "$host" + done + for i in 2 {128..150}; do + run_and_grep "^10\.0\.0\.$i\s+" getent ahosts -s myhostname "$HOSTNAME" + run_and_grep "^10\.0\.0\.$i\s+" getent ahostsv4 -s myhostname "$HOSTNAME" + run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahosts -s myhostname "10.0.0.$i" + run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahostsv4 -s myhostname "10.0.0.$i" + done + run_and_grep "^fe80:[^ ]+\s+$HOSTNAME$" getent hosts -s myhostname "$HOSTNAME" + run_and_grep "^fe80:[^ ]+\s+STREAM" getent ahosts -s myhostname "$HOSTNAME" + run_and_grep "^127\.0\.0\.1\s+localhost$" getent hosts -s myhostname 127.0.0.1 + run_and_grep "^127\.0\.0\.1\s+STREAM\s+127\.0\.0\.1$" getent ahosts -s myhostname 127.0.0.1 + run_and_grep "^::ffff:127\.0\.0\.1\s+STREAM\s+127\.0\.0\.1$" getent ahostsv6 -s myhostname 127.0.0.1 + run_and_grep "^127\.0\.0\.2\s+$HOSTNAME$" getent hosts -s myhostname 127.0.0.2 + run_and_grep "^::1\s+localhost $HOSTNAME$" getent hosts -s myhostname ::1 + run_and_grep "^::1\s+STREAM\s+::1$" getent ahosts -s myhostname ::1 + (! getent ahostsv4 -s myhostname ::1) + + # _gateway + for host in _gateway{,.} 10.0.0.1; do + run_and_grep "^10\.0\.0\.1\s+_gateway$" getent hosts -s myhostname "$host" + run_and_grep "^10\.0\.0\.1\s+STREAM" getent ahosts -s myhostname "$host" + done + + # _outbound + for host in _outbound{,.} 10.0.0.2; do + run_and_grep "^10\.0\.0\.2\s+" getent hosts -s myhostname "$host" + run_and_grep "^10\.0\.0\.2\s+STREAM" getent ahosts -s myhostname "$host" + done + + # Non-existent records + for database in hosts ahosts ahostsv4 ahostsv6; do + (! getent "$database" -s myhostname this.should.not.exist) + done + (! getent hosts -s myhostname 10.254.254.1) + (! getent hosts -s myhostname fd00:dead:beef:cafe::1) +} + +test_varlink() { + A="$(mktemp -u)" + B="$(mktemp -u)" + varlinkctl call /run/systemd/io.systemd.Hostname io.systemd.Hostname.Describe '{}' --json=short > "$A" + hostnamectl --json=short > "$B" + cmp "$A" "$B" +} + +run_testcases + +touch /testok diff --git a/test/units/TEST-72-SYSUPDATE.sh b/test/units/TEST-72-SYSUPDATE.sh new file mode 100755 index 0000000..5e658e0 --- /dev/null +++ b/test/units/TEST-72-SYSUPDATE.sh @@ -0,0 +1,273 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -eux +set -o pipefail + +SYSUPDATE=/lib/systemd/systemd-sysupdate +SECTOR_SIZES=(512 4096) +WORKDIR="$(mktemp -d /var/tmp/test-72-XXXXXX)" +BACKING_FILE="$WORKDIR/joined.raw" +export SYSTEMD_ESP_PATH="$WORKDIR/esp" +export SYSTEMD_XBOOTLDR_PATH="$WORKDIR/xbootldr" +export SYSTEMD_PAGER=cat +export SYSTEMD_LOG_LEVEL=debug + +if [[ ! -x "$SYSUPDATE" ]]; then + echo "no systemd-sysupdate" >/skipped + exit 77 +fi + +# Loopback devices may not be supported. They are used because sfdisk cannot +# change the sector size of a file, and we want to test both 512 and 4096 byte +# sectors. If loopback devices are not supported, we can only test one sector +# size, and the underlying device is likely to have a sector size of 512 bytes. +if [[ ! -e /dev/loop-control ]]; then + echo "No loopback device support" + SECTOR_SIZES=(512) +fi + +at_exit() { + set +e + + losetup -n --output NAME --associated "$BACKING_FILE" | while read -r loop_dev; do + losetup --detach "$loop_dev" + done + + rm -rf "$WORKDIR" +} + +trap at_exit EXIT + +new_version() { + local sector_size="${1:?}" + local version="${2:?}" + + # Create a pair of random partition payloads, and compress one + dd if=/dev/urandom of="$WORKDIR/source/part1-$version.raw" bs="$sector_size" count=2048 + dd if=/dev/urandom of="$WORKDIR/source/part2-$version.raw" bs="$sector_size" count=2048 + gzip -k -f "$WORKDIR/source/part2-$version.raw" + + # Create a random "UKI" payload + echo $RANDOM >"$WORKDIR/source/uki-$version.efi" + + # Create a random extra payload + echo $RANDOM >"$WORKDIR/source/uki-extra-$version.efi" + + # Create tarball of a directory + mkdir -p "$WORKDIR/source/dir-$version" + echo $RANDOM >"$WORKDIR/source/dir-$version/foo.txt" + echo $RANDOM >"$WORKDIR/source/dir-$version/bar.txt" + tar --numeric-owner -C "$WORKDIR/source/dir-$version/" -czf "$WORKDIR/source/dir-$version.tar.gz" . + + (cd "$WORKDIR/source" && sha256sum uki* part* dir-*.tar.gz >SHA256SUMS) +} + +update_now() { + # Update to newest version. First there should be an update ready, then we + # do the update, and then there should not be any ready anymore + + "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new + "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no update + (! "$SYSUPDATE" --definitions="$WORKDIR/defs" --verify=no check-new) +} + +verify_version() { + local block_device="${1:?}" + local sector_size="${2:?}" + local version="${3:?}" + local part1_number="${4:?}" + local part2_number="${5:?}" + local gpt_reserved_sectors part1_offset part2_offset + + gpt_reserved_sectors=$((1024 * 1024 / sector_size)) + part1_offset=$(((part1_number - 1) * 2048 + gpt_reserved_sectors)) + part2_offset=$(((part2_number - 1) * 2048 + gpt_reserved_sectors)) + + # Check the partitions + dd if="$block_device" bs="$sector_size" skip="$part1_offset" count=2048 | cmp "$WORKDIR/source/part1-$version.raw" + dd if="$block_device" bs="$sector_size" skip="$part2_offset" count=2048 | cmp "$WORKDIR/source/part2-$version.raw" + + # Check the UKI + cmp "$WORKDIR/source/uki-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version+3-0.efi" + test -z "$(ls -A "$WORKDIR/esp/EFI/Linux")" + + # Check the extra efi + cmp "$WORKDIR/source/uki-extra-$version.efi" "$WORKDIR/xbootldr/EFI/Linux/uki_$version.efi.extra.d/extra.addon.efi" + + # Check the directories + cmp "$WORKDIR/source/dir-$version/foo.txt" "$WORKDIR/dirs/current/foo.txt" + cmp "$WORKDIR/source/dir-$version/bar.txt" "$WORKDIR/dirs/current/bar.txt" +} + +for sector_size in "${SECTOR_SIZES[@]}"; do + # Disk size of: + # - 1MB for GPT + # - 4 partitions of 2048 sectors each + # - 1MB for backup GPT + disk_size=$((sector_size * 2048 * 4 + 1024 * 1024 * 2)) + rm -f "$BACKING_FILE" + truncate -s "$disk_size" "$BACKING_FILE" + + if [[ -e /dev/loop-control ]]; then + blockdev="$(losetup --find --show --sector-size "$sector_size" "$BACKING_FILE")" + else + blockdev="$BACKING_FILE" + fi + + sfdisk "$blockdev" <"$WORKDIR/defs/01-first.conf" <"$WORKDIR/defs/02-second.conf" <"$WORKDIR/defs/03-third.conf" <"$WORKDIR/defs/04-fourth.conf" <"$WORKDIR/defs/05-fifth.conf" <"$WORKDIR/defs/02-second.conf" <"$WORKDIR/defs/03-third.conf" <>/run/systemd/system/systemd-localed.service.d/override.conf <>/run/systemd/system/systemd-vconsole-setup.service.d/override.conf <&1) + assert_in "Warning:" "$output" + assert_in "Command Line: LANG=C.UTF-8" "$output" + assert_in "LC_CTYPE=ja_JP.UTF-8" "$output" + assert_in "System Locale:" "$output" + + # change locale + for i in $(localectl list-locales); do + assert_rc 0 localectl set-locale "LANG=C" "LC_CTYPE=$i" + if [[ -f /etc/default/locale ]]; then + assert_eq "$(cat /etc/default/locale)" "LANG=C +LC_CTYPE=$i" + else + assert_eq "$(cat /etc/locale.conf)" "LANG=C +LC_CTYPE=$i" + fi + output=$(localectl) + assert_in "System Locale: LANG=C" "$output" + assert_in "LC_CTYPE=$i" "$output" + output=$(systemctl show-environment) + assert_in "LANG=C" "$output" + assert_in "LC_CTYPE=$i" "$output" + + assert_rc 0 localectl set-locale "$i" + if [[ -f /etc/default/locale ]]; then + assert_eq "$(cat /etc/default/locale)" "LANG=$i" + else + assert_eq "$(cat /etc/locale.conf)" "LANG=$i" + fi + output=$(localectl) + assert_in "System Locale: LANG=$i" "$output" + assert_not_in "LC_CTYPE=" "$output" + output=$(systemctl show-environment) + assert_in "LANG=$i" "$output" + assert_not_in "LC_CTYPE=" "$output" + done + + # test if localed auto-runs locale-gen + if command -v locale-gen >/dev/null 2>&1 && + ! localectl list-locales | grep -F "de_DE.UTF-8"; then + + # clear previous locale + systemctl stop systemd-localed.service + rm -f /etc/locale.conf /etc/default/locale + + # change locale + assert_rc 0 localectl set-locale de_DE.UTF-8 + if [[ -f /etc/default/locale ]]; then + assert_eq "$(cat /etc/default/locale)" "LANG=de_DE.UTF-8" + else + assert_eq "$(cat /etc/locale.conf)" "LANG=de_DE.UTF-8" + fi + assert_in "System Locale: LANG=de_DE.UTF-8" "$(localectl)" + assert_in "LANG=de_DE.UTF-8" "$(systemctl show-environment)" + + # ensure tested locale exists and works now + assert_in "de_DE.UTF-8" "$(localectl list-locales)" + fi +} + +backup_keymap() { + if [[ -f /etc/vconsole.conf ]]; then + cp /etc/vconsole.conf /tmp/vconsole.conf.bak + fi + + if [[ -f /etc/X11/xorg.conf.d/00-keyboard.conf ]]; then + cp /etc/X11/xorg.conf.d/00-keyboard.conf /tmp/00-keyboard.conf.bak + fi + + # Debian/Ubuntu specific file + if [[ -f /etc/default/keyboard ]]; then + cp /etc/default/keyboard /tmp/default-keyboard.bak + fi + + mkdir -p /etc/default +} + +restore_keymap() { + if [[ -f /tmp/vconsole.conf.bak ]]; then + mv /tmp/vconsole.conf.bak /etc/vconsole.conf + else + rm -f /etc/vconsole.conf + fi + + if [[ -f /tmp/00-keyboard.conf.bak ]]; then + mv /tmp/00-keyboard.conf.bak /etc/X11/xorg.conf.d/00-keyboard.conf + else + rm -f /etc/X11/xorg.conf.d/00-keyboard.conf + fi + + if [[ -f /tmp/default-keyboard.bak ]]; then + mv /tmp/default-keyboard.bak /etc/default/keyboard + else + rm -f /etc/default/keyboard + rmdir --ignore-fail-on-non-empty /etc/default + fi +} + +wait_vconsole_setup() { + local i ss + for i in {1..20}; do + (( i > 1 )) && sleep 0.5 + ss="$(systemctl --property SubState --value show systemd-vconsole-setup.service)" + if [[ "$ss" == "exited" || "$ss" == "dead" || "$ss" == "condition" ]]; then + return 0 + elif [[ "$ss" == "failed" ]]; then + echo "WARNING: systemd-vconsole-setup.service failed, ignoring." >&2 + systemctl reset-failed systemd-vconsole-setup.service + return 0 + fi + done + + systemctl status systemd-vconsole-setup.service + return 1 +} + +testcase_vc_keymap() { + local i output vc + + if [[ -z "$(localectl list-keymaps)" ]]; then + echo "No vconsole keymap installed, skipping test." + return + fi + + backup_keymap + trap restore_keymap RETURN + + # should activate daemon and work + assert_in "VC Keymap:" "$(localectl)" + + for i in $(localectl list-keymaps); do + # clear previous conversion from VC -> X11 keymap + systemctl stop systemd-localed.service + wait_vconsole_setup + rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard + + # set VC keymap + assert_rc 0 localectl set-keymap "$i" + output=$(localectl) + + # check VC keymap + vc=$(cat /etc/vconsole.conf) + assert_in "KEYMAP=$i" "$vc" + assert_in "VC Keymap: $i" "$output" + + # check VC -> X11 keymap conversion + if [[ "$i" == "us" ]]; then + assert_in "X11 Layout: us" "$output" + assert_in "X11 Model: pc105\+inet" "$output" + assert_not_in "X11 Variant:" "$output" + assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output" + + assert_in "XKBLAYOUT=us" "$vc" + assert_in "XKBMODEL=pc105\+inet" "$vc" + assert_not_in "XKBVARIANT" "$vc" + assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc" + elif [[ "$i" == "us-acentos" ]]; then + assert_in "X11 Layout: us" "$output" + assert_in "X11 Model: pc105" "$output" + assert_in "X11 Variant: intl" "$output" + assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output" + + assert_in "XKBLAYOUT=us" "$vc" + assert_in "XKBMODEL=pc105" "$vc" + assert_in "XKBVARIANT=intl" "$vc" + assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc" + elif [[ "$i" =~ ^us-.* ]]; then + assert_in "X11 Layout: us" "$output" + assert_in "X11 Model: microsoftpro" "$output" + assert_in "X11 Variant:" "$output" + assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output" + + assert_in "XKBLAYOUT=us" "$vc" + assert_in "XKBMODEL=microsoftpro" "$vc" + assert_in "XKBVARIANT=" "$vc" + assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc" + fi + done + + # gets along without config file + systemctl stop systemd-localed.service + wait_vconsole_setup + rm -f /etc/vconsole.conf + assert_in "VC Keymap: .unset." "$(localectl)" +} + +testcase_x11_keymap() { + local output + + if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then + echo "No x11 keymap installed, skipping test." + return + fi + + backup_keymap + trap restore_keymap RETURN + + # should activate daemon and work + assert_in "X11 Layout:" "$(localectl)" + + # set x11 keymap (layout, model, variant, options) + assert_rc 0 localectl set-x11-keymap us pc105+inet intl terminate:ctrl_alt_bksp + + if [[ -f /etc/default/keyboard ]]; then + assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us +XKBMODEL=pc105+inet +XKBVARIANT=intl +XKBOPTIONS=terminate:ctrl_alt_bksp" + else + output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) + assert_in 'Option "XkbLayout" "us"' "$output" + assert_in 'Option "XkbModel" "pc105\+inet"' "$output" + assert_in 'Option "XkbVariant" "intl"' "$output" + assert_in 'Option "XkbOptions" "terminate:ctrl_alt_bksp"' "$output" + + output=$(cat /etc/vconsole.conf) + assert_in 'XKBLAYOUT=us' "$output" + assert_in 'XKBMODEL=pc105\+inet' "$output" + assert_in 'XKBVARIANT=intl' "$output" + assert_in 'XKBOPTIONS=terminate:ctrl_alt_bksp' "$output" + fi + + output=$(localectl) + assert_in "X11 Layout: us" "$output" + assert_in "X11 Model: pc105\+inet" "$output" + assert_in "X11 Variant: intl" "$output" + assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output" + + # Debian/Ubuntu patch is buggy, unspecified settings are not cleared + rm -f /etc/default/keyboard + + # set x11 keymap (layout, model, variant) + assert_rc 0 localectl set-x11-keymap us pc105+inet intl + + if [[ -f /etc/default/keyboard ]]; then + assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us +XKBMODEL=pc105+inet +XKBVARIANT=intl" + else + output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) + assert_in 'Option "XkbLayout" "us"' "$output" + assert_in 'Option "XkbModel" "pc105\+inet"' "$output" + assert_in 'Option "XkbVariant" "intl"' "$output" + assert_not_in 'Option "XkbOptions"' "$output" + + output=$(cat /etc/vconsole.conf) + assert_in 'XKBLAYOUT=us' "$output" + assert_in 'XKBMODEL=pc105\+inet' "$output" + assert_in 'XKBVARIANT=intl' "$output" + assert_not_in 'XKBOPTIONS' "$output" + fi + + output=$(localectl) + assert_in "X11 Layout: us" "$output" + assert_in "X11 Model: pc105\+inet" "$output" + assert_in "X11 Variant: intl" "$output" + assert_not_in "X11 Options:" "$output" + + # Debian/Ubuntu patch is buggy, unspecified settings are not cleared + rm -f /etc/default/keyboard + + # set x11 keymap (layout, model) + assert_rc 0 localectl set-x11-keymap us pc105+inet + + if [[ -f /etc/default/keyboard ]]; then + assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us +XKBMODEL=pc105+inet" + else + output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) + assert_in 'Option "XkbLayout" "us"' "$output" + assert_in 'Option "XkbModel" "pc105\+inet"' "$output" + assert_not_in 'Option "XkbVariant"' "$output" + assert_not_in 'Option "XkbOptions"' "$output" + + output=$(cat /etc/vconsole.conf) + assert_in 'XKBLAYOUT=us' "$output" + assert_in 'XKBMODEL=pc105\+inet' "$output" + assert_not_in 'XKBVARIANT' "$output" + assert_not_in 'XKBOPTIONS' "$output" + fi + + output=$(localectl) + assert_in "X11 Layout: us" "$output" + assert_in "X11 Model: pc105\+inet" "$output" + assert_not_in "X11 Variant:" "$output" + assert_not_in "X11 Options:" "$output" + + # Debian/Ubuntu patch is buggy, unspecified settings are not cleared + rm -f /etc/default/keyboard + + # set x11 keymap (layout) + assert_rc 0 localectl set-x11-keymap us + + if [[ -f /etc/default/keyboard ]]; then + assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us" + else + output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) + assert_in 'Option "XkbLayout" "us"' "$output" + assert_not_in 'Option "XkbModel"' "$output" + assert_not_in 'Option "XkbVariant"' "$output" + assert_not_in 'Option "XkbOptions"' "$output" + + output=$(cat /etc/vconsole.conf) + assert_in 'XKBLAYOUT=us' "$output" + assert_not_in 'XKBMODEL' "$output" + assert_not_in 'XKBVARIANT' "$output" + assert_not_in 'XKBOPTIONS' "$output" + fi + + output=$(localectl) + assert_in "X11 Layout: us" "$output" + assert_not_in "X11 Model:" "$output" + assert_not_in "X11 Variant:" "$output" + assert_not_in "X11 Options:" "$output" + + # gets along without config file + systemctl stop systemd-localed.service + rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard + output=$(localectl) + assert_in "X11 Layout: .unset." "$output" + assert_not_in "X11 Model:" "$output" + assert_not_in "X11 Variant:" "$output" + assert_not_in "X11 Options:" "$output" +} + +testcase_convert() { + if [[ -z "$(localectl list-keymaps)" ]]; then + echo "No vconsole keymap installed, skipping test." + return + fi + + if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then + echo "No x11 keymap installed, skipping test." + return + fi + + backup_keymap + trap restore_keymap RETURN + + # clear previous settings + systemctl stop systemd-localed.service + wait_vconsole_setup + rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard + + # set VC keymap without conversion + assert_rc 0 localectl --no-convert set-keymap us + output=$(localectl) + + # check VC keymap + vc=$(cat /etc/vconsole.conf) + assert_in "KEYMAP=us" "$vc" + assert_in "VC Keymap: us" "$output" + + # check VC -> X11 keymap conversion (nothing set) + assert_in "X11 Layout: .unset." "$output" + assert_not_in "X11 Model:" "$output" + assert_not_in "X11 Variant:" "$output" + assert_not_in "X11 Options:" "$output" + + assert_not_in "XKBLAYOUT=" "$vc" + assert_not_in "XKBMODEL=" "$vc" + assert_not_in "XKBVARIANT=" "$vc" + assert_not_in "XKBOPTIONS=" "$vc" + + # set VC keymap with conversion + assert_rc 0 localectl set-keymap us + output=$(localectl) + + # check VC keymap + vc=$(cat /etc/vconsole.conf) + assert_in "KEYMAP=us" "$vc" + assert_in "VC Keymap: us" "$output" + + # check VC -> X11 keymap conversion + assert_in "X11 Layout: us" "$output" + assert_in "X11 Model: pc105\+inet" "$output" + assert_not_in "X11 Variant:" "$output" + assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output" + + assert_in "XKBLAYOUT=us" "$vc" + assert_in "XKBMODEL=pc105\+inet" "$vc" + assert_not_in "XKBVARIANT" "$vc" + assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc" + + # clear previous settings + systemctl stop systemd-localed.service + wait_vconsole_setup + rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard + + # set x11 keymap (layout) without conversion + assert_rc 0 localectl --no-convert set-x11-keymap us + + assert_not_in "KEYMAP=" "$(cat /etc/vconsole.conf)" + assert_in "VC Keymap: .unset." "$(localectl)" + + if [[ -f /etc/default/keyboard ]]; then + assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us" + else + output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) + assert_in 'Option "XkbLayout" "us"' "$output" + assert_not_in 'Option "XkbModel"' "$output" + assert_not_in 'Option "XkbVariant"' "$output" + assert_not_in 'Option "XkbOptions"' "$output" + + output=$(cat /etc/vconsole.conf) + assert_in 'XKBLAYOUT=us' "$output" + assert_not_in 'XKBMODEL=' "$output" + assert_not_in 'XKBVARIANT=' "$output" + assert_not_in 'XKBOPTIONS=' "$output" + fi + + output=$(localectl) + assert_in "X11 Layout: us" "$output" + assert_not_in "X11 Model:" "$output" + assert_not_in "X11 Variant:" "$output" + assert_not_in "X11 Options:" "$output" + + # set x11 keymap (layout, model) with conversion + assert_rc 0 localectl set-x11-keymap us + + assert_in "KEYMAP=us" "$(cat /etc/vconsole.conf)" + assert_in "VC Keymap: us" "$(localectl)" + + if [[ -f /etc/default/keyboard ]]; then + assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us" + else + output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) + assert_in 'Option "XkbLayout" "us"' "$output" + assert_not_in 'Option "XkbModel"' "$output" + assert_not_in 'Option "XkbVariant"' "$output" + assert_not_in 'Option "XkbOptions"' "$output" + + output=$(cat /etc/vconsole.conf) + assert_in 'XKBLAYOUT=us' "$output" + assert_not_in 'XKBMODEL=' "$output" + assert_not_in 'XKBVARIANT=' "$output" + assert_not_in 'XKBOPTIONS=' "$output" + fi + + output=$(localectl) + assert_in "X11 Layout: us" "$output" + assert_not_in "X11 Model:" "$output" + assert_not_in "X11 Variant:" "$output" + assert_not_in "X11 Options:" "$output" +} + +testcase_validate() { + if [[ -z "$(localectl list-keymaps)" ]]; then + echo "No vconsole keymap installed, skipping test." + return + fi + + if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then + echo "No x11 keymap installed, skipping test." + return + fi + + backup_keymap + trap restore_keymap RETURN + + # clear previous settings + systemctl stop systemd-localed.service + wait_vconsole_setup + rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard + + # create invalid configs + cat >/etc/vconsole.conf </etc/vconsole.conf </etc/vconsole.conf </dev/null; then + echo "No locale-gen support, skipping test." + return 0 + fi + + [[ -e /etc/locale.gen ]] && cp -f /etc/locale.gen /tmp/locale.gen.bak + trap locale_gen_cleanup RETURN + # Overmount the existing locale-gen database with an empty directory + # to force it to regenerate locales + mount -t tmpfs tmpfs /usr/lib/locale + + { + echo -e "en_US.UTF-8 UTF-8" + echo -e " en_US.UTF-8 UTF-8" + echo -e "\ten_US.UTF-8 UTF-8" + echo -e " \t en_US.UTF-8 UTF-8 \t" + } >/etc/locale.gen + + localectl set-locale de_DE.UTF-8 + localectl set-locale en_US.UTF-8 +} + +# Make sure the content of kbd-model-map is the one that the tests expect +# regardless of the version installed on the distro where the testsuite is +# running on. +export SYSTEMD_KBD_MODEL_MAP=/usr/lib/systemd/tests/testdata/test-keymap-util/kbd-model-map + +enable_debug +run_testcases + +touch /testok diff --git a/test/units/TEST-74-AUX-UTILS.battery-check.sh b/test/units/TEST-74-AUX-UTILS.battery-check.sh new file mode 100755 index 0000000..52a92b8 --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.battery-check.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +/usr/lib/systemd/systemd-battery-check --help +/usr/lib/systemd/systemd-battery-check --version + +/usr/lib/systemd/systemd-battery-check || : diff --git a/test/units/TEST-74-AUX-UTILS.bootctl.sh b/test/units/TEST-74-AUX-UTILS.bootctl.sh new file mode 100755 index 0000000..78c0e6e --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.bootctl.sh @@ -0,0 +1,279 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +if systemd-detect-virt --quiet --container; then + echo "running on container, skipping." + exit 0 +fi + +if ! command -v bootctl >/dev/null; then + echo "bootctl not found, skipping." + exit 0 +fi + +if [[ ! -d /usr/lib/systemd/boot/efi ]]; then + echo "sd-boot is not installed, skipping." + exit 0 +fi + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + +basic_tests() { + bootctl "$@" --help + bootctl "$@" --version + + bootctl "$@" install --make-entry-directory=yes + bootctl "$@" remove --make-entry-directory=yes + + bootctl "$@" install --all-architectures + bootctl "$@" remove --all-architectures + + bootctl "$@" install --make-entry-directory=yes --all-architectures + bootctl "$@" remove --make-entry-directory=yes --all-architectures + + bootctl "$@" install + (! bootctl "$@" update) + bootctl "$@" update --graceful + + bootctl "$@" is-installed + bootctl "$@" is-installed --graceful + bootctl "$@" random-seed + + bootctl "$@" + bootctl "$@" status + bootctl "$@" status --quiet + bootctl "$@" list + bootctl "$@" list --quiet + bootctl "$@" list --json=short + bootctl "$@" list --json=pretty + + bootctl "$@" remove + (! bootctl "$@" is-installed) + (! bootctl "$@" is-installed --graceful) +} + +testcase_bootctl_basic() { + assert_in "$(bootctl --print-esp-path)" "^(/boot/|/efi)$" + assert_in "$(bootctl --print-boot-path)" "^(/boot/|/efi)$" + bootctl --print-root-device + + basic_tests +} + +cleanup_image() ( + set +e + + if [[ -z "${IMAGE_DIR:-}" ]]; then + return 0 + fi + + umount "${IMAGE_DIR}/root" + + if [[ -n "${LOOPDEV:-}" ]]; then + losetup -d "${LOOPDEV}" + unset LOOPDEV + fi + + udevadm settle + + rm -rf "${IMAGE_DIR}" + unset IMAGE_DIR + + return 0 +) + +testcase_bootctl_image() { + IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)" + trap cleanup_image RETURN + + truncate -s 256m "${IMAGE_DIR}/image" + + cat >"${IMAGE_DIR}/partscript" </dev/null; then + echo "mdadm not found, skipping." + return 0 + fi + + if ! command -v mkfs.btrfs >/dev/null; then + echo "mkfs.btrfs not found, skipping." + return 0 + fi + + IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)" + trap cleanup_raid RETURN + + truncate -s 256m "${IMAGE_DIR}/image1" + truncate -s 256m "${IMAGE_DIR}/image2" + + cat >"${IMAGE_DIR}/partscript" <"${MAKE_DUMP_SCRIPT:?}" <<\EOF +#!/bin/bash -ex + +bin="${1:?}" +sig="${2:?}" + +ulimit -c unlimited +"$bin" infinity & +pid=$! +# Sync with the "fake" binary, so we kill it once it's fully forked off, +# otherwise we might kill it during fork and kernel would then report +# "wrong" binary name (i.e. $MAKE_DUMP_SCRIPT instead of $CORE_TEST_BIN). +# In this case, wait until the "fake" binary (sleep in this case) enters +# the "interruptible sleep" state, at which point it should be ready +# to be sacrificed. +for _ in {0..9}; do + read -ra self_stat <"/proc/$pid/stat" + [[ "${self_stat[2]}" == S ]] && break + sleep .5 +done +kill -s "$sig" "$pid" +# This should always fail +! wait "$pid" +EOF +chmod +x "$MAKE_DUMP_SCRIPT" + +# Privileged stuff +[[ "$(id -u)" -eq 0 ]] +# Trigger a couple of coredumps +"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP" +"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT" +# In the tests we store the coredumps in journals, so let's generate a couple +# with Storage=external as well +mkdir -p /run/systemd/coredump.conf.d/ +printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf +"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP" +"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT" +rm -fv /run/systemd/coredump.conf.d/99-external.conf +# Wait a bit for the coredumps to get processed +timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_BIN | wc -l) -lt 4 ]]; do sleep 1; done" + +if cgroupfs_supports_user_xattrs; then + # Make sure we can forward crashes back to containers + CONTAINER="TEST-74-AUX-UTILS-container" + + mkdir -p "/var/lib/machines/$CONTAINER" + mkdir -p "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d" + # Bind-mounting /etc into the container kinda defeats the purpose of --volatile=, + # but we need the ASan-related overrides scattered across /etc + cat > "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d/override.conf" </tmp/core.redirected +test -s /tmp/core.redirected +coredumpctl dump -o /tmp/core.output "${CORE_TEST_BIN##*/}" +test -s /tmp/core.output +rm -f /tmp/core.{output,redirected} + +# Unprivileged stuff +# Related issue: https://github.com/systemd/systemd/issues/26912 +UNPRIV_CMD=(systemd-run --user --wait --pipe -M "testuser@.host" --) +# Trigger a couple of coredumps as an unprivileged user +"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP" +"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT" +# In the tests we store the coredumps in journals, so let's generate a couple +# with Storage=external as well +mkdir -p /run/systemd/coredump.conf.d/ +printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf +"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP" +"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT" +rm -fv /run/systemd/coredump.conf.d/99-external.conf +# Wait a bit for the coredumps to get processed +timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_UNPRIV_BIN | wc -l) -lt 4 ]]; do sleep 1; done" + +# root should see coredumps from both binaries +coredumpctl info "$CORE_TEST_UNPRIV_BIN" +coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}" +# The test user should see only their own coredumps +"${UNPRIV_CMD[@]}" coredumpctl +"${UNPRIV_CMD[@]}" coredumpctl info "$CORE_TEST_UNPRIV_BIN" +"${UNPRIV_CMD[@]}" coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}" +(! "${UNPRIV_CMD[@]}" coredumpctl info --all "$CORE_TEST_BIN") +(! "${UNPRIV_CMD[@]}" coredumpctl info --all "${CORE_TEST_BIN##*/}") +# We should have a couple of externally stored coredumps +"${UNPRIV_CMD[@]}" coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out +grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out +rm -f /tmp/coredumpctl.out + +"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true "$CORE_TEST_UNPRIV_BIN" +"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_UNPRIV_BIN##*/}" + +"${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_UNPRIV_BIN" >/tmp/core.redirected +test -s /tmp/core.redirected +"${UNPRIV_CMD[@]}" coredumpctl dump -o /tmp/core.output "${CORE_TEST_UNPRIV_BIN##*/}" +test -s /tmp/core.output +rm -f /tmp/core.{output,redirected} +(! "${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_BIN" >/dev/null) + +# --backtrace mode +# Pass one of the existing journal coredump records to systemd-coredump and +# use our PID as the source to make matching the coredump later easier +# systemd-coredump args: PID UID GID SIGNUM TIMESTAMP CORE_SOFT_RLIMIT HOSTNAME +journalctl -b -n 1 --output=export --output-fields=MESSAGE,COREDUMP COREDUMP_EXE="/usr/bin/test-dump" | + /usr/lib/systemd/systemd-coredump --backtrace $$ 0 0 6 1679509994 12345 mymachine +# Wait a bit for the coredump to get processed +timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $$ | wc -l) -eq 0 ]]; do sleep 1; done" +coredumpctl info "$$" +coredumpctl info COREDUMP_HOSTNAME="mymachine" + +# This used to cause a stack overflow +systemd-run -t --property CoredumpFilter=all ls /tmp +systemd-run -t --property CoredumpFilter=default ls /tmp + +(! coredumpctl --hello-world) +(! coredumpctl -n 0) +(! coredumpctl -n -1) +(! coredumpctl --file=/dev/null) +(! coredumpctl --since=0) +(! coredumpctl --until='') +(! coredumpctl --since=today --until=yesterday) +(! coredumpctl --directory=/ --root=/) +(! coredumpctl --json=foo) +(! coredumpctl -F foo -F bar) +(! coredumpctl list 0) +(! coredumpctl list -- -1) +(! coredumpctl list '') +(! coredumpctl info /../.~=) +(! coredumpctl info '') +(! coredumpctl dump --output=/dev/full "$CORE_TEST_BIN") +(! coredumpctl dump --output=/dev/null --output=/dev/null "$CORE_TEST_BIN") +(! coredumpctl debug --debugger=/bin/false) +(! coredumpctl debug --debugger=/bin/true --debugger-arguments='"') diff --git a/test/units/TEST-74-AUX-UTILS.delta.sh b/test/units/TEST-74-AUX-UTILS.delta.sh new file mode 100755 index 0000000..dabe234 --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.delta.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +at_exit() { + rm -rfv /{run,etc}/systemd/system/delta-test* +} + +trap at_exit EXIT + +# Create a couple of supporting units with overrides +# +# Extended unit +cat >"/run/systemd/system/delta-test-unit-extended.service" <"/run/systemd/system/delta-test-unit-extended.service.d/override.conf" <>/etc/systemd/system/delta-test-unit-overridden.service +# Overridden but equivalent unit +ln -srfv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-equivalent.service +ln -sfv /run/systemd/system/delta-test-unit-extended.service /etc/systemd/system/delta-test-unit-equivalent.service +# Redirected unit +ln -srfv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-redirected.service +ln -sfv /run/systemd/system/delta-test-unit-overidden.service /etc/systemd/system/delta-test-unit-extended.service + +systemctl daemon-reload + +systemd-delta +systemd-delta /run +systemd-delta systemd/system +systemd-delta /run systemd/system /run +systemd-delta /run foo/bar hello/world systemd/system /run +systemd-delta foo/bar +systemd-delta --diff=true +systemd-delta --diff=false + +for type in masked equivalent redirected overridden extended unchanged; do + systemd-delta --type="$type" + systemd-delta --type="$type" /run +done +systemd-delta --type=equivalent,redirected + +(! systemd-delta --diff=foo) +(! systemd-delta --type=foo) +(! systemd-delta --type=equivalent,redirected,foo) diff --git a/test/units/TEST-74-AUX-UTILS.escape.sh b/test/units/TEST-74-AUX-UTILS.escape.sh new file mode 100755 index 0000000..e398d40 --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.escape.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +# Simple wrapper to check both escaping and unescaping of given strings +# Arguments: +# $1 - expected unescaped string +# $2 - expected escaped string +# $3 - optional arguments for systemd-escape +check_escape() { + unescaped="${1?}" + escaped="${2?}" + shift 2 + + assert_eq "$(systemd-escape "$@" -- "$unescaped")" "$escaped" + assert_eq "$(systemd-escape "$@" --unescape -- "$escaped")" "$unescaped" +} + +systemd-escape --help +systemd-escape --version + +check_escape '' '' +check_escape 'hello' 'hello' +check_escape 'hello-world' 'hello\x2dworld' +check_escape '-+ěščřž---🤔' '\x2d\x2b\xc4\x9b\xc5\xa1\xc4\x8d\xc5\x99\xc5\xbe\x2d\x2d\x2d\xf0\x9f\xa4\x94' +check_escape '/this/is/a/path/a b c' '-this-is-a-path-a\x20b\x20c' + +# Multiple strings to escape/unescape +assert_eq "$(systemd-escape 'hello-world' '/dev/loop1' 'template@🐍')" \ + 'hello\x2dworld -dev-loop1 template\x40\xf0\x9f\x90\x8d' +assert_eq "$(systemd-escape --unescape -- 'hello\x2dworld' '-dev-loop1' 'template\x40\xf0\x9f\x90\x8d')" \ + 'hello-world /dev/loop1 template@🐍' + +# --suffix= is not compatible with --unescape +assert_eq "$(systemd-escape --suffix=mount -- '-+ěščřž---🤔')" \ + '\x2d\x2b\xc4\x9b\xc5\xa1\xc4\x8d\xc5\x99\xc5\xbe\x2d\x2d\x2d\xf0\x9f\xa4\x94.mount' +assert_eq "$(systemd-escape --suffix=timer 'this has spaces')" \ + 'this\x20has\x20spaces.timer' +assert_eq "$(systemd-escape --suffix=service 'trailing-spaces ')" \ + 'trailing\x2dspaces\x20\x20.service' +assert_eq "$(systemd-escape --suffix=automount ' leading-spaces')" \ + '\x20\x20\x20leading\x2dspaces.automount' + +# --template= +check_escape 'hello' 'hello@hello.service' --template=hello@.service +check_escape ' what - is _ love? 🤔 ¯\_(ツ)_/¯' \ + 'hello@\x20\x20what\x20\x2d\x20is\x20_\x20love\x3f\x20\xf0\x9f\xa4\x94\x20\xc2\xaf\x5c_\x28\xe3\x83\x84\x29_-\xc2\xaf.service' \ + --template=hello@.service +check_escape '/this/is/where/my/stuff/is/ with spaces though ' \ + 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service' \ + --template=mount-my-stuff@.service +check_escape '/this/is/where/my/stuff/is/ with spaces though ' \ + 'mount-my-stuff@this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service' \ + --template=mount-my-stuff@.service --path + +# --instance (must be used with --unescape) +assert_eq "$(systemd-escape --unescape --instance 'hello@\x20\x20what\x20\x2d\x20is\x20_\x20love\x3f\x20\xf0\x9f\xa4\x94\x20\xc2\xaf\x5c_\x28\xe3\x83\x84\x29_-\xc2\xaf.service')" \ + ' what - is _ love? 🤔 ¯\_(ツ)_/¯' +assert_eq "$(systemd-escape --unescape --instance 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')" \ + '/this/is/where/my/stuff/is/ with spaces though ' +assert_eq "$(systemd-escape --unescape --instance --path 'mount-my-stuff@this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')" \ + '/this/is/where/my/stuff/is/ with spaces though ' + +# --path, reversible cases +check_escape / '-' --path +check_escape '/hello/world' 'hello-world' --path +check_escape '/mnt/smb/おにぎり' \ + 'mnt-smb-\xe3\x81\x8a\xe3\x81\xab\xe3\x81\x8e\xe3\x82\x8a' \ + --path + +# --path, non-reversible cases +assert_eq "$(systemd-escape --path ///////////////)" '-' +assert_eq "$(systemd-escape --path /..)" '-' +assert_eq "$(systemd-escape --path /../.././../.././)" '-' +assert_eq "$(systemd-escape --path /../.././../.././foo)" 'foo' + +# --mangle +assert_eq "$(systemd-escape --mangle 'hello-world')" 'hello-world.service' +assert_eq "$(systemd-escape --mangle '/mount/this')" 'mount-this.mount' +assert_eq "$(systemd-escape --mangle 'my-service@ 🐱 ')" 'my-service@\x20\xf0\x9f\x90\xb1\x20.service' +assert_eq "$(systemd-escape --mangle '/dev/disk/by-emoji/🍎')" 'dev-disk-by\x2demoji-\xf0\x9f\x8d\x8e.device' +assert_eq "$(systemd-escape --mangle 'daily-existential-crisis .timer')" 'daily-existential-crisis\x20.timer' +assert_eq "$(systemd-escape --mangle 'trailing-whitespace.mount ')" 'trailing-whitespace.mount\x20.service' + +(! systemd-escape) +(! systemd-escape --suffix='' hello) +(! systemd-escape --suffix=invalid hello) +(! systemd-escape --suffix=mount --template=hello@.service hello) +(! systemd-escape --suffix=mount --mangle) +(! systemd-escape --template='') +(! systemd-escape --template=@) +(! systemd-escape --template='hello@.service' '') +(! systemd-escape --unescape --template='hello@.service' '@hello.service') +(! systemd-escape --unescape --template='hello@.service' 'hello@.service') +(! systemd-escape --mangle --template=hello@.service hello) +(! systemd-escape --instance 'hello@hello.service') +(! systemd-escape --instance --template=hello@.service 'hello@hello.service') +(! systemd-escape --unescape --instance --path 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service') +(! systemd-escape --path '/../hello/..') +(! systemd-escape --path '.') +(! systemd-escape --path '..') +(! systemd-escape --path "$(set +x; printf '%0.sa' {0..256})") +(! systemd-escape --unescape --path '') +(! systemd-escape --mangle '') diff --git a/test/units/TEST-74-AUX-UTILS.firstboot.sh b/test/units/TEST-74-AUX-UTILS.firstboot.sh new file mode 100755 index 0000000..7bab009 --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.firstboot.sh @@ -0,0 +1,219 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +if ! command -v systemd-firstboot >/dev/null; then + echo "systemd-firstboot not found, skipping the test" + exit 0 +fi + +at_exit() { + if [[ -n "${ROOT:-}" ]]; then + ls -lR "$ROOT" + rm -fr "$ROOT" + fi + + restore_locale +} + +trap at_exit EXIT + +# Generated via `mkpasswd -m sha-512 -S foobarsalt password1` +# shellcheck disable=SC2016 +ROOT_HASHED_PASSWORD1='$6$foobarsalt$YbwdaATX6IsFxvWbY3QcZj2gB31R/LFRFrjlFrJtTTqFtSfn4dfOAg/km2k4Sl.a2g7LOYDo31wMTaEsCo9j41' +# Generated via `mkpasswd -m sha-512 -S foobarsalt password2` +# shellcheck disable=SC2016 +ROOT_HASHED_PASSWORD2='$6$foobarsalt$q.P2932zYMLbKnjFwIxPI8y3iuxeuJ2BgE372LcZMMnj3Gcg/9mJg2LPKUl.ha0TG/.fRNNnRQcLfzM0SNot3.' + +if [[ -f /etc/locale.conf ]]; then + cp /etc/locale.conf /tmp/locale.conf.bak +fi + +# Debian/Ubuntu specific file +if [[ -f /etc/default/locale ]]; then + cp /etc/default/locale /tmp/default-locale.bak +fi + +if [[ -f /etc/locale.gen ]]; then + cp /etc/locale.gen /tmp/locale.gen.bak +fi + +# Make sure at least two locales exist (C.UTF-8 and en_US.UTF-8) as systemd-firstboot --prompt-locale will +# skip writing the locale if it detects only one is installed. +generate_locale en_US.UTF-8 + +# Debian and Ubuntu use /etc/default/locale instead of /etc/locale.conf. Make +# sure we use the appropriate path for locale configuration. +LOCALE_PATH="/etc/locale.conf" +[ -e "$LOCALE_PATH" ] || LOCALE_PATH="/etc/default/locale" +[ -e "$LOCALE_PATH" ] || systemd-firstboot --locale=C.UTF-8 + +# Create a minimal root so we don't modify the testbed +ROOT=test-root +mkdir -p "$ROOT/bin" +# Dummy shell for --root-shell= +touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell" + +systemd-firstboot --root="$ROOT" --locale=foo +grep -q "LANG=foo" "$ROOT$LOCALE_PATH" +rm -fv "$ROOT$LOCALE_PATH" +systemd-firstboot --root="$ROOT" --locale-messages=foo +grep -q "LC_MESSAGES=foo" "$ROOT$LOCALE_PATH" +rm -fv "$ROOT$LOCALE_PATH" +systemd-firstboot --root="$ROOT" --locale=foo --locale-messages=bar +grep -q "LANG=foo" "$ROOT$LOCALE_PATH" +grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH" + +systemd-firstboot --root="$ROOT" --keymap=foo +grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf" + +systemd-firstboot --root="$ROOT" --timezone=Europe/Berlin +readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin" + +systemd-firstboot --root="$ROOT" --hostname "foobar" +grep -q "foobar" "$ROOT/etc/hostname" + +systemd-firstboot --root="$ROOT" --machine-id=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id" + +rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" +systemd-firstboot --root="$ROOT" --root-password=foo +grep -q "^root:x:0:0:" "$ROOT/etc/passwd" +grep -q "^root:" "$ROOT/etc/shadow" +rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" +echo "foo" >root.passwd +systemd-firstboot --root="$ROOT" --root-password-file=root.passwd +grep -q "^root:x:0:0:" "$ROOT/etc/passwd" +grep -q "^root:" "$ROOT/etc/shadow" +rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" root.passwd +# Set the shell together with the password, as firstboot won't touch +# /etc/passwd if it already exists +systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1" --root-shell=/bin/fooshell +grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" +grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow" + +systemd-firstboot --root="$ROOT" --kernel-command-line="foo.bar=42" +grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline" + +# Configs should not get overwritten if they exist unless --force is used +systemd-firstboot --root="$ROOT" \ + --locale=locale-overwrite \ + --locale-messages=messages-overwrite \ + --keymap=keymap-overwrite \ + --timezone=CET \ + --hostname=hostname-overwrite \ + --machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \ + --root-password-hashed="$ROOT_HASHED_PASSWORD2" \ + --root-shell=/bin/barshell \ + --kernel-command-line="hello.world=0" +grep -q "LANG=foo" "$ROOT$LOCALE_PATH" +grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH" +grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf" +readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$" +grep -q "foobar" "$ROOT/etc/hostname" +grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id" +grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" +grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow" +grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline" + +# The same thing, but now with --force +systemd-firstboot --root="$ROOT" --force \ + --locale=locale-overwrite \ + --locale-messages=messages-overwrite \ + --keymap=keymap-overwrite \ + --timezone=CET \ + --hostname=hostname-overwrite \ + --machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \ + --root-password-hashed="$ROOT_HASHED_PASSWORD2" \ + --root-shell=/bin/barshell \ + --kernel-command-line="hello.world=0" +grep -q "LANG=locale-overwrite" "$ROOT$LOCALE_PATH" +grep -q "LC_MESSAGES=messages-overwrite" "$ROOT$LOCALE_PATH" +grep -q "KEYMAP=keymap-overwrite" "$ROOT/etc/vconsole.conf" +readlink "$ROOT/etc/localtime" | grep -q "/CET$" +grep -q "hostname-overwrite" "$ROOT/etc/hostname" +grep -q "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" "$ROOT/etc/machine-id" +grep -q "^root:x:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd" +grep -q "^root:$ROOT_HASHED_PASSWORD2:" "$ROOT/etc/shadow" +grep -q "hello.world=0" "$ROOT/etc/kernel/cmdline" + +# Test that --reset removes all files configured by firstboot. +systemd-firstboot --root="$ROOT" --reset +[[ ! -e "$ROOT/etc/locale.conf" ]] +[[ ! -e "$ROOT/etc/vconsole.conf" ]] +[[ ! -e "$ROOT/etc/localtime" ]] +[[ ! -e "$ROOT/etc/hostname" ]] +[[ ! -e "$ROOT/etc/machine-id" ]] +[[ ! -e "$ROOT/etc/kernel/cmdline" ]] + +# --copy-* options +rm -fr "$ROOT" +mkdir "$ROOT" +# Copy everything at once (--copy) +systemd-firstboot --root="$ROOT" --copy +diff $LOCALE_PATH "$ROOT$LOCALE_PATH" +diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd") +diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow") +[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf" +[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime") +rm -fr "$ROOT" +mkdir "$ROOT" +# Copy everything at once, but now by using separate switches +systemd-firstboot --root="$ROOT" --copy-locale --copy-keymap --copy-timezone --copy-root-password --copy-root-shell +diff $LOCALE_PATH "$ROOT$LOCALE_PATH" +diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd") +diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow") +[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf" +[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime") + +# --prompt-* options +rm -fr "$ROOT" +mkdir -p "$ROOT/bin" +touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell" +# Temporarily disable pipefail to avoid `echo: write error: Broken pipe +set +o pipefail +# We can do only limited testing here, since it's all an interactive stuff, +# so --prompt and --prompt-root-password are skipped on purpose +echo -ne "\nfoo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale +grep -q "LANG=foo" "$ROOT$LOCALE_PATH" +grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH" +# systemd-firstboot in prompt-keymap mode requires keymaps to be installed so +# it can present them as a list to the user. As Debian does not ship/provide +# compatible keymaps (from the kbd package), skip this test if the keymaps are +# missing. +if [ -d "/usr/share/keymaps/" ] || [ -d "/usr/share/kbd/keymaps/" ] || [ -d "/usr/lib/kbd/keymaps/" ] ; then + echo -ne "\nfoo\n" | systemd-firstboot --root="$ROOT" --prompt-keymap + grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf" +fi +echo -ne "\nEurope/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezone +readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$" +echo -ne "\nfoobar\n" | systemd-firstboot --root="$ROOT" --prompt-hostname +grep -q "foobar" "$ROOT/etc/hostname" +echo -ne "\n/bin/fooshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell +grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" +# Existing files should not get overwritten +echo -ne "\n/bin/barshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell +grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" +# Now without the welcome screen but with force +echo -ne "/bin/barshell\n" | systemd-firstboot --root="$ROOT" --force --prompt-root-shell --welcome=no +grep -q "^root:.*:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd" +# Re-enable pipefail +set -o pipefail + +# Assorted tests +rm -fr "$ROOT" +mkdir "$ROOT" + +systemd-firstboot --root="$ROOT" --setup-machine-id +grep -E "[a-z0-9]{32}" "$ROOT/etc/machine-id" + +systemd-firstboot --root="$ROOT" --delete-root-password +diff <(echo) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow") + +(! systemd-firstboot --root="$ROOT" --root-shell=/bin/nonexistentshell) +(! systemd-firstboot --root="$ROOT" --machine-id=invalidmachineid) +(! systemd-firstboot --root="$ROOT" --timezone=Foo/Bar) diff --git a/test/units/TEST-74-AUX-UTILS.id128.sh b/test/units/TEST-74-AUX-UTILS.id128.sh new file mode 100755 index 0000000..f91cd5f --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.id128.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +systemd-id128 --help +systemd-id128 help +systemd-id128 show +systemd-id128 show --pretty | tail +systemd-id128 show --value | tail +systemd-id128 show 4f68bce3e8cd4db196e7fbcaf984b709 # root-x86-64 +systemd-id128 show --pretty 4f68bce3e8cd4db196e7fbcaf984b709 +systemd-id128 show root-x86-64 +systemd-id128 show --pretty root-x86-64 +[[ "$(systemd-id128 show 4f68bce3e8cd4db196e7fbcaf984b709)" = "$(systemd-id128 show root-x86-64)" ]] +[[ "$(systemd-id128 show 4f68bce3-e8cd-4db1-96e7-fbcaf984b709)" = "$(systemd-id128 show root-x86-64)" ]] + +systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709 +systemd-id128 show --pretty root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709 +[[ "$(systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709 -P)" = "8ee5535e7cb14c249e1d28b8dfbb939c" ]] + +systemd-id128 show -j +systemd-id128 show --no-pager +systemd-id128 show --json=short +systemd-id128 show --no-legend +systemd-id128 show --no-pager --no-legend +systemd-id128 show root -P -u + +[[ "$(systemd-id128 new | wc -c)" -eq 33 ]] +systemd-id128 new -p +systemd-id128 new -u +systemd-id128 new -a 4f68bce3e8cd4db196e7fbcaf984b709 + +systemd-id128 machine-id +systemd-id128 machine-id --pretty +systemd-id128 machine-id --uuid +systemd-id128 machine-id --app-specific=4f68bce3e8cd4db196e7fbcaf984b709 +assert_eq "$(systemd-id128 machine-id)" "$(>"$root/etc/machine-id" + machine_id="$(systemd-machine-id-setup --print --root "$root")" + diff <(echo "$machine_id") "$root/etc/machine-id" +} + +testcase_transient() { + local root transient_id committed_id + + root="$(mktemp -d)" + trap "root_cleanup $root" RETURN + root_mock "$root" + + systemd-machine-id-setup --print --root "$root" + echo abc >>"$root/etc/machine-id" + mount -o remount,ro "$root" + mount -t tmpfs tmpfs "$root/run" + transient_id="$(systemd-machine-id-setup --print --root "$root")" + mount -o remount,rw "$root" + committed_id="$(systemd-machine-id-setup --print --commit --root "$root")" + [[ "$transient_id" == "$committed_id" ]] + diff "$root/etc/machine-id" "$root/run/machine-id" +} + +# Check if we correctly processed the invalid machine ID we set up in the respective +# test.sh file +systemctl --state=failed --no-legend --no-pager >/failed +test ! -s /failed + +run_testcases diff --git a/test/units/TEST-74-AUX-UTILS.modules-load.sh b/test/units/TEST-74-AUX-UTILS.modules-load.sh new file mode 100755 index 0000000..ceac826 --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.modules-load.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +MODULES_LOAD_BIN="/usr/lib/systemd/systemd-modules-load" +CONFIG_FILE="/run/modules-load.d/99-test.conf" + +at_exit() { + rm -rfv "${CONFIG_FILE:?}" +} + +trap at_exit EXIT + +if systemd-detect-virt -cq; then + echo "Running in a container, skipping the systemd-modules-load test..." + exit 0 +fi + +ORIG_MODULES_LOAD_CONFIG="$(systemd-analyze cat-config modules-load.d)" + +# Check if we have required kernel modules +modprobe --all --resolve-alias dummy + +mkdir -p /run/modules-load.d/ + +"$MODULES_LOAD_BIN" +"$MODULES_LOAD_BIN" --help +"$MODULES_LOAD_BIN" --version + +# Explicit config file +modprobe -v --all --remove dummy +printf "dummy" >"$CONFIG_FILE" +"$MODULES_LOAD_BIN" "$CONFIG_FILE" |& tee /tmp/out.log +grep -E "Inserted module .*dummy" /tmp/out.log + +# Implicit config file +modprobe -v --all --remove dummy +printf "dummy" >"$CONFIG_FILE" +"$MODULES_LOAD_BIN" |& tee /tmp/out.log +grep -E "Inserted module .*dummy" /tmp/out.log + +# Valid & invalid data mixed together +modprobe -v --all --remove dummy +cat >"$CONFIG_FILE" </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")" +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" +systemctl status "$WORK_DIR/mnt" +systemd-mount --list --full +test -e "$WORK_DIR/mnt/foo.bar" +systemd-umount "$WORK_DIR/mnt" +# Same thing, but with explicitly specified filesystem and disabled filesystem check +systemd-mount --type=ext4 --fsck=no --collect "$LOOP" "$WORK_DIR/mnt" +systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").mount" +test -e "$WORK_DIR/mnt/foo.bar" +systemd-mount --umount "$LOOP" +# Discover additional metadata (unit description should now contain filesystem label) +systemd-mount --no-ask-password --discover "$LOOP" "$WORK_DIR/mnt" +test -e "$WORK_DIR/mnt/foo.bar" +systemctl show -P Description "$WORK_DIR/mnt" | grep -q sd-mount-test +systemd-umount "$WORK_DIR/mnt" +# Set a unit description +systemd-mount --description="Very Important Unit" "$LOOP" "$WORK_DIR/mnt" +test -e "$WORK_DIR/mnt/foo.bar" +systemctl show -P Description "$WORK_DIR/mnt" | grep -q "Very Important Unit" +systemd-umount "$WORK_DIR/mnt" +# Set a property +systemd-mount --property="Description=Foo Bar" "$LOOP" "$WORK_DIR/mnt" +test -e "$WORK_DIR/mnt/foo.bar" +systemctl show -P Description "$WORK_DIR/mnt" | grep -q "Foo Bar" +systemd-umount "$WORK_DIR/mnt" +# Set mount options +systemd-mount --options=ro,x-foo-bar "$LOOP" "$WORK_DIR/mnt" +test -e "$WORK_DIR/mnt/foo.bar" +systemctl show -P Options "$WORK_DIR/mnt" | grep -Eq "(^ro|,ro)" +systemctl show -P Options "$WORK_DIR/mnt" | grep -q "x-foo-bar" +systemd-umount "$WORK_DIR/mnt" + +# Mount with only source set +systemd-mount "$LOOP" +systemctl status /run/media/system/sd-mount-test +systemd-mount --list --full +test -e /run/media/system/sd-mount-test/foo.bar +systemd-umount LABEL=sd-mount-test + +# Automount +systemd-mount --automount=yes "$LOOP" "$WORK_DIR/mnt" +systemd-mount --list --full +systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount" +[[ "$(systemctl show -P ActiveState "$WORK_DIR/mnt")" == inactive ]] +test -e "$WORK_DIR/mnt/foo.bar" +systemctl status "$WORK_DIR/mnt" +systemd-umount "$WORK_DIR/mnt" +# Automount + automount-specific property +systemd-mount -A --automount-property="Description=Bar Baz" "$LOOP" "$WORK_DIR/mnt" +systemctl show -P Description "$(systemd-escape --path "$WORK_DIR/mnt").automount" | grep -q "Bar Baz" +test -e "$WORK_DIR/mnt/foo.bar" +# Call --umount via --machine=, first with a relative path (bad) and then with +# an absolute one (good) +(! systemd-umount --machine=.host "$(realpath --relative-to=. "$WORK_DIR/mnt")") +systemd-umount --machine=.host "$WORK_DIR/mnt" + +# ext4 doesn't support uid=/gid= +(! systemd-mount -t ext4 --owner=testuser "$LOOP" "$WORK_DIR/mnt") + +# Automount + --bind-device +systemd-mount --automount=yes --bind-device --timeout-idle-sec=1 "$LOOP" "$WORK_DIR/mnt" +systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount" +# Trigger the automount +test -e "$WORK_DIR/mnt/foo.bar" +# Wait until it's idle again +sleep 1.5 +# Safety net for slower/overloaded systems +timeout 10s bash -c "while systemctl is-active -q $WORK_DIR/mnt; do sleep .2; done" +systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount" +# Disassemble the underlying block device +losetup -d "$LOOP" +unset LOOP +# The automount unit should disappear once the underlying blockdev is gone +timeout 10s bash -c "while systemctl status '$(systemd-escape --path "$WORK_DIR/mnt".automount)'; do sleep .2; done" + +# Mount a disk image +systemd-mount --discover "$WORK_DIR/simple.img" +# We can access files in the image even if the loopback block device is not initialized by udevd. +test -e /run/media/system/simple.img/foo.bar +# systemd-mount --list and systemd-umount require the loopback block device is initialized by udevd. +udevadm settle --timeout 30 +assert_in "/dev/loop.* ext4 +sd-mount-test" "$(systemd-mount --list --full)" +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")" +# 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" +systemctl status "$WORK_DIR/mnt" +[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt")" == "testuser:testuser" ]] +touch "$WORK_DIR/mnt/hello" +[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt/hello")" == "testuser:testuser" ]] +systemd-umount LABEL=owner-vfat 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 < /run/credstore/network.network.50-testme <"/usr/lib/systemd/network/$NETWORK_NAME" <new <"+4" <"/usr/lib/systemd/network/$NETDEV_NAME" <"/usr/lib/systemd/network/$LINK_NAME" </run/systemd/networkd.conf.d/10-hoge.conf <"${USER_DIRS_CONF:?}" <<\EOF +XDG_DESKTOP_DIR="$HOME/my-fancy-desktop" +XDG_INVALID + +XDG_DOWNLOAD_DIR = "$HOME" +XDG_TEMPLATES_DIR="/templates" +# Invalid records +XDG_TEMPLATES_DIR=/not-templates" +XDG_TEMPLATES_DIR="/also-not-teplates +XDG_TEMPLATES_DIR="" +XDG_TEMPLATES_DIR="../" + +XDG_PUBLICSHARE_DIR="$HOME/cat-pictures" +XDG_DOCUMENTS_DIR="$HOME/top/secret/documents" +XDG_MUSIC_DIR="/tmp/vaporwave" +XDG_PICTURES_DIR="$HOME/Pictures" +XDG_VIDEOS_DIR="$HOME/🤔" +EOF + +systemd-path --help +systemd-path --version +systemd-path +systemd-path temporary system-binaries user binfmt + +assert_eq "$(systemd-path system-runtime)" "/run" +assert_eq "$(systemd-path --suffix='' system-runtime)" "/run" +assert_eq "$(systemd-path --suffix='🤔' system-runtime)" "/run/🤔" +assert_eq "$(systemd-path --suffix=hello system-runtime)" "/run/hello" + +# Note for the stuff below: everything defaults to $HOME, only the desktop +# directory defaults to $HOME/Desktop. +# +# Check the user-dirs.dir stuff from above +assert_eq "$(systemd-path user)" "/root" +assert_eq "$(systemd-path user-desktop)" "/root/my-fancy-desktop" +assert_eq "$(systemd-path user-documents)" "/root/top/secret/documents" +assert_eq "$(systemd-path user-download)" "/root" +assert_eq "$(systemd-path user-music)" "/tmp/vaporwave" +assert_eq "$(systemd-path user-pictures)" "/root/Pictures" +assert_eq "$(systemd-path user-public)" "/root/cat-pictures" +assert_eq "$(systemd-path user-templates)" "/templates" +assert_eq "$(systemd-path user-videos)" "/root/🤔" + +# Remove the user-dirs.dir file and check the defaults +rm -fv "$USER_DIRS_CONF" +[[ ! -e "$USER_DIRS_CONF" ]] +assert_eq "$(systemd-path user-desktop)" "/root/Desktop" +for dir in "" documents download music pictures public templates videos; do + assert_eq "$(systemd-path "user${dir:+-$dir}")" "/root" +done + +# sd-path should consider only absolute $HOME +assert_eq "$(HOME=/hello-world systemd-path user)" "/hello-world" +assert_eq "$(HOME=hello-world systemd-path user)" "/root" +assert_eq "$(HOME=/hello systemd-path --suffix=world user)" "/hello/world" +assert_eq "$(HOME=hello systemd-path --suffix=world user)" "/root/world" +# Same with some other env variables +assert_in "/my-config" "$(HOME='' XDG_CONFIG_HOME=/my-config systemd-path search-configuration)" +assert_in "/my-config/foo" "$(HOME='' XDG_CONFIG_HOME=/my-config systemd-path --suffix=foo search-configuration)" +assert_in "/my-home/.config/foo" "$(HOME=/my-home XDG_CONFIG_HOME=my-config systemd-path --suffix=foo search-configuration)" +assert_not_in "my-config" "$(HOME=my-config XDG_CONFIG_HOME=my-config systemd-path search-configuration)" + +(! systemd-path '') +(! systemd-path system-binaries 🤔 user) +(! systemd-path --xyz) diff --git a/test/units/TEST-74-AUX-UTILS.pstore.sh b/test/units/TEST-74-AUX-UTILS.pstore.sh new file mode 100755 index 0000000..9be8066 --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.pstore.sh @@ -0,0 +1,258 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +systemctl log-level info + +if systemd-detect-virt -cq; then + echo "Running in a container, skipping the systemd-pstore test..." + exit 0 +fi + +DUMMY_DMESG_0="$(mktemp)" +cat >"$DUMMY_DMESG_0" <<\EOF +6,17159,5340096332127,-;usb 1-4: USB disconnect, device number 124 +6,17160,5340109662397,-;input: WH-1000XM3 (AVRCP) as /devices/virtual/input/input293 +6,17161,5343126458360,-;loop0: detected capacity change from 0 to 3145728 +6,17162,5343126766065,-; loop0: p1 p2 +6,17163,5343126815038,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none. +6,17164,5343158037334,-;EXT4-fs (loop0p1): unmounting filesystem. +6,17165,5343158072598,-;loop0: detected capacity change from 0 to 3145728 +6,17166,5343158073563,-; loop0: p1 p2 +6,17167,5343158074325,-; loop0: p1 p2 +6,17168,5343158140859,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none. +6,17169,5343158182977,-;EXT4-fs (loop0p1): unmounting filesystem. +6,17170,5343158700241,-;loop0: detected capacity change from 0 to 3145728 +6,17171,5343158700439,-; loop0: p1 p2 +6,17172,5343158701120,-; loop0: p1 p2 +EOF + +DUMMY_DMESG_1="$(mktemp)" +cat >"$DUMMY_DMESG_1" <<\EOF +Nechť již hříšné saxofony ďáblů rozezvučí síň úděsnými tóny waltzu, tanga a quickstepu. +Příliš žluťoučký kůň úpěl ďábelské ódy. +Zvlášť zákeřný učeň s ďolíčky běží podél zóny úlů. +Vyciď křišťálový nůž, ó učiň úděsné líbivým! +Loď čeří kýlem tůň obzvlášť v Grónské úžině +Ó, náhlý déšť již zvířil prach a čilá laň teď běží s houfcem gazel k úkrytům. +Vypätá dcéra grófa Maxwella s IQ nižším ako kôň núti čeľaď hrýzť hŕbu jabĺk. +Kŕdeľ šťastných ďatľov učí pri ústí Váhu mĺkveho koňa obhrýzať kôru a žrať čerstvé mäso. +Stróż pchnął kość w quiz gędźb vel fax myjń. +Portez ce vieux whisky au juge blond qui fume! +EOF + +file_count() { find "${1:?}" -type f | wc -l; } +file_size() { wc -l <"${1:?}"; } +random_efi_timestamp() { printf "%0.10d" "$((1000000000 + RANDOM))"; } + +# The dmesg- filename contains the backend-type and the Common Platform Error Record, CPER, +# record id, a 64-bit number. +# +# Files are processed in reverse lexigraphical order so as to properly reconstruct original dmesg. + +prepare_efi_logs() { + local file="${1:?}" + local timestamp="${2:?}" + local chunk count filename + + # For the EFI backend, the 3 least significant digits of record id encodes a + # "count" number, the next 2 least significant digits for the dmesg part + # (chunk) number, and the remaining digits as the timestamp. See + # linux/drivers/firmware/efi/efi-pstore.c in efi_pstore_write(). + count="$(file_size "$file")" + chunk=0 + # The sed in the process substitution below just reverses the file + while read -r line; do + filename="$(printf "dmesg-efi-%0.10d%0.2d%0.3d" "$timestamp" "$chunk" "$count")" + echo "$line" >"/sys/fs/pstore/$filename" + chunk=$((chunk + 1)) + done < <(sed '1!G;h;$!d' "$file") + + if [[ "$chunk" -eq 0 ]]; then + echo >&2 "No dmesg-efi files were created" + exit 1 + fi +} + +prepare_erst_logs() { + local file="${1:?}" + local start_id="${2:?}" + local id filename + + # For the ERST backend, the record is a monotonically increasing number, seeded as + # a timestamp. See linux/drivers/acpi/apei/erst.c in erst_writer(). + id="$start_id" + # The sed in the process substitution below just reverses the file + while read -r line; do + filename="$(printf "dmesg-erst-%0.16d" "$id")" + echo "$line" >"/sys/fs/pstore/$filename" + id=$((id + 1)) + done < <(sed '1!G;h;$!d' "$file") + + if [[ "$id" -eq "$start_id" ]]; then + echo >&2 "No dmesg-erst files were created" + exit 1 + fi + + # ID of the last dmesg file will be the ID of the erst subfolder + echo "$((id - 1))" +} + +prepare_pstore_config() { + local storage="${1:?}" + local unlink="${2:?}" + + systemctl stop systemd-pstore + + rm -fr /sys/fs/pstore/* /var/lib/systemd/pstore/* + + mkdir -p /run/systemd/pstore.conf.d + cat >/run/systemd/pstore.conf.d/99-test.conf </run/systemd/system/systemd-pstore.service.d/99-StartLimitInterval.conf </sys/fs/pstore/foo.bar + [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)" + start_pstore + [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]] + [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]] + filename="$(printf "/var/lib/systemd/pstore/%s/%0.3d/dmesg.txt" "$timestamp" "$(file_size "${!dmesg}")")" + diff "${!dmesg}" "$filename" + grep "hello world" "/var/lib/systemd/pstore/foo.bar" + done + # Check that we kept all previous records as well + for timestamp in "${timestamps[@]}"; do + [[ -d "/var/lib/systemd/pstore/$timestamp" ]] + [[ "$(file_count "/var/lib/systemd/pstore/$timestamp/")" -gt 0 ]] + done + + : "Backend: EFI; Storage: journal; Unlink: $unlink" + timestamp="$(random_efi_timestamp)" + prepare_pstore_config "journal" "$unlink" + prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp" + [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)" + start_pstore + [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]] + [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]] + diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d") + + : "Backend: ERST; Storage: external; Unlink: $unlink" + prepare_pstore_config "external" "$unlink" + last_id="$(prepare_erst_logs "$DUMMY_DMESG_0" 0)" + [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)" + start_pstore + [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]] + [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]] + # We always log to journal + diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d") + filename="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id")" + diff "$DUMMY_DMESG_0" "$filename" + + : "Backend: ERST; Storage: external; Unlink: $unlink; multiple dmesg files" + last_ids=() + prepare_pstore_config "external" "$unlink" + for i in {0..9}; do + # Create a name reference to one of the $DUMMY_DMESG_X variables + dmesg="DUMMY_DMESG_$((i % 2))" + last_id="$(prepare_erst_logs "${!dmesg}" "$((i * 100))")" + last_ids+=("$last_id") + # Add one "random" (non-dmesg) file as well + echo "hello world" >/sys/fs/pstore/foo.bar + [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)" + start_pstore + [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]] + [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]] + filename="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id")" + diff "${!dmesg}" "$filename" + grep "hello world" "/var/lib/systemd/pstore/foo.bar" + done + # Check that we kept all previous records as well + for last_id in "${last_ids[@]}"; do + directory="$(printf "/var/lib/systemd/pstore/%0.16d" "$last_id")" + [[ -d "$directory" ]] + [[ "$(file_count "$directory")" -gt 0 ]] + done + + : "Backend: ERST; Storage: journal; Unlink: $unlink" + prepare_pstore_config "journal" "$unlink" + last_id="$(prepare_erst_logs "$DUMMY_DMESG_0" 0)" + [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)" + start_pstore + [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]] + [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]] + diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d") +done diff --git a/test/units/TEST-74-AUX-UTILS.run.sh b/test/units/TEST-74-AUX-UTILS.run.sh new file mode 100755 index 0000000..b4ff72e --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.run.sh @@ -0,0 +1,247 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2016 +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +systemd-run --help --no-pager +systemd-run --version +systemd-run --no-ask-password true +systemd-run --no-block --collect true + +export PARENT_FOO=bar +touch /tmp/public-marker + +: "Transient service (system daemon)" +systemd-run --wait --pipe \ + bash -xec '[[ "$("$TMP_KVER" +mount --bind "$TMP_KVER" /proc/version +UNIT_KVER="$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/version)" +assert_eq "$KVER" "$UNIT_KVER" +umount /proc/version +rm -f "$TMP_KVER" + +if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; then + # Check that invoking the tool under the run0 alias name works + run0 ls / + assert_eq "$(run0 echo foo)" "foo" + # Check if we set some expected environment variables + for arg in "" "--user=root" "--user=testuser"; do + assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER" + assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")" + assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")" + done + # Let's chain a couple of run0 calls together, for fun + readarray -t cmdline < <(printf "%.0srun0\n" {0..31}) + assert_eq "$("${cmdline[@]}" bash -c 'echo $SUDO_USER')" "$USER" +fi diff --git a/test/units/TEST-74-AUX-UTILS.sh b/test/units/TEST-74-AUX-UTILS.sh new file mode 100755 index 0000000..9c2a033 --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + +run_subtests + +touch /testok diff --git a/test/units/TEST-74-AUX-UTILS.socket.sh b/test/units/TEST-74-AUX-UTILS.socket.sh new file mode 100755 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 </run/systemd/system/per-source-limit@.service <&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/TEST-74-AUX-UTILS.varlinkctl.sh b/test/units/TEST-74-AUX-UTILS.varlinkctl.sh new file mode 100755 index 0000000..1d5a98b --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.varlinkctl.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# Unset $PAGER so we don't have to use --no-pager everywhere +export PAGER= + +varlinkctl --help +varlinkctl help --no-pager +varlinkctl --version +varlinkctl --json=help + +# TODO: abstract namespace sockets (@...) +# Path to a socket +varlinkctl info /run/systemd/journal/io.systemd.journal +varlinkctl info /run/systemd/../systemd/../../run/systemd/journal/io.systemd.journal +varlinkctl info "./$(realpath --relative-to="$PWD" /run/systemd/journal/io.systemd.journal)" +varlinkctl info unix:/run/systemd/journal/io.systemd.journal +varlinkctl info --json=off /run/systemd/journal/io.systemd.journal +varlinkctl info --json=pretty /run/systemd/journal/io.systemd.journal | jq . +varlinkctl info --json=short /run/systemd/journal/io.systemd.journal | jq . +varlinkctl info -j /run/systemd/journal/io.systemd.journal | jq . + +varlinkctl list-interfaces /run/systemd/journal/io.systemd.journal +varlinkctl list-interfaces -j /run/systemd/journal/io.systemd.journal | jq . + +varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal +varlinkctl introspect -j /run/systemd/journal/io.systemd.journal io.systemd.Journal | jq . + +if command -v userdbctl >/dev/null; then + systemctl start systemd-userdbd + varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }' + varlinkctl call -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }' | jq . + # 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 + +IDL_FILE="$(mktemp)" +varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal | tee "${IDL_FILE:?}" +varlinkctl validate-idl "$IDL_FILE" +varlinkctl validate-idl "$IDL_FILE" +cat /bin/sh >"$IDL_FILE" +(! varlinkctl validate-idl "$IDL_FILE") + +if [[ -x /usr/lib/systemd/systemd-pcrextend ]]; then + # Path to an executable + varlinkctl info /usr/lib/systemd/systemd-pcrextend + varlinkctl info exec:/usr/lib/systemd/systemd-pcrextend + varlinkctl list-interfaces /usr/lib/systemd/systemd-pcrextend + varlinkctl introspect /usr/lib/systemd/systemd-pcrextend io.systemd.PCRExtend +fi + +# SSH transport +SSHBINDIR="$(mktemp -d)" + +rm_rf_sshbindir() { + rm -rf "$SSHBINDIR" +} + +trap rm_rf_sshbindir EXIT + +# Create a fake "ssh" binary that validates everything works as expected +cat > "$SSHBINDIR"/ssh <<'EOF' +#!/bin/sh + +set -xe + +test "$1" = "-W" +test "$2" = "/run/systemd/journal/io.systemd.journal" +test "$3" = "foobar" + +exec socat - UNIX-CONNECT:/run/systemd/journal/io.systemd.journal +EOF + +chmod +x "$SSHBINDIR"/ssh + +SYSTEMD_SSH="$SSHBINDIR/ssh" varlinkctl info ssh:foobar:/run/systemd/journal/io.systemd.journal + +# Go through all varlink sockets we can find under /run/systemd/ for some extra coverage +find /run/systemd/ -name "io.systemd*" -type s | while read -r socket; do + varlinkctl info "$socket" + + varlinkctl list-interfaces "$socket" | while read -r interface; do + varlinkctl introspect "$socket" "$interface" + done +done + +(! varlinkctl) +(! varlinkctl "") +(! varlinkctl info) +(! varlinkctl info "") +(! varlinkctl info /run/systemd/notify) +(! varlinkctl info /run/systemd/private) +# Relative paths must begin with ./ +(! varlinkctl info "$(realpath --relative-to="$PWD" /run/systemd/journal/io.systemd.journal)") +(! varlinkctl info unix:) +(! varlinkctl info unix:"") +(! varlinkctl info exec:) +(! varlinkctl info exec:"") +(! varlinkctl list-interfaces) +(! varlinkctl list-interfaces "") +(! varlinkctl introspect) +(! varlinkctl introspect /run/systemd/journal/io.systemd.journal) +(! varlinkctl introspect /run/systemd/journal/io.systemd.journal "") +(! varlinkctl introspect "" "") +(! varlinkctl call) +(! varlinkctl call "") +(! varlinkctl call "" "") +(! varlinkctl call "" "" "") +(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord /dev/null); then + TMPDIR=$(mktemp -d -p /tmp resolvconf-tests.XXXXXX) + RESOLVCONF="$TMPDIR"/resolvconf + ln -s "$(command -v resolvectl 2>/dev/null)" "$RESOLVCONF" +fi +echo nameserver 10.0.2.1 10.0.2.2 | "$RESOLVCONF" -a hoge +echo nameserver 10.0.2.3 10.0.2.4 | "$RESOLVCONF" -a hoge.foo +assert_in '10.0.2.1 10.0.2.2' "$(resolvectl dns hoge)" +assert_in '10.0.2.3 10.0.2.4' "$(resolvectl dns hoge.foo)" +echo nameserver 10.0.3.1 10.0.3.2 | "$RESOLVCONF" -a hoge.inet.ipsec.192.168.35 +echo nameserver 10.0.3.3 10.0.3.4 | "$RESOLVCONF" -a hoge.foo.dhcp +assert_in '10.0.3.1 10.0.3.2' "$(resolvectl dns hoge)" +assert_in '10.0.3.3 10.0.3.4' "$(resolvectl dns hoge.foo)" + +# Tests for _localdnsstub and _localdnsproxy +assert_in '127.0.0.53' "$(resolvectl query _localdnsstub)" +assert_in '_localdnsstub' "$(resolvectl query 127.0.0.53)" +assert_in '127.0.0.54' "$(resolvectl query _localdnsproxy)" +assert_in '_localdnsproxy' "$(resolvectl query 127.0.0.54)" + +assert_in '127.0.0.53' "$(dig @127.0.0.53 _localdnsstub)" +assert_in '_localdnsstub' "$(dig @127.0.0.53 -x 127.0.0.53)" +assert_in '127.0.0.54' "$(dig @127.0.0.53 _localdnsproxy)" +assert_in '_localdnsproxy' "$(dig @127.0.0.53 -x 127.0.0.54)" + +# Tests for mDNS and LLMNR settings +mkdir -p /run/systemd/resolved.conf.d +{ + echo "[Resolve]" + echo "MulticastDNS=no" + echo "LLMNR=no" +} >/run/systemd/resolved.conf.d/mdns-llmnr.conf +restart_resolved +# make sure networkd is not running. +systemctl stop systemd-networkd.service +assert_in 'no' "$(resolvectl mdns hoge)" +assert_in 'no' "$(resolvectl llmnr hoge)" +# Tests that reloading works +{ + echo "[Resolve]" + echo "MulticastDNS=yes" + echo "LLMNR=yes" +} >/run/systemd/resolved.conf.d/mdns-llmnr.conf +systemctl reload systemd-resolved.service +# defaults to yes (both the global and per-link settings are yes) +assert_in 'yes' "$(resolvectl mdns hoge)" +assert_in 'yes' "$(resolvectl llmnr hoge)" +# set per-link setting +resolvectl mdns hoge yes +resolvectl llmnr hoge yes +assert_in 'yes' "$(resolvectl mdns hoge)" +assert_in 'yes' "$(resolvectl llmnr hoge)" +resolvectl mdns hoge resolve +resolvectl llmnr hoge resolve +assert_in 'resolve' "$(resolvectl mdns hoge)" +assert_in 'resolve' "$(resolvectl llmnr hoge)" +resolvectl mdns hoge no +resolvectl llmnr hoge no +assert_in 'no' "$(resolvectl mdns hoge)" +assert_in 'no' "$(resolvectl llmnr hoge)" +# downgrade global setting to resolve +{ + echo "[Resolve]" + echo "MulticastDNS=resolve" + echo "LLMNR=resolve" +} >/run/systemd/resolved.conf.d/mdns-llmnr.conf +systemctl reload systemd-resolved.service +# set per-link setting +resolvectl mdns hoge yes +resolvectl llmnr hoge yes +assert_in 'resolve' "$(resolvectl mdns hoge)" +assert_in 'resolve' "$(resolvectl llmnr hoge)" +resolvectl mdns hoge resolve +resolvectl llmnr hoge resolve +assert_in 'resolve' "$(resolvectl mdns hoge)" +assert_in 'resolve' "$(resolvectl llmnr hoge)" +resolvectl mdns hoge no +resolvectl llmnr hoge no +assert_in 'no' "$(resolvectl mdns hoge)" +assert_in 'no' "$(resolvectl llmnr hoge)" +# downgrade global setting to no +{ + echo "[Resolve]" + echo "MulticastDNS=no" + echo "LLMNR=no" +} >/run/systemd/resolved.conf.d/mdns-llmnr.conf +systemctl reload systemd-resolved.service +# set per-link setting +resolvectl mdns hoge yes +resolvectl llmnr hoge yes +assert_in 'no' "$(resolvectl mdns hoge)" +assert_in 'no' "$(resolvectl llmnr hoge)" +resolvectl mdns hoge resolve +resolvectl llmnr hoge resolve +assert_in 'no' "$(resolvectl mdns hoge)" +assert_in 'no' "$(resolvectl llmnr hoge)" +resolvectl mdns hoge no +resolvectl llmnr hoge no +assert_in 'no' "$(resolvectl mdns hoge)" +assert_in 'no' "$(resolvectl llmnr hoge)" + +# Cleanup +rm -f /run/systemd/resolved.conf.d/mdns-llmnr.conf +ip link del hoge +ip link del hoge.foo + +### SETUP ### +# Configure network +hostnamectl hostname ns1.unsigned.test +cat >>/etc/hosts </run/systemd/network/10-dns0.netdev </run/systemd/network/10-dns0.network </run/systemd/network/10-dns1.netdev </run/systemd/network/10-dns1.network </run/systemd/resolved.conf.d/test.conf +ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf +# Override the default NTA list, which turns off DNSSEC validation for (among +# others) the test. domain +mkdir -p "/etc/dnssec-trust-anchors.d/" +echo local >/etc/dnssec-trust-anchors.d/local.negative + +# Copy over our knot configuration +mkdir -p /var/lib/knot/zones/ /etc/knot/ +cp -rfv /usr/lib/systemd/tests/testdata/knot-data/zones/* /var/lib/knot/zones/ +cp -fv /usr/lib/systemd/tests/testdata/knot-data/knot.conf /etc/knot/knot.conf +chgrp -R knot /etc/knot/ /var/lib/knot/ +chmod -R ug+rwX /var/lib/knot/ +chmod -R g+r /etc/knot/ + +# Sign the root zone +keymgr . generate algorithm=ECDSAP256SHA256 ksk=yes zsk=yes +# Create a trust anchor for resolved with our root zone +keymgr . ds | sed 's/ DS/ IN DS/g' >/etc/dnssec-trust-anchors.d/root.positive +# Create a bind-compatible trust anchor (for delv) +# Note: the trust-anchors directive is relatively new, so use the original +# managed-keys one until it's widespread enough +{ + echo 'managed-keys {' + keymgr . dnskey | sed -r 's/^\. DNSKEY ([0-9]+ [0-9]+ [0-9]+) (.+)$/. static-key \1 "\2";/g' + echo '};' +} >/etc/bind.keys +# Create an /etc/bind/bind.keys symlink, which is used by delv on Ubuntu +mkdir -p /etc/bind +ln -svf /etc/bind.keys /etc/bind/bind.keys + +# Start the services +systemctl unmask systemd-networkd +systemctl restart systemd-networkd +/usr/lib/systemd/systemd-networkd-wait-online --interface=dns1:routable --timeout=60 +systemctl reload systemd-resolved +systemctl start resolved-dummy-server + +# Create knot's runtime dir, since from certain version it's provided only by +# the package and not created by tmpfiles/systemd +if [[ ! -d /run/knot ]]; then + mkdir -p /run/knot + chown -R knot:knot /run/knot +fi +systemctl start knot +# Wait a bit for the keys to propagate +sleep 4 + +systemctl status resolved-dummy-server +networkctl status +resolvectl status +resolvectl log-level debug + +# Start monitoring queries +systemd-run -u resolvectl-monitor.service -p Type=notify resolvectl monitor +systemd-run -u resolvectl-monitor-json.service -p Type=notify resolvectl monitor --json=short + +# FIXME: knot, unfortunately, incorrectly complains about missing zone files for zones +# that are forwarded using the `dnsproxy` module. Until the issue is resolved, +# let's fall back to pre-processing the `zone-check` output a bit before checking it +# +# See: https://gitlab.nic.cz/knot/knot-dns/-/issues/913 +run knotc zone-check || : +sed -i '/forwarded.test./d' "$RUN_OUT" +[[ ! -s "$RUN_OUT" ]] +# We need to manually propagate the DS records of onlinesign.test. to the parent +# zone, since they're generated online +knotc zone-begin test. +if knotc zone-get test. onlinesign.test. ds | grep .; then + # Drop any old DS records, if present (e.g. on test re-run) + knotc zone-unset test. onlinesign.test. ds +fi +# Propagate the new DS records +while read -ra line; do + knotc zone-set test. "${line[0]}" 600 "${line[@]:1}" +done < <(keymgr onlinesign.test. ds) +knotc zone-commit test. + +knotc reload +sleep 2 + +### SETUP END ### + +: "--- nss-resolve/nss-myhostname tests" +# Sanity check +TIMESTAMP=$(date '+%F %T') +# Issue: https://github.com/systemd/systemd/issues/23951 +# With IPv6 enabled +run getent -s resolve ahosts ns1.unsigned.test +grep -qE "^fd00:dead:beef:cafe::1\s+STREAM\s+ns1\.unsigned\.test" "$RUN_OUT" +monitor_check_rr "$TIMESTAMP" "ns1.unsigned.test IN AAAA fd00:dead:beef:cafe::1" +# With IPv6 disabled +# Issue: https://github.com/systemd/systemd/issues/23951 +disable_ipv6 +run getent -s resolve ahosts ns1.unsigned.test +grep -qE "^10\.0\.0\.1\s+STREAM\s+ns1\.unsigned\.test" "$RUN_OUT" +(! grep -qE "fd00:dead:beef:cafe::1" "$RUN_OUT") +monitor_check_rr "$TIMESTAMP" "ns1.unsigned.test IN A 10.0.0.1" +enable_ipv6 + +# Issue: https://github.com/systemd/systemd/issues/18812 +# PR: https://github.com/systemd/systemd/pull/18896 +# Follow-up issue: https://github.com/systemd/systemd/issues/23152 +# Follow-up PR: https://github.com/systemd/systemd/pull/23161 +# With IPv6 enabled +run getent -s resolve ahosts localhost +grep -qE "^::1\s+STREAM\s+localhost" "$RUN_OUT" +run getent -s myhostname ahosts localhost +grep -qE "^::1\s+STREAM\s+localhost" "$RUN_OUT" +# With IPv6 disabled +disable_ipv6 +run getent -s resolve ahosts localhost +grep -qE "^127\.0\.0\.1\s+STREAM\s+localhost" "$RUN_OUT" +(! grep -qE "::1" "$RUN_OUT") +run getent -s myhostname ahosts localhost +grep -qE "^127\.0\.0\.1\s+STREAM\s+localhost" "$RUN_OUT" +enable_ipv6 + +# Issue: https://github.com/systemd/systemd/issues/25088 +run getent -s resolve hosts 127.128.0.5 +grep -qEx '127\.128\.0\.5\s+localhost5(\s+localhost5?\.localdomain[45]?){4}' "$RUN_OUT" +[ "$(wc -l <"$RUN_OUT")" -eq 1 ] + +# Issue: https://github.com/systemd/systemd/issues/20158 +run dig +noall +answer +additional localhost5. +grep -qEx 'localhost5\.\s+0\s+IN\s+A\s+127\.128\.0\.5' "$RUN_OUT" +[ "$(wc -l <"$RUN_OUT")" -eq 1 ] +run dig +noall +answer +additional localhost5.localdomain4. +grep -qEx 'localhost5\.localdomain4\.\s+0\s+IN\s+CNAME\s+localhost5\.' "$RUN_OUT" +grep -qEx 'localhost5\.\s+0\s+IN\s+A\s+127\.128\.0\.5' "$RUN_OUT" +[ "$(wc -l <"$RUN_OUT")" -eq 2 ] + +: "--- Basic resolved tests ---" +# Issue: https://github.com/systemd/systemd/issues/22229 +# PR: https://github.com/systemd/systemd/pull/22231 +FILTERED_NAMES=( + "0.in-addr.arpa" + "255.255.255.255.in-addr.arpa" + "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" + "hello.invalid" + "hello.alt" +) + +for name in "${FILTERED_NAMES[@]}"; do + (! run host "$name") + grep -qF "NXDOMAIN" "$RUN_OUT" +done + +# Follow-up +# Issue: https://github.com/systemd/systemd/issues/22401 +# PR: https://github.com/systemd/systemd/pull/22414 +run dig +noall +authority +comments SRV . +grep -qF "status: NOERROR" "$RUN_OUT" +grep -qE "IN\s+SOA\s+ns1\.unsigned\.test\." "$RUN_OUT" + +run resolvectl query -t SVCB svcb.test +grep -qF 'alpn="dot"' "$RUN_OUT" +grep -qF "ipv4hint=10.0.0.1" "$RUN_OUT" + +run resolvectl query -t HTTPS https.test +grep -qF 'alpn="h2,h3"' "$RUN_OUT" + +: "--- ZONE: unsigned.test. ---" +run dig @ns1.unsigned.test +short unsigned.test A unsigned.test AAAA +grep -qF "10.0.0.101" "$RUN_OUT" +grep -qF "fd00:dead:beef:cafe::101" "$RUN_OUT" +run resolvectl query unsigned.test +grep -qF "10.0.0.10" "$RUN_OUT" +grep -qF "fd00:dead:beef:cafe::101" "$RUN_OUT" +grep -qF "authenticated: no" "$RUN_OUT" +run dig @ns1.unsigned.test +short MX unsigned.test +grep -qF "15 mail.unsigned.test." "$RUN_OUT" +run resolvectl query --legend=no -t MX unsigned.test +grep -qF "unsigned.test IN MX 15 mail.unsigned.test" "$RUN_OUT" + + +: "--- ZONE: signed.test (static DNSSEC) ---" +# Check the trust chain (with and without systemd-resolved in between +# Issue: https://github.com/systemd/systemd/issues/22002 +# PR: https://github.com/systemd/systemd/pull/23289 +run_delv @ns1.unsigned.test signed.test +grep -qF "; fully validated" "$RUN_OUT" +run_delv signed.test +grep -qF "; fully validated" "$RUN_OUT" + +for addr in "${DNS_ADDRESSES[@]}"; do + run_delv "@$addr" -t A mail.signed.test + grep -qF "; fully validated" "$RUN_OUT" + run_delv "@$addr" -t AAAA mail.signed.test + grep -qF "; fully validated" "$RUN_OUT" +done +run resolvectl query mail.signed.test +grep -qF "10.0.0.11" "$RUN_OUT" +grep -qF "fd00:dead:beef:cafe::11" "$RUN_OUT" +grep -qF "authenticated: yes" "$RUN_OUT" + +run dig +short signed.test +grep -qF "10.0.0.10" "$RUN_OUT" +run resolvectl query signed.test +grep -qF "signed.test: 10.0.0.10" "$RUN_OUT" +grep -qF "authenticated: yes" "$RUN_OUT" +run dig @ns1.unsigned.test +short MX signed.test +grep -qF "10 mail.signed.test." "$RUN_OUT" +run resolvectl query --legend=no -t MX signed.test +grep -qF "signed.test IN MX 10 mail.signed.test" "$RUN_OUT" +# Check a non-existent domain +run dig +dnssec this.does.not.exist.signed.test +grep -qF "status: NXDOMAIN" "$RUN_OUT" +# Check a wildcard record +run resolvectl query -t TXT this.should.be.authenticated.wild.signed.test +grep -qF 'this.should.be.authenticated.wild.signed.test IN TXT "this is a wildcard"' "$RUN_OUT" +grep -qF "authenticated: yes" "$RUN_OUT" +# Check SRV support +run resolvectl service _mysvc._tcp signed.test +grep -qF "myservice.signed.test:1234" "$RUN_OUT" +grep -qF "10.0.0.20" "$RUN_OUT" +grep -qF "fd00:dead:beef:cafe::17" "$RUN_OUT" +grep -qF "authenticated: yes" "$RUN_OUT" + +# Test service resolve over Varlink +run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"name":"","type":"_mysvc._tcp","domain":"signed.test"}' +grep -qF '"services":[{"priority":10,"weight":5,"port":1234,"hostname":"myservice.signed.test","canonicalName":"myservice.signed.test","addresses":[{"ifindex":' "$RUN_OUT" +grep -qF '"family":10,"address":[253,0,222,173,190,239,202,254,0,0,0,0,0,0,0,23]' "$RUN_OUT" +grep -qF '"family":2,"address":[10,0,0,20]' "$RUN_OUT" +grep -qF '}]}],"txt":["This is TXT for myservice"],"canonical":{"name":null,"type":"_mysvc._tcp","domain":"signed.test"},"flags":' "$RUN_OUT" + +# without name +run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test"}' +# without txt (SD_RESOLVE_NO_TXT) +run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":64}' +(! grep -qF '"txt"' "$RUN_OUT") +# without address (SD_RESOLVE_NO_ADDRESS) +run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":128}' +(! grep -qF '"addresses"' "$RUN_OUT") +# without txt and address +run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":192}' +(! grep -qF '"txt"' "$RUN_OUT") +(! grep -qF '"addresses"' "$RUN_OUT") + +(! run resolvectl service _invalidsvc._udp signed.test) +grep -qE "invalidservice\.signed\.test' not found" "$RUN_OUT" +run resolvectl service _untrustedsvc._udp signed.test +grep -qF "myservice.untrusted.test:1111" "$RUN_OUT" +grep -qF "10.0.0.123" "$RUN_OUT" +grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT" +grep -qF "authenticated: yes" "$RUN_OUT" +# Check OPENPGPKEY support +run_delv -t OPENPGPKEY 5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test +grep -qF "; fully validated" "$RUN_OUT" +run resolvectl openpgp mr.smith@signed.test +grep -qF "5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test" "$RUN_OUT" +grep -qF "authenticated: yes" "$RUN_OUT" +# Check zone transfers (AXFR/IXFR) +# Note: since resolved doesn't support zone transfers, let's just make sure it +# simply refuses such requests without choking on them +# See: https://github.com/systemd/systemd/pull/30809#issuecomment-1880102804 +run dig @ns1.unsigned.test AXFR signed.test +grep -qE "SOA\s+ns1.unsigned.test. root.unsigned.test." "$RUN_OUT" +run dig AXFR signed.test +grep -qF "; Transfer failed" "$RUN_OUT" +run dig @ns1.unsigned.test IXFR=43 signed.test +grep -qE "SOA\s+ns1.unsigned.test. root.unsigned.test." "$RUN_OUT" +run dig IXFR=43 signed.test +grep -qF "; Transfer failed" "$RUN_OUT" + +# DNSSEC validation with multiple records of the same type for the same name +# Issue: https://github.com/systemd/systemd/issues/22002 +# PR: https://github.com/systemd/systemd/pull/23289 +check_domain() { + local domain="${1:?}" + local record="${2:?}" + local message="${3:?}" + local addr + + for addr in "${DNS_ADDRESSES[@]}"; do + run_delv "@$addr" -t "$record" "$domain" + grep -qF "$message" "$RUN_OUT" + done + + run_delv -t "$record" "$domain" + grep -qF "$message" "$RUN_OUT" + + run resolvectl query "$domain" + grep -qF "authenticated: yes" "$RUN_OUT" +} + +check_domain "dupe.signed.test" "A" "; fully validated" +check_domain "dupe.signed.test" "AAAA" "; negative response, fully validated" +check_domain "dupe-ipv6.signed.test" "AAAA" "; fully validated" +check_domain "dupe-ipv6.signed.test" "A" "; negative response, fully validated" +check_domain "dupe-mixed.signed.test" "A" "; fully validated" +check_domain "dupe-mixed.signed.test" "AAAA" "; fully validated" + +# Test resolution of CNAME chains +TIMESTAMP=$(date '+%F %T') +run resolvectl query -t A cname-chain.signed.test +grep -qF "follow14.final.signed.test IN A 10.0.0.14" "$RUN_OUT" +grep -qF "authenticated: yes" "$RUN_OUT" + +monitor_check_rr "$TIMESTAMP" "follow10.so.close.signed.test IN CNAME follow11.yet.so.far.signed.test" +monitor_check_rr "$TIMESTAMP" "follow11.yet.so.far.signed.test IN CNAME follow12.getting.hot.signed.test" +monitor_check_rr "$TIMESTAMP" "follow12.getting.hot.signed.test IN CNAME follow13.almost.final.signed.test" +monitor_check_rr "$TIMESTAMP" "follow13.almost.final.signed.test IN CNAME follow14.final.signed.test" +monitor_check_rr "$TIMESTAMP" "follow14.final.signed.test IN A 10.0.0.14" + +# Non-existing RR + CNAME chain +#run dig +dnssec AAAA cname-chain.signed.test +#grep -qF "status: NOERROR" "$RUN_OUT" +#grep -qE "^follow14\.final\.signed\.test\..+IN\s+NSEC\s+" "$RUN_OUT" + + +: "--- ZONE: onlinesign.test (dynamic DNSSEC) ---" +# Check the trust chain (with and without systemd-resolved in between +# Issue: https://github.com/systemd/systemd/issues/22002 +# PR: https://github.com/systemd/systemd/pull/23289 +run_delv @ns1.unsigned.test sub.onlinesign.test +grep -qF "; fully validated" "$RUN_OUT" +run_delv sub.onlinesign.test +grep -qF "; fully validated" "$RUN_OUT" + +run dig +short sub.onlinesign.test +grep -qF "10.0.0.133" "$RUN_OUT" +run resolvectl query sub.onlinesign.test +grep -qF "sub.onlinesign.test: 10.0.0.133" "$RUN_OUT" +grep -qF "authenticated: yes" "$RUN_OUT" +run dig @ns1.unsigned.test +short TXT onlinesign.test +grep -qF '"hello from onlinesign"' "$RUN_OUT" +run resolvectl query --legend=no -t TXT onlinesign.test +grep -qF 'onlinesign.test IN TXT "hello from onlinesign"' "$RUN_OUT" + +for addr in "${DNS_ADDRESSES[@]}"; do + run_delv "@$addr" -t A dual.onlinesign.test + grep -qF "10.0.0.135" "$RUN_OUT" + run_delv "@$addr" -t AAAA dual.onlinesign.test + grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT" + run_delv "@$addr" -t ANY ipv6.onlinesign.test + grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT" +done +run resolvectl query dual.onlinesign.test +grep -qF "10.0.0.135" "$RUN_OUT" +grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT" +grep -qF "authenticated: yes" "$RUN_OUT" +run resolvectl query ipv6.onlinesign.test +grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT" +grep -qF "authenticated: yes" "$RUN_OUT" + +# Check a non-existent domain +# Note: mod-onlinesign utilizes Minimally Covering NSEC Records, hence the +# different response than with "standard" DNSSEC +run dig +dnssec this.does.not.exist.onlinesign.test +grep -qF "status: NOERROR" "$RUN_OUT" +grep -qF "NSEC \\000.this.does.not.exist.onlinesign.test." "$RUN_OUT" +# Check a wildcard record +run resolvectl query -t TXT this.should.be.authenticated.wild.onlinesign.test +grep -qF 'this.should.be.authenticated.wild.onlinesign.test IN TXT "this is an onlinesign wildcard"' "$RUN_OUT" +grep -qF "authenticated: yes" "$RUN_OUT" + +# Resolve via dbus method +TIMESTAMP=$(date '+%F %T') +run busctl call org.freedesktop.resolve1 /org/freedesktop/resolve1 org.freedesktop.resolve1.Manager ResolveHostname 'isit' 0 secondsub.onlinesign.test 0 0 +grep -qF '10 0 0 134 "secondsub.onlinesign.test"' "$RUN_OUT" +monitor_check_rr "$TIMESTAMP" "secondsub.onlinesign.test IN A 10.0.0.134" + + +: "--- ZONE: untrusted.test (DNSSEC without propagated DS records) ---" +# Issue: https://github.com/systemd/systemd/issues/23955 +# FIXME +resolvectl flush-caches +#run dig +short untrusted.test A untrusted.test AAAA +#grep -qF "10.0.0.121" "$RUN_OUT" +#grep -qF "fd00:dead:beef:cafe::121" "$RUN_OUT" +run resolvectl query untrusted.test +grep -qF "untrusted.test:" "$RUN_OUT" +grep -qF "10.0.0.121" "$RUN_OUT" +grep -qF "fd00:dead:beef:cafe::121" "$RUN_OUT" +grep -qF "authenticated: no" "$RUN_OUT" +run resolvectl service _mysvc._tcp untrusted.test +grep -qF "myservice.untrusted.test:1234" "$RUN_OUT" +grep -qF "10.0.0.123" "$RUN_OUT" +grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT" + +# Issue: https://github.com/systemd/systemd/issues/19472 +# 1) Query for a non-existing RR should return NOERROR + NSEC (?), not NXDOMAIN +# FIXME: re-enable once the issue is resolved +#run dig +dnssec AAAA untrusted.test +#grep -qF "status: NOERROR" "$RUN_OUT" +#grep -qE "^untrusted\.test\..+IN\s+NSEC\s+" "$RUN_OUT" +## 2) Query for a non-existing name should return NXDOMAIN, not SERVFAIL +#run dig +dnssec this.does.not.exist.untrusted.test +#grep -qF "status: NXDOMAIN" "$RUN_OUT" + +: "--- ZONE: forwarded.test (queries forwarded to our dummy test server) ---" +JOURNAL_CURSOR="$(mktemp)" +journalctl -n0 -q --cursor-file="$JOURNAL_CURSOR" + +# See "test-resolved-dummy-server.c" for the server part +(! run resolvectl query nope.forwarded.test) +grep -qF "nope.forwarded.test" "$RUN_OUT" +grep -qF "not found" "$RUN_OUT" + +# SERVFAIL + EDE code 6: DNSSEC Bogus +(! run resolvectl query edns-bogus-dnssec.forwarded.test) +grep -qE "^edns-bogus-dnssec.forwarded.test:.+: upstream-failure \(DNSSEC Bogus\)" "$RUN_OUT" +# Same thing, but over Varlink +(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-bogus-dnssec.forwarded.test"}') +grep -qF "io.systemd.Resolve.DNSSECValidationFailed" "$RUN_OUT" +grep -qF '{"result":"upstream-failure","extendedDNSErrorCode":6}' "$RUN_OUT" +journalctl --sync +journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(DNSSEC Bogus\). Lookup failed." + +# SERVFAIL + EDE code 16: Censored + extra text +(! run resolvectl query edns-extra-text.forwarded.test) +grep -qE "^edns-extra-text.forwarded.test.+: SERVFAIL \(Censored: Nothing to see here!\)" "$RUN_OUT" +(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-extra-text.forwarded.test"}') +grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT" +grep -qF '{"rcode":2,"extendedDNSErrorCode":16,"extendedDNSErrorMessage":"Nothing to see here!"}' "$RUN_OUT" +journalctl --sync +journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Censored: Nothing to see here!\)" + +# SERVFAIL + EDE code 0: Other + extra text +(! run resolvectl query edns-code-zero.forwarded.test) +grep -qE "^edns-code-zero.forwarded.test:.+: SERVFAIL \(Other: 🐱\)" "$RUN_OUT" +(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-code-zero.forwarded.test"}') +grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT" +grep -qF '{"rcode":2,"extendedDNSErrorCode":0,"extendedDNSErrorMessage":"🐱"}' "$RUN_OUT" +journalctl --sync +journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(Other: 🐱\)" + +# SERVFAIL + invalid EDE code +(! run resolvectl query edns-invalid-code.forwarded.test) +grep -qE "^edns-invalid-code.forwarded.test:.+: SERVFAIL \([0-9]+\)" "$RUN_OUT" +(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code.forwarded.test"}') +grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT" +grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+}' "$RUN_OUT" +journalctl --sync +journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+\)" + +# SERVFAIL + invalid EDE code + extra text +(! run resolvectl query edns-invalid-code-with-extra-text.forwarded.test) +grep -qE '^edns-invalid-code-with-extra-text.forwarded.test:.+: SERVFAIL \([0-9]+: Hello \[#\]\$%~ World\)' "$RUN_OUT" +(! run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveHostname '{"name" : "edns-invalid-code-with-extra-text.forwarded.test"}') +grep -qF "io.systemd.Resolve.DNSError" "$RUN_OUT" +grep -qE '{"rcode":2,"extendedDNSErrorCode":[0-9]+,"extendedDNSErrorMessage":"Hello \[#\]\$%~ World"}' "$RUN_OUT" +journalctl --sync +journalctl -u systemd-resolved.service --cursor-file="$JOURNAL_CURSOR" --grep "Server returned error: SERVFAIL \(\d+: Hello \[\#\]\\$%~ World\)" + +### Test resolvectl show-cache +run resolvectl show-cache +run resolvectl show-cache --json=short +run resolvectl show-cache --json=pretty + +# Issue: https://github.com/systemd/systemd/issues/29580 (part #1) +dig @127.0.0.54 signed.test + +systemctl stop resolvectl-monitor.service +systemctl stop resolvectl-monitor-json.service + +# Issue: https://github.com/systemd/systemd/issues/29580 (part #2) +# +# Check for any warnings regarding malformed messages +(! journalctl -u resolvectl-monitor.service -u reseolvectl-monitor-json.service -p warning --grep malformed) +# Verify that all queries recorded by `resolvectl monitor --json` produced a valid JSON +# with expected fields +journalctl -p info -o cat _SYSTEMD_UNIT="resolvectl-monitor-json.service" | while read -r line; do + # Check that both "question" and "answer" fields are arrays + # + # The expression is slightly more complicated due to the fact that the "answer" field is optional, + # so we need to select it only if it's present, otherwise the type == "array" check would fail + echo "$line" | jq -e '[. | .question, (select(has("answer")) | .answer) | type == "array"] | all' +done + +# Test serve stale feature and NFTSet= if nftables is installed +if command -v nft >/dev/null; then + ### Test without serve stale feature ### + NFT_FILTER_NAME=dns_port_filter + + drop_dns_outbound_traffic() { + nft add table inet $NFT_FILTER_NAME + nft add chain inet $NFT_FILTER_NAME output \{ type filter hook output priority 0 \; \} + nft add rule inet $NFT_FILTER_NAME output ip daddr 10.0.0.1 udp dport 53 drop + nft add rule inet $NFT_FILTER_NAME output ip daddr 10.0.0.1 tcp dport 53 drop + nft add rule inet $NFT_FILTER_NAME output ip6 daddr fd00:dead:beef:cafe::1 udp dport 53 drop + nft add rule inet $NFT_FILTER_NAME output ip6 daddr fd00:dead:beef:cafe::1 tcp dport 53 drop + } + + run dig stale1.unsigned.test -t A + grep -qE "NOERROR" "$RUN_OUT" + sleep 2 + drop_dns_outbound_traffic + set +e + # Make sure we give sd-resolved enough time to timeout (5-10s) before giving up + # See: https://github.com/systemd/systemd/issues/31639#issuecomment-2009152617 + run dig +tries=1 +timeout=15 stale1.unsigned.test -t A + set -eux + grep -qE "no servers could be reached" "$RUN_OUT" + nft flush ruleset + + ### Test TIMEOUT with serve stale feature ### + + mkdir -p /run/systemd/resolved.conf.d + { + echo "[Resolve]" + echo "StaleRetentionSec=1d" + } >/run/systemd/resolved.conf.d/test.conf + ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf + systemctl reload systemd-resolved.service + + run dig stale1.unsigned.test -t A + grep -qE "NOERROR" "$RUN_OUT" + sleep 2 + drop_dns_outbound_traffic + # Make sure we give sd-resolved enough time to timeout (5-10s) and serve the stale data (see above) + run dig +tries=1 +timeout=15 stale1.unsigned.test -t A + grep -qE "NOERROR" "$RUN_OUT" + grep -qE "10.0.0.112" "$RUN_OUT" + + nft flush ruleset + + ### Test NXDOMAIN with serve stale feature ### + # NXDOMAIN response should replace the cache with NXDOMAIN response + run dig stale1.unsigned.test -t A + grep -qE "NOERROR" "$RUN_OUT" + # Delete stale1 record from zone + knotc zone-begin unsigned.test + knotc zone-unset unsigned.test stale1 A + knotc zone-commit unsigned.test + knotc reload + sleep 2 + run dig stale1.unsigned.test -t A + grep -qE "NXDOMAIN" "$RUN_OUT" + + nft flush ruleset + + ### NFTSet= test + nft add table inet sd_test + nft add set inet sd_test c '{ type cgroupsv2; }' + nft add set inet sd_test u '{ typeof meta skuid; }' + nft add set inet sd_test g '{ typeof meta skgid; }' + + # service + systemd-run --unit test-nft.service --service-type=exec -p DynamicUser=yes \ + -p 'NFTSet=cgroup:inet:sd_test:c user:inet:sd_test:u group:inet:sd_test:g' sleep 10000 + run nft list set inet sd_test c + grep -qF "test-nft.service" "$RUN_OUT" + uid=$(getent passwd test-nft | cut -d':' -f3) + run nft list set inet sd_test u + grep -qF "$uid" "$RUN_OUT" + gid=$(getent passwd test-nft | cut -d':' -f4) + run nft list set inet sd_test g + grep -qF "$gid" "$RUN_OUT" + systemctl stop test-nft.service + + # scope + run systemd-run --scope -u test-nft.scope -p 'NFTSet=cgroup:inet:sd_test:c' nft list set inet sd_test c + grep -qF "test-nft.scope" "$RUN_OUT" + + mkdir -p /run/systemd/system + # socket + { + echo "[Socket]" + echo "ListenStream=12345" + echo "BindToDevice=lo" + echo "NFTSet=cgroup:inet:sd_test:c" + } >/run/systemd/system/test-nft.socket + { + echo "[Service]" + echo "ExecStart=/usr/bin/sleep 10000" + } >/run/systemd/system/test-nft.service + systemctl daemon-reload + systemctl start test-nft.socket + systemctl status test-nft.socket + run nft list set inet sd_test c + grep -qF "test-nft.socket" "$RUN_OUT" + systemctl stop test-nft.socket + rm -f /run/systemd/system/test-nft.{socket,service} + + # slice + mkdir /run/systemd/system/system.slice.d + { + echo "[Slice]" + echo "NFTSet=cgroup:inet:sd_test:c" + } >/run/systemd/system/system.slice.d/00-test-nft.conf + systemctl daemon-reload + run nft list set inet sd_test c + grep -qF "system.slice" "$RUN_OUT" + rm -rf /run/systemd/system/system.slice.d + + nft flush ruleset +else + echo "nftables is not installed. Skipped serve stale feature and NFTSet= tests." +fi + +### Test resolvectl show-server-state ### +run resolvectl show-server-state +grep -qF "10.0.0.1" "$RUN_OUT" +grep -qF "Interface" "$RUN_OUT" + +run resolvectl show-server-state --json=short +grep -qF "10.0.0.1" "$RUN_OUT" +grep -qF "Interface" "$RUN_OUT" + +run resolvectl show-server-state --json=pretty +grep -qF "10.0.0.1" "$RUN_OUT" +grep -qF "Interface" "$RUN_OUT" + +### Test resolvectl statistics ### +run resolvectl statistics +grep -qF "Transactions" "$RUN_OUT" +grep -qF "Cache" "$RUN_OUT" +grep -qF "Failure Transactions" "$RUN_OUT" +grep -qF "DNSSEC Verdicts" "$RUN_OUT" + +run resolvectl statistics --json=short +grep -qF "transactions" "$RUN_OUT" +grep -qF "cache" "$RUN_OUT" +grep -qF "dnssec" "$RUN_OUT" + +run resolvectl statistics --json=pretty +grep -qF "transactions" "$RUN_OUT" +grep -qF "cache" "$RUN_OUT" +grep -qF "dnssec" "$RUN_OUT" + +### Test resolvectl reset-statistics ### +run resolvectl reset-statistics + +run resolvectl reset-statistics --json=pretty + +run resolvectl reset-statistics --json=short + +test "$(resolvectl --json=short query -t AAAA localhost)" == '{"key":{"class":1,"type":28,"name":"localhost"},"address":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]}' +test "$(resolvectl --json=short query -t A localhost)" == '{"key":{"class":1,"type":1,"name":"localhost"},"address":[127,0,0,1]}' + +# Test ResolveRecord RR resolving via Varlink +test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":1}' --json=short | jq -rc 'del(.rrs | .[] | .ifindex)')" == '{"rrs":[{"rr":{"key":{"class":1,"type":1,"name":"localhost"},"address":[127,0,0,1]},"raw":"CWxvY2FsaG9zdAAAAQABAAAAAAAEfwAAAQ=="}],"flags":786945}' +test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":28}' --json=short | jq -rc 'del(.rrs | .[] | .ifindex)')" == '{"rrs":[{"rr":{"key":{"class":1,"type":28,"name":"localhost"},"address":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]},"raw":"CWxvY2FsaG9zdAAAHAABAAAAAAAQAAAAAAAAAAAAAAAAAAAAAQ=="}],"flags":786945}' + +# Ensure that reloading keeps the manually configured address +{ + echo "[Resolve]" + echo "DNS=8.8.8.8" +} >/run/systemd/resolved.conf.d/reload.conf +resolvectl dns dns0 1.1.1.1 +systemctl reload systemd-resolved.service +resolvectl status +resolvectl dns dns0 | grep -qF "1.1.1.1" +# For some reason piping this last command to grep fails with: +# 'resolvectl[1378]: Failed to print table: Broken pipe' +# so use an intermediate file in /tmp/ +resolvectl >/tmp/output +grep -qF "DNS Servers: 8.8.8.8" /tmp/output + +# Check if resolved exits cleanly. +restart_resolved + +touch /testok diff --git a/test/units/TEST-76-SYSCTL.sh b/test/units/TEST-76-SYSCTL.sh new file mode 100755 index 0000000..855d0ef --- /dev/null +++ b/test/units/TEST-76-SYSCTL.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +export SYSTEMD_LOG_LEVEL=debug + +echo "foo.bar=42" >/tmp/foo.conf +assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf +assert_rc 1 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf + +echo "-foo.foo=42" >/tmp/foo.conf +assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf +assert_rc 0 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf + +if ! systemd-detect-virt --quiet --container; then + ip link add hoge type dummy + udevadm wait /sys/class/net/hoge + + cat >/tmp/foo.conf </proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp + echo 0 >/proc/sys/net/ipv4/conf/hoge/bootp_relay + echo 0 >/proc/sys/net/ipv4/conf/hoge/disable_policy + + assert_rc 0 /usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/hoge /tmp/foo.conf + assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp)" "1" + assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/bootp_relay)" "1" + assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/disable_policy)" "0" +fi + +touch /testok diff --git a/test/units/TEST-78-SIGQUEUE.sh b/test/units/TEST-78-SIGQUEUE.sh new file mode 100755 index 0000000..46afd3c --- /dev/null +++ b/test/units/TEST-78-SIGQUEUE.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +if ! env --block-signal=SIGUSR1 true 2> /dev/null ; then + echo "env tool too old, can't block signals, skipping test." >&2 + echo OK >/testok + exit 0 +fi + +systemd-analyze log-level debug + +UNIT="test-sigqueue-$RANDOM.service" + +systemd-run -u "$UNIT" -p Type=notify -p DynamicUser=1 -- env --block-signal=SIGRTMIN+7 systemd-notify --exec --ready \; sleep infinity + +systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT" +systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT" +systemctl kill --kill-whom=main --kill-value=7 --signal=SIGRTMIN+7 "$UNIT" +systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT" +systemctl kill --kill-whom=main --kill-value=32 --signal=SIGRTMIN+7 "$UNIT" +systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT" + +# We simply check that six signals are queued now. There's no easy way to check +# from shell which ones those are, hence we don't check that. +P=$(systemctl show -P MainPID "$UNIT") + +test "$(grep SigQ: /proc/"$P"/status | cut -d: -f2 | cut -d/ -f1)" -eq 6 + +systemctl stop $UNIT + +systemd-analyze log-level info + +touch /testok diff --git a/test/units/TEST-79-MEMPRESS.sh b/test/units/TEST-79-MEMPRESS.sh new file mode 100755 index 0000000..2b1de20 --- /dev/null +++ b/test/units/TEST-79-MEMPRESS.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# We not just test if the file exists, but try to read from it, since if +# CONFIG_PSI_DEFAULT_DISABLED is set in the kernel the file will exist and can +# be opened, but any read()s will fail with EOPNOTSUPP, which we want to +# detect. +if ! cat /proc/pressure/memory >/dev/null ; then + echo "kernel too old, has no PSI." >&2 + echo OK >/testok + exit 0 +fi + +systemd-analyze log-level debug + +CGROUP=/sys/fs/cgroup/"$(systemctl show TEST-79-MEMPRESS.service -P ControlGroup)" +test -d "$CGROUP" + +if ! test -f "$CGROUP"/memory.pressure ; then + echo "No memory accounting/PSI delegated via cgroup, can't test." >&2 + echo OK >/testok + exit 0 +fi + +UNIT="test-mempress-$RANDOM.service" +SCRIPT="/tmp/mempress-$RANDOM.sh" + +cat >"$SCRIPT" <<'EOF' +#!/bin/bash + +set -ex + +export +id + +test -n "$MEMORY_PRESSURE_WATCH" +test "$MEMORY_PRESSURE_WATCH" != /dev/null +test -w "$MEMORY_PRESSURE_WATCH" + +ls -al "$MEMORY_PRESSURE_WATCH" + +EXPECTED="$(echo -n -e "some 123000 2000000\x00" | base64)" + +test "$EXPECTED" = "$MEMORY_PRESSURE_WRITE" + +EOF + +chmod +x "$SCRIPT" + +systemd-run \ + -u "$UNIT" \ + -p Type=exec \ + -p ProtectControlGroups=1 \ + -p DynamicUser=1 \ + -p MemoryPressureWatch=on \ + -p MemoryPressureThresholdSec=123ms \ + -p BindPaths=$SCRIPT \ + `# Make sanitizers happy when DynamicUser=1 pulls in instrumented systemd NSS modules` \ + -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \ + --wait "$SCRIPT" + +rm "$SCRIPT" + +systemd-analyze log-level info + +touch /testok diff --git a/test/units/TEST-80-NOTIFYACCESS.sh b/test/units/TEST-80-NOTIFYACCESS.sh new file mode 100755 index 0000000..97b222a --- /dev/null +++ b/test/units/TEST-80-NOTIFYACCESS.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2016 +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +mkfifo /tmp/syncfifo1 /tmp/syncfifo2 + +sync_in() { + read -r x < /tmp/syncfifo1 + test "$x" = "$1" +} + +sync_out() { + echo "$1" > /tmp/syncfifo2 +} + +export SYSTEMD_LOG_LEVEL=debug + +systemctl --no-block start notify.service + +sync_in a + +assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all" +assert_eq "$(systemctl show notify.service -p StatusText --value)" "Test starts" + +sync_out b +sync_in c + +assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "main" +assert_eq "$(systemctl show notify.service -p StatusText --value)" "Sending READY=1 in an unprivileged process" +assert_rc 3 systemctl --quiet is-active notify.service + +sync_out d +sync_in e + +systemctl --quiet is-active notify.service +assert_eq "$(systemctl show notify.service -p StatusText --value)" "OK" +assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "none" + +systemctl stop notify.service +assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all" + +rm /tmp/syncfifo1 /tmp/syncfifo2 + +# Now test basic fdstore behaviour + +MYSCRIPT="/tmp/myscript$RANDOM.sh" +cat >> "$MYSCRIPT" <<'EOF' +#!/usr/bin/env bash +set -eux +set -o pipefail +test "$FDSTORE" -eq 7 +N="/tmp/$RANDOM" +echo $RANDOM > "$N" +systemd-notify --fd=4 --fdname=quux --pid=parent 4< "$N" +rm "$N" +systemd-notify --ready +exec sleep infinity +EOF + +chmod +x "$MYSCRIPT" + +MYUNIT="myunit$RANDOM.service" +systemd-run -u "$MYUNIT" -p Type=notify -p FileDescriptorStoreMax=7 "$MYSCRIPT" + +test "$(systemd-analyze fdstore "$MYUNIT" | wc -l)" -eq 2 +systemd-analyze fdstore "$MYUNIT" --json=short +systemd-analyze fdstore "$MYUNIT" --json=short | grep -P -q '\[{"fdname":"quux","type":.*,"devno":\[.*\],"inode":.*,"rdevno":null,"path":"/tmp/.*","flags":"ro"}\]' + +systemctl stop "$MYUNIT" +rm "$MYSCRIPT" + +systemd-analyze log-level debug + +# Test fdstore pinning (this will pull in fdstore-pin.service fdstore-nopin.service) +systemctl start fdstore-pin.target + +assert_eq "$(systemctl show fdstore-pin.service -P FileDescriptorStorePreserve)" yes +assert_eq "$(systemctl show fdstore-nopin.service -P FileDescriptorStorePreserve)" restart +assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running +assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running +assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 +assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1 + +# The file descriptor store should survive service restarts +systemctl restart fdstore-pin.service fdstore-nopin.service + +assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 +assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1 +assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running +assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running + +# It should not survive the service stop plus a later start (unless pinned) +systemctl stop fdstore-pin.service fdstore-nopin.service + +assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 +assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0 +assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned +assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead + +systemctl start fdstore-pin.service fdstore-nopin.service + +assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 +assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0 +assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running +assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running + +systemctl stop fdstore-pin.service fdstore-nopin.service + +assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 +assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0 +assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned +assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead + +systemctl clean fdstore-pin.service --what=fdstore + +assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 0 +assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0 +assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead +assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead + +touch /testok diff --git a/test/units/TEST-81-GENERATORS.debug-generator.sh b/test/units/TEST-81-GENERATORS.debug-generator.sh new file mode 100755 index 0000000..ef1e205 --- /dev/null +++ b/test/units/TEST-81-GENERATORS.debug-generator.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2235 +set -eux +set -o pipefail + +# shellcheck source=test/units/generator-utils.sh +. "$(dirname "$0")/generator-utils.sh" + +GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-debug-generator" +OUT_DIR="$(mktemp -d /tmp/debug-generator.XXX)" + +at_exit() { + rm -frv "${OUT_DIR:?}" +} + +trap at_exit EXIT + +test -x "${GENERATOR_BIN:?}" + +# Potential FIXME: +# - debug-generator should gracefully handle duplicated mask/wants +# - also, handle gracefully empty mask/wants +ARGS=( + "systemd.mask=masked-no-suffix" + "systemd.mask=masked.service" + "systemd.mask=masked.socket" + "systemd.wants=wanted-no-suffix" + "systemd.wants=wanted.service" + "systemd.wants=wanted.mount" + "rd.systemd.mask=masked-initrd.service" + "rd.systemd.wants=wanted-initrd.service" +) + +# Regular (non-initrd) scenario +# +: "debug-shell: regular" +CMDLINE="ro root=/ ${ARGS[*]} rd.systemd.debug_shell" +SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null +link_eq "$OUT_DIR/early/masked.service" /dev/null +link_eq "$OUT_DIR/early/masked.socket" /dev/null +link_endswith "$OUT_DIR/early/default.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service +link_endswith "$OUT_DIR/early/default.target.wants/wanted.service" /lib/systemd/system/wanted.service +link_endswith "$OUT_DIR/early/default.target.wants/wanted.mount" /lib/systemd/system/wanted.mount +# Following stuff should be ignored, as it's prefixed with rd. +test ! -h "$OUT_DIR/early/masked-initrd.service" +test ! -h "$OUT_DIR/early/default.target.wants/wants-initrd.service" +test ! -h "$OUT_DIR/early/default.target.wants/debug-shell.service" +test ! -d "$OUT_DIR/early/initrd.target.wants" + +# Let's re-run the generator with systemd.debug_shell that should be honored +: "debug-shell: regular + systemd.debug_shell" +CMDLINE="$CMDLINE systemd.debug_shell" +SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service + +# Same thing, but with custom tty +: "debug-shell: regular + systemd.debug_shell=/dev/tty666" +CMDLINE="$CMDLINE systemd.debug_shell=/dev/tty666" +SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service +grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf" + +# Same thing, but with custom tty using systemd.default_debug_tty +: "debug-shell: regular + systemd.default_debug_tty=/dev/tty666 systemd.debug_shell=yes" +CMDLINE="$CMDLINE systemd.default_debug_tty=/dev/tty666 systemd.debug_shell=yes" +SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service +grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf" + +# Now override the default target via systemd.unit= +: "debug-shell: regular + systemd.unit=" +CMDLINE="$CMDLINE systemd.unit=my-fancy.target" +SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null +link_eq "$OUT_DIR/early/masked.service" /dev/null +link_eq "$OUT_DIR/early/masked.socket" /dev/null +link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service +link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.service" /lib/systemd/system/wanted.service +link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.mount" /lib/systemd/system/wanted.mount +link_endswith "$OUT_DIR/early/my-fancy.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service +test ! -d "$OUT_DIR/early/default.target.wants" + + +# Initrd scenario +: "debug-shell: initrd" +CMDLINE="ro root=/ ${ARGS[*]} systemd.debug_shell" +SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null +link_endswith "$OUT_DIR/early/initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service +# The non-initrd stuff (i.e. without the rd. suffix) should be ignored in +# this case +test ! -h "$OUT_DIR/early/masked-no-suffix.service" +test ! -h "$OUT_DIR/early/masked.service" +test ! -h "$OUT_DIR/early/masked.socket" +test ! -h "$OUT_DIR/early/initrd.target.wants/debug-shell.service" +test ! -d "$OUT_DIR/early/default.target.wants" + +# Again, but with rd.systemd.debug_shell +: "debug-shell: initrd + rd.systemd.debug_shell" +CMDLINE="$CMDLINE rd.systemd.debug_shell" +SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +link_endswith "$OUT_DIR/early/initrd.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service + +# Override the default target +: "debug-shell: initrd + rd.systemd.unit" +CMDLINE="$CMDLINE rd.systemd.unit=my-fancy-initrd.target" +SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null +link_endswith "$OUT_DIR/early/my-fancy-initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service +test ! -d "$OUT_DIR/early/initrd.target.wants" diff --git a/test/units/TEST-81-GENERATORS.environment-d-generator.sh b/test/units/TEST-81-GENERATORS.environment-d-generator.sh new file mode 100755 index 0000000..5bc3978 --- /dev/null +++ b/test/units/TEST-81-GENERATORS.environment-d-generator.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2235 +set -eux +set -o pipefail + +# shellcheck source=test/units/generator-utils.sh +. "$(dirname "$0")/generator-utils.sh" + +GENERATOR_BIN="/usr/lib/systemd/user-environment-generators/30-systemd-environment-d-generator" +CONFIG_FILE="/run/environment.d/99-test.conf" +OUT_FILE="$(mktemp)" + +at_exit() { + set +e + rm -frv "${CONFIG_FILE:?}" "${OUT_FILE:?}" + systemctl -M testuser@.host --user daemon-reload +} + +trap at_exit EXIT + +test -x "${GENERATOR_BIN:?}" +mkdir -p /run/environment.d/ + +cat >"$CONFIG_FILE" <"$OUT_FILE" + +# Check if the generator is correctly called in a user session +systemctl -M testuser@.host --user daemon-reload +systemctl -M testuser@.host --user show-environment | tee "$OUT_FILE" +check_environment "$OUT_FILE" + +(! "$GENERATOR_BIN" foo) diff --git a/test/units/TEST-81-GENERATORS.fstab-generator.sh b/test/units/TEST-81-GENERATORS.fstab-generator.sh new file mode 100755 index 0000000..0c1b27e --- /dev/null +++ b/test/units/TEST-81-GENERATORS.fstab-generator.sh @@ -0,0 +1,407 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2235,SC2233 +set -eux +set -o pipefail + +# shellcheck source=test/units/generator-utils.sh +. "$(dirname "$0")/generator-utils.sh" + +GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-fstab-generator" +NETWORK_FS_RX="^(afs|ceph|cifs|gfs|gfs2|ncp|ncpfs|nfs|nfs4|ocfs2|orangefs|pvfs2|smb3|smbfs|davfs|glusterfs|lustre|sshfs)$" +OUT_DIR="$(mktemp -d /tmp/fstab-generator.XXX)" +FSTAB="$(mktemp)" + +at_exit() { + mountpoint -q /proc/cmdline && umount /proc/cmdline + rm -fr "${OUT_DIR:?}" "${FSTAB:?}" +} + +trap at_exit EXIT + +test -x "${GENERATOR_BIN:?}" + +FSTAB_GENERAL=( + # Valid entries + "/dev/test2 /nofail ext4 nofail 0 0" + "/dev/test3 /regular btrfs defaults 0 0" + "/dev/test4 /x-systemd.requires xfs x-systemd.requires=foo.service 0 0" + "/dev/test5 /x-systemd.before-after xfs x-systemd.before=foo.service,x-systemd.after=bar.mount 0 0" + "/dev/test6 /x-systemd.wanted-required-by xfs x-systemd.wanted-by=foo.service,x-systemd.required-by=bar.device 0 0" + "/dev/test7 /x-systemd.requires-mounts-for xfs x-systemd.requires-mounts-for=/foo/bar/baz 0 0" + "/dev/test8 /x-systemd.automount-idle-timeout vfat x-systemd.automount,x-systemd.idle-timeout=50s 0 0" + "/dev/test9 /x-systemd.makefs xfs x-systemd.makefs 0 0" + "/dev/test10 /x-systemd.growfs xfs x-systemd.growfs 0 0" + "/dev/test11 /_netdev ext4 defaults,_netdev 0 0" + "/dev/test12 /_rwonly ext4 x-systemd.rw-only 0 0" + "/dev/test13 /chaos1 zfs x-systemd.rw-only,x-systemd.requires=hello.service,x-systemd.after=my.device 0 0" + "/dev/test14 /chaos2 zfs x.systemd.wanted-by=foo.service,x-systemd.growfs,x-systemd.makefs 0 0" + "/dev/test15 /fstype/auto auto defaults 0 0" + "/dev/test16 /fsck/me ext4 defaults 0 1" + "/dev/test17 /also/fsck/me ext4 defaults,x-systemd.requires-mounts-for=/var/lib/foo 0 99" + "/dev/test18 /swap swap defaults 0 0" + "/dev/test19 /swap/makefs swap defaults,x-systemd.makefs 0 0" + "/dev/test20 /var xfs defaults,x-systemd.device-timeout=1h 0 0" + "/dev/test21 /usr ext4 defaults 0 1" + "/dev/test22 /initrd/mount ext2 defaults,x-systemd.rw-only,x-initrd.mount 0 1" + "/dev/test23 /initrd/mount/nofail ext3 defaults,nofail,x-initrd.mount 0 1" + "/dev/test24 /initrd/mount/deps ext4 x-initrd.mount,x-systemd.before=early.service,x-systemd.after=late.service 0 1" + + # Incomplete, but valid entries + "/dev/incomplete1 /incomplete1" + "/dev/incomplete2 /incomplete2 ext4" + "/dev/incomplete3 /incomplete3 ext4 defaults" + "/dev/incomplete4 /incomplete4 ext4 defaults 0" + + # Remote filesystems + "/dev/remote1 /nfs nfs bg 0 0" + "/dev/remote2 /nfs4 nfs4 bg 0 0" + "bar.tld:/store /remote/storage nfs ro,x-systemd.wanted-by=store.service 0 0" + "user@host.tld:/remote/dir /remote/top-secret sshfs rw,x-systemd.before=naughty.service 0 0" + "foo.tld:/hello /hello/world ceph defaults 0 0" + "//192.168.0.1/storage /cifs-storage cifs automount,nofail 0 0" +) + +FSTAB_GENERAL_ROOT=( + # rootfs with bunch of options we should ignore and fsck enabled + "/dev/test1 / ext4 noauto,nofail,x-systemd.automount,x-systemd.wanted-by=foo,x-systemd.required-by=bar 0 1" + "${FSTAB_GENERAL[@]}" +) + +FSTAB_MINIMAL=( + "/dev/loop1 /foo/bar ext3 defaults 0 0" +) + +FSTAB_DUPLICATE=( + "/dev/dup1 / ext4 defaults 0 1" + "/dev/dup2 / ext4 defaults,x-systemd.requires=foo.mount 0 2" +) + +FSTAB_INVALID=( + # Ignored entries + "/dev/ignored1 /sys/fs/cgroup/foo ext4 defaults 0 0" + "/dev/ignored2 /sys/fs/selinux ext4 defaults 0 0" + "/dev/ignored3 /dev/console ext4 defaults 0 0" + "/dev/ignored4 /proc/kmsg ext4 defaults 0 0" + "/dev/ignored5 /proc/sys ext4 defaults 0 0" + "/dev/ignored6 /proc/sys/kernel/random/boot_id ext4 defaults 0 0" + "/dev/ignored7 /run/host ext4 defaults 0 0" + "/dev/ignored8 /run/host/foo ext4 defaults 0 0" + "/dev/ignored9 /autofs autofs defaults 0 0" + "/dev/invalid1 not-a-path ext4 defaults 0 0" + "" + "/dev/invalid1" + " " + "\\" + "$" +) + +check_fstab_mount_units() { + local what where fstype opts passno unit + local item opt split_options filtered_options supp service device arg + local array_name="${1:?}" + local out_dir="${2:?}/normal" + # Get a reference to the array from its name + local -n fstab_entries="$array_name" + + # Running the checks in a container is pretty much useless, since we don't + # generate any mounts, but don't skip the whole test to test the "skip" + # paths as well + in_container && return 0 + + for item in "${fstab_entries[@]}"; do + # Don't use a pipe here, as it would make the variables out of scope + read -r what where fstype opts _ passno <<< "$item" + + # Skip non-initrd mounts in initrd + if in_initrd_host && ! [[ "$opts" =~ x-initrd.mount ]]; then + continue + fi + + if [[ "$fstype" == swap ]]; then + unit="$(systemd-escape --suffix=swap --path "${what:?}")" + cat "$out_dir/$unit" + + grep -qE "^What=$what$" "$out_dir/$unit" + if [[ "$opts" != defaults ]]; then + grep -qE "^Options=$opts$" "$out_dir/$unit" + fi + + if [[ "$opts" =~ x-systemd.makefs ]]; then + service="$(systemd-escape --template=systemd-mkswap@.service --path "$what")" + test -e "$out_dir/$service" + fi + + continue + fi + + # If we're parsing host's fstab in initrd, prefix all mount targets + # with /sysroot + in_initrd_host && where="/sysroot${where:?}" + unit="$(systemd-escape --suffix=mount --path "${where:?}")" + cat "$out_dir/$unit" + + # Check the general stuff + grep -qE "^What=$what$" "$out_dir/$unit" + grep -qE "^Where=$where$" "$out_dir/$unit" + if [[ -n "$fstype" ]] && [[ "$fstype" != auto ]]; then + grep -qE "^Type=$fstype$" "$out_dir/$unit" + fi + if [[ -n "$opts" ]] && [[ "$opts" != defaults ]]; then + # Some options are not propagated to the generated unit + if [[ "$where" == / || "$where" == /usr ]]; then + filtered_options="$(opt_filter "$opts" "(noauto|nofail|x-systemd.(wanted-by=|required-by=|automount|device-timeout=))")" + else + filtered_options="$(opt_filter "$opts" "^x-systemd.device-timeout=")" + fi + + if [[ "${filtered_options[*]}" != defaults ]]; then + grep -qE "^Options=.*$filtered_options.*$" "$out_dir/$unit" + fi + fi + + if ! [[ "$opts" =~ (noauto|x-systemd.(wanted-by=|required-by=|automount)) ]]; then + # We don't create the Requires=/Wants= symlinks for noauto/automount mounts + # and for mounts that use x-systemd.wanted-by=/required-by= + if in_initrd_host; then + if [[ "$where" == / ]] || ! [[ "$opts" =~ nofail ]]; then + link_eq "$out_dir/initrd-fs.target.requires/$unit" "../$unit" + else + link_eq "$out_dir/initrd-fs.target.wants/$unit" "../$unit" + fi + elif [[ "$fstype" =~ $NETWORK_FS_RX || "$opts" =~ _netdev ]]; then + # Units with network filesystems should have a Requires= dependency + # on the remote-fs.target, unless they use nofail or are an nfs "bg" + # mounts, in which case the dependency is downgraded to Wants= + if [[ "$opts" =~ nofail ]] || [[ "$fstype" =~ ^(nfs|nfs4) && "$opts" =~ bg ]]; then + link_eq "$out_dir/remote-fs.target.wants/$unit" "../$unit" + else + link_eq "$out_dir/remote-fs.target.requires/$unit" "../$unit" + fi + else + # Similarly, local filesystems should have a Requires= dependency on + # the local-fs.target, unless they use nofail, in which case the + # dependency is downgraded to Wants=. Rootfs is a special case, + # since we always ignore nofail there + if [[ "$where" == / ]] || ! [[ "$opts" =~ nofail ]]; then + link_eq "$out_dir/local-fs.target.requires/$unit" "../$unit" + else + link_eq "$out_dir/local-fs.target.wants/$unit" "../$unit" + fi + fi + fi + + if [[ "${passno:=0}" -ne 0 ]]; then + # Generate systemd-fsck@.service dependencies, if applicable + if in_initrd && [[ "$where" == / || "$where" == /usr ]]; then + continue + fi + + if [[ "$where" == / ]]; then + link_endswith "$out_dir/local-fs.target.wants/systemd-fsck-root.service" "/lib/systemd/system/systemd-fsck-root.service" + else + service="$(systemd-escape --template=systemd-fsck@.service --path "$what")" + grep -qE "^After=$service$" "$out_dir/$unit" + if [[ "$where" == /usr ]]; then + grep -qE "^Wants=$service$" "$out_dir/$unit" + else + grep -qE "^Requires=$service$" "$out_dir/$unit" + fi + fi + fi + + # Check various x-systemd options + # + # First, split them into an array to make splitting them even further + # easier + IFS="," read -ra split_options <<< "$opts" + # and process them one by one. + # + # Note: the "machinery" below might (and probably does) miss some + # combinations of supported options, so tread carefully + for opt in "${split_options[@]}"; do + if [[ "$opt" =~ ^x-systemd.requires= ]]; then + service="$(opt_get_arg "$opt")" + grep -qE "^Requires=$service$" "$out_dir/$unit" + grep -qE "^After=$service$" "$out_dir/$unit" + elif [[ "$opt" =~ ^x-systemd.before= ]]; then + service="$(opt_get_arg "$opt")" + grep -qE "^Before=$service$" "$out_dir/$unit" + elif [[ "$opt" =~ ^x-systemd.after= ]]; then + service="$(opt_get_arg "$opt")" + grep -qE "^After=$service$" "$out_dir/$unit" + elif [[ "$opt" =~ ^x-systemd.wanted-by= ]]; then + service="$(opt_get_arg "$opt")" + if [[ "$where" == / ]]; then + # This option is ignored for rootfs mounts + (! link_eq "$out_dir/$service.wants/$unit" "../$unit") + else + link_eq "$out_dir/$service.wants/$unit" "../$unit" + fi + elif [[ "$opt" =~ ^x-systemd.required-by= ]]; then + service="$(opt_get_arg "$opt")" + if [[ "$where" == / ]]; then + # This option is ignored for rootfs mounts + (! link_eq "$out_dir/$service.requires/$unit" "../$unit") + else + link_eq "$out_dir/$service.requires/$unit" "../$unit" + fi + elif [[ "$opt" =~ ^x-systemd.requires-mounts-for= ]]; then + arg="$(opt_get_arg "$opt")" + grep -qE "^RequiresMountsFor=$arg$" "$out_dir/$unit" + elif [[ "$opt" == x-systemd.device-bound ]]; then + # This is implied for fstab mounts + : + elif [[ "$opt" == x-systemd.automount ]]; then + # The $unit should have an accompanying automount unit + supp="$(systemd-escape --suffix=automount --path "$where")" + if [[ "$where" == / ]]; then + # This option is ignored for rootfs mounts + test ! -e "$out_dir/$supp" + (! link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp") + else + test -e "$out_dir/$supp" + link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp" + fi + elif [[ "$opt" =~ ^x-systemd.idle-timeout= ]]; then + # The timeout applies to the automount unit, not the original + # mount one + arg="$(opt_get_arg "$opt")" + supp="$(systemd-escape --suffix=automount --path "$where")" + grep -qE "^TimeoutIdleSec=$arg$" "$out_dir/$supp" + elif [[ "$opt" =~ ^x-systemd.device-timeout= ]]; then + arg="$(opt_get_arg "$opt")" + device="$(systemd-escape --suffix=device --path "$what")" + grep -qE "^JobRunningTimeoutSec=$arg$" "$out_dir/${device}.d/50-device-timeout.conf" + elif [[ "$opt" == x-systemd.makefs ]]; then + service="$(systemd-escape --template=systemd-makefs@.service --path "$what")" + test -e "$out_dir/$service" + link_eq "$out_dir/${unit}.requires/$service" "../$service" + elif [[ "$opt" == x-systemd.rw-only ]]; then + grep -qE "^ReadWriteOnly=yes$" "$out_dir/$unit" + elif [[ "$opt" == x-systemd.growfs ]]; then + service="$(systemd-escape --template=systemd-growfs@.service --path "$where")" + link_endswith "$out_dir/${unit}.wants/$service" "/lib/systemd/system/systemd-growfs@.service" + elif [[ "$opt" == bg ]] && [[ "$fstype" =~ ^(nfs|nfs4)$ ]]; then + # We "convert" nfs bg mounts to fg, so we can do the job-control + # ourselves + grep -qE "^Options=.*\bx-systemd.mount-timeout=infinity\b" "$out_dir/$unit" + grep -qE "^Options=.*\bfg\b.*" "$out_dir/$unit" + elif [[ "$opt" =~ ^x-systemd\. ]]; then + echo >&2 "Unhandled mount option: $opt" + exit 1 + fi + done + done +} + +: "fstab-generator: regular" +printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB" +cat "$FSTAB" +SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +check_fstab_mount_units FSTAB_GENERAL_ROOT "$OUT_DIR" + +# Skip the rest when running in a container, as it makes little sense to check +# initrd-related stuff there and fstab-generator might have a bit strange +# behavior during certain tests, like https://github.com/systemd/systemd/issues/27156 +if in_container; then + echo "Running in a container, skipping the rest of the fstab-generator tests..." + exit 0 +fi + +# In this mode we treat the entries as "regular" ones +: "fstab-generator: initrd - initrd fstab" +printf "%s\n" "${FSTAB_GENERAL[@]}" >"$FSTAB" +cat "$FSTAB" +SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR" +SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_SYSROOT_FSTAB=/dev/null check_fstab_mount_units FSTAB_GENERAL "$OUT_DIR" + +# In this mode we prefix the mount target with /sysroot and ignore all mounts +# that don't have the x-initrd.mount flag +: "fstab-generator: initrd - host fstab" +printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB" +cat "$FSTAB" +SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_GENERAL_ROOT "$OUT_DIR" + +# Check the default stuff that we (almost) always create in initrd +: "fstab-generator: initrd default" +SYSTEMD_PROC_CMDLINE="root=/dev/sda2" SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR" +test -e "$OUT_DIR/normal/sysroot.mount" +test -e "$OUT_DIR/normal/systemd-fsck-root.service" +link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount" +link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount" + +: "fstab-generator: run as systemd-sysroot-fstab-check in initrd" +ln -svf "$GENERATOR_BIN" /tmp/systemd-sysroot-fstab-check +(! /tmp/systemd-sysroot-fstab-check foo) +(! SYSTEMD_IN_INITRD=0 /tmp/systemd-sysroot-fstab-check) +printf "%s\n" "${FSTAB_GENERAL[@]}" >"$FSTAB" +SYSTEMD_IN_INITRD=1 SYSTEMD_SYSROOT_FSTAB="$FSTAB" /tmp/systemd-sysroot-fstab-check + +: "fstab-generator: duplicate" +printf "%s\n" "${FSTAB_DUPLICATE[@]}" >"$FSTAB" +cat "$FSTAB" +(! SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR") + +: "fstab-generator: invalid" +printf "%s\n" "${FSTAB_INVALID[@]}" >"$FSTAB" +cat "$FSTAB" +# Don't care about the exit code here +SYSTEMD_PROC_CMDLINE="" SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR" || : +# No mounts should get created here +[[ "$(find "$OUT_DIR" -name "*.mount" | wc -l)" -eq 0 ]] + +: "fstab-generator: kernel args - fstab=0" +printf "%s\n" "${FSTAB_MINIMAL[@]}" >"$FSTAB" +SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +(! SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR") +SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +(! SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR") + +: "fstab-generator: kernel args - rd.fstab=0" +printf "%s\n" "${FSTAB_MINIMAL[@]}" >"$FSTAB" +SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR" +SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +(! SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR") + +: "fstab-generator: kernel args - systemd.swap=0" +printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB" +cat "$FSTAB" +SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="systemd.swap=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +# No swap units should get created here +[[ "$(find "$OUT_DIR" -name "*.swap" | wc -l)" -eq 0 ]] + +# Possible TODO +# - combine the rootfs & usrfs arguments and mix them with fstab entries +# - systemd.volatile= +: "fstab-generator: kernel args - root= + rootfstype= + rootflags=" +# shellcheck disable=SC2034 +EXPECTED_FSTAB=( + "/dev/disk/by-label/rootfs / ext4 noexec,ro 0 1" +) +CMDLINE="root=LABEL=rootfs rootfstype=ext4 rootflags=noexec" +SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +# The /proc/cmdline here is a dummy value to tell the in_initrd_host() function +# we're parsing host's fstab, but it's all on the kernel cmdline instead +SYSTEMD_IN_INITRD=1 SYSTEMD_SYSROOT_FSTAB=/proc/cmdline check_fstab_mount_units EXPECTED_FSTAB "$OUT_DIR" + +# This is a very basic sanity test that involves manual checks, since adding it +# to the check_fstab_mount_units() function would make it way too complex +# (yet another possible TODO) +: "fstab-generator: kernel args - mount.usr= + mount.usrfstype= + mount.usrflags=" +CMDLINE="mount.usr=UUID=be780f43-8803-4a76-9732-02ceda6e9808 mount.usrfstype=ext4 mount.usrflags=noexec,nodev" +SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +cat "$OUT_DIR/normal/sysroot-usr.mount" "$OUT_DIR/normal/sysusr-usr.mount" +# The general idea here is to mount the device to /sysusr/usr and then +# bind-mount /sysusr/usr to /sysroot/usr +grep -qE "^What=/dev/disk/by-uuid/be780f43-8803-4a76-9732-02ceda6e9808$" "$OUT_DIR/normal/sysusr-usr.mount" +grep -qE "^Where=/sysusr/usr$" "$OUT_DIR/normal/sysusr-usr.mount" +grep -qE "^Type=ext4$" "$OUT_DIR/normal/sysusr-usr.mount" +grep -qE "^Options=noexec,nodev,ro$" "$OUT_DIR/normal/sysusr-usr.mount" +link_eq "$OUT_DIR/normal/initrd-usr-fs.target.requires/sysusr-usr.mount" "../sysusr-usr.mount" +grep -qE "^What=/sysusr/usr$" "$OUT_DIR/normal/sysroot-usr.mount" +grep -qE "^Where=/sysroot/usr$" "$OUT_DIR/normal/sysroot-usr.mount" +grep -qE "^Options=bind$" "$OUT_DIR/normal/sysroot-usr.mount" +link_eq "$OUT_DIR/normal/initrd-fs.target.requires/sysroot-usr.mount" "../sysroot-usr.mount" diff --git a/test/units/TEST-81-GENERATORS.getty-generator.sh b/test/units/TEST-81-GENERATORS.getty-generator.sh new file mode 100755 index 0000000..d1dd22c --- /dev/null +++ b/test/units/TEST-81-GENERATORS.getty-generator.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2235 +set -eux +set -o pipefail +# Disable history expansion so we don't have to escape ! in strings below +set +o histexpand + +# shellcheck source=test/units/generator-utils.sh +. "$(dirname "$0")/generator-utils.sh" + +GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-getty-generator" +OUT_DIR="$(mktemp -d /tmp/getty-generator.XXX)" + +at_exit() { + rm -frv "${OUT_DIR:?}" +} + +trap at_exit EXIT + +test -x "${GENERATOR_BIN:?}" + +if in_container; then + # Do a limited test in a container, as writing to /dev is usually restrited + : "getty-generator: \$container_ttys env (container)" + # In a container we allow only /dev/pts/* ptys + PID1_ENVIRON="container_ttys=tty0 pts/0 /dev/tty0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" + + # console-getty.service is always pulled in in containers + link_endswith "$OUT_DIR/normal/getty.target.wants/console-getty.service" "/lib/systemd/system/console-getty.service" + link_endswith "$OUT_DIR/normal/getty.target.wants/container-getty@0.service" "/lib/systemd/system/container-getty@.service" + test ! -e "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service" + test ! -h "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service" + + exit 0 +fi + +DUMMY_ACTIVE_CONSOLES=( + "hvc99" + "xvc99" + "hvsi99" + "sclp_line99" + "ttysclp99" + "3270!tty99" + "dummy99" +) +DUMMY_INACTIVE_CONSOLES=( + "inactive99" + "xvc199" +) +DUMMY_CONSOLES=( + "${DUMMY_ACTIVE_CONSOLES[@]}" + "${DUMMY_INACTIVE_CONSOLES[@]}" +) +# Create a bunch of dummy consoles +for console in "${DUMMY_CONSOLES[@]}"; do + mknod "/dev/$console" c 4 0 +done +# Sneak in one "not-a-tty" console +touch /dev/notatty99 +# Temporarily replace /sys/class/tty/console/active with our list of dummy +# consoles so getty-generator can process them +echo -ne "${DUMMY_ACTIVE_CONSOLES[@]}" /dev/notatty99 >/tmp/dummy-active-consoles +mount -v --bind /tmp/dummy-active-consoles /sys/class/tty/console/active + +: "getty-generator: no arguments" +# Sneak in an invalid value for $SYSTEMD_GETTY_AUTO to test things out +PID1_ENVIRON="SYSTEMD_GETTY_AUTO=foo" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +for console in "${DUMMY_ACTIVE_CONSOLES[@]}"; do + unit="$(systemd-escape --template serial-getty@.service "$console")" + link_endswith "$OUT_DIR/normal/getty.target.wants/$unit" "/lib/systemd/system/serial-getty@.service" +done +for console in "${DUMMY_INACTIVE_CONSOLES[@]}" /dev/notatty99; do + unit="$(systemd-escape --template serial-getty@.service "$console")" + test ! -e "$OUT_DIR/normal/getty.target.wants/$unit" + test ! -h "$OUT_DIR/normal/getty.target.wants/$unit" +done + +: "getty-generator: systemd.getty_auto=0 on kernel cmdline" +SYSTEMD_PROC_CMDLINE="systemd.getty_auto=foo systemd.getty_auto=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]] + +: "getty-generator: SYSTEMD_GETTY_AUTO=0 in PID1's environment" +PID1_ENVIRON="SYSTEMD_GETTY_AUTO=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]] + +# Cleanup +umount /sys/class/tty/console/active --lazy +rm -f "${DUMMY_CONSOLES[@]/#//dev/}" /dev/notatty99 diff --git a/test/units/TEST-81-GENERATORS.run-generator.sh b/test/units/TEST-81-GENERATORS.run-generator.sh new file mode 100755 index 0000000..9bd74ef --- /dev/null +++ b/test/units/TEST-81-GENERATORS.run-generator.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2235 +set -eux +set -o pipefail + +# shellcheck source=test/units/generator-utils.sh +. "$(dirname "$0")/generator-utils.sh" + +GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-run-generator" +OUT_DIR="$(mktemp -d /tmp/run-generator.XXX)" + +at_exit() { + rm -frv "${OUT_DIR:?}" +} + +trap at_exit EXIT + +test -x "${GENERATOR_BIN:?}" + +check_kernel_cmdline_target() { + local out_dir="${1:?}/normal" + + cat "$out_dir/kernel-command-line.target" + grep -qE "^Requires=kernel-command-line.service$" "$out_dir/kernel-command-line.target" + grep -qE "^After=kernel-command-line.service$" "$out_dir/kernel-command-line.target" + + link_eq "$out_dir/default.target" "kernel-command-line.target" +} + +: "run-generator: empty cmdline" +SYSTEMD_PROC_CMDLINE="" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]] + +: "run-generator: single command" +CMDLINE="systemd.run='echo hello world'" +SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +check_kernel_cmdline_target "$OUT_DIR" +UNIT="$OUT_DIR/normal/kernel-command-line.service" +cat "$UNIT" +systemd-analyze verify --man=no --recursive-errors=no "$UNIT" +grep -qE "^SuccessAction=exit$" "$UNIT" +grep -qE "^FailureAction=exit$" "$UNIT" +grep -qE "^ExecStart=echo hello world$" "$UNIT" + +: "run-generator: multiple commands + success/failure actions" +ARGS=( + # These should be ignored + "systemd.run" + "systemd.run_success_action" + "systemd.run_failure_action" + + # Set actions which we will overwrite later + "systemd.run_success_action=" + "systemd.run_failure_action=" + + "systemd.run=/bin/false" + "systemd.run=" + "systemd.run=/bin/true" + "systemd.run='echo this is a long string'" + + "systemd.run_success_action=reboot" + "systemd.run_failure_action=poweroff-force" +) +CMDLINE="${ARGS[*]}" +SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" +check_kernel_cmdline_target "$OUT_DIR" +UNIT="$OUT_DIR/normal/kernel-command-line.service" +cat "$UNIT" +systemd-analyze verify --man=no --recursive-errors=no "$UNIT" +grep -qE "^SuccessAction=reboot$" "$UNIT" +grep -qE "^FailureAction=poweroff-force$" "$UNIT" +grep -qE "^ExecStart=/bin/false$" "$UNIT" +grep -qE "^ExecStart=$" "$UNIT" +grep -qE "^ExecStart=/bin/true$" "$UNIT" +grep -qE "^ExecStart=echo this is a long string$" "$UNIT" diff --git a/test/units/TEST-81-GENERATORS.sh b/test/units/TEST-81-GENERATORS.sh new file mode 100755 index 0000000..9c2a033 --- /dev/null +++ b/test/units/TEST-81-GENERATORS.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + +run_subtests + +touch /testok diff --git a/test/units/TEST-81-GENERATORS.system-update-generator.sh b/test/units/TEST-81-GENERATORS.system-update-generator.sh new file mode 100755 index 0000000..8ee1fee --- /dev/null +++ b/test/units/TEST-81-GENERATORS.system-update-generator.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2235 +set -eux +set -o pipefail + +# shellcheck source=test/units/generator-utils.sh +. "$(dirname "$0")/generator-utils.sh" + +GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-system-update-generator" +OUT_DIR="$(mktemp -d /tmp/system-update-generator-generator.XXX)" + +at_exit() { + rm -frv "${OUT_DIR:?}" /system-update +} + +trap at_exit EXIT + +test -x "${GENERATOR_BIN:?}" + +rm -f /system-update + +: "system-update-generator: no /system-update flag" +run_and_list "$GENERATOR_BIN" "$OUT_DIR" +[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]] + +: "system-update-generator: with /system-update flag" +touch /system-update +run_and_list "$GENERATOR_BIN" "$OUT_DIR" +link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target" + +: "system-update-generator: kernel cmdline warnings" +# We should warn if the default target is overridden on the kernel cmdline +# by a runlevel or systemd.unit=, but still generate the symlink +SYSTEMD_PROC_CMDLINE="systemd.unit=foo.bar 3" run_and_list "$GENERATOR_BIN" "$OUT_DIR" |& tee /tmp/system-update-generator.log +link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target" +grep -qE "Offline system update overridden .* systemd.unit=" /tmp/system-update-generator.log +grep -qE "Offline system update overridden .* runlevel" /tmp/system-update-generator.log diff --git a/test/units/TEST-82-SOFTREBOOT.sh b/test/units/TEST-82-SOFTREBOOT.sh new file mode 100755 index 0000000..f86bc92 --- /dev/null +++ b/test/units/TEST-82-SOFTREBOOT.sh @@ -0,0 +1,268 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +at_exit() { + # Since the soft-reboot drops the enqueued end.service, we won't shutdown + # the test VM if the test fails and have to wait for the watchdog to kill + # us (which may take quite a long time). Let's just forcibly kill the machine + # instead to save CI resources. + if [[ $? -ne 0 ]]; then + echo >&2 "Test failed, shutting down the machine..." + systemctl poweroff -ff + fi +} + +trap at_exit EXIT + +systemd-analyze log-level debug + +export SYSTEMD_LOG_LEVEL=debug + +if [ -f /run/TEST-82-SOFTREBOOT.touch3 ]; then + echo "This is the fourth boot!" + systemd-notify --status="Fourth Boot" + + test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 3 + + rm /run/TEST-82-SOFTREBOOT.touch3 + mount + rmdir /original-root /run/nextroot + + # Check that the fdstore entry still exists + test "$LISTEN_FDS" -eq 3 + read -r x <&5 + test "$x" = "oinkoink" + + # Check that the surviving services are still around + test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active" + test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active" + test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active" + test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active" + + [[ ! -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/TEST-82-SOFTREBOOT.touch2 ]; then + echo "This is the third boot!" + systemd-notify --status="Third Boot" + + test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 2 + + rm /run/TEST-82-SOFTREBOOT.touch2 + + # Check that the fdstore entry still exists + test "$LISTEN_FDS" -eq 2 + read -r x <&4 + test "$x" = "miaumiau" + + # Upload another entry + T="/dev/shm/fdstore.$RANDOM" + echo "oinkoink" >"$T" + systemd-notify --fd=3 --pid=parent 3<"$T" + rm "$T" + + # Check that the surviving services are still around + test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active" + test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active" + test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active" + test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active" + + # Test that we really are in the new overlayfs root fs + read -r x "$T" + systemd-notify --fd=3 --pid=parent 3<"$T" + rm "$T" + + # Check that the surviving services are still around + test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive.service)" = "active" + test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-survive-argv.service)" = "active" + test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive-sigterm.service)" != "active" + test "$(systemctl show -P ActiveState TEST-82-SOFTREBOOT-nosurvive.service)" != "active" + + # This time we test the /run/nextroot/ root switching logic. (We synthesize a new rootfs from the old via overlayfs) + mkdir -p /run/nextroot /tmp/nextroot-lower /original-root + mount -t tmpfs tmpfs /tmp/nextroot-lower + echo miep >/tmp/nextroot-lower/lower + + # Copy os-release away, so that we can manipulate it and check that it is updated in the propagate + # directory across soft reboots. Try to cover corner cases by truncating it. + mkdir -p /tmp/nextroot-lower/usr/lib + grep ID /etc/os-release >/tmp/nextroot-lower/usr/lib/os-release + echo MARKER=1 >>/tmp/nextroot-lower/usr/lib/os-release + cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release + (! grep -q MARKER=1 /etc/os-release) + + mount -t overlay nextroot /run/nextroot -o lowerdir=/tmp/nextroot-lower:/,ro + + # Bind our current root into the target so that we later can return to it + mount --bind / /run/nextroot/original-root + + # Restart the unit that is not supposed to survive + systemd-run --collect --service-type=exec --unit=TEST-82-SOFTREBOOT-nosurvive.service sleep infinity + + # Now ensure there are no naming clashes and a bunch of transient units all succeed + for _ in $(seq 1 25); do + systemd-run --wait true + done + + # Now issue the soft reboot. We should be right back soon. Given /run/nextroot exists, we should + # automatically do a softreboot instead of normal reboot. + touch /run/TEST-82-SOFTREBOOT.touch2 + systemctl --no-block reboot + + # Now block until the soft-boot killing spree kills us + exec sleep infinity +else + # This is the first boot + systemd-notify --status="First Boot" + + test "$(busctl -j get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SoftRebootsCount | jq -r '.data')" -eq 0 + + # Let's upload an fd to the fdstore, so that we can verify fdstore passing works correctly + T="/dev/shm/fdstore.$RANDOM" + echo "wuffwuff" >"$T" + systemd-notify --fd=3 --pid=parent 3<"$T" + rm "$T" + + survive_sigterm="/dev/shm/survive-sigterm-$RANDOM.sh" + cat >"$survive_sigterm" <"$survive_argv" </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-..sh) +# Run all subtests (i.e. files named as $TESTNAME..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-01.sh b/test/units/testsuite-01.sh deleted file mode 100755 index 870b62d..0000000 --- a/test/units/testsuite-01.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# Check if the colored --version output behaves correctly -SYSTEMD_COLORS=256 systemctl --version - -# Check if we properly differentiate between a full systemd setup and a "light" -# version of it that's done during daemon-reexec -# -# See: https://github.com/systemd/systemd/issues/27106 -if systemd-detect-virt -q --container; then - # We initialize /run/systemd/container only during a full setup - test -e /run/systemd/container - cp -afv /run/systemd/container /tmp/container - rm -fv /run/systemd/container - systemctl daemon-reexec - test ! -e /run/systemd/container - cp -afv /tmp/container /run/systemd/container -else - # We bring the loopback netdev up only during a full setup, so it should - # not get brought back up during reexec if we disable it beforehand - [[ "$(ip -o link show lo)" =~ LOOPBACK,UP ]] - ip link set lo down - [[ "$(ip -o link show lo)" =~ state\ DOWN ]] - systemctl daemon-reexec - [[ "$(ip -o link show lo)" =~ state\ DOWN ]] - ip link set lo up - - # We also disable coredumps only during a full setup - sysctl -w kernel.core_pattern=dont-overwrite-me - systemctl daemon-reexec - diff <(echo dont-overwrite-me) <(sysctl --values kernel.core_pattern) -fi - -# Collect failed units & do one daemon-reload to a basic sanity check -systemctl --state=failed --no-legend --no-pager | tee /failed -test ! -s /failed -systemctl daemon-reload - -# Check that the early setup is actually skipped on reexec. -# If the early setup is done more than once, then several timestamps, -# e.g. SecurityStartTimestamp, are re-initialized, and causes an ABRT -# of systemd-analyze blame. See issue #27187. -systemd-analyze blame - -# Test for 'systemd-update-utmp runlevel' vs 'systemctl daemon-reexec'. -# See issue #27163. -# shellcheck disable=SC2034 -for _ in {0..10}; do - systemctl daemon-reexec & - pid_reexec=$! - # shellcheck disable=SC2034 - for _ in {0..10}; do - SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-update-utmp runlevel - done - wait "$pid_reexec" -done - -touch /testok 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-03.sh b/test/units/testsuite-03.sh deleted file mode 100755 index e3567c2..0000000 --- a/test/units/testsuite-03.sh +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -# Simple test for that daemon-reexec works in container. -# See: https://github.com/systemd/systemd/pull/23883 -systemctl daemon-reexec - -# Test merging of a --job-mode=ignore-dependencies job into a previously -# installed job. - -systemctl start --no-block hello-after-sleep.target - -systemctl list-jobs >/root/list-jobs.txt -until grep 'sleep\.service.*running' /root/list-jobs.txt; do - systemctl list-jobs >/root/list-jobs.txt -done - -grep 'hello\.service.*waiting' /root/list-jobs.txt - -# This is supposed to finish quickly, not wait for sleep to finish. -START_SEC=$(date -u '+%s') -systemctl start --job-mode=ignore-dependencies hello -END_SEC=$(date -u '+%s') -ELAPSED=$((END_SEC-START_SEC)) - -test "$ELAPSED" -lt 3 - -# sleep should still be running, hello not. -systemctl list-jobs >/root/list-jobs.txt -grep 'sleep\.service.*running' /root/list-jobs.txt -grep 'hello\.service' /root/list-jobs.txt && exit 1 -systemctl stop sleep.service hello-after-sleep.target - -# Some basic testing that --show-transaction does something useful -(! systemctl is-active systemd-importd) -systemctl -T start systemd-importd -systemctl is-active systemd-importd -systemctl --show-transaction stop systemd-importd -(! systemctl is-active systemd-importd) - -# Test for a crash when enqueuing a JOB_NOP when other job already exists -systemctl start --no-block hello-after-sleep.target -# hello.service should still be waiting, so these try-restarts will collapse -# into NOPs. -systemctl try-restart --job-mode=fail hello.service -systemctl try-restart hello.service -systemctl stop hello.service sleep.service hello-after-sleep.target - -# TODO: add more job queueing/merging tests here. - -# Test that restart propagates to activating units -systemctl -T --no-block start always-activating.service -systemctl list-jobs | grep 'always-activating.service' -ACTIVATING_ID_PRE=$(systemctl show -P InvocationID always-activating.service) -systemctl -T start always-activating.socket # Wait for the socket to come up -systemctl -T restart always-activating.socket -ACTIVATING_ID_POST=$(systemctl show -P InvocationID always-activating.service) -[ "$ACTIVATING_ID_PRE" != "$ACTIVATING_ID_POST" ] || exit 1 - -# Test for irreversible jobs -systemctl start unstoppable.service - -# This is expected to fail with 'job cancelled' -systemctl stop unstoppable.service && exit 1 -# But this should succeed -systemctl stop --job-mode=replace-irreversibly unstoppable.service - -# We're going to shutdown soon. Let's see if it succeeds when -# there's an active service that tries to be unstoppable. -# Shutdown of the container/VM will hang if not. -systemctl start unstoppable.service - -# Test waiting for a started units to terminate again -cat </run/systemd/system/wait2.service -[Unit] -Description=Wait for 2 seconds -[Service] -ExecStart=/bin/sh -ec 'sleep 2' -EOF -cat </run/systemd/system/wait5fail.service -[Unit] -Description=Wait for 5 seconds and fail -[Service] -ExecStart=/bin/sh -ec 'sleep 5; false' -EOF - -# wait2 succeeds -START_SEC=$(date -u '+%s') -systemctl start --wait wait2.service -END_SEC=$(date -u '+%s') -ELAPSED=$((END_SEC-START_SEC)) -[[ "$ELAPSED" -ge 2 ]] && [[ "$ELAPSED" -le 4 ]] || exit 1 - -# wait5fail fails, so systemctl should fail -START_SEC=$(date -u '+%s') -(! systemctl start --wait wait2.service wait5fail.service) -END_SEC=$(date -u '+%s') -ELAPSED=$((END_SEC-START_SEC)) -[[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1 - -# Test time-limited scopes -START_SEC=$(date -u '+%s') -set +e -systemd-run --scope --property=RuntimeMaxSec=3s sleep 10 -RESULT=$? -END_SEC=$(date -u '+%s') -ELAPSED=$((END_SEC-START_SEC)) -[[ "$ELAPSED" -ge 3 ]] && [[ "$ELAPSED" -le 5 ]] || exit 1 -[[ "$RESULT" -ne 0 ]] || exit 1 - -# Test transactions with cycles -# Provides coverage for issues like https://github.com/systemd/systemd/issues/26872 -for i in {0..19}; do - cat >"/run/systemd/system/transaction-cycle$i.service" <"/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.SYSTEMD_JOURNAL_COMPRESS.sh b/test/units/testsuite-04.SYSTEMD_JOURNAL_COMPRESS.sh deleted file mode 100755 index 96d096d..0000000 --- a/test/units/testsuite-04.SYSTEMD_JOURNAL_COMPRESS.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# https://bugzilla.redhat.com/show_bug.cgi?id=2183546 -mkdir /run/systemd/system/systemd-journald.service.d -MACHINE_ID="$(/run/systemd/system/systemd-journald.service.d/compress.conf <&1 | grep -q -F 'compress=${c}'; do sleep .5; done" - - # $SYSTEMD_JOURNAL_COMPRESS= also works for journal-remote - if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then - for cc in NONE XZ LZ4 ZSTD; do - rm -f /tmp/foo.journal - SYSTEMD_JOURNAL_COMPRESS="${cc}" /usr/lib/systemd/systemd-journal-remote --split-mode=none -o /tmp/foo.journal --getter="journalctl -b -o export -t $ID" - SYSTEMD_LOG_LEVEL=debug journalctl --verify --quiet --file /tmp/foo.journal 2>&1 | grep -q -F "compress=${cc}" - journalctl -t "$ID" -o cat --file /tmp/foo.journal | grep -q -F "hoge with ${c}" - done - fi -done - -rm /run/systemd/system/systemd-journald.service.d/compress.conf -systemctl daemon-reload -systemctl restart systemd-journald.service -systemctl reset-failed systemd-journald.service -journalctl --rotate diff --git a/test/units/testsuite-04.bsod.sh b/test/units/testsuite-04.bsod.sh deleted file mode 100755 index 30f0cb0..0000000 --- a/test/units/testsuite-04.bsod.sh +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -if systemd-detect-virt -cq; then - echo "This test requires a VM, skipping the test" - exit 0 -fi - -# shellcheck disable=SC2317 -at_exit() { - local EC=$? - - if [[ $EC -ne 0 ]] && [[ -e /tmp/console.dump ]]; then - cat /tmp/console.dump - fi - - if mountpoint -q /var/log/journal; then - journalctl --relinquish-var - umount /var/log/journal - journalctl --flush - fi - - return 0 -} - -vcs_dump_and_check() { - local expected_message="${1:?}" - - # It might take a while before the systemd-bsod stuff appears on the VCS, - # so try it a couple of times - for _ in {0..9}; do - setterm --term linux --dump --file /tmp/console.dump - if grep -aq "Press any key to exit" /tmp/console.dump && - grep -aq "$expected_message" /tmp/console.dump && - grep -aq "The current boot has failed" /tmp/console.dump; then - - return 0 - fi - - sleep .5 - done - - return 1 -} - -# Since systemd-bsod always fetches only the first emergency message from the -# current boot, let's temporarily overmount /var/log/journal with a tmpfs, -# as we're going to wipe it multiple times, but we need to keep the original -# journal intact for the other tests to work correctly. -trap at_exit EXIT -mount -t tmpfs tmpfs /var/log/journal -systemctl restart systemd-journald - -systemctl stop systemd-bsod - -# Since we just wiped the journal, there should be no emergency messages and -# systemd-bsod should be just a no-op -timeout 10s /usr/lib/systemd/systemd-bsod -setterm --term linux --dump --file /tmp/console.dump -(! grep "The current boot has failed" /tmp/console.dump) - -# systemd-bsod should pick up emergency messages only with UID=0, so let's check -# that as well -systemd-run --user --machine testuser@ --wait --pipe systemd-cat -p emerg echo "User emergency message" -systemd-cat -p emerg echo "Root emergency message" -journalctl --sync -# Set $SYSTEMD_COLORS so systemd-bsod also prints out the QR code -SYSTEMD_COLORS=256 /usr/lib/systemd/systemd-bsod & -PID=$! -vcs_dump_and_check "Root emergency message" -grep -aq "Scan the QR code" /tmp/console.dump -# TODO: check if systemd-bsod exits on a key press (didn't figure this one out yet) -kill $PID -timeout 10 bash -c "while kill -0 $PID; do sleep .5; done" - -# Wipe the journal -journalctl --vacuum-size=1 --rotate -(! journalctl -q -b -p emerg --grep .) - -# Check the systemd-bsod.service as well -# Note: the systemd-bsod.service unit has ConditionVirtualization=no, so let's -# temporarily override it just for the test -mkdir /run/systemd/system/systemd-bsod.service.d -printf '[Unit]\nConditionVirtualization=\n' >/run/systemd/system/systemd-bsod.service.d/99-override.conf -systemctl daemon-reload -systemctl start systemd-bsod -systemd-cat -p emerg echo "Service emergency message" -vcs_dump_and_check "Service emergency message" -systemctl stop systemd-bsod - -# Wipe the journal -journalctl --vacuum-size=1 --rotate -(! journalctl -q -b -p emerg --grep .) - -# Same as above, but make sure the service responds to signals even when there are -# no "emerg" messages, see systemd/systemd#30084 -(! systemctl is-active systemd-bsod) -systemctl start systemd-bsod -timeout 5s bash -xec 'until systemctl is-active systemd-bsod; do sleep .5; done' -timeout 5s systemctl stop systemd-bsod -timeout 5s bash -xec 'while systemctl is-active systemd-bsod; do sleep .5; done' diff --git a/test/units/testsuite-04.corrupted-journals.sh b/test/units/testsuite-04.corrupted-journals.sh deleted file mode 100755 index 2123b10..0000000 --- a/test/units/testsuite-04.corrupted-journals.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -JOURNAL_DIR="$(mktemp -d)" -REMOTE_OUT="$(mktemp -d)" -# tar on C8S doesn't support the --zstd option -unzstd --stdout "/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") -# First, try each of them sequentially. Skip this part when running with plain -# QEMU, as it is excruciatingly slow -# Note: we care only about exit code 124 (timeout) and special bash exit codes -# >124 (like signals) -if [[ "$(systemd-detect-virt -v)" != "qemu" ]]; then - while read -r file; do - timeout 10 journalctl --file="$file" --boot >/dev/null || [[ $? -lt 124 ]] - timeout 10 journalctl --file="$file" --verify >/dev/null || [[ $? -lt 124 ]] - timeout 10 journalctl --file="$file" --output=export >/dev/null || [[ $? -lt 124 ]] - timeout 10 journalctl --file="$file" --fields >/dev/null || [[ $? -lt 124 ]] - timeout 10 journalctl --file="$file" --list-boots >/dev/null || [[ $? -lt 124 ]] - if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then - timeout 10 /usr/lib/systemd/systemd-journal-remote \ - --getter="journalctl --file=$file --output=export" \ - --split-mode=none \ - --output="$REMOTE_OUT/system.journal" || [[ $? -lt 124 ]] - timeout 10 journalctl --directory="$REMOTE_OUT" >/dev/null || [[ $? -lt 124 ]] - rm -f "$REMOTE_OUT"/* - fi - done < <(find "$JOURNAL_DIR" -type f) -fi -# And now all at once -timeout 30 journalctl --directory="$JOURNAL_DIR" --boot >/dev/null || [[ $? -lt 124 ]] -timeout 30 journalctl --directory="$JOURNAL_DIR" --verify >/dev/null || [[ $? -lt 124 ]] -timeout 30 journalctl --directory="$JOURNAL_DIR" --output=export >/dev/null || [[ $? -lt 124 ]] -timeout 30 journalctl --directory="$JOURNAL_DIR" --fields >/dev/null || [[ $? -lt 124 ]] -timeout 30 journalctl --directory="$JOURNAL_DIR" --list-boots >/dev/null || [[ $? -lt 124 ]] -if [[ -x /usr/lib/systemd/systemd-journal-remote ]]; then - timeout 30 /usr/lib/systemd/systemd-journal-remote \ - --getter="journalctl --directory=$JOURNAL_DIR --output=export" \ - --split-mode=none \ - --output="$REMOTE_OUT/system.journal" || [[ $? -lt 124 ]] - timeout 30 journalctl --directory="$REMOTE_OUT" >/dev/null || [[ $? -lt 124 ]] - rm -f "$REMOTE_OUT"/* -fi diff --git a/test/units/testsuite-04.fss.sh b/test/units/testsuite-04.fss.sh deleted file mode 100755 index 03351b8..0000000 --- a/test/units/testsuite-04.fss.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# Forward Secure Sealing - -if ! journalctl --version | grep -qF +GCRYPT; then - echo "Built without gcrypt, skipping the FSS tests" - exit 0 -fi - -journalctl --force --setup-keys --interval=2 |& tee /tmp/fss -FSS_VKEY="$(sed -rn '/([a-f0-9]{6}\-){3}[a-f0-9]{6}\/[a-f0-9]+\-[a-f0-9]+/p' /tmp/fss)" -[[ -n "$FSS_VKEY" ]] - -# Generate some buzz in the journal and wait until the FSS key is changed -# at least once -systemd-cat cat /etc/os-release -sleep 4 -# Seal the journal -journalctl --rotate -# Verification should fail without a valid FSS key -(! journalctl --verify) -(! journalctl --verify --verify-key="") -(! journalctl --verify --verify-key="000000-000000-000000-000000/00000000-00000") -# FIXME: ignore --verify result until #27532 is resolved -journalctl --verify --verify-key="$FSS_VKEY" || : - -# Sealing + systemd-journal-remote -/usr/lib/systemd/systemd-journal-remote --getter="journalctl -n 5 -o export" \ - --split-mode=none \ - --seal=yes \ - --output=/tmp/sealed.journal -(! journalctl --file=/tmp/sealed.journal --verify) -(! journalctl --file=/tmp/sealed.journal --verify --verify-key="") -(! journalctl --file=/tmp/sealed.journal --verify --verify-key="000000-000000-000000-000000/00000000-00000") -# FIXME: ignore --verify result until #27532 is resolved -journalctl --file=/tmp/sealed.journal --verify --verify-key="$FSS_VKEY" || : -rm -f /tmp/sealed.journal - -# Return back to a journal without FSS -rm -fv "/var/log/journal/$(Journal" -curl -Lfs http://localhost:19531/browse | grep -qF "Journal" -(! 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 <Journal" -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.journal-remote.sh b/test/units/testsuite-04.journal-remote.sh deleted file mode 100755 index c7b99b1..0000000 --- a/test/units/testsuite-04.journal-remote.sh +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2016 -set -eux -set -o pipefail - -if [[ ! -x /usr/lib/systemd/systemd-journal-remote || ! -x /usr/lib/systemd/systemd-journal-upload ]]; then - echo "Built without systemd-journal-remote/upload support, skipping the test" - exit 0 -fi - -if ! command -v openssl >/dev/null; then - echo "openssl command not available, skipping the tests" - exit 0 -fi - -at_exit() { - set +e - - systemctl stop systemd-journal-upload - systemctl stop systemd-journal-remote.{socket,service} - # Remove any remote journals on exit, so we don't try to export them together - # with the local journals, causing a mess - rm -rf /var/log/journal/remote -} - -trap at_exit EXIT - -TEST_MESSAGE="-= This is a test message $RANDOM =-" -TEST_TAG="$(systemd-id128 new)" - -echo "$TEST_MESSAGE" | systemd-cat -t "$TEST_TAG" -journalctl --sync - -/usr/lib/systemd/systemd-journal-remote --version -/usr/lib/systemd/systemd-journal-remote --help -/usr/lib/systemd/systemd-journal-upload --version -/usr/lib/systemd/systemd-journal-upload --help - -# Generate a self-signed certificate for systemd-journal-remote -# -# Note: older OpenSSL requires a config file with some extra options, unfortunately -# Note2: /run here is used on purpose, since the systemd-journal-remote service uses PrivateTmp=yes -mkdir -p /run/systemd/journal-remote-tls -cat >/tmp/openssl.conf </run/systemd/journal-remote.conf.d/99-test.conf </run/systemd/journal-upload.conf.d/99-test.conf </run/systemd/remote-pki/ca.conf </run/systemd/remote-pki/client.conf </run/systemd/remote-pki/server.conf </run/systemd/remote-pki/ca.srl -# Generate a client key and signing request -openssl req -nodes -newkey rsa:2048 -sha256 \ - -config /run/systemd/remote-pki/client.conf \ - -keyout /run/systemd/remote-pki/client.key \ - -out /run/systemd/remote-pki/client.csr -# Sign the request with the CA key -openssl x509 -req -days 7 \ - -in /run/systemd/remote-pki/client.csr \ - -CA /run/systemd/remote-pki/ca.crt \ - -CAkey /run/systemd/remote-pki/ca.key \ - -out /run/systemd/remote-pki/client.crt -# And do the same for the server -openssl req -nodes -newkey rsa:2048 -sha256 \ - -config /run/systemd/remote-pki/server.conf \ - -keyout /run/systemd/remote-pki/server.key \ - -out /run/systemd/remote-pki/server.csr -openssl x509 -req -days 7 \ - -in /run/systemd/remote-pki/server.csr \ - -CA /run/systemd/remote-pki/ca.crt \ - -CAkey /run/systemd/remote-pki/ca.key \ - -out /run/systemd/remote-pki/server.crt -chown -R systemd-journal-remote:systemd-journal /run/systemd/remote-pki -chmod -R g+rwX /run/systemd/remote-pki - -# Reconfigure journal-upload/journal remote with the new keys -cat >/run/systemd/journal-remote.conf.d/99-test.conf </run/systemd/journal-upload.conf.d/99-test.conf </run/systemd/journal-upload.conf.d/99-test.conf </run/systemd/system/systemd-journal-upload.service.d/99-test.conf <\n<6>\n<7>\n" "" --level-prefix true -# Remove trailing spaces -write_and_match "Trailing spaces \t \n" "Trailing spaces\n" --level-prefix false -write_and_match "<5>Trailing spaces \t \n" "Trailing spaces\n" --level-prefix true -# Don't remove leading spaces -write_and_match " \t Leading spaces\n" " \t Leading spaces\n" --level-prefix false -write_and_match "<5> \t Leading spaces\n" " \t Leading spaces\n" --level-prefix true - -# --output-fields restricts output -ID="$(systemd-id128 new)" -echo -ne "foo" | systemd-cat -t "$ID" --level-prefix false -# Let's test varlinkctl a bit, i.e. implement the equivalent of 'journalctl --sync' via varlinkctl -varlinkctl call /run/systemd/journal/io.systemd.journal io.systemd.Journal.Synchronize '{}' -journalctl -b -o export --output-fields=MESSAGE,FOO --output-fields=PRIORITY,MESSAGE -t "$ID" >/tmp/output -[[ $(wc -l /dev/null - -# -b always behaves like -b0 -journalctl -q -b-1 -b0 | head -1 >/tmp/expected -journalctl -q -b-1 -b | head -1 >/tmp/output -diff /tmp/expected /tmp/output -# ... even when another option follows (both of these should fail due to -m) -{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 >/tmp/expected -{ journalctl -ball -b -m 2>&1 || :; } | head -1 >/tmp/output -diff /tmp/expected /tmp/output - -# https://github.com/systemd/systemd/issues/13708 -ID=$(systemd-id128 new) -systemd-cat -t "$ID" bash -c 'echo parent; (echo child) & wait' & -PID=$! -wait $PID -journalctl --sync -# We can drop this grep when https://github.com/systemd/systemd/issues/13937 -# has a fix. -journalctl -b -o export -t "$ID" --output-fields=_PID | grep '^_PID=' >/tmp/output -[[ $(wc -l /tmp/expected -systemd-cat -t "$ID" /bin/sh -c 'env echo -n "This will";echo;env echo -n "usually fail";echo;env echo -n "and be truncated";echo;' -journalctl --sync -journalctl -b -o cat -t "$ID" >/tmp/output -diff /tmp/expected /tmp/output -[[ $(journalctl -b -o cat -t "$ID" --output-fields=_TRANSPORT | grep -Pc "^stdout$") -eq 3 ]] -[[ $(journalctl -b -o cat -t "$ID" --output-fields=_LINE_BREAK | grep -Pc "^pid-change$") -eq 3 ]] -[[ $(journalctl -b -o cat -t "$ID" --output-fields=_PID | sort -u | grep -c "^.*$") -eq 3 ]] -[[ $(journalctl -b -o cat -t "$ID" --output-fields=MESSAGE | grep -Pc "^(This will|usually fail|and be truncated)$") -eq 3 ]] - -# test that LogLevelMax can also suppress logging about services, not only by services -systemctl start silent-success -journalctl --sync -[[ -z "$(journalctl -b -q -u silent-success.service)" ]] - -# Exercise the matching machinery -SYSTEMD_LOG_LEVEL=debug journalctl -b -n 1 /dev/null /dev/zero /dev/null /dev/null /dev/null -journalctl -b -n 1 /bin/true /bin/false -journalctl -b -n 1 /bin/true + /bin/false -journalctl -b -n 1 -r --unit "systemd*" - -systemd-run --user -M "testuser@.host" /bin/echo hello -journalctl --sync -journalctl -b -n 1 -r --user-unit "*" - -(! journalctl -b /dev/lets-hope-this-doesnt-exist) -(! journalctl -b /dev/null /dev/zero /dev/this-also-shouldnt-exist) -(! journalctl -b --unit "this-unit-should-not-exist*") - -# Facilities & priorities -journalctl --facility help -journalctl --facility kern -n 1 -journalctl --facility syslog --priority 0..3 -n 1 -journalctl --facility syslog --priority 3..0 -n 1 -journalctl --facility user --priority 0..0 -n 1 -journalctl --facility daemon --priority warning -n 1 -journalctl --facility daemon --priority warning..info -n 1 -journalctl --facility daemon --priority notice..crit -n 1 -journalctl --facility daemon --priority 5..crit -n 1 - -# Assorted combinations -journalctl -o help -journalctl -q -n all -a | grep . >/dev/null -journalctl -q --no-full | grep . >/dev/null -journalctl -q --user --system | grep . >/dev/null -journalctl --namespace "*" | grep . >/dev/null -journalctl --namespace "" | grep . >/dev/null -journalctl -q --namespace "+foo-bar-baz-$RANDOM" | grep . >/dev/null -(! journalctl -q --namespace "foo-bar-baz-$RANDOM" | grep .) -journalctl --root / | grep . >/dev/null -journalctl --cursor "t=0;t=-1;t=0;t=0x0" | grep . >/dev/null -journalctl --header | grep system.journal -journalctl --field _EXE | grep . >/dev/null -journalctl --no-hostname --utc --catalog | grep . >/dev/null -# Exercise executable_is_script() and the related code, e.g. `journalctl -b /path/to/a/script.sh` should turn -# into ((_EXE=/bin/bash AND _COMM=script.sh) AND _BOOT_ID=c002e3683ba14fa8b6c1e12878386514) -journalctl -b "$(readlink -f "$0")" | grep . >/dev/null -journalctl -b "$(systemd-id128 boot-id)" | grep . >/dev/null -journalctl --since yesterday --reverse | grep . >/dev/null -journalctl --machine .host | grep . >/dev/null -# Log something that journald will forward to wall -echo "Oh no!" | systemd-cat -t "emerg$RANDOM" -p emerg --stderr-priority emerg - -TAG="$(systemd-id128 new)" -echo "Foo Bar Baz" | systemd-cat -t "$TAG" -journalctl --sync -# Relevant excerpt from journalctl(1): -# If the pattern is all lowercase, matching is case insensitive. Otherwise, matching is case sensitive. -# This can be overridden with the --case-sensitive option -journalctl -e -t "$TAG" --grep "Foo Bar Baz" -journalctl -e -t "$TAG" --grep "foo bar baz" -(! journalctl -e -t "$TAG" --grep "foo Bar baz") -journalctl -e -t "$TAG" --case-sensitive=false --grep "foo Bar baz" - -(! journalctl --facility hopefully-an-unknown-facility) -(! journalctl --priority hello-world) -(! journalctl --priority 0..128) -(! journalctl --priority 0..systemd) - -# Other options -journalctl --disk-usage -journalctl --dmesg -n 1 -journalctl --fields -journalctl --list-boots -journalctl --update-catalog -journalctl --list-catalog - -# Add new tests before here, the journald restarts below -# may make tests flappy. - -# Don't lose streams on restart -systemctl start forever-print-hola -sleep 3 -systemctl restart systemd-journald -sleep 3 -systemctl stop forever-print-hola -[[ ! -f "/tmp/i-lose-my-logs" ]] - -# https://github.com/systemd/systemd/issues/4408 -rm -f /tmp/i-lose-my-logs -systemctl start forever-print-hola -sleep 3 -systemctl kill --signal=SIGKILL systemd-journald -sleep 3 -[[ ! -f "/tmp/i-lose-my-logs" ]] -systemctl stop forever-print-hola - -set +o pipefail -# https://github.com/systemd/systemd/issues/15528 -journalctl --follow --file=/var/log/journal/*/* | head -n1 | grep . -# https://github.com/systemd/systemd/issues/24565 -journalctl --follow --merge | head -n1 | grep . -set -o pipefail - -# https://github.com/systemd/systemd/issues/26746 -rm -f /tmp/issue-26746-log /tmp/issue-26746-cursor -ID="$(systemd-id128 new)" -journalctl -t "$ID" --follow --cursor-file=/tmp/issue-26746-cursor | tee /tmp/issue-26746-log & -systemd-cat -t "$ID" /bin/sh -c 'echo hogehoge' -# shellcheck disable=SC2016 -timeout 10 bash -c 'until [[ -f /tmp/issue-26746-log && "$(cat /tmp/issue-26746-log)" =~ hogehoge ]]; do sleep .5; done' -pkill -TERM journalctl -timeout 10 bash -c 'until test -f /tmp/issue-26746-cursor; do sleep .5; done' -CURSOR_FROM_FILE="$(cat /tmp/issue-26746-cursor)" -CURSOR_FROM_JOURNAL="$(journalctl -t "$ID" --output=export MESSAGE=hogehoge | sed -n -e '/__CURSOR=/ { s/__CURSOR=//; p }')" -test "$CURSOR_FROM_FILE" = "$CURSOR_FROM_JOURNAL" - -# Check that the seqnum field at least superficially works -systemd-cat echo "ya" -journalctl --sync -SEQNUM1=$(journalctl -o export -n 1 | grep -Ea "^__SEQNUM=" | cut -d= -f2) -systemd-cat echo "yo" -journalctl --sync -SEQNUM2=$(journalctl -o export -n 1 | grep -Ea "^__SEQNUM=" | cut -d= -f2) -test "$SEQNUM2" -gt "$SEQNUM1" - -# Test for journals without RTC -# See: https://github.com/systemd/systemd/issues/662 -JOURNAL_DIR="$(mktemp -d)" -while read -r file; do - filename="${file##*/}" - unzstd "$file" -o "$JOURNAL_DIR/${filename%*.zst}" -done < <(find /test-journals/no-rtc -name "*.zst") - -journalctl --directory="$JOURNAL_DIR" --list-boots --output=json >/tmp/lb1 -diff -u /tmp/lb1 - <<'EOF' -[{"index":-3,"boot_id":"5ea5fc4f82a14186b5332a788ef9435e","first_entry":1666569600994371,"last_entry":1666584266223608},{"index":-2,"boot_id":"bea6864f21ad4c9594c04a99d89948b0","first_entry":1666569601005945,"last_entry":1666584347230411},{"index":-1,"boot_id":"4c708e1fd0744336be16f3931aa861fb","first_entry":1666569601017222,"last_entry":1666584354649355},{"index":0,"boot_id":"35e8501129134edd9df5267c49f744a4","first_entry":1666569601009823,"last_entry":1666584438086856}] -EOF -rm -rf "$JOURNAL_DIR" /tmp/lb1 - -# 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 -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 -# --after-cursor= + --unit= -# The format of the "Starting ..." message depends on StatusUnitFormat=, so match only the beginning -# which should be enough in this case -[[ "$(journalctl -n 1 -p info -o cat --unit="$UNIT_NAME" --after-cursor="$(<"$CURSOR_FILE")" _PID=1 )" =~ ^Starting\ ]] -# There should be no such messages before the cursor -[[ -z "$(journalctl -n 1 -p info -o cat --unit="$UNIT_NAME" --after-cursor="$(<"$CURSOR_FILE")" --reverse)" ]] -# --cursor-file= + a journal filter -diff <(journalctl --cursor-file="$CURSOR_FILE" -p info -o cat _SYSTEMD_UNIT="$UNIT_NAME") - <$P/rlimits.conf <= 5.1 -# -# * in POSIX mode -c a -f options show values in 512-byte increments; let's hope -# we never run in the POSIX mode -systemd-run --wait --pipe "${ARGUMENTS[@]}" \ - bash -xec 'KB=1; MB=$((KB * 1024)); GB=$((MB * 1024)); TB=$((GB * 1024)); - : CPU; [[ $(ulimit -St) -eq 10 ]]; [[ $(ulimit -Ht) -eq 15 ]]; - : FSIZE; [[ $(ulimit -Sf) -eq $((96 * GB)) ]]; [[ $(ulimit -Hf) -eq $((96 * GB)) ]]; - : DATA; [[ $(ulimit -Sd) == unlimited ]]; [[ $(ulimit -Hd) == unlimited ]]; - : STACK; [[ $(ulimit -Ss) -eq $((8 * MB)) ]]; [[ $(ulimit -Hs) -eq $((8 * MB)) ]]; - : CORE; [[ $(ulimit -Sc) -eq $((17 * MB)) ]]; [[ $(ulimit -Hc) -eq $((17 * MB)) ]]; - : RSS; [[ $(ulimit -Sm) -eq $((27 * GB)) ]]; [[ $(ulimit -Hm) -eq $((27 * GB)) ]]; - : NOFILE; [[ $(ulimit -Sn) -eq 7 ]]; [[ $(ulimit -Hn) -eq 127 ]]; - : AS; [[ $(ulimit -Sv) == unlimited ]]; [[ $(ulimit -Hv) == unlimited ]]; - : NPROC; [[ $(ulimit -Su) -eq 64 ]]; [[ $(ulimit -Hu) == unlimited ]]; - : MEMLOCK; [[ $(ulimit -Sl) -eq $((37 * MB)) ]]; [[ $(ulimit -Hl) -eq $((37 * MB)) ]]; - : LOCKS; [[ $(ulimit -Sx) -eq 19 ]]; [[ $(ulimit -Hx) -eq 1021 ]]; - : SIGPENDING; [[ $(ulimit -Si) -eq 21 ]]; [[ $(ulimit -Hi) -eq 21 ]]; - : MSGQUEUE; [[ $(ulimit -Sq) -eq 666 ]]; [[ $(ulimit -Hq) -eq 666 ]]; - : NICE; [[ $(ulimit -Se) -eq 4 ]]; [[ $(ulimit -He) -eq 4 ]]; - : RTPRIO; [[ $(ulimit -Sr) -eq 8 ]]; [[ $(ulimit -Hr) -eq 8 ]]; - ulimit -R || exit 0; - : RTTIME; [[ $(ulimit -SR) -eq 666666 ]]; [[ $(ulimit -HR) -eq 666666 ]];' - -# RestrictFileSystems= -# -# Note: running instrumented binaries requires at least /proc to be accessible, so let's -# skip the test when we're running under sanitizers -# -# Note: $GCOV_ERROR_LOG is used during coverage runs to suppress errors when creating *.gcda files, -# since gcov can't access the restricted filesystem (as expected) -if [[ ! -v ASAN_OPTIONS ]] && systemctl --version | grep "+BPF_FRAMEWORK" && kernel_supports_lsm bpf; then - ROOTFS="$(df --output=fstype /usr/bin | sed --quiet 2p)" - systemd-run --wait --pipe -p RestrictFileSystems="" ls / - systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar" ls / - (! systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS" ls /proc) - (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="foo" ls /) - systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar baz proc" ls /proc - systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /proc - systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /sys/fs/cgroup - - systemd-run --wait --pipe -p RestrictFileSystems="~" ls / - systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls / - systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls / - (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="~$ROOTFS" ls /) - (! systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls /proc) - (! systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls /proc) - (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /proc) - (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /sys) - systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls / - (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /proc) - (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /dev) - (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /sys) -fi - -# Make sure we properly (de)serialize various string arrays, including whitespaces -# See: https://github.com/systemd/systemd/issues/31214 -systemd-run --wait --pipe -p Environment="FOO='bar4 '" \ - bash -xec '[[ $FOO == "bar4 " ]]' -systemd-run --wait --pipe -p Environment="FOO='bar4 ' BAR='\n\n'" \ - bash -xec "[[ \$FOO == 'bar4 ' && \$BAR == $'\n\n' ]]" -systemd-run --wait --pipe -p Environment='FOO="bar4 \\ "' -p Environment="BAR='\n\t'" \ - bash -xec "[[ \$FOO == 'bar4 \\ ' && \$BAR == $'\n\t' ]]" -TEST_ENV_FILE="/tmp/test-env-file-$RANDOM- " -cat >"$TEST_ENV_FILE" </run/systemd/system/my.service <<\EOF -[Service] -Type=oneshot -ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = my.timer' -ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_REALTIME_USEC"' -ExecStartPre=sh -c 'test -n "$TRIGGER_TIMER_MONOTONIC_USEC"' -ExecStart=/bin/echo Timer runs me -EOF - -cat >/run/systemd/system/my.timer </run/systemd/system/my.timer.d/override.conf </run/systemd/system/badbin_assert.service </run/systemd/system/badbin_assert.socket <$U </run/systemd/system/issue-3171@.service </tmp/test-mainpid.sh <<\EOF -#!/usr/bin/env bash - -set -eux -set -o pipefail - -# Create a number of children, and make one the main one -sleep infinity & -disown - -sleep infinity & -MAINPID=$! -disown - -sleep infinity & -disown - -echo $MAINPID >/run/mainpidsh/pid -EOF -chmod +x /tmp/test-mainpid.sh - -systemd-run --unit=test-mainpidsh.service \ - -p StandardOutput=tty \ - -p StandardError=tty \ - -p Type=forking \ - -p RuntimeDirectory=mainpidsh \ - -p PIDFile=/run/mainpidsh/pid \ - /tmp/test-mainpid.sh -test "$(systemctl show -P MainPID test-mainpidsh.service)" -eq "$(cat /run/mainpidsh/pid)" - -cat >/tmp/test-mainpid2.sh <<\EOF -#!/usr/bin/env bash - -set -eux -set -o pipefail - -# Create a number of children, and make one the main one -sleep infinity & -disown - -sleep infinity & -MAINPID=$! -disown - -sleep infinity & -disown - -echo $MAINPID >/run/mainpidsh2/pid -chown 1001:1001 /run/mainpidsh2/pid -EOF -chmod +x /tmp/test-mainpid2.sh - -systemd-run --unit=test-mainpidsh2.service \ - -p StandardOutput=tty \ - -p StandardError=tty \ - -p Type=forking \ - -p RuntimeDirectory=mainpidsh2 \ - -p PIDFile=/run/mainpidsh2/pid \ - /tmp/test-mainpid2.sh -test "$(systemctl show -P MainPID test-mainpidsh2.service)" -eq "$(cat /run/mainpidsh2/pid)" - -cat >/dev/shm/test-mainpid3.sh <>"$TMP_MOUNTINFO" -mount --bind "$TMP_MOUNTINFO" /proc/1/mountinfo -systemctl daemon-reload -# On affected versions this would throw an error: -# Failed to get properties: Bad message -systemctl status foo-mountinfo.mount - -umount /proc/1/mountinfo -systemctl daemon-reload -rm -f "$TMP_MOUNTINFO" - -# Check invalid characters in a mount unit -# -# systemd already handles this and refuses to load the invalid string, e.g.: -# foo-fstab.mount:9: String is not UTF-8 clean, ignoring assignment: What=//localhost/foo���bar -# -# a) Unit generated from /etc/fstab -[[ -e /etc/fstab ]] && cp -f /etc/fstab /tmp/fstab.bak - -LANG="C.UTF-8" printf '//localhost/foo\ufffebar /foo/fstab cifs defaults 0 0\n' >/etc/fstab -systemctl daemon-reload -[[ "$(systemctl show -P UnitFileState foo-fstab.mount)" == bad ]] - -# b) Unit generated from /etc/fstab (but the invalid character is in options) -LANG="C.UTF-8" printf '//localhost/foobar /foo/fstab/opt cifs nosuid,a\ufffeb,noexec 0 0\n' >/etc/fstab -systemctl daemon-reload -[[ "$(systemctl show -P UnitFileState foo-fstab-opt.mount)" == bad ]] -rm -f /etc/fstab - -[[ -e /tmp/fstab.bak ]] && mv -f /tmp/fstab.bak /etc/fstab -systemctl daemon-reload - -# c) Mount unit -mkdir -p /run/systemd/system -LANG="C.UTF-8" printf '[Mount]\nWhat=//localhost/foo\ufffebar\nWhere=/foo/unit\nType=cifs\nOptions=noexec\n' >/run/systemd/system/foo-unit.mount -systemctl daemon-reload -[[ "$(systemctl show -P UnitFileState foo-unit.mount)" == bad ]] -rm -f /run/systemd/system/foo-unit.mount - -# d) Mount unit (but the invalid character is in Options=) -mkdir -p /run/systemd/system -LANG="C.UTF-8" printf '[Mount]\nWhat=//localhost/foobar\nWhere=/foo/unit/opt\nType=cifs\nOptions=noexec,a\ufffeb,nosuid\n' >/run/systemd/system/foo-unit-opt.mount -systemctl daemon-reload -[[ "$(systemctl show -P UnitFileState foo-unit-opt.mount)" == bad ]] -rm -f /run/systemd/system/foo-unit-opt.mount diff --git a/test/units/testsuite-07.poll-limit.sh b/test/units/testsuite-07.poll-limit.sh deleted file mode 100755 index 480d7ee..0000000 --- a/test/units/testsuite-07.poll-limit.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -systemd-analyze log-level debug - -cat > /run/systemd/system/floodme@.service < /run/systemd/system/floodme.socket <&2 "This test can't run in a container" - exit 1 -fi - -# This test requires systemd to run in the initrd as well, which is not the case -# for mkinitrd-based initrd (Ubuntu/Debian) -if [[ "$(systemctl show -P InitRDTimestampMonotonic)" -eq 0 ]]; then - echo "systemd didn't run in the initrd, skipping the test" - touch /skipped - exit 0 -fi - -# We should've created a mount under /run in initrd (see the other half of the test) -# that should've survived the transition from initrd to the real system -test -d /run/initrd-mount-target -mountpoint /run/initrd-mount-target -[[ -e /run/initrd-mount-target/hello-world ]] - -# Copy the prepared shutdown initrd to its intended location. Check the respective -# test.sh file for details -mkdir -p /run/initramfs -cp -r /shutdown-initrd/* /run/initramfs/ - -touch /testok diff --git a/test/units/testsuite-09.journal.sh b/test/units/testsuite-09.journal.sh deleted file mode 100755 index 2ef192c..0000000 --- a/test/units/testsuite-09.journal.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -get_first_boot_id() { - journalctl -b "${1:?}" -o json -n +1 | jq -r '._BOOT_ID' -} - -get_last_boot_id() { - journalctl -b "${1:?}" -o json -n 1 | jq -r '._BOOT_ID' -} - -get_first_timestamp() { - journalctl -b "${1:?}" -o json -n +1 | jq -r '.__REALTIME_TIMESTAMP' -} - -get_last_timestamp() { - journalctl -b "${1:?}" -o json -n 1 | jq -r '.__REALTIME_TIMESTAMP' -} - -# Issue: #29275, second part -# Now let's check if the boot entries are in the correct/expected order -index=0 -SYSTEMD_LOG_LEVEL=debug journalctl --list-boots -journalctl --list-boots -o json | jq -r '.[] | [.index, .boot_id, .first_entry, .last_entry] | @tsv' | - while read -r offset boot_id first_ts last_ts; do - : "Boot #$((++index)) ($offset) with ID $boot_id" - - # Try the "regular" (non-json) variants first, as they provide a helpful - # error message if something is not right - SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$index" - SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$offset" - SYSTEMD_LOG_LEVEL=debug journalctl -q -n 0 -b "$boot_id" - - # Check the boot ID of the first entry - entry_boot_id="$(get_first_boot_id "$index")" - assert_eq "$entry_boot_id" "$boot_id" - entry_boot_id="$(get_first_boot_id "$offset")" - assert_eq "$entry_boot_id" "$boot_id" - entry_boot_id="$(get_first_boot_id "$boot_id")" - assert_eq "$entry_boot_id" "$boot_id" - - # Check the timestamp of the first entry - entry_ts="$(get_first_timestamp "$index")" - assert_eq "$entry_ts" "$first_ts" - entry_ts="$(get_first_timestamp "$offset")" - assert_eq "$entry_ts" "$first_ts" - entry_ts="$(get_first_timestamp "$boot_id")" - assert_eq "$entry_ts" "$first_ts" - - # Check the boot ID of the last entry - entry_boot_id="$(get_last_boot_id "$index")" - assert_eq "$entry_boot_id" "$boot_id" - entry_boot_id="$(get_last_boot_id "$offset")" - assert_eq "$entry_boot_id" "$boot_id" - entry_boot_id="$(get_last_boot_id "$boot_id")" - assert_eq "$entry_boot_id" "$boot_id" - - # Check the timestamp of the last entry - if [[ "$offset" != "0" ]]; then - entry_ts="$(get_last_timestamp "$index")" - assert_eq "$entry_ts" "$last_ts" - entry_ts="$(get_last_timestamp "$offset")" - assert_eq "$entry_ts" "$last_ts" - entry_ts="$(get_last_timestamp "$boot_id")" - assert_eq "$entry_ts" "$last_ts" - fi - done 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-09.sh b/test/units/testsuite-09.sh deleted file mode 100755 index cd95660..0000000 --- a/test/units/testsuite-09.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -NUM_REBOOT=4 - -# shellcheck source=test/units/test-control.sh -. "$(dirname "$0")"/test-control.sh - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -systemd-cat echo "Reboot count: $REBOOT_COUNT" -systemd-cat journalctl --list-boots - -run_subtests - -if [[ "$REBOOT_COUNT" -lt "$NUM_REBOOT" ]]; then - systemctl_final reboot -elif [[ "$REBOOT_COUNT" -gt "$NUM_REBOOT" ]]; then - assert_not_reached -fi - -touch /testok diff --git a/test/units/testsuite-13.machinectl.sh b/test/units/testsuite-13.machinectl.sh deleted file mode 100755 index b5f90f6..0000000 --- a/test/units/testsuite-13.machinectl.sh +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2016 -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -export PAGER= - -at_exit() { - set +e - - machinectl status long-running >/dev/null && machinectl kill --signal=KILL long-running - mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done" - [[ -n "${NSPAWN_FRAGMENT:-}" ]] && rm -f "/etc/systemd/nspawn/$NSPAWN_FRAGMENT" "/var/lib/machines/$NSPAWN_FRAGMENT" - rm -f /run/systemd/nspawn/*.nspawn -} - -trap at_exit EXIT - -# Mount tmpfs over /var/lib/machines to not pollute the image -mkdir -p /var/lib/machines -mount -t tmpfs tmpfs /var/lib/machines - -# Create a couple of containers we can refer to in tests -for i in {0..4}; do - create_dummy_container "/var/lib/machines/container$i" - machinectl start "container$i" -done -# Create one "long running" container with some basic signal handling -create_dummy_container /var/lib/machines/long-running -cat >/var/lib/machines/long-running/sbin/init <<\EOF -#!/usr/bin/bash -x - -PID=0 - -trap "touch /poweroff" RTMIN+4 -trap "touch /reboot" INT -trap "touch /trap" TRAP -trap 'kill $PID' EXIT - -# We need to wait for the sleep process asynchronously in order to allow -# bash to process signals -sleep infinity & -PID=$! -while :; do - wait || : -done -EOF -machinectl start long-running - -machinectl -machinectl --no-pager --help -machinectl --version -machinectl list -machinectl list --no-legend --no-ask-password - -machinectl status long-running long-running long-running -machinectl status --full long-running -machinectl status --quiet --lines=1 long-running -machinectl status --lines=0 --max-addresses=0 long-running -machinectl status --machine=testuser@.host long-running -machinectl status --output=help long-running -while read -r output; do - machinectl status --output="$output" long-running -done < <(machinectl --output=help) - -machinectl show -machinectl show --all -machinectl show --all --machine=root@ -machinectl show --all --machine=testuser@ -[[ "$(machinectl show --property=PoolPath --value)" == "/var/lib/machines" ]] -machinectl show long-running -machinectl show long-running long-running long-running --all -[[ "$(machinectl show --property=RootDirectory --value long-running)" == "/var/lib/machines/long-running" ]] - -machinectl enable long-running -test -L /etc/systemd/system/machines.target.wants/systemd-nspawn@long-running.service -machinectl enable long-running long-running long-running container1 -machinectl disable long-running -test ! -L /etc/systemd/system/machines.target.wants/systemd-nspawn@long-running.service -machinectl disable long-running long-running long-running container1 - -[[ "$(machinectl shell testuser@ /usr/bin/bash -c 'echo -ne $FOO')" == "" ]] -[[ "$(machinectl shell --setenv=FOO=bar testuser@ /usr/bin/bash -c 'echo -ne $FOO')" == "bar" ]] - -[[ "$(machinectl show --property=State --value long-running)" == "running" ]] -# Equivalent to machinectl kill --signal=SIGRTMIN+4 --kill-whom=leader -rm -f /var/lib/machines/long-running/poweroff -machinectl poweroff long-running -timeout 10 bash -c "until test -e /var/lib/machines/long-running/poweroff; do sleep .5; done" -# Equivalent to machinectl kill --signal=SIGINT --kill-whom=leader -rm -f /var/lib/machines/long-running/reboot -machinectl reboot long-running -timeout 10 bash -c "until test -e /var/lib/machines/long-running/reboot; do sleep .5; done" -# Skip machinectl terminate for now, as it doesn't play well with our "init" -rm -f /var/lib/machines/long-running/trap -machinectl kill --signal=SIGTRAP --kill-whom=leader long-running -timeout 10 bash -c "until test -e /var/lib/machines/long-running/trap; do sleep .5; done" -# Multiple machines at once -machinectl poweroff long-running long-running long-running -machinectl reboot long-running long-running long-running -machinectl kill --signal=SIGTRAP --kill-whom=leader long-running long-running long-running -# All used signals should've been caught by a handler -[[ "$(machinectl show --property=State --value long-running)" == "running" ]] - -cp /etc/machine-id /tmp/foo -machinectl copy-to long-running /tmp/foo /root/foo -test -f /var/lib/machines/long-running/root/foo -machinectl copy-from long-running /root/foo /tmp/bar -diff /tmp/foo /tmp/bar -rm -f /tmp/{foo,bar} - -# machinectl bind is covered by testcase_check_machinectl_bind() in nspawn tests - -machinectl list-images -machinectl list-images --no-legend -machinectl image-status -machinectl image-status container1 -machinectl image-status container1 container1 container{0..4} -machinectl show-image -machinectl show-image container1 -machinectl show-image container1 container1 container{0..4} - -machinectl clone container1 clone1 -machinectl show-image clone1 -machinectl rename clone1 clone2 -(! machinectl show-image clone1) -machinectl show-image clone2 -if lsattr -d /var/lib/machines >/dev/null; then - [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]] - machinectl read-only clone2 yes - [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == yes ]] - machinectl read-only clone2 no - [[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]] -fi -machinectl remove clone2 -for i in {0..4}; do - machinectl clone container1 "clone$i" -done -machinectl remove clone{0..4} -for i in {0..4}; do - machinectl clone container1 ".hidden$i" -done -machinectl list-images --all -test -d /var/lib/machines/.hidden1 -machinectl clean -test ! -d /var/lib/machines/.hidden1 - -# Prepare a simple raw container -mkdir -p /tmp/mnt -dd if=/dev/zero of=/tmp/container.raw bs=1M count=64 -mkfs.ext4 /tmp/container.raw -mount -o loop /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 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 show-image --property=Type --value container-raw-reimport)" == "raw" ]] -rm -f /tmp/container{,-export}.raw - -# Prepare a simple tar.gz container -tar -pczf /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 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 - -# 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" ]] -machinectl start container-dir -rm -fr /tmp/container.dir - -timeout 10 bash -c "until machinectl clean --all; do sleep .5; done" - -NSPAWN_FRAGMENT="machinectl-test-$RANDOM.nspawn" -cat >"/var/lib/machines/$NSPAWN_FRAGMENT" </tmp/fragment.nspawn <"$OCI/config.json" <"$OCI/config.json" </prestart" - ], - "env" : [ - "PRESTART_FOO=prestart_bar", - "ALSO_FOO=also_bar" - ], - "timeout" : 666 - }, - { - "path" : "/bin/touch", - "args" : [ - "/tmp/also-prestart" - ] - } - ], - "poststart" : [ - { - "path" : "/bin/sh", - "args" : [ - "touch", - "/poststart" - ] - } - ], - "poststop" : [ - { - "path" : "/bin/sh", - "args" : [ - "touch", - "/poststop" - ] - } - ] - }, - "annotations" : { - "hello.world" : "1", - "foo" : "bar" - } -} -EOF -# Create a simple "entrypoint" script that validates that the container -# is created correctly according to the OCI config -cat >"$OCI/rootfs/entrypoint.sh" <"$OCI/config.json" <"$OCI/config.json" <"$root/bin/getent" <<\EOF -#!/bin/bash - -if [[ $# -eq 0 ]]; then - : -elif [[ $1 == passwd ]]; then - echo "testuser:x:1000:1000:testuser:/:/bin/sh" -elif [[ $1 == initgroups ]]; then - echo "testuser" -fi -EOF - chmod +x "$root/bin/getent" - systemd-nspawn --directory="$root" bash -xec '[[ $USER == root ]]' - systemd-nspawn --directory="$root" --user=testuser bash -xec '[[ $USER == testuser ]]' - - # --settings= + .nspawn files - mkdir -p /run/systemd/nspawn/ - uuid="deadbeef-dead-dead-beef-000000000000" - echo -ne "[Exec]\nMachineID=deadbeef-dead-dead-beef-111111111111" >/run/systemd/nspawn/foo-bar.nspawn - systemd-nspawn --directory="$root" \ - --machine=foo-bar \ - --settings=yes \ - bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]' - systemd-nspawn --directory="$root" \ - --machine=foo-bar \ - --uuid="$uuid" \ - --settings=yes \ - bash -xec "[[ \$container_uuid == $uuid ]]" - systemd-nspawn --directory="$root" \ - --machine=foo-bar \ - --uuid="$uuid" \ - --settings=override \ - bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]' - systemd-nspawn --directory="$root" \ - --machine=foo-bar \ - --uuid="$uuid" \ - --settings=trusted \ - bash -xec "[[ \$container_uuid == $uuid ]]" - - # Mounts - mkdir "$tmpdir"/{1,2,3} - touch "$tmpdir/1/one" "$tmpdir/2/two" "$tmpdir/3/three" - touch "$tmpdir/foo" - # --bind= - systemd-nspawn --directory="$root" \ - ${COVERAGE_BUILD_DIR:+--bind="$COVERAGE_BUILD_DIR"} \ - --bind="$tmpdir:/foo" \ - --bind="$tmpdir:/also-foo:noidmap,norbind" \ - bash -xec 'test -e /foo/foo; touch /foo/bar; test -e /also-foo/bar' - test -e "$tmpdir/bar" - # --bind-ro= - systemd-nspawn --directory="$root" \ - --bind-ro="$tmpdir:/foo" \ - --bind-ro="$tmpdir:/bar:noidmap,norbind" \ - bash -xec 'test -e /foo/foo; touch /foo/baz && exit 1; touch /bar && exit 1; true' - # --inaccessible= - systemd-nspawn --directory="$root" \ - --inaccessible=/var \ - bash -xec 'touch /var/foo && exit 1; true' - # --tmpfs= - systemd-nspawn --directory="$root" \ - --tmpfs=/var:rw,nosuid,noexec \ - bash -xec 'touch /var/nope' - test ! -e "$root/var/nope" - # --overlay= - systemd-nspawn --directory="$root" \ - --overlay="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \ - bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/foo' - test -e "$tmpdir/3/foo" - # --overlay-ro= - systemd-nspawn --directory="$root" \ - --overlay-ro="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \ - bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/nope && exit 1; true' - test ! -e "$tmpdir/3/nope" - rm -fr "$tmpdir" - - # --port (sanity only) - systemd-nspawn --network-veth --directory="$root" --port=80 --port=90 true - systemd-nspawn --network-veth --directory="$root" --port=80:8080 true - systemd-nspawn --network-veth --directory="$root" --port=tcp:80 true - systemd-nspawn --network-veth --directory="$root" --port=tcp:80:8080 true - systemd-nspawn --network-veth --directory="$root" --port=udp:80 true - systemd-nspawn --network-veth --directory="$root" --port=udp:80:8080 --port=tcp:80:8080 true - (! systemd-nspawn --network-veth --directory="$root" --port= true) - (! systemd-nspawn --network-veth --directory="$root" --port=-1 true) - (! systemd-nspawn --network-veth --directory="$root" --port=: true) - (! systemd-nspawn --network-veth --directory="$root" --port=icmp:80:8080 true) - (! systemd-nspawn --network-veth --directory="$root" --port=tcp::8080 true) - (! systemd-nspawn --network-veth --directory="$root" --port=8080: true) - # Exercise adding/removing ports from an interface - systemd-nspawn --directory="$root" \ - --network-veth \ - --port=6667 \ - --port=80:8080 \ - --port=udp:53 \ - --port=tcp:22:2222 \ - bash -xec 'ip addr add dev host0 10.0.0.10/24; ip a; ip addr del dev host0 10.0.0.10/24' - - # --load-credential=, --set-credential= - echo "foo bar" >/tmp/cred.path - systemd-nspawn --directory="$root" \ - --load-credential=cred.path:/tmp/cred.path \ - --set-credential="cred.set:hello world" \ - bash -xec '[[ "$("/run/systemd/nspawn/$container.nspawn" <"$root/entrypoint.sh" <<\EOF -#!/bin/bash -set -ex - -env - -[[ "$1" == "foo bar" ]] -[[ "$2" == "bar baz" ]] - -[[ "$USER" == root ]] -[[ "$FOO" == bar ]] -[[ "$BAZ" == "hello world" ]] -[[ "$PWD" == /tmp ]] -[[ "$container_uuid" == f28f129b-5187-4b12-80a8-9421ec4b4ad4 ]] -[[ "$(ulimit -S -n)" -eq 1024 ]] -[[ "$(ulimit -H -n)" -eq 2048 ]] -[[ "$(ulimit -S -r)" -eq 8 ]] -[[ "$(ulimit -H -r)" -eq 16 ]] -[[ "$("/run/systemd/nspawn/$container.nspawn" <"$root/etc/passwd" - (! systemd-nspawn --directory="$root" \ - --private-users=pick \ - --bind-user=nspawn-bind-user-1 \ - --bind-user=nspawn-bind-user-2 \ - true) - rm -f "$root/etc/passwd" - - echo "nspawn-bind-user-2:x:1000:" >"$root/etc/group" - (! systemd-nspawn --directory="$root" \ - --private-users=pick \ - --bind-user=nspawn-bind-user-1 \ - --bind-user=nspawn-bind-user-2 \ - true) - rm -f "$root/etc/group" - - rm -fr "$root" -} - -testcase_bind_tmp_path() { - # https://github.com/systemd/systemd/issues/4789 - local root - - root="$(mktemp -d /var/lib/machines/testsuite-13.bind-tmp-path.XXX)" - create_dummy_container "$root" - : >/tmp/bind - systemd-nspawn --register=no \ - --directory="$root" \ - --bind=/tmp/bind \ - bash -c 'test -e /tmp/bind' - - rm -fr "$root" /tmp/bind -} - -testcase_norbind() { - # https://github.com/systemd/systemd/issues/13170 - local root - - root="$(mktemp -d /var/lib/machines/testsuite-13.norbind-path.XXX)" - mkdir -p /tmp/binddir/subdir - echo -n "outer" >/tmp/binddir/subdir/file - mount -t tmpfs tmpfs /tmp/binddir/subdir - echo -n "inner" >/tmp/binddir/subdir/file - create_dummy_container "$root" - - systemd-nspawn --register=no \ - --directory="$root" \ - --bind=/tmp/binddir:/mnt:norbind \ - bash -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; exit 1; fi' - - umount /tmp/binddir/subdir - rm -fr "$root" /tmp/binddir/ -} - -rootidmap_cleanup() { - local dir="${1:?}" - - mountpoint -q "$dir/bind" && umount "$dir/bind" - rm -fr "$dir" -} - -testcase_rootidmap() { - local root cmd permissions - local owner=1000 - - root="$(mktemp -d /var/lib/machines/testsuite-13.rootidmap-path.XXX)" - # Create ext4 image, as ext4 supports idmapped-mounts. - mkdir -p /tmp/rootidmap/bind - dd if=/dev/zero of=/tmp/rootidmap/ext4.img bs=4k count=2048 - mkfs.ext4 /tmp/rootidmap/ext4.img - mount /tmp/rootidmap/ext4.img /tmp/rootidmap/bind - trap "rootidmap_cleanup /tmp/rootidmap/" RETURN - - touch /tmp/rootidmap/bind/file - chown -R "$owner:$owner" /tmp/rootidmap/bind - - create_dummy_container "$root" - cmd='PERMISSIONS=$(stat -c "%u:%g" /mnt/file); if [[ $PERMISSIONS != "0:0" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /mnt/other_file' - if ! SYSTEMD_LOG_TARGET=console \ - systemd-nspawn --register=no \ - --directory="$root" \ - --bind=/tmp/rootidmap/bind:/mnt:rootidmap \ - bash -c "$cmd" |& tee nspawn.out; then - if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then - echo "idmapped mounts are not supported, skipping the test..." - return 0 - fi - - return 1 - fi - - permissions=$(stat -c "%u:%g" /tmp/rootidmap/bind/other_file) - if [[ $permissions != "$owner:$owner" ]]; then - echo "*** wrong permissions: $permissions" - [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1 - fi -} - -testcase_notification_socket() { - # https://github.com/systemd/systemd/issues/4944 - local root - local cmd='echo a | nc -U -u -w 1 /run/host/notify' - - root="$(mktemp -d /var/lib/machines/testsuite-13.check_notification_socket.XXX)" - create_dummy_container "$root" - - systemd-nspawn --register=no --directory="$root" bash -x -c "$cmd" - systemd-nspawn --register=no --directory="$root" -U bash -x -c "$cmd" - - rm -fr "$root" -} - -testcase_os_release() { - local root entrypoint os_release_source - - root="$(mktemp -d /var/lib/machines/testsuite-13.os-release.XXX)" - create_dummy_container "$root" - entrypoint="$root/entrypoint.sh" - cat >"$entrypoint" <<\EOF -#!/usr/bin/bash -ex - -. /tmp/os-release -[[ -n "${ID:-}" && "$ID" != "$container_host_id" ]] && exit 1 -[[ -n "${VERSION_ID:-}" && "$VERSION_ID" != "$container_host_version_id" ]] && exit 1 -[[ -n "${BUILD_ID:-}" && "$BUILD_ID" != "$container_host_build_id" ]] && exit 1 -[[ -n "${VARIANT_ID:-}" && "$VARIANT_ID" != "$container_host_variant_id" ]] && exit 1 - -cd /tmp -(cd /run/host && md5sum os-release) | md5sum -c -EOF - chmod +x "$entrypoint" - - os_release_source="/etc/os-release" - if [[ ! -r "$os_release_source" ]]; then - os_release_source="/usr/lib/os-release" - elif [[ -L "$os_release_source" ]]; then - # Ensure that /etc always wins if available - cp --remove-destination -fv /usr/lib/os-release /etc/os-release - echo MARKER=1 >>/etc/os-release - fi - - systemd-nspawn --register=no \ - --directory="$root" \ - --bind="$os_release_source:/tmp/os-release" \ - "${entrypoint##"$root"}" - - if grep -q MARKER /etc/os-release; then - ln -svrf /usr/lib/os-release /etc/os-release - fi - - rm -fr "$root" -} - -testcase_machinectl_bind() { - local service_path service_name root container_name ec - local cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep .5; done; exit 1;' - - root="$(mktemp -d /var/lib/machines/testsuite-13.machinectl-bind.XXX)" - create_dummy_container "$root" - container_name="$(basename "$root")" - - service_path="$(mktemp /run/systemd/system/nspawn-machinectl-bind-XXX.service)" - service_name="${service_path##*/}" - cat >"$service_path" </dev/null || ! selinuxenabled; then - echo >&2 "SELinux is not enabled, skipping SELinux-related tests" - return 0 - fi - - local root - - root="$(mktemp -d /var/lib/machines/testsuite-13.selinux.XXX)" - create_dummy_container "$root" - chcon -R -t container_t "$root" - - systemd-nspawn --register=no \ - --boot \ - --directory="$root" \ - --selinux-apifs-context=system_u:object_r:container_file_t:s0:c0,c1 \ - --selinux-context=system_u:system_r:container_t:s0:c0,c1 - - rm -fr "$root" -} - -testcase_ephemeral_config() { - # https://github.com/systemd/systemd/issues/13297 - local root container_name - - root="$(mktemp -d /var/lib/machines/testsuite-13.ephemeral-config.XXX)" - create_dummy_container "$root" - container_name="$(basename "$root")" - - mkdir -p /run/systemd/nspawn/ - rm -f "/etc/systemd/nspawn/$container_name.nspawn" - cat >"/run/systemd/nspawn/$container_name.nspawn" <&2 "Unified cgroup hierarchy is not supported, skipping..." - return 0 - fi - - if [[ "$use_cgns" == "yes" && "$IS_CGNS_SUPPORTED" == "no" ]]; then - echo >&2 "CGroup namespaces are not supported, skipping..." - return 0 - fi - - root="$(mktemp -d "/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3.XXX")" - create_dummy_container "$root" - - SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ - systemd-nspawn --register=no \ - --directory="$root" \ - --boot - - SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ - systemd-nspawn --register=no \ - --directory="$root" \ - --private-network \ - --boot - - if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ - systemd-nspawn --register=no \ - --directory="$root" \ - --private-users=pick \ - --boot; then - [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "network" ]] && return 1 - else - [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "network" ]] && return 1 - fi - - if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ - systemd-nspawn --register=no \ - --directory="$root" \ - --private-network \ - --private-users=pick \ - --boot; then - [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "yes" ]] && return 1 - else - [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "yes" ]] && return 1 - fi - - local netns_opt="--network-namespace-path=/proc/self/ns/net" - local net_opt - local net_opts=( - "--network-bridge=lo" - "--network-interface=lo" - "--network-ipvlan=lo" - "--network-macvlan=lo" - "--network-veth" - "--network-veth-extra=lo" - "--network-zone=zone" - ) - - # --network-namespace-path and network-related options cannot be used together - for net_opt in "${net_opts[@]}"; do - echo "$netns_opt in combination with $net_opt should fail" - if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ - systemd-nspawn --register=no \ - --directory="$root" \ - --boot \ - "$netns_opt" \ - "$net_opt"; then - echo >&2 "unexpected pass" - return 1 - fi - done - - # allow combination of --network-namespace-path and --private-network - SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ - systemd-nspawn --register=no \ - --directory="$root" \ - --boot \ - --private-network \ - "$netns_opt" - - # test --network-namespace-path works with a network namespace created by "ip netns" - ip netns add nspawn_test - netns_opt="--network-namespace-path=/run/netns/nspawn_test" - SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ - systemd-nspawn --register=no \ - --directory="$root" \ - --network-namespace-path=/run/netns/nspawn_test \ - ip a | grep -v -E '^1: lo.*UP' - ip netns del nspawn_test - - rm -fr "$root" - - return 0 -} - -testcase_check_os_release() { - # https://github.com/systemd/systemd/issues/29185 - local base common_opts root - - base="$(mktemp -d /var/lib/machines/testsuite-13.check_os_release_base.XXX)" - root="$(mktemp -d /var/lib/machines/testsuite-13.check_os_release.XXX)" - create_dummy_container "$base" - cp -d "$base"/{bin,sbin,lib,lib64} "$root/" - common_opts=( - --boot - --register=no - --directory="$root" - --bind-ro="$base/usr:/usr" - ) - - # Empty /etc/ & /usr/ - (! systemd-nspawn "${common_opts[@]}") - (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}") - (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=foo systemd-nspawn "${common_opts[@]}") - SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}" - - # Empty /usr/ + a broken /etc/os-release -> /usr/os-release symlink - ln -svrf "$root/etc/os-release" "$root/usr/os-release" - (! systemd-nspawn "${common_opts[@]}") - (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}") - SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}" - - rm -fr "$root" "$base" -} - -run_testcases - -for api_vfs_writable in yes no network; do - matrix_run_one no no $api_vfs_writable - matrix_run_one yes no $api_vfs_writable - matrix_run_one no yes $api_vfs_writable - matrix_run_one yes yes $api_vfs_writable -done diff --git a/test/units/testsuite-13.nss-mymachines.sh b/test/units/testsuite-13.nss-mymachines.sh deleted file mode 100755 index b566c73..0000000 --- a/test/units/testsuite-13.nss-mymachines.sh +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2016 -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -at_exit() { - set +e - - machinectl kill --signal=KILL nss-mymachines-{noip,singleip,manyips} - mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done" - rm -f /run/systemd/nspawn/*.nspawn -} - -trap at_exit EXIT - -mkdir -p /var/lib/machines -mount -t tmpfs tmpfs /var/lib/machines - -# Create a bunch of containers that: -# 1) Have no IP addresses assigned -create_dummy_container /var/lib/machines/nss-mymachines-noip -cat >/var/lib/machines/nss-mymachines-noip/sbin/init <<\EOF -#!/usr/bin/bash -ex - -ip addr show dev ve-noip -touch /initialized -sleep infinity & -# Run the sleep command asynchronously, so bash is able to process signals -while :; do - wait || : -done -EOF -# 2) Have one IP address assigned (IPv4 only) -create_dummy_container /var/lib/machines/nss-mymachines-singleip -cat >/var/lib/machines/nss-mymachines-singleip/sbin/init <<\EOF -#!/usr/bin/bash -ex - -ip addr add 10.1.0.2/24 dev ve-singleip -ip addr show dev ve-singleip -touch /initialized -sleep infinity & -while :; do - wait || : -done -EOF -# 3) Have bunch of IP addresses assigned (both IPv4 and IPv6) -create_dummy_container /var/lib/machines/nss-mymachines-manyips -cat >/var/lib/machines/nss-mymachines-manyips/sbin/init <<\EOF -#!/usr/bin/bash -ex - -ip addr add 10.2.0.2/24 dev ve-manyips -for i in {100..120}; do - ip addr add 10.2.0.$i/24 dev ve-manyips -done -ip addr add fd00:dead:beef:cafe::2/64 dev ve-manyips -ip addr show dev ve-manyips -touch /initialized -sleep infinity -while :; do - wait || : -done -EOF -# Create the respective .nspawn config files -mkdir -p /run/systemd/nspawn -for container in noip singleip manyips; do - cat >"/run/systemd/nspawn/nss-mymachines-$container.nspawn" </dev/null || : - rm -f /{etc,run,usr/lib}/systemd/system/"$unit_name" - rm -fr /{etc,run,usr/lib}/systemd/system/"$unit_name".d - rm -fr /{etc,run,usr/lib}/systemd/system/"$unit_name".{wants,requires} - if [[ $unit_name == *@* ]]; then - base="${unit_name%@*}" - suffix="${unit_name##*.}" - systemctl stop "$base@"*."$suffix" 2>/dev/null || : - rm -f /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix" - rm -fr /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix".d - rm -fr /{etc,run,usr/lib}/systemd/system/"$base@"*."$suffix".{wants,requires} - fi -} - -clear_units() { - for u in "$@"; do - clear_unit "$u" - done - systemctl daemon-reload -} - -create_service() { - local service_name="${1:?}" - clear_units "${service_name}".service - - cat >/etc/systemd/system/"$service_name".service </run/systemd/system/service.d/override.conf <"/run/systemd/system/$dropin/override.conf" <"/run/systemd/system/$dropin/override.conf" </run/systemd/system/a-b-c.slice </etc/systemd/system/service.d/drop1.conf - echo -e '[Service]\nStandardInputText=bbb' >/etc/systemd/system/a-.service.d/drop2.conf - echo -e '[Service]\nStandardInputText=ccc' >/etc/systemd/system/a-b-.service.d/drop3.conf - echo -e '[Service]\nStandardInputText=ddd' >/etc/systemd/system/a-b-c.service.d/drop4.conf - - # There's no fragment yet, so this fails - systemctl cat a-b-c.service && exit 1 - - # xxx → eHh4Cg== - systemd-run -u a-b-c.service -p StandardInputData=eHh4Cg== sleep infinity - - data=$(systemctl show -P StandardInputData a-b-c.service) - # xxx\naaa\n\bbb\nccc\nddd\n → eHh4… - test "$data" = "eHh4CmFhYQpiYmIKY2NjCmRkZAo=" - - # Do a reload and check again - systemctl daemon-reload - data=$(systemctl show -P StandardInputData a-b-c.service) - test "$data" = "eHh4CmFhYQpiYmIKY2NjCmRkZAo=" - - clear_units a-b-c.service - rm /etc/systemd/system/service.d/drop1.conf \ - /etc/systemd/system/a-.service.d/drop2.conf \ - /etc/systemd/system/a-b-.service.d/drop3.conf -} - -testcase_transient_slice_dropins() { - echo "Testing dropins for a transient slice..." - echo "*** test transient slice drop-ins" - - # FIXME: implement reloading of individual units. - # - # The settings here are loaded twice. For most settings it doesn't matter, - # but Documentation is not deduplicated, so we current get repeated entried - # which is a bug. - - mkdir -p /etc/systemd/system/slice.d - mkdir -p /etc/systemd/system/a-.slice.d - mkdir -p /etc/systemd/system/a-b-.slice.d - mkdir -p /etc/systemd/system/a-b-c.slice.d - - echo -e '[Unit]\nDocumentation=man:drop1' >/etc/systemd/system/slice.d/drop1.conf - echo -e '[Unit]\nDocumentation=man:drop2' >/etc/systemd/system/a-.slice.d/drop2.conf - echo -e '[Unit]\nDocumentation=man:drop3' >/etc/systemd/system/a-b-.slice.d/drop3.conf - echo -e '[Unit]\nDocumentation=man:drop4' >/etc/systemd/system/a-b-c.slice.d/drop4.conf - - # Invoke daemon-reload to make sure that the call below doesn't fail - systemctl daemon-reload - - # No fragment is required, so this works - systemctl cat a-b-c.slice - - busctl call \ - org.freedesktop.systemd1 \ - /org/freedesktop/systemd1 \ - org.freedesktop.systemd1.Manager \ - StartTransientUnit 'ssa(sv)a(sa(sv))' \ - 'a-b-c.slice' 'replace' \ - 1 \ - 'Documentation' as 1 'man:drop5' \ - 0 - - data=$(systemctl show -P Documentation a-b-c.slice) - test "$data" = "man:drop1 man:drop2 man:drop3 man:drop4 man:drop5 man:drop1 man:drop2 man:drop3 man:drop4" - - # Do a reload and check again - systemctl daemon-reload - data=$(systemctl show -P Documentation a-b-c.slice) - test "$data" = "man:drop5 man:drop1 man:drop2 man:drop3 man:drop4" - - clear_units a-b-c.slice - rm /etc/systemd/system/slice.d/drop1.conf \ - /etc/systemd/system/a-.slice.d/drop2.conf \ - /etc/systemd/system/a-b-.slice.d/drop3.conf -} - -testcase_template_dropins() { - echo "Testing template dropins..." - - create_services foo bar@ yup@ - - # Declare some deps to check if the body was loaded - cat >>/etc/systemd/system/bar@.service <>/etc/systemd/system/yup@.service </usr/lib/systemd/system/test15-a.service.d/override.conf </usr/lib/systemd/system/test15-a.service.d/wants-b.conf </tmp/testsuite-15-test15-a-dropin-directory/override.conf <>"$TESTLOG" - fi -} - -wait_for_timeout() -{ - local unit="$1" - local time="$2" - - while [[ $time -gt 0 ]]; do - if [[ "$(systemctl show --property=Result "$unit")" == "Result=timeout" ]]; then - return 0 - fi - - sleep 1 - time=$((time - 1)) - done - - journalctl -u "$unit" >>"$TESTLOG" - - return 1 -} - -# This checks all stages, start, runtime and stop, can be extended by -# EXTEND_TIMEOUT_USEC - -wait_for success_all - -# These check that EXTEND_TIMEOUT_USEC that occurs at greater than the -# extend timeout interval but less then the stage limit (TimeoutStartSec, -# RuntimeMaxSec, TimeoutStopSec) still succeed. - -wait_for success_start -wait_for success_runtime -wait_for success_stop - -# These ensure that EXTEND_TIMEOUT_USEC will still timeout in the -# appropriate stage, after the stage limit, when the EXTEND_TIMEOUT_USEC -# message isn't sent within the extend timeout interval. - -wait_for fail_start startfail -wait_for fail_stop stopfail -wait_for fail_runtime runtimefail - -# These ensure that RuntimeMaxSec is honored for scope and service units -# when they are created. -runtime_max_sec=5 - -systemd-run \ - --property=RuntimeMaxSec=${runtime_max_sec}s \ - -u runtime-max-sec-test-1.service \ - /usr/bin/sh -c "while true; do sleep 1; done" -wait_for_timeout runtime-max-sec-test-1.service $((runtime_max_sec + 2)) - -systemd-run \ - --property=RuntimeMaxSec=${runtime_max_sec}s \ - --scope \ - -u runtime-max-sec-test-2.scope \ - /usr/bin/sh -c "while true; do sleep 1; done" & -wait_for_timeout runtime-max-sec-test-2.scope $((runtime_max_sec + 2)) - -# These ensure that RuntimeMaxSec is honored for scope and service -# units if the value is changed and then the manager is reloaded. -systemd-run \ - -u runtime-max-sec-test-3.service \ - /usr/bin/sh -c "while true; do sleep 1; done" -mkdir -p /etc/systemd/system/runtime-max-sec-test-3.service.d/ -cat > /etc/systemd/system/runtime-max-sec-test-3.service.d/override.conf << EOF -[Service] -RuntimeMaxSec=${runtime_max_sec}s -EOF -systemctl daemon-reload -wait_for_timeout runtime-max-sec-test-3.service $((runtime_max_sec + 2)) - -systemd-run \ - --scope \ - -u runtime-max-sec-test-4.scope \ - /usr/bin/sh -c "while true; do sleep 1; done" & - -# Wait until the unit is running to avoid race with creating the override. -until systemctl is-active runtime-max-sec-test-4.scope; do - sleep 1 -done -mkdir -p /etc/systemd/system/runtime-max-sec-test-4.scope.d/ -cat > /etc/systemd/system/runtime-max-sec-test-4.scope.d/override.conf << EOF -[Scope] -RuntimeMaxSec=${runtime_max_sec}s -EOF -systemctl daemon-reload -wait_for_timeout runtime-max-sec-test-4.scope $((runtime_max_sec + 2)) - -if [[ -f "$TESTLOG" ]]; then - # no mv - cp "$TESTLOG" /test.log - exit 1 -fi - -touch /testok diff --git a/test/units/testsuite-17.00.sh b/test/units/testsuite-17.00.sh deleted file mode 100755 index d2aec60..0000000 --- a/test/units/testsuite-17.00.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -# Tests for issue #28588 and #28653. - -# On boot, services need to be started in the following order: -# 1. systemd-tmpfiles-setup-dev-early.service -# 2. systemd-sysusers.service -# 3. systemd-tmpfiles-setup-dev.service -# 4. systemd-udevd.service - -output="$(systemctl show --property After --value systemd-udevd.service)" -assert_in "systemd-tmpfiles-setup-dev-early.service" "$output" -assert_in "systemd-sysusers.service" "$output" -assert_in "systemd-tmpfiles-setup-dev.service" "$output" - -output="$(systemctl show --property After --value systemd-tmpfiles-setup-dev.service)" -assert_in "systemd-tmpfiles-setup-dev-early.service" "$output" -assert_in "systemd-sysusers.service" "$output" - -output="$(systemctl show --property After --value systemd-sysusers.service)" -assert_in "systemd-tmpfiles-setup-dev-early.service" "$output" - -check_owner_and_mode() { - local dev=${1?} - local user=${2?} - local group=${3?} - local mode=${4:-} - - if [[ -e "$dev" ]]; then - assert_in "$user" "$(stat --format=%U "$dev")" - assert_in "$group" "$(stat --format=%G "$dev")" - if [[ -n "$mode" ]]; then - assert_in "$mode" "$(stat --format=%#0a "$dev")" - fi - fi - - return 0 -} - -# Check owner and access mode specified in static-nodes-permissions.conf -check_owner_and_mode /dev/snd/seq root audio 0660 -check_owner_and_mode /dev/snd/timer root audio 0660 -check_owner_and_mode /dev/loop-control root disk 0660 -check_owner_and_mode /dev/net/tun root root 0666 -check_owner_and_mode /dev/fuse root root 0666 -check_owner_and_mode /dev/vfio/vfio root root 0666 -check_owner_and_mode /dev/kvm root kvm -check_owner_and_mode /dev/vhost-net root kvm -check_owner_and_mode /dev/vhost-vsock root kvm - -exit 0 diff --git a/test/units/testsuite-17.01.sh b/test/units/testsuite-17.01.sh deleted file mode 100755 index 44f36f5..0000000 --- a/test/units/testsuite-17.01.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex -set -o pipefail - -mkdir -p /run/udev/rules.d/ - -rm -f /run/udev/rules.d/50-testsuite.rules -udevadm control --reload -udevadm trigger --settle /dev/sda - -while : ; do - ( - udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service - udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service - systemctl show -p WantedBy foobar.service | grep -q -v sda - systemctl show -p WantedBy waldo.service | grep -q -v sda - ) && break - - sleep .5 -done - -cat >/run/udev/rules.d/50-testsuite.rules </run/udev/rules.d/50-testsuite.rules </run/udev/rules.d/50-testsuite.rules </run/udev/rules.d/50-testsuite.rules </run/udev/rules.d/50-testsuite.rules <"$TMPDIR"/monitor.txt & - KILL_PID="$!" - - # make sure that 'udevadm monitor' actually monitor uevents - sleep 1 - - since="$(date '+%H:%M:%S')" - - # add another interface which will conflict with an existing interface - ip link add foobar type dummy - - for _ in {1..40}; do - if ( - grep -q 'ACTION=add' "$TMPDIR"/monitor.txt - grep -q 'DEVPATH=/devices/virtual/net/foobar' "$TMPDIR"/monitor.txt - grep -q 'SUBSYSTEM=net' "$TMPDIR"/monitor.txt - grep -q 'INTERFACE=foobar' "$TMPDIR"/monitor.txt - grep -q 'ID_NET_DRIVER=dummy' "$TMPDIR"/monitor.txt - grep -q 'ID_NET_NAME=foobar' "$TMPDIR"/monitor.txt - # Even when network interface renaming is failed, SYSTEMD_ALIAS with the conflicting name will be broadcast. - grep -q 'SYSTEMD_ALIAS=/sys/subsystem/net/devices/hoge' "$TMPDIR"/monitor.txt - grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt - grep -q 'UDEV_WORKER_ERRNO=17' "$TMPDIR"/monitor.txt - grep -q 'UDEV_WORKER_ERRNO_NAME=EEXIST' "$TMPDIR"/monitor.txt - ); then - cat "$TMPDIR"/monitor.txt - found=1 - break - fi - sleep .5 - done - test -n "$found" - - timeout 30 bash -c "until journalctl _PID=1 _COMM=systemd --since $since | grep -q 'foobar: systemd-udevd failed to process the device, ignoring: File exists'; do sleep 1; done" - # check if the invalid SYSTEMD_ALIAS property for the interface foobar is ignored by PID1 - assert_eq "$(systemctl show --property=SysFSPath --value /sys/subsystem/net/devices/hoge)" "/sys/devices/virtual/net/hoge" -} - -test_netif_renaming_conflict - -exit 0 diff --git a/test/units/testsuite-17.03.sh b/test/units/testsuite-17.03.sh deleted file mode 100755 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}" </etc/udev/udev.conf <"$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.04.sh b/test/units/testsuite-17.04.sh deleted file mode 100755 index d1c3c85..0000000 --- a/test/units/testsuite-17.04.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex -set -o pipefail - -mkdir -p /run/udev/rules.d/ - -test ! -f /run/udev/tags/added/c1:3 -test ! -f /run/udev/tags/changed/c1:3 -udevadm info /dev/null | grep -E 'E: (TAGS|CURRENT_TAGS)=.*:(added|changed):' && exit 1 - -cat >/run/udev/rules.d/50-testsuite.rules </run/udev/rules.d/00-debug.rules </run/udev/rules.d/50-testsuite.rules </run/udev/rules.d/50-testsuite.rules < 1 )) && sleep 0.5 - if systemctl --quiet is-active "${1?}"; then - return 0 - fi - done - return 1 -)} - -wait_service_inactive() {( - set +ex - for i in {1..20}; do - (( i > 1 )) && sleep 0.5 - systemctl --quiet is-active "${1?}" - if [[ "$?" == "3" ]]; then - return 0 - fi - done - return 1 -)} - -mkdir -p /run/systemd/system -cat >/run/systemd/system/both.service </run/systemd/system/on-add.service </run/systemd/system/on-change.service </run/udev/rules.d/50-testsuite.rules </run/systemd/system/both.service </run/systemd/system/on-add.service </run/systemd/system/on-change.service </run/udev/rules.d/50-testsuite.rules < 1)) && sleep .5 - - ( - systemctl -q is-active /dev/test/symlink-to-null-on-add - ! systemctl -q is-active /dev/test/symlink-to-null-on-change - systemctl -q is-active /sys/test/alias-to-null-on-add - ! systemctl -q is-active /sys/test/alias-to-null-on-change - ) && break -done -assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-add -assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-change -assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-add -assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-change - -udevadm trigger --settle --action change /dev/null -for i in {1..20}; do - ((i > 1)) && sleep .5 - - ( - ! systemctl -q is-active /dev/test/symlink-to-null-on-add - systemctl -q is-active /dev/test/symlink-to-null-on-change - ! systemctl -q is-active /sys/test/alias-to-null-on-add - systemctl -q is-active /sys/test/alias-to-null-on-change - ) && break -done -assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-add -assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-change -assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-add -assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-change - -udevadm trigger --settle --action add /dev/null -for i in {1..20}; do - ((i > 1)) && sleep .5 - - ( - systemctl -q is-active /dev/test/symlink-to-null-on-add - ! systemctl -q is-active /dev/test/symlink-to-null-on-change - systemctl -q is-active /sys/test/alias-to-null-on-add - ! systemctl -q is-active /sys/test/alias-to-null-on-change - ) && break -done -assert_rc 0 systemctl -q is-active /dev/test/symlink-to-null-on-add -assert_rc 3 systemctl -q is-active /dev/test/symlink-to-null-on-change -assert_rc 0 systemctl -q is-active /sys/test/alias-to-null-on-add -assert_rc 3 systemctl -q is-active /sys/test/alias-to-null-on-change - -# cleanup -rm -f /run/udev/rules.d/50-testsuite.rules -udevadm control --reload - -exit 0 diff --git a/test/units/testsuite-17.09.sh b/test/units/testsuite-17.09.sh deleted file mode 100755 index 9993196..0000000 --- a/test/units/testsuite-17.09.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -# This is a test for issue #24987. - -mkdir -p /run/udev/rules.d/ -cat >/run/udev/rules.d/50-testsuite.rules <"$TMPDIR"/monitor.txt 2>&1 & -KILL_PID="$!" - -FOUND= -for _ in {1..40}; do - if grep -F 'UDEV - the event which udev sends out after rule processing' "$TMPDIR"/monitor.txt; then - FOUND=1 - break - fi - sleep .5 -done -[[ -n "$FOUND" ]] - -udevadm trigger --verbose --settle --action add /dev/null - -FOUND= -for _ in {1..40}; do - if ! grep -e 'UDEV *\[[0-9.]*\] *add *\/devices\/virtual\/mem\/null (mem)' "$TMPDIR"/monitor.txt; then - sleep .5 - continue - fi - - FOUND=1 - for i in {1..100}; do - if ! grep -F "$(printf 'XXX%03i=0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' "$i")" "$TMPDIR"/monitor.txt; then - FOUND= - break - fi - done - if [[ -n "$FOUND" ]]; then - break; - fi - - sleep .5 -done -[[ -n "$FOUND" ]] - -# cleanup -rm -f /run/udev/rules.d/50-testsuite.rules -udevadm control --reload - -kill "$KILL_PID" -rm -rf "$TMPDIR" - -exit 0 diff --git a/test/units/testsuite-17.10.sh b/test/units/testsuite-17.10.sh deleted file mode 100755 index f229dcf..0000000 --- a/test/units/testsuite-17.10.sh +++ /dev/null @@ -1,254 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -# Coverage test for udevadm - -# shellcheck disable=SC2317 -cleanup_17_10() { - set +e - - losetup -d "$loopdev" - rm -f "$blk" - - ip link delete "$netdev" -} - -# Set up some test devices -trap cleanup_17_10 EXIT - -netdev=dummy17.10 -ip link add $netdev type dummy - -blk="$(mktemp)" -dd if=/dev/zero of="$blk" bs=1M count=1 -loopdev="$(losetup --show -f "$blk")" - -udevadm -h - -udevadm control -e -udevadm control -l emerg -udevadm control -l alert -udevadm control -l crit -udevadm control -l err -udevadm control -l warning -udevadm control -l notice -udevadm control --log-level info -udevadm control --log-level debug -(! udevadm control -l hello) -udevadm control -s -udevadm control -S -udevadm control -R -udevadm control -p HELLO=world -udevadm control -m 42 -udevadm control --ping -udevadm control -t 5 -udevadm control -h - -udevadm info /dev/null -udevadm info /sys/class/net/$netdev -udevadm info "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)" -udevadm info --property DEVNAME /sys/class/net/$netdev -udevadm info --property DEVNAME --value /sys/class/net/$netdev -udevadm info --property HELLO /sys/class/net/$netdev -udevadm info -p class/net/$netdev -udevadm info -p /class/net/$netdev -udevadm info --json=off -p class/net/$netdev -udevadm info --json=pretty -p class/net/$netdev | jq . -udevadm info --json=short -p class/net/$netdev | jq . -udevadm info -n null -udevadm info -q all /sys/class/net/$netdev -udevadm info -q name /dev/null -udevadm info -q path /sys/class/net/$netdev -udevadm info -q property /sys/class/net/$netdev -udevadm info -q symlink /sys/class/net/$netdev -udevadm info -q name -r /dev/null -udevadm info --query symlink --root /sys/class/net/$netdev -(! udevadm info -q hello -r /sys/class/net/$netdev) -udevadm info -a /sys/class/net/$netdev -udevadm info -t >/dev/null -udevadm info --tree /sys/class/net/$netdev -udevadm info -x /sys/class/net/$netdev -udevadm info -x -q path /sys/class/net/$netdev -udevadm info -P TEST_ /sys/class/net/$netdev -udevadm info -d /dev/null -udevadm info -e >/dev/null -udevadm info -e --json=off >/dev/null -udevadm info -e --json=pretty | jq . >/dev/null -udevadm info -e --json=short | jq . >/dev/null -udevadm info -e --subsystem-match acpi >/dev/null -udevadm info -e --subsystem-nomatch acpi >/dev/null -udevadm info -e --attr-match ifindex=2 >/dev/null -udevadm info -e --attr-nomatch ifindex=2 >/dev/null -udevadm info -e --property-match SUBSYSTEM=acpi >/dev/null -udevadm info -e --tag-match systemd >/dev/null -udevadm info -e --sysname-match lo >/dev/null -udevadm info -e --name-match /sys/class/net/$netdev >/dev/null -udevadm info -e --parent-match /sys/class/net/$netdev >/dev/null -udevadm info -e --initialized-match >/dev/null -udevadm info -e --initialized-nomatch >/dev/null -# udevadm info -c -udevadm info -w /sys/class/net/$netdev -udevadm info --wait-for-initialization=5 /sys/class/net/$netdev -udevadm info -h - -assert_rc 124 timeout 1 udevadm monitor -assert_rc 124 timeout 1 udevadm monitor -k -assert_rc 124 timeout 1 udevadm monitor -u -assert_rc 124 timeout 1 udevadm monitor -s net -assert_rc 124 timeout 1 udevadm monitor --subsystem-match net/$netdev -assert_rc 124 timeout 1 udevadm monitor -t systemd -assert_rc 124 timeout 1 udevadm monitor --tag-match hello -udevadm monitor -h - -udevadm settle -udevadm settle -t 5 -udevadm settle -E /sys/class/net/$netdev -udevadm settle -h - -udevadm test /dev/null -udevadm info /sys/class/net/$netdev -udevadm test "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)" -udevadm test -a add /sys/class/net/$netdev -udevadm test -a change /sys/class/net/$netdev -udevadm test -a move /sys/class/net/$netdev -udevadm test -a online /sys/class/net/$netdev -udevadm test -a offline /sys/class/net/$netdev -udevadm test -a bind /sys/class/net/$netdev -udevadm test -a unbind /sys/class/net/$netdev -udevadm test -a help /sys/class/net/$netdev -udevadm test --action help -(! udevadm test -a hello /sys/class/net/$netdev) -udevadm test -N early /sys/class/net/$netdev -udevadm test -N late /sys/class/net/$netdev -udevadm test --resolve-names never /sys/class/net/$netdev -(! udevadm test -N hello /sys/class/net/$netdev) -udevadm test -h - -# udevadm test-builtin path_id "$loopdev" -udevadm test-builtin net_id /sys/class/net/$netdev -udevadm test-builtin net_id "$(systemd-escape -p --suffix device /sys/devices/virtual/net/$netdev)" -udevadm test-builtin -a add net_id /sys/class/net/$netdev -udevadm test-builtin -a remove net_id /sys/class/net/$netdev -udevadm test-builtin -a change net_id /sys/class/net/$netdev -udevadm test-builtin -a move net_id /sys/class/net/$netdev -udevadm test-builtin -a online net_id /sys/class/net/$netdev -udevadm test-builtin -a offline net_id /sys/class/net/$netdev -udevadm test-builtin -a bind net_id /sys/class/net/$netdev -udevadm test-builtin -a unbind net_id /sys/class/net/$netdev -udevadm test-builtin -a help net_id /sys/class/net/$netdev -udevadm test-builtin net_setup_link /sys/class/net/$netdev -udevadm test-builtin blkid "$loopdev" -udevadm test-builtin input_id /sys/class/net/$netdev -udevadm test-builtin keyboard /dev/null -# udevadm test-builtin kmod /sys/class/net/$netdev -udevadm test-builtin uaccess /dev/null -# udevadm test-builtin usb_id dev/null -(! udevadm test-builtin hello /sys/class/net/$netdev) -# systemd-hwdb update is extremely slow when combined with sanitizers and run -# in a VM without acceleration, so let's just skip the one particular test -# if we detect this combination -if ! [[ -v ASAN_OPTIONS && "$(systemd-detect-virt -v)" == "qemu" ]]; then - modprobe scsi_debug - scsidev=$(readlink -f /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/[0-9]*) - mkdir -p /etc/udev/hwdb.d - cat >/etc/udev/hwdb.d/99-test.hwdb <"${workdir}/default_output_1_success" <"${workdir}/default_output_1_fail" <"${workdir}/output_0_files" <"${out}" - if [ -f "${exo}" ]; then - diff -u "${exo}" "${out}" - elif [ -f "${rules}" ]; then - diff -u "${workdir}/default_output_1_success" "${out}" - fi -} - -assert_0() { - assert_0_impl "$@" - next_test_number -} - -assert_1_impl() { - local rc - set +e - udevadm verify "$@" >"${out}" 2>"${err}" - rc=$? - set -e - assert_eq "$rc" 1 - - if [ -f "${exp}" ]; then - diff -u "${exp}" "${err}" - fi - - if [ -f "${exo}" ]; then - diff -u "${exo}" "${out}" - elif [ -f "${rules}" ]; then - diff -u "${workdir}/default_output_1_fail" "${out}" - fi -} - -assert_1() { - assert_1_impl "$@" - next_test_number -} - -# initialize variables -next_test_number - -assert_0 -h -assert_0 --help -assert_0 -V -assert_0 --version -assert_0 /dev/null - -# unrecognized option '--unknown' -assert_1 --unknown -# option requires an argument -- 'N' -assert_1 -N -# --resolve-names= takes "early" or "never" -assert_1 -N now -# option '--resolve-names' requires an argument -assert_1 --resolve-names -# --resolve-names= takes "early" or "never" -assert_1 --resolve-names=now -# Failed to parse rules file ./nosuchfile: No such file or directory -assert_1 ./nosuchfile -# Failed to parse rules file ./nosuchfile: No such file or directory -cat >"${exo}" <"${exo}" -assert_0 --root="${workdir}" --no-summary - -# Directory with a single *.rules file. -cp "${workdir}/default_output_1_success" "${exo}" -assert_0 "${rules_dir}" - -# Combination of --root= and FILEs is not supported. -assert_1 --root="${workdir}" /dev/null -# No rules files found in nosuchdir -assert_1 --root=nosuchdir - -cd "${rules_dir}" - -# UDEV_LINE_SIZE 16384 -printf '%16383s\n' ' ' >"${rules}" -assert_0 "${rules}" - -# Failed to parse rules file ${rules}: No buffer space available -printf '%16384s\n' ' ' >"${rules}" -echo "Failed to parse rules file ${rules}: No buffer space available" >"${exp}" -assert_1 "${rules}" - -{ - printf 'RUN+="/bin/true",%8174s\\\n' ' ' - printf 'RUN+="/bin/false"%8174s\\\n' ' ' - echo -} >"${rules}" -assert_0 "${rules}" - -printf 'RUN+="/bin/true"%8176s\\\n #\n' ' ' ' ' >"${rules}" -echo >>"${rules}" -cat >"${exp}" <"${rules}" -cat >"${exp}" <"${rules}" - cat >"${exp}" <"${rules}" - cat >"${exp}" <"${rules}" <<'EOF' -KERNEL=="a|b", KERNEL=="a|c", NAME="d" -KERNEL=="a|b", KERNEL!="a|c", NAME="d" -KERNEL!="a", KERNEL!="b", NAME="c" -KERNEL=="|a", KERNEL=="|b", NAME="c" -KERNEL=="*", KERNEL=="a*", NAME="b" -KERNEL=="a*", KERNEL=="c*|ab*", NAME="d" -PROGRAM="a", RESULT=="b" -EOF -assert_0 "${rules}" - -echo 'GOTO="a"' >"${rules}" -cat >"${exp}" <"${rules}" <<'EOF' -GOTO="a" -LABEL="a" -EOF -assert_0 "${rules}" - -cat >"${rules}" <<'EOF' -GOTO="b" -LABEL="b" -LABEL="b" -EOF -cat >"${exp}" <"${rules}" <<'EOF' -GOTO="a" -LABEL="a", LABEL="b" -EOF -cat >"${exp}" <"${rules}" <<'EOF' -KERNEL!="", KERNEL=="?*", KERNEL=="", NAME="a" -EOF -cat >"${exp}" <"${rules}" <<'EOF' -ACTION=="a"NAME="b" -EOF -cat >"${exp}" <"${rules}" <<'EOF' -ACTION=="a" ,NAME="b" -EOF -cat >"${exp}" <"${workdir}/${exp}" -cd - -assert_1 --root="${workdir}" -cd - - -# udevadm verify path/ -sed "s|sample-[0-9]*.rules|${workdir}/${rules_dir}/&|" sample-*.exp >"${workdir}/${exp}" -cd - -assert_1 "${rules_dir}" -cd - - -exit 0 diff --git a/test/units/testsuite-17.12.sh b/test/units/testsuite-17.12.sh deleted file mode 100755 index ccc91bf..0000000 --- a/test/units/testsuite-17.12.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -create_link_file() { - name=${1?} - - mkdir -p /run/systemd/network/ - cat >/run/systemd/network/10-test.link < "$rules" <<'EOF' -ENV{FOO}=="?*", ENV{PROP_FOO}="$env{FOO}" -ENV{BAR}=="?*", ENV{PROP_BAR}="$env{BAR}" -EOF - -udevadm control --reload - -test_not_property /dev/null PROP_FOO -test_not_property /dev/null PROP_BAR - -: Setting of a property works - -udevadm control --property FOO=foo -udevadm trigger --action change --settle /dev/null -test_property /dev/null PROP_FOO foo -test_not_property /dev/null PROP_BAR - -: Change of a property works - -udevadm control --property FOO=goo -udevadm trigger --action change --settle /dev/null -test_property /dev/null PROP_FOO goo - -: Removal of a property works - -udevadm control --property FOO= -udevadm trigger --action change --settle /dev/null -test_not_property /dev/null PROP_FOO - -: Repeated removal of a property does nothing - -udevadm control --property FOO= -udevadm trigger --action change --settle /dev/null -test_not_property /dev/null PROP_FOO - -: Multiple properties can be set at once - -udevadm control --property FOO=foo --property BAR=bar -udevadm trigger --action change --settle /dev/null -test_property /dev/null PROP_FOO foo -test_property /dev/null PROP_BAR bar - -: Multiple setting of the same property is handled correctly - -udevadm control --property FOO=foo --property FOO=42 -udevadm trigger --action change --settle /dev/null -test_property /dev/null PROP_FOO 42 - -: Mix of settings and removals of the same property is handled correctly - -udevadm control -p FOO= -p FOO=foo -p BAR=car -p BAR= -udevadm trigger --action change --settle /dev/null -test_property /dev/null PROP_FOO foo -test_not_property /dev/null PROP_BAR - -exit 0 diff --git a/test/units/testsuite-17.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-17.sh b/test/units/testsuite-17.sh deleted file mode 100755 index 14ceeba..0000000 --- a/test/units/testsuite-17.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/test-control.sh -. "$(dirname "$0")"/test-control.sh - -udevadm settle - -run_subtests - -touch /testok diff --git a/test/units/testsuite-18.service b/test/units/testsuite-18.service deleted file mode 100644 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-18.sh b/test/units/testsuite-18.sh deleted file mode 100755 index 44b792f..0000000 --- a/test/units/testsuite-18.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -systemd-run --wait -p FailureAction=poweroff true -(! systemd-run --wait -p SuccessAction=poweroff false) - -if ! test -f /firstphase ; then - echo OK >/firstphase - systemd-run --wait -p SuccessAction=reboot true -else - echo OK >/testok - systemd-run --wait -p FailureAction=poweroff false -fi - -sleep infinity diff --git a/test/units/testsuite-19.ExitType-cgroup.sh b/test/units/testsuite-19.ExitType-cgroup.sh deleted file mode 100755 index cd221d7..0000000 --- a/test/units/testsuite-19.ExitType-cgroup.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env bash -set -eux - -# Test ExitType=cgroup - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -if [[ "$(get_cgroup_hierarchy)" != unified ]]; then - echo "Skipping $0 as we're not running with the unified cgroup hierarchy" - exit 0 -fi - -systemd-analyze log-level debug - -# Multiple level process tree, parent process stays up -cat >/tmp/test19-exit-cgroup.sh < sleep -sleep infinity & -disown - -# process tree: systemd -> bash -> bash -> sleep -((sleep infinity); true) & - -systemd-notify --ready - -# Run the stop/kill command -\$1 & - -# process tree: systemd -> bash -> sleep -sleep infinity -EOF -chmod +x /tmp/test19-exit-cgroup.sh - -# service should be stopped cleanly -systemd-run --wait \ - --unit=one \ - --property="Type=notify" \ - --property="ExitType=cgroup" \ - /tmp/test19-exit-cgroup.sh 'systemctl stop one' - -# same thing with a truthy exec condition -systemd-run --wait \ - --unit=two \ - --property="Type=notify" \ - --property="ExitType=cgroup" \ - --property="ExecCondition=true" \ - /tmp/test19-exit-cgroup.sh 'systemctl stop two' - -# false exec condition: systemd-run should exit immediately with status code: 1 -(! systemd-run --wait \ - --unit=three \ - --property="Type=notify" \ - --property="ExitType=cgroup" \ - --property="ExecCondition=false" \ - /tmp/test19-exit-cgroup.sh) - -# service should exit uncleanly (main process exits with SIGKILL) -(! systemd-run --wait \ - --unit=four \ - --property="Type=notify" \ - --property="ExitType=cgroup" \ - /tmp/test19-exit-cgroup.sh 'systemctl kill --signal 9 four') - - -# Multiple level process tree, parent process exits quickly -cat >/tmp/test19-exit-cgroup-parentless.sh < sleep -sleep infinity & - -# process tree: systemd -> bash -> sleep -((sleep infinity); true) & - -systemd-notify --ready - -# Run the stop/kill command after this bash process exits -(sleep 1; \$1) & -EOF -chmod +x /tmp/test19-exit-cgroup-parentless.sh - -# service should be stopped cleanly -systemd-run --wait \ - --unit=five \ - --property="Type=notify" \ - --property="ExitType=cgroup" \ - /tmp/test19-exit-cgroup-parentless.sh 'systemctl stop five' - -# service should still exit cleanly despite SIGKILL (the main process already exited cleanly) -systemd-run --wait \ - --unit=six \ - --property="Type=notify" \ - --property="ExitType=cgroup" \ - /tmp/test19-exit-cgroup-parentless.sh 'systemctl kill --signal 9 six' - - -systemd-analyze log-level info diff --git a/test/units/testsuite-19.cleanup-slice.sh b/test/units/testsuite-19.cleanup-slice.sh deleted file mode 100755 index 5d63160..0000000 --- a/test/units/testsuite-19.cleanup-slice.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -export SYSTEMD_LOG_LEVEL=debug - -# Create service with KillMode=none inside a slice -cat </run/systemd/system/test19cleanup.service -[Unit] -Description=Test 19 cleanup Service -[Service] -Slice=test19cleanup.slice -Type=exec -ExecStart=sleep infinity -KillMode=none -EOF -cat </run/systemd/system/test19cleanup.slice -[Unit] -Description=Test 19 cleanup Slice -EOF - -# Start service -systemctl start test19cleanup.service -assert_rc 0 systemd-cgls /test19cleanup.slice - -pid=$(systemctl show --property MainPID --value test19cleanup) -ps "$pid" - -# Stop slice -# The sleep process will not be killed because of KillMode=none -# Since there is still a process running under it, the /test19cleanup.slice cgroup won't be removed -systemctl stop test19cleanup.slice - -ps "$pid" - -# Kill sleep process manually -kill -s TERM "$pid" -while kill -0 "$pid" 2>/dev/null; do sleep 0.1; done - -timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice/test19cleanup.service >& /dev/null; do sleep .5; done' -assert_rc 1 systemd-cgls /test19cleanup.slice/test19cleanup.service - -# Check that empty cgroup /test19cleanup.slice has been removed -timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice >& /dev/null; do sleep .5; done' -assert_rc 1 systemd-cgls /test19cleanup.slice diff --git a/test/units/testsuite-19.delegate.sh b/test/units/testsuite-19.delegate.sh deleted file mode 100755 index 74d36c4..0000000 --- a/test/units/testsuite-19.delegate.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# Test cgroup delegation in the unified hierarchy - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -if [[ "$(get_cgroup_hierarchy)" != unified ]]; then - echo "Skipping $0 as we're not running with the unified cgroup hierarchy" - exit 0 -fi - -at_exit() { - set +e - userdel -r test -} - -systemd-run --wait \ - --unit=test-0.service \ - --property="DynamicUser=1" \ - --property="Delegate=" \ - test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \ - -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.procs -a \ - -w /sys/fs/cgroup/system.slice/test-0.service/cgroup.subtree_control - -# Test if this also works for some of the more recent attrs the kernel might or might not support -for attr in cgroup.threads memory.oom.group memory.reclaim ; do - - if grep -q "$attr" /sys/kernel/cgroup/delegate ; then - systemd-run --wait \ - --unit=test-0.service \ - --property="DynamicUser=1" \ - --property="Delegate=" \ - test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \ - -w /sys/fs/cgroup/system.slice/test-0.service/"$attr" - fi -done - -systemd-run --wait \ - --unit=test-1.service \ - --property="DynamicUser=1" \ - --property="Delegate=memory pids" \ - grep -q memory /sys/fs/cgroup/system.slice/test-1.service/cgroup.controllers - -systemd-run --wait \ - --unit=test-2.service \ - --property="DynamicUser=1" \ - --property="Delegate=memory pids" \ - grep -q pids /sys/fs/cgroup/system.slice/test-2.service/cgroup.controllers - -# "io" is not among the controllers enabled by default for all units, verify that -grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers - -# Run a service with "io" enabled, and verify it works -systemd-run --wait \ - --unit=test-3.service \ - --property="IOAccounting=yes" \ - --property="Slice=system-foo-bar-baz.slice" \ - grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test-3.service/cgroup.controllers - -# We want to check if "io" is removed again from the controllers -# list. However, PID 1 (rightfully) does this asynchronously. In order -# to force synchronization on this, let's start a short-lived service -# which requires PID 1 to refresh the cgroup tree, so that we can -# verify that this all works. -systemd-run --wait --unit=test-4.service true - -# And now check again, "io" should have vanished -grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers - -# Check that unprivileged delegation works for scopes -useradd test ||: -systemd-run --uid=test \ - --property="User=test" \ - --property="Delegate=yes" \ - --slice workload.slice \ - --unit test-workload0.scope\ - --scope \ - test -w /sys/fs/cgroup/workload.slice/test-workload0.scope -a \ - -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.procs -a \ - -w /sys/fs/cgroup/workload.slice/test-workload0.scope/cgroup.subtree_control - -# Verify that DelegateSubgroup= affects ownership correctly -unit="test-subgroup-$RANDOM.service" -systemd-run --wait \ - --unit="$unit" \ - --property="DynamicUser=1" \ - --property="Delegate=pids" \ - --property="DelegateSubgroup=foo" \ - test -w "/sys/fs/cgroup/system.slice/$unit" -a \ - -w "/sys/fs/cgroup/system.slice/$unit/foo" - -# Check that for the subgroup also attributes that aren't covered by -# regular (i.e. main cgroup) delegation ownership rules are delegated properly -if test -f /sys/fs/cgroup/cgroup.max.depth; then - unit="test-subgroup-$RANDOM.service" - systemd-run --wait \ - --unit="$unit" \ - --property="DynamicUser=1" \ - --property="Delegate=pids" \ - --property="DelegateSubgroup=zzz" \ - test -w "/sys/fs/cgroup/system.slice/$unit/zzz/cgroup.max.depth" -fi - -# Check that the invoked process itself is also in the subgroup -unit="test-subgroup-$RANDOM.service" -systemd-run --wait \ - --unit="$unit" \ - --property="DynamicUser=1" \ - --property="Delegate=pids" \ - --property="DelegateSubgroup=bar" \ - grep -q -x -F "0::/system.slice/$unit/bar" /proc/self/cgroup diff --git a/test/units/testsuite-19.service b/test/units/testsuite-19.service deleted file mode 100644 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-19.sh b/test/units/testsuite-19.sh deleted file mode 100755 index 9c2a033..0000000 --- a/test/units/testsuite-19.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/test-control.sh -. "$(dirname "$0")"/test-control.sh - -run_subtests - -touch /testok diff --git a/test/units/testsuite-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-21.sh b/test/units/testsuite-21.sh deleted file mode 100755 index 02673ab..0000000 --- a/test/units/testsuite-21.sh +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# Save the end.service state before we start fuzzing, as it might get changed -# on the fly by one of the fuzzers -systemctl list-jobs | grep -F 'end.service' && SHUTDOWN_AT_EXIT=1 || SHUTDOWN_AT_EXIT=0 - -# shellcheck disable=SC2317 -at_exit() { - set +e - # We have to call the end.service/poweroff explicitly even if it's specified on - # the kernel cmdline via systemd.wants=end.service, since dfuzzer calls - # org.freedesktop.systemd1.Manager.ClearJobs() which drops the service - # from the queue - if [[ $SHUTDOWN_AT_EXIT -ne 0 ]] && ! systemctl poweroff; then - # PID1 is down let's try to save the journal - journalctl --sync # journal can be down as well so let's ignore exit codes here - systemctl -ff poweroff # sync() and reboot(RB_POWER_OFF) - fi -} - -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 - -# TODO -# * check for possibly newly introduced buses? -BUS_LIST=( - org.freedesktop.home1 - org.freedesktop.hostname1 - org.freedesktop.import1 - org.freedesktop.locale1 - org.freedesktop.login1 - org.freedesktop.machine1 - org.freedesktop.portable1 - org.freedesktop.resolve1 - org.freedesktop.systemd1 - org.freedesktop.timedate1 -) - -# systemd-oomd requires PSI -if tail -n +1 /proc/pressure/{cpu,io,memory}; then - BUS_LIST+=( - org.freedesktop.oom1 - ) -fi - -# Some services require specific conditions: -# - systemd-timesyncd can't run in a container -# - systemd-networkd can run in a container if it has CAP_NET_ADMIN capability -if ! systemd-detect-virt --container; then - BUS_LIST+=( - org.freedesktop.network1 - org.freedesktop.timesync1 - ) -elif busctl introspect org.freedesktop.network1 / &>/dev/null; then - BUS_LIST+=( - org.freedesktop.network1 - ) -fi - -SESSION_BUS_LIST=( - org.freedesktop.systemd1 -) - -# Maximum payload size generated by dfuzzer (in bytes) - default: 50K -PAYLOAD_MAX=50000 -# Tweak the maximum payload size if we're running under sanitizers, since -# with larger payloads we start hitting reply timeouts -if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then - PAYLOAD_MAX=10000 # 10K -fi - -# Overmount /var/lib/machines with a size-limited tmpfs, as fuzzing -# the org.freedesktop.machine1 stuff makes quite a mess -mount -t tmpfs -o size=50M tmpfs /var/lib/machines - -# Fuzz both the system and the session buses (where applicable) -for bus in "${BUS_LIST[@]}"; do - echo "Bus: $bus (system)" - systemd-run --pipe --wait \ - -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus" - - # Let's reload the systemd daemon to test (de)serialization as well - systemctl daemon-reload - # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved - systemctl daemon-reexec -done - -umount /var/lib/machines - -for bus in "${SESSION_BUS_LIST[@]}"; do - echo "Bus: $bus (session)" - systemd-run --machine 'testuser@.host' --user --pipe --wait \ - -- dfuzzer -b "$PAYLOAD_MAX" -n "$bus" - - # Let's reload the systemd user daemon to test (de)serialization as well - systemctl --machine 'testuser@.host' --user daemon-reload - # FIXME: explicitly trigger reexecute until systemd/systemd#27204 is resolved - systemctl --machine 'testuser@.host' --user daemon-reexec -done - -touch /testok diff --git a/test/units/testsuite-22.01.sh b/test/units/testsuite-22.01.sh deleted file mode 100755 index 2276b75..0000000 --- a/test/units/testsuite-22.01.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# -# With "e" don't attempt to set permissions when file doesn't exist, see -# https://github.com/systemd/systemd/pull/6682. -set -eux -set -o pipefail - -rm -fr /tmp/test - -echo "e /tmp/test - root root 1d" | systemd-tmpfiles --create - - -test ! -e /tmp/test diff --git a/test/units/testsuite-22.02.sh b/test/units/testsuite-22.02.sh deleted file mode 100755 index b883a96..0000000 --- a/test/units/testsuite-22.02.sh +++ /dev/null @@ -1,167 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# -# Basic tests for types creating directories -set -eux -set -o pipefail - -rm -fr /tmp/{C,d,D,e} -mkdir /tmp/{C,d,D,e} - -# -# 'd' -# -mkdir /tmp/d/2 -chmod 777 /tmp/d/2 - -systemd-tmpfiles --create - < /tmp/C/3/f1 - -systemd-tmpfiles --create - </tmp/F/truncated -echo "This should be truncated" >/tmp/F/truncated-with-content - -systemd-tmpfiles --create - </tmp/F/rw-fs/foo -(! systemd-tmpfiles --create -) </tmp/F/rw-fs/foo -(! systemd-tmpfiles --create -) < fails. -(! systemd-tmpfiles --create -) </dev/null | grep -qv '^[\?\-]$'; then - TEST_TMPFILES_AGEBY_BTIME=1 -fi - -touch -a --date "2 minutes ago" /tmp/ageby/d1/f1 -touch -m --date "4 minutes ago" /tmp/ageby/d2/f1 - -# Create a bunch of other files. -touch /tmp/ageby/d{1,2}/f{2..4} - -# For "ctime". -touch /tmp/ageby/d3/f1 -chmod +x /tmp/ageby/d3/f1 -sleep 1 - -# For "btime". -touch /tmp/ageby/d4/f1 -sleep 1 - -# More files with recent "{a,b}time" values. -touch /tmp/ageby/d{3,4}/f{2..4} - -# Check for cleanup of "f1" in each of "/tmp/d{1..4}". -systemd-tmpfiles --clean - <<-EOF -d /tmp/ageby/d1 - - - a:1m - -e /tmp/ageby/d2 - - - m:3m - -D /tmp/ageby/d3 - - - c:2s - -EOF - -for d in d{1..3}; do - test ! -f "/tmp/ageby/${d}/f1" -done - -if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then - systemd-tmpfiles --clean - <<-EOF -d /tmp/ageby/d4 - - - b:1s - -EOF - - test ! -f "/tmp/ageby/d4/f1" -else - # Remove the file manually. - rm "/tmp/ageby/d4/f1" -fi - -# Check for an invalid "age" and "age-by" arguments. -for a in ':' ':1s' '2:1h' 'nope:42h' '" :7m"' 'm:' '::' '"+r^w-x:2/h"' 'b ar::64'; do - systemd-tmpfiles --clean - <&1 | grep -q -F 'Invalid age' -d /tmp/ageby - - - ${a} - -EOF -done - -for d in d{1..4}; do - for f in f{2..4}; do - test -f "/tmp/ageby/${d}/${f}" - done -done - -# Check for parsing with whitespace, repeated values -# for "age-by" (valid arguments). -for a in '" a:24h"' 'cccaab:2h' '" aa : 4h"' '" a A B C c:1h"'; do - systemd-tmpfiles --clean - </usr/lib/tmpfiles.d/L-z.conf</etc/tmpfiles.d/L-z.conf</usr/lib/tmpfiles.d/L-a.conf</etc/tmpfiles.d/L-a.conf</usr/lib/tmpfiles.d/w-$i.conf</etc/tmpfiles.d/w-$i.conf</usr/lib/tmpfiles.d/w-b.conf</tmp/testsuite-23.counter - -if [ "$counter" -eq 5 ] ; then - systemctl kill --kill-whom=main -sUSR1 testsuite-23.service -fi - -exec sleep 1.5 diff --git a/test/units/testsuite-23.ExecReload.sh b/test/units/testsuite-23.ExecReload.sh deleted file mode 100755 index b497f73..0000000 --- a/test/units/testsuite-23.ExecReload.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# Test ExecReload= (PR #13098) - -systemd-analyze log-level debug - -export SYSTEMD_PAGER= -SERVICE_PATH="$(mktemp /etc/systemd/system/execreloadXXX.service)" -SERVICE_NAME="${SERVICE_PATH##*/}" - -echo "[#1] Failing ExecReload= should not kill the service" -cat >"$SERVICE_PATH" <"$SERVICE_PATH" <"$SERVICE_PATH" </tmp/forking1.sh </tmp/forking2.sh </tmp/notify1.sh <&2' -cmp /tmp/stdout <&2' -cmp /tmp/stdout <&2' -cmp /tmp/stdout <&2' -cmp /tmp/stdout </run/systemd/system/test-service.service </run/systemd/system/test-service.service </run/systemd/system/tmp-hoge.mount </run/systemd/system/test-service.socket <$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.percentj-wantedby.sh b/test/units/testsuite-23.percentj-wantedby.sh deleted file mode 100755 index e9ffaba..0000000 --- a/test/units/testsuite-23.percentj-wantedby.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- -# ex: ts=8 sw=4 sts=4 et filetype=sh -set -eux -set -o pipefail - -# Ensure %j Wants directives work -systemd-run --wait \ - --property="Type=oneshot" \ - --property="Wants=testsuite-23-specifier-j-wants.service" \ - --property="After=testsuite-23-specifier-j-wants.service" \ - true - -test -f /tmp/tetsuite-23-specifier-j-done diff --git a/test/units/testsuite-23.runtime-bind-paths.sh b/test/units/testsuite-23.runtime-bind-paths.sh deleted file mode 100755 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.sh b/test/units/testsuite-23.sh deleted file mode 100755 index a929c8b..0000000 --- a/test/units/testsuite-23.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/test-control.sh -. "$(dirname "$0")"/test-control.sh - -# Note: the signal shenanigans are necessary for the Upholds= tests -run_subtests_with_signals SIGUSR1 SIGUSR2 SIGRTMIN+1 - -touch /testok diff --git a/test/units/testsuite-23.start-stop-no-reload.sh b/test/units/testsuite-23.start-stop-no-reload.sh deleted file mode 100755 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 </run/systemd/system/testsuite-23-no-reload.service </run/systemd/system/testsuite-23-no-reload.service </run/systemd/system/testsuite-23-no-reload.target </run/systemd/system/testsuite-23-no-reload.service <&2 "Found units in failed state" - exit 1 -fi - -at_exit() { - set +e - - mountpoint -q /proc/cmdline && umount /proc/cmdline - rm -f /etc/crypttab - [[ -e /tmp/crypttab.bak ]] && cp -fv /tmp/crypttab.bak /etc/crypttab - [[ -n "${STORE_LOOP:-}" ]] && losetup -d "$STORE_LOOP" - [[ -n "${WORKDIR:-}" ]] && rm -rf "$WORKDIR" - - systemctl daemon-reload -} - -trap at_exit EXIT - -cryptsetup_start_and_check() { - local expect_fail=0 - local ec volume unit - - if [[ "${1:?}" == "-f" ]]; then - expect_fail=1 - shift - fi - - for volume in "$@"; do - unit="systemd-cryptsetup@$volume.service" - - # The unit existence check should always pass - [[ "$(systemctl show -P LoadState "$unit")" == loaded ]] - systemctl list-unit-files "$unit" - - systemctl start "$unit" && ec=0 || ec=$? - if [[ "$expect_fail" -ne 0 ]]; then - if [[ "$ec" -eq 0 ]]; then - echo >&2 "Unexpected pass when starting $unit" - return 1 - fi - - return 0 - fi - - if [[ "$ec" -ne 0 ]]; then - echo >&2 "Unexpected fail when starting $unit" - return 1 - fi - - systemctl status "$unit" - test -e "/dev/mapper/$volume" - systemctl stop "$unit" - test ! -e "/dev/mapper/$volume" - done - - return 0 -} - -# Note: some stuff (especially TPM-related) is already tested by TEST-70-TPM2, -# so focus more on other areas instead - -# Use a common workdir to make the cleanup easier -WORKDIR="$(mktemp -d)" - -# Prepare a couple of LUKS2-encrypted disk images -# -# 1) Image with an empty password -IMAGE_EMPTY="$WORKDIR/empty.img)" -IMAGE_EMPTY_KEYFILE="$WORKDIR/empty.keyfile" -IMAGE_EMPTY_KEYFILE_ERASE="$WORKDIR/empty-erase.keyfile" -IMAGE_EMPTY_KEYFILE_ERASE_FAIL="$WORKDIR/empty-erase-fail.keyfile)" -truncate -s 32M "$IMAGE_EMPTY" -echo -n passphrase >"$IMAGE_EMPTY_KEYFILE" -chmod 0600 "$IMAGE_EMPTY_KEYFILE" -cryptsetup luksFormat --batch-mode \ - --pbkdf pbkdf2 \ - --pbkdf-force-iterations 1000 \ - --use-urandom \ - "$IMAGE_EMPTY" "$IMAGE_EMPTY_KEYFILE" -PASSWORD=passphrase NEWPASSWORD="" systemd-cryptenroll --password "$IMAGE_EMPTY" -# Duplicate the key file to test keyfile-erase as well -cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE" -# The key should get erased even on a failed attempt, so test that too -cp -v "$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL" - -# 2) Image with a detached header and a key file offset + size -IMAGE_DETACHED="$WORKDIR/detached.img" -IMAGE_DETACHED_KEYFILE="$WORKDIR/detached.keyfile" -IMAGE_DETACHED_KEYFILE2="$WORKDIR/detached.keyfile2" -IMAGE_DETACHED_HEADER="$WORKDIR/detached.header" -truncate -s 32M "$IMAGE_DETACHED" -dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE" count=64 bs=1 -dd if=/dev/urandom of="$IMAGE_DETACHED_KEYFILE2" count=32 bs=1 -chmod 0600 "$IMAGE_DETACHED_KEYFILE" "$IMAGE_DETACHED_KEYFILE2" -cryptsetup luksFormat --batch-mode \ - --pbkdf pbkdf2 \ - --pbkdf-force-iterations 1000 \ - --use-urandom \ - --header "$IMAGE_DETACHED_HEADER" \ - --keyfile-offset 32 \ - --keyfile-size 16 \ - "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE" -# Also, add a second key file to key slot 8 -# Note: --key-slot= behaves as --new-key-slot= when used alone for backwards compatibility -cryptsetup luksAddKey --batch-mode \ - --header "$IMAGE_DETACHED_HEADER" \ - --key-file "$IMAGE_DETACHED_KEYFILE" \ - --keyfile-offset 32 \ - --keyfile-size 16 \ - --key-slot 8 \ - "$IMAGE_DETACHED" "$IMAGE_DETACHED_KEYFILE2" - -# Prepare a couple of dummy devices we'll store a copy of the detached header -# and one of the keys on to test if systemd-cryptsetup correctly mounts them -# when necessary -STORE_IMAGE="$WORKDIR/store.img" -truncate -s 64M "$STORE_IMAGE" -STORE_LOOP="$(losetup --show --find --partscan "$STORE_IMAGE")" -sfdisk "$STORE_LOOP" </etc/crypttab </tmp/cmdline.tmp -mount --bind /tmp/cmdline.tmp /proc/cmdline -# Run the systemd-cryptsetup-generator once explicitly, to collect coverage, -# as during daemon-reload we run generators in a sandbox -mkdir -p /tmp/systemd-cryptsetup-generator.out -/usr/lib/systemd/system-generators/systemd-cryptsetup-generator /tmp/systemd-cryptsetup-generator.out/ -systemctl daemon-reload -systemctl list-unit-files "systemd-cryptsetup@*" - -cryptsetup_start_and_check empty_key -test -e "$IMAGE_EMPTY_KEYFILE_ERASE" -cryptsetup_start_and_check empty_key_erase -test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE" -test -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL" -cryptsetup_start_and_check -f empty_key_erase_fail -test ! -e "$IMAGE_EMPTY_KEYFILE_ERASE_FAIL" -cryptsetup_start_and_check -f empty_fail{0..1} -cryptsetup_start_and_check empty{0..1} -# First, check if we correctly fail without any key -cryptsetup_start_and_check -f empty_nokey -# And now provide the key via /{etc,run}/cryptsetup-keys.d/ -mkdir -p /run/cryptsetup-keys.d -cp "$IMAGE_EMPTY_KEYFILE" /run/cryptsetup-keys.d/empty_nokey.key -cryptsetup_start_and_check empty_nokey - -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 - -touch /testok 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-25.sh b/test/units/testsuite-25.sh deleted file mode 100755 index b298c50..0000000 --- a/test/units/testsuite-25.sh +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -export SYSTEMD_PAGER=cat - -dd if=/dev/urandom of=/var/tmp/testimage.raw bs=$((1024*1024+7)) count=5 - -# Test import -machinectl import-raw /var/tmp/testimage.raw -machinectl image-status testimage -test -f /var/lib/machines/testimage.raw -cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw - -# Test export -machinectl export-raw testimage /var/tmp/testimage2.raw -cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw -rm /var/tmp/testimage2.raw - -# Test compressed export (gzip) -machinectl export-raw testimage /var/tmp/testimage2.raw.gz -gunzip /var/tmp/testimage2.raw.gz -cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw -rm /var/tmp/testimage2.raw - -# Test clone -machinectl clone testimage testimage3 -test -f /var/lib/machines/testimage3.raw -machinectl image-status testimage3 -test -f /var/lib/machines/testimage.raw -machinectl image-status testimage -cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw -cmp /var/tmp/testimage.raw /var/lib/machines/testimage3.raw - -# Test removal -machinectl remove testimage -test ! -f /var/lib/machines/testimage.raw -(! machinectl image-status testimage) - -# Test export of clone -machinectl export-raw testimage3 /var/tmp/testimage3.raw -cmp /var/tmp/testimage.raw /var/tmp/testimage3.raw -rm /var/tmp/testimage3.raw - -# Test rename -machinectl rename testimage3 testimage4 -test -f /var/lib/machines/testimage4.raw -machinectl image-status testimage4 -test ! -f /var/lib/machines/testimage3.raw -(! machinectl image-status testimage3) -cmp /var/tmp/testimage.raw /var/lib/machines/testimage4.raw - -# Test export of rename -machinectl export-raw testimage4 /var/tmp/testimage4.raw -cmp /var/tmp/testimage.raw /var/tmp/testimage4.raw -rm /var/tmp/testimage4.raw - -# Test removal -machinectl remove testimage4 -test ! -f /var/lib/machines/testimage4.raw -(! machinectl image-status testimage4) - -# → And now, let's test directory trees ← # - -# Set up a directory we can import -mkdir /var/tmp/scratch -mv /var/tmp/testimage.raw /var/tmp/scratch/ -touch /var/tmp/scratch/anotherfile -mkdir /var/tmp/scratch/adirectory -echo "piep" >/var/tmp/scratch/adirectory/athirdfile - -# Test import-fs -machinectl import-fs /var/tmp/scratch/ -test -d /var/lib/machines/scratch -machinectl image-status scratch - -# Test export-tar -machinectl export-tar scratch /var/tmp/scratch.tar.gz -test -f /var/tmp/scratch.tar.gz -mkdir /var/tmp/extract -(cd /var/tmp/extract ; tar xzf /var/tmp/scratch.tar.gz) -diff -r /var/tmp/scratch/ /var/tmp/extract/ -rm -rf /var/tmp/extract - -# Test import-tar -machinectl import-tar /var/tmp/scratch.tar.gz scratch2 -test -d /var/lib/machines/scratch2 -machinectl image-status scratch2 -diff -r /var/tmp/scratch/ /var/lib/machines/scratch2 - -# Test removal -machinectl remove scratch -test ! -f /var/lib/machines/scratch -(! machinectl image-status scratch) - -# Test clone -machinectl clone scratch2 scratch3 -test -d /var/lib/machines/scratch2 -machinectl image-status scratch2 -test -d /var/lib/machines/scratch3 -machinectl image-status scratch3 -diff -r /var/tmp/scratch/ /var/lib/machines/scratch3 - -# Test removal -machinectl remove scratch2 -test ! -f /var/lib/machines/scratch2 -(! machinectl image-status scratch2) - -# Test rename -machinectl rename scratch3 scratch4 -test -d /var/lib/machines/scratch4 -machinectl image-status scratch4 -test ! -f /var/lib/machines/scratch3 -(! machinectl image-status scratch3) -diff -r /var/tmp/scratch/ /var/lib/machines/scratch4 - -# Test removal -machinectl remove scratch4 -test ! -f /var/lib/machines/scratch4 -(! machinectl image-status scratch4) - -# Test import-tar hyphen/stdin pipe behavior -# shellcheck disable=SC2002 -cat /var/tmp/scratch.tar.gz | machinectl import-tar - scratch5 -test -d /var/lib/machines/scratch5 -machinectl image-status scratch5 -diff -r /var/tmp/scratch/ /var/lib/machines/scratch5 - -# Test export-tar hyphen/stdout pipe behavior -mkdir -p /var/tmp/extract -machinectl export-tar scratch5 - | tar xvf - -C /var/tmp/extract/ -diff -r /var/tmp/scratch/ /var/tmp/extract/ -rm -rf /var/tmp/extract - -rm -rf /var/tmp/scratch - -# Test removal -machinectl remove scratch5 -test ! -f /var/lib/machines/scratch5 -(! machinectl image-status scratch5) - -touch /testok diff --git a/test/units/testsuite-26.service b/test/units/testsuite-26.service deleted file mode 100644 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-26.sh b/test/units/testsuite-26.sh deleted file mode 100755 index 1e11c42..0000000 --- a/test/units/testsuite-26.sh +++ /dev/null @@ -1,465 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2016 -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -at_exit() { - if [[ -v UNIT_NAME && -e "/usr/lib/systemd/system/$UNIT_NAME" ]]; then - rm -fvr "/usr/lib/systemd/system/$UNIT_NAME" "/etc/systemd/system/$UNIT_NAME.d" "+4" - fi - - rm -f /etc/init.d/issue-24990 - return 0 -} - -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" -cat >"/usr/lib/systemd/system/$UNIT_NAME" <<\EOF -[Unit] -Description=systemctl test - -[Service] -ExecStart=sleep infinity -ExecReload=true - -# For systemctl clean -CacheDirectory=%n -ConfigurationDirectory=%n -LogsDirectory=%n -RuntimeDirectory=%n -StateDirectory=%n - -[Install] -WantedBy=multi-user.target -EOF - -# Configure the preset setting for the unit file -mkdir /run/systemd/system-preset/ -echo "disable $UNIT_NAME" >/run/systemd/system-preset/99-systemd-test.preset - -EDITOR='true' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null -[ ! -e "/etc/systemd/system/$UNIT_NAME.d/override.conf" ] - -printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' >"+4" -EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null -printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf" - -printf '%b' '[Service]\n' 'ExecStart=\n' 'ExecStart=sleep 10d' >"+4" -EDITOR='mv' script -ec 'systemctl edit "$UNIT_NAME"' /dev/null -printf '%s\n' '[Service]' 'ExecStart=' 'ExecStart=sleep 10d' | cmp - "/etc/systemd/system/$UNIT_NAME.d/override.conf" - -# Double free when editing a template unit (#26483) -EDITOR='true' script -ec 'systemctl edit user@0' /dev/null - -# Argument help -systemctl --state help -systemctl --signal help -systemctl --type help - -# list-dependencies -systemctl list-dependencies systemd-journald -systemctl list-dependencies --after systemd-journald -systemctl list-dependencies --before systemd-journald -systemctl list-dependencies --after --reverse systemd-journald -systemctl list-dependencies --before --reverse systemd-journald -systemctl list-dependencies --plain systemd-journald - -# list-* verbs -systemctl list-units -systemctl list-units --recursive -systemctl list-units --type=socket -systemctl list-units --type=service,timer -# Compat: --type= allows load states for compatibility reasons -systemctl list-units --type=loaded -systemctl list-units --type=loaded,socket -systemctl list-units --legend=yes -a "systemd-*" -systemctl list-units --state=active -systemctl list-units --with-dependencies systemd-journald.service -systemctl list-units --with-dependencies --after systemd-journald.service -systemctl list-units --with-dependencies --before --reverse systemd-journald.service -systemctl list-sockets -systemctl list-sockets --legend=no -a "*journal*" -systemctl list-sockets --show-types -systemctl list-sockets --state=listening -systemctl list-timers -a -l -systemctl list-jobs -systemctl list-jobs --after -systemctl list-jobs --before -systemctl list-jobs --after --before -systemctl list-jobs "*" -systemctl list-dependencies sysinit.target --type=socket,mount -systemctl list-dependencies multi-user.target --state=active -systemctl list-dependencies sysinit.target --state=mounted --all -systemctl list-paths -systemctl list-paths --legend=no -a "systemd*" - -test_list_unit_files() { - systemctl list-unit-files "$@" - systemctl list-unit-files "$@" "*journal*" -} - -test_list_unit_files -test_list_unit_files --root=/ - -# is-* verbs -# Should return 4 for a missing unit file -assert_rc 4 systemctl --quiet is-active not-found.service -assert_rc 4 systemctl --quiet is-failed not-found.service -assert_rc 4 systemctl --quiet is-enabled not-found.service -# is-active: return 3 when the unit exists but inactive -assert_rc 3 systemctl --quiet is-active "$UNIT_NAME" -# is-enabled: return 1 when the unit exists but disabled -assert_rc 1 systemctl --quiet is-enabled "$UNIT_NAME" - -# Basic service management -systemctl start --show-transaction "$UNIT_NAME" -systemctl status -n 5 "$UNIT_NAME" -systemctl is-active "$UNIT_NAME" -systemctl reload -T "$UNIT_NAME" -systemctl restart -T "$UNIT_NAME" -systemctl try-restart --show-transaction "$UNIT_NAME" -systemctl try-reload-or-restart --show-transaction "$UNIT_NAME" -systemctl kill "$UNIT_NAME" -(! systemctl is-active "$UNIT_NAME") -systemctl restart "$UNIT_NAME" -systemctl is-active "$UNIT_NAME" -systemctl restart "$UNIT_NAME" -systemctl stop "$UNIT_NAME" -(! systemctl is-active "$UNIT_NAME") - -assert_eq "$(systemctl is-system-running)" "$(systemctl is-failed)" - -# enable/disable/preset -test_enable_disable_preset() { - (! systemctl is-enabled "$@" "$UNIT_NAME") - systemctl enable "$@" "$UNIT_NAME" - systemctl is-enabled "$@" -l "$UNIT_NAME" - # We created a preset file for this unit above with a "disable" policy - systemctl preset "$@" "$UNIT_NAME" - (! systemctl is-enabled "$@" "$UNIT_NAME") - systemctl reenable "$@" "$UNIT_NAME" - systemctl is-enabled "$@" "$UNIT_NAME" - systemctl preset "$@" --preset-mode=enable-only "$UNIT_NAME" - systemctl is-enabled "$@" "$UNIT_NAME" - systemctl preset "$@" --preset-mode=disable-only "$UNIT_NAME" - (! systemctl is-enabled "$@" "$UNIT_NAME") - systemctl enable "$@" --runtime "$UNIT_NAME" - [[ -e "/run/systemd/system/multi-user.target.wants/$UNIT_NAME" ]] - systemctl is-enabled "$@" "$UNIT_NAME" - systemctl disable "$@" "$UNIT_NAME" - # The unit should be still enabled, as we didn't use the --runtime switch - systemctl is-enabled "$@" "$UNIT_NAME" - systemctl disable "$@" --runtime "$UNIT_NAME" - (! systemctl is-enabled "$@" "$UNIT_NAME") -} - -test_enable_disable_preset -test_enable_disable_preset --root=/ - -# mask/unmask/revert -test_mask_unmask_revert() { - systemctl disable "$@" "$UNIT_NAME" - [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]] - systemctl mask "$@" "$UNIT_NAME" - [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]] - systemctl unmask "$@" "$UNIT_NAME" - [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]] - systemctl mask "$@" "$UNIT_NAME" - [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked ]] - systemctl revert "$@" "$UNIT_NAME" - [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]] - systemctl mask "$@" --runtime "$UNIT_NAME" - [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]] - # This should be a no-op without the --runtime switch - systemctl unmask "$@" "$UNIT_NAME" - [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == masked-runtime ]] - systemctl unmask "$@" --runtime "$UNIT_NAME" - [[ "$(systemctl is-enabled "$@" "$UNIT_NAME")" == disabled ]] -} - -test_mask_unmask_revert -test_mask_unmask_revert --root=/ - -# add-wants/add-requires -(! systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service") -systemctl add-wants "$UNIT_NAME" "systemd-journald.service" -systemctl show -P Wants "$UNIT_NAME" | grep "systemd-journald.service" -(! systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service") -systemctl add-requires "$UNIT_NAME" "systemd-journald.service" -systemctl show -P Requires "$UNIT_NAME" | grep "systemd-journald.service" - -# set-property -systemctl set-property "$UNIT_NAME" IPAccounting=yes MemoryMax=1234567 -systemctl cat "$UNIT_NAME" -# These properties should be saved to a persistent storage -grep -r "IPAccounting=yes" "/etc/systemd/system.control/${UNIT_NAME}.d/" -grep -r "MemoryMax=1234567" "/etc/systemd/system.control/${UNIT_NAME}.d" -systemctl revert "$UNIT_NAME" -(! grep -r "IPAccounting=" "/etc/systemd/system.control/${UNIT_NAME}.d/") -(! grep -r "MemoryMax=" "/etc/systemd/system.control/${UNIT_NAME}.d/") -# Same stuff, but with --runtime, which should use /run -systemctl set-property --runtime "$UNIT_NAME" CPUAccounting=no CPUQuota=10% -systemctl cat "$UNIT_NAME" -grep -r "CPUAccounting=no" "/run/systemd/system.control/${UNIT_NAME}.d/" -grep -r "CPUQuota=10%" "/run/systemd/system.control/${UNIT_NAME}.d/" -systemctl revert "$UNIT_NAME" -(! grep -r "CPUAccounting=" "/run/systemd/system.control/${UNIT_NAME}.d/") -(! grep -r "CPUQuota=" "/run/systemd/system.control/${UNIT_NAME}.d/") - -# Failed-unit related tests -(! systemd-run --wait --unit "failed.service" /bin/false) -systemctl is-failed failed.service -systemctl --state=failed | grep failed.service -systemctl --failed | grep failed.service -systemctl reset-failed "fail*.service" -(! systemctl is-failed failed.service) - -# clean -systemctl restart "$UNIT_NAME" -systemctl stop "$UNIT_NAME" -# Check if the directories from *Directory= directives exist -# (except RuntimeDirectory= in /run, which is removed when the unit is stopped) -for path in /var/lib /var/cache /var/log /etc; do - [[ -e "$path/$UNIT_NAME" ]] -done -# Run the cleanup -for what in "" configuration state cache logs runtime all; do - systemctl clean ${what:+--what="$what"} "$UNIT_NAME" -done -# All respective directories should be removed -for path in /run /var/lib /var/cache /var/log /etc; do - [[ ! -e "$path/$UNIT_NAME" ]] -done - -# --timestamp -for value in pretty us µs utc us+utc µs+utc; do - systemctl show -P KernelTimestamp --timestamp="$value" -done - -# set-default/get-default -test_get_set_default() { - target="$(systemctl get-default "$@")" - systemctl set-default "$@" emergency.target - [[ "$(systemctl get-default "$@")" == emergency.target ]] - systemctl set-default "$@" "$target" - [[ "$(systemctl get-default "$@")" == "$target" ]] -} - -test_get_set_default -test_get_set_default --root=/ - -# show/status -systemctl show --property "" -# Pick a heavily sandboxed unit for the best effect on coverage -systemctl show systemd-logind.service -systemctl status -# Ignore the exit code in this case, as it might try to load non-existing units -systemctl status -a >/dev/null || : -systemctl status -a --state active,running,plugged >/dev/null -systemctl status "systemd-*.timer" -systemctl status "systemd-journald*.socket" -systemctl status "sys-devices-*-ttyS0.device" -systemctl status -- -.mount -systemctl status 1 - -# --marked -systemctl restart "$UNIT_NAME" -systemctl set-property "$UNIT_NAME" Markers=needs-restart -systemctl show -P Markers "$UNIT_NAME" | grep needs-restart -systemctl reload-or-restart --marked -(! systemctl show -P Markers "$UNIT_NAME" | grep needs-restart) - -# --dry-run with destructive verbs -# kexec is skipped intentionally, as it requires a bit more involved setup -VERBS=( - default - emergency - exit - halt - hibernate - hybrid-sleep - poweroff - reboot - rescue - suspend - suspend-then-hibernate -) - -for verb in "${VERBS[@]}"; do - systemctl --dry-run "$verb" - - if [[ "$verb" =~ (halt|poweroff|reboot) ]]; then - systemctl --dry-run --message "Hello world" "$verb" - systemctl --dry-run --no-wall "$verb" - systemctl --dry-run -f "$verb" - systemctl --dry-run -ff "$verb" - fi -done - -# Aux verbs & assorted checks -systemctl is-active "*-journald.service" -systemctl cat "*journal*" -systemctl cat "$UNIT_NAME" -systemctl help "$UNIT_NAME" -systemctl service-watchdogs -systemctl service-watchdogs "$(systemctl service-watchdogs)" - -# show/set-environment -# Make sure PATH is set -systemctl show-environment | grep -q '^PATH=' -# Let's add an entry and override a built-in one -systemctl set-environment PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/testaddition FOO=BAR -# Check that both are set -systemctl show-environment | grep -q '^PATH=.*testaddition$' -systemctl show-environment | grep -q '^FOO=BAR$' -systemctl daemon-reload -# Check again after the reload -systemctl show-environment | grep -q '^PATH=.*testaddition$' -systemctl show-environment | grep -q '^FOO=BAR$' -# Check that JSON output is supported -systemctl show-environment --output=json | grep -q '^{.*"FOO":"BAR".*}$' -# Drop both -systemctl unset-environment FOO PATH -# Check that one is gone and the other reverted to the built-in -systemctl show-environment | grep '^FOO=$' && exit 1 -systemctl show-environment | grep '^PATH=.*testaddition$' && exit 1 -systemctl show-environment | grep -q '^PATH=' -# Check import-environment -export IMPORT_THIS=hello -export IMPORT_THIS_TOO=world -systemctl import-environment IMPORT_THIS IMPORT_THIS_TOO -systemctl show-environment | grep "^IMPORT_THIS=$IMPORT_THIS" -systemctl show-environment | grep "^IMPORT_THIS_TOO=$IMPORT_THIS_TOO" -systemctl unset-environment IMPORT_THIS IMPORT_THIS_TOO -(! systemctl show-environment | grep "^IMPORT_THIS=") -(! systemctl show-environment | grep "^IMPORT_THIS_TOO=") - -# test for sysv-generator (issue #24990) -if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then - # This is configurable via -Dsysvinit-path=, but we can't get the value - # at runtime, so let's just support the two most common paths for now. - [[ -d /etc/rc.d/init.d ]] && SYSVINIT_PATH="/etc/rc.d/init.d" || SYSVINIT_PATH="/etc/init.d" - - # invalid dependency - cat >"${SYSVINIT_PATH:?}/issue-24990" <<\EOF -#!/bin/bash - -### BEGIN INIT INFO -# Provides:test1 test2 -# Required-Start:test1 $remote_fs $network -# Required-Stop:test1 $remote_fs $network -# Description:Test -# Short-Description: Test -### END INIT INFO - -case "$1" in - start) - echo "Starting issue-24990.service" - sleep 1000 & - ;; - stop) - echo "Stopping issue-24990.service" - sleep 10 & - ;; - *) - echo "Usage: service test {start|stop|restart|status}" - ;; -esac -EOF - - chmod +x "$SYSVINIT_PATH/issue-24990" - systemctl daemon-reload - [[ -L /run/systemd/generator.late/test1.service ]] - [[ -L /run/systemd/generator.late/test2.service ]] - assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service" - assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service" - output=$(systemctl cat issue-24990) - assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output" - assert_in "Description=LSB: Test" "$output" - assert_in "After=test1.service" "$output" - assert_in "After=remote-fs.target" "$output" - assert_in "After=network-online.target" "$output" - assert_in "Wants=network-online.target" "$output" - assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output" - assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output" - systemctl status issue-24990 || : - systemctl show issue-24990 - assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)" - assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)" - - if ! systemctl is-active network-online.target; then - systemctl start network-online.target - fi - - systemctl restart issue-24990 - systemctl stop issue-24990 - - # valid dependency - cat >"$SYSVINIT_PATH/issue-24990" <<\EOF -#!/bin/bash - -### BEGIN INIT INFO -# Provides:test1 test2 -# Required-Start:$remote_fs -# Required-Stop:$remote_fs -# Description:Test -# Short-Description: Test -### END INIT INFO - -case "$1" in - start) - echo "Starting issue-24990.service" - sleep 1000 & - ;; - stop) - echo "Stopping issue-24990.service" - sleep 10 & - ;; - *) - echo "Usage: service test {start|stop|restart|status}" - ;; -esac -EOF - - chmod +x "$SYSVINIT_PATH/issue-24990" - systemctl daemon-reload - [[ -L /run/systemd/generator.late/test1.service ]] - [[ -L /run/systemd/generator.late/test2.service ]] - assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service" - assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service" - output=$(systemctl cat issue-24990) - assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output" - assert_in "Description=LSB: Test" "$output" - assert_in "After=remote-fs.target" "$output" - assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output" - assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output" - systemctl status issue-24990 || : - systemctl show issue-24990 - assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)" - assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)" - - systemctl restart issue-24990 - systemctl stop issue-24990 -fi - -# %J in WantedBy= causes ABRT (#26467) -cat >/run/systemd/system/test-WantedBy.service </run/systemd/system.conf.d/10-timeout.conf </run/systemd/system/systemd-portabled.service.d/override.conf -[Service] -Environment=SYSTEMD_LOG_LEVEL=debug -EOF - -portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0 - -portablectl is-attached minimal-app0 -portablectl inspect /usr/share/minimal_0.raw minimal-app0.service -systemctl is-active minimal-app0.service -systemctl is-active minimal-app0-foo.service -systemctl is-active minimal-app0-bar.service && exit 1 - -portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0 - -portablectl is-attached minimal-app0 -portablectl inspect /usr/share/minimal_0.raw minimal-app0.service -systemctl is-active minimal-app0.service -systemctl is-active minimal-app0-bar.service -systemctl is-active minimal-app0-foo.service && exit 1 - -portablectl list | grep -q -F "minimal_1" -busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' - -portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0 - -portablectl list | grep -q -F "No images." -busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 - -# Ensure we don't regress (again) when using --force - -portablectl "${ARGS[@]}" attach --force --now --runtime /usr/share/minimal_0.raw minimal-app0 - -portablectl is-attached --force minimal-app0 -portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service -systemctl is-active minimal-app0.service -systemctl is-active minimal-app0-foo.service -systemctl is-active minimal-app0-bar.service && exit 1 - -portablectl "${ARGS[@]}" reattach --force --now --runtime /usr/share/minimal_1.raw minimal-app0 - -portablectl is-attached --force minimal-app0 -portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service -systemctl is-active minimal-app0.service -systemctl is-active minimal-app0-bar.service -systemctl is-active minimal-app0-foo.service && exit 1 - -portablectl list | grep -q -F "minimal_1" -busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' - -portablectl detach --force --now --runtime /usr/share/minimal_1.raw minimal-app0 - -portablectl list | grep -q -F "No images." -busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 - -# portablectl also works with directory paths rather than images - -unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw -unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw - -portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0 - -systemctl is-active minimal-app0.service -systemctl is-active minimal-app0-foo.service -systemctl is-active minimal-app0-bar.service && exit 1 - -portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0 - -systemctl is-active minimal-app0.service -systemctl is-active minimal-app0-bar.service -systemctl is-active minimal-app0-foo.service && exit 1 - -portablectl list | grep -q -F "minimal_1" -busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' - -portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0 - -portablectl list | grep -q -F "No images." -busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 - -portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app0 - -systemctl is-active app0.service -status="$(portablectl is-attached --extension app0 minimal_0)" -[[ "${status}" == "running-runtime" ]] - -grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf - -portablectl "${ARGS[@]}" reattach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0 - -systemctl is-active app0.service -status="$(portablectl is-attached --extension app0 minimal_1)" -[[ "${status}" == "running-runtime" ]] - -grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_1.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf - -portablectl detach --now --runtime --extension /usr/share/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 -portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_0.raw app0 - -systemctl is-active app0.service -status="$(portablectl is-attached --extension app0_1 minimal_0)" -[[ "${status}" == "running-runtime" ]] - -portablectl detach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_1.raw app0 -rm -f /tmp/app0_1.0.raw - -portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/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 -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 - -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 -systemctl daemon-reload -systemctl restart app1.service - -systemctl is-active app1.service -status="$(portablectl is-attached --extension app1 minimal_0)" -[[ "${status}" == "running-runtime" ]] - -portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1 - -# Ensure that the combination of read-only images, state directory and dynamic user works, and that -# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while -# after the service is attached before the file appears. -grep -q -F bar "${STATE_DIRECTORY}/app0/foo" -grep -q -F baz "${STATE_DIRECTORY}/app1/foo" - -# Ensure that we can override the check on extension-release.NAME -cp /usr/share/app0.raw /tmp/app10.raw -portablectl "${ARGS[@]}" attach --force --now --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 - -systemctl is-active app0.service -status="$(portablectl is-attached --extension /tmp/app10.raw /usr/share/minimal_0.raw)" -[[ "${status}" == "running-runtime" ]] - -portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/app10.raw" - -# Ensure that we can detach even when an image has been deleted already (stop the unit manually as -# portablectl won't find it) -rm -f /tmp/app10.raw -systemctl stop app0.service -portablectl detach --force --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 - -# portablectl also accepts confexts -portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app0.raw --extension /usr/share/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}" == "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 detach --now --runtime --extension /usr/share/app0.raw --extension /usr/share/conf0.raw /usr/share/minimal_0.raw app0 - -# 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 /usr/share/minimal_0.raw /tmp/rootdir - -# Fix up os-release to drop the valid PORTABLE_SERVICES field (because we are -# bypassing the sysext logic in portabled here it will otherwise not see the -# extensions additional valid prefix) -grep -v "^PORTABLE_PREFIXES=" /tmp/rootdir/etc/os-release >/tmp/os-release-fix/etc/os-release - -mount -t overlay overlay -o lowerdir=/tmp/os-release-fix:/tmp/app1:/tmp/rootdir /tmp/overlay - -grep . /tmp/overlay/usr/lib/extension-release.d/* -grep . /tmp/overlay/etc/os-release - -portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/overlay app1 - -systemctl is-active app1.service - -portablectl detach --now --runtime overlay app1 - -umount /tmp/overlay - -portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 - -systemctl is-active app0.service -systemctl is-active app1.service - -portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release -portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0 -portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2 -portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service -portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service - -grep -q -F "LogExtraFields=PORTABLE=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app0.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app0.service.d/20-portable.conf - -grep -q -F "LogExtraFields=PORTABLE=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app1.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app1.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app1.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf -grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app1.service.d/20-portable.conf - -portablectl detach --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 - -# Attempt to disable the app unit during detaching. Requires --copy=symlink to reproduce. -# Provides coverage for https://github.com/systemd/systemd/issues/23481 -portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0 -portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0 -# attach and detach again to check if all drop-in configs are removed even if the main unit files are removed -portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0 -portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0 - -umount /tmp/rootdir -umount /tmp/app0 -umount /tmp/app1 - -# Lack of ID field in os-release should be rejected, but it caused a crash in the past instead -mkdir -p /tmp/emptyroot/usr/lib -mkdir -p /tmp/emptyext/usr/lib/extension-release.d -touch /tmp/emptyroot/usr/lib/os-release -touch /tmp/emptyext/usr/lib/extension-release.d/extension-release.emptyext - -# Remote peer disconnected -> portabled crashed -res="$(! portablectl attach --extension /tmp/emptyext /tmp/emptyroot 2> >(grep "Remote peer disconnected"))" -test -z "${res}" - -touch /testok diff --git a/test/units/testsuite-30.service b/test/units/testsuite-30.service deleted file mode 100644 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-30.sh b/test/units/testsuite-30.sh deleted file mode 100755 index 104c87b..0000000 --- a/test/units/testsuite-30.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -systemd-analyze log-level debug - -systemctl disable --now systemd-timesyncd.service - -timedatectl set-timezone Europe/Berlin -timedatectl set-time 1980-10-15 - -systemd-run --on-timezone-change touch /tmp/timezone-changed -systemd-run --on-clock-change touch /tmp/clock-changed - -test ! -f /tmp/timezone-changed -test ! -f /tmp/clock-changed - -timedatectl set-timezone Europe/Kiev - -while test ! -f /tmp/timezone-changed ; do sleep .5 ; done - -timedatectl set-time 2018-1-1 - -while test ! -f /tmp/clock-changed ; do sleep .5 ; done - -systemd-analyze log-level info - -touch /testok diff --git a/test/units/testsuite-31.service b/test/units/testsuite-31.service deleted file mode 100644 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-31.sh b/test/units/testsuite-31.sh deleted file mode 100755 index 03aba36..0000000 --- a/test/units/testsuite-31.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -if journalctl -b -t systemd --grep '\.device: Changed plugged -> dead'; then - exit 1 -fi - -touch /testok diff --git a/test/units/testsuite-32.service b/test/units/testsuite-32.service deleted file mode 100644 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-32.sh b/test/units/testsuite-32.sh deleted file mode 100755 index 83b548a..0000000 --- a/test/units/testsuite-32.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# Let's run this test only if the "memory.oom.group" cgroupfs attribute -# exists. This test is a bit too strict, since the "memory.events"/"oom_kill" -# logic has been around since a longer time than "memory.oom.group", but it's -# an easier thing to test for, and also: let's not get confused by older -# kernels where the concept was still new. - -if test -f /sys/fs/cgroup/system.slice/testsuite-32.service/memory.oom.group; then - systemd-analyze log-level debug - - # Run a service that is guaranteed to be the first candidate for OOM killing - systemd-run --unit=oomtest.service \ - -p Type=exec -p OOMScoreAdjust=1000 -p OOMPolicy=stop -p MemoryAccounting=yes \ - sleep infinity - - # Trigger an OOM killer run - echo 1 >/proc/sys/kernel/sysrq - echo f >/proc/sysrq-trigger - - while : ; do - STATE="$(systemctl show -P ActiveState oomtest.service)" - [ "$STATE" = "failed" ] && break - sleep .5 - done - - RESULT="$(systemctl show -P Result oomtest.service)" - test "$RESULT" = "oom-kill" - - systemd-analyze log-level info -fi - -touch /testok diff --git a/test/units/testsuite-34.service b/test/units/testsuite-34.service deleted file mode 100644 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-34.sh b/test/units/testsuite-34.sh deleted file mode 100755 index d15b675..0000000 --- a/test/units/testsuite-34.sh +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -systemd-analyze log-level debug - -test_directory() { - local directory="$1" - local path="$2" - - # cleanup for previous invocation - for i in xxx xxx2 yyy zzz x:yz x:yz2; do - rm -rf "${path:?}/${i}" "${path:?}/private/${i}" - done - - # Set everything up without DynamicUser=1 - - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz touch "${path}"/zzz/test - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}=zzz:xxx zzz:xxx2" -p TemporaryFileSystem="${path}" bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test" - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test - (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing) - - test -d "${path}"/zzz - test ! -L "${path}"/zzz - test ! -e "${path}"/private/zzz - - test ! -e "${path}"/xxx - test ! -e "${path}"/private/xxx - test ! -e "${path}"/xxx2 - test ! -e "${path}"/private/xxx2 - test -L "${path}"/yyy - test ! -e "${path}"/private/yyy - - test -f "${path}"/zzz/test - test ! -e "${path}"/zzz/test-missing - - # Convert to DynamicUser=1 - - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}=zzz:xxx zzz:xxx2" \ - -p TemporaryFileSystem="${path}" -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test" - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test - (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=1 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing) - - test -L "${path}"/zzz - test -d "${path}"/private/zzz - - test ! -e "${path}"/xxx - test ! -e "${path}"/private/xxx - test ! -e "${path}"/xxx2 - test ! -e "${path}"/private/xxx2 - test -L "${path}"/yyy # previous symlink is not removed - test ! -e "${path}"/private/yyy - - test -f "${path}"/zzz/test - test ! -e "${path}"/zzz/test-missing - - # Convert back - - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz -p TemporaryFileSystem="${path}" test -f "${path}"/zzz/test - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:yyy test -f "${path}"/yyy/test - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}" test -f "${path}"/xxx/test - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}=zzz:xxx zzz:xxx2" -p TemporaryFileSystem="${path}" bash -c "test -f ${path}/xxx/test && test -f ${path}/xxx2/test" - systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz:xxx -p TemporaryFileSystem="${path}":ro test -f "${path}"/xxx/test - (! systemd-run --wait -p RuntimeDirectoryPreserve=yes -p DynamicUser=0 -p "${directory}"=zzz test -f "${path}"/zzz/test-missing) - - test -d "${path}"/zzz - test ! -L "${path}"/zzz - test ! -e "${path}"/private/zzz - - test ! -e "${path}"/xxx - test ! -e "${path}"/private/xxx - test ! -e "${path}"/xxx2 - test ! -e "${path}"/private/xxx2 - test -L "${path}"/yyy - test ! -e "${path}"/private/yyy - - test -f "${path}"/zzz/test - test ! -e "${path}"/zzz/test-missing - - # Exercise the unit parsing paths too - cat >/run/systemd/system/testservice-34.service </run/systemd/system/testservice-34-check-writable.service <<\EOF -[Unit] -Description=Check writable directories when DynamicUser= with StateDirectory= - -[Service] -# Relevant only for sanitizer runs -EnvironmentFile=-/usr/lib/systemd/systemd-asan-env - -Type=oneshot -DynamicUser=yes -StateDirectory=waldo quux/pief aaa/bbb aaa aaa/ccc xxx/yyy:aaa/111 xxx:aaa/222 xxx/zzz:aaa/333 - -# Make sure that the state directories are really the only writable directory besides the obvious candidates -ExecStart=bash -c ' \ - set -eux; \ - set -o pipefail; \ - declare -a writable_dirs; \ - readarray -t writable_dirs < <(find / \( -path /var/tmp -o -path /tmp -o -path /proc -o -path /dev/mqueue -o -path /dev/shm -o \ - -path /sys/fs/bpf -o -path /dev/.lxc -o -path /sys/devices/system/cpu \) \ - -prune -o -type d -writable -print 2>/dev/null | sort -u); \ - [[ "$${#writable_dirs[@]}" == "8" ]]; \ - [[ "$${writable_dirs[0]}" == "/var/lib/private/aaa" ]]; \ - [[ "$${writable_dirs[1]}" == "/var/lib/private/aaa/bbb" ]]; \ - [[ "$${writable_dirs[2]}" == "/var/lib/private/aaa/ccc" ]]; \ - [[ "$${writable_dirs[3]}" == "/var/lib/private/quux/pief" ]]; \ - [[ "$${writable_dirs[4]}" == "/var/lib/private/waldo" ]]; \ - [[ "$${writable_dirs[5]}" == "/var/lib/private/xxx" ]]; \ - [[ "$${writable_dirs[6]}" == "/var/lib/private/xxx/yyy" ]]; \ - [[ "$${writable_dirs[7]}" == "/var/lib/private/xxx/zzz" ]]; \ -' -EOF - systemctl daemon-reload - systemctl start testservice-34-check-writable.service -} - -test_directory "StateDirectory" "/var/lib" -test_directory "RuntimeDirectory" "/run" -test_directory "CacheDirectory" "/var/cache" -test_directory "LogsDirectory" "/var/log" - -test_check_writable - -systemd-analyze log-level info - -touch /testok diff --git a/test/units/testsuite-35.service b/test/units/testsuite-35.service deleted file mode 100644 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-35.sh b/test/units/testsuite-35.sh deleted file mode 100755 index 36e26da..0000000 --- a/test/units/testsuite-35.sh +++ /dev/null @@ -1,660 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/test-control.sh -. "$(dirname "$0")"/test-control.sh -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -cleanup_test_user() ( - set +ex - - pkill -u "$(id -u logind-test-user)" - sleep 1 - pkill -KILL -u "$(id -u logind-test-user)" - userdel -r logind-test-user - - return 0 -) - -setup_test_user() { - mkdir -p /var/spool/cron /var/spool/mail - useradd -m -s /bin/bash logind-test-user - trap cleanup_test_user EXIT -} - -test_enable_debug() { - mkdir -p /run/systemd/system/systemd-logind.service.d - cat >/run/systemd/system/systemd-logind.service.d/debug.conf </run/systemd/logind.conf.d/kill-user-processes.conf </run/systemd/logind.conf.d/kill-user-processes.conf </dev/null; then - echo "command evemu-device not found, skipping" - return - fi - if ! command -v evemu-event >/dev/null; then - echo "command evemu-event not found, skipping" - return - fi - - trap teardown_suspend RETURN - - # save pid - pid=$(systemctl show systemd-logind.service -p ExecMainPID --value) - - # create fake suspend - mkdir -p /run/systemd/system/systemd-suspend.service.d - cat >/run/systemd/system/systemd-suspend.service.d/override.conf </run/udev/rules.d/70-logindtest-lid.rules </run/lidswitch.evemu <&2 - exit 1 - fi - input_name=${input_name%/device/name} - lid_dev=/dev/${input_name#/sys/class/} - udevadm info --wait-for-initialization=10s "$lid_dev" - udevadm settle - - # close lid - evemu-event "$lid_dev" --sync --type 5 --code 0 --value 1 - # need to wait for 30s suspend inhibition after boot - wait_suspend 31 - # open lid again - evemu-event "$lid_dev" --sync --type 5 --code 0 --value 0 - - # waiting for 30s inhibition time between suspends - sleep 30 - - # now closing lid should cause instant suspend - evemu-event "$lid_dev" --sync --type 5 --code 0 --value 1 - wait_suspend 2 - evemu-event "$lid_dev" --sync --type 5 --code 0 --value 0 - - assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid" -} - -testcase_shutdown() { - local pid - - # save pid - pid=$(systemctl show systemd-logind.service -p ExecMainPID --value) - - # scheduled shutdown with wall message - shutdown 2>&1 - sleep 5 - shutdown -c || : - # logind should still be running - assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid" - - # scheduled shutdown without wall message - shutdown --no-wall 2>&1 - sleep 5 - shutdown -c --no-wall || true - assert_eq "$(systemctl show systemd-logind.service -p ExecMainPID --value)" "$pid" -} - -cleanup_session() ( - set +ex - - local uid s - - uid=$(id -u logind-test-user) - - loginctl disable-linger logind-test-user - - systemctl stop getty@tty2.service - - for s in $(loginctl --no-legend list-sessions | awk '$3 == "logind-test-user" { print $1 }'); do - echo "INFO: stopping session $s" - loginctl terminate-session "$s" - done - - loginctl terminate-user logind-test-user - - if ! timeout 30 bash -c "while loginctl --no-legend | grep -q logind-test-user; do sleep 1; done"; then - echo "WARNING: session for logind-test-user still active, ignoring." - fi - - pkill -u "$uid" - sleep 1 - pkill -KILL -u "$uid" - - if ! timeout 30 bash -c "while systemctl is-active --quiet user@${uid}.service; do sleep 1; done"; then - echo "WARNING: user@${uid}.service is still active, ignoring." - fi - - if ! timeout 30 bash -c "while systemctl is-active --quiet user-runtime-dir@${uid}.service; do sleep 1; done"; then - echo "WARNING: user-runtime-dir@${uid}.service is still active, ignoring." - fi - - if ! timeout 30 bash -c "while systemctl is-active --quiet user-${uid}.slice; do sleep 1; done"; then - echo "WARNING: user-${uid}.slice is still active, ignoring." - fi - - rm -rf /run/systemd/system/getty@tty2.service.d - systemctl daemon-reload - - return 0 -) - -teardown_session() ( - set +ex - - cleanup_session - - rm -f /run/udev/rules.d/70-logindtest-scsi_debug-user.rules - udevadm control --reload - rmmod scsi_debug - - return 0 -) - -check_session() ( - set +ex - - local seat session leader_pid - - if [[ $(loginctl --no-legend | grep -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 }') - 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 }') - if [[ -z "$session" ]]; then - echo "no session found for user logind-test-user" >&2 - return 1 - fi - - if ! loginctl session-status "$session" | grep -q "Unit: session-${session}\.scope"; then - echo "cannot find scope unit for session $session" >&2 - return 1 - fi - - leader_pid=$(loginctl session-status "$session" | awk '$1 == "Leader:" { print $2 }') - if [[ -z "$leader_pid" ]]; then - echo "cannot found leader process for session $session" >&2 - return 1 - fi - - # cgroup v1: "1:name=systemd:/user.slice/..."; unified hierarchy: "0::/user.slice" - if ! grep -q -E '(name=systemd|^0:):.*session.*scope' /proc/"$leader_pid"/cgroup; then - echo "FAIL: process $leader_pid is not in the session cgroup" >&2 - cat /proc/self/cgroup - return 1 - fi -) - -create_session() { - # login with the test user to start a session - mkdir -p /run/systemd/system/getty@tty2.service.d - cat >/run/systemd/system/getty@tty2.service.d/override.conf < 1 )) && sleep 1 - check_session && break - done - check_session - assert_eq "$(loginctl --no-legend | awk '$3=="logind-test-user" { print $5 }')" "tty2" -} - -testcase_sanity_check() { - # Exercise basic loginctl options - - if [[ ! -c /dev/tty2 ]]; then - echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}." - return - fi - - trap cleanup_session RETURN - create_session - - # Run most of the loginctl commands from a user session to make - # the seat/session autodetection work-ish - systemd-run --user --pipe --wait -M "logind-test-user@.host" bash -eux <<\EOF - loginctl list-sessions - loginctl 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; }') - loginctl kill-session --signal=SIGCONT "$session" - # FIXME(?) - #loginctl kill-session --signal=SIGCONT --kill-whom=leader "$session" - - loginctl list-users - loginctl user-status - loginctl show-user -a - loginctl show-user -P IdleAction - loginctl kill-user --signal=SIGCONT "" - - loginctl list-seats - loginctl seat-status - loginctl show-seat - loginctl show-seat -P IdleActionUSec -EOF - - # Requires root privileges - loginctl lock-sessions - loginctl unlock-sessions - loginctl flush-devices -} - -testcase_session() { - local dev - - if systemd-detect-virt --quiet --container; then - echo "Skipping ACL tests in container" - return - fi - - if [[ ! -c /dev/tty2 ]]; then - echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}." - return - fi - - trap teardown_session RETURN - - create_session - - # scsi_debug should not be loaded yet - if [[ -d /sys/bus/pseudo/drivers/scsi_debug ]]; then - echo "scsi_debug module is already loaded." >&2 - exit 1 - fi - - # we use scsi_debug to create new devices which we can put ACLs on - # tell udev about the tagging, so that logind can pick it up - mkdir -p /run/udev/rules.d - cat >/run/udev/rules.d/70-logindtest-scsi_debug-user.rules </dev/null; do sleep 1; done' - dev=/dev/$(ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null) - if [[ ! -b "$dev" ]]; then - echo "cannot find suitable scsi block device" >&2 - exit 1 - fi - udevadm settle - udevadm info "$dev" - - # trigger logind and activate session - loginctl activate "$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')" - - # check ACL - sleep 1 - assert_in "user:logind-test-user:rw-" "$(getfacl -p "$dev")" - - # hotplug: new device appears while logind is running - rmmod scsi_debug - modprobe scsi_debug - timeout 30 bash -c 'until ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null; do sleep 1; done' - dev=/dev/$(ls /sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block 2>/dev/null) - if [[ ! -b "$dev" ]]; then - echo "cannot find suitable scsi block device" >&2 - exit 1 - fi - udevadm settle - - # check ACL - sleep 1 - assert_in "user:logind-test-user:rw-" "$(getfacl -p "$dev")" -} - -teardown_lock_idle_action() ( - set +eux - - rm -f /run/systemd/logind.conf.d/idle-action-lock.conf - systemctl restart systemd-logind.service - - cleanup_session - - return 0 -) - -testcase_lock_idle_action() { - local ts - - if [[ ! -c /dev/tty2 ]]; then - echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}." - return - fi - - if loginctl --no-legend | grep -q logind-test-user; then - echo >&2 "Session of the 'logind-test-user' is already present." - exit 1 - fi - - trap teardown_lock_idle_action RETURN - - create_session - - ts="$(date '+%H:%M:%S')" - - mkdir -p /run/systemd/logind.conf.d - cat >/run/systemd/logind.conf.d/idle-action-lock.conf <&2 "System haven't entered idle state at least 2 times." - exit 1 - fi -} - -testcase_session_properties() { - local s - - if [[ ! -c /dev/tty2 ]]; then - echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}." - return - fi - - trap cleanup_session RETURN - create_session - - s=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $1 }') - /usr/lib/systemd/tests/unit-tests/manual/test-session-properties "/org/freedesktop/login1/session/_3${s?}" /dev/tty2 -} - -testcase_list_users_sessions_seats() { - local session seat - - if [[ ! -c /dev/tty2 ]]; then - echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}." - return - fi - - trap cleanup_session RETURN - create_session - - # Activate the session - loginctl activate "$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')" - - session=$(loginctl list-sessions --no-legend | 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 }')" '-' - - loginctl list-seats --no-legend | grep -Fwq "${seat?}" - - assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $1 }')" "$(id -ru logind-test-user)" - assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" no - assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $4 }')" active - - loginctl enable-linger logind-test-user - assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" yes - - for s in $(loginctl list-sessions --no-legend | 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 - echo "WARNING: session for logind-test-user still active, ignoring." - return - fi - - assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $4 }')" lingering -} - -teardown_stop_idle_session() ( - set +eux - - rm -f /run/systemd/logind.conf.d/stop-idle-session.conf - systemctl restart systemd-logind.service - - cleanup_session -) - -testcase_stop_idle_session() { - local id ts - - if [[ ! -c /dev/tty2 ]]; then - echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}." - return - fi - - create_session - trap teardown_stop_idle_session RETURN - - id="$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1; }')" - ts="$(date '+%H:%M:%S')" - - mkdir -p /run/systemd/logind.conf.d - cat >/run/systemd/logind.conf.d/stop-idle-session.conf <&2 - return - fi - - typeset -i BND MASK - - # Get PID 1's bounding set - BND="0x$(grep 'CapBnd:' /proc/1/status | cut -d: -f2 | tr -d '[:space:]')" - - # CAP_CHOWN | CAP_KILL - MASK=$(((1 << 0) | (1 << 5))) - - if [ $((BND & MASK)) -ne "$MASK" ] ; then - echo "CAP_CHOWN or CAP_KILL not available in bounding set, skipping test." >&2 - return - fi - - PAMSERVICE="pamserv$RANDOM" - TRANSIENTUNIT="capwakealarm$RANDOM.service" - SCRIPT="/tmp/capwakealarm$RANDOM.sh" - - cat > /etc/pam.d/"$PAMSERVICE" < "$SCRIPT" <<'EOF' -#!/bin/bash -set -ex -typeset -i AMB MASK -AMB="0x$(grep 'CapAmb:' /proc/self/status | cut -d: -f2 | tr -d '[:space:]')" -MASK=$(((1 << 0) | (1 << 5))) -test "$AMB" -eq "$MASK" -EOF - - chmod +x "$SCRIPT" - - systemd-run -u "$TRANSIENTUNIT" -p PAMName="$PAMSERVICE" -p Type=oneshot -p User=logind-test-user -p StandardError=tty "$SCRIPT" - - rm -f "$SCRIPT" "$PAMSERVICE" -} - -setup_test_user -test_enable_debug -run_testcases - -touch /testok 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-36.sh b/test/units/testsuite-36.sh deleted file mode 100755 index 8a53b98..0000000 --- a/test/units/testsuite-36.sh +++ /dev/null @@ -1,352 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck disable=SC2317 -at_exit() { - # shellcheck disable=SC2181 - if [[ $? -ne 0 ]]; then - # We're exiting with a non-zero EC, let's dump test artifacts - # for easier debugging - [[ -v straceLog && -f "$straceLog" ]] && cat "$straceLog" - [[ -v journalLog && -f "$journalLog" ]] && cat "$journalLog" - fi -} - -trap at_exit EXIT - -systemd-analyze log-level debug -systemd-analyze log-target journal - -# Log files -straceLog='strace.log' -journalLog='journal.log' - -# Systemd config files -testUnit='numa-test.service' -testUnitFile="/run/systemd/system/$testUnit" -testUnitNUMAConf="$testUnitFile.d/numa.conf" - -# Sleep constants (we should probably figure out something better but nothing comes to mind) -sleepAfterStart=1 - -# Journal cursor for easier navigation -journalCursorFile="jounalCursorFile" - -startStrace() { - coproc strace -qq -p 1 -o "$straceLog" -e set_mempolicy -s 1024 ${1:+"$1"} - # Wait for strace to properly "initialize", i.e. until PID 1 has the TracerPid - # field set to the current strace's PID - until awk -v spid="$COPROC_PID" '/^TracerPid:/ {exit !($2 == spid);}' /proc/1/status; do sleep 0.1; done -} - -stopStrace() { - [[ -v COPROC_PID ]] || return - - local PID=$COPROC_PID - kill -s TERM "$PID" - # Make sure the strace process is indeed dead - while kill -0 "$PID" 2>/dev/null; do sleep 0.1; done -} - -startJournalctl() { - : >"$journalCursorFile" - # Save journal's cursor for later navigation - journalctl --no-pager --cursor-file="$journalCursorFile" -n0 -ocat -} - -stopJournalctl() { - local unit="${1:-init.scope}" - # Using journalctl --sync should be better than using SIGRTMIN+1, as - # the --sync wait until the synchronization is complete - echo "Force journald to write all queued messages" - journalctl --sync - journalctl -u "$unit" --cursor-file="$journalCursorFile" >"$journalLog" -} - -checkNUMA() { - # NUMA enabled system should have at least NUMA node0 - test -e /sys/devices/system/node/node0 -} - -writePID1NUMAPolicy() { - cat >"$confDir/numa.conf" <"$testUnitFile" -} - -writeTestUnitNUMAPolicy() { - cat >"$testUnitNUMAConf" <"$LOGFILE" - grep "NUMAPolicy=$NUMA_POLICY" "$LOGFILE" - - : >"$LOGFILE" - - if [ -n "$NUMA_MASK" ]; then - systemctl show -p NUMAMask "$UNIT_NAME" >"$LOGFILE" - grep "NUMAMask=$NUMA_MASK" "$LOGFILE" - fi -} - -writeTestUnit - -# Create systemd config drop-in directory -confDir="/run/systemd/system.conf.d/" -mkdir -p "$confDir" - -if ! checkNUMA; then - echo >&2 "NUMA is not supported on this machine, switching to a simple sanity check" - - echo "PID1 NUMAPolicy=default && NUMAMask=0 check without NUMA support" - writePID1NUMAPolicy "default" "0" - startJournalctl - systemctl daemon-reload - stopJournalctl - grep "NUMA support not available, ignoring" "$journalLog" - - echo "systemd-run NUMAPolicy=default && NUMAMask=0 check without NUMA support" - runUnit='numa-systemd-run-test.service' - startJournalctl - systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit "$runUnit" sleep 1000 - sleep $sleepAfterStart - pid1StopUnit "$runUnit" - stopJournalctl "$runUnit" - grep "NUMA support not available, ignoring" "$journalLog" - -else - echo "PID1 NUMAPolicy support - Default policy w/o mask" - writePID1NUMAPolicy "default" - pid1ReloadWithStrace - # Kernel requires that nodemask argument is set to NULL when setting default policy - grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog" - - echo "PID1 NUMAPolicy support - Default policy w/ mask" - writePID1NUMAPolicy "default" "0" - pid1ReloadWithStrace - grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog" - - echo "PID1 NUMAPolicy support - Bind policy w/o mask" - writePID1NUMAPolicy "bind" - pid1ReloadWithJournal - grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog" - - echo "PID1 NUMAPolicy support - Bind policy w/ mask" - writePID1NUMAPolicy "bind" "0" - pid1ReloadWithStrace - grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" "$straceLog" - - echo "PID1 NUMAPolicy support - Interleave policy w/o mask" - writePID1NUMAPolicy "interleave" - pid1ReloadWithJournal - grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog" - - echo "PID1 NUMAPolicy support - Interleave policy w/ mask" - writePID1NUMAPolicy "interleave" "0" - pid1ReloadWithStrace - grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" "$straceLog" - - echo "PID1 NUMAPolicy support - Preferred policy w/o mask" - writePID1NUMAPolicy "preferred" - pid1ReloadWithJournal - # Preferred policy with empty node mask is actually allowed and should reset allocation policy to default - grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog" && { echo >&2 "unexpected pass"; exit 1; } - - echo "PID1 NUMAPolicy support - Preferred policy w/ mask" - writePID1NUMAPolicy "preferred" "0" - pid1ReloadWithStrace - grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" "$straceLog" - - echo "PID1 NUMAPolicy support - Local policy w/o mask" - writePID1NUMAPolicy "local" - pid1ReloadWithStrace - # Kernel requires that nodemask argument is set to NULL when setting default policy - # The unpatched versions of strace don't recognize the MPOL_LOCAL constant and - # return a numerical constant instead (with a comment): - # set_mempolicy(0x4 /* MPOL_??? */, NULL, 0) = 0 - # Let's cover this scenario as well - grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog" - - echo "PID1 NUMAPolicy support - Local policy w/ mask" - writePID1NUMAPolicy "local" "0" - pid1ReloadWithStrace - grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog" - - echo "Unit file NUMAPolicy support - Default policy w/o mask" - writeTestUnitNUMAPolicy "default" - pid1StartUnitWithStrace "$testUnit" - systemctlCheckNUMAProperties "$testUnit" "default" - pid1StopUnit "$testUnit" - grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog" - - echo "Unit file NUMAPolicy support - Default policy w/ mask" - writeTestUnitNUMAPolicy "default" "0" - pid1StartUnitWithStrace "$testUnit" - systemctlCheckNUMAProperties "$testUnit" "default" "0" - pid1StopUnit $testUnit - # Mask must be ignored - grep "set_mempolicy(MPOL_DEFAULT, NULL" "$straceLog" - - echo "Unit file NUMAPolicy support - Bind policy w/o mask" - writeTestUnitNUMAPolicy "bind" - pid1StartUnitWithJournal "$testUnit" - pid1StopUnit "$testUnit" - [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]] - - echo "Unit file NUMAPolicy support - Bind policy w/ mask" - writeTestUnitNUMAPolicy "bind" "0" - pid1StartUnitWithStrace "$testUnit" - systemctlCheckNUMAProperties "$testUnit" "bind" "0" - pid1StopUnit "$testUnit" - grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" "$straceLog" - - echo "Unit file NUMAPolicy support - Interleave policy w/o mask" - writeTestUnitNUMAPolicy "interleave" - pid1StartUnitWithStrace "$testUnit" - pid1StopUnit "$testUnit" - [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]] - - echo "Unit file NUMAPolicy support - Interleave policy w/ mask" - writeTestUnitNUMAPolicy "interleave" "0" - pid1StartUnitWithStrace "$testUnit" - systemctlCheckNUMAProperties "$testUnit" "interleave" "0" - pid1StopUnit "$testUnit" - grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" "$straceLog" - - echo "Unit file NUMAPolicy support - Preferred policy w/o mask" - writeTestUnitNUMAPolicy "preferred" - pid1StartUnitWithJournal "$testUnit" - systemctlCheckNUMAProperties "$testUnit" "preferred" - pid1StopUnit "$testUnit" - [[ $(systemctl show "$testUnit" -P ExecMainStatus) == "242" ]] && { echo >&2 "unexpected pass"; exit 1; } - - echo "Unit file NUMAPolicy support - Preferred policy w/ mask" - writeTestUnitNUMAPolicy "preferred" "0" - pid1StartUnitWithStrace "$testUnit" - systemctlCheckNUMAProperties "$testUnit" "preferred" "0" - pid1StopUnit "$testUnit" - grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" "$straceLog" - - echo "Unit file NUMAPolicy support - Local policy w/o mask" - writeTestUnitNUMAPolicy "local" - pid1StartUnitWithStrace "$testUnit" - systemctlCheckNUMAProperties "$testUnit" "local" - pid1StopUnit "$testUnit" - grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog" - - echo "Unit file NUMAPolicy support - Local policy w/ mask" - writeTestUnitNUMAPolicy "local" "0" - pid1StartUnitWithStrace "$testUnit" - systemctlCheckNUMAProperties "$testUnit" "local" "0" - pid1StopUnit "$testUnit" - # Mask must be ignored - grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" "$straceLog" - - echo "Unit file CPUAffinity=NUMA support" - writeTestUnitNUMAPolicy "bind" "0" - echo "CPUAffinity=numa" >>"$testUnitNUMAConf" - systemctl daemon-reload - systemctl start "$testUnit" - systemctlCheckNUMAProperties "$testUnit" "bind" "0" - cpulist="$(cat /sys/devices/system/node/node0/cpulist)" - affinity_systemd="$(systemctl show --value -p CPUAffinity "$testUnit")" - [ "$cpulist" = "$affinity_systemd" ] - pid1StopUnit "$testUnit" - - echo "systemd-run NUMAPolicy support" - runUnit='numa-systemd-run-test.service' - - systemd-run -p NUMAPolicy=default --unit "$runUnit" sleep 1000 - systemctlCheckNUMAProperties "$runUnit" "default" - pid1StopUnit "$runUnit" - - systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit "$runUnit" sleep 1000 - systemctlCheckNUMAProperties "$runUnit" "default" "" - pid1StopUnit "$runUnit" - - systemd-run -p NUMAPolicy=bind -p NUMAMask=0 --unit "$runUnit" sleep 1000 - systemctlCheckNUMAProperties "$runUnit" "bind" "0" - pid1StopUnit "$runUnit" - - systemd-run -p NUMAPolicy=interleave -p NUMAMask=0 --unit "$runUnit" sleep 1000 - systemctlCheckNUMAProperties "$runUnit" "interleave" "0" - pid1StopUnit "$runUnit" - - systemd-run -p NUMAPolicy=preferred -p NUMAMask=0 --unit "$runUnit" sleep 1000 - systemctlCheckNUMAProperties "$runUnit" "preferred" "0" - pid1StopUnit "$runUnit" - - systemd-run -p NUMAPolicy=local --unit "$runUnit" sleep 1000 - systemctlCheckNUMAProperties "$runUnit" "local" - pid1StopUnit "$runUnit" - - systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit "$runUnit" sleep 1000 - systemctlCheckNUMAProperties "$runUnit" "local" "" - pid1StopUnit "$runUnit" - - systemd-run -p NUMAPolicy=local -p NUMAMask=0 -p CPUAffinity=numa --unit "$runUnit" sleep 1000 - systemctlCheckNUMAProperties "$runUnit" "local" "" - systemctl cat "$runUnit" | grep -q 'CPUAffinity=numa' - pid1StopUnit "$runUnit" -fi - -# Cleanup -rm -rf "$confDir" -systemctl daemon-reload - -systemd-analyze log-level info - -touch /testok diff --git a/test/units/testsuite-38-sleep.service b/test/units/testsuite-38-sleep.service deleted file mode 100644 index c116c80..0000000 --- a/test/units/testsuite-38-sleep.service +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[Service] -ExecStart=/bin/sleep 3600 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-38.sh b/test/units/testsuite-38.sh deleted file mode 100755 index 5fc87fc..0000000 --- a/test/units/testsuite-38.sh +++ /dev/null @@ -1,301 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2317 -set -eux -set -o pipefail - -# shellcheck source=test/units/test-control.sh -. "$(dirname "$0")"/test-control.sh - -systemd-analyze log-level debug - -unit=testsuite-38-sleep.service - -start_test_service() { - systemctl daemon-reload - systemctl start "${unit}" -} - -dbus_freeze() { - local name object_path suffix - - suffix="${1##*.}" - name="${1%".$suffix"}" - object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}" - - busctl call \ - org.freedesktop.systemd1 \ - "${object_path}" \ - org.freedesktop.systemd1.Unit \ - Freeze -} - -dbus_thaw() { - local name object_path suffix - - suffix="${1##*.}" - name="${1%".$suffix"}" - object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}" - - busctl call \ - org.freedesktop.systemd1 \ - "${object_path}" \ - org.freedesktop.systemd1.Unit \ - Thaw -} - -dbus_freeze_unit() { - busctl call \ - org.freedesktop.systemd1 \ - /org/freedesktop/systemd1 \ - org.freedesktop.systemd1.Manager \ - FreezeUnit \ - s \ - "$1" -} - -dbus_thaw_unit() { - busctl call \ - org.freedesktop.systemd1 \ - /org/freedesktop/systemd1 \ - org.freedesktop.systemd1.Manager \ - ThawUnit \ - s \ - "$1" -} - -dbus_can_freeze() { - local name object_path suffix - - suffix="${1##*.}" - name="${1%".$suffix"}" - object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}" - - busctl get-property \ - org.freedesktop.systemd1 \ - "${object_path}" \ - org.freedesktop.systemd1.Unit \ - CanFreeze -} - -check_freezer_state() { - local name object_path suffix - - suffix="${1##*.}" - name="${1%".$suffix"}" - object_path="/org/freedesktop/systemd1/unit/${name//-/_2d}_2e${suffix}" - - for _ in {0..10}; do - state=$(busctl get-property \ - org.freedesktop.systemd1 \ - "${object_path}" \ - org.freedesktop.systemd1.Unit \ - FreezerState | cut -d " " -f2 | tr -d '"') - - # Ignore the intermediate freezing & thawing states in case we check - # the unit state too quickly - [[ "$state" =~ ^(freezing|thawing)$ ]] || break - sleep .5 - done - - [ "$state" = "$2" ] || { - echo "error: unexpected freezer state, expected: $2, actual: $state" >&2 - exit 1 - } -} - -check_cgroup_state() { - grep -q "frozen $2" /sys/fs/cgroup/system.slice/"$1"/cgroup.events -} - -testcase_dbus_api() { - echo "Test that DBus API works:" - echo -n " - Freeze(): " - dbus_freeze "${unit}" - check_freezer_state "${unit}" "frozen" - check_cgroup_state "$unit" 1 - echo "[ OK ]" - - echo -n " - Thaw(): " - dbus_thaw "${unit}" - check_freezer_state "${unit}" "running" - check_cgroup_state "$unit" 0 - echo "[ OK ]" - - echo -n " - FreezeUnit(): " - dbus_freeze_unit "${unit}" - check_freezer_state "${unit}" "frozen" - check_cgroup_state "$unit" 1 - echo "[ OK ]" - - echo -n " - ThawUnit(): " - dbus_thaw_unit "${unit}" - check_freezer_state "${unit}" "running" - check_cgroup_state "$unit" 0 - echo "[ OK ]" - - echo -n " - CanFreeze(): " - output=$(dbus_can_freeze "${unit}") - [ "$output" = "b true" ] - echo "[ OK ]" - - echo -} - -testcase_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:" - - systemctl start "$unit" - - echo -n " - freeze: " - systemctl freeze "$unit" - check_freezer_state "${unit}" "frozen" - check_cgroup_state "$unit" 1 - # Freezing already frozen unit should be NOP and return quickly - timeout 3s systemctl freeze "$unit" - echo "[ OK ]" - - echo -n " - thaw: " - systemctl thaw "$unit" - check_freezer_state "${unit}" "running" - check_cgroup_state "$unit" 0 - # Likewise thawing already running unit shouldn't block - timeout 3s systemctl thaw "$unit" - echo "[ OK ]" - - systemctl stop "$unit" - - echo -} - -testcase_systemctl_show() { - echo "Test systemctl show integration:" - - systemctl start "$unit" - - echo -n " - FreezerState property: " - state=$(systemctl show -p FreezerState --value "$unit") - [ "$state" = "running" ] - systemctl freeze "$unit" - state=$(systemctl show -p FreezerState --value "$unit") - [ "$state" = "frozen" ] - systemctl thaw "$unit" - echo "[ OK ]" - - echo -n " - CanFreeze property: " - state=$(systemctl show -p CanFreeze --value "$unit") - [ "$state" = "yes" ] - echo "[ OK ]" - - systemctl stop "$unit" - echo -} - -testcase_recursive() { - local slice="bar.slice" - local unit="baz.service" - - systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 - - echo "Test recursive freezing:" - - echo -n " - freeze: " - 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 - echo "[ OK ]" - - echo -n " - thaw: " - 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 - echo "[ OK ]" - - systemctl stop "$unit" - systemctl stop "$slice" - - echo -} - -testcase_preserve_state() { - local slice="bar.slice" - local unit="baz.service" - - systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 - - echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):" - - echo -n " - freeze from outside: " - echo 1 >/sys/fs/cgroup/"${slice}"/cgroup.freeze - # Give kernel some time to freeze the slice - sleep 1 - - # Our state should not be affected - check_freezer_state "${slice}" "running" - check_freezer_state "${unit}" "running" - - # However actual kernel state should be frozen - grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events - grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events - echo "[ OK ]" - - echo -n " - thaw from outside: " - echo 0 >/sys/fs/cgroup/"${slice}"/cgroup.freeze - sleep 1 - - check_freezer_state "${unit}" "running" - check_freezer_state "${slice}" "running" - grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events - grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events - echo "[ OK ]" - - echo -n " - thaw from outside while inner service is frozen: " - systemctl freeze "$unit" - check_freezer_state "${unit}" "frozen" - echo 1 >/sys/fs/cgroup/"${slice}"/cgroup.freeze - echo 0 >/sys/fs/cgroup/"${slice}"/cgroup.freeze - check_freezer_state "${slice}" "running" - check_freezer_state "${unit}" "frozen" - echo "[ OK ]" - - systemctl stop "$unit" - systemctl stop "$slice" - - echo -} - -if [[ -e /sys/fs/cgroup/system.slice/cgroup.freeze ]]; then - start_test_service - run_testcases -fi - -touch /testok diff --git a/test/units/testsuite-43.service b/test/units/testsuite-43.service deleted file mode 100644 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-43.sh b/test/units/testsuite-43.sh deleted file mode 100755 index 4f31a33..0000000 --- a/test/units/testsuite-43.sh +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -if [[ "$(sysctl -ne kernel.apparmor_restrict_unprivileged_userns)" -eq 1 ]]; then - echo "Cannot create unprivileged user namespaces" >/skipped - exit 0 -fi - -systemd-analyze log-level debug - -runas testuser systemd-run --wait --user --unit=test-private-users \ - -p PrivateUsers=yes -P echo hello - -runas testuser systemctl --user log-level debug - -runas testuser systemd-run --wait --user --unit=test-private-tmp-innerfile \ - -p PrivateTmp=yes \ - -P touch /tmp/innerfile.txt -# File should not exist outside the job's tmp directory. -test ! -e /tmp/innerfile.txt - -touch /tmp/outerfile.txt -# File should not appear in unit's private tmp. -runas testuser systemd-run --wait --user --unit=test-private-tmp-outerfile \ - -p PrivateTmp=yes \ - -P test ! -e /tmp/outerfile.txt - -# Confirm that creating a file in home works -runas testuser systemd-run --wait --user --unit=test-unprotected-home \ - -P touch /home/testuser/works.txt -test -e /home/testuser/works.txt - -# Confirm that creating a file in home is blocked under read-only -(! runas testuser systemd-run --wait --user --unit=test-protect-home-read-only \ - -p ProtectHome=read-only \ - -P bash -c ' - test -e /home/testuser/works.txt || exit 10 - touch /home/testuser/blocked.txt && exit 11 - ') -test ! -e /home/testuser/blocked.txt - -# Check that tmpfs hides the whole directory -runas testuser systemd-run --wait --user --unit=test-protect-home-tmpfs \ - -p ProtectHome=tmpfs \ - -P test ! -e /home/testuser - -# Confirm that home, /root, and /run/user are inaccessible under "yes" -# shellcheck disable=SC2016 -runas testuser systemd-run --wait --user --unit=test-protect-home-yes \ - -p ProtectHome=yes \ - -P bash -c ' - test "$(stat -c %a /home)" = "0" - test "$(stat -c %a /root)" = "0" - test "$(stat -c %a /run/user)" = "0" - ' - -# Confirm we cannot change groups because we only have one mapping in the user -# namespace (no CAP_SETGID in the parent namespace to write the additional -# mapping of the user supplied group and thus cannot change groups to an -# unmapped group ID) -(! runas testuser systemd-run --wait --user --unit=test-group-fail \ - -p PrivateUsers=yes -p Group=daemon \ - -P true) - -# Check that with a new user namespace we can bind mount -# files and use a different root directory -runas testuser systemd-run --wait --user --unit=test-bind-mount \ - -p BindPaths=/dev/null:/etc/os-release \ - test ! -s /etc/os-release - -runas testuser systemd-run --wait --user --unit=test-read-write \ - -p ReadOnlyPaths=/ \ - -p ReadWritePaths="/var /run /tmp" \ - -p NoExecPaths=/ -p ExecPaths=/usr \ - test ! -w /etc/os-release - -runas testuser systemd-run --wait --user --unit=test-caps \ - -p PrivateUsers=yes -p AmbientCapabilities=CAP_SYS_ADMIN \ - -p CapabilityBoundingSet=CAP_SYS_ADMIN \ - test -s /etc/os-release - -runas testuser systemd-run --wait --user --unit=test-devices \ - -p PrivateDevices=yes -p PrivateIPC=yes \ - sh -c "ls -1 /dev/ | wc -l | grep -q -F 18" - -# Same check as test/test-execute/exec-privatenetwork-yes.service -runas testuser systemd-run --wait --user --unit=test-network \ - -p PrivateNetwork=yes \ - /bin/sh -x -c '! ip link | grep -E "^[0-9]+: " | grep -Ev ": (lo|(erspan|gre|gretap|ip_vti|ip6_vti|ip6gre|ip6tnl|sit|tunl)0@.*):"' - -(! runas testuser systemd-run --wait --user --unit=test-hostname \ - -p ProtectHostname=yes \ - hostnamectl hostname foo) - -(! runas testuser systemd-run --wait --user --unit=test-clock \ - -p ProtectClock=yes \ - timedatectl set-time "2012-10-30 18:17:16") - -(! runas testuser systemd-run --wait --user --unit=test-kernel-tunable \ - -p ProtectKernelTunables=yes \ - sh -c "echo 0 >/proc/sys/user/max_user_namespaces") - -(! runas testuser systemd-run --wait --user --unit=test-kernel-mod \ - -p ProtectKernelModules=yes \ - sh -c "modprobe -r overlay && modprobe overlay") - -if sysctl kernel.dmesg_restrict=0; then - (! runas testuser systemd-run --wait --user --unit=test-kernel-log \ - -p ProtectKernelLogs=yes -p LogNamespace=yes \ - dmesg) -fi - -unsquashfs -no-xattrs -d /tmp/img /usr/share/minimal_0.raw -runas testuser systemd-run --wait --user --unit=test-root-dir \ - -p RootDirectory=/tmp/img \ - grep MARKER=1 /etc/os-release - -mkdir /tmp/img_bind -mount --bind /tmp/img /tmp/img_bind -runas testuser systemd-run --wait --user --unit=test-root-dir-bind \ - -p RootDirectory=/tmp/img_bind -p MountFlags=private \ - grep MARKER=1 /etc/os-release -umount /tmp/img_bind - -# Unprivileged overlayfs was added to Linux 5.11, so try to detect it first -mkdir -p /tmp/a /tmp/b /tmp/c -if unshare --mount --user --map-root-user mount -t overlay overlay /tmp/c -o lowerdir=/tmp/a:/tmp/b; then - unsquashfs -no-xattrs -d /tmp/app2 /usr/share/app1.raw - runas testuser systemd-run --wait --user --unit=test-extension-dir \ - -p ExtensionDirectories=/tmp/app2 \ - -p TemporaryFileSystem=/run -p RootDirectory=/tmp/img \ - -p MountAPIVFS=yes \ - grep PORTABLE_PREFIXES=app1 /usr/lib/extension-release.d/extension-release.app2 -fi - -systemd-analyze log-level info - -touch /testok diff --git a/test/units/testsuite-44.service b/test/units/testsuite-44.service deleted file mode 100644 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-44.sh b/test/units/testsuite-44.sh deleted file mode 100755 index fbd4ae6..0000000 --- a/test/units/testsuite-44.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux - -systemd-analyze log-level debug - -systemd-run --wait -p LogNamespace=foobar echo "hello world" - -journalctl --namespace=foobar --sync -journalctl -o cat --namespace=foobar >/tmp/hello-world -journalctl -o cat >/tmp/no-hello-world - -grep "^hello world$" /tmp/hello-world -(! grep "^hello world$" /tmp/no-hello-world) - -systemd-analyze log-level info - -touch /testok diff --git a/test/units/testsuite-45.service b/test/units/testsuite-45.service deleted file mode 100644 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-45.sh b/test/units/testsuite-45.sh deleted file mode 100755 index b426927..0000000 --- a/test/units/testsuite-45.sh +++ /dev/null @@ -1,408 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later - -set -eux -set -o pipefail - -# shellcheck source=test/units/test-control.sh -. "$(dirname "$0")"/test-control.sh -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -testcase_timedatectl() { - timedatectl --no-pager --help - timedatectl --version - - timedatectl - timedatectl --no-ask-password - timedatectl status --machine=testuser@.host - timedatectl status - timedatectl show - timedatectl show --all - timedatectl show -p NTP - timedatectl show -p NTP --value - timedatectl list-timezones - - if ! systemd-detect-virt -qc; then - systemctl enable --runtime --now systemd-timesyncd - timedatectl timesync-status - timedatectl show-timesync - fi -} - -restore_timezone() { - if [[ -f /tmp/timezone.bak ]]; then - mv /tmp/timezone.bak /etc/timezone - else - rm -f /etc/timezone - fi -} - -testcase_timezone() { - local ORIG_TZ= - - # Debian/Ubuntu specific file - if [[ -f /etc/timezone ]]; then - mv /etc/timezone /tmp/timezone.bak - fi - - trap restore_timezone RETURN - - if [[ -L /etc/localtime ]]; then - ORIG_TZ=$(readlink /etc/localtime | sed 's#^.*zoneinfo/##') - echo "original tz: $ORIG_TZ" - fi - - echo 'timedatectl works' - assert_in "Local time:" "$(timedatectl --no-pager)" - - echo 'change timezone' - assert_eq "$(timedatectl --no-pager set-timezone Europe/Kiev 2>&1)" "" - assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "Europe/Kiev" - if [[ -f /etc/timezone ]]; then - assert_eq "$(cat /etc/timezone)" "Europe/Kiev" - fi - assert_in "Time zone: Europe/Kiev \(EES*T, \+0[0-9]00\)" "$(timedatectl)" - - if [[ -n "$ORIG_TZ" ]]; then - echo 'reset timezone to original' - assert_eq "$(timedatectl set-timezone "$ORIG_TZ" 2>&1)" "" - assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "$ORIG_TZ" - if [[ -f /etc/timezone ]]; then - assert_eq "$(cat /etc/timezone)" "$ORIG_TZ" - fi - fi -} - -restore_adjtime() { - if [[ -e /etc/adjtime.bak ]]; then - mv /etc/adjtime.bak /etc/adjtime - else - rm /etc/adjtime - fi -} - -check_adjtime_not_exist() { - if [[ -e /etc/adjtime ]]; then - echo "/etc/adjtime unexpectedly exists." >&2 - exit 1 - fi -} - -testcase_adjtime() { - # test setting UTC vs. LOCAL in /etc/adjtime - if [[ -e /etc/adjtime ]]; then - mv /etc/adjtime /etc/adjtime.bak - fi - - trap restore_adjtime RETURN - - echo 'no adjtime file' - rm -f /etc/adjtime - timedatectl set-local-rtc 0 - check_adjtime_not_exist - timedatectl set-local-rtc 1 - assert_eq "$(cat /etc/adjtime)" "0.0 0 0 -0 -LOCAL" - timedatectl set-local-rtc 0 - check_adjtime_not_exist - - echo 'UTC set in adjtime file' - printf '0.0 0 0\n0\nUTC\n' >/etc/adjtime - timedatectl set-local-rtc 0 - assert_eq "$(cat /etc/adjtime)" "0.0 0 0 -0 -UTC" - timedatectl set-local-rtc 1 - assert_eq "$(cat /etc/adjtime)" "0.0 0 0 -0 -LOCAL" - - echo 'non-zero values in adjtime file' - printf '0.1 123 0\n0\nLOCAL\n' >/etc/adjtime - timedatectl set-local-rtc 0 - assert_eq "$(cat /etc/adjtime)" "0.1 123 0 -0 -UTC" - timedatectl set-local-rtc 1 - assert_eq "$(cat /etc/adjtime)" "0.1 123 0 -0 -LOCAL" - - echo 'fourth line adjtime file' - printf '0.0 0 0\n0\nLOCAL\nsomethingelse\n' >/etc/adjtime - timedatectl set-local-rtc 0 - assert_eq "$(cat /etc/adjtime)" "0.0 0 0 -0 -UTC -somethingelse" - timedatectl set-local-rtc 1 - assert_eq "$(cat /etc/adjtime)" "0.0 0 0 -0 -LOCAL -somethingelse" - - echo 'no final newline in adjtime file' - printf '0.0 0 0\n0\nUTC' >/etc/adjtime - timedatectl set-local-rtc 0 - check_adjtime_not_exist - printf '0.0 0 0\n0\nUTC' >/etc/adjtime - timedatectl set-local-rtc 1 - assert_eq "$(cat /etc/adjtime)" "0.0 0 0 -0 -LOCAL" - - echo 'only one line in adjtime file' - printf '0.0 0 0\n' >/etc/adjtime - timedatectl set-local-rtc 0 - check_adjtime_not_exist - printf '0.0 0 0\n' >/etc/adjtime - timedatectl set-local-rtc 1 - assert_eq "$(cat /etc/adjtime)" "0.0 0 0 -0 -LOCAL" - - echo 'only one line in adjtime file, no final newline' - printf '0.0 0 0' >/etc/adjtime - timedatectl set-local-rtc 0 - check_adjtime_not_exist - printf '0.0 0 0' >/etc/adjtime - timedatectl set-local-rtc 1 - assert_eq "$(cat /etc/adjtime)" "0.0 0 0 -0 -LOCAL" - - echo 'only two lines in adjtime file' - printf '0.0 0 0\n0\n' >/etc/adjtime - timedatectl set-local-rtc 0 - check_adjtime_not_exist - printf '0.0 0 0\n0\n' >/etc/adjtime - timedatectl set-local-rtc 1 - assert_eq "$(cat /etc/adjtime)" "0.0 0 0 -0 -LOCAL" - - echo 'only two lines in adjtime file, no final newline' - printf '0.0 0 0\n0' >/etc/adjtime - timedatectl set-local-rtc 0 - check_adjtime_not_exist - printf '0.0 0 0\n0' >/etc/adjtime - timedatectl set-local-rtc 1 - assert_eq "$(cat /etc/adjtime)" "0.0 0 0 -0 -LOCAL" - - echo 'unknown value in 3rd line of adjtime file' - printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime - timedatectl set-local-rtc 0 - check_adjtime_not_exist - printf '0.0 0 0\n0\nFOO\n' >/etc/adjtime - timedatectl set-local-rtc 1 - assert_eq "$(cat /etc/adjtime)" "0.0 0 0 -0 -LOCAL" -} - -assert_ntp() { - local value="${1:?}" - - for _ in {0..9}; do - [[ "$(busctl get-property org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 NTP)" == "b $value" ]] && return 0 - sleep .5 - done - - return 1 -} - -assert_timedated_signal() { - local timestamp="${1:?}" - local value="${2:?}" - local args=(-q -n 1 --since="$timestamp" -p info _SYSTEMD_UNIT="busctl-monitor" + SYSLOG_IDENTIFIER="busctl-monitor") - - journalctl --sync - - for _ in {0..9}; do - if journalctl "${args[@]}" --grep .; then - [[ "$(journalctl "${args[@]}" -o cat | jq -r '.payload.data[1].NTP.data')" == "$value" ]]; - return 0 - fi - - sleep .5 - done - - return 1 -} - -assert_timesyncd_state() { - local state="${1:?}" - - for _ in {0..9}; do - [[ "$(systemctl show systemd-timesyncd.service -P ActiveState)" == "$state" ]] && return 0 - sleep .5 - done - - return 1 -} - -testcase_ntp() { - # timesyncd has ConditionVirtualization=!container by default; drop/mock that for testing - if systemd-detect-virt --container --quiet; then - systemctl disable --quiet --now systemd-timesyncd - mkdir -p /run/systemd/system/systemd-timesyncd.service.d - cat >/run/systemd/system/systemd-timesyncd.service.d/container.conf </dev/null; then - echo "This test requires systemd-networkd, skipping..." - return 0 - fi - - # Create a dummy interface managed by networkd, so we can configure link NTP servers - mkdir -p /run/systemd/network/ - cat >/etc/systemd/network/10-ntp99.netdev </etc/systemd/network/10-ntp99.network </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:?}" <"${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 </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 </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 </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 </run/systemd/system/testservice-50e.service </run/systemd/system/testservice-50f.service </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 </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-52.sh b/test/units/testsuite-52.sh deleted file mode 100755 index 16ff507..0000000 --- a/test/units/testsuite-52.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex -set -o pipefail - -systemd-analyze log-level debug - -systemctl enable test-honor-first-shutdown.service -systemctl start test-honor-first-shutdown.service - -touch /testok diff --git a/test/units/testsuite-53.service b/test/units/testsuite-53.service deleted file mode 100644 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-53.sh b/test/units/testsuite-53.sh deleted file mode 100755 index 84cd661..0000000 --- a/test/units/testsuite-53.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -: >/failed - -# Reset host date to current time, 3 days in the past. -date -s "-3 days" - -# Run a timer for every 15 minutes. -systemd-run --unit test-timer --on-calendar "*:0/15:0" true - -next_elapsed=$(systemctl show test-timer.timer -p NextElapseUSecRealtime --value) -next_elapsed=$(date -d "${next_elapsed}" +%s) -now=$(date +%s) -time_delta=$((next_elapsed - now)) - -# Check that the timer will elapse in less than 20 minutes. -((0 < time_delta && time_delta < 1200)) || { - echo 'Timer elapse outside of the expected 20 minute window.' - echo " next_elapsed=${next_elapsed}" - echo " now=${now}" - echo " time_delta=${time_delta}" - echo '' -} >>/failed - -if test ! -s /failed ; then - rm -f /failed - touch /testok -fi diff --git a/test/units/testsuite-54.service b/test/units/testsuite-54.service deleted file mode 100644 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-54.sh b/test/units/testsuite-54.sh deleted file mode 100755 index bcbe7a1..0000000 --- a/test/units/testsuite-54.sh +++ /dev/null @@ -1,319 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2016 -set -eux - -systemd-analyze log-level debug - -run_with_cred_compare() { - local cred="${1:?}" - local exp="${2?}" - shift 2 - - diff <(systemd-run -p SetCredential="$cred" --wait --pipe -- systemd-creds "$@") <(echo -ne "$exp") -} - -# Sanity checks -# -# Create a dummy "full" disk (similar to /dev/full) to check out-of-space -# scenarios -mkdir /tmp/full -mount -t tmpfs -o size=1,nr_inodes=1 tmpfs /tmp/full - -# verb: setup -# Run this first, otherwise any encrypted credentials wouldn't be decryptable -# as we regenerate the host key -rm -fv /var/lib/systemd/credential.secret -systemd-creds setup -test -e /var/lib/systemd/credential.secret -rm -fv /var/lib/systemd/credential.secret - -# Prepare a couple of dummy credentials for the cat/list verbs -CRED_DIR="$(mktemp -d)" -ENC_CRED_DIR="$(mktemp -d)" -echo foo >"$CRED_DIR/secure-or-weak" -echo foo >"$CRED_DIR/insecure" -echo foo | systemd-creds --name="encrypted" encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted" -echo foo | systemd-creds encrypt - - | base64 -d >"$ENC_CRED_DIR/encrypted-unnamed" -chmod -R 0400 "$CRED_DIR" "$ENC_CRED_DIR" -chmod -R 0444 "$CRED_DIR/insecure" -mkdir /tmp/empty/ - -systemd-creds --system -systemd-creds --no-pager --help -systemd-creds --version -systemd-creds has-tpm2 || : -systemd-creds has-tpm2 -q || : - -# verb: list -systemd-creds list --system -ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --no-legend -ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=pretty | jq -ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=short | jq -ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds list --json=off -ENCRYPTED_CREDENTIALS_DIRECTORY="/tmp/empty/" CREDENTIALS_DIRECTORY="/tmp/empty/" systemd-creds list - -# verb: cat -for cred in secure-or-weak insecure encrypted encrypted-unnamed; do - ENCRYPTED_CREDENTIALS_DIRECTORY="$ENC_CRED_DIR" CREDENTIALS_DIRECTORY="$CRED_DIR" systemd-creds cat "$cred" -done -run_with_cred_compare "mycred:" "" cat mycred -run_with_cred_compare "mycred:\n" "\n" cat mycred -run_with_cred_compare "mycred:foo" "foo" cat mycred -run_with_cred_compare "mycred:foo" "foofoofoo" cat mycred mycred mycred -# Note: --newline= does nothing when stdout is not a tty, which is the case here -run_with_cred_compare "mycred:foo" "foo" --newline=yes cat mycred -run_with_cred_compare "mycred:foo" "foo" --newline=no cat mycred -run_with_cred_compare "mycred:foo" "foo" --newline=auto cat mycred -run_with_cred_compare "mycred:foo" "foo" --transcode=no cat mycred -run_with_cred_compare "mycred:foo" "foo" --transcode=0 cat mycred -run_with_cred_compare "mycred:foo" "foo" --transcode=false cat mycred -run_with_cred_compare "mycred:foo" "Zm9v" --transcode=base64 cat mycred -run_with_cred_compare "mycred:Zm9v" "foo" --transcode=unbase64 cat mycred -run_with_cred_compare "mycred:Zm9v" "foofoofoo" --transcode=unbase64 cat mycred mycred mycred -run_with_cred_compare "mycred:Zm9vCg==" "foo\n" --transcode=unbase64 cat mycred -run_with_cred_compare "mycred:hello world" "68656c6c6f20776f726c64" --transcode=hex cat mycred -run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello world" --transcode=unhex cat mycred -run_with_cred_compare "mycred:68656c6c6f20776f726c64" "hello worldhello world" --transcode=unhex cat mycred mycred -run_with_cred_compare "mycred:68656c6c6f0a776f726c64" "hello\nworld" --transcode=unhex cat mycred -run_with_cred_compare 'mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' '{"foo":"bar","baz":[3,4]}\n' --json=short cat mycred -systemd-run -p SetCredential='mycred:{ "foo" : "bar", "baz" : [ 3, 4 ] }' --wait --pipe -- systemd-creds --json=pretty cat mycred | jq - -# verb: encrypt/decrypt -echo "According to all known laws of aviation..." >/tmp/cred.orig -systemd-creds --with-key=host encrypt /tmp/cred.orig /tmp/cred.enc -systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec -diff /tmp/cred.orig /tmp/cred.dec -rm -f /tmp/cred.{enc,dec} -# --pretty -cred_name="fo'''o''bar" -cred_option="$(systemd-creds --pretty --name="$cred_name" encrypt /tmp/cred.orig -)" -mkdir -p /run/systemd/system -cat >/run/systemd/system/test-54-pretty-cred.service </tmp/ts54-concat -(cat /etc/passwd /etc/shadow && echo -n wuff) | cmp /tmp/ts54-concat -rm /tmp/ts54-concat - -# Test that SetCredential= acts as fallback for LoadCredential= -echo piff >/tmp/ts54-fallback -[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "piff" ] -rm /tmp/ts54-fallback -[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "poff" ] - -if systemd-detect-virt -q -c ; then - expected_credential=mynspawncredential - expected_value=strangevalue -elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then - # Verify that passing creds through kernel cmdline works - [ "$(systemd-creds --system cat kernelcmdlinecred)" = "uff" ] - [ "$(systemd-creds --system cat waldi)" = "woooofffwufffwuff" ] - - # And that it also works via SMBIOS - [ "$(systemd-creds --system cat smbioscredential)" = "magicdata" ] - [ "$(systemd-creds --system cat binarysmbioscredential)" = "magicbinarydata" ] - - # If we aren't run in nspawn, we are run in qemu - systemd-detect-virt -q -v - expected_credential=myqemucredential - expected_value=othervalue - - # Verify that writing a sysctl via the kernel cmdline worked - [ "$(cat /proc/sys/kernel/domainname)" = "sysctltest" ] - - # Verify that creating a user via sysusers via the kernel cmdline worked - grep -q ^credtestuser: /etc/passwd - - # Verify that writing a file via tmpfiles worked - [ "$(cat /tmp/sourcedfromcredential)" = "tmpfilessecret" ] - [ "$(cat /etc/motd.d/50-provision.conf)" = "hello" ] - [ "$(cat /etc/issue.d/50-provision.conf)" = "welcome" ] -else - echo "qemu_fw_cfg support missing in kernel. Sniff!" - expected_credential="" - expected_value="" -fi - -if [ "$expected_credential" != "" ] ; then - # If this test is run in nspawn a credential should have been passed to us. See test/TEST-54-CREDS/test.sh - [ "$(systemd-creds --system cat "$expected_credential")" = "$expected_value" ] - - # Test that propagation from system credential to service credential works - [ "$(systemd-run -p LoadCredential="$expected_credential" --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ] - - # Check it also works, if we rename it while propagating it - [ "$(systemd-run -p LoadCredential=miau:"$expected_credential" --pipe --wait systemd-creds cat miau)" = "$expected_value" ] - - # Combine it with a fallback (which should have no effect, given the cred should be passed down) - [ "$(systemd-run -p LoadCredential="$expected_credential" -p SetCredential="$expected_credential":zzz --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ] - - # This should succeed - systemd-run -p AssertCredential="$expected_credential" -p Type=oneshot true - - # And this should fail - (! systemd-run -p AssertCredential="undefinedcredential" -p Type=oneshot true) -fi - -# Verify that the creds are immutable -(! systemd-run -p LoadCredential=passwd:/etc/passwd \ - -p DynamicUser=1 \ - --unit=test-54-immutable-touch.service \ - --wait \ - touch '${CREDENTIALS_DIRECTORY}/passwd') -(! systemd-run -p LoadCredential=passwd:/etc/passwd \ - -p DynamicUser=1 \ - --unit=test-54-immutable-rm.service \ - --wait \ - rm '${CREDENTIALS_DIRECTORY}/passwd') - -# Check directory-based loading -mkdir -p /tmp/ts54-creds/sub -echo -n a >/tmp/ts54-creds/foo -echo -n b >/tmp/ts54-creds/bar -echo -n c >/tmp/ts54-creds/baz -echo -n d >/tmp/ts54-creds/sub/qux -systemd-run -p LoadCredential=cred:/tmp/ts54-creds \ - -p DynamicUser=1 \ - --unit=test-54-dir.service \ - --wait \ - --pipe \ - cat '${CREDENTIALS_DIRECTORY}/cred_foo' \ - '${CREDENTIALS_DIRECTORY}/cred_bar' \ - '${CREDENTIALS_DIRECTORY}/cred_baz' \ - '${CREDENTIALS_DIRECTORY}/cred_sub_qux' >/tmp/ts54-concat -cmp /tmp/ts54-concat <(echo -n abcd) -rm /tmp/ts54-concat -rm -rf /tmp/ts54-creds - -# Check that globs work as expected -mkdir -p /run/credstore -echo -n a >/run/credstore/test.creds.first -echo -n b >/run/credstore/test.creds.second -mkdir -p /etc/credstore -echo -n c >/etc/credstore/test.creds.third -systemd-run -p "ImportCredential=test.creds.*" \ - --unit=test-54-ImportCredential.service \ - -p DynamicUser=1 \ - --wait \ - --pipe \ - cat '${CREDENTIALS_DIRECTORY}/test.creds.first' \ - '${CREDENTIALS_DIRECTORY}/test.creds.second' \ - '${CREDENTIALS_DIRECTORY}/test.creds.third' >/tmp/ts54-concat -cmp /tmp/ts54-concat <(echo -n abc) - -# Now test encrypted credentials (only supported when built with OpenSSL though) -if systemctl --version | grep -q -- +OPENSSL ; then - echo -n $RANDOM >/tmp/test-54-plaintext - systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext - systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext - - systemd-run -p LoadCredentialEncrypted=test-54:/tmp/test-54-ciphertext \ - --wait \ - --pipe \ - cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext - - echo -n $RANDOM >/tmp/test-54-plaintext - systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext - systemd-creds decrypt --name=test-54 /tmp/test-54-ciphertext | cmp /tmp/test-54-plaintext - - systemd-run -p SetCredentialEncrypted=test-54:"$(cat /tmp/test-54-ciphertext)" \ - --wait \ - --pipe \ - cat '${CREDENTIALS_DIRECTORY}/test-54' | cmp /tmp/test-54-plaintext - - rm /tmp/test-54-plaintext /tmp/test-54-ciphertext -fi - -# https://github.com/systemd/systemd/issues/27275 -systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \ - -p 'ExecStartPre=true' \ - -p 'ExecStartPre=systemd-creds cat os' \ - --unit=test-54-exec-start.service \ - --wait \ - --pipe \ - true | cmp /etc/os-release - -if ! systemd-detect-virt -q -c ; then - # Validate that the credential we inserted via the initrd logic arrived - test "$(systemd-creds cat --system myinitrdcred)" = "guatemala" - - # Check that the fstab credential logic worked - test -d /injected - grep -q /injected /proc/self/mountinfo - - # Make sure the getty generator processed the credentials properly - systemctl -P Wants show getty.target | grep -q container-getty@idontexist.service -fi - -systemd-analyze log-level info - -touch /testok diff --git a/test/units/testsuite-55-testbloat.service b/test/units/testsuite-55-testbloat.service deleted file mode 100644 index 6c8e3c9..0000000 --- a/test/units/testsuite-55-testbloat.service +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[Unit] -Description=Create a lot of memory pressure - -[Service] -# A VERY small memory.high will cause the 'stress' (trying to use a lot of memory) -# to throttle and be put under heavy pressure. -MemoryHigh=3M -Slice=testsuite-55-workload.slice -ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1 diff --git a/test/units/testsuite-55-testchill.service b/test/units/testsuite-55-testchill.service deleted file mode 100644 index 369b802..0000000 --- a/test/units/testsuite-55-testchill.service +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[Unit] -Description=No memory pressure - -[Service] -MemoryHigh=3M -Slice=testsuite-55-workload.slice -ExecStart=sleep infinity diff --git a/test/units/testsuite-55-testmunch.service b/test/units/testsuite-55-testmunch.service deleted file mode 100644 index 3730059..0000000 --- a/test/units/testsuite-55-testmunch.service +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[Unit] -Description=Create some memory pressure - -[Service] -MemoryHigh=12M -Slice=testsuite-55-workload.slice -ExecStart=stress --timeout 3m --vm 10 --vm-bytes 200M --vm-keep --vm-stride 1 diff --git a/test/units/testsuite-55-workload.slice b/test/units/testsuite-55-workload.slice deleted file mode 100644 index d117b75..0000000 --- a/test/units/testsuite-55-workload.slice +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[Unit] -Description=Test slice for memory pressure kills - -[Slice] -CPUAccounting=true -MemoryAccounting=true -IOAccounting=true -TasksAccounting=true -ManagedOOMMemoryPressure=kill -ManagedOOMMemoryPressureLimit=20% diff --git a/test/units/testsuite-55.service b/test/units/testsuite-55.service deleted file mode 100644 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-55.sh b/test/units/testsuite-55.sh deleted file mode 100755 index 81617db..0000000 --- a/test/units/testsuite-55.sh +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh - . "$(dirname "$0")"/util.sh - -systemd-analyze log-level debug - -# Ensure that the init.scope.d drop-in is applied on boot -test "$(cat /sys/fs/cgroup/init.scope/memory.high)" != "max" - -# Loose checks to ensure the environment has the necessary features for systemd-oomd -[[ -e /proc/pressure ]] || echo "no PSI" >>/skipped -[[ "$(get_cgroup_hierarchy)" == "unified" ]] || echo "no cgroupsv2" >>/skipped -[[ -x /usr/lib/systemd/systemd-oomd ]] || echo "no oomd" >>/skipped -if [[ -s /skipped ]]; then - exit 0 -fi - -rm -rf /run/systemd/system/testsuite-55-testbloat.service.d - -# Activate swap file if we are in a VM -if systemd-detect-virt --vm --quiet; then - if [[ "$(findmnt -n -o FSTYPE /)" == btrfs ]]; then - btrfs filesystem mkswapfile -s 64M /swapfile - else - dd if=/dev/zero of=/swapfile bs=1M count=64 - chmod 0600 /swapfile - mkswap /swapfile - fi - - swapon /swapfile - swapon --show -fi - -# Configure oomd explicitly to avoid conflicts with distro dropins -mkdir -p /run/systemd/oomd.conf.d/ -cat >/run/systemd/oomd.conf.d/99-oomd-test.conf </run/systemd/system/-.slice.d/99-oomd-test.conf </run/systemd/system/user@.service.d/99-oomd-test.conf </run/systemd/system/systemd-oomd.service.d/debug.conf </run/systemd/system/testsuite-55-testchill.service.d/99-MemoryHigh.conf </run/systemd/system/testsuite-55-testbloat.service.d/override.conf </dev/null; then - echo "no systemd-repart" >/skipped - exit 0 -fi - -# shellcheck source=test/units/test-control.sh -. "$(dirname "$0")"/test-control.sh -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -export SYSTEMD_LOG_LEVEL=debug -export PAGER=cat - -# Disable use of special glyphs such as → -export SYSTEMD_UTF8=0 - -seed=750b6cd5c4ae4012a15e7be3c29e6a47 - -if ! systemd-detect-virt --quiet --container; then - udevadm control --log-level debug -fi - -machine="$(uname -m)" -if [ "${machine}" = "x86_64" ]; then - root_guid=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709 - root_uuid=60F33797-1D71-4DCB-AA6F-20564F036CD0 - root_uuid2=73A4CCD2-EAF5-44DA-A366-F99188210FDC - usr_guid=8484680C-9521-48C6-9C11-B0720656F69E - usr_uuid=7E3369DD-D653-4513-ADF5-B993A9F20C16 - architecture="x86-64" -elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then - root_guid=44479540-F297-41B2-9AF7-D131D5F0458A - root_uuid=02B4253F-29A4-404E-8972-1669D3B03C87 - root_uuid2=268E0FD3-B468-4806-A823-E533FE9BB9CC - usr_guid=75250D76-8CC6-458E-BD66-BD47CC81A812 - usr_uuid=7B42FFB0-B0E1-4395-B20B-C78F4A571648 - architecture="x86" -elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then - root_guid=B921B045-1DF0-41C3-AF44-4C6F280D3FAE - root_uuid=055D0227-53A6-4033-85C3-9A5973EFF483 - root_uuid2=F7DBBE48-8FD0-4833-8411-AA34E7C8E60A - usr_guid=B0E01050-EE5F-4390-949A-9101B17104E9 - usr_uuid=FCE3C75E-D6A4-44C0-87F0-4C105183FB1F - architecture="arm64" -elif [ "${machine}" = "arm" ]; then - root_guid=69DAD710-2CE4-4E3C-B16C-21A1D49ABED3 - root_uuid=567DA89E-8DE2-4499-8D10-18F212DFF034 - root_uuid2=813ECFE5-4C89-4193-8A52-437493F2F96E - usr_guid=7D0359A3-02B3-4F0A-865C-654403E70625 - usr_uuid=71E93DC2-5073-42CB-8A84-A354E64D8966 - architecture="arm" -elif [ "${machine}" = "loongarch64" ]; then - root_guid=77055800-792C-4F94-B39A-98C91B762BB6 - root_uuid=D8EFC2D2-0133-41E4-BDCB-3B9F4CFDDDE8 - root_uuid2=36499F9E-0688-40C1-A746-EA8FD9543C56 - usr_guid=E611C702-575C-4CBE-9A46-434FA0BF7E3F - usr_uuid=031FFA75-00BB-49B6-A70D-911D2D82A5B7 - architecture="loongarch64" -elif [ "${machine}" = "ia64" ]; then - root_guid=993D8D3D-F80E-4225-855A-9DAF8ED7EA97 - root_uuid=DCF33449-0896-4EA9-BC24-7D58AEEF522D - root_uuid2=C2A6CAB7-ABEA-4FBA-8C48-CB4C52E6CA38 - usr_guid=4301D2A6-4E3B-4B2A-BB94-9E0B2C4225EA - usr_uuid=BC2BCCE7-80D6-449A-85CC-637424CE5241 - architecture="ia64" -elif [ "${machine}" = "s390x" ]; then - root_guid=5EEAD9A9-FE09-4A1E-A1D7-520D00531306 - root_uuid=7EBE0C85-E27E-48EC-B164-F4807606232E - root_uuid2=2A074E1C-2A19-4094-A0C2-24B1A5D52FCB - usr_guid=8A4F5770-50AA-4ED3-874A-99B710DB6FEA - usr_uuid=51171D30-35CF-4A49-B8B5-9478B9B796A5 - architecture="s390x" -elif [ "${machine}" = "ppc64le" ]; then - root_guid=C31C45E6-3F39-412E-80FB-4809C4980599 - root_uuid=061E67A1-092F-482F-8150-B525D50D6654 - root_uuid2=A6687CEF-4E4F-44E7-90B3-CDA52EA81739 - usr_guid=15BB03AF-77E7-4D4A-B12B-C0D084F7491C - usr_uuid=C0D0823B-8040-4C7C-A629-026248E297FB - architecture="ppc64-le" -else - echo "Unexpected uname -m: ${machine} in testsuite-58.sh, please fix me" - exit 1 -fi - -testcase_basic() { - local defs imgs output - local loop volume - - defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")" - imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")" - # shellcheck disable=SC2064 - trap "rm -rf '$defs' '$imgs'" RETURN - chmod 0755 "$defs" - - echo "*** 1. create an empty image ***" - - systemd-repart --offline="$OFFLINE" \ - --empty=create \ - --size=1G \ - --seed="$seed" \ - "$imgs/zzz" - - output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$') - - assert_eq "$output" "label: gpt -label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD -device: $imgs/zzz -unit: sectors -first-lba: 2048 -last-lba: 2097118" - - echo "*** 2. Testing with root, root2, home, and swap ***" - - tee "$defs/root.conf" <>"$defs/home.conf" - echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>"$defs/home.conf" - - systemd-repart --offline="$OFFLINE" \ - --definitions="$defs" \ - --dry-run=no \ - --seed="$seed" \ - "$imgs/zzz" - - output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$') - - assert_eq "$output" "label: gpt -label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD -device: $imgs/zzz -unit: sectors -first-lba: 2048 -last-lba: 2097118 -$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\" -$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\" -$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\" -$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\" -$imgs/zzz5 : start= 1908696, size= 188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"" - - echo "*** 4. Resizing to 2G ***" - - systemd-repart --offline="$OFFLINE" \ - --definitions="$defs" \ - --size=2G \ - --dry-run=no \ - --seed="$seed" \ - "$imgs/zzz" - - output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$') - - assert_eq "$output" "label: gpt -label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD -device: $imgs/zzz -unit: sectors -first-lba: 2048 -last-lba: 4194270 -$imgs/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\" -$imgs/zzz2 : start= 593904, size= 591856, type=${root_guid}, uuid=${root_uuid}, name=\"root-${architecture}\", attrs=\"GUID:59\" -$imgs/zzz3 : start= 1185760, size= 591864, type=${root_guid}, uuid=${root_uuid2}, name=\"root-${architecture}-2\", attrs=\"GUID:59\" -$imgs/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\" -$imgs/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name=\"custom_label\"" - - echo "*** 5. Testing with root, root2, home, swap, another partition, and partition copy ***" - - dd if=/dev/urandom of="$imgs/block-copy" bs=4096 count=10240 - - tee "$defs/extra2.conf" </dev/null - umount "$imgs/mount" -} - -testcase_dropin() { - local defs imgs output - - defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")" - imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")" - # shellcheck disable=SC2064 - trap "rm -rf '$defs' '$imgs'" RETURN - chmod 0755 "$defs" - - tee "$defs/root.conf" < 32.0M", - "old_padding" : 0, - "raw_padding" : 0, - "padding" : "-> 0B", - "activity" : "create", - "drop-in_files" : [ - "$defs/root.conf.d/override1.conf", - "$defs/root.conf.d/override2.conf" - ] - } -] -EOF -} - -testcase_multiple_definitions() { - local defs imgs output - - defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")" - imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")" - # shellcheck disable=SC2064 - trap "rm -rf '$defs' '$imgs'" RETURN - chmod 0755 "$defs" - - mkdir -p "$defs/1" - tee "$defs/1/root1.conf" < 32.0M", - "old_padding" : 0, - "raw_padding" : 0, - "padding" : "-> 0B", - "activity" : "create" - }, - { - "type" : "swap", - "label" : "label2", - "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3", - "partno" : 1, - "file" : "$defs/2/root2.conf", - "node" : "$imgs/zzz2", - "offset" : 34603008, - "old_size" : 0, - "raw_size" : 33554432, - "size" : "-> 32.0M", - "old_padding" : 0, - "raw_padding" : 0, - "padding" : "-> 0B", - "activity" : "create" - } -] -EOF -} - -testcase_copy_blocks() { - local defs imgs output - - defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")" - imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")" - # shellcheck disable=SC2064 - trap "rm -rf '$defs' '$imgs'" RETURN - chmod 0755 "$defs" - - echo "*** First, create a disk image and verify its in order ***" - - tee "$defs/esp.conf" <"$defs/verity.openssl.cnf" </dev/null; then - continue - fi - - tee "$defs/root-$format.conf" </dev/null; then - tee "$defs/root-squashfs.conf" </dev/null; then - echo "Skipping free area calculation test without squashfs." - return - fi - - defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")" - imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")" - # shellcheck disable=SC2064 - trap "rm -rf '$defs' '$imgs'" RETURN - chmod 0755 "$defs" - - # https://github.com/systemd/systemd/issues/28225 - echo "*** free area calculation ***" - - tee "$defs/00-ESP.conf" <"$defs/10-part1.conf" - echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=1\n" >"$defs/11-dropped-first.conf" - echo -ne "[Partition]\nType=root\n" >"$defs/12-part2.conf" - echo -ne "[Partition]\nType=root\nSizeMinBytes=1T\nPriority=2\n" >"$defs/13-dropped-second.conf" - - systemd-repart --empty=allow --pretty=yes --dry-run=no --definitions="$defs" "$image" - - sfdisk -q -l "$image" - [[ "$(sfdisk -q -l "$image" | grep -c "$image")" -eq 2 ]] -} - -OFFLINE="yes" -run_testcases - -# Online image builds need loop devices so we can't run them in nspawn. -if ! systemd-detect-virt --container; then - OFFLINE="no" - run_testcases -fi - -# Valid block sizes on the Linux block layer are >= 512 and <= PAGE_SIZE, and -# must be powers of 2. Which leaves exactly four different ones to test on -# typical hardware -test_sector 512 -test_sector 1024 -test_sector 2048 -test_sector 4096 - -touch /testok diff --git a/test/units/testsuite-59.service b/test/units/testsuite-59.service deleted file mode 100644 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-59.sh b/test/units/testsuite-59.sh deleted file mode 100755 index 1b622b3..0000000 --- a/test/units/testsuite-59.sh +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -fail() { - systemd-analyze log-level info - exit 1 -} - -# Wait for a service to enter a state within a timeout period, if it doesn't -# enter the desired state within the timeout period then this function will -# exit the test case with a non zero exit code. -wait_on_state_or_fail() { - service=$1 - expected_state=$2 - timeout=$3 - - state=$(systemctl show "$service" --property=ActiveState --value) - while [ "$state" != "$expected_state" ]; do - if [ "$timeout" = "0" ]; then - fail - fi - timeout=$((timeout - 1)) - sleep 1 - state=$(systemctl show "$service" --property=ActiveState --value) - done -} - -systemd-analyze log-level debug - - -cat >/run/systemd/system/testservice-fail-59.service </run/systemd/system/testservice-fail-restart-59.service </run/systemd/system/testservice-abort-restart-59.service </run/systemd/system.conf.d/50-test-59-reload.conf </run/notify-reload-test.sh </run/systemd/system/tmp-deptest.mount </run/systemd/system/"$unit" <&2 "Test mount \"$unit\" unit isn't mounted" - return 1 - } - mountpoint -q "$tmpdir" - - trap 'systemctl stop $unit' RETURN - - # Trigger the mount ratelimiting - cd "$(mktemp -d)" - mkdir foo - for _ in {1..50}; do - mount --bind foo foo - umount foo - done - - # Unmount the test mount and start it immediately again via systemd - umount "$tmpdir" - systemctl start "$unit" - - # Make sure it is seen as mounted by systemd and it actually is mounted - [[ "$(systemctl show --property SubState --value "$unit")" = "mounted" ]] || { - echo >&2 "Test mount \"$unit\" unit isn't in \"mounted\" state" - return 1 - } - - mountpoint -q "$tmpdir" || { - echo >&2 "Test mount \"$unit\" is in \"mounted\" state, actually is not mounted" - return 1 - } -} - -test_issue_23796() { - local mount_path mount_mytmpfs - - mount_path="$(command -v mount 2>/dev/null)" - mount_mytmpfs="${mount_path/\/bin/\/sbin}.mytmpfs" - cat >"$mount_mytmpfs" </run/systemd/system/tmp-hoge.mount <>/skipped - exit 0 -fi - -if systemctl --version | grep -q -F -- "-BPF_FRAMEWORK"; then - echo "bpf-framework is disabled" >>/skipped - exit 0 -fi - -trap teardown EXIT -setup - -systemctl start --wait testsuite-62-1.service -systemctl start --wait testsuite-62-2.service -systemctl start --wait testsuite-62-3.service -systemctl start --wait testsuite-62-4.service -systemctl start --wait testsuite-62-5.service - -touch /testok diff --git a/test/units/testsuite-63.service b/test/units/testsuite-63.service deleted file mode 100644 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-63.sh b/test/units/testsuite-63.sh deleted file mode 100755 index ea8cd94..0000000 --- a/test/units/testsuite-63.sh +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -systemctl log-level debug - -# Test that a path unit continuously triggering a service that fails condition checks eventually fails with -# the trigger-limit-hit error. -rm -f /tmp/nonexistent -systemctl start test63.path -touch /tmp/test63 - -# Make sure systemd has sufficient time to hit the trigger limit for test63.path. -# shellcheck disable=SC2016 -timeout 30 bash -c 'until test "$(systemctl show test63.path -P ActiveState)" = failed; do sleep .2; done' -test "$(systemctl show test63.service -P ActiveState)" = inactive -test "$(systemctl show test63.service -P Result)" = success -test "$(systemctl show test63.path -P Result)" = trigger-limit-hit - -# Test that starting the service manually doesn't affect the path unit. -rm -f /tmp/test63 -systemctl reset-failed -systemctl start test63.path -systemctl start test63.service -test "$(systemctl show test63.service -P ActiveState)" = inactive -test "$(systemctl show test63.service -P Result)" = success -test "$(systemctl show test63.path -P ActiveState)" = active -test "$(systemctl show test63.path -P Result)" = success - -# Test that glob matching works too, with $TRIGGER_PATH -systemctl start test63-glob.path -touch /tmp/test63-glob-foo -timeout 60 bash -c 'until systemctl -q is-active test63-glob.service; do sleep .2; done' -test "$(systemctl show test63-glob.service -P ActiveState)" = active -test "$(systemctl show test63-glob.service -P Result)" = success - -test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[["trigger_unit","test63-glob.path"],["trigger_path","/tmp/test63-glob-foo"]]}' - -systemctl stop test63-glob.path test63-glob.service - -test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[]}' - -# tests for issue https://github.com/systemd/systemd/issues/24577#issuecomment-1522628906 -rm -f /tmp/hoge -systemctl start test63-issue-24577.path -systemctl status -n 0 test63-issue-24577.path -systemctl status -n 0 test63-issue-24577.service || : -systemctl list-jobs -output=$(systemctl list-jobs --no-legend) -assert_not_in "test63-issue-24577.service" "$output" -assert_not_in "test63-issue-24577-dep.service" "$output" - -touch /tmp/hoge -systemctl status -n 0 test63-issue-24577.path -systemctl status -n 0 test63-issue-24577.service || : -systemctl list-jobs -output=$(systemctl list-jobs --no-legend) -assert_in "test63-issue-24577.service" "$output" -assert_in "test63-issue-24577-dep.service" "$output" - -# even if the service is stopped, it will be soon retriggered. -systemctl stop test63-issue-24577.service -systemctl status -n 0 test63-issue-24577.path -systemctl status -n 0 test63-issue-24577.service || : -systemctl list-jobs -output=$(systemctl list-jobs --no-legend) -assert_in "test63-issue-24577.service" "$output" -assert_in "test63-issue-24577-dep.service" "$output" - -rm -f /tmp/hoge -systemctl stop test63-issue-24577.service -systemctl status -n 0 test63-issue-24577.path -systemctl status -n 0 test63-issue-24577.service || : -systemctl list-jobs -output=$(systemctl list-jobs --no-legend) -assert_not_in "test63-issue-24577.service" "$output" -assert_in "test63-issue-24577-dep.service" "$output" - -# Test for race condition fixed by https://github.com/systemd/systemd/pull/30768 -# Here's the schedule of events that we to happen during this test: -# (This test) (The service) -# .path unit monitors /tmp/copyme for changes -# Take lock on /tmp/noexeit ↓ -# Write to /tmp/copyme ↓ -# Wait for deactivating Started -# ↓ Copies /tmp/copyme to /tmp/copied -# ↓ Tells manager it's shutting down -# Ensure service did the copy Tries to lock /tmp/noexit and blocks -# Write to /tmp/copyme ↓ -# -# Now at this point the test can diverge. If we regress, this second write is -# missed and we'll see: -# ... (second write) ... (blocked) -# Drop lock on /tmp/noexit ↓ -# Wait for service to do copy Unblocks and exits -# ↓ (dead) -# ↓ -# (timeout) -# Test fails -# -# Otherwise, we'll see: -# ... (second write) ... (blocked) -# Drop lock on /tmp/noexit ↓ and .path unit queues a new start job -# Wait for service to do copy Unblocks and exits -# ↓ Starts again b/c of queued job -# ↓ Copies again -# Test Passes -systemctl start test63-pr-30768.path -exec {lock}<>/tmp/noexit -flock -e $lock -echo test1 > /tmp/copyme -# shellcheck disable=SC2016 -timeout 30 bash -c 'until test "$(systemctl show test63-pr-30768.service -P ActiveState)" = deactivating; do sleep .2; done' -diff /tmp/copyme /tmp/copied -echo test2 > /tmp/copyme -exec {lock}<&- -timeout 30 bash -c 'until diff /tmp/copyme /tmp/copied; do sleep .2; done' - -systemctl log-level info - -touch /testok diff --git a/test/units/testsuite-64.service b/test/units/testsuite-64.service deleted file mode 100644 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-64.sh b/test/units/testsuite-64.sh deleted file mode 100755 index 65e5f6c..0000000 --- a/test/units/testsuite-64.sh +++ /dev/null @@ -1,1192 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# vi: ts=4 sw=4 tw=0 et: - -set -eux -set -o pipefail - -# Check if all symlinks under /dev/disk/ are valid -# shellcheck disable=SC2120 -helper_check_device_symlinks() {( - set +x - - local dev link path paths target - - [[ $# -gt 0 ]] && paths=("$@") || paths=("/dev/disk" "/dev/mapper") - - # Check if all given paths are valid - for path in "${paths[@]}"; do - if ! test -e "$path"; then - echo >&2 "Path '$path' doesn't exist" - return 1 - fi - done - - while read -r link; do - target="$(readlink -f "$link")" - # Both checks should do virtually the same thing, but check both to be - # on the safe side - if [[ ! -e "$link" || ! -e "$target" ]]; then - echo >&2 "ERROR: symlink '$link' points to '$target' which doesn't exist" - return 1 - fi - - # Check if the symlink points to the correct device in /dev - dev="/dev/$(udevadm info -q name "$link")" - if [[ "$target" != "$dev" ]]; then - echo >&2 "ERROR: symlink '$link' points to '$target' but '$dev' was expected" - return 1 - fi - done < <(find "${paths[@]}" -type l) -)} - -helper_check_udev_watch() {( - set +x - - local link target id dev - - while read -r link; do - target="$(readlink "$link")" - if [[ ! -L "/run/udev/watch/$target" ]]; then - echo >&2 "ERROR: symlink /run/udev/watch/$target does not exist" - return 1 - fi - if [[ "$(readlink "/run/udev/watch/$target")" != "$(basename "$link")" ]]; then - echo >&2 "ERROR: symlink target of /run/udev/watch/$target is inconsistent with $link" - return 1 - fi - - if [[ "$target" =~ ^[0-9]+$ ]]; then - # $link is ID -> wd - id="$(basename "$link")" - else - # $link is wd -> ID - id="$target" - fi - - if [[ "${id:0:1}" == "b" ]]; then - dev="/dev/block/${id:1}" - elif [[ "${id:0:1}" == "c" ]]; then - dev="/dev/char/${id:1}" - else - echo >&2 "ERROR: unexpected device ID '$id'" - return 1 - fi - - if [[ ! -e "$dev" ]]; then - echo >&2 "ERROR: device '$dev' corresponding to symlink '$link' does not exist" - return 1 - fi - done < <(find /run/udev/watch -type l) -)} - -check_device_unit() {( - set +x - - local log_level link links path syspath unit - - log_level="${1?}" - path="${2?}" - unit=$(systemd-escape --path --suffix=device "$path") - - [[ "$log_level" == 1 ]] && echo "INFO: check_device_unit($unit)" - - syspath=$(systemctl show --value --property SysFSPath "$unit" 2>/dev/null) - if [[ -z "$syspath" ]]; then - [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit not found." - return 1 - fi - - if [[ ! -L "$path" ]]; then - if [[ ! -d "$syspath" ]]; then - [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit exists for $syspath but it does not exist." - return 1 - fi - return 0 - fi - - if [[ ! -b "$path" && ! -c "$path" ]]; then - [[ "$log_level" == 1 ]] && echo >&2 "ERROR: invalid file type $path" - return 1 - fi - - read -r -a links < <(udevadm info -q symlink "$syspath" 2>/dev/null) - for link in "${links[@]}"; do - if [[ "/dev/$link" == "$path" ]]; then # DEVLINKS= given by -q symlink are relative to /dev - return 0 - fi - done - - read -r -a links < <(udevadm info "$syspath" | sed -ne '/SYSTEMD_ALIAS=/ { s/^E: SYSTEMD_ALIAS=//; p }' 2>/dev/null) - for link in "${links[@]}"; do - if [[ "$link" == "$path" ]]; then # SYSTEMD_ALIAS= are absolute - return 0 - fi - done - - [[ "$log_level" == 1 ]] && echo >&2 "ERROR: $unit exists for $syspath but it does not have the corresponding DEVLINKS or SYSTEMD_ALIAS." - return 1 -)} - -check_device_units() {( - set +x - - local log_level path paths - - log_level="${1?}" - shift - paths=("$@") - - for path in "${paths[@]}"; do - if ! check_device_unit "$log_level" "$path"; then - return 1 - fi - done - - while read -r unit _; do - path=$(systemd-escape --path --unescape "$unit") - if ! check_device_unit "$log_level" "$path"; then - return 1 - fi - done < <(systemctl list-units --all --type=device --no-legend dev-* | awk '$1 !~ /dev-tty.+/ { print $1 }' | sed -e 's/\.device$//') - - return 0 -)} - -helper_check_device_units() {( - set +x - - local i - - for i in {1..20}; do - (( i > 1 )) && sleep 0.5 - if check_device_units 0 "$@"; then - return 0 - fi - done - - check_device_units 1 "$@" -)} - -testcase_megasas2_basic() { - lsblk -S - [[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]] -} - -testcase_nvme_basic() { - local expected_symlinks=() - local i - - for (( i = 0; i < 5; i++ )); do - expected_symlinks+=( - # both replace mode provides the same devlink - /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef"$i" - # with nsid - /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef"$i"_1 - ) - done - for (( i = 5; i < 10; i++ )); do - expected_symlinks+=( - # old replace mode - /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl__deadbeef_"$i" - # newer replace mode - /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____deadbeef__"$i" - # with nsid - /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____deadbeef__"$i"_1 - ) - done - for (( i = 10; i < 15; i++ )); do - expected_symlinks+=( - # old replace mode does not provide devlink, as serial contains "/" - # newer replace mode - /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____dead_beef_"$i" - # with nsid - /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_____dead_beef_"$i"_1 - ) - done - for (( i = 15; i < 20; i++ )); do - expected_symlinks+=( - # old replace mode does not provide devlink, as serial contains "/" - # newer replace mode - /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_dead_.._.._beef_"$i" - # with nsid - /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_dead_.._.._beef_"$i"_1 - ) - done - - udevadm settle - ls /dev/disk/by-id - for i in "${expected_symlinks[@]}"; do - udevadm wait --settle --timeout=30 "$i" - done - - lsblk --noheadings | grep "^nvme" - [[ "$(lsblk --noheadings | grep -c "^nvme")" -ge 20 ]] -} - -testcase_nvme_subsystem() { - local expected_symlinks=( - # Controller(s) - /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef - /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_16 - /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_17 - # Shared namespaces - /dev/disk/by-path/pci-*-nvme-16 - /dev/disk/by-path/pci-*-nvme-17 - ) - - udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" -} - -testcase_virtio_scsi_identically_named_partitions() { - local num - - if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then - num=$((4 * 4)) - else - num=$((16 * 8)) - fi - - lsblk --noheadings -a -o NAME,PARTLABEL - [[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq "$num" ]] -} - -testcase_multipath_basic_failover() { - local dmpath i path wwid - - # Configure multipath - cat >/etc/multipath.conf <<\EOF -defaults { - # Use /dev/mapper/$WWN paths instead of /dev/mapper/mpathX - user_friendly_names no - find_multipaths yes - enable_foreign "^$" -} - -blacklist_exceptions { - property "(SCSI_IDENT_|ID_WWN)" -} - -blacklist { -} -EOF - modprobe -v dm_multipath - systemctl start multipathd.service - systemctl status multipathd.service - multipath -ll - udevadm settle - ls -l /dev/disk/by-id/ - - for i in {0..15}; do - wwid="deaddeadbeef$(printf "%.4d" "$i")" - path="/dev/disk/by-id/wwn-0x$wwid" - dmpath="$(readlink -f "$path")" - - lsblk "$path" - multipath -C "$dmpath" - # We should have 4 active paths for each multipath device - [[ "$(multipath -l "$path" | grep -c running)" -eq 4 ]] - done - - # Test failover (with the first multipath device that has a partitioned disk) - echo "${FUNCNAME[0]}: test failover" - local device expected link mpoint part - local -a devices - mkdir -p /mnt - mpoint="$(mktemp -d /mnt/mpathXXX)" - wwid="deaddeadbeef0000" - path="/dev/disk/by-id/wwn-0x$wwid" - - # All following symlinks should exists and should be valid - local -a part_links=( - "/dev/disk/by-id/wwn-0x$wwid-part2" - "/dev/disk/by-partlabel/failover_part" - "/dev/disk/by-partuuid/deadbeef-dead-dead-beef-000000000000" - "/dev/disk/by-label/failover_vol" - "/dev/disk/by-uuid/deadbeef-dead-dead-beef-111111111111" - ) - udevadm wait --settle --timeout=30 "${part_links[@]}" - helper_check_device_units "${part_links[@]}" - - # Choose a random symlink to the failover data partition each time, for - # a better coverage - part="${part_links[$RANDOM % ${#part_links[@]}]}" - - # Get all devices attached to a specific multipath device (in H:C:T:L format) - # and sort them in a random order, so we cut off different paths each time - mapfile -t devices < <(multipath -l "$path" | grep -Eo '[0-9]+:[0-9]+:[0-9]+:[0-9]+' | sort -R) - if [[ "${#devices[@]}" -ne 4 ]]; then - echo "Expected 4 devices attached to WWID=$wwid, got ${#devices[@]} instead" - return 1 - fi - # Drop the last path from the array, since we want to leave at least one path active - unset "devices[3]" - # Mount the first multipath partition, write some data we can check later, - # and then disconnect the remaining paths one by one while checking if we - # can still read/write from the mount - mount -t ext4 "$part" "$mpoint" - expected=0 - echo -n "$expected" >"$mpoint/test" - # Sanity check we actually wrote what we wanted - [[ "$(<"$mpoint/test")" == "$expected" ]] - - for device in "${devices[@]}"; do - echo offline >"/sys/class/scsi_device/$device/device/state" - [[ "$(<"$mpoint/test")" == "$expected" ]] - expected="$((expected + 1))" - echo -n "$expected" >"$mpoint/test" - - # Make sure all symlinks are still valid - udevadm wait --settle --timeout=30 "${part_links[@]}" - helper_check_device_units "${part_links[@]}" - done - - multipath -l "$path" - # Three paths should be now marked as 'offline' and one as 'running' - [[ "$(multipath -l "$path" | grep -c offline)" -eq 3 ]] - [[ "$(multipath -l "$path" | grep -c running)" -eq 1 ]] - - umount "$mpoint" - rm -fr "$mpoint" -} - -testcase_simultaneous_events_1() { - local disk expected i iterations key link num_part part partscript rule target timeout - local -a devices symlinks - local -A running - - if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then - num_part=2 - iterations=10 - timeout=240 - else - num_part=10 - iterations=100 - timeout=30 - fi - - for disk in {0..9}; do - link="/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_deadbeeftest${disk}" - target="$(readlink -f "$link")" - if [[ ! -b "$target" ]]; then - echo "ERROR: failed to find the test SCSI block device $link" - return 1 - fi - - devices+=("$target") - done - - for ((part = 1; part <= num_part; part++)); do - symlinks+=( - "/dev/disk/by-partlabel/test${part}" - ) - done - - partscript="$(mktemp)" - - cat >"$partscript" <"$rule" <&2 "ERROR: symlink '/dev/disk/by-partlabel/test${part}' points to '$target' but '$expected' was expected" - return 1 - fi - done - fi - done - - helper_check_device_units - rm -f "$rule" "$partscript" - - udevadm control --reload -} - -testcase_simultaneous_events_2() { - local disk expected i iterations key link num_part part script_dir target timeout - local -a devices symlinks - local -A running - - script_dir="$(mktemp --directory "/tmp/test-udev-storage.script.XXXXXXXXXX")" - # shellcheck disable=SC2064 - trap "rm -rf '$script_dir'" RETURN - - if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then - num_part=20 - iterations=1 - timeout=2400 - else - num_part=100 - iterations=3 - timeout=300 - fi - - for disk in {0..9}; do - link="/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_deadbeeftest${disk}" - target="$(readlink -f "$link")" - if [[ ! -b "$target" ]]; then - echo "ERROR: failed to find the test SCSI block device $link" - return 1 - fi - - devices+=("$target") - done - - for ((i = 1; i <= iterations; i++)); do - cat >"$script_dir/partscript-$i" <>/etc/fstab - systemctl daemon-reload - mount "/tmp/lvmluksmnt" - mountpoint "/tmp/lvmluksmnt" - # Temporarily suspend the LUKS device and trigger udev - basically what `cryptsetup resize` - # does but in a more deterministic way suitable for a test/reproducer - for _ in {0..5}; do - dmsetup suspend "/dev/mapper/lvmluksmap" - udevadm trigger -v --settle "/dev/mapper/lvmluksmap" - dmsetup resume "/dev/mapper/lvmluksmap" - # The mount should survive this sequence of events - mountpoint "/tmp/lvmluksmnt" - done - # Cleanup - umount "/tmp/lvmluksmnt" - cryptsetup close "/dev/mapper/lvmluksmap" - sed -i "/lvmluksfs/d" "/etc/fstab" - systemctl daemon-reload - - # Disable the VG and check symlinks... - lvm vgchange -an "$vgroup" - udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup" "/dev/disk/by-label/mylvpart1" - helper_check_device_symlinks "/dev/disk" - helper_check_device_units - - # reenable the VG and check the symlinks again if all LVs are properly activated - lvm vgchange -ay "$vgroup" - udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1" - helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" - helper_check_device_units - - # Same as above, but now with more "stress" - if [[ -v ASAN_OPTIONS || "$(systemd-detect-virt -v)" == "qemu" ]]; then - iterations=10 - else - iterations=50 - fi - - for ((i = 1; i <= iterations; i++)); do - lvm vgchange -an "$vgroup" - lvm vgchange -ay "$vgroup" - - if ((i % 5 == 0)); then - udevadm wait --settle --timeout="$timeout" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1" - helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" - helper_check_device_units - fi - done - - # Remove the first LV - lvm lvremove -y "$vgroup/mypart1" - udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/mypart1" - udevadm wait --timeout=0 "/dev/$vgroup/mypart2" - helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" - helper_check_device_units - - # Create & remove LVs in a loop, i.e. with more "stress" - if [[ -v ASAN_OPTIONS ]]; then - iterations=8 - partitions=16 - elif [[ "$(systemd-detect-virt -v)" == "qemu" ]]; then - iterations=8 - partitions=8 - else - iterations=16 - partitions=16 - fi - - for ((i = 1; i <= iterations; i++)); do - # 1) Create some logical volumes - for ((part = 0; part < partitions; part++)); do - lvm lvcreate -y -L 4M "$vgroup" -n "looppart$part" - done - - # 2) Immediately remove them - lvm lvremove -y $(seq -f "$vgroup/looppart%g" 0 "$((partitions - 1))") - - # 3) On every 4th iteration settle udev and check if all partitions are - # indeed gone, and if all symlinks are still valid - if ((i % 4 == 0)); then - for ((part = 0; part < partitions; part++)); do - udevadm wait --settle --timeout="$timeout" --removed "/dev/$vgroup/looppart$part" - done - helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" - helper_check_device_units - fi - done -} - -testcase_btrfs_basic() { - local dev_stub i label mpoint uuid - local devices=( - /dev/disk/by-id/ata-foobar_deadbeefbtrfs{0..3} - ) - - ls -l "${devices[@]}" - - echo "Single device: default settings" - uuid="deadbeef-dead-dead-beef-000000000000" - label="btrfs_root" - udevadm lock --device="${devices[0]}" mkfs.btrfs -f -L "$label" -U "$uuid" "${devices[0]}" - udevadm wait --settle --timeout=30 "${devices[0]}" "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label" - btrfs filesystem show - helper_check_device_symlinks - helper_check_device_units - - echo "Multiple devices: using partitions, data: single, metadata: raid1" - uuid="deadbeef-dead-dead-beef-000000000001" - label="btrfs_mpart" - udevadm lock --device="${devices[0]}" sfdisk --wipe=always "${devices[0]}" </etc/crypttab - for ((i = 0; i < ${#devices[@]}; i++)); do - # Intentionally use weaker cipher-related settings, since we don't care - # about security here as it's a throwaway LUKS partition - cryptsetup luksFormat -q \ - --use-urandom --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \ - --uuid "deadbeef-dead-dead-beef-11111111111$i" --label "encdisk$i" "${devices[$i]}" /etc/btrfs_keyfile - udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/deadbeef-dead-dead-beef-11111111111$i" "/dev/disk/by-label/encdisk$i" - # Add the device into /etc/crypttab, reload systemd, and then activate - # the device so we can create a filesystem on it later - echo "encbtrfs$i UUID=deadbeef-dead-dead-beef-11111111111$i /etc/btrfs_keyfile luks" >>/etc/crypttab - systemctl daemon-reload - systemctl start "systemd-cryptsetup@encbtrfs$i" - done - helper_check_device_symlinks - helper_check_device_units - # Check if we have all necessary DM devices - ls -l /dev/mapper/encbtrfs{0..3} - # Create a multi-device btrfs filesystem on the LUKS devices - udevadm lock \ - --device=/dev/mapper/encbtrfs0 \ - --device=/dev/mapper/encbtrfs1 \ - --device=/dev/mapper/encbtrfs2 \ - --device=/dev/mapper/encbtrfs3 \ - mkfs.btrfs -f -M -d raid1 -m raid1 -L "$label" -U "$uuid" /dev/mapper/encbtrfs{0..3} - udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label" - btrfs filesystem show - helper_check_device_symlinks - helper_check_device_units - # Mount it and write some data to it we can compare later - mount -t btrfs /dev/mapper/encbtrfs0 "$mpoint" - echo "hello there" >"$mpoint/test" - # "Deconstruct" the btrfs device and check if we're in a sane state (symlink-wise) - umount "$mpoint" - systemctl stop systemd-cryptsetup@encbtrfs{0..3} - udevadm wait --settle --timeout=30 --removed "/dev/disk/by-uuid/$uuid" - helper_check_device_symlinks - helper_check_device_units - # Add the mount point to /etc/fstab and check if the device can be put together - # automagically. The source device is the DM name of the first LUKS device - # (from /etc/crypttab). We have to specify all LUKS devices manually, as - # registering the necessary devices is usually initrd's job (via btrfs device scan) - dev_stub="/dev/mapper/encbtrfs" - echo "/dev/mapper/encbtrfs0 $mpoint btrfs device=${dev_stub}0,device=${dev_stub}1,device=${dev_stub}2,device=${dev_stub}3 0 2" >>/etc/fstab - # Tell systemd about the new mount - systemctl daemon-reload - # Restart cryptsetup.target to trigger autounlock of partitions in /etc/crypttab - systemctl restart cryptsetup.target - # Start the corresponding mount unit and check if the btrfs device was reconstructed - # correctly - systemctl start "${mpoint##*/}.mount" - udevadm wait --settle --timeout=30 "/dev/disk/by-uuid/$uuid" "/dev/disk/by-label/$label" - btrfs filesystem show - helper_check_device_symlinks - helper_check_device_units - grep "hello there" "$mpoint/test" - # Cleanup - systemctl stop "${mpoint##*/}.mount" - systemctl stop systemd-cryptsetup@encbtrfs{0..3} - sed -i "/${mpoint##*/}/d" /etc/fstab - : >/etc/crypttab - rm -fr "$mpoint" - systemctl daemon-reload - udevadm settle -} - -testcase_iscsi_lvm() { - local dev i label link lun_id mpoint target_name uuid - local target_ip="127.0.0.1" - local target_port="3260" - local vgroup="iscsi_lvm$RANDOM" - local expected_symlinks=() - local devices=( - /dev/disk/by-id/ata-foobar_deadbeefiscsi{0..3} - ) - - ls -l "${devices[@]}" - - # Start the target daemon - systemctl start tgtd - systemctl status tgtd - - echo "iSCSI LUNs backed by devices" - # See RFC3721 and RFC7143 - target_name="iqn.2021-09.com.example:iscsi.test" - # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each - # backed by a device - tgtadm --lld iscsi --op new --mode target --tid=1 --targetname "$target_name" - for ((i = 0; i < ${#devices[@]}; i++)); do - # lun-0 is reserved by iSCSI - lun_id="$((i + 1))" - tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun "$lun_id" -b "${devices[$i]}" - tgtadm --lld iscsi --op update --mode logicalunit --tid 1 --lun "$lun_id" - expected_symlinks+=( - "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$lun_id" - ) - done - tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL - # Configure the iSCSI initiator - iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover - iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login - udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" - helper_check_device_symlinks - helper_check_device_units - # Cleanup - iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout - tgtadm --lld iscsi --op delete --mode target --tid=1 - - echo "iSCSI LUNs backed by files + LVM" - # Note: we use files here to "trick" LVM the disks are indeed on a different - # host, so it doesn't automagically detect another path to the backing - # device once we disconnect the iSCSI devices - target_name="iqn.2021-09.com.example:iscsi.lvm.test" - mpoint="$(mktemp -d /iscsi_storeXXX)" - expected_symlinks=() - # Use the first device as it's configured with larger capacity - mkfs.ext4 -L iscsi_store "${devices[0]}" - udevadm wait --settle --timeout=30 "${devices[0]}" - mount "${devices[0]}" "$mpoint" - for i in {1..4}; do - dd if=/dev/zero of="$mpoint/lun$i.img" bs=1M count=32 - done - # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each - # backed by a file - tgtadm --lld iscsi --op new --mode target --tid=2 --targetname "$target_name" - # lun-0 is reserved by iSCSI - for i in {1..4}; do - tgtadm --lld iscsi --op new --mode logicalunit --tid 2 --lun "$i" -b "$mpoint/lun$i.img" - tgtadm --lld iscsi --op update --mode logicalunit --tid 2 --lun "$i" - expected_symlinks+=( - "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$i" - ) - done - tgtadm --lld iscsi --op bind --mode target --tid 2 -I ALL - # Configure the iSCSI initiator - iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover - iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login - udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" - helper_check_device_symlinks - helper_check_device_units - # Add all iSCSI devices into a LVM volume group, create two logical volumes, - # and check if necessary symlinks exist (and are valid) - lvm pvcreate -y "${expected_symlinks[@]}" - lvm pvs - lvm vgcreate "$vgroup" -y "${expected_symlinks[@]}" - lvm vgs - lvm vgchange -ay "$vgroup" - lvm lvcreate -y -L 4M "$vgroup" -n mypart1 - lvm lvcreate -y -L 8M "$vgroup" -n mypart2 - lvm lvs - udevadm wait --settle --timeout=30 "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" - mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1" - udevadm wait --settle --timeout=30 "/dev/disk/by-label/mylvpart1" - helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" - helper_check_device_units - # Disconnect the iSCSI devices and check all the symlinks - iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout - # "Reset" the DM state, since we yanked the backing storage from under the LVM, - # so the currently active VGs/LVs are invalid - dmsetup remove_all --deferred - # The LVM and iSCSI related symlinks should be gone - udevadm wait --settle --timeout=30 --removed "/dev/$vgroup" "/dev/disk/by-label/mylvpart1" "${expected_symlinks[@]}" - helper_check_device_symlinks "/dev/disk" - helper_check_device_units - # Reconnect the iSCSI devices and check if everything get detected correctly - iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover - iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login - udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" "/dev/$vgroup/mypart1" "/dev/$vgroup/mypart2" "/dev/disk/by-label/mylvpart1" - helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" - helper_check_device_units - # Cleanup - iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout - tgtadm --lld iscsi --op delete --mode target --tid=2 - umount "$mpoint" - rm -rf "$mpoint" -} - -testcase_long_sysfs_path() { - local cursor link logfile mpoint - local expected_symlinks=( - "/dev/disk/by-label/data_vol" - "/dev/disk/by-label/swap_vol" - "/dev/disk/by-partlabel/test_swap" - "/dev/disk/by-partlabel/test_part" - "/dev/disk/by-partuuid/deadbeef-dead-dead-beef-000000000000" - "/dev/disk/by-uuid/deadbeef-dead-dead-beef-111111111111" - "/dev/disk/by-uuid/deadbeef-dead-dead-beef-222222222222" - ) - - # Create a cursor file to skip messages generated by udevd in initrd, as it - # might not be the same up-to-date version as we currently run (hence generating - # messages we check for later and making the test fail) - cursor="$(mktemp)" - journalctl --cursor-file="${cursor:?}" -n0 -q - - # Make sure the test device is connected and show its "wonderful" path - stat /sys/block/vda - readlink -f /sys/block/vda/dev - - udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" - - # Try to mount the data partition manually (using its label) - mpoint="$(mktemp -d /logsysfsXXX)" - mount LABEL=data_vol "$mpoint" - touch "$mpoint/test" - umount "$mpoint" - # Do the same, but with UUID and using fstab - echo "UUID=deadbeef-dead-dead-beef-222222222222 $mpoint ext4 defaults 0 0" >>/etc/fstab - systemctl daemon-reload - mount "$mpoint" - timeout 30 bash -c "until systemctl -q is-active '$mpoint'; do sleep .2; done" - test -e "$mpoint/test" - umount "$mpoint" - - # Test out the swap partition - swapon -v -L swap_vol - swapoff -v -L swap_vol - - udevadm settle - - logfile="$(mktemp)" - # Check state of affairs after https://github.com/systemd/systemd/pull/22759 - # Note: can't use `--cursor-file` here, since we don't want to update the cursor - # after using it - [[ "$(journalctl --after-cursor="$(<"$cursor")" -q --no-pager -o short-monotonic -p info --grep "Device path.*vda.?' too long to fit into unit name" | wc -l)" -eq 0 ]] - [[ "$(journalctl --after-cursor="$(<"$cursor")" -q --no-pager -o short-monotonic --grep "Unit name .*vda.?\.device\" too long, falling back to hashed unit name" | wc -l)" -gt 0 ]] - # Check if the respective "hashed" units exist and are active (plugged) - systemctl status --no-pager "$(readlink -f /sys/block/vda/vda1)" - systemctl status --no-pager "$(readlink -f /sys/block/vda/vda2)" - # Make sure we don't unnecessarily spam the log - { journalctl -b -q --no-pager -o short-monotonic -p info --grep "/sys/devices/.+/vda[0-9]?" _PID=1 + UNIT=systemd-udevd.service || :;} | tee "$logfile" - [[ "$(wc -l <"$logfile")" -lt 10 ]] - - : >/etc/fstab - rm -fr "${cursor:?}" "${logfile:?}" "${mpoint:?}" -} - -testcase_mdadm_basic() { - local i part_name raid_name raid_dev uuid - local expected_symlinks=() - local devices=( - /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..4} - ) - - ls -l "${devices[@]}" - - echo "Mirror raid (RAID 1)" - raid_name="mdmirror" - raid_dev="/dev/md/$raid_name" - part_name="${raid_name}_part" - uuid="aaaaaaaa:bbbbbbbb:cccccccc:00000001" - expected_symlinks=( - "$raid_dev" - "/dev/disk/by-id/md-name-H:$raid_name" - "/dev/disk/by-id/md-uuid-$uuid" - "/dev/disk/by-label/$part_name" # ext4 partition - ) - # Create a simple RAID 1 with an ext4 filesystem - echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..1} -v -f --level=1 --raid-devices=2 - udevadm wait --settle --timeout=30 "$raid_dev" - mkfs.ext4 -L "$part_name" "$raid_dev" - udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" - for i in {0..9}; do - echo "Disassemble - reassemble loop, iteration #$i" - mdadm -v --stop "$raid_dev" - udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}" - mdadm --assemble "$raid_dev" --name "$raid_name" -v - udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" - done - helper_check_device_symlinks - helper_check_device_units - # Cleanup - mdadm -v --stop "$raid_dev" - udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}" - - echo "Parity raid (RAID 5)" - raid_name="mdparity" - raid_dev="/dev/md/$raid_name" - part_name="${raid_name}_part" - uuid="aaaaaaaa:bbbbbbbb:cccccccc:00000101" - expected_symlinks=( - "$raid_dev" - "/dev/disk/by-id/md-name-H:$raid_name" - "/dev/disk/by-id/md-uuid-$uuid" - "/dev/disk/by-label/$part_name" # ext4 partition - ) - # Create a simple RAID 5 with an ext4 filesystem - echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..2} -v -f --level=5 --raid-devices=3 - udevadm wait --settle --timeout=30 "$raid_dev" - mkfs.ext4 -L "$part_name" "$raid_dev" - udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" - for i in {0..9}; do - echo "Disassemble - reassemble loop, iteration #$i" - mdadm -v --stop "$raid_dev" - udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}" - mdadm --assemble "$raid_dev" --name "$raid_name" -v - udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" - done - helper_check_device_symlinks - helper_check_device_units - # Cleanup - mdadm -v --stop "$raid_dev" - udevadm wait --settle --timeout=30 --removed "${expected_symlinks[@]}" - helper_check_device_units - - echo "Mirror + parity raid (RAID 10) + multiple partitions" - raid_name="mdmirpar" - raid_dev="/dev/md/$raid_name" - part_name="${raid_name}_part" - uuid="aaaaaaaa:bbbbbbbb:cccccccc:00001010" - expected_symlinks=( - "$raid_dev" - "/dev/disk/by-id/md-name-H:$raid_name" - "/dev/disk/by-id/md-uuid-$uuid" - "/dev/disk/by-label/$part_name" # ext4 partition - # Partitions - "${raid_dev}1" - "${raid_dev}2" - "${raid_dev}3" - "/dev/disk/by-id/md-name-H:$raid_name-part1" - "/dev/disk/by-id/md-name-H:$raid_name-part2" - "/dev/disk/by-id/md-name-H:$raid_name-part3" - "/dev/disk/by-id/md-uuid-$uuid-part1" - "/dev/disk/by-id/md-uuid-$uuid-part2" - "/dev/disk/by-id/md-uuid-$uuid-part3" - ) - # Create a simple RAID 10 with an ext4 filesystem - echo y | mdadm --create "$raid_dev" --name "$raid_name" --uuid "$uuid" /dev/disk/by-id/ata-foobar_deadbeefmdadm{0..3} -v -f --level=10 --raid-devices=4 - udevadm wait --settle --timeout=30 "$raid_dev" - # Partition the raid device - # Here, 'udevadm lock' is meaningless, as udevd does not lock MD devices. - sfdisk --wipe=always "$raid_dev" <&2 "Missing verification handler for test case '$TEST_FUNCTION_NAME'" - exit 1 -fi - -echo "TEST_FUNCTION_NAME=$TEST_FUNCTION_NAME" -"$TEST_FUNCTION_NAME" -udevadm settle - -echo "Check if all symlinks under /dev/disk/ are valid (post-test)" -helper_check_device_symlinks - -udevadm control --log-level info - -systemctl status systemd-udevd - -touch /testok diff --git a/test/units/testsuite-65.service b/test/units/testsuite-65.service deleted file mode 100644 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-65.sh b/test/units/testsuite-65.sh deleted file mode 100755 index a6bb38d..0000000 --- a/test/units/testsuite-65.sh +++ /dev/null @@ -1,909 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2016 -set -eux - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -systemctl log-level debug -export SYSTEMD_LOG_LEVEL=debug - -# Sanity checks -# -# We can't really test time, critical-chain and plot verbs here, as -# the testsuite service is a part of the boot transaction, so let's assume -# they fail -systemd-analyze || : -systemd-analyze time || : -systemd-analyze critical-chain || : -# blame -systemd-analyze blame -systemd-run --wait --user --pipe -M testuser@.host systemd-analyze blame -# plot -systemd-analyze plot >/dev/null || : -systemd-analyze plot --json=pretty >/dev/null || : -systemd-analyze plot --json=short >/dev/null || : -systemd-analyze plot --json=off >/dev/null || : -systemd-analyze plot --json=pretty --no-legend >/dev/null || : -systemd-analyze plot --json=short --no-legend >/dev/null || : -systemd-analyze plot --json=off --no-legend >/dev/null || : -systemd-analyze plot --table >/dev/null || : -systemd-analyze plot --table --no-legend >/dev/null || : -# legacy/deprecated options (moved to systemctl, but still usable from analyze) -systemd-analyze log-level -systemd-analyze log-level "$(systemctl log-level)" -systemd-analyze get-log-level -systemd-analyze set-log-level "$(systemctl log-level)" -systemd-analyze log-target -systemd-analyze log-target "$(systemctl log-target)" -systemd-analyze get-log-target -systemd-analyze set-log-target "$(systemctl log-target)" -systemd-analyze service-watchdogs -systemd-analyze service-watchdogs "$(systemctl service-watchdogs)" -# dot -systemd-analyze dot >/dev/null -systemd-analyze dot systemd-journald.service >/dev/null -systemd-analyze dot systemd-journald.service systemd-logind.service >/dev/null -systemd-analyze dot --from-pattern="*" --from-pattern="*.service" systemd-journald.service >/dev/null -systemd-analyze dot --to-pattern="*" --to-pattern="*.service" systemd-journald.service >/dev/null -systemd-analyze dot --from-pattern="*.service" --to-pattern="*.service" systemd-journald.service >/dev/null -systemd-analyze dot --order systemd-journald.service systemd-logind.service >/dev/null -systemd-analyze dot --require systemd-journald.service systemd-logind.service >/dev/null -systemd-analyze dot "systemd-*.service" >/dev/null -(! systemd-analyze dot systemd-journald.service systemd-logind.service "*" bbb ccc) -# dump -# this should be rate limited to 10 calls in 10 minutes for unprivileged callers -for _ in {1..10}; do - runas testuser systemd-analyze dump systemd-journald.service >/dev/null -done -(! runas testuser systemd-analyze dump >/dev/null) -# still limited after a reload -systemctl daemon-reload -(! runas testuser systemd-analyze dump >/dev/null) -# and a re-exec -systemctl daemon-reexec -(! runas testuser systemd-analyze dump >/dev/null) -# privileged call, so should not be rate limited -for _ in {1..10}; do - systemd-analyze dump systemd-journald.service >/dev/null -done -systemd-analyze dump >/dev/null -systemd-analyze dump "*" >/dev/null -systemd-analyze dump "*.socket" >/dev/null -systemd-analyze dump "*.socket" "*.service" aaaaaaa ... >/dev/null -systemd-analyze dump systemd-journald.service >/dev/null -systemd-analyze malloc >/dev/null -(! systemd-analyze dump "") -# unit-files -systemd-analyze unit-files >/dev/null -systemd-analyze unit-files systemd-journald.service >/dev/null -systemd-analyze unit-files "*" >/dev/null -systemd-analyze unit-files "*" aaaaaa "*.service" "*.target" >/dev/null -systemd-analyze unit-files --user >/dev/null -systemd-analyze unit-files --user "*" aaaaaa "*.service" "*.target" >/dev/null -# unit-paths -systemd-analyze unit-paths -systemd-analyze unit-paths --user -systemd-analyze unit-paths --global -# exist-status -systemd-analyze exit-status -systemd-analyze exit-status STDOUT BPF -systemd-analyze exit-status 0 1 {63..65} -(! systemd-analyze exit-status STDOUT BPF "hello*") -# 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*") -# condition -mkdir -p /run/systemd/system -UNIT_NAME="analyze-condition-$RANDOM.service" -cat >"/run/systemd/system/$UNIT_NAME" <1.0 -ConditionPathExists=/etc/os-release - -[Service] -ExecStart=/bin/true -EOF -systemctl daemon-reload -systemd-analyze condition --unit="$UNIT_NAME" -systemd-analyze condition 'ConditionKernelVersion = ! <4.0' \ - 'ConditionKernelVersion = >=3.1' \ - 'ConditionACPower=|false' \ - 'ConditionArchitecture=|!arm' \ - 'AssertPathExists=/etc/os-release' -(! systemd-analyze condition 'ConditionArchitecture=|!arm' 'AssertXYZ=foo') -(! systemd-analyze condition 'ConditionKernelVersion=<1.0') -(! systemd-analyze condition 'AssertKernelVersion=<1.0') -# 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) -# 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) -fi -# calendar -systemd-analyze calendar '*-2-29 0:0:0' -systemd-analyze calendar --iterations=5 '*-2-29 0:0:0' -systemd-analyze calendar '*-* *:*:*' -systemd-analyze calendar --iterations=5 '*-* *:*:*' -systemd-analyze calendar --iterations=50 '*-* *:*:*' -systemd-analyze calendar --iterations=0 '*-* *:*:*' -systemd-analyze calendar --iterations=5 '01-01-22 01:00:00' -systemd-analyze calendar --base-time=yesterday --iterations=5 '*-* *:*:*' -(! systemd-analyze calendar --iterations=0 '*-* 99:*:*') -(! systemd-analyze calendar --base-time=never '*-* *:*:*') -(! systemd-analyze calendar 1) -(! systemd-analyze calendar "") -# timestamp -systemd-analyze timestamp now -systemd-analyze timestamp -- -1 -systemd-analyze timestamp yesterday now tomorrow -(! systemd-analyze timestamp yesterday never tomorrow) -(! systemd-analyze timestamp 1) -(! systemd-analyze timestamp '*-2-29 0:0:0') -(! systemd-analyze timestamp "") -# timespan -systemd-analyze timespan 1 -systemd-analyze timespan 1s 300s '1year 0.000001s' -(! systemd-analyze timespan 1s 300s aaaaaa '1year 0.000001s') -(! systemd-analyze timespan -- -1) -(! systemd-analyze timespan '*-2-29 0:0:0') -(! systemd-analyze timespan "") -# cat-config -systemd-analyze cat-config systemd/system.conf >/dev/null -systemd-analyze cat-config /etc/systemd/system.conf >/dev/null -systemd-analyze cat-config systemd/system.conf systemd/journald.conf >/dev/null -systemd-analyze cat-config systemd/system.conf foo/bar systemd/journald.conf >/dev/null -systemd-analyze cat-config foo/bar -systemd-analyze cat-config --tldr systemd/system.conf >/dev/null -systemd-analyze cat-config --tldr /etc/systemd/system.conf >/dev/null -systemd-analyze cat-config --tldr systemd/system.conf systemd/journald.conf >/dev/null -systemd-analyze cat-config --tldr systemd/system.conf foo/bar systemd/journald.conf >/dev/null -systemd-analyze cat-config --tldr foo/bar -# security -systemd-analyze security -systemd-analyze security --json=off -systemd-analyze security --json=pretty | jq -systemd-analyze security --json=short | jq - -if [[ ! -v ASAN_OPTIONS ]]; then - # check that systemd-analyze cat-config paths work in a chroot - mkdir -p /tmp/root - mount --bind / /tmp/root - systemd-analyze cat-config systemd/system-preset >/tmp/out1 - chroot /tmp/root systemd-analyze cat-config systemd/system-preset >/tmp/out2 - diff /tmp/out{1,2} -fi - -# verify -mkdir -p /tmp/img/usr/lib/systemd/system/ -mkdir -p /tmp/img/opt/ - -touch /tmp/img/opt/script0.sh -chmod +x /tmp/img/opt/script0.sh - -cat </tmp/img/usr/lib/systemd/system/testfile.service -[Service] -ExecStart = /opt/script0.sh -EOF - -set +e -# Default behaviour is to recurse through all dependencies when unit is loaded -(! systemd-analyze verify --root=/tmp/img/ testfile.service) - -# As above, recurses through all dependencies when unit is loaded -(! systemd-analyze verify --recursive-errors=yes --root=/tmp/img/ testfile.service) - -# Recurses through unit file and its direct dependencies when unit is loaded -(! systemd-analyze verify --recursive-errors=one --root=/tmp/img/ testfile.service) - -set -e - -# zero exit status since dependencies are ignored when unit is loaded -systemd-analyze verify --recursive-errors=no --root=/tmp/img/ testfile.service - -rm /tmp/img/usr/lib/systemd/system/testfile.service - -cat </tmp/testfile.service -[Unit] -foo = bar - -[Service] -ExecStart = echo hello -EOF - -cat </tmp/testfile2.service -[Unit] -Requires = testfile.service - -[Service] -ExecStart = echo hello -EOF - -# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded -systemd-analyze verify --recursive-errors=no /tmp/testfile2.service - -set +e -# Non-zero exit status since all associated dependencies are recursively loaded when the unit file is loaded -(! systemd-analyze verify --recursive-errors=yes /tmp/testfile2.service) -set -e - -rm /tmp/testfile.service -rm /tmp/testfile2.service - -cat </tmp/sample.service -[Unit] -Description = A Sample Service - -[Service] -ExecStart = echo hello -Slice=support.slice -EOF - -# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded -systemd-analyze verify --recursive-errors=no /tmp/sample.service - -cat </tmp/testfile.service -[Service] -ExecStart = echo hello -DeviceAllow=/dev/sda -EOF - -# Prevent regression from #13380 and #20859 where we can't verify hidden files -cp /tmp/testfile.service /tmp/.testfile.service - -systemd-analyze verify /tmp/.testfile.service - -rm /tmp/.testfile.service - -# Alias a unit file's name on disk (see #20061) -cp /tmp/testfile.service /tmp/testsrvc - -(! systemd-analyze verify /tmp/testsrvc) - -systemd-analyze verify /tmp/testsrvc:alias.service - -# Zero exit status since the value used for comparison determine exposure to security threats is by default 100 -systemd-analyze security --offline=true /tmp/testfile.service - -#The overall exposure level assigned to the unit is greater than the set threshold -(! systemd-analyze security --threshold=90 --offline=true /tmp/testfile.service) - -# Ensure we print the list of ACLs, see https://github.com/systemd/systemd/issues/23185 -systemd-analyze security --offline=true /tmp/testfile.service | grep -q -F "/dev/sda" - -rm /tmp/testfile.service - -cat </tmp/img/usr/lib/systemd/system/testfile.service -[Service] -ExecStart = echo hello -PrivateNetwork = yes -PrivateDevices = yes -PrivateUsers = yes -EOF - -# The new overall exposure level assigned to the unit is less than the set thresholds -# Verifies that the --offline= option works with --root= -systemd-analyze security --threshold=90 --offline=true --root=/tmp/img/ testfile.service - -cat </tmp/foo@.service -[Service] -ExecStart=ls -EOF - -cat </tmp/hoge@test.service -[Service] -ExecStart=ls -EOF - -# issue #30357 -pushd /tmp -systemd-analyze verify foo@bar.service -systemd-analyze verify foo@.service -systemd-analyze verify hoge@test.service -(! systemd-analyze verify hoge@nonexist.service) -(! systemd-analyze verify hoge@.service) -popd -pushd / -systemd-analyze verify tmp/foo@bar.service -systemd-analyze verify tmp/foo@.service -systemd-analyze verify tmp/hoge@test.service -(! systemd-analyze verify tmp/hoge@nonexist.service) -(! systemd-analyze verify tmp/hoge@.service) -popd -pushd /usr -systemd-analyze verify ../tmp/foo@bar.service -systemd-analyze verify ../tmp/foo@.service -systemd-analyze verify ../tmp/hoge@test.service -(! systemd-analyze verify ../tmp/hoge@nonexist.service) -(! systemd-analyze verify ../tmp/hoge@.service) -popd -systemd-analyze verify /tmp/foo@bar.service -systemd-analyze verify /tmp/foo@.service -systemd-analyze verify /tmp/hoge@test.service -(! systemd-analyze verify /tmp/hoge@nonexist.service) -(! systemd-analyze verify /tmp/hoge@.service) - -# Added an additional "INVALID_ID" id to the .json to verify that nothing breaks when input is malformed -# The PrivateNetwork id description and weight was changed to verify that 'security' is actually reading in -# values from the .json file when required. The default weight for "PrivateNetwork" is 2500, and the new weight -# assigned to that id in the .json file is 6000. This increased weight means that when the "PrivateNetwork" key is -# set to 'yes' (as above in the case of testfile.service) in the content of the unit file, the overall exposure -# level for the unit file should decrease to account for that increased weight. -cat </tmp/testfile.json -{"UserOrDynamicUser": - {"description_bad": "Service runs as root user", - "weight": 0, - "range": 10 - }, -"SupplementaryGroups": - {"description_good": "Service has no supplementary groups", - "description_bad": "Service runs with supplementary groups", - "description_na": "Service runs as root, option does not matter", - "weight": 200, - "range": 1 - }, -"PrivateDevices": - {"description_good": "Service has no access to hardware devices", - "description_bad": "Service potentially has access to hardware devices", - "weight": 1000, - "range": 1 - }, -"PrivateMounts": - {"description_good": "Service cannot install system mounts", - "description_bad": "Service may install system mounts", - "weight": 1000, - "range": 1 - }, -"PrivateNetwork": - {"description_good": "Service doesn't have access to the host's network", - "description_bad": "Service has access to the host's network", - "weight": 6000, - "range": 1 - }, -"PrivateTmp": - {"description_good": "Service has no access to other software's temporary files", - "description_bad": "Service has access to other software's temporary files", - "weight": 1000, - "range": 1 - }, -"PrivateUsers": - {"description_good": "Service does not have access to other users", - "description_bad": "Service has access to other users", - "weight": 1000, - "range": 1 - }, -"ProtectControlGroups": - {"description_good": "Service cannot modify the control group file system", - "description_bad": "Service may modify the control group file system", - "weight": 1000, - "range": 1 - }, -"ProtectKernelModules": - {"description_good": "Service cannot load or read kernel modules", - "description_bad": "Service may load or read kernel modules", - "weight": 1000, - "range": 1 - }, -"ProtectKernelTunables": - {"description_good": "Service cannot alter kernel tunables (/proc/sys, …)", - "description_bad": "Service may alter kernel tunables", - "weight": 1000, - "range": 1 - }, -"ProtectKernelLogs": - {"description_good": "Service cannot read from or write to the kernel log ring buffer", - "description_bad": "Service may read from or write to the kernel log ring buffer", - "weight": 1000, - "range": 1 - }, -"ProtectClock": - {"description_good": "Service cannot write to the hardware clock or system clock", - "description_bad": "Service may write to the hardware clock or system clock", - "weight": 1000, - "range": 1 - }, -"ProtectHome": - {"weight": 1000, - "range": 10 - }, -"ProtectHostname": - {"description_good": "Service cannot change system host/domainname", - "description_bad": "Service may change system host/domainname", - "weight": 50, - "range": 1 - }, -"ProtectSystem": - {"weight": 1000, - "range": 10 - }, -"RootDirectoryOrRootImage": - {"description_good": "Service has its own root directory/image", - "description_bad": "Service runs within the host's root directory", - "weight": 200, - "range": 1 - }, -"LockPersonality": - {"description_good": "Service cannot change ABI personality", - "description_bad": "Service may change ABI personality", - "weight": 100, - "range": 1 - }, -"MemoryDenyWriteExecute": - {"description_good": "Service cannot create writable executable memory mappings", - "description_bad": "Service may create writable executable memory mappings", - "weight": 100, - "range": 1 - }, -"NoNewPrivileges": - {"description_good": "Service processes cannot acquire new privileges", - "description_bad": "Service processes may acquire new privileges", - "weight": 1000, - "range": 1 - }, -"CapabilityBoundingSet_CAP_SYS_ADMIN": - {"description_good": "Service has no administrator privileges", - "description_bad": "Service has administrator privileges", - "weight": 1500, - "range": 1 - }, -"CapabilityBoundingSet_CAP_SET_UID_GID_PCAP": - {"description_good": "Service cannot change UID/GID identities/capabilities", - "description_bad": "Service may change UID/GID identities/capabilities", - "weight": 1500, - "range": 1 - }, -"CapabilityBoundingSet_CAP_SYS_PTRACE": - {"description_good": "Service has no ptrace() debugging abilities", - "description_bad": "Service has ptrace() debugging abilities", - "weight": 1500, - "range": 1 - }, -"CapabilityBoundingSet_CAP_SYS_TIME": - {"description_good": "Service processes cannot change the system clock", - "description_bad": "Service processes may change the system clock", - "weight": 1000, - "range": 1 - }, -"CapabilityBoundingSet_CAP_NET_ADMIN": - {"description_good": "Service has no network configuration privileges", - "description_bad": "Service has network configuration privileges", - "weight": 1000, - "range": 1 - }, -"CapabilityBoundingSet_CAP_SYS_RAWIO": - {"description_good": "Service has no raw I/O access", - "description_bad": "Service has raw I/O access", - "weight": 1000, - "range": 1 - }, -"CapabilityBoundingSet_CAP_SYS_MODULE": - {"description_good": "Service cannot load kernel modules", - "description_bad": "Service may load kernel modules", - "weight": 1000, - "range": 1 - }, -"CapabilityBoundingSet_CAP_AUDIT": - {"description_good": "Service has no audit subsystem access", - "description_bad": "Service has audit subsystem access", - "weight": 500, - "range": 1 - }, -"CapabilityBoundingSet_CAP_SYSLOG": - {"description_good": "Service has no access to kernel logging", - "description_bad": "Service has access to kernel logging", - "weight": 500, - "range": 1 - }, -"CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE": - {"description_good": "Service has no privileges to change resource use parameters", - "description_bad": "Service has privileges to change resource use parameters", - "weight": 500, - "range": 1 - }, -"CapabilityBoundingSet_CAP_MKNOD": - {"description_good": "Service cannot create device nodes", - "description_bad": "Service may create device nodes", - "weight": 500, - "range": 1 - }, -"CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP": - {"description_good": "Service cannot change file ownership/access mode/capabilities", - "description_bad": "Service may change file ownership/access mode/capabilities unrestricted", - "weight": 1000, - "range": 1 - }, -"CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER": - {"description_good": "Service cannot override UNIX file/IPC permission checks", - "description_bad": "Service may override UNIX file/IPC permission checks", - "weight": 1000, - "range": 1 - }, -"CapabilityBoundingSet_CAP_KILL": - {"description_good": "Service cannot send UNIX signals to arbitrary processes", - "description_bad": "Service may send UNIX signals to arbitrary processes", - "weight": 500, - "range": 1 - }, -"CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW": - {"description_good": "Service has no elevated networking privileges", - "description_bad": "Service has elevated networking privileges", - "weight": 500, - "range": 1 - }, -"CapabilityBoundingSet_CAP_SYS_BOOT": - {"description_good": "Service cannot issue reboot()", - "description_bad": "Service may issue reboot()", - "weight": 100, - "range": 1 - }, -"CapabilityBoundingSet_CAP_MAC": - {"description_good": "Service cannot adjust SMACK MAC", - "description_bad": "Service may adjust SMACK MAC", - "weight": 100, - "range": 1 - }, -"CapabilityBoundingSet_CAP_LINUX_IMMUTABLE": - {"description_good": "Service cannot mark files immutable", - "description_bad": "Service may mark files immutable", - "weight": 75, - "range": 1 - }, -"CapabilityBoundingSet_CAP_IPC_LOCK": - {"description_good": "Service cannot lock memory into RAM", - "description_bad": "Service may lock memory into RAM", - "weight": 50, - "range": 1 - }, -"CapabilityBoundingSet_CAP_SYS_CHROOT": - {"description_good": "Service cannot issue chroot()", - "description_bad": "Service may issue chroot()", - "weight": 50, - "range": 1 - }, -"CapabilityBoundingSet_CAP_BLOCK_SUSPEND": - {"description_good": "Service cannot establish wake locks", - "description_bad": "Service may establish wake locks", - "weight": 25, - "range": 1 - }, -"CapabilityBoundingSet_CAP_WAKE_ALARM": - {"description_good": "Service cannot program timers that wake up the system", - "description_bad": "Service may program timers that wake up the system", - "weight": 25, - "range": 1 - }, -"CapabilityBoundingSet_CAP_LEASE": - {"description_good": "Service cannot create file leases", - "description_bad": "Service may create file leases", - "weight": 25, - "range": 1 - }, -"CapabilityBoundingSet_CAP_SYS_TTY_CONFIG": - {"description_good": "Service cannot issue vhangup()", - "description_bad": "Service may issue vhangup()", - "weight": 25, - "range": 1 - }, -"CapabilityBoundingSet_CAP_SYS_PACCT": - {"description_good": "Service cannot use acct()", - "description_bad": "Service may use acct()", - "weight": 25, - "range": 1 - }, -"CapabilityBoundingSet_CAP_BPF": - {"description_good": "Service may load BPF programs", - "description_bad": "Service may not load BPF programs", - "weight": 25, - "range": 1 - }, -"UMask": - {"weight": 100, - "range": 10 - }, -"KeyringMode": - {"description_good": "Service doesn't share key material with other services", - "description_bad": "Service shares key material with other service", - "weight": 1000, - "range": 1 - }, -"ProtectProc": - {"description_good": "Service has restricted access to process tree(/proc hidepid=)", - "description_bad": "Service has full access to process tree(/proc hidepid=)", - "weight": 1000, - "range": 3 - }, -"ProcSubset": - {"description_good": "Service has no access to non-process/proc files(/proc subset=)", - "description_bad": "Service has full access to non-process/proc files(/proc subset=)", - "weight": 10, - "range": 1 - }, -"NotifyAccess": - {"description_good": "Service child processes cannot alter service state", - "description_bad": "Service child processes may alter service state", - "weight": 1000, - "range": 1 - }, -"RemoveIPC": - {"description_good": "Service user cannot leave SysV IPC objects around", - "description_bad": "Service user may leave SysV IPC objects around", - "description_na": "Service runs as root, option does not apply", - "weight": 100, - "range": 1 - }, -"Delegate": - {"description_good": "Service does not maintain its own delegated control group subtree", - "description_bad": "Service maintains its own delegated control group subtree", - "weight": 100, - "range": 1 - }, -"RestrictRealtime": - {"description_good": "Service realtime scheduling access is restricted", - "description_bad": "Service may acquire realtime scheduling", - "weight": 500, - "range": 1 - }, -"RestrictSUIDSGID": - {"description_good": "SUID/SGIDfilecreationbyserviceisrestricted", - "description_bad": "ServicemaycreateSUID/SGIDfiles", - "weight": 1000, - "range": 1 - }, -"RestrictNamespaces_user": - {"description_good": "Servicecannotcreateusernamespaces", - "description_bad": "Servicemaycreateusernamespaces", - "weight": 1500, - "range": 1 - }, -"RestrictNamespaces_mnt": - {"description_good": "Service cannot create file system namespaces", - "description_bad": "Service may create file system namespaces", - "weight": 500, - "range": 1 - }, -"RestrictNamespaces_ipc": - {"description_good": "Service cannot create IPC namespaces", - "description_bad": "Service may create IPC namespaces", - "weight": 500, - "range": 1 - }, -"RestrictNamespaces_pid": - {"description_good": "Service cannot create process namespaces", - "description_bad": "Service may create process namespaces", - "weight": 500, - "range": 1 - }, -"RestrictNamespaces_cgroup": - {"description_good": "Service cannot create cgroup namespaces", - "description_bad": "Service may create cgroup namespaces", - "weight": 500, - "range": 1 - }, -"RestrictNamespaces_net": - {"description_good": "Service cannot create network namespaces", - "description_bad": "Service may create network namespaces", - "weight": 500, - "range": 1 - }, -"RestrictNamespaces_uts": - {"description_good": "Service cannot create hostname namespaces", - "description_bad": "Service may create hostname namespaces", - "weight": 100, - "range": 1 - }, -"RestrictAddressFamilies_AF_INET_INET6": - {"description_good": "Service cannot allocate Internet sockets", - "description_bad": "Service may allocate Internet sockets", - "weight": 1500, - "range": 1 - }, -"RestrictAddressFamilies_AF_UNIX": - {"description_good": "Service cannot allocate local sockets", - "description_bad": "Service may allocate local sockets", - "weight": 25, - "range": 1 - }, -"RestrictAddressFamilies_AF_NETLINK": - {"description_good": "Service cannot allocate netlink sockets", - "description_bad": "Service may allocate netlink sockets", - "weight": 200, - "range": 1 - }, -"RestrictAddressFamilies_AF_PACKET": - {"description_good": "Service cannot allocate packet sockets", - "description_bad": "Service may allocate packet sockets", - "weight": 1000, - "range": 1 - }, -"RestrictAddressFamilies_OTHER": - {"description_good": "Service cannot allocate exotic sockets", - "description_bad": "Service may allocate exotic sockets", - "weight": 1250, - "range": 1 - }, -"SystemCallArchitectures": - {"weight": 1000, - "range": 10 - }, -"SystemCallFilter_swap": - {"weight": 1000, - "range": 10 - }, -"SystemCallFilter_obsolete": - {"weight": 250, - "range": 10 - }, -"SystemCallFilter_clock": - {"weight": 1000, - "range": 10 - }, -"SystemCallFilter_cpu_emulation": - {"weight": 250, - "range": 10 - }, -"SystemCallFilter_debug": - {"weight": 1000, - "range": 10 - }, -"SystemCallFilter_mount": - {"weight": 1000, - "range": 10 - }, -"SystemCallFilter_module": - {"weight": 1000, - "range": 10 - }, -"SystemCallFilter_raw_io": - {"weight": 1000, - "range": 10 - }, -"SystemCallFilter_reboot": - {"weight": 1000, - "range": 10 - }, -"SystemCallFilter_privileged": - {"weight": 700, - "range": 10 - }, -"SystemCallFilter_resources": - {"weight": 700, - "range": 10 - }, -"IPAddressDeny": - {"weight": 1000, - "range": 10 - }, -"DeviceAllow": - {"weight": 1000, - "range": 10 - }, -"AmbientCapabilities": - {"description_good": "Service process does not receive ambient capabilities", - "description_bad": "Service process receives ambient capabilities", - "weight": 500, - "range": 1 - }, -"INVALID_ID": - {"weight": 1000, - "range": 10 - } -} -EOF - -# Reads in custom security requirements from the parsed .json file and uses these for comparison -systemd-analyze security --threshold=90 --offline=true \ - --security-policy=/tmp/testfile.json \ - --root=/tmp/img/ testfile.service - -# The strict profile adds a lot of sanboxing options -systemd-analyze security --threshold=25 --offline=true \ - --security-policy=/tmp/testfile.json \ - --profile=strict \ - --root=/tmp/img/ testfile.service - -# The trusted profile doesn't add any sanboxing options -(! systemd-analyze security --threshold=25 --offline=true \ - --security-policy=/tmp/testfile.json \ - --profile=/usr/lib/systemd/portable/profile/trusted/service.conf \ - --root=/tmp/img/ testfile.service) - -(! systemd-analyze security --threshold=50 --offline=true \ - --security-policy=/tmp/testfile.json \ - --root=/tmp/img/ testfile.service) - -rm /tmp/img/usr/lib/systemd/system/testfile.service - -if systemd-analyze --version | grep -q -F "+ELFUTILS"; then - systemd-analyze inspect-elf --json=short /lib/systemd/systemd | grep -q -F '"elfType":"executable"' -fi - -systemd-analyze --threshold=90 security systemd-journald.service - -# issue 23663 -check() {( - set +x - output=$(systemd-analyze security --offline="${2?}" "${3?}" | grep -F 'SystemCallFilter=') - assert_in "System call ${1?} list" "$output" - assert_in "[+✓] SystemCallFilter=~@swap" "$output" - assert_in "[+✓] SystemCallFilter=~@resources" "$output" - assert_in "[+✓] SystemCallFilter=~@reboot" "$output" - assert_in "[+✓] SystemCallFilter=~@raw-io" "$output" - assert_in "[-✗] SystemCallFilter=~@privileged" "$output" - assert_in "[+✓] SystemCallFilter=~@obsolete" "$output" - assert_in "[+✓] SystemCallFilter=~@mount" "$output" - assert_in "[+✓] SystemCallFilter=~@module" "$output" - assert_in "[+✓] SystemCallFilter=~@debug" "$output" - assert_in "[+✓] SystemCallFilter=~@cpu-emulation" "$output" - assert_in "[-✗] SystemCallFilter=~@clock" "$output" -)} - -export -n SYSTEMD_LOG_LEVEL - -mkdir -p /run/systemd/system -cat >/run/systemd/system/allow-list.service </run/systemd/system/deny-list.service <&1) -name=$(echo "$output" | awk '{ print $4 }' | cut -d';' -f1) - -check allow yes /run/systemd/transient/"$name" -check allow no "$name" - -output=$(systemd-run -p "SystemCallFilter=~@known" -p "SystemCallFilter=@system-service" -p "SystemCallFilter=~@resources:ENOANO @privileged" -p "SystemCallFilter=@clock" sleep 60 2>&1) -name=$(echo "$output" | awk '{ print $4 }' | cut -d';' -f1) - -check deny yes /run/systemd/transient/"$name" -check deny no "$name" - -# Let's also test the "image-policy" verb - -systemd-analyze image-policy '*' 2>&1 | grep -q -F "Long form: =verity+signed+encrypted+unprotected+unused+absent" -systemd-analyze image-policy '-' 2>&1 | grep -q -F "Long form: =unused+absent" -systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -F "Long form: usr=verity:home=encrypted:=unused+absent" -systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^home \+encrypted \+' -systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^usr \+verity \+' -systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^root \+ignore \+' -systemd-analyze image-policy 'home=encrypted:usr=verity' 2>&1 | grep -q -e '^usr-verity \+unprotected \+' - -(! systemd-analyze image-policy 'doedel') - -# Output is very hard to predict, but let's run it for coverage anyway -systemd-analyze pcrs -systemd-analyze pcrs --json=pretty -systemd-analyze pcrs 14 7 0 ima - -systemd-analyze log-level info - -touch /testok diff --git a/test/units/testsuite-66-deviceisolation.service b/test/units/testsuite-66-deviceisolation.service deleted file mode 100644 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-66.sh b/test/units/testsuite-66.sh deleted file mode 100755 index 147335a..0000000 --- a/test/units/testsuite-66.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -RESULTS_FILE=/tmp/testsuite66serviceresults - -systemd-analyze log-level debug - -systemctl start testsuite-66-deviceisolation.service - -sleep 5 -grep -q "Operation not permitted" "$RESULTS_FILE" - -systemctl daemon-reload -systemctl daemon-reexec - -systemctl stop testsuite-66-deviceisolation.service - -grep -q "thisshouldnotbehere" "$RESULTS_FILE" && exit 42 - -systemd-analyze log-level info - -touch /testok diff --git a/test/units/testsuite-67.service b/test/units/testsuite-67.service deleted file mode 100644 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-67.sh b/test/units/testsuite-67.sh deleted file mode 100755 index a42fd66..0000000 --- a/test/units/testsuite-67.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -euxo pipefail - -export DM_NAME="integrity_test" -export FULL_DM_DEV_NAME="/dev/mapper/${DM_NAME}" -export FS_UUID="01234567-ffff-eeee-eeee-0123456789ab" -export GEN="/var/run/systemd/generator" - -image_dir="" - -cleanup() -{ - if [ -z "${image_dir}" ]; then - return - fi - - if [ -f "${image_dir}/image" ]; then - if [ -e "${FULL_DM_DEV_NAME}" ]; then - integritysetup close "${DM_NAME}" - fi - losetup -d "${loop}" - fi - - rm -rf "${image_dir}" -} - -trap cleanup EXIT - -build_integrity_tab() -{ -cat <"/etc/integritytab" -${DM_NAME} ${loop} - integrity-algorithm=$1 -EOF -} - -image_dir="$(mktemp -d -t -p / integrity.tmp.XXXXXX)" -if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then - echo "mktemp under / failed" - exit 1 -fi - -dd if=/dev/zero of="${image_dir}/image" bs=1048576 count=64 || exit 1 -dd if=/dev/zero of="${image_dir}/data" bs=1048576 count=64 || exit 1 -loop="$(losetup --show -f "${image_dir}/image")" - -if [[ ! -e ${loop} ]]; then - echo "Loopback device created not found!" - exit 1 -fi - -# Do one iteration with a separate data device, to test those branches -separate_data=1 - -for algorithm in crc32c crc32 sha1 sha256 -do - if [ "${separate_data}" -eq 1 ]; then - data_option="--data-device=${image_dir}/data" - else - data_option="" - fi - integritysetup format "${loop}" --batch-mode -I "${algorithm}" "${data_option}" || exit 1 - integritysetup open -I "${algorithm}" "${loop}" "${DM_NAME}" "${data_option}" || exit 1 - mkfs.ext4 -U "${FS_UUID}" "${FULL_DM_DEV_NAME}" || exit 1 - - # Give userspace time to handle udev events for new FS showing up ... - udevadm settle - - integritysetup close "${DM_NAME}" || exit 1 - - # create integritytab, generate units, start service - if [ "${separate_data}" -eq 1 ]; then - data_option=",data-device=${image_dir}/data" - else - data_option="" - fi - build_integrity_tab "${algorithm}${data_option}" - - # Cause the generator to re-run - systemctl daemon-reload || exit 1 - - # Check for existence of unit files... - if [[ ! -e "/run/systemd/generator/systemd-integritysetup@${DM_NAME}.service" ]]; then - echo "Service file does not exist!" - exit 1 - fi - - # Make sure we are in a consistent state, e.g. not already active before we start - systemctl stop systemd-integritysetup@"${DM_NAME}".service || exit 1 - systemctl start systemd-integritysetup@"${DM_NAME}".service || exit 1 - # Reset the start-limit counters, as we're going to restart the service a couple of times - systemctl reset-failed systemd-integritysetup@"${DM_NAME}".service - - # Check the signature on the FS to ensure we can retrieve it and that is matches - if [ -e "${FULL_DM_DEV_NAME}" ]; then - # If a separate device is used for the metadata storage, then blkid will return one of the loop devices - if [ "${separate_data}" -eq 1 ]; then - dev_name="$(integritysetup status ${DM_NAME} | grep '^\s*device:' | awk '{print $2}')" - else - dev_name="${FULL_DM_DEV_NAME}" - fi - if [ "${dev_name}" != "$(blkid -U "${FS_UUID}")" ]; then - echo "Failed to locate FS with matching UUID!" - exit 1 - fi - else - echo "Failed to bring up integrity device!" - exit 1 - fi - - systemctl stop systemd-integritysetup@"${DM_NAME}".service || exit 1 - - if [ -e "${FULL_DM_DEV_NAME}" ]; then - echo "Expecting ${FULL_DM_DEV_NAME} to not exist after stopping unit!" - exit 1 - fi - - separate_data=0 -done - -touch /testok diff --git a/test/units/testsuite-68.service b/test/units/testsuite-68.service deleted file mode 100644 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-68.sh b/test/units/testsuite-68.sh deleted file mode 100755 index 11da48a..0000000 --- a/test/units/testsuite-68.sh +++ /dev/null @@ -1,216 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex -set -o pipefail - -# Wait for a service to enter a state within a timeout period, if it doesn't -# enter the desired state within the timeout period then this function will -# exit the test case with a non zero exit code. -wait_on_state_or_fail() { - service=$1 - expected_state=$2 - timeout=$3 - - state=$(systemctl show "$service" --property=ActiveState --value) - while [ "$state" != "$expected_state" ]; do - if [ "$timeout" = "0" ]; then - systemd-analyze log-level info - exit 1 - fi - timeout=$((timeout - 1)) - sleep 1 - state=$(systemctl show "$service" --property=ActiveState --value) - done -} - -systemd-analyze log-level debug - -cat >/run/systemd/system/testservice-failure-68.service </run/systemd/system/testservice-failure-68-template.service </run/systemd/system/testservice-success-68.service </run/systemd/system/testservice-success-68-template.service </tmp/check_on_success.sh <<"EOF" -#!/bin/sh - -set -ex -env | sort -if [ "$MONITOR_SERVICE_RESULT" != "success" ]; then - echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'success'" - exit 1 -fi - -if [ "$MONITOR_EXIT_CODE" != "exited" ]; then - echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'" - exit 1 -fi - -if [ "$MONITOR_EXIT_STATUS" != "0" ]; then - echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '0'" - exit 1 -fi - -if [ -z "$MONITOR_INVOCATION_ID" ]; then - echo "MONITOR_INVOCATION_ID unset" - exit 1 -fi - -if [ "$MONITOR_UNIT" != "testservice-success-68.service" ] && - [ "$MONITOR_UNIT" != "testservice-success-68-template.service" ] && - [ "$MONITOR_UNIT" != "testservice-transient-success-68.service" ]; then - - echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-success-68[-template].service'" - exit 1 -fi - -exit 0 -EOF -chmod +x /tmp/check_on_success.sh - -cat >/run/systemd/system/testservice-success-exit-handler-68.service </run/systemd/system/testservice-success-exit-handler-68-template@.service </tmp/check_on_failure.sh <<"EOF" -#!/bin/sh - -set -ex -env | sort -if [ "$MONITOR_SERVICE_RESULT" != "exit-code" ]; then - echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'exit-code'" - exit 1 -fi - -if [ "$MONITOR_EXIT_CODE" != "exited" ]; then - echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'" - exit 1 -fi - -if [ "$MONITOR_EXIT_STATUS" != "1" ]; then - echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '1'" - exit 1 -fi - -if [ -z "$MONITOR_INVOCATION_ID" ]; then - echo "MONITOR_INVOCATION_ID unset" - exit 1 -fi - -if [ "$MONITOR_UNIT" != "testservice-failure-68.service" ] && - [ "$MONITOR_UNIT" != "testservice-failure-68-template.service" ] && - [ "$MONITOR_UNIT" != "testservice-transient-failure-68.service" ]; then - - echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-failure-68[-template].service'" - exit 1 -fi - -exit 0 -EOF -chmod +x /tmp/check_on_failure.sh - - -cat >/run/systemd/system/testservice-failure-exit-handler-68.service </run/systemd/system/testservice-failure-exit-handler-68-template@.service < /tmp/testdata -systemd-creds encrypt /tmp/testdata /tmp/testdata.encrypted --with-key=tpm2 -# LoadCredentialEncrypted -systemd-run -p PrivateDevices=yes -p LoadCredentialEncrypted=testdata.encrypted:/tmp/testdata.encrypted --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata -# SetCredentialEncrypted -systemd-run -p PrivateDevices=yes -p SetCredentialEncrypted=testdata.encrypted:"$(cat /tmp/testdata.encrypted)" --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata - -rm -f /tmp/testdata diff --git a/test/units/testsuite-70.cryptenroll.sh b/test/units/testsuite-70.cryptenroll.sh deleted file mode 100755 index 3f8c14e..0000000 --- a/test/units/testsuite-70.cryptenroll.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -cryptenroll_wipe_and_check() {( - set +o pipefail - - : >/tmp/cryptenroll.out - systemd-cryptenroll "$@" |& tee /tmp/cryptenroll.out - grep -qE "Wiped slot [[:digit:]]+" /tmp/cryptenroll.out -)} - -# There is an external issue with libcryptsetup on ppc64 that hits 95% of Ubuntu ppc64 test runs, so skip it -if [[ "$(uname -m)" == "ppc64le" ]]; then - echo "Skipping systemd-cryptenroll tests on ppc64le, see https://github.com/systemd/systemd/issues/27716" - exit 0 -fi - -export SYSTEMD_LOG_LEVEL=debug -IMAGE="$(mktemp /tmp/systemd-cryptenroll-XXX.image)" - -truncate -s 20M "$IMAGE" -echo -n password >/tmp/password -# Change file mode to avoid "/tmp/password has 0644 mode that is too permissive" messages -chmod 0600 /tmp/password -cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/password - -# Enroll additional tokens, keys, and passwords to exercise the list and wipe stuff -systemd-cryptenroll --unlock-key-file=/tmp/password --tpm2-device=auto "$IMAGE" -NEWPASSWORD="" systemd-cryptenroll --unlock-key-file=/tmp/password --password "$IMAGE" -NEWPASSWORD=foo systemd-cryptenroll --unlock-key-file=/tmp/password --password "$IMAGE" -for _ in {0..9}; do - systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE" -done -PASSWORD="" NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE" -# Do some basic checks before we start wiping stuff -systemd-cryptenroll "$IMAGE" -systemd-cryptenroll "$IMAGE" | grep password -systemd-cryptenroll "$IMAGE" | grep recovery -# Let's start wiping -cryptenroll_wipe_and_check "$IMAGE" --wipe=empty -(! cryptenroll_wipe_and_check "$IMAGE" --wipe=empty) -cryptenroll_wipe_and_check "$IMAGE" --wipe=empty,0 -PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --wipe=0,0,empty,0,pkcs11,fido2,000,recovery,password --password -systemd-cryptenroll "$IMAGE" | grep password -(! systemd-cryptenroll "$IMAGE" | grep recovery) -# We shouldn't be able to wipe all keyslots without enrolling a new key first -(! systemd-cryptenroll "$IMAGE" --wipe=all) -PASSWORD=foo NEWPASSWORD=foo cryptenroll_wipe_and_check "$IMAGE" --password --wipe=all -# Check if the newly (and only) enrolled password works -(! systemd-cryptenroll --unlock-key-file=/tmp/password --recovery-key "$IMAGE") -(! PASSWORD="" systemd-cryptenroll --recovery-key "$IMAGE") -PASSWORD=foo systemd-cryptenroll --recovery-key "$IMAGE" - -systemd-cryptenroll --fido2-with-client-pin=false "$IMAGE" -systemd-cryptenroll --fido2-with-user-presence=false "$IMAGE" -systemd-cryptenroll --fido2-with-user-verification=false "$IMAGE" -systemd-cryptenroll --tpm2-pcrs=8 "$IMAGE" -systemd-cryptenroll --tpm2-pcrs=boot-loader-code+boot-loader-config "$IMAGE" - -(! systemd-cryptenroll --fido2-with-client-pin=false) -(! systemd-cryptenroll --fido2-with-user-presence=f "$IMAGE" /tmp/foo) -(! systemd-cryptenroll --fido2-with-client-pin=1234 "$IMAGE") -(! systemd-cryptenroll --fido2-with-user-presence=1234 "$IMAGE") -(! systemd-cryptenroll --fido2-with-user-verification=1234 "$IMAGE") -(! systemd-cryptenroll --tpm2-with-pin=1234 "$IMAGE") -(! systemd-cryptenroll --recovery-key --password "$IMAGE") -(! systemd-cryptenroll --password --recovery-key "$IMAGE") -(! systemd-cryptenroll --password --fido2-device=auto "$IMAGE") -(! systemd-cryptenroll --password --pkcs11-token-uri=auto "$IMAGE") -(! systemd-cryptenroll --password --tpm2-device=auto "$IMAGE") -(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-fido2-device=auto "$IMAGE") -(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-key-file=/tmp/unlock "$IMAGE") -(! systemd-cryptenroll --fido2-credential-algorithm=es512 "$IMAGE") -(! systemd-cryptenroll --tpm2-public-key-pcrs=key "$IMAGE") -(! systemd-cryptenroll --tpm2-pcrs=key "$IMAGE") -(! systemd-cryptenroll --tpm2-pcrs=44+8 "$IMAGE") -(! systemd-cryptenroll --tpm2-pcrs=hello "$IMAGE") -(! systemd-cryptenroll --wipe-slot "$IMAGE") -(! systemd-cryptenroll --wipe-slot=10240000 "$IMAGE") -(! systemd-cryptenroll --fido2-device=auto --unlock-fido2-device=auto "$IMAGE") - -rm -f "$IMAGE" diff --git a/test/units/testsuite-70.cryptsetup.sh b/test/units/testsuite-70.cryptsetup.sh deleted file mode 100755 index 4cd627f..0000000 --- a/test/units/testsuite-70.cryptsetup.sh +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -export SYSTEMD_LOG_LEVEL=debug - -cryptsetup_has_token_plugin_support() { - local plugin_path - - plugin_path="$(cryptsetup --help | sed -nr 's/.*LUKS2 external token plugin path: (.*)\./\1/p')/libcryptsetup-token-systemd-tpm2.so)" - cryptsetup --help | grep -q 'LUKS2 external token plugin support is compiled-in' && [[ -f "$plugin_path" ]] -} - -tpm_check_failure_with_wrong_pin() { - local testIMAGE="${1:?}" - local badpin="${2:?}" - local goodpin="${3:?}" - - # We need to be careful not to trigger DA lockout; allow 2 failures - tpm2_dictionarylockout -s -n 2 - (! PIN=$badpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1) - # Verify the correct PIN works, to be sure the failure wasn't a DA lockout - PIN=$goodpin systemd-cryptsetup attach test-volume "$testIMAGE" - tpm2-device=auto,headless=1 - systemd-cryptsetup detach test-volume - # Clear/reset the DA lockout counter - tpm2_dictionarylockout -c -} - -at_exit() { - # Evict the TPM primary key that we persisted - if [[ -n "${PERSISTENT_HANDLE:-}" ]]; then - tpm2_evictcontrol -c "$PERSISTENT_HANDLE" - fi -} - -trap at_exit EXIT - -# Prepare a fresh disk image -IMAGE="$(mktemp /tmp/systemd-cryptsetup-XXX.IMAGE)" - -truncate -s 20M "$IMAGE" -echo -n passphrase >/tmp/passphrase -# Change file mode to avoid "/tmp/passphrase has 0644 mode that is too permissive" messages -chmod 0600 /tmp/passphrase -cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase - -# Unlocking via keyfile -systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto "$IMAGE" - -# Enroll unlock with default PCR policy -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto "$IMAGE" -systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -# Check with wrong PCR -tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000 -(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1) - -# Enroll unlock with PCR+PIN policy -systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" -PASSWORD=passphrase NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE" -PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -# Check failure with wrong PIN; try a few times to make sure we avoid DA lockout -for _ in {0..3}; do - tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456 -done - -# Check LUKS2 token plugin unlock (i.e. without specifying tpm2-device=auto) -if cryptsetup_has_token_plugin_support; then - PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - headless=1 - systemd-cryptsetup detach test-volume - - # Check failure with wrong PIN - for _ in {0..3}; do - tpm_check_failure_with_wrong_pin "$IMAGE" 123457 123456 - done -else - echo 'cryptsetup has no LUKS2 token plugin support, skipping' -fi - -# Check failure with wrong PCR (and correct PIN) -tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000 -(! PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1) - -# Enroll unlock with PCR 0+7 -systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 "$IMAGE" -systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -# Check with wrong PCR 0 -tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000 -(! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1) - -if tpm_has_pcr sha256 12; then - # Enroll using an explicit PCR value (that does match current PCR value) - systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" - EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) - PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE" - systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 - systemd-cryptsetup detach test-volume - - # Same as above plus more PCRs without the value or alg specified - systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" - EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) - PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE" - systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 - systemd-cryptsetup detach test-volume - - # Same as above plus more PCRs with hash alg specified but hash value not specified - systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" - EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) - PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1:sha256,12:sha256=$EXPECTED_PCR_VALUE,3" "$IMAGE" - systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 - systemd-cryptsetup detach test-volume - - # Now the interesting part, enrolling using a hash value that doesn't match the current PCR value - systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" - tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12 - CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) - EXPECTED_PCR_VALUE=$(cat /tmp/pcr.dat /tmp/pcr.dat | openssl dgst -sha256 -r | cut -d ' ' -f 1) - PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$IMAGE" - (! systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1) - tpm2_pcrextend "12:sha256=$CURRENT_PCR_VALUE" - systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 - systemd-cryptsetup detach test-volume - - # enroll TPM using device key instead of direct access, then verify unlock using TPM - tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12 - CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) - tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub - systemd-analyze srk > /tmp/srk2.pub - cmp /tmp/srk.pub /tmp/srk2.pub - if [ -f /run/systemd/tpm2-srk-public-key.tpm2b_public ] ; then - cmp /tmp/srk.pub /run/systemd/tpm2-srk-public-key.tpm2b_public - fi - - # --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS - if openssl_supports_kdf SSKDF; then - PASSWORD=passphrase systemd-cryptenroll --tpm2-device-key=/tmp/srk.pub --tpm2-pcrs="12:sha256=$CURRENT_PCR_VALUE" "$IMAGE" - systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 - systemd-cryptsetup detach test-volume - fi - - rm -f /tmp/pcr.dat /tmp/srk.pub -fi - -# Use default (0) seal key handle -systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0 "$IMAGE" -systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x0 "$IMAGE" -systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -# Use SRK seal key handle -systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=81000001 "$IMAGE" -systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x81000001 "$IMAGE" -systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -# Test invalid ranges: pcr, nv, session, permanent -systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" -(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=7 "$IMAGE") # PCR -(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x01000001 "$IMAGE") # NV index -(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x02000001 "$IMAGE") # HMAC/loaded session -(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x03000001 "$IMAGE") # Policy/saved session -(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x40000001 "$IMAGE") # Permanent - -# Use non-SRK persistent seal key handle (by creating/persisting new key) -PRIMARY=/tmp/primary.ctx -tpm2_createprimary -c "$PRIMARY" -PERSISTENT_LINE=$(tpm2_evictcontrol -c "$PRIMARY" | grep persistent-handle) -PERSISTENT_HANDLE="0x${PERSISTENT_LINE##*0x}" -tpm2_flushcontext -t - -systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="${PERSISTENT_HANDLE#0x}" "$IMAGE" -systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" -PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="$PERSISTENT_HANDLE" "$IMAGE" -systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 -systemd-cryptsetup detach test-volume - -# --tpm2-device-key= requires OpenSSL >= 3 with KDF-SS -if openssl_supports_kdf SSKDF; then - # Make sure that --tpm2-device-key= also works with systemd-repart - tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub - mkdir /tmp/dditest - cat > /tmp/dditest/50-root.conf </tmp/tpmdata1 -echo foobar >/tmp/tpmdata2 - -cat >/tmp/result </tmp/result.json </tmp/result </tmp/result.json </dev/null; then - MEASURE_BANKS+=("--bank=sha1") -fi - -# Sign current PCR state with it -"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: | tee "/tmp/pcrsign.sig" -dd if=/dev/urandom of=/tmp/pcrtestdata bs=1024 count=64 -systemd-creds encrypt /tmp/pcrtestdata /tmp/pcrtestdata.encrypted --with-key=host+tpm2-with-public-key --tpm2-public-key="/tmp/pcrsign-public.pem" -systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" | cmp - /tmp/pcrtestdata - -# Invalidate PCR, decrypting should fail now -tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000 -(! systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" >/dev/null) - -# Sign new PCR state, decrypting should work now. -"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig2" -systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig2" | cmp - /tmp/pcrtestdata - -# Now, do the same, but with a cryptsetup binding -truncate -s 20M "$IMAGE" -cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase -# Ensure that an unrelated signature, when not requested, is not used -touch /run/systemd/tpm2-pcr-signature.json -systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" "$IMAGE" -# Reset and use the signature now -rm -f /run/systemd/tpm2-pcr-signature.json -systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" -systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-public-key="/tmp/pcrsign-public.pem" --tpm2-signature="/tmp/pcrsign.sig2" "$IMAGE" - -# Check if we can activate that (without the token module stuff) -SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1 -SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup detach test-volume2 - -# Check if we can activate that (and a second time with the token module stuff enabled) -SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1 -SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup detach test-volume2 - -# After extending the PCR things should fail -tpm2_pcrextend 11:sha256=0000000000000000000000000000000000000000000000000000000000000000 -(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1) -(! SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1) - -# But once we sign the current PCRs, we should be able to unlock again -"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: >"/tmp/pcrsign.sig3" -SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1 -systemd-cryptsetup detach test-volume2 -SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1 -systemd-cryptsetup detach test-volume2 - -# Test --append mode and de-duplication. With the same parameters signing should not add a new entry -"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig3" >"/tmp/pcrsign.sig4" -cmp "/tmp/pcrsign.sig3" "/tmp/pcrsign.sig4" - -# Sign one more phase, this should -"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig4" >"/tmp/pcrsign.sig5" -(! cmp "/tmp/pcrsign.sig4" "/tmp/pcrsign.sig5") - -# Should still be good to unlock, given the old entry still exists -SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach test-volume2 "$IMAGE" - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig5",headless=1 -systemd-cryptsetup detach test-volume2 - -# Adding both signatures once more should not change anything, due to the deduplication -"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: --append="/tmp/pcrsign.sig5" >"/tmp/pcrsign.sig6" -"$SD_MEASURE" sign --current "${MEASURE_BANKS[@]}" --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=quux:waldo --append="/tmp/pcrsign.sig6" >"/tmp/pcrsign.sig7" -cmp "/tmp/pcrsign.sig5" "/tmp/pcrsign.sig7" - -rm -f "$IMAGE" diff --git a/test/units/testsuite-70.pcrextend.sh b/test/units/testsuite-70.pcrextend.sh deleted file mode 100755 index 318fce0..0000000 --- a/test/units/testsuite-70.pcrextend.sh +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -export SYSTEMD_LOG_LEVEL=debug -SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend" - -if [[ ! -x "${SD_PCREXTEND:?}" ]] || ! tpm_has_pcr sha256 11 || ! tpm_has_pcr sha256 15; then - echo "$SD_PCREXTEND or PCR sysfs files not found, skipping PCR extension tests" - exit 0 -fi - -at_exit() { - if [[ $? -ne 0 ]]; then - # Dump the event log on fail, to make debugging a bit easier - jq --seq --slurp /etc/machine-id -SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --machine-id -mv /etc/machine-id.save /etc/machine-id -tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15 - -# And check it matches expectations -diff /tmp/newpcr15 \ - <(cat /tmp/oldpcr15 <(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256) - -# Check that the event log record was properly written -test "$(jq --seq --slurp ".[$RECORD_COUNT].pcr" /run/systemd/system/systemd-pcrextend.socket.d/50-no-condition.conf </tmp/borked -set +e -SYSTEMD_MEASURE_LOG_USERSPACE=/tmp/borked "$SD_PCRLOCK" cel --no-pager --json=pretty -ret=$? -set -e -# If it crashes the exit code will be 149 -test $ret -eq 1 - -SYSTEMD_COLORS=256 "$SD_PCRLOCK" -"$SD_PCRLOCK" cel --no-pager --json=pretty -"$SD_PCRLOCK" log --pcr="$PCRS" -"$SD_PCRLOCK" log --json=pretty --pcr="$PCRS" -"$SD_PCRLOCK" list-components -"$SD_PCRLOCK" list-components --location=250- -"$SD_PCRLOCK" list-components --location=250-:350- -"$SD_PCRLOCK" lock-firmware-config -"$SD_PCRLOCK" lock-gpt -"$SD_PCRLOCK" lock-machine-id -"$SD_PCRLOCK" lock-file-system -"$SD_PCRLOCK" lock-file-system / -"$SD_PCRLOCK" predict --pcr="$PCRS" -"$SD_PCRLOCK" predict --pcr="0x1+0x3+4" -"$SD_PCRLOCK" predict --json=pretty --pcr="$PCRS" - -SD_STUB="$(find /usr/lib/systemd/boot/efi/ -name "systemd-boot*.efi" | head -n1)" -if [[ -n "$SD_STUB" ]]; then - "$SD_PCRLOCK" lock-pe "$SD_STUB" - "$SD_PCRLOCK" lock-pe <"$SD_STUB" - "$SD_PCRLOCK" lock-uki "$SD_STUB" - "$SD_PCRLOCK" lock-uki <"$SD_STUB" -fi - -PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=yes -# Repeat immediately (this call will have to reuse the nvindex, rather than create it) -"$SD_PCRLOCK" make-policy --pcr="$PCRS" -"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force - -img="/tmp/pcrlock.img" -truncate -s 20M "$img" -echo -n hoho >/tmp/pcrlockpwd -chmod 0600 /tmp/pcrlockpwd -cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$img" /tmp/pcrlockpwd - -systemd-cryptenroll --unlock-key-file=/tmp/pcrlockpwd --tpm2-device=auto --tpm2-pcrlock=/var/lib/systemd/pcrlock.json --tpm2-public-key= --wipe-slot=tpm2 "$img" -systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless -systemd-cryptsetup detach pcrlock - -# Measure something into PCR 16 (the "debug" PCR), which should make the activation fail -"$SD_PCREXTEND" --pcr=16 test70 - -"$SD_PCRLOCK" cel --json=pretty - -(! systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless ) - -# Now add a component for it, rebuild policy and it should work (we'll rebuild -# once like that, but don't provide the recovery pin. This should fail, since -# the PCR is hosed after all. But then we'll use recovery pin, and it should -# work. -echo -n test70 | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock --pcr=16 -(! "$SD_PCRLOCK" make-policy --pcr="$PCRS") -PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=yes - -systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless -systemd-cryptsetup detach pcrlock - -# And now let's do it the clean way, and generate the right policy ahead of time. -echo -n test70-take-two | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock --pcr=16 -"$SD_PCRLOCK" make-policy --pcr="$PCRS" - -"$SD_PCREXTEND" --pcr=16 test70-take-two - -"$SD_PCRLOCK" cel --json=pretty - -systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless -systemd-cryptsetup detach pcrlock - -"$SD_PCRLOCK" remove-policy - -"$SD_PCRLOCK" unlock-firmware-config -"$SD_PCRLOCK" unlock-gpt -"$SD_PCRLOCK" unlock-machine-id -"$SD_PCRLOCK" unlock-file-system -"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock -"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock - -(! "$SD_PCRLOCK" "") -(! "$SD_PCRLOCK" predict --pcr=-1) -(! "$SD_PCRLOCK" predict --pcr=foo) -(! "$SD_PCRLOCK" predict --pcr=1+1) -(! "$SD_PCRLOCK" predict --pcr=1+++++1) -(! "$SD_PCRLOCK" make-policy --nv-index=0) -(! "$SD_PCRLOCK" make-policy --nv-index=foo) -(! "$SD_PCRLOCK" list-components --location=:) -(! "$SD_PCRLOCK" lock-gpt "") -(! "$SD_PCRLOCK" lock-gpt /dev/sr0) -(! "$SD_PCRLOCK" lock-pe /dev/full) -(! "$SD_PCRLOCK" lock-pe /bin/true) -(! "$SD_PCRLOCK" lock-uki /dev/full) -(! "$SD_PCRLOCK" lock-uki /bin/true) -(! "$SD_PCRLOCK" lock-file-system "") - -rm "$img" /tmp/pcrlockpwd 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-70.sh b/test/units/testsuite-70.sh deleted file mode 100755 index 9c2a033..0000000 --- a/test/units/testsuite-70.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/test-control.sh -. "$(dirname "$0")"/test-control.sh - -run_subtests - -touch /testok diff --git a/test/units/testsuite-70.tpm2-setup.sh b/test/units/testsuite-70.tpm2-setup.sh deleted file mode 100755 index faf6fe7..0000000 --- a/test/units/testsuite-70.tpm2-setup.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -export SYSTEMD_LOG_LEVEL=debug -SD_TPM2SETUP="/usr/lib/systemd/systemd-tpm2-setup" - -if [[ ! -x "${SD_TPM2SETUP:?}" ]]; then - echo "$SD_TPM2SETUP not found, skipping the test" - exit 0 -fi - -"$SD_TPM2SETUP" --help -"$SD_TPM2SETUP" --version -"$SD_TPM2SETUP" --tpm2-device=list -"$SD_TPM2SETUP" --tpm2-device=auto -"$SD_TPM2SETUP" --tpm2-device=/dev/tpm0 -"$SD_TPM2SETUP" --early=yes -"$SD_TPM2SETUP" --early=yes -"$SD_TPM2SETUP" --early=no -"$SD_TPM2SETUP" --early=no - -(! "$SD_TPM2SETUP" "") -(! "$SD_TPM2SETUP" --tpm2-device=) -(! "$SD_TPM2SETUP" --tpm2-device=/dev/null) -(! "$SD_TPM2SETUP" --foo=bar) diff --git a/test/units/testsuite-71.service b/test/units/testsuite-71.service deleted file mode 100644 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-71.sh b/test/units/testsuite-71.sh deleted file mode 100755 index da765a9..0000000 --- a/test/units/testsuite-71.sh +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later - -set -eux -set -o pipefail - -# shellcheck source=test/units/test-control.sh -. "$(dirname "$0")"/test-control.sh -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -restore_hostname() { - if [[ -e /tmp/hostname.bak ]]; then - mv /tmp/hostname.bak /etc/hostname - else - rm -f /etc/hostname - fi -} - -testcase_hostname() { - local orig= - - if [[ -f /etc/hostname ]]; then - cp /etc/hostname /tmp/hostname.bak - orig=$(cat /etc/hostname) - fi - - trap restore_hostname RETURN - - # should activate daemon and work - if [[ -n "$orig" ]]; then - assert_in "Static hostname: $orig" "$(hostnamectl)" - fi - assert_in "Kernel: $(uname -s) $(uname -r)" "$(hostnamectl)" - - # change hostname - assert_rc 0 hostnamectl set-hostname testhost - assert_eq "$(cat /etc/hostname)" "testhost" - assert_in "Static hostname: testhost" "$(hostnamectl)" - - if [[ -n "$orig" ]]; then - # reset to original - assert_rc 0 hostnamectl set-hostname "$orig" - assert_eq "$(cat /etc/hostname)" "$orig" - assert_in "Static hostname: $orig" "$(hostnamectl)" - fi -} - -restore_machine_info() { - if [[ -e /tmp/machine-info.bak ]]; then - mv /tmp/machine-info.bak /etc/machine-info - else - rm -f /etc/machine-info - fi -} - -get_chassis() ( - # shellcheck source=/dev/null - . /etc/machine-info - - echo "$CHASSIS" -) - -testcase_chassis() { - local i - - if [[ -f /etc/machine-info ]]; then - cp /etc/machine-info /tmp/machine-info.bak - fi - - trap restore_machine_info RETURN - - # Invalid chassis type is refused - assert_rc 1 hostnamectl chassis hoge - - # Valid chassis types - for i in vm container desktop laptop convertible server tablet handset watch embedded; do - hostnamectl chassis "$i" - assert_eq "$(hostnamectl chassis)" "$i" - assert_eq "$(get_chassis)" "$i" - done - - systemctl stop systemd-hostnamed.service - rm -f /etc/machine-info - - # fallback chassis type - if systemd-detect-virt --quiet --container; then - assert_eq "$(hostnamectl chassis)" container - elif systemd-detect-virt --quiet --vm; then - assert_eq "$(hostnamectl chassis)" vm - fi -} - -restore_sysfs_dmi() { - umount /sys/class/dmi/id - rm -rf /run/systemd/system/systemd-hostnamed.service.d - systemctl daemon-reload - systemctl stop systemd-hostnamed -} - -testcase_firmware_date() { - # No DMI on s390x or ppc - if [[ ! -d /sys/class/dmi/id ]]; then - echo "/sys/class/dmi/id not found, skipping firmware date tests." - return 0 - fi - - trap restore_sysfs_dmi RETURN - - # Ignore /sys being mounted as tmpfs - mkdir -p /run/systemd/system/systemd-hostnamed.service.d/ - cat >/run/systemd/system/systemd-hostnamed.service.d/override.conf </sys/class/dmi/id/uevent - - echo '09/08/2000' >/sys/class/dmi/id/bios_date - systemctl stop systemd-hostnamed - assert_in '2000-09-08' "$(hostnamectl)" - - echo '2022' >/sys/class/dmi/id/bios_date - systemctl stop systemd-hostnamed - assert_not_in 'Firmware Date' "$(hostnamectl)" - - echo 'garbage' >/sys/class/dmi/id/bios_date - systemctl stop systemd-hostnamed - assert_not_in 'Firmware Date' "$(hostnamectl)" -} - -testcase_nss-myhostname() { - local database host i - - HOSTNAME="$(hostnamectl hostname)" - - # Set up a dummy network for _gateway and _outbound labels - ip link add foo type dummy - ip link set up dev foo - ip addr add 10.0.0.2/24 dev foo - for i in {128..150}; do - ip addr add "10.0.0.$i/24" dev foo - done - ip route add 10.0.0.1 dev foo - ip route add default via 10.0.0.1 dev foo - - # Note: `getent hosts` probes gethostbyname2(), whereas `getent ahosts` probes gethostbyname3() - # and gethostbyname4() (through getaddrinfo() -> gaih_inet() -> get_nss_addresses()) - getent hosts -s myhostname - getent ahosts -s myhostname - - # With IPv6 disabled - sysctl -w net.ipv6.conf.all.disable_ipv6=1 - # Everything under .localhost and .localhost.localdomain should resolve to localhost - for host in {foo.,foo.bar.baz.,.,}localhost{,.} {foo.,foo.bar.baz.,.,}localhost.localdomain{,.}; do - run_and_grep "^127\.0\.0\.1\s+localhost$" getent hosts -s myhostname "$host" - run_and_grep "^127\.0\.0\.1\s+STREAM\s+localhost" getent ahosts -s myhostname "$host" - run_and_grep "^127\.0\.0\.1\s+STREAM\s+localhost" getent ahostsv4 -s myhostname "$host" - (! getent ahostsv6 -s myhostname localhost) - done - for i in 2 {128..150}; do - run_and_grep "^10\.0\.0\.$i\s+$HOSTNAME$" getent hosts -s myhostname "$HOSTNAME" - run_and_grep "^10\.0\.0\.$i\s+" getent ahosts -s myhostname "$HOSTNAME" - run_and_grep "^10\.0\.0\.$i\s+" getent ahostsv4 -s myhostname "$HOSTNAME" - run_and_grep "^10\.0\.0\.$i\s+$HOSTNAME$" getent hosts -s myhostname "10.0.0.$i" - run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahosts -s myhostname "10.0.0.$i" - run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahostsv4 -s myhostname "10.0.0.$i" - done - for database in hosts ahosts ahostsv4 ahostsv6; do - (! getent "$database" -s myhostname ::1) - done - (! getent ahostsv6 -s myhostname "$HOSTNAME") - run_and_grep -n "^fe80:[^ ]+\s+STREAM$" getent ahosts -s myhostname "$HOSTNAME" - - # With IPv6 enabled - sysctl -w net.ipv6.conf.all.disable_ipv6=0 - # Everything under .localhost and .localhost.localdomain should resolve to localhost - for host in {foo.,foo.bar.baz.,.,}localhost{,.} {foo.,foo.bar.baz.,.,}localhost.localdomain{,.}; do - run_and_grep "^::1\s+localhost$" getent hosts -s myhostname "$host" - run_and_grep "^::1\s+STREAM" getent ahosts -s myhostname "$host" - run_and_grep "^127\.0\.0\.1\s+STREAM" getent ahosts -s myhostname "$host" - run_and_grep "^127\.0\.0\.1\s+STREAM" getent ahostsv4 -s myhostname "$host" - run_and_grep -n "^::1\s+STREAM" getent ahostsv4 -s myhostname "$host" - run_and_grep "^::1\s+STREAM" getent ahostsv6 -s myhostname "$host" - run_and_grep -n "^127\.0\.0\.1\s+STREAM" getent ahostsv6 -s myhostname "$host" - done - for i in 2 {128..150}; do - run_and_grep "^10\.0\.0\.$i\s+" getent ahosts -s myhostname "$HOSTNAME" - run_and_grep "^10\.0\.0\.$i\s+" getent ahostsv4 -s myhostname "$HOSTNAME" - run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahosts -s myhostname "10.0.0.$i" - run_and_grep "^10\.0\.0\.$i\s+STREAM\s+10\.0\.0\.$i$" getent ahostsv4 -s myhostname "10.0.0.$i" - done - run_and_grep "^fe80:[^ ]+\s+$HOSTNAME$" getent hosts -s myhostname "$HOSTNAME" - run_and_grep "^fe80:[^ ]+\s+STREAM" getent ahosts -s myhostname "$HOSTNAME" - run_and_grep "^127\.0\.0\.1\s+localhost$" getent hosts -s myhostname 127.0.0.1 - run_and_grep "^127\.0\.0\.1\s+STREAM\s+127\.0\.0\.1$" getent ahosts -s myhostname 127.0.0.1 - run_and_grep "^::ffff:127\.0\.0\.1\s+STREAM\s+127\.0\.0\.1$" getent ahostsv6 -s myhostname 127.0.0.1 - run_and_grep "^127\.0\.0\.2\s+$HOSTNAME$" getent hosts -s myhostname 127.0.0.2 - run_and_grep "^::1\s+localhost $HOSTNAME$" getent hosts -s myhostname ::1 - run_and_grep "^::1\s+STREAM\s+::1$" getent ahosts -s myhostname ::1 - (! getent ahostsv4 -s myhostname ::1) - - # _gateway - for host in _gateway{,.} 10.0.0.1; do - run_and_grep "^10\.0\.0\.1\s+_gateway$" getent hosts -s myhostname "$host" - run_and_grep "^10\.0\.0\.1\s+STREAM" getent ahosts -s myhostname "$host" - done - - # _outbound - for host in _outbound{,.} 10.0.0.2; do - run_and_grep "^10\.0\.0\.2\s+" getent hosts -s myhostname "$host" - run_and_grep "^10\.0\.0\.2\s+STREAM" getent ahosts -s myhostname "$host" - done - - # Non-existent records - for database in hosts ahosts ahostsv4 ahostsv6; do - (! getent "$database" -s myhostname this.should.not.exist) - done - (! getent hosts -s myhostname 10.254.254.1) - (! getent hosts -s myhostname fd00:dead:beef:cafe::1) -} - -run_testcases - -touch /testok 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-72.sh b/test/units/testsuite-72.sh deleted file mode 100755 index de657a2..0000000 --- a/test/units/testsuite-72.sh +++ /dev/null @@ -1,278 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- -# ex: ts=8 sw=4 sts=4 et filetype=sh -set -eux -set -o pipefail - -SYSUPDATE=/lib/systemd/systemd-sysupdate -SECTOR_SIZES="512 4096" -BACKING_FILE=/var/tmp/72-joined.raw -export SYSTEMD_ESP_PATH=/var/tmp/72-esp -export SYSTEMD_XBOOTLDR_PATH=/var/tmp/72-xbootldr -export SYSTEMD_PAGER=cat -export SYSTEMD_LOG_LEVEL=debug - -if ! test -x "$SYSUPDATE"; then - echo "no systemd-sysupdate" >/skipped - exit 0 -fi - -# Loopback devices may not be supported. They are used because sfdisk cannot -# change the sector size of a file, and we want to test both 512 and 4096 byte -# sectors. If loopback devices are not supported, we can only test one sector -# size, and the underlying device is likely to have a sector size of 512 bytes. -if [[ ! -e /dev/loop-control ]]; then - echo "No loopback device support" - SECTOR_SIZES="512" -fi - -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 -} - -new_version() { - # Inputs: - # $1: sector size - # $2: version - - # 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" - - # Create a random "UKI" payload - echo $RANDOM >"/var/tmp/72-source/uki-$2.efi" - - # Create a random extra payload - echo $RANDOM >"/var/tmp/72-source/uki-extra-$2.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" . - - ( cd /var/tmp/72-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 ) -} - -verify_version() { - # Inputs: - # $1: block device - # $2: sector size - # $3: version - # $4: partition number of part1 - # $5: partition number of part2 - - gpt_reserved_sectors=$(( 1024 * 1024 / $2 )) - part1_offset=$(( ( $4 - 1 ) * 2048 + gpt_reserved_sectors )) - part2_offset=$(( ( $5 - 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" - - # 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)" - - # 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" - - # 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 -} - -for sector_size in $SECTOR_SIZES ; do - # Disk size of: - # - 1MB for GPT - # - 4 partitions of 2048 sectors each - # - 1MB for backup GPT - disk_size=$(( sector_size * 2048 * 4 + 1024 * 1024 * 2 )) - rm -f "$BACKING_FILE" - truncate -s "$disk_size" "$BACKING_FILE" - - if [[ -e /dev/loop-control ]]; then - # shellcheck disable=SC2086 - blockdev="$(losetup --find --show --sector-size $sector_size $BACKING_FILE)" - else - blockdev="$BACKING_FILE" - fi - - sfdisk "$blockdev" </var/tmp/72-defs/01-first.conf </var/tmp/72-defs/02-second.conf </var/tmp/72-defs/03-third.conf </var/tmp/72-defs/04-fourth.conf </var/tmp/72-defs/05-fifth.conf </var/tmp/72-defs/02-second.conf </var/tmp/72-defs/03-third.conf <>/run/systemd/system/systemd-localed.service.d/override.conf <>/run/systemd/system/systemd-vconsole-setup.service.d/override.conf </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 - - # create invalid locale - mkdir -p /usr/lib/locale/xx_XX.UTF-8 - assert_not_in "xx_XX.UTF-8" "$(localectl list-locales)" - - if [[ -z "$(localectl list-locales)" ]]; then - echo "No locale installed, skipping test." - return - fi - - # start with a known default environment and make sure to also give a - # default value to LC_CTYPE= since we're about to also set/unset it. We - # also reload PID1 configuration to make sure that PID1 environment itself - # is updated as it's not always been the case. - assert_rc 0 localectl set-locale "LANG=en_US.UTF-8" "LC_CTYPE=C" - systemctl daemon-reload - output=$(localectl) - assert_in "System Locale: LANG=en_US.UTF-8" "$output" - assert_in "LC_CTYPE=C" "$output" - output=$(systemctl show-environment) - assert_in "LANG=en_US.UTF-8" "$output" - assert_in "LC_CTYPE=C" "$output" - - # warn when kernel command line has locale settings - output=$(SYSTEMD_PROC_CMDLINE="locale.LANG=C.UTF-8 locale.LC_CTYPE=ja_JP.UTF-8" localectl 2>&1) - assert_in "Warning:" "$output" - assert_in "Command Line: LANG=C.UTF-8" "$output" - assert_in "LC_CTYPE=ja_JP.UTF-8" "$output" - assert_in "System Locale:" "$output" - - # change locale - for i in $(localectl list-locales); do - assert_rc 0 localectl set-locale "LANG=C" "LC_CTYPE=$i" - if [[ -f /etc/default/locale ]]; then - assert_eq "$(cat /etc/default/locale)" "LANG=C -LC_CTYPE=$i" - else - assert_eq "$(cat /etc/locale.conf)" "LANG=C -LC_CTYPE=$i" - fi - output=$(localectl) - assert_in "System Locale: LANG=C" "$output" - assert_in "LC_CTYPE=$i" "$output" - output=$(systemctl show-environment) - assert_in "LANG=C" "$output" - assert_in "LC_CTYPE=$i" "$output" - - assert_rc 0 localectl set-locale "$i" - if [[ -f /etc/default/locale ]]; then - assert_eq "$(cat /etc/default/locale)" "LANG=$i" - else - assert_eq "$(cat /etc/locale.conf)" "LANG=$i" - fi - output=$(localectl) - assert_in "System Locale: LANG=$i" "$output" - assert_not_in "LC_CTYPE=" "$output" - output=$(systemctl show-environment) - assert_in "LANG=$i" "$output" - assert_not_in "LC_CTYPE=" "$output" - done - - # test if localed auto-runs locale-gen - if command -v locale-gen >/dev/null 2>&1 && - ! localectl list-locales | grep -F "de_DE.UTF-8"; then - - # clear previous locale - systemctl stop systemd-localed.service - rm -f /etc/locale.conf /etc/default/locale - - # change locale - assert_rc 0 localectl set-locale de_DE.UTF-8 - if [[ -f /etc/default/locale ]]; then - assert_eq "$(cat /etc/default/locale)" "LANG=de_DE.UTF-8" - else - assert_eq "$(cat /etc/locale.conf)" "LANG=de_DE.UTF-8" - fi - assert_in "System Locale: LANG=de_DE.UTF-8" "$(localectl)" - assert_in "LANG=de_DE.UTF-8" "$(systemctl show-environment)" - - # ensure tested locale exists and works now - assert_in "de_DE.UTF-8" "$(localectl list-locales)" - fi -} - -backup_keymap() { - if [[ -f /etc/vconsole.conf ]]; then - cp /etc/vconsole.conf /tmp/vconsole.conf.bak - fi - - if [[ -f /etc/X11/xorg.conf.d/00-keyboard.conf ]]; then - cp /etc/X11/xorg.conf.d/00-keyboard.conf /tmp/00-keyboard.conf.bak - fi - - # Debian/Ubuntu specific file - if [[ -f /etc/default/keyboard ]]; then - cp /etc/default/keyboard /tmp/default-keyboard.bak - fi - - mkdir -p /etc/default -} - -restore_keymap() { - if [[ -f /tmp/vconsole.conf.bak ]]; then - mv /tmp/vconsole.conf.bak /etc/vconsole.conf - else - rm -f /etc/vconsole.conf - fi - - if [[ -f /tmp/00-keyboard.conf.bak ]]; then - mv /tmp/00-keyboard.conf.bak /etc/X11/xorg.conf.d/00-keyboard.conf - else - rm -f /etc/X11/xorg.conf.d/00-keyboard.conf - fi - - if [[ -f /tmp/default-keyboard.bak ]]; then - mv /tmp/default-keyboard.bak /etc/default/keyboard - else - rm -f /etc/default/keyboard - rmdir --ignore-fail-on-non-empty /etc/default - fi -} - -wait_vconsole_setup() { - local i ss - for i in {1..20}; do - (( i > 1 )) && sleep 0.5 - ss="$(systemctl --property SubState --value show systemd-vconsole-setup.service)" - if [[ "$ss" == "exited" || "$ss" == "dead" || "$ss" == "condition" ]]; then - return 0 - elif [[ "$ss" == "failed" ]]; then - echo "WARNING: systemd-vconsole-setup.service failed, ignoring." >&2 - systemctl reset-failed systemd-vconsole-setup.service - return 0 - fi - done - - systemctl status systemd-vconsole-setup.service - return 1 -} - -testcase_vc_keymap() { - local i output vc - - if [[ -z "$(localectl list-keymaps)" ]]; then - echo "No vconsole keymap installed, skipping test." - return - fi - - backup_keymap - trap restore_keymap RETURN - - # should activate daemon and work - assert_in "VC Keymap:" "$(localectl)" - - for i in $(localectl list-keymaps); do - # clear previous conversion from VC -> X11 keymap - systemctl stop systemd-localed.service - wait_vconsole_setup - rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard - - # set VC keymap - assert_rc 0 localectl set-keymap "$i" - output=$(localectl) - - # check VC keymap - vc=$(cat /etc/vconsole.conf) - assert_in "KEYMAP=$i" "$vc" - assert_in "VC Keymap: $i" "$output" - - # check VC -> X11 keymap conversion - if [[ "$i" == "us" ]]; then - assert_in "X11 Layout: us" "$output" - assert_in "X11 Model: pc105\+inet" "$output" - assert_not_in "X11 Variant:" "$output" - assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output" - - assert_in "XKBLAYOUT=us" "$vc" - assert_in "XKBMODEL=pc105\+inet" "$vc" - assert_not_in "XKBVARIANT" "$vc" - assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc" - elif [[ "$i" == "us-acentos" ]]; then - assert_in "X11 Layout: us" "$output" - assert_in "X11 Model: pc105" "$output" - assert_in "X11 Variant: intl" "$output" - assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output" - - assert_in "XKBLAYOUT=us" "$vc" - assert_in "XKBMODEL=pc105" "$vc" - assert_in "XKBVARIANT=intl" "$vc" - assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc" - elif [[ "$i" =~ ^us-.* ]]; then - assert_in "X11 Layout: us" "$output" - assert_in "X11 Model: microsoftpro" "$output" - assert_in "X11 Variant:" "$output" - assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output" - - assert_in "XKBLAYOUT=us" "$vc" - assert_in "XKBMODEL=microsoftpro" "$vc" - assert_in "XKBVARIANT=" "$vc" - assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc" - fi - done - - # gets along without config file - systemctl stop systemd-localed.service - wait_vconsole_setup - rm -f /etc/vconsole.conf - assert_in "VC Keymap: .unset." "$(localectl)" -} - -testcase_x11_keymap() { - local output - - if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then - echo "No x11 keymap installed, skipping test." - return - fi - - backup_keymap - trap restore_keymap RETURN - - # should activate daemon and work - assert_in "X11 Layout:" "$(localectl)" - - # set x11 keymap (layout, model, variant, options) - assert_rc 0 localectl set-x11-keymap us pc105+inet intl terminate:ctrl_alt_bksp - - if [[ -f /etc/default/keyboard ]]; then - assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us -XKBMODEL=pc105+inet -XKBVARIANT=intl -XKBOPTIONS=terminate:ctrl_alt_bksp" - else - output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) - assert_in 'Option "XkbLayout" "us"' "$output" - assert_in 'Option "XkbModel" "pc105\+inet"' "$output" - assert_in 'Option "XkbVariant" "intl"' "$output" - assert_in 'Option "XkbOptions" "terminate:ctrl_alt_bksp"' "$output" - - output=$(cat /etc/vconsole.conf) - assert_in 'XKBLAYOUT=us' "$output" - assert_in 'XKBMODEL=pc105\+inet' "$output" - assert_in 'XKBVARIANT=intl' "$output" - assert_in 'XKBOPTIONS=terminate:ctrl_alt_bksp' "$output" - fi - - output=$(localectl) - assert_in "X11 Layout: us" "$output" - assert_in "X11 Model: pc105\+inet" "$output" - assert_in "X11 Variant: intl" "$output" - assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output" - - # Debian/Ubuntu patch is buggy, unspecified settings are not cleared - rm -f /etc/default/keyboard - - # set x11 keymap (layout, model, variant) - assert_rc 0 localectl set-x11-keymap us pc105+inet intl - - if [[ -f /etc/default/keyboard ]]; then - assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us -XKBMODEL=pc105+inet -XKBVARIANT=intl" - else - output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) - assert_in 'Option "XkbLayout" "us"' "$output" - assert_in 'Option "XkbModel" "pc105\+inet"' "$output" - assert_in 'Option "XkbVariant" "intl"' "$output" - assert_not_in 'Option "XkbOptions"' "$output" - - output=$(cat /etc/vconsole.conf) - assert_in 'XKBLAYOUT=us' "$output" - assert_in 'XKBMODEL=pc105\+inet' "$output" - assert_in 'XKBVARIANT=intl' "$output" - assert_not_in 'XKBOPTIONS' "$output" - fi - - output=$(localectl) - assert_in "X11 Layout: us" "$output" - assert_in "X11 Model: pc105\+inet" "$output" - assert_in "X11 Variant: intl" "$output" - assert_not_in "X11 Options:" "$output" - - # Debian/Ubuntu patch is buggy, unspecified settings are not cleared - rm -f /etc/default/keyboard - - # set x11 keymap (layout, model) - assert_rc 0 localectl set-x11-keymap us pc105+inet - - if [[ -f /etc/default/keyboard ]]; then - assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us -XKBMODEL=pc105+inet" - else - output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) - assert_in 'Option "XkbLayout" "us"' "$output" - assert_in 'Option "XkbModel" "pc105\+inet"' "$output" - assert_not_in 'Option "XkbVariant"' "$output" - assert_not_in 'Option "XkbOptions"' "$output" - - output=$(cat /etc/vconsole.conf) - assert_in 'XKBLAYOUT=us' "$output" - assert_in 'XKBMODEL=pc105\+inet' "$output" - assert_not_in 'XKBVARIANT' "$output" - assert_not_in 'XKBOPTIONS' "$output" - fi - - output=$(localectl) - assert_in "X11 Layout: us" "$output" - assert_in "X11 Model: pc105\+inet" "$output" - assert_not_in "X11 Variant:" "$output" - assert_not_in "X11 Options:" "$output" - - # Debian/Ubuntu patch is buggy, unspecified settings are not cleared - rm -f /etc/default/keyboard - - # set x11 keymap (layout) - assert_rc 0 localectl set-x11-keymap us - - if [[ -f /etc/default/keyboard ]]; then - assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us" - else - output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) - assert_in 'Option "XkbLayout" "us"' "$output" - assert_not_in 'Option "XkbModel"' "$output" - assert_not_in 'Option "XkbVariant"' "$output" - assert_not_in 'Option "XkbOptions"' "$output" - - output=$(cat /etc/vconsole.conf) - assert_in 'XKBLAYOUT=us' "$output" - assert_not_in 'XKBMODEL' "$output" - assert_not_in 'XKBVARIANT' "$output" - assert_not_in 'XKBOPTIONS' "$output" - fi - - output=$(localectl) - assert_in "X11 Layout: us" "$output" - assert_not_in "X11 Model:" "$output" - assert_not_in "X11 Variant:" "$output" - assert_not_in "X11 Options:" "$output" - - # gets along without config file - systemctl stop systemd-localed.service - rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard - output=$(localectl) - assert_in "X11 Layout: .unset." "$output" - assert_not_in "X11 Model:" "$output" - assert_not_in "X11 Variant:" "$output" - assert_not_in "X11 Options:" "$output" -} - -testcase_convert() { - if [[ -z "$(localectl list-keymaps)" ]]; then - echo "No vconsole keymap installed, skipping test." - return - fi - - if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then - echo "No x11 keymap installed, skipping test." - return - fi - - backup_keymap - trap restore_keymap RETURN - - # clear previous settings - systemctl stop systemd-localed.service - wait_vconsole_setup - rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard - - # set VC keymap without conversion - assert_rc 0 localectl --no-convert set-keymap us - output=$(localectl) - - # check VC keymap - vc=$(cat /etc/vconsole.conf) - assert_in "KEYMAP=us" "$vc" - assert_in "VC Keymap: us" "$output" - - # check VC -> X11 keymap conversion (nothing set) - assert_in "X11 Layout: .unset." "$output" - assert_not_in "X11 Model:" "$output" - assert_not_in "X11 Variant:" "$output" - assert_not_in "X11 Options:" "$output" - - assert_not_in "XKBLAYOUT=" "$vc" - assert_not_in "XKBMODEL=" "$vc" - assert_not_in "XKBVARIANT=" "$vc" - assert_not_in "XKBOPTIONS=" "$vc" - - # set VC keymap with conversion - assert_rc 0 localectl set-keymap us - output=$(localectl) - - # check VC keymap - vc=$(cat /etc/vconsole.conf) - assert_in "KEYMAP=us" "$vc" - assert_in "VC Keymap: us" "$output" - - # check VC -> X11 keymap conversion - assert_in "X11 Layout: us" "$output" - assert_in "X11 Model: pc105\+inet" "$output" - assert_not_in "X11 Variant:" "$output" - assert_in "X11 Options: terminate:ctrl_alt_bksp" "$output" - - assert_in "XKBLAYOUT=us" "$vc" - assert_in "XKBMODEL=pc105\+inet" "$vc" - assert_not_in "XKBVARIANT" "$vc" - assert_in "XKBOPTIONS=terminate:ctrl_alt_bksp" "$vc" - - # clear previous settings - systemctl stop systemd-localed.service - wait_vconsole_setup - rm -f /etc/vconsole.conf /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard - - # set x11 keymap (layout) without conversion - assert_rc 0 localectl --no-convert set-x11-keymap us - - assert_not_in "KEYMAP=" "$(cat /etc/vconsole.conf)" - assert_in "VC Keymap: .unset." "$(localectl)" - - if [[ -f /etc/default/keyboard ]]; then - assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us" - else - output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) - assert_in 'Option "XkbLayout" "us"' "$output" - assert_not_in 'Option "XkbModel"' "$output" - assert_not_in 'Option "XkbVariant"' "$output" - assert_not_in 'Option "XkbOptions"' "$output" - - output=$(cat /etc/vconsole.conf) - assert_in 'XKBLAYOUT=us' "$output" - assert_not_in 'XKBMODEL=' "$output" - assert_not_in 'XKBVARIANT=' "$output" - assert_not_in 'XKBOPTIONS=' "$output" - fi - - output=$(localectl) - assert_in "X11 Layout: us" "$output" - assert_not_in "X11 Model:" "$output" - assert_not_in "X11 Variant:" "$output" - assert_not_in "X11 Options:" "$output" - - # set x11 keymap (layout, model) with conversion - assert_rc 0 localectl set-x11-keymap us - - assert_in "KEYMAP=us" "$(cat /etc/vconsole.conf)" - assert_in "VC Keymap: us" "$(localectl)" - - if [[ -f /etc/default/keyboard ]]; then - assert_eq "$(cat /etc/default/keyboard)" "XKBLAYOUT=us" - else - output=$(cat /etc/X11/xorg.conf.d/00-keyboard.conf) - assert_in 'Option "XkbLayout" "us"' "$output" - assert_not_in 'Option "XkbModel"' "$output" - assert_not_in 'Option "XkbVariant"' "$output" - assert_not_in 'Option "XkbOptions"' "$output" - - output=$(cat /etc/vconsole.conf) - assert_in 'XKBLAYOUT=us' "$output" - assert_not_in 'XKBMODEL=' "$output" - assert_not_in 'XKBVARIANT=' "$output" - assert_not_in 'XKBOPTIONS=' "$output" - fi - - output=$(localectl) - assert_in "X11 Layout: us" "$output" - assert_not_in "X11 Model:" "$output" - assert_not_in "X11 Variant:" "$output" - assert_not_in "X11 Options:" "$output" -} - -testcase_validate() { - if [[ -z "$(localectl list-keymaps)" ]]; then - echo "No vconsole keymap installed, skipping test." - return - fi - - if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then - echo "No x11 keymap installed, skipping test." - return - fi - - backup_keymap - trap restore_keymap RETURN - - # clear previous settings - systemctl stop systemd-localed.service - wait_vconsole_setup - rm -f /etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard - - # create invalid configs - cat >/etc/vconsole.conf </etc/vconsole.conf </etc/vconsole.conf </dev/null; then - echo "No locale-gen support, skipping test." - return 0 - fi - - [[ -e /etc/locale.gen ]] && cp -f /etc/locale.gen /tmp/locale.gen.bak - trap locale_gen_cleanup RETURN - # Overmount the existing locale-gen database with an empty directory - # to force it to regenerate locales - mount -t tmpfs tmpfs /usr/lib/locale - - { - echo -e "en_US.UTF-8 UTF-8" - echo -e " en_US.UTF-8 UTF-8" - echo -e "\ten_US.UTF-8 UTF-8" - echo -e " \t en_US.UTF-8 UTF-8 \t" - } >/etc/locale.gen - - localectl set-locale de_DE.UTF-8 - localectl set-locale en_US.UTF-8 -} - -# Make sure the content of kbd-model-map is the one that the tests expect -# regardless of the version installed on the distro where the testsuite is -# running on. -export SYSTEMD_KBD_MODEL_MAP=/usr/lib/systemd/tests/testdata/test-keymap-util/kbd-model-map - -enable_debug -run_testcases - -touch /testok diff --git a/test/units/testsuite-74.battery-check.sh b/test/units/testsuite-74.battery-check.sh deleted file mode 100755 index 52a92b8..0000000 --- a/test/units/testsuite-74.battery-check.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -/usr/lib/systemd/systemd-battery-check --help -/usr/lib/systemd/systemd-battery-check --version - -/usr/lib/systemd/systemd-battery-check || : diff --git a/test/units/testsuite-74.bootctl.sh b/test/units/testsuite-74.bootctl.sh deleted file mode 100755 index 4be7bfd..0000000 --- a/test/units/testsuite-74.bootctl.sh +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -if systemd-detect-virt --quiet --container; then - echo "running on container, skipping." - exit 0 -fi - -if ! command -v bootctl >/dev/null; then - echo "bootctl not found, skipping." - exit 0 -fi - -if [[ ! -d /usr/lib/systemd/boot/efi ]]; then - echo "sd-boot is not installed, skipping." - exit 0 -fi - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -# shellcheck source=test/units/test-control.sh -. "$(dirname "$0")"/test-control.sh - -basic_tests() { - bootctl "$@" --help - bootctl "$@" --version - - bootctl "$@" install --make-entry-directory=yes - bootctl "$@" remove --make-entry-directory=yes - - bootctl "$@" install --all-architectures - bootctl "$@" remove --all-architectures - - bootctl "$@" install --make-entry-directory=yes --all-architectures - bootctl "$@" remove --make-entry-directory=yes --all-architectures - - bootctl "$@" install - (! bootctl "$@" update) - bootctl "$@" update --graceful - - bootctl "$@" is-installed - bootctl "$@" is-installed --graceful - bootctl "$@" random-seed - - bootctl "$@" - bootctl "$@" status - bootctl "$@" status --quiet - bootctl "$@" list - bootctl "$@" list --quiet - bootctl "$@" list --json=short - bootctl "$@" list --json=pretty - - bootctl "$@" remove - (! bootctl "$@" is-installed) - (! bootctl "$@" is-installed --graceful) -} - -testcase_bootctl_basic() { - assert_eq "$(bootctl --print-esp-path)" "/efi" - assert_eq "$(bootctl --print-boot-path)" "/boot" - bootctl --print-root-device - - basic_tests -} - -cleanup_image() ( - set +e - - if [[ -z "${IMAGE_DIR:-}" ]]; then - return 0 - fi - - umount "${IMAGE_DIR}/root" - - if [[ -n "${LOOPDEV:-}" ]]; then - losetup -d "${LOOPDEV}" - unset LOOPDEV - fi - - udevadm settle - - rm -rf "${IMAGE_DIR}" - unset IMAGE_DIR - - return 0 -) - -testcase_bootctl_image() { - IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)" - trap cleanup_image RETURN - - truncate -s 256m "${IMAGE_DIR}/image" - - cat >"${IMAGE_DIR}/partscript" </dev/null; then - echo "mdadm not found, skipping." - return 0 - fi - - if ! command -v mkfs.btrfs >/dev/null; then - echo "mkfs.btrfs not found, skipping." - return 0 - fi - - IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)" - trap cleanup_raid RETURN - - truncate -s 256m "${IMAGE_DIR}/image1" - truncate -s 256m "${IMAGE_DIR}/image2" - - cat >"${IMAGE_DIR}/partscript" <"${MAKE_DUMP_SCRIPT:?}" <<\EOF -#!/bin/bash -ex - -bin="${1:?}" -sig="${2:?}" - -ulimit -c unlimited -"$bin" infinity & -pid=$! -# Sync with the "fake" binary, so we kill it once it's fully forked off, -# otherwise we might kill it during fork and kernel would then report -# "wrong" binary name (i.e. $MAKE_DUMP_SCRIPT instead of $CORE_TEST_BIN). -# In this case, wait until the "fake" binary (sleep in this case) enters -# the "interruptible sleep" state, at which point it should be ready -# to be sacrificed. -for _ in {0..9}; do - read -ra self_stat <"/proc/$pid/stat" - [[ "${self_stat[2]}" == S ]] && break - sleep .5 -done -kill -s "$sig" "$pid" -# This should always fail -! wait "$pid" -EOF -chmod +x "$MAKE_DUMP_SCRIPT" - -# Privileged stuff -[[ "$(id -u)" -eq 0 ]] -# Trigger a couple of coredumps -"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP" -"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT" -# In the tests we store the coredumps in journals, so let's generate a couple -# with Storage=external as well -mkdir -p /run/systemd/coredump.conf.d/ -printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf -"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP" -"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT" -rm -fv /run/systemd/coredump.conf.d/99-external.conf -# Wait a bit for the coredumps to get processed -timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_BIN | wc -l) -lt 4 ]]; do sleep 1; done" - -# Make sure we can forward crashes back to containers -CONTAINER="testsuite-74-container" - -mkdir -p "/var/lib/machines/$CONTAINER" -mkdir -p "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d" -# Bind-mounting /etc into the container kinda defeats the purpose of --volatile=, -# but we need the ASan-related overrides scattered across /etc -cat > "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d/override.conf" << EOF -[Service] -ExecStart= -ExecStart=systemd-nspawn --quiet --link-journal=try-guest --keep-unit --machine=%i --boot \ - --volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id -EOF -systemctl daemon-reload - -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" - - [[ "$(systemd-run -M "$CONTAINER" -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l)" -eq 0 ]] - machinectl copy-to "$CONTAINER" "$MAKE_DUMP_SCRIPT" - systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGABRT" - systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGTRAP" - # Wait a bit for the coredumps to get processed - timeout 30 bash -c "while [[ \$(systemd-run -M $CONTAINER -q --wait --pipe coredumpctl list -q --no-legend /usr/bin/sleep | wc -l) -lt 2 ]]; do sleep 1; done" -fi - -coredumpctl -SYSTEMD_LOG_LEVEL=debug coredumpctl -coredumpctl --help -coredumpctl --version -coredumpctl --no-pager --no-legend -coredumpctl --all -coredumpctl -1 -coredumpctl -n 1 -coredumpctl --reverse -coredumpctl -F COREDUMP_EXE -coredumpctl --json=short | jq -coredumpctl --json=pretty | jq -coredumpctl --json=off -coredumpctl --root=/ -coredumpctl --directory=/var/log/journal -coredumpctl --file="/var/log/journal/$(/tmp/core.redirected -test -s /tmp/core.redirected -coredumpctl dump -o /tmp/core.output "${CORE_TEST_BIN##*/}" -test -s /tmp/core.output -rm -f /tmp/core.{output,redirected} - -# Unprivileged stuff -# Related issue: https://github.com/systemd/systemd/issues/26912 -UNPRIV_CMD=(systemd-run --user --wait --pipe -M "testuser@.host" --) -# Trigger a couple of coredumps as an unprivileged user -"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP" -"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT" -# In the tests we store the coredumps in journals, so let's generate a couple -# with Storage=external as well -mkdir -p /run/systemd/coredump.conf.d/ -printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf -"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP" -"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT" -rm -fv /run/systemd/coredump.conf.d/99-external.conf -# Wait a bit for the coredumps to get processed -timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_UNPRIV_BIN | wc -l) -lt 4 ]]; do sleep 1; done" - -# root should see coredumps from both binaries -coredumpctl info "$CORE_TEST_UNPRIV_BIN" -coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}" -# The test user should see only their own coredumps -"${UNPRIV_CMD[@]}" coredumpctl -"${UNPRIV_CMD[@]}" coredumpctl info "$CORE_TEST_UNPRIV_BIN" -"${UNPRIV_CMD[@]}" coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}" -(! "${UNPRIV_CMD[@]}" coredumpctl info --all "$CORE_TEST_BIN") -(! "${UNPRIV_CMD[@]}" coredumpctl info --all "${CORE_TEST_BIN##*/}") -# We should have a couple of externally stored coredumps -"${UNPRIV_CMD[@]}" coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out -grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out -rm -f /tmp/coredumpctl.out - -"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true "$CORE_TEST_UNPRIV_BIN" -"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_UNPRIV_BIN##*/}" - -"${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_UNPRIV_BIN" >/tmp/core.redirected -test -s /tmp/core.redirected -"${UNPRIV_CMD[@]}" coredumpctl dump -o /tmp/core.output "${CORE_TEST_UNPRIV_BIN##*/}" -test -s /tmp/core.output -rm -f /tmp/core.{output,redirected} -(! "${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_BIN" >/dev/null) - -# --backtrace mode -# Pass one of the existing journal coredump records to systemd-coredump and -# use our PID as the source to make matching the coredump later easier -# systemd-coredump args: PID UID GID SIGNUM TIMESTAMP CORE_SOFT_RLIMIT HOSTNAME -journalctl -b -n 1 --output=export --output-fields=MESSAGE,COREDUMP COREDUMP_EXE="/usr/bin/test-dump" | - /usr/lib/systemd/systemd-coredump --backtrace $$ 0 0 6 1679509994 12345 mymachine -# Wait a bit for the coredump to get processed -timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $$ | wc -l) -eq 0 ]]; do sleep 1; done" -coredumpctl info "$$" -coredumpctl info COREDUMP_HOSTNAME="mymachine" - -# This used to cause a stack overflow -systemd-run -t --property CoredumpFilter=all ls /tmp -systemd-run -t --property CoredumpFilter=default ls /tmp - -(! coredumpctl --hello-world) -(! coredumpctl -n 0) -(! coredumpctl -n -1) -(! coredumpctl --file=/dev/null) -(! coredumpctl --since=0) -(! coredumpctl --until='') -(! coredumpctl --since=today --until=yesterday) -(! coredumpctl --directory=/ --root=/) -(! coredumpctl --json=foo) -(! coredumpctl -F foo -F bar) -(! coredumpctl list 0) -(! coredumpctl list -- -1) -(! coredumpctl list '') -(! coredumpctl info /../.~=) -(! coredumpctl info '') -(! coredumpctl dump --output=/dev/full "$CORE_TEST_BIN") -(! coredumpctl dump --output=/dev/null --output=/dev/null "$CORE_TEST_BIN") -(! coredumpctl debug --debugger=/bin/false) -(! coredumpctl debug --debugger=/bin/true --debugger-arguments='"') diff --git a/test/units/testsuite-74.delta.sh b/test/units/testsuite-74.delta.sh deleted file mode 100755 index a0e1cb5..0000000 --- a/test/units/testsuite-74.delta.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -at_exit() { - rm -rfv /{run,etc}/systemd/system/delta-test* -} - -trap at_exit EXIT - -# Create a couple of supporting units with overrides -# -# Extended unit -cat >"/run/systemd/system/delta-test-unit-extended.service" <"/run/systemd/system/delta-test-unit-extended.service.d/override.conf" <>/etc/systemd/system/delta-test-unit-overridden.service -# Overridden but equivalent unit -ln -srfv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-equivalent.service -ln -sfv /run/systemd/system/delta-test-unit-extended.service /etc/systemd/system/delta-test-unit-equivalent.service -# Redirected unit -ln -srfv /run/systemd/system/delta-test-unit-extended.service /run/systemd/system/delta-test-unit-redirected.service -ln -sfv /run/systemd/system/delta-test-unit-overidden.service /etc/systemd/system/delta-test-unit-extended.service - -systemctl daemon-reload - -systemd-delta -systemd-delta /run -systemd-delta systemd/system -systemd-delta /run systemd/system /run -systemd-delta /run foo/bar hello/world systemd/system /run -systemd-delta foo/bar -systemd-delta --diff=true -systemd-delta --diff=false - -for type in masked equivalent redirected overridden extended unchanged; do - systemd-delta --type="$type" - systemd-delta --type="$type" /run -done -systemd-delta --type=equivalent,redirected - -(! systemd-delta --diff=foo) -(! systemd-delta --type=foo) -(! systemd-delta --type=equivalent,redirected,foo) diff --git a/test/units/testsuite-74.escape.sh b/test/units/testsuite-74.escape.sh deleted file mode 100755 index e398d40..0000000 --- a/test/units/testsuite-74.escape.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -# Simple wrapper to check both escaping and unescaping of given strings -# Arguments: -# $1 - expected unescaped string -# $2 - expected escaped string -# $3 - optional arguments for systemd-escape -check_escape() { - unescaped="${1?}" - escaped="${2?}" - shift 2 - - assert_eq "$(systemd-escape "$@" -- "$unescaped")" "$escaped" - assert_eq "$(systemd-escape "$@" --unescape -- "$escaped")" "$unescaped" -} - -systemd-escape --help -systemd-escape --version - -check_escape '' '' -check_escape 'hello' 'hello' -check_escape 'hello-world' 'hello\x2dworld' -check_escape '-+ěščřž---🤔' '\x2d\x2b\xc4\x9b\xc5\xa1\xc4\x8d\xc5\x99\xc5\xbe\x2d\x2d\x2d\xf0\x9f\xa4\x94' -check_escape '/this/is/a/path/a b c' '-this-is-a-path-a\x20b\x20c' - -# Multiple strings to escape/unescape -assert_eq "$(systemd-escape 'hello-world' '/dev/loop1' 'template@🐍')" \ - 'hello\x2dworld -dev-loop1 template\x40\xf0\x9f\x90\x8d' -assert_eq "$(systemd-escape --unescape -- 'hello\x2dworld' '-dev-loop1' 'template\x40\xf0\x9f\x90\x8d')" \ - 'hello-world /dev/loop1 template@🐍' - -# --suffix= is not compatible with --unescape -assert_eq "$(systemd-escape --suffix=mount -- '-+ěščřž---🤔')" \ - '\x2d\x2b\xc4\x9b\xc5\xa1\xc4\x8d\xc5\x99\xc5\xbe\x2d\x2d\x2d\xf0\x9f\xa4\x94.mount' -assert_eq "$(systemd-escape --suffix=timer 'this has spaces')" \ - 'this\x20has\x20spaces.timer' -assert_eq "$(systemd-escape --suffix=service 'trailing-spaces ')" \ - 'trailing\x2dspaces\x20\x20.service' -assert_eq "$(systemd-escape --suffix=automount ' leading-spaces')" \ - '\x20\x20\x20leading\x2dspaces.automount' - -# --template= -check_escape 'hello' 'hello@hello.service' --template=hello@.service -check_escape ' what - is _ love? 🤔 ¯\_(ツ)_/¯' \ - 'hello@\x20\x20what\x20\x2d\x20is\x20_\x20love\x3f\x20\xf0\x9f\xa4\x94\x20\xc2\xaf\x5c_\x28\xe3\x83\x84\x29_-\xc2\xaf.service' \ - --template=hello@.service -check_escape '/this/is/where/my/stuff/is/ with spaces though ' \ - 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service' \ - --template=mount-my-stuff@.service -check_escape '/this/is/where/my/stuff/is/ with spaces though ' \ - 'mount-my-stuff@this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service' \ - --template=mount-my-stuff@.service --path - -# --instance (must be used with --unescape) -assert_eq "$(systemd-escape --unescape --instance 'hello@\x20\x20what\x20\x2d\x20is\x20_\x20love\x3f\x20\xf0\x9f\xa4\x94\x20\xc2\xaf\x5c_\x28\xe3\x83\x84\x29_-\xc2\xaf.service')" \ - ' what - is _ love? 🤔 ¯\_(ツ)_/¯' -assert_eq "$(systemd-escape --unescape --instance 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')" \ - '/this/is/where/my/stuff/is/ with spaces though ' -assert_eq "$(systemd-escape --unescape --instance --path 'mount-my-stuff@this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service')" \ - '/this/is/where/my/stuff/is/ with spaces though ' - -# --path, reversible cases -check_escape / '-' --path -check_escape '/hello/world' 'hello-world' --path -check_escape '/mnt/smb/おにぎり' \ - 'mnt-smb-\xe3\x81\x8a\xe3\x81\xab\xe3\x81\x8e\xe3\x82\x8a' \ - --path - -# --path, non-reversible cases -assert_eq "$(systemd-escape --path ///////////////)" '-' -assert_eq "$(systemd-escape --path /..)" '-' -assert_eq "$(systemd-escape --path /../.././../.././)" '-' -assert_eq "$(systemd-escape --path /../.././../.././foo)" 'foo' - -# --mangle -assert_eq "$(systemd-escape --mangle 'hello-world')" 'hello-world.service' -assert_eq "$(systemd-escape --mangle '/mount/this')" 'mount-this.mount' -assert_eq "$(systemd-escape --mangle 'my-service@ 🐱 ')" 'my-service@\x20\xf0\x9f\x90\xb1\x20.service' -assert_eq "$(systemd-escape --mangle '/dev/disk/by-emoji/🍎')" 'dev-disk-by\x2demoji-\xf0\x9f\x8d\x8e.device' -assert_eq "$(systemd-escape --mangle 'daily-existential-crisis .timer')" 'daily-existential-crisis\x20.timer' -assert_eq "$(systemd-escape --mangle 'trailing-whitespace.mount ')" 'trailing-whitespace.mount\x20.service' - -(! systemd-escape) -(! systemd-escape --suffix='' hello) -(! systemd-escape --suffix=invalid hello) -(! systemd-escape --suffix=mount --template=hello@.service hello) -(! systemd-escape --suffix=mount --mangle) -(! systemd-escape --template='') -(! systemd-escape --template=@) -(! systemd-escape --template='hello@.service' '') -(! systemd-escape --unescape --template='hello@.service' '@hello.service') -(! systemd-escape --unescape --template='hello@.service' 'hello@.service') -(! systemd-escape --mangle --template=hello@.service hello) -(! systemd-escape --instance 'hello@hello.service') -(! systemd-escape --instance --template=hello@.service 'hello@hello.service') -(! systemd-escape --unescape --instance --path 'mount-my-stuff@-this-is-where-my-stuff-is-\x20with\x20spaces\x20though\x20.service') -(! systemd-escape --path '/../hello/..') -(! systemd-escape --path '.') -(! systemd-escape --path '..') -(! systemd-escape --path "$(set +x; printf '%0.sa' {0..256})") -(! systemd-escape --unescape --path '') -(! systemd-escape --mangle '') diff --git a/test/units/testsuite-74.firstboot.sh b/test/units/testsuite-74.firstboot.sh deleted file mode 100755 index be08575..0000000 --- a/test/units/testsuite-74.firstboot.sh +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -if ! command -v systemd-firstboot >/dev/null; then - echo "systemd-firstboot not found, skipping the test" - exit 0 -fi - -at_exit() { - if [[ -n "${ROOT:-}" ]]; then - ls -lR "$ROOT" - rm -fr "$ROOT" - fi -} - -trap at_exit EXIT - -# Generated via `mkpasswd -m sha-512 -S foobarsalt password1` -# shellcheck disable=SC2016 -ROOT_HASHED_PASSWORD1='$6$foobarsalt$YbwdaATX6IsFxvWbY3QcZj2gB31R/LFRFrjlFrJtTTqFtSfn4dfOAg/km2k4Sl.a2g7LOYDo31wMTaEsCo9j41' -# Generated via `mkpasswd -m sha-512 -S foobarsalt password2` -# shellcheck disable=SC2016 -ROOT_HASHED_PASSWORD2='$6$foobarsalt$q.P2932zYMLbKnjFwIxPI8y3iuxeuJ2BgE372LcZMMnj3Gcg/9mJg2LPKUl.ha0TG/.fRNNnRQcLfzM0SNot3.' - -# Debian and Ubuntu use /etc/default/locale instead of /etc/locale.conf. Make -# sure we use the appropriate path for locale configuration. -LOCALE_PATH="/etc/locale.conf" -[ -e "$LOCALE_PATH" ] || LOCALE_PATH="/etc/default/locale" -[ -e "$LOCALE_PATH" ] || systemd-firstboot --locale=C.UTF-8 - -# Create a minimal root so we don't modify the testbed -ROOT=test-root -mkdir -p "$ROOT/bin" -# Dummy shell for --root-shell= -touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell" - -systemd-firstboot --root="$ROOT" --locale=foo -grep -q "LANG=foo" "$ROOT$LOCALE_PATH" -rm -fv "$ROOT$LOCALE_PATH" -systemd-firstboot --root="$ROOT" --locale-messages=foo -grep -q "LC_MESSAGES=foo" "$ROOT$LOCALE_PATH" -rm -fv "$ROOT$LOCALE_PATH" -systemd-firstboot --root="$ROOT" --locale=foo --locale-messages=bar -grep -q "LANG=foo" "$ROOT$LOCALE_PATH" -grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH" - -systemd-firstboot --root="$ROOT" --keymap=foo -grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf" - -systemd-firstboot --root="$ROOT" --timezone=Europe/Berlin -readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin" - -systemd-firstboot --root="$ROOT" --hostname "foobar" -grep -q "foobar" "$ROOT/etc/hostname" - -systemd-firstboot --root="$ROOT" --machine-id=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id" - -rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" -systemd-firstboot --root="$ROOT" --root-password=foo -grep -q "^root:x:0:0:" "$ROOT/etc/passwd" -grep -q "^root:" "$ROOT/etc/shadow" -rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" -echo "foo" >root.passwd -systemd-firstboot --root="$ROOT" --root-password-file=root.passwd -grep -q "^root:x:0:0:" "$ROOT/etc/passwd" -grep -q "^root:" "$ROOT/etc/shadow" -rm -fv "$ROOT/etc/passwd" "$ROOT/etc/shadow" root.passwd -# Set the shell together with the password, as firstboot won't touch -# /etc/passwd if it already exists -systemd-firstboot --root="$ROOT" --root-password-hashed="$ROOT_HASHED_PASSWORD1" --root-shell=/bin/fooshell -grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" -grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow" - -systemd-firstboot --root="$ROOT" --kernel-command-line="foo.bar=42" -grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline" - -# Configs should not get overwritten if they exist unless --force is used -systemd-firstboot --root="$ROOT" \ - --locale=locale-overwrite \ - --locale-messages=messages-overwrite \ - --keymap=keymap-overwrite \ - --timezone=CET \ - --hostname=hostname-overwrite \ - --machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \ - --root-password-hashed="$ROOT_HASHED_PASSWORD2" \ - --root-shell=/bin/barshell \ - --kernel-command-line="hello.world=0" -grep -q "LANG=foo" "$ROOT$LOCALE_PATH" -grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH" -grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf" -readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$" -grep -q "foobar" "$ROOT/etc/hostname" -grep -q "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "$ROOT/etc/machine-id" -grep -q "^root:x:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" -grep -q "^root:$ROOT_HASHED_PASSWORD1:" "$ROOT/etc/shadow" -grep -q "foo.bar=42" "$ROOT/etc/kernel/cmdline" - -# The same thing, but now with --force -systemd-firstboot --root="$ROOT" --force \ - --locale=locale-overwrite \ - --locale-messages=messages-overwrite \ - --keymap=keymap-overwrite \ - --timezone=CET \ - --hostname=hostname-overwrite \ - --machine-id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \ - --root-password-hashed="$ROOT_HASHED_PASSWORD2" \ - --root-shell=/bin/barshell \ - --kernel-command-line="hello.world=0" -grep -q "LANG=locale-overwrite" "$ROOT$LOCALE_PATH" -grep -q "LC_MESSAGES=messages-overwrite" "$ROOT$LOCALE_PATH" -grep -q "KEYMAP=keymap-overwrite" "$ROOT/etc/vconsole.conf" -readlink "$ROOT/etc/localtime" | grep -q "/CET$" -grep -q "hostname-overwrite" "$ROOT/etc/hostname" -grep -q "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" "$ROOT/etc/machine-id" -grep -q "^root:x:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd" -grep -q "^root:$ROOT_HASHED_PASSWORD2:" "$ROOT/etc/shadow" -grep -q "hello.world=0" "$ROOT/etc/kernel/cmdline" - -# Test that --reset removes all files configured by firstboot. -systemd-firstboot --root="$ROOT" --reset -[[ ! -e "$ROOT/etc/locale.conf" ]] -[[ ! -e "$ROOT/etc/vconsole.conf" ]] -[[ ! -e "$ROOT/etc/localtime" ]] -[[ ! -e "$ROOT/etc/hostname" ]] -[[ ! -e "$ROOT/etc/machine-id" ]] -[[ ! -e "$ROOT/etc/kernel/cmdline" ]] - -# --copy-* options -rm -fr "$ROOT" -mkdir "$ROOT" -# Copy everything at once (--copy) -systemd-firstboot --root="$ROOT" --copy -diff $LOCALE_PATH "$ROOT$LOCALE_PATH" -diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd") -diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow") -[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf" -[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime") -rm -fr "$ROOT" -mkdir "$ROOT" -# Copy everything at once, but now by using separate switches -systemd-firstboot --root="$ROOT" --copy-locale --copy-keymap --copy-timezone --copy-root-password --copy-root-shell -diff $LOCALE_PATH "$ROOT$LOCALE_PATH" -diff <(awk -F: '/^root/ { print $7; }' /etc/passwd) <(awk -F: '/^root/ { print $7; }' "$ROOT/etc/passwd") -diff <(awk -F: '/^root/ { print $2; }' /etc/shadow) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow") -[[ -e /etc/vconsole.conf ]] && diff /etc/vconsole.conf "$ROOT/etc/vconsole.conf" -[[ -e /etc/localtime ]] && diff <(readlink /etc/localtime) <(readlink "$ROOT/etc/localtime") - -# --prompt-* options -rm -fr "$ROOT" -mkdir -p "$ROOT/bin" -touch "$ROOT/bin/fooshell" "$ROOT/bin/barshell" -# Temporarily disable pipefail to avoid `echo: write error: Broken pipe -set +o pipefail -# We can do only limited testing here, since it's all an interactive stuff, -# so --prompt and --prompt-root-password are skipped on purpose -echo -ne "\nfoo\nbar\n" | systemd-firstboot --root="$ROOT" --prompt-locale -grep -q "LANG=foo" "$ROOT$LOCALE_PATH" -grep -q "LC_MESSAGES=bar" "$ROOT$LOCALE_PATH" -# systemd-firstboot in prompt-keymap mode requires keymaps to be installed so -# it can present them as a list to the user. As Debian does not ship/provide -# compatible keymaps (from the kbd package), skip this test if the keymaps are -# missing. -if [ -d "/usr/share/keymaps/" ] || [ -d "/usr/share/kbd/keymaps/" ] || [ -d "/usr/lib/kbd/keymaps/" ] ; then - echo -ne "\nfoo\n" | systemd-firstboot --root="$ROOT" --prompt-keymap - grep -q "KEYMAP=foo" "$ROOT/etc/vconsole.conf" -fi -echo -ne "\nEurope/Berlin\n" | systemd-firstboot --root="$ROOT" --prompt-timezone -readlink "$ROOT/etc/localtime" | grep -q "Europe/Berlin$" -echo -ne "\nfoobar\n" | systemd-firstboot --root="$ROOT" --prompt-hostname -grep -q "foobar" "$ROOT/etc/hostname" -echo -ne "\n/bin/fooshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell -grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" -# Existing files should not get overwritten -echo -ne "\n/bin/barshell\n" | systemd-firstboot --root="$ROOT" --prompt-root-shell -grep -q "^root:.*:0:0:.*:/bin/fooshell$" "$ROOT/etc/passwd" -# Now without the welcome screen but with force -echo -ne "/bin/barshell\n" | systemd-firstboot --root="$ROOT" --force --prompt-root-shell --welcome=no -grep -q "^root:.*:0:0:.*:/bin/barshell$" "$ROOT/etc/passwd" -# Re-enable pipefail -set -o pipefail - -# Assorted tests -rm -fr "$ROOT" -mkdir "$ROOT" - -systemd-firstboot --root="$ROOT" --setup-machine-id -grep -E "[a-z0-9]{32}" "$ROOT/etc/machine-id" - -systemd-firstboot --root="$ROOT" --delete-root-password -diff <(echo) <(awk -F: '/^root/ { print $2; }' "$ROOT/etc/shadow") - -(! systemd-firstboot --root="$ROOT" --root-shell=/bin/nonexistentshell) -(! systemd-firstboot --root="$ROOT" --machine-id=invalidmachineid) -(! systemd-firstboot --root="$ROOT" --timezone=Foo/Bar) diff --git a/test/units/testsuite-74.id128.sh b/test/units/testsuite-74.id128.sh deleted file mode 100755 index c1b80d6..0000000 --- a/test/units/testsuite-74.id128.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -systemd-id128 --help -systemd-id128 help -systemd-id128 show -systemd-id128 show --pretty | tail -systemd-id128 show --value | tail -systemd-id128 show 4f68bce3e8cd4db196e7fbcaf984b709 # root-x86-64 -systemd-id128 show --pretty 4f68bce3e8cd4db196e7fbcaf984b709 -systemd-id128 show root-x86-64 -systemd-id128 show --pretty root-x86-64 -[[ "$(systemd-id128 show 4f68bce3e8cd4db196e7fbcaf984b709)" = "$(systemd-id128 show root-x86-64)" ]] -[[ "$(systemd-id128 show 4f68bce3-e8cd-4db1-96e7-fbcaf984b709)" = "$(systemd-id128 show root-x86-64)" ]] - -systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709 -systemd-id128 show --pretty root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709 -[[ "$(systemd-id128 show root-x86-64 --app-specific=4f68bce3e8cd4db196e7fbcaf984b709 -P)" = "8ee5535e7cb14c249e1d28b8dfbb939c" ]] - -[[ "$(systemd-id128 new | wc -c)" -eq 33 ]] -systemd-id128 new -p -systemd-id128 new -u -systemd-id128 new -a 4f68bce3e8cd4db196e7fbcaf984b709 - -systemd-id128 machine-id -systemd-id128 machine-id --pretty -systemd-id128 machine-id --uuid -systemd-id128 machine-id --app-specific=4f68bce3e8cd4db196e7fbcaf984b709 -assert_eq "$(systemd-id128 machine-id)" "$(>"$root/etc/machine-id" - machine_id="$(systemd-machine-id-setup --print --root "$root")" - diff <(echo "$machine_id") "$root/etc/machine-id" -} - -testcase_transient() { - local root transient_id committed_id - - root="$(mktemp -d)" - trap "root_cleanup $root" RETURN - root_mock "$root" - - systemd-machine-id-setup --print --root "$root" - echo abc >>"$root/etc/machine-id" - mount -o remount,ro "$root" - mount -t tmpfs tmpfs "$root/run" - transient_id="$(systemd-machine-id-setup --print --root "$root")" - mount -o remount,rw "$root" - committed_id="$(systemd-machine-id-setup --print --commit --root "$root")" - [[ "$transient_id" == "$committed_id" ]] - diff "$root/etc/machine-id" "$root/run/machine-id" -} - -# Check if we correctly processed the invalid machine ID we set up in the respective -# test.sh file -systemctl --state=failed --no-legend --no-pager >/failed -test ! -s /failed - -run_testcases diff --git a/test/units/testsuite-74.modules-load.sh b/test/units/testsuite-74.modules-load.sh deleted file mode 100755 index 3d00e07..0000000 --- a/test/units/testsuite-74.modules-load.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -MODULES_LOAD_BIN="/usr/lib/systemd/systemd-modules-load" -CONFIG_FILE="/run/modules-load.d/99-test.conf" - -at_exit() { - rm -rfv "${CONFIG_FILE:?}" -} - -trap at_exit EXIT - -if systemd-detect-virt -cq; then - echo "Running in a container, skipping the systemd-modules-load test..." - exit 0 -fi - -# Check if we have required kernel modules -modprobe --all --resolve-alias loop dummy - -mkdir -p /run/modules-load.d/ - -"$MODULES_LOAD_BIN" -"$MODULES_LOAD_BIN" --help -"$MODULES_LOAD_BIN" --version - -# Explicit config file -modprobe -v --all --remove loop dummy -printf "loop\ndummy" >"$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" -"$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 -cat >"$CONFIG_FILE" </dev/null; then - echo "Container detected, skipping the test" - exit 0 -fi - -at_exit() { - set +e - - [[ -n "${LOOP:-}" ]] && losetup -d "$LOOP" - [[ -n "${WORK_DIR:-}" ]] && rm -fr "$WORK_DIR" -} - -trap at_exit EXIT - -WORK_DIR="$(mktemp -d)" - -systemd-mount --list -systemd-mount --list --full -systemd-mount --list --no-legend -systemd-mount --list --no-pager -systemd-mount --list --quiet - -# Set up a simple block device for further tests -dd if=/dev/zero of="$WORK_DIR/simple.img" bs=1M count=16 -LOOP="$(losetup --show --find "$WORK_DIR/simple.img")" -mkfs.ext4 -L sd-mount-test "$LOOP" -mkdir "$WORK_DIR/mnt" -mount "$LOOP" "$WORK_DIR/mnt" -touch "$WORK_DIR/mnt/foo.bar" -umount "$LOOP" -(! mountpoint "$WORK_DIR/mnt") - -# Mount with both source and destination set -systemd-mount "$LOOP" "$WORK_DIR/mnt" -systemctl status "$WORK_DIR/mnt" -systemd-mount --list --full -test -e "$WORK_DIR/mnt/foo.bar" -systemd-umount "$WORK_DIR/mnt" -# Same thing, but with explicitly specified filesystem and disabled filesystem check -systemd-mount --type=ext4 --fsck=no --collect "$LOOP" "$WORK_DIR/mnt" -systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").mount" -test -e "$WORK_DIR/mnt/foo.bar" -systemd-mount --umount "$LOOP" -# Discover additional metadata (unit description should now contain filesystem label) -systemd-mount --no-ask-password --discover "$LOOP" "$WORK_DIR/mnt" -test -e "$WORK_DIR/mnt/foo.bar" -systemctl show -P Description "$WORK_DIR/mnt" | grep -q sd-mount-test -systemd-umount "$WORK_DIR/mnt" -# Set a unit description -systemd-mount --description="Very Important Unit" "$LOOP" "$WORK_DIR/mnt" -test -e "$WORK_DIR/mnt/foo.bar" -systemctl show -P Description "$WORK_DIR/mnt" | grep -q "Very Important Unit" -systemd-umount "$WORK_DIR/mnt" -# Set a property -systemd-mount --property="Description=Foo Bar" "$LOOP" "$WORK_DIR/mnt" -test -e "$WORK_DIR/mnt/foo.bar" -systemctl show -P Description "$WORK_DIR/mnt" | grep -q "Foo Bar" -systemd-umount "$WORK_DIR/mnt" -# Set mount options -systemd-mount --options=ro,x-foo-bar "$LOOP" "$WORK_DIR/mnt" -test -e "$WORK_DIR/mnt/foo.bar" -systemctl show -P Options "$WORK_DIR/mnt" | grep -Eq "(^ro|,ro)" -systemctl show -P Options "$WORK_DIR/mnt" | grep -q "x-foo-bar" -systemd-umount "$WORK_DIR/mnt" - -# Mount with only source set -systemd-mount "$LOOP" -systemctl status /run/media/system/sd-mount-test -systemd-mount --list --full -test -e /run/media/system/sd-mount-test/foo.bar -systemd-umount LABEL=sd-mount-test - -# Automount -systemd-mount --automount=yes "$LOOP" "$WORK_DIR/mnt" -systemd-mount --list --full -systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount" -[[ "$(systemctl show -P ActiveState "$WORK_DIR/mnt")" == inactive ]] -test -e "$WORK_DIR/mnt/foo.bar" -systemctl status "$WORK_DIR/mnt" -systemd-umount "$WORK_DIR/mnt" -# Automount + automount-specific property -systemd-mount -A --automount-property="Description=Bar Baz" "$LOOP" "$WORK_DIR/mnt" -systemctl show -P Description "$(systemd-escape --path "$WORK_DIR/mnt").automount" | grep -q "Bar Baz" -test -e "$WORK_DIR/mnt/foo.bar" -# Call --umount via --machine=, first with a relative path (bad) and then with -# an absolute one (good) -(! systemd-umount --machine=.host "$(realpath --relative-to=. "$WORK_DIR/mnt")") -systemd-umount --machine=.host "$WORK_DIR/mnt" - -# ext4 doesn't support uid=/gid= -(! systemd-mount -t ext4 --owner=testuser "$LOOP" "$WORK_DIR/mnt") - -# Automount + --bind-device -systemd-mount --automount=yes --bind-device --timeout-idle-sec=1 "$LOOP" "$WORK_DIR/mnt" -systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount" -# Trigger the automount -test -e "$WORK_DIR/mnt/foo.bar" -# Wait until it's idle again -sleep 1.5 -# Safety net for slower/overloaded systems -timeout 10s bash -c "while systemctl is-active -q $WORK_DIR/mnt; do sleep .2; done" -systemctl status "$(systemd-escape --path "$WORK_DIR/mnt").automount" -# Disassemble the underlying block device -losetup -d "$LOOP" -unset LOOP -# The automount unit should disappear once the underlying blockdev is gone -timeout 10s bash -c "while systemctl status '$(systemd-escape --path "$WORK_DIR/mnt".automount)'; do sleep .2; done" - -# Mount a disk image -systemd-mount --discover "$WORK_DIR/simple.img" -# We can access files in the image even if the loopback block device is not initialized by udevd. -test -e /run/media/system/simple.img/foo.bar -# systemd-mount --list and systemd-umount require the loopback block device is initialized by udevd. -udevadm settle --timeout 30 -assert_in "/dev/loop.* ext4 +sd-mount-test" "$(systemd-mount --list --full)" -systemd-umount "$WORK_DIR/simple.img" - -# --owner + vfat -# -# Create a vfat image, as ext4 doesn't support uid=/gid= fixating for all -# files/directories -dd if=/dev/zero of="$WORK_DIR/owner-vfat.img" bs=1M count=16 -LOOP="$(losetup --show --find "$WORK_DIR/owner-vfat.img")" -mkfs.vfat -n owner-vfat "$LOOP" -# Mount it and check the UID/GID -[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt")" == "root:root" ]] -systemd-mount --owner=testuser "$LOOP" "$WORK_DIR/mnt" -systemctl status "$WORK_DIR/mnt" -[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt")" == "testuser:testuser" ]] -touch "$WORK_DIR/mnt/hello" -[[ "$(stat -c "%U:%G" "$WORK_DIR/mnt/hello")" == "testuser:testuser" ]] -systemd-umount LABEL=owner-vfat - -# tmpfs -mkdir -p "$WORK_DIR/mnt/foo/bar" -systemd-mount --tmpfs "$WORK_DIR/mnt/foo" -test ! -d "$WORK_DIR/mnt/foo/bar" -touch "$WORK_DIR/mnt/foo/baz" -systemd-umount "$WORK_DIR/mnt/foo" -test -d "$WORK_DIR/mnt/foo/bar" -test ! -e "$WORK_DIR/mnt/foo/baz" diff --git a/test/units/testsuite-74.networkctl.sh b/test/units/testsuite-74.networkctl.sh deleted file mode 100755 index 0a687af..0000000 --- a/test/units/testsuite-74.networkctl.sh +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2016 -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -at_exit() { - systemctl stop systemd-networkd - - if [[ -v NETWORK_NAME && -v NETDEV_NAME && -v LINK_NAME ]]; then - rm -fvr {/usr/lib,/etc}/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 -} - -trap at_exit EXIT - -export NETWORK_NAME="10-networkctl-test-$RANDOM.network" -export NETDEV_NAME="10-networkctl-test-$RANDOM.netdev" -export LINK_NAME="10-networkctl-test-$RANDOM.link" -cat >"/usr/lib/systemd/network/$NETWORK_NAME" <new <"+4" <"/usr/lib/systemd/network/$NETDEV_NAME" <"/usr/lib/systemd/network/$LINK_NAME" <"${USER_DIRS_CONF:?}" <<\EOF -XDG_DESKTOP_DIR="$HOME/my-fancy-desktop" -XDG_INVALID - -XDG_DOWNLOAD_DIR = "$HOME" -XDG_TEMPLATES_DIR="/templates" -# Invalid records -XDG_TEMPLATES_DIR=/not-templates" -XDG_TEMPLATES_DIR="/also-not-teplates -XDG_TEMPLATES_DIR="" -XDG_TEMPLATES_DIR="../" - -XDG_PUBLICSHARE_DIR="$HOME/cat-pictures" -XDG_DOCUMENTS_DIR="$HOME/top/secret/documents" -XDG_MUSIC_DIR="/tmp/vaporwave" -XDG_PICTURES_DIR="$HOME/Pictures" -XDG_VIDEOS_DIR="$HOME/🤔" -EOF - -systemd-path --help -systemd-path --version -systemd-path -systemd-path temporary system-binaries user binfmt - -assert_eq "$(systemd-path system-runtime)" "/run" -assert_eq "$(systemd-path --suffix='' system-runtime)" "/run" -assert_eq "$(systemd-path --suffix='🤔' system-runtime)" "/run/🤔" -assert_eq "$(systemd-path --suffix=hello system-runtime)" "/run/hello" - -# Note for the stuff below: everything defaults to $HOME, only the desktop -# directory defaults to $HOME/Desktop. -# -# Check the user-dirs.dir stuff from above -assert_eq "$(systemd-path user)" "/root" -assert_eq "$(systemd-path user-desktop)" "/root/my-fancy-desktop" -assert_eq "$(systemd-path user-documents)" "/root/top/secret/documents" -assert_eq "$(systemd-path user-download)" "/root" -assert_eq "$(systemd-path user-music)" "/tmp/vaporwave" -assert_eq "$(systemd-path user-pictures)" "/root/Pictures" -assert_eq "$(systemd-path user-public)" "/root/cat-pictures" -assert_eq "$(systemd-path user-templates)" "/templates" -assert_eq "$(systemd-path user-videos)" "/root/🤔" - -# Remove the user-dirs.dir file and check the defaults -rm -fv "$USER_DIRS_CONF" -[[ ! -e "$USER_DIRS_CONF" ]] -assert_eq "$(systemd-path user-desktop)" "/root/Desktop" -for dir in "" documents download music pictures public templates videos; do - assert_eq "$(systemd-path "user${dir:+-$dir}")" "/root" -done - -# sd-path should consider only absolute $HOME -assert_eq "$(HOME=/hello-world systemd-path user)" "/hello-world" -assert_eq "$(HOME=hello-world systemd-path user)" "/root" -assert_eq "$(HOME=/hello systemd-path --suffix=world user)" "/hello/world" -assert_eq "$(HOME=hello systemd-path --suffix=world user)" "/root/world" -# Same with some other env variables -assert_in "/my-config" "$(HOME='' XDG_CONFIG_HOME=/my-config systemd-path search-configuration)" -assert_in "/my-config/foo" "$(HOME='' XDG_CONFIG_HOME=/my-config systemd-path --suffix=foo search-configuration)" -assert_in "/my-home/.config/foo" "$(HOME=/my-home XDG_CONFIG_HOME=my-config systemd-path --suffix=foo search-configuration)" -assert_not_in "my-config" "$(HOME=my-config XDG_CONFIG_HOME=my-config systemd-path search-configuration)" - -(! systemd-path '') -(! systemd-path system-binaries 🤔 user) -(! systemd-path --xyz) diff --git a/test/units/testsuite-74.pstore.sh b/test/units/testsuite-74.pstore.sh deleted file mode 100755 index 9be8066..0000000 --- a/test/units/testsuite-74.pstore.sh +++ /dev/null @@ -1,258 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -systemctl log-level info - -if systemd-detect-virt -cq; then - echo "Running in a container, skipping the systemd-pstore test..." - exit 0 -fi - -DUMMY_DMESG_0="$(mktemp)" -cat >"$DUMMY_DMESG_0" <<\EOF -6,17159,5340096332127,-;usb 1-4: USB disconnect, device number 124 -6,17160,5340109662397,-;input: WH-1000XM3 (AVRCP) as /devices/virtual/input/input293 -6,17161,5343126458360,-;loop0: detected capacity change from 0 to 3145728 -6,17162,5343126766065,-; loop0: p1 p2 -6,17163,5343126815038,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none. -6,17164,5343158037334,-;EXT4-fs (loop0p1): unmounting filesystem. -6,17165,5343158072598,-;loop0: detected capacity change from 0 to 3145728 -6,17166,5343158073563,-; loop0: p1 p2 -6,17167,5343158074325,-; loop0: p1 p2 -6,17168,5343158140859,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none. -6,17169,5343158182977,-;EXT4-fs (loop0p1): unmounting filesystem. -6,17170,5343158700241,-;loop0: detected capacity change from 0 to 3145728 -6,17171,5343158700439,-; loop0: p1 p2 -6,17172,5343158701120,-; loop0: p1 p2 -EOF - -DUMMY_DMESG_1="$(mktemp)" -cat >"$DUMMY_DMESG_1" <<\EOF -Nechť již hříšné saxofony ďáblů rozezvučí síň úděsnými tóny waltzu, tanga a quickstepu. -Příliš žluťoučký kůň úpěl ďábelské ódy. -Zvlášť zákeřný učeň s ďolíčky běží podél zóny úlů. -Vyciď křišťálový nůž, ó učiň úděsné líbivým! -Loď čeří kýlem tůň obzvlášť v Grónské úžině -Ó, náhlý déšť již zvířil prach a čilá laň teď běží s houfcem gazel k úkrytům. -Vypätá dcéra grófa Maxwella s IQ nižším ako kôň núti čeľaď hrýzť hŕbu jabĺk. -Kŕdeľ šťastných ďatľov učí pri ústí Váhu mĺkveho koňa obhrýzať kôru a žrať čerstvé mäso. -Stróż pchnął kość w quiz gędźb vel fax myjń. -Portez ce vieux whisky au juge blond qui fume! -EOF - -file_count() { find "${1:?}" -type f | wc -l; } -file_size() { wc -l <"${1:?}"; } -random_efi_timestamp() { printf "%0.10d" "$((1000000000 + RANDOM))"; } - -# The dmesg- filename contains the backend-type and the Common Platform Error Record, CPER, -# record id, a 64-bit number. -# -# Files are processed in reverse lexigraphical order so as to properly reconstruct original dmesg. - -prepare_efi_logs() { - local file="${1:?}" - local timestamp="${2:?}" - local chunk count filename - - # For the EFI backend, the 3 least significant digits of record id encodes a - # "count" number, the next 2 least significant digits for the dmesg part - # (chunk) number, and the remaining digits as the timestamp. See - # linux/drivers/firmware/efi/efi-pstore.c in efi_pstore_write(). - count="$(file_size "$file")" - chunk=0 - # The sed in the process substitution below just reverses the file - while read -r line; do - filename="$(printf "dmesg-efi-%0.10d%0.2d%0.3d" "$timestamp" "$chunk" "$count")" - echo "$line" >"/sys/fs/pstore/$filename" - chunk=$((chunk + 1)) - done < <(sed '1!G;h;$!d' "$file") - - if [[ "$chunk" -eq 0 ]]; then - echo >&2 "No dmesg-efi files were created" - exit 1 - fi -} - -prepare_erst_logs() { - local file="${1:?}" - local start_id="${2:?}" - local id filename - - # For the ERST backend, the record is a monotonically increasing number, seeded as - # a timestamp. See linux/drivers/acpi/apei/erst.c in erst_writer(). - id="$start_id" - # The sed in the process substitution below just reverses the file - while read -r line; do - filename="$(printf "dmesg-erst-%0.16d" "$id")" - echo "$line" >"/sys/fs/pstore/$filename" - id=$((id + 1)) - done < <(sed '1!G;h;$!d' "$file") - - if [[ "$id" -eq "$start_id" ]]; then - echo >&2 "No dmesg-erst files were created" - exit 1 - fi - - # ID of the last dmesg file will be the ID of the erst subfolder - echo "$((id - 1))" -} - -prepare_pstore_config() { - local storage="${1:?}" - local unlink="${2:?}" - - systemctl stop systemd-pstore - - rm -fr /sys/fs/pstore/* /var/lib/systemd/pstore/* - - mkdir -p /run/systemd/pstore.conf.d - cat >/run/systemd/pstore.conf.d/99-test.conf </run/systemd/system/systemd-pstore.service.d/99-StartLimitInterval.conf </sys/fs/pstore/foo.bar - [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)" - start_pstore - [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]] - [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]] - filename="$(printf "/var/lib/systemd/pstore/%s/%0.3d/dmesg.txt" "$timestamp" "$(file_size "${!dmesg}")")" - diff "${!dmesg}" "$filename" - grep "hello world" "/var/lib/systemd/pstore/foo.bar" - done - # Check that we kept all previous records as well - for timestamp in "${timestamps[@]}"; do - [[ -d "/var/lib/systemd/pstore/$timestamp" ]] - [[ "$(file_count "/var/lib/systemd/pstore/$timestamp/")" -gt 0 ]] - done - - : "Backend: EFI; Storage: journal; Unlink: $unlink" - timestamp="$(random_efi_timestamp)" - prepare_pstore_config "journal" "$unlink" - prepare_efi_logs "$DUMMY_DMESG_0" "$timestamp" - [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)" - start_pstore - [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]] - [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]] - diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d") - - : "Backend: ERST; Storage: external; Unlink: $unlink" - prepare_pstore_config "external" "$unlink" - last_id="$(prepare_erst_logs "$DUMMY_DMESG_0" 0)" - [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)" - start_pstore - [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]] - [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]] - # We always log to journal - diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d") - filename="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id")" - diff "$DUMMY_DMESG_0" "$filename" - - : "Backend: ERST; Storage: external; Unlink: $unlink; multiple dmesg files" - last_ids=() - prepare_pstore_config "external" "$unlink" - for i in {0..9}; do - # Create a name reference to one of the $DUMMY_DMESG_X variables - dmesg="DUMMY_DMESG_$((i % 2))" - last_id="$(prepare_erst_logs "${!dmesg}" "$((i * 100))")" - last_ids+=("$last_id") - # Add one "random" (non-dmesg) file as well - echo "hello world" >/sys/fs/pstore/foo.bar - [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)" - start_pstore - [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]] - [[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]] - filename="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id")" - diff "${!dmesg}" "$filename" - grep "hello world" "/var/lib/systemd/pstore/foo.bar" - done - # Check that we kept all previous records as well - for last_id in "${last_ids[@]}"; do - directory="$(printf "/var/lib/systemd/pstore/%0.16d" "$last_id")" - [[ -d "$directory" ]] - [[ "$(file_count "$directory")" -gt 0 ]] - done - - : "Backend: ERST; Storage: journal; Unlink: $unlink" - prepare_pstore_config "journal" "$unlink" - last_id="$(prepare_erst_logs "$DUMMY_DMESG_0" 0)" - [[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)" - start_pstore - [[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]] - [[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]] - diff "$DUMMY_DMESG_0" <(journalctl -o cat --output-fields=FILE --cursor-file=/tmp/journal.cursor | sed "/^$/d") -done diff --git a/test/units/testsuite-74.run.sh b/test/units/testsuite-74.run.sh deleted file mode 100755 index e894932..0000000 --- a/test/units/testsuite-74.run.sh +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2016 -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -systemd-run --help --no-pager -systemd-run --version -systemd-run --no-ask-password true -systemd-run --no-block --collect true - -export PARENT_FOO=bar -touch /tmp/public-marker - -: "Transient service (system daemon)" -systemd-run --wait --pipe \ - bash -xec '[[ "$( "$V" -mount --bind "$V" /proc/version - -B=$(systemd-run -q --wait --pipe -p ProtectProc=invisible cat /proc/version) - -assert_eq "$A" "$B" - -umount /proc/version -rm "$V" 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-74.sh b/test/units/testsuite-74.sh deleted file mode 100755 index 9c2a033..0000000 --- a/test/units/testsuite-74.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/test-control.sh -. "$(dirname "$0")"/test-control.sh - -run_subtests - -touch /testok diff --git a/test/units/testsuite-74.varlinkctl.sh b/test/units/testsuite-74.varlinkctl.sh deleted file mode 100755 index 5a96269..0000000 --- a/test/units/testsuite-74.varlinkctl.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# Unset $PAGER so we don't have to use --no-pager everywhere -export PAGER= - -varlinkctl --help -varlinkctl help --no-pager -varlinkctl --version -varlinkctl --json=help - -# TODO: abstract namespace sockets (@...) -# Path to a socket -varlinkctl info /run/systemd/journal/io.systemd.journal -varlinkctl info /run/systemd/../systemd/../../run/systemd/journal/io.systemd.journal -varlinkctl info "./$(realpath --relative-to="$PWD" /run/systemd/journal/io.systemd.journal)" -varlinkctl info unix:/run/systemd/journal/io.systemd.journal -varlinkctl info --json=off /run/systemd/journal/io.systemd.journal -varlinkctl info --json=pretty /run/systemd/journal/io.systemd.journal | jq . -varlinkctl info --json=short /run/systemd/journal/io.systemd.journal | jq . -varlinkctl info -j /run/systemd/journal/io.systemd.journal | jq . - -varlinkctl list-interfaces /run/systemd/journal/io.systemd.journal -varlinkctl list-interfaces -j /run/systemd/journal/io.systemd.journal | jq . - -varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal -varlinkctl introspect -j /run/systemd/journal/io.systemd.journal io.systemd.Journal | jq . - -if command -v userdbctl >/dev/null; then - systemctl start systemd-userdbd - varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }' - varlinkctl call -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }' | jq . - varlinkctl call --more /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' - varlinkctl call --more -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' | jq --seq . - varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' - (! varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' | grep .) -fi - -IDL_FILE="$(mktemp)" -varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal | tee "${IDL_FILE:?}" -varlinkctl validate-idl "$IDL_FILE" -varlinkctl validate-idl "$IDL_FILE" -cat /bin/sh >"$IDL_FILE" -(! varlinkctl validate-idl "$IDL_FILE") - -if [[ -x /usr/lib/systemd/systemd-pcrextend ]]; then - # Path to an executable - varlinkctl info /usr/lib/systemd/systemd-pcrextend - varlinkctl info exec:/usr/lib/systemd/systemd-pcrextend - varlinkctl list-interfaces /usr/lib/systemd/systemd-pcrextend - varlinkctl introspect /usr/lib/systemd/systemd-pcrextend io.systemd.PCRExtend -fi - -# Go through all varlink sockets we can find under /run/systemd/ for some extra coverage -find /run/systemd/ -name "io.systemd*" -type s | while read -r socket; do - varlinkctl info "$socket" - - varlinkctl list-interfaces "$socket" | while read -r interface; do - varlinkctl introspect "$socket" "$interface" - done -done - -(! varlinkctl) -(! varlinkctl "") -(! varlinkctl info) -(! varlinkctl info "") -(! varlinkctl info /run/systemd/notify) -(! varlinkctl info /run/systemd/private) -# Relative paths must begin with ./ -(! varlinkctl info "$(realpath --relative-to="$PWD" /run/systemd/journal/io.systemd.journal)") -(! varlinkctl info unix:) -(! varlinkctl info unix:"") -(! varlinkctl info exec:) -(! varlinkctl info exec:"") -(! varlinkctl list-interfaces) -(! varlinkctl list-interfaces "") -(! varlinkctl introspect) -(! varlinkctl introspect /run/systemd/journal/io.systemd.journal) -(! varlinkctl introspect /run/systemd/journal/io.systemd.journal "") -(! varlinkctl introspect "" "") -(! varlinkctl call) -(! varlinkctl call "") -(! varlinkctl call "" "") -(! varlinkctl call "" "" "") -(! varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord /dev/null); then - TMPDIR=$(mktemp -d -p /tmp resolvconf-tests.XXXXXX) - RESOLVCONF="$TMPDIR"/resolvconf - ln -s "$(command -v resolvectl 2>/dev/null)" "$RESOLVCONF" -fi -echo nameserver 10.0.2.1 10.0.2.2 | "$RESOLVCONF" -a hoge -echo nameserver 10.0.2.3 10.0.2.4 | "$RESOLVCONF" -a hoge.foo -assert_in '10.0.2.1 10.0.2.2' "$(resolvectl dns hoge)" -assert_in '10.0.2.3 10.0.2.4' "$(resolvectl dns hoge.foo)" -echo nameserver 10.0.3.1 10.0.3.2 | "$RESOLVCONF" -a hoge.inet.ipsec.192.168.35 -echo nameserver 10.0.3.3 10.0.3.4 | "$RESOLVCONF" -a hoge.foo.dhcp -assert_in '10.0.3.1 10.0.3.2' "$(resolvectl dns hoge)" -assert_in '10.0.3.3 10.0.3.4' "$(resolvectl dns hoge.foo)" - -# Tests for _localdnsstub and _localdnsproxy -assert_in '127.0.0.53' "$(resolvectl query _localdnsstub)" -assert_in '_localdnsstub' "$(resolvectl query 127.0.0.53)" -assert_in '127.0.0.54' "$(resolvectl query _localdnsproxy)" -assert_in '_localdnsproxy' "$(resolvectl query 127.0.0.54)" - -assert_in '127.0.0.53' "$(dig @127.0.0.53 _localdnsstub)" -assert_in '_localdnsstub' "$(dig @127.0.0.53 -x 127.0.0.53)" -assert_in '127.0.0.54' "$(dig @127.0.0.53 _localdnsproxy)" -assert_in '_localdnsproxy' "$(dig @127.0.0.53 -x 127.0.0.54)" - -# Tests for mDNS and LLMNR settings -mkdir -p /run/systemd/resolved.conf.d -{ - echo "[Resolve]" - echo "MulticastDNS=yes" - echo "LLMNR=yes" -} >/run/systemd/resolved.conf.d/mdns-llmnr.conf -restart_resolved -# make sure networkd is not running. -systemctl stop systemd-networkd.service -# defaults to yes (both the global and per-link settings are yes) -assert_in 'yes' "$(resolvectl mdns hoge)" -assert_in 'yes' "$(resolvectl llmnr hoge)" -# set per-link setting -resolvectl mdns hoge yes -resolvectl llmnr hoge yes -assert_in 'yes' "$(resolvectl mdns hoge)" -assert_in 'yes' "$(resolvectl llmnr hoge)" -resolvectl mdns hoge resolve -resolvectl llmnr hoge resolve -assert_in 'resolve' "$(resolvectl mdns hoge)" -assert_in 'resolve' "$(resolvectl llmnr hoge)" -resolvectl mdns hoge no -resolvectl llmnr hoge no -assert_in 'no' "$(resolvectl mdns hoge)" -assert_in 'no' "$(resolvectl llmnr hoge)" -# downgrade global setting to resolve -{ - echo "[Resolve]" - echo "MulticastDNS=resolve" - echo "LLMNR=resolve" -} >/run/systemd/resolved.conf.d/mdns-llmnr.conf -restart_resolved -# set per-link setting -resolvectl mdns hoge yes -resolvectl llmnr hoge yes -assert_in 'resolve' "$(resolvectl mdns hoge)" -assert_in 'resolve' "$(resolvectl llmnr hoge)" -resolvectl mdns hoge resolve -resolvectl llmnr hoge resolve -assert_in 'resolve' "$(resolvectl mdns hoge)" -assert_in 'resolve' "$(resolvectl llmnr hoge)" -resolvectl mdns hoge no -resolvectl llmnr hoge no -assert_in 'no' "$(resolvectl mdns hoge)" -assert_in 'no' "$(resolvectl llmnr hoge)" -# downgrade global setting to no -{ - echo "[Resolve]" - echo "MulticastDNS=no" - echo "LLMNR=no" -} >/run/systemd/resolved.conf.d/mdns-llmnr.conf -restart_resolved -# set per-link setting -resolvectl mdns hoge yes -resolvectl llmnr hoge yes -assert_in 'no' "$(resolvectl mdns hoge)" -assert_in 'no' "$(resolvectl llmnr hoge)" -resolvectl mdns hoge resolve -resolvectl llmnr hoge resolve -assert_in 'no' "$(resolvectl mdns hoge)" -assert_in 'no' "$(resolvectl llmnr hoge)" -resolvectl mdns hoge no -resolvectl llmnr hoge no -assert_in 'no' "$(resolvectl mdns hoge)" -assert_in 'no' "$(resolvectl llmnr hoge)" - -# Cleanup -rm -f /run/systemd/resolved.conf.d/mdns-llmnr.conf -ip link del hoge -ip link del hoge.foo - -### SETUP ### -# Configure network -hostnamectl hostname ns1.unsigned.test -cat >>/etc/hosts </etc/systemd/network/10-dns0.netdev </etc/systemd/network/10-dns0.network </run/systemd/resolved.conf.d/test.conf -ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf -# Override the default NTA list, which turns off DNSSEC validation for (among -# others) the test. domain -mkdir -p "/etc/dnssec-trust-anchors.d/" -echo local >/etc/dnssec-trust-anchors.d/local.negative - -# Sign the root zone -keymgr . generate algorithm=ECDSAP256SHA256 ksk=yes zsk=yes -# Create a trust anchor for resolved with our root zone -keymgr . ds | sed 's/ DS/ IN DS/g' >/etc/dnssec-trust-anchors.d/root.positive -# Create a bind-compatible trust anchor (for delv) -# Note: the trust-anchors directive is relatively new, so use the original -# managed-keys one until it's widespread enough -{ - echo 'managed-keys {' - keymgr . dnskey | sed -r 's/^\. DNSKEY ([0-9]+ [0-9]+ [0-9]+) (.+)$/. static-key \1 "\2";/g' - echo '};' -} >/etc/bind.keys -# Create an /etc/bind/bind.keys symlink, which is used by delv on Ubuntu -mkdir -p /etc/bind -ln -svf /etc/bind.keys /etc/bind/bind.keys - -# Start the services -systemctl unmask systemd-networkd -systemctl start systemd-networkd -restart_resolved -# Create knot's runtime dir, since from certain version it's provided only by -# the package and not created by tmpfiles/systemd -if [[ ! -d /run/knot ]]; then - mkdir -p /run/knot - chown -R knot:knot /run/knot -fi -systemctl start knot -# Wait a bit for the keys to propagate -sleep 4 - -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 - -# 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 -[[ ! -s "$RUN_OUT" ]] -# We need to manually propagate the DS records of onlinesign.test. to the parent -# zone, since they're generated online -knotc zone-begin test. -if knotc zone-get test. onlinesign.test. ds | grep .; then - # Drop any old DS records, if present (e.g. on test re-run) - knotc zone-unset test. onlinesign.test. ds -fi -# Propagate the new DS records -while read -ra line; do - knotc zone-set test. "${line[0]}" 600 "${line[@]:1}" -done < <(keymgr onlinesign.test. ds) -knotc zone-commit test. - -knotc reload - -### SETUP END ### - -: "--- nss-resolve/nss-myhostname tests" -# Sanity check -TIMESTAMP=$(date '+%F %T') -# Issue: https://github.com/systemd/systemd/issues/23951 -# With IPv6 enabled -run getent -s resolve ahosts ns1.unsigned.test -grep -qE "^fd00:dead:beef:cafe::1\s+STREAM\s+ns1\.unsigned\.test" "$RUN_OUT" -monitor_check_rr "$TIMESTAMP" "ns1.unsigned.test IN AAAA fd00:dead:beef:cafe::1" -# With IPv6 disabled -# Issue: https://github.com/systemd/systemd/issues/23951 -disable_ipv6 -run getent -s resolve ahosts ns1.unsigned.test -grep -qE "^10\.0\.0\.1\s+STREAM\s+ns1\.unsigned\.test" "$RUN_OUT" -(! grep -qE "fd00:dead:beef:cafe::1" "$RUN_OUT") -monitor_check_rr "$TIMESTAMP" "ns1.unsigned.test IN A 10.0.0.1" -enable_ipv6 - -# Issue: https://github.com/systemd/systemd/issues/18812 -# PR: https://github.com/systemd/systemd/pull/18896 -# Follow-up issue: https://github.com/systemd/systemd/issues/23152 -# Follow-up PR: https://github.com/systemd/systemd/pull/23161 -# With IPv6 enabled -run getent -s resolve ahosts localhost -grep -qE "^::1\s+STREAM\s+localhost" "$RUN_OUT" -run getent -s myhostname ahosts localhost -grep -qE "^::1\s+STREAM\s+localhost" "$RUN_OUT" -# With IPv6 disabled -disable_ipv6 -run getent -s resolve ahosts localhost -grep -qE "^127\.0\.0\.1\s+STREAM\s+localhost" "$RUN_OUT" -(! grep -qE "::1" "$RUN_OUT") -run getent -s myhostname ahosts localhost -grep -qE "^127\.0\.0\.1\s+STREAM\s+localhost" "$RUN_OUT" -enable_ipv6 - -# Issue: https://github.com/systemd/systemd/issues/25088 -run getent -s resolve hosts 127.128.0.5 -grep -qEx '127\.128\.0\.5\s+localhost5(\s+localhost5?\.localdomain[45]?){4}' "$RUN_OUT" -[ "$(wc -l <"$RUN_OUT")" -eq 1 ] - -# Issue: https://github.com/systemd/systemd/issues/20158 -run dig +noall +answer +additional localhost5. -grep -qEx 'localhost5\.\s+0\s+IN\s+A\s+127\.128\.0\.5' "$RUN_OUT" -[ "$(wc -l <"$RUN_OUT")" -eq 1 ] -run dig +noall +answer +additional localhost5.localdomain4. -grep -qEx 'localhost5\.localdomain4\.\s+0\s+IN\s+CNAME\s+localhost5\.' "$RUN_OUT" -grep -qEx 'localhost5\.\s+0\s+IN\s+A\s+127\.128\.0\.5' "$RUN_OUT" -[ "$(wc -l <"$RUN_OUT")" -eq 2 ] - -: "--- Basic resolved tests ---" -# Issue: https://github.com/systemd/systemd/issues/22229 -# PR: https://github.com/systemd/systemd/pull/22231 -FILTERED_NAMES=( - "0.in-addr.arpa" - "255.255.255.255.in-addr.arpa" - "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" - "hello.invalid" - "hello.alt" -) - -for name in "${FILTERED_NAMES[@]}"; do - (! run host "$name") - grep -qF "NXDOMAIN" "$RUN_OUT" -done - -# Follow-up -# Issue: https://github.com/systemd/systemd/issues/22401 -# PR: https://github.com/systemd/systemd/pull/22414 -run dig +noall +authority +comments SRV . -grep -qF "status: NOERROR" "$RUN_OUT" -grep -qE "IN\s+SOA\s+ns1\.unsigned\.test\." "$RUN_OUT" - - -: "--- ZONE: unsigned.test. ---" -run dig @ns1.unsigned.test +short unsigned.test A unsigned.test AAAA -grep -qF "10.0.0.101" "$RUN_OUT" -grep -qF "fd00:dead:beef:cafe::101" "$RUN_OUT" -run resolvectl query unsigned.test -grep -qF "10.0.0.10" "$RUN_OUT" -grep -qF "fd00:dead:beef:cafe::101" "$RUN_OUT" -grep -qF "authenticated: no" "$RUN_OUT" -run dig @ns1.unsigned.test +short MX unsigned.test -grep -qF "15 mail.unsigned.test." "$RUN_OUT" -run resolvectl query --legend=no -t MX unsigned.test -grep -qF "unsigned.test IN MX 15 mail.unsigned.test" "$RUN_OUT" - - -: "--- ZONE: signed.test (static DNSSEC) ---" -# Check the trust chain (with and without systemd-resolved in between -# Issue: https://github.com/systemd/systemd/issues/22002 -# PR: https://github.com/systemd/systemd/pull/23289 -run_delv @ns1.unsigned.test signed.test -grep -qF "; fully validated" "$RUN_OUT" -run_delv signed.test -grep -qF "; fully validated" "$RUN_OUT" - -for addr in "${DNS_ADDRESSES[@]}"; do - run_delv "@$addr" -t A mail.signed.test - grep -qF "; fully validated" "$RUN_OUT" - run_delv "@$addr" -t AAAA mail.signed.test - grep -qF "; fully validated" "$RUN_OUT" -done -run resolvectl query mail.signed.test -grep -qF "10.0.0.11" "$RUN_OUT" -grep -qF "fd00:dead:beef:cafe::11" "$RUN_OUT" -grep -qF "authenticated: yes" "$RUN_OUT" - -run dig +short signed.test -grep -qF "10.0.0.10" "$RUN_OUT" -run resolvectl query signed.test -grep -qF "signed.test: 10.0.0.10" "$RUN_OUT" -grep -qF "authenticated: yes" "$RUN_OUT" -run dig @ns1.unsigned.test +short MX signed.test -grep -qF "10 mail.signed.test." "$RUN_OUT" -run resolvectl query --legend=no -t MX signed.test -grep -qF "signed.test IN MX 10 mail.signed.test" "$RUN_OUT" -# Check a non-existent domain -run dig +dnssec this.does.not.exist.signed.test -grep -qF "status: NXDOMAIN" "$RUN_OUT" -# Check a wildcard record -run resolvectl query -t TXT this.should.be.authenticated.wild.signed.test -grep -qF 'this.should.be.authenticated.wild.signed.test IN TXT "this is a wildcard"' "$RUN_OUT" -grep -qF "authenticated: yes" "$RUN_OUT" -# Check SRV support -run resolvectl service _mysvc._tcp signed.test -grep -qF "myservice.signed.test:1234" "$RUN_OUT" -grep -qF "10.0.0.20" "$RUN_OUT" -grep -qF "fd00:dead:beef:cafe::17" "$RUN_OUT" -grep -qF "authenticated: yes" "$RUN_OUT" -(! run resolvectl service _invalidsvc._udp signed.test) -grep -qE "invalidservice\.signed\.test' not found" "$RUN_OUT" -run resolvectl service _untrustedsvc._udp signed.test -grep -qF "myservice.untrusted.test:1111" "$RUN_OUT" -grep -qF "10.0.0.123" "$RUN_OUT" -grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT" -grep -qF "authenticated: yes" "$RUN_OUT" -# Check OPENPGPKEY support -run_delv -t OPENPGPKEY 5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test -grep -qF "; fully validated" "$RUN_OUT" -run resolvectl openpgp mr.smith@signed.test -grep -qF "5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test" "$RUN_OUT" -grep -qF "authenticated: yes" "$RUN_OUT" - -# DNSSEC validation with multiple records of the same type for the same name -# Issue: https://github.com/systemd/systemd/issues/22002 -# PR: https://github.com/systemd/systemd/pull/23289 -check_domain() { - local domain="${1:?}" - local record="${2:?}" - local message="${3:?}" - local addr - - for addr in "${DNS_ADDRESSES[@]}"; do - run_delv "@$addr" -t "$record" "$domain" - grep -qF "$message" "$RUN_OUT" - done - - run_delv -t "$record" "$domain" - grep -qF "$message" "$RUN_OUT" - - run resolvectl query "$domain" - grep -qF "authenticated: yes" "$RUN_OUT" -} - -check_domain "dupe.signed.test" "A" "; fully validated" -check_domain "dupe.signed.test" "AAAA" "; negative response, fully validated" -check_domain "dupe-ipv6.signed.test" "AAAA" "; fully validated" -check_domain "dupe-ipv6.signed.test" "A" "; negative response, fully validated" -check_domain "dupe-mixed.signed.test" "A" "; fully validated" -check_domain "dupe-mixed.signed.test" "AAAA" "; fully validated" - -# Test resolution of CNAME chains -TIMESTAMP=$(date '+%F %T') -run resolvectl query -t A cname-chain.signed.test -grep -qF "follow14.final.signed.test IN A 10.0.0.14" "$RUN_OUT" -grep -qF "authenticated: yes" "$RUN_OUT" - -monitor_check_rr "$TIMESTAMP" "follow10.so.close.signed.test IN CNAME follow11.yet.so.far.signed.test" -monitor_check_rr "$TIMESTAMP" "follow11.yet.so.far.signed.test IN CNAME follow12.getting.hot.signed.test" -monitor_check_rr "$TIMESTAMP" "follow12.getting.hot.signed.test IN CNAME follow13.almost.final.signed.test" -monitor_check_rr "$TIMESTAMP" "follow13.almost.final.signed.test IN CNAME follow14.final.signed.test" -monitor_check_rr "$TIMESTAMP" "follow14.final.signed.test IN A 10.0.0.14" - -# Non-existing RR + CNAME chain -run dig +dnssec AAAA cname-chain.signed.test -grep -qF "status: NOERROR" "$RUN_OUT" -grep -qE "^follow14\.final\.signed\.test\..+IN\s+NSEC\s+" "$RUN_OUT" - - -: "--- ZONE: onlinesign.test (dynamic DNSSEC) ---" -# Check the trust chain (with and without systemd-resolved in between -# Issue: https://github.com/systemd/systemd/issues/22002 -# PR: https://github.com/systemd/systemd/pull/23289 -run_delv @ns1.unsigned.test sub.onlinesign.test -grep -qF "; fully validated" "$RUN_OUT" -run_delv sub.onlinesign.test -grep -qF "; fully validated" "$RUN_OUT" - -run dig +short sub.onlinesign.test -grep -qF "10.0.0.133" "$RUN_OUT" -run resolvectl query sub.onlinesign.test -grep -qF "sub.onlinesign.test: 10.0.0.133" "$RUN_OUT" -grep -qF "authenticated: yes" "$RUN_OUT" -run dig @ns1.unsigned.test +short TXT onlinesign.test -grep -qF '"hello from onlinesign"' "$RUN_OUT" -run resolvectl query --legend=no -t TXT onlinesign.test -grep -qF 'onlinesign.test IN TXT "hello from onlinesign"' "$RUN_OUT" - -for addr in "${DNS_ADDRESSES[@]}"; do - run_delv "@$addr" -t A dual.onlinesign.test - grep -qF "10.0.0.135" "$RUN_OUT" - run_delv "@$addr" -t AAAA dual.onlinesign.test - grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT" - run_delv "@$addr" -t ANY ipv6.onlinesign.test - grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT" -done -run resolvectl query dual.onlinesign.test -grep -qF "10.0.0.135" "$RUN_OUT" -grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT" -grep -qF "authenticated: yes" "$RUN_OUT" -run resolvectl query ipv6.onlinesign.test -grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT" -grep -qF "authenticated: yes" "$RUN_OUT" - -# Check a non-existent domain -# Note: mod-onlinesign utilizes Minimally Covering NSEC Records, hence the -# different response than with "standard" DNSSEC -run dig +dnssec this.does.not.exist.onlinesign.test -grep -qF "status: NOERROR" "$RUN_OUT" -grep -qF "NSEC \\000.this.does.not.exist.onlinesign.test." "$RUN_OUT" -# Check a wildcard record -run resolvectl query -t TXT this.should.be.authenticated.wild.onlinesign.test -grep -qF 'this.should.be.authenticated.wild.onlinesign.test IN TXT "this is an onlinesign wildcard"' "$RUN_OUT" -grep -qF "authenticated: yes" "$RUN_OUT" - -# Resolve via dbus method -TIMESTAMP=$(date '+%F %T') -run busctl call org.freedesktop.resolve1 /org/freedesktop/resolve1 org.freedesktop.resolve1.Manager ResolveHostname 'isit' 0 secondsub.onlinesign.test 0 0 -grep -qF '10 0 0 134 "secondsub.onlinesign.test"' "$RUN_OUT" -monitor_check_rr "$TIMESTAMP" "secondsub.onlinesign.test IN A 10.0.0.134" - - -: "--- ZONE: untrusted.test (DNSSEC without propagated DS records) ---" -# Issue: https://github.com/systemd/systemd/issues/23955 -# FIXME -resolvectl flush-caches -#run dig +short untrusted.test A untrusted.test AAAA -#grep -qF "10.0.0.121" "$RUN_OUT" -#grep -qF "fd00:dead:beef:cafe::121" "$RUN_OUT" -run resolvectl query untrusted.test -grep -qF "untrusted.test:" "$RUN_OUT" -grep -qF "10.0.0.121" "$RUN_OUT" -grep -qF "fd00:dead:beef:cafe::121" "$RUN_OUT" -grep -qF "authenticated: no" "$RUN_OUT" -run resolvectl service _mysvc._tcp untrusted.test -grep -qF "myservice.untrusted.test:1234" "$RUN_OUT" -grep -qF "10.0.0.123" "$RUN_OUT" -grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT" - -# Issue: https://github.com/systemd/systemd/issues/19472 -# 1) Query for a non-existing RR should return NOERROR + NSEC (?), not NXDOMAIN -# FIXME: re-enable once the issue is resolved -#run dig +dnssec AAAA untrusted.test -#grep -qF "status: NOERROR" "$RUN_OUT" -#grep -qE "^untrusted\.test\..+IN\s+NSEC\s+" "$RUN_OUT" -## 2) Query for a non-existing name should return NXDOMAIN, not SERVFAIL -#run dig +dnssec this.does.not.exist.untrusted.test -#grep -qF "status: NXDOMAIN" "$RUN_OUT" - -### Test resolvectl show-cache -run resolvectl show-cache -run resolvectl show-cache --json=short -run resolvectl show-cache --json=pretty - -# Issue: https://github.com/systemd/systemd/issues/29580 (part #1) -dig @127.0.0.54 signed.test - -systemctl stop resolvectl-monitor.service -systemctl stop resolvectl-monitor-json.service - -# Issue: https://github.com/systemd/systemd/issues/29580 (part #2) -# -# Check for any warnings regarding malformed messages -(! journalctl -p warning --grep malformed _SYSTEMD_UNIT="resolvectl-monitor-json.service" + SYSLOG_IDENTIFIER="resolvectl-monitor-json") -# 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 - # Check that both "question" and "answer" fields are arrays - # - # The expression is slightly more complicated due to the fact that the "answer" field is optional, - # so we need to select it only if it's present, otherwise the type == "array" check would fail - echo "$line" | jq -e '[. | .question, (select(has("answer")) | .answer) | type == "array"] | all' -done - -# Test serve stale feature and NFTSet= if nftables is installed -if command -v nft >/dev/null; then - ### Test without serve stale feature ### - NFT_FILTER_NAME=dns_port_filter - - drop_dns_outbound_traffic() { - nft add table inet $NFT_FILTER_NAME - nft add chain inet $NFT_FILTER_NAME output \{ type filter hook output priority 0 \; \} - nft add rule inet $NFT_FILTER_NAME output ip daddr 10.0.0.1 udp dport 53 drop - nft add rule inet $NFT_FILTER_NAME output ip daddr 10.0.0.1 tcp dport 53 drop - nft add rule inet $NFT_FILTER_NAME output ip6 daddr fd00:dead:beef:cafe::1 udp dport 53 drop - nft add rule inet $NFT_FILTER_NAME output ip6 daddr fd00:dead:beef:cafe::1 tcp dport 53 drop - } - - run dig stale1.unsigned.test -t A - grep -qE "NOERROR" "$RUN_OUT" - sleep 2 - drop_dns_outbound_traffic - set +e - run dig stale1.unsigned.test -t A - set -eux - grep -qE "no servers could be reached" "$RUN_OUT" - nft flush ruleset - - ### Test TIMEOUT with serve stale feature ### - - mkdir -p /run/systemd/resolved.conf.d - { - echo "[Resolve]" - echo "StaleRetentionSec=1d" - } >/run/systemd/resolved.conf.d/test.conf - ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf - restart_resolved - - 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 - grep -qE "NOERROR" "$RUN_OUT" - grep -qE "10.0.0.112" "$RUN_OUT" - - nft flush ruleset - - ### Test NXDOMAIN with serve stale feature ### - # NXDOMAIN response should replace the cache with NXDOMAIN response - run dig stale1.unsigned.test -t A - grep -qE "NOERROR" "$RUN_OUT" - # Delete stale1 record from zone - knotc zone-begin unsigned.test - knotc zone-unset unsigned.test stale1 A - knotc zone-commit unsigned.test - knotc reload - sleep 2 - run dig stale1.unsigned.test -t A - grep -qE "NXDOMAIN" "$RUN_OUT" - - nft flush ruleset - - ### NFTSet= test - nft add table inet sd_test - nft add set inet sd_test c '{ type cgroupsv2; }' - nft add set inet sd_test u '{ typeof meta skuid; }' - nft add set inet sd_test g '{ typeof meta skgid; }' - - # service - systemd-run --unit test-nft.service --service-type=exec -p DynamicUser=yes \ - -p 'NFTSet=cgroup:inet:sd_test:c user:inet:sd_test:u group:inet:sd_test:g' sleep 10000 - run nft list set inet sd_test c - grep -qF "test-nft.service" "$RUN_OUT" - uid=$(getent passwd test-nft | cut -d':' -f3) - run nft list set inet sd_test u - grep -qF "$uid" "$RUN_OUT" - gid=$(getent passwd test-nft | cut -d':' -f4) - run nft list set inet sd_test g - grep -qF "$gid" "$RUN_OUT" - systemctl stop test-nft.service - - # scope - run systemd-run --scope -u test-nft.scope -p 'NFTSet=cgroup:inet:sd_test:c' nft list set inet sd_test c - grep -qF "test-nft.scope" "$RUN_OUT" - - mkdir -p /run/systemd/system - # socket - { - echo "[Socket]" - echo "ListenStream=12345" - echo "BindToDevice=lo" - echo "NFTSet=cgroup:inet:sd_test:c" - } >/run/systemd/system/test-nft.socket - { - echo "[Service]" - echo "ExecStart=/usr/bin/sleep 10000" - } >/run/systemd/system/test-nft.service - systemctl daemon-reload - systemctl start test-nft.socket - systemctl status test-nft.socket - run nft list set inet sd_test c - grep -qF "test-nft.socket" "$RUN_OUT" - systemctl stop test-nft.socket - rm -f /run/systemd/system/test-nft.{socket,service} - - # slice - mkdir /run/systemd/system/system.slice.d - { - echo "[Slice]" - echo "NFTSet=cgroup:inet:sd_test:c" - } >/run/systemd/system/system.slice.d/00-test-nft.conf - systemctl daemon-reload - run nft list set inet sd_test c - grep -qF "system.slice" "$RUN_OUT" - rm -rf /run/systemd/system/system.slice.d - - nft flush ruleset -else - echo "nftables is not installed. Skipped serve stale feature and NFTSet= tests." -fi - -### Test resolvectl show-server-state ### -run resolvectl show-server-state -grep -qF "10.0.0.1" "$RUN_OUT" -grep -qF "Interface" "$RUN_OUT" - -run resolvectl show-server-state --json=short -grep -qF "10.0.0.1" "$RUN_OUT" -grep -qF "Interface" "$RUN_OUT" - -run resolvectl show-server-state --json=pretty -grep -qF "10.0.0.1" "$RUN_OUT" -grep -qF "Interface" "$RUN_OUT" - -### Test resolvectl statistics ### -run resolvectl statistics -grep -qF "Transactions" "$RUN_OUT" -grep -qF "Cache" "$RUN_OUT" -grep -qF "Failure Transactions" "$RUN_OUT" -grep -qF "DNSSEC Verdicts" "$RUN_OUT" - -run resolvectl statistics --json=short -grep -qF "transactions" "$RUN_OUT" -grep -qF "cache" "$RUN_OUT" -grep -qF "dnssec" "$RUN_OUT" - -run resolvectl statistics --json=pretty -grep -qF "transactions" "$RUN_OUT" -grep -qF "cache" "$RUN_OUT" -grep -qF "dnssec" "$RUN_OUT" - -### Test resolvectl reset-statistics ### -run resolvectl reset-statistics - -run resolvectl reset-statistics --json=pretty - -run resolvectl reset-statistics --json=short - -# Check if resolved exits cleanly. -restart_resolved - -touch /testok diff --git a/test/units/testsuite-76.service b/test/units/testsuite-76.service deleted file mode 100644 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-76.sh b/test/units/testsuite-76.sh deleted file mode 100755 index 855d0ef..0000000 --- a/test/units/testsuite-76.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -export SYSTEMD_LOG_LEVEL=debug - -echo "foo.bar=42" >/tmp/foo.conf -assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf -assert_rc 1 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf - -echo "-foo.foo=42" >/tmp/foo.conf -assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf -assert_rc 0 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf - -if ! systemd-detect-virt --quiet --container; then - ip link add hoge type dummy - udevadm wait /sys/class/net/hoge - - cat >/tmp/foo.conf </proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp - echo 0 >/proc/sys/net/ipv4/conf/hoge/bootp_relay - echo 0 >/proc/sys/net/ipv4/conf/hoge/disable_policy - - assert_rc 0 /usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/hoge /tmp/foo.conf - assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp)" "1" - assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/bootp_relay)" "1" - assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/disable_policy)" "0" -fi - -touch /testok diff --git a/test/units/testsuite-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-server.socket b/test/units/testsuite-77-server.socket deleted file mode 100644 index 4305077..0000000 --- a/test/units/testsuite-77-server.socket +++ /dev/null @@ -1,6 +0,0 @@ -[Unit] -Description=TEST-77-OPENFILE server socket - -[Socket] -ListenStream=/tmp/test.sock -Accept=yes diff --git a/test/units/testsuite-77-server@.service b/test/units/testsuite-77-server@.service deleted file mode 100644 index 8e99ac8..0000000 --- a/test/units/testsuite-77-server@.service +++ /dev/null @@ -1,7 +0,0 @@ -[Unit] -Description=TEST-77-OPENFILE server - -[Service] -ExecStart=echo "Socket" -StandardInput=socket -StandardOutput=socket 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) - </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-78.sh b/test/units/testsuite-78.sh deleted file mode 100755 index 46afd3c..0000000 --- a/test/units/testsuite-78.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex -set -o pipefail - -if ! env --block-signal=SIGUSR1 true 2> /dev/null ; then - echo "env tool too old, can't block signals, skipping test." >&2 - echo OK >/testok - exit 0 -fi - -systemd-analyze log-level debug - -UNIT="test-sigqueue-$RANDOM.service" - -systemd-run -u "$UNIT" -p Type=notify -p DynamicUser=1 -- env --block-signal=SIGRTMIN+7 systemd-notify --exec --ready \; sleep infinity - -systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT" -systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT" -systemctl kill --kill-whom=main --kill-value=7 --signal=SIGRTMIN+7 "$UNIT" -systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT" -systemctl kill --kill-whom=main --kill-value=32 --signal=SIGRTMIN+7 "$UNIT" -systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT" - -# We simply check that six signals are queued now. There's no easy way to check -# from shell which ones those are, hence we don't check that. -P=$(systemctl show -P MainPID "$UNIT") - -test "$(grep SigQ: /proc/"$P"/status | cut -d: -f2 | cut -d/ -f1)" -eq 6 - -systemctl stop $UNIT - -systemd-analyze log-level info - -touch /testok diff --git a/test/units/testsuite-79.service b/test/units/testsuite-79.service deleted file mode 100644 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-79.sh b/test/units/testsuite-79.sh deleted file mode 100755 index 205f7f3..0000000 --- a/test/units/testsuite-79.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex -set -o pipefail - -# We not just test if the file exists, but try to read from it, since if -# CONFIG_PSI_DEFAULT_DISABLED is set in the kernel the file will exist and can -# be opened, but any read()s will fail with EOPNOTSUPP, which we want to -# detect. -if ! cat /proc/pressure/memory >/dev/null ; then - echo "kernel too old, has no PSI." >&2 - echo OK >/testok - exit 0 -fi - -systemd-analyze log-level debug - -CGROUP=/sys/fs/cgroup/"$(systemctl show testsuite-79.service -P ControlGroup)" -test -d "$CGROUP" - -if ! test -f "$CGROUP"/memory.pressure ; then - echo "No memory accounting/PSI delegated via cgroup, can't test." >&2 - echo OK >/testok - exit 0 -fi - -UNIT="test-mempress-$RANDOM.service" -SCRIPT="/tmp/mempress-$RANDOM.sh" - -cat >"$SCRIPT" <<'EOF' -#!/bin/bash - -set -ex - -export -id - -test -n "$MEMORY_PRESSURE_WATCH" -test "$MEMORY_PRESSURE_WATCH" != /dev/null -test -w "$MEMORY_PRESSURE_WATCH" - -ls -al "$MEMORY_PRESSURE_WATCH" - -EXPECTED="$(echo -n -e "some 123000 2000000\x00" | base64)" - -test "$EXPECTED" = "$MEMORY_PRESSURE_WRITE" - -EOF - -chmod +x "$SCRIPT" - -systemd-run -u "$UNIT" -p Type=exec -p ProtectControlGroups=1 -p DynamicUser=1 -p MemoryPressureWatch=on -p MemoryPressureThresholdSec=123ms -p BindPaths=$SCRIPT --wait "$SCRIPT" - -rm "$SCRIPT" - -systemd-analyze log-level info - -touch /testok diff --git a/test/units/testsuite-80.service b/test/units/testsuite-80.service deleted file mode 100644 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-80.sh b/test/units/testsuite-80.sh deleted file mode 100755 index 97b222a..0000000 --- a/test/units/testsuite-80.sh +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2016 -set -eux -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -mkfifo /tmp/syncfifo1 /tmp/syncfifo2 - -sync_in() { - read -r x < /tmp/syncfifo1 - test "$x" = "$1" -} - -sync_out() { - echo "$1" > /tmp/syncfifo2 -} - -export SYSTEMD_LOG_LEVEL=debug - -systemctl --no-block start notify.service - -sync_in a - -assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all" -assert_eq "$(systemctl show notify.service -p StatusText --value)" "Test starts" - -sync_out b -sync_in c - -assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "main" -assert_eq "$(systemctl show notify.service -p StatusText --value)" "Sending READY=1 in an unprivileged process" -assert_rc 3 systemctl --quiet is-active notify.service - -sync_out d -sync_in e - -systemctl --quiet is-active notify.service -assert_eq "$(systemctl show notify.service -p StatusText --value)" "OK" -assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "none" - -systemctl stop notify.service -assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all" - -rm /tmp/syncfifo1 /tmp/syncfifo2 - -# Now test basic fdstore behaviour - -MYSCRIPT="/tmp/myscript$RANDOM.sh" -cat >> "$MYSCRIPT" <<'EOF' -#!/usr/bin/env bash -set -eux -set -o pipefail -test "$FDSTORE" -eq 7 -N="/tmp/$RANDOM" -echo $RANDOM > "$N" -systemd-notify --fd=4 --fdname=quux --pid=parent 4< "$N" -rm "$N" -systemd-notify --ready -exec sleep infinity -EOF - -chmod +x "$MYSCRIPT" - -MYUNIT="myunit$RANDOM.service" -systemd-run -u "$MYUNIT" -p Type=notify -p FileDescriptorStoreMax=7 "$MYSCRIPT" - -test "$(systemd-analyze fdstore "$MYUNIT" | wc -l)" -eq 2 -systemd-analyze fdstore "$MYUNIT" --json=short -systemd-analyze fdstore "$MYUNIT" --json=short | grep -P -q '\[{"fdname":"quux","type":.*,"devno":\[.*\],"inode":.*,"rdevno":null,"path":"/tmp/.*","flags":"ro"}\]' - -systemctl stop "$MYUNIT" -rm "$MYSCRIPT" - -systemd-analyze log-level debug - -# Test fdstore pinning (this will pull in fdstore-pin.service fdstore-nopin.service) -systemctl start fdstore-pin.target - -assert_eq "$(systemctl show fdstore-pin.service -P FileDescriptorStorePreserve)" yes -assert_eq "$(systemctl show fdstore-nopin.service -P FileDescriptorStorePreserve)" restart -assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running -assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running -assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 -assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1 - -# The file descriptor store should survive service restarts -systemctl restart fdstore-pin.service fdstore-nopin.service - -assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 -assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1 -assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running -assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running - -# It should not survive the service stop plus a later start (unless pinned) -systemctl stop fdstore-pin.service fdstore-nopin.service - -assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 -assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0 -assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned -assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead - -systemctl start fdstore-pin.service fdstore-nopin.service - -assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 -assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0 -assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running -assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running - -systemctl stop fdstore-pin.service fdstore-nopin.service - -assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 -assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0 -assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned -assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead - -systemctl clean fdstore-pin.service --what=fdstore - -assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 0 -assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0 -assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead -assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead - -touch /testok diff --git a/test/units/testsuite-81.debug-generator.sh b/test/units/testsuite-81.debug-generator.sh deleted file mode 100755 index fddf85a..0000000 --- a/test/units/testsuite-81.debug-generator.sh +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2235 -set -eux -set -o pipefail - -# shellcheck source=test/units/generator-utils.sh -. "$(dirname "$0")/generator-utils.sh" - -GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-debug-generator" -OUT_DIR="$(mktemp -d /tmp/debug-generator.XXX)" - -at_exit() { - rm -frv "${OUT_DIR:?}" -} - -trap at_exit EXIT - -test -x "${GENERATOR_BIN:?}" - -# Potential FIXME: -# - debug-generator should gracefully handle duplicated mask/wants -# - also, handle gracefully empty mask/wants -ARGS=( - "systemd.mask=masked-no-suffix" - "systemd.mask=masked.service" - "systemd.mask=masked.socket" - "systemd.wants=wanted-no-suffix" - "systemd.wants=wanted.service" - "systemd.wants=wanted.mount" - "rd.systemd.mask=masked-initrd.service" - "rd.systemd.wants=wanted-initrd.service" -) - -# Regular (non-initrd) scenario -# -: "debug-shell: regular" -CMDLINE="ro root=/ ${ARGS[*]} rd.systemd.debug_shell" -SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null -link_eq "$OUT_DIR/early/masked.service" /dev/null -link_eq "$OUT_DIR/early/masked.socket" /dev/null -link_endswith "$OUT_DIR/early/default.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service -link_endswith "$OUT_DIR/early/default.target.wants/wanted.service" /lib/systemd/system/wanted.service -link_endswith "$OUT_DIR/early/default.target.wants/wanted.mount" /lib/systemd/system/wanted.mount -# Following stuff should be ignored, as it's prefixed with rd. -test ! -h "$OUT_DIR/early/masked-initrd.service" -test ! -h "$OUT_DIR/early/default.target.wants/wants-initrd.service" -test ! -h "$OUT_DIR/early/default.target.wants/debug-shell.service" -test ! -d "$OUT_DIR/early/initrd.target.wants" - -# Let's re-run the generator with systemd.debug_shell that should be honored -: "debug-shell: regular + systemd.debug_shell" -CMDLINE="$CMDLINE systemd.debug_shell" -SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service - -# Same thing, but with custom tty -: "debug-shell: regular + systemd.debug_shell=/dev/tty666" -CMDLINE="$CMDLINE systemd.debug_shell=/dev/tty666" -SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service -grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf" - -# Now override the default target via systemd.unit= -: "debug-shell: regular + systemd.unit=" -CMDLINE="$CMDLINE systemd.unit=my-fancy.target" -SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_eq "$OUT_DIR/early/masked-no-suffix.service" /dev/null -link_eq "$OUT_DIR/early/masked.service" /dev/null -link_eq "$OUT_DIR/early/masked.socket" /dev/null -link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted-no-suffix.service" /lib/systemd/system/wanted-no-suffix.service -link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.service" /lib/systemd/system/wanted.service -link_endswith "$OUT_DIR/early/my-fancy.target.wants/wanted.mount" /lib/systemd/system/wanted.mount -link_endswith "$OUT_DIR/early/my-fancy.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service -test ! -d "$OUT_DIR/early/default.target.wants" - - -# Initrd scenario -: "debug-shell: initrd" -CMDLINE="ro root=/ ${ARGS[*]} systemd.debug_shell" -SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null -link_endswith "$OUT_DIR/early/initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service -# The non-initrd stuff (i.e. without the rd. suffix) should be ignored in -# this case -test ! -h "$OUT_DIR/early/masked-no-suffix.service" -test ! -h "$OUT_DIR/early/masked.service" -test ! -h "$OUT_DIR/early/masked.socket" -test ! -h "$OUT_DIR/early/initrd.target.wants/debug-shell.service" -test ! -d "$OUT_DIR/early/default.target.wants" - -# Again, but with rd.systemd.debug_shell -: "debug-shell: initrd + rd.systemd.debug_shell" -CMDLINE="$CMDLINE rd.systemd.debug_shell" -SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_endswith "$OUT_DIR/early/initrd.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service - -# Override the default target -: "debug-shell: initrd + rd.systemd.unit" -CMDLINE="$CMDLINE rd.systemd.unit=my-fancy-initrd.target" -SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_eq "$OUT_DIR/early/masked-initrd.service" /dev/null -link_endswith "$OUT_DIR/early/my-fancy-initrd.target.wants/wanted-initrd.service" /lib/systemd/system/wanted-initrd.service -test ! -d "$OUT_DIR/early/initrd.target.wants" diff --git a/test/units/testsuite-81.environment-d-generator.sh b/test/units/testsuite-81.environment-d-generator.sh deleted file mode 100755 index 5bc3978..0000000 --- a/test/units/testsuite-81.environment-d-generator.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2235 -set -eux -set -o pipefail - -# shellcheck source=test/units/generator-utils.sh -. "$(dirname "$0")/generator-utils.sh" - -GENERATOR_BIN="/usr/lib/systemd/user-environment-generators/30-systemd-environment-d-generator" -CONFIG_FILE="/run/environment.d/99-test.conf" -OUT_FILE="$(mktemp)" - -at_exit() { - set +e - rm -frv "${CONFIG_FILE:?}" "${OUT_FILE:?}" - systemctl -M testuser@.host --user daemon-reload -} - -trap at_exit EXIT - -test -x "${GENERATOR_BIN:?}" -mkdir -p /run/environment.d/ - -cat >"$CONFIG_FILE" <"$OUT_FILE" - -# Check if the generator is correctly called in a user session -systemctl -M testuser@.host --user daemon-reload -systemctl -M testuser@.host --user show-environment | tee "$OUT_FILE" -check_environment "$OUT_FILE" - -(! "$GENERATOR_BIN" foo) diff --git a/test/units/testsuite-81.fstab-generator.sh b/test/units/testsuite-81.fstab-generator.sh deleted file mode 100755 index 50c4b2f..0000000 --- a/test/units/testsuite-81.fstab-generator.sh +++ /dev/null @@ -1,406 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2235,SC2233 -set -eux -set -o pipefail - -# shellcheck source=test/units/generator-utils.sh -. "$(dirname "$0")/generator-utils.sh" - -GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-fstab-generator" -NETWORK_FS_RX="^(afs|ceph|cifs|gfs|gfs2|ncp|ncpfs|nfs|nfs4|ocfs2|orangefs|pvfs2|smb3|smbfs|davfs|glusterfs|lustre|sshfs)$" -OUT_DIR="$(mktemp -d /tmp/fstab-generator.XXX)" -FSTAB="$(mktemp)" - -at_exit() { - rm -fr "${OUT_DIR:?}" "${FSTAB:?}" -} - -trap at_exit EXIT - -test -x "${GENERATOR_BIN:?}" - -FSTAB_GENERAL=( - # Valid entries - "/dev/test2 /nofail ext4 nofail 0 0" - "/dev/test3 /regular btrfs defaults 0 0" - "/dev/test4 /x-systemd.requires xfs x-systemd.requires=foo.service 0 0" - "/dev/test5 /x-systemd.before-after xfs x-systemd.before=foo.service,x-systemd.after=bar.mount 0 0" - "/dev/test6 /x-systemd.wanted-required-by xfs x-systemd.wanted-by=foo.service,x-systemd.required-by=bar.device 0 0" - "/dev/test7 /x-systemd.requires-mounts-for xfs x-systemd.requires-mounts-for=/foo/bar/baz 0 0" - "/dev/test8 /x-systemd.automount-idle-timeout vfat x-systemd.automount,x-systemd.idle-timeout=50s 0 0" - "/dev/test9 /x-systemd.makefs xfs x-systemd.makefs 0 0" - "/dev/test10 /x-systemd.growfs xfs x-systemd.growfs 0 0" - "/dev/test11 /_netdev ext4 defaults,_netdev 0 0" - "/dev/test12 /_rwonly ext4 x-systemd.rw-only 0 0" - "/dev/test13 /chaos1 zfs x-systemd.rw-only,x-systemd.requires=hello.service,x-systemd.after=my.device 0 0" - "/dev/test14 /chaos2 zfs x.systemd.wanted-by=foo.service,x-systemd.growfs,x-systemd.makefs 0 0" - "/dev/test15 /fstype/auto auto defaults 0 0" - "/dev/test16 /fsck/me ext4 defaults 0 1" - "/dev/test17 /also/fsck/me ext4 defaults,x-systemd.requires-mounts-for=/var/lib/foo 0 99" - "/dev/test18 /swap swap defaults 0 0" - "/dev/test19 /swap/makefs swap defaults,x-systemd.makefs 0 0" - "/dev/test20 /var xfs defaults,x-systemd.device-timeout=1h 0 0" - "/dev/test21 /usr ext4 defaults 0 1" - "/dev/test22 /initrd/mount ext2 defaults,x-systemd.rw-only,x-initrd.mount 0 1" - "/dev/test23 /initrd/mount/nofail ext3 defaults,nofail,x-initrd.mount 0 1" - "/dev/test24 /initrd/mount/deps ext4 x-initrd.mount,x-systemd.before=early.service,x-systemd.after=late.service 0 1" - - # Incomplete, but valid entries - "/dev/incomplete1 /incomplete1" - "/dev/incomplete2 /incomplete2 ext4" - "/dev/incomplete3 /incomplete3 ext4 defaults" - "/dev/incomplete4 /incomplete4 ext4 defaults 0" - - # Remote filesystems - "/dev/remote1 /nfs nfs bg 0 0" - "/dev/remote2 /nfs4 nfs4 bg 0 0" - "bar.tld:/store /remote/storage nfs ro,x-systemd.wanted-by=store.service 0 0" - "user@host.tld:/remote/dir /remote/top-secret sshfs rw,x-systemd.before=naughty.service 0 0" - "foo.tld:/hello /hello/world ceph defaults 0 0" - "//192.168.0.1/storage /cifs-storage cifs automount,nofail 0 0" -) - -FSTAB_GENERAL_ROOT=( - # rootfs with bunch of options we should ignore and fsck enabled - "/dev/test1 / ext4 noauto,nofail,x-systemd.automount,x-systemd.wanted-by=foo,x-systemd.required-by=bar 0 1" - "${FSTAB_GENERAL[@]}" -) - -FSTAB_MINIMAL=( - "/dev/loop1 /foo/bar ext3 defaults 0 0" -) - -FSTAB_DUPLICATE=( - "/dev/dup1 / ext4 defaults 0 1" - "/dev/dup2 / ext4 defaults,x-systemd.requires=foo.mount 0 2" -) - -FSTAB_INVALID=( - # Ignored entries - "/dev/ignored1 /sys/fs/cgroup/foo ext4 defaults 0 0" - "/dev/ignored2 /sys/fs/selinux ext4 defaults 0 0" - "/dev/ignored3 /dev/console ext4 defaults 0 0" - "/dev/ignored4 /proc/kmsg ext4 defaults 0 0" - "/dev/ignored5 /proc/sys ext4 defaults 0 0" - "/dev/ignored6 /proc/sys/kernel/random/boot_id ext4 defaults 0 0" - "/dev/ignored7 /run/host ext4 defaults 0 0" - "/dev/ignored8 /run/host/foo ext4 defaults 0 0" - "/dev/ignored9 /autofs autofs defaults 0 0" - "/dev/invalid1 not-a-path ext4 defaults 0 0" - "" - "/dev/invalid1" - " " - "\\" - "$" -) - -check_fstab_mount_units() { - local what where fstype opts passno unit - local item opt split_options filtered_options supp service device arg - local array_name="${1:?}" - local out_dir="${2:?}/normal" - # Get a reference to the array from its name - local -n fstab_entries="$array_name" - - # Running the checks in a container is pretty much useless, since we don't - # generate any mounts, but don't skip the whole test to test the "skip" - # paths as well - in_container && return 0 - - for item in "${fstab_entries[@]}"; do - # Don't use a pipe here, as it would make the variables out of scope - read -r what where fstype opts _ passno <<< "$item" - - # Skip non-initrd mounts in initrd - if in_initrd_host && ! [[ "$opts" =~ x-initrd.mount ]]; then - continue - fi - - if [[ "$fstype" == swap ]]; then - unit="$(systemd-escape --suffix=swap --path "${what:?}")" - cat "$out_dir/$unit" - - grep -qE "^What=$what$" "$out_dir/$unit" - if [[ "$opts" != defaults ]]; then - grep -qE "^Options=$opts$" "$out_dir/$unit" - fi - - if [[ "$opts" =~ x-systemd.makefs ]]; then - service="$(systemd-escape --template=systemd-mkswap@.service --path "$what")" - test -e "$out_dir/$service" - fi - - continue - fi - - # If we're parsing host's fstab in initrd, prefix all mount targets - # with /sysroot - in_initrd_host && where="/sysroot${where:?}" - unit="$(systemd-escape --suffix=mount --path "${where:?}")" - cat "$out_dir/$unit" - - # Check the general stuff - grep -qE "^What=$what$" "$out_dir/$unit" - grep -qE "^Where=$where$" "$out_dir/$unit" - if [[ -n "$fstype" ]] && [[ "$fstype" != auto ]]; then - grep -qE "^Type=$fstype$" "$out_dir/$unit" - fi - if [[ -n "$opts" ]] && [[ "$opts" != defaults ]]; then - # Some options are not propagated to the generated unit - if [[ "$where" == / ]]; then - filtered_options="$(opt_filter "$opts" "(noauto|nofail|x-systemd.(wanted-by=|required-by=|automount|device-timeout=))")" - else - filtered_options="$(opt_filter "$opts" "^x-systemd.device-timeout=")" - fi - - if [[ "${filtered_options[*]}" != defaults ]]; then - grep -qE "^Options=.*$filtered_options.*$" "$out_dir/$unit" - fi - fi - - if ! [[ "$opts" =~ (noauto|x-systemd.(wanted-by=|required-by=|automount)) ]]; then - # We don't create the Requires=/Wants= symlinks for noauto/automount mounts - # and for mounts that use x-systemd.wanted-by=/required-by= - if in_initrd_host; then - if [[ "$where" == / ]] || ! [[ "$opts" =~ nofail ]]; then - link_eq "$out_dir/initrd-fs.target.requires/$unit" "../$unit" - else - link_eq "$out_dir/initrd-fs.target.wants/$unit" "../$unit" - fi - elif [[ "$fstype" =~ $NETWORK_FS_RX || "$opts" =~ _netdev ]]; then - # Units with network filesystems should have a Requires= dependency - # on the remote-fs.target, unless they use nofail or are an nfs "bg" - # mounts, in which case the dependency is downgraded to Wants= - if [[ "$opts" =~ nofail ]] || [[ "$fstype" =~ ^(nfs|nfs4) && "$opts" =~ bg ]]; then - link_eq "$out_dir/remote-fs.target.wants/$unit" "../$unit" - else - link_eq "$out_dir/remote-fs.target.requires/$unit" "../$unit" - fi - else - # Similarly, local filesystems should have a Requires= dependency on - # the local-fs.target, unless they use nofail, in which case the - # dependency is downgraded to Wants=. Rootfs is a special case, - # since we always ignore nofail there - if [[ "$where" == / ]] || ! [[ "$opts" =~ nofail ]]; then - link_eq "$out_dir/local-fs.target.requires/$unit" "../$unit" - else - link_eq "$out_dir/local-fs.target.wants/$unit" "../$unit" - fi - fi - fi - - if [[ "${passno:=0}" -ne 0 ]]; then - # Generate systemd-fsck@.service dependencies, if applicable - if in_initrd && [[ "$where" == / || "$where" == /usr ]]; then - continue - fi - - if [[ "$where" == / ]]; then - link_endswith "$out_dir/local-fs.target.wants/systemd-fsck-root.service" "/lib/systemd/system/systemd-fsck-root.service" - else - service="$(systemd-escape --template=systemd-fsck@.service --path "$what")" - grep -qE "^After=$service$" "$out_dir/$unit" - if [[ "$where" == /usr ]]; then - grep -qE "^Wants=$service$" "$out_dir/$unit" - else - grep -qE "^Requires=$service$" "$out_dir/$unit" - fi - fi - fi - - # Check various x-systemd options - # - # First, split them into an array to make splitting them even further - # easier - IFS="," read -ra split_options <<< "$opts" - # and process them one by one. - # - # Note: the "machinery" below might (and probably does) miss some - # combinations of supported options, so tread carefully - for opt in "${split_options[@]}"; do - if [[ "$opt" =~ ^x-systemd.requires= ]]; then - service="$(opt_get_arg "$opt")" - grep -qE "^Requires=$service$" "$out_dir/$unit" - grep -qE "^After=$service$" "$out_dir/$unit" - elif [[ "$opt" =~ ^x-systemd.before= ]]; then - service="$(opt_get_arg "$opt")" - grep -qE "^Before=$service$" "$out_dir/$unit" - elif [[ "$opt" =~ ^x-systemd.after= ]]; then - service="$(opt_get_arg "$opt")" - grep -qE "^After=$service$" "$out_dir/$unit" - elif [[ "$opt" =~ ^x-systemd.wanted-by= ]]; then - service="$(opt_get_arg "$opt")" - if [[ "$where" == / ]]; then - # This option is ignored for rootfs mounts - (! link_eq "$out_dir/$service.wants/$unit" "../$unit") - else - link_eq "$out_dir/$service.wants/$unit" "../$unit" - fi - elif [[ "$opt" =~ ^x-systemd.required-by= ]]; then - service="$(opt_get_arg "$opt")" - if [[ "$where" == / ]]; then - # This option is ignored for rootfs mounts - (! link_eq "$out_dir/$service.requires/$unit" "../$unit") - else - link_eq "$out_dir/$service.requires/$unit" "../$unit" - fi - elif [[ "$opt" =~ ^x-systemd.requires-mounts-for= ]]; then - arg="$(opt_get_arg "$opt")" - grep -qE "^RequiresMountsFor=$arg$" "$out_dir/$unit" - elif [[ "$opt" == x-systemd.device-bound ]]; then - # This is implied for fstab mounts - : - elif [[ "$opt" == x-systemd.automount ]]; then - # The $unit should have an accompanying automount unit - supp="$(systemd-escape --suffix=automount --path "$where")" - if [[ "$where" == / ]]; then - # This option is ignored for rootfs mounts - test ! -e "$out_dir/$supp" - (! link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp") - else - test -e "$out_dir/$supp" - link_eq "$out_dir/local-fs.target.requires/$supp" "../$supp" - fi - elif [[ "$opt" =~ ^x-systemd.idle-timeout= ]]; then - # The timeout applies to the automount unit, not the original - # mount one - arg="$(opt_get_arg "$opt")" - supp="$(systemd-escape --suffix=automount --path "$where")" - grep -qE "^TimeoutIdleSec=$arg$" "$out_dir/$supp" - elif [[ "$opt" =~ ^x-systemd.device-timeout= ]]; then - arg="$(opt_get_arg "$opt")" - device="$(systemd-escape --suffix=device --path "$what")" - grep -qE "^JobRunningTimeoutSec=$arg$" "$out_dir/${device}.d/50-device-timeout.conf" - elif [[ "$opt" == x-systemd.makefs ]]; then - service="$(systemd-escape --template=systemd-makefs@.service --path "$what")" - test -e "$out_dir/$service" - link_eq "$out_dir/${unit}.requires/$service" "../$service" - elif [[ "$opt" == x-systemd.rw-only ]]; then - grep -qE "^ReadWriteOnly=yes$" "$out_dir/$unit" - elif [[ "$opt" == x-systemd.growfs ]]; then - service="$(systemd-escape --template=systemd-growfs@.service --path "$where")" - link_endswith "$out_dir/${unit}.wants/$service" "/lib/systemd/system/systemd-growfs@.service" - elif [[ "$opt" == bg ]] && [[ "$fstype" =~ ^(nfs|nfs4)$ ]]; then - # We "convert" nfs bg mounts to fg, so we can do the job-control - # ourselves - grep -qE "^Options=.*\bx-systemd.mount-timeout=infinity\b" "$out_dir/$unit" - grep -qE "^Options=.*\bfg\b.*" "$out_dir/$unit" - elif [[ "$opt" =~ ^x-systemd\. ]]; then - echo >&2 "Unhandled mount option: $opt" - exit 1 - fi - done - done -} - -: "fstab-generator: regular" -printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB" -cat "$FSTAB" -SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -check_fstab_mount_units FSTAB_GENERAL_ROOT "$OUT_DIR" - -# Skip the rest when running in a container, as it makes little sense to check -# initrd-related stuff there and fstab-generator might have a bit strange -# behavior during certain tests, like https://github.com/systemd/systemd/issues/27156 -if in_container; then - echo "Running in a container, skipping the rest of the fstab-generator tests..." - exit 0 -fi - -# In this mode we treat the entries as "regular" ones -: "fstab-generator: initrd - initrd fstab" -printf "%s\n" "${FSTAB_GENERAL[@]}" >"$FSTAB" -cat "$FSTAB" -SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR" -SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_SYSROOT_FSTAB=/dev/null check_fstab_mount_units FSTAB_GENERAL "$OUT_DIR" - -# In this mode we prefix the mount target with /sysroot and ignore all mounts -# that don't have the x-initrd.mount flag -: "fstab-generator: initrd - host fstab" -printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB" -cat "$FSTAB" -SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_GENERAL_ROOT "$OUT_DIR" - -# Check the default stuff that we (almost) always create in initrd -: "fstab-generator: initrd default" -SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null run_and_list "$GENERATOR_BIN" "$OUT_DIR" -test -e "$OUT_DIR/normal/sysroot.mount" -test -e "$OUT_DIR/normal/systemd-fsck-root.service" -link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount" -link_eq "$OUT_DIR/normal/initrd-root-fs.target.requires/sysroot.mount" "../sysroot.mount" - -: "fstab-generator: run as systemd-sysroot-fstab-check in initrd" -ln -svf "$GENERATOR_BIN" /tmp/systemd-sysroot-fstab-check -(! /tmp/systemd-sysroot-fstab-check foo) -(! SYSTEMD_IN_INITRD=0 /tmp/systemd-sysroot-fstab-check) -printf "%s\n" "${FSTAB_GENERAL[@]}" >"$FSTAB" -SYSTEMD_IN_INITRD=1 SYSTEMD_SYSROOT_FSTAB="$FSTAB" /tmp/systemd-sysroot-fstab-check - -: "fstab-generator: duplicate" -printf "%s\n" "${FSTAB_DUPLICATE[@]}" >"$FSTAB" -cat "$FSTAB" -(! SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR") - -: "fstab-generator: invalid" -printf "%s\n" "${FSTAB_INVALID[@]}" >"$FSTAB" -cat "$FSTAB" -# Don't care about the exit code here -SYSTEMD_FSTAB="$FSTAB" run_and_list "$GENERATOR_BIN" "$OUT_DIR" || : -# No mounts should get created here -[[ "$(find "$OUT_DIR" -name "*.mount" | wc -l)" -eq 0 ]] - -: "fstab-generator: kernel args - fstab=0" -printf "%s\n" "${FSTAB_MINIMAL[@]}" >"$FSTAB" -SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -(! SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR") -SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -(! SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR") - -: "fstab-generator: kernel args - rd.fstab=0" -printf "%s\n" "${FSTAB_MINIMAL[@]}" >"$FSTAB" -SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR" -SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="rd.fstab=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -(! SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB="$FSTAB" check_fstab_mount_units FSTAB_MINIMAL "$OUT_DIR") - -: "fstab-generator: kernel args - systemd.swap=0" -printf "%s\n" "${FSTAB_GENERAL_ROOT[@]}" >"$FSTAB" -cat "$FSTAB" -SYSTEMD_FSTAB="$FSTAB" SYSTEMD_PROC_CMDLINE="systemd.swap=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -# No swap units should get created here -[[ "$(find "$OUT_DIR" -name "*.swap" | wc -l)" -eq 0 ]] - -# Possible TODO -# - combine the rootfs & usrfs arguments and mix them with fstab entries -# - systemd.volatile= -: "fstab-generator: kernel args - root= + rootfstype= + rootflags=" -# shellcheck disable=SC2034 -EXPECTED_FSTAB=( - "/dev/disk/by-label/rootfs / ext4 noexec,ro 0 1" -) -CMDLINE="root=LABEL=rootfs rootfstype=ext4 rootflags=noexec" -SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -# The /proc/cmdline here is a dummy value to tell the in_initrd_host() function -# we're parsing host's fstab, but it's all on the kernel cmdline instead -SYSTEMD_IN_INITRD=1 SYSTEMD_SYSROOT_FSTAB=/proc/cmdline check_fstab_mount_units EXPECTED_FSTAB "$OUT_DIR" - -# This is a very basic sanity test that involves manual checks, since adding it -# to the check_fstab_mount_units() function would make it way too complex -# (yet another possible TODO) -: "fstab-generator: kernel args - mount.usr= + mount.usrfstype= + mount.usrflags=" -CMDLINE="mount.usr=UUID=be780f43-8803-4a76-9732-02ceda6e9808 mount.usrfstype=ext4 mount.usrflags=noexec,nodev" -SYSTEMD_IN_INITRD=1 SYSTEMD_FSTAB=/dev/null SYSTEMD_SYSROOT_FSTAB=/dev/null SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -cat "$OUT_DIR/normal/sysroot-usr.mount" "$OUT_DIR/normal/sysusr-usr.mount" -# The general idea here is to mount the device to /sysusr/usr and then -# bind-mount /sysusr/usr to /sysroot/usr -grep -qE "^What=/dev/disk/by-uuid/be780f43-8803-4a76-9732-02ceda6e9808$" "$OUT_DIR/normal/sysusr-usr.mount" -grep -qE "^Where=/sysusr/usr$" "$OUT_DIR/normal/sysusr-usr.mount" -grep -qE "^Type=ext4$" "$OUT_DIR/normal/sysusr-usr.mount" -grep -qE "^Options=noexec,nodev,ro$" "$OUT_DIR/normal/sysusr-usr.mount" -link_eq "$OUT_DIR/normal/initrd-usr-fs.target.requires/sysusr-usr.mount" "../sysusr-usr.mount" -grep -qE "^What=/sysusr/usr$" "$OUT_DIR/normal/sysroot-usr.mount" -grep -qE "^Where=/sysroot/usr$" "$OUT_DIR/normal/sysroot-usr.mount" -grep -qE "^Options=bind$" "$OUT_DIR/normal/sysroot-usr.mount" -link_eq "$OUT_DIR/normal/initrd-fs.target.requires/sysroot-usr.mount" "../sysroot-usr.mount" diff --git a/test/units/testsuite-81.getty-generator.sh b/test/units/testsuite-81.getty-generator.sh deleted file mode 100755 index 103e966..0000000 --- a/test/units/testsuite-81.getty-generator.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2235 -set -eux -set -o pipefail -# Disable history expansion so we don't have to escape ! in strings below -set +o histexpand - -# shellcheck source=test/units/generator-utils.sh -. "$(dirname "$0")/generator-utils.sh" - -GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-getty-generator" -OUT_DIR="$(mktemp -d /tmp/getty-generator.XXX)" - -at_exit() { - rm -frv "${OUT_DIR:?}" -} - -trap at_exit EXIT - -test -x "${GENERATOR_BIN:?}" - -if in_container; then - # Do a limited test in a container, as writing to /dev is usually restrited - : "getty-generator: \$container_ttys env (container)" - # In a container we allow only /dev/pts/* ptys - PID1_ENVIRON="container_ttys=tty0 pts/0 /dev/tty0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" - - # console-getty.service is always pulled in in containers - link_endswith "$OUT_DIR/normal/getty.target.wants/console-getty.service" "/lib/systemd/system/console-getty.service" - link_endswith "$OUT_DIR/normal/getty.target.wants/container-getty@0.service" "/lib/systemd/system/container-getty@.service" - test ! -e "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service" - test ! -h "$OUT_DIR/normal/getty.target.wants/container-getty@tty0.service" - - exit 0 -fi - -DUMMY_ACTIVE_CONSOLES=( - "hvc99" - "xvc99" - "hvsi99" - "sclp_line99" - "ttysclp99" - "3270!tty99" - "dummy99" -) -DUMMY_INACTIVE_CONSOLES=( - "inactive99" - "xvc199" -) -DUMMY_CONSOLES=( - "${DUMMY_ACTIVE_CONSOLES[@]}" - "${DUMMY_INACTIVE_CONSOLES[@]}" -) -# Create a bunch of dummy consoles -for console in "${DUMMY_CONSOLES[@]}"; do - mknod "/dev/$console" c 4 0 -done -# Sneak in one "not-a-tty" console -touch /dev/notatty99 -# Temporarily replace /sys/class/tty/console/active with our list of dummy -# consoles so getty-generator can process them -echo -ne "${DUMMY_ACTIVE_CONSOLES[@]}" /dev/notatty99 >/tmp/dummy-active-consoles -mount -v --bind /tmp/dummy-active-consoles /sys/class/tty/console/active - -: "getty-generator: no arguments" -# Sneak in an invalid value for $SYSTEMD_GETTY_AUTO to test things out -PID1_ENVIRON="SYSTEMD_GETTY_AUTO=foo" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -for console in "${DUMMY_ACTIVE_CONSOLES[@]}"; do - unit="$(systemd-escape --template serial-getty@.service "$console")" - link_endswith "$OUT_DIR/normal/getty.target.wants/$unit" "/lib/systemd/system/serial-getty@.service" -done -for console in "${DUMMY_INACTIVE_CONSOLES[@]}" /dev/notatty99; do - unit="$(systemd-escape --template serial-getty@.service "$console")" - test ! -e "$OUT_DIR/normal/getty.target.wants/$unit" - test ! -h "$OUT_DIR/normal/getty.target.wants/$unit" -done - -: "getty-generator: systemd.getty_auto=0 on kernel cmdline" -SYSTEMD_PROC_CMDLINE="systemd.getty_auto=foo systemd.getty_auto=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]] - -: "getty-generator: SYSTEMD_GETTY_AUTO=0 in PID1's environment" -PID1_ENVIRON="SYSTEMD_GETTY_AUTO=0" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]] - -# Cleanup -umount /sys/class/tty/console/active -rm -f "${DUMMY_CONSOLES[@]/#//dev/}" /dev/notatty99 diff --git a/test/units/testsuite-81.run-generator.sh b/test/units/testsuite-81.run-generator.sh deleted file mode 100755 index 9bd74ef..0000000 --- a/test/units/testsuite-81.run-generator.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2235 -set -eux -set -o pipefail - -# shellcheck source=test/units/generator-utils.sh -. "$(dirname "$0")/generator-utils.sh" - -GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-run-generator" -OUT_DIR="$(mktemp -d /tmp/run-generator.XXX)" - -at_exit() { - rm -frv "${OUT_DIR:?}" -} - -trap at_exit EXIT - -test -x "${GENERATOR_BIN:?}" - -check_kernel_cmdline_target() { - local out_dir="${1:?}/normal" - - cat "$out_dir/kernel-command-line.target" - grep -qE "^Requires=kernel-command-line.service$" "$out_dir/kernel-command-line.target" - grep -qE "^After=kernel-command-line.service$" "$out_dir/kernel-command-line.target" - - link_eq "$out_dir/default.target" "kernel-command-line.target" -} - -: "run-generator: empty cmdline" -SYSTEMD_PROC_CMDLINE="" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]] - -: "run-generator: single command" -CMDLINE="systemd.run='echo hello world'" -SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -check_kernel_cmdline_target "$OUT_DIR" -UNIT="$OUT_DIR/normal/kernel-command-line.service" -cat "$UNIT" -systemd-analyze verify --man=no --recursive-errors=no "$UNIT" -grep -qE "^SuccessAction=exit$" "$UNIT" -grep -qE "^FailureAction=exit$" "$UNIT" -grep -qE "^ExecStart=echo hello world$" "$UNIT" - -: "run-generator: multiple commands + success/failure actions" -ARGS=( - # These should be ignored - "systemd.run" - "systemd.run_success_action" - "systemd.run_failure_action" - - # Set actions which we will overwrite later - "systemd.run_success_action=" - "systemd.run_failure_action=" - - "systemd.run=/bin/false" - "systemd.run=" - "systemd.run=/bin/true" - "systemd.run='echo this is a long string'" - - "systemd.run_success_action=reboot" - "systemd.run_failure_action=poweroff-force" -) -CMDLINE="${ARGS[*]}" -SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR" -check_kernel_cmdline_target "$OUT_DIR" -UNIT="$OUT_DIR/normal/kernel-command-line.service" -cat "$UNIT" -systemd-analyze verify --man=no --recursive-errors=no "$UNIT" -grep -qE "^SuccessAction=reboot$" "$UNIT" -grep -qE "^FailureAction=poweroff-force$" "$UNIT" -grep -qE "^ExecStart=/bin/false$" "$UNIT" -grep -qE "^ExecStart=$" "$UNIT" -grep -qE "^ExecStart=/bin/true$" "$UNIT" -grep -qE "^ExecStart=echo this is a long string$" "$UNIT" diff --git a/test/units/testsuite-81.service b/test/units/testsuite-81.service deleted file mode 100644 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-81.sh b/test/units/testsuite-81.sh deleted file mode 100755 index 9c2a033..0000000 --- a/test/units/testsuite-81.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eux -set -o pipefail - -# shellcheck source=test/units/test-control.sh -. "$(dirname "$0")"/test-control.sh - -run_subtests - -touch /testok diff --git a/test/units/testsuite-81.system-update-generator.sh b/test/units/testsuite-81.system-update-generator.sh deleted file mode 100755 index 8ee1fee..0000000 --- a/test/units/testsuite-81.system-update-generator.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -# shellcheck disable=SC2235 -set -eux -set -o pipefail - -# shellcheck source=test/units/generator-utils.sh -. "$(dirname "$0")/generator-utils.sh" - -GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-system-update-generator" -OUT_DIR="$(mktemp -d /tmp/system-update-generator-generator.XXX)" - -at_exit() { - rm -frv "${OUT_DIR:?}" /system-update -} - -trap at_exit EXIT - -test -x "${GENERATOR_BIN:?}" - -rm -f /system-update - -: "system-update-generator: no /system-update flag" -run_and_list "$GENERATOR_BIN" "$OUT_DIR" -[[ "$(find "$OUT_DIR" ! -type d | wc -l)" -eq 0 ]] - -: "system-update-generator: with /system-update flag" -touch /system-update -run_and_list "$GENERATOR_BIN" "$OUT_DIR" -link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target" - -: "system-update-generator: kernel cmdline warnings" -# We should warn if the default target is overridden on the kernel cmdline -# by a runlevel or systemd.unit=, but still generate the symlink -SYSTEMD_PROC_CMDLINE="systemd.unit=foo.bar 3" run_and_list "$GENERATOR_BIN" "$OUT_DIR" |& tee /tmp/system-update-generator.log -link_endswith "$OUT_DIR/early/default.target" "/lib/systemd/system/system-update.target" -grep -qE "Offline system update overridden .* systemd.unit=" /tmp/system-update-generator.log -grep -qE "Offline system update overridden .* runlevel" /tmp/system-update-generator.log diff --git a/test/units/testsuite-82.service b/test/units/testsuite-82.service deleted file mode 100644 index a8fc4f9..0000000 --- a/test/units/testsuite-82.service +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -[Unit] -Description=TEST-82-SOFTREBOOT -DefaultDependencies=no -After=basic.target - -[Service] -Type=oneshot -ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh -FileDescriptorStoreMax=3 -NotifyAccess=all diff --git a/test/units/testsuite-82.sh b/test/units/testsuite-82.sh deleted file mode 100755 index b5e6ded..0000000 --- a/test/units/testsuite-82.sh +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -ex -set -o pipefail - -# shellcheck source=test/units/util.sh -. "$(dirname "$0")"/util.sh - -at_exit() { - # Since the soft-reboot drops the enqueued end.service, we won't shutdown - # the test VM if the test fails and have to wait for the watchdog to kill - # us (which may take quite a long time). Let's just forcibly kill the machine - # instead to save CI resources. - if [[ $? -ne 0 ]]; then - echo >&2 "Test failed, shutting down the machine..." - systemctl poweroff -ff - fi -} - -trap at_exit EXIT - -systemd-analyze log-level debug - -export SYSTEMD_LOG_LEVEL=debug - -if [ -f /run/testsuite82.touch3 ]; then - echo "This is the fourth boot!" - systemd-notify --status="Fourth Boot" - - rm /run/testsuite82.touch3 - mount - rmdir /original-root /run/nextroot - - # Check that the fdstore entry still exists - test "$LISTEN_FDS" -eq 3 - read -r x <&5 - test "$x" = "oinkoink" - - # Check that the surviving services are still around - test "$(systemctl show -P ActiveState testsuite-82-survive.service)" = "active" - test "$(systemctl show -P ActiveState testsuite-82-survive-argv.service)" = "active" - test "$(systemctl show -P ActiveState testsuite-82-nosurvive-sigterm.service)" != "active" - test "$(systemctl show -P ActiveState testsuite-82-nosurvive.service)" != "active" - - # Check journals - journalctl -o short-monotonic --no-hostname --grep '(will soft-reboot|KILL|corrupt)' - assert_eq "$(journalctl -q -o short-monotonic -u systemd-journald.service --grep 'corrupt')" "" - - # All succeeded, exit cleanly now - -elif [ -f /run/testsuite82.touch2 ]; then - echo "This is the third boot!" - systemd-notify --status="Third Boot" - - rm /run/testsuite82.touch2 - - # Check that the fdstore entry still exists - test "$LISTEN_FDS" -eq 2 - read -r x <&4 - test "$x" = "miaumiau" - - # Upload another entry - T="/dev/shm/fdstore.$RANDOM" - echo "oinkoink" >"$T" - systemd-notify --fd=3 --pid=parent 3<"$T" - rm "$T" - - # Check that the surviving services are still around - test "$(systemctl show -P ActiveState testsuite-82-survive.service)" = "active" - test "$(systemctl show -P ActiveState testsuite-82-survive-argv.service)" = "active" - test "$(systemctl show -P ActiveState testsuite-82-nosurvive-sigterm.service)" != "active" - test "$(systemctl show -P ActiveState testsuite-82-nosurvive.service)" != "active" - - # Test that we really are in the new overlayfs root fs - read -r x "$T" - systemd-notify --fd=3 --pid=parent 3<"$T" - rm "$T" - - # Check that the surviving services are still around - test "$(systemctl show -P ActiveState testsuite-82-survive.service)" = "active" - test "$(systemctl show -P ActiveState testsuite-82-survive-argv.service)" = "active" - test "$(systemctl show -P ActiveState testsuite-82-nosurvive-sigterm.service)" != "active" - test "$(systemctl show -P ActiveState testsuite-82-nosurvive.service)" != "active" - - # This time we test the /run/nextroot/ root switching logic. (We synthesize a new rootfs from the old via overlayfs) - mkdir -p /run/nextroot /tmp/nextroot-lower /original-root - mount -t tmpfs tmpfs /tmp/nextroot-lower - echo miep >/tmp/nextroot-lower/lower - - # Copy os-release away, so that we can manipulate it and check that it is updated in the propagate - # directory across soft reboots. Try to cover corner cases by truncating it. - mkdir -p /tmp/nextroot-lower/usr/lib - grep ID /etc/os-release >/tmp/nextroot-lower/usr/lib/os-release - echo MARKER=1 >>/tmp/nextroot-lower/usr/lib/os-release - cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release - (! grep -q MARKER=1 /etc/os-release) - - mount -t overlay nextroot /run/nextroot -o lowerdir=/tmp/nextroot-lower:/,ro - - # Bind our current root into the target so that we later can return to it - mount --bind / /run/nextroot/original-root - - # Restart the unit that is not supposed to survive - systemd-run --collect --service-type=exec --unit=testsuite-82-nosurvive.service sleep infinity - - # Now issue the soft reboot. We should be right back soon. Given /run/nextroot exists, we should - # automatically do a softreboot instead of normal reboot. - touch /run/testsuite82.touch2 - systemctl --no-block reboot - - # Now block until the soft-boot killing spree kills us - exec sleep infinity -else - # This is the first boot - systemd-notify --status="First Boot" - - # Let's upload an fd to the fdstore, so that we can verify fdstore passing works correctly - T="/dev/shm/fdstore.$RANDOM" - echo "wuffwuff" >"$T" - systemd-notify --fd=3 --pid=parent 3<"$T" - rm "$T" - - survive_sigterm="/dev/shm/survive-sigterm-$RANDOM.sh" - cat >"$survive_sigterm" <"$survive_argv" < '$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" <"$initdir/opt/script0.sh" <\${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" <"$initdir/opt/script1.sh" <\${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" <"$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 +} -- cgit v1.2.3